Enhance NPC encounter logging and fix item type references in scenario scripts

This commit is contained in:
Z. Cliffe Schreuders
2025-12-01 17:31:12 +00:00
parent 0cf9e0ba62
commit a4606f596c
9 changed files with 64 additions and 23 deletions

View File

@@ -545,10 +545,23 @@ module BreakEscape
new_npcs = npc_ids - @game.player_state['encounteredNPCs']
return if new_npcs.empty?
# Log detailed information about each new NPC encountered
new_npcs.each do |npc_id|
npc_data = room_data['npcs'].find { |npc| npc['id'] == npc_id }
if npc_data
display_name = npc_data['displayName'] || npc_id
npc_type = npc_data['npcType'] || 'unknown'
Rails.logger.info "[BreakEscape] 🎭 NPC ENCOUNTERED: #{display_name} (#{npc_id}) - Type: #{npc_type} - Room: #{room_id}"
else
Rails.logger.info "[BreakEscape] 🎭 NPC ENCOUNTERED: #{npc_id} - Room: #{room_id}"
end
end
@game.player_state['encounteredNPCs'] = (@game.player_state['encounteredNPCs'] + new_npcs).uniq
@game.save!
Rails.logger.debug "[BreakEscape] Tracked NPC encounters: #{new_npcs.join(', ')}"
total_encountered = @game.player_state['encounteredNPCs'].length
Rails.logger.info "[BreakEscape] ✅ Tracked #{new_npcs.length} new NPC encounter(s) in room #{room_id}. Total NPCs encountered: #{total_encountered}"
rescue => e
Rails.logger.error "[BreakEscape] Error tracking NPC encounters: #{e.message}\n#{e.backtrace.first(5).join("\n")}"
# Continue without tracking to avoid breaking room loading
@@ -744,6 +757,17 @@ module BreakEscape
end
end
end
# Priority 3: Items held by NPCs in this room
room_data['npcs']&.each do |npc|
next unless npc['itemsHeld'].present?
npc['itemsHeld'].each do |held_item|
if held_item['type'] == item_type && (held_item['key_id'] == item_id || held_item['id'] == item_id || held_item['name'] == item_name || held_item['name'] == item_id)
return { item: held_item, location: { type: 'npc', npc_id: npc['id'], room_id: room_id } }
end
end
end
end
nil

View File

@@ -102,8 +102,24 @@ module BreakEscape
# NPC tracking
def encounter_npc!(npc_id)
player_state['encounteredNPCs'] ||= []
player_state['encounteredNPCs'] << npc_id unless player_state['encounteredNPCs'].include?(npc_id)
save!
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']
scenario_data['rooms'].each do |_room_id, room_data|
npc_data = room_data['npcs']&.find { |npc| npc['id'] == npc_id }
if npc_data && npc_data['displayName']
npc_display_name = npc_data['displayName']
break
end
end
end
Rails.logger.info "[BreakEscape] 🎭 NPC ENCOUNTERED (via encounter_npc!): #{npc_display_name} (#{npc_id})"
save!
end
end
# Global variables (synced with client)

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

View File

@@ -306,8 +306,8 @@ class NPCBehavior {
return;
}
const roomWorldX = roomData.worldX || 0;
const roomWorldY = roomData.worldY || 0;
const roomWorldX = roomData.position?.x ?? roomData.worldX ?? 0;
const roomWorldY = roomData.position?.y ?? roomData.worldY ?? 0;
const validWaypoints = [];

View File

