Russian-Doom / russian-doom

A limit-removing source port of Doom, Heretic and Hexen. It has numerous vanilla bug fixes, enhanced 640x400 and 1280x800 rendering resolutions, improved game palettes and offers many optional aesthetic game enhancements along with the maximum possible translation to the Russian language.
GNU General Public License v2.0
80 stars 13 forks source link

Make the monsters fall under their center of gravity when they are dead #11

Closed AXDOOMER closed 6 years ago

AXDOOMER commented 7 years ago

This will avoid situations where a monster's corpse wouldn't fall of a ledge only because a tiny bit of its corpse touches the ledge, so it would hang in the air instead of falling. This often occurs with monsters that have a big radius.

fabiangreffrath commented 7 years ago

Is this what is called "torque" in MBF?

AXDOOMER commented 7 years ago

Torque seems to be different: https://forum.zdoom.org/viewtopic.php?f=18&t=22389

I'm only asking to make the dead monsters fall under their center of gravity, not to simulate torque. This would be crazy to implement since Doom doesn't respect Newton's law and the way physics are done on the X and Y axis is totally independent from the Z axis. Doom is nowhere near full 3D in regards to physics.

The way I would do it is that when a corpse stops moving, the game would check if the center of the monster is touching the floor, if not, then fall down. (but in reality it would be more complex to implement I believe)

fabiangreffrath commented 7 years ago

I think this is exactly what Killough called "torque".

Have a look at this rather descriptive comment here: https://github.com/team-eternity/WinMBF/blob/master/Source/p_map.c#L796

AXDOOMER commented 7 years ago

It also says:

// and allows objects to roll off of the edges of moving lifts, or
// to slide up and then back down stairs, or to fall into a ditch.

Which is a lot more complicated than what I am asking for. I'm only talking about gravity.

If you look in P_ApplyTorque, what he does becomes even more complex:

  // Doom has no concept of potential energy, much less
  // of rotation, so we have to creatively simulate these 
  // systems somehow :)

Graf Zahl on the ZDoom forums said (link in my previous comment):

That code never worked. It once was in ZDoom but was removed due to the major problems 
it caused. It won't be re-added. Even in MBF it was possibly one of the most map breaking 
features ever.
fabiangreffrath commented 7 years ago

I see, thanks for the rationale!

JNechaevsky commented 7 years ago

Hmmm. Actually, this sounds interesting. But how it can be implemented? As an abstract thought, I think dead monster's radius should be reduced to... 1/3 for example, but this can become a real problem for sprite clipping - dead bodies can be clipped a bit to the walls, or fall down into small holes.

AXDOOMER commented 7 years ago

I think dead monster's radius should be reduced to... 1/3 for example, but this can become a real problem for sprite clipping - dead bodies can be clipped a bit to the walls, or fall down into small holes.

Reduce their radius only when they stop moving then, that way they won't be pushed too close to the walls. Even better: You could use the real radius when doing XY movement, but when doing Z movement (i.e. gravity), then you could set the radius to 1/3 or whatever works best.

JNechaevsky commented 7 years ago

I'm afraid it sound too hard for me, don't have any idea how to make it possible. I need some kind of starting point, where to start to look/research.

fabiangreffrath commented 7 years ago

I have pointed to some code in MBF in one of my previous replies, but since @AXDOOMER stated that this is not the code he was talking about in the first place, I am also not sure what he actually meant?

AXDOOMER commented 7 years ago

I am only asking to fix this awkward situation where a dead monster whose radius touches a ledge ends up floating in the air: doom01 The MBF code is too complicated for the simple thing that this bug is. It would be like taking a hammer to crush a fly. It would also bring in many bugs since it simulates very complicated stuff. I appreciate when the corpse of Imp that I just shot with a shotgun climbs the stairs. I never tried this MBF to see the torque in action, but bringing in its code would probably remove this unique result of Doom's special physics.

Gravity should affect monsters whose center is not directly touching the ground. I don't know how to fix it, but at the same time it would be a great occasion to improve the AI to fix these bugs: Monsters stuck in doortracks, walls or hanging off lifts. Even though it may be a challenge, see it as an occasion to learn more about Doom's inner workings and improve your coding skills. The end result could even be a feature that would also be implemented in Russian-Doom, Crispy-Doom or DoomRetro.

You could start by looking at how MBF uses the function PIT_ApplyTorque to apply torque to monsters (e.g. Where is it called in the code?), then create you own function that makes a corpse that stands over a ledge fall off and call it at the appropriate place. If you start by fixing the monsters hanging off lifts bug, then maybe you will have a better idea on how to fix this bug. You may also try out MBF's code to see if it suits without being problematic.

fabiangreffrath commented 7 years ago

I think this has been suggested before and maybe it's just that simple:

In p_inter.c:P_KillMobj() there is the line target->height >>= 2;. Maybe it already helps if you do the same for target->radius?

If we just had a savegame of the moment just before @AXDOOMER killed the Spider in the screenshot above. ;)

fabiangreffrath commented 7 years ago

To be extra sure please add if (!target->player && target->radius > 1) .

JNechaevsky commented 7 years ago

Actually... This seems to be working! After some genocide on MAP30 I haven't noticed aby changes (but in fact they was - it's just hard to catch a right moment), so I've just tried this in custom map. Take a look, before and after. I'm moving toward Arachnotrong for some pushing effect.

https://youtu.be/PVFkJQjd2Ao

P.S. Tested with second recommendation, i.e.:

    if (!target->player && target->radius > 1)
    target->radius >>= 2;

P.P.S. Desyncs internal demos! Definition (singleplayer) is needed.

JNechaevsky commented 7 years ago

I think I like it and will gladly merge it to both main and DOS projects. The full code (credits will be added of course):

p_inter.c @@ P_KillMobj

    target->flags |= MF_CORPSE|MF_DROPOFF;
    target->height >>= 2;
    if ((singleplayer) && (!target->player && target->radius > 1))
    target->radius >>= 2;

@AXDOOMER is it what we want?

AXDOOMER commented 7 years ago

https://youtu.be/PVFkJQjd2Ao

Nice, but can you make sure that the radius is restored properly when the monster is resurrected by an Arch-Vile? Also, why !target->player? Is there a bug when the radius of the player is reduced?

fabiangreffrath commented 7 years ago

Very good point!

fabiangreffrath commented 7 years ago

I took out target->player because its possible to resurrect players with the IDDQD cheat. I didn't think about resurrecting monsters, though. ;)

JNechaevsky commented 7 years ago

For resurrection by Arch-Vile, this seems to be:

p_enemy.c @@ A_VileChase

    {
        // got one!
        temp = actor->target;
        actor->target = corpsehit;
        A_FaceTarget (actor);
        actor->target = temp;

        P_SetMobjState (actor, S_VILE_HEAL1);
        S_StartSound (corpsehit, sfx_slop);
        info = corpsehit->info;

        P_SetMobjState (corpsehit,info->raisestate);
        corpsehit->height <<= 2;
        if (singleplayer)             // <------- Because it was "halfed" only in singleplayer
            corpsehit->radius <<= 2;  // <------- Restore radius
        corpsehit->flags = info->flags;
        corpsehit->health = info->spawnhealth;
        corpsehit->target = NULL;

        return;
    }

Am I right?

AXDOOMER commented 7 years ago

Seems all right. <<= 2 is more like multiplying by 4 though, because it was quartered.

JNechaevsky commented 7 years ago

Hm, I was thinking that >>= 2 is the division by two. Or no? I'm not familiar with these operators, unfortunatelly.

AXDOOMER commented 7 years ago

It shifts the bits. You can experiment using this online calculator.

Multiplication:

00010111 (+23) one left-shift (multiplication by two) = 00101110 (+46)

Division:

00010111 (+23) one right-shift (division by two) = 00001011 (+11)

If the height of the monster is not a multiple of 4, then when you shift right twice, you won't be able to recover the original height when shifting left twice again.

JNechaevsky commented 7 years ago

Ahh, got it, thanks. Maybe it will be safer to use something like: target->radius/2 and target->radius*2 ?

AXDOOMER commented 7 years ago

No, it does the same, except it's doing target->radius/4 and target->radius*4 behind the scenes. Shifting is the most optimized way to do it. I think making the radius 4 times smaller is good.

JNechaevsky commented 7 years ago

<<= 4 and then >>= 4? But isn't it too much? I'm afraid it can cause problems with visual clipping - monsters may fall too close to the wall.

AXDOOMER commented 7 years ago

You should test it and see what's better. Either way, there is a case where there could be a bug. Let's say you kill an Imp with a rocket launcher and its corpse is thrown against the wall. An Arch-Vile may try to resurrect it and it could be stuck in the wall. It's a speculation because I don't know if it can really happen.

AXDOOMER commented 7 years ago

I just checked the source code and Arch-Viles will not resurrect monsters which would be stuck inside linedefs.

JNechaevsky commented 7 years ago

Aha, good to know! I'll check rocket/imp case tomorrow, it must be really important, thank you for mention. Just tomorrow, I'm falling asleep after today's raidings.

fabiangreffrath commented 7 years ago

<<= 4 and then >>= 4

No, this would multiply with and divide by 2^4.

As @AXDOOMER already pointed out, you take away the two least significant bits of the binary number by using >>= 2. Just like taking away the two right-most ciphers from a decimal number results in division by 100 (e.g. 1200 -> 12), because 100 = 10^2, taking away the two right-most ciphers from a binary number results in division by 4, because 4 = 2^2.

Note that this is strictly only defined for positive numbers. A more portable way would be e.g. target->radius /= 4 and target->radius *= 4, but this should not be necessary here.

JNechaevsky commented 7 years ago

Done some tests with both bit-shifting and dividing/multiplying. News are not very good. On bit-shifting 4, it is possible to blow away monsters through the wall: http://savepic.ru/13865070.png

