Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8bd1f3c
feat(worldbuilder): Adds MCP bridge for AI-driven map editing
pWn3d1337 Sep 30, 2026
bb0b9e5
fix(worldbuilder): Fixes MCP flood fill replace and camera pitch
pWn3d1337 Sep 30, 2026
da4f8ed
feat(worldbuilder): Adds MCP heightmap and mask image import/export
pWn3d1337 Sep 30, 2026
1fb125d
feat(worldbuilder): Adds MCP map mirroring, rotation and symmetrize
pWn3d1337 Sep 30, 2026
5362fe1
feat(worldbuilder): Adds MCP procedural terrain and auto texturing
pWn3d1337 Sep 30, 2026
14fc57a
test(worldbuilder): Covers MCP terrain generation in the smoke test
pWn3d1337 Oct 1, 2026
a16b6c8
feat(worldbuilder): Adds MCP skirmish AI setup and check commands
pWn3d1337 Oct 1, 2026
e12f5bb
feat(worldbuilder): Adds MCP objects.scatter for decoration
pWn3d1337 Oct 1, 2026
6178d41
feat(worldbuilder): Adds MCP roads.route with terrain-aware routing
pWn3d1337 Oct 1, 2026
727dade
feat(worldbuilder): Adds MCP map.usage and map.replace
pWn3d1337 Oct 1, 2026
533636e
feat(worldbuilder): Adds MCP map.generate_starts
pWn3d1337 Oct 1, 2026
a6cd4a9
feat(worldbuilder): Adds MCP map.resize
pWn3d1337 Oct 1, 2026
89dd0ea
worldbuilder mcp stuff
pWn3d1337 Oct 1, 2026
e308813
fix(filesystem): Skips the Generals install assert when its archives …
pWn3d1337 Oct 9, 2026
9108405
feat(worldbuilder): Adds MCP script, team and build list editing
pWn3d1337 Oct 9, 2026
7c2afd0
feat(engine): Adds read accessors to template data for tools
pWn3d1337 Oct 10, 2026
1043180
feat(worldbuilder): Adds MCP faction descriptions and AI scaffolding
pWn3d1337 Oct 10, 2026
98b150e
fix(ai): Raises the skill set limit to the number of purchasable scie…
pWn3d1337 Oct 10, 2026
2af3560
feat(worldbuilder): Adds MCP access to AIData.ini and base layouts
pWn3d1337 Oct 10, 2026
9481196
fix(debug): Keeps -ignoreAsserts in effect during shutdown
pWn3d1337 Oct 10, 2026
fa4b6cb
feat(ai): Adds a logged skirmish between AI players from the command …
pWn3d1337 Oct 10, 2026
98e58c0
feat(worldbuilder): Adds MCP tools for AI matches and SideInfo editing
pWn3d1337 Oct 10, 2026
a22e94e
feat(ai): Lets an AI match use other script and AIData files
pWn3d1337 Oct 10, 2026
66f4dc4
feat(worldbuilder): Lets MCP AI matches play the open scripts
pWn3d1337 Oct 10, 2026
27e76e6
feat(ai): Runs AI matches without the game client with -headless
pWn3d1337 Oct 10, 2026
e716d8c
feat(worldbuilder): Runs MCP AI matches headless by default
pWn3d1337 Oct 10, 2026
9aba1fc
fix(thing): Makes prerequisites of an ObjectExtend replace the inheri…
pWn3d1337 Oct 10, 2026
a4da30e
feat(worldbuilder): Adds MCP support for maps and AI scripts with ships
pWn3d1337 Oct 10, 2026
6d595ad
feat(worldbuilder): Adds shipyards and ships to the MCP faction descr…
pWn3d1337 Oct 10, 2026
31ffab1
docs: Adds CLAUDE.md to the repository with a WorldBuilder MCP section
pWn3d1337 Oct 11, 2026
56e3000
Merge branch 'main' into worldbuilder_mcp
pWn3d1337 Oct 17, 2026
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
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
!.gitattributes
!.github
!.gitmodules
!.mcp.json

*.user
*.ncb
Expand Down Expand Up @@ -51,7 +52,6 @@ thumbs.db

