Skip to content

refactor: Split preserve retail behavior flags#2691

Merged
xezon merged 7 commits intoTheSuperHackers:mainfrom
Stubbjax:split-preserve-retail-behavior-flags
May 8, 2026
Merged

refactor: Split preserve retail behavior flags#2691
xezon merged 7 commits intoTheSuperHackers:mainfrom
Stubbjax:split-preserve-retail-behavior-flags

Conversation

@Stubbjax
Copy link
Copy Markdown

@Stubbjax Stubbjax commented May 6, 2026

This change splits the PRESERVE_RETAIL_BEHAVIOR flag into a separate, dedicated flag for each fix. This allows publishers to more easily toggle controversial fixes without having to manually look through the code and figure out how particular fixes work or are implemented.

@Stubbjax Stubbjax self-assigned this May 6, 2026
@Stubbjax Stubbjax added Gen Relates to Generals ZH Relates to Zero Hour Refactor Edits the code with insignificant behavior changes, is never user facing labels May 6, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 6, 2026

Greptile Summary

This PR replaces the single PRESERVE_RETAIL_BEHAVIOR macro with 14 granular, per-fix PRESERVE_* flags, making it straightforward for publishers to toggle individual behavioral fixes without digging through source code. A search across the full repository confirms no remaining references to PRESERVE_RETAIL_BEHAVIOR; all 14 call sites are correctly re-pointed.

  • 14 new flags added to GameDefines.h: nine default to 1 (preserving prior retail behavior) and five default to 0 (enabling fixes approved by the Game Design Committee). The RETAIL_COMPATIBLE_CRC guard still forces the retail path for simulation-critical code wherever it was already present.
  • ALLOW_MONEY_PER_MINUTE_FOR_PLAYER (default 0) replaces the former !PRESERVE_RETAIL_BEHAVIOR guard in both GlobalData.cpp files; the INI key remains disabled by default, so runtime behavior is unchanged.
  • Both Generals/ and GeneralsMD/ (Zero Hour) trees are updated in lockstep, and PRESERVE_PREMATURE_BATTLE_BUS_DEATH / PRESERVE_STRUCTURE_STEALTH_DURING_REPAIR are correctly applied only where the respective game-specific code exists.

Confidence Score: 5/5

Safe to merge. The refactoring is a pure mechanical substitution with no missed call sites and no logic inversions.

Every former use of PRESERVE_RETAIL_BEHAVIOR has been replaced with its correctly named counterpart; a codebase-wide search returns zero remaining references. The five flags intentionally defaulted to 0 carry explicit Game Design Committee approval comments, and simulation-critical paths continue to be gated by RETAIL_COMPATIBLE_CRC wherever that guard already existed. The ALLOW_MONEY_PER_MINUTE_FOR_PLAYER rename preserves the same disabled-by-default runtime behavior as the previous !PRESERVE_RETAIL_BEHAVIOR expression.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/GameDefines.h Replaces the single PRESERVE_RETAIL_BEHAVIOR flag with 14 granular PRESERVE_* flags plus a new ALLOW_MONEY_PER_MINUTE_FOR_PLAYER flag; all defaults are logically consistent with the original behavior or intentionally changed with Game Design Committee approval.
Core/GameEngine/Source/GameClient/SelectionInfo.cpp Replaces PRESERVE_RETAIL_BEHAVIOR with PRESERVE_OCCUPANT_DETECTION_VIA_DRAG_SELECTION (default 1) in two guards; both #if expressions evaluate identically to before.
Generals/Code/GameEngine/Source/Common/GlobalData.cpp Replaces #if !PRESERVE_RETAIL_BEHAVIOR with #if ALLOW_MONEY_PER_MINUTE_FOR_PLAYER (default 0); the AllowMoneyPerMinuteForPlayer INI entry remains disabled by default, preserving prior behavior.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp Zero Hour-only file; swaps PRESERVE_RETAIL_BEHAVIOR for PRESERVE_PREMATURE_BATTLE_BUS_DEATH (default 1), keeping retail battle-bus death logic unchanged.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp Zero Hour-only file; swaps PRESERVE_RETAIL_BEHAVIOR for PRESERVE_STRUCTURE_STEALTH_DURING_REPAIR (default 0, GDC-approved fix); the healing-damage stealth guard is now disabled by default.
Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp Both trimOldHistoricDamage and processHistoricDamage guard expressions swap PRESERVE_RETAIL_BEHAVIOR for PRESERVE_UNRELIABLE_FIRESTORMS (default 0, GDC-approved); RETAIL_COMPATIBLE_CRC still forces the retail path when CRC compat is required.
Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/CrateCollide.cpp m_allowMultiPickup initialization now uses PRESERVE_MULTI_CRATE_PICKUP (default 0, GDC-approved); multi-pickup is disabled by default.

