Skip to content

bugfix: Detonated GLA Demo Battle Buses are no longer instantly deleted without firing their death weapons - #3264

Open
Stubbjax wants to merge 1 commit into
TheSuperHackers:mainfrom
Stubbjax:fix-unhandled-undead-body-deaths
Open

bugfix: Detonated GLA Demo Battle Buses are no longer instantly deleted without firing their death weapons#3264
Stubbjax wants to merge 1 commit into
TheSuperHackers:mainfrom
Stubbjax:fix-unhandled-undead-body-deaths

Conversation

@Stubbjax

@Stubbjax Stubbjax commented Sep 7, 2026

Copy link
Copy Markdown

This change fixes an issue where the GLA Demo Battle Bus would not trigger any death behaviours besides SlowDeathBehavior modules when initially killed via an unhandled death type. This resolves the issue where a SUICIDED death would instantly delete the Battle Bus without dealing any suicide damage if the Demolitions upgrade was researched.

Before

The Battle Bus deals no damage to surrounding enemies when suicided

BEFORE.mp4

After

The Battle Bus now deals damage to surrounding enemies when suicided

AFTER.mp4

@Stubbjax Stubbjax self-assigned this Sep 7, 2026
@Stubbjax Stubbjax added Buff Makes a thing more powerful Bug Something is not working right, typically is user facing GLA Affects GLA faction Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour NoRetail This fix or change is not applicable with Retail game compatibility labels Sep 7, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Ensure Battle Bus detonations trigger all death behaviors

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Distinguishes Battle Bus second-life transitions from normal slow-death selections.
• Routes normal selections through the full death pipeline, firing all configured behaviors.
• Stops second-life processing when damage side effects already killed the object.
Diagram

graph TD
  A["Lethal damage"] --> B["Undead body"] --> C["Active damage"] --> D{"Object dead?"}
  D -- Yes --> E["Stop processing"]
  D -- No --> F["Death selector"]
  F -- "Bus first death" --> G["Second life"]
  F -- "Real death" --> H["Die callbacks"]
Loading
High-Level Assessment

The polymorphic capability check is appropriate because the selected behavior knows whether it represents the Battle Bus transition or a real death. Re-entering ActiveBody's damage pipeline also preserves established health, scoring, and onDie dispatch semantics; duplicating those callbacks in UndeadBody would be riskier.

Files changed (3) +19 / -0

Bug fix (3) +19 / -0
BattleBusSlowDeathBehavior.hExpose Battle Bus death-transition state +1/-0

Expose Battle Bus death-transition state

• Overrides the new death classification method using the behavior's existing real-death state. This preserves the specialized first-death transition while allowing later deaths to use normal processing.

GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BattleBusSlowDeathBehavior.h

SlowDeathBehavior.hAdd real-death classification to slow-death behaviors +2/-0

Add real-death classification to slow-death behaviors

• Extends the slow-death interface with a real-death query. Standard slow-death behaviors default to real deaths so UndeadBody can route them through complete death handling.

GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlowDeathBehavior.h

UndeadBody.cppRoute unhandled second-life deaths through full death processing +16/-0

Route unhandled second-life deaths through full death processing

• Stops second-life handling if the initial damage application already killed the object. When selection resolves to a regular slow-death behavior, it forces death through ActiveBody so every onDie module—including Demolitions death weapons—executes.

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR corrects the UndeadBody death transition so genuine deaths execute the complete die-module lifecycle rather than entering only slow-death processing.

  • Adds an explicit distinction between genuine and Battle Bus transitional slow deaths.
  • Re-enters the base damage path for genuine deaths, allowing death weapons and other die modules to run.
  • Avoids starting a second life after the object has already become effectively dead.

Confidence Score: 5/5

The PR appears safe to merge, with the updated flow preserving transitional Battle Bus deaths while restoring complete processing for genuine deaths.

The effective-death guard distinguishes already-completed deaths from intercepted first-life damage, and the forced base damage call reaches Object::onDie without recursively re-entering UndeadBody second-life logic.

Important Files Changed

Filename Overview
GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BattleBusSlowDeathBehavior.h Exposes the existing Battle Bus real-death state through the new slow-death interface.
GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlowDeathBehavior.h Adds a real-death query with a default implementation inherited by the concrete slow-death hierarchy.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp Prevents second-life processing after completed deaths and routes genuine second-life selections through the full die-module lifecycle.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Lethal damage reaches UndeadBody] --> B[ActiveBody applies damage]
    B --> C{Object effectively dead?}
    C -->|Yes| D[Return; onDie lifecycle already handled]
    C -->|No| E[Initialize second life]
    E --> F[Select applicable SlowDeathBehavior]
    F --> G{Real death?}
    G -->|No: Battle Bus transition| H[Begin transitional slow death]
    G -->|Yes| I[Set forced-kill flag]
    I --> J[ActiveBody applies final death]
    J --> K[Object onDie invokes applicable die modules]
    K --> L[Death weapons and slow-death behavior execute]
Loading

Reviews (1): Last reviewed commit: "bugfix: Detonated GLA Demo Battle Buses ..." | Re-trigger Greptile

if (sdu->isRealDeath())
{
damageInfo->in.m_kill = true;
ActiveBody::attemptDamage(damageInfo);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I am confused by this implementation. The function says startSecondLife, so we now expect a second life with health restore, armor set change, ..., but here it will attempt the kill afterwards. Should this code perhaps be executed before this function is called? Aka shouldStartSecondLife is false when suicided?

The reason I am raising this is, the code reads confusing this way, regardless whether functionally it is working correctly in game.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This implementation follows the existing setup where a death type handled by a different SlowDeathBehavior (not BattleBusSlowDeathBehavior) would immediately kill the unit. Instead of manually calling the SlowDeathBehavior modules' death logic, they are now intrinsically called through the damage → death path, and only if there's no SlowDeathBehavior to handle it.

Explicitly checking for SUICIDED or any such death types is not as clean or flexible because a mod might use a custom setup where the death types are handled differently. For example, someone might make a zombie unit that immediately dies without rising again if it dies via a HOLY death type.

Perhaps the naming is the source of the confusion. We already have attemptDamage - would attemptSecondLife be more suitable?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It is not just the function name. Look at all the things that happen before the suicide:

m_isSecondLife = TRUE;

setMaxHealth(data->m_secondLifeMaxHealth, FULLY_HEAL);

setArmorSetFlag(ARMORSET_SECOND_LIFE);

So it fully prepares for a second life, but then it dies. How does that make sense?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Perhaps it does not make sense, but this is how the retail logic already works. Whether the original logic makes sense is secondary to this fix, which I intended to involve minimal refactoring for safety and simplicity. Would you prefer I move all of those lines below the death logic?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes I think the suicide kill should be before it decides to go to second life.

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

Labels

Buff Makes a thing more powerful Bug Something is not working right, typically is user facing GLA Affects GLA faction 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.

2 participants