## AI assistant config (user-specific)
/.claude/
/CLAUDE.md

## IntelliJ, CLion, etc.
/.idea
Expand All @@ -66,4 +66,5 @@ build.ninja

## Python
__pycache__/
*.pyc
*.pyc
.venv/
8 changes: 8 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"worldbuilder": {
"command": "uv",
"args": ["run", "--directory", "scripts/worldbuilder-mcp", "server.py"]
}
}
}
157 changes: 157 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this fork is

A **modding-focused fork** of TheSuperHackers/GeneralsGameCode, the community port of the *Command & Conquer: Generals* / *Zero Hour* engine from VS6/C++98 to VS2022/C++20.

**This fork only cares about Zero Hour (`GeneralsMD/`).** Treat the original Generals (`Generals/`) as out of scope unless explicitly asked. Upstream rules say "Zero Hour changes take precedence; mirror to Generals" — here, skip the mirror step. `Core/` holds the shared engine and must be considered for most changes.

**Game data INIs** (Object/Weapon/OCL/etc. that configure the modules this engine parses) live outside the repo at `F:\Operation-Firestorm-2.0\Data\INI`. Read/search there when a task involves how units, weapons, or modules are configured in-game.

## Build

This fork builds with the modern VS2022 toolchain (not VS6). The relevant preset is in `CMakeUserPresets.json`:

```bash
cmake --preset win32-modding-zh # configure (Release + RTS_DEBUG_LOGGING + RTS_BUILD_OPTION_DEBUG + stacktrace + crashing)
cmake --build build/win32 --preset w32-modding-zh # builds targets: z_generals, z_worldbuilder
```

- `win32-modding-zh` writes to `build/win32` and enables in-game logging/asserts/stacktraces for mod debugging.
- Stock presets also exist: `win32` (clean Release), `win32-debug`, `win32-vcpkg` (uses vcpkg toolchain for zlib/ffmpeg), `unix`, `mingw-w64-i686`, plus the `vc6*` family used upstream for **retail replay compatibility** (not needed for modding work).
- `compile_commands.json` is exported (`CMAKE_EXPORT_COMPILE_COMMANDS=ON`) under the build dir.

### Verifying a change compiles

Use the **"BUILD ZH AND WB FOR MODDING"** preset (`w32-modding-zh`). To verify, build only what the change touches:
- Build `z_generals` (`generalszh.exe`) for normal engine/game changes.
- Build `z_worldbuilder` (`worldbuilder.exe`) only for WorldBuilder-specific changes.

```bash
cmake --build build/win32 --preset w32-modding-zh --target z_generals
```

Run cmake/ninja from a **VS dev shell** (`vcvarsall.bat amd64_x86`), not a plain PowerShell/Bash session, or the build fails to launch the compiler. If a configured `build/win32` errors with `CreateProcess failed: The system cannot find the file specified`, reconfigure (`cmake --preset win32-modding-zh`) from that dev shell to regenerate compiler paths.

### Targets / output names

Zero Hour targets are prefixed `z_` (Generals uses `g_`):
- `z_generals` → `generalszh.exe` (the game)
- `z_worldbuilder` → `worldbuilder.exe` (map editor)
- Other ZH tools: `z_imagepacker`, `z_mapcachebuilder`, `z_guiedit`, `z_w3dview`, etc.

### Key build options (`cmake/config-build.cmake`)

`RTS_BUILD_ZEROHOUR` (default ON), `RTS_BUILD_GENERALS` (turn OFF to skip original Generals), `RTS_BUILD_OPTION_DEBUG`, `RTS_BUILD_OPTION_PROFILE`, `RTS_DEBUG_LOGGING`, `RTS_DEBUG_CRASHING`, `RTS_DEBUG_STACKTRACE`, `RTS_BUILD_OPTION_FFMPEG`. Debug defines: `RTS_DEBUG WWDEBUG DEBUG`; Release: `RTS_RELEASE NDEBUG`.

## Architecture

Three source roots, each a CMake subtree:

- **`Core/`** — engine + libraries shared by both games. Don't duplicate here what already lives in Core.
- `Core/GameEngine/` — base engine.
- `Core/GameEngineDevice/` — platform/render backends (DirectX 8).
- `Core/Libraries/Source/WWVegas/` — Westwood "WW" graphics/math/3D framework (WW3D2, WWMath, WWLib...). Foundational; touched indirectly by most rendering work.
- `Core/Libraries/Source/Compression/`, `debug/`, `profile/`.
- `Core/Tools/` — shared dev tools.
- **`GeneralsMD/Code/`** — Zero Hour specific code. **Primary work area.**
- **`Generals/Code/`** — original Generals. Ignore for this fork.

### Engine split (the big picture)

Inside `GameEngine/Source/`:
- **`GameLogic/`** — simulation, rules, game state. Deterministic; this is what must stay in sync across the network and across replays.
- **`GameClient/`** — rendering, UI, input, audio — everything player-facing and machine-local.
- **`Common/`** — shared interfaces/types between Logic and Client (`GameEngine/Include/Common/`).
- **`GameNetwork/`** — netcode.
- `GameEngineDevice/Source/{W3DDevice,Win32Device}/` — concrete implementations of Client abstractions on W3D (DirectX) and Win32.

The Logic/Client separation is load-bearing: gameplay-affecting logic must be deterministic (replays/multiplayer depend on it). Keep rendering/UI out of `GameLogic`. Entry point: `GeneralsMD/Code/Main/WinMain.cpp`.

## Code conventions

- **Match surrounding legacy style.** Original code is C++98-flavored and deliberately simple. Prefer not to introduce modern C++ features unless they make the change clearly more robust/maintainable. Manual memory management (`new`/`delete`) is the norm.
- **Scope discipline.** Edits touch only the lines serving the change. Never mix a large refactor with a logic change in the same commit.
- **No `TheSuperHackers` change-marker comments.** That convention belongs to the upstream `TheSuperHackers/GeneralsGameCode` repo this was forked from — do not add those markers here. Use plain comments that explain *why* a change was made, only where the reason isn't obvious from the code.
- **Commit / PR titles** use Conventional Commits: `bugfix:`, `fix:`, `feat:`, `build:`, `perf:`, `refactor:`, `tweak:`, `chore:`, `ci:`, `docs:`, `style:`, `test:`, `unify:`. Optional scope, e.g. `bugfix(system): fix uninitialized memory in Get_OS_Info`. Present tense ("Fixes", not "Fixed").
- When naming game units in docs/comments, spell out and prefix with faction (USA/China/GLA/Boss/Civilian), e.g. "USA Missile Defender" not "MD".

## Testing

No unit-test suite for gameplay. Correctness for engine changes is verified via **replay compatibility** (`GeneralsReplays/`):

```
generalszh.exe -jobs 4 -headless -replay subfolder/*.rep
```

Copy replays into `%USERPROFILE%/Documents/Command and Conquer Generals Zero Hour Data/Replays/<subfolder>` and maps into `.../Maps`. Exit code signals pass/fail. Note: upstream CI runs this against a **VC6 optimized, `RTS_BUILD_OPTION_DEBUG=OFF`** build for retail-byte compatibility — modding-preset builds are not retail-compatible and won't match retail replays.

Running the game requires an installed copy of retail Generals/Zero Hour (assets).

## Tooling / linting

- `.clang-tidy` at root; custom checks in `scripts/clang-tidy-plugin/`. Run via `scripts/run-clang-tidy.py` against `compile_commands.json`.
- `scripts/cpp/` holds maintenance helpers (include-case fixes, whitespace cleanup, refactor scripts).
- `scripts/docker-build.sh` / `docker-install.sh --detect` for Linux builds + install to a detected game dir.

## WorldBuilder MCP

An MCP server that drives a running WorldBuilder: it edits the open map, its scripts and teams, and the AI data around them. Use its tools (`mcp__worldbuilder__*`) for map and script work instead of editing `.map` / `.scb` files by hand. Full tool reference: `scripts/worldbuilder-mcp/README.md`; every tool also documents itself.