@@ -112,8 +112,9 @@ export function calculateNPCWorldPosition(npc, roomData) {
// Support grid coordinates (tile-based positioning)
if (position.x !== undefined && position.y !== undefined) {
const roomWorldX = roomData.worldX || 0;
const roomWorldY = roomData.worldY || 0;
// Room position is stored in roomData.position (from room loading system)
const roomWorldX = roomData.position?.x ?? roomData.worldX ?? 0;
const roomWorldY = roomData.position?.y ?? roomData.worldY ?? 0;
return {
x: roomWorldX + (position.x * TILE_SIZE),
@@ -935,8 +936,8 @@ function handleNPCCollision(npcSprite, otherNPC) {
const roomData = window.rooms?.[roomId];
if (roomData) {
const roomWorldX = roomData.worldX || 0;
const roomWorldY = roomData.worldY || 0;
const roomWorldX = roomData.position?.x ?? roomData.worldX ?? 0;
const roomWorldY = roomData.position?.y ?? roomData.worldY ?? 0;
// Check if avoidance waypoint is on walkable tile
let isWalkable = true;
@@ -1096,8 +1097,8 @@ function handleNPCPlayerCollision(npcSprite, player) {
const roomData = window.rooms?.[roomId];
if (roomData) {
const roomWorldX = roomData.worldX || 0;
const roomWorldY = roomData.worldY || 0;
const roomWorldX = roomData.position?.x ?? roomData.worldX ?? 0;
const roomWorldY = roomData.position?.y ?? roomData.worldY ?? 0;
// Check if avoidance waypoint is on walkable tile
let isWalkable = true;

View File

@@ -90,8 +90,8 @@
**Problem:** NPCs' items used wrong `type` field values - #give_item tags reference `type`, not `id`
**User Feedback:**
- "NPC sarah_martinez doesn't have visitor_badge. An NPC must hold the items they give away"
- "Still says sarah_martinez doesn't hold visitor_badge -- maybe it needs to be specified in the give by the type 'keycard'"
- "NPC sarah_martinez doesn't have id_badge. An NPC must hold the items they give away"
- "Still says sarah_martinez doesn't hold id_badge -- maybe it needs to be specified in the give by the type 'keycard'"
**Root Cause:** Misunderstood how #give_item tags work:
- Items should NOT have `id` fields
@@ -101,7 +101,7 @@
**Fix Applied:**
- Removed ALL `id` fields from NPC items
- Changed item types to match #give_item tag parameters:
- Sarah: `visitor_badge` - changed type from "keycard" to "visitor_badge"
- Sarah: `id_badge` - changed type from "keycard" to "id_badge"
- Kevin: `lockpick` - type already correct
- Kevin: `rfid_cloner` - changed type from "tool" to "rfid_cloner"
- Updated SCENARIO_JSON_FORMAT_GUIDE.md with correct pattern
@@ -114,7 +114,7 @@
// ✅ CORRECT - Item type matches Ink tag parameter
"itemsHeld": [
{
"type": "visitor_badge", // Matches #give_item:visitor_badge
"type": "id_badge", // Matches #give_item:id_badge
"name": "Visitor Badge",
"takeable": true
}
@@ -123,14 +123,14 @@
```ink
// In Ink script
#give_item:visitor_badge // References item type!
#give_item:id_badge // References item type!
```
```json
// ❌ INCORRECT - Don't add id field
"itemsHeld": [
{
"id": "visitor_badge", // DON'T DO THIS
"id": "id_badge", // DON'T DO THIS
"type": "keycard", // Wrong - doesn't match tag
"name": "Visitor Badge"
}

View File

@@ -48,7 +48,7 @@ VAR asked_about_kevin = false
=== receive_badge ===
~ has_badge = true
#give_item:visitor_badge
#give_item:id_badge
#complete_task:meet_reception
Sarah: Here you go. This gets you into public areas.

File diff suppressed because one or more lines are too long

View File

@@ -57,7 +57,7 @@ password_hints = "Common passwords: Marketing123, Campaign2024, Viral_Dynamics_A
"id": "briefing_cutscene",
"displayName": "Agent 0x99",
"npcType": "person",
"position": { "x": 5, "y": 5 },
"position": { "x": 500, "y": 500 },
"spriteSheet": "hacker",
"spriteConfig": {
"idleFrameStart": 20,
@@ -75,7 +75,7 @@ password_hints = "Common passwords: Marketing123, Campaign2024, Viral_Dynamics_A
"id": "sarah_martinez",
"displayName": "Sarah Martinez",
"npcType": "person",
"position": { "x": 3, "y": 4 },
"position": { "x": 4, "y": 1.5 },
"spriteSheet": "hacker-red",
"spriteTalk": "assets/characters/hacker-red-talk.png",
"spriteConfig": {
@@ -86,7 +86,7 @@ password_hints = "Common passwords: Marketing123, Campaign2024, Viral_Dynamics_A
"currentKnot": "start",
"itemsHeld": [
{
"type": "visitor_badge",
"type": "id_badge",
"name": "Visitor Badge",
"takeable": true,
"observations": "Temporary visitor badge for office access"
@@ -121,7 +121,7 @@ password_hints = "Common passwords: Marketing123, Campaign2024, Viral_Dynamics_A
"id": "kevin_park",
"displayName": "Kevin Park",
"npcType": "person",
"position": { "x": 10, "y": 7 },
"position": { "x": 8, "y": 7 },
"spriteSheet": "hacker",
"spriteConfig": {
"idleFrameStart": 20,