Skip to content

unify(worldbuilder): Align PolygonTrigger serialization and editor prerequisites - #3024

Open
OmarAglan wants to merge 1 commit into
TheSuperHackers:mainfrom
OmarAglan:unify/worldbuilder-prerequisites
Open

unify(worldbuilder): Align PolygonTrigger serialization and editor prerequisites#3024
OmarAglan wants to merge 1 commit into
TheSuperHackers:mainfrom
OmarAglan:unify/worldbuilder-prerequisites

Conversation

@OmarAglan

@OmarAglan OmarAglan commented Jul 28, 2026

Copy link
Copy Markdown

Merge with Rebase

Zero Hour's WorldBuilder expects a few engine features that Generals does not currently expose. Keeping these prerequisites separate avoids hiding engine changes inside the larger WorldBuilder merge.

This pull adds only the required prerequisites outside WorldBuilder:

  • Polygon triggers gain layer names and the selection/render state used by the editor.
  • Both games can read PolygonTriggers versions 1 through 4.
  • RETAIL_COMPATIBLE_WORLDBUILDER is enabled by default. Generals therefore writes retail-compatible version 3 trigger data, while Zero Hour continues writing version 4 data with layer names.
  • Disabling the compatibility macro allows Generals to write version 4 data with layer names as well.
  • ThingTemplate exposes the vision, ambient sound, and weapon information used by WorldBuilder.
  • The ambient-sound map-object keys used by the editor are registered in Generals.

No WorldBuilder files are changed here. The follow-up merge remains focused on the editor implementation itself.

Testing

  • git diff --check
  • Confirmed the PolygonTrigger writer implementation matches in Generals and Zero Hour
  • Confirmed the pull changes only seven prerequisite files outside WorldBuilder
  • Confirmed the branch contains one commit against main
  • Generals WorldBuilder runs successfully in local testing

Comment thread Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
Comment thread Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp Outdated
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

Adds the engine prerequisites needed to unify WorldBuilder while preserving retail Generals map compatibility.

  • Adds version-4 polygon-trigger layer-name support to both game variants.
  • Keeps Generals output on trigger version 3 by default while Zero Hour writes version 4.
  • Retains malformed triggers for save restoration but excludes them from named gameplay-area lookup.
  • Exposes WorldBuilder-required ThingTemplate data and registers ambient-sound property keys in Generals.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/GameDefines.h Defines retail-compatible WorldBuilder output as the default shared configuration.
Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp Adds version-4 layer-name parsing and conditional version-3/version-4 writing while preserving invalid triggers for snapshot compatibility.
Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp Prevents malformed retained triggers from being returned as named gameplay areas.
GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp Retains malformed triggers and aligns conditional writer behavior with the Generals implementation.
GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp Applies the same valid-area lookup restriction to Zero Hour.
Generals/Code/GameEngine/Include/Common/ThingTemplate.h Exposes vision, ambient-sound, and weapon-template information required by WorldBuilder.
Generals/Code/GameEngine/Include/Common/WellKnownKeys.h Registers the ambient-sound map-object properties consumed by the editor.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Map["PolygonTriggers map chunk"] --> Reader["Version-aware reader"]
    Reader --> TriggerList["Complete trigger list"]
    TriggerList --> Snapshot["Save/snapshot restoration by ID"]
    TriggerList --> Lookup["Named gameplay-area lookup"]
    Lookup --> Valid{"At least two points?"}
    Valid -->|Yes| Gameplay["Scripts and AI"]
    Valid -->|No| Hidden["Retained only for compatibility"]
    Writer["Map writer"] --> Target{"Build target and compatibility mode"}
    Target -->|Retail-compatible Generals| V3["Version 3 without layer names"]
    Target -->|Zero Hour or compatibility disabled| V4["Version 4 with layer names"]
Loading

Reviews (7): Last reviewed commit: "unify(worldbuilder): Add Generals compat..." | Re-trigger Greptile

@Skyaero42 Skyaero42 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This will make the Generals World builder fail loading maps.

@OmarAglan

Copy link
Copy Markdown
Author

This will make the Generals World builder fail loading maps.

Can you explain why?
I will try to fix it.

@OmarAglan
OmarAglan force-pushed the unify/worldbuilder-prerequisites branch from 628bfe3 to 3bf6fb4 Compare July 30, 2026 08:20
@OmarAglan

Copy link
Copy Markdown
Author

Changes:

  • Added RETAIL_COMPATIBLE_WORLDBUILDER, enabled by default.
  • Generals now writes PolygonTriggers version 3 without layerName when compatibility is enabled.
  • Disabling the macro enables version 4 with layer serialization.
  • The reader remains capable of loading versions 1–4.

