Skip to content

bugfix: Occupants killed by their containers now kill their occupants#2239

Open
Stubbjax wants to merge 5 commits intoTheSuperHackers:mainfrom
Stubbjax:apply-occupant-damage-recursively
Open

bugfix: Occupants killed by their containers now kill their occupants#2239
Stubbjax wants to merge 5 commits intoTheSuperHackers:mainfrom
Stubbjax:apply-occupant-damage-recursively

Conversation

@Stubbjax
Copy link

@Stubbjax Stubbjax commented Feb 2, 2026

Closes #184

This change fixes an issue where occupants killed via the destruction of their container do not kill their occupants, and so on. For example, if a Chinook containing a Troop Crawler dies, the Red Guards inside the Troop Crawler survive and fall to the ground, which looks silly and can leave them floating in the air.

Before

A Troop Crawler dies inside a Chinook, but its occupants survive and fall to the ground or float in the air

YES_SURVIVORS.mp4

After

A Troop Crawler dies inside a Chinook, and kills its own occupants as a result

NO_SURVIVORS.mp4

@Stubbjax Stubbjax self-assigned this Feb 2, 2026
@Stubbjax Stubbjax added Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour NoRetail This fix or change is not applicable with Retail game compatibility labels Feb 2, 2026
@greptile-apps
Copy link

greptile-apps bot commented Feb 2, 2026

Greptile Summary

This PR fixes a recursive container death bug where occupants inside nested containers (e.g., Red Guards inside a Troop Crawler inside a Chinook) would survive when the outer container died.

Key Changes:

  • Added percentDamage parameter to processDamageToContained() to enable consistent damage propagation through nested containers
  • Reordered operations in onDie() for non-retail builds to call killRidersWhoAreNotFreeToExit() before processDamageToContained(), ensuring proper handling of nested containers
  • Added DISABLED_HELD check in isSpecificRiderFreeToExit() to prevent nested containers from exiting when their parent container is disabled
  • Introduced specific death types (DEATH_BURNED vs DEATH_NORMAL) to prevent infantry corpses from dropping out of dying containers

The fix works by ensuring that when a container dies and kills its occupants, those occupants' own onDie() methods are triggered, which then recursively kill their own occupants through the callback chain.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are well-structured and address the reported bug correctly. The logic uses conditional compilation to maintain retail compatibility, the parameter passing is consistent across all implementations, and the recursive death chain through onDie() callbacks ensures nested occupants are properly handled. Previous review comments have been addressed.
  • No files require special attention

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp Reordered operations in onDie() and updated processDamageToContained() to use parameter instead of module data
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp Added death type specification and DISABLED_HELD check for nested containers
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp Reordered operations in onDie() and introduced killContained boolean for clearer logic
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp Added death type specification and DISABLED_HELD check for nested containers

Last reviewed commit: 93adcf7

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 2, 2026

Additional Comments (1)

Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Should use percentDamage parameter instead of data->m_damagePercentageToUnits to match the GeneralsMD implementation and ensure nested containers receive the correct damage value.

		Real damage = object->getBodyModule()->getMaxHealth() * percentDamage;
Prompt To Fix With AI
This is a comment left during a code review.
Path: Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Line: 1307:1307

Comment:
Should use `percentDamage` parameter instead of `data->m_damagePercentageToUnits` to match the GeneralsMD implementation and ensure nested containers receive the correct damage value.

```suggestion
		Real damage = object->getBodyModule()->getMaxHealth() * percentDamage;
```

How can I resolve this? If you propose a fix, please make it concise.

@Stubbjax Stubbjax force-pushed the apply-occupant-damage-recursively branch from 61f80fa to b1aaa96 Compare February 2, 2026 14:32
@Stubbjax Stubbjax force-pushed the apply-occupant-damage-recursively branch from b1aaa96 to 05dc39e Compare February 2, 2026 14:35
@xezon
Copy link

xezon commented Feb 2, 2026

Does this also happen with Troopcrawler in helix?

@Stubbjax
Copy link
Author

Stubbjax commented Feb 3, 2026

Does this also happen with Troopcrawler in helix?

A Troop Crawler takes up eight slots and thus cannot fit inside a Helix. But we can demonstrate the same behaviour with a Helix by loading an Ambulance or Technical.

AMBO_LIX.mp4

Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change will need to be Merged With Rebase, because the unify commit needs to be kept separate. The commit titles need to be fit to standard.

@@ -1364,8 +1359,16 @@ void OpenContain::processDamageToContained()

DEBUG_ASSERTCRASH( object, ("Contain list must not contain null element") );

// TheSuperHackers @bugfix Stubbjax 02/02/2026 If the parent container kills its occupants
// on death, then those occupants also kill their occupants, and so on.
if (killContained)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this condition correct? What if the damage dealt to occupants is just 0.5? Shouldn't that also transfer the damage?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? If a Humvee could be stored inside an Overlord, and the Overlord was destroyed, would it make sense to apply that 0.5 damage to the Humvee's passengers in addition to the Humvee itself? There is no precedent for this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes if the health of the contained transport is 0.4 and the damage is 0.5, then we want to kill the passengers of the transport as well right? Or will they be killed anyway? If they are, then it begs the question why is the damage applied in that event but not when applying 1.0 damage?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon further investigation, it turns out that the DamagePercentToUnits defined in the Chinook / Helix TransportContain modules is a red herring. Setting either of these fields to a value < 100% still results in the contained units dying, which is due to a call to killRidersWhoAreNotFreeToExit (though this causes the infantry corpses to drop out like vehicles do due to the lack of the burned death type via this logic path).

Only the direct occupants of airborne containers are considered not FREE_TO_EXIT (see ChinookAIUpdate::getAiFreeToExit), which prevents the killRidersWhoAreNotFreeToExit logic from triggering for successive occupants of contained transports such as those inside contained Troop Crawlers. It would seem killRidersWhoAreNotFreeToExit was never called in retail anyway due to the DamagePercentToUnits applying first, which is always set to 100% for airborne containers (where the FREE_TO_EXIT status applies).

I've solved this by returning false if the parent container has the held status in the isSpecificRiderFreeToExit function. I've also swapped the order of killRidersWhoAreNotFreeToExit and processDamageToContained, which avoids calling redundant logic in cases where riders are not FREE_TO_EXIT and DamagePercentToUnits < 100%.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Infantry don't die in some transports when transports are in the Chinook

2 participants