diff --git a/AGENTS.md b/AGENTS.md index 9a252b0d4..867567e22 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -141,6 +141,20 @@ Diff patterns that were reliable (more in the cheat sheet): not a source difference. - A mismatching argument count or `ret N` usually means the generated header is wrong; report it instead of working around it. - Diffs can reveal real bugs in existing reimplementations (wrong constants, wrong strides); fix those. +- A decompiled `goto` that lands *after* a shared block (a clamp ladder, a common tail) is usually not a + jump at all: put the block once, after the if/else that feeds it, and give the skipped arm the value the + block maps to itself (`step = 0` for a clamp that leaves 0 alone). MSVC then emits the original's + "jump straight past the block" for that arm on its own. Duplicating the block into each arm instead does + *not* get tail-merged and scores far worse - worth ~38 points on `updateShowHiLayerOrResetChangedLayer`. +- Thresholds in such a ladder are the literals the `cmp` shows, not what the decompiler printed: + `cmp eax, 0x3c; jle` is `step > 60`, and Ghidra renders the same code as `iVar1 < 60`. +- A folded address constant where the original has an `imul` means a named local: we wrote + `(y - 1) * 400 + x` and got one constant, while the original multiplied a `y - 1` local by 400. + Give each reused coordinate its own local and reassign it as the original walks the neighbours. +- An extra 4 bytes of frame with no local to explain it is usually a value the decompiler folded into an + expression but the original held in a variable (a copy taken across a call, a mask computed once). +- A mask applied inside a loop over a value that cannot change was hoisted in the original; look for + `and reg, MASK` before the first call and keep the masked result in a local. ## Agent Skills diff --git a/IMPLEMENTATION_CHEAT_SHEET.md b/IMPLEMENTATION_CHEAT_SHEET.md index 0b7151e3b..2ac9a43ed 100644 --- a/IMPLEMENTATION_CHEAT_SHEET.md +++ b/IMPLEMENTATION_CHEAT_SHEET.md @@ -50,6 +50,34 @@ This also applies when putting instructions inside the condition branches. Naturally, the logic flow needs to be kept, so the update of a variable can not move before or after another usage of the same variable. +### Statement Order + +The order of instructions in the assembly is not the order of the statements in the original source. +Within a boundary the compiler schedules freely, so do not copy the instruction order into the source. + +This matters most for long runs of independent stores into one object, as in `AICState::setAICParameters_NN`: +the whole body is `this->aics[aicIndex]. = ;` over ~130 fields, and the compiler emits +those `mov dword ptr [eax + ], ` instructions in an order of its own. Rewriting all 16 of +those functions so their statements followed the original's store order made every one of them *worse* +(e.g. `setAICParameters_14` 82.4% -> 50.0%). + +That does not make the order unrecoverable. The compiler moves only the stores whose value sits in a **register**: +when a constant's register is about to be reloaded with another constant, the stores still waiting for the old value +are pulled up in front of the reload, and stores of a newly loaded constant are gathered behind it. Stores of an +**immediate** (`mov dword ptr [eax + off], 0x46`) stay where the source put them. So: + +1. Take the immediate stores of the original as the skeleton of the source order. Across a family of sibling + functions the skeleton is the same template, so vote the precedence of every pair of fields over all siblings - + a field that is a register store in one function is an immediate in another. +2. Put each register store back at its place in that template (mostly struct order, with the template's own local + swaps: `unknown002` before `unknown001`, `populationPerFarm` before `unknown011`, `recruitProbDefWeak` before + `recruitProbDefDefault`, `RecruitIntervalWeak` before `RecruitInterval`, `AttMaxAssassins` before `AttUnit2`). +3. Compile and move the few statements that still sit elsewhere next to their neighbour in the original. + +All 16 `setAICParameters_NN` went from 75-95% to 100% this way, with no type or header change; the earlier attempt +failed because it copied the *instruction* order, hoisted stores included. Which constant lands in which register +then follows by itself - it was a consequence of the statement order, not of register pressure. + ## Structure ### Bitwise Operations @@ -99,6 +127,56 @@ SEC_RNG::ptr->currentNumber1 % 4 - The assembly may contain multiple assigns of the same value to the same variable within the same boundary. This might be optimization suggesting there was only one assign. - The decompiler may suggest multiple assigns of the same value to the same variable within the same boundary. If this is not present in the assembly, it might be an artifact. - If a variable is stored in a temporary, before it is directly being incremented or decremented by one, suggests a post-increment/decrement, even more if the temporary is used after this. +- Do not hoist a repeated sub-expression into a local just because it appears several times. `int const halfWidth = barWidth / 2;` + used at four call sites lost against the original, which recomputes `barWidth / 2` at every use (SHC_3BB0A8C1_0x004B20B0, 67.6% -> 82.4%). + Write the expression out again at each use and let the compiler decide. +- **A local that only copies a field lets the compiler fold what the original kept separate.** MSVC forward-substitutes + an expression built from a register value (`int aiType = pd.aiType; ... int aicIndex = aiType - 1;`) into its use, so + `aics[aicIndex].x` becomes `[reg*0x2a4 + this + (off - 0x2a4)]` with no decrement left. It does **not** substitute + an expression that contains a memory read across a store, a call or a branch. The original's + `test eax, eax; je; add eax, -1; imul eax, eax, 0x2a4; ... [eax + ecx + off]` therefore needs the field tested + directly and read again for the index: + ```cpp + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_NULL) + return FALSE; + int aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + ``` + `determineAIPlayerHelp`, `determineAIPlayerAttackRequestResponse`, `addUnitToSmallestPatrolTribe`, + `setCurrentAttackStrength` all went to 100% on this alone. Fifteen other spellings of the index (casts, pointer + arithmetic, an inlined accessor, in-place decrement, unsigned) all folded - only the missing local matters. The + same applies to any other decompiler local for a field (`tracker`, `outerPatrolGroupsCount`): reading the field at + every use took `getTargetableBuildingForPlayerID` from 65% to 100%. +- **A constant index held in a local stops the per-struct multiply being shared.** When the original recomputes + `playerID * 0x39f4` in every basic block (`mov eax, ecx; imul eax, eax, 0x39f4` three times, with `playerID` staying + in its register) while we multiply once, the array index in the source was a *variable* that the optimizer only + later found to be constant. With a variable index MSVC factors each access by its own element size + (`playerID * 0x1cfa` for a `short` array, `* 0xe7d` for an `int` array), so the three accesses are three different + expressions and nothing is common. Write `int tribeIndex = 11;` and index with it: + ```cpp + int tribeIndex = 11; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return; + int tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + ``` + Eight `AI::AICState` tribe-command functions went from 38-62% to 100% (`setTribe0xbToAggressiveAndAttack`, + `sendTribeToAttack`, `aiCommandTribe11/13`, ...). The same happens when the index is a field the code has just + compared against a constant: `pd.aivCurrentPauseIndex == 1 && pd.aivPauses[pd.aivCurrentPauseIndex] > 0`, not + `aivPauses[1]`. It is not universal - on functions whose index is a loop sum (`aiTribeIDs[166 + i]`) the local made + things worse. +- A constant kept in a callee-saved register from the top of the function (`mov ebx, 1` before the first branch, later + `mov [field], ebx`) is a **result variable** initialised at the top and stored once at the end, not two literal + stores: `int isNotNervous = 1; ... if (cond) isNotNervous = 0; else {...} field = isNotNervous;` + (`computeNervousness` 77% -> 100%). +- `add reg, -N` where we emit `sub reg, N` or `cmp reg, N` means two constants were merged: the source compared a + derived value, `aicIndex == 7` with `aicIndex = aiType - 1`, not `aiType == 8` + (`removeOrganismsAndSetMoveDestinationPairs` 97.9% -> 100%). +- Where two arguments are loaded on either side of a store (`mov ecx, [x]` ... `mov [stance], 1` ... `mov eax, [y]`), + give each a local and put the statements in that order: `int x = pd.someX; tribe.unitStance = ...; int y = pd.someY;` + (`makeUnitsGoDefensiveAndBackToSomeLocation` 95.7% -> 100%). Without a store in between, the *declaration order* + of two such locals picks which one gets `ecx` (`sendUnitsToAttackBreachedCastle` 98.7% -> 100%). +- The opposite does happen for the operands of a single statement: in `SHC_3BB0A8C1_0x00522520` the original evaluates + `unitID / 16` and `unitID % 16` into locals before the compound assignment. If a `|=`/`&=` statement with computed index + and mask does not match, give the index and the mask a local each and keep the declaration order the assembly shows. ### Conditionals @@ -110,6 +188,14 @@ SEC_RNG::ptr->currentNumber1 % 4 - If a condition suddenly uses a normally signed variable as unsigned value (e.g. `ja` compared to signed `jg`) and most times also contains a subtraction, it suggests a range condition optimization. The actual value was moved to 0 to allow expressing this via a single check: - Decomp code example: `DAT_GameCore.missionNumber1to20 - 1U < 0x14` - Original (likely): `1 <= DAT_GameCore.missionNumber1to20 && DAT_GameCore.missionNumber1to20 <= 20` +- A two-sided range check that is the whole body of a `BOOL`-returning function is compiled branchless. Write it as one + expression, `return lo <= value && value <= hi;`, not as nested `if`s with early returns, and use the literals the + assembly shows rather than a `short`/`ushort` local holding the field (SHC_3BB0A8C1_0x00530FD0, 78.3% -> 100%). +- Which form a boolean result takes is **not** predictable and has to be tried both ways. The folded + `return (x & MASK) == VALUE;` matched at only 40% for SHC_3BB0A8C1_0x004549C0; spelling it out as + `if ((x & MASK) == VALUE) { return TRUE; } else { return FALSE; }`, with locals for the computed index and the loaded + byte, reached 100%. Together with the range-check case above this means: folded expression and explicit + `TRUE`/`FALSE` branches are two separate candidates, measure both. - A switch with fewer then 4 cases is often simplified and uses subtractions in assembly to compare the value: ``` MOV EAX,[DAT_SoundEffectsHelperData1.SEC_Section1079. volumeLevel] @@ -196,6 +282,48 @@ you might have found an unrolled loop. Therefore, try to reproduce the logic in If GOTOs are present that clearly jump to the start of a loop, but the logic does not allow to do this without a GOTO, for example from a loop inside a loop, you might be able to move the continue or break condition to the outside. Methods could be placing a fitting condition related to the contained loop conditions after the loop or using a boolean flag that then functions as conditional. Both can sometimes be optimized away. +A global used as a running counter alongside the loop index is usually incremented inside the loop, even when its final +value is a constant. Ghidra shows the folded result: `DAT_CurrentUnitSlotID = 2500;` placed around the loop is really +`DAT_CurrentUnitSlotID = 1;` before it and `DAT_CurrentUnitSlotID += 1;` as the first statement of the body. This was +worth the last 8-10% on the whole `Map::Version::UpgradeMapUnitsTo_*` family (0x0053B310, 0x0053B530, 0x0053B570, +0x0053B5E0 all 90% -> 100%). + +`!=` as the loop bound suppresses MSVC's unrolling. If the original ends the loop with `jne` while we emit `jl` and an +unrolled body, write `for (int i = 1; i != 2500; ++i)` instead of `i < 2500` (SHC_3BB0A8C1_0x0053B340, 53% -> 91%). + +- A loop whose early exit returns the same value as the code after the loop was a `break`, not a `return`. The tell is + callee-saved registers pushed *after* the loop's entry test and one shared `mov eax, result` behind the loop, where + an early `return result;` makes us push everything in the prologue (`assignRequiredIdleEngineersToNewTribe`, + `addEngineersToSelection`, both ~88% -> 100%). +- Do not add a guard the loop condition already expresses. `if (id == 0) return 0;` in front of + `for (; id != 0; id = next(id))` moved one `push` across a branch; without it the compiler derives the same early + exit itself and places it where the original does (`aiRequiresExtraOxtethers`). Likewise put the block the + original keeps inline first: `if (best > 20) { store; return 1; } return 0;` rather than the inverted guard. + +- **Several exits that end in the same statement were usually one statement.** MSVC copies a short shared tail + (a store or a call followed by the epilogue) into each predecessor, so three `mov word ptr [tribe.unitStance], 2` + + `ret` blocks in the original do not mean three assignments in the source. Writing the assignment once after an + `if / else if` ladder instead of `stance = X; return;` in every arm changes how often each value is *used*, and the + use count is what ranks values for the callee-saved registers - so this is the fix when a function matches + instruction for instruction except that two registers are swapped (`tribeID` in `ebx` and the tribe offset in `ebp` + on one side, the reverse on the other). `giveMoveCommandToSortieUnits` 79.6% -> 100%, `instructTribe166ToMove` + 81.8% -> 100%, `assignUnitToATribe` 86.1% -> 100% (one `addUnitToTribe(unitID, tribeID)` after the branches + instead of three calls). When one of the arms must skip a call the others share, a `bool` set in the arms and + tested once afterwards compiles away and gives the single call site the original has. +- A search loop whose "found a free slot" exit must skip the check that follows the loop has its end test **inside + the body**: `for (;; i++) { if (i >= count) { if (best == 0) return 0; break; } ... if (free) { best = create(); + bestIndex = i; break; } ... }`. A `for (i < count)` loop with the check after it tests `best` on both paths, and + duplicating the tail into the create path changes the frame. With the test in the body the compiler rotates it to + the bottom and lays the blocks out as the original does (`getSmallestPatrolTribe` 65.2% -> 100%). The same + function needed `int i = 0;` declared *first*, before the other zero-initialised locals, to get the original's + `xor` order - declaration order did not matter anywhere else it was tried. + +- `mov edx, esi; add esi, 1` at the **top** of a loop body, with the body then indexing by the copy, is a `while` + loop that takes the index and advances the counter first: `while (i < count) { int index = i; i++; if (...[index]...) + { ...; return; } }`. A `for (...; i++)` with the same body does not produce it. Together with `int i = 0;` + declared where the original's `xor esi, esi` sits (here before an earlier early-return test, which then compares + against that zero register) this took `generateSiegeCreationInformation` from 39.4% to 100%. + ### GOTO A function may contain multiple GOTOs. @@ -230,6 +358,19 @@ It is hard to find these cases. I one situation, local variables that were used ## Functions +### Return Values + +A function declared `void` whose assembly still leaves a value in EAX at the return usually returns it. The most common +case is a function that returns the ID it was passed: changing `void EntityState::activateProjectileEntity(int entityID)` +to `int` with a trailing `return entityID;` took SHC_3BB0A8C1_0x004039B0 from 36.4% to 100%. Fix the signature in the +generated header rather than working around the difference. + +### Link-Time Code Generation + +A volatile register (usually ECX or EDX) that stays live across a call in the original, without being reloaded, means +that call was compiled with LTCG. This is fixable, not a permanent blocker: add the `.cpp` to `cmake/compiler-flags-gl.txt` +so it is built with `/GL`. SHC_3BB0A8C1_0x00504EE0 went from 38.5% to 100% and SHC_3BB0A8C1_0x0045B7F0 to 100% that way. + ### Parameters Parameters might not be pushed like normal in certain cases. diff --git a/build.bat b/build.bat index 40f1f3b08..028dee29a 100644 --- a/build.bat +++ b/build.bat @@ -23,16 +23,21 @@ if "%~1"=="" ( set "PRESET=%~1" set "TARGET=%~2" -:: --- Kill mspdbsrv.exe if it's running --- -tasklist | find /I "mspdbsrv.exe" >nul -if not errorlevel 1 ( - if not defined WRAP_QUIET echo Stopping mspdbsrv.exe... - taskkill /f /t /im mspdbsrv.exe >nul - if errorlevel 1 ( - if not defined WRAP_QUIET echo [WARNING] Failed to stop mspdbsrv.exe, continuing... - ) +:: --- Give this worktree its own PDB server --- +:: mspdbsrv.exe is a per-user singleton keyed on _MSPDBSRV_ENDPOINT_, and every worktree +:: carries its own copy of the toolchain. Sharing one endpoint across worktrees makes +:: cl.exe talk to whichever copy started first and fail with +:: fatal error C1090: PDB API call failed, error code '23' +if not defined _MSPDBSRV_ENDPOINT_ ( + for %%I in ("%~dp0.") do set "_MSPDBSRV_ENDPOINT_=openshc_%%~nxI" ) +:: --- Kill this worktree's mspdbsrv.exe if it's running --- +:: Only ours: killing every instance by image name takes down the PDB server of any other +:: worktree that is building at the same time, which fails their build with C1090. +powershell -NoProfile -Command ^ + "Get-Process mspdbsrv -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '%~dp0*' } | Stop-Process -Force -ErrorAction SilentlyContinue" >nul 2>&1 + :: --- Run cmake configure using preset --- if not defined WRAP_QUIET echo [INFO] Configuring with preset "%PRESET%"... call .\cmakew --preset "%PRESET%" diff --git a/cmake/compiler-flags-gl.txt b/cmake/compiler-flags-gl.txt index 848d70de6..0cc460ba3 100644 --- a/cmake/compiler-flags-gl.txt +++ b/cmake/compiler-flags-gl.txt @@ -7,3 +7,23 @@ src/OpenSHC/IO/ResourceManager/swapMapDataWithNextMap.cpp src/OpenSHC/UI/Rendering/WindowAndDirectDraw/takeScreenshot.cpp src/OpenSHC/IO/ResourceManager/readFirstPartOfCurrentResourceIntoMemory.cpp src/OpenSHC/IO/ResourceManager/readNextPartOfCurrentResourceIntoMemory.cpp +src/OpenSHC/Map/TileMapState/getBuildingSizeIndexMappingData.cpp +src/OpenSHC/Map/TileMapState/recountTotalOwnedMoats.cpp +src/OpenSHC/Map/TileMapState/setSignpostDistanceForCampaignMission.cpp +src/OpenSHC/Map/TileMapState/determineBuildingPlacementRotation.cpp +src/OpenSHC/Map/TileMapState/updateBuildingPlacementRotationPreview.cpp +src/OpenSHC/Map/TileMapState/updateMoatCountdownTimers.cpp +src/OpenSHC/Map/TileMapState/updateGameRelatedValue.cpp +src/OpenSHC/Map/TileMapState/clearDrawBridgeWater.cpp +src/OpenSHC/Map/TileMapState/clearFixedSizeTwoBuildingFootprint.cpp +src/OpenSHC/Map/TileMapState/clearRockFootprintFlags.cpp +src/OpenSHC/Map/TileMapState/clearSizeFiveBuildingFootprintAndMoats.cpp +src/OpenSHC/Map/TileMapState/clearTreeFootprintFlags.cpp +src/OpenSHC/Map/TileMapState/floodMoatUnderRemovedBuilding.cpp +src/OpenSHC/Map/TileMapState/isUnitBlockingSizeFiveFootprint.cpp +src/OpenSHC/Map/TileMapState/markBuildingFootprintFlag.cpp +src/OpenSHC/Map/TileMapState/placeOilsmelter.cpp +src/OpenSHC/Map/TileMapState/renderWallPlacementPreview.cpp +src/OpenSHC/Map/TileMapState/setMiscDisplayLayer.cpp +src/OpenSHC/Map/TileMapState/storeMinAndMaxHeightOfArea.cpp +src/OpenSHC/Map/TileMapState/unmarkBuildingFootprintFlag.cpp diff --git a/reccmp/dll/requirements.txt b/reccmp/dll/requirements.txt index fc16b93e6..f8eaa3e13 100644 --- a/reccmp/dll/requirements.txt +++ b/reccmp/dll/requirements.txt @@ -1 +1 @@ -git+https://github.com/isledecomp/reccmp \ No newline at end of file +git+https://github.com/sourcehold/reccmp diff --git a/src/OpenSHC/Map/Buildings/Building.hpp b/src/OpenSHC/Map/Buildings/Building.hpp index c2e44eaa5..6614308bb 100644 --- a/src/OpenSHC/Map/Buildings/Building.hpp +++ b/src/OpenSHC/Map/Buildings/Building.hpp @@ -99,8 +99,8 @@ namespace Map { ushort microX; // 0x000000E8 length: 2 ushort microY; // 0x000000EA length: 2 short terrainHeightUnk; // 0x000000EC length: 2 - ushort x; // 0x000000EE length: 2 - ushort y; // 0x000000F0 length: 2 + short x; // 0x000000EE length: 2 + short y; // 0x000000F0 length: 2 undefined1 padding_0xf2[2]; // 0x000000F2 length: 2 uint currentTilePositionAdjusted; // 0x000000F4 length: 4 uint widthOrHeight; // 0x000000F8 length: 4 @@ -140,42 +140,7 @@ namespace Map { short field138_0x1c2; // 0x000001C2 length: 2 short growCounter; // 0x000001C4 length: 2 undefined1 padding_0x1c6[2]; // 0x000001C6 length: 2 - int tileRef1; // 0x000001C8 length: 4 - int tileRef2; // 0x000001CC length: 4 - int tileRef3; // 0x000001D0 length: 4 - int tileRef4; // 0x000001D4 length: 4 - int tileRef5; // 0x000001D8 length: 4 - int tileRef6; // 0x000001DC length: 4 - int tileRef7; // 0x000001E0 length: 4 - int tileRef8; // 0x000001E4 length: 4 - int tileRef9; // 0x000001E8 length: 4 - int tileRef10; // 0x000001EC length: 4 - int tileRef11; // 0x000001F0 length: 4 - int tileRef12; // 0x000001F4 length: 4 - int tileRef13; // 0x000001F8 length: 4 - int tileRef14; // 0x000001FC length: 4 - int tileRef15; // 0x00000200 length: 4 - int tileRef16; // 0x00000204 length: 4 - int tileRef17; // 0x00000208 length: 4 - int tileRef18; // 0x0000020C length: 4 - int tileRef19; // 0x00000210 length: 4 - int tileRef20; // 0x00000214 length: 4 - int tileRef21; // 0x00000218 length: 4 - int tileRef22; // 0x0000021C length: 4 - int tileRef23; // 0x00000220 length: 4 - int tileRef24; // 0x00000224 length: 4 - int tileRef25; // 0x00000228 length: 4 - int tileRef26; // 0x0000022C length: 4 - int tileRef27; // 0x00000230 length: 4 - int tileRef28; // 0x00000234 length: 4 - int tileRef29; // 0x00000238 length: 4 - int tileRef30; // 0x0000023C length: 4 - int tileRef31; // 0x00000240 length: 4 - int tileRef32; // 0x00000244 length: 4 - int tileRef33; // 0x00000248 length: 4 - int tileRef34; // 0x0000024C length: 4 - int tileRef35; // 0x00000250 length: 4 - int tileRef36; // 0x00000254 length: 4 + int tileRefs[36]; // 0x000001C8 length: 144 undefined1 wheatGrowStateRelated; // 0x00000258 length: 1 undefined1 padding_0x259[3]; // 0x00000259 length: 3 int tunnelerCounter; // 0x0000025C length: 4 diff --git a/src/OpenSHC/Map/LogicHelpers/Logic1.hpp b/src/OpenSHC/Map/LogicHelpers/Logic1.hpp index b1b0e4dfe..6f621f3bf 100644 --- a/src/OpenSHC/Map/LogicHelpers/Logic1.hpp +++ b/src/OpenSHC/Map/LogicHelpers/Logic1.hpp @@ -20,6 +20,7 @@ namespace Map { L_PLAIN2_AND_PITCH = 8, // 0x00000008 L_BORDER = 16, // 0x00000010 L_BORDER_EDGE = 32, // 0x00000020 + L_UNNAMED_0x40 = 64, // 0x00000040 L_ROCKY = 128, // 0x00000080 L_WALL_OR_GATEHOUSE = 256, // 0x00000100 L_CRENEL = 512, // 0x00000200 diff --git a/src/OpenSHC/Map/Moat.hpp b/src/OpenSHC/Map/Moat.hpp index a676966a9..1d35bbf0e 100644 --- a/src/OpenSHC/Map/Moat.hpp +++ b/src/OpenSHC/Map/Moat.hpp @@ -15,10 +15,9 @@ namespace Map { // SIZE: 0x00000010 typedef struct Moat { - ushort tile; // 0x00000000 length: 2 - undefined1 padding_0x2[2]; // 0x00000002 length: 2 - undefined2 x; // 0x00000004 length: 2 - undefined2 y; // 0x00000006 length: 2 + int tile; // 0x00000000 length: 4 + short x; // 0x00000004 length: 2 + short y; // 0x00000006 length: 2 short zeroOrTwo; // 0x00000008 length: 2 undefined1 padding_0xa[2]; // 0x0000000A length: 2 char owner; // 0x0000000C length: 1 diff --git a/src/OpenSHC/Map/TerrainDefinedData.hpp b/src/OpenSHC/Map/TerrainDefinedData.hpp index b857a9499..a2a38ceee 100644 --- a/src/OpenSHC/Map/TerrainDefinedData.hpp +++ b/src/OpenSHC/Map/TerrainDefinedData.hpp @@ -43,9 +43,8 @@ namespace Map { XYPair StockpilePathableOffsets[9]; // 0x000001DC length: 72 undefined1 padding_0x224[64]; // 0x00000224 length: 64 XYPair field130_0x264[3][12]; // 0x00000264 length: 288 - Point8IntXY unkXYOffsets_0x384[8]; // 0x00000384 length: 64 - Point8IntXY field132_0x3c4[8]; // 0x000003C4 length: 64 - undefined1 padding_0x404[64]; // 0x00000404 length: 64 + Point8IntXY keepOutbuildingOffsets_0x384[3][4]; // 0x00000384 length: 96 + Point8IntXY keepOutbuildingOffsets_0x3e4[3][4]; // 0x000003E4 length: 96 XYPair field197_0x444[24]; // 0x00000444 length: 192 undefined1 padding_0x504[8]; // 0x00000504 length: 8 XYPair field206_0x50c; // 0x0000050C length: 8 @@ -59,7 +58,9 @@ namespace Map { XYPair field370_0x5cc; // 0x000005CC length: 8 undefined1 padding_0x5d4[184]; // 0x000005D4 length: 184 XYPair field555_0x68c; // 0x0000068C length: 8 - undefined1 padding_0x694[440]; // 0x00000694 length: 440 + undefined1 padding_0x694[308]; // 0x00000694 length: 308 + int field600_0x7c8[32]; // 0x000007C8 length: 128 + undefined1 padding_0x848[4]; // 0x00000848 length: 4 int field996_0x84c[25]; // 0x0000084C length: 100 undefined1 padding_0x8b0[4]; // 0x000008B0 length: 4 int field1001_0x8b4[25]; // 0x000008B4 length: 100 diff --git a/src/OpenSHC/Map/TileMapState.hpp b/src/OpenSHC/Map/TileMapState.hpp index 4eeb5af15..7900da981 100644 --- a/src/OpenSHC/Map/TileMapState.hpp +++ b/src/OpenSHC/Map/TileMapState.hpp @@ -78,11 +78,11 @@ namespace Map { uchar unitDeathHeatMap[80400]; // 0x00428370 length: 80400 uchar SEC_TileMap1104[80400]; // 0x0043BD80 length: 80400 uchar SEC_PathfindingCostTileMap1105[9][80400]; // 0x0044F790 length: 723600 - undefined4 someIndex; // 0x00500220 length: 4 - undefined4 someLimit; // 0x00500224 length: 4 - undefined4 someYLike; // 0x00500228 length: 4 - undefined4 someYLikeLimit; // 0x0050022C length: 4 - byte mapping40x40[40][40]; // 0x00500230 length: 1600 + int someIndex; // 0x00500220 length: 4 + int someLimit; // 0x00500224 length: 4 + int someYLike; // 0x00500228 length: 4 + int someYLikeLimit; // 0x0050022C length: 4 + char mapping40x40[40][40]; // 0x00500230 length: 1600 Moat moats[16000]; // 0x00500870 length: 256000 int currentMoatCount; // 0x0053F070 length: 4 undefined4 moatTileCount; // 0x0053F074 length: 4 @@ -90,19 +90,19 @@ namespace Map { undefined4 ALG_MoatYResult; // 0x0053F07C length: 4 int someMoatTile; // 0x0053F080 length: 4 PitchDitch pitchDitches[4000]; // 0x0053F084 length: 80000 - undefined4 maxPitchDitchCount; // 0x00552904 length: 4 + int maxPitchDitchCount; // 0x00552904 length: 4 undefined1 padding_0x552908[4]; // 0x00552908 length: 4 short specialAreasArray[8]; // 0x0055290C length: 16 - undefined4 temporaryTerrainTypeIndex; // 0x0055291C length: 4 - undefined4 SEC_Section1052; // 0x00552920 length: 4 - undefined4 SEC_Section1053; // 0x00552924 length: 4 - undefined4 SEC_Section1054; // 0x00552928 length: 4 + int temporaryTerrainTypeIndex; // 0x0055291C length: 4 + int SEC_Section1052; // 0x00552920 length: 4 + int SEC_Section1053; // 0x00552924 length: 4 + int SEC_Section1054; // 0x00552928 length: 4 int temporaryTerrainTypeArray[1000]; // 0x0055292C length: 4000 int temporaryTerrainTypeBinaryArray[1000]; // 0x005538CC length: 4000 undefined4 forceUpdateLogicalAndMiscDisplayLayers; // 0x0055486C length: 4 - undefined4 forceUpdateTextureTilemap; // 0x00554870 length: 4 - undefined4 forceUpdateGFXLayers; // 0x00554874 length: 4 - undefined4 forceUpdateMacroLayerFlag; // 0x00554878 length: 4 + int forceUpdateTextureTilemap; // 0x00554870 length: 4 + int forceUpdateGFXLayers; // 0x00554874 length: 4 + int forceUpdateMacroLayerFlag; // 0x00554878 length: 4 undefined4 field68_0x55487c; // 0x0055487C length: 4 undefined1 padding_0x554880[8]; // 0x00554880 length: 8 undefined4 placedBuildingID; // 0x00554888 length: 4 @@ -123,7 +123,7 @@ namespace Map { undefined4 refreshRelatedOne; // 0x005548C4 length: 4 undefined4 field93_0x5548c8; // 0x005548C8 length: 4 undefined4 flatViewToggleValue1; // 0x005548CC length: 4 - undefined1 padding_0x5548d0[4]; // 0x005548D0 length: 4 + undefined4 field95_0x5548d0; // 0x005548D0 length: 4 undefined4 flatViewToggleValue2; // 0x005548D4 length: 4 undefined4 refreshRelatedTwo; // 0x005548D8 length: 4 undefined4 field101_0x5548dc; // 0x005548DC length: 4 @@ -158,8 +158,8 @@ namespace Map { undefined4 buildingSpriteID2; // 0x00554950 length: 4 undefined4 field131_0x554954; // 0x00554954 length: 4 undefined4 buildingHeightLimit; // 0x00554958 length: 4 - undefined4 buildingMinHeight; // 0x0055495C length: 4 - undefined4 buildingMaxHeight; // 0x00554960 length: 4 + int buildingMinHeight; // 0x0055495C length: 4 + int buildingMaxHeight; // 0x00554960 length: 4 undefined4 buildingMaxHeightDifference; // 0x00554964 length: 4 undefined4 buildingPlacementProperty_3; // 0x00554968 length: 4 undefined4 buildingPlacementProperty_4; // 0x0055496C length: 4 diff --git a/src/OpenSHC/Map/TileMapState/Constructor_TileMapState.cpp b/src/OpenSHC/Map/TileMapState/Constructor_TileMapState.cpp new file mode 100644 index 000000000..ce35f75e9 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/Constructor_TileMapState.cpp @@ -0,0 +1,72 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00515F40 + TileMapState* TileMapState::Constructor_TileMapState() + { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupMovementDirectionTranslationMatrix, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMapping, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupTileMapSections, this)(); + this->editorActiveBrush = 7; + this->refreshCertainTileMap = 2; + this->counter1 = 0; + this->mapperMax = FALSE; + this->unknownZero_0x5548fc = 0; + this->unknownZero_0x554904 = 0; + this->unknownZero_0x554900 = 0; + this->currentMapperCommand = OpenSHC::Commands::M_MAPPER_NULL; + this->refreshRelatedOne = 1; + this->field93_0x5548c8 = 0; + this->refreshRelatedTwo = 0; + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + this->forceUpdateTextureTilemap = 1; + this->forceUpdateGFXLayers = 1; + this->refreshCertainTileMap_old = 4; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setChangedLayerToThreeAndMapping0x40x40, this)(); + this->moatTileCount = 0; + this->field122_0x554930 = 0; + this->field78_0x55488c = 0x50; + this->lastTime = timeGetTime(); + this->rockOrientation = 0; + this->field80_0x554894 = 0; + this->unknownTime_0x5549bc = timeGetTime(); + this->field161_0x5549c0 = 1; + this->temporaryTerrainTypeIndex = 0; + this->field188_0x554a14 = 0; + DAT_GameCore::instance.isTimeHalted = FALSE; + DAT_GameState::instance.mapAndTime.skirmishFogOfWar = 0; + this->ptr_LogicLayer = this->LogicLayer; + this->ptr_ChangedLayer = this->ChangedLayer; + this->ptr_TerrainTypeTileMap = this->Logic2Layer; + this->ptr_TerrainHeightTileMap = this->HeightLayer; + this->ptr_PathConnectionLayer = this->PathConnectionLayer; + this->ptr_OccupancyLayer = this->OccupancyLayer; + this->ptr_DamageLayer = this->DamageLayer; + this->ptr_MiscDisplayLayer = this->MiscDisplayLayer; + this->ptr_MovementDirectionTranslationMatrix = this; + this->ptr_SpecialAreasArray = this->specialAreasArray; + this->ptr_AIZoneLayer = this->AIZoneLayer; + this->currentMoatCount = 16000; + this->maxPitchDitchCount = 4000; + return this; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp b/src/OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp new file mode 100644 index 000000000..4ad078a7f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp @@ -0,0 +1,354 @@ +/** + HAND WRITTEN - not generated. + + path: 'OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp' + + The original gathers the neighbours of DAT_SomeTile into bitFlag with handwritten assembly, and that + assembly is a MACRO: the identical instruction stream appears verbatim inside + setBitFlagBasedOnWallTowerGatehouseOrKeep (0x004FF870, once), updateGFXLayers (0x004FC9E0, eleven + times), updateMacroLayerRelated (0x004FDB00, eleven times) and updateGfxLayer (0x00509180, nineteen + times) - never as a call. It is recognisable by "mov edx, 0" (no compiler materialises zero that + way), by advancing the row pointer with repeated "add esi, ecx" instead of one scaled lea, and, in + the TERRAIN variants, by loading a dword at a one-byte offset and masking it down to the low byte. + + Five shapes occur, selected by which layer pointer is read and how many neighbours are gathered: + + LOGIC - ptr_LogicLayer, 4-byte elements: "shl eax, 2", "add esi, ecx" x4, [esi +/- 4] + MISC - ptr_MiscDisplayLayer, 2-byte elements: "shl eax, 1", "add esi, ecx" x2, [esi +/- 2] + TERRAIN - ptr_TerrainTypeTileMap, 1-byte elements: no shift, "add esi, ecx" x1, [esi +/- 1] + _4 - east, west, north and south only + _8 - also north-west, north-east, south-west and south-east + + bitFlag bits: 0x02 west, 0x20 east, 0x80 north, 0x08 south, + 0x01 north-west, 0x40 north-east, 0x04 south-west, 0x10 south-east. + + Every expansion clobbers eax, ebx, ecx, edx, esi and edi and leaves the result in this->bitFlag. + + ID must be unique within the enclosing function; it exists only to keep the asm labels of two + expansions apart. MASK must be a numeric literal or a preprocessor constant: MASM has no "|" + operator, so an enum OR expression cannot be used as an asm operand. + + The original reaches the fields through the global instance's absolute address; we go through "this" + because MSVC inline asm cannot name DAT_TileMapState::instance ("::" is not parseable in an asm + operand). That costs the "mov eax, this" instructions and nothing else. +*/ + +#pragma once + +#include "OpenSHC/Map/TileMapState.hpp" + +// clang-format off + +#define MACRO_NEIGHBOUR_FLAGS_LOGIC_4(ID, MASK) \ + __asm mov eax, this \ + __asm mov edi, dword ptr [eax]TileMapState.ptr_MovementDirectionTranslationMatrix \ + __asm mov esi, dword ptr [eax]TileMapState.ptr_LogicLayer \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeTile \ + __asm shl eax, 0x2 \ + __asm add esi, eax \ + __asm push esi \ + __asm mov eax, this \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeY \ + __asm shl eax, 0x5 \ + __asm add edi, eax \ + __asm mov edx, 0x0 \ + __asm mov eax, dword ptr [esi + 0x4] \ + __asm mov ebx, dword ptr [esi - 0x4] \ + __asm and eax, MASK \ + __asm jz east_done_##ID \ + __asm or edx, 0x20 \ + __asm east_done_##ID: \ + __asm and ebx, MASK \ + __asm jz west_done_##ID \ + __asm or edx, 0x2 \ + __asm west_done_##ID: \ + __asm mov ecx, dword ptr [edi] \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz north_done_##ID \ + __asm or edx, 0x80 \ + __asm north_done_##ID: \ + __asm mov ecx, dword ptr [edi + 0x10] \ + __asm pop esi \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz south_done_##ID \ + __asm or edx, 0x8 \ + __asm south_done_##ID: \ + __asm mov eax, this \ + __asm mov byte ptr [eax]TileMapState.bitFlag, dl + +#define MACRO_NEIGHBOUR_FLAGS_LOGIC_8(ID, MASK) \ + __asm mov eax, this \ + __asm mov edi, dword ptr [eax]TileMapState.ptr_MovementDirectionTranslationMatrix \ + __asm mov esi, dword ptr [eax]TileMapState.ptr_LogicLayer \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeTile \ + __asm shl eax, 0x2 \ + __asm add esi, eax \ + __asm push esi \ + __asm mov eax, this \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeY \ + __asm shl eax, 0x5 \ + __asm add edi, eax \ + __asm mov edx, 0x0 \ + __asm mov eax, dword ptr [esi + 0x4] \ + __asm mov ebx, dword ptr [esi - 0x4] \ + __asm and eax, MASK \ + __asm jz east_done_##ID \ + __asm or edx, 0x20 \ + __asm east_done_##ID: \ + __asm and ebx, MASK \ + __asm jz west_done_##ID \ + __asm or edx, 0x2 \ + __asm west_done_##ID: \ + __asm mov ecx, dword ptr [edi] \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi - 0x4] \ + __asm mov ebx, dword ptr [esi + 0x4] \ + __asm mov ecx, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz north_west_done_##ID \ + __asm or edx, 0x1 \ + __asm north_west_done_##ID: \ + __asm and ebx, MASK \ + __asm jz north_east_done_##ID \ + __asm or edx, 0x40 \ + __asm north_east_done_##ID: \ + __asm and ecx, MASK \ + __asm jz north_done_##ID \ + __asm or edx, 0x80 \ + __asm north_done_##ID: \ + __asm mov ecx, dword ptr [edi + 0x10] \ + __asm pop esi \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi - 0x4] \ + __asm mov ebx, dword ptr [esi + 0x4] \ + __asm mov ecx, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz south_west_done_##ID \ + __asm or edx, 0x4 \ + __asm south_west_done_##ID: \ + __asm and ebx, MASK \ + __asm jz south_east_done_##ID \ + __asm or edx, 0x10 \ + __asm south_east_done_##ID: \ + __asm and ecx, MASK \ + __asm jz south_done_##ID \ + __asm or edx, 0x8 \ + __asm south_done_##ID: \ + __asm mov eax, this \ + __asm mov byte ptr [eax]TileMapState.bitFlag, dl + +#define MACRO_NEIGHBOUR_FLAGS_MISC_8(ID, MASK) \ + __asm mov eax, this \ + __asm mov edi, dword ptr [eax]TileMapState.ptr_MovementDirectionTranslationMatrix \ + __asm mov esi, dword ptr [eax]TileMapState.ptr_MiscDisplayLayer \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeTile \ + __asm shl eax, 0x1 \ + __asm add esi, eax \ + __asm push esi \ + __asm mov eax, this \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeY \ + __asm shl eax, 0x5 \ + __asm add edi, eax \ + __asm mov edx, 0x0 \ + __asm mov eax, dword ptr [esi + 0x2] \ + __asm mov ebx, dword ptr [esi - 0x2] \ + __asm and eax, MASK \ + __asm jz east_done_##ID \ + __asm or edx, 0x20 \ + __asm east_done_##ID: \ + __asm and ebx, MASK \ + __asm jz west_done_##ID \ + __asm or edx, 0x2 \ + __asm west_done_##ID: \ + __asm mov ecx, dword ptr [edi] \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi - 0x2] \ + __asm mov ebx, dword ptr [esi + 0x2] \ + __asm mov ecx, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz north_west_done_##ID \ + __asm or edx, 0x1 \ + __asm north_west_done_##ID: \ + __asm and ebx, MASK \ + __asm jz north_east_done_##ID \ + __asm or edx, 0x40 \ + __asm north_east_done_##ID: \ + __asm and ecx, MASK \ + __asm jz north_done_##ID \ + __asm or edx, 0x80 \ + __asm north_done_##ID: \ + __asm mov ecx, dword ptr [edi + 0x10] \ + __asm pop esi \ + __asm add esi, ecx \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi - 0x2] \ + __asm mov ebx, dword ptr [esi + 0x2] \ + __asm mov ecx, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz south_west_done_##ID \ + __asm or edx, 0x4 \ + __asm south_west_done_##ID: \ + __asm and ebx, MASK \ + __asm jz south_east_done_##ID \ + __asm or edx, 0x10 \ + __asm south_east_done_##ID: \ + __asm and ecx, MASK \ + __asm jz south_done_##ID \ + __asm or edx, 0x8 \ + __asm south_done_##ID: \ + __asm mov eax, this \ + __asm mov byte ptr [eax]TileMapState.bitFlag, dl + +#define MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(ID, MASK) \ + __asm mov eax, this \ + __asm mov edi, dword ptr [eax]TileMapState.ptr_MovementDirectionTranslationMatrix \ + __asm mov esi, dword ptr [eax]TileMapState.ptr_TerrainTypeTileMap \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeTile \ + __asm add esi, eax \ + __asm push esi \ + __asm mov eax, this \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeY \ + __asm shl eax, 0x5 \ + __asm add edi, eax \ + __asm mov edx, 0x0 \ + __asm mov eax, dword ptr [esi + 0x1] \ + __asm mov ebx, dword ptr [esi - 0x1] \ + __asm and eax, MASK \ + __asm jz east_done_##ID \ + __asm or edx, 0x20 \ + __asm east_done_##ID: \ + __asm and ebx, MASK \ + __asm jz west_done_##ID \ + __asm or edx, 0x2 \ + __asm west_done_##ID: \ + __asm mov ecx, dword ptr [edi] \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz north_done_##ID \ + __asm or edx, 0x80 \ + __asm north_done_##ID: \ + __asm mov ecx, dword ptr [edi + 0x10] \ + __asm pop esi \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz south_done_##ID \ + __asm or edx, 0x8 \ + __asm south_done_##ID: \ + __asm mov eax, this \ + __asm mov byte ptr [eax]TileMapState.bitFlag, dl + +#define MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(ID, MASK) \ + __asm mov eax, this \ + __asm mov edi, dword ptr [eax]TileMapState.ptr_MovementDirectionTranslationMatrix \ + __asm mov esi, dword ptr [eax]TileMapState.ptr_TerrainTypeTileMap \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeTile \ + __asm add esi, eax \ + __asm push esi \ + __asm mov eax, this \ + __asm mov eax, dword ptr [eax]TileMapState.DAT_SomeY \ + __asm shl eax, 0x5 \ + __asm add edi, eax \ + __asm mov edx, 0x0 \ + __asm mov eax, dword ptr [esi + 0x1] \ + __asm mov ebx, dword ptr [esi - 0x1] \ + __asm and eax, MASK \ + __asm jz east_done_##ID \ + __asm or edx, 0x20 \ + __asm east_done_##ID: \ + __asm and ebx, MASK \ + __asm jz west_done_##ID \ + __asm or edx, 0x2 \ + __asm west_done_##ID: \ + __asm mov ecx, dword ptr [edi] \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi - 0x1] \ + __asm mov ebx, dword ptr [esi + 0x1] \ + __asm mov ecx, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz north_west_done_##ID \ + __asm or edx, 0x1 \ + __asm north_west_done_##ID: \ + __asm and ebx, MASK \ + __asm jz north_east_done_##ID \ + __asm or edx, 0x40 \ + __asm north_east_done_##ID: \ + __asm and ecx, MASK \ + __asm jz north_done_##ID \ + __asm or edx, 0x80 \ + __asm north_done_##ID: \ + __asm mov ecx, dword ptr [edi + 0x10] \ + __asm pop esi \ + __asm add esi, ecx \ + __asm mov eax, dword ptr [esi - 0x1] \ + __asm mov ebx, dword ptr [esi + 0x1] \ + __asm mov ecx, dword ptr [esi] \ + __asm and eax, MASK \ + __asm jz south_west_done_##ID \ + __asm or edx, 0x4 \ + __asm south_west_done_##ID: \ + __asm and ebx, MASK \ + __asm jz south_east_done_##ID \ + __asm or edx, 0x10 \ + __asm south_east_done_##ID: \ + __asm and ecx, MASK \ + __asm jz south_done_##ID \ + __asm or edx, 0x8 \ + __asm south_done_##ID: \ + __asm mov eax, this \ + __asm mov byte ptr [eax]TileMapState.bitFlag, dl + +/* + A second handwritten macro: mark DAT_SomeTile and its eight neighbours as changed (2) in the + changed layer. It appears verbatim in createPlateau (0x00501F50), useLevelBrush (0x00502110) and + useTerrainHeightBrush (0x00502680), each time right after DAT_SomeTile and DAT_SomeY are stored. + Same tells as above: a bare push/pop of esi around the north row, and the fields read through the + global instance's absolute address. + + Clobbers eax, ecx, edx and esi. "this" is loaded once into ecx (which the original does not need) + so that no named operand is resolved while the pushed esi is on the stack. +*/ +#define MACRO_MARK_CHANGED_NEIGHBOURS() \ + __asm mov ecx, this \ + __asm mov esi, dword ptr [ecx]TileMapState.ptr_ChangedLayer \ + __asm mov eax, dword ptr [ecx]TileMapState.DAT_SomeTile \ + __asm add esi, eax \ + __asm push esi \ + __asm mov edx, dword ptr [ecx]TileMapState.ptr_MovementDirectionTranslationMatrix \ + __asm mov eax, dword ptr [ecx]TileMapState.DAT_SomeY \ + __asm shl eax, 0x5 \ + __asm add edx, eax \ + __asm mov byte ptr [esi + 0x1], 0x2 \ + __asm mov byte ptr [esi - 0x1], 0x2 \ + __asm mov byte ptr [esi], 0x2 \ + __asm mov ecx, dword ptr [edx] \ + __asm add esi, ecx \ + __asm mov byte ptr [esi - 0x1], 0x2 \ + __asm mov byte ptr [esi + 0x1], 0x2 \ + __asm mov byte ptr [esi], 0x2 \ + __asm mov ecx, dword ptr [edx + 0x10] \ + __asm pop esi \ + __asm add esi, ecx \ + __asm mov byte ptr [esi - 0x1], 0x2 \ + __asm mov byte ptr [esi + 0x1], 0x2 \ + __asm mov byte ptr [esi], 0x2 + +// clang-format on diff --git a/src/OpenSHC/Map/TileMapState/advanceMoatDigProgress.cpp b/src/OpenSHC/Map/TileMapState/advanceMoatDigProgress.cpp new file mode 100644 index 000000000..8d03dbb10 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/advanceMoatDigProgress.cpp @@ -0,0 +1,53 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" + +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500500 + undefined4 TileMapState::advanceMoatDigProgress(int moatID) + { + this->moats[moatID].fillProgress = this->moats[moatID].fillProgress + 1; + if ((char)this->moats[moatID].fillProgress <= 3) { + return 0; + } + + int tile = this->moats[moatID].tile; + this->moats[moatID].someCountDown = 0; + this->moats[moatID].fillProgress = 4; + this->moats[moatID].stage = 2; + this->HeightLayer[tile] = 0; + this->LogicLayer[this->moats[moatID].tile] + = this->LogicLayer[this->moats[moatID].tile] & ~L_MOAT_DUG_OR_PLANNED; + this->LogicLayer[this->moats[moatID].tile] = this->LogicLayer[this->moats[moatID].tile] & ~L_PLAIN2_AND_PITCH; + this->LogicLayer[this->moats[moatID].tile] = this->LogicLayer[this->moats[moatID].tile] | L_MOAT; + this->Logic2Layer[this->moats[moatID].tile] = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(2, this->moats[moatID].x, this->moats[moatID].y); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(this->moats[moatID].y, this->moats[moatID].tile); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + return 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/applyRockGraphicsToFootprint.cpp b/src/OpenSHC/Map/TileMapState/applyRockGraphicsToFootprint.cpp new file mode 100644 index 000000000..3a5769ea9 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/applyRockGraphicsToFootprint.cpp @@ -0,0 +1,67 @@ + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB770 + void TileMapState::applyRockGraphicsToFootprint(int rockID) + { + short rockX = DAT_LandscapeState::instance.rocks[rockID].x; + short rockY = DAT_LandscapeState::instance.rocks[rockID].y; + int orientation = DAT_LandscapeState::instance.rocks[rockID].orientation + this->mapOrientation; + if (orientation > 7) { + orientation = orientation - 8; + } + short size = DAT_LandscapeState::instance.rocks[rockID].size; + short surface = (short)(orientation / 2) * size * size; + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + index, DAT_LandscapeState::instance.rocks[rockID].size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + rockY].addXgetTile + rockX + + this->buildingX; + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0) { + if (this->field93_0x5548c8 == 0) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance[DAT_LandscapeState::instance.rocks[rockID] + .unknownGMID] + + (short)DAT_LandscapeState::instance.rocks[rockID].gfx + + (short)this->buildingRotationRelatedValue + surface) + - 1; + } else if (DAT_LandscapeState::instance.rocks[rockID].size == 1) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance[DAT_LandscapeState::instance.rocks[rockID] + .unknownGMID] + + (short)DAT_LandscapeState::instance.rocks[rockID].gfx + + (short)this->buildingRotationRelatedValue + surface) + - 1; + } else { + this->GfxLayer[tile] + = (short)MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::getRandomRockImageOffset, + DAT_LandscapeState::ptr)(((byte)this->RandomLayer[tile] & 3) + 1) + + (short) + GMTotalPicturesProcessed::instance[DAT_LandscapeState::instance.rocks[rockID].unknownGMID] + + surface - 1; + } + } + index++; + } while (index < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/applyTreeBrushToLogicalLayer.cpp b/src/OpenSHC/Map/TileMapState/applyTreeBrushToLogicalLayer.cpp new file mode 100644 index 000000000..23496d440 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/applyTreeBrushToLogicalLayer.cpp @@ -0,0 +1,58 @@ + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508DD0 + void TileMapState::applyTreeBrushToLogicalLayer(int treeID, int noVariation) + { + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[MACRO_CALL_MEMBER( + OpenSHC::Map::LandscapeState_Func::getValueFrom0UpTo3ForTreeTypeAndTreeStage, DAT_LandscapeState::ptr)( + DAT_LandscapeState::instance.trees[treeID].treeType, DAT_LandscapeState::instance.trees[treeID].stage)]; + int baseY = (short)DAT_LandscapeState::instance.trees[treeID].yPosition; + int baseTile = (short)DAT_LandscapeState::instance.trees[treeID].xPosition + + DAT_ViewportRenderState::instance.translationMatrix[baseY].addXgetTile; + + int tile = baseTile; + int y = baseY; + for (int tileIndex = 0; tileIndex < brushSize; tileIndex++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, tileIndex, &tile, (int*)&y, baseTile, baseY); + if ((this->LogicLayer[tile] & L_BORDER) != 0) { + break; + } + if ((this->LogicLayer[tile] & (L_SEA | L_MARSH | L_MOAT)) == 0) { + if (noVariation == 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_TREE_VARIATION; + } else { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_TREE_VARIATION; + } + } + } + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(7, + (short)DAT_LandscapeState::instance.trees[treeID].xPosition, (short)DAT_LandscapeState::instance.trees[treeID].yPosition); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/changeMapOrientation.cpp b/src/OpenSHC/Map/TileMapState/changeMapOrientation.cpp new file mode 100644 index 000000000..4b939eca2 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/changeMapOrientation.cpp @@ -0,0 +1,63 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00501B90 + void TileMapState::changeMapOrientation(int mapOrientation) + { + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::saveFocusTileAndCenterPreview, + DAT_ViewportRenderState::ptr)(); + DAT_TileMapState::instance.mapOrientation = mapOrientation; + MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::restoreFocusTile, DAT_ViewportRenderState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::forceFullTileMapRedraw, this)(); + MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::refreshAllBuildingTileDisplays, DAT_BuildingsState::ptr)(); + DAT_BuildingsState::instance.field4_0x10 = 5; + switch (this->mapOrientation) { + case 0: + this->field84_0x5548a4 = 3; + this->field88_0x5548b4 = 1; + this->field85_0x5548a8 = 2; + this->field86_0x5548ac = 4; + this->field87_0x5548b0 = 5; + return; + case 2: + this->field84_0x5548a4 = 5; + this->field88_0x5548b4 = 1; + this->field85_0x5548a8 = 4; + this->field86_0x5548ac = 6; + this->field87_0x5548b0 = 7; + return; + case 4: + this->field84_0x5548a4 = 7; + this->field85_0x5548a8 = 6; + this->field86_0x5548ac = 0; + this->field87_0x5548b0 = 1; + this->field88_0x5548b4 = -1; + return; + case 6: + this->field84_0x5548a4 = 1; + this->field85_0x5548a8 = 0; + this->field86_0x5548ac = 2; + this->field87_0x5548b0 = 3; + this->field88_0x5548b4 = -1; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/checkBuildingCanBePlacedHere.cpp b/src/OpenSHC/Map/TileMapState/checkBuildingCanBePlacedHere.cpp new file mode 100644 index 000000000..b1d81a58f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/checkBuildingCanBePlacedHere.cpp @@ -0,0 +1,904 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Synchrony/GameSynchronyState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingFailReasonEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::Buildings::BuildingFailReasonEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Unable to use type for symbol _x + */ + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005037B0 + void TileMapState::checkBuildingCanBePlacedHere( + int playerID, uint x, uint y__fertileLandCount, MappersEnum commandBuildingType, int buildingSize) + { + uint uVar1; + byte bVar2; + BuildingType _buildingType; + int _buildRange; + BOOLEnum BVar3; + int iVar4; + uint uVar5; + int iVar6; + int iVar7; + MappersEnum _commandBuildingType; + int iVar8; + int* piVar9; + int bVar10; + int bVar11; + int _grassCount; + int _distance; + int _range; + int _boulderCount; + int _ironCount; + int _oilCount; + int local_4; + uint _x; + uint _y; + /* the parameter carries the y position in, then is reused as a tile counter */ + _y = y__fertileLandCount; + _x = x; + local_4 = 0; + int sizeDefaulted = 0; + if (buildingSize < 0) { + buildingSize = 2; + sizeDefaulted = 1; + } + _commandBuildingType = (MappersEnum)(short)(undefined2)commandBuildingType; + this->buildingPlacementFail = FALSE; + this->placementWarning = 0; + this->field127_0x554944 = 0; + this->uiBuildingRotation = 0xf; + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingSpriteSheetID_1 + = DAT_BuildingDefinedData::instance.Building_SpriteSheet_ID_Array_1[_buildingType].intValue; + this->buildingSpriteID1 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::getSpriteID, DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingSpriteID2 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::getSpriteID2, DAT_BuildingsState::ptr)(_commandBuildingType); + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingHeightLimit = DAT_BuildingDefinedData::instance.BuildingPlacement_HeightLimit[_buildingType]; + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingMaxHeightDifference + = DAT_BuildingDefinedData::instance.BuildingPlacement_MaxHeightDifference[_buildingType]; + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingPlacementProperty_3 + = DAT_BuildingDefinedData::instance.BuildingPlacement_Property_3[_buildingType]; + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingPlacementProperty_4 + = DAT_BuildingDefinedData::instance.BuildingPlacement_Property_4[_buildingType]; + if (((DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SOLITARY) + || (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] != -1)) + || (DAT_GameSynchronyState::instance.currentAIArray[playerID] == 0)) { + this->buildingPlacementProperty_4 = 0; + } + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingPlacementProperty_5 + = DAT_BuildingDefinedData::instance.BuildingPlacement_Property_5[_buildingType]; + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingPlacementProperty_6 + = DAT_BuildingDefinedData::instance.BuildingPlacement_Property_6[_buildingType]; + _buildingType + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(_commandBuildingType); + this->buildingPlacementProperty_7 + = DAT_BuildingDefinedData::instance.BuildingPlacement_Property_7[_buildingType]; + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_SIEGE_TOWER_BASE) { + return; + } + _boulderCount = 0; + _ironCount = 0; + _oilCount = 0; + y__fertileLandCount = 0; + _grassCount = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::storeMinAndMaxHeightOfArea, this)(x, _y, buildingSize); + if (sizeDefaulted != 0) + goto switchD_0050397c_caseD_33; + if (0x138 < (int)_commandBuildingType) { + if (325 < (int)_commandBuildingType) { + if ((int)_commandBuildingType < 328) + goto switchD_0050397c_caseD_33; + if (_commandBuildingType == 358) + goto switchD_0050397c_caseD_be; + } + switchD_0050397c_caseD_35: + if ((DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR) + && (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT)) { + bVar10 = DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY; + bVar11 = true; + x = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + x, buildingSize); + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, + DAT_PathFindingState::ptr)(playerID, this->buildingX + _x, this->buildingY + _y, + (int)((int)((-(uint)bVar10 & 0xfffffff1) + 0x1e))); + if (BVar3 != FALSE) { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x11); + bVar11 = false; + break; + } + x = x + 1; + } while ((int)x < this->constructionTileCount); + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isSignPostWithinDistance, + DAT_PathFindingState::ptr)(_x, _y, DAT_GameState::instance.mapAndTime.unk_signpostDistance + 5); + if (BVar3 == FALSE) { + if (bVar11) + goto LAB_00503d46; + } else { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x15); + } + LAB_00503d3c: + this->buildingPlacementFail = TRUE; + } + goto LAB_00503d46; + } + if (_commandBuildingType == OpenSHC::Commands::M_MAPPER_DOG_CAGE) { + if (9 < DAT_GameState::instance.playerDataArray[playerID].dogCageCount) { + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)1000); + return; + } + switchD_0050397c_caseD_62: + local_4 = 5; + switchD_0050397c_caseD_64: + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_EDITOR) { + iVar8 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar8, buildingSize); + iVar4 + = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, + DAT_PathFindingState::ptr)(playerID, (int)((int)(this->buildingX + x)), + (int)((int)(this->buildingY + _y)), 7, -1, -1, -1); + if (iVar4 != 0) + goto LAB_005039d6; + iVar8 = iVar8 + 1; + } while (iVar8 < this->constructionTileCount); + } else if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT) { + if (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SOLITARY) { + _distance = 30; + _range = 30; + } else { + MACRO_CALL_MEMBER( + OpenSHC::Synchrony::GameSynchronyState_Func::isAIPlayer, DAT_GameSynchronyState::ptr)(playerID); + _distance = 15; + _range = 7; + } + x = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + x, buildingSize); + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, + DAT_PathFindingState::ptr)(playerID, this->buildingX + _x, this->buildingY + _y, _distance); + if (BVar3 != FALSE) { + LAB_00503b32: + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x11); + LAB_00503b48: + bVar11 = false; + break; + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + iVar8 = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, + DAT_PathFindingState::ptr)(playerID, (int)((int)(this->buildingX + _x)), + (int)((int)(this->buildingY + _y)), _range, -1, -1, -1); + if (iVar8 != 0) + goto LAB_00503b32; + _buildRange + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getCastleBuildRangeForMapSize, this)(); + iVar8 = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::isTileInRangeOfKeepRange, + DAT_PathFindingState::ptr)( + playerID, this->buildingX + _x, this->buildingY + _y, _buildRange + local_4); + if (iVar8 == 0) + goto LAB_00503b11; + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x12); + goto LAB_00503b48; + } + LAB_00503b11: + x = x + 1; + bVar11 = true; + } while ((int)x < this->constructionTileCount); + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isSignPostWithinDistance, + DAT_PathFindingState::ptr)(_x, _y, DAT_GameState::instance.mapAndTime.unk_signpostDistance + 5); + if (BVar3 == FALSE) { + if (bVar11) + goto LAB_00503d46; + } else { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x15); + } + this->buildingPlacementFail = TRUE; + if (this->buildingPlacementFailReason == OpenSHC::Map::Buildings::BFRE_DEFAULT_CANT_PLACE_THAT_THERE) { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x12); + } + } + } else { + switch (_commandBuildingType) { + case OpenSHC::Commands::M_MAPPER_WOODSMAN: + case OpenSHC::Commands::M_MAPPER_OXENBASE: + case OpenSHC::Commands::M_MAPPER_QUARRY: + case OpenSHC::Commands::M_MAPPER_TUNNEL: + case OpenSHC::Commands::M_MAPPER_TUNNEL_CONSTRUCTION: + case OpenSHC::Commands::M_MAPPER_WHEATFARM: + case OpenSHC::Commands::M_MAPPER_HOPSFARM: + case OpenSHC::Commands::M_MAPPER_APPLEFARM: + case OpenSHC::Commands::M_MAPPER_CATTLEFARM: + case OpenSHC::Commands::M_MAPPER_IRON_MINE: + case OpenSHC::Commands::M_MAPPER_PITCH_WORKINGS: + case OpenSHC::Commands::M_MAPPER_QUARRYPILE: + goto switchD_0050397c_caseD_33; + case OpenSHC::Commands::M_MAPPER_STORES: + case OpenSHC::Commands::M_MAPPER_GRANARY: + case OpenSHC::Commands::M_MAPPER_ARMOURY: + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + iVar8 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar8, buildingSize); + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, + DAT_PathFindingState::ptr)(playerID, this->buildingX + x, this->buildingY + _y, 3); + if (BVar3 != FALSE) + goto LAB_005039d6; + iVar8 = iVar8 + 1; + } while (iVar8 < this->constructionTileCount); + } + break; + default: + goto switchD_0050397c_caseD_35; + case OpenSHC::Commands::M_MAPPER_KILLING_PIT: + case OpenSHC::Commands::M_MAPPER_PITCH_DITCH: + case OpenSHC::Commands::M_MAPPER_DRAWBRIDGE: + goto switchD_0050397c_caseD_62; + case OpenSHC::Commands::M_MAPPER_GATEHOUSE: + case OpenSHC::Commands::M_MAPPER_GATE_MAIN: + case OpenSHC::Commands::M_MAPPER_GATE_INNER: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD: + case OpenSHC::Commands::M_MAPPER_GATE_POSTERN: + case OpenSHC::Commands::M_MAPPER_MOAT: + case OpenSHC::Commands::M_MAPPER_ANTIMOAT: + case OpenSHC::Commands::M_MAPPER_TOWER1: + case OpenSHC::Commands::M_MAPPER_TOWER2: + case OpenSHC::Commands::M_MAPPER_TOWER3: + case OpenSHC::Commands::M_MAPPER_TOWER4: + case OpenSHC::Commands::M_MAPPER_TOWER5: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1A: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1B: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1C: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1D: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1B: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2B: + goto switchD_0050397c_caseD_64; + case OpenSHC::Commands::M_MAPPER_CATAPULT: + case OpenSHC::Commands::M_MAPPER_TREBUCHET: + case OpenSHC::Commands::M_MAPPER_SIEGE_TOWER: + case OpenSHC::Commands::M_MAPPER_BATTERING_RAM: + case OpenSHC::Commands::M_MAPPER_PORTABLE_SHIELD: + switchD_0050397c_caseD_be: + if ((DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR) + && (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT)) { + iVar8 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar8, buildingSize); + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, + DAT_PathFindingState::ptr)(playerID, this->buildingX + x, this->buildingY + _y, 3); + if (BVar3 != FALSE) + goto LAB_005039d6; + iVar8 = iVar8 + 1; + } while (iVar8 < this->constructionTileCount); + } + } + } + LAB_00503d46: + if (this->buildingPlacementFail == FALSE) { + iVar8 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar8, buildingSize); + iVar4 = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findSomeSuitableLocationUnk, + DAT_PathFindingState::ptr)(playerID, this->buildingX + _x, this->buildingY + _y, 2); + if (iVar4 != 0) { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x11); + this->buildingPlacementFail = TRUE; + break; + } + iVar8 = iVar8 + 1; + } while (iVar8 < this->constructionTileCount); + } + switchD_0050397c_caseD_33: + if (((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_DRAWBRIDGE) && (0xc < this->buildingMaxHeight)) { + this->buildingPlacementFail = TRUE; + } + x = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, buildingSize); + uVar5 = this->buildingY + _y; + if (399 < this->buildingX + _x) { + this->buildingPlacementFail = 2; + return; + } + if (399 < uVar5) { + this->buildingPlacementFail = 2; + return; + } + if (*(char*)(uVar5 * 400 + 0x21aec98 + this->buildingX + _x) == '\0') { + this->buildingPlacementFail = 2; + return; + } + iVar8 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + this->buildingX + _x; + uVar5 = this->LogicLayer[iVar8]; + if ((uVar5 & 0x30) != 0) { + this->buildingPlacementFail = 2; + return; + } + if ((uVar5 & 0x20000) != 0) { + _boulderCount = _boulderCount + 1; + } + if ((uVar5 & 0x80000) != 0) { + _ironCount = _ironCount + 1; + } + if ((int)uVar5 < 0) { + _oilCount = _oilCount + 1; + } + if ((uVar5 & 0x100000) == 0) { + bVar2 = this->Logic2Layer[iVar8]; + if ((bVar2 & 0x10) != 0) { + /* + grass + */ + y__fertileLandCount = y__fertileLandCount + 1; + _grassCount = _grassCount + 1; + } + if ((char)bVar2 < '\0') { + /* + thick scrub + */ + y__fertileLandCount = y__fertileLandCount + 1; + _grassCount = _grassCount + 1; + } + if ((bVar2 & 1) != 0) { + /* + scrub + */ + y__fertileLandCount = y__fertileLandCount + 1; + } + } + iVar8 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + iVar8, playerID, _commandBuildingType, 0); + if (iVar8 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + } while ((int)x < this->constructionTileCount); + if (((((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_CATTLEFARM) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_WHEATFARM)) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_HOPSFARM)) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_APPLEFARM)) { + if ((int)y__fertileLandCount < this->constructionTileCount) { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x16); + this->buildingPlacementFail = TRUE; + return; + } + if (_grassCount < 50) { + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x17); + this->buildingPlacementFail = TRUE; + } + } else { + if ((undefined2)commandBuildingType != OpenSHC::Commands::M_MAPPER_QUARRY) { + if ((((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_KEEP1) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_KEEP2)) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_KEEP3)) { + buildingSize = (_commandBuildingType - OpenSHC::Commands::M_MAPPER_KEEP1) * 0x60 + 0xb491b8; + commandBuildingType = OpenSHC::Commands::M_MAPPER_NULL; + do { + uVar5 = *(int*)(buildingSize + 4) + _y; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(*(int*)buildingSize + _x, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar8 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + + *(int*)buildingSize + _x; + if ((this->LogicLayer[iVar8] & 0x30) != 0) { + this->buildingPlacementFail = 2; + return; + } + iVar8 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar8, playerID, _commandBuildingType, 0); + if (iVar8 != 0) { + this->buildingPlacementFail = TRUE; + } + buildingSize = buildingSize + 8; + commandBuildingType = (MappersEnum)(commandBuildingType + OpenSHC::Commands::M_MAPPER_AREA); + } while ((int)commandBuildingType < 3); + iVar4 = (_commandBuildingType - OpenSHC::Commands::M_MAPPER_KEEP1) * 0x20; + iVar6 = _x + *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 900); + iVar8 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 904); + x = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 7); + uVar5 = this->buildingY + _y + iVar8; + uVar1 = this->buildingX + iVar6; + if (399 < uVar1) { + this->buildingPlacementFail = 2; + return; + } + if (399 < uVar5) { + this->buildingPlacementFail = 2; + return; + } + if (*(char*)(uVar5 * 400 + 0x21aec98 + uVar1) == '\0') { + this->buildingPlacementFail = 2; + return; + } + iVar7 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + this->buildingX + + iVar6; + if ((this->LogicLayer[iVar7] & 0x30) != 0) { + this->buildingPlacementFail = 2; + return; + } + iVar7 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar7, playerID, _commandBuildingType, 0); + if (iVar7 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + } while ((int)x < this->constructionTileCount); + iVar8 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 1000); + iVar4 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 0x3e4) + _x; + x = 0; + while (true) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 5); + iVar6 = this->buildingX; + uVar5 = this->buildingY + iVar8 + _y; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(iVar4 + this->buildingX, uVar5); + if ((BVar3 == FALSE) + || (iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + iVar6 + + iVar4, + (this->LogicLayer[iVar6] & 0x30) != 0)) + break; + iVar6 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar6, playerID, _commandBuildingType, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + if (this->constructionTileCount <= (int)x) { + return; + } + } + } else { + if (((((undefined2)commandBuildingType != OpenSHC::Commands::M_MAPPER_GATE_WOOD1A) + && ((undefined2)commandBuildingType != OpenSHC::Commands::M_MAPPER_GATE_WOOD1B)) + && ((undefined2)commandBuildingType != OpenSHC::Commands::M_MAPPER_GATE_WOOD1C)) + && ((undefined2)commandBuildingType != OpenSHC::Commands::M_MAPPER_GATE_WOOD1D)) { + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_IRON_MINE) { + if (3 < _ironCount) { + return; + } + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_NOT_IRON_ORE; + this->buildingPlacementFail = TRUE; + return; + } + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_PITCH_WORKINGS) { + if (0 < _oilCount) { + return; + } + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_NOT_OIL_MARSH; + this->buildingPlacementFail = TRUE; + return; + } + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_STORES) { + if (DAT_GameState::instance.playerDataArray[playerID].stockpile.id == 0) { + return; + } + iVar8 + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getEmptyBuildingCount, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_STOCKPILE); + if (iVar8 < 0x20) { + BVar3 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::hasBuildingAsNeighbour, + DAT_BuildingsState::ptr)(playerID, (int)((int)(_x)), (int)((int)(_y)), buildingSize, + OpenSHC::Map::Buildings::BT_STOCKPILE); + if (BVar3 != FALSE) { + return; + } + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_NOT_ADJ_STOCKPILE; + return; + } + this->buildingPlacementFail = TRUE; + return; + } + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_GRANARY) { + if (DAT_GameState::instance.playerDataArray[playerID].granary.id == 0) { + return; + } + iVar8 + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getEmptyBuildingCount, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_GRANARY); + if (iVar8 < 8) { + BVar3 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::hasBuildingAsNeighbour, + DAT_BuildingsState::ptr)(playerID, (int)((int)(_x)), (int)((int)(_y)), buildingSize, + OpenSHC::Map::Buildings::BT_GRANARY); + if (BVar3 != FALSE) { + return; + } + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_NOT_ADJ_GRANARY; + return; + } + this->buildingPlacementFail = TRUE; + return; + } + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_ARMOURY) { + if (DAT_GameState::instance.playerDataArray[playerID].armory.id == 0) { + return; + } + iVar8 + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getEmptyBuildingCount, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_ARMORY); + if (iVar8 < 8) { + BVar3 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::hasBuildingAsNeighbour, + DAT_BuildingsState::ptr)(playerID, (int)((int)(_x)), (int)((int)(_y)), buildingSize, + OpenSHC::Map::Buildings::BT_ARMORY); + if (BVar3 != FALSE) { + return; + } + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_NOT_ADJ_ARMORY; + return; + } + this->buildingPlacementFail = TRUE; + return; + } + if (((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_BARRACKS_EURO) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_BARRACKS_ARAB)) { + iVar4 = _x + + DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][0] + .x; + iVar8 = DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][0] + .y; + x = 0; + while (true) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 5); + uVar5 = this->buildingY + _y + iVar8; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->buildingX + iVar4, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + + this->buildingX + iVar4; + if ((this->LogicLayer[iVar6] & 0x30) != 0) + break; + iVar6 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + iVar6, playerID, _commandBuildingType, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + if (this->constructionTileCount <= (int)x) { + iVar4 = DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][1] + .x + + _x; + iVar8 = DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][1] + .y; + x = 0; + while (true) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + x, 5); + iVar6 = this->buildingX; + uVar5 = this->buildingY + iVar8 + _y; + BVar3 = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->buildingX + iVar4, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + + iVar6 + iVar4; + if ((this->LogicLayer[iVar6] & 0x30) != 0) + break; + iVar6 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + iVar6, playerID, _commandBuildingType, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + if (this->constructionTileCount <= (int)x) { + iVar4 = DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][2] + .x + + _x; + iVar8 = DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][2] + .y; + x = 0; + while (true) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, + this)(x, 5); + iVar6 = this->buildingX; + uVar5 = this->buildingY + iVar8 + _y; + BVar3 = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->buildingX + iVar4, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5] + .addXgetTile + + iVar6 + iVar4; + if ((this->LogicLayer[iVar6] & 0x30) != 0) + break; + iVar6 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar6, playerID, _commandBuildingType, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + if (this->constructionTileCount <= (int)x) { + return; + } + } + this->buildingPlacementFail = 2; + return; + } + } + this->buildingPlacementFail = 2; + return; + } + } + this->buildingPlacementFail = 2; + return; + } + if (((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_ENGINEERS_GUILD) + || ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_TUNNELERS_GUILD)) { + iVar4 = _x + + DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][1] + .x; + iVar8 = DAT_TerrainDefinedData::instance + .BuildingPartsOffsets[this->DAT_TempBuildingRotation][1] + .y; + x = 0; + while (true) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 5); + uVar5 = this->buildingY + _y + iVar8; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->buildingX + iVar4, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + + this->buildingX + iVar4; + if ((this->LogicLayer[iVar6] & 0x30) != 0) + break; + iVar6 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + iVar6, playerID, _commandBuildingType, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + if (this->constructionTileCount <= (int)x) { + return; + } + } + this->buildingPlacementFail = 2; + return; + } + if ((undefined2)commandBuildingType == OpenSHC::Commands::M_MAPPER_OIL_SMELTER) { + iVar4 + = _x + DAT_TerrainDefinedData::instance.field63_0x19c[this->DAT_TempBuildingRotation].x; + iVar8 = DAT_TerrainDefinedData::instance.field63_0x19c[this->DAT_TempBuildingRotation].y; + x = 0; + while (true) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 4); + uVar5 = this->buildingY + _y + iVar8; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->buildingX + iVar4, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + + this->buildingX + iVar4; + if ((this->LogicLayer[iVar6] & 0x30) != 0) + break; + iVar6 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + iVar6, playerID, OpenSHC::Commands::M_MAPPER_OIL_SMELTER, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + if (this->constructionTileCount <= (int)x) { + return; + } + } + this->buildingPlacementFail = 2; + return; + } + if ((undefined2)commandBuildingType != OpenSHC::Commands::M_MAPPER_TUNNEL_CONSTRUCTION) { + return; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::algTunnelerFindTarget, + DAT_PathFindingState::ptr)( + playerID, 0, (int)((int)(76)), (int)((int)(_x)), (int)((int)(_y))); + if (DAT_PathFindingState::instance.ALG_TargetTile != 0) { + return; + } + this->buildingPlacementFail = TRUE; + return; + } + iVar8 = _commandBuildingType * 3 + -0x1a4; + iVar6 = _x + *(int*)((int)DAT_TerrainDefinedData::ptr + iVar8 * 8 + 0x56c); + iVar8 = iVar8 * 8; + iVar4 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar8 + 0x570); + x = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 3); + uVar5 = this->buildingY + _y + iVar4; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->buildingX + iVar6, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar7 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + this->buildingX + + iVar6; + if ((this->LogicLayer[iVar7] & 0x30) != 0) { + this->buildingPlacementFail = 2; + return; + } + iVar7 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar7, playerID, _commandBuildingType, 0); + if (iVar7 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + } while ((int)x < this->constructionTileCount); + iVar4 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar8 + 0x57c); + iVar8 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar8 + 0x578) + _x; + x = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(x, 3); + iVar6 = this->buildingX; + uVar5 = this->buildingY + iVar4 + _y; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(iVar8 + this->buildingX, uVar5); + if (BVar3 == FALSE) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[uVar5].addXgetTile + iVar6 + iVar8; + if ((this->LogicLayer[iVar6] & 0x30) != 0) { + this->buildingPlacementFail = 2; + return; + } + iVar6 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar6, playerID, _commandBuildingType, 0); + if (iVar6 != 0) { + this->buildingPlacementFail = TRUE; + } + x = x + 1; + } while ((int)x < this->constructionTileCount); + commandBuildingType = OpenSHC::Commands::M_MAPPER_NULL; + piVar9 + = (int*)((int)DAT_TerrainDefinedData::ptr + (_commandBuildingType * 3 + -0x1a4) * 0x10 + 0x5cc); + while (true) { + iVar8 = piVar9[1]; + iVar4 = *piVar9; + BVar3 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(_x + iVar4, iVar8 + _y); + if ((BVar3 == FALSE) + || (iVar8 = DAT_ViewportRenderState::instance.translationMatrix[iVar8 + _y].addXgetTile + + iVar4 + _x, + (this->LogicLayer[iVar8] & 0x30) != 0)) + break; + iVar8 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar8, playerID, _commandBuildingType, 0); + if (iVar8 != 0) { + this->buildingPlacementFail = TRUE; + } + commandBuildingType = (MappersEnum)(commandBuildingType + OpenSHC::Commands::M_MAPPER_AREA); + piVar9 = piVar9 + 2; + if (5 < (int)commandBuildingType) { + return; + } + } + } + this->buildingPlacementFail = 2; + return; + } + if (_boulderCount < 8) { + this->buildingPlacementFail = TRUE; + return; + } + } + return; + LAB_005039d6: + this->buildingPlacementFailReason = ((BuildingFailReasonEnum)0x11); + goto LAB_00503d3c; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/checkDrawbridgePlacement.cpp b/src/OpenSHC/Map/TileMapState/checkDrawbridgePlacement.cpp new file mode 100644 index 000000000..820a054af --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/checkDrawbridgePlacement.cpp @@ -0,0 +1,97 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + + using OpenSHC::Map::Buildings::BuildingLogicalState; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FA2D0 + void TileMapState::checkDrawbridgePlacement(int x, int y) + { + if (this->buildingPlacementFail != FALSE) { + return; + } + this->buildingPlacementFail = TRUE; + + for (int rotation = 0; rotation < 4; rotation++) { + int buildings[9]; + int count = 0; + do { + uint offsetY = DAT_TerrainDefinedData::instance.drawBridgeOffsets[rotation][count].y + y; + int offsetX = DAT_TerrainDefinedData::instance.drawBridgeOffsets[rotation][count].x; + buildings[count] = 0; + if ((uint)(offsetX + x) > 399 || offsetY > 399) { + break; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[offsetY * 400 + offsetX + x] == 0) { + break; + } + int tile = DAT_ViewportRenderState::instance.translationMatrix[offsetY].addXgetTile + offsetX + x; + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + buildings[count] = this->BuildingLayer[tile]; + count++; + } while (count < 9); + + if (buildings[0] == 0) { + continue; + } + if (DAT_BuildingsState::instance.buildings[buildings[0]].logicalState != OpenSHC::Map::Buildings::BLS_NORMAL) { + continue; + } + int span; + if (DAT_BuildingsState::instance.buildings[buildings[0]].buildingType == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE) { + span = 7; + } else { + if (DAT_BuildingsState::instance.buildings[buildings[0]].buildingType != OpenSHC::Map::Buildings::BT_GATEHOUSESMALL) { + continue; + } + span = 5; + } + /* the gatehouse faces along one axis, so only two of the four rotations can carry a bridge */ + if (DAT_BuildingsState::instance.buildings[buildings[0]].buildingVariation == 0x51) { + if (rotation == 1 || rotation == 3) { + continue; + } + } else if (DAT_BuildingsState::instance.buildings[buildings[0]].buildingVariation == 0x50 + && (rotation == 0 || rotation == 2)) { + continue; + } + + int matching = 0; + for (int i = 0; i < span; i++) { + if (buildings[i] == buildings[0]) { + matching++; + } + } + if (matching == span) { + this->buildingPlacementFail = FALSE; + this->uiBuildingRotation = rotation * 2; + this->field127_0x554944 = this->uiBuildingRotation * 25; + return; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearBuildingDisplayFlagsAndEntities.cpp b/src/OpenSHC/Map/TileMapState/clearBuildingDisplayFlagsAndEntities.cpp new file mode 100644 index 000000000..9b01d6b50 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearBuildingDisplayFlagsAndEntities.cpp @@ -0,0 +1,40 @@ + +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB360 + void TileMapState::clearBuildingDisplayFlagsAndEntities(int buildingID, int param_2) + { + int buildingX = DAT_BuildingsState::instance.buildings[buildingID].x; + int buildingY = DAT_BuildingsState::instance.buildings[buildingID].y; + uint widthOrHeight = DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, widthOrHeight); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + buildingY].addXgetTile + + this->buildingX + buildingX; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xfff3; + if (param_2 == 1) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, DAT_EntityState::ptr)(tile); + } + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearBuildingFootprintAndRemoveSiegeTower.cpp b/src/OpenSHC/Map/TileMapState/clearBuildingFootprintAndRemoveSiegeTower.cpp new file mode 100644 index 000000000..8d13b1691 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearBuildingFootprintAndRemoveSiegeTower.cpp @@ -0,0 +1,62 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::States::UnitState; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAC70 + void TileMapState::clearBuildingFootprintAndRemoveSiegeTower(int x, int y, int size) + { + int tileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(tileIndex, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_BUILDING | L_STAIRS | L_CRENEL_VARIATIONUnk + | L_KEEP_NON_MANOR_HOUSE); + this->BuildingLayer[tile] = 0; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)( + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile], tile); + short unitID = this->UnitLayer[tile]; + if (unitID != 0 && DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_TOWER) { + DAT_UnitsState::instance.units[unitID].state.generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + } + tileIndex++; + } while (tileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearBuildingFootprintAndResetUnits.cpp b/src/OpenSHC/Map/TileMapState/clearBuildingFootprintAndResetUnits.cpp new file mode 100644 index 000000000..c0280b2c9 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearBuildingFootprintAndResetUnits.cpp @@ -0,0 +1,65 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAB70 + void TileMapState::clearBuildingFootprintAndResetUnits(int x, int y, int size) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_BUILDING | L_STAIRS | L_CRENEL_VARIATIONUnk + | L_KEEP_NON_MANOR_HOUSE); + if ((this->MiscDisplayLayer[tile] & 0x20) != 0) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffdf; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + } + short buildingID = this->BuildingLayer[tile]; + this->BuildingLayer[tile] = 0; + if (DAT_BuildingsState::instance.buildings[buildingID].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isFootprintIndexOnEdge, this)( + buildingSizeTileIndex, size) + == FALSE) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x4000; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x2000; + } + for (short unitID = this->UnitLayer[tile]; unitID != 0; + unitID = DAT_UnitsState::instance.units[unitID].nextUnitOnTheSameTile) { + DAT_UnitsState::instance.units[unitID].vanish = 0; + DAT_UnitsState::instance.units[unitID].unknownCountdown_0x402 = 0; + } + buildingSizeTileIndex++; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearBuildingFootprintWithEdgeRubble.cpp b/src/OpenSHC/Map/TileMapState/clearBuildingFootprintWithEdgeRubble.cpp new file mode 100644 index 000000000..348bc685b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearBuildingFootprintWithEdgeRubble.cpp @@ -0,0 +1,59 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAD70 + void TileMapState::clearBuildingFootprintWithEdgeRubble(int x, int y, int size) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_BUILDING | L_STAIRS | L_CRENEL_VARIATIONUnk + | L_KEEP_NON_MANOR_HOUSE); + if ((this->MiscDisplayLayer[tile] & 0x20) != 0) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffdf; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + } + short buildingID = this->BuildingLayer[tile]; + this->BuildingLayer[tile] = 0; + if (DAT_BuildingsState::instance.buildings[buildingID].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isFootprintIndexOnEdge, this)( + buildingSizeTileIndex, size) + == FALSE) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x4000; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x2000; + } + buildingSizeTileIndex++; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearBuildingFromTerrain.cpp b/src/OpenSHC/Map/TileMapState/clearBuildingFromTerrain.cpp new file mode 100644 index 000000000..885a7a85c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearBuildingFromTerrain.cpp @@ -0,0 +1,96 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00515A20 + void TileMapState::clearBuildingFromTerrain(int buildingID) + { + uint y = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + uint size = DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + uint x = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_KILLINGPIT) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::validateBuildingPlacementAtTile, this)( + DAT_BuildingsState::instance.buildings[buildingID].owner, x, y); + if (this->buildingPlacementFail != FALSE) { + return; + } + } + + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_STOCKPILE) { + if (DAT_BuildingsState::instance.buildings[buildingID].field125_0x190 == 1) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearStockpileFootprintTiles, this)(x, y); + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearFixedSizeTwoBuildingFootprint, this)(x, y); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateAreaBasedOnSurrounding, this)(x, y, size); + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_WHEATFARM || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_HOPFARM) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintAndResetUnits, this)(x, y, size); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingDisplayFlagsAndEntities, this)(buildingID, 1); + int surfaceArea; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_WHEATFARM) { + surfaceArea = 36; + } else { + surfaceArea = 24; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingTilesAndTrees, this)(buildingID, surfaceArea); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::unmarkBuildingFootprintFlag, this)(x, y, 9); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateAreaBasedOnSurrounding, this)(x, y, 9); + size = 6; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_DAIRYFARM) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintAndResetUnits, this)(x, y, size); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingDisplayFlagsAndEntities, this)(buildingID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingTilesAndTrees, this)(buildingID, 0x1b); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::unmarkBuildingFootprintFlag, this)(x, y, 10); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateAreaBasedOnSurrounding, this)(x, y, 10); + size = 6; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_APPLEFARM) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintAndResetUnits, this)(x, y, size); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingDisplayFlagsAndEntities, this)(buildingID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingTilesAndTrees, this)(buildingID, 8); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::unmarkBuildingFootprintFlag, this)(x, y, 0xb); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateAreaBasedOnSurrounding, this)(x, y, 0xb); + size = 6; + } else { + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_DRAWBRIDGE) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearSizeFiveBuildingFootprintAndMoats, this)(x, y); + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_CATAPULT || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_TREBUCHET || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_BATTERINGRAM || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_SIEGETOWER || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_SHIELD || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_FIREBALLISTA) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintWithEdgeRubble, this)(x, y, size); + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintAndRemoveSiegeTower, this)(x, y, size); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintAndResetUnits, this)(x, y, size); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFootprintAndResetUnits, this)(x, y, size); + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingDisplayFlagsAndEntities, this)(buildingID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateAreaBasedOnSurrounding, this)(x, y, size); + } + + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(size + 6, x, y); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + this->field204_0x554a30 = 1; + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearBuildingTilesAndTrees.cpp b/src/OpenSHC/Map/TileMapState/clearBuildingTilesAndTrees.cpp new file mode 100644 index 000000000..e6e731787 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearBuildingTilesAndTrees.cpp @@ -0,0 +1,58 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAAB0 + void TileMapState::clearBuildingTilesAndTrees(int buildingID, int surfaceArea) + { + int _treeID; + Building* _pTileRef; + int _tile; + if (0 < surfaceArea) { + _pTileRef = &DAT_BuildingsState::instance.buildings[buildingID]; + do { + _tile = _pTileRef->tileRefs[0]; + if ((((this->LogicLayer[_tile] & L_FARM_FIELD_APPLE) != 0) + && (_treeID = (int)this->OrganismLayer[_tile], _treeID != 0)) + && (_treeID < 2000)) { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)(_treeID); + } + this->LogicLayer[_tile] = this->LogicLayer[_tile] + & ~(L_BUILDING | L_FARM_FIELD_WHEAT | L_FARM_FIELD_HOP | L_FARM_FIELD_APPLE | L_FARM_FIELD_DAIRY); + this->DamageLayer[_tile] = 0; + this->BuildingLayer[_tile] = 0; + if (DAT_BuildingsState::instance.buildings[buildingID].noRubble == 0) { + this->BuildingWasLayer[_tile] = '\0'; + } + this->ChangedLayer[_tile] = 2; + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)( + (int)DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_tile], _tile); + /* + process next tile + */ + _pTileRef = &_pTileRef->tileRefs[1]; + surfaceArea = surfaceArea + -1; + } while (surfaceArea != 0); + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearDrawBridgeWater.cpp b/src/OpenSHC/Map/TileMapState/clearDrawBridgeWater.cpp new file mode 100644 index 000000000..fafa2f2c0 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearDrawBridgeWater.cpp @@ -0,0 +1,42 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508870 + void TileMapState::clearDrawBridgeWater(int x, int y) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 5); + int targetedTile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + this->LogicLayer[targetedTile] = this->LogicLayer[targetedTile] & ~L_MOAT; + this->HeightLayer[targetedTile] = 8; + uint moatID = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(targetedTile); + if (moatID != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatData, this)(moatID); + this->LogicLayer[targetedTile] = this->LogicLayer[targetedTile] & ~L_MOAT; + this->BuildingWasLayer[targetedTile] = 0; + } + this->BuildingLayer[targetedTile] = 0; + buildingSizeTileIndex++; + this->BuildingWasLayer[targetedTile] = 0; + this->ChangedLayer[targetedTile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearFixedSizeTwoBuildingFootprint.cpp b/src/OpenSHC/Map/TileMapState/clearFixedSizeTwoBuildingFootprint.cpp new file mode 100644 index 000000000..9a0a736ad --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearFixedSizeTwoBuildingFootprint.cpp @@ -0,0 +1,49 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAE50 + void TileMapState::clearFixedSizeTwoBuildingFootprint(int x, int y) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 2); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_STOCKPILEUnk | L_WALL_OR_GATEHOUSE | L_CRENEL | L_BUILDING | L_STAIRS | L_CRENEL_VARIATIONUnk + | L_KEEP_NON_MANOR_HOUSE); + int buildingID = this->BuildingLayer[tile]; + this->BuildingLayer[tile] = 0; + if (DAT_BuildingsState::instance.buildings[buildingID].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x4000; + } + buildingSizeTileIndex++; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearInvalidMoatEntries.cpp b/src/OpenSHC/Map/TileMapState/clearInvalidMoatEntries.cpp new file mode 100644 index 000000000..b0a7f46b2 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearInvalidMoatEntries.cpp @@ -0,0 +1,37 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005006C0 + void TileMapState::clearInvalidMoatEntries() + { + for (int moatID = 1; moatID < this->currentMoatCount; moatID++) { + if (this->moats[moatID].owner == 0) { + continue; + } + int tile = this->moats[moatID].tile; + if ((DAT_TileMapState::instance.LogicLayer[tile] & (L_MOAT_DUG_OR_PLANNED | L_MOAT)) != 0) { + continue; + } + if (this->BuildingLayer[tile] != 0) { + continue; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatData, this)(moatID); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearMoat.cpp b/src/OpenSHC/Map/TileMapState/clearMoat.cpp new file mode 100644 index 000000000..3e42a0053 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearMoat.cpp @@ -0,0 +1,33 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005005F0 + void TileMapState::clearMoat(int tile) + { + if ((this->LogicLayer[tile] & L_MOAT_DUG_OR_PLANNED) == 0) { + return; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatDataAtTile, this)(tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile, + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]); + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT_DUG_OR_PLANNED; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearMoatData.cpp b/src/OpenSHC/Map/TileMapState/clearMoatData.cpp new file mode 100644 index 000000000..13d0818ec --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearMoatData.cpp @@ -0,0 +1,28 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005002D0 + void TileMapState::clearMoatData(uint moatID) + { + if (moatID > 15999) { + return; + } + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(Moat), 0, &this->moats[moatID]); + this->moatTileCount = this->moatTileCount - 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearMoatDataAtTile.cpp b/src/OpenSHC/Map/TileMapState/clearMoatDataAtTile.cpp new file mode 100644 index 000000000..f7282fa57 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearMoatDataAtTile.cpp @@ -0,0 +1,28 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500310 + void TileMapState::clearMoatDataAtTile(int x, int y) + { + for (int moatID = 0; moatID < this->currentMoatCount; moatID++) { + if (this->moats[moatID].x == x && this->moats[moatID].y == y) { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(Moat), 0, &this->moats[moatID]); + this->moatTileCount = this->moatTileCount - 1; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearRockFootprintFlags.cpp b/src/OpenSHC/Map/TileMapState/clearRockFootprintFlags.cpp new file mode 100644 index 000000000..3d79072c7 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearRockFootprintFlags.cpp @@ -0,0 +1,36 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_ROCKY; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB970 + void TileMapState::clearRockFootprintFlags(int rockID) + { + int rockX = (short)DAT_LandscapeState::instance.rocks[rockID].x; + int rockY = (short)DAT_LandscapeState::instance.rocks[rockID].y; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, DAT_LandscapeState::instance.rocks[rockID].size); + buildingSizeTileIndex++; + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + rockY].addXgetTile + rockX + + this->buildingX; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->OrganismLayer[tile] = 0; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearSizeFiveBuildingFootprintAndMoats.cpp b/src/OpenSHC/Map/TileMapState/clearSizeFiveBuildingFootprintAndMoats.cpp new file mode 100644 index 000000000..1d87c3ef0 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearSizeFiveBuildingFootprintAndMoats.cpp @@ -0,0 +1,60 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_MOAT; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508760 + void TileMapState::clearSizeFiveBuildingFootprintAndMoats(int x, int y) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 5); + int targetedTile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + short buildingID = this->BuildingLayer[targetedTile]; + short variation = DAT_BuildingsState::instance.buildings[buildingID].buildingVariation; + this->LogicLayer[targetedTile] = this->LogicLayer[targetedTile] & ~L_BUILDING; + if (DAT_TerrainDefinedData::instance.DrawbridgeOrientationMapping[variation / 2][buildingSizeTileIndex] + != 0) { + uint moatID + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(targetedTile); + if (moatID != 0) { + if (DAT_BuildingsState::instance.buildings[buildingID].noRubble == 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatData, this)(moatID); + this->LogicLayer[targetedTile] = this->LogicLayer[targetedTile] & ~L_MOAT; + this->Logic2Layer[targetedTile] = this->Logic2Layer[targetedTile] | L2_EARTH_AND_STONES; + this->BuildingWasLayer[targetedTile] = 0; + this->HeightLayer[targetedTile] = 8; + } else { + this->LogicLayer[targetedTile] = this->LogicLayer[targetedTile] | L_MOAT; + } + } + } + if ((this->LogicLayer[targetedTile] & L_MOAT) == 0) { + this->Logic2Layer[targetedTile] = this->Logic2Layer[targetedTile] | L2_EARTH_AND_STONES; + this->BuildingWasLayer[targetedTile] = 0; + } + this->BuildingLayer[targetedTile] = 0; + buildingSizeTileIndex++; + this->ChangedLayer[targetedTile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearStockpileFootprintTiles.cpp b/src/OpenSHC/Map/TileMapState/clearStockpileFootprintTiles.cpp new file mode 100644 index 000000000..af75f0302 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearStockpileFootprintTiles.cpp @@ -0,0 +1,64 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAF70 + void TileMapState::clearStockpileFootprintTiles(int x, int y) + { + int tile; + for (int offset = 0; offset < 9; offset += 3) { + tile = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.StockpilePathableOffsets[offset + 0].y + y] + .addXgetTile + + x + DAT_TerrainDefinedData::instance.StockpilePathableOffsets[offset + 0].x; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_STOCKPILEUnk | L_WALL_OR_GATEHOUSE); + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + if (DAT_BuildingsState::instance.buildings[this->AlphaGFXLayer[tile]].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x4000; + } + this->AlphaGFXLayer[tile] = 0; + tile = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.StockpilePathableOffsets[offset + 1].y + y] + .addXgetTile + + x + DAT_TerrainDefinedData::instance.StockpilePathableOffsets[offset + 1].x; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_STOCKPILEUnk | L_WALL_OR_GATEHOUSE); + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + if (DAT_BuildingsState::instance.buildings[this->AlphaGFXLayer[tile]].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x4000; + } + this->AlphaGFXLayer[tile] = 0; + tile = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.StockpilePathableOffsets[offset + 2].y + y] + .addXgetTile + + x + DAT_TerrainDefinedData::instance.StockpilePathableOffsets[offset + 2].x; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_STOCKPILEUnk | L_WALL_OR_GATEHOUSE); + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + if (DAT_BuildingsState::instance.buildings[this->AlphaGFXLayer[tile]].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x4000; + } + this->AlphaGFXLayer[tile] = 0; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/clearTreeFootprintFlags.cpp b/src/OpenSHC/Map/TileMapState/clearTreeFootprintFlags.cpp new file mode 100644 index 000000000..2e61daab1 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearTreeFootprintFlags.cpp @@ -0,0 +1,39 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB8E0 + void TileMapState::clearTreeFootprintFlags(int treeID) + { + int treeX = (short)DAT_LandscapeState::instance.trees[treeID].xPosition; + int treeY = (short)DAT_LandscapeState::instance.trees[treeID].yPosition; + this->OrganismLayer[DAT_LandscapeState::instance.trees[treeID].tile] = 0; + this->LogicLayer[DAT_LandscapeState::instance.trees[treeID].tile] + = this->LogicLayer[DAT_LandscapeState::instance.trees[treeID].tile] & ~L_TREE; + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + index, DAT_LandscapeState::instance.trees[treeID].size); + index++; + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + treeY].addXgetTile + + this->buildingX + treeX; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_TREE_VARIATION; + } while (index < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/collectCliffEdgeTilesForClimbData.cpp b/src/OpenSHC/Map/TileMapState/collectCliffEdgeTilesForClimbData.cpp new file mode 100644 index 000000000..488778c2b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/collectCliffEdgeTilesForClimbData.cpp @@ -0,0 +1,67 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ClimbLogicDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_HIGH; + using OpenSHC::Map::LogicHelpers::L2_STONES_OR_DRIVEN_SANDUnk; + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_SEA; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC340 + void TileMapState::collectCliffEdgeTilesForClimbData() + { + this->temporaryTerrainTypeIndex = 0; + for (int tile = 0; tile < 80400; tile++) { + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + continue; + } + + if ((this->LogicLayer[tile] & L_RIVER) != 0 + && (this->Logic2Layer[tile] & L2_STONES_OR_DRIVEN_SANDUnk) != 0) { + this->temporaryTerrainTypeBinaryArray[this->temporaryTerrainTypeIndex] = 1; + this->temporaryTerrainTypeArray[this->temporaryTerrainTypeIndex] = tile; + for (int index = 0; index < 8; index++) { + int neighbour + = this->directionTranslationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[tile]] + [DAT_ClimbLogicDefinedData::instance.field5_0x2c[index]] + + tile; + if ((this->LogicLayer[neighbour] & L_RIVER) != 0 + && this->HeightLayer[tile] + 0x50 < (uint)this->HeightLayer[neighbour]) { + this->temporaryTerrainTypeBinaryArray[this->temporaryTerrainTypeIndex] = 0; + break; + } + } + this->temporaryTerrainTypeIndex = this->temporaryTerrainTypeIndex + 1; + if ((int)this->temporaryTerrainTypeIndex > 999) { + return; + } + } + + if (((this->LogicLayer[tile] & L_RIVER) != 0 && (this->Logic2Layer[tile] & L2_OASIS_GRASS) != 0) + || ((this->LogicLayer[tile] & L_SEA) != 0 && (this->Logic2Layer[tile] & L2_PLATEAU_HIGH) != 0)) { + this->temporaryTerrainTypeBinaryArray[this->temporaryTerrainTypeIndex] = 1; + this->temporaryTerrainTypeArray[this->temporaryTerrainTypeIndex] = tile; + this->temporaryTerrainTypeIndex = this->temporaryTerrainTypeIndex + 1; + if ((int)this->temporaryTerrainTypeIndex > 999) { + return; + } + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/computeClimbRampRotation.cpp b/src/OpenSHC/Map/TileMapState/computeClimbRampRotation.cpp new file mode 100644 index 000000000..c7dd32baa --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/computeClimbRampRotation.cpp @@ -0,0 +1,96 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC810 + int TileMapState::computeClimbRampRotation(int tile, uint x, uint y) + { + uint height; + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0) { + height = this->HeightLayer[tile]; + } else { + height = this->DefaultHeightLayer[tile]; + } + + int neighbour = this->directionTranslationMatrix[y][this->field86_0x5548ac] + tile; + uint heightAt86; + if ((this->LogicLayer[neighbour] & L_WALL_OR_GATEHOUSE) == 0) { + heightAt86 = this->HeightLayer[neighbour]; + } else { + heightAt86 = this->DefaultHeightLayer[neighbour]; + } + + neighbour = this->directionTranslationMatrix[y][this->field85_0x5548a8] + tile; + uint heightAt85; + if ((this->LogicLayer[neighbour] & L_WALL_OR_GATEHOUSE) == 0) { + heightAt85 = this->HeightLayer[neighbour]; + } else { + heightAt85 = this->DefaultHeightLayer[neighbour]; + } + + neighbour = this->directionTranslationMatrix[y][this->field84_0x5548a4] + tile; + uint heightAt84; + if ((this->LogicLayer[neighbour] & L_WALL_OR_GATEHOUSE) == 0) { + heightAt84 = this->HeightLayer[neighbour]; + } else { + heightAt84 = this->DefaultHeightLayer[neighbour]; + } + + uint rotation; + if (heightAt86 + 0x14 < height && heightAt86 < 0x88) { + if (heightAt85 + 0x14 < height && heightAt85 < 0x88) { + if (heightAt84 + 0x14 < height && heightAt84 < 0x88) { + rotation = 5; + } else { + rotation = 3; + } + } else { + rotation = 1; + } + } else if (heightAt85 + 0x14 < height && heightAt85 < 0x88) { + if (heightAt86 + 0x14 < height && heightAt86 < 0x88) { + rotation = 3; + } else { + rotation = 2; + } + } else if (heightAt86 < 0x88 || heightAt85 < 0x88) { + /* 0 when the tile is not high enough above its neighbour, 5 when it is */ + rotation = (uint)(height <= heightAt84 + 0x14) - 1 & 5; + } else { + rotation = 4; + } + + if (this->mapOrientation == 0) { + if (rotation == 1) { + this->field112_0x554908 = 0x20 - (x & 0x1f); + } else { + this->field112_0x554908 = 0x20 - (y & 0x1f); + } + } else if (this->mapOrientation == 4) { + if (rotation == 1) { + this->field112_0x554908 = (x & 0x1f) + 1; + } else { + this->field112_0x554908 = (y & 0x1f) + 1; + } + } else if (this->mapOrientation == 2) { + this->field112_0x554908 = (y & 0x1f) + 1; + } else { + this->field112_0x554908 = 0x20 - (y & 0x1f); + } + if (this->field112_0x554908 > 0x1f) { + this->field112_0x554908 = 1; + } + return rotation; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/computeTileAlongAxisOffset.cpp b/src/OpenSHC/Map/TileMapState/computeTileAlongAxisOffset.cpp new file mode 100644 index 000000000..545ad94f3 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/computeTileAlongAxisOffset.cpp @@ -0,0 +1,33 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500B50 + int TileMapState::computeTileAlongAxisOffset(int tile, int addend, uint offset) + { + int yStep = 1; + int direction = 4; + if ((int)offset < 0) { + yStep = -1; + direction = 0; + } + + int y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + for (int remaining = abs((int)offset); remaining > 0; remaining--) { + tile = tile + this->directionTranslationMatrix[y][direction]; + y = y + yStep; + } + return tile + addend; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/computeTileCliffEdgeFlags.cpp b/src/OpenSHC/Map/TileMapState/computeTileCliffEdgeFlags.cpp new file mode 100644 index 000000000..0271b3297 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/computeTileCliffEdgeFlags.cpp @@ -0,0 +1,114 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Game/GameMode.hpp" + +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_RIVER; + + using OpenSHC::Game::GameMode; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC0B0 + void TileMapState::computeTileCliffEdgeFlags(int tile, int x, int y) + { + int height = this->HeightLayer[tile]; + if (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_MULTIPLAYER) { + return; + } + + /* the height of each of the eight neighbours, or this tile's own where there is no river */ + int heights[8]; + int lowest = height; + int highest = height; + for (int i = 0; i < 8; i++) { + uint neighbourY = y + DAT_TerrainDefinedData::instance.field2476_0x372c[i].y; + uint neighbourX = x + DAT_TerrainDefinedData::instance.field2476_0x372c[i].x; + heights[i] = height; + if (neighbourX < 400 && neighbourY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + neighbourX] != 0) { + int neighbour = DAT_ViewportRenderState::instance.translationMatrix[neighbourY].addXgetTile + + DAT_TerrainDefinedData::instance.field2476_0x372c[i].x + x; + if ((this->LogicLayer[neighbour] & L_RIVER) != 0) { + heights[i] = this->HeightLayer[neighbour]; + if (heights[i] < lowest) { + lowest = heights[i]; + } else if (heights[i] > highest) { + highest = heights[i]; + } + } + } + } + + this->Logic2Layer[tile] + = this->Logic2Layer[tile] & (OpenSHC::Map::LogicHelpers::L2_BEACH | OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS | OpenSHC::Map::LogicHelpers::L2_PLATEAU_HIGH); + byte flags = this->Logic2Layer[tile]; + if (lowest >= height) { + /* a bank all round: low step becomes a beach, a high one a cliff */ + if (highest > height) { + if (highest <= height + 0x10) { + flags = flags | 0x80; + } else { + flags = flags | 0x40; + } + } + } else if (this->mapOrientation == 4) { + if (heights[0] >= height) { + if (heights[6] < height) { + flags = flags | 4; + } + } else if (heights[6] < height) { + flags = flags | 1; + } else { + flags = flags | 2; + } + } else { + /* the bank runs along whichever axis the map is turned to */ + int along = 0; + int across = 0; + bool turned = true; + if (this->mapOrientation == 0) { + along = heights[2]; + across = heights[4]; + } else if (this->mapOrientation == 2) { + along = heights[4]; + across = heights[6]; + } else if (this->mapOrientation == 6) { + along = heights[0]; + across = heights[2]; + } else { + turned = false; + } + if (turned) { + if (along < height) { + if (across < height) { + flags = flags | 1; + } else { + flags = flags | 4; + } + } else if (across < height) { + flags = flags | 2; + } + } + } + this->Logic2Layer[tile] = flags; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/computeTileLuminescence.cpp b/src/OpenSHC/Map/TileMapState/computeTileLuminescence.cpp new file mode 100644 index 000000000..3b05be5df --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/computeTileLuminescence.cpp @@ -0,0 +1,290 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F7A80 + void TileMapState::computeTileLuminescence(int tile, int y) + { + uint logic = this->LogicLayer[tile]; + int shade = 2; + int alongShade = 2; + int acrossShade = 2; + int diagonal = 0; + int opposite = 0; + uint along = 0; + uint across = 0; + if ((logic & (L_BORDER | L_BORDER_EDGE | L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0) { + return; + } + + /* the light comes from one corner of the screen, so the rays turn with the map */ + switch (this->mapOrientation) { + case 0: + diagonal = 5; + opposite = 1; + along = 4; + across = 6; + break; + case 2: + diagonal = 7; + opposite = 3; + along = 6; + across = 0; + break; + case 4: + diagonal = 1; + opposite = 5; + along = 0; + across = 2; + break; + case 6: + diagonal = 3; + opposite = 7; + along = 2; + across = 4; + } + + uint ownHeight = this->HeightLayer[tile]; + if ((logic & L_TREE) != 0) { + int stage = MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::getTreeGrowthTargetStage, DAT_LandscapeState::ptr)( + this->OrganismLayer[tile]); + if (stage > 2) { + shade = stage; + } + } else if ((logic & L_TREE_VARIATION) != 0) { + shade = 5; + } + + /* first ray: along the light's diagonal, shading hardest close to the tile */ + uint height = 0; + int rayTile = tile; + int rayY = y; + uint step = 6; + do { + rayTile = rayTile + this->directionTranslationMatrix[rayY][diagonal]; + rayY = rayY + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[diagonal].int_.yOffset; + if ((this->LogicLayer[rayTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + if ((this->LogicLayer[rayTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0) { + height = this->HeightLayer[rayTile]; + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag3, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]) + == 0) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]) + < 0x10) { + height = this->HeightLayer[rayTile] + 0x10; + } else { + height = (uint)this->HeightLayer[rayTile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]); + } + } + if ((this->LogicLayer[rayTile] & L_TREE) != 0) { + height = height + + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::getTreeGrowthTargetStage, DAT_LandscapeState::ptr)( + this->OrganismLayer[rayTile]) + * 7; + } else if ((this->LogicLayer[rayTile] & L_TREE_VARIATION) != 0) { + height = height + 0x28; + } + int rise = height - ownHeight; + uint lit; + if (rise < 1) { + lit = 2; + } else if (rise > 0x12) { + lit = step + 1; + } else if (rise < 3) { + lit = 0; + } else if (rise < 7) { + lit = step + -3; + } else if (rise < 0xb) { + lit = step + -2; + } else if (rise < 0xf) { + lit = step + -1; + } else { + lit = step; + } + if (shade < (int)lit) { + shade = lit; + } + step = step - 1; + } while ((int)step > 1); + + /* second ray: one cardinal step off the diagonal */ + rayTile = this->directionTranslationMatrix[y][along] + tile; + rayY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[along].int_.yOffset + y; + along = 4; + uint sampled = height; + do { + if ((this->LogicLayer[rayTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + if ((this->LogicLayer[rayTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0) { + sampled = this->HeightLayer[rayTile]; + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag3, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]) + == 0) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]) + < 0x10) { + sampled = this->HeightLayer[rayTile] + 0x10; + } else { + sampled = (uint)this->HeightLayer[rayTile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]); + } + } + if ((this->LogicLayer[rayTile] & L_TREE) != 0) { + sampled = sampled + + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::getTreeGrowthTargetStage, DAT_LandscapeState::ptr)( + this->OrganismLayer[rayTile]) + * 7; + } else if ((this->LogicLayer[rayTile] & L_TREE_VARIATION) != 0) { + sampled = sampled + 0x28; + } + int rise = sampled - ownHeight; + if (rise < 1) { + height = 2; + } else if (rise > 0x12) { + height = along + 1; + } else if (rise < 7) { + height = 0; + } else if (rise < 0xd) { + height = along - 1; + } else { + height = along; + } + if (alongShade < (int)height) { + alongShade = height; + } + rayTile = rayTile + this->directionTranslationMatrix[rayY][diagonal]; + along = along - 1; + rayY = rayY + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[diagonal].int_.yOffset; + } while (along < 0x80000000); + + /* third ray: the same, one cardinal step the other way */ + rayTile = this->directionTranslationMatrix[y][across] + tile; + rayY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[across].int_.yOffset + y; + across = 4; + do { + if ((this->LogicLayer[rayTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + if ((this->LogicLayer[rayTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0) { + sampled = this->HeightLayer[rayTile]; + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag3, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]) + == 0) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]) + < 0x10) { + sampled = this->HeightLayer[rayTile] + 0x10; + } else { + sampled = (uint)this->HeightLayer[rayTile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[rayTile]); + } + } + if ((this->LogicLayer[rayTile] & L_TREE) != 0) { + sampled = sampled + + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::getTreeGrowthTargetStage, DAT_LandscapeState::ptr)( + this->OrganismLayer[rayTile]) + * 7; + } else if ((this->LogicLayer[rayTile] & L_TREE_VARIATION) != 0) { + sampled = sampled + 0x28; + } + int rise = sampled - ownHeight; + if (rise < 1) { + height = 2; + } else if (rise > 0x12) { + height = across + 1; + } else if (rise < 7) { + height = 0; + } else if (rise < 0xd) { + height = across - 1; + } else { + height = across; + } + if (acrossShade < (int)height) { + acrossShade = height; + } + rayTile = rayTile + this->directionTranslationMatrix[rayY][diagonal]; + across = across - 1; + rayY = rayY + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[diagonal].int_.yOffset; + } while (across < 0x80000000); + + if (shade + -2 < (int)((acrossShade - 4) + alongShade)) { + shade = (acrossShade - 2) + alongShade; + if (shade > 6) { + shade = 7; + } + } else if (shade == 2) { + /* nothing casts onto the tile, so shade it by the slope it sits on */ + int diagonalTile = this->directionTranslationMatrix[y][diagonal] + tile; + uint lightSide = ownHeight; + if ((this->LogicLayer[diagonalTile] & (L_BORDER | L_BORDER_EDGE)) == 0) { + if ((this->LogicLayer[diagonalTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0) { + lightSide = this->HeightLayer[diagonalTile]; + } else { + lightSide = sampled; + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag3, DAT_BuildingsState::ptr)( + this->BuildingLayer[diagonalTile]) + == 0) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)( + this->BuildingLayer[diagonalTile]) + < 0x10) { + lightSide = this->HeightLayer[diagonalTile] + 0x10; + } else { + lightSide = (uint)this->HeightLayer[diagonalTile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(this->BuildingLayer[diagonalTile]); + } + } + } + } + int oppositeTile = this->directionTranslationMatrix[y][opposite] + tile; + uint darkSide = ownHeight; + if ((this->LogicLayer[oppositeTile] & (L_BORDER | L_BORDER_EDGE)) == 0) { + darkSide = this->HeightLayer[oppositeTile]; + } + char slope = (int)lightSide < (int)(ownHeight - 4); + if (ownHeight + 4 < darkSide) { + slope = slope + '\x01'; + } + if ((this->LogicLayer[tile] & L_MOAT) == 0) { + if (slope == '\x01') { + shade = 1; + } else if (slope == '\x02') { + shade = 0; + } + } + } + this->LuminesenceLayer[tile] = (uchar)shade; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/computeWallCornerRenderRotation.cpp b/src/OpenSHC/Map/TileMapState/computeWallCornerRenderRotation.cpp new file mode 100644 index 000000000..af521d95f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/computeWallCornerRenderRotation.cpp @@ -0,0 +1,93 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC650 + int TileMapState::computeWallCornerRenderRotation(uint x) + { + int tile = this->DAT_SomeTile; + int y = this->DAT_SomeY; + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isWallConnectionHeightValid, this)( + this->DAT_SomeTile, this->DAT_SomeY, 2) + != FALSE) { + if (this->mapOrientation == 0) { + if ((x & 0xf) != 0xf) { + return 17 - (x & 0xf); + } + return 2; + } + if (this->mapOrientation == 4) { + if ((x & 0xf) != 0) { + return (x & 0xf) + 2; + } + return 2; + } + if (this->mapOrientation == 2) { + if ((x & 0xf) == 0) { + return 1; + } + return (x & 0xf) + 0x11; + } + if (this->mapOrientation != 6) { + return 0; + } + if ((x & 0xf) == 0xf) { + return 1; + } + return 32 - (x & 0xf); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isWallConnectionHeightValid, this)(tile, y, 0) + != FALSE) { + if (this->mapOrientation == 0) { + if ((y & 0xf) != 0xf) { + return 32 - (y & 0xf); + } + return 1; + } + if (this->mapOrientation == 4) { + if ((y & 0xf) != 0) { + return (y & 0xf) + 0x11; + } + return 1; + } + if (this->mapOrientation == 6) { + if ((y & 0xf) == 0) { + return 2; + } + return (y & 0xf) + 2; + } + if (this->mapOrientation != 2) { + return 0; + } + if ((y & 0xf) == 0xf) { + return 2; + } + return 0x11 - (y & 0xf); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getWallFlagForOrientedDirection, this)(tile, y, 2) + != 0) { + return 48; + } + /* 0x3f when the other direction carries a wall too, 0x21 when it does not */ + return (-(uint)(MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getWallFlagForOrientedDirection, this)( + tile, y, 4) + != 0) + & 0x1e) + + 0x21; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/countLogicPropertyInSurroundingTiles.cpp b/src/OpenSHC/Map/TileMapState/countLogicPropertyInSurroundingTiles.cpp new file mode 100644 index 000000000..59f262ebd --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/countLogicPropertyInSurroundingTiles.cpp @@ -0,0 +1,31 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8530 + void TileMapState::countLogicPropertyInSurroundingTiles(int tile, int y, uint logic1) + { + this->DAT_CardinalTilesAroundTile = 0; + if ((this->LogicLayer[this->directionTranslationMatrix[y][0] + tile] & logic1) != 0) { + this->DAT_CardinalTilesAroundTile++; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][2] + tile] & logic1) != 0) { + this->DAT_CardinalTilesAroundTile++; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][4] + tile] & logic1) != 0) { + this->DAT_CardinalTilesAroundTile++; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][6] + tile] & logic1) != 0) { + this->DAT_CardinalTilesAroundTile++; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/countMoatsOwnedByEachPlayer.cpp b/src/OpenSHC/Map/TileMapState/countMoatsOwnedByEachPlayer.cpp new file mode 100644 index 000000000..f6e120b27 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/countMoatsOwnedByEachPlayer.cpp @@ -0,0 +1,35 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005000E0 + void TileMapState::countMoatsOwnedByEachPlayer() + { + DAT_GameState::instance.playerDataArray[1].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[2].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[3].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[4].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[5].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[6].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[7].moatsOwned = 0; + DAT_GameState::instance.playerDataArray[8].moatsOwned = 0; + for (int moatID = 1; moatID < 16000; moatID++) { + char owner = this->moats[moatID].owner; + if (owner != 0) { + DAT_GameState::instance.playerDataArray[owner].moatsOwned + = DAT_GameState::instance.playerDataArray[owner].moatsOwned + 1; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/countUnfinishedMoatTilesForPlayer.cpp b/src/OpenSHC/Map/TileMapState/countUnfinishedMoatTilesForPlayer.cpp new file mode 100644 index 000000000..571fdbf2c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/countUnfinishedMoatTilesForPlayer.cpp @@ -0,0 +1,28 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500180 + int TileMapState::countUnfinishedMoatTilesForPlayer(int playerID) + { + int unfinishedMoatTiles = 0; + for (int moatID = 1; moatID < 16000; moatID++) { + if (this->moats[moatID].owner != 0 && this->moats[moatID].owner == playerID + && (this->LogicLayer[this->moats[moatID].tile] & L_MOAT) == 0) { + unfinishedMoatTiles++; + } + } + return unfinishedMoatTiles; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/countUnownedPitchDitches.cpp b/src/OpenSHC/Map/TileMapState/countUnownedPitchDitches.cpp new file mode 100644 index 000000000..2c34cd403 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/countUnownedPitchDitches.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500800 + int TileMapState::countUnownedPitchDitches() + { + int unownedPitchDitches = 0; + for (int pitchDitchID = 0; pitchDitchID < 4000; pitchDitchID++) { + if (this->pitchDitches[pitchDitchID].owner == 0) { + unownedPitchDitches++; + } + } + return unownedPitchDitches; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/createMoatData.cpp b/src/OpenSHC/Map/TileMapState/createMoatData.cpp new file mode 100644 index 000000000..6f1a52f25 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/createMoatData.cpp @@ -0,0 +1,71 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode2.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Game::GameMode2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005110B0 + int TileMapState::createMoatData(undefined4 playerID, uint x, uint y, int filled) + { + if (x > 399 || y > 399) { + return 0; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return 0; + } + + int existingMoatID = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)( + DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x); + if (existingMoatID != 0) { + return existingMoatID; + } + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_SIEGE_THAT + && MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::hasEnoughGoldForMoat, DAT_BuildingsState::ptr)() + == 0) { + return 0; + } + + for (int moatID = 1; moatID < 16000; moatID++) { + if (this->moats[moatID].owner != 0) { + continue; + } + + if (this->currentMoatCount <= moatID) { + this->currentMoatCount = moatID + 1; + } + this->moats[moatID].owner = (char)playerID; + this->moats[moatID].x = (short)x; + this->moats[moatID].y = (short)y; + this->moats[moatID].tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + this->moats[moatID].zeroOrTwo = 2; + if (filled == 0) { + this->moats[moatID].stage = 1; + this->moats[moatID].fillProgress = 0; + this->moatTileCount = this->moatTileCount + 1; + return moatID; + } + this->moats[moatID].stage = 2; + this->moats[moatID].fillProgress = 4; + this->moatTileCount = this->moatTileCount + 1; + return moatID; + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/createPlateau.cpp b/src/OpenSHC/Map/TileMapState/createPlateau.cpp new file mode 100644 index 000000000..e4a3bcf2a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/createPlateau.cpp @@ -0,0 +1,86 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00501F50 + void TileMapState::createPlateau(int tile, uint the_y, int brush, int plateauHeightSetting) + { + int baseTile = tile; + uint baseY = the_y; + /* the original reuses the brush parameter to hold the size */ + brush = DAT_TerrainDefinedData::instance.BrushSizeArray[brush]; + + int maxHeightInBrush = 0; + for (int index = 0; index < brush; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, &tile, (int*)&the_y, baseTile, baseY); + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) == 0 + && this->HeightLayer[tile] > maxHeightInBrush) { + maxHeightInBrush = this->HeightLayer[tile]; + } + } + + tile = baseTile; + the_y = baseY; + for (int index = 0; index < brush; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, &tile, (int*)&the_y, baseTile, baseY); + int brushTile = tile; + uint logic = this->LogicLayer[brushTile]; + if ((logic & (L_BORDER | L_BORDER_EDGE)) == 0 + && (logic & (L_SEA | L_BUILDING | L_KEEP_NON_MANOR_HOUSE | L_MARSH | L_MOAT)) == 0 + && this->BuildingLayer[brushTile] == 0 + && (logic + & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_MOAT_DUG_OR_PLANNED | L_KEEP_NON_MANOR_HOUSE | L_MOAT)) + == 0) { + this->LogicLayer[brushTile] = this->LogicLayer[brushTile] & ~L_ROCKY; + if (plateauHeightSetting == 4) { + this->HeightLayer[brushTile] = 80; + } else if (plateauHeightSetting == 8) { + this->HeightLayer[brushTile] = 130; + } + this->DefaultHeightLayer[brushTile] = this->HeightLayer[brushTile]; + if ((this->LogicLayer[brushTile] & L_RIVER) != 0) { + this->HeightLayer[brushTile] = this->HeightLayer[brushTile] - 8; + } + uint brushY = the_y; + this->Logic2Layer[brushTile] = (byte)plateauHeightSetting; + this->DAT_SomeTile = brushTile; + this->DAT_SomeY = brushY; + + /* handwritten assembly in the original: mark this tile and its eight neighbours changed */ + MACRO_MARK_CHANGED_NEIGHBOURS() + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(brushY, brushTile); + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/createWaterForDrawBridge.cpp b/src/OpenSHC/Map/TileMapState/createWaterForDrawBridge.cpp new file mode 100644 index 000000000..ac39768a7 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/createWaterForDrawBridge.cpp @@ -0,0 +1,53 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00515C80 + void TileMapState::createWaterForDrawBridge(int buildingID) + { + uint xPosition = DAT_BuildingsState::instance.buildings[buildingID].x; + uint widthOrHeight = DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + uint yPosition = DAT_BuildingsState::instance.buildings[buildingID].y; + int variation = DAT_BuildingsState::instance.buildings[buildingID].buildingVariation / 2; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 5); + uint footprintY = yPosition + this->buildingY; + int tile = DAT_ViewportRenderState::instance.translationMatrix[footprintY].addXgetTile + this->buildingX + + xPosition; + if (DAT_TerrainDefinedData::instance.DrawbridgeOrientationMapping[variation][buildingSizeTileIndex] != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::createMoatData, this)( + DAT_BuildingsState::instance.buildings[buildingID].owner, this->buildingX + xPosition, footprintY, + 1); + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMoatVisualStateAtTile, this)(tile, 0); + this->HeightLayer[tile] = 0; + } + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateBuildingGraphicsLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(widthOrHeight + 6, xPosition, yPosition); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/demolishBuildingsInKeepsConstructionFootprint.cpp b/src/OpenSHC/Map/TileMapState/demolishBuildingsInKeepsConstructionFootprint.cpp new file mode 100644 index 000000000..51f27554a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/demolishBuildingsInKeepsConstructionFootprint.cpp @@ -0,0 +1,97 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingLogicalState; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FA760 + void TileMapState::demolishBuildingsInKeepsConstructionFootprint(undefined4 param_1, int x, int y, + undefined4 buildingType, int sizeIndex, int param_6, int param_7) + { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, sizeIndex); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + if (this->BuildingLayer[tile] != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + index++; + } while (index < this->constructionTileCount); + + /* the keep drags three towers and two outbuildings with it, laid out per keep variant */ + int variant = (short)buildingType - 0x28; + { + int tile = DAT_ViewportRenderState::instance.translationMatrix[DAT_TerrainDefinedData::instance.field130_0x264[variant][0].y + y].addXgetTile + + DAT_TerrainDefinedData::instance.field130_0x264[variant][0].x + x; + if (this->BuildingLayer[tile] != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + } + + { + int tile = DAT_ViewportRenderState::instance.translationMatrix[DAT_TerrainDefinedData::instance.field130_0x264[variant][1].y + y].addXgetTile + + DAT_TerrainDefinedData::instance.field130_0x264[variant][1].x + x; + if (this->BuildingLayer[tile] != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + } + + { + int tile = DAT_ViewportRenderState::instance.translationMatrix[DAT_TerrainDefinedData::instance.field130_0x264[variant][2].y + y].addXgetTile + + DAT_TerrainDefinedData::instance.field130_0x264[variant][2].x + x; + if (this->BuildingLayer[tile] != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + } + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, 7); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x384[variant][0].yOffset].addXgetTile + this->buildingX + x + + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x384[variant][0].xOffset; + if (this->BuildingLayer[tile] != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + index++; + } while (index < this->constructionTileCount); + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, 5); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x3e4[variant][0].yOffset].addXgetTile + this->buildingX + x + + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x3e4[variant][0].xOffset; + if (this->BuildingLayer[tile] != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + index++; + } while (index < this->constructionTileCount); + + for (int buildingID = 1; buildingID < 2000; buildingID++) { + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == OpenSHC::Map::Buildings::BLS_REMOVE) { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/destroyPitchDitch.cpp b/src/OpenSHC/Map/TileMapState/destroyPitchDitch.cpp new file mode 100644 index 000000000..0773ecc90 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/destroyPitchDitch.cpp @@ -0,0 +1,41 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500890 + void TileMapState::destroyPitchDitch(int pitchDitchID) + { + this->LogicLayer[this->pitchDitches[pitchDitchID].tile] + = this->LogicLayer[this->pitchDitches[pitchDitchID].tile] & ~L_PLAIN2_AND_PITCH; + this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] + = this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] & 0xdfff; + this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] + = this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] & 0xbfff; + if ((this->LogicLayer[this->pitchDitches[pitchDitchID].tile] & L_WALL_OR_GATEHOUSE) == 0 + && this->BuildingLayer[this->pitchDitches[pitchDitchID].tile] == 0) { + this->HeightLayer[this->pitchDitches[pitchDitchID].tile] + = this->DefaultHeightLayer[this->pitchDitches[pitchDitchID].tile]; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(4, this->pitchDitches[pitchDitchID].x, this->pitchDitches[pitchDitchID].y); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(PitchDitch), 0, &this->pitchDitches[pitchDitchID]); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/destroyWallsOfPlayer.cpp b/src/OpenSHC/Map/TileMapState/destroyWallsOfPlayer.cpp new file mode 100644 index 000000000..2bfbf7468 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/destroyWallsOfPlayer.cpp @@ -0,0 +1,60 @@ +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F93E0 + void TileMapState::destroyWallsOfPlayer(int playerID) + { + for (int tile = 0; tile < 80400; tile++) { + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0) { + continue; + } + if ((this->WallOwnerLayer[tile] & 7) + 1 != playerID) { + continue; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, DAT_EntityState::ptr)( + tile); + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_STAIRS | L_UNKNOWN_WALL_RELATED | L_CRENEL_VARIATIONUnk); + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + this->DamageLayer[tile] = 0; + uint y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + DAT_TileMapState::instance.field204_0x554a30 = 1; + int x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(7, x, y); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/determineBuildingPlacementRotation.cpp b/src/OpenSHC/Map/TileMapState/determineBuildingPlacementRotation.cpp new file mode 100644 index 000000000..78697b0c0 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/determineBuildingPlacementRotation.cpp @@ -0,0 +1,160 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FA000 + void TileMapState::determineBuildingPlacementRotation(int x, int y) + { + if (this->buildingPlacementFail != FALSE) { + this->uiBuildingRotation = 8; + this->field127_0x554944 = 0x80; + return; + } + + /* count, per rotation, how many of its four anchor tiles carry usable wall */ + int walls[4]; + for (int rotation = 0; rotation < 4; rotation++) { + walls[rotation] = 0; + if ((uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 0] + x) <= 399 + && (uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 1] + y) <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 1] + y) * 400 + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 0] + x] + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 1] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 0] + x] + & L_WALL_OR_GATEHOUSE) + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 1] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 0] + x] + & (L_STOCKPILEUnk | L_STAIRS)) + == 0) { + walls[rotation] = 1; + } + if ((uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 2] + x) <= 399 + && (uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 3] + y) <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 3] + y) * 400 + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 2] + x] + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 3] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 2] + x] + & L_WALL_OR_GATEHOUSE) + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 3] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 2] + x] + & (L_STOCKPILEUnk | L_STAIRS)) + == 0) { + walls[rotation] = walls[rotation] + 1; + } + if ((uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 4] + x) <= 399 + && (uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 5] + y) <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 5] + y) * 400 + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 4] + x] + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 5] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 4] + x] + & L_WALL_OR_GATEHOUSE) + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 5] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 4] + x] + & (L_STOCKPILEUnk | L_STAIRS)) + == 0) { + walls[rotation] = walls[rotation] + 1; + } + if ((uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 6] + x) <= 399 + && (uint)(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 7] + y) <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[(DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 7] + y) * 400 + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 6] + x] + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 7] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 6] + x] + & L_WALL_OR_GATEHOUSE) + != 0 + && (this->LogicLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 7] + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2465_0x1e4c[rotation * 8 + 6] + x] + & (L_STOCKPILEUnk | L_STAIRS)) + == 0) { + walls[rotation] = walls[rotation] + 1; + } + } + + bool decided = false; + if (walls[0] == 4) { + if (walls[3] == 4) { + this->uiBuildingRotation = 7; + decided = true; + } else if (walls[1] == 4) { + this->uiBuildingRotation = 1; + decided = true; + } + } + if (!decided && walls[2] == 4) { + if (walls[3] == 4) { + this->uiBuildingRotation = 5; + decided = true; + } else if (walls[1] == 4) { + this->uiBuildingRotation = 3; + decided = true; + } + } + if (!decided) { + if (walls[0] == 4) { + this->uiBuildingRotation = 0; + } else if (walls[1] == 4) { + this->uiBuildingRotation = 2; + } else if (walls[2] == 4) { + this->uiBuildingRotation = 4; + } else if (walls[3] == 4) { + this->uiBuildingRotation = 6; + } else { + this->uiBuildingRotation = 8; + } + } + + int relative; + if (this->uiBuildingRotation == 8) { + relative = 8; + } else { + relative = this->uiBuildingRotation - this->mapOrientation; + if (relative < 0) { + this->field127_0x554944 = (relative + 8) * 0x10; + return; + } + } + this->field127_0x554944 = relative << 4; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/eraseAreaWithBrush.cpp b/src/OpenSHC/Map/TileMapState/eraseAreaWithBrush.cpp new file mode 100644 index 000000000..40e7a1be4 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/eraseAreaWithBrush.cpp @@ -0,0 +1,135 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508EC0 + void TileMapState::eraseAreaWithBrush(uint x, uint y, uint brush) + { + uint baseX = x; + if (x > 399) { + return; + } + if (y > 399) { + return; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return; + } + + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[brush]; + uint baseTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + /* the original reuses the brush parameter as the saved y and x as the walking tile */ + brush = y; + x = baseTile; + + if (brushSize > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, (int*)&x, (int*)&y, baseTile, brush); + if ((this->LogicLayer[x] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + int organism = (short)this->OrganismLayer[x]; + if (organism != 0) { + /* tree ids come first, rock ids are biased by 2000 */ + if (organism >= 2000) { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeRock, DAT_LandscapeState::ptr)(organism - 2000); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(8, baseX, y); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)(organism); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(3, baseX, y); + } + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + DAT_TileMapState::instance.field204_0x554a30 = 1; + } + index++; + } while (index < brushSize); + } + + y = brush; + if (brushSize > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, (int*)&x, (int*)&y, baseTile, brush); + if ((this->LogicLayer[x] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + this->field151_0x554998 = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::validateBuildingPlacementAtTile, this)(0, baseX, y); + if (this->buildingPlacementFail == FALSE) { + if (this->field151_0x554998 != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::spawnEraserTileEffect, this)( + 0, DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + baseX); + } else if (this->field131_0x554954 != 0xffffffff) { + /* razing a keep takes the stockpile with it, and the other way round */ + short owner = DAT_BuildingsState::instance.buildings[this->field131_0x554954].owner; + if (this->field131_0x554954 == DAT_GameState::instance.playerDataArray[owner].keep.id + && DAT_GameState::instance.playerDataArray[owner].stockpile.id != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuildingAndLinkedDuplicates, + DAT_BuildingsState::ptr)(DAT_GameState::instance.playerDataArray[owner].stockpile.id); + } + if (DAT_BuildingsState::instance.buildings[this->field131_0x554954].buildingType == OpenSHC::Map::Buildings::BT_STOCKPILE + && DAT_GameState::instance.playerDataArray[owner].keep.id != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuildingAndLinkedDuplicates, + DAT_BuildingsState::ptr)(DAT_GameState::instance.playerDataArray[owner].keep.id); + } + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuildingAndLinkedDuplicates, DAT_BuildingsState::ptr)( + this->field131_0x554954); + } + } + index++; + } while (index < brushSize); + } + + y = brush; + if (brushSize > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, (int*)&x, (int*)&y, baseTile, brush); + if ((this->LogicLayer[x] & (L_BORDER | L_BORDER_EDGE)) != 0) { + return; + } + for (int unit = (short)DAT_TileMapState::instance.UnitLayer[x]; unit != 0; + unit = DAT_UnitsState::instance.units[unit].nextUnitOnTheSameTile) { + DAT_UnitsState::instance.units[unit].logicalState = OpenSHC::Map::Units::ULS_REMOVE; + } + index++; + } while (index < brushSize); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/evaluateBuildingPlacementAtCursor.cpp b/src/OpenSHC/Map/TileMapState/evaluateBuildingPlacementAtCursor.cpp new file mode 100644 index 000000000..9049c2495 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/evaluateBuildingPlacementAtCursor.cpp @@ -0,0 +1,227 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingFailReasonEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::Buildings::BuildingFailReasonEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00504A30 + void TileMapState::evaluateBuildingPlacementAtCursor(int playerID, uint x, uint y) + { + uint anchorX = x; + int moatTile = 0; + if (y > 399 || x > 399 || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + this->buildingPlacementFail = TRUE; + return; + } + this->buildingPlacementFail = FALSE; + if (DAT_ViewportRenderState::instance.viewportState.field15_0x3c != 0) { + moatTile = DAT_ViewportRenderState::instance.viewportState.field24_0x60; + this->field153_0x5549a0 = DAT_ViewportRenderState::instance.viewportState.field24_0x60; + } + + /* the flat view has no ray hit, so the tile under the mouse is used directly */ + uint buildingID; + int wallTile = DAT_ViewportRenderState::instance.viewportState.field21_0x54; + if (this->flatViewToggleValue1 == 0) { + buildingID = DAT_ViewportRenderState::instance.viewportState.mouseRayBuildingID; + } else { + buildingID = this->BuildingLayer[DAT_ViewportRenderState::instance.viewportState.mouseTile]; + wallTile = 0; + if ((DAT_TileMapState::instance.LogicLayer[DAT_ViewportRenderState::instance.viewportState.mouseTile] & L_WALL_OR_GATEHOUSE) != 0 + && ((DAT_TileMapState::instance.LogicLayer[DAT_ViewportRenderState::instance.viewportState.mouseTile] & L_STOCKPILEUnk) == 0 || buildingID == 0)) { + DAT_ViewportRenderState::instance.viewportState.field21_0x54 = DAT_ViewportRenderState::instance.viewportState.mouseTile; + wallTile = DAT_ViewportRenderState::instance.viewportState.mouseTile; + } + } + if (buildingID != 0) { + anchorX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + y = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + } + + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR + && DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT + && MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, DAT_PathFindingState::ptr)(playerID, anchorX, y, + (-(uint)(DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) & 0xfffffff1) + 0x1e) + != FALSE) { + this->field194_0x554a20 = 1; + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x14; + if (buildingID != 0) { + if (DAT_BuildingsState::instance.buildings[buildingID].owner == playerID || playerID == 0) { + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x14; + this->field194_0x554a20 = 1; + return; + } + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_DEFAULT_CANT_PLACE_THAT_THERE; + return; + } + if (wallTile > 0) { + if ((this->LogicLayer[wallTile] & L_WALL_OR_GATEHOUSE) == 0 + || (this->WallOwnerLayer[wallTile] & 7) + 1 == playerID) { + this->buildingPlacementFail = TRUE; + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x14; + this->field194_0x554a20 = 1; + return; + } + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_DEFAULT_CANT_PLACE_THAT_THERE; + return; + } + if (moatTile > 0 && (this->LogicLayer[moatTile] & L_MOAT) != 0) { + int moatID = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(moatTile); + if (moatID == 0) { + return; + } + if (DAT_GameState::instance.mapAndTime.playerTeams[this->moats[moatID].owner] + == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + return; + } + } + this->buildingPlacementFailReason = OpenSHC::Map::Buildings::BFRE_DEFAULT_CANT_PLACE_THAT_THERE; + return; + } + + if (DAT_ViewportRenderState::instance.viewportState.somePitchDitchID != 0) { + if (this->pitchDitches[DAT_ViewportRenderState::instance.viewportState.somePitchDitchID].owner != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + this->buildingPlacementFail = TRUE; + return; + } + this->field131_0x554954 = -DAT_ViewportRenderState::instance.viewportState.somePitchDitchID; + return; + } + + if ((int)buildingID < 1 + || (DAT_BuildingsState::instance.buildings[buildingID].owner != playerID && playerID != 0)) { + this->buildingPlacementFail = TRUE; + } + + bool checkMoat = true; + if (wallTile > 0) { + /* a stockpile tile carries the wall flag too, but nothing may be built on it */ + if ((this->LogicLayer[wallTile] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[wallTile] & L_STOCKPILEUnk) != 0) { + this->buildingPlacementFail = TRUE; + return; + } + if ((this->LogicLayer[wallTile] & L_WALL_OR_GATEHOUSE) != 0 && this->BuildingLayer[wallTile] == 0 + && this->field152_0x55499c == 0) { + if ((this->WallOwnerLayer[wallTile] & 7) + 1 != playerID) { + return; + } + this->buildingPlacementFail = FALSE; + this->field151_0x554998 = 1; + if ((this->MiscDisplayLayer[wallTile] & 0x400) != 0) { + this->buildingPlacementFail = TRUE; + } + checkMoat = false; + } + } + if (checkMoat) { + if (moatTile < 1 || (this->LogicLayer[moatTile] & L_MOAT) == 0 || buildingID != 0) { + if (this->field151_0x554998 != 0 || this->field152_0x55499c != 0) { + this->buildingPlacementFail = TRUE; + } + } else if (this->field151_0x554998 == 0) { + int moatID = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(moatTile); + if (moatID == 0) { + return; + } + if (DAT_GameState::instance.mapAndTime.playerTeams[this->moats[moatID].owner] + != DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + return; + } + this->field152_0x55499c = 1; + this->buildingPlacementFail = (BOOLEnum)((this->MiscDisplayLayer[moatTile] & 0x400) != 0); + wallTile = moatTile; + } else { + this->buildingPlacementFail = TRUE; + } + } + + if (wallTile > 0 && this->UnitLayer[wallTile] != 0) { + this->buildingPlacementFail = TRUE; + } + if (buildingID != 0) { + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR) { + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_SIEGE_THAT) { + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_SIGNPOST) { + this->buildingPlacementFail = TRUE; + } + } else { + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_UNKNOWN1: + case OpenSHC::Map::Buildings::BT_MANORHOUSE: + case OpenSHC::Map::Buildings::BT_STONEKEEP: + case OpenSHC::Map::Buildings::BT_STRONGHOLD: + case OpenSHC::Map::Buildings::BT_SIGNPOST: + case OpenSHC::Map::Buildings::BT_CAMPGROUND: + case OpenSHC::Map::Buildings::BT_KEEPDOOR_LEFT: + case OpenSHC::Map::Buildings::BT_KEEPDOOR_RIGHT: + case OpenSHC::Map::Buildings::BT_KEEPDOOR: + case OpenSHC::Map::Buildings::BT_POND: + this->buildingPlacementFail = TRUE; + } + } + } + if ((int)buildingID < 0) { + this->field131_0x554954 = buildingID; + return; + } + } + + if ((this->LogicLayer[DAT_BuildingsState::instance.buildings[buildingID].currentTilePositionAdjusted] + & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) + == 0 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_GATEHOUSELARGE + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_GATEHOUSESMALL + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_WOODGATE1 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_WOODGATE2 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_KEEPDOOR + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_DRAWBRIDGE + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_KILLINGPIT + && this->field151_0x554998 == 0 && this->field152_0x55499c == 0) { + this->buildingPlacementFail = TRUE; + } + this->field131_0x554954 = buildingID; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/findNearestFriendlyMoatTileForDigging.cpp b/src/OpenSHC/Map/TileMapState/findNearestFriendlyMoatTileForDigging.cpp new file mode 100644 index 000000000..577949cef --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/findNearestFriendlyMoatTileForDigging.cpp @@ -0,0 +1,113 @@ + +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005111D0 + BOOLEnum TileMapState::findNearestFriendlyMoatTileForDigging(int playerID, int unitID, int number) + { + int unitX = DAT_UnitsState::instance.units[unitID].x; + int unitY = DAT_UnitsState::instance.units[unitID].y; + ushort area = this->PathConnectionLayer[DAT_ViewportRenderState::instance.translationMatrix[unitY].addXgetTile + + unitX]; + int best = 1000; + BOOLEnum bestMoatID = ~FALSE; + if (0 < this->currentMoatCount) { + for (int moatID = 0; moatID < this->currentMoatCount; moatID++) { + /* digging works on own moat, filling in works on anyone else's */ + if (number == 1 + && DAT_GameState::instance.mapAndTime.playerTeams[this->moats[moatID].owner] + != DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + continue; + } + if (number == 2 + && DAT_GameState::instance.mapAndTime.playerTeams[this->moats[moatID].owner] + == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + continue; + } + if (this->moats[moatID].stage != number) { + continue; + } + int tile = this->moats[moatID].tile; + if (this->BuildingLayer[tile] != 0 && this->moats[moatID].zeroOrTwo == 0) { + continue; + } + if ((this->LogicLayer[tile] & (L_MOAT_DUG_OR_PLANNED | L_MOAT)) == 0) { + continue; + } + if (this->moats[moatID].someCountDown >= 'd') { + continue; + } + if (this->UnitLayer[tile] != 0 && DAT_UnitsState::instance.units[this->UnitLayer[tile]].someUnitStat4 == 0) { + continue; + } + if (number == 2 + && MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::findTileInSameAreaAndNoTooHeightDifference, this)( + area, tile, this->moats[moatID].y) + == FALSE) { + continue; + } + + byte pathCost = DAT_TileMapState::instance.SEC_PathfindingCostTileMap1105[this->moats[moatID].owner][tile]; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, DAT_DirectionAlgorithmState::ptr)( + unitX, unitY, this->moats[moatID].x, this->moats[moatID].y); + int score = DAT_DirectionAlgorithmState::instance.distanceHigh; + int fromTarget; + if (DAT_UnitsState::instance.units[unitID].targetX < 1) { + fromTarget = 0; + } else { + fromTarget = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(DAT_UnitsState::instance.units[unitID].targetX, DAT_UnitsState::instance.units[unitID].targetY, + this->moats[moatID].x, this->moats[moatID].y); + } + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] == -1) { + score = score + pathCost * 10; + } + score = fromTarget / 2 + this->moats[moatID].someCountDown + score; + if (score < best) { + bestMoatID = moatID; + best = score; + } + } + + if (bestMoatID == FALSE) { + return FALSE; + } + } + /* bug:fixme: with no moats at all the original indexes moats[-1] here */ + this->moats[bestMoatID].someCountDown = this->moats[bestMoatID].someCountDown + 20; + return bestMoatID; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/findNearestValidDigTileNearTarget.cpp b/src/OpenSHC/Map/TileMapState/findNearestValidDigTileNearTarget.cpp new file mode 100644 index 000000000..9343e79cf --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/findNearestValidDigTileNearTarget.cpp @@ -0,0 +1,82 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500980 + undefined4 TileMapState::findNearestValidDigTileNearTarget(uint x1, uint y1, uint x2, uint y2) + { + if (x1 > 399 || y1 > 399) { + return 0; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y1 * 400 + x1] == 0) { + return 0; + } + if (x2 > 399 || y2 > 399) { + return 0; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y2 * 400 + x2] == 0) { + return 0; + } + + ushort area + = this->PathConnectionLayer[DAT_ViewportRenderState::instance.translationMatrix[y1].addXgetTile + x1]; + int targetTile = DAT_ViewportRenderState::instance.translationMatrix[y2].addXgetTile + x2; + uint targetHeight = this->HeightLayer[targetTile]; + int bestDistance = 10000; + int bestIndex = -1; + if (this->BuildingLayer[targetTile] != 0) { + targetHeight = targetHeight + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(this->BuildingLayer[targetTile]); + } + + for (int index = 0; index < 4; index++) { + int fromXPosition = DAT_TerrainDefinedData::instance.field2478_0x378c[index].x + x2; + int fromYPosition = DAT_TerrainDefinedData::instance.field2478_0x378c[index].y + y2; + int tile = DAT_ViewportRenderState::instance.translationMatrix[fromYPosition].addXgetTile + fromXPosition; + if (this->PathConnectionLayer[tile] != area) { + continue; + } + uint height = this->HeightLayer[tile]; + if (this->BuildingLayer[tile] != 0) { + height = height + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + if ((int)height >= (int)(targetHeight + 0x10) || (int)(targetHeight - 0x10) >= (int)height) { + continue; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(x1, y1, fromXPosition, fromYPosition); + if (DAT_DirectionAlgorithmState::instance.distanceHigh < bestDistance) { + bestDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + bestIndex = index; + } + } + + if (bestIndex >= 0) { + this->field155_0x5549a8 = DAT_TerrainDefinedData::instance.field2478_0x378c[bestIndex].x + x2; + this->field156_0x5549ac = DAT_TerrainDefinedData::instance.field2478_0x378c[bestIndex].y + y2; + return 1; + } + this->field155_0x5549a8 = x2; + this->field156_0x5549ac = y2; + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/findTileInSameAreaAndNoTooHeightDifference.cpp b/src/OpenSHC/Map/TileMapState/findTileInSameAreaAndNoTooHeightDifference.cpp new file mode 100644 index 000000000..061bb4f63 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/findTileInSameAreaAndNoTooHeightDifference.cpp @@ -0,0 +1,32 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500370 + BOOLEnum TileMapState::findTileInSameAreaAndNoTooHeightDifference(int area, int tile, int yUnk) + { + for (int attempts = 0; attempts < 8; attempts++) { + int neighbour = this->directionTranslationMatrix[yUnk][attempts] + tile; + if (this->HeightLayer[neighbour] > this->HeightLayer[tile] + 0x10) { + continue; + } + if ((short)this->PathConnectionLayer[neighbour] != area) { + continue; + } + return TRUE; + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/floodMoatUnderRemovedBuilding.cpp b/src/OpenSHC/Map/TileMapState/floodMoatUnderRemovedBuilding.cpp new file mode 100644 index 000000000..ac60813f8 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/floodMoatUnderRemovedBuilding.cpp @@ -0,0 +1,52 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508910 + void TileMapState::floodMoatUnderRemovedBuilding(int buildingID) + { + uint yPosition = DAT_BuildingsState::instance.buildings[buildingID].y; + uint xPosition = DAT_BuildingsState::instance.buildings[buildingID].x; + uint widthOrHeight = DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + int variation = DAT_BuildingsState::instance.buildings[buildingID].buildingVariation / 2; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 5); + int targetedTile + = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + yPosition].addXgetTile + + this->buildingX + xPosition; + if (DAT_TerrainDefinedData::instance.DrawbridgeOrientationMapping[variation][buildingSizeTileIndex] != 0) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(targetedTile) + != 0) { + this->LogicLayer[targetedTile] = this->LogicLayer[targetedTile] | L_MOAT; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMoatVisualStateAtTile, this)(targetedTile, 2); + } + } + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateBuildingGraphicsLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(widthOrHeight + 6, xPosition, yPosition); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/forceFullTileMapRedraw.cpp b/src/OpenSHC/Map/TileMapState/forceFullTileMapRedraw.cpp new file mode 100644 index 000000000..f70f85ee4 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/forceFullTileMapRedraw.cpp @@ -0,0 +1,24 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005017A0 + void TileMapState::forceFullTileMapRedraw() + { + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + this->forceUpdateTextureTilemap = 1; + this->forceUpdateGFXLayers = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setChangedLayerToThreeAndMapping0x40x40, this)(); + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/generateDustClouds.cpp b/src/OpenSHC/Map/TileMapState/generateDustClouds.cpp new file mode 100644 index 000000000..fd6b234fe --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/generateDustClouds.cpp @@ -0,0 +1,70 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/Map/Entities/EntityType.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Entities::EntityType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC490 + void TileMapState::generateDustClouds() + { + short seed = SEC_RNG::instance.currentNumber2; + int random = SEC_RNG::instance.currentNumber2; + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + this->SEC_Section1052 = this->SEC_Section1052 + 1; + if (random % 500 == this->SEC_Section1052 % 500) { + this->SEC_Section1053 = random % 0xb4; + } + if (random % 0x4b0 == this->SEC_Section1052 % 0x4b0) { + this->SEC_Section1054 = random % 0xb4; + } + if (this->SEC_Section1053 != 0) { + this->SEC_Section1053 = this->SEC_Section1053 - 1; + } + if (this->SEC_Section1054 != 0) { + this->SEC_Section1054 = this->SEC_Section1054 - 1; + } + + for (int i = 0; i < this->temporaryTerrainTypeIndex; i++) { + int tile = this->temporaryTerrainTypeArray[i]; + /* one tile out of every 64 puffs, selected by the tile's own random value */ + if ((short)(this->RandomLayer[tile] ^ seed) % 64 != this->SEC_Section1052 % 64) { + continue; + } + int stage = this->temporaryTerrainTypeBinaryArray[i]; + if (stage == 0) { + if (this->SEC_Section1054 != 0) { + continue; + } + } else { + if (this->SEC_Section1053 != 0) { + continue; + } + if (stage != 1) { + stage = 2; + } + } + MACRO_CALL_MEMBER( + OpenSHC::Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, 0, 0, + (tile - DAT_ViewportRenderState::instance.translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]].addXgetTile) * 8 + 4, + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] * 8 + 4, this->HeightLayer[tile], 0, 0, 0, + OpenSHC::Map::Entities::ET_DUST_CLOUD, stage); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getBuildingHurtSFXID.cpp b/src/OpenSHC/Map/TileMapState/getBuildingHurtSFXID.cpp new file mode 100644 index 000000000..6e090a705 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getBuildingHurtSFXID.cpp @@ -0,0 +1,62 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingLogicalState; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8210 + uint TileMapState::getBuildingHurtSFXID(int tile) + { + short buildingID = this->BuildingLayer[tile]; + if (buildingID == 0) { + return this->LogicLayer[tile] >> 7 & 2; + } + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == 0) { + return 0; + } + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == OpenSHC::Map::Buildings::BLS_REMOVE) { + return 0; + } + if (DAT_BuildingsState::instance.buildings[buildingID].currentHealth == 0) { + return 0; + } + + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_BARRACKS: + case OpenSHC::Map::Buildings::BT_STOCKPILE: + case OpenSHC::Map::Buildings::BT_ARMORY: + case OpenSHC::Map::Buildings::BT_ENGINEERSGUILD: + case OpenSHC::Map::Buildings::BT_TUNNELERSGUILD: + case OpenSHC::Map::Buildings::BT_CHAPEL: + case OpenSHC::Map::Buildings::BT_CHURCH: + case OpenSHC::Map::Buildings::BT_CATHEDRAL: + case OpenSHC::Map::Buildings::BT_MANORHOUSE: + case OpenSHC::Map::Buildings::BT_STONEKEEP: + case OpenSHC::Map::Buildings::BT_STRONGHOLD: + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + case OpenSHC::Map::Buildings::BT_GATEHOUSE: + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + case OpenSHC::Map::Buildings::BT_OUTPOST_EUROPEAN: + return 2; + default: + return 1; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getBuildingSizeForCommandBuildingType.cpp b/src/OpenSHC/Map/TileMapState/getBuildingSizeForCommandBuildingType.cpp new file mode 100644 index 000000000..eb8862e2c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getBuildingSizeForCommandBuildingType.cpp @@ -0,0 +1,228 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Commands::MappersEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FA550 + int TileMapState::getBuildingSizeForCommandBuildingType(MappersEnum commandBuildingType) + { + /* + The original dispatches through a byte index table at 0x004FA614 (0x147 entries, biased by + M_MAPPER_UNDUGMOAT) into a jump table at 0x004FA5DC, which is what MSVC emits for a dense + switch; Ghidra could not normalise it and rendered the dispatch as a read through an + unnamed pointer array. The case groups below are that byte table, decoded. + + field159_0x5549b8 is written through the global instance, not through this, which is how + the original addresses it. + */ + switch (commandBuildingType) { + case OpenSHC::Commands::M_MAPPER_FLETCHER: + case OpenSHC::Commands::M_MAPPER_HOVEL: + case OpenSHC::Commands::M_MAPPER_BAKER: + case OpenSHC::Commands::M_MAPPER_BREWER: + case OpenSHC::Commands::M_MAPPER_GRANARY: + case OpenSHC::Commands::M_MAPPER_ARMOURY: + case OpenSHC::Commands::M_MAPPER_POLETURNER: + case OpenSHC::Commands::M_MAPPER_BLACKSMITH: + case OpenSHC::Commands::M_MAPPER_ARMOURER: + case OpenSHC::Commands::M_MAPPER_TANNER: + case OpenSHC::Commands::M_MAPPER_IRON_MINE: + case OpenSHC::Commands::M_MAPPER_PITCH_WORKINGS: + case OpenSHC::Commands::M_MAPPER_TOWER2: + case OpenSHC::Commands::M_MAPPER_GARDEN10: + case OpenSHC::Commands::M_MAPPER_GARDEN11: + case OpenSHC::Commands::M_MAPPER_GARDEN12: + case OpenSHC::Commands::M_MAPPER_OIL_SMELTER: + case (MappersEnum)258: + case (MappersEnum)259: + case (MappersEnum)260: + case OpenSHC::Commands::M_MAPPER_WATERPOT: + return 4; + case OpenSHC::Commands::M_MAPPER_STORES: + case OpenSHC::Commands::M_MAPPER_TRADEPOST: + case OpenSHC::Commands::M_MAPPER_BARRACKS_EURO: + case OpenSHC::Commands::M_MAPPER_BARRACKS_ARAB: + case OpenSHC::Commands::M_MAPPER_ENGINEERS_GUILD: + case OpenSHC::Commands::M_MAPPER_TUNNELERS_GUILD: + case OpenSHC::Commands::M_MAPPER_INN: + case OpenSHC::Commands::M_MAPPER_GATE_INNER: + case OpenSHC::Commands::M_MAPPER_DRAWBRIDGE: + case OpenSHC::Commands::M_MAPPER_TOWER3: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1B: + case OpenSHC::Commands::M_MAPPER_OUTPOST_EURO: + case OpenSHC::Commands::M_MAPPER_OUTPOST_ARAB: + case OpenSHC::Commands::M_MAPPER_CESS_PIT1: + case OpenSHC::Commands::M_MAPPER_CESS_PIT2: + case OpenSHC::Commands::M_MAPPER_CESS_PIT3: + case OpenSHC::Commands::M_MAPPER_CESS_PIT4: + case OpenSHC::Commands::M_MAPPER_DUNGEON: + case OpenSHC::Commands::M_MAPPER_DUNKING_STOOL: + case OpenSHC::Commands::M_MAPPER_DANCING_BEAR: + case OpenSHC::Commands::M_MAPPER_POND1: + case OpenSHC::Commands::M_MAPPER_POND2_SMALL: + return 5; + case OpenSHC::Commands::M_MAPPER_QUARRY: + case OpenSHC::Commands::M_MAPPER_STABLES: + case OpenSHC::Commands::M_MAPPER_HEALER: + case OpenSHC::Commands::M_MAPPER_CHURCH1: + case OpenSHC::Commands::M_MAPPER_TOWER4: + case OpenSHC::Commands::M_MAPPER_TOWER5: + case OpenSHC::Commands::M_MAPPER_POND3_LARGE1: + case OpenSHC::Commands::M_MAPPER_POND4_LARGE2: + return 6; + case OpenSHC::Commands::M_MAPPER_WOODSMAN: + case OpenSHC::Commands::M_MAPPER_TUNNEL: + case OpenSHC::Commands::M_MAPPER_TUNNEL_CONSTRUCTION: + case OpenSHC::Commands::M_MAPPER_MILL: + case OpenSHC::Commands::M_MAPPER_HUNTER: + case OpenSHC::Commands::M_MAPPER_TOWER1: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1A: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1B: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1C: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1D: + case OpenSHC::Commands::M_MAPPER_GARDEN7: + case OpenSHC::Commands::M_MAPPER_GARDEN8: + case OpenSHC::Commands::M_MAPPER_GARDEN9: + case OpenSHC::Commands::M_MAPPER_MAYPOLE: + case OpenSHC::Commands::M_MAPPER_STOCKS: + case OpenSHC::Commands::M_MAPPER_CATAPULT: + case OpenSHC::Commands::M_MAPPER_TREBUCHET: + case OpenSHC::Commands::M_MAPPER_SIEGE_TOWER: + case OpenSHC::Commands::M_MAPPER_BATTERING_RAM: + case OpenSHC::Commands::M_MAPPER_PORTABLE_SHIELD: + case OpenSHC::Commands::M_MAPPER_BURNING_STAKE: + case OpenSHC::Commands::M_MAPPER_RACK_STRETCHING: + case OpenSHC::Commands::M_MAPPER_CHOPPING_BLOCK: + case OpenSHC::Commands::M_MAPPER_DOG_CAGE: + case OpenSHC::Commands::M_MAPPER_WELL: + case OpenSHC::Commands::M_MAPPER_ARAB_BALLISTA: + return 3; + case OpenSHC::Commands::M_MAPPER_KEEP1: + case OpenSHC::Commands::M_MAPPER_KEEP2: + case OpenSHC::Commands::M_MAPPER_GATE_MAIN: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2B: + return 7; + case OpenSHC::Commands::M_MAPPER_CAMP_FIRE: + case OpenSHC::Commands::M_MAPPER_KILLING_PIT: + case OpenSHC::Commands::M_MAPPER_PITCH_DITCH: + case (MappersEnum)248: + case (MappersEnum)249: + case (MappersEnum)250: + case (MappersEnum)251: + case OpenSHC::Commands::M_MAPPER_SHRINE5: + return 1; + case OpenSHC::Commands::M_MAPPER_KEEP3: + case OpenSHC::Commands::M_MAPPER_APPLEFARM: + return 0xb; + case OpenSHC::Commands::M_MAPPER_WHEATFARM: + case OpenSHC::Commands::M_MAPPER_HOPSFARM: + case OpenSHC::Commands::M_MAPPER_CHURCH2: + return 9; + case OpenSHC::Commands::M_MAPPER_CHURCH3: + return 0xd; + case OpenSHC::Commands::M_MAPPER_CATTLEFARM: + return 0xa; + case OpenSHC::Commands::M_MAPPER_UNDUGMOAT: + case OpenSHC::Commands::M_MAPPER_DUGMOAT: + case OpenSHC::Commands::M_MAPPER_MOAT: + case OpenSHC::Commands::M_MAPPER_ANTIMOAT: + DAT_TileMapState::instance.field159_0x5549b8 = 1; + return 2; + case OpenSHC::Commands::M_MAPPER_OXENBASE: + case OpenSHC::Commands::M_MAPPER_SIGNPOST: + case OpenSHC::Commands::M_MAPPER_GATE_POSTERN: + case OpenSHC::Commands::M_MAPPER_GARDEN1: + case OpenSHC::Commands::M_MAPPER_GARDEN2: + case OpenSHC::Commands::M_MAPPER_GARDEN3: + case OpenSHC::Commands::M_MAPPER_GARDEN4: + case OpenSHC::Commands::M_MAPPER_GARDEN5: + case OpenSHC::Commands::M_MAPPER_GARDEN6: + case OpenSHC::Commands::M_MAPPER_GALLOWS: + case (MappersEnum)252: + case (MappersEnum)253: + case (MappersEnum)254: + case (MappersEnum)255: + case (MappersEnum)256: + case (MappersEnum)257: + case OpenSHC::Commands::M_MAPPER_GIBBET: + case OpenSHC::Commands::M_MAPPER_STATUE1: + case OpenSHC::Commands::M_MAPPER_STATUE2: + case OpenSHC::Commands::M_MAPPER_STATUE3: + case OpenSHC::Commands::M_MAPPER_STATUE4: + case OpenSHC::Commands::M_MAPPER_STATUE5: + case OpenSHC::Commands::M_MAPPER_SHRINE1: + case OpenSHC::Commands::M_MAPPER_SHRINE2: + case OpenSHC::Commands::M_MAPPER_SHRINE3: + case OpenSHC::Commands::M_MAPPER_SHRINE4: + return 2; + case OpenSHC::Commands::M_MAPPER_FLAG_TYPE0: + case OpenSHC::Commands::M_MAPPER_FLAG_TYPE1: + case OpenSHC::Commands::M_MAPPER_FLAG_TYPE2: + case OpenSHC::Commands::M_MAPPER_FLAG_TYPE3: + case OpenSHC::Commands::M_MAPPER_HEADS: + case OpenSHC::Commands::M_MAPPER_BRAZIER: + case OpenSHC::Commands::M_MAPPER_MANGONEL: + case OpenSHC::Commands::M_MAPPER_BALLISTA: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARCHERS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_SPEARMEN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_PIKEMEN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_MACEMEN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_XBOWMEN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_SWORDSMEN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_KNIGHTS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_LADDERMEN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ENGINEERS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ENGINEERS_POTS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_MONKS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_CATAPULTS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_TREBUCHETS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_BATTERING_RAMS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_SIEGE_TOWERS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_PORTABLE_SHIELDS: + case OpenSHC::Commands::M_MAPPER_PEOPLE_TUNNELERS: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT1: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT2: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT3: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT4: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT5: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT6: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINT7: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_BOW: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_SLAVE: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_SLINGER: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_ASSASIN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_HORSEMAN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_SWORDSMAN: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_GRENADIER: + case OpenSHC::Commands::M_MAPPER_PEOPLE_ARAB_BALLISTA: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM1: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM2: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM3: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM4: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM5: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM6: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTM7: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTE1: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTE2: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTT1: + case OpenSHC::Commands::M_MAPPER_PLACE_ASSEMBLY_POINTK1: + return 0; + } + return -1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getBuildingSizeIndexMappingData.cpp b/src/OpenSHC/Map/TileMapState/getBuildingSizeIndexMappingData.cpp new file mode 100644 index 000000000..1ed38d438 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getBuildingSizeIndexMappingData.cpp @@ -0,0 +1,41 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingSizeIndexMapping.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9880 + void TileMapState::getBuildingSizeIndexMappingData(int buildingSizeTileIndex, int buildingWidthOrHeight) + { + this->constructionTileCount = buildingWidthOrHeight * buildingWidthOrHeight; + this->buildingX = DAT_BuildingSizeIndexMapping::instance[buildingWidthOrHeight][buildingSizeTileIndex][0]; + this->buildingY = DAT_BuildingSizeIndexMapping::instance[buildingWidthOrHeight][buildingSizeTileIndex][1]; + switch (this->mapOrientation) { + case 0: + this->buildingRotationRelatedValue + = DAT_BuildingSizeIndexMapping::instance[buildingWidthOrHeight][buildingSizeTileIndex][2]; + return; + case 2: + this->buildingRotationRelatedValue + = DAT_BuildingSizeIndexMapping::instance[buildingWidthOrHeight][buildingSizeTileIndex][3]; + return; + case 4: + this->buildingRotationRelatedValue + = DAT_BuildingSizeIndexMapping::instance[buildingWidthOrHeight][buildingSizeTileIndex][4]; + return; + case 6: + this->buildingRotationRelatedValue + = DAT_BuildingSizeIndexMapping::instance[buildingWidthOrHeight][buildingSizeTileIndex][5]; + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getCastleBuildRangeForMapSize.cpp b/src/OpenSHC/Map/TileMapState/getCastleBuildRangeForMapSize.cpp new file mode 100644 index 000000000..467ff840d --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getCastleBuildRangeForMapSize.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FBC30 + undefined4 TileMapState::getCastleBuildRangeForMapSize() + { + switch (this->mapSize) { + case 160: + return 45; + case 200: + return 50; + case 300: + return 60; + case 400: + return 70; + } + return 70; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getHeightAtTileIncludingOwnersBuildings.cpp b/src/OpenSHC/Map/TileMapState/getHeightAtTileIncludingOwnersBuildings.cpp new file mode 100644 index 000000000..d7d7b417d --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getHeightAtTileIncludingOwnersBuildings.cpp @@ -0,0 +1,93 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FFF10 + uint TileMapState::getHeightAtTileIncludingOwnersBuildings(int tile, int playerID) + { + /* + The early layer reads go through the global instance's absolute address, while the + BuildingLayer reads inside the switch arms go through this; follow each one separately. + */ + if (DAT_TileMapState::instance.PathConnectionLayer[tile] == 0) { + return 0; + } + if ((DAT_TileMapState::instance.MiscDisplayLayer[tile] & 0x1000) != 0) { + return 0; + } + + if ((DAT_TileMapState::instance.LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0) { + if (playerID != 0 && (this->WallOwnerLayer[tile] & 7) + 1 != playerID) { + return 0; + } + return DAT_TileMapState::instance.HeightLayer[tile]; + } + + short buildingID = DAT_TileMapState::instance.BuildingLayer[tile]; + if (buildingID == 0) { + return 0; + } + if (playerID != 0 && DAT_BuildingsState::instance.buildings[buildingID].owner != playerID) { + return 0; + } + + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_STONEKEEP: + case OpenSHC::Map::Buildings::BT_STRONGHOLD: + case OpenSHC::Map::Buildings::BT_KEEPFOUR: + case OpenSHC::Map::Buildings::BT_KEEPFIVE: + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + for (int direction = 0; direction < 8; direction++) { + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState:: + instance.tileTranslationMatrix_YComponent[tile]][direction] + + tile] + != buildingID) { + return 0; + } + } + return MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTotalHeightAtTile, this)(tile); + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: { + int firstDirection = 0; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingVariation == 0x50) { + firstDirection = 2; + } + int secondDirection = firstDirection + 4; + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState:: + instance.tileTranslationMatrix_YComponent[tile]][firstDirection] + + tile] + != buildingID) { + return 0; + } + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState:: + instance.tileTranslationMatrix_YComponent[tile]][secondDirection] + + tile] + != buildingID) { + return 0; + } + return MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTotalHeightAtTile, this)(tile); + } + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getMaxWallHeightInBrushArea.cpp b/src/OpenSHC/Map/TileMapState/getMaxWallHeightInBrushArea.cpp new file mode 100644 index 000000000..180917435 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getMaxWallHeightInBrushArea.cpp @@ -0,0 +1,43 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00502950 + int TileMapState::getMaxWallHeightInBrushArea(int param_1, uint param_2) + { + int baseTile = param_1; + uint baseY = param_2; + int maxWallHeight = 0; + for (int index = 1; index < 5; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, ¶m_1, (int*)¶m_2, baseTile, baseY); + if ((this->LogicLayer[param_1] & L_WALL_OR_GATEHOUSE) == 0) { + continue; + } + if ((this->LogicLayer[param_1] & L_STOCKPILEUnk) != 0) { + continue; + } + if ((this->LogicLayer[param_1] & L_CRENEL) != 0) { + continue; + } + if (this->HeightLayer[param_1] > maxWallHeight) { + maxWallHeight = this->HeightLayer[param_1]; + } + } + return maxWallHeight; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getNonFarmFieldBuildingHealthAtTileOr1000.cpp b/src/OpenSHC/Map/TileMapState/getNonFarmFieldBuildingHealthAtTileOr1000.cpp new file mode 100644 index 000000000..9dbee901f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getNonFarmFieldBuildingHealthAtTileOr1000.cpp @@ -0,0 +1,53 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_APPLE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_DAIRY; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_HOP; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_WHEAT; + + using OpenSHC::Map::Buildings::BuildingLogicalState; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8160 + int TileMapState::getNonFarmFieldBuildingHealthAtTileOr1000(int tile) + { + short buildingID = this->BuildingLayer[tile]; + if (buildingID <= 0) { + return 1000; + } + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == 0) { + return 1000; + } + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == OpenSHC::Map::Buildings::BLS_REMOVE) { + return 1000; + } + if (DAT_BuildingsState::instance.buildings[buildingID].currentHealth == 0) { + return 1000; + } + if (DAT_BuildingDefinedData::instance + .BuildingTypeHasHealth[DAT_BuildingsState::instance.buildings[buildingID].buildingType] + == 0) { + return 1000; + } + + if ((this->LogicLayer[tile] & (L_FARM_FIELD_WHEAT | L_FARM_FIELD_HOP | L_FARM_FIELD_APPLE | L_FARM_FIELD_DAIRY)) + != 0) { + return 1000; + } + return DAT_BuildingsState::instance.buildings[buildingID].currentHealth; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getOrientationThatIsWallTowerOrGatehouse.cpp b/src/OpenSHC/Map/TileMapState/getOrientationThatIsWallTowerOrGatehouse.cpp new file mode 100644 index 000000000..8a13583f2 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getOrientationThatIsWallTowerOrGatehouse.cpp @@ -0,0 +1,42 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8B50 + int TileMapState::getOrientationThatIsWallTowerOrGatehouse(uint x, uint y) + { + if (x > 399 || y > 399) { + return 0xf; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return 0xf; + } + + int rowTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + for (int index = 0; index < 8; index++) { + int orientation = DAT_TerrainDefinedData::instance.SomeOrientationOrderArray[index]; + int neighbour = this->directionTranslationMatrix[y][orientation] + rowTile; + if ((this->LogicLayer[neighbour] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[neighbour] & (L_STOCKPILEUnk | L_CRENEL_VARIATIONUnk)) == 0) { + return orientation; + } + } + return 0xf; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getPitchDitchIDForTile.cpp b/src/OpenSHC/Map/TileMapState/getPitchDitchIDForTile.cpp new file mode 100644 index 000000000..e58957b63 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getPitchDitchIDForTile.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500850 + int TileMapState::getPitchDitchIDForTile(int tile) + { + for (int pitchDitchID = 1; pitchDitchID < this->maxPitchDitchCount; pitchDitchID++) { + if (this->pitchDitches[pitchDitchID].owner != 0 && tile == this->pitchDitches[pitchDitchID].tile) { + return pitchDitchID; + } + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getRubbleGraphicStageForDamageLevel.cpp b/src/OpenSHC/Map/TileMapState/getRubbleGraphicStageForDamageLevel.cpp new file mode 100644 index 000000000..f51fe7e8e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getRubbleGraphicStageForDamageLevel.cpp @@ -0,0 +1,79 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FA460 + undefined4 TileMapState::getRubbleGraphicStageForDamageLevel(int rotation) + { + /* the result lives in eax from the start (xor eax, eax), every comparison goes through ecx */ + int result = 0; + int orientation = this->mapOrientation; + if (orientation == 0) { + if (rotation == 0x50) { + result = 1; + } else if (rotation == 0x51) { + result = 0; + } else if (rotation == 1) { + result = 2; + } else if (rotation == 7) { + result = 5; + } else if (rotation == 3) { + result = 3; + } else if (rotation == 5) { + result = 4; + } + } else if (orientation == 4) { + if (rotation == 0x50) { + result = 1; + } else if (rotation == 0x51) { + result = 0; + } else if (rotation == 1) { + result = 4; + } else if (rotation == 7) { + result = 3; + } else if (rotation == 3) { + result = 5; + } else if (rotation == 5) { + result = 2; + } + } else if (orientation == 2) { + if (rotation == 0x50) { + result = 0; + } else if (rotation == 0x51) { + result = 1; + } else if (rotation == 1) { + result = 5; + } else if (rotation == 7) { + result = 4; + } else if (rotation == 3) { + result = 2; + } else if (rotation == 5) { + result = 3; + } + } else if (orientation == 6) { + if (rotation == 0x50) { + result = 0; + } else if (rotation == 0x51) { + result = 1; + } else if (rotation == 1) { + result = 3; + } else if (rotation == 7) { + result = 2; + } else if (rotation == 3) { + result = 4; + } else if (rotation == 5) { + result = 5; + } + } + return result; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getTileForBrush.cpp b/src/OpenSHC/Map/TileMapState/getTileForBrush.cpp new file mode 100644 index 000000000..8da5f89fd --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getTileForBrush.cpp @@ -0,0 +1,64 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FBD80 + void TileMapState::getTileForBrush(int square, int index, int* tilePointer, int* yPointer, int baseTile, uint y) + { + if (square != 0) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::getTileInSquareBrush, DAT_PathFindingState::ptr)( + index, baseTile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile, y); + *tilePointer = DAT_PathFindingState::instance.resultTile; + *yPointer = DAT_PathFindingState::instance.resultY; + return; + } + + if (index >= 37) { + index = 0; + } + int direction = DAT_TerrainDefinedData::instance.field12_0xc[index]; + if (direction == 0xf) { + return; + } + if (direction < 8) { + *tilePointer = *tilePointer + this->directionTranslationMatrix[*yPointer][direction]; + *yPointer = *yPointer + + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset; + return; + } + + /* 20 to 27 step twice in a direction, 40 to 47 three times */ + if (direction < 28) { + direction = direction - 20; + } else { + if (direction >= 48) { + return; + } + direction = direction - 40; + *tilePointer = *tilePointer + this->directionTranslationMatrix[*yPointer][direction]; + *yPointer = *yPointer + + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset; + } + *tilePointer = *tilePointer + this->directionTranslationMatrix[*yPointer][direction]; + *yPointer + = *yPointer + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset; + *tilePointer = *tilePointer + this->directionTranslationMatrix[*yPointer][direction]; + *yPointer + = *yPointer + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getTotalHeightAt.cpp b/src/OpenSHC/Map/TileMapState/getTotalHeightAt.cpp new file mode 100644 index 000000000..9b71be422 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getTotalHeightAt.cpp @@ -0,0 +1,151 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FFBA0 + int TileMapState::getTotalHeightAt(int tile, int playerID) + { + uint logic = this->LogicLayer[tile]; + int extra = 0; + int exposed = 0; + if ((logic & L_KEEP_NON_MANOR_HOUSE) != 0) { + int buildingID = this->BuildingLayer[tile]; + extra = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)(buildingID); + for (int i = 0; i < 8; i++) { + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]][i] + tile] != buildingID) { + exposed = 1; + break; + } + } + if (playerID != 0 && DAT_BuildingsState::instance.buildings[buildingID].owner != playerID) { + exposed = 0; + } + } else if ((logic & L_BUILDING) != 0) { + int buildingID = this->BuildingLayer[tile]; + int type = DAT_BuildingsState::instance.buildings[buildingID].buildingType; + switch (type) { + case 0x3d: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + extra = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)(buildingID) + 0x14; + exposed = 1; + break; + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + extra = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)(buildingID); + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_MANORHOUSE) { + for (int i = 0; i < 8; i++) { + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]][i] + tile] != buildingID) { + exposed = 1; + break; + } + } + } + break; + case 0x2d: + case 0x2e: + extra = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, DAT_BuildingsState::ptr)(buildingID); + exposed = 1; + break; + default: + extra = DAT_BuildingDefinedData::instance.BuildingHeights[type]; + break; + } + if (playerID != 0 && DAT_BuildingsState::instance.buildings[buildingID].owner != playerID) { + exposed = 0; + } + } else if ((logic & L_ROCKY) != 0 && (logic & L_WALL_OR_GATEHOUSE) == 0) { + /* rock ids are biased by 2000 */ + int organism = this->OrganismLayer[tile]; + if (organism >= 2000) { + short rockType = DAT_LandscapeState::instance.rocks[organism - 2000].type; + if (rockType >= 0xd) { + extra = 0x46; + } else if (rockType >= 9) { + extra = 0x32; + } + } + } else if ((logic & L_CRENEL_VARIATIONUnk) != 0) { + extra = 0x14; + exposed = 1; + if (playerID != 0 && (this->WallOwnerLayer[tile] & 7) + 1 != playerID) { + exposed = 0; + } + } else if ((logic & L_CRENEL) != 0) { + extra = 10; + exposed = 1; + if (playerID != 0 && (this->WallOwnerLayer[tile] & 7) + 1 != playerID) { + exposed = 0; + } + } else { + short building = this->BuildingLayer[tile]; + if (building != 0) { + int buildingID = building; + short type = DAT_BuildingsState::instance.buildings[buildingID].buildingType; + extra = 0x1e; + if (type == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE + || type == OpenSHC::Map::Buildings::BT_GATEHOUSESMALL) { + /* a gatehouse only counts as cover while both of its span tiles are still standing */ + int first; + int second; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingVariation == 0x50) { + first = 2; + second = 6; + } else { + first = 0; + second = 4; + } + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]][first] + tile] != buildingID) { + exposed = 1; + } + if (this->BuildingLayer[DAT_TileMapState::instance.directionTranslationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]][second] + tile] != buildingID) { + exposed = 1; + } + } + if (playerID != 0 && DAT_BuildingsState::instance.buildings[buildingID].owner != playerID) { + exposed = 0; + } + } + } + + extra = extra + this->HeightLayer[tile]; + if (exposed != 0) { + return extra; + } + return -1 - extra; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getTotalHeightAtTile.cpp b/src/OpenSHC/Map/TileMapState/getTotalHeightAtTile.cpp new file mode 100644 index 000000000..cb093d406 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getTotalHeightAtTile.cpp @@ -0,0 +1,43 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FFB10 + uint TileMapState::getTotalHeightAtTile(int tile) + { + int buildingID = this->BuildingLayer[tile]; + if (buildingID != 0) { + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE) { + return MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(buildingID) + + this->DefaultHeightLayer[tile]; + } + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_GATEHOUSESMALL) { + return MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(buildingID) + + this->DefaultHeightLayer[tile]; + } + return MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(buildingID) + + this->HeightLayer[tile]; + } + return this->HeightLayer[tile]; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getUnownedMoatCount.cpp b/src/OpenSHC/Map/TileMapState/getUnownedMoatCount.cpp new file mode 100644 index 000000000..4d1302890 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getUnownedMoatCount.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500210 + int TileMapState::getUnownedMoatCount() + { + int unownedMoatCount = 0; + for (int moatID = 1; moatID < 16000; moatID++) { + if (this->moats[moatID].owner == 0) { + unownedMoatCount++; + } + } + return unownedMoatCount; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/getWallFlagForOrientedDirection.cpp b/src/OpenSHC/Map/TileMapState/getWallFlagForOrientedDirection.cpp new file mode 100644 index 000000000..9cb0eaacd --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/getWallFlagForOrientedDirection.cpp @@ -0,0 +1,40 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F89C0 + uint TileMapState::getWallFlagForOrientedDirection(int tile, int y, int twoOrFour) + { + int direction = 0; + if (twoOrFour == 2) { + if (this->mapOrientation == 0) { + direction = 2; + } else if (this->mapOrientation == 2) { + direction = 4; + } else if (this->mapOrientation == 4) { + direction = 6; + } + } else { + if (twoOrFour != 4) { + return 0; + } + if (this->mapOrientation == 0) { + direction = 4; + } else if (this->mapOrientation == 2) { + direction = 6; + } else if (this->mapOrientation == 6) { + direction = 2; + } + } + return this->LogicLayer[this->directionTranslationMatrix[y][direction] + tile] >> 8 & 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/hasHigherNeighborWithStairs.cpp b/src/OpenSHC/Map/TileMapState/hasHigherNeighborWithStairs.cpp new file mode 100644 index 000000000..3a52d360f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/hasHigherNeighborWithStairs.cpp @@ -0,0 +1,40 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8A40 + BOOLEnum TileMapState::hasHigherNeighborWithStairs(int tile, int y, int direction) + { + int rotatedDirection = 0; + if (this->mapOrientation == 0) { + rotatedDirection = direction; + } else if (this->mapOrientation == 4) { + rotatedDirection = direction + 4; + } else if (this->mapOrientation == 2) { + rotatedDirection = direction + 2; + } else if (this->mapOrientation == 6) { + rotatedDirection = direction + 6; + } + if (rotatedDirection >= 8) { + rotatedDirection = rotatedDirection - 8; + } + + int neighbour = this->directionTranslationMatrix[y][rotatedDirection] + tile; + if (this->HeightLayer[neighbour] > this->HeightLayer[tile]) { + return this->LogicLayer[neighbour] >> 0xb & TRUE; + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/hasHigherPlainNeighborWithWallOrGatehouse.cpp b/src/OpenSHC/Map/TileMapState/hasHigherPlainNeighborWithWallOrGatehouse.cpp new file mode 100644 index 000000000..f088cca28 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/hasHigherPlainNeighborWithWallOrGatehouse.cpp @@ -0,0 +1,49 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8AC0 + BOOLEnum TileMapState::hasHigherPlainNeighborWithWallOrGatehouse(int tile, int y, int direction) + { + int rotatedDirection = 0; + if (this->mapOrientation == 0) { + rotatedDirection = direction; + } else if (this->mapOrientation == 4) { + rotatedDirection = direction + 4; + } else if (this->mapOrientation == 2) { + rotatedDirection = direction + 2; + } else if (this->mapOrientation == 6) { + rotatedDirection = direction + 6; + } + if (rotatedDirection >= 8) { + rotatedDirection = rotatedDirection - 8; + } + + int neighbour = this->directionTranslationMatrix[y][rotatedDirection] + tile; + if (this->HeightLayer[tile] >= this->HeightLayer[neighbour]) { + return FALSE; + } + if ((this->LogicLayer[neighbour] & L_CRENEL) != 0) { + return FALSE; + } + if ((this->LogicLayer[neighbour] & L_STOCKPILEUnk) != 0) { + return FALSE; + } + return this->LogicLayer[neighbour] >> 8 & TRUE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/hasOnlyTowerNeighborsNoWalls.cpp b/src/OpenSHC/Map/TileMapState/hasOnlyTowerNeighborsNoWalls.cpp new file mode 100644 index 000000000..025b448e9 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/hasOnlyTowerNeighborsNoWalls.cpp @@ -0,0 +1,54 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8900 + BOOLEnum TileMapState::hasOnlyTowerNeighborsNoWalls(int tile, int y) + { + int unknownWallRelated = 0; + int wallOrGatehouse = 0; + int offset = this->directionTranslationMatrix[y][0] + tile; + if ((this->LogicLayer[offset] & L_UNKNOWN_WALL_RELATED) != 0) { + unknownWallRelated++; + } else if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0) { + wallOrGatehouse++; + } + offset = this->directionTranslationMatrix[y][2] + tile; + if ((this->LogicLayer[offset] & L_UNKNOWN_WALL_RELATED) != 0) { + unknownWallRelated++; + } else if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0) { + wallOrGatehouse++; + } + offset = this->directionTranslationMatrix[y][4] + tile; + if ((this->LogicLayer[offset] & L_UNKNOWN_WALL_RELATED) != 0) { + unknownWallRelated++; + } else if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0) { + wallOrGatehouse++; + } + offset = this->directionTranslationMatrix[y][6] + tile; + if ((this->LogicLayer[offset] & L_UNKNOWN_WALL_RELATED) != 0) { + unknownWallRelated++; + } else if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0) { + wallOrGatehouse++; + } + if (unknownWallRelated != 0 && wallOrGatehouse == 0) { + return TRUE; + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/increaseHeightForTunnelSingleTile.cpp b/src/OpenSHC/Map/TileMapState/increaseHeightForTunnelSingleTile.cpp new file mode 100644 index 000000000..d9bdfab7e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/increaseHeightForTunnelSingleTile.cpp @@ -0,0 +1,53 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F80E0 + void TileMapState::increaseHeightForTunnelSingleTile(int tile, uint x, uint y, int increment) + { + if ((this->LogicLayer[tile] + & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_RIVER | L_FORD | L_KEEP_NON_MANOR_HOUSE | L_MARSH | L_MOAT)) + != 0) { + return; + } + if ((this->LogicLayer[tile] & (L_SEA | L_BORDER | L_BORDER_EDGE | L_ROCKY)) != 0) { + return; + } + if (this->BuildingLayer[tile] != 0) { + return; + } + + if ((this->HeightLayer[tile] < 16 || this->DefaultHeightLayer[tile] < 16) + && (int)((uint)this->HeightLayer[tile] + increment) >= 0) { + this->HeightLayer[tile] = this->HeightLayer[tile] + (char)increment; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(4, x, y); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/increaseHeightForTunnelWithBrush.cpp b/src/OpenSHC/Map/TileMapState/increaseHeightForTunnelWithBrush.cpp new file mode 100644 index 000000000..3b83fca06 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/increaseHeightForTunnelWithBrush.cpp @@ -0,0 +1,61 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00501CD0 + void TileMapState::increaseHeightForTunnelWithBrush(int tile, uint x, uint y, int increment) + { + int baseTile = tile; + uint baseY = y; + for (int index = 0; index < 9; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 0, index, &tile, (int*)&y, baseTile, baseY); + if ((this->LogicLayer[tile] & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0) { + continue; + } + if (this->BuildingLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] & (L_SEA | L_BORDER | L_BORDER_EDGE | L_ROCKY)) != 0) { + continue; + } + if ((this->LogicLayer[tile] & (L_RIVER | L_FORD | L_MARSH | L_MOAT)) != 0) { + continue; + } + + if ((this->HeightLayer[tile] < 0x10 || this->DefaultHeightLayer[tile] < 0x10) + && (int)((uint)this->HeightLayer[tile] + increment) >= 0) { + this->HeightLayer[tile] = this->HeightLayer[tile] + (char)increment; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(5, x, y); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isBuildingPlacementAllowedAtTile.cpp b/src/OpenSHC/Map/TileMapState/isBuildingPlacementAllowedAtTile.cpp new file mode 100644 index 000000000..ad9e6e751 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isBuildingPlacementAllowedAtTile.cpp @@ -0,0 +1,235 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Synchrony/GameSynchronyState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_APPLE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_DAIRY; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_HOP; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_WHEAT; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Game::GameMode; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + @return int 0 if allowed, 1 if not allowed, 2 not allowed because of clashing building placement decompilerscript: + committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9A60 + int TileMapState::isBuildingPlacementAllowedAtTile( + int tile, int playerID, MappersEnum commandBuildingType, int param_4) + { + uint logic = this->LogicLayer[tile]; + uint wallOrGate = logic & L_WALL_OR_GATEHOUSE; + uint height = this->HeightLayer[tile]; + if (wallOrGate != 0 && (this->WallOwnerLayer[tile] & 7) + 1 == playerID) { + /* a gate or tower on our own wall is measured against the ground, not the wall top */ + switch (commandBuildingType) { + case OpenSHC::Commands::M_MAPPER_GATEHOUSE: + case OpenSHC::Commands::M_MAPPER_GATE_MAIN: + case OpenSHC::Commands::M_MAPPER_GATE_INNER: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD: + case OpenSHC::Commands::M_MAPPER_GATE_POSTERN: + case OpenSHC::Commands::M_MAPPER_TOWER1: + case OpenSHC::Commands::M_MAPPER_TOWER2: + case OpenSHC::Commands::M_MAPPER_TOWER3: + case OpenSHC::Commands::M_MAPPER_TOWER4: + case OpenSHC::Commands::M_MAPPER_TOWER5: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1A: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1B: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1C: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1D: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1B: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2B: + height = this->DefaultHeightLayer[tile]; + } + } + + if (this->buildingHeightLimit < 0) { + if ((int)height < -this->buildingHeightLimit) { + return 1; + } + } else if (this->buildingHeightLimit < (int)height) { + if (commandBuildingType == OpenSHC::Commands::M_MAPPER_CATTLEFARM) { + this->placementWarning = 1; + return 1; + } + if (commandBuildingType == OpenSHC::Commands::M_MAPPER_APPLEFARM) { + this->placementWarning = 2; + return 1; + } + if (commandBuildingType == OpenSHC::Commands::M_MAPPER_HOPSFARM) { + this->placementWarning = 3; + return 1; + } + if (commandBuildingType != OpenSHC::Commands::M_MAPPER_WHEATFARM) { + return 1; + } + this->placementWarning = 4; + return 1; + } + if (this->buildingMaxHeightDifference + this->buildingMinHeight < (int)height) { + return 1; + } + if ((logic & L_PLAIN2_AND_PITCH) != 0 && commandBuildingType == OpenSHC::Commands::M_MAPPER_PITCH_DITCH) { + return 1; + } + if (this->BuildingLayer[tile] != 0) { + return 2; + } + + if (this->UnitLayer[tile] != 0) { + int unitID = (short)this->UnitLayer[tile]; + if (MACRO_CALL_MEMBER( + OpenSHC::Synchrony::GameSynchronyState_Func::isAIPlayer, DAT_GameSynchronyState::ptr)(playerID) + == FALSE) { + if (DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_CHICKEN) { + return 1; + } + } else { + if (DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_LORD) { + return 1; + } + if (DAT_UnitsState::instance.units[unitID].isSelectable_OR_matchTime != 0 + && DAT_UnitsState::instance.units[unitID].owner != playerID) { + return 1; + } + } + } + + if ((logic & L_SEA) != 0) { + return 1; + } + if ((logic & (L_BORDER | L_BORDER_EDGE)) != 0) { + return 1; + } + if ((logic & L_RIVER) != 0) { + return 1; + } + if (wallOrGate != 0) { + if ((this->WallOwnerLayer[tile] & 7) + 1 != playerID) { + return 1; + } + /* only a gate or a tower may be built onto a wall */ + switch (commandBuildingType) { + case OpenSHC::Commands::M_MAPPER_GATEHOUSE: + case OpenSHC::Commands::M_MAPPER_GATE_MAIN: + case OpenSHC::Commands::M_MAPPER_GATE_INNER: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD: + case OpenSHC::Commands::M_MAPPER_GATE_POSTERN: + case OpenSHC::Commands::M_MAPPER_TOWER1: + case OpenSHC::Commands::M_MAPPER_TOWER2: + case OpenSHC::Commands::M_MAPPER_TOWER3: + case OpenSHC::Commands::M_MAPPER_TOWER4: + case OpenSHC::Commands::M_MAPPER_TOWER5: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1A: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1B: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1C: + case OpenSHC::Commands::M_MAPPER_GATE_WOOD1D: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE1B: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2A: + case OpenSHC::Commands::M_MAPPER_GATE_STONE2B: + break; + default: + return 1; + } + } + if ((logic & L_PLAIN1_AND_FARM) != 0 && param_4 == 0) { + return 1; + } + if ((logic & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0) { + return 1; + } + + if ((logic & (L_TREE | L_TREE_VARIATION)) != 0 && this->OrganismLayer[tile] != 0 + && this->OrganismLayer[tile] < 2000) { + /* scrub can be built over, a real tree cannot, except by the AI outside solitary */ + switch (DAT_LandscapeState::instance.trees[this->OrganismLayer[tile]].treeType) { + case (TreeType)5: + case (TreeType)6: + case (TreeType)7: + case (TreeType)8: + case (TreeType)9: + case (TreeType)0xa: + case (TreeType)0xb: + case (TreeType)0xc: + case (TreeType)0xd: + case (TreeType)0xe: + case (TreeType)0x10: + case (TreeType)0x11: + case (TreeType)0x12: + case (TreeType)0x13: + break; + default: + if (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SOLITARY) { + return 1; + } + if (MACRO_CALL_MEMBER(OpenSHC::Synchrony::GameSynchronyState_Func::isAIPlayer, + DAT_GameSynchronyState::ptr)(playerID) + == 0) { + return 1; + } + } + } + + if ((logic & (L_FARM_FIELD_WHEAT | L_FARM_FIELD_HOP | L_FARM_FIELD_APPLE | L_FARM_FIELD_DAIRY)) != 0 + && param_4 == 0) { + return 1; + } + if ((logic & L_FORD) != 0) { + return 1; + } + + bool onRock = false; + if ((char)logic < 0 && wallOrGate == 0) { + onRock = true; + } + if ((!onRock || this->buildingPlacementProperty_4 != 0) + && ((logic & L_MOAT) == 0 || this->buildingPlacementProperty_7 != 0) + && (this->buildingPlacementProperty_6 == 2 || (logic & L_MARSH) == 0 + || this->buildingPlacementProperty_6 != 0)) { + return 0; + } + return 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isCliffDropInDirection.cpp b/src/OpenSHC/Map/TileMapState/isCliffDropInDirection.cpp new file mode 100644 index 000000000..818805103 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isCliffDropInDirection.cpp @@ -0,0 +1,21 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC7C0 + BOOLEnum TileMapState::isCliffDropInDirection(int tile, undefined4 param_2, int y) + { + int direction = (this->field84_0x5548a4 + 4) % 8; + return (uint)this->HeightLayer[tile] + > (uint)(this->HeightLayer[this->directionTranslationMatrix[y][direction] + tile] + 0x14); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isFootprintIndexOnEdge.cpp b/src/OpenSHC/Map/TileMapState/isFootprintIndexOnEdge.cpp new file mode 100644 index 000000000..2c0cff146 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isFootprintIndexOnEdge.cpp @@ -0,0 +1,34 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9930 + BOOLEnum TileMapState::isFootprintIndexOnEdge(int counter, int size) + { + if (size < 3) { + return TRUE; + } + if (counter < size) { + return TRUE; + } + if (counter >= (size - 1) * size) { + return TRUE; + } + if (counter % size == 0) { + return TRUE; + } + return counter % size == size - 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isTileEnclosedByWalls.cpp b/src/OpenSHC/Map/TileMapState/isTileEnclosedByWalls.cpp new file mode 100644 index 000000000..950815b86 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isTileEnclosedByWalls.cpp @@ -0,0 +1,38 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8640 + undefined4 TileMapState::isTileEnclosedByWalls(int tile, int y) + { + int offset = this->directionTranslationMatrix[y][0] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 && (this->LogicLayer[offset] & L_STOCKPILEUnk) == 0) { + return 1; + } + offset = this->directionTranslationMatrix[y][4] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 && (this->LogicLayer[offset] & L_STOCKPILEUnk) == 0) { + return 1; + } + offset = this->directionTranslationMatrix[y][2] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 && (this->LogicLayer[offset] & L_STOCKPILEUnk) == 0) { + return 1; + } + offset = this->directionTranslationMatrix[y][6] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 && (this->LogicLayer[offset] & L_STOCKPILEUnk) == 0) { + return 1; + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isTileEnclosedByWallsOrGates.cpp b/src/OpenSHC/Map/TileMapState/isTileEnclosedByWallsOrGates.cpp new file mode 100644 index 000000000..1b7667eb1 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isTileEnclosedByWallsOrGates.cpp @@ -0,0 +1,46 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F85B0 + BOOLEnum TileMapState::isTileEnclosedByWallsOrGates(int tile, int y) + { + int offset = this->directionTranslationMatrix[y][0] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[offset] & (L_STOCKPILEUnk | L_CRENEL)) == 0) { + return TRUE; + } + offset = this->directionTranslationMatrix[y][4] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[offset] & (L_STOCKPILEUnk | L_CRENEL)) == 0) { + return TRUE; + } + offset = this->directionTranslationMatrix[y][2] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[offset] & (L_STOCKPILEUnk | L_CRENEL)) == 0) { + return TRUE; + } + offset = this->directionTranslationMatrix[y][6] + tile; + if ((this->LogicLayer[offset] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[offset] & (L_STOCKPILEUnk | L_CRENEL)) == 0) { + return TRUE; + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isTileSuitableForBrushPlacement.cpp b/src/OpenSHC/Map/TileMapState/isTileSuitableForBrushPlacement.cpp new file mode 100644 index 000000000..e314f618a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isTileSuitableForBrushPlacement.cpp @@ -0,0 +1,76 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FBE70 + BOOLEnum TileMapState::isTileSuitableForBrushPlacement(int tile, uint x, uint y) + { + byte height = this->HeightLayer[tile]; + /* the tile suits a brush as soon as it stands above any one of its eight neighbours */ + uint neighbourY = y - 1; + if (x <= 399 && neighbourY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + x] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y - 1].addXgetTile + x]) > 0) { + return TRUE; + } + uint neighbourX = x + 1; + if (neighbourX <= 399 && neighbourY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + neighbourX] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y - 1].addXgetTile + x + 1]) > 0) { + return TRUE; + } + if (neighbourX <= 399 && y <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + neighbourX] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x + 1]) > 0) { + return TRUE; + } + neighbourY = y + 1; + if (neighbourX <= 399 && neighbourY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + neighbourX] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y + 1].addXgetTile + x + 1]) > 0) { + return TRUE; + } + if (x <= 399 && neighbourY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + x] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y + 1].addXgetTile + x]) > 0) { + return TRUE; + } + neighbourX = x - 1; + if (neighbourX <= 399 && neighbourY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + neighbourX] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y + 1].addXgetTile + x - 1]) > 0) { + return TRUE; + } + if (neighbourX <= 399 && y <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + neighbourX] != 0 + && (int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x - 1]) > 0) { + return TRUE; + } + + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, DAT_ViewportRenderState::ptr)( + neighbourX, y - 1) + == FALSE) { + return FALSE; + } + if ((int)(height - this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[y - 1].addXgetTile + x - 1]) < 1) { + return FALSE; + } + return TRUE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isUnitBlockingSizeFiveFootprint.cpp b/src/OpenSHC/Map/TileMapState/isUnitBlockingSizeFiveFootprint.cpp new file mode 100644 index 000000000..fac316068 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isUnitBlockingSizeFiveFootprint.cpp @@ -0,0 +1,43 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAEE0 + BOOLEnum TileMapState::isUnitBlockingSizeFiveFootprint(int buildingID) + { + int buildingX = DAT_BuildingsState::instance.buildings[buildingID].x; + int buildingY = DAT_BuildingsState::instance.buildings[buildingID].y; + int variation = DAT_BuildingsState::instance.buildings[buildingID].buildingVariation / 2; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 5); + if (DAT_TerrainDefinedData::instance.field2469_0x21ec[variation][buildingSizeTileIndex] == 0 + && this->UnitLayer[DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + buildingY] + .addXgetTile + + this->buildingX + buildingX] + != 0) { + return TRUE; + } + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + return FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isValidCastleSiteLocation.cpp b/src/OpenSHC/Map/TileMapState/isValidCastleSiteLocation.cpp new file mode 100644 index 000000000..af13fd19f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isValidCastleSiteLocation.cpp @@ -0,0 +1,118 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005024F0 + BOOLEnum TileMapState::isValidCastleSiteLocation(int x, uint y, int cbt) + { + int baseTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + uint baseY = y; + /* x is reused as the count of tiles that qualified */ + x = 0; + + int enemyRange; + int searchRange; + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + enemyRange = 15; + searchRange = 7; + } else { + enemyRange = 30; + searchRange = 30; + } + + int tile = baseTile; + for (int index = 0; index < 9; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(0, index, &tile, (int*)&y, baseTile, baseY); + /* the brush call may move both, so the original keeps its own copies for later use */ + int brushTile = tile; + uint brushY = y; + if (this->OrganismLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0) { + continue; + } + if (this->BuildingLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] + & (L_WALL_OR_GATEHOUSE | L_BOULDERS | L_IRON | L_RIVER | L_FORD | L_MARSH)) + != 0) { + continue; + } + if ((this->LogicLayer[tile] & (L_SEA | L_PLAIN1_AND_FARM | L_ROCKY)) != 0) { + continue; + } + + uint brushX = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR + && DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT + && MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, DAT_PathFindingState::ptr)( + DAT_GameSynchronyState::instance.currentPlayerSlotID, brushX, y, enemyRange) + != FALSE) { + continue; + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + int buildRange = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getCastleBuildRangeForMapSize, this)(); + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, DAT_PathFindingState::ptr)( + DAT_GameSynchronyState::instance.currentPlayerSlotID, brushX, brushY, searchRange, -1, -1, buildRange + 5) + != 0) { + continue; + } + } + + /* a moat site must be clear of moat, a moat-fill site must sit on one */ + if (cbt == 0x6a) { + if ((this->LogicLayer[brushTile] & (L_MOAT | L_MOAT_DUG_OR_PLANNED)) == 0) { + x = x + 1; + } + } else if (cbt != 0x6b || (this->LogicLayer[brushTile] & L_MOAT_DUG_OR_PLANNED) != 0) { + x = x + 1; + } + } + return (BOOLEnum)(x != 0); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isWallConnectionHeightValid.cpp b/src/OpenSHC/Map/TileMapState/isWallConnectionHeightValid.cpp new file mode 100644 index 000000000..d9cf431a0 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isWallConnectionHeightValid.cpp @@ -0,0 +1,65 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8840 + BOOLEnum TileMapState::isWallConnectionHeightValid(int tile, int y, int param_3) + { + int tileHeight = this->HeightLayer[tile]; + int firstDirection = 0; + int secondDirection = 0; + if (param_3 == 0) { + secondDirection = 4; + } else if (param_3 == 2) { + firstDirection = 2; + secondDirection = 6; + } else if (param_3 == 4) { + secondDirection = param_3; + } else if (param_3 == 6) { + firstDirection = 2; + secondDirection = 6; + } + + int first = this->directionTranslationMatrix[y][firstDirection] + tile; + if ((this->LogicLayer[first] & L_WALL_OR_GATEHOUSE) == 0) { + return FALSE; + } + if ((this->LogicLayer[first] & L_STOCKPILEUnk) != 0) { + return FALSE; + } + int minHeight = tileHeight - 0x10; + if (this->HeightLayer[first] < minHeight && this->DamageLayer[first] == 0) { + return FALSE; + } + + int second = this->directionTranslationMatrix[y][secondDirection] + tile; + if ((this->LogicLayer[second] & L_WALL_OR_GATEHOUSE) == 0) { + return FALSE; + } + if ((this->LogicLayer[second] & L_STOCKPILEUnk) != 0) { + return FALSE; + } + if (this->HeightLayer[second] < minHeight && this->DamageLayer[second] == 0) { + return FALSE; + } + return TRUE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isWallCornerForCardinalDirection.cpp b/src/OpenSHC/Map/TileMapState/isWallCornerForCardinalDirection.cpp new file mode 100644 index 000000000..8a5e8f66b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isWallCornerForCardinalDirection.cpp @@ -0,0 +1,59 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F86D0 + BOOLEnum TileMapState::isWallCornerForCardinalDirection(int tile, int y, int direction) + { + int first = 0; + int second = 0; + int third = 0; + int fourth = 0; + if (direction == 0) { + first = 0; + second = 4; + third = 2; + fourth = 6; + } else if (direction == 2) { + first = 2; + second = 6; + third = 0; + fourth = 4; + } else if (direction == 4) { + first = 0; + second = 4; + third = 2; + fourth = 6; + } else if (direction == 6) { + first = 2; + second = 6; + third = 0; + fourth = 4; + } + + if ((this->LogicLayer[this->directionTranslationMatrix[y][first] + tile] & L_CRENEL) == 0) { + return FALSE; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][second] + tile] & L_CRENEL) == 0) { + return FALSE; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][third] + tile] & L_CRENEL) != 0) { + return FALSE; + } + return ~(this->LogicLayer[this->directionTranslationMatrix[y][fourth] + tile] >> 9) & TRUE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/isWallCornerForDiagonalDirection.cpp b/src/OpenSHC/Map/TileMapState/isWallCornerForDiagonalDirection.cpp new file mode 100644 index 000000000..12f19fe77 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/isWallCornerForDiagonalDirection.cpp @@ -0,0 +1,56 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8780 + uint TileMapState::isWallCornerForDiagonalDirection(int tile, int y, int direction) + { + int first = 0; + int second = 0; + int third = 0; + int fourth = 0; + if (direction == 1) { + first = 4; + second = 6; + third = 0; + fourth = 2; + } else if (direction == 3) { + first = 0; + second = 6; + third = 4; + fourth = 2; + } else if (direction == 5) { + first = 0; + second = 2; + third = 4; + fourth = 6; + } else if (direction == 7) { + first = 4; + second = 2; + third = 0; + fourth = 6; + } + + if ((this->LogicLayer[this->directionTranslationMatrix[y][first] + tile] & L_CRENEL) == 0) { + return 0; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][second] + tile] & L_CRENEL) == 0) { + return 0; + } + if ((this->LogicLayer[this->directionTranslationMatrix[y][third] + tile] & L_CRENEL) != 0) { + return 0; + } + return ~((uint)this->LogicLayer[this->directionTranslationMatrix[y][fourth] + tile] >> 9) & 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/markBuildingFootprintFlag.cpp b/src/OpenSHC/Map/TileMapState/markBuildingFootprintFlag.cpp new file mode 100644 index 000000000..11a026e86 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/markBuildingFootprintFlag.cpp @@ -0,0 +1,33 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FA9D0 + void TileMapState::markBuildingFootprintFlag(int param_1, int param_2, int param_3) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, param_3); + buildingSizeTileIndex++; + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_2].addXgetTile + + this->buildingX + param_1; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_PLAIN1_AND_FARM; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xfff3; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/noop1.cpp b/src/OpenSHC/Map/TileMapState/noop1.cpp new file mode 100644 index 000000000..6e1eca8d6 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/noop1.cpp @@ -0,0 +1,16 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F70D0 + void TileMapState::noop1(undefined4 param_1) { return; } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeApplefarm.cpp b/src/OpenSHC/Map/TileMapState/placeApplefarm.cpp new file mode 100644 index 000000000..fefdae821 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeApplefarm.cpp @@ -0,0 +1,132 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::LogicHelpers::Logic1; + using OpenSHC::Map::LogicHelpers::Logic2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00515740 + void TileMapState::placeApplefarm( + int playerID, uint x, uint y, BuildingType buildingType, undefined4 param_5, int param_6, int* param_7) + { + int _buildingID; + int iVar1; + int local_4; + int _buildingTileRef; + local_4 = 0; + _buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, x, y, (undefined4)((int)(param_7)), (int)(short)(undefined2)buildingType, 3, playerID, param_6); + this->placedBuildingID = _buildingID; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::markBuildingFootprintFlag, this)(x, (int)((int)(y)), 10); + _buildingTileRef = DAT_TerrainDefinedData::instance.AppleFarmOffsets[1].y; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[0] + = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.AppleFarmOffsets[0].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[0].x + x; + iVar1 = DAT_TerrainDefinedData::instance.AppleFarmOffsets[2].y + y; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[1] + = DAT_ViewportRenderState::instance.translationMatrix[_buildingTileRef + y].addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[1].x + x; + _buildingTileRef = DAT_TerrainDefinedData::instance.AppleFarmOffsets[3].y; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[2] + = DAT_ViewportRenderState::instance.translationMatrix[iVar1].addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[2].x + x; + iVar1 = DAT_TerrainDefinedData::instance.AppleFarmOffsets[4].y + y; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[3] + = DAT_ViewportRenderState::instance.translationMatrix[_buildingTileRef + y].addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[3].x + x; + _buildingTileRef = DAT_TerrainDefinedData::instance.AppleFarmOffsets[5].y; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[4] + = DAT_ViewportRenderState::instance.translationMatrix[iVar1].addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[4].x + x; + iVar1 = DAT_TerrainDefinedData::instance.AppleFarmOffsets[6].y + y; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[5] + = DAT_ViewportRenderState::instance.translationMatrix[_buildingTileRef + y].addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[5].x + x; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[6] + = DAT_ViewportRenderState::instance.translationMatrix[iVar1].addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[6].x + x; + DAT_BuildingsState::instance.buildings[_buildingID].tileRefs[7] + = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.AppleFarmOffsets[7].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.AppleFarmOffsets[7].x + x; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(local_4, 3); + param_6._0_2_ = (short)_buildingID; + _buildingTileRef = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + x + + this->buildingX; + this->HeightLayer[_buildingTileRef] = (byte)param_7; + this->LogicLayer[_buildingTileRef] = this->LogicLayer[_buildingTileRef] | 0x400; + this->BuildingLayer[_buildingTileRef] = (short)param_6; + this->BuildingWasLayer[_buildingTileRef] = (undefined1)buildingType; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)(playerID, _buildingTileRef, + this->buildingY + y, 2, OpenSHC::Map::LogicHelpers::L_NONE, OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB); + local_4 = local_4 + 1; + this->ChangedLayer[_buildingTileRef] = 2; + } while (local_4 < this->constructionTileCount); + /* + &tileRefs[0] of buildingID + */ + buildingType = _buildingID * 0x32c + OpenSHC::Map::Buildings::0xf986fc; + /* + apple tree relative coordinates + */ + param_7 = &DAT_TerrainDefinedData::instance.AppleFarmOffsets[0].x; + do { + _buildingTileRef = *(int*)buildingType; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(_buildingTileRef); + this->LogicLayer[_buildingTileRef] = this->LogicLayer[_buildingTileRef] | 0x4000000; + this->ChangedLayer[_buildingTileRef] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::placeAppleTree, DAT_LandscapeState::ptr)( + _buildingID, (undefined4)((int)(*param_7 + x)), (undefined4)((int)(param_7[1] + y))); + buildingType = buildingType + OpenSHC::Map::Buildings::BT_OXTETHER; + param_7 = param_7 + 2; + } while ((int)param_7 < 0xb49eb0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(_buildingID); + if (((playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) + && (DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[_buildingID].buildingTypeBasedEmployeeCount)) + && ((DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || (DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)))) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)(1, 0x4d, 1, (TextMessageBLLookupStructUnion)0x0, 100, 6000); + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeBarracks.cpp b/src/OpenSHC/Map/TileMapState/placeBarracks.cpp new file mode 100644 index 000000000..8abc62855 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeBarracks.cpp @@ -0,0 +1,182 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005076A0 + void TileMapState::placeBarracks(int playerID, uint xPosition, int* yPosition_param, undefined4 buildingType, + uint buildingSize, int buildingOrientation, undefined4 param_7) + { + int* piVar1; + int _buildingID; + int iVar2; + int iVar3; + uint uVar4; + int iVar5; + uint uVar6; + int iVar7; + int local_c; + piVar1 = yPosition_param; + iVar7 = DAT_GameCore::instance.uniqueGameObjectTracker; + iVar5 = 0; + if (buildingOrientation == 0xf) { + local_c = 0; + } else { + local_c = buildingOrientation / 2; + } + _buildingID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, + DAT_BuildingsState::ptr)(playerID, xPosition, (uint)((int)(yPosition_param)), (undefined4)((int)(param_7)), + (BuildingType)((int)((int)(short)buildingType)), buildingSize, playerID, 0xf); + iVar2 = (int)(short)_buildingID; + this->placedBuildingID = iVar2; + DAT_BuildingsState::instance.buildings[iVar2].uidWhenPlaced = iVar7; + yPosition_param = &DAT_BuildingsState::instance.buildings[iVar2].tileRefs[0]; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar5, (int)((int)(buildingSize))); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + (int)piVar1].addXgetTile + + this->buildingX + xPosition; + *yPosition_param = iVar3; + this->HeightLayer[iVar3] = (byte)param_7; + this->LogicLayer[iVar3] = this->LogicLayer[iVar3] | 0x400; + this->BuildingLayer[iVar3] = (short)_buildingID; + iVar5 = iVar5 + 1; + yPosition_param = yPosition_param + 1; + this->BuildingWasLayer[iVar3] = (uchar)buildingType; + this->ChangedLayer[iVar3] = 2; + } while (iVar5 < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(iVar2); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(iVar2); + uVar6 = (int)piVar1 + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_c][0].y; + uVar4 = xPosition + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_c][0].x; + iVar5 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, uVar4, + uVar6, (undefined4)((int)(param_7)), OpenSHC::Map::Buildings::BT_PARADEGROUND3, 5, playerID, 0xf); + _buildingID = (int)(short)iVar5; + DAT_BuildingsState::instance.buildings[_buildingID].uidWhenPlaced = iVar7; + DAT_BuildingsState::instance.buildings[_buildingID].quarryStockpileID = (undefined2)this->placedBuildingID; + buildingSize = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSize, (int)((int)(DAT_BuildingsState::instance.buildings[_buildingID].widthOrHeight))); + iVar2 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + uVar6].addXgetTile + + this->buildingX + uVar4; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar2); + this->HeightLayer[iVar2] = (byte)param_7; + if ((this->buildingX == 2) && (this->buildingY == 2)) { + this->LogicLayer[iVar2] = this->LogicLayer[iVar2] | 0x400; + } + this->BuildingLayer[iVar2] = (short)iVar5; + buildingSize = buildingSize + 1; + this->BuildingWasLayer[iVar2] = (uchar)buildingType; + this->ChangedLayer[iVar2] = 2; + } while ((int)buildingSize < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, uVar4, uVar6); + uVar6 = DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_c][1].y + (int)piVar1; + uVar4 = DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_c][1].x + xPosition; + iVar5 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, uVar4, + uVar6, (undefined4)((int)(param_7)), OpenSHC::Map::Buildings::BT_PARADEGROUND4, 5, playerID, 0xf); + _buildingID = (int)(short)iVar5; + DAT_BuildingsState::instance.buildings[_buildingID].uidWhenPlaced = iVar7; + DAT_BuildingsState::instance.buildings[_buildingID].quarryStockpileID = (undefined2)this->placedBuildingID; + buildingSize = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSize, (int)((int)(DAT_BuildingsState::instance.buildings[_buildingID].widthOrHeight))); + iVar2 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + uVar6].addXgetTile + + this->buildingX + uVar4; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar2); + this->HeightLayer[iVar2] = (byte)param_7; + if ((this->buildingX == 2) && (this->buildingY == 2)) { + this->LogicLayer[iVar2] = this->LogicLayer[iVar2] | 0x400; + } + this->BuildingLayer[iVar2] = (short)iVar5; + buildingSize = buildingSize + 1; + this->BuildingWasLayer[iVar2] = (uchar)buildingType; + this->ChangedLayer[iVar2] = 2; + } while ((int)buildingSize < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, uVar4, uVar6); + uVar6 = DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_c][2].y + (int)piVar1; + uVar4 = DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_c][2].x + xPosition; + iVar5 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, uVar4, + uVar6, (undefined4)((int)(param_7)), OpenSHC::Map::Buildings::BT_PARADEGROUND2, 5, playerID, 0xf); + _buildingID = (int)(short)iVar5; + DAT_BuildingsState::instance.buildings[_buildingID].uidWhenPlaced = iVar7; + DAT_BuildingsState::instance.buildings[_buildingID].quarryStockpileID = (undefined2)this->placedBuildingID; + buildingSize = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSize, (int)((int)(DAT_BuildingsState::instance.buildings[_buildingID].widthOrHeight))); + iVar7 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + uVar6].addXgetTile + + this->buildingX + uVar4; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar7); + this->HeightLayer[iVar7] = (byte)param_7; + if ((this->buildingX == 2) && (this->buildingY == 2)) { + this->LogicLayer[iVar7] = this->LogicLayer[iVar7] | 0x400; + } + this->BuildingLayer[iVar7] = (short)iVar5; + this->BuildingWasLayer[iVar7] = (uchar)buildingType; + buildingSize = buildingSize + 1; + this->ChangedLayer[iVar7] = 2; + } while ((int)buildingSize < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(_buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, uVar4, uVar6); + if ((short)buildingType == 8) { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupMercenaryPostCampgroundPositions, + DAT_BuildingsState::ptr)(playerID); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBarracksCampgroundPositions, + DAT_BuildingsState::ptr)(playerID); + } + if (((playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) + && (DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[_buildingID].buildingTypeBasedEmployeeCount)) + && ((DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || (DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)))) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)(1, 0x4d, 1, (TextMessageBLLookupStructUnion)0x0, 100, 6000); + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeBuilding.cpp b/src/OpenSHC/Map/TileMapState/placeBuilding.cpp new file mode 100644 index 000000000..07f9358bd --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeBuilding.cpp @@ -0,0 +1,331 @@ + +#include "OpenSHC/Game.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Map/WallAndPitchState.func.hpp" +#include "OpenSHC/UI/Helpers.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_WallAndPitchState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005162D0 + void TileMapState::placeBuilding( + PlayerID playerID, int x, int y, MappersEnum cbt, int buildingSize, int buildingOrientation) + { + MappersEnum command = (MappersEnum)(short)cbt; + int pitchDitchID = 0; + if (this->field122_0x554930 == 0) { + if (buildingOrientation == 0xf) { + this->DAT_TempBuildingRotation = 0; + } else { + this->DAT_TempBuildingRotation = buildingOrientation / 2; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::checkBuildingCanBePlacedHere, this)(playerID, x, y, cbt, buildingSize); + if (this->buildingPlacementFail != FALSE) { + return; + } + } + this->field122_0x554930 = 0; + + BuildingType buildingType = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(command); + short shortType = (short)buildingType; + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_CRUSADER_TUTORIAL) { + if (MACRO_CALL(OpenSHC::Game_Func::Tutorial_IsActionAllowed)(2, (short)shortType) == FALSE) { + MACRO_CALL(OpenSHC::UI::Helpers_Func::SetTutorialHintActiveWithTimestamp)(); + return; + } + MACRO_CALL(OpenSHC::UI::Helpers_Func::SetTutorialBuildingActionState)( + 7, (BuildingType)(int)(short)shortType); + } + + /* clear whatever the footprint was before the building goes down */ + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, buildingSize); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_SIEGE_THAT + && (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0) { + DAT_GameState::instance.playerDataArray[playerID].startResources[4] + = DAT_GameState::instance.playerDataArray[playerID].startResources[4] + 1; + } + if ((this->LogicLayer[tile] & L_ROCKY) != 0 && this->OrganismLayer[tile] > 1999) { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeRock, DAT_LandscapeState::ptr)( + this->OrganismLayer[tile] - 2000); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, buildingSize); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, this->buildingX + x, this->buildingY + y); + } + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_CRENEL_VARIATIONUnk | L_UNKNOWN_WALL_RELATED | L_STAIRS | L_CRENEL | L_WALL_OR_GATEHOUSE + | L_PLAIN2_AND_PITCH); + this->HeightLayer[tile] = DAT_TileMapState::instance.DefaultHeightLayer[tile]; + this->DamageLayer[tile] = 0; + if ((this->LogicLayer[tile] & L_TREE) != 0 && this->OrganismLayer[tile] < 2000) { + /* every tree goes, scrub or not; only the reported reason differs */ + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)( + this->OrganismLayer[tile]); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(3, x, y); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, buildingSize); + } + if ((short)this->UnitLayer[tile] != 0) { + /* a unit standing where a walkable building goes is left alone */ + switch (shortType) { + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + case OpenSHC::Map::Buildings::BT_DRAWBRIDGE: + case OpenSHC::Map::Buildings::BT_CAMPFIRE: + case OpenSHC::Map::Buildings::BT_PARADEGROUND: + case OpenSHC::Map::Buildings::BT_CAMPGROUND: + case OpenSHC::Map::Buildings::BT_PARADEGROUND2: + case OpenSHC::Map::Buildings::BT_PARADEGROUND3: + case OpenSHC::Map::Buildings::BT_PARADEGROUND4: + case OpenSHC::Map::Buildings::BT_PARADEGROUND5: + case OpenSHC::Map::Buildings::BT_KILLINGPIT: + case OpenSHC::Map::Buildings::BT_PITCHDITCH: + case OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED: + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + break; + default: + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::deleteUnit, DAT_UnitsState::ptr)( + (short)this->UnitLayer[tile]); + } + } + if ((this->LogicLayer[tile] & (L_MOAT | L_MOAT_DUG_OR_PLANNED)) != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatDataAtTile, this)(this->buildingX + x, this->buildingY + y); + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_MOAT | L_MOAT_DUG_OR_PLANNED); + } + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xfc3f; + index++; + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + DAT_TileMapState::instance.field204_0x554a30 = 1; + } while (index < this->constructionTileCount); + + /* the building sits at the middle of the heights it covers */ + uint highest = 0; + uint lowest = 1000; + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, buildingSize); + uint tileHeight = this->HeightLayer[DAT_ViewportRenderState::instance + .translationMatrix[this->buildingY + y] + .addXgetTile + + this->buildingX + x]; + if (highest < tileHeight) { + highest = tileHeight; + } + if (tileHeight < lowest) { + lowest = tileHeight; + } + index++; + } while (index < this->constructionTileCount); + int averageHeight = (highest - lowest) / 2 + lowest; + + if (shortType == OpenSHC::Map::Buildings::BT_TUNNEL && playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::playTunnelerCommandSpeech, DAT_TribesState::ptr)(); + } + + switch ((int)shortType) { + case OpenSHC::Map::Buildings::BT_MERCENARYPOST: + case OpenSHC::Map::Buildings::BT_BARRACKS: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBarracks, this)(playerID, x, (int*)y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_STOCKPILE: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeStockpile, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + default: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeWorkshopOrHovel, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_QUARRY: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeQuarry, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_ENGINEERSGUILD: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeEngineersguild, this)(playerID, (int*)x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_TUNNELERSGUILD: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeTunnelersguild, this)(playerID, (int*)x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_OILSMELTER: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeOilsmelter, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_WHEATFARM: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeWheatfarm, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_HOPFARM: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeHopfarm, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_APPLEFARM: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeApplefarm, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, (int*)averageHeight); + break; + case OpenSHC::Map::Buildings::BT_DAIRYFARM: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeDairyfarm, this)(playerID, x, y, buildingType, buildingSize, (int*)buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeGatehouseLarge, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeGatehouseSmall, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_DRAWBRIDGE: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeDrawbridge, this)(playerID, x, y, buildingType, buildingSize, (int*)buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_FIREBALLISTA: + case OpenSHC::Map::Buildings::BT_CATAPULT: + case OpenSHC::Map::Buildings::BT_TREBUCHET: + case OpenSHC::Map::Buildings::BT_BATTERINGRAM: + case OpenSHC::Map::Buildings::BT_SIEGETOWER: + case OpenSHC::Map::Buildings::BT_SHIELD: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeSiegeTent, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_KILLINGPIT: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeKillingPit, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeSiegetowerPlaced, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeTower, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_UNKNOWN1: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::stampBuildingOntoTileMap, this)(playerID, x, y, buildingType, + command + ~OpenSHC::Commands::M_MAPPER_MP_KEEP8, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_MANORHOUSE: + case OpenSHC::Map::Buildings::BT_STONEKEEP: + case OpenSHC::Map::Buildings::BT_STRONGHOLD: + case OpenSHC::Map::Buildings::BT_KEEPFOUR: + case OpenSHC::Map::Buildings::BT_KEEPFIVE: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeKeep, this)(playerID, x, y, buildingType, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_WOODGATE1: + break; + case OpenSHC::Map::Buildings::BT_PITCHDITCH: + pitchDitchID = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placePitchDitch, this)(playerID, x, y); + break; + case OpenSHC::Map::Buildings::BT_GARDEN: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placePositiveFearfactor, this)(playerID, x, y, buildingType, + command - OpenSHC::Commands::M_MAPPER_GARDEN1, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_CESSPIT: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placePositiveFearfactor, this)(playerID, x, y, buildingType, + command - OpenSHC::Commands::M_MAPPER_CESS_PIT1, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_STATUE: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placePositiveFearfactor, this)(playerID, x, y, buildingType, + command - OpenSHC::Commands::M_MAPPER_STATUE1, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_SHRINE: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placePositiveFearfactor, this)(playerID, x, y, buildingType, + command - OpenSHC::Commands::M_MAPPER_SHRINE1, buildingSize, buildingOrientation, averageHeight); + break; + case OpenSHC::Map::Buildings::BT_POND: + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placePositiveFearfactor, this)(playerID, x, y, buildingType, + command - OpenSHC::Commands::M_MAPPER_POND1, buildingSize, buildingOrientation, averageHeight); + break; + } + + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + if (shortType != OpenSHC::Map::Buildings::BT_PITCHDITCH) { + MACRO_CALL_MEMBER(OpenSHC::Map::WallAndPitchState_Func::startBuildingDestructionConfirmation, + DAT_WallAndPitchState::ptr)(this->placedBuildingID); + } else if (pitchDitchID != 0) { + MACRO_CALL_MEMBER( + OpenSHC::Map::WallAndPitchState_Func::placePitchDitch, DAT_WallAndPitchState::ptr)(pitchDitchID); + } + } + MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::processPlacementResourceLossForBuildingType, + DAT_BuildingsState::ptr)(playerID, (BuildingType)(int)(short)shortType, 0); + if (MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func:: + hasLessWoodThanTheCostOfAWoodcuttersHutAndNoWoodcutters, + DAT_BuildingsState::ptr)(playerID, (int)(short)shortType) + != 0) { + DAT_BuildingsState::instance.buildings[this->placedBuildingID].field203_0x288 = 1; + } + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + int radius; + if (buildingSize < 6) { + radius = 9; + } else { + radius = buildingSize + 5; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(radius, x, y); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + DAT_TileMapState::instance.field204_0x554a30 = 1; + if (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SOLITARY) { + this->forceUpdateMacroLayerFlag = 1; + this->field68_0x55487c = 200; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeDairyfarm.cpp b/src/OpenSHC/Map/TileMapState/placeDairyfarm.cpp new file mode 100644 index 000000000..4e4f3ee8a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeDairyfarm.cpp @@ -0,0 +1,126 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::Logic1; + using OpenSHC::Map::LogicHelpers::Logic2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005154D0 + void TileMapState::placeDairyfarm(int param_1, uint param_2, uint param_3, undefined4 param_4, undefined4 param_5, + int* param_6, undefined4 param_7) + { + int iVar1; + uint uVar2; + int buildingID; + int iVar3; + int* piVar4; + int iVar5; + uVar2 = param_3; + iVar3 = 0; + buildingID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, + DAT_BuildingsState::ptr)(param_1, param_2, param_3, (undefined4)((int)(param_7)), + (BuildingType)((int)((int)(short)param_4)), 3, param_1, (int)((int)(param_6))); + this->placedBuildingID = buildingID; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::markBuildingFootprintFlag, this)( + param_2, (int)((int)(param_3)), 10); + iVar5 = 0; + param_6 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[0]; + do { + iVar1 = DAT_GameState::instance.playerDataArray[param_1].dairyFarmVariationMod4; + /* + 0 to 3*27 + 26 = 107 + */ + iVar1 = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field1009_0xf5c[iVar1][iVar5].offset.y + + param_3] + .addXgetTile + + DAT_TerrainDefinedData::instance.field1009_0xf5c[iVar1][iVar5].offset.x + param_2; + *param_6 = iVar1; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar1); + if (3 < iVar5) { + this->DamageLayer[*param_6] + = (byte)DAT_TerrainDefinedData::instance + .field1009_0xf5c[DAT_GameState::instance.playerDataArray[param_1].dairyFarmVariationMod4] + [iVar5] + .property; + } + param_6 = param_6 + 1; + iVar5 = iVar5 + 1; + } while (iVar5 < 27); + piVar4 = &DAT_GameState::instance.playerDataArray[param_1].dairyFarmVariationMod4; + *piVar4 = *piVar4 + 1; + if (3 < DAT_GameState::instance.playerDataArray[param_1].dairyFarmVariationMod4) { + DAT_GameState::instance.playerDataArray[param_1].dairyFarmVariationMod4 = 0; + } + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(iVar3, 3); + param_3._0_2_ = (short)buildingID; + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + uVar2].addXgetTile + + this->buildingX + param_2; + this->HeightLayer[iVar5] = (byte)param_7; + this->LogicLayer[iVar5] = this->LogicLayer[iVar5] | 0x400; + this->BuildingLayer[iVar5] = (short)param_3; + this->BuildingWasLayer[iVar5] = (uchar)param_4; + this->ChangedLayer[iVar5] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)(param_1, iVar5, + this->buildingY + uVar2, 2, OpenSHC::Map::LogicHelpers::L_NONE, + OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB); + iVar3 = iVar3 + 1; + } while (iVar3 < this->constructionTileCount); + piVar4 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[4]; + iVar3 = 23; + do { + iVar5 = *piVar4; + this->LogicLayer[iVar5] = this->LogicLayer[iVar5] | 0x8000000; + this->BuildingLayer[iVar5] = (short)param_3; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)( + (int)DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[iVar5], iVar5); + piVar4 = piVar4 + 1; + iVar3 = iVar3 + -1; + this->BuildingWasLayer[iVar5] = (uchar)param_4; + this->ChangedLayer[iVar5] = 2; + } while (iVar3 != 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + if (((param_1 == DAT_GameSynchronyState::instance.currentPlayerSlotID) + && (DAT_GameState::instance.playerDataArray[param_1].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount)) + && ((DAT_GameState::instance.playerDataArray[param_1].populationCap + <= DAT_GameState::instance.playerDataArray[param_1].currentPopulation + || (DAT_GameState::instance.playerDataArray[param_1].popularity < 5000)))) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)(1, 0x4d, 1, (TextMessageBLLookupStructUnion)0x0, 100, 6000); + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeDefensiveStructureTile.cpp b/src/OpenSHC/Map/TileMapState/placeDefensiveStructureTile.cpp new file mode 100644 index 000000000..c906657ff --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeDefensiveStructureTile.cpp @@ -0,0 +1,127 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumShort": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005034A0 + void TileMapState::placeDefensiveStructureTile(int playerID, uint x, uint y, MappersEnum mapper) + { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::validateWallPlacementAtTile, this)(playerID, x, y, mapper); + if (this->illegalBuild != FALSE) { + return; + } + + int tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + short command = (short)mapper; + this->DamageLayer[tile] = 0; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + if (command == OpenSHC::Commands::M_MAPPER_WALL) { + this->HeightLayer[tile] = this->DefaultHeightLayer[tile] + 0x5a; + } else if (command == OpenSHC::Commands::M_MAPPER_WOODWALL) { + this->HeightLayer[tile] = this->DefaultHeightLayer[tile] + 0x3c; + } else { + bool crenellated = true; + if (command == OpenSHC::Commands::M_MAPPER_CRENAL) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_CRENEL; + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x62; + } else if (command == OpenSHC::Commands::M_MAPPER_CRENAL2) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_CRENEL; + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x44; + } else { + crenellated = false; + if (command == OpenSHC::Commands::M_MAPPER_STAIR1) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_STAIRS; + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x50; + } else if (command == OpenSHC::Commands::M_MAPPER_STAIR2) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_STAIRS; + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x40; + } else if (command == OpenSHC::Commands::M_MAPPER_STAIR3) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_STAIRS; + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x30; + } else if (command == OpenSHC::Commands::M_MAPPER_STAIR4) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_STAIRS; + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x20; + } + } + /* a crenel on an odd tile of one axis carries the alternate variation */ + if (crenellated && ((x & 1) == 0) != ((y & 1) == 0)) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_CRENEL_VARIATIONUnk; + } + } + + uint logic = this->LogicLayer[tile]; + this->LogicLayer[tile] + = logic & ~(L_UNKNOWN_WALL_RELATED | L_BOULDERS | L_PEBBLES | L_IRON) | L_WALL_OR_GATEHOUSE; + if ((logic & L_PLAIN2_AND_PITCH) != 0) { + this->LogicLayer[tile] + = logic & ~(L_PLAIN2_AND_PITCH | L_UNKNOWN_WALL_RELATED | L_BOULDERS | L_PEBBLES | L_IRON) + | L_WALL_OR_GATEHOUSE; + this->HeightLayer[tile] = this->HeightLayer[tile] + 4; + } + if ((this->LogicLayer[tile] & L_TREE) != 0 && this->OrganismLayer[tile] < 2000) { + DAT_LandscapeState::instance.trees[this->OrganismLayer[tile]].state = 3; + } + if (command == OpenSHC::Commands::M_MAPPER_WOODWALL) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_UNKNOWN_WALL_RELATED; + } + this->WallOwnerLayer[tile] = this->WallOwnerLayer[tile] & 0xf8 | (char)playerID - 1U; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)(y, tile); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoat, this)(tile); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(7, x, y); + if (command != OpenSHC::Commands::M_MAPPER_STAIR1 && command != OpenSHC::Commands::M_MAPPER_STAIR2 + && command != OpenSHC::Commands::M_MAPPER_STAIR3 && command != OpenSHC::Commands::M_MAPPER_STAIR4 + && command != OpenSHC::Commands::M_MAPPER_STAIR5 && command != OpenSHC::Commands::M_MAPPER_STAIR6) { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processWallBuildingLoss, DAT_BuildingsState::ptr)(playerID, 1, 0, 0); + } + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeDrawbridge.cpp b/src/OpenSHC/Map/TileMapState/placeDrawbridge.cpp new file mode 100644 index 000000000..b123fac22 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeDrawbridge.cpp @@ -0,0 +1,68 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00514DE0 + void TileMapState::placeDrawbridge( + int playerID, uint x, uint y, undefined4 buildingType, uint width, int* orientation, undefined4 averageHeight) + { + int buildingID; + int _tileIndex; + Building* _pTileRef; + int tile; + _tileIndex = 0; + buildingID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, + DAT_BuildingsState::ptr)(playerID, x, y, (undefined4)((int)(averageHeight)), + (BuildingType)((int)((int)(short)buildingType)), width, playerID, (int)((int)(orientation))); + orientation = DAT_TerrainDefinedData::instance.DrawbridgeOrientationMapping[(int)orientation / 2]; + _pTileRef = &DAT_BuildingsState::instance.buildings[buildingID]; + this->placedBuildingID = buildingID; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + _tileIndex, (int)((int)(width))); + /* + fixme: repurposed parameter + */ + averageHeight._0_2_ = (short)buildingID; + tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + _pTileRef->tileRefs[0] = tile; + this->BuildingLayer[tile] = (short)averageHeight; + this->BuildingWasLayer[tile] = (uchar)buildingType; + this->ChangedLayer[tile] = 2; + if (DAT_TerrainDefinedData::instance.DrawbridgeTileMoatProperty[_tileIndex] != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | 1024; + } + if (*orientation == 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_MOAT); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::createMoatData, this)( + playerID, this->buildingX + x, this->buildingY + y, 1); + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_MOAT); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMoatVisualStateAtTile, this)(tile, 0); + this->HeightLayer[tile] = 0; + } + orientation = orientation + 1; + _tileIndex = _tileIndex + 1; + _pTileRef = &_pTileRef->tileRefs[1]; + } while (_tileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeEngineersguild.cpp b/src/OpenSHC/Map/TileMapState/placeEngineersguild.cpp new file mode 100644 index 000000000..eba40b8f6 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeEngineersguild.cpp @@ -0,0 +1,99 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507BD0 + void TileMapState::placeEngineersguild( + int param_1, int* param_2, uint param_3, undefined4 param_4, uint param_5, int param_6, undefined4 param_7) + { + int* piVar1; + int iVar2; + int buildingID; + int iVar3; + uint y; + uint x; + int iVar4; + int iVar5; + int local_8; + piVar1 = param_2; + iVar5 = DAT_GameCore::instance.uniqueGameObjectTracker; + iVar4 = 0; + if (param_6 == 0xf) { + local_8 = 0; + } else { + local_8 = param_6 / 2; + } + iVar2 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, + DAT_BuildingsState::ptr)(param_1, (uint)((int)(param_2)), param_3, (undefined4)((int)(param_7)), + (BuildingType)((int)((int)(short)param_4)), param_5, param_1, 0xf); + buildingID = (int)(short)iVar2; + this->placedBuildingID = buildingID; + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = iVar5; + param_2 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[0]; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar4, (int)((int)(param_5))); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + this->buildingX + (int)piVar1; + *param_2 = iVar3; + this->HeightLayer[iVar3] = (byte)param_7; + this->LogicLayer[iVar3] = this->LogicLayer[iVar3] | 1024; + this->BuildingLayer[iVar3] = (short)iVar2; + iVar4 = iVar4 + 1; + param_2 = param_2 + 1; + this->BuildingWasLayer[iVar3] = (uchar)param_4; + this->ChangedLayer[iVar3] = 2; + } while (iVar4 < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + x = (int)piVar1 + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_8][1].x; + y = param_3 + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_8][1].y; + iVar4 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, x, y, (undefined4)((int)(param_7)), OpenSHC::Map::Buildings::BT_PARADEGROUND, 5, param_1, 0xf); + iVar2 = (int)(short)iVar4; + DAT_BuildingsState::instance.buildings[iVar2].uidWhenPlaced = iVar5; + DAT_BuildingsState::instance.buildings[iVar2].quarryStockpileID = (undefined2)this->placedBuildingID; + param_5 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + param_5, (int)((int)(DAT_BuildingsState::instance.buildings[iVar2].widthOrHeight))); + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar5); + this->HeightLayer[iVar5] = (byte)param_7; + this->BuildingLayer[iVar5] = (short)iVar4; + param_5 = param_5 + 1; + this->BuildingWasLayer[iVar5] = (uchar)param_4; + this->ChangedLayer[iVar5] = 2; + } while ((int)param_5 < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(iVar2); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(iVar2); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, x, y); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupEngineersGuildParagroundPositions, + DAT_BuildingsState::ptr)(param_1); + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeGatehouseLarge.cpp b/src/OpenSHC/Map/TileMapState/placeGatehouseLarge.cpp new file mode 100644 index 000000000..13eb06d5e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeGatehouseLarge.cpp @@ -0,0 +1,62 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507560 + void TileMapState::placeGatehouseLarge( + int param_1, uint param_2, uint param_3, undefined4 param_4, uint param_5, int param_6, undefined4 param_7) + { + int uniqueID = DAT_GameCore::instance.uniqueGameObjectTracker; + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, param_2, param_3, param_7, (BuildingType)(short)param_4, param_5, param_1, param_6); + this->placedBuildingID = buildingID; + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = uniqueID; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, param_5); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + param_2 + this->buildingX; + if (buildingSizeTileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[buildingSizeTileIndex] = tile; + } + this->HeightLayer[tile] = (char)param_7 + 0x5a; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_WALL_OR_GATEHOUSE; + this->WallOwnerLayer[tile] = this->WallOwnerLayer[tile] & 0xf8 | (char)param_1 - 1U; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x20; + this->BuildingLayer[tile] = (short)buildingID; + buildingSizeTileIndex++; + this->BuildingWasLayer[tile] = (uchar)param_4; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + + int climbVariant = 0; + if (param_6 == 0x50) { + climbVariant = 2; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::createClimbData, DAT_PathFindingState::ptr)( + 3, buildingID, 0, climbVariant, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeGatehouseSmall.cpp b/src/OpenSHC/Map/TileMapState/placeGatehouseSmall.cpp new file mode 100644 index 000000000..78a745e03 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeGatehouseSmall.cpp @@ -0,0 +1,62 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507420 + void TileMapState::placeGatehouseSmall( + int param_1, uint param_2, uint param_3, undefined4 param_4, uint param_5, int param_6, undefined4 param_7) + { + int uniqueID = DAT_GameCore::instance.uniqueGameObjectTracker; + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, param_2, param_3, param_7, (BuildingType)(short)param_4, param_5, param_1, param_6); + this->placedBuildingID = buildingID; + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = uniqueID; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, param_5); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + param_2 + this->buildingX; + if (buildingSizeTileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[buildingSizeTileIndex] = tile; + } + this->HeightLayer[tile] = (char)param_7 + 0x5a; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_WALL_OR_GATEHOUSE; + this->WallOwnerLayer[tile] = this->WallOwnerLayer[tile] & 0xf8 | (char)param_1 - 1U; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x20; + this->BuildingLayer[tile] = (short)buildingID; + buildingSizeTileIndex++; + this->BuildingWasLayer[tile] = (uchar)param_4; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + + int climbVariant = 0; + if (param_6 == 0x50) { + climbVariant = 2; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::createClimbData, DAT_PathFindingState::ptr)( + 4, buildingID, 0, climbVariant, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeHopfarm.cpp b/src/OpenSHC/Map/TileMapState/placeHopfarm.cpp new file mode 100644 index 000000000..1a90b369f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeHopfarm.cpp @@ -0,0 +1,136 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::Logic1; + using OpenSHC::Map::LogicHelpers::Logic2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005151C0 + void TileMapState::placeHopfarm( + int param_1, uint param_2, uint param_3, undefined4 param_4, undefined4 param_5, int param_6, int param_7) + { + undefined1* puVar1; + uint uVar2; + int buildingID; + XYPair* pXVar3; + int iVar4; + int* piVar5; + int buildingSizeTileIndex; + uVar2 = param_2; + buildingSizeTileIndex = 0; + buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(param_1, param_2, + param_3, (undefined4)((int)(param_7)), (BuildingType)((int)((int)(short)param_4)), 3, param_1, param_6); + this->placedBuildingID = buildingID; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::markBuildingFootprintFlag, this)( + param_2, (int)((int)(param_3)), 9); + iVar4 = *(int*)&DAT_GameState::instance.playerDataArray[param_1].unkHopFarmVariation; + param_2 = 4; + pXVar3 = DAT_TerrainDefinedData::instance.HopFarmProperty1[iVar4]; + piVar5 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[1]; + do { + piVar5[-1] = DAT_ViewportRenderState::instance.translationMatrix[pXVar3->y + param_3].addXgetTile + + ((XYPair*)&pXVar3->x)->x + uVar2; + *piVar5 = DAT_ViewportRenderState::instance.translationMatrix[pXVar3[1].y + param_3].addXgetTile + + pXVar3[1].x + uVar2; + piVar5[1] = DAT_ViewportRenderState::instance.translationMatrix[pXVar3[2].y + param_3].addXgetTile + + pXVar3[2].x + uVar2; + piVar5[2] = DAT_ViewportRenderState::instance.translationMatrix[pXVar3[3].y + param_3].addXgetTile + + pXVar3[3].x + uVar2; + piVar5[3] = DAT_ViewportRenderState::instance.translationMatrix[pXVar3[4].y + param_3].addXgetTile + + pXVar3[4].x + uVar2; + param_2 = param_2 - 1; + piVar5[4] = DAT_ViewportRenderState::instance.translationMatrix[pXVar3[5].y + param_3].addXgetTile + + pXVar3[5].x + uVar2; + pXVar3 = pXVar3 + 6; + piVar5 = piVar5 + 6; + } while (param_2 != 0); + *(ushort*)&DAT_BuildingsState::instance.buildings[buildingID].field_0x26e + = (-(ushort)(iVar4 != 0) & 0xffb6) + 0x51; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, 3); + param_6._0_2_ = (short)buildingID; + iVar4 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + this->buildingX + uVar2; + this->HeightLayer[iVar4] = (byte)param_7; + this->LogicLayer[iVar4] = this->LogicLayer[iVar4] | 1024; + this->BuildingLayer[iVar4] = (short)param_6; + this->BuildingWasLayer[iVar4] = (uchar)param_4; + this->ChangedLayer[iVar4] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)(param_1, iVar4, + this->buildingY + param_3, 2, OpenSHC::Map::LogicHelpers::L_NONE, + OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB); + buildingSizeTileIndex = buildingSizeTileIndex + 1; + } while (buildingSizeTileIndex < this->constructionTileCount); + param_7 = 0; + do { + iVar4 = *(int*)&DAT_GameState::instance.playerDataArray[param_1].unkHopFarmVariation; + iVar4 + = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.HopFarmProperty1[iVar4][param_7].y + param_3] + .addXgetTile + + DAT_TerrainDefinedData::instance.HopFarmProperty1[iVar4][param_7].x + uVar2; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar4); + this->LogicLayer[iVar4] = this->LogicLayer[iVar4] | 33554432; + this->BuildingLayer[iVar4] = (short)param_6; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)( + DAT_TerrainDefinedData::instance + .HopFarmProperty1[*(int*)&DAT_GameState::instance.playerDataArray[param_1].unkHopFarmVariation] + [param_7] + .y + + param_3, + iVar4); + param_7 = param_7 + 1; + this->BuildingWasLayer[iVar4] = (uchar)param_4; + this->ChangedLayer[iVar4] = 2; + } while (param_7 < 0x18); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + puVar1 = &DAT_GameState::instance.playerDataArray[param_1].unkHopFarmVariation; + *(int*)puVar1 = *(int*)puVar1 + 1; + if (1 < *(int*)&DAT_GameState::instance.playerDataArray[param_1].unkHopFarmVariation) { + *(undefined4*)&DAT_GameState::instance.playerDataArray[param_1].unkHopFarmVariation = 0; + } + if (((param_1 == DAT_GameSynchronyState::instance.currentPlayerSlotID) + && (DAT_GameState::instance.playerDataArray[param_1].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount)) + && ((DAT_GameState::instance.playerDataArray[param_1].populationCap + <= DAT_GameState::instance.playerDataArray[param_1].currentPopulation + || (DAT_GameState::instance.playerDataArray[param_1].popularity < 5000)))) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)(1, 0x4d, 1, (TextMessageBLLookupStructUnion)0x0, 100, 6000); + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeKeep.cpp b/src/OpenSHC/Map/TileMapState/placeKeep.cpp new file mode 100644 index 000000000..c2da3db09 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeKeep.cpp @@ -0,0 +1,232 @@ + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/Player/PlayerDataBuildingCategoryEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + + using OpenSHC::Game::Player::PlayerDataBuildingCategoryEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::LogicHelpers::Logic1; + using OpenSHC::Map::LogicHelpers::Logic2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005146D0 + void TileMapState::placeKeep( + int playerID, uint x, uint y, BuildingType type, uint size, int orientation, int xyValue) + { + uint baseX = x; + uint baseY = y; + if (this->field195_0x554a24 != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::demolishBuildingsInKeepsConstructionFootprint, this)( + playerID, x, y, type, size, orientation, xyValue); + this->field195_0x554a24 = 0; + } + + int uid = DAT_GameCore::instance.uniqueGameObjectTracker; + int keepKind = 0; + byte logic2 = this->Logic2Layer[DAT_ViewportRenderState::instance.translationMatrix[baseY].addXgetTile + baseX]; + int keepID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, baseX, baseY, xyValue, (BuildingType)(short)type, size, playerID, 0xf); + this->placedBuildingID = keepID; + DAT_BuildingsState::instance.buildings[keepID].uidWhenPlaced = uid; + short keepOrientation = (short)orientation; + DAT_BuildingsState::instance.buildings[keepID].orientation = keepOrientation; + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::computeBuildingCategoryEntryPointAndDestroyEarlierBuilding, DAT_GameState::ptr)( + keepID, playerID, OpenSHC::Game::Player::PDBCE_KEEP); + if (orientation == 0xf) { + orientation = 0; + } + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + baseY].addXgetTile + this->buildingX + baseX; + if (index < 0x24) { + DAT_BuildingsState::instance.buildings[keepID].tileRefs[index] = tile; + } + this->HeightLayer[tile] = (byte)xyValue; + if ((short)type == OpenSHC::Map::Buildings::BT_MANORHOUSE) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } else { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_KEEP_NON_MANOR_HOUSE; + } + this->BuildingLayer[tile] = (short)keepID; + this->BuildingWasLayer[tile] = (uchar)type; + /* grass and scrub come back as scrub, anything else as bare earth */ + Logic2 terrain; + uint brush; + if ((logic2 & (OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS | OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB)) == 0) { + terrain = OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + brush = 6; + } else { + terrain = OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB; + brush = 3; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)( + playerID, tile, this->buildingY + baseY, brush, OpenSHC::Map::LogicHelpers::L_NONE, terrain); + this->ChangedLayer[tile] = 2; + index++; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(keepID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(keepID); + if ((short)type == OpenSHC::Map::Buildings::BT_MANORHOUSE || (short)type == OpenSHC::Map::Buildings::BT_STONEKEEP) { + keepKind = 2; + } + + /* the three keep doors and the two outbuildings are laid out per keep type and orientation */ + int group = orientation / 2 + -0xa0 + (short)type * 4; + { + int doorX = DAT_TerrainDefinedData::instance.field130_0x264[0][group * 3 + 0].x + baseX; + uint doorY = DAT_TerrainDefinedData::instance.field130_0x264[0][group * 3 + 0].y + baseY; + int doorTile = DAT_ViewportRenderState::instance.translationMatrix[doorY].addXgetTile + doorX; + int doorID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, doorX, doorY, xyValue, OpenSHC::Map::Buildings::BT_KEEPDOOR_LEFT, 1, playerID, 0xf); + DAT_BuildingsState::instance.buildings[doorID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[doorID].unknownManorHouseOrStoneKeepRelated = keepKind; + DAT_BuildingsState::instance.buildings[doorID].quarryStockpileID = (short)this->placedBuildingID; + DAT_BuildingsState::instance.buildings[doorID].orientation = keepOrientation; + this->LogicLayer[doorTile] = this->LogicLayer[doorTile] | L_BUILDING; + this->BuildingLayer[doorTile] = (short)doorID; + this->ChangedLayer[doorTile] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)( + (short)DAT_BuildingsState::instance.buildings[doorID].y, doorTile); + } + + { + int doorX = DAT_TerrainDefinedData::instance.field130_0x264[0][group * 3 + 1].x + baseX; + uint doorY = DAT_TerrainDefinedData::instance.field130_0x264[0][group * 3 + 1].y + baseY; + int doorTile = DAT_ViewportRenderState::instance.translationMatrix[doorY].addXgetTile + doorX; + int doorID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, doorX, doorY, xyValue, OpenSHC::Map::Buildings::BT_KEEPDOOR, 1, playerID, 0xf); + DAT_BuildingsState::instance.buildings[doorID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[doorID].unknownManorHouseOrStoneKeepRelated = keepKind / 2; + DAT_BuildingsState::instance.buildings[doorID].quarryStockpileID = (short)this->placedBuildingID; + DAT_BuildingsState::instance.buildings[doorID].orientation = keepOrientation; + this->MiscDisplayLayer[doorTile] = this->MiscDisplayLayer[doorTile] | 4; + this->BuildingLayer[doorTile] = (short)doorID; + this->ChangedLayer[doorTile] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)( + (short)DAT_BuildingsState::instance.buildings[doorID].y, doorTile); + } + + { + int doorX = DAT_TerrainDefinedData::instance.field130_0x264[0][group * 3 + 2].x + baseX; + uint doorY = DAT_TerrainDefinedData::instance.field130_0x264[0][group * 3 + 2].y + baseY; + int doorTile = DAT_ViewportRenderState::instance.translationMatrix[doorY].addXgetTile + doorX; + int doorID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, doorX, doorY, xyValue, OpenSHC::Map::Buildings::BT_KEEPDOOR_RIGHT, 1, playerID, 0xf); + DAT_BuildingsState::instance.buildings[doorID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[doorID].unknownManorHouseOrStoneKeepRelated = keepKind; + DAT_BuildingsState::instance.buildings[doorID].quarryStockpileID = (short)this->placedBuildingID; + DAT_BuildingsState::instance.buildings[doorID].orientation = keepOrientation; + this->LogicLayer[doorTile] = this->LogicLayer[doorTile] | L_BUILDING; + this->BuildingLayer[doorTile] = (short)doorID; + this->ChangedLayer[doorTile] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)( + (short)DAT_BuildingsState::instance.buildings[doorID].y, doorTile); + } + + uint campX = baseX + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x384[0][group].xOffset; + uint campY = baseY + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x384[0][group].yOffset; + int campID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, campX, campY, xyValue, OpenSHC::Map::Buildings::BT_CAMPGROUND, 7, playerID, 0xf); + DAT_BuildingsState::instance.buildings[campID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[campID].quarryStockpileID = (short)this->placedBuildingID; + DAT_BuildingsState::instance.buildings[campID].orientation = keepOrientation; + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::computeBuildingCategoryEntryPointAndDestroyEarlierBuilding, DAT_GameState::ptr)( + campID, playerID, OpenSHC::Game::Player::PDBCE_CAMPGROUND); + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, 7); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)( + DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + campY].addXgetTile + this->buildingX + campX); + index++; + } while (index < this->constructionTileCount); + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + index, DAT_BuildingsState::instance.buildings[campID].widthOrHeight); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + campY].addXgetTile + this->buildingX + campX; + if (index < 0x18) { + DAT_BuildingsState::instance.buildings[campID].tileRefs[index] + = DAT_ViewportRenderState::instance + .translationMatrix[(short)DAT_BuildingsState::instance.buildings[campID].y + DAT_TerrainDefinedData::instance.field197_0x444[index].y] + .addXgetTile + + (short)DAT_BuildingsState::instance.buildings[campID].x + DAT_TerrainDefinedData::instance.field197_0x444[index].x; + } + this->HeightLayer[tile] = (byte)xyValue; + /* only the campground's cross of five middle tiles blocks movement */ + if (this->buildingX == 3 && this->buildingY == 3) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } + if (this->buildingX == 3 && this->buildingY == 2) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } + if (this->buildingX == 3 && this->buildingY == 4) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } + if (this->buildingX == 2 && this->buildingY == 3) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } + if (this->buildingX == 4 && this->buildingY == 3) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } + this->BuildingLayer[tile] = (short)campID; + this->ChangedLayer[tile] = 2; + Logic2 terrain; + uint brush; + if ((logic2 & (OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS | OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB)) == 0) { + terrain = OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + brush = 6; + } else { + terrain = OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB; + brush = 3; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)( + playerID, tile, this->buildingY + campY, brush, OpenSHC::Map::LogicHelpers::L_NONE, terrain); + index++; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(campID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(campID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(10, campX, campY); + + int stockpileX = DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x3e4[0][group].xOffset; + int stockpileY = DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x3e4[0][group].yOffset; + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, 5); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)( + DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + stockpileY + baseY].addXgetTile + this->buildingX + + stockpileX + baseX); + index++; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeStockpile, this)( + playerID, stockpileX + baseX, stockpileY + baseY, 10, 5, 0xf, xyValue); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeKillingPit.cpp b/src/OpenSHC/Map/TileMapState/placeKillingPit.cpp new file mode 100644 index 000000000..df9b8323e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeKillingPit.cpp @@ -0,0 +1,89 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::Logic1; + using OpenSHC::Map::LogicHelpers::Logic2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00514520 + void TileMapState::placeKillingPit(int playerID, uint x, uint y, undefined4 buildingType, uint sizeIndex, + int buildingState, int height) + { + /* the original masks the terrain bits once, before the building is even created */ + int grassOrScrub = this->Logic2Layer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x] + & (OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS | OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB); + int index = 0; + int buildingID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, x, y, height, + (BuildingType)(int)(short)buildingType, sizeIndex, playerID, buildingState); + this->placedBuildingID = buildingID; + if ((short)buildingType != 0x43) { + height = height - 4; + if (height < 0) { + height = 0; + } + } + + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, sizeIndex); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + if (index < 0x24) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[index] = tile; + } + this->HeightLayer[tile] = (byte)height; + this->BuildingLayer[tile] = (short)buildingID; + this->BuildingWasLayer[tile] = (uchar)(short)buildingType; + if ((short)buildingType != 0x43) { + /* bug:fixme: a pit dug in grass comes back as thick scrub, not as the terrain it replaced */ + Logic2 terrain; + if (grassOrScrub == 0) { + terrain = OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + } else { + terrain = OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)( + playerID, tile, this->buildingY + y, 3, OpenSHC::Map::LogicHelpers::L_NONE, terrain); + } + index++; + this->ChangedLayer[tile] = 2; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount + && (DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)( + 1, 0x4d, 1, OpenSHC::UI::TextMessageBLLookupStructUnion(), 100, 6000); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeOilsmelter.cpp b/src/OpenSHC/Map/TileMapState/placeOilsmelter.cpp new file mode 100644 index 000000000..f22de7a1e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeOilsmelter.cpp @@ -0,0 +1,89 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508030 + void TileMapState::placeOilsmelter(int playerID, uint x, uint y, undefined4 height, uint sizeIndex, + int buildingTypeLike, undefined4 param_7) + { + int uid = DAT_GameCore::instance.uniqueGameObjectTracker; + + if (buildingTypeLike == 0xf) { + buildingTypeLike = 0; + } else { + buildingTypeLike = buildingTypeLike / 2; + } + + /* the id is kept zero-extended for the layer stores and sign-extended for the array index */ + ushort smelterID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, x, y, + param_7, (BuildingType)(int)(short)height, sizeIndex, playerID, 0xf); + int buildingID = (short)smelterID; + this->placedBuildingID = buildingID; + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = uid; + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, sizeIndex); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + this->HeightLayer[tile] = (byte)param_7; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = smelterID; + index++; + this->BuildingWasLayer[tile] = (uchar)height; + this->ChangedLayer[tile] = 2; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + + /* the campfire sits at a fixed offset from the smelter, chosen by the smelter's size */ + x = x + DAT_TerrainDefinedData::instance.field63_0x19c[buildingTypeLike].x; + y = y + DAT_TerrainDefinedData::instance.field63_0x19c[buildingTypeLike].y; + ushort campfire = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, x, y, + param_7, OpenSHC::Map::Buildings::BT_CAMPFIRE, 4, playerID, 0xf); + int campfireID = (short)campfire; + DAT_BuildingsState::instance.buildings[campfireID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[campfireID].quarryStockpileID = (short)this->placedBuildingID; + DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID = smelterID; + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + index, DAT_BuildingsState::instance.buildings[campfireID].widthOrHeight); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(tile); + this->HeightLayer[tile] = (byte)param_7; + this->BuildingLayer[tile] = campfire; + index++; + this->BuildingWasLayer[tile] = (uchar)height; + this->ChangedLayer[tile] = 2; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setTileRefsForOilSmelter, DAT_BuildingsState::ptr)(campfireID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(campfireID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(campfireID); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, x, y); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placePitchDitch.cpp b/src/OpenSHC/Map/TileMapState/placePitchDitch.cpp new file mode 100644 index 000000000..09131e506 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placePitchDitch.cpp @@ -0,0 +1,74 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005116E0 + int TileMapState::placePitchDitch(undefined4 playerID, uint x, uint y) + { + if (x > 399 || y > 399) { + return 0; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return 0; + } + + int existingPitchDitchID = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getPitchDitchIDForTile, this)( + DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x); + if (existingPitchDitchID != 0) { + return existingPitchDitchID; + } + + for (int pitchDitchID = 1; pitchDitchID < 4000; pitchDitchID++) { + if (this->pitchDitches[pitchDitchID].owner != 0) { + continue; + } + + if (this->maxPitchDitchCount <= pitchDitchID) { + this->maxPitchDitchCount = pitchDitchID + 1; + } + this->pitchDitches[pitchDitchID].owner = (short)playerID; + this->pitchDitches[pitchDitchID].uid = DAT_GameCore::instance.uniqueGameObjectTracker; + DAT_GameCore::instance.uniqueGameObjectTracker = DAT_GameCore::instance.uniqueGameObjectTracker + 1; + this->pitchDitches[pitchDitchID].y = (short)y; + this->pitchDitches[pitchDitchID].x = (short)x; + this->pitchDitches[pitchDitchID].state = 0; + this->pitchDitches[pitchDitchID].field7_0x12 = 0; + this->pitchDitches[pitchDitchID].tile + = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + this->pitchDitches[pitchDitchID].rng = SEC_RNG::instance.currentNumber2; + if (this->HeightLayer[this->pitchDitches[pitchDitchID].tile] < 5) { + this->HeightLayer[this->pitchDitches[pitchDitchID].tile] = 0; + } else { + this->HeightLayer[this->pitchDitches[pitchDitchID].tile] + = this->HeightLayer[this->pitchDitches[pitchDitchID].tile] - 4; + } + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + this->LogicLayer[this->pitchDitches[pitchDitchID].tile] + = this->LogicLayer[this->pitchDitches[pitchDitchID].tile] | L_PLAIN2_AND_PITCH; + this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] + = this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] & 0xbfff; + this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] + = this->MiscDisplayLayer[this->pitchDitches[pitchDitchID].tile] & 0xdfff; + this->ChangedLayer[this->pitchDitches[pitchDitchID].tile] = 2; + return pitchDitchID; + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placePositiveFearfactor.cpp b/src/OpenSHC/Map/TileMapState/placePositiveFearfactor.cpp new file mode 100644 index 000000000..22e644faa --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placePositiveFearfactor.cpp @@ -0,0 +1,67 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00506F40 + void TileMapState::placePositiveFearfactor(int playerID, uint x, uint y, undefined4 buildingType, + undefined4 variation, uint buildingSize, undefined4 param_7, undefined4 height) + { + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, x, y, height, (BuildingType)(short)buildingType, buildingSize, playerID, (short)variation); + this->placedBuildingID = buildingID; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, buildingSize); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + if (buildingSizeTileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[buildingSizeTileIndex] = tile; + } + this->HeightLayer[tile] = (byte)height; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = (short)buildingID; + buildingSizeTileIndex++; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount + && (DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)( + 1, 0x4d, 1, OpenSHC::UI::TextMessageBLLookupStructUnion(), 100, 6000); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeQuarry.cpp b/src/OpenSHC/Map/TileMapState/placeQuarry.cpp new file mode 100644 index 000000000..8ffcf266b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeQuarry.cpp @@ -0,0 +1,137 @@ + +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumShort": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508250 + void TileMapState::placeQuarry(int playerID, uint x, uint y, undefined4 buildingType, uint buildingSize, + int buildingOrientation, uint height) + { + uint baseY = y; + int uid = DAT_GameCore::instance.uniqueGameObjectTracker; + int quarryID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, x, y, height, + (BuildingType)(int)(short)buildingType, buildingSize, playerID, buildingOrientation); + this->placedBuildingID = quarryID; + DAT_BuildingsState::instance.buildings[quarryID].uidWhenPlaced = uid; + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, buildingSize); + /* the original reuses the y parameter to hold the truncated building id */ + y = (short)quarryID; + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + baseY].addXgetTile + x + this->buildingX; + index++; + this->HeightLayer[tile] = (byte)height; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = (short)y; + this->BuildingWasLayer[tile] = (uchar)buildingType; + this->ChangedLayer[tile] = 2; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(quarryID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(quarryID); + + /* nine attempts at a pile location, moving further from the quarry each time */ + int attempt = 1; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findQuarryPileLocation, DAT_BuildingsState::ptr)( + playerID, x, baseY, buildingSize, 2, attempt, OpenSHC::Commands::M_MAPPER_QUARRYPILE); + if (this->buildingPlacementFail == FALSE) { + break; + } + attempt++; + } while (attempt < 10); + if (this->buildingPlacementFail != FALSE) { + return; + } + + int pileX = x + DAT_BuildingsState::instance.DAT_TempXOffset; + int pileY = baseY + DAT_BuildingsState::instance.DAT_TempYOffset; + buildingSize = 0; + height = 1000; + uint highest = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(buildingSize, 2); + uint tileHeight = this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + pileY].addXgetTile + + pileX + this->buildingX]; + if (highest < tileHeight) { + highest = tileHeight; + } + if (tileHeight < height) { + height = tileHeight; + } + buildingSize = buildingSize + 1; + } while ((int)buildingSize < this->constructionTileCount); + int pileHeight = (int)(highest - height) / 2 + height; + + buildingSize = 0; + int pileID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, pileX, pileY, + pileHeight, OpenSHC::Map::Buildings::BT_QUARRYSTOCKPILE, 2, playerID, buildingOrientation); + MACRO_CALL_MEMBER(OpenSHC::AI::AIVState_Func::incrementStructureHeatMapTile, DAT_AIVState::ptr)( + pileX, pileY); + DAT_BuildingsState::instance.buildings[pileID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[quarryID].quarryStockpileID = (short)pileID; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(buildingSize, 2); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + pileY].addXgetTile + pileX + this->buildingX; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(tile); + this->HeightLayer[tile] = (byte)pileHeight; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = (short)pileID; + buildingSize = buildingSize + 1; + this->BuildingWasLayer[tile] = (uchar)buildingType; + this->ChangedLayer[tile] = 2; + } while ((int)buildingSize < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(pileID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(pileID); + + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < DAT_BuildingsState::instance.buildings[pileID].buildingTypeBasedEmployeeCount + && (DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)) { + /* "Not enough workers available to run this building." */ + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)( + 1, 0x4d, 1, OpenSHC::UI::TextMessageBLLookupStructUnion(), 100, 6000); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeRock.cpp b/src/OpenSHC/Map/TileMapState/placeRock.cpp new file mode 100644 index 000000000..f5f011b83 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeRock.cpp @@ -0,0 +1,145 @@ + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_OIL; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB4D0 + void TileMapState::placeRock(uint x, uint y, uint kind) + { + int rockType = 0; + int size = 1; + if (x > 399) { + return; + } + if (y > 399) { + return; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return; + } + /* every rock kind has its own footprint size and rock type */ + if ((short)kind == 0) { + rockType = 1; + } else if ((short)kind == 1) { + rockType = 2; + } else if ((short)kind == 2) { + rockType = 3; + } else if ((short)kind == 3) { + rockType = 4; + } else if ((short)kind == 4) { + size = 2; + rockType = 5; + } else if ((short)kind == 5) { + size = 2; + rockType = 6; + } else if ((short)kind == 6) { + size = 2; + rockType = 7; + } else if ((short)kind == 7) { + size = 2; + rockType = 8; + } else if ((short)kind == 8) { + size = 3; + rockType = 9; + } else if ((short)kind == 9) { + size = 3; + rockType = 0xa; + } else if ((short)kind == 0xa) { + size = 3; + rockType = 0xb; + } else if ((short)kind == 0xb) { + size = 3; + rockType = 0xc; + } else { + if ((short)kind == 0xc) { + rockType = 0xd; + } else if ((short)kind == 0xd) { + rockType = 0xe; + } else if ((short)kind == 0xe) { + rockType = 0xf; + } else if ((short)kind == 0xf) { + rockType = 0x10; + } + if (rockType != 0) { + size = 4; + } + } + + /* the original reuses the kind parameter to hold the lowest height under the footprint */ + kind = 1000; + uint highest = 0; + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + return; + } + if ((this->LogicLayer[tile] & L_BUILDING) != 0) { + return; + } + if (this->OrganismLayer[tile] != 0) { + return; + } + if (highest < this->HeightLayer[tile]) { + highest = this->HeightLayer[tile]; + } + if (this->HeightLayer[tile] < kind) { + kind = this->HeightLayer[tile]; + } + index++; + } while (index < this->constructionTileCount); + + byte height = (char)((int)(highest - kind) / 2) + (char)kind; + int rockID = MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::createRock, DAT_LandscapeState::ptr)( + x, y, rockType, size, this->rockOrientation); + index = 0; + short organism = (short)rockID + 2000; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + index++; + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + this->HeightLayer[tile] = height; + this->DefaultHeightLayer[tile] = height; + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_SEA | L_PLAIN2_AND_PITCH | L_BOULDERS | L_PEBBLES | L_IRON | L_RIVER | L_FORD | L_MARSH | L_MOAT + | L_OIL); + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_ROCKY; + this->OrganismLayer[tile] = organism; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(9, x, y); + this->forceUpdateMacroLayerFlag = 1; + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeSiegeTent.cpp b/src/OpenSHC/Map/TileMapState/placeSiegeTent.cpp new file mode 100644 index 000000000..fc3cb9cc3 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeSiegeTent.cpp @@ -0,0 +1,68 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00506E10 + void TileMapState::placeSiegeTent( + int playerID, uint x, uint y, undefined4 buildingType, uint buildingSize, int param_6, undefined4 height) + { + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, x, y, height, (BuildingType)(short)buildingType, buildingSize, playerID, param_6); + this->placedBuildingID = buildingID; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, buildingSize); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + if (buildingSizeTileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[buildingSizeTileIndex] = tile; + } + this->HeightLayer[tile] = (byte)height; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = (short)buildingID; + buildingSizeTileIndex++; + this->BuildingWasLayer[tile] = (uchar)(short)buildingType; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount + && (DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)( + 1, 0x4d, 1, OpenSHC::UI::TextMessageBLLookupStructUnion(), 100, 6000); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeSiegetowerPlaced.cpp b/src/OpenSHC/Map/TileMapState/placeSiegetowerPlaced.cpp new file mode 100644 index 000000000..80b5873e9 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeSiegetowerPlaced.cpp @@ -0,0 +1,50 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00506D40 + void TileMapState::placeSiegetowerPlaced( + int param_1, uint param_2, uint param_3, undefined4 param_4, uint param_5, int param_6, undefined4 param_7) + { + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, param_2, param_3, param_7, (BuildingType)(short)param_4, param_5, param_1, param_6); + this->placedBuildingID = buildingID; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, param_5); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + this->buildingX + param_2; + if (buildingSizeTileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[buildingSizeTileIndex] = tile; + } + this->HeightLayer[tile] = (byte)param_7; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_KEEP_NON_MANOR_HOUSE; + this->BuildingLayer[tile] = (short)buildingID; + buildingSizeTileIndex++; + this->BuildingWasLayer[tile] = (uchar)(short)param_4; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeStockpile.cpp b/src/OpenSHC/Map/TileMapState/placeStockpile.cpp new file mode 100644 index 000000000..e7b718403 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeStockpile.cpp @@ -0,0 +1,94 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508540 + void TileMapState::placeStockpile(int playerID, int x, int y, undefined4 buildingType, undefined4 param_5, + int variation, int averageHeight) + { + int uid = DAT_GameCore::instance.uniqueGameObjectTracker; + byte height = (char)averageHeight + 10; + byte wallOwner = (char)playerID - 1; + int type = (short)buildingType; + + int buildingID; + for (int part = 0; part < 4; part++) { + buildingID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)(playerID, + DAT_TerrainDefinedData::instance.Stockpile_BuildingPartsOffsets[part].x + x, DAT_TerrainDefinedData::instance.Stockpile_BuildingPartsOffsets[part].y + y, + averageHeight + 10, (BuildingType)type, 2, playerID, variation); + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = uid; + DAT_BuildingsState::instance.buildings[buildingID].field125_0x190 = part + 1; + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, 2); + index++; + int tile = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.Stockpile_BuildingPartsOffsets[part].y + this->buildingY + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.Stockpile_BuildingPartsOffsets[part].x + x + this->buildingX; + this->HeightLayer[tile] = height; + /* the stockpile body itself cannot be walked over */ + this->LogicLayer[tile] = this->LogicLayer[tile] | (L_STOCKPILEUnk | L_WALL_OR_GATEHOUSE | L_BUILDING); + this->WallOwnerLayer[tile] = this->WallOwnerLayer[tile] & 0xf8 | wallOwner; + this->BuildingLayer[tile] = (ushort)buildingID; + this->BuildingWasLayer[tile] = (uchar)buildingType; + this->ChangedLayer[tile] = 2; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + + this->placedBuildingID = buildingID; + /* the surrounding tiles carry the stockpile graphic but stay walkable */ + for (int slot = 0; slot < 9; slot++) { + int tile = DAT_ViewportRenderState::instance.translationMatrix[DAT_TerrainDefinedData::instance.StockpilePathableOffsets[slot].y + y].addXgetTile + + DAT_TerrainDefinedData::instance.StockpilePathableOffsets[slot].x + x; + this->LogicLayer[tile] = this->LogicLayer[tile] | (L_STOCKPILEUnk | L_WALL_OR_GATEHOUSE); + this->HeightLayer[tile] = height; + this->AlphaGFXLayer[tile] = (ushort)buildingID; + this->BuildingWasLayer[tile] = (uchar)buildingType; + this->WallOwnerLayer[tile] = this->WallOwnerLayer[tile] & 0xf8 | wallOwner; + this->ChangedLayer[tile] = 2; + } + + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount + && (DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)( + 1, 0x4d, 1, OpenSHC::UI::TextMessageBLLookupStructUnion(), 100, 6000); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeTower.cpp b/src/OpenSHC/Map/TileMapState/placeTower.cpp new file mode 100644 index 000000000..dbb3f9633 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeTower.cpp @@ -0,0 +1,62 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507130 + void TileMapState::placeTower( + int param_1, uint param_2, uint param_3, undefined4 param_4, uint size, int param_6, undefined4 param_7) + { + int uniqueID = DAT_GameCore::instance.uniqueGameObjectTracker; + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, param_2, param_3, param_7, (BuildingType)(short)param_4, size, param_1, param_6); + this->placedBuildingID = buildingID; + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = uniqueID; + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + param_2 + this->buildingX; + if (index < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[index] = tile; + } + this->HeightLayer[tile] = (byte)param_7; + if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[size][0] == index) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } else if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[size][1] == index) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } else if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[size][2] == index) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } else if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[size][3] == index) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + } else { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_KEEP_NON_MANOR_HOUSE; + } + this->BuildingLayer[tile] = (short)buildingID; + index++; + this->BuildingWasLayer[tile] = (uchar)param_4; + this->ChangedLayer[tile] = 2; + } while (index < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeTree.cpp b/src/OpenSHC/Map/TileMapState/placeTree.cpp new file mode 100644 index 000000000..a5f682917 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeTree.cpp @@ -0,0 +1,104 @@ + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00515DA0 + void TileMapState::placeTree(uint x, uint y, undefined4 treeType) + { + if (x > 399 || y > 399) { + return; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return; + } + + int stage = MACRO_CALL_MEMBER( + OpenSHC::Map::LandscapeState_Func::getValueFrom0UpTo3ForTreeTypeAndTreeStage, DAT_LandscapeState::ptr)( + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::mapUITreeTypeToLogicalTreeType, + DAT_LandscapeState::ptr)((short)treeType), + 3); + uint baseTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[stage]; + if ((this->LogicLayer[baseTile] + & (L_SEA | L_PLAIN1_AND_FARM | L_BORDER | L_BORDER_EDGE | L_ROCKY | L_WALL_OR_GATEHOUSE | L_BUILDING + | L_TREE_VARIATION | L_BOULDERS | L_PEBBLES | L_IRON | L_RIVER | L_FORD | L_KEEP_NON_MANOR_HOUSE + | L_MARSH | L_MOAT)) + != 0) { + return; + } + + uint tile = baseTile; + uint brushY = y; + int index = 0; + if (brushSize > 0) { + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, (int*)&tile, (int*)&brushY, baseTile, y); + if ((this->LogicLayer[tile] & L_BORDER) != 0) { + break; + } + if ((this->LogicLayer[tile] & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_RIVER | L_KEEP_NON_MANOR_HOUSE)) + != 0) { + return; + } + if ((this->LogicLayer[tile] & L_ROCKY) != 0) { + return; + } + if (this->OrganismLayer[tile] != 0) { + return; + } + index++; + } while (index < brushSize); + } + + int treeID = MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::createTree, DAT_LandscapeState::ptr)(x, y, + (TreeType)(short)MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::mapUITreeTypeToLogicalTreeType, + DAT_LandscapeState::ptr)((short)treeType), + stage, 0, 0, 3); + this->LogicLayer[DAT_LandscapeState::instance.trees[treeID].tile] + = this->LogicLayer[DAT_LandscapeState::instance.trees[treeID].tile] & ~L_ROCKY; + this->LogicLayer[DAT_LandscapeState::instance.trees[treeID].tile] + = this->LogicLayer[DAT_LandscapeState::instance.trees[treeID].tile] | L_TREE; + this->OrganismLayer[DAT_LandscapeState::instance.trees[treeID].tile] = (short)treeID; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::applyTreeBrushToLogicalLayer, this)(treeID, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)( + DAT_LandscapeState::instance.trees[treeID].yPosition, DAT_LandscapeState::instance.trees[treeID].tile); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeTunnelersguild.cpp b/src/OpenSHC/Map/TileMapState/placeTunnelersguild.cpp new file mode 100644 index 000000000..322e0500a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeTunnelersguild.cpp @@ -0,0 +1,99 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507E00 + void TileMapState::placeTunnelersguild( + int param_1, int* param_2, uint param_3, undefined4 param_4, uint param_5, int param_6, undefined4 param_7) + { + int* piVar1; + int iVar2; + int buildingID; + int iVar3; + uint y; + uint x; + int iVar4; + int iVar5; + int local_8; + piVar1 = param_2; + iVar5 = DAT_GameCore::instance.uniqueGameObjectTracker; + iVar4 = 0; + if (param_6 == 0xf) { + local_8 = 0; + } else { + local_8 = param_6 / 2; + } + iVar2 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, + DAT_BuildingsState::ptr)(param_1, (uint)((int)(param_2)), param_3, (undefined4)((int)(param_7)), + (BuildingType)((int)((int)(short)param_4)), param_5, param_1, 0xf); + buildingID = (int)(short)iVar2; + this->placedBuildingID = buildingID; + DAT_BuildingsState::instance.buildings[buildingID].uidWhenPlaced = iVar5; + param_2 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[0]; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar4, (int)((int)(param_5))); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + this->buildingX + (int)piVar1; + *param_2 = iVar3; + this->HeightLayer[iVar3] = (byte)param_7; + this->LogicLayer[iVar3] = this->LogicLayer[iVar3] | 1024; + this->BuildingLayer[iVar3] = (short)iVar2; + iVar4 = iVar4 + 1; + param_2 = param_2 + 1; + this->BuildingWasLayer[iVar3] = (uchar)param_4; + this->ChangedLayer[iVar3] = 2; + } while (iVar4 < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + x = (int)piVar1 + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_8][1].x; + y = param_3 + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[local_8][1].y; + iVar4 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, x, y, (undefined4)((int)(param_7)), OpenSHC::Map::Buildings::BT_PARADEGROUND5, 5, param_1, 0xf); + iVar2 = (int)(short)iVar4; + DAT_BuildingsState::instance.buildings[iVar2].uidWhenPlaced = iVar5; + DAT_BuildingsState::instance.buildings[iVar2].quarryStockpileID = (undefined2)this->placedBuildingID; + param_5 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + param_5, (int)((int)(DAT_BuildingsState::instance.buildings[iVar2].widthOrHeight))); + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar5); + this->HeightLayer[iVar5] = (byte)param_7; + this->BuildingLayer[iVar5] = (short)iVar4; + param_5 = param_5 + 1; + this->BuildingWasLayer[iVar5] = (uchar)param_4; + this->ChangedLayer[iVar5] = 2; + } while ((int)param_5 < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(iVar2); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(iVar2); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, x, y); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupTunnelersGuildCampgroundPositions, + DAT_BuildingsState::ptr)(param_1); + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeWalls.cpp b/src/OpenSHC/Map/TileMapState/placeWalls.cpp new file mode 100644 index 000000000..3d9d5b1ff --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeWalls.cpp @@ -0,0 +1,281 @@ + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/WallAndPitchState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_WallAndPitchState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::States::UnitState; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00502F30 + void TileMapState::placeWalls( + int playerID, uint x1, uint y1, uint x2, uint y2, MappersEnum wallType, int tileCountUnk) + { + int budget = 0; + int wallCount = 0; + int placed = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::validateWallBuildPath, this)(playerID, x1, y1, x2, y2, wallType); + if (this->illegalBuild != FALSE) { + return; + } + + this->constructionTileCount = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::getWallTilesThatCanBeBuilt, DAT_GameState::ptr)( + playerID, 4); + int overshoot = 2; + if ((short)wallType == OpenSHC::Commands::M_MAPPER_STAIR) { + budget = this->field119_0x554924; + } + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + MACRO_CALL_MEMBER(OpenSHC::Map::WallAndPitchState_Func::resetWallPlacementInfo, DAT_WallAndPitchState::ptr)(); + } + + int upY = y1 - y2; + int downY = y2 - y1; + int rightX = x2 - x1; + int* translation = &DAT_ViewportRenderState::instance.translationMatrix[y1].addXgetTile; + uint x = x1; + int leftX = x1 - x2; + bool stairAborted = false; + do { + if ((short)wallType == OpenSHC::Commands::M_MAPPER_STAIR && budget < 0x18) { + stairAborted = true; + break; + } + if (tileCountUnk <= placed || this->constructionTileCount < wallCount) { + break; + } + placed++; + int tile = *translation + x; + bool towerNeighbours = false; + if ((short)wallType == OpenSHC::Commands::M_MAPPER_STAIR + && (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0) { + stairAborted = true; + break; + } + /* a wall dragged over a crenel replaces it without counting against the tally */ + bool overCrenel = false; + if ((short)wallType == OpenSHC::Commands::M_MAPPER_WALL && (this->LogicLayer[tile] & L_CRENEL) != 0) { + overCrenel = true; + } + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0 || this->DamageLayer[tile] != 0 || overCrenel) { + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + MACRO_CALL_MEMBER(OpenSHC::Map::WallAndPitchState_Func::addWallPlacementInfoForTile, DAT_WallAndPitchState::ptr)(tile); + } + if (this->DamageLayer[tile] != 0 || overCrenel) { + this->DamageLayer[tile] = 0; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + if (overCrenel) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_CRENEL | L_CRENEL_VARIATIONUnk) + | L_WALL_OR_GATEHOUSE; + } + } + + bool applyWall = true; + bool updateLinkages = true; + if ((short)wallType == OpenSHC::Commands::M_MAPPER_WALL) { + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x5a; + } else if ((short)wallType == OpenSHC::Commands::M_MAPPER_WOODWALL) { + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x3c; + } else if ((short)wallType == OpenSHC::Commands::M_MAPPER_CRENAL) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_CRENEL; + BOOLEnum onlyTowers = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::hasOnlyTowerNeighborsNoWalls, this)(tile, y1); + if (onlyTowers == FALSE) { + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x62; + } else { + this->HeightLayer[tile] = this->HeightLayer[tile] + 0x44; + } + towerNeighbours = onlyTowers != FALSE; + /* a crenel on an odd tile of one axis carries the alternate variation */ + if (((x & 1) == 0) != ((y1 & 1) == 0)) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_CRENEL_VARIATIONUnk; + } + } else if ((short)wallType == OpenSHC::Commands::M_MAPPER_STAIR) { + int steps = (budget - (int)(uint)this->HeightLayer[tile]) + -0x10; + if (steps > 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_STAIRS; + this->HeightLayer[tile] = this->HeightLayer[tile] + (char)steps; + } else { + applyWall = false; + updateLinkages = overCrenel; + } + } + + if (applyWall && !overCrenel) { + wallCount++; + this->LogicLayer[tile] + = this->LogicLayer[tile] & ~(L_UNKNOWN_WALL_RELATED | L_BOULDERS | L_PEBBLES | L_IRON) + | L_WALL_OR_GATEHOUSE; + if (this->UnitLayer[tile] != 0 + && DAT_UnitsState::instance.units[(short)this->UnitLayer[tile]].unitType == OpenSHC::Map::Units::UT_CHICKEN) { + int unitID = (short)this->UnitLayer[tile]; + DAT_UnitsState::instance.units[unitID].state.generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + DAT_UnitsState::instance.units[unitID].updateTickTracker = 0; + DAT_UnitsState::instance.units[unitID].disappearFadeAlphaCountdown = 0; + } + if ((this->LogicLayer[tile] & L_PLAIN2_AND_PITCH) != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->HeightLayer[tile] = this->HeightLayer[tile] + 4; + } + if ((this->LogicLayer[tile] & L_TREE) != 0 && this->OrganismLayer[tile] < 2000) { + /* scrub is flattened by the wall, a real tree is left standing */ + switch (DAT_LandscapeState::instance.trees[this->OrganismLayer[tile]].treeType) { + case (TreeType)5: + case (TreeType)6: + case (TreeType)7: + case (TreeType)8: + case (TreeType)9: + case (TreeType)0xa: + case (TreeType)0xb: + case (TreeType)0xc: + case (TreeType)0xd: + case (TreeType)0xe: + case (TreeType)0x10: + case (TreeType)0x11: + case (TreeType)0x12: + case (TreeType)0x13: + DAT_LandscapeState::instance.trees[this->OrganismLayer[tile]].state = 3; + } + } + if ((short)wallType == OpenSHC::Commands::M_MAPPER_WOODWALL || towerNeighbours) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_UNKNOWN_WALL_RELATED; + } + this->WallOwnerLayer[tile] = this->WallOwnerLayer[tile] & 0xf8 | (char)playerID - 1U; + } + if (updateLinkages) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)( + y1, tile); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoat, this)(tile); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, DAT_PathFindingState::ptr)(7, x, y1); + } + } + + int stepX = leftX; + if ((int)x < (int)x2) { + stepX = rightX; + } + int stepY = upY; + if ((int)y1 < (int)y2) { + stepY = downY; + } + if ((short)wallType == OpenSHC::Commands::M_MAPPER_STAIR) { + if (stepY < stepX) { + if ((int)x < (int)x2) { + leftX = leftX + 1; + x = x + 1; + rightX = rightX - 1; + } else { + leftX = leftX - 1; + x = x - 1; + rightX = rightX + 1; + } + } else if (stepY < 1) { + overshoot = overshoot - 1; + } else if ((int)y1 < (int)y2) { + y1 = y1 + 1; + translation = translation + 3; + upY = upY + 1; + downY = downY - 1; + } else { + y1 = y1 - 1; + translation = translation - 3; + upY = upY - 1; + downY = downY + 1; + } + } else { + if (stepX != 0) { + int advance = 1; + if ((int)x < (int)x2) { + leftX = leftX + 1; + rightX = rightX - 1; + } else { + leftX = leftX - 1; + advance = -1; + rightX = rightX + 1; + } + x = x + advance; + } + if (stepY != 0) { + if ((int)y1 < (int)y2) { + y1 = y1 + 1; + translation = translation + 3; + upY = upY + 1; + downY = downY - 1; + } else { + y1 = y1 - 1; + translation = translation - 3; + upY = upY - 1; + downY = downY + 1; + } + } + if (x == x2 && y1 == y2) { + overshoot = overshoot - 1; + } + } + if (budget > 0xf) { + budget = budget - 0x10; + } + } while (x != x2 || y1 != y2 || overshoot != 0); + + int woodCount; + int stoneCount; + if (!stairAborted && (short)wallType == OpenSHC::Commands::M_MAPPER_WOODWALL) { + stoneCount = 0; + woodCount = wallCount; + } else { + woodCount = 0; + stoneCount = wallCount; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processWallBuildingLoss, DAT_BuildingsState::ptr)( + playerID, stoneCount, woodCount, 0); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + this->field204_0x554a30 = 1; + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeWheatfarm.cpp b/src/OpenSHC/Map/TileMapState/placeWheatfarm.cpp new file mode 100644 index 000000000..8f385cf9a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeWheatfarm.cpp @@ -0,0 +1,120 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::Logic1; + using OpenSHC::Map::LogicHelpers::Logic2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00514F30 + void TileMapState::placeWheatfarm(int playerID, uint x, uint y, undefined4 buildingType, undefined4 param_5, + int variation, undefined4 averageHeight) + { + uint uVar1; + int buildingID; + XYPair* pXVar2; + int iVar3; + int* piVar4; + int iVar5; + uVar1 = x; + iVar5 = 0; + buildingID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, + DAT_BuildingsState::ptr)(playerID, x, y, (undefined4)((int)(averageHeight)), + (BuildingType)((int)((int)(short)buildingType)), 3, playerID, variation); + this->placedBuildingID = buildingID; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::markBuildingFootprintFlag, this)(x, (int)((int)(y)), 9); + x = 6; + pXVar2 = DAT_TerrainDefinedData::instance + .WheatFarmTiles[DAT_GameState::instance.playerDataArray[playerID].unkWheatFarmOrientation]; + piVar4 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[1]; + do { + piVar4[-1] = DAT_ViewportRenderState::instance.translationMatrix[pXVar2->y + y].addXgetTile + uVar1 + + ((XYPair*)&pXVar2->x)->x; + *piVar4 = DAT_ViewportRenderState::instance.translationMatrix[pXVar2[1].y + y].addXgetTile + pXVar2[1].x + + uVar1; + piVar4[1] = DAT_ViewportRenderState::instance.translationMatrix[pXVar2[2].y + y].addXgetTile + pXVar2[2].x + + uVar1; + piVar4[2] = DAT_ViewportRenderState::instance.translationMatrix[pXVar2[3].y + y].addXgetTile + pXVar2[3].x + + uVar1; + piVar4[3] = DAT_ViewportRenderState::instance.translationMatrix[pXVar2[4].y + y].addXgetTile + pXVar2[4].x + + uVar1; + x = x - 1; + piVar4[4] = DAT_ViewportRenderState::instance.translationMatrix[pXVar2[5].y + y].addXgetTile + pXVar2[5].x + + uVar1; + pXVar2 = pXVar2 + 6; + piVar4 = piVar4 + 6; + } while (x != 0); + iVar3 = DAT_GameState::instance.playerDataArray[playerID].unkWheatFarmOrientation + 1; + DAT_GameState::instance.playerDataArray[playerID].unkWheatFarmOrientation = iVar3; + if (1 < iVar3) { + DAT_GameState::instance.playerDataArray[playerID].unkWheatFarmOrientation = 0; + } + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(iVar5, 3); + /* + fixme: repurposed + */ + variation._0_2_ = (short)buildingID; + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + uVar1 + + this->buildingX; + this->HeightLayer[iVar3] = (byte)averageHeight; + this->LogicLayer[iVar3] = this->LogicLayer[iVar3] | 1024; + this->BuildingLayer[iVar3] = (short)variation; + this->BuildingWasLayer[iVar3] = (uchar)buildingType; + this->ChangedLayer[iVar3] = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setTerrain, this)(playerID, iVar3, this->buildingY + y, + 2, OpenSHC::Map::LogicHelpers::L_NONE, OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB); + iVar5 = iVar5 + 1; + } while (iVar5 < this->constructionTileCount); + piVar4 = &DAT_BuildingsState::instance.buildings[buildingID].tileRefs[0]; + iVar5 = 0x24; + do { + iVar3 = *piVar4; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetTileAndClearMoat, this)(iVar3); + this->LogicLayer[iVar3] = this->LogicLayer[iVar3] | 16777216; + this->BuildingLayer[iVar3] = (short)variation; + piVar4 = piVar4 + 1; + iVar5 = iVar5 + -1; + this->BuildingWasLayer[iVar3] = (uchar)buildingType; + this->ChangedLayer[iVar3] = 2; + } while (iVar5 != 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + if (((playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) + && (DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount)) + && ((DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || (DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)))) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)(1, 0x4d, 1, (TextMessageBLLookupStructUnion)0x0, 100, 6000); + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/placeWorkshopOrHovel.cpp b/src/OpenSHC/Map/TileMapState/placeWorkshopOrHovel.cpp new file mode 100644 index 000000000..796a39daa --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/placeWorkshopOrHovel.cpp @@ -0,0 +1,74 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/BottomLeftTextDisplayState.func.hpp" + +#include "OpenSHC/Globals/DAT_BottomLeftTextDisplayState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00506BD0 + void TileMapState::placeWorkshopOrHovel( + int playerID, uint x, uint y, BuildingType type, uint size, int orientation, undefined4 averageHeight) + { + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + playerID, x, y, averageHeight, (BuildingType)(short)type, size, playerID, orientation); + /* the original stores only the low half of the int field */ + *(short*)&DAT_BuildingsState::instance.buildings[buildingID].hovelVisualStyle + = (short)DAT_GameState::instance.playerDataArray[playerID].hovelCountUpToEight; + this->placedBuildingID = buildingID; + if (type == OpenSHC::Map::Buildings::BT_HOVEL) { + DAT_GameState::instance.playerDataArray[playerID].hovelCountUpToEight + = (DAT_GameState::instance.playerDataArray[playerID].hovelCountUpToEight + 1) % 7; + } + int tileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(tileIndex, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + if (tileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[tileIndex] = tile; + } + this->HeightLayer[tile] = (byte)averageHeight; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = (short)buildingID; + tileIndex++; + this->BuildingWasLayer[tile] = (uchar)type; + this->ChangedLayer[tile] = 2; + } while (tileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + if (playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.playerDataArray[playerID].availablePeasantsAtFire + < (int)DAT_BuildingsState::instance.buildings[buildingID].buildingTypeBasedEmployeeCount + && (DAT_GameState::instance.playerDataArray[playerID].populationCap + <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation + || DAT_GameState::instance.playerDataArray[playerID].popularity < 5000)) { + MACRO_CALL_MEMBER(OpenSHC::UI::BottomLeftTextDisplayState_Func::setBottomLeftTextDisplayText, + DAT_BottomLeftTextDisplayState::ptr)( + 1, 0x4d, 1, OpenSHC::UI::TextMessageBLLookupStructUnion(), 100, 6000); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/planMoat.cpp b/src/OpenSHC/Map/TileMapState/planMoat.cpp new file mode 100644 index 000000000..be0e376d8 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/planMoat.cpp @@ -0,0 +1,67 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00514480 + void TileMapState::planMoat(undefined4 playerID, int tile, uint tileY) + { + uint logic = this->LogicLayer[tile]; + if ((logic & (L_BORDER | L_BORDER_EDGE)) != 0) { + return; + } + if (this->OrganismLayer[tile] != 0) { + return; + } + if ((logic & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0) { + return; + } + if (this->BuildingLayer[tile] != 0) { + return; + } + if ((logic & (L_WALL_OR_GATEHOUSE | L_BOULDERS | L_IRON | L_RIVER | L_FORD | L_MARSH)) != 0) { + return; + } + if ((logic & (L_SEA | L_PLAIN1_AND_FARM | L_ROCKY)) != 0) { + return; + } + + int moatX = tile - DAT_ViewportRenderState::instance.translationMatrix[tileY].addXgetTile; + if ((logic & (L_MOAT_DUG_OR_PLANNED | L_MOAT)) == 0 && this->HeightLayer[tile] <= 0xc) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::createMoatData, this)(playerID, moatX, tileY, 0) != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_MOAT_DUG_OR_PLANNED; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(tileY, tile); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/prepareAreaForBuildingPlacement.cpp b/src/OpenSHC/Map/TileMapState/prepareAreaForBuildingPlacement.cpp new file mode 100644 index 000000000..24f2cd275 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/prepareAreaForBuildingPlacement.cpp @@ -0,0 +1,107 @@ + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_APPLE; + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_APPLE; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005160C0 + BOOLEnum TileMapState::prepareAreaForBuildingPlacement( + int playerID, uint x, uint y, MappersEnum commandBuildingType, int buildingWidthOrHeight) + { + BOOLEnum result = FALSE; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::storeMinAndMaxHeightOfArea, this)(x, y, buildingWidthOrHeight); + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, buildingWidthOrHeight); + uint tileY = y + this->buildingY; + if (x + this->buildingX > 399 || tileY > 399) { + return FALSE; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[tileY * 400 + x + this->buildingX] == 0) { + return FALSE; + } + int tile = DAT_ViewportRenderState::instance.translationMatrix[tileY].addXgetTile + this->buildingX + x; + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + return FALSE; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + tile, playerID, (MappersEnum)(short)commandBuildingType, 1) + == 1) { + return FALSE; + } + index++; + } while (index < this->constructionTileCount); + + /* the original walks the footprint again through the command parameter */ + commandBuildingType = OpenSHC::Commands::M_MAPPER_NULL; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + commandBuildingType, buildingWidthOrHeight); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + x + this->buildingX; + int buildingID = DAT_TileMapState::instance.BuildingLayer[tile]; + bool cleared = false; + if (buildingID != 0) { + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_WOODCUTTERSHUT || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_OXTETHER || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_QUARRY || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_IRONMINE || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_WHEATFARM || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_HOPFARM || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_APPLEFARM || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_DAIRYFARM) { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(buildingID, playerID, 50); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFromTerrain, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::updatePrimaryBuildingPlayerDataReferences, + DAT_GameState::ptr)(buildingID); + cleared = true; + } + } else if ((DAT_TileMapState::instance.LogicLayer[tile] & L_FARM_FIELD_APPLE) != 0) { + buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::getFarmThatHasTile, + DAT_BuildingsState::ptr)(tile); + if (buildingID != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(buildingID, playerID, 0x32); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearBuildingFromTerrain, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::updatePrimaryBuildingPlayerDataReferences, + DAT_GameState::ptr)(buildingID); + cleared = true; + } + } + if (cleared) { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 0x32c, '\0', DAT_BuildingsState::instance.buildings + buildingID); + result = TRUE; + } + commandBuildingType = (MappersEnum)(commandBuildingType + OpenSHC::Commands::M_MAPPER_AREA); + } while ((int)commandBuildingType < this->constructionTileCount); + return result; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/prepareMap.cpp b/src/OpenSHC/Map/TileMapState/prepareMap.cpp new file mode 100644 index 000000000..3798b1392 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/prepareMap.cpp @@ -0,0 +1,184 @@ + +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/MapPropertiesState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/TroopValueState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Map/WildlifeState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Text/UserTextHandler.func.hpp" +#include "OpenSHC/UI/HoveredState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_HoveredState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_MapPropertiesState.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_WallAndPitchState.hpp" +#include "OpenSHC/Globals/DAT_WildlifeState.hpp" +#include "OpenSHC/Globals/FilePackagerObj.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumShort": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00512450 + void TileMapState::prepareMap() + { + int savedY = DAT_ViewportRenderState::instance.viewportState.viewportY; + int savedX = DAT_ViewportRenderState::instance.viewportState.viewportX; + if (DAT_GameSynchronyState::instance.currentGameModeCopy_SEC_Section1106 == OpenSHC::Game::GM_SKIRMISH_SINGLE_PLAYER) { + DAT_GameSynchronyState::instance.currentGameMode = OpenSHC::Game::GM_SKIRMISH_SINGLE_PLAYER; + DAT_GameSynchronyState::instance.isHost = TRUE; + char* name = MACRO_CALL_MEMBER( + OpenSHC::Text::UserTextHandler_Func::getTextArrayPointer, DAT_UserTextHandlerState::ptr)(0); + /* the human player's name goes into slot 1, terminator included */ + int i = 0; + do { + DAT_GameSynchronyState::instance.DAT_PlayerNames[1][i] = name[i]; + i++; + } while (name[i - 1] != '\0'); + MACRO_CALL_MEMBER(OpenSHC::AI::AIVState_Func::syncAIPlayerNamesAndBuildIntervals, DAT_AIVState::ptr)(); + DAT_GameSynchronyState::instance.currentPlayerSlotID = 1; + } else if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_CAMPAIGN_MISSION) { + if (DAT_GameCore::instance.missionNumber1to20 < 16) { + DAT_GameSynchronyState::instance.currentGameMode = OpenSHC::Game::GM_SOLITARY; + } + } else if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_BUILDERUnk) { + DAT_GameSynchronyState::instance.currentGameMode = OpenSHC::Game::GM_SOLITARY; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::changeMapOrientation, this)(this->mapOrientation); + DAT_PathFindingState::instance.searchGeneration = 1; + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', DAT_TileMapState::instance.WalkLayer); + DAT_ViewportRenderState::instance.viewportState.viewportX = savedX; + DAT_ViewportRenderState::instance.viewportState.viewportY = savedY; + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::setViewportBasedOnMapSize, DAT_ViewportRenderState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::setTileSystemMemoryLookupArrays, DAT_ViewportRenderState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::tweakValidTilesToExcludeMapBorders, DAT_ViewportRenderState::ptr)(); + DAT_BuildingsState::instance.maxBuildingsCount = 2000; + DAT_UnitsState::instance.maxUnitCount = 2500; + DAT_PathFindingState::instance.maxClimbDataCount = 200; + DAT_EntityState::instance.maxEntityCount = 3000; + this->currentMoatCount = 16000; + DAT_LandscapeState::instance.maxTreeCount = 2000; + this->maxPitchDitchCount = 4000; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetHeightAndMapBorders, this)(this->mapSize); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::toggleFlatView, this)(0); + DAT_TileMapState::instance.field95_0x5548d0 = 0; + DAT_TileMapState::instance.flatViewToggleValue2 = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setChangedLayerToThreeAndMapping0x40x40, this)(); + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + this->forceUpdateTextureTilemap = 1; + this->forceUpdateGFXLayers = 1; + this->forceUpdateMacroLayerFlag = 1; + this->field68_0x55487c = 500; + + /* every subsystem gets a chance to migrate what it loaded from an older map format */ + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::upgradeTribeArrayLayoutForMapVersion, + DAT_TribesState::ptr)((PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::uselessFunction, DAT_TroopValueState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::upgradeMapFormatForUnits, DAT_UnitsState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::upgradeMapFormatLogicLayer, this)((PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::upgradeBuildingsForMapVersion, DAT_BuildingsState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::upgradeTreesAndRocksForMapVersion, DAT_LandscapeState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::upgradeMapTribesState, DAT_TribesState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::migrateGameStateForMapVersion, DAT_GameState::ptr)((PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::handleMapVersionUpgrade, DAT_EntityState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::recountTotalOwnedMoats, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageLayerForEachBuildingAtEachTile, DAT_PathFindingState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateSeparateAreaTileMap, DAT_PathFindingState::ptr)(1); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::updatePathLinkageLayerForAllBuildings, DAT_BuildingsState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateClimbData, DAT_PathFindingState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::clearBuildingValueWhenStateIsTwo, DAT_BuildingsState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::setCurrentTimeOnSomeTrees, DAT_LandscapeState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::resetWind, DAT_LandscapeState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::collectCliffEdgeTilesForClimbData, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearInvalidMoatEntries, this)(); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::initializeGameStateAfterMapLoad, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::MapPropertiesState_Func::sortEventsByDate, DAT_MapPropertiesState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::applyVersionUpgradeAccessibilityRecompute, DAT_BuildingsState::ptr)( + (PackagedFileMagicNum)(int)FilePackagerObj::instance.versionNumOfCurrentFileTypeUnk, + (PackagedFileMagicNum)(int)FilePackagerObj::instance.packagerMapVersionNumUnk); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateSeparateAreaTileMap, DAT_PathFindingState::ptr)(1); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::updatePathLinkageLayerForAllBuildings, DAT_BuildingsState::ptr)(); + + for (int grid = 0; grid < 40; grid++) { + MACRO_CALL_MEMBER(OpenSHC::Map::WildlifeState_Func::updateWildlifeGrid, DAT_WildlifeState::ptr)(grid); + } + MACRO_CALL_MEMBER(OpenSHC::Map::WildlifeState_Func::updateWildlife, DAT_WildlifeState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::WildlifeState_Func::updateSection1034Info, DAT_WildlifeState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::WildlifeState_Func::updateNofFpoints, DAT_WildlifeState::ptr)(); + MACRO_CALL(OpenSHC::Map_Func::ResetSomeValuesFunctionUnk)(); + DAT_WallAndPitchState::instance.countdown = 0; + MACRO_CALL_MEMBER(OpenSHC::UI::HoveredState_Func::clearHoveredState, DAT_HoveredState::ptr)(); + DAT_GameCore::instance.gamePausedLogical = 0xffffffff; + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::processGameTick, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateSeparateAreaTileMap, DAT_PathFindingState::ptr)(1); + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::setTileColorsDependingOnMapSize, DAT_MinimapViewState::ptr)(0, 100); + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::setMapPropertyDependingOnMapSize, DAT_MinimapViewState::ptr)(0, 100); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/processDamageToBuilding.cpp b/src/OpenSHC/Map/TileMapState/processDamageToBuilding.cpp new file mode 100644 index 000000000..800a0cfb8 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/processDamageToBuilding.cpp @@ -0,0 +1,474 @@ + +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Audio/MSS/SoundSystem.func.hpp" +#include "OpenSHC/Audio/SFX/SFXState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Entities.func.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Map/Version.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/DE/SHCDE/eSFX.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Entities/EntityType.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_SFXState.hpp" +#include "OpenSHC/Globals/DAT_SoundSystemState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::DE::SHCDE::eSFX; + using OpenSHC::Game::GameMode; + using OpenSHC::Map::Buildings::BuildingLogicalState; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Entities::EntityType; + using OpenSHC::Map::Units::States::UnitState; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + beware the renames! this is for both stones as well as units hitting walls decompilerscript: committed: + 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00516B80 + BOOLEnum TileMapState::processDamageToBuilding(int tile, uint xPosition, uint yPosition, int damageUnk, + int param_5, int playerID, BOOLEnum aiBuildDelayRelated, int unitID) + { + BOOLEnum destroyed = FALSE; + byte replaced = 0; + /* a negative player id is a request to spare that player's own team */ + bool spareOwnTeam = false; + if (playerID < 0) { + playerID = -playerID; + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] == -1) { + spareOwnTeam = true; + } + } + + uint logic = this->LogicLayer[tile]; + if ((logic & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 && this->BuildingLayer[tile] == 0) { + if ((logic & L_WALL_OR_GATEHOUSE) == 0) { + destroyed = TRUE; + } else { + if ((logic & L_STOCKPILEUnk) != 0) { + return FALSE; + } + uint wallOwner = this->WallOwnerLayer[tile] & 7; + if (spareOwnTeam && DAT_GameState::instance.mapAndTime.playerTeams[playerID] == DAT_GameState::instance.mapAndTime.playerTeams[wallOwner + 1]) { + return FALSE; + } + DAT_GameState::instance.playerDataArray[wallOwner + 1].defensesDamagedByPlayer = (short)playerID; + if (unitID != 0) { + if (DAT_GameState::instance.mapAndTime.playerTeams[wallOwner + 1] == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + = DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + 1; + } else { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk = 0; + } + } + /* a wall standing alone takes half again the damage, a well connected one half */ + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::countLogicPropertyInSurroundingTiles, this)( + tile, yPosition, L_KEEP_NON_MANOR_HOUSE | L_WALL_OR_GATEHOUSE); + if (this->DAT_CardinalTilesAroundTile < 2) { + damageUnk = damageUnk + damageUnk / 2; + } else if (this->DAT_CardinalTilesAroundTile > 2) { + damageUnk = damageUnk / 2; + } + if (param_5 != 0) { + damageUnk = 0; + } + DAT_TroopValueState::instance.attackInfo.field128059_0x469e0 + = DAT_TroopValueState::instance.attackInfo.field128059_0x469e0 + 1; + DAT_GameCore::instance.cowPoisonTrackerUnk + = DAT_GameCore::instance.cowPoisonTrackerUnk + damageUnk * 10; + if (param_5 == 0 && (this->LogicLayer[tile] & L_UNKNOWN_WALL_RELATED) == 0 + && this->DamageLayer[tile] == 0 + && this->DefaultHeightLayer[tile] + 60 < (uint)this->HeightLayer[tile]) { + this->HeightLayer[tile] = this->HeightLayer[tile] - 20; + } + for (int hit = 0; hit < damageUnk; hit++) { + if (this->HeightLayer[tile] <= this->DefaultHeightLayer[tile]) { + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, + DAT_EntityState::ptr)(tile); + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_STAIRS | L_UNKNOWN_WALL_RELATED + | L_CRENEL_VARIATIONUnk); + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + this->DamageLayer[tile] = 0; + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + this->field204_0x554a30 = 1; + destroyed = TRUE; + break; + } + this->HeightLayer[tile] = this->HeightLayer[tile] - 1; + if ((char)this->DamageLayer[tile] < 200) { + this->DamageLayer[tile] = this->DamageLayer[tile] + 1; + } + } + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(yPosition, tile); + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(7, xPosition, yPosition); + return ~-(uint)replaced & destroyed; + } + + int buildingID = this->BuildingLayer[tile]; + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == (BuildingLogicalState)0 || DAT_BuildingsState::instance.buildings[buildingID].logicalState == OpenSHC::Map::Buildings::BLS_REMOVE) { + return TRUE; + } + short health = DAT_BuildingsState::instance.buildings[buildingID].currentHealth; + if (health == 0) { + return TRUE; + } + DAT_GameCore::instance.cowPoisonTrackerUnk = DAT_GameCore::instance.cowPoisonTrackerUnk + damageUnk * 10; + if (DAT_BuildingDefinedData::instance.BuildingTypeHasHealth[(short)DAT_BuildingsState::instance.buildings[buildingID].buildingType] == 0) { + return TRUE; + } + if ((this->LogicLayer[tile] & 0xf000000) != 0) { + return TRUE; + } + if (spareOwnTeam && DAT_GameState::instance.mapAndTime.playerTeams[playerID] == DAT_GameState::instance.mapAndTime.playerTeams[DAT_BuildingsState::instance.buildings[buildingID].owner]) { + return FALSE; + } + DAT_BuildingsState::instance.buildings[buildingID].currentHealth = health - (short)damageUnk; + + if (aiBuildDelayRelated == TRUE + && (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_GATEHOUSESMALL)) { + /* a damaged gatehouse loses its drawbridges straight away */ + int bridge = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findParticularBuilding, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight, OpenSHC::Map::Buildings::BT_DRAWBRIDGE, 0); + if (bridge != 0) { + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[bridge].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(bridge); + int second = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findParticularBuilding, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight, OpenSHC::Map::Buildings::BT_DRAWBRIDGE, bridge); + if (second != 0) { + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[second].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(second); + } + } + } + + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + DAT_GameState::instance.playerDataArray[DAT_BuildingsState::instance.buildings[buildingID].owner].defensesDamagedByPlayer = (short)playerID; + break; + default: + break; + } + if (unitID != 0) { + if (DAT_GameState::instance.mapAndTime.playerTeams[DAT_BuildingsState::instance.buildings[buildingID].owner] == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + = DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + 1; + } else { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk = 0; + } + } + + short remaining = DAT_BuildingsState::instance.buildings[buildingID].currentHealth; + if (remaining > 0) { + /* a siege tower's rider takes the damage with it */ + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED && DAT_BuildingsState::instance.buildings[buildingID].unitRefID != 0 + && DAT_BuildingsState::instance.buildings[buildingID].unitRefUID == DAT_UnitsState::instance.units[DAT_BuildingsState::instance.buildings[buildingID].unitRefID].uid && DAT_BuildingsState::instance.buildings[buildingID].maxHealth != 0) { + DAT_UnitsState::instance.units[DAT_BuildingsState::instance.buildings[buildingID].unitRefID].health + = ((int)remaining * DAT_UnitsState::instance.units[DAT_BuildingsState::instance.buildings[buildingID].unitRefID].maxHealth) / (int)DAT_BuildingsState::instance.buildings[buildingID].maxHealth; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(7, xPosition, yPosition); + return ~-(uint)replaced & destroyed; + } + + if (DAT_BuildingsState::instance.buildings[buildingID].owner == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpdateDestroyedBuildingCountData)(1); + } + int owner = DAT_BuildingsState::instance.buildings[buildingID].owner; + if (aiBuildDelayRelated == TRUE && DAT_GameState::instance.playerDataArray[owner].resourceRebuildDelay == 0) { + DAT_GameState::instance.playerDataArray[owner].resourceRebuildDelay = 1; + } + int destroyedType = (short)DAT_BuildingsState::instance.buildings[buildingID].buildingType; + if (destroyedType != OpenSHC::Map::Buildings::BT_UNKNOWN3 && (destroyedType < 0x56 || destroyedType > 0x59)) { + DAT_GameSynchronyState::instance.finalResults.finalBuildingsBurned[playerID] + = DAT_GameSynchronyState::instance.finalResults.finalBuildingsBurned[playerID] + 1; + DAT_GameSynchronyState::instance.finalResults.finalBuildingsDestroyedWeighted[playerID] + = DAT_GameSynchronyState::instance.finalResults.finalBuildingsDestroyedWeighted[playerID] + + DAT_BuildingDefinedData::instance.field42_0x4154[destroyedType]; + DAT_GameSynchronyState::instance.finalResults.finalBuidingsDestroyed[owner] + = DAT_GameSynchronyState::instance.finalResults.finalBuidingsDestroyed[owner] + 1; + /* a per-attacker tally inside the owner's player data, 0x20 apart */ + int* tally = (int*)((char*)&DAT_GameState::instance.playerDataArray[owner].field889_0x2be0 + playerID * 0x20); + *tally = *tally + 1; + } + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_OILSMELTER: + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playAnger2BikFromPlayerToPlayer, DAT_AICState::ptr)( + owner, playerID); + break; + default: + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playVictory2BikFromPlayerToPlayer, DAT_AICState::ptr)( + owner, playerID); + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY + && playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.mapAndTime.playerTeams[owner] != DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + && (DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_TOWER4 || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_TOWER5 + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_CHURCH + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == OpenSHC::Map::Buildings::BT_CATHEDRAL) + && MACRO_CALL_MEMBER(OpenSHC::Audio::MSS::SoundSystem_Func::shouldSoundXNotBePlaying, + DAT_SoundSystemState::ptr)() + == FALSE) { + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber1, SEC_RNG::ptr)(); + if (DAT_GameCore::instance.genieVoiceActive != FALSE) { + /* "Watch it crumble" */ + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)("Genie_36.wav"); + } + } + + bool destroyNormally = false; + int towerX = 0; + int towerY = 0; + int towerOwner = 0; + int towerSize = 0; + MappersEnum towerCommand = OpenSHC::Commands::M_MAPPER_NULL; + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_OILSMELTER: + if (DAT_BuildingsState::instance.buildings[buildingID].resources[7] == 0) { + destroyNormally = true; + break; + } + { + /* a smelter with oil in it goes up, and a full one throws burning oil about */ + int oilAmount = DAT_BuildingsState::instance.buildings[buildingID].resources[7]; + int smelterX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + int smelterOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + uint smelterTile = DAT_BuildingsState::instance.buildings[buildingID].currentTilePositionAdjusted; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + int smelterY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + if (oilAmount < 5) { + MACRO_CALL(OpenSHC::Map::Entities_Func::SetPlaceOnFire)(smelterOwner, smelterX * 8 + 0x10, + smelterY * 8 + 0x10, this->HeightLayer[smelterTile], 3); + } else { + MACRO_CALL(OpenSHC::Map::Entities_Func::SetPlaceOnFire)(smelterOwner, smelterX * 8 + 0x10, + smelterY * 8 + 0x10, this->HeightLayer[smelterTile], 5); + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::spawnProjectileEntity, + DAT_EntityState::ptr)(0, smelterOwner, 0, smelterX * 8 + 0x10, smelterY * 8 + 0x10, + this->HeightLayer[smelterTile], 0, 0, 0, + OpenSHC::Map::Entities::EntityTypeInt__ET_EXPLOSION, 0); + } + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + smelterX, smelterY, OpenSHC::DE::SHCDE::FX_IGNITE_PITCH); + destroyed = TRUE; + } + break; + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + { + int bridge = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findParticularBuilding, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight, OpenSHC::Map::Buildings::BT_DRAWBRIDGE, 0); + if (bridge != 0) { + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[bridge].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(bridge); + int second = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findParticularBuilding, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight, OpenSHC::Map::Buildings::BT_DRAWBRIDGE, bridge); + if (second != 0) { + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[second].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(second); + } + } + ushort gateY = DAT_BuildingsState::instance.buildings[buildingID].y; + ushort gateX = DAT_BuildingsState::instance.buildings[buildingID].x; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)(buildingID, 50); + this->showNoRubbleWhenDestroyingBuilding = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[buildingID].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + (short)gateX, (short)gateY, OpenSHC::DE::SHCDE::FX_TOWER_SMASH); + destroyed = TRUE; + } + break; + case OpenSHC::Map::Buildings::BT_TUNNEL: + { + /* the tunneller inside is buried where he stands */ + uint tunneller = DAT_BuildingsState::instance.buildings[buildingID].unitRefID; + if (DAT_UnitsState::instance.units[tunneller].state.generic + != (OpenSHC::Map::Units::States::US_STAND_UPUnk + | OpenSHC::Map::Units::States::US_IDLEUnk)) { + DAT_UnitsState::instance.units[tunneller].totalSizeOfPathPlan = DAT_UnitsState::instance.units[tunneller].currentIndexInPathPlan; + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::applyTunnelDamageAlongPathPlan, + DAT_UnitsState::ptr)(tunneller); + } + DAT_UnitsState::instance.units[tunneller].state.generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + DAT_UnitsState::instance.units[tunneller].disappearFadeAlphaCountdown = 0x20; + DAT_UnitsState::instance.units[tunneller].updateTickTracker = 0x20; + } + destroyNormally = true; + break; + default: + destroyNormally = true; + break; + case OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED: + { + int rider = DAT_BuildingsState::instance.buildings[buildingID].unitRefID; + if (rider != 0 && DAT_BuildingsState::instance.buildings[buildingID].unitRefUID == DAT_UnitsState::instance.units[rider].uid) { + DAT_UnitsState::instance.units[rider].state.generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + DAT_UnitsState::instance.units[rider].disappearFadeAlphaCountdown = 0; + DAT_UnitsState::instance.units[rider].updateTickTracker = 0; + DAT_UnitsState::instance.units[rider].workplaceBuildingID_1 = 0; + } + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + destroyed = TRUE; + } + break; + case OpenSHC::Map::Buildings::BT_TOWER1: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x19); + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + towerX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + towerSize = 3; + towerCommand = OpenSHC::Commands::M_MAPPER_TOWER1_DESTROYED; + replaced = 1; + break; + case OpenSHC::Map::Buildings::BT_TOWER2: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + towerX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + towerSize = 4; + towerCommand = OpenSHC::Commands::M_MAPPER_TOWER2_DESTROYED; + replaced = 1; + break; + case OpenSHC::Map::Buildings::BT_TOWER3: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + towerX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + towerSize = 5; + towerCommand = OpenSHC::Commands::M_MAPPER_TOWER3_DESTROYED; + replaced = 1; + break; + case OpenSHC::Map::Buildings::BT_TOWER4: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + towerX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + towerSize = 6; + towerCommand = OpenSHC::Commands::M_MAPPER_TOWER4_DESTROYED; + replaced = 1; + break; + case OpenSHC::Map::Buildings::BT_TOWER5: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + towerX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + towerSize = 6; + towerCommand = OpenSHC::Commands::M_MAPPER_TOWER5_DESTROYED; + replaced = 1; + break; + } + + if (replaced != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, this)( + towerOwner, towerX, towerY, towerCommand, towerSize, 0xf); + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + towerX, towerY, OpenSHC::DE::SHCDE::FX_TOWER_SMASH); + destroyed = TRUE; + } else if (destroyNormally) { + short rubbleOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + this->showNoRubbleWhenDestroyingBuilding = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[destroyedType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, OpenSHC::DE::SHCDE::FX_BUILDING_SMASH); + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY + && playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.mapAndTime.playerTeams[rubbleOwner] != DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + && DAT_GameCore::instance.genieVoiceActive != FALSE) { + if (destroyedType == 0x13 || destroyedType == 0xb) { + /* "Excellent" */ + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)("Genie_23.wav"); + } + if (destroyedType == 8 || destroyedType == 9) { + /* "Bravo" */ + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)("Genie_24.wav"); + } + } + destroyed = TRUE; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(7, xPosition, yPosition); + return ~-(uint)replaced & destroyed; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/processDamageToBuildingThunk.cpp b/src/OpenSHC/Map/TileMapState/processDamageToBuildingThunk.cpp new file mode 100644 index 000000000..22790ea80 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/processDamageToBuildingThunk.cpp @@ -0,0 +1,24 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00409300 + BOOLEnum TileMapState::processDamageToBuildingThunk(int targetedTile, uint targetedTileX, uint targetedTileY, + int damage, int param_5, int playerID, BOOLEnum aiBuildDelayRelated) + { + BOOLEnum BVar1; + BVar1 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::processDamageToBuilding, this)( + targetedTile, targetedTileX, targetedTileY, damage, param_5, playerID, aiBuildDelayRelated, 0); + return BVar1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/processEntityDamageToBuildingCollateral.cpp b/src/OpenSHC/Map/TileMapState/processEntityDamageToBuildingCollateral.cpp new file mode 100644 index 000000000..42143523d --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/processEntityDamageToBuildingCollateral.cpp @@ -0,0 +1,381 @@ + +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Audio/SFX/SFXState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Entities.func.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/DE/SHCDE/eSFX.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Entities/EntityType.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_SFXState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::DE::SHCDE::eSFX; + using OpenSHC::Game::GameMode; + using OpenSHC::Map::Buildings::BuildingLogicalState; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Entities::EntityType; + using OpenSHC::Map::Units::States::UnitState; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00517790 + void TileMapState::processEntityDamageToBuildingCollateral( + int tile, uint x, uint y, int damage, int playerID, undefined4 unused, int unitID) + { + uint baseY = y; + int baseTile = tile; + /* a negative player id is a request to spare that player's own team */ + bool spareOwnTeam = false; + if (playerID < 0) { + playerID = -playerID; + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] == -1) { + spareOwnTeam = true; + } + } + + for (int index = 0; index < 9; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 0, index, &tile, (int*)&y, baseTile, baseY); + uint logic = this->LogicLayer[tile]; + if ((logic & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 && this->BuildingLayer[tile] == 0) { + /* bare wall: grind its height down towards the ground it stands on */ + if ((logic & L_WALL_OR_GATEHOUSE) == 0 || (logic & L_STOCKPILEUnk) != 0) { + continue; + } + uint wallOwner = this->WallOwnerLayer[tile] & 7; + if (spareOwnTeam && DAT_GameState::instance.mapAndTime.playerTeams[playerID] == DAT_GameState::instance.mapAndTime.playerTeams[wallOwner + 1]) { + continue; + } + DAT_GameState::instance.playerDataArray[wallOwner + 1].defensesDamagedByPlayer = (short)playerID; + if (unitID != 0) { + if (DAT_GameState::instance.mapAndTime.playerTeams[wallOwner + 1] == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + = DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + 1; + } else { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk = 0; + } + } + DAT_GameCore::instance.cowPoisonTrackerUnk + = DAT_GameCore::instance.cowPoisonTrackerUnk + damage * 10; + for (int hit = 0; hit < damage; hit++) { + if (this->HeightLayer[tile] <= this->DefaultHeightLayer[tile]) { + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, + DAT_EntityState::ptr)(tile); + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_CRENEL_VARIATIONUnk | L_UNKNOWN_WALL_RELATED | L_STAIRS | L_CRENEL + | L_PLAIN2_AND_PITCH); + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + this->DamageLayer[tile] = 0; + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + this->field204_0x554a30 = 1; + break; + } + this->HeightLayer[tile] = this->HeightLayer[tile] - 1; + if ((char)this->DamageLayer[tile] < 200) { + this->DamageLayer[tile] = this->DamageLayer[tile] + 1; + } + } + continue; + } + + int buildingID = this->BuildingLayer[tile]; + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == (BuildingLogicalState)0 || DAT_BuildingsState::instance.buildings[buildingID].logicalState == OpenSHC::Map::Buildings::BLS_REMOVE) { + continue; + } + short health = DAT_BuildingsState::instance.buildings[buildingID].currentHealth; + if (health == 0) { + continue; + } + int buildingType = (short)DAT_BuildingsState::instance.buildings[buildingID].buildingType; + if (DAT_BuildingDefinedData::instance.BuildingTypeHasHealth[buildingType] == 0) { + continue; + } + if ((logic & 0xf000000) != 0 || (logic & L_STOCKPILEUnk) != 0) { + continue; + } + if (spareOwnTeam && DAT_GameState::instance.mapAndTime.playerTeams[playerID] == DAT_GameState::instance.mapAndTime.playerTeams[DAT_BuildingsState::instance.buildings[buildingID].owner]) { + continue; + } + + short newHealth = health - (short)damage; + DAT_GameCore::instance.cowPoisonTrackerUnk = DAT_GameCore::instance.cowPoisonTrackerUnk + damage * 10; + DAT_BuildingsState::instance.buildings[buildingID].currentHealth = newHealth; + switch (buildingType) { + case 0x2d: + case 0x2e: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + DAT_GameState::instance.playerDataArray[DAT_BuildingsState::instance.buildings[buildingID].owner].defensesDamagedByPlayer = (short)playerID; + } + if (unitID != 0) { + if (DAT_GameState::instance.mapAndTime.playerTeams[DAT_BuildingsState::instance.buildings[buildingID].owner] == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + = DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk + 1; + } else { + DAT_UnitsState::instance.units[unitID].friendlyFireCounterUnk = 0; + } + } + if (newHealth > 0) { + continue; + } + + if (buildingType != 0x4f && (buildingType < 0x56 || buildingType > 0x59)) { + short owner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_GameSynchronyState::instance.finalResults.finalBuildingsBurned[playerID] + = DAT_GameSynchronyState::instance.finalResults.finalBuildingsBurned[playerID] + 1; + DAT_GameSynchronyState::instance.finalResults.finalBuidingsDestroyed[owner] + = DAT_GameSynchronyState::instance.finalResults.finalBuidingsDestroyed[owner] + 1; + /* a per-attacker tally inside the owner's player data, 0x20 apart */ + int* tally = (int*)((char*)&DAT_GameState::instance.playerDataArray[owner].field889_0x2be0 + playerID * 0x20); + *tally = *tally + 1; + } + switch (buildingType) { + case 0x1c: + case 0x2d: + case 0x2e: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playAnger2BikFromPlayerToPlayer, DAT_AICState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, playerID); + break; + default: + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playVictory2BikFromPlayerToPlayer, DAT_AICState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, playerID); + } + + bool destroyNormally = false; + int towerX; + int towerY; + short towerOwner; + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_OILSMELTER: + if (DAT_BuildingsState::instance.buildings[buildingID].resources[7] == 0) { + destroyNormally = true; + break; + } + { + /* a smelter with oil in it goes up, and a full one throws burning oil about */ + int oilAmount = DAT_BuildingsState::instance.buildings[buildingID].resources[7]; + uint smelterTile = DAT_BuildingsState::instance.buildings[buildingID].currentTilePositionAdjusted; + int smelterX = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + int smelterOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + int smelterY = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + if (oilAmount < 5) { + MACRO_CALL(OpenSHC::Map::Entities_Func::SetPlaceOnFire)(smelterOwner, smelterX * 8 + 0x10, + smelterY * 8 + 0x10, this->HeightLayer[smelterTile], 3); + } else { + MACRO_CALL(OpenSHC::Map::Entities_Func::SetPlaceOnFire)(smelterOwner, smelterX * 8 + 0x10, + smelterY * 8 + 0x10, this->HeightLayer[smelterTile], 5); + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::spawnProjectileEntity, + DAT_EntityState::ptr)(0, smelterOwner, 0, smelterX * 8 + 0x10, smelterY * 8 + 0x10, + this->HeightLayer[smelterTile], 0, 0, 0, + OpenSHC::Map::Entities::EntityTypeInt__ET_EXPLOSION, 0); + } + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + smelterX, smelterY, OpenSHC::DE::SHCDE::FX_IGNITE_PITCH); + } + break; + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + { + /* a gatehouse takes its drawbridges with it */ + int bridge = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findParticularBuilding, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight, OpenSHC::Map::Buildings::BT_DRAWBRIDGE, 0); + if (bridge != 0) { + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[bridge].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(bridge); + bridge = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findParticularBuilding, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight, OpenSHC::Map::Buildings::BT_DRAWBRIDGE, bridge); + if (bridge != 0) { + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[bridge].buildingType] + == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(bridge); + } + } + ushort gateY = DAT_BuildingsState::instance.buildings[buildingID].y; + ushort gateX = DAT_BuildingsState::instance.buildings[buildingID].x; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[(short)DAT_BuildingsState::instance.buildings[buildingID].buildingType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + (short)gateX, (short)gateY, OpenSHC::DE::SHCDE::FX_TOWER_SMASH); + } + break; + case OpenSHC::Map::Buildings::BT_TUNNEL: + { + /* the tunneller inside is buried where he stands */ + uint tunneller = DAT_BuildingsState::instance.buildings[buildingID].unitRefID; + if (DAT_UnitsState::instance.units[tunneller].state.generic + != (OpenSHC::Map::Units::States::US_STAND_UPUnk + | OpenSHC::Map::Units::States::US_IDLEUnk)) { + DAT_UnitsState::instance.units[tunneller].totalSizeOfPathPlan + = DAT_UnitsState::instance.units[tunneller].currentIndexInPathPlan; + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::applyTunnelDamageAlongPathPlan, + DAT_UnitsState::ptr)(tunneller); + } + DAT_UnitsState::instance.units[tunneller].state.generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + DAT_UnitsState::instance.units[tunneller].disappearFadeAlphaCountdown = 0x20; + DAT_UnitsState::instance.units[tunneller].updateTickTracker = 0x20; + } + destroyNormally = true; + break; + default: + destroyNormally = true; + break; + case OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED: + { + int rider = DAT_BuildingsState::instance.buildings[buildingID].unitRefID; + if (rider != 0 && DAT_BuildingsState::instance.buildings[buildingID].unitRefUID == DAT_UnitsState::instance.units[rider].uid) { + DAT_UnitsState::instance.units[rider].state.generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + DAT_UnitsState::instance.units[rider].disappearFadeAlphaCountdown = 0; + DAT_UnitsState::instance.units[rider].updateTickTracker = 0; + DAT_UnitsState::instance.units[rider].workplaceBuildingID_1 = 0; + } + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + } + break; + case OpenSHC::Map::Buildings::BT_TOWER1: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x19); + towerX = DAT_BuildingsState::instance.buildings[buildingID].x; + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, this)(towerOwner, + (short)towerX, (short)towerY, OpenSHC::Commands::M_MAPPER_TOWER1_DESTROYED, 3, 0xf); + break; + case OpenSHC::Map::Buildings::BT_TOWER2: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerX = DAT_BuildingsState::instance.buildings[buildingID].x; + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, this)(towerOwner, + (short)towerX, (short)towerY, OpenSHC::Commands::M_MAPPER_TOWER2_DESTROYED, 4, 0xf); + break; + case OpenSHC::Map::Buildings::BT_TOWER3: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerX = DAT_BuildingsState::instance.buildings[buildingID].x; + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, this)(towerOwner, + (short)towerX, (short)towerY, OpenSHC::Commands::M_MAPPER_TOWER3_DESTROYED, 5, 0xf); + break; + case OpenSHC::Map::Buildings::BT_TOWER4: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerX = DAT_BuildingsState::instance.buildings[buildingID].x; + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, this)(towerOwner, + (short)towerX, (short)towerY, OpenSHC::Commands::M_MAPPER_TOWER4_DESTROYED, 6, 0xf); + break; + case OpenSHC::Map::Buildings::BT_TOWER5: + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processDamageToUnitsOnBuilding, DAT_BuildingsState::ptr)( + buildingID, 0x32); + towerX = DAT_BuildingsState::instance.buildings[buildingID].x; + towerOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + DAT_BuildingsState::instance.buildings[buildingID].noRubble = 0; + towerY = DAT_BuildingsState::instance.buildings[buildingID].y; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::deleteBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, this)(towerOwner, + (short)towerX, (short)towerY, OpenSHC::Commands::M_MAPPER_TOWER5_DESTROYED, 6, 0xf); + break; + } + + if (destroyNormally) { + int destroyedType = (short)DAT_BuildingsState::instance.buildings[buildingID].buildingType; + short destroyedOwner = DAT_BuildingsState::instance.buildings[buildingID].owner; + this->showNoRubbleWhenDestroyingBuilding + = DAT_BuildingDefinedData::instance.BuildingShowRubbleWhenDestroyed[destroyedType] == 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + (short)DAT_BuildingsState::instance.buildings[buildingID].x, (short)DAT_BuildingsState::instance.buildings[buildingID].y, OpenSHC::DE::SHCDE::FX_BUILDING_SMASH); + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY + && playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.mapAndTime.playerTeams[destroyedOwner] != DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + && DAT_GameCore::instance.genieVoiceActive != FALSE) { + if (destroyedType == 0x13 || destroyedType == 0xb) { + /* "Excellent" */ + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)("Genie_23.wav"); + } + if (destroyedType == 8 || destroyedType == 9) { + /* "Bravo" */ + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)("Genie_24.wav"); + } + } + } + } + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(9, x, baseY); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/processEntityDamageToBuildingCollateralThunk.cpp b/src/OpenSHC/Map/TileMapState/processEntityDamageToBuildingCollateralThunk.cpp new file mode 100644 index 000000000..e8d1816ec --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/processEntityDamageToBuildingCollateralThunk.cpp @@ -0,0 +1,23 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:56:35.138000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00401060 + void TileMapState::processEntityDamageToBuildingCollateralThunk( + int tile, uint x_2, uint y_2, int damage, int playerID, undefined4 unused) + { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::processEntityDamageToBuildingCollateral, this)( + tile, x_2, y_2, damage, playerID, (undefined4)((int)(unused)), 0); + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/processMapOrientationChange.cpp b/src/OpenSHC/Map/TileMapState/processMapOrientationChange.cpp new file mode 100644 index 000000000..a0e54f346 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/processMapOrientationChange.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00512920 + void TileMapState::processMapOrientationChange() + { + if ((int)this->DAT_FutureMapOrientation >= 8) { + return; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::changeMapOrientation, this)(this->DAT_FutureMapOrientation); + this->DAT_FutureMapOrientation = 8; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/propagateCliffEdgeFlagFromNeighbor.cpp b/src/OpenSHC/Map/TileMapState/propagateCliffEdgeFlagFromNeighbor.cpp new file mode 100644 index 000000000..0f82ec6bd --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/propagateCliffEdgeFlagFromNeighbor.cpp @@ -0,0 +1,54 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS; + using OpenSHC::Map::LogicHelpers::L2_STONES_OR_DRIVEN_SANDUnk; + using OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB; + using OpenSHC::Map::LogicHelpers::L_RIVER; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC280 + void TileMapState::propagateCliffEdgeFlagFromNeighbor(int tile, int x, int y) + { + int height = this->HeightLayer[tile]; + if (this->Logic2Layer[tile] != 0) { + return; + } + + for (int index = 0; index < 8; index++) { + uint neighbourY = DAT_TerrainDefinedData::instance.field2476_0x372c[index].y + y; + uint neighbourX = DAT_TerrainDefinedData::instance.field2476_0x372c[index].x + x; + if (neighbourX > 399 || neighbourY > 399) { + continue; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[neighbourY * 400 + neighbourX] == 0) { + continue; + } + int neighbour = DAT_ViewportRenderState::instance.translationMatrix[neighbourY].addXgetTile + + DAT_TerrainDefinedData::instance.field2476_0x372c[index].x + x; + if ((this->LogicLayer[neighbour] & L_RIVER) == 0) { + continue; + } + if ((this->Logic2Layer[neighbour] & (L2_OASIS_GRASS | L2_STONES_OR_DRIVEN_SANDUnk)) == 0) { + continue; + } + if (this->HeightLayer[neighbour] < height) { + continue; + } + this->Logic2Layer[tile] = L2_THICK_SCRUB; + return; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/raiseLand.cpp b/src/OpenSHC/Map/TileMapState/raiseLand.cpp new file mode 100644 index 000000000..6311488e4 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/raiseLand.cpp @@ -0,0 +1,92 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00501D90 + void TileMapState::raiseLand(int tile, uint y, uint brush, int change) + { + int baseTile = tile; + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[brush]; + /* the original reuses the brush parameter to hold the base y */ + brush = y; + for (int index = 0; index < brushSize; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, &tile, (int*)&y, baseTile, brush); + int brushTile = tile; + uint logic = this->LogicLayer[brushTile]; + if ((logic & (L_BORDER | L_BORDER_EDGE)) == 0 + && ((logic & (L_SEA | L_MARSH)) == 0 || this->unknownZero_0x554904 != 0) + && ((logic & (L_SEA | L_MARSH)) != 0 || this->unknownZero_0x554904 != 1) + && (logic + & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_MOAT_DUG_OR_PLANNED | L_KEEP_NON_MANOR_HOUSE | L_MOAT)) + == 0 + && this->BuildingLayer[brushTile] == 0) { + int minHeight; + int maxHeight; + if ((logic & L_SEA) != 0) { + minHeight = 0; + maxHeight = 0; + } else if ((logic & (L_SEA | L_MARSH | L_MOAT)) != 0) { + minHeight = 0; + maxHeight = 20; + } else { + minHeight = 8; + maxHeight = 128; + } + this->LogicLayer[brushTile] &= ~L_ROCKY; + if ((this->LogicLayer[brushTile] & L_RIVER) != 0) { + this->HeightLayer[brushTile] += 8; + } + this->ChangedLayer[brushTile] = 2; + if ((int)this->HeightLayer[brushTile] <= minHeight) { + this->HeightLayer[brushTile] = (byte)minHeight; + } + if (this->HeightLayer[brushTile] > 0 || change >= 0) { + this->HeightLayer[brushTile] += (char)change; + this->Logic2Layer[brushTile] = 0; + } + if ((int)this->HeightLayer[brushTile] < minHeight) { + this->HeightLayer[brushTile] = (byte)minHeight; + } + if ((int)this->HeightLayer[brushTile] > maxHeight) { + this->HeightLayer[brushTile] = (byte)maxHeight; + } + this->DefaultHeightLayer[brushTile] = this->HeightLayer[brushTile]; + if ((this->LogicLayer[brushTile] & L_RIVER) != 0) { + this->HeightLayer[brushTile] -= 8; + } + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, brushTile); + } + } + this->forceUpdateTextureTilemap = 1; + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/reassignWallOwnershipForPlayer.cpp b/src/OpenSHC/Map/TileMapState/reassignWallOwnershipForPlayer.cpp new file mode 100644 index 000000000..7a7970207 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/reassignWallOwnershipForPlayer.cpp @@ -0,0 +1,90 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9000 + void TileMapState::reassignWallOwnershipForPlayer(int playerID, int newOwner) + { + for (int tile = 0; tile < 80400; tile += 6) { + if ((this->LogicLayer[tile + 0] & L_WALL_OR_GATEHOUSE) != 0) { + bool matches; + if (this->BuildingLayer[tile + 0] == 0) { + matches = (this->WallOwnerLayer[tile + 0] & 7) + 1 == playerID; + } else { + matches = DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile + 0]].owner == newOwner; + } + if (matches) { + this->WallOwnerLayer[tile + 0] = this->WallOwnerLayer[tile + 0] & 0xf8 | (char)newOwner - 1U; + } + } + if ((this->LogicLayer[tile + 1] & L_WALL_OR_GATEHOUSE) != 0) { + bool matches; + if (this->BuildingLayer[tile + 1] == 0) { + matches = (this->WallOwnerLayer[tile + 1] & 7) + 1 == playerID; + } else { + matches = DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile + 1]].owner == newOwner; + } + if (matches) { + this->WallOwnerLayer[tile + 1] = this->WallOwnerLayer[tile + 1] & 0xf8 | (char)newOwner - 1U; + } + } + if ((this->LogicLayer[tile + 2] & L_WALL_OR_GATEHOUSE) != 0) { + bool matches; + if (this->BuildingLayer[tile + 2] == 0) { + matches = (this->WallOwnerLayer[tile + 2] & 7) + 1 == playerID; + } else { + matches = DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile + 2]].owner == newOwner; + } + if (matches) { + this->WallOwnerLayer[tile + 2] = this->WallOwnerLayer[tile + 2] & 0xf8 | (char)newOwner - 1U; + } + } + if ((this->LogicLayer[tile + 3] & L_WALL_OR_GATEHOUSE) != 0) { + bool matches; + if (this->BuildingLayer[tile + 3] == 0) { + matches = (this->WallOwnerLayer[tile + 3] & 7) + 1 == playerID; + } else { + matches = DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile + 3]].owner == newOwner; + } + if (matches) { + this->WallOwnerLayer[tile + 3] = this->WallOwnerLayer[tile + 3] & 0xf8 | (char)newOwner - 1U; + } + } + if ((this->LogicLayer[tile + 4] & L_WALL_OR_GATEHOUSE) != 0) { + bool matches; + if (this->BuildingLayer[tile + 4] == 0) { + matches = (this->WallOwnerLayer[tile + 4] & 7) + 1 == playerID; + } else { + matches = DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile + 4]].owner == newOwner; + } + if (matches) { + this->WallOwnerLayer[tile + 4] = this->WallOwnerLayer[tile + 4] & 0xf8 | (char)newOwner - 1U; + } + } + if ((this->LogicLayer[tile + 5] & L_WALL_OR_GATEHOUSE) != 0) { + bool matches; + if (this->BuildingLayer[tile + 5] == 0) { + matches = (this->WallOwnerLayer[tile + 5] & 7) + 1 == playerID; + } else { + matches = DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile + 5]].owner == newOwner; + } + if (matches) { + this->WallOwnerLayer[tile + 5] = this->WallOwnerLayer[tile + 5] & 0xf8 | (char)newOwner - 1U; + } + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/rebuildShowHiLayerFromHeights.cpp b/src/OpenSHC/Map/TileMapState/rebuildShowHiLayerFromHeights.cpp new file mode 100644 index 000000000..a3183dde5 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/rebuildShowHiLayerFromHeights.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F79D0 + void TileMapState::rebuildShowHiLayerFromHeights() + { + for (int tile = 0; tile < 80400; tile += 8) { + this->ShowHiLayer[tile] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile]]; + this->ShowHiLayer[tile + 1] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 1]]; + this->ShowHiLayer[tile + 2] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 2]]; + this->ShowHiLayer[tile + 3] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 3]]; + this->ShowHiLayer[tile + 4] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 4]]; + this->ShowHiLayer[tile + 5] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 5]]; + this->ShowHiLayer[tile + 6] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 6]]; + this->ShowHiLayer[tile + 7] = (uchar)this->heightBasedScreenYOffset[this->HeightLayer[tile + 7]]; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/recountTotalOwnedMoats.cpp b/src/OpenSHC/Map/TileMapState/recountTotalOwnedMoats.cpp new file mode 100644 index 000000000..674e823f7 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/recountTotalOwnedMoats.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005000A0 + void TileMapState::recountTotalOwnedMoats() + { + this->currentMoatCount = 0; + for (int moatID = 1; moatID < 16000; moatID++) { + if (this->moats[moatID].owner != '\0') { + this->currentMoatCount = moatID + 1; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/removeMoat.cpp b/src/OpenSHC/Map/TileMapState/removeMoat.cpp new file mode 100644 index 000000000..cff75b9b7 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/removeMoat.cpp @@ -0,0 +1,51 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_MOAT; + + using OpenSHC::Map::LogicHelpers::L_MOAT; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005113F0 + BOOLEnum TileMapState::removeMoat(uint moatID, int param_2) + { + this->moats[moatID].fillProgress = this->moats[moatID].fillProgress - 1; + if ((char)this->moats[moatID].fillProgress >= 1) { + return FALSE; + } + + this->HeightLayer[this->moats[moatID].tile] = 8; + this->LogicLayer[this->moats[moatID].tile] = this->LogicLayer[this->moats[moatID].tile] & ~L_MOAT; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(2, this->moats[moatID].x, this->moats[moatID].y); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(this->moats[moatID].y, this->moats[moatID].tile); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + int tile = this->moats[moatID].tile; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatData, this)(moatID); + if (this->BuildingLayer[tile] != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateBuildingGraphicsLayer, this)( + this->BuildingLayer[tile]); + } + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + return TRUE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/renderPreviewMapperWithBrush.cpp b/src/OpenSHC/Map/TileMapState/renderPreviewMapperWithBrush.cpp new file mode 100644 index 000000000..c8ec200be --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/renderPreviewMapperWithBrush.cpp @@ -0,0 +1,198 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + whenever terrain is selected and you try to place it decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00508A00 + void TileMapState::renderPreviewMapperWithBrush(uint x, uint y, MappersEnum mapper) + { + if (x > 399) { + return; + } + if (y > 399) { + return; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return; + } + + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[this->editorActiveBrush]; + int baseTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + if (mapper == OpenSHC::Commands::M_MAPPER_POND1 || mapper == OpenSHC::Commands::M_MAPPER_POND2_SMALL + || mapper == OpenSHC::Commands::M_MAPPER_POND3_LARGE1 || mapper == OpenSHC::Commands::M_MAPPER_POND4_LARGE2) { + this->buildingPlacementFail = FALSE; + return; + } + this->buildingPlacementFail = TRUE; + if (mapper == OpenSHC::Commands::M_MAPPER_SIGNPOST) { + return; + } + + /* every mapper draws its own preview colour */ + ushort gfx; + if ((int)mapper > OpenSHC::Commands::M_MAPPER_DUNES) { + gfx = 0x38; + } else if (mapper == OpenSHC::Commands::M_MAPPER_DUNES) { + gfx = 0x3c; + } else { + switch (mapper) { + case OpenSHC::Commands::M_MAPPER_SEA: + case OpenSHC::Commands::M_MAPPER_SHALLOWS: + case OpenSHC::Commands::M_MAPPER_RIVER: + case OpenSHC::Commands::M_MAPPER_MARSH: + case OpenSHC::Commands::M_MAPPER_DUGMOAT: + case OpenSHC::Commands::M_MAPPER_MOAT: + gfx = 0x39; + break; + case OpenSHC::Commands::M_MAPPER_SCRUB: + case OpenSHC::Commands::M_MAPPER_DIRT: + gfx = 0x3c; + break; + case OpenSHC::Commands::M_MAPPER_BEACH: + case OpenSHC::Commands::M_MAPPER_FORD: + gfx = 0x3a; + break; + case OpenSHC::Commands::M_MAPPER_ROCKY: + case OpenSHC::Commands::M_MAPPER_STONES: + case OpenSHC::Commands::M_MAPPER_BOULDERS: + case OpenSHC::Commands::M_MAPPER_PEBBLES: + case OpenSHC::Commands::M_MAPPER_IRON: + gfx = 0x3b; + break; + case OpenSHC::Commands::M_MAPPER_CHESTNUT: + case OpenSHC::Commands::M_MAPPER_OAK: + case OpenSHC::Commands::M_MAPPER_PINE: + case OpenSHC::Commands::M_MAPPER_BIRCH: + case OpenSHC::Commands::M_MAPPER_SHRUB1A: + case OpenSHC::Commands::M_MAPPER_SHRUB1B: + case OpenSHC::Commands::M_MAPPER_SHRUB1C: + case OpenSHC::Commands::M_MAPPER_SHRUB1D: + case OpenSHC::Commands::M_MAPPER_SHRUB1E: + case OpenSHC::Commands::M_MAPPER_SHRUB2A: + case OpenSHC::Commands::M_MAPPER_SHRUB2B: + case OpenSHC::Commands::M_MAPPER_SHRUB2C: + case OpenSHC::Commands::M_MAPPER_SHRUB2D: + case OpenSHC::Commands::M_MAPPER_SHRUB2E: + case OpenSHC::Commands::M_MAPPER_SHRUB3A: + case OpenSHC::Commands::M_MAPPER_SHRUB3B: + case OpenSHC::Commands::M_MAPPER_SHRUB3C: + case OpenSHC::Commands::M_MAPPER_SHRUB3D: + case OpenSHC::Commands::M_MAPPER_DEER: + case OpenSHC::Commands::M_MAPPER_LION: + case OpenSHC::Commands::M_MAPPER_RABBIT: + case OpenSHC::Commands::M_MAPPER_CAMEL: + case OpenSHC::Commands::M_MAPPER_CROW_SEAGULL: + case OpenSHC::Commands::M_MAPPER_SEAGULL: + /* a single organism is placed on one tile, whatever the brush size */ + brushSize = 1; + case OpenSHC::Commands::M_MAPPER_RAISE: + case OpenSHC::Commands::M_MAPPER_LOWER: + case OpenSHC::Commands::M_MAPPER_EQUALISE: + case OpenSHC::Commands::M_MAPPER_MOUNTAIN: + case OpenSHC::Commands::M_MAPPER_HILL: + case OpenSHC::Commands::M_MAPPER_DELETE: + gfx = 0x3e; + break; + case OpenSHC::Commands::M_MAPPER_UNDUGMOAT: + case OpenSHC::Commands::M_MAPPER_OIL: + gfx = 0x3d; + break; + default: + gfx = 0x38; + } + } + + switch (mapper) { + case OpenSHC::Commands::M_MAPPER_UNDUGMOAT: + case OpenSHC::Commands::M_MAPPER_DUGMOAT: + case OpenSHC::Commands::M_MAPPER_MOAT: + case OpenSHC::Commands::M_MAPPER_ANTIMOAT: + /* a moat may not be dug too near an enemy, a signpost, or an opponent's buildings */ + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR + && DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT) { + int enemyRange; + int searchRange; + if (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SOLITARY) { + enemyRange = 0x1e; + searchRange = 30; + } else { + enemyRange = 0xf; + searchRange = 7; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, DAT_PathFindingState::ptr)( + DAT_GameSynchronyState::instance.currentPlayerSlotID, x, y, enemyRange) + != FALSE) { + gfx = 0x3e; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isSignPostWithinDistance, DAT_PathFindingState::ptr)( + x, y, DAT_GameState::instance.mapAndTime.unk_signpostDistance + 5) + != FALSE) { + gfx = 0x3e; + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + int buildRange = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getCastleBuildRangeForMapSize, this)(); + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, DAT_PathFindingState::ptr)( + DAT_GameSynchronyState::instance.currentPlayerSlotID, x, y, searchRange, -1, -1, buildRange + 5) + != 0) { + gfx = 0x3e; + } + } + } + } + + int tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + uint brushY = y; + if (mapper == OpenSHC::Commands::M_MAPPER_DUGMOAT || mapper == OpenSHC::Commands::M_MAPPER_UNDUGMOAT + || mapper == OpenSHC::Commands::M_MAPPER_MOAT || mapper == OpenSHC::Commands::M_MAPPER_ANTIMOAT) { + /* a moat preview always covers the nine tiles around the cursor */ + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(0, index, &tile, (int*)&brushY, baseTile, y); + index++; + this->ConstructionGFXLayer[tile] = gfx; + } while (index < 9); + return; + } + if (brushSize < 1) { + return; + } + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, &tile, (int*)&brushY, baseTile, y); + index++; + this->ConstructionGFXLayer[tile] = gfx; + } while (index < brushSize); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/renderWallDragPreview.cpp b/src/OpenSHC/Map/TileMapState/renderWallDragPreview.cpp new file mode 100644 index 000000000..3d9cdd600 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/renderWallDragPreview.cpp @@ -0,0 +1,191 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8BD0 + void TileMapState::renderWallDragPreview( + int playerID, uint x1, uint y1, uint x2, uint y2, undefined4 command) + { + int budget = 0; + if (x1 > 399 || y1 > 399 || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y1 * 400 + x1] == 0) { + return; + } + if (x2 > 399 || y2 > 399 || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y2 * 400 + x2] == 0) { + return; + } + + this->DAT_WallTileCountCurrentDrag = 0; + int overshoot = 2; + if ((short)command == 0x1b) { + budget = this->field119_0x554924; + if (this->field118_0x554920 == 0) { + this->ConstructionGFXLayer[DAT_ViewportRenderState::instance.translationMatrix[y1].addXgetTile + x1] + = (short)GMTotalPicturesProcessed::instance[6] + 0x13; + return; + } + } + + /* the drag walks one tile at a time towards (x2, y2), on whichever axis is further away */ + int upY = y1 - y2; + int downY = y2 - y1; + uint x = x1; + uint y = y1; + int leftX = x1 - x2; + int rightX = x2 - x1; + int* translation = &DAT_ViewportRenderState::instance.translationMatrix[y1].addXgetTile; + int tile; + do { + if ((short)command == 0x1b && budget < 0x18) { + break; + } + tile = *translation + x; + if ((short)command == 0x1b && (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0) { + break; + } + if (this->constructionTileCount <= this->DAT_WallTileCountCurrentDrag) { + break; + } + + /* a crenel dragged over an existing one does not count against the tile budget */ + bool onCrenel = false; + if ((short)command == 0x19 && (this->LogicLayer[tile] & L_CRENEL) != 0) { + onCrenel = true; + } + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0 || this->DamageLayer[tile] != 0 || onCrenel) { + if (!onCrenel) { + this->DAT_WallTileCountCurrentDrag = this->DAT_WallTileCountCurrentDrag + 1; + } + if (this->illegalBuild == FALSE) { + if ((short)command == 0x19) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6]; + } else if ((short)command == 0x2e) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6] + 1; + } else if ((short)command == 0x1a) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6] + 4; + } else if ((short)command == 0x1b) { + /* a stair climbs one graphic per eight units of height below the wall top */ + int steps = (budget - (int)(uint)this->HeightLayer[tile]) / 8 - 1; + if (steps > 0) { + if (steps > 10) { + steps = 10; + } + this->ConstructionGFXLayer[tile] = (short)steps + 8 + (ushort)GMTotalPicturesProcessed::instance[6]; + } else { + this->DAT_WallTileCountCurrentDrag = this->DAT_WallTileCountCurrentDrag - 1; + } + } + } else if ((short)command == 0x19) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6] + 0x13; + } else if ((short)command == 0x2e) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6] + 0x13; + } else if ((short)command == 0x1a) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6] + 0x14; + } else if ((short)command == 0x1b) { + this->ConstructionGFXLayer[tile] = (ushort)GMTotalPicturesProcessed::instance[6] + 0x13; + } + } + + int stepX = leftX; + if ((int)x < (int)x2) { + stepX = rightX; + } + int stepY = upY; + if ((int)y < (int)y2) { + stepY = downY; + } + if ((short)command == 0x1b) { + if (stepY < stepX) { + if ((int)x < (int)x2) { + leftX = leftX + 1; + x = x + 1; + rightX = rightX - 1; + } else { + leftX = leftX - 1; + x = x - 1; + rightX = rightX + 1; + } + } else if (stepY > 0) { + if ((int)y < (int)y2) { + translation = translation + 3; + y = y + 1; + upY = upY + 1; + downY = downY - 1; + } else { + translation = translation - 3; + y = y - 1; + upY = upY - 1; + downY = downY + 1; + } + } else { + overshoot = overshoot - 1; + } + } else { + if (stepX != 0) { + if ((int)x < (int)x2) { + leftX = leftX + 1; + x = x + 1; + rightX = rightX - 1; + } else { + leftX = leftX - 1; + x = x - 1; + rightX = rightX + 1; + } + } + if (stepY != 0) { + if ((int)y < (int)y2) { + translation = translation + 3; + y = y + 1; + upY = upY + 1; + downY = downY - 1; + } else { + translation = translation - 3; + y = y - 1; + upY = upY - 1; + downY = downY + 1; + } + } + if (x == x2 && y == y2) { + overshoot = overshoot - 1; + } + } + if (budget > 0x10 && overshoot == 2) { + budget = budget - 0x10; + } + } while (x != x2 || y != y2 || overshoot != 0); + + if (this->illegalBuild != FALSE) { + this->DAT_WallTileCountCurrentDrag = 0; + } + if (this->field145_0x554980 != 0) { + return; + } + if ((short)command == 0x2e) { + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processWallBuildingLoss, DAT_BuildingsState::ptr)( + playerID, 0, this->DAT_WallTileCountCurrentDrag, 1); + return; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::processWallBuildingLoss, DAT_BuildingsState::ptr)( + playerID, this->DAT_WallTileCountCurrentDrag, 0, 1); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/renderWallPlacementPreview.cpp b/src/OpenSHC/Map/TileMapState/renderWallPlacementPreview.cpp new file mode 100644 index 000000000..1ffb26087 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/renderWallPlacementPreview.cpp @@ -0,0 +1,130 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB9E0 + void TileMapState::renderWallPlacementPreview(uint x, uint y, short kind) + { + int index = 0; + int base = 0; + int size = 0; + if (x > 399) { + return; + } + if (y > 399) { + return; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return; + } + this->buildingPlacementFail = FALSE; + /* every wall kind has its own footprint size and first graphic */ + if (kind == 0) { + size = 1; + base = 0x211; + } else if (kind == 1) { + size = 1; + base = 0x215; + } else if (kind == 2) { + size = 1; + base = 0x219; + } else if (kind == 3) { + size = 1; + base = 0x21d; + } else if (kind == 4) { + size = 2; + base = 0x1d1; + } else if (kind == 5) { + size = 2; + base = 0x1e1; + } else if (kind == 6) { + size = 2; + base = 0x1f1; + } else if (kind == 7) { + size = 2; + base = 0x201; + } else if (kind == 8) { + size = 3; + base = 0x141; + } else if (kind == 9) { + size = 3; + base = 0x165; + } else if (kind == 0xa) { + size = 3; + base = 0x189; + } else if (kind == 0xb) { + size = 3; + base = 0x1ad; + } else { + if (kind == 0xc) { + base = 0x41; + } else if (kind == 0xd) { + base = 0x81; + } else if (kind == 0xe) { + base = 0xc1; + } else if (kind == 0xf) { + base = 0x101; + } + if (base != 0) { + size = 4; + } + } + + int rotation = this->rockOrientation + this->mapOrientation; + if (this->field80_0x554894 == 5) { + rotation = this->mapOrientation; + } + if (rotation >= 8) { + rotation = rotation - 8; + } + /* the rotation part of the graphic index does not change over the footprint */ + int variationStep = rotation / 2 * size * size; + + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + this->buildingX + x; + uint logic = this->LogicLayer[tile]; + if ((logic & (L_BORDER | L_BORDER_EDGE)) != 0) { + return; + } + if ((logic & L_BUILDING) != 0) { + return; + } + if (this->OrganismLayer[tile] != 0) { + return; + } + index++; + } while (index < this->constructionTileCount); + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + this->ConstructionGFXLayer[DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y] + .addXgetTile + + this->buildingX + x] + = (short)this->buildingRotationRelatedValue + (short)GMTotalPicturesProcessed::instance[0x38] + + (short)variationStep + (short)base - 1; + index++; + } while (index < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/resetAreaBasedOnLogicalLayer.cpp b/src/OpenSHC/Map/TileMapState/resetAreaBasedOnLogicalLayer.cpp new file mode 100644 index 000000000..f6a5a762a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/resetAreaBasedOnLogicalLayer.cpp @@ -0,0 +1,72 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005127B0 + void TileMapState::resetAreaBasedOnLogicalLayer() + { + for (int tile = 0; tile < 80400; tile++) { + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) == 0) { + continue; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::useEraserBrush, this)(tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile, + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile], 1); + this->GfxLayer[tile] = 0; + this->AlphaGFXLayer[tile] = 0; + this->ConstructionGFXLayer[tile] = 0; + this->PillarGFXLayer[tile] = 1; + this->WallGFXLayer[tile] = 0; + this->FloatingLayer[tile] = 0; + this->UnitLayer[tile] = 0; + this->EntityLayer[tile] = 0; + this->EntityLayerLT25[tile] = 0; + this->BuildingLayer[tile] = 0; + this->LogicLayer[tile] = 0; + this->Logic2Layer[tile] = 0; + this->ChangedLayer[tile] = 0; + this->OrganismLayer[tile] = 0; + this->DamageLayer[tile] = 0; + this->MacroLayer[tile] = 0; + this->HeightLayer[tile] = 8; + this->DefaultHeightLayer[tile] = 8; + this->ShowHiLayer[tile] = 0; + this->MiscDisplayLayer[tile] = 0; + this->LuminesenceLayer[tile] = 0; + this->WallOwnerLayer[tile] = 0; + this->BuildingWasLayer[tile] = 0; + this->OccupancyLayer[tile] = 0; + this->AIZoneLayer[tile] = 0; + this->AIInfoLayer[tile] = 0; + this->unitDeathHeatMap[tile] = 0; + this->SEC_TileMap1104[tile] = 0; + this->SEC_PathfindingCostTileMap1105[0][tile] = 0; + this->SEC_PathfindingCostTileMap1105[1][tile] = 0; + this->SEC_PathfindingCostTileMap1105[2][tile] = 0; + this->SEC_PathfindingCostTileMap1105[3][tile] = 0; + this->SEC_PathfindingCostTileMap1105[4][tile] = 0; + this->SEC_PathfindingCostTileMap1105[5][tile] = 0; + this->SEC_PathfindingCostTileMap1105[6][tile] = 0; + this->SEC_PathfindingCostTileMap1105[7][tile] = 0; + this->SEC_PathfindingCostTileMap1105[8][tile] = 0; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/resetHeightAndMapBorders.cpp b/src/OpenSHC/Map/TileMapState/resetHeightAndMapBorders.cpp new file mode 100644 index 000000000..5b1060dd1 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/resetHeightAndMapBorders.cpp @@ -0,0 +1,111 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F6CF0 + void TileMapState::resetHeightAndMapBorders(int mapSize) + { + int inset = (400 - mapSize) / 2; + this->mapSize = mapSize; + for (int tile = 0; tile < 80400; tile++) { + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_BORDER | L_BORDER_EDGE); + } + + /* the two outer diagonals of the diamond carry the border flags */ + int rowLength = 0; + int tile = 0; + do { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BORDER; + tile++; + if (rowLength > 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BORDER_EDGE; + } + tile = tile + rowLength; + if (rowLength > 0 && (this->LogicLayer[tile - 1] & L_BORDER) == 0) { + this->LogicLayer[tile - 1] = L_BORDER_EDGE; + } + this->LogicLayer[tile] = L_BORDER; + rowLength = rowLength + 2; + tile++; + } while (rowLength < 400); + + for (rowLength = 398; rowLength >= 0; rowLength = rowLength - 2) { + this->LogicLayer[tile] = L_BORDER; + tile++; + if (rowLength > 0) { + this->LogicLayer[tile] = L_BORDER_EDGE; + } + tile = tile + rowLength; + if (rowLength > 0 && (this->LogicLayer[tile - 1] & L_BORDER) == 0) { + this->LogicLayer[tile - 1] = L_BORDER_EDGE; + } + this->LogicLayer[tile] = L_BORDER; + tile++; + } + + /* and again inset by (400 - mapSize) / 2 for a map smaller than the full diamond */ + int row = 0; + tile = 0; + for (int skipped = 0; skipped < inset; skipped++) { + tile = tile + 2 + row; + row = row + 2; + } + int firstRow = row; + for (; row < 400; row = row + 2) { + this->LogicLayer[tile + inset] = this->LogicLayer[tile + inset] | L_BORDER; + tile++; + if (row - inset > 0) { + this->LogicLayer[tile + inset] = this->LogicLayer[tile + inset] | L_BORDER_EDGE; + } + tile = tile + row; + if (row - inset > 0 && (this->LogicLayer[tile - inset] & L_BORDER) == 0) { + this->LogicLayer[tile - inset - 1] = this->LogicLayer[tile - inset - 1] | L_BORDER_EDGE; + } + this->LogicLayer[tile - inset] = this->LogicLayer[tile - inset] | L_BORDER; + tile++; + } + for (row = 398; row >= firstRow; row = row - 2) { + this->LogicLayer[tile + inset] = this->LogicLayer[tile + inset] | L_BORDER; + tile++; + if (row - inset > 0) { + this->LogicLayer[tile + inset] = this->LogicLayer[tile + inset] | L_BORDER_EDGE; + } + tile = tile + row; + if (row - inset > 0 && (this->LogicLayer[tile - inset] & L_BORDER) == 0) { + this->LogicLayer[tile - inset - 1] = this->LogicLayer[tile - inset - 1] | L_BORDER_EDGE; + } + this->LogicLayer[tile - inset] = this->LogicLayer[tile - inset] | L_BORDER; + tile++; + } + if (mapSize != 400) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::setWalkabilityBorderLogicLayerForSmallerMapSizes, + DAT_PathFindingState::ptr)(); + } + + /* every border tile ends up at the minimum height */ + for (int borderTile = 0; borderTile < 80400; borderTile++) { + if ((this->LogicLayer[borderTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + this->HeightLayer[borderTile] = 8; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/resetMoatArray.cpp b/src/OpenSHC/Map/TileMapState/resetMoatArray.cpp new file mode 100644 index 000000000..6777783f0 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/resetMoatArray.cpp @@ -0,0 +1,26 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500680 + void TileMapState::resetMoatArray() + { + for (int moatID = 0; moatID < 16000; moatID++) { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(Moat), 0, &this->moats[moatID]); + } + this->moatTileCount = 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/resetPitchDitchArray.cpp b/src/OpenSHC/Map/TileMapState/resetPitchDitchArray.cpp new file mode 100644 index 000000000..4baf6e027 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/resetPitchDitchArray.cpp @@ -0,0 +1,26 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005007D0 + void TileMapState::resetPitchDitchArray() + { + this->maxPitchDitchCount = 4000; + for (int pitchDitchID = 0; pitchDitchID < 4000; pitchDitchID++) { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(PitchDitch), 0, &this->pitchDitches[pitchDitchID]); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/resetTileAndClearMoat.cpp b/src/OpenSHC/Map/TileMapState/resetTileAndClearMoat.cpp new file mode 100644 index 000000000..63b7c6780 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/resetTileAndClearMoat.cpp @@ -0,0 +1,77 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507350 + void TileMapState::resetTileAndClearMoat(int tile) + { + int y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int rowTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_PLAIN2_AND_PITCH | L_WALL_OR_GATEHOUSE | L_CRENEL | L_STAIRS | L_UNKNOWN_WALL_RELATED + | L_CRENEL_VARIATIONUnk); + this->HeightLayer[tile] = DAT_TileMapState::instance.DefaultHeightLayer[tile]; + this->DamageLayer[tile] = 0; + if ((this->LogicLayer[tile] & L_TREE) != 0) { + int treeID = this->OrganismLayer[tile]; + if (treeID < 2000) { + switch (DAT_LandscapeState::instance.trees[treeID].treeType) { + case (TreeType)5: + case (TreeType)6: + case (TreeType)7: + case (TreeType)8: + case (TreeType)9: + case (TreeType)10: + case (TreeType)11: + case (TreeType)12: + case (TreeType)13: + case (TreeType)14: + case (TreeType)16: + case (TreeType)17: + case (TreeType)18: + case (TreeType)19: + DAT_LandscapeState::instance.trees[treeID].state = 3; + } + } + } + if ((this->LogicLayer[tile] & (L_MOAT | L_MOAT_DUG_OR_PLANNED)) != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatDataAtTile, this)(tile - rowTile, y); + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_MOAT | L_MOAT_DUG_OR_PLANNED); + } + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xfc3f; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/resetTileToDefaultState.cpp b/src/OpenSHC/Map/TileMapState/resetTileToDefaultState.cpp new file mode 100644 index 000000000..60e41b77a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/resetTileToDefaultState.cpp @@ -0,0 +1,36 @@ +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F81D0 + void TileMapState::resetTileToDefaultState(int param_1, uint param_2, uint param_3) + { + this->LogicLayer[param_1] = this->LogicLayer[param_1] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_STAIRS | L_UNKNOWN_WALL_RELATED | L_CRENEL_VARIATIONUnk); + this->HeightLayer[param_1] = this->DefaultHeightLayer[param_1]; + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, DAT_EntityState::ptr)( + param_1); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(5, param_2, param_3); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/returnMoatIDForPlayerIDs.cpp b/src/OpenSHC/Map/TileMapState/returnMoatIDForPlayerIDs.cpp new file mode 100644 index 000000000..797027058 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/returnMoatIDForPlayerIDs.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500290 + int TileMapState::returnMoatIDForPlayerIDs(int playerID, int playerID2) + { + for (int moatID = 1; moatID < this->currentMoatCount; moatID++) { + if (this->moats[moatID].tile == playerID && this->moats[moatID].owner == playerID2) { + return moatID; + } + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/returnOwnedMoatAtTile.cpp b/src/OpenSHC/Map/TileMapState/returnOwnedMoatAtTile.cpp new file mode 100644 index 000000000..5174adaec --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/returnOwnedMoatAtTile.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500250 + int TileMapState::returnOwnedMoatAtTile(int targetedTile) + { + for (int moatID = 1; moatID < this->currentMoatCount; moatID++) { + if (this->moats[moatID].owner != 0 && this->moats[moatID].tile == targetedTile) { + return moatID; + } + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/returnSomeHeight.cpp b/src/OpenSHC/Map/TileMapState/returnSomeHeight.cpp new file mode 100644 index 000000000..aaf72d2cb --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/returnSomeHeight.cpp @@ -0,0 +1,84 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FF990 + uint TileMapState::returnSomeHeight(int tile, int param_2) + { + int extraHeight = 0; + if ((this->LogicLayer[tile] & L_KEEP_NON_MANOR_HOUSE) != 0) { + return this->HeightLayer[tile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(this->BuildingLayer[tile]); + } + + if ((this->LogicLayer[tile] & L_BUILDING) != 0) { + short buildingID = this->BuildingLayer[tile]; + switch (DAT_BuildingsState::instance.buildings[buildingID].buildingType) { + case OpenSHC::Map::Buildings::BT_GATEHOUSELARGE: + case OpenSHC::Map::Buildings::BT_GATEHOUSESMALL: + case OpenSHC::Map::Buildings::BT_TOWER: + case OpenSHC::Map::Buildings::BT_TOWER1: + case OpenSHC::Map::Buildings::BT_TOWER2: + case OpenSHC::Map::Buildings::BT_TOWER3: + case OpenSHC::Map::Buildings::BT_TOWER4: + case OpenSHC::Map::Buildings::BT_TOWER5: + return this->HeightLayer[tile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(buildingID) + + 10; + case OpenSHC::Map::Buildings::BT_HUNTERSHUT: + if (param_2 != 0) { + return this->HeightLayer[tile]; + } + default: + return this->HeightLayer[tile] + + DAT_BuildingDefinedData::instance + .BuildingHeights[DAT_BuildingsState::instance.buildings[buildingID].buildingType]; + case OpenSHC::Map::Buildings::BT_MANORHOUSE: + case OpenSHC::Map::Buildings::BT_STONEKEEP: + case OpenSHC::Map::Buildings::BT_STRONGHOLD: + case OpenSHC::Map::Buildings::BT_KEEPFOUR: + case OpenSHC::Map::Buildings::BT_KEEPFIVE: + return this->HeightLayer[tile] + + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(buildingID); + } + } + + /* + OrganismLayer holds tree ids below 2000 and rock ids biased by 2000; Ghidra rendered the + rock lookup as an out-of-bounds index into trees[0x636]. + */ + if ((this->LogicLayer[tile] & L_ROCKY) != 0 && (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0 + && this->OrganismLayer[tile] >= 2000) { + short rockType = DAT_LandscapeState::instance.rocks[this->OrganismLayer[tile] - 2000].type; + if (rockType >= 0xd) { + return this->HeightLayer[tile] + 0x46; + } + if (rockType >= 9) { + extraHeight = 0x32; + } + } + return this->HeightLayer[tile] + extraHeight; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/rotateFearFactorBuildingVariations.cpp b/src/OpenSHC/Map/TileMapState/rotateFearFactorBuildingVariations.cpp new file mode 100644 index 000000000..8ad6da067 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/rotateFearFactorBuildingVariations.cpp @@ -0,0 +1,102 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Commands::MappersEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F8300 + void TileMapState::rotateFearFactorBuildingVariations() + { + /* + currentMapperCommand is reached through the global instance's absolute address, and the + switch below is the original's byte table at 0x004F847C into the jump table at 0x004F8454, + biased by M_MAPPER_GARDEN1. The "- 1 & 6" forms are the original's setcc/dec/and idiom. + */ + DWORD now = timeGetTime(); + if (this->field80_0x554894 == 5) { + if (this->rockOrientation == 0) { + this->rockOrientation = 2; + } else if (this->rockOrientation == 2) { + this->rockOrientation = 4; + } else { + this->rockOrientation = (this->rockOrientation != 4) - 1 & 6; + } + } + if ((int)(this->lastTime + 1000) > (int)now) { + return; + } + + this->lastTime = now; + this->field78_0x55488c = (this->field78_0x55488c == 0x50) + 0x50; + if (this->field80_0x554894 == 0) { + if (this->rockOrientation == 0) { + this->rockOrientation = 2; + } else if (this->rockOrientation == 2) { + this->rockOrientation = 4; + } else { + this->rockOrientation = (this->rockOrientation != 4) - 1 & 6; + } + } + + switch (DAT_TileMapState::instance.currentMapperCommand) { + case OpenSHC::Commands::M_MAPPER_GARDEN1: + case OpenSHC::Commands::M_MAPPER_GARDEN2: + case OpenSHC::Commands::M_MAPPER_GARDEN3: + case OpenSHC::Commands::M_MAPPER_GARDEN4: + case OpenSHC::Commands::M_MAPPER_GARDEN5: + case OpenSHC::Commands::M_MAPPER_GARDEN7: + case OpenSHC::Commands::M_MAPPER_GARDEN8: + case OpenSHC::Commands::M_MAPPER_GARDEN10: + case OpenSHC::Commands::M_MAPPER_GARDEN11: + case OpenSHC::Commands::M_MAPPER_CESS_PIT1: + case OpenSHC::Commands::M_MAPPER_CESS_PIT2: + case OpenSHC::Commands::M_MAPPER_CESS_PIT3: + case OpenSHC::Commands::M_MAPPER_STATUE1: + case OpenSHC::Commands::M_MAPPER_STATUE2: + case OpenSHC::Commands::M_MAPPER_STATUE3: + case OpenSHC::Commands::M_MAPPER_STATUE4: + case OpenSHC::Commands::M_MAPPER_SHRINE1: + case OpenSHC::Commands::M_MAPPER_POND1: + case OpenSHC::Commands::M_MAPPER_POND3_LARGE1: + DAT_TileMapState::instance.currentMapperCommand + = (MappersEnum)(DAT_TileMapState::instance.currentMapperCommand + OpenSHC::Commands::M_MAPPER_AREA); + return; + case OpenSHC::Commands::M_MAPPER_GARDEN6: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_GARDEN1; + return; + case OpenSHC::Commands::M_MAPPER_GARDEN9: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_GARDEN7; + return; + case OpenSHC::Commands::M_MAPPER_GARDEN12: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_GARDEN10; + return; + case OpenSHC::Commands::M_MAPPER_STATUE5: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_STATUE1; + return; + case OpenSHC::Commands::M_MAPPER_POND2_SMALL: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_POND1; + return; + case OpenSHC::Commands::M_MAPPER_POND4_LARGE2: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_POND3_LARGE1; + return; + case OpenSHC::Commands::M_MAPPER_SHRINE2: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_SHRINE1; + return; + case OpenSHC::Commands::M_MAPPER_CESS_PIT4: + DAT_TileMapState::instance.currentMapperCommand = OpenSHC::Commands::M_MAPPER_CESS_PIT1; + return; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setBitFlagBasedOnWallTowerGatehouseOrKeep.cpp b/src/OpenSHC/Map/TileMapState/setBitFlagBasedOnWallTowerGatehouseOrKeep.cpp new file mode 100644 index 000000000..7103bd749 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setBitFlagBasedOnWallTowerGatehouseOrKeep.cpp @@ -0,0 +1,42 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +#define NEIGHBOUR_FLAGS_MASK_WALL_OR_KEEP 0x10000100 // L_WALL_OR_GATEHOUSE | L_KEEP_NON_MANOR_HOUSE + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FF870 + byte TileMapState::setBitFlagBasedOnWallTowerGatehouseOrKeep(int x, int y) + { + /* + The body is the handwritten neighbour-flag macro; see NeighbourFlagsAsm.hpp. Written as C++ + it would be: + + this->bitFlag = 0; + if ((this->ptr_LogicLayer[this->DAT_SomeTile + 1] & (L_WALL_OR_GATEHOUSE | L_KEEP_NON_MANOR_HOUSE)) != 0) { + this->bitFlag = 0x20; + } + ... and so on for the eight neighbours, where the north row is offset by + ptr_MovementDirectionTranslationMatrix[DAT_SomeY * 8 + 0] and the south row by [+ 4]. + */ + this->DAT_SomeY = y; + this->DAT_SomeTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, DAT_ViewportRenderState::ptr)(x, y); + + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(1, NEIGHBOUR_FLAGS_MASK_WALL_OR_KEEP) + + return this->bitFlag; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setChangedLayerToThreeAndMapping0x40x40.cpp b/src/OpenSHC/Map/TileMapState/setChangedLayerToThreeAndMapping0x40x40.cpp new file mode 100644 index 000000000..69d856d91 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setChangedLayerToThreeAndMapping0x40x40.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/OS.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F6AE0 + void TileMapState::setChangedLayerToThreeAndMapping0x40x40() + { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(this->ChangedLayer), 3, this->ChangedLayer); + MACRO_CALL(OpenSHC::OS_Func::_memset)(this->mapping40x40, 3, sizeof(this->mapping40x40)); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::setChangedLayerZeroBasedOn40x40Layer, + DAT_PathFindingState::ptr)(1); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setConstructionGFXLayerBasedOnPlacementChecks.cpp b/src/OpenSHC/Map/TileMapState/setConstructionGFXLayerBasedOnPlacementChecks.cpp new file mode 100644 index 000000000..e2b04b96e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setConstructionGFXLayerBasedOnPlacementChecks.cpp @@ -0,0 +1,794 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Type propagation algorithm not settling + */ + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "MappersEnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00504F10 + int TileMapState::setConstructionGFXLayerBasedOnPlacementChecks(int x, int y, MappersEnum type, int size) + { + int iVar1; + BuildingType BVar2; + undefined2 uVar8; + int iVar3; + int iVar4; + int iVar5; + int iVar6; + int iVar7; + ushort uVar9; + MappersEnum MVar10; + XYPair* pXVar11; + int* piVar12; + int iVar13; + int iVar14; + int iVar15; + int local_14; + int _offset; + short sprite; + short command; + iVar1 = x; + command = (short)type; + iVar14 = this->field188_0x554a14; + local_14 = 0; + this->field188_0x554a14 = 0; + if (iVar14 != 0) { + BVar2 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)((MappersEnum)command); + this->buildingSpriteSheetID_1 = DAT_BuildingDefinedData::instance.Building_SpriteSheet_ID_Array_1[BVar2].intValue; + this->buildingSpriteID1 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getSpriteID, + DAT_BuildingsState::ptr)((MappersEnum)command); + this->buildingSpriteID2 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getSpriteID2, + DAT_BuildingsState::ptr)((MappersEnum)command); + } else { + if (this->buildingPlacementFail == 2) { + this->field188_0x554a14 = 0; + return (int)(2); + } + if (this->buildingPlacementFail != FALSE) { + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + local_14, size); + iVar14 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + if ((this->LogicLayer[iVar14] & 0x10000500U) == 0) { + iVar15 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar14, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + this->ConstructionGFXLayer[iVar14] = (-(ushort)(iVar15 != 0) & 0xfff9) + 0x3e; + } + local_14 = local_14 + 1; + } while (local_14 < this->constructionTileCount); + uVar8 = (undefined2)((uint)local_14 >> 0x10); + if (((command == OpenSHC::Commands::M_MAPPER_KEEP1) || (command == OpenSHC::Commands::M_MAPPER_KEEP2)) + || (command == OpenSHC::Commands::M_MAPPER_KEEP3)) { + piVar12 + = (int*)((int)DAT_TerrainDefinedData::ptr + ((short)command + -0x3c) * 0x60 + 0x264); + x = 3; + do { + iVar14 = DAT_ViewportRenderState::instance.translationMatrix[piVar12[1] + y].addXgetTile + + *piVar12 + iVar1; + if ((this->LogicLayer[iVar14] & 0x10000500U) == 0) { + iVar15 + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar14, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + this->ConstructionGFXLayer[iVar14] = (-(ushort)(iVar15 != 0) & 0xfff9) + 0x3e; + } + piVar12 = piVar12 + 2; + x = x + -1; + } while (x != 0); + iVar3 = ((short)command + -0x3c) * 0x20; + iVar14 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar3 + 900); + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar3 + 0x388); + local_14 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + local_14, 7); + iVar13 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y + iVar15] + .addXgetTile + + this->buildingX + iVar1 + iVar14; + if ((this->LogicLayer[iVar13] & 0x10000500U) == 0) { + iVar4 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar13, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + this->ConstructionGFXLayer[iVar13] = (-(ushort)(iVar4 != 0) & 0xfff9) + 0x3e; + } + local_14 = local_14 + 1; + } while (local_14 < this->constructionTileCount); + iVar14 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar3 + 0x3e4); + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar3 + 1000); + iVar3 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar3, 5); + iVar13 = this->buildingY + iVar15 + y; + iVar4 = DAT_ViewportRenderState::instance.translationMatrix[iVar13].addXgetTile + + this->buildingX + iVar14 + iVar1; + if ((this->LogicLayer[iVar4] & 0x10000500U) == 0) { + iVar13 + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar4, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + iVar13 = (-(uint)(iVar13 != 0) & 0xfffffff9) + 0x3e; + this->ConstructionGFXLayer[iVar4] = (ushort)iVar13; + } + uVar8 = (undefined2)((uint)iVar13 >> 0x10); + iVar3 = iVar3 + 1; + } while (iVar3 < this->constructionTileCount); + } + if (((command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1A) || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1B)) + || ((command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1C || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1D)))) { + MVar10 = (MappersEnum)command; + iVar14 = MVar10 * 3 + -0x1a4; + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar14 * 8 + 0x56c); + iVar14 = iVar14 * 8; + iVar3 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar14 + 0x570); + iVar13 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar13, 3); + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + iVar3 + y] + .addXgetTile + + this->buildingX + iVar15 + iVar1; + iVar4 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar5, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), MVar10, 0); + iVar13 = iVar13 + 1; + this->ConstructionGFXLayer[iVar5] = (-(ushort)(iVar4 != 0) & 0xfff9) + 0x3e; + } while (iVar13 < this->constructionTileCount); + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar14 + 0x578); + iVar14 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar14 + 0x57c); + iVar3 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar3, 3); + iVar4 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + iVar14 + y] + .addXgetTile + + this->buildingX + iVar15 + iVar1; + iVar13 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar4, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), MVar10, 0); + iVar3 = iVar3 + 1; + this->ConstructionGFXLayer[iVar4] = (-(ushort)(iVar13 != 0) & 0xfff9) + 0x3e; + } while (iVar3 < this->constructionTileCount); + piVar12 = (int*)((int)DAT_TerrainDefinedData::ptr + (MVar10 * 3 + -0x1a4) * 0x10 + 0x5cc); + type = OpenSHC::Commands::M_MAPPER_FOREST; + do { + iVar15 = DAT_ViewportRenderState::instance.translationMatrix[piVar12[1] + y].addXgetTile + + *piVar12 + iVar1; + iVar14 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, this)( + iVar15, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), MVar10, 0); + iVar14 = (-(uint)(iVar14 != 0) & 0xfffffff9) + 0x3e; + piVar12 = piVar12 + 2; + type = (MappersEnum)(type - OpenSHC::Commands::M_MAPPER_AREA); + this->ConstructionGFXLayer[iVar15] = (ushort)iVar14; + } while (type != OpenSHC::Commands::M_MAPPER_NULL); + return iVar14; + } + if ((command == OpenSHC::Commands::M_MAPPER_BARRACKS_EURO) || (command == OpenSHC::Commands::M_MAPPER_BARRACKS_ARAB)) { + iVar14 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar14, 5); + iVar3 = this->buildingX; + iVar15 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile; + if ((this->LogicLayer[iVar1 + this->buildingX + iVar15 + 5] & 0x10000500U) == 0) { + iVar13 + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar15 + this->buildingX + iVar1 + 5, + (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + this->ConstructionGFXLayer[iVar1 + iVar3 + iVar15 + 5] + = (-(ushort)(iVar13 != 0) & 0xfff9) + 0x3e; + } + iVar14 = iVar14 + 1; + } while (iVar14 < this->constructionTileCount); + iVar14 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar14, 5); + iVar15 + = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5].addXgetTile + + this->buildingX + iVar1; + if ((this->LogicLayer[iVar15] & 0x10000500U) == 0) { + iVar3 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar15, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + this->ConstructionGFXLayer[iVar15] = (-(ushort)(iVar3 != 0) & 0xfff9) + 0x3e; + } + iVar14 = iVar14 + 1; + } while (iVar14 < this->constructionTileCount); + iVar14 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar14, 5); + iVar3 = this->buildingX; + iVar13 = this->buildingY + y + 5; + iVar15 + = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5].addXgetTile; + if ((this->LogicLayer[iVar1 + this->buildingX + iVar15 + 5] & 0x10000500U) == 0) { + iVar13 + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar15 + this->buildingX + iVar1 + 5, + (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + iVar13 = (-(uint)(iVar13 != 0) & 0xfffffff9) + 0x3e; + this->ConstructionGFXLayer[iVar1 + iVar3 + iVar15 + 5] = (ushort)iVar13; + } + iVar14 = iVar14 + 1; + } while (iVar14 < this->constructionTileCount); + return iVar13; + } + if ((command != OpenSHC::Commands::M_MAPPER_ENGINEERS_GUILD) && (command != OpenSHC::Commands::M_MAPPER_TUNNELERS_GUILD)) { + if (command != OpenSHC::Commands::M_MAPPER_OIL_SMELTER) { + /* the original returns eax with a leftover tile index still in its high half */ + return (ushort)command; + } + iVar14 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar14, 4); + iVar15 = this->buildingY + y + 4; + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 4].addXgetTile + + this->buildingX + iVar1; + if ((this->LogicLayer[iVar3] & 0x10000500U) == 0) { + iVar15 + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar3, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + OpenSHC::Commands::M_MAPPER_OIL_SMELTER, 0); + iVar15 = (-(uint)(iVar15 != 0) & 0xfffffff9) + 0x3e; + this->ConstructionGFXLayer[iVar3] = (ushort)iVar15; + } + iVar14 = iVar14 + 1; + } while (iVar14 < this->constructionTileCount); + return iVar15; + } + iVar14 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar14, 5); + iVar15 = (this->buildingY + y + 5) * 3; + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5].addXgetTile + + this->buildingX + iVar1; + if ((this->LogicLayer[iVar3] & 0x10000500U) == 0) { + iVar15 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, + this)(iVar3, (int)((int)(DAT_GameSynchronyState::instance.currentPlayerSlotID)), + (MappersEnum)command, 0); + iVar15 = (-(uint)(iVar15 != 0) & 0xfffffff9) + 0x3e; + this->ConstructionGFXLayer[iVar3] = (ushort)iVar15; + } + iVar14 = iVar14 + 1; + } while (iVar14 < this->constructionTileCount); + return iVar15; + } + } + if (command == OpenSHC::Commands::M_MAPPER_WHEATFARM) { + size = 3; + } else if (command == OpenSHC::Commands::M_MAPPER_HOPSFARM) { + size = 3; + } else if (command == OpenSHC::Commands::M_MAPPER_APPLEFARM) { + size = 3; + } else if (command == OpenSHC::Commands::M_MAPPER_CATTLEFARM) { + size = 3; + } else if ((((command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1A) || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1C)) + || (command == OpenSHC::Commands::M_MAPPER_GATE_STONE1A)) + || (command == OpenSHC::Commands::M_MAPPER_GATE_STONE2A)) { + this->field78_0x55488c = 0x51; + } else if (((command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1B) || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1D)) + || ((command == OpenSHC::Commands::M_MAPPER_GATE_STONE1B || (command == OpenSHC::Commands::M_MAPPER_GATE_STONE2B)))) { + this->field78_0x55488c = 0x50; + } + MVar10 = (MappersEnum)command; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(local_14, size); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + iVar1; + iVar15 = this->uiBuildingRotation; + if (((this->uiBuildingRotation != 0xf) && (this->uiBuildingRotation != 8)) + && (iVar15 = this->uiBuildingRotation - this->mapOrientation, iVar15 < 0)) { + iVar15 = iVar15 + 8; + } + sprite = (short)this->buildingSpriteID1; + if ((this->mapOrientation == 2) || (this->mapOrientation == 6)) { + if (this->field78_0x55488c != 0x51) + goto LAB_00505714; + } else if (this->field78_0x55488c == 0x51) { + LAB_00505714: + sprite = (short)this->buildingSpriteID2; + } + if ((command == OpenSHC::Commands::M_MAPPER_DRAWBRIDGE) && (iVar15 != 0)) { + if (iVar15 == 2) { + iVar15 = 3; + } else if (iVar15 == 4) { + iVar15 = 2; + } else if (iVar15 == 6) { + iVar15 = 1; + } + } + BVar2 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(MVar10); + if (BVar2 == OpenSHC::Map::Buildings::BT_UNKNOWN1) { + sprite = sprite + (short)(&DAT_BuildingDefinedData::instance.field209_0x976c[9].y)[MVar10]; + } else { + BVar2 = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(MVar10); + if (BVar2 == OpenSHC::Map::Buildings::BT_HOVEL) { + sprite = (short)DAT_BuildingDefinedData::instance.SomeSpriteArray1[DAT_GameState::instance + .playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .hovelCountUpToEight]; + } + } + if (this->uiBuildingRotation == 0xf) { + this->ConstructionGFXLayer[iVar3] + = ((short)GMTotalPicturesProcessed::instance[this->buildingSpriteSheetID_1] + sprite + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + this->ConstructionGFXLayer[iVar3] + = ((short)(iVar15 * size * size) + + (short)GMTotalPicturesProcessed::instance[this->buildingSpriteSheetID_1] + sprite + + (short)this->buildingRotationRelatedValue) + - 1; + } + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + local_14 = local_14 + 1; + if (this->constructionTileCount <= local_14) { + iVar15 = (ushort)command; + if ((((command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1A) || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1B)) + || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1C)) + || (command == OpenSHC::Commands::M_MAPPER_GATE_WOOD1D)) { + /* + 0 3 6 9 + */ + _offset = MVar10 * 3 + -420; + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + _offset * 8 + 0x50c); + iVar3 = *(int*)((int)DAT_TerrainDefinedData::ptr + _offset * 8 + 0x510); + /* + 0 24 48 72 + */ + iVar4 = _offset * 8; + iVar13 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 0x514); + iVar5 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar5, 2); + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[iVar3 + y + this->buildingY] + .addXgetTile + + this->buildingX + iVar15 + iVar1; + this->ConstructionGFXLayer[iVar6] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[0x34]; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar6] = this->MiscDisplayLayer[iVar6] | 0x8000; + } + iVar5 = iVar5 + 1; + } while (iVar5 < this->constructionTileCount); + pXVar11 = &DAT_TerrainDefinedData::instance.field555_0x68c + iVar13 * 5; + iVar5 = 0; + do { + iVar6 = DAT_ViewportRenderState::instance.translationMatrix[pXVar11->y + iVar3 + y].addXgetTile + + pXVar11->x + iVar15 + iVar1; + if (iVar5 < 1) { + this->ConstructionGFXLayer[iVar6] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x60e + (short)iVar5; + } else { + iVar7 = DAT_TerrainDefinedData::instance.field600_0x7c8[iVar5 + iVar13 * 4] + - this->mapOrientation; + if (iVar7 < 0) { + iVar7 = iVar7 + 8; + } + this->ConstructionGFXLayer[iVar6] = (short)GMTotalPicturesProcessed::instance[0x34] + + (short)(iVar7 / 2) * 4 + 0x60e + (short)iVar5; + } + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar6] = this->MiscDisplayLayer[iVar6] | 0x8000; + } + iVar5 = iVar5 + 1; + pXVar11 = pXVar11 + 1; + } while (iVar5 < 5); + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 0x518); + iVar3 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 0x51c); + iVar13 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar4 + 0x520); + iVar4 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar4, 2); + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[iVar3 + y + this->buildingY] + .addXgetTile + + this->buildingX + iVar15 + iVar1; + this->ConstructionGFXLayer[iVar5] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[0x34]; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar5] = this->MiscDisplayLayer[iVar5] | 0x8000; + } + iVar4 = iVar4 + 1; + } while (iVar4 < this->constructionTileCount); + pXVar11 = &DAT_TerrainDefinedData::instance.field555_0x68c + iVar13 * 5; + iVar4 = 0; + do { + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[pXVar11->y + iVar3 + y].addXgetTile + + pXVar11->x + iVar15 + iVar1; + if (iVar4 < 1) { + this->ConstructionGFXLayer[iVar5] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x60e + (short)iVar4; + } else { + iVar6 = DAT_TerrainDefinedData::instance.field600_0x7c8[iVar4 + iVar13 * 4] + - this->mapOrientation; + if (iVar6 < 0) { + iVar6 = iVar6 + 8; + } + this->ConstructionGFXLayer[iVar5] = (short)GMTotalPicturesProcessed::instance[0x34] + + (short)(iVar6 / 2) * 4 + 0x60e + (short)iVar4; + } + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar5] = this->MiscDisplayLayer[iVar5] | 0x8000; + } + iVar4 = iVar4 + 1; + pXVar11 = pXVar11 + 1; + } while (iVar4 < 5); + piVar12 = (int*)((int)DAT_TerrainDefinedData::ptr + (MVar10 * 3 + -0x1a4) * 0x10 + 0x5cc); + type = OpenSHC::Commands::M_MAPPER_FOREST; + do { + iVar15 = DAT_ViewportRenderState::instance.translationMatrix[piVar12[1] + y].addXgetTile + iVar1 + + *piVar12; + this->ConstructionGFXLayer[iVar15] = ((byte)this->RandomLayer[iVar15] & 3) + 0x138 + + (short)GMTotalPicturesProcessed::instance[6]; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar15] = this->MiscDisplayLayer[iVar15] | 0x8000; + } + piVar12 = piVar12 + 2; + type = (MappersEnum)(type - OpenSHC::Commands::M_MAPPER_AREA); + } while (type != OpenSHC::Commands::M_MAPPER_NULL); + } else { + if (command == OpenSHC::Commands::M_MAPPER_WHEATFARM) { + iVar15 = 0; + do { + iVar3 = DAT_GameState::instance + .playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .unkWheatFarmOrientation; + iVar3 = DAT_ViewportRenderState::instance + .translationMatrix + [DAT_TerrainDefinedData::instance.WheatFarmTiles[iVar3][iVar15].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.WheatFarmTiles[iVar3][iVar15].x + iVar1; + this->ConstructionGFXLayer[iVar3] + = ((byte)this->RandomLayer[iVar3] & 3) + (short)GMTotalPicturesProcessed::instance[0xe]; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < 0x24); + return iVar3; + } + if (command == OpenSHC::Commands::M_MAPPER_HOPSFARM) { + iVar15 = 0; + do { + iVar3 = *(int*)&DAT_GameState::instance + .playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .unkHopFarmVariation; + iVar3 = DAT_ViewportRenderState::instance + .translationMatrix + [DAT_TerrainDefinedData::instance.HopFarmProperty1[iVar3][iVar15].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.HopFarmProperty1[iVar3][iVar15].x + iVar1; + this->ConstructionGFXLayer[iVar3] = ((byte)this->RandomLayer[iVar3] & 1) * 9 + 0x25 + + (short)GMTotalPicturesProcessed::instance[0xe]; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < 0x18); + return iVar3; + } + if (command == OpenSHC::Commands::M_MAPPER_CATTLEFARM) { + iVar15 = 0; + do { + iVar3 = DAT_GameState::instance + .playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .dairyFarmVariationMod4; + iVar13 + = DAT_ViewportRenderState::instance + .translationMatrix + [DAT_TerrainDefinedData::instance.field1009_0xf5c[iVar3][iVar15 + 4].offset.y + + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field1009_0xf5c[iVar3][iVar15 + 4].offset.x + iVar1; + iVar3 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getRubbleGraphicStageForDamageLevel, this)( + DAT_TerrainDefinedData::instance.field1009_0xf5c[iVar3][iVar15 + 4].property); + iVar3 = iVar3 + 0x37 + GMTotalPicturesProcessed::instance[0xe]; + this->ConstructionGFXLayer[iVar13] = (ushort)iVar3; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar13] = this->MiscDisplayLayer[iVar13] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < 0x17); + return iVar3; + } + if (command == OpenSHC::Commands::M_MAPPER_APPLEFARM) { + pXVar11 = DAT_TerrainDefinedData::instance.AppleFarmOffsets; + do { + iVar15 = DAT_ViewportRenderState::instance.translationMatrix[pXVar11->y + y].addXgetTile + + pXVar11->x + iVar1; + this->ConstructionGFXLayer[iVar15] = (short)GMTotalPicturesProcessed::instance[0xe] + 0x3d; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar15] = this->MiscDisplayLayer[iVar15] | 0x8000; + } + pXVar11 = pXVar11 + 1; + } while ((int)pXVar11 < 0xb49eb0); + return iVar15; + } + if ((command == OpenSHC::Commands::M_MAPPER_BARRACKS_EURO) || (command == OpenSHC::Commands::M_MAPPER_BARRACKS_ARAB)) { + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 5); + iVar13 = this->buildingX; + iVar3 + = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile; + this->ConstructionGFXLayer[iVar1 + this->buildingX + iVar3 + 5] + = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x48; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar1 + iVar13 + iVar3 + 5] + = this->MiscDisplayLayer[iVar1 + iVar13 + iVar3 + 5] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 5); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5] + .addXgetTile + + this->buildingX + iVar1; + this->ConstructionGFXLayer[iVar3] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x7a; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 5); + iVar13 = this->buildingX; + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5] + .addXgetTile; + iVar4 = iVar3 + this->buildingX; + this->ConstructionGFXLayer[iVar1 + this->buildingX + iVar3 + 5] + = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x61; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar1 + iVar13 + iVar3 + 5] + = this->MiscDisplayLayer[iVar1 + iVar13 + iVar3 + 5] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + return iVar4 + iVar1 + 5; + } + if (command == OpenSHC::Commands::M_MAPPER_ENGINEERS_GUILD) { + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 5); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5] + .addXgetTile + + this->buildingX + iVar1; + this->ConstructionGFXLayer[iVar3] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x93; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + return iVar3; + } + if (command == OpenSHC::Commands::M_MAPPER_TUNNELERS_GUILD) { + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 5); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 5] + .addXgetTile + + this->buildingX + iVar1; + this->ConstructionGFXLayer[iVar3] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0xac; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + return iVar3; + } + if (command == OpenSHC::Commands::M_MAPPER_OIL_SMELTER) { + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 4); + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[y + this->buildingY + 4] + .addXgetTile + + this->buildingX + iVar1; + this->ConstructionGFXLayer[iVar3] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x128; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + return iVar3; + } + if (command == OpenSHC::Commands::M_MAPPER_KEEP1) { + uVar9 = (ushort)(this->mapOrientation == 2); + iVar15 = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field130_0x264[0][0].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field130_0x264[0][0].x + iVar1; + this->ConstructionGFXLayer[iVar15] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x523 + uVar9; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar15] = this->MiscDisplayLayer[iVar15] | 0x8000; + } + iVar15 = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field130_0x264[0][1].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field130_0x264[0][1].x + iVar1; + this->ConstructionGFXLayer[iVar15] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x52e + uVar9; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar15] = this->MiscDisplayLayer[iVar15] | 0x8000; + } + iVar15 = DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field130_0x264[0][2].y + y] + .addXgetTile + + DAT_TerrainDefinedData::instance.field130_0x264[0][2].x + iVar1; + this->ConstructionGFXLayer[iVar15] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x527 + uVar9; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar15] = this->MiscDisplayLayer[iVar15] | 0x8000; + } + iVar3 = iVar1 + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x384[0][0].xOffset; + iVar13 = y + DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x384[0][0].yOffset; + iVar15 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar15, 7); + iVar4 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + iVar13] + .addXgetTile + + this->buildingX + iVar3; + this->ConstructionGFXLayer[iVar4] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x17; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar4] = this->MiscDisplayLayer[iVar4] | 0x8000; + } + iVar15 = iVar15 + 1; + } while (iVar15 < this->constructionTileCount); + iVar1 = DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x3e4[0][0].xOffset + iVar1; + iVar15 = DAT_TerrainDefinedData::instance.keepOutbuildingOffsets_0x3e4[0][0].yOffset + y; + iVar3 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar3, 5); + iVar13 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + iVar15] + .addXgetTile + + this->buildingX + iVar1; + this->ConstructionGFXLayer[iVar13] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[7] + 0x19; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar13] = this->MiscDisplayLayer[iVar13] | 0x8000; + } + iVar3 = iVar3 + 1; + } while (iVar3 < this->constructionTileCount); + return iVar13; + } + if ((command == OpenSHC::Commands::M_MAPPER_KEEP2) || (command == OpenSHC::Commands::M_MAPPER_KEEP3)) { + uVar9 = (ushort)(this->mapOrientation == 2); + iVar15 = (MVar10 - OpenSHC::Commands::M_MAPPER_KEEP1) * 0x60; + iVar3 = DAT_ViewportRenderState::instance + .translationMatrix[*(int*)((int)DAT_TerrainDefinedData::ptr + iVar15 + 0x268) + y] + .addXgetTile + + *(int*)((int)DAT_TerrainDefinedData::ptr + iVar15 + 0x264) + iVar1; + this->ConstructionGFXLayer[iVar3] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x521 + uVar9; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar3 = DAT_ViewportRenderState::instance + .translationMatrix[*(int*)((int)DAT_TerrainDefinedData::ptr + iVar15 + 0x270) + y] + .addXgetTile + + *(int*)((int)DAT_TerrainDefinedData::ptr + iVar15 + 0x26c) + iVar1; + this->ConstructionGFXLayer[iVar3] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x529 + uVar9; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar3] = this->MiscDisplayLayer[iVar3] | 0x8000; + } + iVar15 = DAT_ViewportRenderState::instance + .translationMatrix[*(int*)((int)DAT_TerrainDefinedData::ptr + iVar15 + 0x278) + y] + .addXgetTile + + *(int*)((int)DAT_TerrainDefinedData::ptr + iVar15 + 0x274) + iVar1; + this->ConstructionGFXLayer[iVar15] + = (short)GMTotalPicturesProcessed::instance[0x34] + 0x525 + uVar9; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar15] = this->MiscDisplayLayer[iVar15] | 0x8000; + } + iVar13 = (MVar10 - OpenSHC::Commands::M_MAPPER_KEEP1) * 0x20; + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar13 + 900); + iVar3 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar13 + 0x388); + iVar4 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar4, 7); + iVar5 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y + iVar3] + .addXgetTile + + this->buildingX + iVar1 + iVar15; + this->ConstructionGFXLayer[iVar5] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[6] + 0x17; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar5] = this->MiscDisplayLayer[iVar5] | 0x8000; + } + iVar4 = iVar4 + 1; + } while (iVar4 < this->constructionTileCount); + iVar15 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar13 + 0x3e4); + iVar3 = *(int*)((int)DAT_TerrainDefinedData::ptr + iVar13 + 1000); + iVar13 = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + iVar13, 5); + iVar4 = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + iVar3 + y] + .addXgetTile + + this->buildingX + iVar15 + iVar1; + this->ConstructionGFXLayer[iVar4] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[7] + 0x19; + if (iVar14 != 0) { + this->MiscDisplayLayer[iVar4] = this->MiscDisplayLayer[iVar4] | 0x8000; + } + iVar13 = iVar13 + 1; + } while (iVar13 < this->constructionTileCount); + return iVar4; + } + } + return iVar15; + } + } while (true); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setLand.cpp b/src/OpenSHC/Map/TileMapState/setLand.cpp new file mode 100644 index 000000000..8b4a4bf34 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setLand.cpp @@ -0,0 +1,91 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_HIGH; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_MEDIUM; + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CLIMBABLE; + using OpenSHC::Map::LogicHelpers::L_DEFAULT_EARTH_OR_TEXTURE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_APPLE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_DAIRY; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_HOP; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_WHEAT; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + related to setting oasis type decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00502390 + void TileMapState::setLand(int tile, uint y, uint brushType) + { + int baseTile = tile; + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[brushType]; + uint baseY = y; + int index = 0; + if (brushSize > 0) { + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, &tile, (int*)&y, baseTile, baseY); + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) == 0 + && (this->LogicLayer[tile] & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 + && this->BuildingLayer[tile] == 0 && (this->LogicLayer[tile] & L_TREE) == 0 + && ((this->LogicLayer[tile] & L_ROCKY) == 0 || this->OrganismLayer[tile] == 0)) { + if (this->DefaultHeightLayer[tile] < 8) { + this->DefaultHeightLayer[tile] = 8; + } + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffdf; + if ((this->LogicLayer[tile] & (L_MOAT | L_MOAT_DUG_OR_PLANNED)) != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatDataAtTile, this)(tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[tile]] + .addXgetTile, + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]); + this->LogicLayer[tile] = this->LogicLayer[tile] & ~(L_MOAT | L_MOAT_DUG_OR_PLANNED); + } + this->LogicLayer[tile] = this->LogicLayer[tile] + & (L_STOCKPILEUnk | L_PLAIN1_AND_FARM | L_BORDER | L_BORDER_EDGE | L_BUILDING + | L_MOAT_DUG_OR_PLANNED | L_DEFAULT_EARTH_OR_TEXTURE | L_UNKNOWN_WALL_RELATED | L_CLIMBABLE + | L_FARM_FIELD_WHEAT | L_FARM_FIELD_HOP | L_FARM_FIELD_APPLE | L_FARM_FIELD_DAIRY + | L_KEEP_NON_MANOR_HOUSE | L_MOAT); + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xf83f; + this->Logic2Layer[tile] = 0; + if (this->HeightLayer[tile] == 80) { + this->Logic2Layer[tile] = L2_PLATEAU_MEDIUM; + } else if (this->HeightLayer[tile] == 130) { + this->Logic2Layer[tile] = L2_PLATEAU_HIGH; + } + this->WallOwnerLayer[tile] = 0; + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + } + index++; + } while (index < brushSize); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setMapRotation.cpp b/src/OpenSHC/Map/TileMapState/setMapRotation.cpp new file mode 100644 index 000000000..c6e48ec9f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setMapRotation.cpp @@ -0,0 +1,20 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F70E0 + void TileMapState::setMapRotation(undefined4 newRotation) + { + this->DAT_FutureMapOrientation = newRotation; + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setMapSize.cpp b/src/OpenSHC/Map/TileMapState/setMapSize.cpp new file mode 100644 index 000000000..9e6b379e6 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setMapSize.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F6F90 + void TileMapState::setMapSize(int size) + { + int index = 0; + while (size <= DAT_TerrainDefinedData::instance.MapSizes[index]) { + index++; + } + + if (DAT_TerrainDefinedData::instance.MapSizes[index] == -1) { + this->mapSize = DAT_TerrainDefinedData::instance.MapSizes[0]; + return; + } + this->mapSize = DAT_TerrainDefinedData::instance.MapSizes[index]; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setMiscDisplayLayer.cpp b/src/OpenSHC/Map/TileMapState/setMiscDisplayLayer.cpp new file mode 100644 index 000000000..2d33c4ad8 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setMiscDisplayLayer.cpp @@ -0,0 +1,127 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB0C0 + void TileMapState::setMiscDisplayLayer(int buildingID) + { + int rotation = this->mapOrientation / 2; + if (DAT_BuildingsState::instance.buildings[buildingID].unknownStockpileOrSignpostRelated == 0) { + return; + } + + int x = (short)DAT_BuildingsState::instance.buildings[buildingID].x; + int y = (short)DAT_BuildingsState::instance.buildings[buildingID].y; + uint size = DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + OpenSHC::Map::Buildings::BuildingTypeShort type = DAT_BuildingsState::instance.buildings[buildingID].buildingType; + int typeInt = (short)type; + int offsetIndex = rotation + size * 4; + int workerTile = DAT_TerrainDefinedData::instance.orientationRelativeBuildingTileOffset1[offsetIndex]; + int displayTile = DAT_TerrainDefinedData::instance.orientationRelativeBuildingTileOffset2[offsetIndex]; + int extraTile = -1; + switch (typeInt) { + case OpenSHC::Map::Buildings::BT_IRONMINE: + case OpenSHC::Map::Buildings::BT_PITCHRIG: + case OpenSHC::Map::Buildings::BT_HUNTERSHUT: + case OpenSHC::Map::Buildings::BT_BLACKSMITH: + case OpenSHC::Map::Buildings::BT_BREWERY: + case OpenSHC::Map::Buildings::BT_OILSMELTER: + case OpenSHC::Map::Buildings::BT_STABLES: + case OpenSHC::Map::Buildings::BT_MANORHOUSE: + case OpenSHC::Map::Buildings::BT_KEEPFOUR: + case OpenSHC::Map::Buildings::BT_KEEPFIVE: + case OpenSHC::Map::Buildings::BT_TUNNEL: + case OpenSHC::Map::Buildings::BT_FIREBALLISTA: + case OpenSHC::Map::Buildings::BT_MAYPOLE: + case OpenSHC::Map::Buildings::BT_CATAPULT: + case OpenSHC::Map::Buildings::BT_TREBUCHET: + case OpenSHC::Map::Buildings::BT_BATTERINGRAM: + case OpenSHC::Map::Buildings::BT_SIEGETOWER: + case OpenSHC::Map::Buildings::BT_SHIELD: + case OpenSHC::Map::Buildings::BT_UNKNOWN4: + /* these carry the worker on the display tile and the display on the worker tile */ + workerTile = displayTile; + displayTile = DAT_TerrainDefinedData::instance.orientationRelativeBuildingTileOffset1[offsetIndex]; + break; + case OpenSHC::Map::Buildings::BT_DRAWBRIDGE: + switch (rotation) { + case 0: + displayTile = 0x13; + extraTile = 6; + break; + case 1: + displayTile = 0x15; + extraTile = 8; + break; + case 2: + displayTile = 5; + extraTile = 0x12; + break; + case 3: + displayTile = 3; + extraTile = 0x10; + } + break; + case OpenSHC::Map::Buildings::BT_GALLOWS: + case OpenSHC::Map::Buildings::BT_GIBBET: + workerTile = -1; + } + + if ((int)size < 3 && type != OpenSHC::Map::Buildings::BT_GIBBET && type != OpenSHC::Map::Buildings::BT_GALLOWS) { + this->MiscDisplayLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x] + = this->MiscDisplayLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x] | 4; + return; + } + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + index++; + this->MiscDisplayLayer[DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + x + this->buildingX] + = this->MiscDisplayLayer[DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + x + + this->buildingX] + & 0xfff3; + } while (index < this->constructionTileCount); + + index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)(index, size); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + x + this->buildingX; + if (typeInt == OpenSHC::Map::Buildings::BT_GRANARY) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 8; + } else if (typeInt == OpenSHC::Map::Buildings::BT_ARMORY) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 8; + } else if (index == workerTile) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 8; + /* fixme: xPosition and the field after it are written as one int */ + *(int*)&DAT_BuildingsState::instance.buildings[buildingID].xPosition = tile; + } + if (index == displayTile) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 4; + DAT_BuildingsState::instance.buildings[buildingID].miscDisplayLayerTile = tile; + } + if (index == extraTile) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0xc; + DAT_BuildingsState::instance.buildings[buildingID].miscDisplayLayerTile = tile; + } + index++; + } while (index < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setMoatOwnerForAllMatching.cpp b/src/OpenSHC/Map/TileMapState/setMoatOwnerForAllMatching.cpp new file mode 100644 index 000000000..d68ba14a4 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setMoatOwnerForAllMatching.cpp @@ -0,0 +1,23 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500750 + void TileMapState::setMoatOwnerForAllMatching(int param_1, undefined4 param_2) + { + for (int moatID = 1; moatID < 16000; moatID++) { + if (this->moats[moatID].owner != '\0' && this->moats[moatID].owner == param_1) { + this->moats[moatID].owner = (char)param_2; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setMoatVisualStateAtTile.cpp b/src/OpenSHC/Map/TileMapState/setMoatVisualStateAtTile.cpp new file mode 100644 index 000000000..8786e5c85 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setMoatVisualStateAtTile.cpp @@ -0,0 +1,25 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500720 + void TileMapState::setMoatVisualStateAtTile(int tile, undefined4 zeroOrTwo) + { + int iVar1; + iVar1 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(tile); + if (iVar1 != 0) { + this->moats[iVar1].zeroOrTwo = (short)zeroOrTwo; + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setSignpostDistanceForCampaignMission.cpp b/src/OpenSHC/Map/TileMapState/setSignpostDistanceForCampaignMission.cpp new file mode 100644 index 000000000..4a239a3a7 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setSignpostDistanceForCampaignMission.cpp @@ -0,0 +1,46 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode2.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_MapPropertiesState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Game::GameMode2; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500BB0 + void TileMapState::setSignpostDistanceForCampaignMission() + { + DAT_GameState::instance.mapAndTime.unk_signpostDistance = 30; + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_CAMPAIGN_MISSION) { + if (DAT_GameCore::instance.missionNumber1to20 <= 2) { + DAT_GameState::instance.mapAndTime.unk_signpostDistance = 0; + return; + } + if (DAT_GameCore::instance.missionNumber1to20 <= 4) { + DAT_GameState::instance.mapAndTime.unk_signpostDistance = 15; + return; + } + if (DAT_GameCore::instance.missionNumber1to20 == 6) { + DAT_GameState::instance.mapAndTime.unk_signpostDistance = 37; + } + return; + } + + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_ECONOMIC_CAMPAIGN_SH1 + || (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_BUILDERUnk + && (int)DAT_MapPropertiesState::instance.SEC_U3_MapType2_1 >= 2)) { + DAT_GameState::instance.mapAndTime.unk_signpostDistance = 7; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setTerrain.cpp b/src/OpenSHC/Map/TileMapState/setTerrain.cpp new file mode 100644 index 000000000..3b1a9bae2 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setTerrain.cpp @@ -0,0 +1,447 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +// mask for the handwritten neighbour-flag macro; MASM has no "|", so this must be a literal +#define NEIGHBOUR_FLAGS_MASK_L2_BEACH 0x20 // L2_BEACH + +#pragma optimize("", off) + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::LogicHelpers::L2_BEACH; + using OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + using OpenSHC::Map::LogicHelpers::L2_MOAT_UNDUG; + using OpenSHC::Map::LogicHelpers::L2_NONE; + using OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS; + using OpenSHC::Map::LogicHelpers::L2_SCRUB; + using OpenSHC::Map::LogicHelpers::L2_STONES_OR_DRIVEN_SANDUnk; + using OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB; + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_NONE; + using OpenSHC::Map::LogicHelpers::L_OIL; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_UNNAMED_0x40; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00512940 + void TileMapState::setTerrain(int playerID, int tile, uint yParam, uint brushType, Logic1 flags1, Logic2 flags2) + { + /* + Holds one expansion of the handwritten neighbour-flag macro (see NeighbourFlagsAsm.hpp), + so the original was built without optimisation and the pragma above is needed to reproduce + its frame-pointer, reload-per-access shape. The locals are declared together because at + /Od the stack slots are observable and MSVC assigns them per function, not per scope. + + L_UNNAMED_0x40 is the one Logic1 bit the generated enum has no name for; the original both + tests flags1 against it and sets it on the tile. + */ + int baseTile; + uint baseY; + uint x; + int moatEnemyRange; + int moatOpponentRange; + int index; + int size; + + size = DAT_TerrainDefinedData::instance.BrushSizeArray[brushType]; + baseTile = tile; + baseY = yParam; + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + moatEnemyRange = 0xf; + moatOpponentRange = 7; + } else { + moatEnemyRange = 0x1e; + moatOpponentRange = 0x1e; + } + + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_EDITOR && flags1 != L_MOAT + && (flags2 == L2_EARTH_AND_STONES || flags2 == L2_OASIS_GRASS || flags2 == L2_BEACH + || flags2 == L2_STONES_OR_DRIVEN_SANDUnk || flags2 == L2_SCRUB || flags2 == L2_THICK_SCRUB)) { + /* + this functions processes all oasis tiles + */ + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setLand, this)(tile, yParam, brushType); + } + if (flags1 == L_MOAT) { + size = 9; + } + + for (index = 0; index < size; index++) { + if (flags1 == L_MOAT) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 0, index, &tile, (int*)&yParam, baseTile, baseY); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)( + 1, index, &tile, (int*)&yParam, baseTile, baseY); + } + if ((this->LogicLayer[tile] & L_BORDER) != 0) { + continue; + } + + if (flags1 == L_SEA) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || this->OrganismLayer[tile] != 0) { + continue; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PEBBLES; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_SEA; + this->HeightLayer[tile] = 0; + this->DefaultHeightLayer[tile] = 0; + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + } else if (flags1 == L_RIVER || flags1 == L_FORD) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || this->OrganismLayer[tile] != 0) { + continue; + } + if (flags2 != L2_NONE && (this->LogicLayer[tile] & L_RIVER) == 0) { + continue; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + if (flags1 == L_RIVER) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + } else { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PEBBLES; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + if (flags2 != L2_BEACH) { + this->WallGFXLayer[tile] = this->RandomLayer[tile] & 7; + } + if (flags2 == L2_OASIS_GRASS) { + this->Logic2Layer[tile] = L2_OASIS_GRASS; + } + if (flags2 == L2_BEACH) { + this->Logic2Layer[tile] = L2_BEACH; + this->WallGFXLayer[tile] = ((ushort)DAT_GameCore::instance.mapTimeInTicks & 8) + + this->WallGFXLayer[tile] * 0x20 + (this->RandomLayer[tile] & 7) * 0x400; + } + this->LogicLayer[tile] = this->LogicLayer[tile] | flags1; + if (this->HeightLayer[tile] > 7) { + this->HeightLayer[tile] = this->DefaultHeightLayer[tile] - 8; + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + } + } else if (flags1 == L_ROCKY) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_RIVER) != 0 || this->OrganismLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] & L_SEA) == 0) { + if (this->HeightLayer[tile] < 8) { + this->HeightLayer[tile] = 8; + this->DefaultHeightLayer[tile] = 8; + } + } else { + this->HeightLayer[tile] = 0; + this->DefaultHeightLayer[tile] = 0; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PEBBLES; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_ROCKY; + } else if (flags1 == L_IRON) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_SEA) != 0 || (this->LogicLayer[tile] & L_RIVER) != 0 + || this->OrganismLayer[tile] != 0) { + continue; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PEBBLES; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_IRON; + } else if (flags1 == L_PEBBLES) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_RIVER) != 0 || this->OrganismLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] & L_SEA) == 0) { + if (this->HeightLayer[tile] < 8) { + this->HeightLayer[tile] = 8; + this->DefaultHeightLayer[tile] = 8; + } + } else { + this->HeightLayer[tile] = 0; + this->DefaultHeightLayer[tile] = 0; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_PEBBLES; + } else if (flags1 == L_MARSH) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_RIVER) != 0 || this->OrganismLayer[tile] != 0) { + continue; + } + this->HeightLayer[tile] = 8; + this->DefaultHeightLayer[tile] = 8; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_MARSH; + } else if (flags1 == L_OIL) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_RIVER) != 0 || this->OrganismLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] & L_MARSH) == 0) { + continue; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_OIL; + } else if (flags1 == L_MOAT) { + if (this->OrganismLayer[tile] != 0 + || (this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_SEA) != 0 || (this->LogicLayer[tile] & L_RIVER) != 0 + || (this->LogicLayer[tile] & L_FORD) != 0 || (this->LogicLayer[tile] & L_ROCKY) != 0 + || (this->LogicLayer[tile] & L_BOULDERS) != 0 || (this->LogicLayer[tile] & L_IRON) != 0 + || (this->LogicLayer[tile] & L_MARSH) != 0 || (this->LogicLayer[tile] & L_PLAIN1_AND_FARM) != 0) { + continue; + } + x = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::computeTileXOffset, + DAT_ViewportRenderState::ptr)(tile, yParam); + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR + && DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT + && MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, + DAT_PathFindingState::ptr)(playerID, x, yParam, moatEnemyRange) + != FALSE) { + continue; + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, + DAT_PathFindingState::ptr)(playerID, x, yParam, moatOpponentRange, -1, -1, + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getCastleBuildRangeForMapSize, this)() + + 5) + != 0) { + continue; + } + } + if ((this->LogicLayer[tile] & L_MOAT) != 0 && flags2 != L2_MOAT_UNDUG) { + continue; + } + if (this->HeightLayer[tile] >= 0xd) { + continue; + } + if (flags2 == L2_NONE) { + if ((this->LogicLayer[tile] & L_MOAT_DUG_OR_PLANNED) == 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::createMoatData, this)( + playerID, x, yParam, 0) + != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_MOAT_DUG_OR_PLANNED; + } + } else if (flags2 == L2_SCRUB) { + if ((this->LogicLayer[tile] & L_MOAT_DUG_OR_PLANNED) != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT_DUG_OR_PLANNED; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatDataAtTile, this)(x, yParam); + } + } else { + this->HeightLayer[tile] = 8; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + if (flags2 == L2_EARTH_AND_STONES) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::createMoatData, this)( + playerID, x, yParam, 1) + != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT_DUG_OR_PLANNED; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_MOAT; + this->HeightLayer[tile] = 0; + } + } else if (flags2 == L2_MOAT_UNDUG) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoatDataAtTile, this)(x, yParam); + this->HeightLayer[tile] = 8; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT_DUG_OR_PLANNED; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + } + } + } else if (flags2 != L2_EARTH_AND_STONES && flags2 != L2_OASIS_GRASS && flags2 != L2_BEACH + && flags2 != L2_STONES_OR_DRIVEN_SANDUnk && flags2 != L2_SCRUB && flags2 != L2_THICK_SCRUB) { + if (flags1 == L_BOULDERS) { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & L_RIVER) != 0 || this->OrganismLayer[tile] != 0) { + continue; + } + if ((this->LogicLayer[tile] & L_SEA) == 0) { + if (this->HeightLayer[tile] < 8) { + this->HeightLayer[tile] = 8; + this->DefaultHeightLayer[tile] = 8; + } + } else { + this->HeightLayer[tile] = 0; + this->DefaultHeightLayer[tile] = 0; + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PEBBLES; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + this->Logic2Layer[tile] = 0; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BOULDERS; + } else if (flags1 == L_UNNAMED_0x40) { + if ((this->LogicLayer[tile] & L_SEA) == 0 + || (this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || this->OrganismLayer[tile] != 0) { + continue; + } + this->LogicLayer[tile] = this->LogicLayer[tile] | L_UNNAMED_0x40; + } + } else { + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + || this->BuildingLayer[tile] != 0 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 + || (this->LogicLayer[tile] & (L_SEA | L_RIVER | L_FORD | L_MARSH | L_MOAT)) != 0 + || this->OrganismLayer[tile] != 0) { + continue; + } + if (flags2 == L2_EARTH_AND_STONES) { + if ((this->LogicLayer[tile] & L_IRON) != 0 || (this->LogicLayer[tile] & L_BOULDERS) != 0 + || (this->Logic2Layer[tile] & L2_BEACH) != 0) { + continue; + } + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(1, NEIGHBOUR_FLAGS_MASK_L2_BEACH) + if (this->bitFlag != 0) { + continue; + } + } + + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffdf; + if (flags2 != L2_STONES_OR_DRIVEN_SANDUnk && flags2 != L2_SCRUB && flags2 != L2_THICK_SCRUB + && flags2 != L2_EARTH_AND_STONES && flags2 != L2_OASIS_GRASS) { + if (flags2 == L2_BEACH) { + this->HeightLayer[tile] = 0; + this->DefaultHeightLayer[tile] = 0; + } else { + this->HeightLayer[tile] = 8; + this->DefaultHeightLayer[tile] = 8; + } + } + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_SEA; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_UNNAMED_0x40; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_RIVER; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_FORD; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_ROCKY; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BOULDERS; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_IRON; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MARSH; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_OIL; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_MOAT; + if (flags2 != L2_EARTH_AND_STONES) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN2_AND_PITCH; + } + this->Logic2Layer[tile] = (byte)flags2; + } + + if (flags1 != L_NONE) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(yParam, tile); + } + } + } + +} +} + +#pragma optimize("", on) diff --git a/src/OpenSHC/Map/TileMapState/setXYBasedOnMoatID.cpp b/src/OpenSHC/Map/TileMapState/setXYBasedOnMoatID.cpp new file mode 100644 index 000000000..761e2be5c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setXYBasedOnMoatID.cpp @@ -0,0 +1,57 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005003D0 + int TileMapState::setXYBasedOnMoatID(int moatID, int param_2, uint x, uint y) + { + if (x > 399 || y > 399) { + return 0; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return 0; + } + + int tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + if (param_2 == 1) { + this->ALG_MoatXResult = this->moats[moatID].x; + this->ALG_MoatYResult = this->moats[moatID].y; + return DAT_ViewportRenderState::instance.translationMatrix[this->ALG_MoatYResult].addXgetTile + + this->ALG_MoatXResult; + } + if (param_2 != 2) { + return 0; + } + + for (int direction = 0; direction < 8; direction++) { + int neighbour + = this->directionTranslationMatrix[this->moats[moatID].y][direction] + this->moats[moatID].tile; + if ((uint)(this->HeightLayer[tile] + 0x10) < (uint)this->HeightLayer[neighbour]) { + continue; + } + if (this->PathConnectionLayer[neighbour] != this->PathConnectionLayer[tile]) { + continue; + } + + this->someMoatTile = this->moats[moatID].tile; + this->ALG_MoatXResult = this->moats[moatID].x + + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset; + this->ALG_MoatYResult = this->moats[moatID].y + + DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset; + return neighbour; + } + return 0; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setupAllMapSections.cpp b/src/OpenSHC/Map/TileMapState/setupAllMapSections.cpp new file mode 100644 index 000000000..1f333bc72 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setupAllMapSections.cpp @@ -0,0 +1,164 @@ + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_WallAndPitchState.hpp" +#include "OpenSHC/Globals/DAT_WindowAndDirectDraw.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00512100 + void TileMapState::setupAllMapSections() + { + this->temporaryTerrainTypeIndex = 0; + this->SEC_Section1052 = 0; + this->SEC_Section1053 = 0; + this->SEC_Section1054 = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupTileMapSections, this)(); + DAT_WallAndPitchState::instance.countdown = 0; + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::setTileSystemMemoryLookupArrays, DAT_ViewportRenderState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::tweakValidTilesToExcludeMapBorders, DAT_ViewportRenderState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::setupViewport, DAT_ViewportRenderState::ptr)(0, 0, + DAT_WindowAndDirectDraw::instance.resolutionX, DAT_WindowAndDirectDraw::instance.resolutionY - 128); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupLogicalMapBorders, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateShowHiLayerOrResetChangedLayer, this)(); + + /* the border tiles get their own graphic, everything else the plain one */ + for (int tile = 0; tile < 80400; tile += 6) { + if (this->LogicLayer[tile + 0] == L_BORDER) { + this->GfxLayer[tile + 0] = (ushort)GMTotalPicturesProcessed::instance[2]; + } else if (this->LogicLayer[tile + 0] == L_BORDER_EDGE) { + this->GfxLayer[tile + 0] = (ushort)GMTotalPicturesProcessed::instance[2] + 1; + } else { + this->GfxLayer[tile + 0] = (ushort)GMTotalPicturesProcessed::instance[2] + 2; + } + if (this->LogicLayer[tile + 1] == L_BORDER) { + this->GfxLayer[tile + 1] = (ushort)GMTotalPicturesProcessed::instance[2]; + } else if (this->LogicLayer[tile + 1] == L_BORDER_EDGE) { + this->GfxLayer[tile + 1] = (ushort)GMTotalPicturesProcessed::instance[2] + 1; + } else { + this->GfxLayer[tile + 1] = (ushort)GMTotalPicturesProcessed::instance[2] + 2; + } + if (this->LogicLayer[tile + 2] == L_BORDER) { + this->GfxLayer[tile + 2] = (ushort)GMTotalPicturesProcessed::instance[2]; + } else if (this->LogicLayer[tile + 2] == L_BORDER_EDGE) { + this->GfxLayer[tile + 2] = (ushort)GMTotalPicturesProcessed::instance[2] + 1; + } else { + this->GfxLayer[tile + 2] = (ushort)GMTotalPicturesProcessed::instance[2] + 2; + } + if (this->LogicLayer[tile + 3] == L_BORDER) { + this->GfxLayer[tile + 3] = (ushort)GMTotalPicturesProcessed::instance[2]; + } else if (this->LogicLayer[tile + 3] == L_BORDER_EDGE) { + this->GfxLayer[tile + 3] = (ushort)GMTotalPicturesProcessed::instance[2] + 1; + } else { + this->GfxLayer[tile + 3] = (ushort)GMTotalPicturesProcessed::instance[2] + 2; + } + if (this->LogicLayer[tile + 4] == L_BORDER) { + this->GfxLayer[tile + 4] = (ushort)GMTotalPicturesProcessed::instance[2]; + } else if (this->LogicLayer[tile + 4] == L_BORDER_EDGE) { + this->GfxLayer[tile + 4] = (ushort)GMTotalPicturesProcessed::instance[2] + 1; + } else { + this->GfxLayer[tile + 4] = (ushort)GMTotalPicturesProcessed::instance[2] + 2; + } + if (this->LogicLayer[tile + 5] == L_BORDER) { + this->GfxLayer[tile + 5] = (ushort)GMTotalPicturesProcessed::instance[2]; + } else if (this->LogicLayer[tile + 5] == L_BORDER_EDGE) { + this->GfxLayer[tile + 5] = (ushort)GMTotalPicturesProcessed::instance[2] + 1; + } else { + this->GfxLayer[tile + 5] = (ushort)GMTotalPicturesProcessed::instance[2] + 2; + } + } + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ShortValue, DAT_LowLevelMemory::ptr)(80400, 1, this->PillarGFXLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(80400, '\b', this->HeightLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\b', this->DefaultHeightLayer); + DAT_PathFindingState::instance.searchGeneration = 1; + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(160800, '\0', DAT_TileMapState::instance.WalkLayer); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::setChangedLayerZeroBasedOn40x40Layer, DAT_PathFindingState::ptr)(0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateLogicalTileMapRelatedSections, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateGfxLayer, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateGFXLayers, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetMoatArray, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::resetPitchDitchArray, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::toggleFlatView, this)(0); + DAT_TileMapState::instance.field95_0x5548d0 = 0; + DAT_TileMapState::instance.flatViewToggleValue2 = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::clearBuildings, DAT_BuildingsState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::clearRocksAndTrees, DAT_LandscapeState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::resetWind, DAT_LandscapeState::ptr)(); + MACRO_CALL(OpenSHC::Global_Func::DoNothing)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::clearAllUnits, DAT_UnitsState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::clearAllTribes, DAT_TribesState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::clearMapAndTimeAndPlayerData, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::resetVariousCountsAndStatisticsAndStartGoodsAndResources, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::clearCurrentResourcesAndStrongWalls, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::initializeGameStateAfterMapLoad, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::fillWith0xFF, DAT_GameState::ptr)(); + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::clearAnimalSpawnLocationsUnk, DAT_TribesState::ptr)(); + + for (int tribe = 0; tribe < 1000; tribe++) { + DAT_AICState::instance.tribeIDArray[tribe] = 0; + DAT_AICState::instance.tribeUIDArray[tribe] = 0; + } + MACRO_CALL(OpenSHC::Global_Func::DoNothing)(); + MACRO_CALL(OpenSHC::Global_Func::DoNothing)(); + MACRO_CALL_MEMBER( + OpenSHC::Map::Entities::EntityState_Func::clearEntityArrayAndSeagullArray, DAT_EntityState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setChangedLayerToThreeAndMapping0x40x40, this)(); + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + this->forceUpdateTextureTilemap = 1; + this->forceUpdateGFXLayers = 1; + this->forceUpdateMacroLayerFlag = 1; + this->field68_0x55487c = 200; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageLayerForEachBuildingAtEachTile, DAT_PathFindingState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateSeparateAreaTileMap, DAT_PathFindingState::ptr)(1); + this->forceUpdateMacroLayerFlag = 1; + this->field68_0x55487c = 500; + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::setTileColorsDependingOnMapSize, DAT_MinimapViewState::ptr)(0, 100); + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::setMapPropertyDependingOnMapSize, DAT_MinimapViewState::ptr)(0, 100); + DAT_GameCore::instance.currentlyInGameUnk_0xa4 = FALSE; + MACRO_CALL(OpenSHC::Map_Func::ResetSomeValuesFunctionUnk)(); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setupBuildingSizeIndexMapping.cpp b/src/OpenSHC/Map/TileMapState/setupBuildingSizeIndexMapping.cpp new file mode 100644 index 000000000..136526442 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setupBuildingSizeIndexMapping.cpp @@ -0,0 +1,33 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00503720 + void TileMapState::setupBuildingSizeIndexMapping() + { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(1); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(2); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(3); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(4); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(5); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(6); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(7); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(8); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(9); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(10); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(11); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(12); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setupBuildingSizeIndexMappingForBuildingWithSize, this)(13); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setupBuildingSizeIndexMappingForBuildingWithSize.cpp b/src/OpenSHC/Map/TileMapState/setupBuildingSizeIndexMappingForBuildingWithSize.cpp new file mode 100644 index 000000000..592db8e37 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setupBuildingSizeIndexMappingForBuildingWithSize.cpp @@ -0,0 +1,135 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingSizeIndexMapping.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9590 + void TileMapState::setupBuildingSizeIndexMappingForBuildingWithSize(int buildingSize) + { + int tileCount = buildingSize * buildingSize; + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 4056, '\0', DAT_BuildingSizeIndexMapping::instance + buildingSize); + if (buildingSize <= 1) { + return; + } + + /* pass 0: the unrotated footprint, in reading order */ + for (int y = 0; y < buildingSize; y++) { + for (int x = 0; x < buildingSize; x++) { + DAT_BuildingSizeIndexMapping::instance[buildingSize][y * buildingSize + x][0] = x; + DAT_BuildingSizeIndexMapping::instance[buildingSize][y * buildingSize + x][1] = y; + } + } + + /* passes 1 to 4: the same tiles walked diagonally, one pass per rotation */ + int last = tileCount - 1; + int x = 0; + int y = 0; + int run = 1; + int index = last; + for (int diagonal = 0; diagonal < buildingSize * 2 - 1; diagonal++) { + for (int step = 0; step < run; step++) { + DAT_BuildingSizeIndexMapping::instance[buildingSize][y * buildingSize + x][2] = index; + index--; + if (step < run - 1) { + x--; + y++; + } + } + if (diagonal < buildingSize - 1) { + run++; + x = diagonal + 1; + y = 0; + } else { + run--; + x = buildingSize - 1; + y = (1 - (buildingSize - 1)) + diagonal; + } + } + + x = buildingSize - 1; + y = 0; + run = 1; + index = last; + for (int diagonal = 0; diagonal < buildingSize * 2 - 1; diagonal++) { + for (int step = 0; step < run; step++) { + DAT_BuildingSizeIndexMapping::instance[buildingSize][y * buildingSize + x][3] = index; + index--; + if (step < run - 1) { + x--; + y--; + } + } + if (diagonal < buildingSize - 1) { + run++; + x = buildingSize - 1; + y = diagonal + 1; + } else { + run--; + x = (buildingSize - 1) + (buildingSize - 2) - diagonal; + y = buildingSize - 1; + } + } + + x = buildingSize - 1; + y = buildingSize - 1; + run = 1; + index = last; + for (int diagonal = 0; diagonal < buildingSize * 2 - 1; diagonal++) { + for (int step = 0; step < run; step++) { + DAT_BuildingSizeIndexMapping::instance[buildingSize][y * buildingSize + x][4] = index; + index--; + if (step < run - 1) { + x++; + y--; + } + } + if (diagonal < buildingSize - 1) { + run++; + x = buildingSize - 2 - diagonal; + y = buildingSize - 1; + } else { + run--; + x = 0; + y = (buildingSize - 2 - diagonal) + (buildingSize - 1); + } + } + + x = 0; + y = buildingSize - 1; + run = 1; + index = last; + for (int diagonal = 0; diagonal < buildingSize * 2 - 1; diagonal++) { + for (int step = 0; step < run; step++) { + DAT_BuildingSizeIndexMapping::instance[buildingSize][y * buildingSize + x][5] = index; + index--; + if (step < run - 1) { + x++; + y++; + } + } + if (diagonal < buildingSize - 1) { + run++; + x = 0; + y = buildingSize - 2 - diagonal; + } else { + run--; + x = (1 - (buildingSize - 1)) + diagonal; + y = 0; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setupLogicalMapBorders.cpp b/src/OpenSHC/Map/TileMapState/setupLogicalMapBorders.cpp new file mode 100644 index 000000000..f5d71273c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setupLogicalMapBorders.cpp @@ -0,0 +1,48 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F6C60 + void TileMapState::setupLogicalMapBorders() + { + int index = 0; + for (int rowWidth = 0; rowWidth < 400; rowWidth += 2) { + this->LogicLayer[index] = L_BORDER; + index++; + if (rowWidth > 0) { + this->LogicLayer[index] = L_BORDER_EDGE; + } + index = index + rowWidth; + if (rowWidth > 0) { + this->LogicLayer[index - 1] = L_BORDER_EDGE; + } + this->LogicLayer[index] = L_BORDER; + index++; + } + for (int rowWidth = 398; rowWidth >= 0; rowWidth -= 2) { + this->LogicLayer[index] = L_BORDER; + index++; + if (rowWidth > 0) { + this->LogicLayer[index] = L_BORDER_EDGE; + } + index = index + rowWidth; + if (rowWidth > 0) { + this->LogicLayer[index - 1] = L_BORDER_EDGE; + } + this->LogicLayer[index] = L_BORDER; + index++; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setupMovementDirectionTranslationMatrix.cpp b/src/OpenSHC/Map/TileMapState/setupMovementDirectionTranslationMatrix.cpp new file mode 100644 index 000000000..f0383259f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setupMovementDirectionTranslationMatrix.cpp @@ -0,0 +1,80 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F6B20 + void TileMapState::setupMovementDirectionTranslationMatrix() + { + /* + Ghidra rendered the two running values of the first loop as directionTranslationMatrix[199][4] + and [200][5]; in the original they are registers, and rows 199 and 200 are only written by + the block after the loop. Rows 0..198 count up in "step" and down in "counter", rows 201.. count + back down again. + */ + int step = 2; + int counter = 0; + int row = 0; + do { + this->directionTranslationMatrix[row][0] = counter - 1; + this->directionTranslationMatrix[row][3] = step + 2; + this->directionTranslationMatrix[row][4] = step + 1; + this->directionTranslationMatrix[row][1] = counter; + this->directionTranslationMatrix[row][5] = step; + this->directionTranslationMatrix[row][2] = 1; + this->directionTranslationMatrix[row][6] = -1; + this->directionTranslationMatrix[row][7] = counter - 2; + this->yArray1[row] = step; + counter = counter - 2; + step = step + 2; + row++; + } while (counter > -398); + + this->directionTranslationMatrix[199][1] = 2 - step; + this->directionTranslationMatrix[199][4] = step; + this->directionTranslationMatrix[199][6] = -1; + this->directionTranslationMatrix[199][2] = 1; + this->directionTranslationMatrix[199][3] = step + 1; + this->directionTranslationMatrix[199][0] = 1 - step; + this->directionTranslationMatrix[199][7] = -step; + this->directionTranslationMatrix[199][5] = step - 1; + this->yArray1[199] = step; + this->directionTranslationMatrix[200][0] = -step; + this->directionTranslationMatrix[200][1] = 1 - step; + this->directionTranslationMatrix[200][2] = 1; + this->directionTranslationMatrix[200][3] = step; + this->directionTranslationMatrix[200][4] = step - 1; + this->directionTranslationMatrix[200][5] = step - 2; + this->directionTranslationMatrix[200][6] = -1; + this->directionTranslationMatrix[200][7] = -1 - step; + this->yArray1[200] = step; + + if (step - 2 > 0) { + int lowerStep = step - 2; + int lowerCounter = -2 - lowerStep; + int lowerRow = 201; + do { + this->directionTranslationMatrix[lowerRow][0] = lowerCounter + 1; + this->directionTranslationMatrix[lowerRow][1] = lowerCounter + 2; + this->directionTranslationMatrix[lowerRow][4] = lowerStep - 1; + this->directionTranslationMatrix[lowerRow][3] = lowerStep; + this->directionTranslationMatrix[lowerRow][7] = lowerCounter; + this->directionTranslationMatrix[lowerRow][2] = 1; + this->directionTranslationMatrix[lowerRow][5] = lowerStep - 2; + this->directionTranslationMatrix[lowerRow][6] = -1; + this->yArray1[lowerRow] = lowerStep; + lowerStep = lowerStep - 2; + lowerCounter = lowerCounter + 2; + lowerRow++; + } while (lowerStep > 0); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/setupTileMapSections.cpp b/src/OpenSHC/Map/TileMapState/setupTileMapSections.cpp new file mode 100644 index 000000000..eb3b1f4f6 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/setupTileMapSections.cpp @@ -0,0 +1,60 @@ + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00511DF0 + void TileMapState::setupTileMapSections() + { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->GfxLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->AlphaGFXLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->ConstructionGFXLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->PillarGFXLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->WallGFXLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->FloatingLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->UnitLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->EntityLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->EntityLayerLT25); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->BuildingLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x4e840, '\0', this->LogicLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->Logic2Layer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->ChangedLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->OrganismLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->DamageLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->MacroLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->HeightLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->DefaultHeightLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->ShowHiLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x27420, '\0', this->MiscDisplayLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->LuminesenceLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->WallOwnerLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->BuildingWasLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->OccupancyLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->AIZoneLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->AIInfoLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->unitDeathHeatMap); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0x13a10, '\0', this->SEC_TileMap1104); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)(0xb0a90, '\0', this->SEC_PathfindingCostTileMap1105); + + for (int tile = 0; tile < 80400; tile++) { + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + this->RandomLayer[tile] = SEC_RNG::instance.currentNumber2; + } + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::changeMapOrientation, this)(0); + this->DAT_FutureMapOrientation = 8; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/spawnEraserTileEffect.cpp b/src/OpenSHC/Map/TileMapState/spawnEraserTileEffect.cpp new file mode 100644 index 000000000..239f217e1 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/spawnEraserTileEffect.cpp @@ -0,0 +1,68 @@ +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/UI/MinimapViewState.func.hpp" +#include "OpenSHC/Map/Entities/EntityType.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_MinimapViewState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_CRENEL_VARIATIONUnk; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_UNKNOWN_WALL_RELATED; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::Entities::EntityType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9F00 + void TileMapState::spawnEraserTileEffect(undefined4 param_1, int tile) + { + uint y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int rowTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xfbff; + if (this->UnitLayer[tile] != 0) { + return; + } + + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, DAT_EntityState::ptr)(tile); + this->LogicLayer[tile] = this->LogicLayer[tile] + & ~(L_WALL_OR_GATEHOUSE | L_CRENEL | L_BUILDING | L_STAIRS | L_UNKNOWN_WALL_RELATED + | L_CRENEL_VARIATIONUnk); + int microY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] * 8; + int microX + = (tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + * 8; + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, 0, + 0, microX, microY, DAT_TileMapState::instance.DefaultHeightLayer[tile], microX + 1, microY + 1, + DAT_TileMapState::instance.DefaultHeightLayer[tile], (EntityType)0x1e, 0); + this->BuildingWasLayer[tile] = 0; + this->DamageLayer[tile] = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(9, tile - rowTile, y); + MACRO_CALL_MEMBER(OpenSHC::UI::MinimapViewState_Func::triggerMinimapRedraw, DAT_MinimapViewState::ptr)(1); + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + DAT_TileMapState::instance.field204_0x554a30 = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/spawnFloatingNumberAroundTile.cpp b/src/OpenSHC/Map/TileMapState/spawnFloatingNumberAroundTile.cpp new file mode 100644 index 000000000..171b352c0 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/spawnFloatingNumberAroundTile.cpp @@ -0,0 +1,49 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/IO/Graphics/GmID.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FB3F0 + void TileMapState::spawnFloatingNumberAroundTile(int x, int y, int type) + { + int radius = type / 2; + int xOffset = DAT_TerrainDefinedData::instance.field2474_0x2764[radius * 60].x; + if (xOffset == -1) { + return; + } + + uint index = 0; + do { + int yOffset = DAT_TerrainDefinedData::instance.field2474_0x2764[radius * 60 + index].y; + int tile = DAT_ViewportRenderState::instance.translationMatrix[yOffset + (y - radius)].addXgetTile + xOffset + + (x - radius); + if ((this->LogicLayer[tile] & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 + && (index & 1) == 0) { + uint frame + = (this->field161_0x5549c0 + yOffset + xOffset + index + (x - radius) - 1 + (y - radius)) % 16; + MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::createFloatingLayerElement, + DAT_ViewportRenderState::ptr)( + OpenSHC::IO::Graphics::GID_FLOATS_NEW, frame + 0xe5, 16, 14, tile, 0x100021); + } + index++; + xOffset = DAT_TerrainDefinedData::instance.field2474_0x2764[radius * 60 + index].x; + } while (xOffset != -1); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/spreadBrazierPlacementAlgorithm.cpp b/src/OpenSHC/Map/TileMapState/spreadBrazierPlacementAlgorithm.cpp new file mode 100644 index 000000000..0d75c3e2a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/spreadBrazierPlacementAlgorithm.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005011A0 + void TileMapState::spreadBrazierPlacementAlgorithm(int playerID, uint x, uint y) + { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::spreadAlgorithmForFlagsAndBraziersUnk, + DAT_PathFindingState::ptr)(playerID, x, y, 4, 1); + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/spreadFlagPlacementAlgorithm.cpp b/src/OpenSHC/Map/TileMapState/spreadFlagPlacementAlgorithm.cpp new file mode 100644 index 000000000..ca401342c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/spreadFlagPlacementAlgorithm.cpp @@ -0,0 +1,27 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00501180 + int TileMapState::spreadFlagPlacementAlgorithm(int param_1, uint param_2, uint param_3) + { + int iVar1; + iVar1 + = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::spreadAlgorithmForFlagsAndBraziersUnk, + DAT_PathFindingState::ptr)(param_1, param_2, param_3, 4, 0); + return iVar1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/stampBuildingOntoTileMap.cpp b/src/OpenSHC/Map/TileMapState/stampBuildingOntoTileMap.cpp new file mode 100644 index 000000000..f97e85e83 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/stampBuildingOntoTileMap.cpp @@ -0,0 +1,47 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507060 + void TileMapState::stampBuildingOntoTileMap(int param_1, uint param_2, uint param_3, undefined4 param_4, + undefined4 param_5, uint param_6, undefined4 param_7, undefined4 param_8) + { + int buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::setupBuildingData, DAT_BuildingsState::ptr)( + param_1, param_2, param_3, param_8, (BuildingType)(short)param_4, param_6, param_1, (short)param_5); + this->placedBuildingID = buildingID; + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, param_6); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_3].addXgetTile + + this->buildingX + param_2; + if (buildingSizeTileIndex < 36) { + DAT_BuildingsState::instance.buildings[buildingID].tileRefs[buildingSizeTileIndex] = tile; + } + this->HeightLayer[tile] = (byte)param_8; + this->LogicLayer[tile] = this->LogicLayer[tile] | L_BUILDING; + this->BuildingLayer[tile] = (short)buildingID; + buildingSizeTileIndex++; + this->ChangedLayer[tile] = 2; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setMiscDisplayLayer, this)(buildingID); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/storeMinAndMaxHeightOfArea.cpp b/src/OpenSHC/Map/TileMapState/storeMinAndMaxHeightOfArea.cpp new file mode 100644 index 000000000..bfb9c76aa --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/storeMinAndMaxHeightOfArea.cpp @@ -0,0 +1,50 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9980 + void TileMapState::storeMinAndMaxHeightOfArea(uint x, uint y, int buildingWidthAndHeight) + { + this->buildingMinHeight = 1000; + this->buildingMaxHeight = 0; + if (x > 399 || y > 399) { + return; + } + if (DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + return; + } + + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, buildingWidthAndHeight); + uint footprintY = this->buildingY + y; + uint footprintX = this->buildingX + x; + if (footprintX <= 399 && footprintY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[footprintY * 400 + footprintX] != 0) { + uint height + = this->HeightLayer[DAT_ViewportRenderState::instance.translationMatrix[footprintY].addXgetTile + + this->buildingX + x]; + if ((int)height > this->buildingMaxHeight) { + this->buildingMaxHeight = height; + } + if ((int)height < this->buildingMinHeight) { + this->buildingMinHeight = height; + } + } + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/swapMoatOwnership.cpp b/src/OpenSHC/Map/TileMapState/swapMoatOwnership.cpp new file mode 100644 index 000000000..6235ce62f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/swapMoatOwnership.cpp @@ -0,0 +1,28 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500790 + void TileMapState::swapMoatOwnership(int param_1, int param_2) + { + for (int moatID = 1; moatID < 16000; moatID++) { + if (this->moats[moatID].owner == 0) { + continue; + } + if (this->moats[moatID].owner == param_1) { + this->moats[moatID].owner = (char)param_2; + } else if (this->moats[moatID].owner == param_2) { + this->moats[moatID].owner = (char)param_1; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/swapPitchOwnership.cpp b/src/OpenSHC/Map/TileMapState/swapPitchOwnership.cpp new file mode 100644 index 000000000..76073ddfc --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/swapPitchOwnership.cpp @@ -0,0 +1,28 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500940 + void TileMapState::swapPitchOwnership(int param_1, int param_2) + { + for (int pitchDitchID = 1; pitchDitchID < 4000; pitchDitchID++) { + if (this->pitchDitches[pitchDitchID].owner == 0) { + continue; + } + if (this->pitchDitches[pitchDitchID].owner == param_1) { + this->pitchDitches[pitchDitchID].owner = (short)param_2; + } else if (this->pitchDitches[pitchDitchID].owner == param_2) { + this->pitchDitches[pitchDitchID].owner = (short)param_1; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/swapWallOwnership.cpp b/src/OpenSHC/Map/TileMapState/swapWallOwnership.cpp new file mode 100644 index 000000000..bb4afeb59 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/swapWallOwnership.cpp @@ -0,0 +1,70 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9220 + void TileMapState::swapWallOwnership(int playerID, int otherPlayerID) + { + for (int tile = 0; tile < 80400; tile += 6) { + if ((this->LogicLayer[tile + 0] & L_WALL_OR_GATEHOUSE) != 0) { + int owner = (this->WallOwnerLayer[tile + 0] & 7) + 1; + if (owner == playerID) { + this->WallOwnerLayer[tile + 0] = this->WallOwnerLayer[tile + 0] & 0xf8 | (char)otherPlayerID - 1U; + } else if (owner == otherPlayerID) { + this->WallOwnerLayer[tile + 0] = this->WallOwnerLayer[tile + 0] & 0xf8 | (char)playerID - 1U; + } + } + if ((this->LogicLayer[tile + 1] & L_WALL_OR_GATEHOUSE) != 0) { + int owner = (this->WallOwnerLayer[tile + 1] & 7) + 1; + if (owner == playerID) { + this->WallOwnerLayer[tile + 1] = this->WallOwnerLayer[tile + 1] & 0xf8 | (char)otherPlayerID - 1U; + } else if (owner == otherPlayerID) { + this->WallOwnerLayer[tile + 1] = this->WallOwnerLayer[tile + 1] & 0xf8 | (char)playerID - 1U; + } + } + if ((this->LogicLayer[tile + 2] & L_WALL_OR_GATEHOUSE) != 0) { + int owner = (this->WallOwnerLayer[tile + 2] & 7) + 1; + if (owner == playerID) { + this->WallOwnerLayer[tile + 2] = this->WallOwnerLayer[tile + 2] & 0xf8 | (char)otherPlayerID - 1U; + } else if (owner == otherPlayerID) { + this->WallOwnerLayer[tile + 2] = this->WallOwnerLayer[tile + 2] & 0xf8 | (char)playerID - 1U; + } + } + if ((this->LogicLayer[tile + 3] & L_WALL_OR_GATEHOUSE) != 0) { + int owner = (this->WallOwnerLayer[tile + 3] & 7) + 1; + if (owner == playerID) { + this->WallOwnerLayer[tile + 3] = this->WallOwnerLayer[tile + 3] & 0xf8 | (char)otherPlayerID - 1U; + } else if (owner == otherPlayerID) { + this->WallOwnerLayer[tile + 3] = this->WallOwnerLayer[tile + 3] & 0xf8 | (char)playerID - 1U; + } + } + if ((this->LogicLayer[tile + 4] & L_WALL_OR_GATEHOUSE) != 0) { + int owner = (this->WallOwnerLayer[tile + 4] & 7) + 1; + if (owner == playerID) { + this->WallOwnerLayer[tile + 4] = this->WallOwnerLayer[tile + 4] & 0xf8 | (char)otherPlayerID - 1U; + } else if (owner == otherPlayerID) { + this->WallOwnerLayer[tile + 4] = this->WallOwnerLayer[tile + 4] & 0xf8 | (char)playerID - 1U; + } + } + if ((this->LogicLayer[tile + 5] & L_WALL_OR_GATEHOUSE) != 0) { + int owner = (this->WallOwnerLayer[tile + 5] & 7) + 1; + if (owner == playerID) { + this->WallOwnerLayer[tile + 5] = this->WallOwnerLayer[tile + 5] & 0xf8 | (char)otherPlayerID - 1U; + } else if (owner == otherPlayerID) { + this->WallOwnerLayer[tile + 5] = this->WallOwnerLayer[tile + 5] & 0xf8 | (char)playerID - 1U; + } + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/toggleFlatView.cpp b/src/OpenSHC/Map/TileMapState/toggleFlatView.cpp new file mode 100644 index 000000000..64545d3f6 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/toggleFlatView.cpp @@ -0,0 +1,23 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F70B0 + void TileMapState::toggleFlatView(int param_1) + { + if (param_1 != this->flatViewToggleValue1) { + this->flatViewToggleValue1 = param_1; + this->flatViewToggleValue2 = param_1; + } + return; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/triggerLoweredView.cpp b/src/OpenSHC/Map/TileMapState/triggerLoweredView.cpp new file mode 100644 index 000000000..cce058098 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/triggerLoweredView.cpp @@ -0,0 +1,66 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::Map::Units::UnitType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F6FD0 + void TileMapState::triggerLoweredView(int param_1) + { + int view = 2; + if (this->flatViewToggleValue2 == 0) { + view = param_1; + } + if (this->refreshCertainTileMap_old == view) { + return; + } + this->counter1 = 0; + this->refreshCertainTileMap = view; + if (view != 3) { + return; + } + if (DAT_GameCore::instance.gamePausedLogical != 0) { + return; + } + + for (int unitID = 1; unitID < (int)DAT_UnitsState::instance.maxUnitCount; unitID++) { + if (DAT_UnitsState::instance.units[unitID].logicalState == OpenSHC::Map::Units::ULS_INVISIBLE) { + continue; + } + if (DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_DRUNK + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_RABBIT + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_HUNTERDOG + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_JESTER + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_CHICKEN + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_CHILD + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_JUGGLER + && DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_FIREEATER) { + continue; + } + + int height = (DAT_UnitsState::instance.units[unitID].buildingHeight + + DAT_UnitsState::instance.units[unitID].terrainOrClimbHeight) + / 10; + if (height > 14) { + height = 14; + } + DAT_UnitsState::instance.units[unitID].heightDiv10 = (char)height; + DAT_UnitsState::instance.units[unitID].field_0x83 = 0; + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/unmarkBuildingFootprintFlag.cpp b/src/OpenSHC/Map/TileMapState/unmarkBuildingFootprintFlag.cpp new file mode 100644 index 000000000..4d7b79030 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/unmarkBuildingFootprintFlag.cpp @@ -0,0 +1,32 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAD20 + void TileMapState::unmarkBuildingFootprintFlag(int param_1, int param_2, int param_3) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, param_3); + buildingSizeTileIndex++; + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + param_2].addXgetTile + + this->buildingX + param_1; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_PLAIN1_AND_FARM; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateAreaBasedOnSurrounding.cpp b/src/OpenSHC/Map/TileMapState/updateAreaBasedOnSurrounding.cpp new file mode 100644 index 000000000..ad57f242c --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateAreaBasedOnSurrounding.cpp @@ -0,0 +1,37 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FAA30 + void TileMapState::updateAreaBasedOnSurrounding(int x, int y, int buildingSize) + { + int area + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findAccessibleAreaNearBuildingLocation, + DAT_BuildingsState::ptr)(x, y, buildingSize); + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, buildingSize); + int tile = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + y].addXgetTile + + this->buildingX + x; + if (this->PathConnectionLayer[tile] == 0) { + this->PathConnectionLayer[tile] = (ushort)area; + } + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateBuildingGraphicsLayer.cpp b/src/OpenSHC/Map/TileMapState/updateBuildingGraphicsLayer.cpp new file mode 100644 index 000000000..c6ec17988 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateBuildingGraphicsLayer.cpp @@ -0,0 +1,338 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00506370 + void TileMapState::updateBuildingGraphicsLayer(int buildingID) + { + ushort buildingXPos = DAT_BuildingsState::instance.buildings[buildingID].x; + ushort buildingYPos = DAT_BuildingsState::instance.buildings[buildingID].y; + DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 = 0; + if (this->refreshRelatedOne == 0 && DAT_BuildingsState::instance.buildings[buildingID].gfxOffset != 0) { + DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 = 1; + } + int stored = DAT_BuildingsState::instance.buildings[buildingID].currentNumberOfResource + - (int)DAT_BuildingsState::instance.buildings[buildingID].someResourceNumber; + + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + index, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight); + int tile + = DAT_ViewportRenderState::instance.translationMatrix[this->buildingY + (short)buildingYPos].addXgetTile + + (short)buildingXPos + this->buildingX; + if (this->field93_0x5548c8 != 0) { + /* the placement overlay replaces every building graphic with its outline */ + this->AlphaGFXLayer[tile] = 0; + this->GfxLayer[tile] + = ((short)DAT_TerrainDefinedData::instance + .BrushSizeArray[DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight + 7] + + (short)this->buildingRotationRelatedValue + (short)GMTotalPicturesProcessed::instance[0x9d]) + - 1; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_STOCKPILE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_QUARRYSTOCKPILE) { + /* a stockpile's pile grows with what is stored on it */ + if (stored < 1) { + this->GfxLayer[tile] = (short)this->buildingRotationRelatedValue + + (short)GMTotalPicturesProcessed::instance[0xf] + 4; + } else { + short resource = DAT_BuildingsState::instance.buildings[buildingID].currentStoredResourceType; + short pile; + if (DAT_TerrainDefinedData::instance.field1001_0x8b4[resource] == 2) { + int remaining + = DAT_BuildingsState::instance.buildings[buildingID].currentLimitOfResource - stored; + pile = (short)this->buildingRotationRelatedValue + + ((short)(remaining / 4) + ((ushort)remaining & 3) * 0xc) * 4; + } else if (DAT_TerrainDefinedData::instance.field1001_0x8b4[resource] == 0) { + pile = (short)this->buildingRotationRelatedValue + -4 + (short)stored * 4; + } else { + pile = (short)this->buildingRotationRelatedValue + + ((short)DAT_BuildingsState::instance.buildings[buildingID].currentLimitOfResource + - (short)stored) + * 4; + } + this->GfxLayer[tile] = (short)GMTotalPicturesProcessed::instance[0xf] + + (short)DAT_TerrainDefinedData::instance.field996_0x84c[resource] + -1 + pile; + } + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_MERCENARYPOST + || DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_BARRACKS + || DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_OUTPOST_EUROPEAN) { + this->GfxLayer[tile] = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID + + (short)this->buildingRotationRelatedValue) + - 1; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_KEEPDOOR_LEFT + || DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_KEEPDOOR_RIGHT) { + /* a side door faces along the keep's axis, so its graphic follows the map rotation */ + uint facing = this->mapOrientation; + if (DAT_BuildingsState::instance.buildings[buildingID].orientation == 6 + || DAT_BuildingsState::instance.buildings[buildingID].orientation == 2) { + facing = (this->mapOrientation + 2) % 8; + } + if (DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 == 0) { + if (facing == 2 || facing == 6) { + this->GfxLayer[tile] + = (short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID; + } else { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID) + - 1; + } + } else if (facing == 2 || facing == 6) { + this->GfxLayer[tile] = (short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset; + } else { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset) + - 1; + } + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_KEEPDOOR) { + if (DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 == 0) { + if (this->mapOrientation != 2 && this->mapOrientation != 6) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID) + - 1; + } else { + this->GfxLayer[tile] + = (short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID; + } + } else { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset) + - 1; + } + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_GATEHOUSESMALL + || DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_WOODGATE1) { + int variation; + if (this->mapOrientation == 2 || this->mapOrientation == 6) { + variation = (DAT_BuildingsState::instance.buildings[buildingID].buildingVariation == 0x50) + 0x50; + } else { + variation = DAT_BuildingsState::instance.buildings[buildingID].buildingVariation; + } + if (DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 == 0) { + if (variation == 0x50) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + this->GfxLayer[tile] + = (((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset2) + + (short)this->buildingRotationRelatedValue) + - 1; + } + } else if (variation != 0x50) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset3 + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + this->GfxLayer[tile] + = (((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset) + + (short)this->buildingRotationRelatedValue) + - 1; + } + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_MANORHOUSE) { + int variation = 0x50; + if (this->mapOrientation == 2 || this->mapOrientation == 6) { + variation = 0x51; + } + if (DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 != 0) { + if (variation == 0x50) { + this->GfxLayer[tile] + = (((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset) + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + this->GfxLayer[tile] + = (short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)this->buildingRotationRelatedValue + 0x298; + } + } else if (variation == 0x50) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + this->GfxLayer[tile] = (short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)this->buildingRotationRelatedValue + 0x236; + } + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == OpenSHC::Map::Buildings::BT_DRAWBRIDGE) { + int plank + = DAT_TerrainDefinedData::instance + .field2467_0x1fec[(int)DAT_BuildingsState::instance.buildings[buildingID].buildingVariation + / 2][index]; + if (plank != 0) { + this->GfxLayer[tile] = (short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)plank + 0x61e; + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::returnOwnedMoatAtTile, this)(tile) == 0) { + this->GfxLayer[tile] = ((byte)this->RandomLayer[tile] & 3) + + (ushort)this->LuminesenceLayer[tile] * 4 + (short)GMTotalPicturesProcessed::instance[2]; + } else { + this->GfxLayer[tile] = ((byte)this->RandomLayer[tile] & 3) + + (this->LuminesenceLayer[tile] + 0x33) * 4 + (short)GMTotalPicturesProcessed::instance[5]; + } + } else if ((DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_KILLINGPIT + || DAT_BuildingsState::instance.buildings[buildingID].state > 0 + || DAT_GameState::instance.mapAndTime + .playerTeams[DAT_BuildingsState::instance.buildings[buildingID].owner] + == DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + || DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_EDITOR) + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED) { + /* a killing pit is invisible to anyone not on the owner's team */ + if (DAT_BuildingsState::instance.buildings[buildingID].buildingVariation == 0xf) { + if (DAT_BuildingsState::instance.buildings[buildingID].field62_0xb0 != 0) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].gfxOffset + + (short)this->buildingRotationRelatedValue) + - 1; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingIsVisuallyActive == 0) { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].visuallyActiveSpriteID + + (short)this->buildingRotationRelatedValue) + - 1; + } + this->AlphaGFXLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID] + .unknownManorHouseOrStoneKeepRelated + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID + + (short)this->buildingRotationRelatedValue) + - 1; + } else { + int relative + = DAT_BuildingsState::instance.buildings[buildingID].buildingVariation - this->mapOrientation; + if (relative < 0) { + relative = relative + 8; + } + short rotation = (short)relative; + short span; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingVariation == 8) { + rotation = (short)DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + span = rotation * 8; + } else { + span = (short)DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + span = span * span; + } + short sprite; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingIsVisuallyActive == 0) { + sprite = (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID; + } else { + sprite = (short)DAT_BuildingsState::instance.buildings[buildingID].visuallyActiveSpriteID; + } + this->GfxLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + sprite + (short)this->buildingRotationRelatedValue + span * rotation) + - 1; + this->AlphaGFXLayer[tile] + = ((short)GMTotalPicturesProcessed::instance + [(short)DAT_BuildingsState::instance.buildings[buildingID].spriteSheetID] + + (short)DAT_BuildingsState::instance.buildings[buildingID].spriteID + + (short)this->buildingRotationRelatedValue + span * rotation) + - 1; + } + } + index++; + } while (index < this->constructionTileCount); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateBuildingPlacementRotationPreview.cpp b/src/OpenSHC/Map/TileMapState/updateBuildingPlacementRotationPreview.cpp new file mode 100644 index 000000000..d56cfad16 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateBuildingPlacementRotationPreview.cpp @@ -0,0 +1,26 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00504EE0 + void TileMapState::updateBuildingPlacementRotationPreview(int param_1, int param_2) + { + BOOLEnum previousPlacementFail = this->buildingPlacementFail; + this->buildingPlacementFail = FALSE; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::determineBuildingPlacementRotation, this)(param_1, param_2); + this->buildingPlacementFail = previousPlacementFail; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateGFXLayers.cpp b/src/OpenSHC/Map/TileMapState/updateGFXLayers.cpp new file mode 100644 index 000000000..a2456a55b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateGFXLayers.cpp @@ -0,0 +1,365 @@ +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +// masks for the handwritten neighbour-flag macro; MASM has no "|", so these must be literals +#define NEIGHBOUR_FLAGS_MASK_MARSH 0x20000000 // L_MARSH +#define NEIGHBOUR_FLAGS_MASK_SEA 0x1 // L_SEA +#define NEIGHBOUR_FLAGS_MASK_DEFAULT_EARTH 0x8000 // L_DEFAULT_EARTH_OR_TEXTURE +#define NEIGHBOUR_FLAGS_MASK_L2_SCRUB 0x1 // L2_SCRUB +#define NEIGHBOUR_FLAGS_MASK_L2_EARTH_AND_STONES 0x2 // L2_EARTH_AND_STONES +#define NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_MEDIUM 0x4 // L2_PLATEAU_MEDIUM +#define NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_HIGH 0x8 // L2_PLATEAU_HIGH +#define NEIGHBOUR_FLAGS_MASK_L2_OASIS_GRASS 0x10 // L2_OASIS_GRASS +#define NEIGHBOUR_FLAGS_MASK_L2_BEACH 0x20 // L2_BEACH +#define NEIGHBOUR_FLAGS_MASK_L2_STONES 0x40 // L2_STONES_OR_DRIVEN_SANDUnk + +#pragma optimize("", off) + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_BEACH; + using OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + using OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_HIGH; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_MEDIUM; + using OpenSHC::Map::LogicHelpers::L2_SCRUB; + using OpenSHC::Map::LogicHelpers::L2_STONES_OR_DRIVEN_SANDUnk; + using OpenSHC::Map::LogicHelpers::L2_THICK_SCRUB; + using OpenSHC::Map::LogicHelpers::L_DEFAULT_EARTH_OR_TEXTURE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FC9E0 + void TileMapState::updateGFXLayers() + { + /* + Contains the handwritten neighbour-flag macro nine times (see NeighbourFlagsAsm.hpp), which + is why the surrounding code is unoptimised: MSVC disables optimisation in a function holding + inline asm, so every field access reloads this from the frame. + + Two stores below are dead in this function but present in the original, because the body is + shared with updateMacroLayerRelated where they are read: "rotation" is reassigned from + MacroLayer at the end of the tile setup, and "height" is never read at all. + */ + /* + The locals are declared here, in this order, because the original was built without + optimisation: its stack slots are observable and MSVC assigns them per function, not per + scope. Declaring them at first use instead costs ~12 points of match (52.6% vs 64.5%). + */ + int rotation; + int blockColumn; + int gfxMode; + int index; + int macroHigh; + int blockRow; + int gfxBaseD; + int luminescence; + int gfxStride; + int subRow; + int subColumn; + int gfxBaseA; + int macroLow; + int gfxBaseE; + int height; + int gfxBaseB; + int gfxBaseC; + int macroMid; + int savedBitFlag; + + if (this->forceUpdateGFXLayers <= 0) { + return; + } + this->forceUpdateGFXLayers = this->forceUpdateGFXLayers - 1; + + if (MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::getYSmallerThanYLimit, DAT_PathFindingState::ptr)() + == FALSE) { + return; + } + + rotation = DAT_PathFindingState::instance.mappingYRelated % 10; + for (blockRow = this->someIndex; blockRow <= this->someLimit; blockRow++) { + for (blockColumn = this->someYLike; blockColumn <= this->someYLikeLimit; blockColumn++) { + if (DAT_TileMapState::instance.mapping40x40[blockRow][blockColumn] != 0) { + subRow = rotation; + rotation = 0; + for (; subRow < 10; subRow++) { + this->DAT_SomeY = blockRow * 10 + subRow; + this->DAT_SomeX = blockColumn * 10; + for (subColumn = 0; subColumn < 10; subColumn++, this->DAT_SomeX++) { + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY) + != FALSE) { + this->DAT_SomeTile + = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY); + if ((this->LogicLayer[this->DAT_SomeTile] & L_DEFAULT_EARTH_OR_TEXTURE) != 0) { + if (this->GfxLayer[this->DAT_SomeTile] == 0) { + luminescence = this->LuminesenceLayer[this->DAT_SomeTile]; + if (luminescence < 2) { + luminescence = 2; + } + macroLow = (short)this->MacroLayer[this->DAT_SomeTile] & 0x3f; + macroMid = ((short)this->MacroLayer[this->DAT_SomeTile] & 0x7c0) >> 6; + macroHigh = ((short)this->MacroLayer[this->DAT_SomeTile] & 0xf000) >> 12; + macroHigh = macroHigh & 3; + rotation = (short)this->MacroLayer[this->DAT_SomeTile]; + this->PillarGFXLayer[this->DAT_SomeTile] + = (ushort)GMTotalPicturesProcessed::instance[9]; + + if ((this->LogicLayer[this->DAT_SomeTile] & L_MARSH) != 0) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(1, NEIGHBOUR_FLAGS_MASK_MARSH) + gfxBaseA = 1; + gfxBaseB = 3; + gfxBaseC = 0x15; + gfxBaseD = 0x21; + gfxBaseE = 0x25; + gfxStride = 4; + gfxMode = 0; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_EARTH_AND_STONES) + != 0) { + height = this->HeightLayer[this->DAT_SomeTile]; + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(2, NEIGHBOUR_FLAGS_MASK_L2_EARTH_AND_STONES) + gfxBaseA = 0x99; + gfxBaseB = 0x9b; + gfxBaseC = 0xad; + gfxBaseD = 0xb9; + gfxBaseE = 0xbd; + gfxStride = 4; + gfxMode = 0; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_OASIS_GRASS) + != 0) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(3, NEIGHBOUR_FLAGS_MASK_L2_OASIS_GRASS) + gfxBaseA = 0x1c9; + gfxBaseB = 0x1cb; + gfxBaseC = 0x1dd; + gfxBaseD = 0x1e9; + gfxBaseE = 0x1ed; + gfxStride = 4; + gfxMode = 0; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] + & L2_STONES_OR_DRIVEN_SANDUnk) + != 0) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(4, NEIGHBOUR_FLAGS_MASK_L2_STONES) + gfxBaseA = 0x261; + gfxBaseB = 0x263; + gfxBaseC = 0x275; + gfxBaseD = 0x281; + gfxBaseE = 0x285; + gfxStride = 4; + gfxMode = 0; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_SCRUB) != 0) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(5, NEIGHBOUR_FLAGS_MASK_L2_SCRUB) + gfxBaseA = 0x2f9; + gfxBaseB = 0x2fb; + gfxBaseC = 0x30d; + gfxBaseD = 0x319; + gfxBaseE = 0x31d; + gfxStride = 4; + gfxMode = 0; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_THICK_SCRUB) + != 0) { + /* the original gathers L2_SCRUB here, not L2_THICK_SCRUB; kept as-is */ + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(6, NEIGHBOUR_FLAGS_MASK_L2_SCRUB) + gfxBaseA = 0x4a9; + gfxBaseB = 0x4ab; + gfxBaseC = 0x4bd; + gfxBaseD = 0x4c9; + gfxBaseE = 0x4cd; + gfxStride = 4; + gfxMode = 0; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_PLATEAU_MEDIUM) + != 0) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(7, NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_MEDIUM) + gfxBaseA = 0x131; + gfxBaseB = 0x133; + gfxBaseC = 0x145; + gfxBaseD = 0x151; + gfxBaseE = 0x155; + gfxStride = 4; + gfxMode = 2; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_PLATEAU_HIGH) + != 0) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(8, NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_HIGH) + gfxBaseA = 0x131; + gfxBaseB = 0x133; + gfxBaseC = 0x145; + gfxBaseD = 0x151; + gfxBaseE = 0x155; + gfxStride = 4; + gfxMode = 2; + } else if (((char)this->Logic2Layer[this->DAT_SomeTile] & L2_BEACH) != 0) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_4(9, NEIGHBOUR_FLAGS_MASK_L2_BEACH) + savedBitFlag = this->bitFlag; + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(10, NEIGHBOUR_FLAGS_MASK_SEA) + this->bitFlag = this->bitFlag | savedBitFlag; + gfxBaseA = 0x391; + gfxBaseB = 0x393; + gfxBaseC = 0x3a5; + gfxBaseD = 0x3b1; + gfxBaseE = 0x3b5; + gfxStride = 4; + gfxMode = 0; + } else { + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(11, NEIGHBOUR_FLAGS_MASK_DEFAULT_EARTH) + gfxBaseA = 0x131; + gfxBaseB = 0x133; + gfxBaseC = 0x145; + gfxBaseD = 0x151; + gfxBaseE = 0x155; + gfxStride = 4; + gfxMode = 2; + } + + if (macroLow == 1) { + this->bitFlag = ~this->bitFlag; + this->bitFlag = this->bitFlag & 0xaa; + if (this->mapOrientation == 0) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 0); + } else if (this->mapOrientation == 2) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 2); + } else if (this->mapOrientation == 4) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 4); + } else if (this->mapOrientation == 6) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 6); + } + + if (gfxMode == 1) { + this->GfxLayer[this->DAT_SomeTile] = 0; + for (index = 0; index < 4; index++) { + if (DAT_TerrainDefinedData::instance.field2296_0x1d54[index][0] + == this->bitFlag) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseA + + (luminescence - 2) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 1) + + DAT_TerrainDefinedData::instance + .field2296_0x1d54[index][1]; + } + } + for (index = 4; index < 8; index++) { + if (DAT_TerrainDefinedData::instance.field2296_0x1d54[index][0] + == this->bitFlag) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseB + + (luminescence - 2) * gfxStride - 1 + + DAT_TerrainDefinedData::instance + .field2296_0x1d54[index][1]; + } + } + if (this->GfxLayer[this->DAT_SomeTile] == 0) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseC + + (luminescence - 2) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } + } else if (luminescence == 2) { + if (this->bitFlag != 0) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseC + + ((short)this->RandomLayer[this->DAT_SomeTile] & 7) - 1; + } else { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseD + + ((short)this->RandomLayer[this->DAT_SomeTile] & 7) - 1; + } + } else if (gfxMode == 2) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseA + + (luminescence - 3) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } else if (this->bitFlag != 0) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseA + + (luminescence - 3) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 1); + } else { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseB + + (luminescence - 3) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 1); + } + } else if (luminescence > 2) { + if (gfxMode == 0) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseB + + (luminescence - 3) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 1); + } else if (gfxMode == 2) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseA + + (luminescence - 3) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } else { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseD + + (luminescence - 2) * gfxStride - 1 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } + } else if (macroLow == 2) { + if (gfxMode == 0) { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseD + + ((short)this->RandomLayer[this->DAT_SomeTile] & 7) - 1; + } else { + this->GfxLayer[this->DAT_SomeTile] + = GMTotalPicturesProcessed::instance[55] + gfxBaseD + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3) - 1; + } + } else if (macroLow == 4) { + macroMid = DAT_TerrainDefinedData::instance + .field2293_0x1a54[this->mapOrientation / 2][macroMid]; + this->GfxLayer[this->DAT_SomeTile] = GMTotalPicturesProcessed::instance[55] + + gfxBaseE + macroMid + macroHigh * 4 - 1; + } else if (macroLow == 8) { + macroMid = DAT_TerrainDefinedData::instance + .field2294_0x1b54[this->mapOrientation / 2][macroMid]; + this->GfxLayer[this->DAT_SomeTile] = macroHigh * 9 + + (GMTotalPicturesProcessed::instance[55] + gfxBaseE + macroMid) + 0xf; + } else if (macroLow == 0x10) { + macroMid = DAT_TerrainDefinedData::instance + .field2295_0x1c54[this->mapOrientation / 2][macroMid]; + this->GfxLayer[this->DAT_SomeTile] = macroHigh * 16 + + (GMTotalPicturesProcessed::instance[55] + gfxBaseE + macroMid) + 0x33; + } + } + } + } + } + } + } + } + } + } +} +} + +#pragma optimize("", on) diff --git a/src/OpenSHC/Map/TileMapState/updateGameRelatedValue.cpp b/src/OpenSHC/Map/TileMapState/updateGameRelatedValue.cpp new file mode 100644 index 000000000..05a9f946f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateGameRelatedValue.cpp @@ -0,0 +1,43 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00511850 + void TileMapState::updateGameRelatedValue() + { + if (DAT_GameState::instance.mapAndTime.gameEventRelatedCountdown == 0) { + return; + } + + if (DAT_GameState::instance.mapAndTime.gameEventRelatedCountdown == 0x28) { + this->field187_0x554a10 = -1; + } + DAT_GameState::instance.mapAndTime.gameEventRelatedCountdown + = DAT_GameState::instance.mapAndTime.gameEventRelatedCountdown - 1; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setSignpostDistanceForCampaignMission, this)(); + int signpostDistance = ((0x28 - DAT_GameState::instance.mapAndTime.gameEventRelatedCountdown) + * DAT_GameState::instance.mapAndTime.unk_signpostDistance) + / 0x28; + if (signpostDistance == this->field187_0x554a10) { + return; + } + + this->field187_0x554a10 = signpostDistance; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::pathfindingUpdate_0x4a8ab0, + DAT_PathFindingState::ptr)(signpostDistance); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateGfxLayer.cpp b/src/OpenSHC/Map/TileMapState/updateGfxLayer.cpp new file mode 100644 index 000000000..dba41031e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateGfxLayer.cpp @@ -0,0 +1,2583 @@ + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +// masks for the handwritten neighbour-flag macro; MASM has no "|", so these must be literals +#define NEIGHBOUR_FLAGS_MASK_RIVER_OR_FORD 0x300000 // L_RIVER | L_FORD +#define NEIGHBOUR_FLAGS_MASK_WALL_OR_GATEHOUSE 0x100 // L_WALL_OR_GATEHOUSE + +/* the original was built without optimisation: frame pointer, locals off ebp, sub esp, 0x118 */ +#pragma optimize("", off) + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BOULDERS; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_DEFAULT_EARTH_OR_TEXTURE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_APPLE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_DAIRY; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_HOP; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_WHEAT; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_IRON; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_OIL; + using OpenSHC::Map::LogicHelpers::L_PEBBLES; + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Restarted to delay deadcode elimination for space: ram + */ + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00509180 + void TileMapState::updateGfxLayer() + { + + OpenSHC::Map::Buildings::BuildingTypeShort BVar1; + ushort uVar2; + ushort uVar3; + short sVar4; + short sVar5; + byte bVar6; + byte bVar7; + int sVar8; + bool bVar10; + int bVar9; + BOOLEnum BVar11; + uint uVar12; + int iVar13; + int iVar14; + uint uVar15; + byte bVar16; + int iVar17; + uint* puVar18; + byte bVar19; + int local_d4; + uint local_c0; + int local_b8; + int local_b4; + short local_b0; + int local_ac; + int local_a4; + int local_a0; + short local_9c; + int local_94; + uint local_74; + int local_70; + int local_68; + int local_60; + int local_54; + uint local_48; + short local_44; + int local_38; + int local_30; + int local_24; + int local_18; + int local_14; + int local_10; + int local_c; + int local_8; + sVar8 = 0; + if (0 < this->forceUpdateTextureTilemap) { + this->forceUpdateTextureTilemap = this->forceUpdateTextureTilemap + -1; + local_c = DAT_PathFindingState::instance.mappingYRelated % 10; + for (local_18 = this->someIndex; local_18 <= this->someLimit; local_18 = local_18 + 1) { + for (local_10 = this->someYLike; local_10 <= this->someYLikeLimit; local_10 = local_10 + 1) { + if (*(char*)(local_18 * 0x28 + 0x1f93438 + local_10) != '\0') { + local_24 = local_c; + local_c = 0; + for (; local_24 < 10; local_24 = local_24 + 1) { + this->DAT_SomeY = local_18 * 10 + local_24; + this->DAT_SomeX = local_10 * 10; + for (local_30 = 0; local_30 < 10; local_30 = local_30 + 1) { + bVar9 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, (uint)((int)(this->DAT_SomeY))); + if ((bVar9 != 0) + && (this->DAT_SomeTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY), + (char)this->ChangedLayer[this->DAT_SomeTile] != 0)) { + if (this->BuildingLayer[this->DAT_SomeTile] == 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::computeTileLuminescence, + this)(this->DAT_SomeTile, this->DAT_SomeY); + iVar17 = this->DAT_SomeX; + this->FloatingLayer[this->DAT_SomeTile] = 0; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xfc3f; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xf7ff; + if ((this->LogicLayer[this->DAT_SomeTile] & L_RIVER) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_FORD) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_FARM_FIELD_WHEAT) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_FARM_FIELD_HOP) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_FARM_FIELD_APPLE + | L_FARM_FIELD_DAIRY) + == 0) { + if ((((this->LogicLayer[this->DAT_SomeTile] + & L_WALL_OR_GATEHOUSE + | L_BUILDING | L_KEEP_NON_MANOR_HOUSE) + == 0) + && (this->BuildingWasLayer[this->DAT_SomeTile] + != '\0')) + && (((short)this->MiscDisplayLayer[this->DAT_SomeTile] + & 0x10) + != 0)) { + local_44 = 0; + switch (this->BuildingWasLayer[this->DAT_SomeTile]) { + case '\t': + case '\n': + case '\v': + case '\x18': + case '\x19': + case '$': + case '%': + case '&': + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '0': + case '4': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'Z': + case 'j': + local_44 = 0xc; + } + if (((short)this->MiscDisplayLayer[this->DAT_SomeTile] + & 0x2000) + == 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed::instance[0x95] + + local_44 + 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 7); + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed::instance[0x95] + + local_44 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 7); + } + } else if ((this->LogicLayer[this->DAT_SomeTile] + & L_WALL_OR_GATEHOUSE) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_SEA + | L_ROCKY | L_WALL_OR_GATEHOUSE | L_BUILDING + | L_BOULDERS | L_PEBBLES | L_IRON | L_RIVER + | L_FARM_FIELD_WHEAT | L_FARM_FIELD_HOP + | L_FARM_FIELD_APPLE | L_FARM_FIELD_DAIRY + | L_KEEP_NON_MANOR_HOUSE | L_MARSH | L_OIL) + == 0) { + local_48 = 0; + this->WallOwnerLayer[this->DAT_SomeTile] + = this->WallOwnerLayer[this->DAT_SomeTile] + & 0xf7; + MACRO_NEIGHBOUR_FLAGS_LOGIC_8( + wall, NEIGHBOUR_FLAGS_MASK_WALL_OR_GATEHOUSE) + bVar19 = this->bitFlag; + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(m1, 2) + bVar19 = ~this->bitFlag & bVar19; + if (bVar19 != 0) { + switch (bVar19 & 0xaa) { + case 10: + local_48 = 2; + if ((bVar19 & 0x15) == 0x15) { + local_48 = 0; + } + break; + case 0x28: + local_48 = (uint)((bVar19 & 0x54) != 0x54); + break; + case 0x82: + local_48 = 3; + if ((bVar19 & 0x45) == 0x45) { + local_48 = 0; + } + break; + case 0xa0: + local_48 = 4; + if ((bVar19 & 0x41) == 0x15) { + local_48 = 0; + } + } + if (local_48 != 0) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(r1, 0x800) + if (this->bitFlag != 0) { + local_48 = 0; + } + } + if (local_48 != 0) { + /* the corner picks its two cardinal + neighbours and the diagonal between them + (the decompiler lost these four arms: + they are the jump table at 0x00510d94) */ + switch (local_48) { + case 1: + local_68 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][2]; + local_60 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][4]; + local_54 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][3]; + break; + case 2: + local_68 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][6]; + local_60 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][4]; + local_54 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][5]; + break; + case 3: + local_68 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][6]; + local_60 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][0]; + local_54 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][7]; + break; + case 4: + local_68 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][2]; + local_60 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][0]; + local_54 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][1]; + break; + } + bVar19 = this->HeightLayer[local_68]; + bVar16 = this->HeightLayer[local_60]; + bVar7 = this->HeightLayer[local_54]; + if ((this->LogicLayer[local_54] & 0x100U) + == 0) { + bVar7 = bVar16; + } + bVar6 = bVar16; + if (bVar19 < bVar16) { + bVar6 = bVar19; + } + if ((bVar6 < bVar7) + && (bVar7 = bVar16, bVar19 < bVar16)) { + bVar7 = bVar19; + } + if (this->HeightLayer[this->DAT_SomeTile] + < bVar7) { + this->WallGFXLayer[this->DAT_SomeTile] + = (ushort)bVar7; + if (((DAT_TileMapState::instance + .DamageLayer[local_68] + != 0) + || (DAT_TileMapState::instance + .DamageLayer[local_60] + != 0)) + || ((DAT_TileMapState::instance + .DamageLayer[local_54] + != 0 + && ((this->LogicLayer[local_54] + & 0x100U) + != 0)))) { + this->WallOwnerLayer[this + ->DAT_SomeTile] + = this->WallOwnerLayer[this + ->DAT_SomeTile] + | 8; + } + } else { + local_48 = 0; + } + if (local_48 != 0) { + uVar12 = ((local_48 + 3) + - this->mapOrientation / 2) + % 4; + switch (uVar12) { + case 0: + this->MiscDisplayLayer[this + ->DAT_SomeTile] + = (short)this->MiscDisplayLayer + [this->DAT_SomeTile] + | 0x40; + this->AlphaGFXLayer[this + ->DAT_SomeTile] = 0x2f; + break; + case 1: + this->MiscDisplayLayer[this + ->DAT_SomeTile] + = (short)this->MiscDisplayLayer + [this->DAT_SomeTile] + | 0x200; + this->AlphaGFXLayer[this + ->DAT_SomeTile] = 0x2e; + break; + case 2: + this->MiscDisplayLayer[this + ->DAT_SomeTile] + = (short)this->MiscDisplayLayer + [this->DAT_SomeTile] + | 0x80; + this->AlphaGFXLayer[this + ->DAT_SomeTile] = 0x31; + break; + case 3: + this->MiscDisplayLayer[this + ->DAT_SomeTile] + = (short)this->MiscDisplayLayer + [this->DAT_SomeTile] + | 0x100; + this->AlphaGFXLayer[this + ->DAT_SomeTile] = 0x30; + } + } + } + } + } + } else { + this->field112_0x554908 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + computeWallCornerRenderRotation, + this)(iVar17); + this->WallGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[10] + -1 + + (short)this->field112_0x554908; + sVar4 = (short)GMTotalPicturesProcessed::instance[0xc]; + if ((char)this->DamageLayer[this->DAT_SomeTile] == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & 0x200U) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] + & 0x800U) + == 0) { + if (this->LuminesenceLayer[this + ->DAT_SomeTile] + < 4) { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x60 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 7); + } else { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x68 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 7); + } + } else { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + hasHigherNeighborWithStairs, + this)( + this->DAT_SomeTile, this->DAT_SomeY, 0); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + hasHigherNeighborWithStairs, + this)(this->DAT_SomeTile, + this->DAT_SomeY, 2); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + hasHigherNeighborWithStairs, + this)(this->DAT_SomeTile, + this->DAT_SomeY, 4); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + hasHigherNeighborWithStairs, + this)(this->DAT_SomeTile, + this->DAT_SomeY, 6); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + hasHigherPlainNeighborWithWallOrGatehouse, + this)( + this->DAT_SomeTile, + this->DAT_SomeY, 0); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + hasHigherPlainNeighborWithWallOrGatehouse, + this)( + this->DAT_SomeTile, + this->DAT_SomeY, 2); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + hasHigherPlainNeighborWithWallOrGatehouse, + this)( + this->DAT_SomeTile, + this->DAT_SomeY, + 4); + if (BVar11 + == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + hasHigherPlainNeighborWithWallOrGatehouse, + this)( + this->DAT_SomeTile, + this->DAT_SomeY, + 6); + if (BVar11 + == FALSE) { + this->GfxLayer + [this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance + [0xc] + + 0x68; + } else { + this->GfxLayer + [this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance + [0xc] + + 0x85; + } + } else { + this->GfxLayer + [this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance + [0xc] + + 0x88; + } + } else { + this->GfxLayer[this + ->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance + [0xc] + + 0x87; + } + } else { + this->GfxLayer[this + ->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed:: + instance + [0xc] + + 0x86; + } + } else { + this->GfxLayer[this + ->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed:: + instance[0xc] + + 0x85; + } + } else { + this->GfxLayer[this + ->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed:: + instance[0xc] + + 0x88; + } + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed:: + instance[0xc] + + 0x87; + } + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance[0xc] + + 0x86; + } + } + } else { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + isWallCornerForCardinalDirection, + this)( + this->DAT_SomeTile, this->DAT_SomeY, 0); + if (BVar11 == FALSE) { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + isWallCornerForCardinalDirection, + this)( + this->DAT_SomeTile, this->DAT_SomeY, 2); + if (BVar11 == FALSE) { + uVar12 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + isWallCornerForDiagonalDirection, + this)(this->DAT_SomeTile, + this->DAT_SomeY, 1); + if (uVar12 == 0) { + uVar12 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + isWallCornerForDiagonalDirection, + this)(this->DAT_SomeTile, + this->DAT_SomeY, 3); + if (uVar12 == 0) { + uVar12 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + isWallCornerForDiagonalDirection, + this)(this->DAT_SomeTile, + this->DAT_SomeY, 5); + if (uVar12 == 0) { + uVar12 = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + isWallCornerForDiagonalDirection, + this)( + this->DAT_SomeTile, + this->DAT_SomeY, 7); + sVar4 = (short) + GMTotalPicturesProcessed:: + instance[0xc]; + if (uVar12 == 0) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x84; + this->LogicLayer[this + ->DAT_SomeTile] + = this->LogicLayer + [this->DAT_SomeTile] + | 0x400000; + } else { + if (this->mapOrientation + == 0) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x81; + } else if ( + this->mapOrientation + == 2) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x80; + } else if ( + this->mapOrientation + == 4) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x83; + } else if ( + this->mapOrientation + == 6) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x82; + } + this->LogicLayer[this + ->DAT_SomeTile] + = this->LogicLayer + [this->DAT_SomeTile] + | 0x400000; + } + } else { + sVar4 = (short) + GMTotalPicturesProcessed:: + instance[0xc]; + if (this->mapOrientation + == 0) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x80; + } else if ( + this->mapOrientation + == 2) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x83; + } else if ( + this->mapOrientation + == 4) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x82; + } else if ( + this->mapOrientation + == 6) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x81; + } + this->LogicLayer[this + ->DAT_SomeTile] + = this->LogicLayer[this + ->DAT_SomeTile] + | 0x400000; + } + } else { + sVar4 = (short) + GMTotalPicturesProcessed:: + instance[0xc]; + if (this->mapOrientation == 0) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x83; + } else if (this->mapOrientation + == 2) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x82; + } else if (this->mapOrientation + == 4) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x81; + } else if (this->mapOrientation + == 6) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x80; + } + this->LogicLayer[this + ->DAT_SomeTile] + = this->LogicLayer[this + ->DAT_SomeTile] + | 0x400000; + } + } else { + sVar4 = (short) + GMTotalPicturesProcessed:: + instance[0xc]; + if (this->mapOrientation == 0) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x82; + } else if (this->mapOrientation + == 2) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x81; + } else if (this->mapOrientation + == 4) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x80; + } else if (this->mapOrientation + == 6) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x83; + } + this->LogicLayer[this->DAT_SomeTile] + = this->LogicLayer[this + ->DAT_SomeTile] + | 0x400000; + } + } else { + sVar4 + = (short)GMTotalPicturesProcessed:: + instance[0xc]; + if ((this->mapOrientation == 0) + || (this->mapOrientation == 4)) { + if ((this->LogicLayer[this + ->DAT_SomeTile] + & 0x400000U) + == 0) { + this->GfxLayer[this + ->DAT_SomeTile] = sVar4 + + 0x70 + + ((short)this->RandomLayer + [this->DAT_SomeTile] + & 3); + } else { + this->GfxLayer[this + ->DAT_SomeTile] = sVar4 + + 0x78 + + ((short)this->RandomLayer + [this->DAT_SomeTile] + & 3); + } + } else if ((this->LogicLayer[this + ->DAT_SomeTile] + & 0x400000U) + == 0) { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x74 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + } else { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x7c + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + } + } + } else { + sVar4 = (short) + GMTotalPicturesProcessed::instance[0xc]; + if ((this->mapOrientation == 0) + || (this->mapOrientation == 4)) { + if ((this->LogicLayer[this + ->DAT_SomeTile] + & 0x400000U) + == 0) { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x74 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + } else { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x7c + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + } + } else if ((this->LogicLayer[this + ->DAT_SomeTile] + & 0x400000U) + == 0) { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x70 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + } else { + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x78 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + } + } + } + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x8a + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 7); + } + } + } + } else { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::computeClimbRampRotation, + this)(this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x800; + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xe] + 0x25 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 1) * 9; + DAT_BuildingsState::instance.field14_0x18e024 = 1; + } + } else { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::computeClimbRampRotation, + this)(this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x800; + this->GfxLayer[this->DAT_SomeTile] + = ((short)this->RandomLayer[this->DAT_SomeTile] & 3) + + (short)GMTotalPicturesProcessed::instance[0xe]; + DAT_BuildingsState::instance.field14_0x18e024 = 1; + } + } else { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isTileSuitableForBrushPlacement, + this)(this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + if (BVar11 == FALSE) { + this->PillarGFXLayer[this->DAT_SomeTile] = 0x20; + } else { + this->PillarGFXLayer[this->DAT_SomeTile] + = ((short)this->RandomLayer[this->DAT_SomeTile] & 0xf) + 0x23; + } + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[5] + 0x29c; + } + } else { + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isTileSuitableForBrushPlacement, this)( + this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + if (BVar11 == FALSE) { + this->PillarGFXLayer[this->DAT_SomeTile] = 0x20; + } else { + this->PillarGFXLayer[this->DAT_SomeTile] + = ((short)this->RandomLayer[this->DAT_SomeTile] & 0xf) + 0x23; + } + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[5] + 0x214; + } + } else if (DAT_BuildingsState::instance + .buildings[this->BuildingLayer[this->DAT_SomeTile]] + .buildingType + == OpenSHC::Map::Buildings::BT_DRAWBRIDGE) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::computeTileLuminescence, + this)(this->DAT_SomeTile, this->DAT_SomeY); + } + } + this->DAT_SomeX = this->DAT_SomeX + 1; + } + } + } + } + } + local_c = DAT_PathFindingState::instance.mappingYRelated % 10; + for (local_18 = this->someIndex; local_18 <= this->someLimit; local_18 = local_18 + 1) { + for (local_10 = this->someYLike; local_10 <= this->someYLikeLimit; local_10 = local_10 + 1) { + if (*(char*)(local_18 * 0x28 + 0x1f93438 + local_10) != '\0') { + local_24 = local_c; + local_c = 0; + for (; local_24 < 10; local_24 = local_24 + 1) { + this->DAT_SomeY = local_18 * 10 + local_24; + this->DAT_SomeX = local_10 * 10; + for (local_30 = 0; local_30 < 10; local_30 = local_30 + 1) { + BVar11 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, (uint)((int)(this->DAT_SomeY))); + if ((((BVar11 != FALSE) + && (this->DAT_SomeTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY), + (this->LogicLayer[this->DAT_SomeTile] & 0x100201U) != 0)) + && ((char)this->ChangedLayer[this->DAT_SomeTile] != 0)) + && ((this->BuildingLayer[this->DAT_SomeTile] == 0 + && ((this->LogicLayer[this->DAT_SomeTile] & 0x10000030U) == 0)))) { + if ((this->LogicLayer[this->DAT_SomeTile] & 1U) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & 0x100000U) != 0) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::computeTileCliffEdgeFlags, this)( + this->DAT_SomeTile, this->DAT_SomeX, this->DAT_SomeY); + } + } else { + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xfffc; + iVar17 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][this->mapOrientation]; + this->field218_0x554a50 = (ushort)this->HeightLayer[iVar17]; + if ((this->LogicLayer[iVar17] & 1U) != 0) { + this->field218_0x554a50 = 0; + } + if ((this->LogicLayer[iVar17] & 0x80U) != 0) { + this->field218_0x554a50 = 0x14; + } + if (0x13 < (short)this->field218_0x554a50) { + sVar4 = (short)GMTotalPicturesProcessed::instance[0xa6]; + if ((short)this->field218_0x554a50 < 0x5b) { + if ((short)this->field218_0x554a50 < 0x29) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x5dc; + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x5e2; + } + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x5ee; + } + this->PillarGFXLayer[this->DAT_SomeTile] + = (ushort)GMTotalPicturesProcessed::instance[9]; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x40; + } + local_74 = 0; + this->bitFlag = 0; + for (local_70 = 0; local_70 < 8; local_70 = local_70 + 1) { + iVar17 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][local_70]; + if (this->HeightLayer[iVar17] < 0x14) { + if ((this->LogicLayer[iVar17] & 0x100031U) == 0) { + this->bitFlag + = this->bitFlag | (byte)(0x80 >> ((byte)local_70 & 0x1f)); + } + if (((this->LogicLayer[iVar17] & 0x100001U) == 0) + && (((char)this->Logic2Layer[iVar17] & 0x20) != 0)) { + local_74 = 0x80 >> ((byte)local_70 & 0x1f) | local_74; + } + } + } + if (this->bitFlag != 0) { + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x100; + this->CertainPathLayer[this->DAT_SomeTile] + = (ushort)this->bitFlag | (ushort)(local_74 << 8); + } + } + if ((this->LogicLayer[this->DAT_SomeTile] & 0x200U) != 0) { + uVar12 = (this->field84_0x5548a4 + 7) % 8; + iVar17 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + uVar12 = (this->field84_0x5548a4 + 1) % 8; + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + if (((((this->LogicLayer[iVar17] | this->LogicLayer[iVar13]) & 1U) == 0) + && (((short)this->MiscDisplayLayer[iVar17] & 0x3c0) != 0)) + && (((short)this->MiscDisplayLayer[iVar13] & 0x3c0) != 0)) { + if (this->LuminesenceLayer[this->DAT_SomeTile] < 4) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x60 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 7); + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x68 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 7); + } + } + uVar12 = (this->field84_0x5548a4 + 1) % 8; + iVar17 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + uVar12 = (this->field84_0x5548a4 + 3) % 8; + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + if (((((this->LogicLayer[iVar17] | this->LogicLayer[iVar13]) & 1U) == 0) + && (((short)this->MiscDisplayLayer[iVar17] & 0x3c0) != 0)) + && (((short)this->MiscDisplayLayer[iVar13] & 0x3c0) != 0)) { + if ((this->DAT_SomeY & 1U) == 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x138; + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x137; + } + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x3c0; + } + uVar12 = (this->field84_0x5548a4 + 5) % 8; + iVar17 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + uVar12 = (this->field84_0x5548a4 + 7) % 8; + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + if (((((this->LogicLayer[iVar17] | this->LogicLayer[iVar13]) & 1U) == 0) + && (((short)this->MiscDisplayLayer[iVar17] & 0x3c0) != 0)) + && (((short)this->MiscDisplayLayer[iVar13] & 0x3c0) != 0)) { + if ((this->DAT_SomeY & 1U) == 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x13a; + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x139; + } + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x3c0; + } + uVar12 = (this->field84_0x5548a4 + 3) % 8; + iVar17 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + uVar12 = (this->field84_0x5548a4 + 5) % 8; + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix[this->DAT_SomeY][uVar12]; + if (((((this->LogicLayer[iVar17] | this->LogicLayer[iVar13]) & 1U) == 0) + && (((short)this->MiscDisplayLayer[iVar17] & 0x3c0) != 0)) + && (((short)this->MiscDisplayLayer[iVar13] & 0x3c0) != 0)) { + uVar12 = (int)this->DAT_SomeY % 8; + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + 0x12f + + (short)uVar12; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x3c0; + } + } + } + this->DAT_SomeX = this->DAT_SomeX + 1; + } + } + } + } + } + local_c = DAT_PathFindingState::instance.mappingYRelated % 10; + for (local_18 = this->someIndex; local_18 <= this->someLimit; local_18 = local_18 + 1) { + for (local_10 = this->someYLike; local_10 <= this->someYLikeLimit; local_10 = local_10 + 1) { + if (*(char*)(local_18 * 0x28 + 0x1f93438 + local_10) != '\0') { + local_24 = local_c; + local_c = 0; + for (; local_24 < 10; local_24 = local_24 + 1) { + this->DAT_SomeY = local_18 * 10 + local_24; + this->DAT_SomeX = local_10 * 10; + for (local_30 = 0; local_30 < 10; local_30 = local_30 + 1) { + BVar11 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, (uint)((int)(this->DAT_SomeY))); + if (((BVar11 != FALSE) + && (this->DAT_SomeTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY), + (this->LogicLayer[this->DAT_SomeTile] & 1U) != 0)) + && ((((char)this->ChangedLayer[this->DAT_SomeTile] != 0 + && (((this->LogicLayer[this->DAT_SomeTile] & 0x10000030U) == 0 + && (this->BuildingLayer[this->DAT_SomeTile] == 0)))) + && (((short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0x40) == 0)))) { + MACRO_NEIGHBOUR_FLAGS_MISC_8(m2, 0x80) + bVar19 = this->bitFlag; + MACRO_NEIGHBOUR_FLAGS_MISC_8(m3, 0x40) + bVar16 = this->bitFlag; + this->bitFlag = bVar16 & ~bVar19; + if (this->bitFlag != 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xa6] + 0x5d0; + this->PillarGFXLayer[this->DAT_SomeTile] + = (ushort)GMTotalPicturesProcessed::instance[9]; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x80; + } + } + this->DAT_SomeX = this->DAT_SomeX + 1; + } + } + } + } + } + local_c = DAT_PathFindingState::instance.mappingYRelated % 10; + for (local_18 = this->someIndex; local_18 <= this->someLimit; local_18 = local_18 + 1) { + for (local_10 = this->someYLike; local_10 <= this->someYLikeLimit; local_10 = local_10 + 1) { + if (*(char*)(local_18 * 0x28 + 0x1f93438 + local_10) != '\0') { + local_24 = local_c; + local_c = 0; + for (; local_24 < 10; local_24 = local_24 + 1) { + this->DAT_SomeY = local_18 * 10 + local_24; + this->DAT_SomeX = local_10 * 10; + for (local_30 = 0; local_30 < 10; local_30 = local_30 + 1) { + BVar11 = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, (uint)((int)(this->DAT_SomeY))); + if (((BVar11 != FALSE) + && (this->DAT_SomeTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY), + (this->LogicLayer[this->DAT_SomeTile] & L_BORDER | L_BORDER_EDGE) == 0)) + && ((char)this->ChangedLayer[this->DAT_SomeTile] != 0)) { + this->ChangedLayer[this->DAT_SomeTile] + = (char)this->ChangedLayer[this->DAT_SomeTile] - 1; + iVar17 = this->DAT_SomeX; + if ((this->LogicLayer[this->DAT_SomeTile] & L_SEA) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_RIVER) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_FORD) == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_PLAIN2_AND_PITCH) == 0) { + if (this->BuildingLayer[this->DAT_SomeTile] != 0) { + iVar13 = (int)this->BuildingLayer[this->DAT_SomeTile]; + if (DAT_BuildingsState::instance.buildings[iVar13].buildingType + == OpenSHC::Map::Buildings::BT_STOCKPILE) { + this->field112_0x554908 + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + computeWallCornerRenderRotation, + this)(this->DAT_SomeX); + this->WallGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[10] + -1 + + (short)this->field112_0x554908; + } + iVar14 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::computeClimbRampRotation, + this)(this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + BVar1 = DAT_BuildingsState::instance.buildings[iVar13] + .buildingType; + if (((BVar1 == OpenSHC::Map::Buildings::BT_FIREBALLISTA) + || ((0x4f < (short)BVar1 && ((short)BVar1 < 0x55)))) + && (iVar14 == 0)) { + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)(((((int)(uint)this + ->HeightLayer[this->DAT_SomeTile] + >> 3) + + -1) + * 0x40) + / 2) + + (short)GMTotalPicturesProcessed::instance[3] + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } else { + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] + | 0x800; + } + if (((DAT_BuildingsState::instance.buildings[iVar13] + .buildingType + == OpenSHC::Map::Buildings::BT_KILLINGPIT) + && (DAT_BuildingsState::instance.buildings[iVar13].state + < 1)) + && (( + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Game::GameStateStructures_Func::isSameTeam, + DAT_GameState::ptr)( + (int)DAT_BuildingsState::instance.buildings[iVar13] + .owner, + (int)((int)(DAT_GameSynchronyState::instance + .currentPlayerSlotID))), + BVar11 == FALSE + && (DAT_GameCore::instance.gameMode_2 + != OpenSHC::Game::GM_EDITOR)))) { + if ((this->LogicLayer[this->DAT_SomeTile] + & L_DEFAULT_EARTH_OR_TEXTURE) + == 0) + goto LAB_00510585; + this->GfxLayer[this->DAT_SomeTile] = 0; + } else { + if (DAT_BuildingsState::instance.buildings[iVar13] + .buildingType + == OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED) + goto LAB_00510585; + if (DAT_BuildingsState::instance.buildings[iVar13] + .currentTilePositionAdjusted + == this->DAT_SomeTile) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + updateBuildingGraphicsLayer, + this)(iVar13); + goto LAB_005109e8; + } + } + goto LAB_0050c199; + } + if ((this->LogicLayer[this->DAT_SomeTile] & L_KEEP_NON_MANOR_HOUSE) + == 0) { + if (((this->LogicLayer[this->DAT_SomeTile] & L_ROCKY) == 0) + || ((this->LogicLayer[this->DAT_SomeTile] + & L_WALL_OR_GATEHOUSE) + != 0)) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_BOULDERS) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_PEBBLES) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_IRON) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] + & L_OIL) + == 0) { + if ((this->LogicLayer[this->DAT_SomeTile] + & L_MOAT) + == 0) { + if (((this->LogicLayer[this + ->DAT_SomeTile] + & L_WALL_OR_GATEHOUSE + | L_FARM_FIELD_WHEAT + | L_FARM_FIELD_HOP + | L_FARM_FIELD_APPLE + | L_FARM_FIELD_DAIRY) + == 0) + || ((this->LogicLayer[this + ->DAT_SomeTile] + & L_FARM_FIELD_APPLE) + != 0)) { + sVar8 = 0; + if ((this->WallOwnerLayer[this + ->DAT_SomeTile] + & 8) + != 0) { + sVar8 = 0x2d; + } + if (((short)this->MiscDisplayLayer + [this->DAT_SomeTile] + & 0x80) + != 0) + goto LAB_0050ffbc; + if ((((this->LogicLayer[this + ->DAT_SomeTile] + & L_WALL_OR_GATEHOUSE + | L_BUILDING + | L_KEEP_NON_MANOR_HOUSE) + != 0) + || (this->BuildingWasLayer + [this->DAT_SomeTile] + == '\0')) + || (((short)this->MiscDisplayLayer + [this->DAT_SomeTile] + & 0x10) + == 0)) + goto LAB_00510585; + } + } else { + this->bitFlag = 0; + for (local_d4 = 0; local_d4 < 8; + local_d4 = local_d4 + 2) { + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY] + [local_d4]; + if ((this->LogicLayer[iVar13] + & L_MOAT) + == 0) { + if (((this->BuildingLayer + [iVar13] + != 0) + && (DAT_BuildingsState::instance + .buildings[this->BuildingLayer + [iVar13]] + .buildingType + == OpenSHC::Map:: + Buildings:: + BT_DRAWBRIDGE)) + && (iVar13 + = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + returnOwnedMoatAtTile, + this)(iVar13), + iVar13 != 0)) { + this->bitFlag + = this->bitFlag + | (byte)(0x80 + >> ((byte)local_d4 + & 0x1f)); + } + } else { + this->bitFlag = this->bitFlag + | (byte)(0x80 + >> ((byte)local_d4 + & 0x1f)); + } + } + this->bitFlag = ~this->bitFlag & 0xaa; + if (this->bitFlag == 0) { + this->bitFlag = 0; + for (local_d4 = 1; local_d4 < 8; + local_d4 = local_d4 + 2) { + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY] + [local_d4]; + if ((this->LogicLayer[iVar13] + & L_MOAT) + == 0) { + if (((this->BuildingLayer + [iVar13] + != 0) + && (DAT_BuildingsState::instance + .buildings + [this->BuildingLayer + [iVar13]] + .buildingType + == OpenSHC::Map:: + Buildings:: + BT_DRAWBRIDGE)) + && (iVar13 + = MACRO_CALL_MEMBER( + OpenSHC::Map:: + TileMapState_Func:: + returnOwnedMoatAtTile, + this)(iVar13), + iVar13 != 0)) { + this->bitFlag + = this->bitFlag + | (byte)(0x80 + >> ((byte) + local_d4 + & 0x1f)); + } + } else { + this->bitFlag + = this->bitFlag + | (byte)(0x80 + >> ((byte)local_d4 + & 0x1f)); + } + } + this->bitFlag + = ~this->bitFlag & 0x55; + } + if (this->mapOrientation == 0) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation:: + DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState:: + ptr)(this->bitFlag, 0); + } else if (this->mapOrientation == 2) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation:: + DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState:: + ptr)(this->bitFlag, 2); + } else if (this->mapOrientation == 4) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation:: + DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState:: + ptr)(this->bitFlag, 4); + } else if (this->mapOrientation == 6) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation:: + DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState:: + ptr)(this->bitFlag, 6); + } + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance[5] + + 0xcc + + (ushort)this->LuminesenceLayer + [this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer[this + ->DAT_SomeTile] + & 3); + for (local_14 = 0; local_14 < 7; + local_14 = local_14 + 1) { + if (DAT_TerrainDefinedData::instance + .field2298_0x1d64[local_14] + .unk1 + == this->bitFlag) { + sVar4 = (short) + GMTotalPicturesProcessed:: + instance[5]; + if (local_14 < 4) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0xec + + (ushort)this->LuminesenceLayer + [this->DAT_SomeTile] + * 4 + + (ushort)DAT_TerrainDefinedData:: + instance + .field2298_0x1d64 + [local_14] + .unk2; + } else if (local_14 == 4) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x10c + + (ushort)this->LuminesenceLayer + [this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer + [this->DAT_SomeTile] + & 3); + } else if (local_14 == 5) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 300 + + (ushort)this->LuminesenceLayer + [this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer + [this->DAT_SomeTile] + & 3); + } else if (local_14 == 6) { + this->GfxLayer[this + ->DAT_SomeTile] + = sVar4 + 0x14c + + (ushort)this->LuminesenceLayer + [this->DAT_SomeTile]; + } + break; + } + } + if (((short)this->MiscDisplayLayer[this + ->DAT_SomeTile] + & 0x80) + != 0) { + LAB_0050ffbc: + uVar12 + = (0xdU - this->mapOrientation) + % 8; + uVar15 = (9 - this->mapOrientation) + % 8; + iVar13 = this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY][uVar15]; + uVar2 = (ushort)iVar17; + uVar3 = (ushort)this->DAT_SomeY; + if (((short)this->MiscDisplayLayer + [this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY] + [uVar12]] + & 0x80) + == 0) { + if (((short)this + ->MiscDisplayLayer + [iVar13] + & 0x80) + == 0) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = this->AlphaGFXLayer[this + ->DAT_SomeTile] + + ((uVar2 ^ uVar3) & 1); + } else if ((this->mapOrientation + == 0) + || (this->mapOrientation + == 4)) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = ((uVar2 ^ uVar3) & 1) + + 0x33 + sVar8; + } else { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = ((uVar2 ^ uVar3) & 1) + + 0x35 + sVar8; + } + } else if ( + ((short)this->MiscDisplayLayer + [iVar13] + & 0x80) + == 0) { + if ((this->mapOrientation == 0) + || (this->mapOrientation + == 4)) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = ((uVar2 ^ uVar3) & 1) + + 0x35 + sVar8; + } else { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = ((uVar2 ^ uVar3) & 1) + + 0x33 + sVar8; + } + } else if ((this->mapOrientation + == 0) + || (this->mapOrientation + == 6)) { + uVar12 + = (int)this->DAT_SomeY % 8; + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = (0x3e - (short)uVar12) + + sVar8; + } else { + uVar12 + = (int)this->DAT_SomeY % 8; + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = (short)uVar12 + 0x37 + + sVar8; + } + MACRO_NEIGHBOUR_FLAGS_LOGIC_8( + r2, 0x200) + if (this->bitFlag != 0) { + switch (this->mapOrientation) { + case 0: + if ((this->bitFlag & 0x82) + == 0x82) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = this->AlphaGFXLayer + [this->DAT_SomeTile] + + 0xe; + } + break; + case 2: + if ((this->bitFlag & 0xa0) + == 0xa0) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = this->AlphaGFXLayer + [this->DAT_SomeTile] + + 0xe; + } + break; + case 4: + if ((this->bitFlag & 0x28) + == 0x28) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = this->AlphaGFXLayer + [this->DAT_SomeTile] + + 0xe; + } + break; + case 6: + if ((this->bitFlag & 10) + == 10) { + this->AlphaGFXLayer[this + ->DAT_SomeTile] + = this->AlphaGFXLayer + [this->DAT_SomeTile] + + 0xe; + } + } + } + if ((this->LogicLayer[this + ->DAT_SomeTile] + & L_PLAIN2_AND_PITCH + | L_MOAT) + == 0) + goto LAB_00510585; + } + } + } else { + this->PillarGFXLayer[this + ->DAT_SomeTile] = (ushort) + GMTotalPicturesProcessed::instance[9]; + this->field112_0x554908 + = (int)(short)this + ->RandomLayer[this->DAT_SomeTile] + & 7; + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed:: + instance[0x37] + + 0x428 + + (short)(this->field112_0x554908 << 4); + sVar4 = (short)this->DAT_SomeY; + sVar5 = (short)this->DAT_SomeX; + if (this->field112_0x554908 == 0) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0x1ff; + } else if (this->field112_0x554908 == 1) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0x1ff; + } else if (this->field112_0x554908 == 2) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0x7f; + } else if (this->field112_0x554908 == 3) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0x7f; + } else if (this->field112_0x554908 == 4) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0xff; + } else if (this->field112_0x554908 == 5) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0xff; + } else if (this->field112_0x554908 == 6) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0x7f; + } else if (this->field112_0x554908 == 7) { + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this + ->DAT_SomeTile] + + sVar5 + sVar4 + & 0x7f; + } + } + } else { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func:: + computeClimbRampRotation, + this)(this->DAT_SomeTile, + (uint)((int)(this->DAT_SomeX)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short) + GMTotalPicturesProcessed::instance[9] + + -1 + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this + ->MiscDisplayLayer[this->DAT_SomeTile] + | 0x800; + this->field112_0x554908 + = (int)(short)this + ->RandomLayer[this->DAT_SomeTile] + & 0xf; + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance + [0xc] + + 0xee + (short)this->field112_0x554908; + } + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + computeClimbRampRotation, + this)(this->DAT_SomeTile, + (uint)((int)(this->DAT_SomeX)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + + -1 + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this + ->MiscDisplayLayer[this->DAT_SomeTile] + | 0x800; + this->field112_0x554908 + = (int)(short)this + ->RandomLayer[this->DAT_SomeTile] + & 0xf; + if (this->LuminesenceLayer[this->DAT_SomeTile] + < 6) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance + [0xc] + + 0x20 + (short)this->field112_0x554908; + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance + [0xc] + + 0x30 + (short)this->field112_0x554908; + } + } + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + computeClimbRampRotation, + this)(this->DAT_SomeTile, + (uint)((int)(this->DAT_SomeX)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] + | 0x800; + this->field112_0x554908 + = (int)(short)this->RandomLayer[this->DAT_SomeTile] + & 0xf; + if (this->LuminesenceLayer[this->DAT_SomeTile] < 6) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + + 0x40 + (short)this->field112_0x554908; + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xc] + + 0x50 + (short)this->field112_0x554908; + } + } + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + computeClimbRampRotation, + this)(this->DAT_SomeTile, + (uint)((int)(this->DAT_SomeX)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] + | 0x800; + iVar13 = (int)this->OrganismLayer[this->DAT_SomeTile]; + if (1999 < iVar13) { + if (*(int*)(&DAT_LandscapeState::instance.trees[0x635] + .unknownDistanceRelatedToCrow + + iVar13 * 0x10) + == this->DAT_SomeTile) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + applyRockGraphicsToFootprint, + this)(iVar13 + -2000); + } + goto LAB_0050c199; + } + this->field112_0x554908 + = (int)(short)this->RandomLayer[this->DAT_SomeTile] & 7; + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0x38] + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 8 + + (short)this->field112_0x554908; + } + } else { + if (DAT_BuildingsState::instance + .buildings[this->BuildingLayer[this->DAT_SomeTile]] + .currentTilePositionAdjusted + != this->DAT_SomeTile) + goto LAB_0050c199; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + updateBuildingGraphicsLayer, + this)((int)this->BuildingLayer[this->DAT_SomeTile]); + } + } else { + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xdfff; + iVar13 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::getPitchDitchIDForTile, this)( + this->DAT_SomeTile); + if (iVar13 != 0) { + if (((this->pitchDitches[iVar13].state < 2) + && (BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Game::GameStateStructures_Func:: + isSameTeam, + DAT_GameState::ptr)( + (int)this->pitchDitches[iVar13].owner, + (int)((int)(DAT_GameSynchronyState::instance + .currentPlayerSlotID))), + BVar11 == FALSE)) + && (DAT_GameCore::instance.gameMode_2 + != OpenSHC::Game::GM_EDITOR)) { + if ((this->LogicLayer[this->DAT_SomeTile] + & L_DEFAULT_EARTH_OR_TEXTURE) + == 0) + goto LAB_00510585; + this->GfxLayer[this->DAT_SomeTile] = 0; + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func:: + computeClimbRampRotation, + this)(this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] + | 0x800; + this->GfxLayer[this->DAT_SomeTile] + = (this->pitchDitches[iVar13].rng & 7U) + + (short)GMTotalPicturesProcessed::instance[0x8c]; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] + | 0x2000; + if (((short)this->MiscDisplayLayer[this->DAT_SomeTile] + & 0x80) + != 0) + goto LAB_0050ffbc; + } + goto LAB_0050c199; + } + LAB_00510585: + uVar12 + = (((int)(uint)this->HeightLayer[this->DAT_SomeTile] >> 3) - 1) + % 16; + local_8 = uVar12 * 0x40 + 1; + iVar13 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::computeClimbRampRotation, + this)(this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + if ((this->HeightLayer[this->DAT_SomeTile] < 0x88) + && (iVar13 == 0)) { + if (local_8 < 1) { + local_8 = 1; + } + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)((local_8 + -1) / 2) + + (short)GMTotalPicturesProcessed::instance[3] + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + BVar11 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::isCliffDropInDirection, + this)(this->DAT_SomeTile, (undefined4)((int)(iVar17)), + this->DAT_SomeY); + if (BVar11 == FALSE) { + bVar10 = false; + } else { + bVar10 = true; + } + } else { + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[9] + -1 + + (short)this->field112_0x554908; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x800; + bVar10 = true; + } + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 0x20) == 0) { + if (this->HeightLayer[this->DAT_SomeTile] < 8) { + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 2) + == 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[2] + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 3); + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0x37] + + 0xb4 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 7); + } + } else if ((this->LogicLayer[this->DAT_SomeTile] + & L_DEFAULT_EARTH_OR_TEXTURE) + == 0) { + if ((bVar10) + && (DAT_GameCore::instance.gameMode_2 + == OpenSHC::Game::GM_EDITOR)) { + this->LogicLayer[this->DAT_SomeTile] + = this->LogicLayer[this->DAT_SomeTile] | 128; + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0x38] + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 8 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 7); + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[2] + + ((short)((int)(uint)this + ->HeightLayer[this->DAT_SomeTile] + >> 3) + + -1) + * 0x20 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 3); + } + } else { + /* + sets section 1001 after placing oasis grass for example + */ + this->GfxLayer[this->DAT_SomeTile] = 0; + } + } else { + this->GfxLayer[this->DAT_SomeTile] = 0; + } + } + } else { + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(c1, NEIGHBOUR_FLAGS_MASK_RIVER_OR_FORD) + bVar19 = this->bitFlag; + this->bitFlag = ~bVar19 & 0xaa; + if (this->bitFlag == 0) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8( + d1, NEIGHBOUR_FLAGS_MASK_RIVER_OR_FORD) + this->bitFlag = ~this->bitFlag; + this->bitFlag = this->bitFlag & 0x55; + } + if (this->mapOrientation == 0) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 0); + } else if (this->mapOrientation == 2) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 2); + } else if (this->mapOrientation == 4) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 4); + } else if (this->mapOrientation == 6) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 6); + } + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[5] + 0x29c + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 8 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 7); + for (local_14 = 0; local_14 < 7; local_14 = local_14 + 1) { + if (DAT_TerrainDefinedData::instance.field2298_0x1d64[local_14].unk1 + == this->bitFlag) { + sVar4 = (short)GMTotalPicturesProcessed::instance[5]; + if (local_14 < 4) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x234 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + (ushort)DAT_TerrainDefinedData::instance + .field2298_0x1d64[local_14] + .unk2; + } else if (local_14 == 4) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x254 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } else if (local_14 == 5) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x274 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } else if (local_14 == 6) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x294 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile]; + } + break; + } + } + } + } else { + this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] & 0xf7; + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(c2, 1) + bVar19 = this->bitFlag; + if ((bVar19 != 0) + && (this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] | 8, + ((char)this->Logic2Layer[this->DAT_SomeTile] & 0x20) != 0)) { + this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] & 0xdf; + this->WallGFXLayer[this->DAT_SomeTile] + = (short)this->RandomLayer[this->DAT_SomeTile] & 7; + } + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(c3, 0x100031) + bVar19 = this->bitFlag; + this->bitFlag = ~bVar19 & 0xaa; + if (this->bitFlag == 0) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(d2, 0x100031) + this->bitFlag = ~this->bitFlag; + this->bitFlag = this->bitFlag & 0x55; + } + if (this->mapOrientation == 0) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 0); + } else if (this->mapOrientation == 2) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 2); + } else if (this->mapOrientation == 4) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 4); + } else if (this->mapOrientation == 6) { + this->bitFlag = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + rotateByteLeft, + DAT_DirectionAlgorithmState::ptr)(this->bitFlag, 6); + } + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::propagateCliffEdgeFlagFromNeighbor, + this)(this->DAT_SomeTile, iVar17, this->DAT_SomeY); + if ((char)this->Logic2Layer[this->DAT_SomeTile] == 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[5] + 0x214 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + for (local_14 = 0; local_14 < 7; local_14 = local_14 + 1) { + if (DAT_TerrainDefinedData::instance.field2298_0x1d64[local_14].unk1 + == this->bitFlag) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(c4, 0x200000) + sVar4 = (short)GMTotalPicturesProcessed::instance[5]; + if (local_14 < 4) { + if (this->bitFlag == 0) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x234 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 4 + + (ushort)DAT_TerrainDefinedData::instance + .field2298_0x1d64[local_14] + .unk2; + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x2dc + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 4 + + (ushort)DAT_TerrainDefinedData::instance + .field2298_0x1d64[local_14] + .unk2; + } + } else if (this->bitFlag == 0) { + if (local_14 == 4) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x254 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 3); + } else if (local_14 == 5) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x274 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] + * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] + & 3); + } else if (local_14 == 6) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x294 + + (ushort)this + ->LuminesenceLayer[this->DAT_SomeTile]; + } + } + break; + } + } + } + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 0x20) != 0) { + sVar4 = (this->WallGFXLayer[this->DAT_SomeTile] & 8) * 8 + 0x38d; + sVar5 = (short)GMTotalPicturesProcessed::instance[5]; + if (this->mapOrientation == 0) { + this->GfxLayer[this->DAT_SomeTile] = sVar5 + sVar4 + -1 + + (short)((int)(this->WallGFXLayer[this->DAT_SomeTile] & 0x3e0) + >> 5) + * 8; + } else if (this->mapOrientation == 2) { + this->GfxLayer[this->DAT_SomeTile] = sVar5 + sVar4 + -1 + + ((short)((int)(this->WallGFXLayer[this->DAT_SomeTile] & 0x3e0) + >> 5) + - 2U + & 7) + * 8; + } else if (this->mapOrientation == 4) { + this->GfxLayer[this->DAT_SomeTile] = sVar5 + sVar4 + -1 + + ((short)((int)(this->WallGFXLayer[this->DAT_SomeTile] & 0x3e0) + >> 5) + - 4U + & 7) + * 8; + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar5 + sVar4 + -1 + + ((short)((int)(this->WallGFXLayer[this->DAT_SomeTile] & 0x3e0) + >> 5) + - 6U + & 7) + * 8; + } + } + sVar4 = (short)GMTotalPicturesProcessed::instance[5]; + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 0x50) == 0) { + if (((int)(char)this->Logic2Layer[this->DAT_SomeTile] & 0x88U) == 0) { + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 1) == 0) { + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 2) == 0) { + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 4) + != 0) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x41c; + } + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x414; + } + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x40c; + } + } else { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(m4, 1) + bVar19 = this->bitFlag; + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(r3, 0x50) + bVar16 = this->bitFlag; + this->bitFlag = bVar16 | bVar19; + local_c0 = 0xffffffff; + if (this->bitFlag == 0) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(m5, 0x100000) + if (this->bitFlag != 0) { + for (local_14 = 0; local_14 < 8; local_14 = local_14 + 1) { + if ((((uint)this->bitFlag + & 1 << ((byte)local_14 & 0x1f)) + != 0) + && (this->HeightLayer[this->DAT_SomeTile + + this->directionTranslationMatrix + [this->DAT_SomeY] + [DAT_TerrainDefinedData::instance + .field2475_0x370c[local_14]]] + <= this->HeightLayer[this->DAT_SomeTile])) { + this->bitFlag = this->bitFlag + & ~(byte)(1 << ((byte)local_14 & 0x1f)); + } + } + if (this->bitFlag != 0) { + if ((this->bitFlag & 0x80) == 0) { + if ((this->bitFlag & 0x20) == 0) { + if ((this->bitFlag & 8) == 0) { + if ((this->bitFlag & 2) == 0) { + if (this->bitFlag == 0x41) { + local_c0 = 4; + } else if (this->bitFlag == 0x50) { + local_c0 = 6; + } else if (this->bitFlag == 0x14) { + local_c0 = 0; + } else if (this->bitFlag == 5) { + local_c0 = 2; + } else if ((this->bitFlag & 0x40) + == 0) { + if ((this->bitFlag & 1) == 0) { + if ((this->bitFlag & 4) == 0) { + if ((this->bitFlag & 0x10) + != 0) { + local_c0 = 7; + } + } else { + local_c0 = 1; + } + } else { + local_c0 = 3; + } + } else { + local_c0 = 5; + } + } else if ((this->bitFlag & 0x80) == 0) { + if ((this->bitFlag & 8) == 0) { + if ((this->bitFlag & 5) == 5) { + local_c0 = 2; + } else { + local_c0 = 2; + } + } else { + local_c0 = 1; + } + } else { + local_c0 = 3; + } + } else if ((this->bitFlag & 2) == 0) { + if ((this->bitFlag & 0x20) == 0) { + if ((this->bitFlag & 0x14) == 0x14) { + local_c0 = 0; + } else { + local_c0 = 0; + } + } else { + local_c0 = 7; + } + } else { + local_c0 = 1; + } + } else if ((this->bitFlag & 8) == 0) { + if ((this->bitFlag & 0x80) == 0) { + if ((this->bitFlag & 0x50) == 0x50) { + local_c0 = 6; + } else { + local_c0 = 6; + } + } else { + local_c0 = 5; + } + } else { + local_c0 = 7; + } + } else if ((this->bitFlag & 0x20) == 0) { + if ((this->bitFlag & 2) == 0) { + if ((this->bitFlag & 0x41) == 0x41) { + local_c0 = 4; + } else { + local_c0 = 4; + } + } else { + local_c0 = 3; + } + } else { + local_c0 = 5; + } + local_c0 = ((local_c0 - this->mapOrientation) + 8) % 8; + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x30c + (short)(local_c0 << 4); + } + } + if (local_c0 == 0xffffffff) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[5] + 0x214 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] & 0x7f; + } + } else { + if ((this->bitFlag & 0x80) == 0) { + if ((this->bitFlag & 0x20) == 0) { + if ((this->bitFlag & 8) == 0) { + if ((this->bitFlag & 2) == 0) { + if (this->bitFlag == 0x40) { + local_c0 = 5; + } else if (this->bitFlag == 1) { + local_c0 = 3; + } else if (this->bitFlag == 4) { + local_c0 = 1; + } else if (this->bitFlag == 0x10) { + local_c0 = 7; + } else if (this->bitFlag == 0x41) { + local_c0 = 4; + } else if (this->bitFlag == 0x50) { + local_c0 = 6; + } else if (this->bitFlag == 0x14) { + local_c0 = 0; + } else if (this->bitFlag == 5) { + local_c0 = 2; + } + } else if ((this->bitFlag & 0x80) == 0) { + if ((this->bitFlag & 8) == 0) { + if ((this->bitFlag & 5) == 5) { + local_c0 = 2; + } else { + local_c0 = 2; + } + } else { + local_c0 = 1; + } + } else { + local_c0 = 3; + } + } else if ((this->bitFlag & 2) == 0) { + if ((this->bitFlag & 0x20) == 0) { + if ((this->bitFlag & 0x14) == 0x14) { + local_c0 = 0; + } else { + local_c0 = 0; + } + } else { + local_c0 = 7; + } + } else { + local_c0 = 1; + } + } else if ((this->bitFlag & 8) == 0) { + if ((this->bitFlag & 0x80) == 0) { + if ((this->bitFlag & 0x50) == 0x50) { + local_c0 = 6; + } else { + local_c0 = 6; + } + } else { + local_c0 = 5; + } + } else { + local_c0 = 7; + } + } else if ((this->bitFlag & 0x20) == 0) { + if ((this->bitFlag & 2) == 0) { + if ((this->bitFlag & 0x41) == 0x41) { + local_c0 = 4; + } else { + local_c0 = 4; + } + } else { + local_c0 = 3; + } + } else { + local_c0 = 5; + } + if (local_c0 == 0xffffffff) { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x214 + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] & 0x7f; + } else { + uVar12 = ((local_c0 - this->mapOrientation) + 8) % 8; + this->GfxLayer[this->DAT_SomeTile] + = sVar4 + 0x30c + (short)(uVar12 << 4); + } + } + } + } else { + this->GfxLayer[this->DAT_SomeTile] = sVar4 + 0x2fc; + } + } + LAB_005109e8: + if ((this->LogicLayer[this->DAT_SomeTile] & 0x10000100U) != 0) { + bVar19 = this->DefaultHeightLayer[this->DAT_SomeTile]; + iVar17 = MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::computeClimbRampRotation, this)( + this->DAT_SomeTile, (uint)((int)(iVar17)), + (uint)((int)(this->DAT_SomeY))); + sVar5 = (short)GMTotalPicturesProcessed::instance[9]; + sVar4 = (short)this->field112_0x554908; + if (this->DefaultHeightLayer[this->DAT_SomeTile] < 0x88) { + if (iVar17 == 0) { + if (((char)this->Logic2Layer[this->DAT_SomeTile] & 2) == 0) { + this->PillarGFXLayer[this->DAT_SomeTile] + = (short)(((((int)(uint)bVar19 >> 3) + -1) * 0x40) / 2) + + (short)GMTotalPicturesProcessed::instance[3] + + (ushort)this->LuminesenceLayer[this->DAT_SomeTile] * 4 + + ((short)this->RandomLayer[this->DAT_SomeTile] & 3); + } else { + this->PillarGFXLayer[this->DAT_SomeTile] = sVar5 + -1 + sVar4; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x800; + } + } else { + this->PillarGFXLayer[this->DAT_SomeTile] = sVar5 + -1 + sVar4; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x800; + } + } else { + this->PillarGFXLayer[this->DAT_SomeTile] = sVar5 + -1 + sVar4; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x800; + } + } + } else { + this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] & 0xf7; + MACRO_NEIGHBOUR_FLAGS_LOGIC_4(c5, 0x100000) + if (this->bitFlag == 0) { + if (((short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xc0) == 0) { + MACRO_NEIGHBOUR_FLAGS_MISC_8(m6, 0x40) + bVar19 = this->bitFlag; + MACRO_NEIGHBOUR_FLAGS_MISC_8(m7, 0x80) + bVar16 = this->bitFlag; + this->bitFlag = bVar16 & ~bVar19; + if (this->bitFlag != 0) { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xa6] + 0x5c4; + this->PillarGFXLayer[this->DAT_SomeTile] + = (ushort)GMTotalPicturesProcessed::instance[9]; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0xc0; + } + } + if (2 < this->LuminesenceLayer[this->DAT_SomeTile]) { + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xffbf; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xff7f; + } + local_38 = this->LuminesenceLayer[this->DAT_SomeTile] - 2; + if (local_38 < 0) { + local_38 = 0; + } + sVar4 = (short)local_38; + if (((short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0xc0) == 0) { + if (((short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0x100) == 0) { + MACRO_NEIGHBOUR_FLAGS_MISC_8(m8, 0x100) + if (this->bitFlag != 0) { + local_ac = 2; + uVar12 = (uint)this->bitFlag + << ((byte)this->mapOrientation & 0x1f); + uVar12 = uVar12 & 0xff | (int)uVar12 >> 8; + switch (uVar12) { + case 1: + local_b0 = 0x1c; + break; + case 2: + case 3: + case 6: + case 7: + local_b0 = 0xc; + local_ac = 4; + break; + case 4: + local_b0 = 0x1a; + break; + default: + local_b4 = 0; + for (local_b8 = 0; local_b8 < 8; local_b8 = local_b8 + 1) { + if ((1 << ((byte)local_b8 & 0x1f) & uVar12) != 0) { + local_b4 = local_b4 + 1; + } + } + if (local_b4 < 4) { + local_b0 = 0x20; + } else { + local_b0 = 0x21; + } + local_ac = 1; + break; + case 8: + case 0xc: + case 0x18: + case 0x1c: + local_b0 = 8; + local_ac = 4; + break; + case 0xe: + case 0xf: + case 0x1e: + case 0x1f: + local_b0 = 0x16; + break; + case 0x10: + local_b0 = 0x18; + break; + case 0x20: + case 0x30: + case 0x60: + case 0x70: + local_b0 = 4; + local_ac = 4; + break; + case 0x38: + case 0x3c: + case 0x78: + case 0x7c: + local_b0 = 0x14; + break; + case 0x40: + local_b0 = 0x1e; + break; + case 0x80: + case 0x81: + case 0xc0: + case 0xc1: + local_b0 = 0; + local_ac = 4; + break; + case 0x83: + case 0x87: + case 0xc3: + case 199: + local_b0 = 0x10; + break; + case 0xe0: + case 0xe1: + case 0xf0: + case 0xf1: + local_b0 = 0x12; + } + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xa6] + + (short)((int)(short)this->RandomLayer[this->DAT_SomeTile] + % local_ac) + + local_b0 + 8 + sVar4 * 0xf6; + this->MiscDisplayLayer[this->DAT_SomeTile] + = (short)this->MiscDisplayLayer[this->DAT_SomeTile] | 0x200; + } + } else { + local_94 = 2; + uVar15 = (int)this->CertainPathLayer[this->DAT_SomeTile] & 0xff; + uVar12 = uVar15 << ((byte)this->mapOrientation & 0x1f); + uVar12 = uVar12 & 0xff | (int)uVar12 >> 8; + switch (uVar12) { + case 1: + local_9c = 0x1c; + break; + case 2: + case 3: + case 6: + case 7: + local_9c = 0xc; + local_94 = 4; + break; + case 4: + local_9c = 0x1a; + break; + default: + local_a0 = 0; + for (local_a4 = 0; local_a4 < 8; local_a4 = local_a4 + 1) { + if ((1 << ((byte)local_a4 & 0x1f) & uVar12) != 0) { + local_a0 = local_a0 + 1; + } + } + if (local_a0 < 4) { + local_9c = 0x20; + } else { + local_9c = 0x21; + } + local_94 = 1; + break; + case 8: + case 0xc: + case 0x18: + case 0x1c: + local_9c = 8; + local_94 = 4; + break; + case 0xe: + case 0xf: + case 0x1e: + case 0x1f: + local_9c = 0x16; + break; + case 0x10: + local_9c = 0x18; + break; + case 0x20: + case 0x30: + case 0x60: + case 0x70: + local_9c = 4; + local_94 = 4; + break; + case 0x38: + case 0x3c: + case 0x78: + case 0x7c: + local_9c = 0x14; + break; + case 0x40: + local_9c = 0x1e; + break; + case 0x80: + case 0x81: + case 0xc0: + case 0xc1: + local_9c = 0; + local_94 = 4; + break; + case 0x83: + case 0x87: + case 0xc3: + case 199: + local_9c = 0x10; + break; + case 0xe0: + case 0xe1: + case 0xf0: + case 0xf1: + local_9c = 0x12; + } + local_9c = (short)((int)(short)this->RandomLayer[this->DAT_SomeTile] + % local_94) + + local_9c; + if (uVar15 + != ((int)this->CertainPathLayer[this->DAT_SomeTile] >> 8 + & 0xffU)) { + local_9c = local_9c + 0x66; + } + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[0xa6] + local_9c + + 0x2a + sVar4 * 0xf6; + } + } + if (((short)this->MiscDisplayLayer[this->DAT_SomeTile] & 0x3c0) == 0) { + this->GfxLayer[this->DAT_SomeTile] + = ((short)this->RandomLayer[this->DAT_SomeTile] & 7) + + (short)GMTotalPicturesProcessed::instance[0xa6] + sVar4 * 0xf6; + this->PillarGFXLayer[this->DAT_SomeTile] + = (ushort)GMTotalPicturesProcessed::instance[9]; + } + } else { + this->GfxLayer[this->DAT_SomeTile] + = (short)GMTotalPicturesProcessed::instance[5] + 0x2fc; + this->Logic2Layer[this->DAT_SomeTile] + = (char)this->Logic2Layer[this->DAT_SomeTile] | 8; + } + } + } + LAB_0050c199: + this->DAT_SomeX = this->DAT_SomeX + 1; + } + } + } + } + } + } + return; + } + +} +} + +#pragma optimize("", on) diff --git a/src/OpenSHC/Map/TileMapState/updateLogicalTileMapRelatedSections.cpp b/src/OpenSHC/Map/TileMapState/updateLogicalTileMapRelatedSections.cpp new file mode 100644 index 000000000..065b1b062 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateLogicalTileMapRelatedSections.cpp @@ -0,0 +1,347 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_BEACH; + using OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + using OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_HIGH; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_MEDIUM; + using OpenSHC::Map::LogicHelpers::L2_SCRUB; + using OpenSHC::Map::LogicHelpers::L2_STONES_OR_DRIVEN_SANDUnk; + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_CRENEL; + using OpenSHC::Map::LogicHelpers::L_DEFAULT_EARTH_OR_TEXTURE; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_HOP; + using OpenSHC::Map::LogicHelpers::L_FARM_FIELD_WHEAT; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_STAIRS; + using OpenSHC::Map::LogicHelpers::L_STOCKPILEUnk; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F70F0 + void TileMapState::updateLogicalTileMapRelatedSections() + { + if (this->forceUpdateLogicalAndMiscDisplayLayers == 0) { + return; + } + this->forceUpdateLogicalAndMiscDisplayLayers = 0; + int tile = DAT_ViewportRenderState::instance + .translationMatrix[DAT_PathFindingState::instance.mappingYRelated] + .firstTileOfRow; + DAT_BuildingsState::instance.field34_0x18e074 = 1; + for (int row = DAT_PathFindingState::instance.mappingYRelated; + row < 400 && row <= DAT_PathFindingState::instance.yLimit; row++) { + for (int column = 0; column < this->yArray1[row]; column++, tile++) { + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) != 0 + || this->ChangedLayer[tile] == 0) { + continue; + } + + uint height = this->HeightLayer[tile]; + int screenY = this->heightBasedScreenYOffset[height]; + this->ShowHiLayer[tile] = '\0'; + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffef; + /* whether the tile draws its own texture or the default earth */ + if (height == 0) { + if (((this->LogicLayer[tile] & L_RIVER) == 0 + && (this->Logic2Layer[tile] & L2_BEACH) != 0) + || (this->LogicLayer[tile] & L_MOAT) != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_DEFAULT_EARTH_OR_TEXTURE; + } else { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_DEFAULT_EARTH_OR_TEXTURE; + } + } else if (height < 9) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_DEFAULT_EARTH_OR_TEXTURE; + } else if ((this->Logic2Layer[tile] & L2_PLATEAU_MEDIUM) != 0 + || (this->Logic2Layer[tile] & L2_PLATEAU_HIGH) != 0 + || (this->Logic2Layer[tile] & L2_EARTH_AND_STONES) != 0 + || (this->Logic2Layer[tile] & L2_OASIS_GRASS) != 0 + || (this->Logic2Layer[tile] & L2_SCRUB) != 0 + || (char)this->Logic2Layer[tile] < '\0' + || (this->Logic2Layer[tile] & L2_STONES_OR_DRIVEN_SANDUnk) != 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] | L_DEFAULT_EARTH_OR_TEXTURE; + } else { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_DEFAULT_EARTH_OR_TEXTURE; + } + + /* a tile level with all eight neighbours keeps its texture */ + for (int i = 0; i < 8; i++) { + int neighbour = this->directionTranslationMatrix[row][i] + tile; + if ((height > 8 || this->HeightLayer[neighbour] == 0) + && height != this->HeightLayer[neighbour]) { + if ((this->LogicLayer[neighbour] & (L_RIVER | L_MOAT)) == 0) { + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_DEFAULT_EARTH_OR_TEXTURE; + } + break; + } + } + if (this->BuildingLayer[tile] != 0 + && DAT_BuildingsState::instance.buildings[this->BuildingLayer[tile]].buildingType + == OpenSHC::Map::Buildings::BT_DRAWBRIDGE) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x10; + } + + if (this->field93_0x5548c8 == 0) { + /* look up the light's diagonal for anything standing high enough to shadow us */ + int walkTile = tile; + int* direction = this->directionTranslationMatrix[row] + this->field84_0x5548a4; + bool shadowed = false; + for (int depth = 0; depth < 0x80; depth = depth + 0x10) { + walkTile = walkTile + *direction; + if ((this->LogicLayer[walkTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + break; + } + if (screenY <= (this->heightBasedScreenYOffset[this->HeightLayer[walkTile]] - depth) + -0x10) { + this->ShowHiLayer[tile] = 0xff; + shadowed = true; + break; + } + direction = direction + this->field88_0x5548b4 * 8; + } + if (height == 0 || shadowed) { + continue; + } + } else { + this->ShowHiLayer[tile] = '\b'; + } + + bool occluded = true; + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 + && (this->LogicLayer[tile] & L_TREE_VARIATION) == 0 + && (this->LogicLayer[tile] & L_FARM_FIELD_WHEAT) == 0 + && (this->LogicLayer[tile] & L_FARM_FIELD_HOP) == 0 + && (this->LogicLayer[tile] & L_CRENEL) == 0 + && (this->LogicLayer[tile] & L_STAIRS) == 0) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x10; + occluded = false; + } + for (int i = 0; i < 8; i++) { + int neighbour = this->directionTranslationMatrix[row][i] + tile; + if ((height > 8 && height != this->HeightLayer[neighbour]) + || (this->LogicLayer[neighbour] & L_TREE_VARIATION) != 0) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffef; + break; + } + if ((this->LogicLayer[neighbour] & L_FARM_FIELD_WHEAT) != 0) { + occluded = true; + } + if ((this->LogicLayer[neighbour] & L_FARM_FIELD_HOP) != 0) { + occluded = true; + } + } + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xfffc; + + bool stepLeft = false; + bool stepRight = false; + int shadowTile = this->directionTranslationMatrix[row][this->field84_0x5548a4] + tile; + int lowest = 0xfa; + if (this->heightBasedScreenYOffset[this->HeightLayer[shadowTile]] < 0xfa) { + lowest = this->heightBasedScreenYOffset[this->HeightLayer[shadowTile]]; + } + int nearly = screenY + 2; + if (nearly < this->heightBasedScreenYOffset[this->HeightLayer[shadowTile]]) { + occluded = true; + } + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0) { + if ((this->LogicLayer[shadowTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[shadowTile]) + != 0) { + occluded = true; + } + int rightTile = this->directionTranslationMatrix[row][this->field86_0x5548ac] + tile; + if (((this->LogicLayer[rightTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[rightTile]) + != 0) + || ((this->LogicLayer[rightTile] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[rightTile] & L_STOCKPILEUnk) == 0)) { + occluded = true; + } + int leftTile = this->directionTranslationMatrix[row][this->field85_0x5548a8] + tile; + if (((this->LogicLayer[leftTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[leftTile]) + != 0) + || ((this->LogicLayer[leftTile] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[leftTile] & L_STOCKPILEUnk) == 0)) { + occluded = true; + } + int rightTile2 = this->directionTranslationMatrix[this->field88_0x5548b4 + row][this->field86_0x5548ac] + shadowTile; + if (((this->LogicLayer[rightTile2] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[rightTile2]) + != 0) + || ((this->LogicLayer[rightTile2] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[rightTile2] & L_STOCKPILEUnk) == 0)) { + occluded = true; + } + int leftTile2 = this->directionTranslationMatrix[this->field88_0x5548b4 + row][this->field85_0x5548a8] + shadowTile; + if (((this->LogicLayer[leftTile2] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[leftTile2]) + != 0) + || ((this->LogicLayer[leftTile2] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[leftTile2] & L_STOCKPILEUnk) == 0)) { + occluded = true; + } + shadowTile = shadowTile + + this->directionTranslationMatrix[this->field88_0x5548b4 + row][this->field84_0x5548a4]; + if ((this->LogicLayer[shadowTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[shadowTile]) + != 0) { + occluded = true; + } + int rightTile3 = this->directionTranslationMatrix[row + this->field88_0x5548b4 * 2][this->field86_0x5548ac] + shadowTile; + if (((this->LogicLayer[rightTile3] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[rightTile3]) + != 0) + || ((this->LogicLayer[rightTile3] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[rightTile3] & L_STOCKPILEUnk) == 0)) { + occluded = true; + } + int leftTile3 = this->directionTranslationMatrix[row + this->field88_0x5548b4 * 2][this->field85_0x5548a8] + shadowTile; + if (((this->LogicLayer[leftTile3] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[leftTile3]) + != 0) + || ((this->LogicLayer[leftTile3] & L_WALL_OR_GATEHOUSE) != 0 + && (this->LogicLayer[leftTile3] & L_STOCKPILEUnk) == 0)) { + occluded = true; + } + shadowTile = shadowTile + + this->directionTranslationMatrix[row + this->field88_0x5548b4 * 2][this->field84_0x5548a4]; + if ((this->LogicLayer[shadowTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[shadowTile]) + != 0) { + occluded = true; + } + } else { + if (DAT_BuildingsState::instance.buildings[this->BuildingLayer[shadowTile]].flag2 != 0) { + occluded = true; + } + if (DAT_BuildingsState::instance.buildings[this->BuildingLayer[shadowTile]].buildingType + == OpenSHC::Map::Buildings::BT_STOCKPILE) { + occluded = true; + } + if (DAT_BuildingsState::instance + .buildings[this->BuildingLayer[this->directionTranslationMatrix + [this->field88_0x5548b4 + row] + [this->field84_0x5548a4] + + shadowTile]] + .flag2 + != 0) { + occluded = true; + } + } + + int leftTile = this->directionTranslationMatrix[row][this->field86_0x5548ac] + tile; + if (this->heightBasedScreenYOffset[this->HeightLayer[leftTile]] < lowest) { + lowest = this->heightBasedScreenYOffset[this->HeightLayer[leftTile]]; + } + if (screenY <= this->heightBasedScreenYOffset[this->HeightLayer[leftTile]]) { + stepLeft = true; + if (nearly < this->heightBasedScreenYOffset[this->HeightLayer[leftTile]]) { + occluded = true; + } + } + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0) { + if ((this->LogicLayer[leftTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) != 0 + && MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[leftTile]) + != 0) { + occluded = true; + } + } else { + if (DAT_BuildingsState::instance.buildings[this->BuildingLayer[leftTile]].flag2 != 0) { + occluded = true; + } + if (DAT_BuildingsState::instance.buildings[this->BuildingLayer[leftTile]].buildingType + == OpenSHC::Map::Buildings::BT_STOCKPILE) { + occluded = true; + } + } + + int rightTile = this->directionTranslationMatrix[row][this->field85_0x5548a8] + tile; + if (this->heightBasedScreenYOffset[this->HeightLayer[rightTile]] < lowest) { + lowest = this->heightBasedScreenYOffset[this->HeightLayer[rightTile]]; + } + if (screenY <= this->heightBasedScreenYOffset[this->HeightLayer[rightTile]]) { + stepRight = true; + if (nearly < this->heightBasedScreenYOffset[this->HeightLayer[rightTile]]) { + occluded = true; + } + } + bool clear; + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0) { + if ((this->LogicLayer[tile] & L_KEEP_NON_MANOR_HOUSE) != 0 + || (this->LogicLayer[rightTile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 + || MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::getBuildingFlag1, DAT_BuildingsState::ptr)( + this->BuildingLayer[rightTile]) + == 0) { + clear = occluded; + } else { + clear = true; + } + } else { + if (DAT_BuildingsState::instance.buildings[this->BuildingLayer[rightTile]].flag2 != 0) { + occluded = true; + } + if (DAT_BuildingsState::instance.buildings[this->BuildingLayer[rightTile]].buildingType + == OpenSHC::Map::Buildings::BT_STOCKPILE) { + clear = true; + } else { + clear = occluded; + } + } + if (clear) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] & 0xffef; + } else { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 0x10; + } + + if (lowest < screenY) { + if (this->field93_0x5548c8 == 0) { + this->ShowHiLayer[tile] = (char)screenY - (char)lowest; + } + if ((this->LogicLayer[tile] & L_SEA) == 0) { + if (stepLeft) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 1; + } + if (stepRight) { + this->MiscDisplayLayer[tile] = this->MiscDisplayLayer[tile] | 2; + } + } + } + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateMacroLayer.cpp b/src/OpenSHC/Map/TileMapState/updateMacroLayer.cpp new file mode 100644 index 000000000..da8a73e08 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateMacroLayer.cpp @@ -0,0 +1,45 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L2_BEACH; + using OpenSHC::Map::LogicHelpers::L2_EARTH_AND_STONES; + using OpenSHC::Map::LogicHelpers::L2_OASIS_GRASS; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_HIGH; + using OpenSHC::Map::LogicHelpers::L2_PLATEAU_MEDIUM; + using OpenSHC::Map::LogicHelpers::L2_SCRUB; + using OpenSHC::Map::LogicHelpers::L_DEFAULT_EARTH_OR_TEXTURE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_NONE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00511020 + void TileMapState::updateMacroLayer() + { + if (this->forceUpdateMacroLayerFlag <= 0) { + return; + } + + this->forceUpdateMacroLayerFlag = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)( + L_DEFAULT_EARTH_OR_TEXTURE, L2_SCRUB, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)(L_MARSH, L_NONE, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)( + L_NONE, L2_EARTH_AND_STONES, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)(L_NONE, L2_OASIS_GRASS, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)(L_NONE, L2_BEACH, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)(L_NONE, L2_PLATEAU_MEDIUM, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated, this)(L_NONE, L2_PLATEAU_HIGH, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updateMacroLayerRelated2, this)(); + this->forceUpdateTextureTilemap = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateMacroLayerRelated.cpp b/src/OpenSHC/Map/TileMapState/updateMacroLayerRelated.cpp new file mode 100644 index 000000000..c78d3d81f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateMacroLayerRelated.cpp @@ -0,0 +1,340 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +// masks for the handwritten neighbour-flag macro; MASM has no "|", so these must be literals +#define NEIGHBOUR_FLAGS_MASK_DEFAULT_EARTH 0x8000 // L_DEFAULT_EARTH_OR_TEXTURE +#define NEIGHBOUR_FLAGS_MASK_MARSH 0x20000000 // L_MARSH +#define NEIGHBOUR_FLAGS_MASK_SEA 0x1 // L_SEA +#define NEIGHBOUR_FLAGS_MASK_L2_SCRUB 0x1 // L2_SCRUB +#define NEIGHBOUR_FLAGS_MASK_L2_EARTH_AND_STONES 0x2 // L2_EARTH_AND_STONES +#define NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_MEDIUM 0x4 // L2_PLATEAU_MEDIUM +#define NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_HIGH 0x8 // L2_PLATEAU_HIGH +#define NEIGHBOUR_FLAGS_MASK_L2_OASIS_GRASS 0x10 // L2_OASIS_GRASS +#define NEIGHBOUR_FLAGS_MASK_L2_BEACH 0x20 // L2_BEACH +#define NEIGHBOUR_FLAGS_MASK_L2_STONES 0x40 // L2_STONES_OR_DRIVEN_SANDUnk +#define NEIGHBOUR_FLAGS_MASK_L2_THICK_SCRUB 0x80 // L2_THICK_SCRUB + +#pragma optimize("", off) + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_OIL; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FDB00 + void TileMapState::updateMacroLayerRelated(uint logic1, uint logic2, int clearMacroFirst) + { + /* + Five copies of the same block/sub-tile loop skeleton, and eleven expansions of the + handwritten neighbour-flag macro (see NeighbourFlagsAsm.hpp). The original was built + without optimisation, so the pragma above is needed to reproduce its frame-pointer, + reload-per-access code shape; the locals are declared together, in this order, because + at /Od the stack slots are observable and MSVC assigns them per function, not per scope. + */ + int previousMacro; + int rotation; + int blockColumn; + int index; + int blockRow; + int neighbourTile; + int subRow; + int subColumn; + int tableY; + int randomValue; + int tableX; + int tileXOffset; + int savedBitFlag; + + rotation = DAT_PathFindingState::instance.mappingYRelated % 10; + for (blockRow = this->someIndex; blockRow <= this->someLimit; blockRow++) { + for (blockColumn = this->someYLike; blockColumn <= this->someYLikeLimit; blockColumn++) { + if (DAT_TileMapState::instance.mapping40x40[blockRow][blockColumn] != 0) { + subRow = rotation; + rotation = 0; + for (; subRow < 10; subRow++) { + this->DAT_SomeY = blockRow * 10 + subRow; + this->DAT_SomeX = blockColumn * 10; + for (subColumn = 0; subColumn < 10; subColumn++, this->DAT_SomeX++) { + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY) + != FALSE) { + this->DAT_SomeTile + = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY); + if ((char)this->ChangedLayer[this->DAT_SomeTile] != 0) { + previousMacro = (short)this->MacroLayer[this->DAT_SomeTile]; + if (clearMacroFirst != 0) { + this->MacroLayer[this->DAT_SomeTile] = 0; + } + if ((this->LogicLayer[this->DAT_SomeTile] & logic1) != 0 + || ((char)this->Logic2Layer[this->DAT_SomeTile] & logic2) != 0) { + if ((this->LogicLayer[this->DAT_SomeTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + this->bitFlag = 0; + } else if (logic1 == 0x8000) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(1, NEIGHBOUR_FLAGS_MASK_DEFAULT_EARTH) + } else if (logic1 == 0x20000000) { + if ((this->LogicLayer[this->DAT_SomeTile] & L_OIL) == 0) { + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(2, NEIGHBOUR_FLAGS_MASK_MARSH) + if (this->bitFlag != 0xff) { + this->MacroLayer[this->DAT_SomeTile] = 1; + } else if (((short)this->RandomLayer[this->DAT_SomeTile] & 0xf) == 0) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } else { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + if (previousMacro != (short)this->MacroLayer[this->DAT_SomeTile]) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func:: + updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(1, this->DAT_SomeX, this->DAT_SomeY); + } + } + } else if (logic2 == 0x2) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(3, NEIGHBOUR_FLAGS_MASK_L2_EARTH_AND_STONES) + } else if (logic2 == 0x10) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(4, NEIGHBOUR_FLAGS_MASK_L2_OASIS_GRASS) + } else if (logic2 == 0x1) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(5, NEIGHBOUR_FLAGS_MASK_L2_SCRUB) + } else if (logic2 == 0x80) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(6, NEIGHBOUR_FLAGS_MASK_L2_THICK_SCRUB) + } else if (logic2 == 0x40) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(7, NEIGHBOUR_FLAGS_MASK_L2_STONES) + } else if (logic2 == 0x20) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(8, NEIGHBOUR_FLAGS_MASK_L2_BEACH) + savedBitFlag = this->bitFlag; + MACRO_NEIGHBOUR_FLAGS_LOGIC_8(9, NEIGHBOUR_FLAGS_MASK_SEA) + this->bitFlag = this->bitFlag | savedBitFlag; + } else if (logic2 == 0x4) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(10, NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_MEDIUM) + } else if (logic2 == 0x8) { + MACRO_NEIGHBOUR_FLAGS_TERRAIN_8(11, NEIGHBOUR_FLAGS_MASK_L2_PLATEAU_HIGH) + } else { + this->bitFlag = 0; + } + if (this->bitFlag != 0xff) { + this->MacroLayer[this->DAT_SomeTile] = 1; + } else if (((short)this->RandomLayer[this->DAT_SomeTile] & 0xf) == 0) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } else { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + if (previousMacro != (short)this->MacroLayer[this->DAT_SomeTile]) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(1, this->DAT_SomeX, this->DAT_SomeY); + } + } + } + } + } + } + } + } + } + rotation = DAT_PathFindingState::instance.mappingYRelated % 10; + for (blockRow = this->someIndex; blockRow <= this->someLimit; blockRow++) { + for (blockColumn = this->someYLike; blockColumn <= this->someYLikeLimit; blockColumn++) { + if (DAT_TileMapState::instance.mapping40x40[blockRow][blockColumn] != 0) { + subRow = rotation; + rotation = 0; + for (; subRow < 10; subRow++) { + this->DAT_SomeY = blockRow * 10 + subRow; + this->DAT_SomeX = blockColumn * 10; + for (subColumn = 0; subColumn < 10; subColumn++, this->DAT_SomeX++) { + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY) + != FALSE) { + this->DAT_SomeTile + = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY); + if ((short)this->MacroLayer[this->DAT_SomeTile] == 0x800) { + tileXOffset = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::computeTileXOffset, + DAT_ViewportRenderState::ptr)(this->DAT_SomeTile, this->DAT_SomeY); + randomValue = (short)this->RandomLayer[this->DAT_SomeTile] & 0xf; + for (index = 0; index < 0x10; index++) { + tableX = DAT_TerrainDefinedData::instance.field2292_0x19d4[index].x; + tableY = DAT_TerrainDefinedData::instance.field2292_0x19d4[index].y; + neighbourTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)( + tileXOffset + tableX, this->DAT_SomeY + tableY); + if ((short)this->MacroLayer[neighbourTile] != 0x800) { + break; + } + } + if (index >= 0x10) { + for (index = 0; index < 0x10; index++) { + tableX = DAT_TerrainDefinedData::instance.field2292_0x19d4[index].x; + tableY = DAT_TerrainDefinedData::instance.field2292_0x19d4[index].y; + neighbourTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)( + tileXOffset + tableX, this->DAT_SomeY + tableY); + this->MacroLayer[neighbourTile] = 0x10; + this->MacroLayer[neighbourTile] + = (short)this->MacroLayer[neighbourTile] + (short)(index << 6); + this->MacroLayer[neighbourTile] + = (short)this->MacroLayer[neighbourTile] + randomValue * 0x1000; + } + } + } + } + } + } + } + } + } + rotation = DAT_PathFindingState::instance.mappingYRelated % 10; + for (blockRow = this->someIndex; blockRow <= this->someLimit; blockRow++) { + for (blockColumn = this->someYLike; blockColumn <= this->someYLikeLimit; blockColumn++) { + if (DAT_TileMapState::instance.mapping40x40[blockRow][blockColumn] != 0) { + subRow = rotation; + rotation = 0; + for (; subRow < 10; subRow++) { + this->DAT_SomeY = blockRow * 10 + subRow; + this->DAT_SomeX = blockColumn * 10; + for (subColumn = 0; subColumn < 10; subColumn++, this->DAT_SomeX++) { + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY) + != FALSE) { + this->DAT_SomeTile + = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY); + if ((short)this->MacroLayer[this->DAT_SomeTile] == 0x800) { + tileXOffset = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::computeTileXOffset, + DAT_ViewportRenderState::ptr)(this->DAT_SomeTile, this->DAT_SomeY); + randomValue = (short)this->RandomLayer[this->DAT_SomeTile] & 0xf; + for (index = 0; index < 9; index++) { + tableX = DAT_TerrainDefinedData::instance.field2291_0x198c[index].x; + tableY = DAT_TerrainDefinedData::instance.field2291_0x198c[index].y; + neighbourTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)( + tileXOffset + tableX, this->DAT_SomeY + tableY); + if ((short)this->MacroLayer[neighbourTile] != 0x800) { + break; + } + } + if (index >= 9) { + for (index = 0; index < 9; index++) { + tableX = DAT_TerrainDefinedData::instance.field2291_0x198c[index].x; + tableY = DAT_TerrainDefinedData::instance.field2291_0x198c[index].y; + neighbourTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)( + tileXOffset + tableX, this->DAT_SomeY + tableY); + this->MacroLayer[neighbourTile] = 8; + this->MacroLayer[neighbourTile] + = (short)this->MacroLayer[neighbourTile] + (short)(index << 6); + this->MacroLayer[neighbourTile] + = (short)this->MacroLayer[neighbourTile] + randomValue * 0x1000; + } + } + } + } + } + } + } + } + } + rotation = DAT_PathFindingState::instance.mappingYRelated % 10; + for (blockRow = this->someIndex; blockRow <= this->someLimit; blockRow++) { + for (blockColumn = this->someYLike; blockColumn <= this->someYLikeLimit; blockColumn++) { + if (DAT_TileMapState::instance.mapping40x40[blockRow][blockColumn] != 0) { + subRow = rotation; + rotation = 0; + for (; subRow < 10; subRow++) { + this->DAT_SomeY = blockRow * 10 + subRow; + this->DAT_SomeX = blockColumn * 10; + for (subColumn = 0; subColumn < 10; subColumn++, this->DAT_SomeX++) { + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY) + != FALSE) { + this->DAT_SomeTile + = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY); + if ((short)this->MacroLayer[this->DAT_SomeTile] == 0x800) { + tileXOffset = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::computeTileXOffset, + DAT_ViewportRenderState::ptr)(this->DAT_SomeTile, this->DAT_SomeY); + randomValue = (short)this->RandomLayer[this->DAT_SomeTile] & 0xf; + for (index = 0; index < 4; index++) { + tableX = DAT_TerrainDefinedData::instance.field2290_0x196c[index].x; + tableY = DAT_TerrainDefinedData::instance.field2290_0x196c[index].y; + neighbourTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)( + tileXOffset + tableX, this->DAT_SomeY + tableY); + if ((short)this->MacroLayer[neighbourTile] != 0x800) { + break; + } + } + if (index >= 4) { + for (index = 0; index < 4; index++) { + tableX = DAT_TerrainDefinedData::instance.field2290_0x196c[index].x; + tableY = DAT_TerrainDefinedData::instance.field2290_0x196c[index].y; + neighbourTile = MACRO_CALL_MEMBER( + OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)( + tileXOffset + tableX, this->DAT_SomeY + tableY); + this->MacroLayer[neighbourTile] = 4; + this->MacroLayer[neighbourTile] + = (short)this->MacroLayer[neighbourTile] + (short)(index << 6); + this->MacroLayer[neighbourTile] + = (short)this->MacroLayer[neighbourTile] + randomValue * 0x1000; + } + } + } + } + } + } + } + } + } + rotation = DAT_PathFindingState::instance.mappingYRelated % 10; + for (blockRow = this->someIndex; blockRow <= this->someLimit; blockRow++) { + for (blockColumn = this->someYLike; blockColumn <= this->someYLikeLimit; blockColumn++) { + if (DAT_TileMapState::instance.mapping40x40[blockRow][blockColumn] != 0) { + subRow = rotation; + rotation = 0; + for (; subRow < 10; subRow++) { + this->DAT_SomeY = blockRow * 10 + subRow; + this->DAT_SomeX = blockColumn * 10; + for (subColumn = 0; subColumn < 10; subColumn++, this->DAT_SomeX++) { + if (MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY) + != FALSE) { + this->DAT_SomeTile + = MACRO_CALL_MEMBER(OpenSHC::Rendering::ViewportRenderState_Func::translateXYToTile, + DAT_ViewportRenderState::ptr)(this->DAT_SomeX, this->DAT_SomeY); + if ((short)this->MacroLayer[this->DAT_SomeTile] == 0x800) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } + } + } + } + } + } + } + } + +} +} + +#pragma optimize("", on) diff --git a/src/OpenSHC/Map/TileMapState/updateMacroLayerRelated2.cpp b/src/OpenSHC/Map/TileMapState/updateMacroLayerRelated2.cpp new file mode 100644 index 000000000..8e202f07e --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateMacroLayerRelated2.cpp @@ -0,0 +1,222 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_SEA; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004FF080 + void TileMapState::updateMacroLayerRelated2() + { + int startRow; + startRow = DAT_PathFindingState::instance.mappingYRelated % 10; + for (int block = this->someIndex; block <= this->someLimit; block++) { + for (int sub = this->someYLike; sub <= this->someYLikeLimit; sub++) { + if (this->mapping40x40[block][sub] == 0) { + continue; + } + for (int row = startRow; row < 10; row++) { + this->DAT_SomeY = block * 10 + row; + this->DAT_SomeX = sub * 10; + int group = 2; + do { + if ((uint)this->DAT_SomeX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + this->DAT_SomeX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + this->DAT_SomeX; + if (this->ChangedLayer[this->DAT_SomeTile] != 0 + && (this->LogicLayer[this->DAT_SomeTile] & L_SEA) != 0) { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->ChangedLayer[this->DAT_SomeTile] != 0 + && (this->LogicLayer[this->DAT_SomeTile] & L_SEA) != 0) { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + } + this->DAT_SomeX = nextX; + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->ChangedLayer[this->DAT_SomeTile] != 0 + && (this->LogicLayer[this->DAT_SomeTile] & L_SEA) != 0) { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + } + this->DAT_SomeX = nextX; + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->ChangedLayer[this->DAT_SomeTile] != 0 + && (this->LogicLayer[this->DAT_SomeTile] & L_SEA) != 0) { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + } + this->DAT_SomeX = nextX; + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->ChangedLayer[this->DAT_SomeTile] != 0 + && (this->LogicLayer[this->DAT_SomeTile] & L_SEA) != 0) { + this->MacroLayer[this->DAT_SomeTile] = 0x800; + } + } + this->DAT_SomeX = nextX; + } + this->DAT_SomeX = this->DAT_SomeX + 1; + group = group + -1; + } while (group != 0); + } + startRow = 0; + } + } + + startRow = DAT_PathFindingState::instance.mappingYRelated % 10; + for (int block = this->someIndex; block <= this->someLimit; block++) { + for (int sub = this->someYLike; sub <= this->someYLikeLimit; sub++) { + if (this->mapping40x40[block][sub] == 0) { + continue; + } + for (int row = startRow; row < 10; row++) { + this->DAT_SomeY = block * 10 + row; + this->DAT_SomeX = sub * 10; + int column = 10; + do { + if ((uint)this->DAT_SomeX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + this->DAT_SomeX] != 0) { + this->DAT_SomeTile = this->DAT_SomeX + + DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile; + if (this->MacroLayer[this->DAT_SomeTile] == 0x800 + && (this->LogicLayer[this->DAT_SomeTile] & L_SEA) != 0) { + ushort random = this->RandomLayer[this->DAT_SomeTile]; + int corner = this->DAT_SomeTile + - DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile; + /* a wave sprite only fits where all sixteen of its tiles are still marked */ + bool complete = true; + for (int i = 0; i < 0x10; i++) { + if (this->MacroLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2292_0x19d4[i].y + + this->DAT_SomeY] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2292_0x19d4[i].x + corner] + != 0x800) { + complete = false; + break; + } + } + if (complete) { + for (int i = 0; i < 0x10; i++) { + this->MacroLayer[DAT_ViewportRenderState::instance + .translationMatrix[DAT_TerrainDefinedData::instance.field2292_0x19d4[i].y + + this->DAT_SomeY] + .addXgetTile + + DAT_TerrainDefinedData::instance.field2292_0x19d4[i].x + corner] + = (ushort)(byte)random * 0x1000 + 0x10 + (short)(i << 6); + } + } + } + } + this->DAT_SomeX = this->DAT_SomeX + 1; + column = column + -1; + } while (column != 0); + } + startRow = 0; + } + } + + startRow = DAT_PathFindingState::instance.mappingYRelated % 10; + for (int block = this->someIndex; block <= this->someLimit; block++) { + for (int sub = this->someYLike; sub <= this->someYLikeLimit; sub++) { + if (this->mapping40x40[block][sub] == 0) { + continue; + } + for (int row = startRow; row < 10; row++) { + this->DAT_SomeY = block * 10 + row; + this->DAT_SomeX = sub * 10; + int group = 2; + do { + if ((uint)this->DAT_SomeX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + this->DAT_SomeX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + this->DAT_SomeX; + if (this->MacroLayer[this->DAT_SomeTile] == 0x800) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->MacroLayer[this->DAT_SomeTile] == 0x800) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } + } + this->DAT_SomeX = nextX; + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->MacroLayer[this->DAT_SomeTile] == 0x800) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } + } + this->DAT_SomeX = nextX; + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->MacroLayer[this->DAT_SomeTile] == 0x800) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } + } + this->DAT_SomeX = nextX; + } + { + uint nextX = this->DAT_SomeX + 1; + if (nextX < 400 && (uint)this->DAT_SomeY < 400 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[this->DAT_SomeY * 400 + nextX] != 0) { + this->DAT_SomeTile = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_SomeY].addXgetTile + nextX; + if (this->MacroLayer[this->DAT_SomeTile] == 0x800) { + this->MacroLayer[this->DAT_SomeTile] = 2; + } + } + this->DAT_SomeX = nextX; + } + this->DAT_SomeX = this->DAT_SomeX + 1; + group = group + -1; + } while (group != 0); + } + startRow = 0; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateMoatCountdownTimers.cpp b/src/OpenSHC/Map/TileMapState/updateMoatCountdownTimers.cpp new file mode 100644 index 000000000..721fa3f16 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateMoatCountdownTimers.cpp @@ -0,0 +1,25 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00500640 + void TileMapState::updateMoatCountdownTimers() + { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::recountTotalOwnedMoats, this)(); + for (int moatID = 0; moatID < this->currentMoatCount; moatID++) { + if (this->moats[moatID].owner != 0 && this->moats[moatID].someCountDown > 0) { + this->moats[moatID].someCountDown = this->moats[moatID].someCountDown - 1; + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updatePathLinkagesForBuilding.cpp b/src/OpenSHC/Map/TileMapState/updatePathLinkagesForBuilding.cpp new file mode 100644 index 000000000..894fa4a1a --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updatePathLinkagesForBuilding.cpp @@ -0,0 +1,81 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00506AD0 + void TileMapState::updatePathLinkagesForBuilding(int buildingID) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight); + int y = DAT_BuildingsState::instance.buildings[buildingID].y + this->buildingY; + int tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + + DAT_BuildingsState::instance.buildings[buildingID].x + this->buildingX; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, + DAT_PathFindingState::ptr)(y, tile); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::clearMoat, this)(tile); + buildingSizeTileIndex++; + } while (buildingSizeTileIndex < this->constructionTileCount); + + short buildingType = DAT_BuildingsState::instance.buildings[buildingID].buildingType; + if (buildingType == OpenSHC::Map::Buildings::BT_GATEHOUSELARGE) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToGates, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_GATEHOUSESMALL) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToGates, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_WOODGATE1) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToGates, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_STONEKEEP) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToKeeps, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_STRONGHOLD) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToKeeps, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_KEEPFOUR) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToKeeps, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_KEEPFIVE) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToKeeps, + DAT_PathFindingState::ptr)(buildingID); + return; + } + if (buildingType == OpenSHC::Map::Buildings::BT_SIEGETOWER_PLACED) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageTileMapRelatedToSiegeTower, + DAT_PathFindingState::ptr)(buildingID); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updatePitchDitches.cpp b/src/OpenSHC/Map/TileMapState/updatePitchDitches.cpp new file mode 100644 index 000000000..1c3c1743d --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updatePitchDitches.cpp @@ -0,0 +1,83 @@ + +#include "OpenSHC/Map/Entities.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_PLAIN2_AND_PITCH; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005114C0 + void TileMapState::updatePitchDitches() + { + if (DAT_GameState::instance.gameTicksLoadBalancer % 10 == 3) { + this->maxPitchDitchCount = 0; + for (int id = 1; id < 4000; id++) { + if (this->pitchDitches[id].owner != 0) { + this->maxPitchDitchCount = id + 1; + } + } + } + + for (int id = 1; id < this->maxPitchDitchCount; id++) { + if (this->pitchDitches[id].owner == 0) { + continue; + } + int tile = this->pitchDitches[id].tile; + if ((this->LogicLayer[tile] & L_PLAIN2_AND_PITCH) == 0 || this->pitchDitches[id].state == -1) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::destroyPitchDitch, this)(id); + } else if (this->pitchDitches[id].state == 0) { + /* the original increments the state rather than storing the literal */ + this->pitchDitches[id].state = this->pitchDitches[id].state + 1; + this->pitchDitches[id].field7_0x12 = 0; + } else if (this->pitchDitches[id].state == 2) { + this->pitchDitches[id].field7_0x12 = this->pitchDitches[id].field7_0x12 + 1; + if (this->pitchDitches[id].field7_0x12 == 0x14) { + this->pitchDitches[id].state = this->pitchDitches[id].state + 1; + this->pitchDitches[id].field7_0x12 = 0; + uint height = DAT_TileMapState::instance.HeightLayer[tile]; + /* the fire spreads to the four adjacent ditches that are still unlit */ + for (int i = 0; i < 4; i++) { + int neighbour = DAT_ViewportRenderState::instance + .translationMatrix[this->pitchDitches[id].y + + DAT_TerrainDefinedData::instance.field2477_0x376c[i].y] + .addXgetTile + + this->pitchDitches[id].x + DAT_TerrainDefinedData::instance.field2477_0x376c[i].x; + if ((this->LogicLayer[neighbour] & L_PLAIN2_AND_PITCH) == 0) { + continue; + } + int other = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getPitchDitchIDForTile, this)(neighbour); + if (other == 0 || this->pitchDitches[other].state != 1) { + continue; + } + if (abs((int)(height - DAT_TileMapState::instance.HeightLayer[neighbour])) < 0x19) { + MACRO_CALL(OpenSHC::Map::Entities_Func::SetPlaceOnFire)(this->pitchDitches[other].owner, + this->pitchDitches[other].x * 8, this->pitchDitches[other].y * 8, + DAT_TileMapState::instance.HeightLayer[this->pitchDitches[other].tile], 3); + this->pitchDitches[other].state = 2; + } + } + } + } else if (this->pitchDitches[id].state == 3) { + this->pitchDitches[id].field7_0x12 = this->pitchDitches[id].field7_0x12 + 1; + if (this->pitchDitches[id].field7_0x12 == 200) { + DAT_TileMapState::instance.HeightLayer[tile] = DAT_TileMapState::instance.DefaultHeightLayer[tile]; + this->pitchDitches[id].state = -1; + } + } + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/updateShowHiLayerOrResetChangedLayer.cpp b/src/OpenSHC/Map/TileMapState/updateShowHiLayerOrResetChangedLayer.cpp new file mode 100644 index 000000000..5c22df355 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/updateShowHiLayerOrResetChangedLayer.cpp @@ -0,0 +1,94 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_RotateMapOrPullDownTerrain.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00501A20 + void TileMapState::updateShowHiLayerOrResetChangedLayer() + { + int importHeight = 0; + this->refreshRelatedTwo = 0; + if (this->refreshCertainTileMap == 0) { + return; + } + + this->counter1 = this->counter1 + 1; + this->refreshCertainTileMap_old = this->refreshCertainTileMap; + this->refreshRelatedOne = 0; + for (int row = 0; row < 256; row++) { + int target = ((uint)row >> 2) + 1; + /* the pull-down eases towards the target in steps that shrink as it gets closer */ + int step; + if (this->refreshCertainTileMap == 3) { + step = this->heightBasedScreenYOffset[row] - target; + } else if (this->refreshCertainTileMap == 4) { + step = row - this->heightBasedScreenYOffset[row]; + } else { + step = 0; + } + if (step > 60) { + step = 30; + } else if (step > 40) { + step = 20; + } else if (step > 30) { + step = 14; + } else if (step > 20) { + step = 8; + } else if (step > 10) { + step = 4; + } else if (step > 6) { + step = 3; + } else if (step > 2) { + step = 2; + } else if (step > 0) { + step = 1; + } else { + step = 0; + } + + if (this->refreshCertainTileMap == 1) { + this->heightBasedScreenYOffset[row] = target; + } else if (this->refreshCertainTileMap == 2) { + this->heightBasedScreenYOffset[row] = row; + } else if (this->refreshCertainTileMap == 3) { + this->heightBasedScreenYOffset[row] = this->heightBasedScreenYOffset[row] - step; + if (this->heightBasedScreenYOffset[row] > target) { + importHeight = 1; + } + } else if (this->refreshCertainTileMap == 4) { + this->heightBasedScreenYOffset[row] = this->heightBasedScreenYOffset[row] + step; + if (this->heightBasedScreenYOffset[row] != row) { + importHeight = 1; + } + } + } + + this->refreshRelatedTwo = 1; + if (importHeight != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::rebuildShowHiLayerFromHeights, this)(); + return; + } + if (this->refreshCertainTileMap == 4 || this->refreshCertainTileMap == 2) { + this->refreshRelatedOne = 1; + } else { + this->refreshRelatedOne = 0; + this->refreshRelatedTwo = 2; + } + this->refreshCertainTileMap = 0; + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::setChangedLayerToThreeAndMapping0x40x40, this)(); + this->counter1 = 0; + DAT_RotateMapOrPullDownTerrain::instance = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/upgradeMapFormatLogicLayer.cpp b/src/OpenSHC/Map/TileMapState/upgradeMapFormatLogicLayer.cpp new file mode 100644 index 000000000..1d5414792 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/upgradeMapFormatLogicLayer.cpp @@ -0,0 +1,48 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Version.func.hpp" + +namespace OpenSHC { +namespace Map { + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00511D70 + void TileMapState::upgradeMapFormatLogicLayer( + PackagedFileMagicNum receivedMapVersion, PackagedFileMagicNum packagerMapVersion) + { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_Unknown1)(); + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_Unknown2)(); + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_Unknown3)(); + if (this->mapSize == 0) { + this->mapSize = 400; + } + if (receivedMapVersion == packagerMapVersion) { + return; + } + + if ((int)receivedMapVersion < 0x66) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_102)(); + } + if ((int)receivedMapVersion < 0x78) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_120)(); + } + if ((int)receivedMapVersion < 0x7d) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_125)(); + } + if ((int)receivedMapVersion < 0x80) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_128)(); + } + if ((int)receivedMapVersion < 0x91) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_145)(); + } + if ((int)receivedMapVersion < 0x93) { + MACRO_CALL(OpenSHC::Map::Version_Func::UpgradeMapLogicToVersion_147)(); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/upgradeTowerLogicLayer.cpp b/src/OpenSHC/Map/TileMapState/upgradeTowerLogicLayer.cpp new file mode 100644 index 000000000..c86f728e7 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/upgradeTowerLogicLayer.cpp @@ -0,0 +1,58 @@ + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00507280 + void TileMapState::upgradeTowerLogicLayer(int buildingID) + { + int buildingSizeTileIndex = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getBuildingSizeIndexMappingData, this)( + buildingSizeTileIndex, DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight); + int tile = DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[buildingID].y + this->buildingY] + .addXgetTile + + DAT_BuildingsState::instance.buildings[buildingID].x + this->buildingX; + this->LogicLayer[tile] = this->LogicLayer[tile] & ~L_BUILDING; + uint widthOrHeight = DAT_BuildingsState::instance.buildings[buildingID].widthOrHeight; + uint logic = this->LogicLayer[tile]; + if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[widthOrHeight][0] == buildingSizeTileIndex) { + logic = logic | L_BUILDING; + } else if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[widthOrHeight][1] + == buildingSizeTileIndex) { + logic = logic | L_BUILDING; + } else if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[widthOrHeight][2] + == buildingSizeTileIndex) { + logic = logic | L_BUILDING; + } else if (DAT_TerrainDefinedData::instance.TowerTileOffsetsBySize[widthOrHeight][3] + == buildingSizeTileIndex) { + logic = logic | L_BUILDING; + } else { + logic = logic | L_KEEP_NON_MANOR_HOUSE; + } + buildingSizeTileIndex++; + this->LogicLayer[tile] = logic; + } while (buildingSizeTileIndex < this->constructionTileCount); + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::updatePathLinkagesForBuilding, this)(buildingID); + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/useEraserBrush.cpp b/src/OpenSHC/Map/TileMapState/useEraserBrush.cpp new file mode 100644 index 000000000..b8500bbde --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/useEraserBrush.cpp @@ -0,0 +1,120 @@ + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005017C0 + void TileMapState::useEraserBrush(uint x, uint y, int brush) + { + uint baseY = y; + uint baseX = x; + brush = DAT_TerrainDefinedData::instance.BrushSizeArray[brush]; + int baseTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + /* the original reuses the x parameter as the tile the brush is walking */ + x = baseTile; + MACRO_CALL_MEMBER(OpenSHC::Map::Entities::EntityState_Func::destroyEntitiesOnTile, DAT_EntityState::ptr)( + baseTile); + + if (brush > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, (int*)&x, (int*)&y, baseTile, baseY); + int organism = (short)this->OrganismLayer[x]; + if (organism != 0) { + /* tree ids come first, rock ids are biased by 2000 */ + if (organism >= 2000) { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeRock, + DAT_LandscapeState::ptr)(organism - 2000); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(8, baseX, y); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, + DAT_LandscapeState::ptr)(organism); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkAndPathLayer, + DAT_PathFindingState::ptr)(3, baseX, y); + } + DAT_PathFindingState::instance.toggleUpdateSeparateAreaTileMap = 1; + } + index++; + } while (index < brush); + } + + y = baseY; + if (brush > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, (int*)&x, (int*)&y, baseTile, baseY); + this->field151_0x554998 = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::validateBuildingPlacementAtTile, this)(0, baseX, y); + if (this->buildingPlacementFail == FALSE) { + if (this->field151_0x554998 != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::spawnEraserTileEffect, this)( + 0, DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + baseX); + } else if (this->field131_0x554954 != 0xffffffff) { + /* razing a keep takes the stockpile with it, and the other way round */ + short owner = DAT_BuildingsState::instance.buildings[this->field131_0x554954].owner; + if (this->field131_0x554954 == DAT_GameState::instance.playerDataArray[owner].keep.id + && DAT_GameState::instance.playerDataArray[owner].stockpile.id != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuildingAndLinkedDuplicates, + DAT_BuildingsState::ptr)(DAT_GameState::instance.playerDataArray[owner].stockpile.id); + } + if (DAT_BuildingsState::instance.buildings[this->field131_0x554954].buildingType == OpenSHC::Map::Buildings::BT_STOCKPILE + && DAT_GameState::instance.playerDataArray[owner].keep.id != 0) { + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuildingAndLinkedDuplicates, + DAT_BuildingsState::ptr)(DAT_GameState::instance.playerDataArray[owner].keep.id); + } + this->showNoRubbleWhenDestroyingBuilding = 1; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::destroyBuildingAndLinkedDuplicates, DAT_BuildingsState::ptr)( + this->field131_0x554954); + } + } + index++; + } while (index < brush); + } + + y = baseY; + if (brush > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, (int*)&x, (int*)&y, baseTile, baseY); + for (int unit = (short)DAT_TileMapState::instance.UnitLayer[x]; unit != 0; + unit = DAT_UnitsState::instance.units[unit].nextUnitOnTheSameTile) { + DAT_UnitsState::instance.units[unit].logicalState = OpenSHC::Map::Units::ULS_REMOVE; + } + index++; + } while (index < brush); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/useLevelBrush.cpp b/src/OpenSHC/Map/TileMapState/useLevelBrush.cpp new file mode 100644 index 000000000..9953b13c1 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/useLevelBrush.cpp @@ -0,0 +1,119 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_MOAT_DUG_OR_PLANNED; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00502110 + void TileMapState::useLevelBrush(int tile, uint y, uint brush, int param_4) + { + int baseTile = tile; + uint baseY = y; + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[brush]; + + int highest = 0; + /* the original reuses the brush parameter to hold the lowest height seen */ + brush = 200; + for (int index = 0; index < brushSize; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, &tile, (int*)&y, baseTile, baseY); + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) == 0) { + int height = this->HeightLayer[tile]; + if (height > highest) { + highest = height; + } + if (height < (int)brush) { + brush = height; + } + } + } + + int average = (int)(brush + highest) / 2; + tile = baseTile; + y = baseY; + for (int index = 0; index < brushSize; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, &tile, (int*)&y, baseTile, baseY); + int brushTile = tile; + uint logic = this->LogicLayer[brushTile]; + if ((logic & (L_BORDER | L_BORDER_EDGE)) != 0) { + continue; + } + if ((logic & (L_SEA | L_MARSH)) != 0 && this->unknownZero_0x554904 == 0) { + continue; + } + if ((logic & (L_SEA | L_MARSH)) == 0 && this->unknownZero_0x554904 == 1) { + continue; + } + if ((logic + & (L_WALL_OR_GATEHOUSE | L_BUILDING | L_MOAT_DUG_OR_PLANNED | L_KEEP_NON_MANOR_HOUSE | L_MOAT)) + != 0) { + continue; + } + if (this->BuildingLayer[brushTile] != 0 || (logic & L_SEA) != 0) { + continue; + } + + if ((logic & L_RIVER) != 0) { + this->HeightLayer[brushTile] = this->HeightLayer[brushTile] + 8; + } + this->LogicLayer[brushTile] = this->LogicLayer[brushTile] & ~L_ROCKY; + /* the tile moves a tenth of the way towards the brush average, plus one */ + byte levelled; + bool levelChanged = true; + if ((int)(uint)this->HeightLayer[brushTile] < average) { + levelled = (char)((average - (int)(uint)this->HeightLayer[brushTile]) / 10) + + this->HeightLayer[brushTile] + 1; + } else if ((int)(uint)this->HeightLayer[brushTile] > average) { + levelled = this->HeightLayer[brushTile] + - (char)(((int)(uint)this->HeightLayer[brushTile] - average) / 10) - 1; + } else { + levelChanged = false; + } + if (levelChanged) { + this->HeightLayer[brushTile] = levelled; + this->Logic2Layer[brushTile] = 0; + } + if (this->HeightLayer[brushTile] < 8) { + this->HeightLayer[brushTile] = 8; + } + if (this->HeightLayer[brushTile] > 0x9c) { + this->HeightLayer[brushTile] = 0x9c; + } + this->DefaultHeightLayer[brushTile] = this->HeightLayer[brushTile]; + if ((this->LogicLayer[brushTile] & L_RIVER) != 0) { + this->HeightLayer[brushTile] = this->HeightLayer[brushTile] - 8; + } + uint brushY = y; + this->DAT_SomeTile = brushTile; + this->DAT_SomeY = brushY; + + /* handwritten assembly in the original: mark this tile and its eight neighbours changed */ + MACRO_MARK_CHANGED_NEIGHBOURS() + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)(brushY, brushTile); + } + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/useTerrainHeightBrush.cpp b/src/OpenSHC/Map/TileMapState/useTerrainHeightBrush.cpp new file mode 100644 index 000000000..994737e10 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/useTerrainHeightBrush.cpp @@ -0,0 +1,132 @@ + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/TileMapState/NeighbourFlagsAsm.hpp" + +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BORDER_EDGE; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x00502680 + void TileMapState::useTerrainHeightBrush(int tile, uint y, uint brush, undefined4 mapper) + { + int baseTile = tile; + uint baseY = y; + int brushSize = DAT_TerrainDefinedData::instance.BrushSizeArray[brush]; + + int highest = 0; + /* the original reuses the brush parameter to hold the lowest height seen */ + brush = 200; + if (brushSize > 0) { + int index = 0; + do { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, &tile, (int*)&y, baseTile, baseY); + if ((this->LogicLayer[tile] & (L_BORDER | L_BORDER_EDGE)) == 0) { + if ((int)(uint)this->HeightLayer[tile] > highest) { + highest = this->HeightLayer[tile]; + } + if ((int)(uint)this->HeightLayer[tile] < (int)brush) { + brush = this->HeightLayer[tile]; + } + } + index++; + } while (index < brushSize); + } + + int average = (int)(brush + highest) / 2; + tile = baseTile; + y = baseY; + for (int index = 0; index < brushSize; index++) { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getTileForBrush, this)(1, index, &tile, (int*)&y, baseTile, baseY); + int brushTile = tile; + if ((this->LogicLayer[brushTile] & (L_BORDER | L_BORDER_EDGE)) != 0) { + continue; + } + if ((this->LogicLayer[brushTile] + & (L_SEA | L_WALL_OR_GATEHOUSE | L_BUILDING | L_KEEP_NON_MANOR_HOUSE | L_MARSH | L_MOAT)) + != 0) { + continue; + } + if (this->BuildingLayer[brushTile] != 0) { + continue; + } + + this->HeightLayer[brushTile] = this->HeightLayer[brushTile] + 1; + if ((short)mapper == 0) { + /* mapper min */ + this->HeightLayer[brushTile] = 8; + this->Logic2Layer[brushTile] = this->Logic2Layer[brushTile] & 0xf3; + } else if ((short)mapper == 1) { + /* mapper max, bug:fixme: is 156 really the maximum height? */ + this->HeightLayer[brushTile] = 156; + this->LogicLayer[brushTile] = this->LogicLayer[brushTile] | 0x8000; + } else if ((short)mapper == 5) { + this->HeightLayer[brushTile] = 100; + } else if ((short)mapper == 2) { + /* scale towards the brush average, in eighths of the ratio to it */ + int ratio; + if (average != 0) { + ratio = this->HeightLayer[brushTile] * 100 / average; + } else { + ratio = 100; + } + ratio = ratio / 8 + 100; + ratio = ratio * this->HeightLayer[brushTile] / 100; + if (ratio > 0x9c) { + ratio = 0x9c; + } + this->HeightLayer[brushTile] = (byte)ratio; + } + + if ((this->LogicLayer[brushTile] & L_ROCKY) != 0) { + int organism = this->OrganismLayer[brushTile]; + if (organism >= 2000) { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeRock, DAT_LandscapeState::ptr)( + organism - 2000); + } + } + this->LogicLayer[brushTile] = this->LogicLayer[brushTile] & ~L_ROCKY; + if (this->HeightLayer[brushTile] < 8) { + this->HeightLayer[brushTile] = 8; + } + if (this->HeightLayer[brushTile] > 0x9c) { + this->HeightLayer[brushTile] = 0x9c; + } + this->DefaultHeightLayer[brushTile] = this->HeightLayer[brushTile]; + if ((this->LogicLayer[brushTile] & L_RIVER) != 0) { + this->HeightLayer[brushTile] = this->HeightLayer[brushTile] - 8; + } + uint brushY = y; + this->DAT_SomeTile = brushTile; + this->DAT_SomeY = brushY; + + /* handwritten assembly in the original: mark this tile and its eight neighbours changed */ + MACRO_MARK_CHANGED_NEIGHBOURS() + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkagesInAllEightDirections, DAT_PathFindingState::ptr)(brushY, brushTile); + } + this->forceUpdateLogicalAndMiscDisplayLayers = 1; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/validateBuildingPlacementAtTile.cpp b/src/OpenSHC/Map/TileMapState/validateBuildingPlacementAtTile.cpp new file mode 100644 index 000000000..407c0196b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/validateBuildingPlacementAtTile.cpp @@ -0,0 +1,68 @@ +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F9DF0 + void TileMapState::validateBuildingPlacementAtTile(int playerID, int x, int y) + { + this->buildingPlacementFail = FALSE; + int tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + int buildingID = this->BuildingLayer[tile]; + if (buildingID < 1 || (DAT_BuildingsState::instance.buildings[buildingID].owner != playerID && playerID != 0)) { + this->buildingPlacementFail = TRUE; + } + if ((this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0 && this->BuildingLayer[tile] == 0) { + if ((this->WallOwnerLayer[tile] & 7) + 1 != playerID) { + return; + } + this->buildingPlacementFail = FALSE; + } + if ((this->LogicLayer[tile] & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) == 0 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_GATEHOUSELARGE + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_GATEHOUSESMALL + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_WOODGATE1 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_WOODGATE2 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_KEEPDOOR + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_DRAWBRIDGE + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_CAMPGROUND + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_PARADEGROUND + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_PARADEGROUND2 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_PARADEGROUND3 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_PARADEGROUND4 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_PARADEGROUND5 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType != OpenSHC::Map::Buildings::BT_CAMPFIRE + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + != OpenSHC::Map::Buildings::BT_KILLINGPIT) { + this->buildingPlacementFail = TRUE; + } + this->field131_0x554954 = buildingID; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/validateWallBuildPath.cpp b/src/OpenSHC/Map/TileMapState/validateWallBuildPath.cpp new file mode 100644 index 000000000..45bf4492f --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/validateWallBuildPath.cpp @@ -0,0 +1,282 @@ + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingFailReasonEnum.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_TREE; + using OpenSHC::Map::LogicHelpers::L_TREE_VARIATION; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Game::GameMode; + using OpenSHC::Game::GameMode2; + using OpenSHC::Map::Buildings::BuildingFailReasonEnum; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names + */ + /* + WARNING: Enum "DPERRInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x005029D0 + void TileMapState::validateWallBuildPath( + int playerID, uint x1, uint y1, uint x2, uint y2, undefined4 command) + { + int budget = 0; + int overshoot = 2; + int enemyRange; + int searchRange; + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + enemyRange = 0xf; + searchRange = 7; + } else { + enemyRange = 0x1e; + searchRange = 0x1e; + } + this->buildingPlacementFail = FALSE; + this->illegalBuild = TRUE; + this->field118_0x554920 = 0; + if (x1 > 399 || y1 > 399 || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y1 * 400 + x1] == 0) { + this->buildingPlacementFail = TRUE; + return; + } + if (x2 > 399 || y2 > 399 || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y2 * 400 + x2] == 0) { + this->buildingPlacementFail = TRUE; + return; + } + + int* translation = &DAT_ViewportRenderState::instance.translationMatrix[y1].addXgetTile; + int tile = *translation + x1; + this->DAT_SomeY = y1; + this->DAT_SomeTile = tile; + if ((short)command == 0x1b) { + /* a stair needs an enclosed wall to lean against, and enough height on it */ + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isTileEnclosedByWallsOrGates, this)(tile, y1) == FALSE) { + return; + } + this->field119_0x554924 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getMaxWallHeightInBrushArea, this)(tile, y1); + if (this->field119_0x554924 < 0x11) { + return; + } + budget = this->field119_0x554924; + } else if ((short)command == 0x1a + && MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isTileEnclosedByWalls, this)(tile, y1) == 0) { + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x13; + return; + } + + int upY = y1 - y2; + int downY = y2 - y1; + int rightX = x2 - x1; + this->field118_0x554920 = 1; + uint y = y1; + uint x = x1; + int leftX = x1 - x2; + do { + tile = *translation + x; + uint logic = this->LogicLayer[tile]; + if ((logic & (L_PLAIN1_AND_FARM | L_BORDER)) != 0) { + return; + } + if ((logic & (L_SEA | L_BUILDING | L_KEEP_NON_MANOR_HOUSE | L_MARSH | L_MOAT)) != 0) { + return; + } + if (this->BuildingLayer[tile] != 0) { + return; + } + if ((logic & (L_TREE | L_TREE_VARIATION)) != 0 && this->OrganismLayer[tile] != 0 + && this->OrganismLayer[tile] < 2000) { + /* scrub can be built through, a real tree cannot */ + switch (DAT_LandscapeState::instance.trees[this->OrganismLayer[tile]].treeType) { + case (TreeType)5: + case (TreeType)6: + case (TreeType)7: + case (TreeType)8: + case (TreeType)9: + case (TreeType)0xa: + case (TreeType)0xb: + case (TreeType)0xc: + case (TreeType)0xd: + case (TreeType)0xe: + case (TreeType)0x10: + case (TreeType)0x11: + case (TreeType)0x12: + case (TreeType)0x13: + break; + default: + return; + } + } + if ((logic & (L_RIVER | L_FORD)) != 0) { + return; + } + if ((char)logic < 0) { + return; + } + if (this->UnitLayer[tile] != 0 && (logic & L_WALL_OR_GATEHOUSE) == 0 + && DAT_UnitsState::instance.units[(short)this->UnitLayer[tile]].unitType != OpenSHC::Map::Units::UT_CHICKEN) { + return; + } + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_EDITOR + && DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SIEGE_THAT + && MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, DAT_PathFindingState::ptr)( + playerID, x, y, enemyRange) + != FALSE) { + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x11; + return; + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, DAT_PathFindingState::ptr)( + playerID, x, y, searchRange, -1, -1, -1) + != 0) { + if (this->buildingPlacementFailReason == (BuildingFailReasonEnum)0x12) { + return; + } + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x13; + return; + } + if (DAT_GameSynchronyState::instance.currentGameMode != OpenSHC::Game::GM_SOLITARY) { + int buildRange = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getCastleBuildRangeForMapSize, this)(); + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isTileInRangeOfKeepRange, DAT_PathFindingState::ptr)( + playerID, x, y, buildRange) + != 0) { + if (this->buildingPlacementFailReason == (BuildingFailReasonEnum)0x12) { + return; + } + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x13; + return; + } + } + } + if (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_EDITOR + && MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isOpponentBuildingInRange, DAT_PathFindingState::ptr)( + playerID, x, y, 7, -1, -1, -1) + != 0) { + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x11; + return; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findSomeSuitableLocationUnk, DAT_PathFindingState::ptr)( + playerID, x, y, 2) + != 0) { + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x11; + return; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isSignPostWithinDistance, DAT_PathFindingState::ptr)( + x, y, DAT_GameState::instance.mapAndTime.unk_signpostDistance + 5) + != FALSE) { + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x15; + return; + } + if ((short)command == 0x1a) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::isTileEnclosedByWalls, this)(tile, y) == 0) { + this->buildingPlacementFailReason = (BuildingFailReasonEnum)0x13; + return; + } + } else if ((short)command == 0x1b + && (budget < 0x11 || (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) != 0)) { + break; + } + + int stepX = leftX; + if ((int)x < (int)x2) { + stepX = rightX; + } + int stepY = upY; + if ((int)y < (int)y2) { + stepY = downY; + } + if ((short)command == 0x1b) { + if (stepY < stepX) { + if ((int)x < (int)x2) { + leftX = leftX + 1; + x = x + 1; + rightX = rightX - 1; + } else { + leftX = leftX - 1; + x = x - 1; + rightX = rightX + 1; + } + } else if (stepY > 0) { + if ((int)y < (int)y2) { + translation = translation + 3; + upY = upY + 1; + y = y + 1; + downY = downY - 1; + } else { + translation = translation - 3; + upY = upY - 1; + y = y - 1; + downY = downY + 1; + } + } else { + overshoot = overshoot - 1; + } + } else { + if (stepX != 0) { + if ((int)x < (int)x2) { + leftX = leftX + 1; + x = x + 1; + rightX = rightX - 1; + } else { + leftX = leftX - 1; + x = x - 1; + rightX = rightX + 1; + } + } + if (stepY != 0) { + if ((int)y < (int)y2) { + translation = translation + 3; + upY = upY + 1; + y = y + 1; + downY = downY - 1; + } else { + translation = translation - 3; + upY = upY - 1; + y = y - 1; + downY = downY + 1; + } + } + if (x == x2 && y == y2) { + overshoot = overshoot - 1; + } + } + if (budget > 0x10) { + budget = budget - 0x10; + } + } while (x != x2 || y != y2 || overshoot != 0); + this->illegalBuild = FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/TileMapState/validateWallPlacementAtTile.cpp b/src/OpenSHC/Map/TileMapState/validateWallPlacementAtTile.cpp new file mode 100644 index 000000000..38e2bd608 --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/validateWallPlacementAtTile.cpp @@ -0,0 +1,74 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + + using OpenSHC::Map::LogicHelpers::L_BORDER; + using OpenSHC::Map::LogicHelpers::L_BUILDING; + using OpenSHC::Map::LogicHelpers::L_FORD; + using OpenSHC::Map::LogicHelpers::L_MARSH; + using OpenSHC::Map::LogicHelpers::L_MOAT; + using OpenSHC::Map::LogicHelpers::L_PLAIN1_AND_FARM; + using OpenSHC::Map::LogicHelpers::L_RIVER; + using OpenSHC::Map::LogicHelpers::L_ROCKY; + using OpenSHC::Map::LogicHelpers::L_SEA; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "MappersEnum": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + // FUNCTION: STRONGHOLDCRUSADER 0x004F94A0 + void TileMapState::validateWallPlacementAtTile(int playerID, uint x, uint y, MappersEnum param_4) + { + this->illegalBuild = TRUE; + int tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + uint logic = this->LogicLayer[tile]; + if ((logic & L_WALL_OR_GATEHOUSE) != 0 && (char)this->DamageLayer[tile] <= 11) { + return; + } + if ((logic & L_BUILDING) != 0) { + return; + } + if (this->BuildingLayer[tile] != 0) { + return; + } + if ((logic & (L_PLAIN1_AND_FARM | L_BORDER)) != 0) { + return; + } + if ((logic & (L_SEA | L_ROCKY | L_RIVER | L_FORD | L_MARSH | L_MOAT)) != 0) { + return; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::isEnemyTooCloseUnk, + DAT_PathFindingState::ptr)(playerID, x, y, 5) + != FALSE) { + return; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findSomeSuitableLocationUnk, + DAT_PathFindingState::ptr)(playerID, x, y, 2) + != 0) { + return; + } + + if (this->UnitLayer[tile] != 0 && (this->LogicLayer[tile] & L_WALL_OR_GATEHOUSE) == 0 + && ((undefined2)param_4 == OpenSHC::Commands::M_MAPPER_CRENAL + || (undefined2)param_4 == OpenSHC::Commands::M_MAPPER_CRENAL2)) { + DAT_UnitsState::instance.units[(short)this->UnitLayer[tile]].logicalState = OpenSHC::Map::Units::ULS_REMOVE; + } + this->illegalBuild = FALSE; + } + +} +} diff --git a/src/OpenSHC/Map/Units/Unit.hpp b/src/OpenSHC/Map/Units/Unit.hpp index ea16549ee..3fe469fae 100644 --- a/src/OpenSHC/Map/Units/Unit.hpp +++ b/src/OpenSHC/Map/Units/Unit.hpp @@ -81,7 +81,7 @@ namespace Map { short gmIDUnk; // 0x0000007E length: 2 short displayColorPlayerID; // 0x00000080 length: 2 undefined1 heightDiv10; // 0x00000082 length: 1 - undefined1 padding_0x83[1]; // 0x00000083 length: 1 + undefined1 field_0x83; // 0x00000083 length: 1 short field58_0x84; // 0x00000084 length: 2 short field59_0x86; // 0x00000086 length: 2 short field60_0x88; // 0x00000088 length: 2 diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index b80b8635f..926bcc4f3 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -385,7 +385,7 @@ SHC_3BB0A8C1_0x00401000 | 0.0% | Pending SHC_3BB0A8C1_0x00401040 | 0.0% | Pending -SHC_3BB0A8C1_0x00401060 | 0.0% | Pending +SHC_3BB0A8C1_0x00401060 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00401090 | 0.0% | Pending @@ -2601,7 +2601,7 @@ SHC_3BB0A8C1_0x004092C0 | 0.0% | Pending SHC_3BB0A8C1_0x004092E0 | 0.0% | Pending -SHC_3BB0A8C1_0x00409300 | 0.0% | Pending +SHC_3BB0A8C1_0x00409300 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00409330 | 0.0% | Pending @@ -38739,37 +38739,37 @@ SHC_3BB0A8C1_0x004F6A70 | 0.0% | Pending SHC_3BB0A8C1_0x004F6A80 | 0.0% | Pending -SHC_3BB0A8C1_0x004F6AE0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F6AE0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F6B20 | 0.0% | Pending +SHC_3BB0A8C1_0x004F6B20 | 30.1% | row counter costs a spill SHC_3BB0A8C1_0x004F6C20 | 0.0% | Pending -SHC_3BB0A8C1_0x004F6C60 | 0.0% | Pending +SHC_3BB0A8C1_0x004F6C60 | 92.3% | border index regalloc -SHC_3BB0A8C1_0x004F6CF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F6CF0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F6DC0 | 0.0% | Pending -SHC_3BB0A8C1_0x004F6F90 | 0.0% | Pending +SHC_3BB0A8C1_0x004F6F90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F6FD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F6FD0 | 98.3% | unit loop regalloc -SHC_3BB0A8C1_0x004F70B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F70B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F70D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F70D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F70E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F70E0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F70F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F70F0 | 47.6% | index locals hoisted SHC_3BB0A8C1_0x004F7140 | 0.0% | Pending SHC_3BB0A8C1_0x004F7170 | 0.0% | Pending -SHC_3BB0A8C1_0x004F79D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F79D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F7A80 | 0.0% | Pending +SHC_3BB0A8C1_0x004F7A80 | 21.9% | 20.69% regalloc, frame size SHC_3BB0A8C1_0x004F7E50 | 0.0% | Pending @@ -38787,13 +38787,13 @@ SHC_3BB0A8C1_0x004F80D4 | 0.0% | Pending SHC_3BB0A8C1_0x004F80D8 | 0.0% | Pending -SHC_3BB0A8C1_0x004F80E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F80E0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F8160 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8160 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F81D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F81D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F8210 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8210 | 94.7% | switch table vs compare chain SHC_3BB0A8C1_0x004F8288 | 0.0% | Pending @@ -38995,7 +38995,7 @@ SHC_3BB0A8C1_0x004F82F0 | 0.0% | Pending SHC_3BB0A8C1_0x004F82F1 | 0.0% | Pending -SHC_3BB0A8C1_0x004F8300 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8300 | 99.3% | global instance access SHC_3BB0A8C1_0x004F8454 | 0.0% | Pending @@ -39355,43 +39355,43 @@ SHC_3BB0A8C1_0x004F8523 | 0.0% | Pending SHC_3BB0A8C1_0x004F8524 | 0.0% | Pending -SHC_3BB0A8C1_0x004F8530 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8530 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F85B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F85B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F8640 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8640 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F86D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F86D0 | 98.4% | direction chain regalloc -SHC_3BB0A8C1_0x004F8780 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8780 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F8840 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8840 | 93.7% | direction chain order -SHC_3BB0A8C1_0x004F8900 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8900 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F89C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F89C0 | 59.1% | orientation blocks not shared -SHC_3BB0A8C1_0x004F8A40 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8A40 | 87.2% | shr vs sar on logic bit -SHC_3BB0A8C1_0x004F8AC0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8AC0 | 90.7% | shr vs sar on logic bit -SHC_3BB0A8C1_0x004F8B50 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8B50 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F8BD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F8BD0 | 44.2% | 44.57% regalloc -SHC_3BB0A8C1_0x004F9000 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9000 | 59.5% | owner store tail-merge -SHC_3BB0A8C1_0x004F9220 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9220 | 88.1% | owner store tail-merge -SHC_3BB0A8C1_0x004F93E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F93E0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F94A0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F94A0 | 67.2% | logic masks merged by cl -SHC_3BB0A8C1_0x004F9590 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9590 | 62.5% | natural x/y, regalloc differs SHC_3BB0A8C1_0x004F9630 | 0.0% | Pending -SHC_3BB0A8C1_0x004F9880 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9880 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F990C | 0.0% | Pending @@ -39407,13 +39407,13 @@ SHC_3BB0A8C1_0x004F9920 | 0.0% | Pending SHC_3BB0A8C1_0x004F9924 | 0.0% | Pending -SHC_3BB0A8C1_0x004F9930 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9930 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F9980 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9980 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F99D0 | 0.0% | Pending -SHC_3BB0A8C1_0x004F9A60 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9A60 | 49.7% | 49.70% global instance base SHC_3BB0A8C1_0x004F9D64 | 0.0% | Pending @@ -39649,17 +39649,17 @@ SHC_3BB0A8C1_0x004F9DE9 | 0.0% | Pending SHC_3BB0A8C1_0x004F9DEA | 0.0% | Pending -SHC_3BB0A8C1_0x004F9DF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9DF0 | 97.1% | buildingType chain regalloc -SHC_3BB0A8C1_0x004F9F00 | 0.0% | Pending +SHC_3BB0A8C1_0x004F9F00 | 82.3% | global instance access -SHC_3BB0A8C1_0x004FA000 | 0.0% | Pending +SHC_3BB0A8C1_0x004FA000 | 33.9% | 33.90% pointer local frame -SHC_3BB0A8C1_0x004FA2D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FA2D0 | 47.4% | 47.37% cold block layout regal -SHC_3BB0A8C1_0x004FA460 | 0.0% | Pending +SHC_3BB0A8C1_0x004FA460 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004FA550 | 0.0% | Pending +SHC_3BB0A8C1_0x004FA550 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004FA5DC | 0.0% | Pending @@ -40343,33 +40343,33 @@ SHC_3BB0A8C1_0x004FA759 | 0.0% | Pending SHC_3BB0A8C1_0x004FA75A | 0.0% | Pending -SHC_3BB0A8C1_0x004FA760 | 0.0% | Pending +SHC_3BB0A8C1_0x004FA760 | 56.3% | 61.02% offset array partition SHC_3BB0A8C1_0x004FA8D0 | 0.0% | Pending -SHC_3BB0A8C1_0x004FA9D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FA9D0 | 96.3% | LTCG, loop alignment -SHC_3BB0A8C1_0x004FAA30 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAA30 | 91.9% | Style pass, asm differs SHC_3BB0A8C1_0x004FAAB0 | 0.0% | Pending SHC_3BB0A8C1_0x004FAAE0 | 0.0% | Pending -SHC_3BB0A8C1_0x004FAB70 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAB70 | 70.4% | ecx not kept across callee -SHC_3BB0A8C1_0x004FAC70 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAC70 | 84.7% | ecx not kept across callee -SHC_3BB0A8C1_0x004FAD20 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAD20 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004FAD70 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAD70 | 77.7% | ecx not kept across callee -SHC_3BB0A8C1_0x004FAE50 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAE50 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004FAEE0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAEE0 | 74.2% | LTCG, regalloc differs -SHC_3BB0A8C1_0x004FAF70 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAF70 | 98.5% | offset walk regalloc -SHC_3BB0A8C1_0x004FB0C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB0C0 | 62.4% | LTCG, rotation hoisted SHC_3BB0A8C1_0x004FB2E0 | 0.0% | Pending @@ -40565,25 +40565,25 @@ SHC_3BB0A8C1_0x004FB354 | 0.0% | Pending SHC_3BB0A8C1_0x004FB358 | 0.0% | Pending -SHC_3BB0A8C1_0x004FB360 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB360 | 79.0% | ecx not kept across callee -SHC_3BB0A8C1_0x004FB3F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB3F0 | 34.3% | XYPair stride regalloc -SHC_3BB0A8C1_0x004FB4D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB4D0 | 41.1% | 43.40% rockType spilled vs reg -SHC_3BB0A8C1_0x004FB770 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB770 | 76.7% | gfx expression regalloc SHC_3BB0A8C1_0x004FB7D0 | 0.0% | Pending -SHC_3BB0A8C1_0x004FB8E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB8E0 | 97.0% | LTCG, add operand order -SHC_3BB0A8C1_0x004FB970 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB970 | 96.8% | LTCG, add operand order SHC_3BB0A8C1_0x004FB990 | 0.0% | Pending -SHC_3BB0A8C1_0x004FB9E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FB9E0 | 77.8% | LTCG, regalloc differs -SHC_3BB0A8C1_0x004FBC30 | 0.0% | Pending +SHC_3BB0A8C1_0x004FBC30 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004FBC6C | 0.0% | Pending @@ -41077,35 +41077,35 @@ SHC_3BB0A8C1_0x004FBD6F | 0.0% | Pending SHC_3BB0A8C1_0x004FBD70 | 0.0% | Pending -SHC_3BB0A8C1_0x004FBD80 | 0.0% | Pending +SHC_3BB0A8C1_0x004FBD80 | 74.1% | step logic fixed, regalloc -SHC_3BB0A8C1_0x004FBE70 | 0.0% | Pending +SHC_3BB0A8C1_0x004FBE70 | 87.2% | 85.48% regalloc -SHC_3BB0A8C1_0x004FC0B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC0B0 | 36.4% | 36.36% regalloc -SHC_3BB0A8C1_0x004FC280 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC280 | 85.2% | XYPair base regalloc -SHC_3BB0A8C1_0x004FC340 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC340 | 62.4% | LogicLayer walk regalloc -SHC_3BB0A8C1_0x004FC490 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC490 | 54.1% | 56.42% array base regalloc -SHC_3BB0A8C1_0x004FC650 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC650 | 45.5% | orientation chain regalloc -SHC_3BB0A8C1_0x004FC7C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC7C0 | 79.0% | setg vs sbb/neg -SHC_3BB0A8C1_0x004FC810 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC810 | 67.2% | height compare regalloc -SHC_3BB0A8C1_0x004FC9E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FC9E0 | 66.7% | signed layer reads -SHC_3BB0A8C1_0x004FDB00 | 0.0% | Pending +SHC_3BB0A8C1_0x004FDB00 | 63.1% | signed layer reads -SHC_3BB0A8C1_0x004FF080 | 0.0% | Pending +SHC_3BB0A8C1_0x004FF080 | 27.3% | 27.06% regalloc SHC_3BB0A8C1_0x004FF420 | 0.0% | Pending -SHC_3BB0A8C1_0x004FF870 | 0.0% | Pending +SHC_3BB0A8C1_0x004FF870 | 74.7% | __asm macro; global not nameable -SHC_3BB0A8C1_0x004FF990 | 0.0% | Pending +SHC_3BB0A8C1_0x004FF990 | 74.5% | switch arm regalloc SHC_3BB0A8C1_0x004FFAAC | 0.0% | Pending @@ -41259,9 +41259,9 @@ SHC_3BB0A8C1_0x004FFB02 | 0.0% | Pending SHC_3BB0A8C1_0x004FFB03 | 0.0% | Pending -SHC_3BB0A8C1_0x004FFB10 | 0.0% | Pending +SHC_3BB0A8C1_0x004FFB10 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004FFBA0 | 0.0% | Pending +SHC_3BB0A8C1_0x004FFBA0 | 41.0% | single exit, owner test merge SHC_3BB0A8C1_0x004FFECC | 0.0% | Pending @@ -41349,7 +41349,7 @@ SHC_3BB0A8C1_0x004FFF01 | 0.0% | Pending SHC_3BB0A8C1_0x004FFF02 | 0.0% | Pending -SHC_3BB0A8C1_0x004FFF10 | 0.0% | Pending +SHC_3BB0A8C1_0x004FFF10 | 44.0% | switch arm regalloc SHC_3BB0A8C1_0x00500060 | 0.0% | Pending @@ -41419,57 +41419,57 @@ SHC_3BB0A8C1_0x00500090 | 0.0% | Pending SHC_3BB0A8C1_0x00500091 | 0.0% | Pending -SHC_3BB0A8C1_0x005000A0 | 0.0% | Pending +SHC_3BB0A8C1_0x005000A0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005000E0 | 0.0% | Pending +SHC_3BB0A8C1_0x005000E0 | 48.7% | owner reload per unrolled step -SHC_3BB0A8C1_0x00500180 | 0.0% | Pending +SHC_3BB0A8C1_0x00500180 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500210 | 0.0% | Pending +SHC_3BB0A8C1_0x00500210 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500250 | 0.0% | Pending +SHC_3BB0A8C1_0x00500250 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500290 | 0.0% | Pending +SHC_3BB0A8C1_0x00500290 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005002D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005002D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500310 | 0.0% | Pending +SHC_3BB0A8C1_0x00500310 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500370 | 0.0% | Pending +SHC_3BB0A8C1_0x00500370 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005003D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005003D0 | 54.3% | moat row walk regalloc -SHC_3BB0A8C1_0x00500500 | 0.0% | Pending +SHC_3BB0A8C1_0x00500500 | 86.5% | moat field reload/regalloc -SHC_3BB0A8C1_0x005005F0 | 0.0% | Pending +SHC_3BB0A8C1_0x005005F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500640 | 0.0% | Pending +SHC_3BB0A8C1_0x00500640 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500680 | 0.0% | Pending +SHC_3BB0A8C1_0x00500680 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005006C0 | 0.0% | Pending +SHC_3BB0A8C1_0x005006C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500720 | 0.0% | Pending +SHC_3BB0A8C1_0x00500720 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500750 | 0.0% | Pending +SHC_3BB0A8C1_0x00500750 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500790 | 0.0% | Pending +SHC_3BB0A8C1_0x00500790 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005007D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005007D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500800 | 0.0% | Pending +SHC_3BB0A8C1_0x00500800 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500850 | 0.0% | Pending +SHC_3BB0A8C1_0x00500850 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500890 | 0.0% | Pending +SHC_3BB0A8C1_0x00500890 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500940 | 0.0% | Pending +SHC_3BB0A8C1_0x00500940 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00500980 | 0.0% | Pending +SHC_3BB0A8C1_0x00500980 | 65.1% | height compare regalloc -SHC_3BB0A8C1_0x00500B50 | 0.0% | Pending +SHC_3BB0A8C1_0x00500B50 | 68.3% | loop guard/regalloc differs -SHC_3BB0A8C1_0x00500BB0 | 0.0% | Pending +SHC_3BB0A8C1_0x00500BB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00500C20 | 0.0% | Pending @@ -41511,9 +41511,9 @@ SHC_3BB0A8C1_0x005010E0 | 0.0% | Pending SHC_3BB0A8C1_0x00501110 | 0.0% | Pending -SHC_3BB0A8C1_0x00501180 | 0.0% | Pending +SHC_3BB0A8C1_0x00501180 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005011A0 | 0.0% | Pending +SHC_3BB0A8C1_0x005011A0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x005011C0 | 0.0% | Pending @@ -41539,13 +41539,13 @@ SHC_3BB0A8C1_0x00501730 | 0.0% | Pending SHC_3BB0A8C1_0x00501760 | 0.0% | Pending -SHC_3BB0A8C1_0x005017A0 | 0.0% | Pending +SHC_3BB0A8C1_0x005017A0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005017C0 | 0.0% | Pending +SHC_3BB0A8C1_0x005017C0 | 49.0% | global instance access -SHC_3BB0A8C1_0x00501A20 | 0.0% | Pending +SHC_3BB0A8C1_0x00501A20 | 91.7% | 84.72% regalloc -SHC_3BB0A8C1_0x00501B90 | 0.0% | Pending +SHC_3BB0A8C1_0x00501B90 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00501CB0 | 0.0% | Pending @@ -41561,25 +41561,25 @@ SHC_3BB0A8C1_0x00501CC4 | 0.0% | Pending SHC_3BB0A8C1_0x00501CC8 | 0.0% | Pending -SHC_3BB0A8C1_0x00501CD0 | 0.0% | Pending +SHC_3BB0A8C1_0x00501CD0 | 82.0% | brush loop regalloc -SHC_3BB0A8C1_0x00501D90 | 0.0% | Pending +SHC_3BB0A8C1_0x00501D90 | 53.1% | const 1 not pooled in ebx -SHC_3BB0A8C1_0x00501F50 | 0.0% | Pending +SHC_3BB0A8C1_0x00501F50 | 35.6% | asm macro, regalloc differs SHC_3BB0A8C1_0x00501FE0 | 0.0% | Pending -SHC_3BB0A8C1_0x00502110 | 0.0% | Pending +SHC_3BB0A8C1_0x00502110 | 43.3% | asm macro, regalloc differs -SHC_3BB0A8C1_0x00502390 | 0.0% | Pending +SHC_3BB0A8C1_0x00502390 | 28.1% | brush loop regalloc -SHC_3BB0A8C1_0x005024F0 | 0.0% | Pending +SHC_3BB0A8C1_0x005024F0 | 51.8% | 47.37% index spill regalloc -SHC_3BB0A8C1_0x00502680 | 0.0% | Pending +SHC_3BB0A8C1_0x00502680 | 50.0% | asm macro, cold arms inline -SHC_3BB0A8C1_0x00502950 | 0.0% | Pending +SHC_3BB0A8C1_0x00502950 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005029D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005029D0 | 28.5% | 34.09% this spilled vs regallo SHC_3BB0A8C1_0x00502F14 | 0.0% | Pending @@ -41615,7 +41615,7 @@ SHC_3BB0A8C1_0x00502F29 | 0.0% | Pending SHC_3BB0A8C1_0x00502F2A | 0.0% | Pending -SHC_3BB0A8C1_0x00502F30 | 0.0% | Pending +SHC_3BB0A8C1_0x00502F30 | 33.9% | 36.78% shared blocks as flags SHC_3BB0A8C1_0x0050347C | 0.0% | Pending @@ -41651,11 +41651,11 @@ SHC_3BB0A8C1_0x00503491 | 0.0% | Pending SHC_3BB0A8C1_0x00503492 | 0.0% | Pending -SHC_3BB0A8C1_0x005034A0 | 0.0% | Pending +SHC_3BB0A8C1_0x005034A0 | 54.5% | 57.24% regalloc -SHC_3BB0A8C1_0x00503720 | 0.0% | Pending +SHC_3BB0A8C1_0x00503720 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005037B0 | 0.0% | Pending +SHC_3BB0A8C1_0x005037B0 | 43.3% | size flag as int set in if SHC_3BB0A8C1_0x005046A0 | 0.0% | Pending @@ -41959,7 +41959,7 @@ SHC_3BB0A8C1_0x00504A26 | 0.0% | Pending SHC_3BB0A8C1_0x00504A27 | 0.0% | Pending -SHC_3BB0A8C1_0x00504A30 | 0.0% | Pending +SHC_3BB0A8C1_0x00504A30 | 49.5% | global instance access SHC_3BB0A8C1_0x00504E88 | 0.0% | Pending @@ -42067,9 +42067,9 @@ SHC_3BB0A8C1_0x00504ED0 | 0.0% | Pending SHC_3BB0A8C1_0x00504ED1 | 0.0% | Pending -SHC_3BB0A8C1_0x00504EE0 | 0.0% | Pending +SHC_3BB0A8C1_0x00504EE0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00504F10 | 0.0% | Pending +SHC_3BB0A8C1_0x00504F10 | 30.2% | Style pass, asm differs SHC_3BB0A8C1_0x005050F0 | 0.0% | Pending @@ -42081,25 +42081,25 @@ SHC_3BB0A8C1_0x00505A10 | 0.0% | Pending SHC_3BB0A8C1_0x005060C0 | 0.0% | Pending -SHC_3BB0A8C1_0x00506370 | 0.0% | Pending +SHC_3BB0A8C1_0x00506370 | 53.2% | signed % and / restored -SHC_3BB0A8C1_0x00506AD0 | 0.0% | Pending +SHC_3BB0A8C1_0x00506AD0 | 77.9% | buildingType chain tail-merge -SHC_3BB0A8C1_0x00506BD0 | 0.0% | Pending +SHC_3BB0A8C1_0x00506BD0 | 75.9% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00506D40 | 0.0% | Pending +SHC_3BB0A8C1_0x00506D40 | 89.6% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00506E10 | 0.0% | Pending +SHC_3BB0A8C1_0x00506E10 | 84.5% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00506F40 | 0.0% | Pending +SHC_3BB0A8C1_0x00506F40 | 85.2% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00507060 | 0.0% | Pending +SHC_3BB0A8C1_0x00507060 | 90.8% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00507130 | 0.0% | Pending +SHC_3BB0A8C1_0x00507130 | 53.1% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00507280 | 0.0% | Pending +SHC_3BB0A8C1_0x00507280 | 54.5% | ecx not kept across callee -SHC_3BB0A8C1_0x00507350 | 0.0% | Pending +SHC_3BB0A8C1_0x00507350 | 74.1% | tree switch jump table SHC_3BB0A8C1_0x00507408 | 0.0% | Pending @@ -42135,9 +42135,9 @@ SHC_3BB0A8C1_0x0050741D | 0.0% | Pending SHC_3BB0A8C1_0x0050741E | 0.0% | Pending -SHC_3BB0A8C1_0x00507420 | 0.0% | Pending +SHC_3BB0A8C1_0x00507420 | 44.8% | setupBuildingData arg regalloc -SHC_3BB0A8C1_0x00507560 | 0.0% | Pending +SHC_3BB0A8C1_0x00507560 | 44.8% | setupBuildingData arg regalloc SHC_3BB0A8C1_0x005076A0 | 0.0% | Pending @@ -42151,21 +42151,21 @@ SHC_3BB0A8C1_0x00507E00 | 0.0% | Pending SHC_3BB0A8C1_0x00507F80 | 0.0% | Pending -SHC_3BB0A8C1_0x00508030 | 0.0% | Pending +SHC_3BB0A8C1_0x00508030 | 69.3% | LTCG, regalloc differs -SHC_3BB0A8C1_0x00508250 | 0.0% | Pending +SHC_3BB0A8C1_0x00508250 | 40.3% | 39.78% regalloc -SHC_3BB0A8C1_0x00508540 | 0.0% | Pending +SHC_3BB0A8C1_0x00508540 | 52.9% | 55.63% operand order regalloc SHC_3BB0A8C1_0x00508590 | 0.0% | Pending -SHC_3BB0A8C1_0x00508760 | 0.0% | Pending +SHC_3BB0A8C1_0x00508760 | 75.0% | LTCG, regalloc differs -SHC_3BB0A8C1_0x00508870 | 0.0% | Pending +SHC_3BB0A8C1_0x00508870 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00508910 | 0.0% | Pending +SHC_3BB0A8C1_0x00508910 | 76.9% | LTCG, regalloc differs -SHC_3BB0A8C1_0x00508A00 | 0.0% | Pending +SHC_3BB0A8C1_0x00508A00 | 71.4% | 71.43% jump table, gfx select SHC_3BB0A8C1_0x00508C7C | 0.0% | Pending @@ -42763,11 +42763,11 @@ SHC_3BB0A8C1_0x00508DC2 | 0.0% | Pending SHC_3BB0A8C1_0x00508DC3 | 0.0% | Pending -SHC_3BB0A8C1_0x00508DD0 | 0.0% | Pending +SHC_3BB0A8C1_0x00508DD0 | 43.1% | extra callee-saved reg -SHC_3BB0A8C1_0x00508EC0 | 0.0% | Pending +SHC_3BB0A8C1_0x00508EC0 | 36.5% | global instance access -SHC_3BB0A8C1_0x00509180 | 0.0% | Pending +SHC_3BB0A8C1_0x00509180 | 26.5% | signed layer reads SHC_3BB0A8C1_0x0050A74E | 0.0% | Pending @@ -44173,23 +44173,23 @@ SHC_3BB0A8C1_0x00511018 | 0.0% | Pending SHC_3BB0A8C1_0x0051101C | 0.0% | Pending -SHC_3BB0A8C1_0x00511020 | 0.0% | Pending +SHC_3BB0A8C1_0x00511020 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x005110B0 | 0.0% | Pending +SHC_3BB0A8C1_0x005110B0 | 84.0% | moat scan regalloc -SHC_3BB0A8C1_0x005111D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005111D0 | 48.8% | global instance access SHC_3BB0A8C1_0x00511240 | 0.0% | Pending -SHC_3BB0A8C1_0x005113F0 | 0.0% | Pending +SHC_3BB0A8C1_0x005113F0 | 54.2% | moat field reload/regalloc -SHC_3BB0A8C1_0x005114C0 | 0.0% | Pending +SHC_3BB0A8C1_0x005114C0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x005115C0 | 0.0% | Pending -SHC_3BB0A8C1_0x005116E0 | 0.0% | Pending +SHC_3BB0A8C1_0x005116E0 | 72.4% | pitch field reload/regalloc -SHC_3BB0A8C1_0x00511850 | 0.0% | Pending +SHC_3BB0A8C1_0x00511850 | 100.0% | Reimplemented SHC_3BB0A8C1_0x005118C0 | 0.0% | Pending @@ -44723,31 +44723,31 @@ SHC_3BB0A8C1_0x00511D60 | 0.0% | Pending SHC_3BB0A8C1_0x00511D64 | 0.0% | Pending -SHC_3BB0A8C1_0x00511D70 | 0.0% | Pending +SHC_3BB0A8C1_0x00511D70 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00511DF0 | 0.0% | Pending +SHC_3BB0A8C1_0x00511DF0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x005120C0 | 0.0% | Pending -SHC_3BB0A8C1_0x00512100 | 0.0% | Pending +SHC_3BB0A8C1_0x00512100 | 89.0% | global instance access SHC_3BB0A8C1_0x00512180 | 0.0% | Pending -SHC_3BB0A8C1_0x00512450 | 0.0% | Pending +SHC_3BB0A8C1_0x00512450 | 79.2% | global instance access -SHC_3BB0A8C1_0x005127B0 | 0.0% | Pending +SHC_3BB0A8C1_0x005127B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x005127D0 | 0.0% | Pending -SHC_3BB0A8C1_0x00512920 | 0.0% | Pending +SHC_3BB0A8C1_0x00512920 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00512940 | 0.0% | Pending +SHC_3BB0A8C1_0x00512940 | 76.0% | Od stack slot order differs -SHC_3BB0A8C1_0x00514480 | 0.0% | Pending +SHC_3BB0A8C1_0x00514480 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00514520 | 0.0% | Pending +SHC_3BB0A8C1_0x00514520 | 75.3% | 75.63% spill order -SHC_3BB0A8C1_0x005146D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005146D0 | 40.4% | 47.46% regalloc SHC_3BB0A8C1_0x00514BC0 | 0.0% | Pending @@ -44767,17 +44767,17 @@ SHC_3BB0A8C1_0x00515680 | 0.0% | Pending SHC_3BB0A8C1_0x00515740 | 0.0% | Pending -SHC_3BB0A8C1_0x00515A20 | 0.0% | Pending +SHC_3BB0A8C1_0x00515A20 | 34.1% | 54.84% x/y regalloc swap -SHC_3BB0A8C1_0x00515C80 | 0.0% | Pending +SHC_3BB0A8C1_0x00515C80 | 52.6% | ecx not kept across callee -SHC_3BB0A8C1_0x00515DA0 | 0.0% | Pending +SHC_3BB0A8C1_0x00515DA0 | 36.1% | brush loop regalloc -SHC_3BB0A8C1_0x00515F40 | 0.0% | Pending +SHC_3BB0A8C1_0x00515F40 | 69.9% | Style pass, asm differs -SHC_3BB0A8C1_0x005160C0 | 0.0% | Pending +SHC_3BB0A8C1_0x005160C0 | 49.5% | global instance access -SHC_3BB0A8C1_0x005162D0 | 0.0% | Pending +SHC_3BB0A8C1_0x005162D0 | 41.9% | global instance access SHC_3BB0A8C1_0x00516A64 | 0.0% | Pending @@ -45133,7 +45133,7 @@ SHC_3BB0A8C1_0x00516B73 | 0.0% | Pending SHC_3BB0A8C1_0x00516B74 | 0.0% | Pending -SHC_3BB0A8C1_0x00516B80 | 0.0% | Pending +SHC_3BB0A8C1_0x00516B80 | 46.0% | 47.86% four labels as flags SHC_3BB0A8C1_0x005176C8 | 0.0% | Pending @@ -45435,7 +45435,7 @@ SHC_3BB0A8C1_0x00517789 | 0.0% | Pending SHC_3BB0A8C1_0x0051778A | 0.0% | Pending -SHC_3BB0A8C1_0x00517790 | 0.0% | Pending +SHC_3BB0A8C1_0x00517790 | 42.5% | 42.52% shared destroy body as SHC_3BB0A8C1_0x00518068 | 0.0% | Pending diff --git a/tools/mcp/openshcmcp.py b/tools/mcp/openshcmcp.py index af461afec..1d380060c 100644 --- a/tools/mcp/openshcmcp.py +++ b/tools/mcp/openshcmcp.py @@ -111,7 +111,7 @@ def extract_multiple_functions_assembly_diffs(function_names: List[str], match_p Returns: Tuple of (success, diffs of the functions, stdout, stderr) """ - cmd = [str(cwd / "reccmp" / "dll" / "run.bat"), "reccmp-reccmp", "--target", "STRONGHOLDCRUSADER", "--json", "diff.json"] + cmd = [str(cwd / "reccmp" / "dll" / "run.bat"), "reccmp-reccmp", "--target", "STRONGHOLDCRUSADER", "--json", "diff.json", "--resolve-wrapped-calls"] try: result = subprocess.run( diff --git a/tools/reimplementation-helper/Get-Incomplete-Status-On-Branch.ps1 b/tools/reimplementation-helper/Get-Incomplete-Status-On-Branch.ps1 new file mode 100644 index 000000000..acab58515 --- /dev/null +++ b/tools/reimplementation-helper/Get-Incomplete-Status-On-Branch.ps1 @@ -0,0 +1 @@ +git diff main HEAD .\status\addresses-SHC-3BB0A8C1.txt | Where-Object {$_.StartsWith("+SHC") -and (-not $_.Contains("| 100"))} | ForEach-Object {$_.SubString(1)} diff --git a/tools/reimplementation-helper/Promote-Branch-To-Worktree.ps1 b/tools/reimplementation-helper/Promote-Branch-To-Worktree.ps1 new file mode 100644 index 000000000..8a188a9b1 --- /dev/null +++ b/tools/reimplementation-helper/Promote-Branch-To-Worktree.ps1 @@ -0,0 +1,36 @@ +param ( + [string]$Branch, + [string]$Path +) + +$ErrorActionPreference = "Stop" + +$CurrentBranch = git branch --show-current + +if ("main" -ne "$CurrentBranch") {Write-Error "Not on 'main'"} + +mkdir "$Path" + +git branch $Branch + +git worktree add "$Path" $Branch + +mkdir "$Path\_original" + +copy "_original\Stronghold Crusader.exe" "$Path\_original\" + +pushd "$Path" + +git submodule update --init --recursive + +reccmp\dll\setup.bat + +git push --set-upstream origin $Branch + +.\tools\reimplementation-control\Enable-Reimplemented-Data.ps1 + +.\build.bat RelWithDebInfo OpenSHC.dll + +reccmp/dll/run.bat reccmp-reccmp --target STRONGHOLDCRUSADER + +popd diff --git a/tools/reimplementation-helper/Set-Sources-From-Incomplete-Status-On-Branch.ps1 b/tools/reimplementation-helper/Set-Sources-From-Incomplete-Status-On-Branch.ps1 new file mode 100644 index 000000000..655754193 --- /dev/null +++ b/tools/reimplementation-helper/Set-Sources-From-Incomplete-Status-On-Branch.ps1 @@ -0,0 +1,43 @@ +# Writes the .cpp files of every function this branch touched but did not finish (status neither +# 100% nor 0.0%) to cmake/openshc-sources.txt.local. Run from the repository root. +param( + [string]$Base = "main", + [string]$StatusFile = "status/addresses-SHC-3BB0A8C1.txt", + [string]$SourceRoot = "src/OpenSHC", + [string]$Destination = "cmake/openshc-sources.txt.local" +) + +$lines = git diff $Base HEAD -- $StatusFile | Where-Object { $_.StartsWith("+SHC") -and (-not $_.Contains("| 100")) -and (-not $_.Contains("| 0.0%")) } | ForEach-Object { $_.SubString(1) } + +# address -> status line +$wanted = @{} +foreach ($line in $lines) { + if ($line -match "^SHC_[0-9A-Fa-f]+_0x([0-9A-Fa-f]+)") { + $wanted[$matches[1].ToUpper()] = $line + } +} + +$root = (Get-Location).Path +$files = New-Object 'System.Collections.Generic.SortedSet[string]' ([System.StringComparer]::Ordinal) +$found = @{} + +# A single .cpp can hold several // FUNCTION: lines, so every match in every file is checked. +Get-ChildItem -Path $SourceRoot -Recurse -Filter *.cpp | Select-String -Pattern "//\s*FUNCTION:\s*\S+\s+0x([0-9A-Fa-f]+)" | ForEach-Object { + $address = $_.Matches[0].Groups[1].Value.ToUpper() + if ($wanted.ContainsKey($address)) { + $found[$address] = $true + [void]$files.Add($_.Path.Substring($root.Length + 1).Replace("\", "/")) + } +} + +foreach ($address in ($wanted.Keys | Sort-Object)) { + if (-not $found.ContainsKey($address)) { + Write-Warning "no .cpp found for: $($wanted[$address])" + } +} + +$target = Join-Path $root $Destination +$text = if ($files.Count -gt 0) { ($files -join "`n") + "`n" } else { "" } +[System.IO.File]::WriteAllText($target, $text, (New-Object System.Text.UTF8Encoding $false)) + +Write-Host "$($wanted.Count) incomplete functions -> $($files.Count) files written to $Destination" diff --git a/tools/reimplementation-helper/batch/README.md b/tools/reimplementation-helper/batch/README.md index 3350aea2b..dd5d15cb3 100644 --- a/tools/reimplementation-helper/batch/README.md +++ b/tools/reimplementation-helper/batch/README.md @@ -12,15 +12,54 @@ rewrite sources, because the Visual Studio bundled one is too old for `.clang-fo | Script | Purpose | |---|---| -| `build_quiet.py [--keep-going]` | `build.bat RelWithDebInfo OpenSHC.dll`, prints only errors and `BUILD_OK`/`BUILD_FAIL` (build.bat exits 0 even on errors). | +| `build_quiet.py [--keep-going]` | `build.bat RelWithDebInfo OpenSHC.dll`, prints only errors and `BUILD_OK`/`BUILD_FAIL` (build.bat exits 0 even on errors). Retries once on a PDB server error, see the note below. | | `syntax_check.py [substr...]` | `cl /Zs` every (matching) list file serially; finds all compile errors in one pass without linking. | | `reccmp_report.py [--run] pct\|save\|cmp\|diff` | Match % and *normalized* % (call/tail-jump targets and resolver addresses ignored) per function, snapshots, before/after comparison, compact asm diffs. | +| `orig_asm.py NAME [--both\|--stats]` | The **original** instruction stream of one function (not the interleaved diff), in address order; `--stats` reports the tells that decide how to reimplement it: `mov reg, 0` (handwritten asm), frame pointer (built /Od), frame size, jump tables. | | `show_functions.py PREFIX [--skip-100]` | Print function bodies under `src/OpenSHC/PREFIX` with their percentages, in the block format `splice_functions.py` reads. | | `splice_functions.py BLOCKS.txt` | Replace many function bodies at once (keeps the `// FUNCTION:` line, can add includes/usings), then clang-format. | | `commit_progress_batch.py PREFIX... [Name=remark]` | One `reimplement:` commit per changed function with its status line, like `commit_progress`. | | `fix_func_refs.py [--drop-include H]` | After a header refactor: rewrite `X_Func::name` references whose resolver moved to another namespace. | | `move_to_resolver_namespace.py FILE...` | After a header refactor: re-wrap a definition in its resolver's new namespace and `git mv` it to the matching folder. | +Analysis helpers, for deciding *what* to change when a function is stuck below 100%: + +| Script | Purpose | +|---|---| +| `diff_triage.py [--run] [filter]` | One line per function: match %, raw and register/slot-normalized difflib ratios, `sub esp` frame size per side, byte-sized `[esp]` accesses per side, `ret` placement. Flags `FRAME` / `BYTE` / `RET` / `alloc-only`. | +| `diff_slots.py NAME` | Every `[esp + N]` slot with its access count on each side, and the slots only we use. Run after `diff_triage.py` reports `FRAME`. | +| `diff_jcc.py [NAME]` | Comparisons whose operator is one strictness step off the original (`jl`/`jle`, `jg`/`jge`, `ja`/`jae`) or whose `cmp` operands are the other way round, with context. | +| `diff_types.py [--run] [NAME]` | The non-comparison counterpart of `diff_jcc.py`: `movsx` against `movzx` for the same field (its signedness is wrong in the generated header), `add` against `sub` with the same magnitude (a sign error in a formula - how `2400/n + 40` was caught being `- 40`). It pairs only equal-length runs inside one reccmp block, so it stays quiet once the streams drift: it reports nothing across the 101 functions of `Game::GameStateStructures` and fires on both bugs when they are reintroduced. | +| `quick_diff.py FILE.cpp... [-q]` | Compile one file with `/FA` and diff it against the original function disassembled straight from `_original/` - about 6 seconds, no link and no reccmp, so a dozen source variants can be tried in the time of one build. Shows the whole original function, which reccmp's diff does not when ours is shorter. A triage loop: confirm a 1.0 with `reccmp_report.py --run`, and do not trust a *rise* of its ratio below about 0.6 - on a badly matching function difflib rewards a different alignment, and one such "gain" was a 10-point loss in reccmp. | +| `reorder_search.py FILE NAME [--pairs] [--dry]` | Hill-climb the match % by reordering independent statements: permute runs of >=3 by default, or swap every adjacent independent pair with `--pairs`. Builds once per move and reverts anything that does not improve. | + +### Choosing what to work on + +| Script | Purpose | +|---|---| +| `rank_functions.py [filter] [--top N]` | Rank by source lines x (1 - match), so the big badly-matching functions come first instead of the small ones with a low percentage. | +| `scan_dispatch.py [filter]` | Functions whose dispatch form disagrees with the original (jump table on one side, if/else-if chain on the other), and which way to fix each. | +| `scan_chains.py [filter] [--min N]` | `if/else if` chains comparing one expression against several constants: the candidates to convert when `scan_dispatch.py` says the original has a table we do not. | +| `try_styles.py FUNC [style...]` | Compile and measure several hand-written variants of one function, restore the winner, print the table. Variants live in `build-RelWithDebInfo/batch/variants/FUNC/*.cpp`. | + +### Readability passes + +These undo decompiler artefacts. Each measured as code-generation neutral or better where +it was applied, but none is guaranteed to be -- re-measure after running one. + +| Script | Purpose | +|---|---| +| `declare_at_use.py [filter]` | Move the leading declaration block down to each variable's first use, and drop declarations that are never used. Only moves one when the first assignment provably dominates every later use. | +| `decast.py [filter]` | Collapse doubled casts, `(int)((int)(x))` -> `(int)(x)`. Skips `(int)((int)X + Y)`, where the inner cast covers one operand and dropping it would move the conversion onto the sum. | +| `deparen.py [filter] [--apply]` | Drop redundant brackets from `if`/`while` conditions using C precedence. Keeps the load-bearing ones (`(a \|\| b) && c`, `(x & 7) == 0`, assignments and comma operators inside conditions). Dry run unless `--apply`. | +| `test_deparen.py` | Precedence tests for `deparen.py`. Run after touching its table: an earlier version silently rewrote `(a != 0 \|\| b != 0) && (x & 0x100) == 0` into a different expression that still compiled. | +| `undiv.py [filter] [--apply]` | Turn MSVC's biased shift for a signed power-of-two division back into a division: `(x + (x >> 0x1f & 7U)) >> 3` -> `x / 8`. Dry run unless `--apply`. Usually a large gain rather than merely neutral: over one namespace it was worth up to 22% on a single function. | +| `test_undiv.py` | Cases for `undiv.py`, including the near-misses it must leave alone: a shift disagreeing with the mask, a mask that is not 2^k - 1, and a bias taken from another variable. | +| `unmod.py [filter] [--apply]` | The same for a signed power-of-two remainder: `v = x & 0x8000000f;` plus the negative-case repair is `v = x % 16`. Forces a signed dividend, because the repair only exists for a signed operand and an unsigned `%` compiles to a bare `and`. | +| `test_unmod.py` | Cases for `unmod.py`: a repair constant that does not complement the mask, a repair applied to another variable, and a bare mask with no repair. | +| `unptr.py [filter] [--apply]` | Replace the decompiler's field-walking pointers (`piVar1 = &x.f; *piVar1 = *piVar1 + 1;`) with the field itself. Only the two-adjacent-line shape, since a pointer freezes the address while the field form re-evaluates the index. Reports which pointers are still in use. | +| `test_unptr.py` | Cases for `unptr.py`, including a pointer declared on the spot (`char* pcVar3 = &...`, whose type prefix must go with it) and the set-up-then-use-later shape it must leave alone. | + ## Typical loop ```sh @@ -33,6 +72,27 @@ python reccmp_report.py diff someFunction # inspe python commit_progress_batch.py Map/Units "someFunction=LTCG ecx reuse" ``` +## Reading `diff_triage.py` + +The two ratios are the quickest way to tell whether more source work can pay: + +- `norm` much higher than `raw` - only register and stack-slot *naming* differs. + The allocator is not reachable from the source; record the reason and move on. +- `norm` still low - real structure or ordering differs; keep looking. +- `FRAME` - one local too many or too few. Chase it with `diff_slots.py`; this has + been worth 10-30 points (a redundant Ghidra temp, or two copies of a parameter + where the original introduced fresh locals instead of reassigning it). +- `BYTE` - we have byte-sized stack slots the original does not: every `bool`/`byte` + local is an `int` in the original. +- `RET` - we inline a cold early-return block the original places out of line. + Restructure so the cold case is last in the source (`if (ok) { body } else { reset }` + rather than a guard at the top), which is what makes MSVC outline it. + +Confirm a `diff_jcc.py` fix by re-running that tool and checking the pair is gone, +not by the percentage: a single instruction moves the score by ~0.1 and tells you +nothing, while three such fixes in one namespace turned out to be real off-by-one +bugs that Ghidra had decompiled wrongly. + ## Notes - `normalized 100%` means only call-target differences remain. `commit_progress_batch.py` @@ -40,3 +100,16 @@ python commit_progress_batch.py Map/Units "someFunction=LTCG ecx reuse" - A file whose `// FUNCTION:` address has no reccmp entry is reported as `--` / skipped. Check the address against `src/precomp/addresses-SHC-3BB0A8C1.hpp`. - Sources are always written as UTF-8 with LF line endings. +- Every script that runs `cl.exe` sets this worktree's own `_MSPDBSRV_ENDPOINT_` + (`common.build_env`), the same name `build.bat` derives. `mspdbsrv.exe` is a per-user + singleton keyed on that endpoint and each worktree carries its own toolchain copy, so a + shared endpoint makes `cl.exe` talk to the wrong server and fail with + `fatal error C1090: PDB API call failed`. +- `build_quiet.py` and `syntax_check.py` both retry such a failure twice, because it has two + causes. A wedged server survives the kill `build.bat` does up front, so the first retry stops + this worktree's `mspdbsrv.exe` (`common.kill_pdb_server`, never other worktrees'). A wedged + *endpoint* outlives every `mspdbsrv.exe` using it, so the second retry moves to a fresh name + (`common.fresh_pdb_endpoint`) rather than retrying into one that will keep failing. +- In `syntax_check.py` a PDB failure is not just a lost run: the fatal line counts as an error + against whichever file hit it and the rest of the batch still gets checked, so without the + retries the output looks like a normal error list and hides the real compile errors. diff --git a/tools/reimplementation-helper/batch/build_quiet.py b/tools/reimplementation-helper/batch/build_quiet.py index 4b0367ab7..76fd17a59 100644 --- a/tools/reimplementation-helper/batch/build_quiet.py +++ b/tools/reimplementation-helper/batch/build_quiet.py @@ -5,13 +5,32 @@ Prints BUILD_OK or BUILD_FAIL as the last line. build.bat exits 0 even on compile errors, so the log is scanned instead of trusting the exit code. --keep-going reruns the build with nmake /K to collect errors from more files. + +The build runs with this worktree's own _MSPDBSRV_ENDPOINT_ (see common.build_env), which is what +build.bat does too; --keep-going bypasses build.bat and would otherwise inherit whatever endpoint +happens to be set. A build that dies with C1090/C1033 gets two retries, because that error has two +different causes: + + 1. a wedged PDB server, which survives the kill build.bat does up front -> stop this worktree's + mspdbsrv.exe and build again; + 2. a wedged *endpoint*, which outlives every mspdbsrv.exe using it -> build again on a fresh + endpoint name, since retrying into the same name fails forever. + +The second cost six consecutive builds once, across a regenerated PCH and PDBs, while the same tree +built first try under a different endpoint name. + +A build that fails without naming any source line gets one more plain retry. cmake's glob +verification dies with an access violation ("Access violation" + U1077 return code 0xffffffff) on the +first build after cmake/openshc-sources.txt.local changes, which is every first build of a new +selection; the reconfigure crashed, not the compile, and a plain re-run succeeds. """ import argparse import re import subprocess +import sys -from common import TMP +from common import PDB_ERROR_RE, TMP, build_env, fresh_pdb_endpoint, kill_pdb_server parser = argparse.ArgumentParser() parser.add_argument("--keep-going", action="store_true") @@ -19,16 +38,53 @@ args = parser.parse_args() log = TMP / "build.log" -with open(log, "w") as out: - subprocess.run(["cmd", "/c", r".\build.bat RelWithDebInfo OpenSHC.dll"], stdout=out, stderr=subprocess.STDOUT) -if args.keep_going: + + +def run_build(endpoint=None): with open(log, "w") as out: - subprocess.run(["cmd", "/c", r".\cmakew --build --preset RelWithDebInfo --target OpenSHC.dll -- /K"], - stdout=out, stderr=subprocess.STDOUT) + subprocess.run(["cmd", "/c", r".\build.bat RelWithDebInfo OpenSHC.dll"], + stdout=out, stderr=subprocess.STDOUT, env=build_env(endpoint)) + if args.keep_going: + with open(log, "w") as out: + subprocess.run(["cmd", "/c", r".\cmakew --build --preset RelWithDebInfo --target OpenSHC.dll -- /K"], + stdout=out, stderr=subprocess.STDOUT, env=build_env(endpoint)) + return log.read_text(errors="replace") + + +text = run_build() +if PDB_ERROR_RE.search(text): + print("note: PDB server error, stopping this worktree's mspdbsrv.exe and retrying", file=sys.stderr) + kill_pdb_server() + text = run_build() +if PDB_ERROR_RE.search(text): + # The endpoint itself can be wedged, in which case it outlives every mspdbsrv.exe using it and + # retrying into the same name fails forever. Move to a fresh one. + endpoint = fresh_pdb_endpoint() + print("note: PDB endpoint still failing, retrying on a fresh endpoint %s" % endpoint, + file=sys.stderr) + kill_pdb_server() + text = run_build(endpoint) + +def compile_errors(text): + return [line for line in text.splitlines() + if re.search(r" error |warning C4700|warning C4715", line) and "U1077" not in line] + + +def failing(text): + return bool(re.search(r" error |U1077", text)) + + +if failing(text) and not compile_errors(text): + # Nothing named a source line, so the compile is not what failed: cmake's glob verification + # crashed during the reconfigure. It succeeds on a plain re-run. + print("note: build failed without a compiler error, retrying once", file=sys.stderr) + text = run_build() -text = log.read_text(errors="replace") -errors = [line for line in text.splitlines() - if re.search(r" error |warning C4700|warning C4715", line) and "U1077" not in line] +errors = compile_errors(text) for line in errors[:args.max]: print(re.sub(r"^.*src.OpenSHC.", "", line)) -print("BUILD_FAIL" if re.search(r" error |U1077", text) else "BUILD_OK") +failed = failing(text) +print("BUILD_FAIL" if failed else "BUILD_OK") +# exit non-zero so `build_quiet.py && reccmp_report.py ...` stops here: a failed build +# leaves the previous DLL in place and reccmp would happily report on the old code +raise SystemExit(1 if failed else 0) diff --git a/tools/reimplementation-helper/batch/common.py b/tools/reimplementation-helper/batch/common.py index a5c05e54a..10ac26db2 100644 --- a/tools/reimplementation-helper/batch/common.py +++ b/tools/reimplementation-helper/batch/common.py @@ -27,6 +27,54 @@ DIFF_JSON = Path("reccmp/dll/diff.json") FUNCTION_RE = re.compile(r"//\s*FUNCTION:\s*STRONGHOLDCRUSADER\s+(0x[0-9A-Fa-f]+)") +# ---------------------------------------------------------------- pdb server + +# mspdbsrv.exe is a per-user singleton keyed on _MSPDBSRV_ENDPOINT_, and every worktree carries its +# own copy of the toolchain. Sharing one endpoint makes cl.exe talk to whichever copy started first +# and fail with "fatal error C1090: PDB API call failed, error code '23'" (or C1033). build.bat +# derives this same name, so setting it here keeps the two in step and also covers the scripts that +# drive cl.exe or cmake directly instead of going through build.bat. +PDB_ENDPOINT = "openshc_" + ROOT.name +PDB_ERROR_RE = re.compile(r"fatal error C10(90|33)") + + +def fresh_pdb_endpoint(): + """A previously unused endpoint name for this worktree. + + An endpoint can wedge in a way that outlives every mspdbsrv.exe using it: the name stays + unusable, and `cl.exe` keeps failing with C1090 no matter how often the server is killed and + restarted. Six consecutive builds failed that way once, across a fresh PCH and PDBs, while the + same tree built first try under a different name. So when killing the server does not help, + move to a new name rather than retrying into the same one. + """ + return "%s_%d" % (PDB_ENDPOINT, os.getpid()) + + +def build_env(endpoint=None): + """Environment for anything that runs cl.exe, with this worktree's PDB endpoint. + + An endpoint already set in the environment wins, so a caller can pin one for a whole session. + """ + env = dict(os.environ) + if endpoint: + env["_MSPDBSRV_ENDPOINT_"] = endpoint + else: + env.setdefault("_MSPDBSRV_ENDPOINT_", PDB_ENDPOINT) + return env + + +def kill_pdb_server(): + """Stop this worktree's mspdbsrv.exe only. + + Killing every instance by image name takes down the PDB server of any other worktree that is + building at the same time and fails their build with the same C1090. + """ + subprocess.run(["powershell", "-NoProfile", "-Command", + "Get-Process mspdbsrv -ErrorAction SilentlyContinue | " + "Where-Object { $_.Path -like '%s*' } | " + "Stop-Process -Force -ErrorAction SilentlyContinue" + % str(ROOT).replace("'", "''")], + capture_output=True) def list_files(existing_only=True): @@ -93,13 +141,34 @@ def function_body_span(text, start=0): def run_reccmp(): result = subprocess.run( - ["cmd", "/c", r"reccmp\dll\run.bat", "reccmp-reccmp", "--target", "STRONGHOLDCRUSADER", "--json", "diff.json"], + ["cmd", "/c", r"reccmp\dll\run.bat", "reccmp-reccmp", "--target", "STRONGHOLDCRUSADER", "--json", "diff.json", "--resolve-wrapped-calls"], capture_output=True, text=True, stdin=subprocess.DEVNULL) if result.returncode: sys.exit("reccmp failed:\n" + result.stdout[-800:] + result.stderr[-800:]) +def warn_if_stale(): + """Warn when diff.json predates a source file it is supposed to describe. + + build.bat exits 0 even when compilation failed, so a failed build leaves the previous + DLL and the previous diff.json in place and every reccmp number silently describes the + code as it was before the edit. That has been mistaken for "the change was neutral" + more than once, so say it out loud. + """ + try: + built = DIFF_JSON.stat().st_mtime + except OSError: + return + newer = [f for f in list_files() if Path(f).exists() and Path(f).stat().st_mtime > built] + if newer: + sys.stderr.write( + "warning: %d source file(s) are newer than the last reccmp run, e.g. %s.\n" + " Re-build (and check for BUILD_FAIL) before trusting these numbers.\n" + % (len(newer), newer[0])) + + def load_diff(): + warn_if_stale() return {int(e["address"], 16): e for e in json.loads(DIFF_JSON.read_text())["data"]} diff --git a/tools/reimplementation-helper/batch/decast.py b/tools/reimplementation-helper/batch/decast.py new file mode 100644 index 000000000..f473c5a25 --- /dev/null +++ b/tools/reimplementation-helper/batch/decast.py @@ -0,0 +1,96 @@ +"""Collapse the decompiler's doubled casts, e.g. (int)((int)(x)) -> (int)(x). + +Only a cast applied to a *whole* casted operand is removed. `(int)((int)X + Y)` looks the +same textually but is not a doubled cast: there the inner cast covers X alone, and +dropping it moves the conversion onto the sum, which changes the generated code. The +`sole_operand` check rejects those. + +Measured as code-generation neutral over the UnitsState namespace (19 casts, no change in +any function), so it is a readability pass -- but re-measure after running it. + +usage: + decast.py [path-filter] +""" + +import sys + +import common + +path_filter = sys.argv[1] if len(sys.argv) > 1 else "" + +# (outer, inner) pairs where dropping the inner cast cannot change the conversion +PAIRS = [("int", "int"), ("uint", "uint"), ("short", "short"), ("ushort", "ushort"), + ("uint", "int"), ("int", "uint"), ("short", "int"), ("ushort", "int")] + + +def match_paren(s, i): + depth = 0 + while i < len(s): + if s[i] == "(": + depth += 1 + elif s[i] == ")": + depth -= 1 + if depth == 0: + return i + i += 1 + raise ValueError("unbalanced") + + +def is_group(s): + s = s.strip() + return s.startswith("(") and match_paren(s, 0) == len(s) - 1 + + +def sole_operand(s): + """True if s is one operand rather than part of a wider expression.""" + s = s.strip() + if is_group(s): + return True + depth = 0 + for ch in s: + if ch in "([": + depth += 1 + elif ch in ")]": + depth -= 1 + elif depth == 0 and ch in "+-*/%&|^<>=!?,": + return False + return True + + +def collapse_once(text): + for outer, inner in PAIRS: + needle = "(%s)((%s)" % (outer, inner) + i = text.find(needle) + while i != -1: + try: + close = match_paren(text, i + len(outer) + 2) + except ValueError: + i = text.find(needle, i + 1) + continue + rest = text[i + len(outer) + 3:close][len(inner) + 2:].strip() + if rest and sole_operand(rest): + body = rest if is_group(rest) else "(%s)" % rest + return text[:i] + "(%s)%s" % (outer, body) + text[close + 1:], True + i = text.find(needle, i + 1) + return text, False + + +total = 0 +for f in common.list_files(): + if path_filter not in f: + continue + text = common.read_text(f).replace("\r\n", "\n") + n = 0 + while True: + text, changed = collapse_once(text) + if not changed: + break + n += 1 + if n > 200: + sys.exit("%s: not converging" % f) + if n: + common.write_text(f, text) + common.format_file(f) + print("%-52s %d" % (f.rsplit("/", 1)[-1][:-4], n)) + total += n +print("%d doubled cast(s) collapsed" % total) diff --git a/tools/reimplementation-helper/batch/declare_at_use.py b/tools/reimplementation-helper/batch/declare_at_use.py new file mode 100644 index 000000000..39741e74d --- /dev/null +++ b/tools/reimplementation-helper/batch/declare_at_use.py @@ -0,0 +1,101 @@ +"""Move a function's leading declaration block down to each variable's first use. + +The decompiler declares everything at the top; the original declared at first use. This +merges the declaration into the first assignment wherever the scope allows it, and drops +declarations that are never used at all. + +Unlike a plain "single assignment" rule this also handles variables assigned more than +once: only the *first* assignment has to dominate every later use, which is checked by +tracking brace depth from that assignment to the last use. Anything it cannot prove is +left alone and reported, so the output doubles as a list of what still needs a hand. + +Declaration position does not change code generation, so this is a readability pass; run +it before measuring, not instead of. + +usage: + declare_at_use.py [path-filter] +""" + +import re +import sys + +import common + +path_filter = sys.argv[1] if len(sys.argv) > 1 else "" + +DECL = re.compile(r"^(?P(?:unsigned\s+|const\s+)*[A-Za-z_][\w:]*)\s+" + r"(?P\**)\s*(?P[A-Za-z_]\w*)\s*" + r"(?P\[[^\]]*\])?\s*;$") + + +def depths(lines): + out, d = [], 0 + for line in lines: + out.append(d) + code = re.sub(r"//.*", "", line) + d += code.count("{") - code.count("}") + return out + + +def move(path): + text = common.read_text(path).replace("\r\n", "\n") + brace = re.compile(r"\n[ \t]*\{[ \t]*\n").search(text, text.index("// FUNCTION:")) + if not brace: + return None + head, lines = text[:brace.end()], text[brace.end():].split("\n") + + decls, i = [], 0 + while i < len(lines): + stripped = lines[i].strip() + if not stripped: + i += 1 + continue + m = DECL.match(stripped) + if not m: + break + decls.append((m.group("name"), m.group("type"), m.group("ptr") or "", + m.group("arr") or "", i)) + i += 1 + if not decls: + return None + + drop = set() + for var, vtype, ptr, arr, decl_at in decls: + word = re.compile(r"(?=", "<", ">"]] +COMPARISON = 7 # index in LEVELS of ==/!=; anything at or past this is a comparison + + +def match_paren(s, i): + depth = 0 + while i < len(s): + if s[i] in "([": + depth += 1 + elif s[i] in ")]": + depth -= 1 + if depth == 0: + return i + i += 1 + return -1 + + +def scan_top(s): + """Yield (index, level, token) for every top-level operator.""" + i = 0 + while i < len(s): + if s[i] in "([": + j = match_paren(s, i) + i = (j + 1) if j != -1 else i + 1 + continue + for level, tokens in enumerate(LEVELS): + hit = None + for tok in tokens: + if not s.startswith(tok, i): + continue + if tok == "=" and (s[i - 1:i] in "=!<>" or s[i + 1:i + 2] == "="): + continue + if tok in ("<", ">") and s[i - 1:i + 1] == "->": + continue + hit = tok + break + if hit: + yield i, level, hit + i += len(hit) - 1 + break + i += 1 + + +def rank(s): + """Level of the loosest top-level operator, or len(LEVELS) when there is none.""" + best = len(LEVELS) + for _, level, _ in scan_top(s): + best = min(best, level) + return best + + +def split_top(s, tok): + parts, last = [], 0 + for idx, _, t in scan_top(s): + if t == tok and idx >= last: + parts.append(s[last:idx]) + last = idx + len(tok) + parts.append(s[last:]) + return parts + + +def is_group(s): + s = s.strip() + return s.startswith("(") and match_paren(s, 0) == len(s) - 1 + + +def simplify(expr): + """Simplify inside expr. Its own outer brackets are the caller's to judge, since only + the caller knows which operator binds it to its siblings.""" + expr = expr.strip() + r = rank(expr) + if r >= len(LEVELS): + return expr + tokens = [t for _, level, t in scan_top(expr) if level == r] + if not tokens or any(t != tokens[0] for t in tokens): + return expr # mixed operators at one level: leave it + tok = tokens[0] + out = [] + for part in split_top(expr, tok): + part = part.strip() + while is_group(part): + inner = part[1:-1].strip() + ri = rank(inner) + if ri <= 1 or not (ri == r or ri >= COMPARISON): + break + part = inner + out.append(simplify(part)) + return (" %s " % tok).join(out) + + +CONDITION = re.compile(r"(?m)^(\s*)(\}\s*else\s+)?(if|while)\s*\(") + + +def process(text): + out, pos, n = [], 0, 0 + for m in CONDITION.finditer(text): + if m.start() < pos: + continue + open_at = text.index("(", m.end() - 1) + close_at = match_paren(text, open_at) + if close_at == -1: + continue + flat = " ".join(text[open_at + 1:close_at].split()) + # the if's own brackets already group the whole condition, so one redundant layer + # directly inside them can always go + outer = flat + while is_group(outer) and rank(outer[1:-1]) > 1: + outer = outer[1:-1].strip() + new = simplify(outer) + if new != flat and ("((" in flat or "))" in flat): + out.append(text[pos:open_at + 1]) + out.append(new) + pos = close_at + n += 1 + out.append(text[pos:]) + return "".join(out), n + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("path_filter", nargs="?", default="") + parser.add_argument("--apply", action="store_true") + args = parser.parse_args() + + total = 0 + for f in common.list_files(): + if args.path_filter not in f: + continue + new, n = process(common.read_text(f).replace("\r\n", "\n")) + if not n: + continue + print("%-52s %d" % (f.rsplit("/", 1)[-1][:-4], n)) + total += n + if args.apply: + common.write_text(f, new) + common.format_file(f) + print("%d condition(s) simplified%s" % (total, "" if args.apply else " (dry run, pass --apply)")) diff --git a/tools/reimplementation-helper/batch/diff_jcc.py b/tools/reimplementation-helper/batch/diff_jcc.py new file mode 100644 index 000000000..aaaa3f202 --- /dev/null +++ b/tools/reimplementation-helper/batch/diff_jcc.py @@ -0,0 +1,84 @@ +"""Find comparisons whose operator or operand order is one step off the original. + +usage: diff_jcc.py [NAME-SUBSTRING] + +Reports two kinds of pair, each with the four preceding instructions so the +site can be found in the source: + + JCC the two sides differ by one strictness step (jl/jle, jg/jge, ja/jae) + or are direction-flipped (jl/jge). A strictness step is a real + off-by-one in the source: write the literal and operator the original + shows, e.g. `cmp x,0xa0; jle` is `x <= 160`, not `x < 160`. + CMP the same two operands compared the other way round; swap the sides of + the comparison in the source. + +This has found genuine bugs where Ghidra's decompilation was wrong, so confirm +a fix by re-running this tool and checking the pair is gone - the match +percentage barely moves for a single instruction and tells you nothing. + +Two pairs it reports are NOT source bugs, so check the context before editing: + * `orig jl / ours jge` where both sides share the `cmp` is block placement, + not an operator - the original puts the if-body out of line and jumps to + it. Fix that by moving the cold case last in the source, not by flipping + the test. + * `orig jns / ours jge` right after an `and` is the same thing twice; the + `and` clears OF so the two encodings are equivalent. +""" + +import re +import sys + +import common + +JCC = re.compile(r"^(j[a-z]+)\s") +CMP = re.compile(r"^cmp (.+?), (.+)$") +STEP = {("jl", "jle"), ("jle", "jl"), ("jg", "jge"), ("jge", "jg"), + ("ja", "jae"), ("jae", "ja"), ("jb", "jbe"), ("jbe", "jb"), + ("jl", "jge"), ("jge", "jl"), ("jg", "jle"), ("jle", "jg")} + +want = sys.argv[1] if len(sys.argv) > 1 else "" + + +def stream(entry): + """Interleaved (kind, text, source-annotation) in emission order. + + Pairing an `orig` line with the `recomp` line that immediately follows it is + the only sound comparison; indexing `block['orig'][i]` against + `block['recomp'][i]` invents pairs out of unrelated instructions. + """ + out = [] + for hunk in entry.get("diff") or []: + for block in hunk[1]: + for kind, rows in block.items(): + for row in rows: + text = row[1] + src = text.split("\t")[1].strip() if "\t" in text else "" + out.append((kind, text.split("\t")[0].strip(), src)) + return out + + +for f, e, address in common.entries(common.load_diff()): + if e is None or (want and want not in e["name"]) or not e.get("diff"): + continue + rows = stream(e) + hits = [] + for i in range(len(rows) - 1): + k1, a, _ = rows[i] + k2, c, src = rows[i + 1] + if k1 != "orig" or k2 != "recomp": + continue + ja, jc = JCC.match(a), JCC.match(c) + if ja and jc and (ja.group(1), jc.group(1)) in STEP: + hits.append(("JCC orig %-4s / ours %-4s" % (ja.group(1), jc.group(1)), src, i)) + continue + ma, mc = CMP.match(a), CMP.match(c) + if ma and mc and ma.group(1).strip() == mc.group(2).strip() and ma.group(2).strip() == mc.group(1).strip(): + hits.append(('CMP orig "%s" / ours "%s"' % (a, c), src, i)) + if not hits: + continue + print("### %s %.1f%%" % (e["name"].split("::")[-1], float(e["matching"]) * 100)) + for tag, src, i in hits: + print(" %-38s %s" % (tag, src)) + for j in range(max(0, i - 4), i): + kind, text, ann = rows[j] + print(" %-7s %-58s %s" % (kind, text[:58], ann)) diff --git a/tools/reimplementation-helper/batch/diff_slots.py b/tools/reimplementation-helper/batch/diff_slots.py new file mode 100644 index 000000000..a13225cb3 --- /dev/null +++ b/tools/reimplementation-helper/batch/diff_slots.py @@ -0,0 +1,41 @@ +"""Census of the `[esp + N]` slots each side touches, to find a local too many. + +usage: diff_slots.py NAME-SUBSTRING + +Prints every stack slot with how often it is accessed, on the original and on +our side. Use it after diff_triage.py reports a FRAME mismatch: one extra +distinct slot on our side is one local the original does not have, and the slot +with the lowest access count is usually the culprit. Grep the diff for that +slot to see what writes it - a 16-bit `mov word ptr [esp+N]` means a `short` +local the original keeps in a register or widens to `int`. +""" + +import re +import sys + +import common + +if len(sys.argv) < 2: + sys.exit(__doc__) +want = sys.argv[1] +SLOT = re.compile(r"\[esp \+ (0x[0-9a-f]+)\]") + +for f, e, address in common.entries(common.load_diff()): + if e is None or want not in e["name"] or not e.get("diff"): + continue + orig, ours = common._asm_lines(e) + counts = [] + for stream in (orig, ours): + c = {} + for line in stream: + for m in SLOT.finditer(line): + v = int(m.group(1), 16) + c[v] = c.get(v, 0) + 1 + counts.append(c) + o, u = counts + print("### %s %.1f%%" % (e["name"], float(e["matching"]) * 100)) + print(" orig %2d slots: %s" % (len(o), " ".join("%#x:%d" % kv for kv in sorted(o.items())))) + print(" ours %2d slots: %s" % (len(u), " ".join("%#x:%d" % kv for kv in sorted(u.items())))) + only_ours = sorted(set(u) - set(o)) + if only_ours: + print(" only ours: %s" % " ".join("%#x (%d uses)" % (k, u[k]) for k in only_ours)) diff --git a/tools/reimplementation-helper/batch/diff_triage.py b/tools/reimplementation-helper/batch/diff_triage.py new file mode 100644 index 000000000..5c832cf4a --- /dev/null +++ b/tools/reimplementation-helper/batch/diff_triage.py @@ -0,0 +1,97 @@ +"""Decide where source work can still help, per function in cmake/openshc-sources.txt.local. + +usage: diff_triage.py [--run] [path-filter] + +Columns, all derived from the last reccmp run: + + pct reccmp match % + raw difflib ratio over the two instruction streams + norm the same ratio after renaming registers to R, `[esp + 0xN]` to [SLOT] + and literals to N + frame `sub esp, N` on each side + byte number of byte-sized `[esp]` accesses on each side + ret position of each `ret`, as a fraction of the stream + +How to read it: + + norm >> raw only register/stack-slot naming is left; stop editing + the source, the allocator is not reachable from it + norm still low real structure or ordering differs; keep looking + frame differs one local too many or too few - chase it with + diff_slots.py, this has been worth 10-30 points + byte on our side only every bool/byte local is an int in the original + ret positions differ we inline a cold early-return block the original + outlines (or the reverse); restructure the guard so the + cold case is last in the source +""" + +import re +import sys + +import common + +REG = re.compile(r"\b(e?[abcd]x|e?[sd]i|e?bp|e?sp|[abcd][lh])\b") +BYTE_SLOT = re.compile(r"(^|[^d])byte ptr \[esp") +FRAME = re.compile(r"^sub esp, (0x[0-9a-f]+|\d+)") + +args = sys.argv[1:] +if args and args[0] == "--run": + common.run_reccmp() + args = args[1:] +path_filter = args[0] if args else "" + + +def normalize(line): + s = line.split("\t")[0].strip() + s = REG.sub("R", s) + s = re.sub(r"\[R \+ 0x[0-9a-f]+\]", "[SLOT]", s) + s = re.sub(r"0x[0-9a-f]+", "N", s) + return re.sub(r"\b\d+\b", "N", s) + + +def frame_of(stream): + for line in stream: + m = FRAME.match(line.split("\t")[0].strip()) + if m: + return m.group(1) + return "-" + + +def ret_positions(stream): + hits = [i for i, t in enumerate(stream) if t.split("\t")[0].strip().startswith("ret")] + return [round(i / max(1, len(stream)), 2) for i in hits][:6] + + +import difflib # noqa: E402 (after common, which chdirs to the repo root) + +rows = [] +for f, e, address in common.entries(common.load_diff()): + if e is None or path_filter not in f or not e.get("diff"): + continue + orig, ours = common._asm_lines(e) + raw = difflib.SequenceMatcher(None, orig, ours, autojunk=False).ratio() + nrm = difflib.SequenceMatcher(None, [normalize(x) for x in orig], + [normalize(x) for x in ours], autojunk=False).ratio() + fo, fu = frame_of(orig), frame_of(ours) + bo = sum(1 for t in orig if BYTE_SLOT.search(t)) + bu = sum(1 for t in ours if BYTE_SLOT.search(t)) + ro, ru = ret_positions(orig), ret_positions(ours) + flags = [] + if fo != fu: + flags.append("FRAME") + if bu > bo: + flags.append("BYTE") + if ro != ru and (len(ro) != len(ru) or any(abs(a - b) > 0.08 for a, b in zip(ro, ru))): + flags.append("RET") + if not flags and nrm - raw > 0.25: + flags.append("alloc-only") + rows.append((float(e["matching"]), e["name"].split("::")[-1], raw, nrm, fo, fu, bo, bu, ro, ru, flags)) + +rows.sort() +print("%-38s %5s %6s %6s %-6s %-6s %4s %4s %s" % + ("function", "pct", "raw", "norm", "frmO", "frmU", "byO", "byU", "flags")) +for m, name, raw, nrm, fo, fu, bo, bu, ro, ru, flags in rows: + print("%-38s %5.1f %6.3f %6.3f %-6s %-6s %4d %4d %s" % + (name, m * 100, raw, nrm, fo, fu, bo, bu, " ".join(flags))) + if "RET" in flags: + print("%-38s orig ret %s ours ret %s" % ("", ro, ru)) diff --git a/tools/reimplementation-helper/batch/diff_types.py b/tools/reimplementation-helper/batch/diff_types.py new file mode 100644 index 000000000..54049f9f2 --- /dev/null +++ b/tools/reimplementation-helper/batch/diff_types.py @@ -0,0 +1,140 @@ +"""Find differences that point at a wrong type, addressing mode or constant, per function in +cmake/openshc-sources.txt.local. + +usage: diff_types.py [--run] [NAME-SUBSTRING] + +`diff_jcc.py` covers comparisons and `diff_triage.py` covers frames and slots; this covers the three +remaining classes that have a source-level fix: + + EXTEND `movsx` on one side and `movzx` on the other for the same operand. The field's + signedness is wrong in the generated header: `char` vs `byte`, `short` vs `ushort`. + Commit header retypes separately from the .cpp work. + SIGNFLIP `add` against `sub` of the same magnitude on the same destination: a sign error in a + formula. Trust the asm over the decompilation - Ghidra rendered `2400 / n + 40` where the + binary has `sub eax, 0x28`, so the formula was `- 40`, and the function reached 100% once + corrected. +A third class, `this->` against the global instance, is deliberately not reported. One function often +reads some fields through `this` and others through `DAT_*::instance`, in both directions at once, so +counting anchored operands fires for almost every function and proves nothing about any single +access. Read it off `reccmp_report.py diff` instead: a `this`-relative operand on one side against a +`StructResolver::Instance<..>` symbol on the other, for the *same* field, is the signal. That needs +the global data reimplementations switched on (the `MACRO_STRUCT_RESOLVER` flag in +`src/OpenSHC/Globals/*.hpp`, never committed); with them off every absolute address looks alike. + +Instructions are paired inside one reccmp replacement block and only when both sides have the same +number of that kind of line. Once the streams drift, the n-th instruction on each side is no longer +the same instruction, and pairing them invents differences that are not there. Confirm every hit +against `reccmp_report.py diff` before editing. + +Both rules were checked in both directions: they fire on the two known bugs when those are +reintroduced (a `char` layer that should be `byte`, and `+ 40` that should be `- 40`) and report +nothing across the 101 functions of `Game::GameStateStructures` once fixed. A third rule, "same +instruction with a different constant", was tried and removed: every one of its hits on that folder +was a loop-induction stride (`imul reg, 0x39f4` against a different element size, `add reg, 4` +against `add reg, 0x14`), never a constant the source chose. + +--run re-runs reccmp first (otherwise reccmp/dll/diff.json from the last run is used). +""" + +import re +import sys + +import common + +IMM_RE = re.compile(r"(-?0x[0-9a-f]+|(?::instance+N]` and `movsx eax, byte ptr [ebx + esi + I::instance+N]` + reach the same array, so only the width and the symbol identify the access. + """ + m = re.search(r"(byte|word|dword) ptr \[(.*)\]", s) + if not m: + return None + inner = re.sub(r"\b(e?[abcd]x|e?[sd]i|e?[sb]p)\b(\*\d)?\s*\+?\s*", "", m.group(2)) + return m.group(1), inner.strip() + + +def blocks(entry): + """[(orig_lines, recomp_lines)] per replacement block.""" + pairs = [] + for hunk in entry.get("diff") or []: + for block in hunk[1]: + o = [op(r[1]) for r in block.get("orig", [])] + r = [op(r[1]) for r in block.get("recomp", [])] + if o and r: + pairs.append((o, r)) + return pairs + + +def hints_for(entry): + pairs = blocks(entry) + hits = [] + + def add(cls, a, b): + if (cls, a, b) not in hits and len(hits) < 25: + hits.append((cls, a, b)) + + def zip_kind(o, r, keep): + a = [x for x in o if keep(x)] + b = [x for x in r if keep(x)] + return list(zip(a, b)) if len(a) == len(b) else [] + + for o, r in pairs: + # EXTEND: same width and same symbol/displacement, different extension. Base registers are + # ignored on purpose - the two sides often reach the same array through different registers, + # and requiring those to match was what made this miss real cases. + for a, b in zip_kind(o, r, lambda x: x.startswith(("movsx ", "movzx "))): + if a.split()[0] != b.split()[0] and mem_shape(a) == mem_shape(b): + add("EXTEND", a, b) + # SIGNFLIP: add against sub (or the reverse) on the same destination with the same magnitude. + # This is what a wrong sign in a formula looks like; the constant itself is identical, so the + # CONST rule below can never see it. + for a, b in zip_kind(o, r, lambda x: x.startswith(("add ", "sub ")) and IMM_RE.search(x)): + ma, mb = IMM_RE.search(a), IMM_RE.search(b) + if (a.split()[0] != b.split()[0] and ma.group(1) == mb.group(1) + and scrub_regs(a[len(a.split()[0]):ma.start()]) + == scrub_regs(b[len(b.split()[0]):mb.start()])): + add("SIGNFLIP", a, b) + return hits + + +def main(): + args = sys.argv[1:] + if args and args[0] == "--run": + common.run_reccmp() + args = args[1:] + want = args[0] if args else "" + byaddr = common.load_diff() + shown = total = 0 + for f, e, a in common.entries(byaddr): + if e is None or float(e["matching"]) >= 1 or want not in e["name"]: + continue + total += 1 + hits = hints_for(e) + if not hits: + continue + shown += 1 + print("### %s %.1f%% norm %.1f%%" + % (e["name"], float(e["matching"]) * 100, common.normalized_ratio(e) * 100)) + for cls, x, y in hits: + print(" %-8s orig: %-44s ours: %s" % (cls, x, y)) + if not total: + print("nothing below 100% in the current list") + else: + print("\n%d of %d function(s) below 100%% have a type or sign hint" % (shown, total)) + + +if __name__ == "__main__": + main() diff --git a/tools/reimplementation-helper/batch/orig_asm.py b/tools/reimplementation-helper/batch/orig_asm.py new file mode 100644 index 000000000..40bd6e4fb --- /dev/null +++ b/tools/reimplementation-helper/batch/orig_asm.py @@ -0,0 +1,137 @@ +"""Print the ORIGINAL instruction stream of one function from the current reccmp diff. + +`reccmp_report.py diff NAME` shows an interleaved diff, which is what you want while closing a +small gap. It is the wrong tool when you need to read the original on its own: + + * the diff only covers instructions reccmp chose to align, so long functions come back truncated; + * reconstructing control flow (a switch, a loop nest, a hand-written asm block) needs the + original's own jump targets and fall-through order, which the interleaving hides. + +This prints just the original side, in address order, so it can be read or grepped like a listing. +It needs no Ghidra connection - it reuses `reccmp/dll/diff.json` from the last +`reccmp_report.py --run` - so it also works when the Ghidra MCP server is down. + +usage: + orig_asm.py NAME [--both] # NAME matches the end of the reccmp symbol name + orig_asm.py NAME --stats # instruction count and the tells for a handwritten original + +`--both` also prints the instructions reccmp matched, marked "=", so the output is the full original +body rather than only the parts that differ. + +`--stats` reports the signals that decide how a function has to be reimplemented at all: + * "mov reg, 0" - no compiler materialises zero that way; the block is handwritten assembly + and belongs in an __asm block (see AGENTS.md and NeighbourFlagsAsm.hpp). + * frame pointer - "push ebp / mov ebp, esp" with locals addressed off ebp means the original + was built without optimisation; the reimplementation needs + #pragma optimize("", off). The same prologue with "and esp, -N" is only + /O2 realigning the stack for a large local, and is NOT an /Od tell. + * "sub esp, N" - the frame size. Matching it exactly is usually the single biggest win for + an unoptimised function, because every local displacement shifts otherwise. + * "jmp dword ptr" - a jump table: Ghidra often renders the dispatch as a call through an + unnamed pointer array and drops the arms, so the switch must be rebuilt + from the byte/jump tables rather than restyled from the decompiler output. +""" + +import io +import json +import re +import sys + +import common + +USAGE = 'usage: orig_asm.py NAME [--both|--stats]' + + +def find(node, name): + """Locate the diff entry whose symbol name ends with `name`.""" + if isinstance(node, dict): + if str(node.get('name', '')).endswith(name): + return node + for value in node.values(): + hit = find(value, name) + if hit: + return hit + elif isinstance(node, list): + for value in node: + hit = find(value, name) + if hit: + return hit + return None + + +def instructions(entry, both): + """Yield (address, text, matched) for the original side, in address order. + + The payload is a list of hunks; each hunk is [header, [group, ...]] and each group is + {"orig"|"both"|"recomp": [[address, text] or [address, text, recomp_address], ...]}. + """ + out = [] + + def walk(node): + if isinstance(node, dict): + for key in ('orig', 'both'): + if key not in node: + continue + if key == 'both' and not both: + continue + for row in node[key]: + if not (isinstance(row, (list, tuple)) and len(row) >= 2): + continue + # reccmp leaves the address empty on rows it synthesised (padding, + # and the blank line it puts between hunks); keep them in order by + # reusing the previous address rather than dropping the instruction. + try: + address = int(row[0], 16) + except (TypeError, ValueError): + address = out[-1][0] if out else 0 + out.append((address, row[1].rstrip(), key == 'both')) + elif isinstance(node, list): + for item in node: + walk(item) + + walk(entry['diff']) + out.sort(key=lambda row: row[0]) + return out + + +def main(): + args = [a for a in sys.argv[1:] if not a.startswith('--')] + flags = {a for a in sys.argv[1:] if a.startswith('--')} + if len(args) != 1 or flags - {'--both', '--stats'}: + sys.exit(USAGE) + name = args[0] + + if not common.DIFF_JSON.exists(): + sys.exit('%s not found; run reccmp_report.py --run pct first' % common.DIFF_JSON) + entry = find(json.load(io.open(str(common.DIFF_JSON), encoding='utf-8')), name) + if entry is None: + sys.exit('no reccmp entry whose name ends with %r' % name) + + rows = instructions(entry, both='--both' in flags or '--stats' in flags) + print('# %s %s %.2f%%' % (entry['name'], entry['address'], float(entry['matching']) * 100)) + + if '--stats' in flags: + text = '\n'.join(row[1] for row in rows) + zero = re.findall(r'\bmov e[a-z]{2}, 0\b', text) + # `push ebp / mov ebp, esp` alone is not an /Od tell: /O2 emits the same pair when a + # function realigns the stack for a large local (`and esp, -N`, usually with __chkstk), + # and there the locals still live at [esp + N]. A real /Od frame addresses them off ebp. + prologue = bool(re.search(r'\bpush ebp\b', text)) and bool(re.search(r'\bmov ebp, esp\b', text)) + realigned = bool(re.search(r'\band esp, 0x[0-9a-f]+\b', text)) + ebp_locals = bool(re.search(r'\[ebp - 0x[0-9a-f]+\]', text)) + frame = prologue and ebp_locals and not realigned + stack = re.findall(r'\bsub esp, (0x[0-9a-f]+|\d+)\b', text) + tables = re.findall(r'\bjmp dword ptr\b', text) + print('# instructions seen: %d (matched and differing)' % len(rows)) + print('# mov reg, 0 : %d%s' % (len(zero), ' <- handwritten assembly' if zero else '')) + print('# frame pointer : %s%s' % (frame, ' <- built /Od; use #pragma optimize' if frame else '')) + print('# frame size : %s' % (', '.join(stack) if stack else 'none')) + print('# jump tables : %d%s' % (len(tables), ' <- rebuild the switch by hand' if tables else '')) + return + + for address, text, matched in rows: + print('%08x %s %s' % (address, '=' if matched else '-', text)) + + +if __name__ == '__main__': + main() diff --git a/tools/reimplementation-helper/batch/quick_diff.py b/tools/reimplementation-helper/batch/quick_diff.py new file mode 100644 index 000000000..2e10caa40 --- /dev/null +++ b/tools/reimplementation-helper/batch/quick_diff.py @@ -0,0 +1,167 @@ +"""Compile ONE source file and diff its code against the original function, in seconds. + +usage: quick_diff.py FILE.cpp... [-q] + +Compiles each file with a DLL compile command plus /FA (no link, no reccmp), disassembles the original function +at the file's `// FUNCTION:` address straight from `_original/` with capstone, normalizes both streams (globals, +call/jump targets and esp slots become tokens) and prints a difflib ratio and a unified diff (- original, + ours). +`-q` prints the ratio only. + +This is a triage loop, not the score: it is stricter than reccmp about register names and looser about stack +offsets and constants, so confirm a 1.0000 with `reccmp_report.py --run`. Unlike reccmp it shows the *whole* +original function - reccmp cuts the original stream at the length of our function, which hides the original's +tail whenever ours is shorter. Needs capstone and a configured build directory. Do not run it while a build is +running: both write the same PDB. +""" +import difflib +import json +import os +import re +import struct +import subprocess +import sys + +import capstone + +import common + +quiet = '-q' in sys.argv +files = [os.path.abspath(f) for f in sys.argv[1:] if f != '-q'] +if not files: + sys.exit(__doc__) +ROOT = str(common.ROOT).replace(chr(92), '/') +os.chdir(ROOT) +ENV = common.build_env() +ENV['PATH'] = os.path.join(ROOT, 'MSVC1400-SP1', 'Common7', 'IDE') + os.pathsep + ENV['PATH'] +cc = json.load(open('build-RelWithDebInfo/compile_commands.json')) +base = [e for e in cc if 'OpenSHC.dll.dir' in e['command'] and '/src/OpenSHC/' in e['file'].replace(chr(92), '/')][0] +out = ROOT + '/build-RelWithDebInfo/batch/quick' +os.makedirs(out, exist_ok=True) + +d = open('_original/Stronghold Crusader.exe', 'rb').read() +pe = struct.unpack_from('= 0x80000000: + return '-' + hex(0x100000000 - v) + return 'G' if v >= 0x400000 else hex(v) + + +def orig(va): + o = off(va) + res = [] + prev = None + end = va + # function ends at first int3/padding after ret/jmp once no pending forward jump target lies beyond + maxtarget = va + for i in md.disasm(d[o:o + 20000], va): + if prev in ('ret', 'jmp') and i.address > maxtarget and (i.mnemonic in ('int3', 'nop') or i.address % 16 == 0 and i.mnemonic == 'int3'): + break + if prev in ('ret', 'jmp') and i.address > maxtarget: + break + s = i.mnemonic + ' ' + i.op_str + if i.mnemonic.startswith('j') or i.mnemonic == 'call': + try: + t = int(i.op_str, 16) + if i.mnemonic != 'call' and va <= t < va + 20000: + maxtarget = max(maxtarget, t) + s = i.mnemonic + ' L' + else: + s = i.mnemonic + ' F' + except ValueError: + pass + else: + s = re.sub(r'0x[0-9a-f]+|\b\d+\b', num, s) + s = re.sub(r'(\w+) [+] G\b', r'\1 + G', s) + if i.mnemonic == 'nop' or s.startswith('lea ') and re.match(r'lea (\w+), \[\1\]$', s) or s in ('mov edi, edi',): + prev = prev + continue + res.append(s.strip()) + prev = i.mnemonic + return res + + +def ours(f, cls, name): + cmd = base['command'] + cmd = re.sub(r'/Fo\S+', lambda m: '/Fo' + out + '/x.obj /FA /Fa' + out + '/x.asm', cmd) + cmd = re.sub(r'-c \S+$', lambda m: '-c ' + f, cmd) + r = subprocess.run(cmd, cwd=base['directory'], env=ENV, capture_output=True, text=True) + if r.returncode: + print('\n'.join(l for l in r.stdout.splitlines() if 'error' in l) or r.stdout[-500:]) + return None + txt = open(out + '/x.asm', errors='replace').read() + m = re.search(r'^(\?%s@%s@\S+) PROC.*?\n(.*?)^\1 ENDP' % (name, cls), txt, re.S | re.M) + if not m: + print('no code found for %s::%s' % (cls, name)) + return None + res = [] + for l in m.group(2).splitlines(): + l = l.split(';')[0].strip() + if not l or l.endswith(':') or re.match(r'^\w+\$ = ', l) or l.startswith('npad') or l.startswith('$') or l.startswith('DD') or l.startswith('DB') or l.startswith('_'): + continue + l = l.replace('\t', ' ') + op = l.split(' ')[0] + if op.startswith('j') and 'DWORD PTR' not in l: + l = op + (' L' if '$' in l else ' F') + elif op == 'call': + l = 'call F' + else: + l = re.sub(r'OFFSET ', '', l) + l = re.sub(r'\?[\w?$@]+', 'G', l) + l = re.sub(r'_\w+\$\[', '[', l) + l = re.sub(r'\b([0-9a-f]+)H\b', lambda m: hex(int(m.group(1), 16)), l) + l = re.sub(r'(? len(_listed) // 2: + sys.stderr.write( + "warning: the last reccmp run covers only %d of the %d functions in the build " + "list.\n Re-run with --run, or this reports on stale results.\n" + % (len(_listed) - _missing, len(_listed))) + if mode == "pct": path_filter = args[1] if len(args) > 1 else "" total = count = 0 @@ -43,6 +55,13 @@ elif mode == "save": snapshot = {e["name"]: [float(e["matching"]), common.normalized_ratio(e)] for f, e, a in common.entries(byaddr) if e} + # A snapshot of a partial run is worse than no snapshot: every function it is missing reads + # as 0.0 later, so the comparison calls the whole namespace "better" and hides any real + # regression among the noise. Refuse instead, rather than writing a file that looks fine. + listed = sum(1 for _ in common.entries(byaddr)) + if listed and len(snapshot) <= listed // 2: + sys.exit("refusing to save: the last reccmp run covers %d of the %d functions in the " + "build list.\nRe-run with --run first." % (len(snapshot), listed)) json.dump(snapshot, open(args[1], "w"), indent=0) elif mode == "cmp": diff --git a/tools/reimplementation-helper/batch/reorder_search.py b/tools/reimplementation-helper/batch/reorder_search.py new file mode 100644 index 000000000..21f20b9c8 --- /dev/null +++ b/tools/reimplementation-helper/batch/reorder_search.py @@ -0,0 +1,204 @@ +"""Hill-climb the match % by reordering independent statements. + +usage: reorder_search.py CPP-FILE FUNCTION-SUBSTRING [--pairs] [--dry] + +MSVC's register allocation is sensitive to the order in which values become +live, so reordering statements that are independent of each other can change it +without changing behaviour. The search applies one move, rebuilds, measures, +and keeps the move only if reccmp's percentage rises; anything else is reverted. +Put only the one file in cmake/openshc-sources.txt.local first, or each trial +costs a full build. + +Moves: + default permute whole runs of >=3 mutually independent statements + (reverse / rotate / counter-rotate) - 3 builds per run + --pairs swap every adjacent independent pair - 1 build per pair, much + slower on a big function but finds things runs cannot + +Two statements count as independent when neither contains a call, their +left-hand sides differ, neither left-hand side appears in the other statement, +no plain local written by one is mentioned by the other, and two writes through +the same array base are distinct named fields of the identical element. That +is deliberately conservative; verify the kept reorder by reading the diff. + +Expect a low hit rate - about one move in ten pays, and usually a fraction of a +point. The exceptions are statements that sit on a boundary, between two calls +or on a loop back-edge, where the order decides what is live in which register: +those have been worth 2 points. Run it after the structural work, not before. +""" + +import json +import re +import subprocess +import sys +from pathlib import Path + +import common # noqa: F401 (chdirs to the repo root) + +CPP, FUNC = sys.argv[1], sys.argv[2] +PAIRS = "--pairs" in sys.argv +DRY = "--dry" in sys.argv +ID = re.compile(r"[A-Za-z_]\w*") +CALL = re.compile(r"\bMACRO_CALL\w*\s*\(|\b\w+\s*\([^)]") +# a `case X:`, `default:` or plain goto label - a boundary between statements, never part of one +LABEL = re.compile(r"^(case\b.*|default\s*|[A-Za-z_]\w*\s*):$") + + +def build_and_score(): + r = subprocess.run([sys.executable, "tools/reimplementation-helper/batch/build_quiet.py"], + capture_output=True, text=True) + if "BUILD_OK" not in r.stdout: + return None + subprocess.run([sys.executable, "tools/reimplementation-helper/batch/reccmp_report.py", "--run", "pct"], + capture_output=True, text=True) + for e in json.loads(Path("reccmp/dll/diff.json").read_text())["data"]: + if e["name"].endswith(FUNC): + return float(e["matching"]) + return None + + +def statements(lines, lo, hi): + """(start, end, depth) for each statement in lines[lo:hi] at a single brace depth.""" + out, depth, i = [], 0, lo + keywords = ("for", "while", "return", "break", "continue", "goto", "case", "default") + while i < hi: + s = lines[i].strip() + d0 = depth + depth += lines[i].count("{") - lines[i].count("}") + if s.endswith(";") and d0 == depth and not s.startswith(keywords): + out.append((i, i + 1, d0)) + i += 1 + continue + if s and not s.endswith((";", "{", "}")) and d0 == depth \ + and not s.startswith(keywords) and not LABEL.match(s): + # A statement wrapped over several lines, but stop at anything that is a boundary + # rather than an operand: without this a `case N:` line is glued to the statement + # under it and the pair can then be swapped out of its own case, which strands the + # other statements of that case after a `break` where they never run. That produced + # a silent behaviour change that reccmp scored 8.9% *better*, because the dead code + # happened to leave an instruction stream closer to the original's. + j = i + while j < hi and not lines[j].strip().endswith(";"): + t = lines[j].strip() + if "{" in lines[j] or "}" in lines[j] or t.startswith(keywords) or LABEL.match(t): + j = -1 + break + j += 1 + if j != -1 and j < hi: + out.append((i, j + 1, d0)) + i = j + 1 + continue + i += 1 + return out + + +def split_assign(text): + m = re.match(r"\s*([^=;]+?)\s*=(?!=)(.*)", text, re.S) + return (None, None) if not m else (" ".join(m.group(1).split()), m.group(2)) + + +def independent(a, b): + if CALL.search(a) or CALL.search(b): + return False + la, _ = split_assign(a) + lb, _ = split_assign(b) + if la is None or lb is None or la == lb: + return False + na, nb = " ".join(a.split()), " ".join(b.split()) + if "[" in la and "[" in lb and la.split("[")[0].strip() == lb.split("[")[0].strip(): + pa, pb = la.rsplit("]", 1), lb.rsplit("]", 1) + if not (pa[0] == pb[0] and pa[1].startswith(".") and pb[1].startswith(".") and pa[1] != pb[1]): + return False + if la in nb or lb in na: + return False + for lhs, other in ((la, nb), (lb, na)): + if ID.fullmatch(lhs) and re.search(r"\b%s\b" % re.escape(lhs), other): + return False + for lhs, other_lhs in ((la, lb), (lb, la)): + if ID.fullmatch(other_lhs) and re.search(r"\b%s\b" % re.escape(other_lhs), lhs): + return False + return True + + +def text_of(lines, entry): + return "\n".join(lines[entry[0]:entry[1]]) + + +def runs(lines, st): + out, i = [], 0 + while i < len(st): + j = i + while j + 1 < len(st): + if st[j][1] != st[j + 1][0] or st[j][2] != st[j + 1][2]: + break + if not all(independent(text_of(lines, st[k]), text_of(lines, st[j + 1])) for k in range(i, j + 1)): + break + j += 1 + if j - i + 1 >= 3: + out.append((i, j)) + i = j + 1 if j > i else i + 1 + return out + + +def read(): + return Path(CPP).read_text(encoding="utf-8").split("\n") + + +def write(ls): + Path(CPP).write_text("\n".join(ls), encoding="utf-8", newline="") + + +best = build_and_score() +if best is None: + sys.exit("baseline build failed") +print("baseline %.2f%%" % (best * 100)) + +improved, rnd = True, 0 +while improved and rnd < 3: + improved, rnd = False, rnd + 1 + lines = read() + f = next(i for i, l in enumerate(lines) if "// FUNCTION:" in l) + st = statements(lines, f, len(lines)) + if PAIRS: + moves = [] + for k in range(len(st) - 1): + if st[k][1] == st[k + 1][0] and st[k][2] == st[k + 1][2] \ + and independent(text_of(lines, st[k]), text_of(lines, st[k + 1])): + moves.append((st[k][0], st[k][1], st[k + 1][0], st[k + 1][1])) + print("round %d: %d candidate swaps" % (rnd, len(moves))) + if DRY: + break + for a0, a1, b0, b1 in reversed(moves): + cur = read() + write(cur[:a0] + cur[b0:b1] + cur[a0:a1] + cur[b1:]) + sc = build_and_score() + if sc is not None and sc > best + 1e-9: + print(" keep swap L%d<->L%d %.2f -> %.2f" % (a0 + 1, b0 + 1, best * 100, sc * 100)) + best, improved = sc, True + else: + write(cur) + else: + rs = runs(lines, st) + print("round %d: %d independent runs (lengths %s)" % (rnd, len(rs), [b - a + 1 for a, b in rs])) + if DRY: + break + for a, b in reversed(rs): + lines = read() + st = statements(lines, next(i for i, l in enumerate(lines) if "// FUNCTION:" in l), len(lines)) + if b >= len(st): + continue + blocks = [lines[st[k][0]:st[k][1]] for k in range(a, b + 1)] + lo, hi, n = st[a][0], st[b][1], len(blocks) + for tag, order in (("reverse", list(reversed(range(n)))), + ("rotate", list(range(1, n)) + [0]), + ("counter-rotate", [n - 1] + list(range(n - 1)))): + cur = read() + shuffled = [line for idx in order for line in blocks[idx]] + write(cur[:lo] + shuffled + cur[hi:]) + sc = build_and_score() + if sc is not None and sc > best + 1e-9: + print(" keep %s L%d..L%d %.2f -> %.2f" % (tag, lo + 1, hi, best * 100, sc * 100)) + best, improved = sc, True + break + write(cur) +print("final %.2f%%" % (best * 100)) diff --git a/tools/reimplementation-helper/batch/scan_chains.py b/tools/reimplementation-helper/batch/scan_chains.py new file mode 100644 index 000000000..299b881b4 --- /dev/null +++ b/tools/reimplementation-helper/batch/scan_chains.py @@ -0,0 +1,48 @@ +"""List if/else-if chains that compare one expression against several constants. + +That is the shape a switch compiles from, so when scan_dispatch.py says the original has +a jump table we do not, this finds the candidates to convert. It is also a readability +report on its own: a chain of six `x == K` tests usually reads better as a switch. + +usage: + scan_chains.py [path-filter] [--min N] +""" + +import argparse +import re + +import common + +parser = argparse.ArgumentParser() +parser.add_argument("path_filter", nargs="?", default="") +parser.add_argument("--min", type=int, default=4, dest="least") +args = parser.parse_args() + +TEST = re.compile(r"(?:\}\s*)?else\s+if\s*\(\s*(?P[^()]+?)\s*==\s*[^()]+\)|" + r"^\s*if\s*\(\s*(?P[^()]+?)\s*==\s*[^()]+\)") + +total = 0 +for f in common.list_files(): + if args.path_filter not in f: + continue + lines = common.read_text(f).splitlines() + lhs_run, start, count = None, 0, 0 + + def report(): + global total + if count >= args.least: + print("%-52s line %-5d %2d branches on %s" + % (f.rsplit("/", 1)[-1][:-4], start + 1, count, lhs_run[:55])) + total += 1 + + for i, line in enumerate(lines): + m = TEST.search(line) + lhs = (m.group("lhs") or m.group("lhs2")).strip() if m else None + if lhs is not None and lhs == lhs_run: + count += 1 + continue + report() + lhs_run, start, count = (lhs, i, 1) if lhs is not None else (None, i, 0) + report() + +print("%d chain(s) with at least %d branches" % (total, args.least)) diff --git a/tools/reimplementation-helper/batch/scan_dispatch.py b/tools/reimplementation-helper/batch/scan_dispatch.py new file mode 100644 index 000000000..3ee5121b0 --- /dev/null +++ b/tools/reimplementation-helper/batch/scan_dispatch.py @@ -0,0 +1,39 @@ +"""Report functions whose dispatch form disagrees with the original. + +A jump table compiles to `jmp dword ptr [reg*4 + ]`. Counting those on each side +of the diff finds the places where the original switched on a value and we wrote an +if/else-if chain, or the other way round. Both directions have been worth several +percent, so it is worth checking before hunting through a diff by hand. + + ours > orig a switch of ours became a table the original does not have + -> try an if/else-if chain + ours < orig the original has a table we do not + -> try a switch, or check that every value has its own case label + (handing values to `default:` and re-testing them with an if drops + them out of the table) + +usage: + scan_dispatch.py [path-filter] +""" + +import sys + +import common + +path_filter = sys.argv[1] if len(sys.argv) > 1 else "" + +rows = [] +for f, e, a in common.entries(common.load_diff()): + if e is None or path_filter not in f: + continue + orig, recomp = common._asm_lines(e) + ours = sum(1 for l in recomp if "jmp dword ptr" in l) + theirs = sum(1 for l in orig if "jmp dword ptr" in l) + if ours != theirs: + rows.append((abs(ours - theirs), ours, theirs, float(e["matching"]), e["name"])) + +rows.sort(reverse=True) +print("%5s %5s %7s %s" % ("ours", "orig", "match", "function")) +for _, ours, theirs, raw, name in rows: + print("%5d %5d %6.1f%% %s" % (ours, theirs, raw * 100, name.split("::")[-1])) +print("%d function(s) where the dispatch form differs" % len(rows)) diff --git a/tools/reimplementation-helper/batch/strip_noise_comments.py b/tools/reimplementation-helper/batch/strip_noise_comments.py new file mode 100644 index 000000000..1e064b748 --- /dev/null +++ b/tools/reimplementation-helper/batch/strip_noise_comments.py @@ -0,0 +1,107 @@ +"""Remove the block comments above a reimplemented function. + +Ghidra prefixes a decompilation with one block comment per analysis complaint, plus the +provenance marker the import writes: + + /* + WARNING: Enum "MappersEnumInt": Some values do not have unique names + */ + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 + */ + +Every block comment in the run directly above a `// FUNCTION:` line is deleted, including +blocks holding hand-written notes. Comments inside a body (the string literal annotated +next to a playWAVSFX call, say) and `//` line comments are never touched. + +Dry run by default like the other rewriting scripts: read the report, then pass --apply. + +usage: + strip_noise_comments.py [path-filter] [--apply] [--show] +""" + +import re +import sys +from pathlib import Path + +import common + +FUNCTION_LINE = re.compile(r"^\s*//\s*FUNCTION:") +# a whole-line comment opener/closer, so a trailing comment on a line of code is not mistaken for one +OPEN = re.compile(r"^\s*/\*") +CLOSE = re.compile(r"\*/\s*$") + + +def content_lines(block): + """The block's text with the /* */ framing and the leading ` * ` decoration stripped.""" + text = "\n".join(block) + text = text[text.index("/*") + 2:] + text = text[:text.rindex("*/")] + out = [] + for line in text.split("\n"): + line = re.sub(r"^\s*\*(?!/)", "", line).strip() + if line: + out.append(line) + return out + + +def preceding_blocks(lines, index): + """Spans (start, end) of the block comments immediately above lines[index], outermost first.""" + spans = [] + j = index - 1 + while j >= 0: + if not CLOSE.search(lines[j]) or lines[j].strip().startswith("//"): + break + k = j + while k >= 0 and not OPEN.match(lines[k]): + k -= 1 + if k < 0: + break + spans.append((k, j)) + j = k - 1 + spans.reverse() + return spans + + +def main(): + path_filter = "" + apply = False + show = False + for arg in sys.argv[1:]: + if arg == "--apply": + apply = True + elif arg == "--show": + show = True + else: + path_filter = arg + + files = sorted(p.as_posix() for p in Path("src/OpenSHC").rglob("*.cpp")) + removed = touched = 0 + for f in files: + if path_filter not in f: + continue + lines = common.read_text(f).replace("\r\n", "\n").split("\n") + drop = set() + n = 0 + for i, line in enumerate(lines): + if not FUNCTION_LINE.match(line): + continue + for start, end in preceding_blocks(lines, i): + drop.update(range(start, end + 1)) + n += 1 + if show: + print("%s:%d | %s" % (f, start + 1, + " / ".join(content_lines(lines[start:end + 1]))[:120])) + if not n: + continue + removed += n + touched += 1 + print("%-60s -%d" % (f[len("src/OpenSHC/"):], n)) + if apply: + common.write_text(f, "\n".join(l for i, l in enumerate(lines) if i not in drop)) + + print("%d block comment(s) in %d file(s)%s" + % (removed, touched, "" if apply else " (dry run, pass --apply)")) + + +main() diff --git a/tools/reimplementation-helper/batch/syntax_check.py b/tools/reimplementation-helper/batch/syntax_check.py index 5fde77df5..e67df175d 100644 --- a/tools/reimplementation-helper/batch/syntax_check.py +++ b/tools/reimplementation-helper/batch/syntax_check.py @@ -4,6 +4,12 @@ Needs build-RelWithDebInfo/compile_commands.json from a previous configure. Files are checked serially: runs sharing the PCH/pdb in parallel give bogus errors. + +cl.exe runs with this worktree's own _MSPDBSRV_ENDPOINT_ (see common.build_env). A run that dies +with C1090/C1033 gets the same two retries build_quiet.py takes - stop this worktree's mspdbsrv.exe, +then move to a fresh endpoint name. A PDB error matters more here than in a build: it is counted as +an error against whichever file happened to hit it, and the files after it in the batch are still +checked, so the run reports a plausible-looking error list that hides the real compile errors. """ import json @@ -12,7 +18,8 @@ import subprocess import sys -from common import BUILD_DIR, ROOT, TMP, list_files +from common import (BUILD_DIR, PDB_ERROR_RE, ROOT, TMP, build_env, fresh_pdb_endpoint, + kill_pdb_server, list_files) commands = json.loads((BUILD_DIR / "compile_commands.json").read_text()) template = next(c["command"] for c in commands if "OpenSHC.dll.dir" in c["command"]) @@ -30,8 +37,25 @@ bat = TMP / "syntax_check.bat" bat.write_text("\n".join(lines) + "\n") -result = subprocess.run(["cmd", "/c", str(bat.resolve())], capture_output=True, text=True) -errors = [l for l in (result.stdout + result.stderr).splitlines() if " error " in l or "fatal" in l] + +def run_check(endpoint=None): + result = subprocess.run(["cmd", "/c", str(bat.resolve())], capture_output=True, text=True, + env=build_env(endpoint)) + return result.stdout + result.stderr + + +output = run_check() +if PDB_ERROR_RE.search(output): + print("note: PDB server error, stopping this worktree's mspdbsrv.exe and retrying", file=sys.stderr) + kill_pdb_server() + output = run_check() +if PDB_ERROR_RE.search(output): + endpoint = fresh_pdb_endpoint() + print("note: PDB endpoint still failing, retrying on a fresh endpoint %s" % endpoint, file=sys.stderr) + kill_pdb_server() + output = run_check(endpoint) + +errors = [l for l in output.splitlines() if " error " in l or "fatal" in l] for line in errors: print(re.sub(r"^.*src.OpenSHC.", "", line)[:260]) print("ERRORS", len(errors), "files", len(files)) diff --git a/tools/reimplementation-helper/batch/test_deparen.py b/tools/reimplementation-helper/batch/test_deparen.py new file mode 100644 index 000000000..9a62a3845 --- /dev/null +++ b/tools/reimplementation-helper/batch/test_deparen.py @@ -0,0 +1,47 @@ +"""Precedence tests for deparen.py. + +Every case here is one where the brackets are load-bearing, or where an earlier version of +the script got it wrong. Run this after touching the precedence table. + +usage: + test_deparen.py +""" + +import deparen + +CASES = [ + # || inside && keeps its brackets (an earlier version dropped them and silently + # changed the expression) + ("(a != 0 || b != 0) && (x & 0x100) == 0", + "(a != 0 || b != 0) && (x & 0x100) == 0"), + # & binds looser than ==, so that group stays + ("((x & 0x100) == 0 || (y & 0x100) == 0)", + "(x & 0x100) == 0 || (y & 0x100) == 0"), + # a plain over-bracketed && chain flattens + ("(((a == 1) && (b == 2)) && (c == 3))", "a == 1 && b == 2 && c == 3"), + # an assignment inside a condition keeps its group + ("(a == 1) && ((v = f(x), v != 0))", "a == 1 && (v = f(x), v != 0)"), + # && inside || is legal to unwrap but clearer kept + ("(a == 1 && b == 2) || c == 3", "(a == 1 && b == 2) || c == 3"), + # bitwise groups stay whatever joins them + ("(a & 7) != 0 && (b | 1) == 3", "(a & 7) != 0 && (b | 1) == 3"), + # a lone comparison unwraps + ("(a == 1)", "a == 1"), + # nested || of the same operator flattens + ("((a == 1 || (b == 2)) || c == 3)", "a == 1 || b == 2 || c == 3"), +] + +if __name__ == "__main__": + failures = 0 + for src, want in CASES: + outer = src + while deparen.is_group(outer) and deparen.rank(outer[1:-1]) > 1: + outer = outer[1:-1].strip() + got = deparen.simplify(outer) + ok = got == want + failures += not ok + print("%-4s %s" % ("ok" if ok else "FAIL", src)) + if not ok: + print(" got %s\n want %s" % (got, want)) + print("\n%d/%d passed" % (len(CASES) - failures, len(CASES))) + raise SystemExit(1 if failures else 0) diff --git a/tools/reimplementation-helper/batch/test_undiv.py b/tools/reimplementation-helper/batch/test_undiv.py new file mode 100644 index 000000000..b58fddbf7 --- /dev/null +++ b/tools/reimplementation-helper/batch/test_undiv.py @@ -0,0 +1,45 @@ +"""Cases for undiv.py. + +The rewrites here are the shapes actually found in the decompiled sources, plus the +near-misses that must be left alone: a mismatched shift, a mask that is not 2^k - 1, and a +bias taken from a different variable than the one being divided. + +usage: + test_undiv.py +""" + +import undiv + +CASES = [ + # the plain form + ("v = (x + (x >> 0x1f & 7U)) >> 3;", "v = x / 8;"), + # the sum wrapped in a signed cast, which is where the shift then sits + ("v = (int)(x + (x >> 0x1f & 0xfU)) >> 4;", "v = x / 16;"), + # a cast on the dividend inside the bias as well as outside it + ("v = (int)((int)a->b + ((int)a->b >> 0x1f & 7U)) >> 3;", "v = a->b / 8;"), + # a wide mask + ("v = ((int)(r + ((int)r >> 0x1f & 0x1ffU)) >> 9) + k;", "v = (r / 512) + k;"), + # two independent divisions in one expression + ("v = ((x + (x >> 0x1f & 7U)) >> 3) - ((y + (y >> 0x1f & 7U)) >> 3);", + "v = (x / 8) - (y / 8);"), + # shift does not match the mask: not this idiom, leave it + ("v = (x + (x >> 0x1f & 7U)) >> 2;", "v = (x + (x >> 0x1f & 7U)) >> 2;"), + # mask is not 2^k - 1 + ("v = (x + (x >> 0x1f & 5U)) >> 3;", "v = (x + (x >> 0x1f & 5U)) >> 3;"), + # the bias is taken from a different variable + ("v = (x + (y >> 0x1f & 7U)) >> 3;", "v = (x + (y >> 0x1f & 7U)) >> 3;"), + # an unbiased shift is an unsigned division or a real shift; not ours to touch + ("v = x >> 3;", "v = x >> 3;"), +] + +if __name__ == "__main__": + failures = 0 + for src, want in CASES: + got, _ = undiv.process(src) + ok = got == want + failures += not ok + print("%-4s %s" % ("ok" if ok else "FAIL", src)) + if not ok: + print(" got %s\n want %s" % (got, want)) + print("\n%d/%d passed" % (len(CASES) - failures, len(CASES))) + raise SystemExit(1 if failures else 0) diff --git a/tools/reimplementation-helper/batch/test_unmod.py b/tools/reimplementation-helper/batch/test_unmod.py new file mode 100644 index 000000000..0d96ee01f --- /dev/null +++ b/tools/reimplementation-helper/batch/test_unmod.py @@ -0,0 +1,60 @@ +"""Cases for unmod.py. + +usage: + test_unmod.py +""" + +import unmod + +PLAIN = """ uint v = x & 0x8000000f; + if ((int)v < 0) { + v = (v - 1 | 0xfffffff0) + 1; + } +""" +SUM = """ uVar9 = (int)a->b + 4U & 0x80000007; + if ((int)uVar9 < 0) { + uVar9 = (uVar9 - 1 | 0xfffffff8) + 1; + } +""" +FIELD = """ this->u[i].r = this->u[i].fixedRng & 0x80000003; + if ((int)this->u[i].r < 0) { + this->u[i].r = (this->u[i].r - 1 | 0xfffffffc) + 1; + } +""" +# the repair constant does not complement the mask: not this idiom +BAD_COMP = """ uint v = x & 0x8000000f; + if ((int)v < 0) { + v = (v - 1 | 0xfffffff8) + 1; + } +""" +# the repair is applied to a different variable +BAD_VAR = """ uint v = x & 0x8000000f; + if ((int)w < 0) { + w = (w - 1 | 0xfffffff0) + 1; + } +""" +# a bare mask, with no repair, is not a signed modulo +BARE = " uint v = x & 0xf;\n" + +CASES = [ + # the dividend gets a signed cast: the repair the compiler emitted only exists for a + # signed operand, so an unsigned `%` would compile to a bare `and` and stop matching + (PLAIN, " uint v = (int)x % 16;\n", 1), + (SUM, " uVar9 = ((int)a->b + 4) % 8;\n", 1), + (FIELD, " this->u[i].r = (int)this->u[i].fixedRng % 4;\n", 1), + (BAD_COMP, BAD_COMP, 0), + (BAD_VAR, BAD_VAR, 0), + (BARE, BARE, 0), +] + +if __name__ == "__main__": + failures = 0 + for src, want, want_n in CASES: + got, n = unmod.process(src) + ok = got == want and n == want_n + failures += not ok + print("%-4s %s" % ("ok" if ok else "FAIL", src.strip().split("\n")[0])) + if not ok: + print(" got %r (%d)\n want %r (%d)" % (got, n, want, want_n)) + print("\n%d/%d passed" % (len(CASES) - failures, len(CASES))) + raise SystemExit(1 if failures else 0) diff --git a/tools/reimplementation-helper/batch/test_unptr.py b/tools/reimplementation-helper/batch/test_unptr.py new file mode 100644 index 000000000..72ca429a8 --- /dev/null +++ b/tools/reimplementation-helper/batch/test_unptr.py @@ -0,0 +1,70 @@ +"""Cases for unptr.py. + +usage: + test_unptr.py +""" + +import unptr + +CASES = [ + # the ordinary increment + (""" piVar1 = &this->units[i].attackedBy; + *piVar1 = *piVar1 + 1; +""", + " this->units[i].attackedBy = this->units[i].attackedBy + 1;\n", 1), + # a wrapped lvalue, which clang-format produces for the long ones + (""" piVar1 = &DAT_GameState::instance.playerDataArray[p] + .totalAttackTroops; + *piVar1 = *piVar1 + 1; +""", + " DAT_GameState::instance.playerDataArray[p].totalAttackTroops = " + "DAT_GameState::instance.playerDataArray[p].totalAttackTroops + 1;\n", 1), + # declared on the spot: the type prefix goes with the pointer, or it is left stranded in + # front of the lvalue and the file stops compiling + (""" char* pcVar3 = &a.moats[t].someCountDown; + *pcVar3 = *pcVar3 + -0x14; +""", + " a.moats[t].someCountDown = a.moats[t].someCountDown + -0x14;\n", 1), + # something other than a plain increment on the right + (""" psVar4 = &a.b; + *psVar4 = *psVar4 - c * 2; +""", + " a.b = a.b - c * 2;\n", 1), + # set up here, dereferenced further down: the index could change in between, so leave it + (""" psVar4 = &this->units[k].field97; + if (this->units[k].field97 > 0) { + *psVar4 = *psVar4 + 1; + } +""", + """ psVar4 = &this->units[k].field97; + if (this->units[k].field97 > 0) { + *psVar4 = *psVar4 + 1; + } +""", 0), + # a store through a different pointer than the one just assigned + (""" piVar1 = &a.b; + *piVar2 = *piVar2 + 1; +""", + """ piVar1 = &a.b; + *piVar2 = *piVar2 + 1; +""", 0), + # a plain pointer that is not a decompiler name is none of our business + (""" rowPtr = &a.b; + *rowPtr = *rowPtr + 1; +""", + """ rowPtr = &a.b; + *rowPtr = *rowPtr + 1; +""", 0), +] + +if __name__ == "__main__": + failures = 0 + for src, want, want_n in CASES: + got, n = unptr.process(src) + ok = got == want and n == want_n + failures += not ok + print("%-4s %s" % ("ok" if ok else "FAIL", src.strip().split("\n")[0])) + if not ok: + print(" got %r (%d)\n want %r (%d)" % (got, n, want, want_n)) + print("\n%d/%d passed" % (len(CASES) - failures, len(CASES))) + raise SystemExit(1 if failures else 0) diff --git a/tools/reimplementation-helper/batch/try_styles.py b/tools/reimplementation-helper/batch/try_styles.py new file mode 100644 index 000000000..4a44d217f --- /dev/null +++ b/tools/reimplementation-helper/batch/try_styles.py @@ -0,0 +1,121 @@ +"""Build and measure several source styles of one function, then keep the best. + +Whether a given style matches better is not predictable: naming a repeatedly read field +gained 18% in one function and lost 12% in another, and reusing a pointer for two rows +helped one function and hurt its near-identical neighbour. So write the candidates out and +measure them instead of arguing about them. + +Put each complete .cpp in + build-RelWithDebInfo/batch/variants//