Reviews (4): Last reviewed commit: "refactor: Order flags lexicographically" | Re-trigger Greptile

Comment thread Core/GameEngine/Source/GameClient/SelectionInfo.cpp
@Mauller
Copy link
Copy Markdown

Mauller commented May 6, 2026

I think it would be better to split these defines out into their own file of GameplayFixDefines or something similar

But keep preserve retail behaviour as an encompassing switch.

Comment thread Core/GameEngine/Include/Common/GameDefines.h Outdated
Comment thread Core/GameEngine/Include/Common/GameDefines.h Outdated
@Stubbjax
Copy link
Copy Markdown
Author

Stubbjax commented May 7, 2026

I think it would be better to split these defines out into their own file of GameplayFixDefines or something similar

But keep preserve retail behaviour as an encompassing switch.

How useful do you think a master flag would be? Does it outweigh the additional maintenance burden?

Comment thread Core/GameEngine/Include/Common/GameDefines.h Outdated
@L3-M
Copy link
Copy Markdown

L3-M commented May 7, 2026

I think after this change, we need to discuss what the workflow would be for controversial features or bug fixes that alter retail behaviour. Do we keep the default as what the author and reviewer think is correct? Or keep the original retail experience as default? Or wait for committee decision and input and implement that as the default?

The cons of the first and third options are that balance changes a lot, has many talks, takes time and many people will disagree.

Over time. This repo was for building the game, not publishing and shipping it. I think it is better to keep the retail behaviour as the default. And let the publishers decide what to enable or not, and whether to follow the committee. This will be helpful too for modders who prefer the retail experience for their mods.

@githubawn
Copy link
Copy Markdown

Much more fun to write it this way:

#ifndef _MSC_VER
    { "AllowMoney", INI::parseBool, nullptr, offsetof(GlobalData, m_allowMoney) },
#elif _MSC_VER >= 1930
    { "DoSomething", INI::parseBool, nullptr, offsetof(GlobalData, m_DoSomething) },
#endif

This _MSC_VER Macro is very powerful, it symbolizes a specific match, one frozen in time. None may touch this. :)

We have the long rumored Retail 1.0 someday. With this Macro style spread across the codebase we might even get to play with the classic Fork with he new tools as well. Like Tracy/SDL3 getting in nowadays.

@xezon
Copy link
Copy Markdown

xezon commented May 7, 2026

I think after this change, we need to discuss what the workflow would be for controversial features or bug fixes that alter retail behaviour. Do we keep the default as what the author and reviewer think is correct? Or keep the original retail experience as default? Or wait for committee decision and input and implement that as the default?

Generally, controversial code changes are default disabled first, but default enabled when approved by the Design Committee.

Over time. This repo was for building the game, not publishing and shipping it. I think it is better to keep the retail behaviour as the default.

Right now, we set defaults for 2 Products:

  1. Retail compatible Community Patch (RETAIL_COMPATIBLE_CRC=1; produces Weekly releases on GitHub)
  2. Community Patch for Generals Online Client (is integrated once in a while)

And let the publishers decide what to enable or not, and whether to follow the committee. This will be helpful too for modders who prefer the retail experience for their mods.

This is already possible. Publishers can override the individual defines of GameDefines.h, without even touching GameDefines.h

Comment thread Core/GameEngine/Include/Common/GameDefines.h Outdated
@xezon
Copy link
Copy Markdown

xezon commented May 7, 2026

Does the title need to clarify that some setups were tweaked?

@Stubbjax
Copy link
Copy Markdown
Author

Stubbjax commented May 7, 2026

Does the title need to clarify that some setups were tweaked?

Could it be in the extended description?

@xezon xezon merged commit 9f0d2fb into TheSuperHackers:main May 8, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gen Relates to Generals Refactor Edits the code with insignificant behavior changes, is never user facing ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants