CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
10.26k stars 4.12k forks source link

Monsters that are able to swim, can attack you though your boat hull and frame, over Z levels, without actually damaging it first. #66983

Closed Krieger1999 closed 11 months ago

Krieger1999 commented 1 year ago

Describe the bug

If your driving on your boat on SEA water, which do has multiple Z levels of water below, than monsters which can swim like these zombie sharks are able to directly attack you from below your boat though your boat hull and boat frame, without damaging it in any way. The attack just gets teleportet though the boat over Z levels in SEA water. This happens when you spot these sharks (can also randomly instantly happen) and then wait a long while, until they can actually just submerge in the water and try to attack you from below your boat.

Attach save file

Kieferschreck 1 Kieferschreck 2 Kieferschreck 3 Kieferschreck 4 Kieferschreck 5

Steps to reproduce

  1. Spawn a at least 3*3 boat on deep sea water
  2. Spawn a monster than can swim next to the boat
  3. wait long enough
  4. See that the monster has submerged in the water and attacks you from below your boat over Z levels.

Expected behavior

Normal monsters should not be able to teleport there attacks though the boat hull and frame, over Z levels. There attacks should first damage and destroy the boat hull at least, befor they can manage to attack you though it.

Or just do not let them magicaly attack you though boat hulls.

Screenshots

No response

Versions and configuration

Windows 10 version: d071694 (tiles) build: 2023-07-16-0559 (2 days ago) mods: Dark days ahead

Additional context

No response

RenechCDDA commented 1 year ago

Oh good, I was actually writing this report when I saw you already posted it.

Thank you for reporting it. Thankfully this seems like it might be simple, I'm running down a possible fix right now. (It was not simple.)

RenechCDDA commented 1 year ago

This bug has several preconditions: 1) 3D FOV must be on. 2) The player character (this wouldn't work against NPCs!) must be on a z-level above the attacker. 3) For water scenarios this meant finding water deeper than 1 z-level (pretty rare!) and having multiple attackers(pretty rare!) so that one of them eventually chooses a downwards movement when jockeying for position. 4) Even after that it is somewhat random(?!) that they attack through the boat's bottom, could take some time with only one attacker below you.

Why does disabling 3D FOV prevent this? With it off I tried spawning a literal ocean of jawed terrors below me (so that some should be stumbling upwards or otherwise getting lucky) and could not get the attacks to happen.

So this patch "works", but breaks a lot of zombie attacks elsewhere (zombies will not attack ever if pathing into your tile costs >1 turn): https://github.com/CleverRaven/Cataclysm-DDA/commit/53f326864f6b661f94a734b15a6b98eb8aed7983

Changing the 3D FOV check in Creature::stumble_invis to logical OR ( || ) prevents the stumbling, but not the attacking.

Why is the is_adjacent() check in monster::melee_attack not preventing this? Actually, what the heck is Creature::is_adjacent doing for z-levels and vehicles? It checks for same z-level but then checks for underwater and if both positions are covered by a vehicle (we do not have submersibles or multi-z-level vehicles!)? Well, I just decided not to touch that.

RenechCDDA commented 1 year ago

Just for sanity's sake, here's a rundown of the return from the underwater check on is_adjacent() (which isn't ever called, because the attacking z-level is different!). If it was as simple as flipping that == to != I would have done it.

!( underwater != target->underwater && here.veh_at( pos() ) && here.veh_at( target->pos() ) );

NOT ( underwater != target->underwater && here.veh_at( pos() ) && here.veh_at( target->pos() ) );

NOT ( TRUE && here.veh_at( pos() ) && here.veh_at( target->pos() ) );

NOT ( TRUE && FALSE && here.veh_at( target->pos() ) );

NOT ( TRUE && FALSE && TRUE );

NOT FALSE

TRUE

Krieger1999 commented 1 year ago

This bug has several preconditions:

  1. 3D FOV must be on.
  2. The player character (this wouldn't work against NPCs!) must be on a z-level above the attacker.
  3. For water scenarios this meant finding water deeper than 1 z-level (pretty rare!) and having multiple attackers(pretty rare!) so that one of them eventually chooses a downwards movement when jockeying for position.
  4. Even after that it is somewhat random(?!) that they attack through the boat's bottom, could take some time with only one attacker below you.

Why does disabling 3D FOV prevent this? With it off I tried spawning a literal ocean of jawed terrors below me (so that some should be stumbling upwards or otherwise getting lucky) and could not get the attacks to happen.

So this patch "works", but breaks a lot of zombie attacks elsewhere (zombies will not attack ever if pathing into your tile costs >1 turn): 53f3268

Changing the 3D FOV check in Creature::stumble_invis to logical OR ( || ) prevents the stumbling, but not the attacking.

Why is the is_adjacent() check in monster::melee_attack not preventing this? Actually, what the heck is Creature::is_adjacent doing for z-levels and vehicles? It checks for same z-level but then checks for underwater and if both positions are covered by a vehicle (we do not have submersibles or multi-z-level vehicles!)? Well, I just decided not to touch that.

It does not take multiple attackers, as you can see in my screenshot. 1 is enough. And it took him exactly 23 seconds to submerge into the water and attack me.

Krieger1999 commented 1 year ago

Just for sanity's sake, here's a rundown of the return from the underwater check on is_adjacent() (which isn't ever called, because the attacking z-level is different!). If it was as simple as flipping that == to != I would have done it.

!( underwater != target->underwater && here.veh_at( pos() ) && here.veh_at( target->pos() ) );

NOT ( underwater != target->underwater && here.veh_at( pos() ) && here.veh_at( target->pos() ) );

NOT ( TRUE && here.veh_at( pos() ) && here.veh_at( target->pos() ) );

NOT ( TRUE && FALSE && here.veh_at( target->pos() ) );

NOT ( TRUE && FALSE && TRUE );

NOT FALSE

TRUE

Thanks for trying!

Procyonae commented 1 year ago

Just thought I'd point out water deeper than 1 z-level is not rare, that's just any lake