Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Memfis

Can two monsters kill each other in a melee infighting?

Recommended Posts

Just wondering... Let's say I got two hell knights infighting via that barrel trick. Can they both perform the finishing blow at the same tic and both die?

Share this post


Link to post

I think I have seen it a couple time when other people play. I've never seen it in any of my playthroughs though.

Share this post


Link to post

Anybody have a video of this happening? That would be hilarious to see.

Share this post


Link to post

I'm going to guess no. The game goes through the thinker for each monster one at a time. If the first one scratched the second, the second's state would be changed to "dead," and it wouldn't be able to scratch back when the game got down the list to dealing with its thinker.

Share this post


Link to post

No, they cannot. Even in the same tic, the code (P_RunThinkers function) performs actions of different monsters one by one. So, the function first handles one Hell Knight. It calls his P_MobjThinker function. This function counts tics that the monster spent in his previous state. It will detect that it's time to go to next state, and call P_SetMobjState function. This function will perform his codepointer action, which is attack (=dealing damage to the other HK). Damage is always inflicted via P_DamageMobj function. This function immediately checks if the enemy's health dropped below 0, and if so, it immediately calls P_KillMobj function. This function immediately calls P_SetMobjState to change the monster's state to its death state and resets his tics he should spend in this state. Done. Only now, P_RunThinkers stops running logic of the first HK and moves to the 2nd one. It calls his P_MobjThinker function. It finds out he's in death state, and continues from that. His previous ready-to-attack state was already lost (changed to death state, as described above), that's why he won't perform his attack, and that's why double kills via instant (non-projectile) means of damage are impossible.

tl;dr - What Linguica said. :)

Share this post


Link to post

It would be interesting to do an experiment with the source code where you ran through P_RunThinkers(), but kept all the changes that affected another thinker in a separate queue, and then applied all those effects only after everything had a chance to do its current action. Then you really could have two monsters scratch each other to death simultaneously.

I wonder how different the game would play if you did that, really. I feel like there wouldn't be any super obvious differences. Actually it might make SSGs gib troopers, now that I think about it.

Share this post


Link to post

It would disallow shotgun/SSG hitscans to pass through weak enemies that get killed by the first few pellets. A lone zombieman would soak an entire SSG blast. I think additional logic would be required to make him gib. Really, this queue would have to be carefully placed somewhere "in the middle" of other functions, which would mess with the code structure more than a little.

Share this post


Link to post

In the PSX Doom TC, I've been able to gib zombies and imps with chainsaws and plasma guns. And yes, even the unberserked fist! How does that work?

Share this post


Link to post
Piper Maru said:

In the PSX Doom TC, I've been able to gib zombies and imps with chainsaws and plasma guns. And yes, even the unberserked fist! How does that work?

Well, for one, zombiemen can be gibbed with a max-damage plasma shot even in vanilla Doom.

Just from this alone, though, I'm going to guess that gibbing in PSX Doom happens at a set threshold for each monster ( unlike in PC Doom, where gib health is the monster's base health made negative ) or it's just simply reduced. Or, increased, since it's a negative.

edit: whoops TC, not PSX Doom

well it's still potentially the same reasons

Share this post


Link to post
scifista42 said:

I think additional logic would be required to make him gib.

I think the additional logic you're describing would be essential to support the kind of change that Linguica describes. Otherwise a shotgun blast would only do the damage of a single shotgun pellet. You'd need to aggregate all the damage done to each object within a tic so that it can be applied at once.

Share this post


Link to post

If multiple damage could be inflicted simultaneously, that also could defeat invulnerability. If much is going on, 1000 points in one tic would be possible, I guess.

Share this post


Link to post
scifista42 said:

I think additional logic would be required to make him gib.

Why? Each pellet would deal damage, gradually reducing health. Then, after dealing all the damage, the game would decide who died and which animation to use. All in one tic.

Share this post


Link to post

Changing the game logic to allow parallel/multithreaded processing of thinkers would also allow double knock-outs (as well as other cumulative effects not normally possible). Of course, maintaining any sort of causality would be extremely more complicated.

Share this post


Link to post
Da Werecat said:

the game would decide who died

Except that no monster died at that point when you separate the process of damaging and dying.

Share this post


Link to post
LogicDeLuxe said:

Except that no monster died at that point when you separate the process of damaging and dying.

HP of 0 or less = dead.

First you perform all attacks and calculate the new HP for each affected actor, and then iterate through actors and change their states to dead (if appropriate).

Share this post


Link to post
Da Werecat said:

HP of 0 or less = dead.

First you perform all attacks and calculate the new HP for each affected actor, and only then iterate through actors and change their states to dead (if appropriate).


This would require adding a lot of double-checks to make sure that nothing is acting on/being acted upon by something that was declared as dead in the tic, and perhaps "rolling back" certain actions.