```
Claude <-- stdio --> scripts/worldbuilder-mcp/server.py <-- TCP 127.0.0.1:47800 --> WorldBuilderZH.exe -mcp
```

### Running it

- WorldBuilder must run **from the mod folder** (`F:\Operation-Firestorm-2.0`, it needs the game data): `WorldBuilderMCP.bat` there starts `WorldBuilderZH.exe -mcp -ignoreAsserts`. Without `-ignoreAsserts` an assert dialog blocks the bridge.
- The server is registered in the repo's `.mcp.json` and started through `uv`. New or changed tools appear only after Claude is **fully quit and restarted**; WorldBuilder must be restarted after rebuilding it.
- The bridge serves one client. If tools fail with "Cannot reach WorldBuilder", WorldBuilder is not running with `-mcp` (or hangs in a dialog).
- WorldBuilder and the game read INI data at startup: restart after changing game data.

### Rules that apply to every edit

- **World units**: 10 per terrain cell, (0,0) at the lower left of the playable area. Heights are raw 0..255 (times `height_map_scale`). Angles in degrees, counterclockwise, 0 = +x. Start with `map_info`.
- **Look names up, never guess**: `objects_list_templates`, `terrain_list_textures`, `roads_list_types`, `sides_list_factions`, `scripts_list_actions` / `scripts_list_conditions`, `scripts_list_values`, `teams_list_properties`.
- **Every edit is one undo step** (`edit_undo`); WorldBuilder keeps 15. Many script edits belong in one `scripts_batch` (one step, all or nothing).
- **Nothing is saved until `map_save`** (or `scripts_export` for a script file). Overwriting an existing file needs an explicit flag; ask before overwriting the user's files.
- Script edits fail while WorldBuilder's own script editor window is open.
- `view_screenshot` shows the result; use it to check terrain and object placement.

### Map editing

- **Terrain**: `terrain_generate` (presets), `terrain_brush`, `terrain_set_heights` / `terrain_import_heightmap`, `terrain_limit_slope`, `terrain_set_passability`; textures with `terrain_paint_texture`, `terrain_auto_texture`, `terrain_auto_blend`.
- **Whole-map operations**: `map_resize`, `map_transform`, `map_symmetrize` (mirror or rotate one part onto the rest), `map_replace`.
- **Objects**: `objects_place` / `objects_place_many` / `objects_scatter`, `objects_modify`, `objects_set_properties`. Owners are team names from `sides_list` (`team` is neutral).
- **Waypoints, areas, water, roads**: `waypoints_add` / `waypoints_link`, `polygons_add`, `roads_add`, `roads_route` (road that follows the terrain).
- **Skirmish maps**: `map_generate_starts` for `Player_<N>_Start` waypoints, then `ai_skirmish_setup` (perimeters, `Center<N>` / `Flank<N>` / `Backdoor<N>` paths) and `ai_skirmish_check`. Plain `Center` labels without a player number are ignored by the AI.
- **Ships**: a map is meant for ships when its `map.ini` sets `EnableShips` (`map_info`: `ships_enabled`; `map_set_info` writes it). The AI then needs waypoints `Shipyard<player>_<n>`, the area `NavalPerimeter<player>` and paths `Naval<player>` / `NavalFlank<player>`; `ai_skirmish_check` lists what is there.

### Script and team editing

- **Read**: `scripts_list` (start with `detail="groups"` on big sets), `scripts_get`, `scripts_find` (who uses a team, flag, object type ...), `teams_list`, `teams_get`. **Write**: `scripts_add`, `scripts_update`, `scripts_move`, `scripts_delete`, the group tools, `teams_set`, `teams_set_many`.
- A script is JSON: flags, `conditions` (a list of OR clauses, each a list of ANDed conditions), `actions`, `false_actions`. Parameters are given in the order of the action's parameter types.
- **Bulk work**: `scripts_copy`, `teams_copy`, `scripts_clone_player`, `scripts_rename` (renames with all references), `scripts_set_values`, `scripts_batch`, `scripts_dump` / `scripts_load` (readable JSON, good for diffs and backups).
- **Checks**: `scripts_verify` (what the editor would flag), `scripts_missing` (broken references by cause), `scripts_analyze` (dead logic), `scripts_compare`.
- **Understanding a set**: `scripts_build_order`, `scripts_trace`, `scripts_by_difficulty`, `teams_summary`, `scripts_report`.
- **Files**: `scripts_import` / `scripts_export` read and write `.scb`. The mod's AI is `Data\Scripts\SkirmishScripts.scb` (about 3400 scripts, 750 teams, one `Skirmish<Faction>` player per faction): import it into a scratch map to work on it, and treat the installed file as the user's, to be overwritten only on request.

### Writing and testing AI

The pieces build on each other; files live in `worldbuilder_factions\` next to the WorldBuilder executable.

1. **What a faction can do**: `faction_generate` (once, and after game data changes), then `faction_get` (`overview`, `tree`, `units`, `naval`, single types). Roles, slots and phases are guesses until the faction's `.overrides.yaml` has been reviewed; types marked `unavailable` cannot be built.
2. **What every AI shares**: `ai_framework_generate` / `ai_framework_get` (common scripts, flags, timers, team roster and hooks, object lists).
3. **What is outside the scripts**: `aidata_get` reads `AIData.ini`. `SKIRMISH_BUILD_BUILDING` only builds structures that have a place in the side's `SkirmishBuildList`; `SideInfo` holds gatherer counts, the base defense structure and the science skill sets. Edit with `buildlist_from_aidata` / `buildlist_export` and `aidata_set_side` (they write only when given a file).
4. **A new faction AI**: `ai_scaffold` derives it from a related faction's player (dry run first), using `faction_map`; then work through its `unmapped` list.
5. **Static check**: `faction_check` on the player finds teams and build orders the faction cannot fulfil.
6. **Play it**: `ai_match_run` lets the game play a headless AI-vs-AI skirmish and `ai_match_report` says what each side built, never built, which scripts and teams never ran and what went wrong. `scripts="open"` plays the scripts open in WorldBuilder and `ai_data=<file>` a changed `AIData.ini`, without installing either. The same seed repeats a match exactly, so run before and after a change.

### Working on the MCP itself

- Bridge commands: `GeneralsMD/Code/Tools/WorldBuilder/src/mcp/McpCmd*.cpp` (handlers run on the UI thread, take and return `McpJson`, fail with `mcpFail`, are registered with `mcpRegisterCommand`). Edits go through an `Undoable` so that undo works.
- Server tools: `scripts/worldbuilder-mcp/server.py`; logic that needs no bridge is in `faction.py`, `framework.py`, `scaffold.py`, `aidata.py`, `match.py`.
- AI matches are an engine feature (`GameLogic/AIMatch.h`): `generalszh.exe -aiMatch <map> -aiPlayers Faction:difficulty,... -aiMatchLog <file> [-headless]`.
- After a change: build `z_worldbuilder`, copy `WorldBuilderZH.exe` to the mod folder, restart it with `-mcp -ignoreAsserts`, run `python scripts/worldbuilder-mcp/smoke_test.py` (it replaces the open map with a test map) and extend that test for new commands.
1 change: 1 addition & 0 deletions Core/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ set(GAMEENGINE_SRC
# Include/Common/StackDump.h
# Include/Common/StateMachine.h
# Include/Common/StatsCollector.h
Include/Common/SciencePurchaseSlots.h
Include/Common/STLTypedefs.h
Include/Common/StreamingArchiveFile.h
Include/Common/SubsystemInterface.h
Expand Down
32 changes: 32 additions & 0 deletions Core/GameEngine/Include/Common/SciencePurchaseSlots.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

// SciencePurchaseSlots.h
// How many sciences the purchase screens offer per rank. Kept apart from the control bar so that
// game logic can size its own tables by it without pulling in the user interface.

#pragma once

enum {
MAX_PURCHASE_SCIENCE_RANK_1 = 7,
MAX_PURCHASE_SCIENCE_RANK_3 = 21,
MAX_PURCHASE_SCIENCE_RANK_8 = 7,
};

/// Every science a player can purchase: one per slot of the purchase screens.
enum { MAX_PURCHASE_SCIENCES = MAX_PURCHASE_SCIENCE_RANK_1 + MAX_PURCHASE_SCIENCE_RANK_3 + MAX_PURCHASE_SCIENCE_RANK_8 };
6 changes: 1 addition & 5 deletions Core/GameEngine/Include/GameClient/ControlBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "Common/GameType.h"
#include "Common/Overridable.h"
#include "Common/Science.h"
#include "Common/SciencePurchaseSlots.h"
#include "GameClient/Color.h"

// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -466,11 +467,6 @@ class CommandButton : public Overridable
//-------------------------------------------------------------------------------------------------
enum { MAX_COMMANDS_PER_SET = 32 }; // user interface max is 14 (but internally it's 18 for script only buttons!)
enum { MAX_RIGHT_HUD_UPGRADE_CAMEOS = 9};
enum {
MAX_PURCHASE_SCIENCE_RANK_1 = 7,
MAX_PURCHASE_SCIENCE_RANK_3 = 21,
MAX_PURCHASE_SCIENCE_RANK_8 = 7,
};
enum { MAX_STRUCTURE_INVENTORY_BUTTONS = 10 }; // there are this many physical buttons in "inventory" windows for structures
enum { MAX_BUILD_QUEUE_BUTTONS = 9 };// physical button count for the build queue
enum { MAX_SPECIAL_POWER_SHORTCUTS = 32};
Expand Down
11 changes: 11 additions & 0 deletions Core/GameEngine/Source/Common/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,14 @@ Int parseReplay(char *args[], int num)
return 1;
}

/// A skirmish between AI players (see AIMatch.h, which reads the match options itself). Tools
/// start it while WorldBuilder or another game is open, so it runs as an instance of its own.
Int parseAIMatch(char *args[], int num)
{
rts::ClientInstance::setMultiInstance(TRUE);
return (num > 1) ? 2 : 1;
}

Int parseJobs(char *args[], int num)
{
if (num > 1)
Expand Down Expand Up @@ -1136,6 +1144,9 @@ static CommandLineParam paramsForStartup[] =
// You can also include wildcards. The file must be in the replay folder or in a subfolder.
{ "-replay", parseReplay },

// Run a skirmish between AI players and log it. The map name follows.
{ "-aiMatch", parseAIMatch },

// TheSuperHackers @feature helmutbuhler 23/05/2025
// Simulate each replay in a separate process and use 1..N processes at the same time.
// (If you have 4 cores, call it with -jobs 4)
Expand Down
4 changes: 4 additions & 0 deletions Core/GameEngine/Source/Common/System/Debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ inline Bool ignoringAsserts()
if (TheGlobalData && TheGlobalData->m_headless)
return true;
#ifdef DEBUG_CRASHING
// Remembered, so that asserts raised during shutdown (after the global data is gone) are ignored too.
static Bool ignoreAssertsWasSet = false;
if (TheGlobalData && TheGlobalData->m_debugIgnoreAsserts)
ignoreAssertsWasSet = true;
if (ignoreAssertsWasSet)
return true;
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ class WorldHeightMap : public RefCountClass,

public: // height map info.
static Int getMinHeightValue() {return K_MIN_HEIGHT;}
static Int getMaxHeightValue() {return K_MAX_HEIGHT;}
/// Highest value a height sample can have: K_MAX_HEIGHT times the HeightMapScale of the map.
static Int getMaxHeightValue();

HeightSampleType *getDataPtr() {return m_data;}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2254,6 +2254,13 @@ Bool WorldHeightMap::setDrawArea(const DrawArea& area)
return false;
}

Int WorldHeightMap::getMaxHeightValue()
{
// Heights are stored as bytes in the map file and multiplied by the scale when loaded.
const Real scale = TheMapData ? TheMapData->m_HeightmapScale : 1.0f;
return (Int)std::round(K_MAX_HEIGHT * scale);
}

Bool WorldHeightMap::setDrawOrg(Int xOrg, Int yOrg)
{
return setDrawArea(createDrawArea(xOrg, yOrg));
Expand Down
Loading
Loading