diasurgical/devilutionX

[Issue Report]: Able to permanently kill player using cursed items

Closed this issue ยท 13 comments

Operating System

Windows x64

DevilutionX version

Other (please specify version number)

Describe

Desktop.2023.08.27.-.01.17.26.07.mp4

By equipping cursed items that bring your life below 1 and dying to a method that doesn't make you drop your items, restarting in town will result in a loop of your character appearing dead in town. The only way around this is to get another player to resurrect you.

To Reproduce

  1. equip cursed items
  2. die without dropping items
  3. restart in town

Expected Behavior

No response

Additional context

No response

Removing the call to CalcPlrInv() in RestartTownLvl() fixes this problem completely. No noticeable side effects. Is there a reason this function needs CalcPlrInv()?

Wouldn't CalcPlrInv() be necessary for when you die and lose items?

Wouldn't CalcPlrInv() be necessary for when you die and lose items?

Yeah right here:

CalcPlrInv(player, false);

And

CalcPlrInv(player, false);

Considering it calls CalcPlrInv() as soon as a player dies, I'm not sure why it needs to be called again when the only change happening when a player is revived is their state is changed from dead to alive and they get some HP (amount depending on if it's restart in town or resurrect).

I didn't test this with Resurrect but I think you might be able to create the same issue if the player's Max Life is -10 or less.

In Single Player, if the player chooses to save their game while their HP is negative, loading that savegame will crash the game client.

image

InitPlayerGFX() only loads the player_graphic::Death sprites, but the player wasn't dead when they saved the game, so the sprites needed to load the savegame don't get loaded.

devilutionX/Source/player.cpp

Lines 2156 to 2160 in 80ee326

if (player._pHitPoints >> 6 == 0) {
player._pgfxnum &= ~0xFU;
LoadPlrGFX(player, player_graphic::Death);
return;
}

Furthermore, if the player equips an item that brings their HP below zero while in the dungeon, the game will instantly kill them. If the player does the same in town, the game prevents the player's death to avoid letting them die in town. If the player then enters the dungeon without removing the cursed items, the game does not instantly kill them. The game should probably consistently kill the player if the player's hp is negative while in the dungeon.

I think there are 2 major issues that lead to undesirable gameplay:

  1. Changing equipment can kill the player
  2. Max life is allowed to go under 1

I think there is a potential solution to consider that might prevent the need for workarounds, such as the workaround that fixes town death via Healing/Heal Other, and whatever workaround would fix this issue report.

Prevent equipment changes that do either of the following:

  • Reduces current life below 1
  • Reduces max life below 1

Items that violate these conditions would have iStatFlag set to false.

Why not just allow the player to die in town instead?

I feel like attempting to prevent equipment change based on the final effect of the change is both a convoluted solution, and very inflexible for further modding later.

For example, what if you want to have an item that drains 1hp every second that can kill the player, but you equip it with 2hp? Your check will not catch that because it wouldn't be an "instantaneous" death, but it would have a very similar effect regardless, so checking at all to me feels pointless.

Why not just allow the player to die in town instead?

I agree that checking for equipment change seems convoluted, but did you mean allow them to die in Town, or not die in Town? If you allow dying in Town, and assuming equipment doesn't drop in Town, you'd never be able to be alive again.

I think just checking for player death when they enter the dungeon, as @StephenCWills said, makes the most sense and is the least complex solution.

Why not just allow the player to die in town instead?

I agree that checking for equipment change seems convoluted, but did you mean allow them to die in Town, or not die in Town? If you allow dying in Town, and assuming equipment doesn't drop in Town, you'd never be able to be alive again.

Drop the equipment in town and allow restarting, just like any normal death, is what I'd propose. It shouldn't be special-cased at all. The special casing only makes everything more complicated than it needs to and introduces extra potential "invalid game states" that would otherwise not need to be considered.

I think just checking for player death when they enter the dungeon, as @StephenCWills said, makes the most sense and is the least complex solution.

I think that's still more complicated than just handling death anywhere equally.

@julealgon That would be fine if the game handled death anywhere equally, but I guess you've never died in Multiplayer on dlvl 16. The player doesn't drop their equipment there. So it is already a special case and would probably wreck your idea.

EDIT: I suppose it's also worth mentioning that dying to PK doesn't drop your items. Just based on KP's video, dying by equipping a cursed item seems to count as a PK. So there's a few more counterexamples to the game handling death anywhere equally.

Why not just allow the player to die in town instead?

I feel like attempting to prevent equipment change based on the final effect of the change is both a convoluted solution, and very inflexible for further modding later.

For example, what if you want to have an item that drains 1hp every second that can kill the player, but you equip it with 2hp? Your check will not catch that because it wouldn't be an "instantaneous" death, but it would have a very similar effect regardless, so checking at all to me feels pointless.

Life drain =/= item bonus that changes Life amount. Equipping a constricting ring would not be prevented with my suggestion at any life amount. Also, you haven't suggested anything that fixes the problem outlined in this Issue report, but instead you seem to support the problem continuing to exist. I'm not sure how you reasonably think the player should be permanently dead in town with no option to unbrick their character other than Resurrect.
Maybe an example would suffice:

Before:

  • Player has 5/10 life
  • Player equips item that is -5 life
  • Player dies

After:

  • Player has 5/10 life
  • Player tries to equip item that is -5 life, but the game won't let them and their character uses the voice line "That would kill me!"

or

Before:

  • Player has 5/10 life
  • Player unequips item that gives +5 life
  • Player dies

After:

  • Player has 5/10 life
  • Player attempts to unequip item that gives +5 life, but the game won't let them and their character uses the voice line "That would kill me!"

Then as far as changing Max Life to a negative number, this should not be allowed. It simply doesn't make sense. How can one have negative max life? The way it currently stands, it's possible to have a current Life greater than Max Life, and it's absurd.
Example: 10/-50

Casting Healing in this state would cause the player to instantly die. Heal Other would do the same. The game should not allow this.

It is also worth stating that another solution that fits my criteria, such as allowing equipment changes no matter what and not killing the player would result in life gaining exploits (not sure about gaining Max Life, but I presume it's entirely possibly). I believe my solution doesn't have any exploits, and provides the player a sufficient safeguard. If you can think of any valid downsides to my solution, I am interested in hearing them so we can brainstorm for an improved solution.

Conclusion

  • Max Life should NEVER be able to go under 1.
  • The player should not be able to instantly die from equipment changes (Life drain isn't instant).

I don't think those conclusions are necessary. Just make it so you can't die/be dead in Town, regardless of their max/current hp, and fix the issue with not dying in dungeon that @StephenCWills mentioned. If you want to provide some additional guardrails, you can do the "that would kill me" sound when someone tries to enter a TP or dungeon entrance from Town with max life <=0, but not strictly necessary.

@kphoenix137

Life drain =/= item bonus that changes Life amount. Equipping a constricting ring would not be prevented with my suggestion at any life amount.

Fair enough. I wasn't trying to suggest they were the same thing.

Also, you haven't suggested anything that fixes the problem outlined in this Issue report, but instead you seem to support the problem continuing to exist. ... I'm not sure how you reasonably think the player should be permanently dead in town with no option to unbrick their character other than Resurrect.

Well, I did just post this right after:

Drop the equipment in town and allow restarting, just like any normal death, is what I'd propose.

My point was just that I didn't see a reason to special-case death handling in town vs the dungeon.

And I know @StephenCWills brought up another scenario where death behavior is special-cased, but that doesn't diminish my point I don't think: I'd rather see all special cases removed, if that was possible: if the player dies, he dies, drops the items where he died, and restarts on the specific tile in town. If the death happened in DLVL 16 or in town itself, I don't see a reason to change any of that behavior.

Again, that's just my personal take. Apparently, most of you disagree with it which is fine.

Then as far as changing Max Life to a negative number, this should not be allowed. It simply doesn't make sense. How can one have negative max life? The way it currently stands, it's possible to have a current Life greater than Max Life, and it's absurd. Example: 10/-50

Totally agree on this one. "Max life" should be capped at the bottom end like other attributes are/should. Bottom cap should probably be 1 for max life, and any reduction in max life instantly affects current life so that current life <= max life at all times.

If you can think of any valid downsides to my solution, I am interested in hearing them so we can brainstorm for an improved solution.

The downside I see is not from a gameplay perspective, but from a game engine complexity perspective. When things work the same way as much as possible, the logic tends to become simpler to manage and less error prone. That's all.

Note as well that special cases will also tend to interact badly with modded content later, as mods will invariably introduce extra scenarios that you'd then want to special case as well because of the original customization.

Imagine for example, a mod where there is a monster threat in town, and your character can now go into combat stance and attack/use spells in town to kill the monsters. With the extra "town-specific" workarounds you create here, you'll make realizing that mod harder as these restrictions would have to be lifted in several places in the code.

That is of course just an example.

And I know @StephenCWills brought up another scenario where death behavior is special-cased, but that doesn't diminish my point I don't think: I'd rather see all special cases removed, if that was possible: if the player dies, he dies, drops the items where he died, and restarts on the specific tile in town. If the death happened in DLVL 16 or in town itself, I don't see a reason to change any of that behavior.

What does diminish your point is not the fact that it works differently, but that it works differently for a reason.

For PK, Blizzard chose to drop an ear as a trophy. I presume they didn't want gear to drop because it would be too much incentive for being a mean-spirited opportunist and diminish co-op play.

The reason dlvl 16 is a special case is because killing Diablo goes directly to the end movie and boots players from the game. There's too much opportunity to lose all your gear to a very poorly timed death.

Ignoring design decisions for the sake of reducing complexity is just short-sighted. Furthermore, and this is just my gripe, but your framing of "I don't see a reason to change any of that behavior" is a bit disingenuous considering you'd actually be the one changing behavior.