With the classic, single-threaded code, a single monster's thinking turn can initiate a quite long cascade of actions, some of which may affect the map's geometry and status itself (e.g. open a door, walk a linedef etc.) which in turn would affect other monsters. Keeping an exact record of what happened and granting the ability to "roll back" actions selectively would require a complete redesign of the engine. Essentially, it would require to re-run the tic up to the point where a monster's death occured -and thus the cascade of events that it would cause if it was alive shouldn't happen. Of course, this will also alter what other monsters would do after it...so a tic no longer has a guaranteed, bounded running time. In the worst-case scenario, you may have to re-run a tic N times, if there are N monster.

Of course, there's the "bum" solution of simply letting some actions happen (before you realize the death of a monster) and ignoring any that you catch after that (when and if you do). A nightmare for the ability to record demos or reproduce gameplay situations....

Share this post


Link to post
Arctangent said:

edit: whoops TC, not PSX Doom

There was a big debate about that in the PSX TC thread. The gib threshold was reduced to one-half of negative hit points because people argued that gibs happened much more often on the console than on the PC. Without reverse-engineering works it was pretty hard to get a conclusive answer, so that change remains a bit controversial.

Share this post


Link to post

I feel like I'm missing something here (which is probably the case). I'm assuming that everything is being calculated in 1 tic, but changing the object's state is moved to the end of the tic instead of being performed right after dealing the damage. So what if a monster opens a door and dies at the exact same moment?

Share this post


Link to post
Gez said:

There was a big debate about that in the PSX TC thread. The gib threshold was reduced to one-half of negative hit points because people argued that gibs happened much more often on the console than on the PC. Without reverse-engineering works it was pretty hard to get a conclusive answer, so that change remains a bit controversial.

Quasar did some digging around with reverse engineering and discovered that the gib health is the same, but certain attacks have different (higher) damage ranges.

Da Werecat said:

I feel like I'm missing something here (which is probably the case). I'm assuming that everything is being calculated in 1 tic, but changing the object's state is moved to the end of the tic instead of being performed right after dealing the damage. So what if a monster opens a door and dies at the exact same moment?

Simply put, they can't. One action per tic, blah blah blah.

Share this post


Link to post
Da Werecat said:

I feel like I'm missing something here (which is probably the case). I'm assuming that everything is being calculated in 1 tic, but changing the object's state is moved to the end of the tic instead of being performed right after dealing the damage. So what if a monster opens a door and dies at the exact same moment?


In vanilla Doom, a monster could open a door in one tic, and still be killed later in that tic. It simply depends by its priority in the thinker's queue (and whether there's a fireball already heading towards it). In certain chains of events, it will manage to open the door without dying, in others it won't.

However, if the action mechanism is changed in such a way as to allow every monster to complete their own actions anyway (walk, shoot, open etc.) and only set their "dead" state at the end of the tic, this would change the game mechanics dramatically.

For example, a lot of infighting monsters would later "discover" that their target was dead. Doors would have been opened by monsters that shouldn't be able to, and so on.

The main difference would be that every monster would be able to complete one last action in its "dying tic". With vanilla Doom's code, this is not always the case, but it depends on the monster's "luck", so to speak (e.g. its killer before or after it in the tic).

You would then witness very strange side-effects:

  • A monster could get hit by a rocket, but still open a door only to be gibbed in the immediately next tic.
  • A cyberdemon surrounded by, say, a bunch of imps, would get a scratch from each of them at the same time, even if he managed to gib them all at once with a single rocket in the same tic.
  • Entering pain states would be pointless, as hits would always land, and attacks couldn't be cancelled within the tic (assuming that ALL status updates would be moved to the end of the tic).
  • A monster in the middle of a resurrection cycle by an archvile, would get resurrected anyway even if the archvile was distracted or killed before the cycle completed (though I think that would happen anyway even in vanilla).
    etc.
You'd need pretty sophisticated checks to avoid all these side-effects to gameplay, or allow some and disallow others. At one extreme, you'd need a complete rollback mechanism in place, and the ability to re-run and branch a tic.

Share this post


Link to post

Maybe modify it so that, if the monster was in the middle of its attacking animation when it's killed, it can still fire it off as it's dying? Sort of like a last ditch effort? I imagine the second one is at least beginning to attack when it's killed, the attack's damage can't have been calculated that quickly.

Share this post


Link to post
MetroidJunkie said:

Maybe modify it so that, if the monster was in the middle of its attacking animation when it's killed, it can still fire it off as it's dying? Sort of like a last ditch effort? I imagine the second one is at least beginning to attack when it's killed, the attack's damage can't have been calculated that quickly.


The problem is that the attacking animation can be several tics longs, even though the attacking frame only gets definitively fired in one specific tic. So that means that you'd either have to keep the monster "alive" until the actual attacking frame occurs, or have it immediately execute it -kinda like a "kamikaze mode".

