Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Core/GameEngine/Include/Common/ThingTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ class ThingTemplate : public Overridable
const ModuleInfo& getClientUpdateModuleInfo() const { return m_clientUpdateModuleInfo; }

const Image *getSelectedPortraitImage() const { return m_selectedPortraitImage; }
const Image *getButtonImage() const { return m_buttonImage; }
const Image *getButtonImage() const { return ((const ThingTemplate*)getFinalOverride())->m_buttonImage; }

//Code renderer handles these states now.
//const AsciiString& getInventoryImageName( InventoryImageType type ) const { return m_inventoryImage[ type ]; }
Expand Down
14 changes: 10 additions & 4 deletions Core/GameEngine/Include/GameClient/ControlBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,13 @@ class CommandButton : public Overridable
Int getMaxShotsToFire() const { return m_maxShotsToFire; }
const ScienceVec& getScienceVec() const { return m_science; }
CommandButtonMappedBorderType getCommandButtonMappedBorderType() const { return m_commandButtonBorder; }
const Image* getButtonImage() const { return m_buttonImage; }
const Image *getButtonImage() const
{
CommandButton* finalOverride = (CommandButton*)getFinalOverride();
if (finalOverride->m_buttonImageName.isNotEmpty())
const_cast<CommandButton*>(finalOverride)->cacheButtonImage();
return finalOverride->m_buttonImage;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

rg -n -C 5 'copyImagesFrom\(|getButtonImage\(|getFinalOverride\(' Core/GameEngine/Source/GameClient/GUI/ControlBar Core/GameEngine/Include/GameClient/ControlBar.h

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 25663


🏁 Script executed:

set -eu
printf '%s\n' '--- shortcut branches in ControlBarCommand.cpp ---'
sed -n '360,475p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp
printf '%s\n' '--- shortcut branches in ControlBar.cpp ---'
sed -n '3400,3520p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
printf '%s\n' '--- CommandButton override declarations and helpers ---'
rg -n -C 8 --glob '*.[ch]' -- 'getFinalOverride\(|friend_getFinalOverride|m_override|override' Core/GameEngine/Include/GameClient/ControlBar.h Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp | sed -n '1,260p'
printf '%s\n' '--- getter diff against merge base ---'
git diff 1b1212f0de1bab9997f86bb1fa7e3c9cb2815674 45d8e2011617b3831e44733d53501b63209ec5f6 -- Core/GameEngine/Include/GameClient/ControlBar.h Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 23377


🏁 Script executed:

set -eu
printf '%s\n' '--- ControlBarCommand.cpp enclosing shortcut function ---'
sed -n '1,390p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp
printf '%s\n' '--- ControlBar.cpp enclosing shortcut function ---'
sed -n '3280,3425p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
printf '%s\n' '--- all getFinalOverride declarations and definitions ---'
rg -n -C 6 --glob '*.{h,hpp,c,cpp,inc}' -- 'getFinalOverride|friend_getFinalOverride|setNextOverride|markAsOverride' Core | sed -n '1,360p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 44644


🏁 Script executed:

set -eu
printf '%s\n' '--- CommandButton and CommandSet declarations ---'
rg -n -C 20 --glob '*.{h,hpp}' -- 'class CommandButton|class CommandSet|getCommandButton\(' Core/GameEngine/Include Core/GameClient/Include 2>/dev/null | sed -n '1,360p'
printf '%s\n' '--- getCommandButton definitions and callers ---'
rg -n -C 10 --glob '*.{c,cpp,h,hpp}' -- 'CommandSet::getCommandButton|getCommandButton\( *Int|getCommandButton\( *[a-zA-Z_]+ *\)' Core | sed -n '1,360p'
printf '%s\n' '--- override base declarations ---'
rg -n -C 12 --glob '*.{h,hpp}' -- 'friend_getFinalOverride|setNextOverride|markAsOverride|class Overridable|Overridable' Core/GameEngine/Include Core/GameClient/Include 2>/dev/null | sed -n '1,360p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 41651


🏁 Script executed:

set -eu
printf '%s\n' '--- exact CommandSet::getCommandButton ---'
sed -n '820,870p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
printf '%s\n' '--- findControlBarOverride declarations and definitions ---'
rg -n -C 14 --glob '*.{h,hpp,c,cpp}' -- 'findControlBarOverride' Core | sed -n '1,300p'
printf '%s\n' '--- Overridable files and methods ---'
rg --files | rg '(^|/)(Overridable|Override)\.(h|hpp|c|cpp)$'
rg -n -C 12 --glob '*.{h,hpp,c,cpp}' -- 'class Overridable|Overridable::|getFinalOverride\(' . | sed -n '1,320p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 36858


🏁 Script executed:

set -eu
printf '%s\n' '--- all tracked findControlBarOverride references ---'
git grep -n -C 10 -i -- 'findControlBarOverride' || true
printf '%s\n' '--- command-button and command-set override creation/parsing ---'
sed -n '1938,2025p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
sed -n '1,180p' Core/GameEngine/Source/Common/INI/INICommandButton.cpp
sed -n '1,180p' Core/GameEngine/Source/Common/INI/INICommandSet.cpp 2>/dev/null || true
git grep -n -C 8 -- 'newCommandButtonOverride\|newCommandSetOverride\|parseCommandButton\|setNextOverride' -- Core/GameEngine/Source Core/GameEngine/Include | sed -n '1,360p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 41501


🏁 Script executed:

set -eu
printf '%s\n' '--- setControlBarOverride callers and lifecycle ---'
git grep -n -C 12 -- 'setControlBarOverride' -- Core Generals GeneralsMD | sed -n '1,360p'
printf '%s\n' '--- command-set lookup and override chain callers ---'
git grep -n -C 8 -- 'findCommandSet(' -- Core/GameEngine/Source/GameClient/GUI/ControlBar Core/GameEngine/Source/GameLogic | sed -n '1,300p'
printf '%s\n' '--- relevant INI load ordering references ---'
git grep -n -C 8 -- 'parseCommandButtonDefinition\|parseCommandSetDefinition\|INI_LOAD_CREATE_OVERRIDES' -- Core/GameEngine/Source/Common Core/GameEngine/Source/GameClient/GUI/ControlBar | sed -n '1,360p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 41713


🏁 Script executed:

set -eu
printf '%s\n' '--- map/solo INI loading sequence ---'
git grep -n -C 12 -E 'solo\.ini|map\.ini|load.*INI|INI_LOAD_CREATE_OVERRIDES' -- Core/GameEngine/Source/GameLogic Core/GameEngine/Source/GameClient Core/GameEngine/Source/Common | sed -n '1,420p'
printf '%s\n' '--- exact shortcut setup and consumer flow ---'
rg -n -C 18 -- 'populateSpecialPowerShortcut\(|setControlCommand\(|commandButton = commandSet->getCommandButton|copyImagesFrom\( command' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp | sed -n '1,420p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 41664


🏁 Script executed:

set -eu
sed -n '748,792p' Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 1791


Write copied images to the final command-button override.

When a command-set slot still points to the base button and a later map/solo override exists, the special-power shortcut updates the base image with copyImagesFrom(). getButtonImage() then reads the final override image. The shortcut can display the old override image instead of the science-selected image.

Resolve the receiver to its final override inside copyImagesFrom() and preserve the existing dirty-state behavior.

Suggested fix
 void CommandButton::copyImagesFrom( const CommandButton *button, Bool markUIDirtyIfChanged ) const
 {
-	if( m_buttonImage != button->getButtonImage() )
+	CommandButton* finalOverride = (CommandButton*)getFinalOverride();
+	if( finalOverride->m_buttonImage != button->getButtonImage() )
 	{
-		m_buttonImage = button->getButtonImage();
+		finalOverride->m_buttonImage = button->getButtonImage();
 
 		//Code renderer handles these states now.

}
void cacheButtonImage();

GameWindow* getWindow() const { return m_window; }
Expand Down Expand Up @@ -776,6 +782,9 @@ class ControlBar : public SubsystemInterface
static void parseCommandSetDefinition( INI *ini );
static void parseCommandButtonDefinition( INI *ini );

/// post process step, after all commands and command sets are loaded
void postProcessCommands();

void drawTransitionHandler();
const Image *getArrowImage(){ return m_genArrow; }
void setArrowImage( const Image *arrowImage ){ m_genArrow = arrowImage; }
Expand Down Expand Up @@ -828,9 +837,6 @@ class ControlBar : public SubsystemInterface
/// show rally point at world location, a nullptr location will hide any visible rally point marker
void showRallyPoint( const Coord3D *loc );

/// post process step, after all commands and command sets are loaded
void postProcessCommands();

// the following methods are for resetting data for various contexts
void resetCommonCommandData(); /// reset shared command data
void resetContainData(); /// reset container data we use to tie controls to objects IDs for containment
Expand Down
13 changes: 11 additions & 2 deletions Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,9 +751,10 @@ Bool CommandButton::isContextCommand() const
// bleah. shouldn't be const, but is. sue me. (srj)
void CommandButton::copyImagesFrom( const CommandButton *button, Bool markUIDirtyIfChanged ) const
{
if( m_buttonImage != button->getButtonImage() )
CommandButton *finalOverride = (CommandButton*)getFinalOverride();
if (finalOverride->m_buttonImage != button->getButtonImage())
{
m_buttonImage = button->getButtonImage();
finalOverride->m_buttonImage = button->getButtonImage();

//Code renderer handles these states now.
//m_disabledImage = button->getDisabledImage();
Expand Down Expand Up @@ -2538,6 +2539,14 @@ void ControlBar::setControlCommand( GameWindow *button, const CommandButton *com
//-------------------------------------------------------------------------------------------------
void CommandButton::cacheButtonImage()
{
// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Use the ButtonImage from the final map.ini command button override.
CommandButton *finalOverride = (CommandButton*)friend_getFinalOverride();
if (finalOverride != this)
{
finalOverride->cacheButtonImage();
return;
}

if (!TheMappedImageCollection) {
return;
}
Expand Down
6 changes: 4 additions & 2 deletions Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ void UpgradeTemplate::cacheButtonImage()
{
m_buttonImage = TheMappedImageCollection->findImageByName( m_buttonImageName );
DEBUG_ASSERTCRASH( m_buttonImage, ("UpgradeTemplate: %s is looking for button image %s but can't find it. Skipping...", m_name.str(), m_buttonImageName.str() ) );
m_buttonImageName.clear(); // we're done with this, so nuke it
// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Resolves references in map object overrides.
if (m_buttonImage)
m_buttonImageName.clear(); // we're done with this, so nuke it
}
}

Expand Down Expand Up @@ -281,7 +283,7 @@ void UpgradeCenter::init()
//-------------------------------------------------------------------------------------------------
void UpgradeCenter::reset()
{
if( TheMappedImageCollection && !buttonImagesCached )
if (TheMappedImageCollection)
{
UpgradeTemplate *upgrade;
for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() )
Expand Down
6 changes: 6 additions & 0 deletions Generals/Code/GameEngine/Source/Common/Thing/ThingFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ AsciiString TheThingTemplateBeingParsedName;

thingTemplate->validate();

// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Resolves references in map object overrides.
if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
{
thingTemplate->resolveNames();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Image changes block unit production

In Generals, this new resolveNames() call runs again on copied prerequisites. Their names were cleared during the first load, but ProductionPrerequisite::resolveNames() still looks up those empty names and replaces the saved pointers with nullptr. An image-only override can therefore lose its required buildings and leave the unit unavailable to build.

Add the empty-name guard already used in Zero Hour before calling this method again.

Comment on lines +434 to +436

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Later unit images stay blank

In Generals, this call resolves each object before later definitions in map.ini or solo.ini have loaded. If its ButtonImage names a MappedImage defined later, ThingTemplate::resolveNames() stores the failed lookup and clears the name. Loading the image afterward cannot repair the blank unit icon.

Resolve object images after both files load, or keep failed names and retry them then.

Knowledge Base Used: RTS engine core

}

Comment on lines +433 to +438

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

rg -n "ThingTemplate::resolveNames|resolveNames\\(|postProcessLoad|addTemplate|add.*Template|m_thingTemplate" Generals/Code/GameEngine/Source/Common Generals/Code/GameEngine/Include/Common
sed -n '400,455p' Generals/Code/GameEngine/Source/Common/Thing/ThingFactory.cpp
sed -n '500,550p' Generals/Code/GameEngine/Source/Common/Thing/ThingFactory.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 5326


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- ThingFactory registration and override creation ---'
sed -n '1,220p' Generals/Code/GameEngine/Source/Common/Thing/ThingFactory.cpp
printf '%s\n' '--- ThingFactory load/post-process block ---'
sed -n '300,555p' Generals/Code/GameEngine/Source/Common/Thing/ThingFactory.cpp
printf '%s\n' '--- ThingTemplate declarations and resolveNames references ---'
rg -n -F -- 'resolveNames' Generals/Code/GameEngine/Source/Common/Thing Generals/Code/GameEngine/Include/Common/Thing
rg -n -F -- 'class ThingTemplate' Generals/Code/GameEngine/Include/Common
printf '%s\n' '--- resolveNames definition context ---'
rg -n -F -- 'void ThingTemplate::resolveNames' Generals/Code/GameEngine
printf '%s\n' '--- template list and override-related declarations ---'
rg -n -F -e 'friend_getNextTemplate' -e 'newOverride' -e 'addTemplate' -e 'm_firstTemplate' Generals/Code/GameEngine/Include/Common Generals/Code/GameEngine/Source/Common/Thing

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 20626


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- locate ThingTemplate source/header ---'
rg --files Generals/Code/GameEngine | rg '(^|/)(ThingTemplate|ThingFactory)\.(h|cpp)$'
printf '%s\n' '--- locate definitions and declarations ---'
rg -n -F --glob '*.{h,cpp}' -e 'ThingTemplate::resolveNames' -e 'resolveNames()' -e 'friend_getFinalOverride' -e 'setNextOverride' -e 'getFinalOverride' Generals/Code/GameEngine || test "$?" -eq 1
printf '%s\n' '--- inspect candidate ThingTemplate files ---'
for f in $(rg --files Generals/Code/GameEngine | rg '(^|/)(ThingTemplate)\.(h|cpp)$'); do
  echo "### $f"
  rg -n -F -e 'resolveNames' -e 'm_override' -e 'm_nextOverride' -e 'friend_getFinalOverride' -e 'setNextOverride' "$f" || true
done
printf '%s\n' '--- inspect all matching resolveNames definitions with context ---'
while IFS=: read -r f n rest; do
  [ -n "$f" ] || continue
  start=$((n-35)); [ "$start" -lt 1 ] && start=1
  end=$((n+90))
  sed -n "${start},${end}p" "$f"
done < <(rg -n -F --glob '*.cpp' -- 'ThingTemplate::resolveNames' Generals/Code/GameEngine || true)

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 4397


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- full repository ThingTemplate search ---'
rg -n -F --hidden --glob '!/.git/**' -e 'class ThingTemplate' -e 'ThingTemplate::' -e 'resolveNames' . || test "$?" -eq 1
printf '%s\n' '--- files containing ThingTemplate-like declarations ---'
rg -l --hidden --glob '!/.git/**' 'ThingTemplate' . | sed -n '1,160p'
printf '%s\n' '--- build/include references to ThingTemplate headers ---'
rg -n --hidden --glob '!/.git/**' -e 'ThingTemplate.h' -e 'ThingTemplate.hpp' -e 'ThingTemplate' Generals/Code | sed -n '1,220p'
printf '%s\n' '--- repository layout around Common headers and source ---'
find Generals/Code -maxdepth 5 -type f | rg '/(Thing|Template|Object|Overridable)' | sed -n '1,220p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 41701


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- ThingTemplate declaration and reference fields ---'
sed -n '320,470p' Core/GameEngine/Include/Common/ThingTemplate.h
printf '%s\n' '--- ThingTemplate resolveNames implementation ---'
sed -n '1235,1335p' Core/GameEngine/Source/Common/Thing/ThingTemplate.cpp
printf '%s\n' '--- related prerequisite resolution implementation ---'
sed -n '1,125p' Generals/Code/GameEngine/Source/Common/RTS/ProductionPrerequisite.cpp
printf '%s\n' '--- override traversal semantics ---'
sed -n '1,125p' Generals/Code/GameEngine/Include/Common/Overridable.h
printf '%s\n' '--- all postProcessLoad and load-type callers in relevant trees ---'
rg -n -F --glob '*.cpp' -e 'postProcessLoad()' -e 'INI_LOAD_CREATE_OVERRIDES' -e 'parseObjectDefinition' Core/GameEngine Generals/Code/GameEngine | sed -n '1,260p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 23979


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- ThingFactory lookup and reset semantics ---'
sed -n '200,310p' Generals/Code/GameEngine/Source/Common/Thing/ThingFactory.cpp
printf '%s\n' '--- complete resolveNames remainder ---'
sed -n '1280,1350p' Core/GameEngine/Source/Common/Thing/Thing/ThingTemplate.cpp 2>/dev/null || sed -n '1280,1350p' Core/GameEngine/Source/Common/Thing/ThingTemplate.cpp
printf '%s\n' '--- map override load entrypoints ---'
sed -n '2140,2230p' Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
printf '%s\n' '--- subsystem post-process ordering ---'
sed -n '130,190p' Core/GameEngine/Source/Common/System/SubsystemInterface.cpp
printf '%s\n' '--- ThingFactory registration declaration and lookup declaration ---'
sed -n '45,135p' Generals/Code/GameEngine/Include/Common/ThingFactory.h

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 16043


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- prerequisite parsing and override behavior ---'
sed -n '650,725p' Core/GameEngine/Source/Common/Thing/ThingTemplate.cpp
sed -n '130,180p' Core/GameEngine/Source/Common/Thing/ThingTemplate.cpp
printf '%s\n' '--- loadMapINI callers and nearby lifecycle ---'
rg -n -F --glob '*.cpp' -e 'loadMapINI(' -e 'postProcessLoadAll(' Generals/Code/GameEngine Core/GameEngine | sed -n '1,160p'
printf '%s\n' '--- prerequisite consumption ---'
rg -n -F --glob '*.cpp' -e 'm_prereqUnits' -e 'calcNumPrereqUnitsOwned' -e 'getAllPossibleBuildFacilityTemplates' Generals/Code/GameEngine Core/GameEngine | sed -n '1,220p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 11559


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- initial post-process and map-load ordering ---'
sed -n '480,525p' Generals/Code/GameEngine/Source/Common/GameEngine.cpp
sed -n '1145,1200p' Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
printf '%s\n' '--- prerequisite consumer block ---'
sed -n '120,180p' Generals/Code/GameEngine/Source/Common/RTS/ProductionPrerequisite.cpp
printf '%s\n' '--- exact map load function with line numbers ---'
nl -ba Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | sed -n '2150,2220p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 8925


Defer resolveNames() until all map definitions are registered.

When an existing object override contains a prerequisite for a new object declared later in map.ini or solo.ini, the immediate call cannot find that object. ProductionPrerequisite::resolveNames() then clears the unresolved name. The override is hidden from ThingFactory::postProcessLoad(), and the global post-process runs before loadMapINI(), so the reference remains null and the prerequisite cannot match the later map-defined object.

Suggested fix
-	// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Resolves references in map object overrides.
-	if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
-	{
-		thingTemplate->resolveNames();
-	}
-
 // go through all thing templates
 for( ThingTemplate *thingTemplate = m_firstTemplate;
 		 thingTemplate;
 		 thingTemplate = thingTemplate->friend_getNextTemplate() )
 {
 
 	// resolve the prerequisite names
-	thingTemplate->resolveNames();
+	static_cast<ThingTemplate*>(thingTemplate->friend_getFinalOverride())->resolveNames();
 	if (TheFileSystem->doesFileExist(fullFledgeFilename)) {
 		DEBUG_LOG(("Loading solo.ini"));
 		INI ini;
 		ini.load( AsciiString(fullFledgeFilename), INI_LOAD_CREATE_OVERRIDES, nullptr );
 	}
 
+	TheThingFactory->postProcessLoad();
+
 	if (TheControlBar)
 		TheControlBar->postProcessCommands();

#if defined(RTS_DEBUG) || defined(DEBUG_CRASHING)
TheThingTemplateBeingParsedName.clear();
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2205,6 +2205,10 @@ void GameLogic::loadMapINI( AsciiString mapName )
ini.load( AsciiString(fullFledgeFilename), INI_LOAD_CREATE_OVERRIDES, nullptr );
}

if (TheControlBar)
TheControlBar->postProcessCommands();
TheUpgradeCenter->reset();

// No error here. There could've just *not* been a map.ini file.

// now look for a string file
Expand Down
8 changes: 7 additions & 1 deletion GeneralsMD/Code/GameEngine/Source/Common/System/Upgrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ void UpgradeTemplate::cacheButtonImage()
{
m_buttonImage = TheMappedImageCollection->findImageByName( m_buttonImageName );
DEBUG_ASSERTCRASH( m_buttonImage, ("UpgradeTemplate: %s is looking for button image %s but can't find it. Skipping...", m_name.str(), m_buttonImageName.str() ) );
m_buttonImageName.clear(); // we're done with this, so nuke it
// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Retains unresolved button images for a later cache pass.
if( m_buttonImage )
m_buttonImageName.clear(); // we're done with this, so nuke it
}
}

Expand Down Expand Up @@ -497,5 +499,9 @@ void UpgradeCenter::parseUpgradeDefinition( INI *ini )
// parse the ini definition
ini->initFromINI( upgrade, upgrade->getFieldParse() );

// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Keeps map.ini upgrade cameos scoped to the map override lifetime.
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
upgrade->cacheButtonImage();

}

Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ AsciiString TheThingTemplateBeingParsedName;

thingTemplate->validate();

// TheSuperHackers @bugfix DrGoldFish 14/10/2026 Resolves references in map object overrides.
if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
{
thingTemplate->resolveNames();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2510,6 +2510,10 @@ void GameLogic::loadMapINI( AsciiString mapName )
ini.load( AsciiString(fullFledgeFilename), INI_LOAD_CREATE_OVERRIDES, nullptr );
}

if (TheControlBar)
TheControlBar->postProcessCommands();
TheUpgradeCenter->reset();

// No error here. There could've just *not* been a map.ini file.

// now look for a string file
Expand Down
Loading