00-Evan / shattered-pixel-dungeon

Shattered Pixel Dungeon is an open-source traditional roguelike dungeon crawler with randomized levels and enemies, and hundreds of items to collect and use. It's based on the source code of Pixel Dungeon, by Watabou.
https://shatteredpixel.com/shatteredpd/
GNU General Public License v3.0
4.72k stars 1.1k forks source link

Attacked immediately after changing floor #1597

Closed MiroslavR closed 8 months ago

MiroslavR commented 9 months ago

v2.2.1 on Android

It seems that if the turn/time indicator doesn't align with the start of a turn, changing the floor gives enemies a turn - at least when they are adjacent to you, you get attacked immediately. I was under the impression that changing the floor doesn't advance time. Weirdly, AoEs like freezing don't get applied.

00-Evan commented 9 months ago

I'm not able to reproduce this, can you let me know exactly what you were using to get on a non-whole turn before ascending/descending? Also, were the enemies on partial turns as well due to effects like chilling?

MiroslavR commented 9 months ago

I attacked with a spear to get to a half turn. And no, the enemies weren't affected by anything.

I've just tried again and I can reproduce consistently. I can also reproduce with an armor of flow and a potion of haste. When testing the armor of flow, however, I've noticed that it doesn't happen when there is very little of the turn remaining: not-repro

MiroslavR commented 9 months ago

I attacked with a spear to get to a half turn.

Hmm, but I can only reproduce this when descending, not when ascending. I struggle to see what the difference between the two could be, so there may be something else at play.

MiroslavR commented 9 months ago

Nevermind, I've since reproduced it when ascending as well as when there was very little of the turn remaining. The only thing I'm certain of is being on a non-whole turn.

Whatever the missing piece is, it does get persisted. Please try this 2.3.0 savegame and descend: game1.zip

00-Evan commented 9 months ago

Thanks for the save file, it was helpful in figuring out what was happening.

Essentially, the game always tries to spend a hero's partial turn before transition, to prevent exploits and to generally simplify partial turn behavior. However, currently the hero isn't yielding to other game actors after that turn is spend, meaning we could have a situation like:

Because of this, hero enters the next depth with time = 1 (depth transitions scale every actor's time back by the current lowest time value, rounded down), which causes the other enemies to act. In this specific case, the entity with time=1.67 is actually the level's respawner, as dark floors spawn an enemy every 33.33 turns instead of every 50.

This could also mean that in specific situations, the player could exploit this, such as:

solution here is for hero to move to depths transition, have their time rounded up, then yield to other potential actors before finally transitioning to the other depth.

EDIT: actually it looks like I already largely closed this loophole back in v2.1.1 (other characters also get their time rounded up if it would otherwise be below the hero's), but the fix only applies to characters, not other actors like the respawner. Fixing should be as simple as extending that.

00-Evan commented 8 months ago

fixed in commit a94186e011a4c257808b811a5a2000b970f799e4