diff --git a/app/controllers/break_escape/games_controller.rb b/app/controllers/break_escape/games_controller.rb index 4a53c04..610661a 100644 --- a/app/controllers/break_escape/games_controller.rb +++ b/app/controllers/break_escape/games_controller.rb @@ -22,6 +22,12 @@ module BreakEscape # Remove 'requires' fields recursively for security filter_requires_recursive(filtered) + # Include objectives state for page reload recovery + # This allows the client to restore completed/progress state + if @game.player_state['objectivesState'].present? + filtered['objectivesState'] = @game.player_state['objectivesState'] + end + render json: filtered rescue => e Rails.logger.error "[BreakEscape] scenario error: #{e.message}\n#{e.backtrace.first(5).join("\n")}" diff --git a/planning_notes/objectives_system/IMPLEMENTATION_PLAN.md b/planning_notes/objectives_system/IMPLEMENTATION_PLAN.md index fad3487..f8f4f80 100644 --- a/planning_notes/objectives_system/IMPLEMENTATION_PLAN.md +++ b/planning_notes/objectives_system/IMPLEMENTATION_PLAN.md @@ -1282,81 +1282,39 @@ if (gameScenario.objectives && window.objectivesManager) { > **CRITICAL**: Objectives data initialization MUST happen in `game.js create()` function, NOT in `main.js`. The scenario JSON is not available until `create()` runs. The manager is created in `main.js`, but `initialize()` with data happens in `game.js`. -### 6. Door Unlock Event Fix +### 6. Door Unlock Events - Already Implemented ✅ -**File:** Update `public/break_escape/js/systems/doors.js` +**File:** `public/break_escape/js/systems/unlock-system.js` (line 560) -**CRITICAL**: The current codebase does NOT emit `door_unlocked` events. Add event emission to the `unlockDoor()` function: +Door unlock events are already emitted from the central `unlock-system.js`: ```javascript -function unlockDoor(doorSprite, roomData) { - const props = doorSprite.doorProperties; - console.log(`Unlocking door: ${props.roomId} -> ${props.connectedRoom}`); - - // Mark door as unlocked - props.locked = false; - - // If roomData was provided from server unlock response, cache it - if (roomData && window.roomDataCache) { - console.log(`📦 Caching room data for ${props.connectedRoom} from unlock response`); - window.roomDataCache.set(props.connectedRoom, roomData); - } - - // Emit door unlocked event for objectives system - if (window.eventDispatcher) { - window.eventDispatcher.emit('door_unlocked', { - roomId: props.roomId, - connectedRoom: props.connectedRoom, - direction: props.direction - }); - console.log(`📋 Emitted door_unlocked event: ${props.roomId} -> ${props.connectedRoom}`); - } - - // TODO: Implement unlock animation/effect - - // Open the door - openDoor(doorSprite); -} +// In unlockTarget() function (line 560) +window.eventDispatcher.emit('door_unlocked', { + roomId: doorProps.roomId, + connectedRoom: doorProps.connectedRoom, + direction: doorProps.direction, + lockType: doorProps.lockType +}); ``` > **NOTE**: Use `data.connectedRoom` when listening to this event - that's the room being unlocked. -### 7. Key Item Event Fix +### 7. Key Item Events - Now Implemented ✅ -**File:** Update `public/break_escape/js/systems/inventory.js` +**File:** `public/break_escape/js/systems/inventory.js` -In the `addKeyToInventory()` function, add event emission after adding the key: +Key pickup events are now emitted in `addKeyToInventory()`: ```javascript -function addKeyToInventory(sprite) { - // ... existing code to add key to keyRing ... - - // Add the key to the key ring - window.inventory.keyRing.keys.push(sprite); - - // Emit item_picked_up event for keys too (for objectives tracking) - // NOTE: Keys currently don't emit events - this is a required fix - if (window.eventDispatcher) { - window.eventDispatcher.emit(`item_picked_up:${sprite.scenarioData.type}`, { - itemType: sprite.scenarioData.type, - itemName: sprite.scenarioData.name, - roomId: window.currentPlayerRoom, - isKey: true - }); - - // Also emit specific key_id event if available - const keyId = sprite.scenarioData?.key_id || sprite.key_id; - if (keyId) { - window.eventDispatcher.emit(`item_picked_up:key:${keyId}`, { - itemType: 'key', - keyId: keyId, - itemName: sprite.scenarioData.name, - roomId: window.currentPlayerRoom - }); - } - } - - // ... rest of existing code ... +// Emit item_picked_up event for keys (matching regular item pickup event format) +if (window.eventDispatcher) { + window.eventDispatcher.emit(`item_picked_up:key`, { + itemType: 'key', + itemName: sprite.scenarioData?.name || 'Unknown Key', + keyId: keyId, + roomId: window.currentPlayerRoom + }); } ``` @@ -1364,18 +1322,18 @@ function addKeyToInventory(sprite) { ## Implementation TODO List -### Phase 0: Prerequisites (Do First) -- [ ] 0.1 Add key pickup events to `inventory.js` `addKeyToInventory()` function -- [ ] 0.2 Verify `item_unlocked` event name in unlock-system.js (currently line 587) -- [ ] 0.3 Verify `door_unlocked` event provides `connectedRoom` property +### Phase 0: Prerequisites ✅ COMPLETED +- [x] 0.1 Verify door_unlocked events exist - Already emitted from `unlock-system.js:560` +- [x] 0.2 Add key pickup events to `inventory.js` - Implemented +- [x] 0.3 Verify `item_unlocked` event name - Confirmed (line 587, 621) +- [x] 0.4 Add `objectivesState` to server bootstrap - Implemented in `games_controller.rb` ### Phase 1: Core Infrastructure (Foundation) - [ ] 1.1 Create database migration for objectives tracking - [ ] 1.2 Add objective methods to `Game` model - [ ] 1.3 Create API endpoints with RESTful routes (`/objectives/tasks/:task_id`) -- [ ] 1.4 Update scenario action to include `objectivesState` -- [ ] 1.5 Create `objectives-manager.js` client module -- [ ] 1.6 Add objectives CSS file +- [ ] 1.4 Create `objectives-manager.js` client module +- [ ] 1.5 Add objectives CSS file ### Phase 2: Event Integration - [ ] 2.1 Subscribe to `item_picked_up:*` wildcard events in ObjectivesManager diff --git a/planning_notes/objectives_system/QUICK_REFERENCE.md b/planning_notes/objectives_system/QUICK_REFERENCE.md index e36a9e6..ab7d1bd 100644 --- a/planning_notes/objectives_system/QUICK_REFERENCE.md +++ b/planning_notes/objectives_system/QUICK_REFERENCE.md @@ -118,9 +118,9 @@ window.debugObjectives.reset(); ## Key Gotchas -1. **CRITICAL**: `door_unlocked` events NOT emitted in current codebase - must add to `doors.js` -2. **Event name**: `item_unlocked` NOT `object_unlocked` -3. **Door event**: `door_unlocked` should provide both `roomId` and `connectedRoom` (use `connectedRoom`) -4. **Keys don't emit events**: Need to add event to `addKeyToInventory()` +1. **Event name**: `item_unlocked` NOT `object_unlocked` +2. **Door unlock events**: Emitted from `unlock-system.js:560` (NOT doors.js) +3. **Door event data**: Provides both `roomId` AND `connectedRoom` (use `connectedRoom` for unlock tasks) +4. **Key pickup events**: Now emitted as `item_picked_up:key` from `addKeyToInventory()` 5. **State restoration**: Server passes `objectivesState` in scenario bootstrap 6. **Reconciliation**: Call `reconcileWithGameState()` after init for late-loaded scenarios diff --git a/planning_notes/objectives_system/TODO_CHECKLIST.md b/planning_notes/objectives_system/TODO_CHECKLIST.md index cd82a1d..89faa89 100644 --- a/planning_notes/objectives_system/TODO_CHECKLIST.md +++ b/planning_notes/objectives_system/TODO_CHECKLIST.md @@ -4,11 +4,11 @@ Track implementation progress here. Check off items as completed. --- -## Phase 0: Prerequisites (Do First) ⬜ -- [ ] 0.1 **CRITICAL**: Add `door_unlocked` event emission to `doors.js` `unlockDoor()` function -- [ ] 0.2 Add key pickup events to `inventory.js` `addKeyToInventory()` function -- [ ] 0.3 Verify `item_unlocked` event name in `unlock-system.js` (line ~587) - ✅ VERIFIED -- [ ] 0.4 Verify `room_entered` events are emitted in `rooms.js` +## Phase 0: Prerequisites (Do First) ✅ +- [x] 0.1 **CRITICAL**: Verify `door_unlocked` event emission exists in `unlock-system.js` - ✅ VERIFIED (line 560) +- [x] 0.2 Add key pickup events to `inventory.js` `addKeyToInventory()` function - ✅ IMPLEMENTED +- [x] 0.3 Verify `item_unlocked` event name in `unlock-system.js` (line ~587) - ✅ VERIFIED +- [x] 0.4 Add `objectivesState` to server bootstrap in `games_controller.rb` - ✅ IMPLEMENTED ## Phase 1: Core Infrastructure ⬜ - [ ] 1.1 Create database migration `db/migrate/XXXXXX_add_objectives_to_games.rb` @@ -121,7 +121,7 @@ _Add implementation notes, blockers, or decisions here:_ | Phase | Status | Completed | |-------|--------|-----------| -| Phase 0: Prerequisites | ⬜ | 0/4 | +| Phase 0: Prerequisites | ✅ | 4/4 | | Phase 1: Core Infrastructure | ⬜ | 0/7 | | Phase 2: Event Integration | ⬜ | 0/6 | | Phase 3: UI Implementation | ⬜ | 0/7 | @@ -131,4 +131,4 @@ _Add implementation notes, blockers, or decisions here:_ | Phase 7: Reconciliation | ⬜ | 0/6 | | Phase 8: Testing | ⬜ | 0/13 | | Phase 9: Documentation | ⬜ | 0/4 | -| **Total** | **⬜** | **0/64** | +| **Total** | **⬜** | **4/64** | diff --git a/planning_notes/objectives_system/review2/CORRECTED_CODE_SNIPPETS.md b/planning_notes/objectives_system/review2/CORRECTED_CODE_SNIPPETS.md index 913d9c4..7b5360e 100644 --- a/planning_notes/objectives_system/review2/CORRECTED_CODE_SNIPPETS.md +++ b/planning_notes/objectives_system/review2/CORRECTED_CODE_SNIPPETS.md @@ -1,114 +1,74 @@ # Corrected Code Snippets - Review 2 -All code ready to copy-paste into implementation. +All prerequisites have been implemented. This document now contains reference code for the objectives system implementation. --- -## Critical Fix: Door Unlock Event Emission +## ✅ Door Unlock Events - Already Working -### File: `public/break_escape/js/systems/doors.js` +### File: `public/break_escape/js/systems/unlock-system.js` -**Location**: `unlockDoor()` function (around line 585) +**Location**: `unlockTarget()` function (line 560) -**Current Code** (MISSING event emission): +Door unlock events are ALREADY emitted from the central unlock-system.js: ```javascript -function unlockDoor(doorSprite, roomData) { - const props = doorSprite.doorProperties; - console.log(`Unlocking door: ${props.roomId} -> ${props.connectedRoom}`); - - // Mark door as unlocked - props.locked = false; - - // If roomData was provided from server unlock response, cache it - if (roomData && window.roomDataCache) { - console.log(`📦 Caching room data for ${props.connectedRoom} from unlock response`); - window.roomDataCache.set(props.connectedRoom, roomData); - } - - // TODO: Implement unlock animation/effect - - // Open the door - openDoor(doorSprite); -} +window.eventDispatcher.emit('door_unlocked', { + roomId: doorProps.roomId, + connectedRoom: doorProps.connectedRoom, + direction: doorProps.direction, + lockType: doorProps.lockType +}); ``` -**CORRECTED Code** (with event emission): -```javascript -function unlockDoor(doorSprite, roomData) { - const props = doorSprite.doorProperties; - console.log(`Unlocking door: ${props.roomId} -> ${props.connectedRoom}`); - - // Mark door as unlocked - props.locked = false; - - // If roomData was provided from server unlock response, cache it - if (roomData && window.roomDataCache) { - console.log(`📦 Caching room data for ${props.connectedRoom} from unlock response`); - window.roomDataCache.set(props.connectedRoom, roomData); - } - - // Emit door unlocked event for objectives system - if (window.eventDispatcher) { - window.eventDispatcher.emit('door_unlocked', { - roomId: props.roomId, - connectedRoom: props.connectedRoom, - direction: props.direction - }); - console.log(`📋 Emitted door_unlocked event: ${props.roomId} -> ${props.connectedRoom}`); - } - - // TODO: Implement unlock animation/effect - - // Open the door - openDoor(doorSprite); -} -``` - -**Why This Matters**: Without this event, `unlock_room` type objectives will never auto-complete. +**No changes needed** - the door_unlocked event was always emitted, just from unlock-system.js not doors.js. --- -## Already Documented Fix: Key Pickup Event Emission +## ✅ Key Pickup Events - Now Implemented ### File: `public/break_escape/js/systems/inventory.js` -**Location**: `addKeyToInventory()` function (around line 433) - -**Insert AFTER** the line: `window.inventory.keyRing.keys.push(sprite);` +**Location**: `addKeyToInventory()` function +**Implemented code** (now in codebase): ```javascript -// Emit item_picked_up event for keys too (for objectives tracking) -// NOTE: Keys currently don't emit events - this is a required fix +// Emit item_picked_up event for keys (matching regular item pickup event format) if (window.eventDispatcher) { - window.eventDispatcher.emit(`item_picked_up:${sprite.scenarioData.type}`, { - itemType: sprite.scenarioData.type, - itemName: sprite.scenarioData.name, - roomId: window.currentPlayerRoom, - isKey: true + window.eventDispatcher.emit(`item_picked_up:key`, { + itemType: 'key', + itemName: sprite.scenarioData?.name || 'Unknown Key', + keyId: keyId, + roomId: window.currentPlayerRoom }); - - // Also emit specific key_id event if available - const keyId = sprite.scenarioData?.key_id || sprite.key_id; - if (keyId) { - window.eventDispatcher.emit(`item_picked_up:key:${keyId}`, { - itemType: 'key', - keyId: keyId, - itemName: sprite.scenarioData.name, - roomId: window.currentPlayerRoom - }); - } } ``` --- +## ✅ Server Bootstrap - Now Implemented + +### File: `app/controllers/break_escape/games_controller.rb` + +**Location**: `scenario` action + +**Implemented code** (now in codebase): +```ruby +# Include objectives state for page reload recovery +# This allows the client to restore completed/progress state +if @game.player_state['objectivesState'].present? + filtered['objectivesState'] = @game.player_state['objectivesState'] +end +``` + +--- + ## ObjectivesManager Event Listener Setup ### File: `public/break_escape/js/systems/objectives-manager.js` **Function**: `setupEventListeners()` -**CORRECTED version** with proper door event handling: +**Reference implementation** for the objectives system: ```javascript /** diff --git a/planning_notes/objectives_system/review2/FINAL_REVIEW.md b/planning_notes/objectives_system/review2/FINAL_REVIEW.md index c771253..affad3e 100644 --- a/planning_notes/objectives_system/review2/FINAL_REVIEW.md +++ b/planning_notes/objectives_system/review2/FINAL_REVIEW.md @@ -3,23 +3,21 @@ **Date**: November 25, 2025 **Reviewer**: AI Assistant **Plan Version**: 1.1 -**Status**: ✅ APPROVED with minor corrections +**Status**: ✅ APPROVED - All prerequisites implemented --- ## Executive Summary -The implementation plan has been thoroughly reviewed against the current codebase. The plan is **well-structured and technically sound**, with all critical event names and initialization flows correctly documented. However, **one critical issue was discovered**: doors do NOT emit `door_unlocked` events in the current codebase. +The implementation plan has been thoroughly reviewed against the current codebase. The plan is **well-structured and technically sound**. All critical event names and initialization flows are correctly documented. -### Critical Finding +### Phase 0 Prerequisites - COMPLETED ✅ -**❌ MISSING: Door unlock events are NOT emitted** +All prerequisite issues have been resolved: -The current codebase in `doors.js` does NOT emit any events when doors are unlocked. This will prevent `unlock_room` objectives from being tracked automatically. - -### Recommendation - -**REQUIRED FIX**: Add event emission to `doors.js` in the `unlockDoor()` function. +1. **✅ Door unlock events** - Already emitted from `unlock-system.js:560` (initial review incorrectly searched only doors.js) +2. **✅ Key pickup events** - Now implemented in `inventory.js:addKeyToInventory()` +3. **✅ Server bootstrap** - Added `objectivesState` to `games_controller.rb` scenario response --- @@ -30,72 +28,27 @@ The current codebase in `doors.js` does NOT emit any events when doors are unloc | Event Name | Location | Status | |------------|----------|--------| | `item_picked_up:*` | `inventory.js:369-374` | ✅ Correct (wildcard works) | +| `item_picked_up:key` | `inventory.js:addKeyToInventory()` | ✅ NOW IMPLEMENTED | | `item_unlocked` | `unlock-system.js:587, 621` | ✅ Correct (NOT object_unlocked) | | `room_entered` | `rooms.js` (via updatePlayerRoom) | ✅ Correct | -| `door_unlocked` | **MISSING** | ❌ NOT EMITTED | +| `door_unlocked` | `unlock-system.js:560` | ✅ Already implemented | -### ❌ CRITICAL: Door Unlock Events Missing +### ✅ Door Unlock Events - VERIFIED WORKING -**File**: `public/break_escape/js/systems/doors.js` -**Function**: `unlockDoor()` (lines ~585-600) +**File**: `public/break_escape/js/systems/unlock-system.js` +**Function**: `unlockTarget()` (line 560) -**Current Code** (No event emission): +The door_unlocked event IS emitted from the central unlock-system.js: ```javascript -function unlockDoor(doorSprite, roomData) { - const props = doorSprite.doorProperties; - console.log(`Unlocking door: ${props.roomId} -> ${props.connectedRoom}`); - - // Mark door as unlocked - props.locked = false; - - // If roomData was provided from server unlock response, cache it - if (roomData && window.roomDataCache) { - console.log(`📦 Caching room data for ${props.connectedRoom} from unlock response`); - window.roomDataCache.set(props.connectedRoom, roomData); - } - - // TODO: Implement unlock animation/effect - - // Open the door - openDoor(doorSprite); -} +window.eventDispatcher.emit('door_unlocked', { + roomId: doorProps.roomId, + connectedRoom: doorProps.connectedRoom, + direction: doorProps.direction, + lockType: doorProps.lockType +}); ``` -**Required Fix**: -```javascript -function unlockDoor(doorSprite, roomData) { - const props = doorSprite.doorProperties; - console.log(`Unlocking door: ${props.roomId} -> ${props.connectedRoom}`); - - // Mark door as unlocked - props.locked = false; - - // If roomData was provided from server unlock response, cache it - if (roomData && window.roomDataCache) { - console.log(`📦 Caching room data for ${props.connectedRoom} from unlock response`); - window.roomDataCache.set(props.connectedRoom, roomData); - } - - // Emit door unlocked event for objectives system - if (window.eventDispatcher) { - window.eventDispatcher.emit('door_unlocked', { - roomId: props.roomId, - connectedRoom: props.connectedRoom, - direction: props.direction - }); - console.log(`📋 Emitted door_unlocked event: ${props.roomId} -> ${props.connectedRoom}`); - } - - // TODO: Implement unlock animation/effect - - // Open the door - openDoor(doorSprite); -} -``` - -**Impact**: Without this fix, `unlock_room` type objectives will NOT auto-complete when players unlock doors. - -### ✅ CORRECT: Key Items Do NOT Emit Events +### ✅ IMPLEMENTED: Key Pickup Events **File**: `public/break_escape/js/systems/inventory.js` **Function**: `addKeyToInventory()` (lines 410-465) diff --git a/public/break_escape/js/systems/inventory.js b/public/break_escape/js/systems/inventory.js index 7d43d22..8057f92 100644 --- a/public/break_escape/js/systems/inventory.js +++ b/public/break_escape/js/systems/inventory.js @@ -440,6 +440,16 @@ function addKeyToInventory(sprite) { lockType: sprite.scenarioData?.lockType }); + // Emit item_picked_up event for keys (matching regular item pickup event format) + if (window.eventDispatcher) { + window.eventDispatcher.emit(`item_picked_up:key`, { + itemType: 'key', + itemName: sprite.scenarioData?.name || 'Unknown Key', + keyId: keyId, + roomId: window.currentPlayerRoom + }); + } + // Update or create the key ring display updateKeyRingDisplay();