mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
feat: Implement Objectives System with UI and Server Sync
- Added ObjectivesManager to track mission objectives and tasks. - Created ObjectivesPanel for displaying objectives in a collapsible HUD. - Integrated objectives state restoration from the server during game initialization. - Implemented task completion and unlocking mechanisms via game actions. - Added CSS styles for the objectives panel with a pixel-art aesthetic. - Developed a test scenario to validate the objectives system functionality. - Updated database schema to include fields for tracking completed objectives and tasks.
This commit is contained in:
8
db/migrate/20251125100000_add_objectives_to_games.rb
Normal file
8
db/migrate/20251125100000_add_objectives_to_games.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class AddObjectivesToGames < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
# Objectives state stored in player_state JSONB (already exists)
|
||||
# Add helper columns for quick queries and stats
|
||||
add_column :break_escape_games, :objectives_completed, :integer, default: 0
|
||||
add_column :break_escape_games, :tasks_completed, :integer, default: 0
|
||||
end
|
||||
end
|
||||
37
db/seeds.rb
37
db/seeds.rb
@@ -24,7 +24,10 @@ def apply_default_metadata(mission, scenario_name)
|
||||
end
|
||||
|
||||
# List all scenario directories
|
||||
scenario_dirs = Dir.glob(BreakEscape::Engine.root.join('scenarios/*')).select { |f| File.directory?(f) }
|
||||
scenario_root = BreakEscape::Engine.root.join('scenarios')
|
||||
puts "Looking for scenarios in: #{scenario_root}"
|
||||
scenario_dirs = Dir.glob("#{scenario_root}/*").select { |f| File.directory?(f) }
|
||||
puts "Found #{scenario_dirs.length} directories"
|
||||
|
||||
created_count = 0
|
||||
updated_count = 0
|
||||
@@ -33,12 +36,17 @@ cybok_total = 0
|
||||
|
||||
scenario_dirs.each do |dir|
|
||||
scenario_name = File.basename(dir)
|
||||
next if SKIP_DIRS.include?(scenario_name)
|
||||
|
||||
if SKIP_DIRS.include?(scenario_name)
|
||||
puts " SKIP: #{scenario_name}"
|
||||
skipped_count += 1
|
||||
next
|
||||
end
|
||||
|
||||
# Check for scenario.json.erb (required for valid mission)
|
||||
scenario_template = File.join(dir, 'scenario.json.erb')
|
||||
unless File.exist?(scenario_template)
|
||||
puts " ⊘ Skipped: #{scenario_name} (no scenario.json.erb)"
|
||||
puts " SKIP: #{scenario_name} (no scenario.json.erb)"
|
||||
skipped_count += 1
|
||||
next
|
||||
end
|
||||
@@ -64,33 +72,33 @@ scenario_dirs.each do |dir|
|
||||
if metadata['cybok'].present?
|
||||
cybok_count = BreakEscape::CybokSyncService.sync_for_mission(mission, metadata['cybok'])
|
||||
cybok_total += cybok_count
|
||||
puts " ✓ #{is_new ? 'Created' : 'Updated'}: #{mission.display_name} (#{cybok_count} CyBOK entries)"
|
||||
puts " #{is_new ? 'CREATE' : 'UPDATE'}: #{mission.display_name} (#{cybok_count} CyBOK)"
|
||||
else
|
||||
puts " ✓ #{is_new ? 'Created' : 'Updated'}: #{mission.display_name}"
|
||||
puts " #{is_new ? 'CREATE' : 'UPDATE'}: #{mission.display_name}"
|
||||
end
|
||||
is_new ? created_count += 1 : updated_count += 1
|
||||
else
|
||||
puts " ✗ Failed: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
|
||||
puts " ERROR: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
|
||||
end
|
||||
rescue JSON::ParserError => e
|
||||
puts " ⚠ Invalid mission.json for #{scenario_name}: #{e.message}"
|
||||
puts " WARN: Invalid mission.json for #{scenario_name}: #{e.message}"
|
||||
# Fall back to defaults
|
||||
apply_default_metadata(mission, scenario_name)
|
||||
if mission.save
|
||||
puts " ✓ #{is_new ? 'Created' : 'Updated'} (defaults): #{mission.display_name}"
|
||||
puts " #{is_new ? 'CREATE' : 'UPDATE'} (defaults): #{mission.display_name}"
|
||||
is_new ? created_count += 1 : updated_count += 1
|
||||
else
|
||||
puts " ✗ Failed: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
|
||||
puts " ERROR: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
|
||||
end
|
||||
end
|
||||
else
|
||||
# No mission.json - use defaults
|
||||
apply_default_metadata(mission, scenario_name)
|
||||
if mission.save
|
||||
puts " ✓ #{is_new ? 'Created' : 'Updated'} (defaults): #{mission.display_name}"
|
||||
puts " #{is_new ? 'CREATE' : 'UPDATE'} (defaults): #{mission.display_name}"
|
||||
is_new ? created_count += 1 : updated_count += 1
|
||||
else
|
||||
puts " ✗ Failed: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
|
||||
puts " ERROR: #{scenario_name} - #{mission.errors.full_messages.join(', ')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -100,10 +108,11 @@ puts '=' * 50
|
||||
puts "Done! #{BreakEscape::Mission.count} missions total."
|
||||
puts " Created: #{created_count}, Updated: #{updated_count}, Skipped: #{skipped_count}"
|
||||
puts " CyBOK entries synced: #{cybok_total}"
|
||||
puts " Collections: #{BreakEscape::Mission.collections.join(', ')}"
|
||||
collections = BreakEscape::Mission.distinct.pluck(:collection).compact
|
||||
puts " Collections: #{collections.join(', ')}"
|
||||
if BreakEscape::CybokSyncService.hacktivity_mode?
|
||||
puts ' Mode: Hacktivity (CyBOK data synced to both tables)'
|
||||
puts ' Mode: Hacktivity'
|
||||
else
|
||||
puts ' Mode: Standalone (CyBOK data in break_escape_cyboks only)'
|
||||
puts ' Mode: Standalone'
|
||||
end
|
||||
puts '=' * 50
|
||||
|
||||
Reference in New Issue
Block a user