Skip to content

Fix: NPCs spawned under a target couldn't attack#946

Open
HarleyGilpin wants to merge 4 commits intoGregHib:mainfrom
HarleyGilpin:main
Open

Fix: NPCs spawned under a target couldn't attack#946
HarleyGilpin wants to merge 4 commits intoGregHib:mainfrom
HarleyGilpin:main

Conversation

@HarleyGilpin
Copy link
Copy Markdown
Contributor

@HarleyGilpin HarleyGilpin commented Apr 10, 2026

Bug Fix: NPCs spawned under a target couldn't attack

Summary

NPCs spawned under a target couldn't attack because Interact mode (used for the initial attack interaction) had no logic to step out from under the target, unlike CombatMovement which explicitly handles this case.


Root Cause

When an NPC with allowed_under = false is on the same tile as its target, the following chain of failures occurs:

Step What Happens
Movement.arrived() Returns false (overlap check fails)
calculate() Queues a no-op step (naive destination = current tile when overlapping)
super.tick() Consumes the no-op step with no movement
cantReach() Called, interaction is cleared, NPC gives up

Fix

Interact.kt (tick()): Before the normal calculate() / processInteraction() flow, check if the NPC is under its target and not allowed there. If so, mirror CombatMovement.stepOut() behavior:

  1. Clear any stale steps
  2. Pick a random cardinal direction
  3. Queue a step in that direction if passable
  4. Call super.tick() to execute the step

This lets the NPC move out from under the target on the first tick, allowing combat to initialize normally on the second tick.


Test

CombatMovementTest.kt: Creates a guard NPC spawned at the same tile as a player, calls interactPlayer(player, "Attack"), ticks twice, and verifies the NPC has moved away and transitioned to CombatMovement.


Demo

Screencast_20260410_061246.webm

Copy link
Copy Markdown
Owner

@GregHib GregHib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! Admittedly this does need a proper architecture solution but this will be a nice short term fix

@HarleyGilpin
Copy link
Copy Markdown
Contributor Author

Thanks for the contribution! Admittedly this does need a proper architecture solution but this will be a nice short term fix

Thanks for your feed back, I moved step-out logic from CombatMovement into Movement. Since "step out from under a character target" is a movement concern rather than a combat concern, the logic was moved to Movement (the base class) where it applies to all modes automatically. The shape == -2 guard scopes it to character targets only, and the shouldQueueStepOut() hook lets CombatMovement skip queuing a random step when both characters are already in active combat movement, clearing the stale steps and letting normal recalculation take over instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants