fix(drop): prevent items and gold from dropping into player-unreachable locations #492
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem Description
When monsters die, objects/chests open, or items/gold are dropped, items can currently land in coordinates that the player character cannot physically reach to pick up:
moveWithCollisioninsrc/game/combat.ts) traverses 4-connected walkable sub-tiles whereterrain.overlap(x, y) === 0.findIsometricDropPositionandfindSafeDropPositioninsrc/game/ground-items.tsdo not verify whether a drop candidate lies in the same 4-connected walkable component as the player (reachableFrom) or drop anchor. Monsters dying across rivers/moats/walls/chasms or near thin barriers can drop items onto isolated islands or sealed rooms.rayTraceSceneand Forces Fallback to the Blocked Origin:rayTraceScene(grid, originX, originY, candX, candY, COLLIDE_MASK_SPAWN_LOS)immediately collides atsteps === 1for every candidate outside the obstacle.firstWalkableCandidatestaysnullandfindIsometricDropPositionfalls back to returning{ x: originX, y: originY }— placing the item directly inside the unreachable wall/void!collisionRayTraceallows diagonal steps(x, y) -> (x + sx, y + sy)without checking the two shared orthogonal neighbors(x + sx, y)and(x, y + sy). Items can be placed across diagonal wall corners where the player cannot walk through.CountingTerraininfindSafeDropPosition:COLLIDE_MASK_SPAWN(0x3E01) omitsCOLLIDE_BLANK(0x0020),COLLIDE_NOPLAYER(0x0008),COLLIDE_WATER(0x00c0), andCOLLIDE_MASK_INVALID(0x0027).findSafeDropPositionunwrapsgrid.gridbefore checkinggrid.overlap(wx, wy), bypassing the player footprint clearance check, and only checks0x0027oncollisionMasks.Solution (Diablo II v1.13c Parity)
COLLIDE_MASK_SPAWN | COLLIDE_MASK_PLAYER_PATH | COLLIDE_BLANK | COLLIDE_WATER | COLLIDE_MASK_INVALID,blocked === 1, andoverlap(x, y) === 0) in bothfindIsometricDropPositionandfindSafeDropPosition.reachableFrom(player coordinates) infindIsometricDropPositionandfindSafeDropPosition, snapping blocked/unreachable origins to the nearest player-reachable walkable floor coordinate and expanding search rings when necessary so items never fall back to unreachable tiles.reachableFrom: { x: this.player.x, y: this.player.y }acrossEngine.triggerMonsterDrop,Engine.dropItem,Engine.dropGold, andNetSceneitem drop handlers.Fixed in commit
9c2e3caonmain.Summary of Changes
COLLIDE_MASK_DROP_BLOCKandisTerrainPointBlockedinsrc/game/ground-items.tsto reject all player-unwalkable sub-tiles (COLLIDE_WALL,COLLIDE_BLANK,COLLIDE_WATER,COLLIDE_NOPLAYER,COLLIDE_MASK_INVALID,COLLIDE_NO_PATH,COLLIDE_DOOR,COLLIDE_OBJECT) as well as player footprint overlaps (CountingTerrain.overlap(x, y) !== 0).isSubTileRayBlocked/isDropPositionBlockedandfindSafeDropPosition.reachableFromsupport infindIsometricDropPositionandfindSafeDropPosition, snapping blocked or isolated drop origins (inside thick walls, over water/void, or across rivers/walls from the player) to the nearest player-reachable walkable floor coordinate.reachableFrominGameEngine.triggerMonsterDrop,GameEngine.dropItem,GameEngine.dropGold, andNetScene.tests/ground-items-reachability.test.ts(all passing,npm run typecheck0 errors, and 136-leveltests/full-spectrum-drop-parity.test.ts100% passing).