IchoTolot: ah ok the bug front :P
Simon: the bug front is exciting
Simon: I love bugs, everybody should report bugs against every softwareLong and technical post, programming.Github bug #132: Walker bypasses trap, faller is killed, found by Ramon. Equivalent to a bug in C++ Lix, Fast flinging through traps, found by geoo. I want to find the best solution to this.
What happens? When you enter and leave triggered traps, the game scans for traps only at your final position. The game knows that you went through a trap somewhere, but it doesn't remember where you encountered a hungry trap. Whenever you somehow went through a hungry or already-eating trap, the game sets a flag. If this flag is set, then after your full motion, the game scans for hungry traps at your final position.
Compare this with our fire solution: When you enter and leave fire within the same frame, the game still remembers that you went through fire, and kills you. You got to perform your full motion and affect the environment, but after that, you die.
Desired instead: When you move through a hungry trap, then leave it during the same frame, this trap kills you. I don't know whether you should move away or not before the trap kills you.
Sketch of call stacksingle lix performs physics update:
perform job:
some work
move ahead:
if encounter trap:
set flag
more work
if flag:
feed trap
dieI don't know whether "more work" should execute when we are going to die. "More work" can easily move you somewhere else, and then it could be harder to determine the correct trap.
Idea #1: throw exceptionHave moveAhead throw on trap encounter, and catch this exception in singleLixPerformsPhysicsUpdate. The intention is to stop performJob early.
Benefit: Program flow mimicks what we see and expect. We die immediately.
Disadvantage: I use the exception in a non-exceptional case. This may happen several times per second. In effect, it's a goto that jumps between methods of different classes.
And exceptions produce the slowest code. Throwing may be idiomatic in Python, you even throw when your iterated range is empty. This isn't Python, exceptions are slow. I had file-searching code that relied on exceptions internally, it became 10 times faster after rewriting internals to return success or fail. You don't want exceptions flying everywhere in a multiplayer game.
Maybe it's not a huge performance dent and I should profile.
Also, are exceptions designed for this? Exceptions are for when you encounter non-bug problems with your input or environment, and you don't know how to handle. But I know exactly how to handle traps. My desire is to stop the outside logic, not to inform the outside logic.
Idea #2: disable movementOnce we touch a hungry trap, disable further movement.
Advantage: Relies on encounter flags (did we touch water, fire, trap, steam, etc.) like every other encounter.
Disadvantage: We don't die immediately. The movement-calling code, "perform job" in the call stack above, contiues to happily call moveAhead, and yet we don't move ahead.
Idea #3: remember reference to trapOnce we touch a hungry trap, remember a reference until the end of performJob.
Advantage: We know the correct trap 100 % of the time, instead of deducing which one it was after the fact. We don't ever remember references to already-eating traps, even though we moved through their area. When the job calls moveAhead, we will still move ahead, even if we die afterwards.
Disadvantage: Extra mutable state. We don't die immediately.
Hmm.I like #3 best, then #1, then #2. (Completing the motion after seeing the trap) shouldn't be problematic. Lixes never affect each other with traps: One lix performs at a time, traps snap immediately before the next lix performs.
-- Simon