Heavy monsters can softlock A_SkullAttack()

Is there something that doesn't work right in the latest GZDoom? Post about it here.

Moderator: GZDoom Developers

Forum rules
Please construct and post a simple demo whenever possible for all bug reports. Please provide links to everything.

If you can include a wad demonstrating the problem, please do so. Bug reports that include fully-constructed demos have a much better chance of being investigated in a timely manner than those that don't.

Please make a new topic for every bug. Don't combine multiple bugs into a single topic. Thanks!
User avatar
MartinHowe
Posts: 2081
Joined: Mon Aug 11, 2003 1:50 pm
Preferred Pronouns: He/Him
Location: East Suffolk (UK)

Heavy monsters can softlock A_SkullAttack()

Post by MartinHowe »

If a flying monster using A_SkullAttack() is stopped by a hitscan impact, not slowed down or put into reverse but just stopped, it stays motionless in the missile state loop and never exits. The wing animations (or whatever) still work, but it no longer moves.

Computer: AMD Ryzen Threadripper PRO 5955WX, Linux Mint 22.1, NVIDIA RTX A4000
Game: Heretic
Loaded Modules: The attached (FrozenChargeAttack.pk3), containing only the following zscript.zs

Code: Select all

class HeavyImp : HereticImp
{
    Default
    {
        mass 1000;
    }
}
Steps:
1 Start Heretic, loading the above ZScript module on the command line with -file
2 Start the game at E1M1
3 Use the console to kill all monsters
4 Navigate to an open area in the water and summon a HeavyImp
5 When it charges you, shoot it with the Elven Wand, make sure you hit it dead on

Expected result:
It flinches or even reverses, then resumes the attack.

Actual result:
It stops moving (but not animating). It's essentially running, well flying, on the spot.

Discussion.
A_SkullAttack requires the actor to have velocity to work. If the velocity becomes zero, presumably this doesn't happen. With standard Gargoyles and Lost Souls, this isn't a problem; they don't weigh much and a bullet or wand strike usually pushes them back or simply slows them down. But with a heavy monster, such as the one I am making, I have found repeatedly that a hitscan impact can reduce their velocity to zero (or at any rate < 1.0). Thus it just stops moving. Thus it never gets a chance to return to the See state or otherwise leave the missile state.

(My current work around is to include #### # 0 { if (vel.x < 1.0) SetState(SeeState); } just before the Loop statement).
Attachments
FrozenChargeAttack.zip
(439 Bytes) Downloaded 4 times
User avatar
Rachael
Posts: 13956
Joined: Tue Jan 13, 2004 1:31 pm
Preferred Pronouns: She/Her
Contact:

Re: Heavy monsters can softlock A_SkullAttack()

Post by Rachael »

So this one is not as easy to solve as you might expect.

The code is checking for "move.IsZero()" which, if we're talking about the classic fixed-point Doom code, would stop the +skullfly object at an expected velocity (very minor).

But since we're using floating point, the precision by which "zero" is detected is quite tiny, and gets even smaller the closer to (0,0) you get on the map.

There is also the conundrum that, as per usual with GZDoom mods, some mod somewhere might depend on this behavior, so a proper fix will require some sort of flag.

Objects slamming into other objects while being +skullfly are just fine because the physics code will zero the velocity completely when the object hits a wall or another object. But hitscans have always been a completely different story - and you can see it even in vanilla Doom (though probably not as reliably as you can in GZDoom).
User avatar
MartinHowe
Posts: 2081
Joined: Mon Aug 11, 2003 1:50 pm
Preferred Pronouns: He/Him
Location: East Suffolk (UK)

Re: Heavy monsters can softlock A_SkullAttack()

Post by MartinHowe »

Thanks @Rachael. I figured it would be difficult, though not for the expected reasons; in particular existing mods depending on something this bizarre :mrgreen:

It's no trouble to check the abs(vel.x) anyway. EDIT: and vel.Y if I've understood the vector stuff correctly.

It would be kinda interesting to know exactly how heavy a monster needs to be before this happens, though given Doom's janky collision logic, it's probably random anyway :P
User avatar
Rachael
Posts: 13956
Joined: Tue Jan 13, 2004 1:31 pm
Preferred Pronouns: She/Her
Contact:

Re: Heavy monsters can softlock A_SkullAttack()

Post by Rachael »

You can do if (bskullfly && (vel.Unit()) < 0.1))
User avatar
MartinHowe
Posts: 2081
Joined: Mon Aug 11, 2003 1:50 pm
Preferred Pronouns: He/Him
Location: East Suffolk (UK)

Re: Heavy monsters can softlock A_SkullAttack()

Post by MartinHowe »

Rachael wrote: Mon Aug 11, 2025 9:05 am You can do if (bskullfly && (vel.Unit()) < 0.1))
Thanks; I tried it, but didn't compile :( Should that be vel.Length()?

In case it helps: with a mass of 200, he does move, but very slowly; Length() is typically 0.6 or thereabouts, with x and y being .0.5 and 0.2 respectively. So unless you're looking closely, the monster can be moving, but imperceptibly and takes ages to hit a wall unless by chance collides with another monster. Shooting them again seems to cycle the velocity from around 0.35 back to around 0.6. Presumably it would have rounded down to zero in the old days?

So for now, if only for the sake of aesthetics, I'll use < 1.0 in my own code until this is sorted; tested and seems to work. It's in the base class so I don't have to code it in every monster that might use it.

Code: Select all

    override void Tick()
    {
        super.Tick();

        if (bSkullFly && (vel.Length() < 1.0))
           SetState(SeeState);
    }
Oh, and you're right; Gargoyles have always been like this, but at least their motion (towards a back wall that they eventually hit and wake up again) was obvious if painfully slow. It always looked janky, but I thought it was by design :lol: Perhaps the solution is some sort of SkullAbortThreshold property?
User avatar
Rachael
Posts: 13956
Joined: Tue Jan 13, 2004 1:31 pm
Preferred Pronouns: She/Her
Contact:

Re: Heavy monsters can softlock A_SkullAttack()

Post by Rachael »

MartinHowe wrote: Mon Aug 11, 2025 9:47 am Perhaps the solution is some sort of SkullAbortThreshold property?
Actually that is a good idea. :)
Boondorl
Posts: 169
Joined: Wed Jul 11, 2018 10:57 pm

Re: Heavy monsters can softlock A_SkullAttack()

Post by Boondorl »

My solution for this in my mods is to usually just save the velocity and reapply it after kickback is calculated when damaged, however I'm not against just adding an Actor flag for this because it would allow others to easily fix this in things like their Doom mods as well. As for the velocity check, it should probably check if it's near zero (e.g. < equal_epsilon) rather than checking real zero since that would put it in a broken state compared to the original Doom. I recently-ish added a flag to disable this "stop when 0" behavior entirely which can be used for custom handling, but in this case it's the default kickback handling causing problems and even avoiding calling the ApplyKickback function doesn't work to my knowledge, it just zeros the velocity.
Post Reply

Return to “Bugs [GZDoom]”