mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
Enhance NPC encounter logging and fix item type references in scenario scripts
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
BIN
public/break_escape/assets/objects/id_badge.png
Normal file
BIN
public/break_escape/assets/objects/id_badge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 B |
@@ -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 = [];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user