Comment thread Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp Outdated
@OmarAglan
OmarAglan force-pushed the unify/worldbuilder-prerequisites branch from 3bf6fb4 to 8ad31ca Compare July 30, 2026 08:48
Comment thread Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h
loc.y = file.readInt();
loc.z = file.readInt();
pTrig->addPoint(loc);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing

+               if (numPoints<2) {
+                       DEBUG_LOG(("Deleting polygon trigger '%s' with %d points.",
+                                       pTrig->getTriggerName().str(), numPoints));
+                       deleteInstance(pTrig);
+                       continue;
+               }

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.

I initially copied the numPoints < 2 filtering into Generals for parity, but tracing the save-loading path showed that this would break existing saves. GameLogic expects the polygon-trigger count and IDs loaded from the map to match the snapshot. Removing a zero or one point trigger during map loading can therefore cause restoration to fail with SC_INVALID_DATA.

I removed that filtering from both Generals and Zero Hour instead. This keeps the implementations unified while preserving save compatibility.

tell me if we can do anything else

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.

after giving it some thought, reintroducing the parser deletion would prevent existing saves from restoring when their trigger counts or IDs no longer match the map.
so a safer solution is retaing malformed triggers internally for snapshot compatibility, but treat triggers with fewer than two points as invalid and exclude them from TerrainLogic::getTriggerAreaByName(), preventing ai guards and script conditions from using the sentinel derived center and radius while preserving the trigger list needed during save restoration.

i hope it works :)

@stephanmeesters stephanmeesters Jul 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

How about:

if (info->version >= K_TRIGGERS_VERSION_4 && numPoints<2) {
	DEBUG_LOG(("Deleting polygon trigger '%s' with %d points.",
			pTrig->getTriggerName().str(), numPoints));
	deleteInstance(pTrig);
	continue;
}

and then restore the PolygonTrigger::isValid() ?

edit: this is assuming all of the ZH maps are K_TRIGGERS_VERSION_4

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.

good direction, but i do not think we can assume every map loaded by Zero Hour uses PolygonTriggers version 4. The reader supports versions 1 to 4, and Zero Hour may load older or Generals maps.

so im suggesting preserving the existing behavior per target so Zero Hour continues filtering malformed triggers for every version, while Generals filters them only for version 4. Generals versions 1–3 would retain them for compatibility with existing saves.

we would also keep isValid() requiring at least two points and retain the TerrainLogic check, because the older Generals triggers that must remain for snapshot identity still must not be exposed as usable gameplay areas.

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.

more explanation for what i did (Used Chatgpt to explain and make it easier to read and discuss):

What is a malformed trigger?

A polygon trigger normally needs at least two points. A trigger with zero or one point is malformed.

These triggers cause two separate problems:

  1. Save compatibility
  2. Invalid gameplay geometry

Why saves are sensitive

When loading a save, the game:

  1. Loads polygon triggers from the original map.
  2. Loads the trigger snapshot from the save.
  3. Requires the map and save to contain the same trigger count and IDs.

If the parser deletes a trigger that existed when the save was created—or retains one that was previously deleted—the counts differ and loading fails with SC_INVALID_DATA.

Historical behavior

The two games behaved differently:

Generals has Retained malformed triggers

Zero Hour has Deleted triggers with fewer than two points

We should also keep:

m_numPoints < 2

in isValid() and keep the TerrainLogic validity check. Those protect Generals when malformed triggers must remain internally for save compatibility.

@stephanmeesters stephanmeesters left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Could you do pass to see if any polygon trigger code was missed that we can unify? And make the title more descriptive? After that LFTM

Comment thread Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp Outdated
@OmarAglan
OmarAglan force-pushed the unify/worldbuilder-prerequisites branch 2 times, most recently from 96f3cbe to a66869c Compare July 30, 2026 09:36
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
@OmarAglan
OmarAglan force-pushed the unify/worldbuilder-prerequisites branch from a66869c to 672a5b4 Compare July 30, 2026 09:48
@OmarAglan OmarAglan changed the title unify(worldbuilder): Add Generals compatibility prerequisites unify(worldbuilder): Align PolygonTrigger serialization and editor prerequisites Jul 30, 2026
@Skyaero42

Skyaero42 commented Aug 2, 2026

Copy link
Copy Markdown

I do not understand the purpose of this PR. If this is a merge (& move) bringing Worldbuilder files into core, it should not contain TheSuperHackers comments.

Use WinMerge or similar program to merge the files (except for copyright header). Then in a second commit or PR, move the files to core.

Look up some of the merged PR's that do unification of the games how it is done.

If this is not a merge & move in that sense, than it needs better explanation on what the purpose is of this PR.

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

Successfully merging this pull request may close these issues.

3 participants