Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some monsters triggered while in the process of dying are brought back to life #3061

Open
SamVanheer opened this issue Feb 26, 2021 · 3 comments

Comments

@SamVanheer
Copy link

Some monsters that are triggered while in the process of dying are brought back to life in an undead state where they are immune to some types of damage.

This happens because these monsters use the default monster Use method:

halflife/dlls/monsters.cpp

Lines 570 to 577 in c7240b9

//=========================================================
// CBaseMonster - USE - will make a monster angry at whomever
// activated it.
//=========================================================
void CBaseMonster :: MonsterUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_IdealMonsterState = MONSTERSTATE_ALERT;
}

Normally when a monster dies it sets the ideal monster state to MONSTERSTATE_DEAD:

m_IdealMonsterState = MONSTERSTATE_DEAD;

But when a monster is triggered while dying the ideal monster state is set to MONSTERSTATE_ALERT. This occurs after its state has been set to dead.

As a result the monster's schedule code will keep going and continues running the AI as normal, only the damage logic will ignore some types of damage since it now treats the monster as a corpse:

halflife/dlls/combat.cpp

Lines 846 to 849 in c7240b9

if ( !IsAlive() )
{
return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}

halflife/dlls/combat.cpp

Lines 1004 to 1014 in c7240b9

if ( bitsDamageType & DMG_GIB_CORPSE )
{
if ( pev->health <= flDamage )
{
pev->health = -50;
Killed( pevAttacker, GIB_ALWAYS );
return 0;
}
// Accumulate corpse gibbing damage, so you can gib with multiple hits
pev->health -= flDamage * 0.1;
}

There doesn't appear to be any use of this feature in Half-Life.

There is one case of it happening in Opposing Force:
In the map of6a2 there are a few monstermaker entities that have empty target keyvalues. These spawn Shock Troopers with a small delay between them. If you kill the first Shock Trooper quickly enough the second monstermaker will trigger this Shock Trooper when it spawns its own monster, which triggers the MonsterUse method. The spawned monsters have no targetname which causes them to match an empty string when triggered.

No monsters will be brought back from the dead because Opposing Force has a workaround for this bug. CBaseMonster::ChangeSchedule has additional logic that prevents a monster from changing schedules if its last schedule ended with TASK_DIE. This isn't a proper fix since it only deals with the symptoms, not the cause.

For reference, in Source this method is a no-op:
https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/server/ai_basenpc.cpp#L4052-L4068

The return statement at the start causes all of the code in the method to be skipped.

It's a good idea to make this a no-op in the SDK, changing it in official games is risky because there could be maps that use the method as intended.

@SamVanheer
Copy link
Author

Looks like this is actually being used in Blue Shift. The map ba_yard4a has 2 frozen Alien Slaves that are supposed to get up to attack if you destroy the ice they're encased in. The slaves are in MONSTERSTATE_SCRIPT until triggered. Seems that this is intentional at least to that extent.

I'd recommend limiting this to the idle and script states like so:

//Only allow this if the NPC is either idle or in a script
switch (m_IdealMonsterState)
{
case MONSTERSTATE_IDLE:
case MONSTERSTATE_SCRIPT:
	m_IdealMonsterState = MONSTERSTATE_ALERT;
	break;
}

It would probably be better if they were broken out of their script by ending the script, but that would require potentially significant changes to the map script.

I'd still recommend disabling this method entirely in the SDK and requiring the use of normal script ending entity setups. Ideally killtargeting a scripted_sequence should produce the same result, but currently they don't clean up if they're killed. See #1747 for that problem.

@SamVanheer
Copy link
Author

The frozen Alien Slaves script can be modified so that instead of triggering the Alien Slaves the scripted_sequence entities holding them in their frozen animation should be triggered instead. This will also break them out of their script, only this time properly.

To change this the map can be ripented. Add targetnames to the scripted_sequence entities, then change the func_breakable entities that target the Alien Slaves to target the scripts instead.

@SamVanheer
Copy link
Author

Note that monstermaker spawns NPCs with a wait schedule which this use function is supposed to preempt. Removing the function altogether breaks a couple other scripts in maps like alien slaves that teleport in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant