Skip to content

Drop pull-aware fatigue when puller has died mid-tick#156

Open
misterwise wants to merge 1 commit intoscreeps:masterfrom
misterwise:fix/pull-fatigue-stranded-on-puller-ttl-death
Open

Drop pull-aware fatigue when puller has died mid-tick#156
misterwise wants to merge 1 commit intoscreeps:masterfrom
misterwise:fix/pull-fatigue-stranded-on-puller-ttl-death

Conversation

@misterwise
Copy link
Copy Markdown

When a puller dies of TTL on the same tick a pull resolves, the move's body-weight fatigue could be stranded on the pulled creep instead of dying with the puller. Reproduction: a 2-MOVE puller pulls a 2-WORK harvester; on the puller's last tick, the puller is iterated first, movement.execute then the lifetime check calls _die and delete roomObjects[puller._id]. The pulled creep's later movement.execute enters _add-fatigue and the chain walk

while(!!object._pulled && !!roomObjects[object._pulled]) {
    object = roomObjects[object._pulled];
}

silently terminates because the puller is gone, then applies the move's fatigue to the pulled creep itself. With no MOVE parts to clear it the harvester is stuck forever (fatigue=4). Vanilla happens to produce the intended outcome when the pulled creep is iterated first, so the visible behavior was order-dependent.

Distinguish "no _pulled" (genuine chain head) from "_pulled but target missing" (dead puller). In the dead-link case return without landing fatigue anywhere — the move's pull-aware fatigue is buried with the puller it should have routed to. The "rest first" branch is preserved verbatim, so the negative-dFatigue / own-MOVE-clear path is unchanged.

When a puller dies of TTL on the same tick a pull resolves, the move's
body-weight fatigue could be stranded on the pulled creep instead of
dying with the puller. Reproduction (screeps-ok catalog MOVE-PULL-012):
a 2-MOVE puller pulls a 2-WORK harvester; on the puller's last tick,
the puller is iterated first, `movement.execute` then the lifetime
check calls `_die` and `delete roomObjects[puller._id]`. The pulled
creep's later `movement.execute` enters `_add-fatigue` and the chain
walk

    while(!!object._pulled && !!roomObjects[object._pulled]) {
        object = roomObjects[object._pulled];
    }

silently terminates because the puller is gone, then applies the move's
fatigue to the pulled creep itself. With no MOVE parts to clear it the
harvester is stuck forever (fatigue=4). Vanilla happens to produce the
intended outcome when the pulled creep is iterated first, so the
visible behavior was order-dependent.

Distinguish "no `_pulled`" (genuine chain head) from "`_pulled` but
target missing" (dead puller). In the dead-link case return without
landing fatigue anywhere — the move's pull-aware fatigue is buried with
the puller it should have routed to. The "rest first" branch is
preserved verbatim, so the negative-dFatigue / own-MOVE-clear path is
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant