diff --git a/assets/images/wolfenstein/walls.png b/assets/images/wolfenstein/walls.png index c49ccb9..b787292 100644 Binary files a/assets/images/wolfenstein/walls.png and b/assets/images/wolfenstein/walls.png differ diff --git a/assets/images/wolfenstein/walls.psd b/assets/images/wolfenstein/walls.psd index c8ddce0..eeb0606 100644 Binary files a/assets/images/wolfenstein/walls.psd and b/assets/images/wolfenstein/walls.psd differ diff --git a/tools/verifyWolfenstein.js b/tools/verifyWolfenstein.js index b035beb..8c7ab41 100644 --- a/tools/verifyWolfenstein.js +++ b/tools/verifyWolfenstein.js @@ -331,6 +331,44 @@ section('4. Enemy AI'); L.fireWeapon(state, rules); check('gunshot does not alert a guard through an open door either', state.enemies[0].state === 'idle', state.enemies[0].state); } + + // Actually landing a hit alerts an enemy unconditionally, independent + // of BOTH other alert paths above: visual detection (LOS+FOV+range) and + // "heard gunfire in the room" (which the door-forced-open case just + // proved does NOT reach across a door, open or not). Guard sits on the + // same row as the door gap (y=2.5) so a bullet fired straight down that + // row travels through the open door and can actually connect, but + // facing east (away from the westward player) keeps it outside its own + // vision cone, and it's in the room-alert-blind "other room" the whole + // time — so alerting here can only be the direct-hit path itself. + { + const level = { + width: W, height: H, cellSize: 64, walls, doors, items: [], + playerStart: { x: 2.5, y: 2.5, angle: 0 }, + enemies: [{ type: 'guard', x: 7.5, y: 2.5, facing: 0 }], + exit: { x: 9.5, y: 1.5, radius: 0.6 }, + }; + const state = L.createState(level, rules); + // Forcing the door open (unlike the fireWeapon-only sub-tests above, + // this one calls L.tick(), which runs stepDoors) also needs `timer` + // set — left at its createState default of 0, stepDoors' auto-close + // countdown underflows on the very first tick and starts sliding the + // door shut again right under the in-flight bullet. + state.doors[0].slide = 1; state.doors[0].target = 1; state.doors[0].timer = rules.constants.doorAutoCloseMs; + state.map.walls[2][5] = 0; + L.switchWeapon(state, 'pistol'); + L.setFireHeld(state, true); + L.tick(state, rules); // fires — projectile now in flight through the open door + let alerted = false, healthDropped = false; + for (let i = 0; i < 60 && !alerted; i++) { + L.setFireHeld(state, false); // one shot only — a second room-alert-free shot isn't the point here + L.tick(state, rules); + if (state.enemies[0].health < rules.enemyById.guard.health) healthDropped = true; + alerted = state.enemies[0].state !== 'idle'; + } + check('the shot actually connected (sanity check for the assertion below)', healthDropped); + check('being shot alerts a guard even with room-alert and vision-cone both blind to it', alerted, state.enemies[0].state); + } } // A dead guard stops acting and can't be hit/killed twice. The pistol is