diff --git a/docs/wiki/stockpile-footprint-cleanup.md b/docs/wiki/stockpile-footprint-cleanup.md new file mode 100644 index 00000000..366e77ad --- /dev/null +++ b/docs/wiki/stockpile-footprint-cleanup.md @@ -0,0 +1,28 @@ +# Stockpile footprint cleanup + +`TileMapState::clearStockpileFootprintTiles` (`0x004FAF70` in Crusader 1.41) +clears the nine walkable tiles belonging to a stockpile. The four building +parts are handled elsewhere. + +For each entry in `TerrainDefinedData::StockpilePathableOffsets`, it: + +1. Clears the tile's `0x102` logic bits and restores its default height. +2. Reads the building index from `AlphaGFXLayer` and checks that building's + `noRubble` field. Zero clears `BuildingWasLayer`; nonzero sets bit `0x4000` + in `MiscDisplayLayer` and preserves `BuildingWasLayer`. +3. Clears `AlphaGFXLayer` after reading the building reference. + +The map layers belong to the supplied `TileMapState` instance. Building +metadata, terrain offsets and row translation come from the corresponding +global game structures. + +## Placement + +`placeKeep` (`0x005146D0`) creates a starting stockpile through `placeStockpile` +(`0x00508540`). That call has no human/AI filter. + +The stockpile branch of `checkBuildingCanBePlacedHere` (`0x005037B0`) looks up +the supplied owner's stockpile. The first stockpile needs no adjacent existing +stockpile. Further placement requires capacity and adjacency to an owned +stockpile, in addition to the earlier terrain and placement checks. +`AIVState::aiPlaceAIVBuilding` (`0x004ED410`) uses the shared placement path. diff --git a/src/OpenSHC/Map/TileMapState/clearStockpileFootprintTiles.cpp b/src/OpenSHC/Map/TileMapState/clearStockpileFootprintTiles.cpp new file mode 100644 index 00000000..81d1377b --- /dev/null +++ b/src/OpenSHC/Map/TileMapState/clearStockpileFootprintTiles.cpp @@ -0,0 +1,31 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004FAF70 + void TileMapState::clearStockpileFootprintTiles(int x, int y) + { + for (int i = 0; i < 9; ++i) { + int tile = DAT_ViewportRenderState::instance + .translationMatrix[y + DAT_TerrainDefinedData::instance.StockpilePathableOffsets[i].y] + .addXgetTile + + x + DAT_TerrainDefinedData::instance.StockpilePathableOffsets[i].x; + + this->LogicLayer[tile] &= ~0x102; + this->HeightLayer[tile] = this->DefaultHeightLayer[tile]; + if (DAT_BuildingsState::instance.buildings[this->AlphaGFXLayer[tile]].noRubble == 0) { + this->BuildingWasLayer[tile] = 0; + } else { + this->MiscDisplayLayer[tile] |= 0x4000; + } + this->AlphaGFXLayer[tile] = 0; + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 10a0c2b0..006579f7 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -40367,7 +40367,7 @@ SHC_3BB0A8C1_0x004FAE50 | 0.0% | Pending SHC_3BB0A8C1_0x004FAEE0 | 0.0% | Pending -SHC_3BB0A8C1_0x004FAF70 | 0.0% | Pending +SHC_3BB0A8C1_0x004FAF70 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004FB0C0 | 0.0% | Pending diff --git a/tools/verification/verify_stockpile_cleanup.py b/tools/verification/verify_stockpile_cleanup.py new file mode 100644 index 00000000..63a20259 --- /dev/null +++ b/tools/verification/verify_stockpile_cleanup.py @@ -0,0 +1,81 @@ +"""Compare a VS2005 /O2 DLL-mode object with original SHC 1.41 under x86 emulation. + +Requires pefile, capstone and unicorn. No game process or copyrighted binary is included. +""" +from pathlib import Path +import argparse, struct, random, hashlib +import pefile +from capstone import Cs, CS_ARCH_X86, CS_MODE_32 +from unicorn import Uc, UC_ARCH_X86, UC_MODE_32 +from unicorn.x86_const import * + +parser=argparse.ArgumentParser(description=__doc__) +parser.add_argument('object',type=Path) +parser.add_argument('original',type=Path) +args=parser.parse_args() +obj=args.object.read_bytes() +_,nsec,_,symptr,nsyms,optsize,_=struct.unpack_from('