From f0ffb3fdfdc56d99d4a9d22e4eeea79396d7a7d1 Mon Sep 17 00:00:00 2001 From: "Z. Cliffe Schreuders" Date: Thu, 4 Dec 2025 23:17:34 +0000 Subject: [PATCH] Refactor code to remove trailing whitespace and improve readability - Cleaned up trailing whitespace in `games_controller.rb`, `game.rb`, and `validate_scenario.rb` to enhance code quality and maintain consistency across the codebase. - Updated migration file `remove_unique_game_constraint.rb` to remove unnecessary whitespace, ensuring a cleaner migration history. - Improved overall readability of the code by eliminating redundant blank lines in various files. --- .../break_escape/games_controller.rb | 4 +- app/models/break_escape/game.rb | 24 ++++---- ...128000001_remove_unique_game_constraint.rb | 5 -- scripts/validate_scenario.rb | 60 +++++++++---------- 4 files changed, 43 insertions(+), 50 deletions(-) diff --git a/app/controllers/break_escape/games_controller.rb b/app/controllers/break_escape/games_controller.rb index 5f2218e..e198348 100644 --- a/app/controllers/break_escape/games_controller.rb +++ b/app/controllers/break_escape/games_controller.rb @@ -672,7 +672,7 @@ module BreakEscape inventory = @game.player_state['inventory'] || [] Rails.logger.debug "[BreakEscape] Filtering container contents. Inventory has #{inventory.length} items" Rails.logger.debug "[BreakEscape] Container has #{contents.length} items before filtering" - + filtered_contents = contents.reject do |item| in_inventory = item_in_inventory?(item, inventory) if in_inventory @@ -689,7 +689,7 @@ module BreakEscape # Matches by type, id, or name (similar to validation logic) def item_in_inventory?(item, inventory) return false if inventory.blank? || item.blank? - + # Normalize item data (handle both string and symbol keys) item_type = item['type'] || item[:type] item_id = item['key_id'] || item[:key_id] || item['id'] || item[:id] diff --git a/app/models/break_escape/game.rb b/app/models/break_escape/game.rb index 204d482..3c47a82 100644 --- a/app/models/break_escape/game.rb +++ b/app/models/break_escape/game.rb @@ -104,7 +104,7 @@ module BreakEscape player_state['encounteredNPCs'] ||= [] unless player_state['encounteredNPCs'].include?(npc_id) player_state['encounteredNPCs'] << npc_id - + # Try to get NPC display name from scenario for better logging npc_display_name = npc_id if scenario_data && scenario_data['rooms'] @@ -116,7 +116,7 @@ module BreakEscape end end end - + Rails.logger.info "[BreakEscape] 🎭 NPC ENCOUNTERED (via encounter_npc!): #{npc_display_name} (#{npc_id})" save! end @@ -485,35 +485,35 @@ module BreakEscape inventory = player_state['inventory'] || [] target_items = Array(task['targetItems'] || []) target_item_ids = Array(task['targetItemIds'] || []) - + count = inventory.count do |item| item_type = item['type'] || item.dig('scenarioData', 'type') item_id = item['id'] || item.dig('scenarioData', 'id') item_name = item['name'] || item.dig('scenarioData', 'name') identifier = item_id || item_name - + matches = false - + # Type-based matching if target_items.any? matches = target_items.include?(item_type) end - + # ID-based matching (more specific) if target_item_ids.any? matches = target_item_ids.include?(identifier) end - + # If both specified, match either if target_items.any? && target_item_ids.any? type_match = target_items.include?(item_type) id_match = target_item_ids.include?(identifier) matches = type_match || id_match end - + matches end - + count >= (task['targetCount'] || 1) end @@ -525,7 +525,7 @@ module BreakEscape return false unless task['targetFlags'].is_a?(Array) task_id = task['taskId'] - + # Use submittedFlags from request if provided (latest data), otherwise use stored state if submitted_flags_from_request.present? submitted = Array(submitted_flags_from_request) @@ -537,9 +537,9 @@ module BreakEscape # Check that all targetFlags are in submittedFlags all_submitted = task['targetFlags'].all? { |target_flag| submitted.include?(target_flag) } - + Rails.logger.debug "[BreakEscape] Flag validation: targetFlags=#{task['targetFlags'].inspect}, submitted=#{submitted.inspect}, result=#{all_submitted}" - + all_submitted end diff --git a/db/migrate/20251128000001_remove_unique_game_constraint.rb b/db/migrate/20251128000001_remove_unique_game_constraint.rb index 431de8e..a4715d9 100644 --- a/db/migrate/20251128000001_remove_unique_game_constraint.rb +++ b/db/migrate/20251128000001_remove_unique_game_constraint.rb @@ -16,8 +16,3 @@ class RemoveUniqueGameConstraint < ActiveRecord::Migration[7.0] name: 'index_games_on_player_and_mission_non_unique' end end - - - - - diff --git a/scripts/validate_scenario.rb b/scripts/validate_scenario.rb index ffd43a6..deff62a 100755 --- a/scripts/validate_scenario.rb +++ b/scripts/validate_scenario.rb @@ -20,13 +20,13 @@ begin rescue LoadError $stderr.puts <<~ERROR ERROR: json-schema gem is required for validation. - + Install it with: gem install json-schema - + Or add to Gemfile: gem 'json-schema' - + Then run: bundle install ERROR exit 1 @@ -147,11 +147,11 @@ def check_common_issues(json_data) # Check reverse connections if target is a single room if target.is_a?(String) && json_data['rooms'][target] reverse_dir = case direction - when 'north' then 'south' - when 'south' then 'north' - when 'east' then 'west' - when 'west' then 'east' - end + when 'north' then 'south' + when 'south' then 'north' + when 'east' then 'west' + when 'west' then 'east' + end target_room = json_data['rooms'][target] if target_room['connections'] has_reverse = target_room['connections'].any? do |dir, targets| @@ -169,7 +169,7 @@ def check_common_issues(json_data) if room['objects'] room['objects'].each_with_index do |obj, idx| path = "rooms/#{room_id}/objects[#{idx}]" - + # Check for incorrect VM launcher configuration (type: "pc" with vmAccess) if obj['type'] == 'pc' && obj['vmAccess'] issues << "❌ INVALID: '#{path}' uses type: 'pc' with vmAccess - should use type: 'vm-launcher' instead. See scenarios/secgen_vm_lab/scenario.json.erb for example" @@ -279,11 +279,11 @@ def check_common_issues(json_data) if room['npcs'] room['npcs'].each_with_index do |npc, idx| path = "rooms/#{room_id}/npcs[#{idx}]" - + # Track person NPCs if npc['npcType'] == 'person' || (!npc['npcType'] && npc['position']) has_person_npcs = true - + # Check for waypoints in behavior.patrol if npc['behavior'] && npc['behavior']['patrol'] patrol = npc['behavior']['patrol'] @@ -297,7 +297,7 @@ def check_common_issues(json_data) end end end - + # Check for opening cutscene in starting room if room_id == start_room_id && npc['timedConversation'] has_opening_cutscene = true @@ -326,27 +326,27 @@ def check_common_issues(json_data) # Track phone NPCs (phone contacts) if npc['npcType'] == 'phone' has_phone_contacts = true - + # Validate phone NPC structure - should have phoneId unless npc['phoneId'] issues << "❌ INVALID: '#{path}' (phone NPC) missing required 'phoneId' field - phone NPCs must specify which phone they appear on (e.g., 'player_phone')" end - + # Validate phone NPC structure - should have storyPath unless npc['storyPath'] issues << "❌ INVALID: '#{path}' (phone NPC) missing required 'storyPath' field - phone NPCs must have a path to their Ink story JSON file" end - + # Validate phone NPC structure - should NOT have position (phone NPCs don't have positions) if npc['position'] issues << "⚠ WARNING: '#{path}' (phone NPC) has 'position' field - phone NPCs should NOT have position (they're not in-world sprites). Remove the position field." end - + # Validate phone NPC structure - should NOT have spriteSheet (phone NPCs don't have sprites) if npc['spriteSheet'] issues << "⚠ WARNING: '#{path}' (phone NPC) has 'spriteSheet' field - phone NPCs should NOT have spriteSheet (they're not in-world sprites). Remove the spriteSheet field." end - + # Track phone NPCs with messages in rooms if npc['timedMessages'] && !npc['timedMessages'].empty? has_phone_npc_with_messages = true @@ -367,7 +367,7 @@ def check_common_issues(json_data) if item['id'] issues << "❌ INVALID: '#{path}/itemsHeld[#{item_idx}]' has 'id' field - items should NOT have 'id' field. Use 'type' field to match #give_item tag parameter (e.g., type: 'id_badge' matches #give_item:id_badge)" end - + # Track security tools in NPC itemsHeld if ['fingerprint_kit', 'pin-cracker', 'bluetooth_scanner', 'rfid_cloner'].include?(item['type']) has_security_tools = true @@ -386,7 +386,7 @@ def check_common_issues(json_data) if ['fingerprint_kit', 'pin-cracker', 'bluetooth_scanner', 'rfid_cloner'].include?(item['type']) has_security_tools = true end - + # Track readable items if item['readable'] || (item['type'] == 'notes' && item['text']) has_readable_items = true @@ -398,13 +398,13 @@ def check_common_issues(json_data) if json_data['phoneNPCs'] json_data['phoneNPCs'].each_with_index do |npc, idx| path = "phoneNPCs[#{idx}]" - + # Flag incorrect structure - phone NPCs should be in rooms, not phoneNPCs section issues << "❌ INVALID: '#{path}' - Phone NPCs should be defined in 'rooms/{room_id}/npcs[]' arrays, NOT in a separate 'phoneNPCs' section. See scenarios/npc-sprite-test3/scenario.json.erb for correct format. Phone NPCs should be in the starting room (or room where phone is accessible) with npcType: 'phone'" - + # Track phone NPCs (phone contacts) - but note they're in wrong location has_phone_contacts = true - + # Track phone NPCs with messages if npc['timedMessages'] && !npc['timedMessages'].empty? has_phone_npc_with_messages = true @@ -465,7 +465,7 @@ def check_common_issues(json_data) issues << "💡 SUGGESTION: Consider adding timedMessages to phone contacts for more engaging interactions - see scenarios/npc-sprite-test3/scenario.json.erb for example. Phone NPCs without timed messages: #{npc_list}" end - # Suggest variety in lock types + # Suggest variety in lock types if lock_types_used.size < 2 issues << "💡 SUGGESTION: Consider adding variety in lock types - scenarios typically use 2+ different lock mechanisms (key, pin, rfid, password). Currently using: #{lock_types_used.to_a.join(', ') || 'none'}. See scenarios/ceo_exfil/scenario.json.erb for examples" end @@ -529,7 +529,6 @@ def check_recommended_fields(json_data) room['objects'].each_with_index do |obj, idx| path = "rooms/#{room_id}/objects[#{idx}]" warnings << "Missing recommended field: '#{path}/observations' - helps players understand what items are" unless obj.key?('observations') - end end @@ -538,28 +537,28 @@ def check_recommended_fields(json_data) if room['npcs'] room['npcs'].each_with_index do |npc, idx| path = "rooms/#{room_id}/npcs[#{idx}]" - + # Phone NPCs should have avatar if npc['npcType'] == 'phone' && !npc['avatar'] warnings << "Missing recommended field: '#{path}/avatar' - phone NPCs should have avatar images" end - + # Person NPCs should have position if npc['npcType'] == 'person' && !npc['position'] warnings << "Missing recommended field: '#{path}/position' - person NPCs need x,y coordinates" end - + # NPCs with storyPath should have currentKnot if npc['storyPath'] && !npc['currentKnot'] warnings << "Missing recommended field: '#{path}/currentKnot' - specifies starting dialogue knot" end # Check for NPCs without behavior (no storyPath, no timedMessages, no timedConversation, no eventMappings) - has_behavior = npc['storyPath'] || + has_behavior = npc['storyPath'] || (npc['timedMessages'] && !npc['timedMessages'].empty?) || npc['timedConversation'] || (npc['eventMappings'] && !npc['eventMappings'].empty?) - + unless has_behavior warnings << "Missing recommended: '#{path}' has no behavior - NPCs should have storyPath, timedMessages, timedConversation, or eventMappings" end @@ -583,7 +582,7 @@ def check_recommended_fields(json_data) json_data['objectives'].each_with_index do |objective, idx| path = "objectives[#{idx}]" warnings << "Missing recommended field: '#{path}/description' - helps players understand the objective" unless objective.key?('description') - + if objective['tasks'] objective['tasks'].each_with_index do |task, task_idx| task_path = "#{path}/tasks[#{task_idx}]" @@ -738,4 +737,3 @@ def main end main if __FILE__ == $PROGRAM_NAME -