On bit-shifting 2 and dividing/multiplying by 2, there is a chance of monsters being stucked in wall after Archvile's resurrection. In more rare cases, Archvile himself can be stucked in resurrected monster. If monsters are resurrected without radius*2, they can't be stucked, but there is a chance of resurrection inside the wall, and of course, after few deaths/resurrections they are loosing their radius completely.

I think, safest way will be something like little bouncing of corpse from the wall, but isn't it too much?

AXDOOMER commented 7 years ago

Here's the way I think it can be fixed:

The engine moves a thing to a higher floor that it touches if it's 24 units high or smaller. To do this, the engine checks what sectors the thing touches and their heights. For example: If the thing is on a 64 units floor and moves to touch a 72 units floor, then it moves up to 72 units.

You could find the code that does this and reduce the radius of the thing only at that moment (for dead things only). You'd save the radius to a temporary variable so it can be restored and then shift the corpse's radius to a smaller value. Once the checks are done, you can restore the original radius. This would avoid every bug that you mention, because the monster would only have the reduced radius while the engine applies gravity to it.

fabiangreffrath commented 7 years ago

This sounds like a neat idea to try out!

The code in question should be in p_mobj.c:P_XYMovement() which calls p_map.c:P_TryMove(). This, in turn, calls p_map.c:P_CheckPosition() which creates a rectangle tmbbox[] around the thing's position with the thing's radius as its edge length.

If this could get modified to e.g. quarter of its value if the thing in question is dead but still has momentum, then I think that could work!

JNechaevsky commented 6 years ago

Made some progress by implementing Killough' "torque" from WinMBF.

Looks somewhat hilarious but it works. Something not good enough with items, though. Armor bonus should fall right after wall stats to move down. Need to investigate it more deeply.

JNechaevsky commented 6 years ago

:see_no_evil: In "as is" implementation it affects on everything that is non dead, i.e. even blood splats, dropped ammo and even player. I think it should be applied strictly to the corpses, because corpse itself is pretty safe object that does not affecting anything. But moving dropped ammo/items is small, but still intervention into original game mechanics, which I prefer to don't do.

Also, affecting corpses only seems to safe for demos, but no sure - dead player is also "corpse", and how it will act in network game is a hard question. Anyways, I'm okay to apply Torque strictly for singleplayer.

Just need to play around with it more to see, isn't there odd things happening.

Conditions should be:

    if (mobj->z > mobj->dropoffz        // Only objects contacting dropoff
    && !(mobj->flags & MF_NOGRAVITY)    // Only objects which fall
    && (mobj->flags & MF_CORPSE))       // [JN] And only for corpses
        P_ApplyTorque(mobj);            // Apply torque
    else
        mobj->intflags &= ~MIF_FALLING, mobj->gear = 0;  // Reset torque
JNechaevsky commented 6 years ago

Okay, so copy-pasting of:

with minimal corrections seems to be working fine, because I'm applying it only to the corpses. Fine, except one thing: sliding (non solid) corpses are blocked by other objects like player.

It feels like - I can walk through the sliding corpse, but sliding corpse can't walk through me, despite of corpse is no solid. Try to imagine this situation in real life!

Probably, some additional work should be done in P_TryMove... But at the moment, no idea what exactly.

JNechaevsky commented 6 years ago

Quick-shot suggestion. Adding

    if (tmthing->intflags & MIF_FALLING)
    return true;

here: https://github.com/JNechaevsky/russian-doom/blob/master/src/doom/p_map.c#L358

seems to resolve previous issue. Need additional testing.

JNechaevsky commented 6 years ago

Merged in https://github.com/JNechaevsky/russian-doom/commit/b5066b15b324a60bc7fa18b5b0f9fae809e7a307!

@AXDOOMER, @fabiangreffrath, guys, could you please briefly inspect this commit? I see it's working pretty well in game, but maybe something not right in my small additions in p_map.c and p_mobj.c?

JNechaevsky commented 6 years ago

Evening/morning, guys!

Alexandre-Xavier, thanks for checking! I will add those 4 recommended spaces. Needles to say, that I will also apply this to DOS version. The only remaining question is - should it be applied into "-vanilla" mode? From one side, I'm trying to keep vanilla mode visually as close as possible to vanilla version. From another, vanilla mode does not bring back any engine bugs and limits. Non-sliding corpses are barely can be called "bug" at all, and I think this feature will be good in vanilla mode.

Also, sorry for it takes about half of year to implement this, but frankly saying, on the May porting this code was something impossible for me, but now I have much more coding experience. It's interesting for me to see how some things was incredibly hard in the beginning and becomes pretty obvious much later. Never stop learning.

Finally, few more thoughts. My theory about "corpse is safe object for gameplay" is a little bit wrong.

Well, in general, yeah, corpse can't affect something in common gameplay. But. It some really rare cases, dead corpse can slide beneath opened door or crusher, and squish! This object is no longer corpse but pool of blood. More of that, if there will be Archvile, he can resurrect corpse/pool, and poof! We got a possible demo desync.

Even this case is possibly rare, this does not mean that it can't happen at all. That's it.