The first solution would mean having "zombie monsters" on their "last legs", still standing and affecting the rest of the game with their presence until they complete their attacks -that can even be a full second, with certain monsters, and do considerable damage in the case of e.g. a Cyberdemon.

The second one would turn dying monsters into instant attack traps -let them live and maybe evade their delayed attacks, or kill them and certainly hit by their "self destruct" mode.

Also, too many exceptions to check for pain states, interrupted LOS etc.

Share this post


Link to post
Maes said:

A cyberdemon surrounded by, say, a bunch of imps, would get a scratch from each of them at the same time, even if he managed to gib them all at once with a single rocket in the same tic.

Frankly, I don't think it would look very different from the situation where the imps would scratch the cyberdemon 1 tic before they're all blasted to pieces. Aside from the possiblility that the cyber will be stunned or even killed before firing back, and the imps will walk away.

But that's kind of the whole point of this thought experiment: what if the monsters could kill each other simultaneously at melee range? Piling up additional hacks to deal with such oddities of the initial hack would defeat its purpose.

Share this post


Link to post
Da Werecat said:

Frankly, I don't think it would look very different from the situation where the imps would scratch the cyberdemon 1 tic before they're all blasted to pieces.


This could still happen in the vanilla code, but it depends on the thinker's relative order and blah blah blah

With the order made irrelevant through such deferred status update hacks, however, the net effect would be that attacks would be effectively unblockable/uninterruptible, and that'd be a major gameplay change.

Share this post


Link to post
Maes said:

The problem is that the attacking animation can be several tics longs, even though the attacking frame only gets definitively fired in one specific tic. So that means that you'd either have to keep the monster "alive" until the actual attacking frame occurs, or have it immediately execute it -kinda like a "kamikaze mode".

The first solution would mean having "zombie monsters" on their "last legs", still standing and affecting the rest of the game with their presence until they complete their attacks -that can even be a full second, with certain monsters, and do considerable damage in the case of e.g. a Cyberdemon.

The second one would turn dying monsters into instant attack traps -let them live and maybe evade their delayed attacks, or kill them and certainly hit by their "self destruct" mode.

Also, too many exceptions to check for pain states, interrupted LOS etc.



In that case, maybe do the first but have it only be for melee attacks? Then, for that full second, it only damages you if you're within range.

Share this post


Link to post
MetroidJunkie said:

In that case, maybe do the first but have it only be for melee attacks? Then, for that full second, it only damages you if you're within range.


That might mean having a super-dying-imp standing and soaking 50 cyberdemon and revenant rockets aimed at you -unless you propose making such monsters effectively ghosts.

Share this post


Link to post
Maes said:

That might mean having a super-dying-imp standing and soaking 50 cyberdemon and revenant rockets aimed at you -unless you propose making such monsters effectively ghosts.


The collision detection would be the same as when it dies normally?

Share this post


Link to post

I saw the phrase "one action per tic" up there earlier and I wanna state unequivocally that this is a very incorrect and incomplete view of what really goes on in the game engine at times.

Lost soul collision is the absolutely best example I can give, because it causes re-entrancy through the clipping engine. In BOOM this can actually cause undefined behavior by violating the expected post-conditions of P_TryMove inside the function P_Move, leaving the variable numspechit with the value -1, causing an out-of-range access to occur which, under Windows, will crash the engine.

The chain of events happens as such:
P_RunThinkers->P_MobjThinker (lost soul is charging but not time to change states; do collsion checking)->P_XYMovement->P_TryMove->P_CheckPosition (object has collided with a monster! Do damage, and...)->P_SetMobjState(mo, mo->info->spawnstate)->A_Look (monster has a target already, start chasing it)->P_SetMobjState(actor, actor->info->seestate)->A_Chase (monster can move, so try to walk)->P_Move->P_TryMove->P_CheckPosition... HEY WAIT A MINUTE!

Yeah we just executed TWO actions, AND now we're making a recursive call into the clipping engine, which is infamously non-re-entrant because it uses global variables throughout. As this tortured stack unwinds itself, the original clipping operation being carried out inside the first P_TryMove on the call stack is now dealing with a corrupted state and returns incorrect results or potentially causes undefined behaviors in the most extreme cases. In the crash case, if P_Move itself occurs on the stack more than once, you get the spechit underflow potential.

Share this post


Link to post
Maes said:

With the order made irrelevant through such deferred status update hacks, however, the net effect would be that attacks would be effectively unblockable/uninterruptible, and that'd be a major gameplay change.

Only at the last possible moment, i.e. in the same tic the actual attack function is performed (the one that does damage or spawns a projectile). You'd have 1/35 of a second less to interrupt the attack.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×