diff --git a/CREDITS.md b/CREDITS.md index 6e74c1718c..62769e87cc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -287,6 +287,7 @@ This page lists all the individual contributions to the project by their author. - Deploy priority filtering - Customizable paradrop missions - Guard range customizations + - Buildable-upon OverlayTypes - **Morton (MortonPL)**: - `XDrawOffset` for animations - Shield passthrough & absorption diff --git a/YRpp b/YRpp index 53447ad735..2ff81ad47e 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 53447ad7355b27d4b5e8a87d0ac543459d3039fc +Subproject commit 2ff81ad47e1c301c8c653de059ab617d185e4009 diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index a2060522c5..3b9f5e7253 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -118,7 +118,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed `DeployToFire` not considering building placement rules for `DeploysInto` buildings and as a result not working properly with `WaterBound` buildings. - Fixed `DeployToFire` not recalculating firer's position on land if it cannot currently deploy. - `Arcing=true` projectile elevation inaccuracy can now be fixed by setting `Arcing.AllowElevationInaccuracy=false`. -- Wall overlays are now drawn with the custom palette defined in `Palette` in `artmd.ini` if possible. - Setting `ReloadInTransport` to true on units with `Ammo` will allow the ammo to be reloaded according to `Reload` or `EmptyReload` timers even while the unit is inside a transport. - It is now possible to enable `Verses` and `PercentAtMax` to be applied on negative damage by setting `ApplyModifiersOnNegativeDamage` to true on the Warhead. - Attached animations on flying units now have their layer updated immediately after the parent unit, if on same layer they always draw above the parent. @@ -146,7 +145,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Units & buildings with `DecloakToFire=false` weapons now cloak while targeting & reloading. - Units with `Sensors=true` will no longer reveal ally buildings. - Air units are now reliably included by target scan with large range and Warhead detonation by large `CellSpread`. -- OverlayTypes now read and use `ZAdjust` if specified in their `artmd.ini` entry. - Weapons with `AA=true` Projectile can now correctly fire at air units when both firer and target are over a bridge. - Fixed disguised units not using the correct palette if target has custom palette. - Building upgrades now consistently use building's `PowerUpN` animation settings corresponding to the upgrade's `PowersUpToLevel` where possible. @@ -221,7 +219,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed the bug that infantry ignored `Passengers` and `SizeLimit` when entering buildings. - Fixed `VoiceDeploy` not played, when deployed through hot-key/command bar. - Fixed the bug that ships can travel on elevated bridges. -- Dehardcoded 255 limit of `OverlayType`. - Fixed an issue where airstrike flare line drawn to target at lower elevation would clip. - Elite technos no longer scatter by default, behaviour is controlled by `SCATTER` veterancy ability now. - Second weapon with `ElectricAssault=yes` will not unconditionally attack your building with `Overpowerable=yes`. @@ -1176,9 +1173,43 @@ ProneSpeed.NoCrawls=1.5 ; floating point value, multiplier ProneSpeed= ; floating point value, multiplier, by default, use the corresponding global value according to Crawls ``` - + +### Buildable-upon OverlayTypes + +- It is now possible to make OverlayTypes allow buildings to be placed on them by setting `CanBeBuiltOn` to true. This still requires the tile's landtype (which is changed to overlay's land type unless OverlayType has `NoUseTileLandType=false`) to allow buildings to be placed. + - If `CanBeBuiltOn.Remove=true`, the overlay will be removed (if it is a wall owned by the player placing the building, it is sold) upon the building being placed. If this is not set to true, buildings with `Wall=true` cannot be placed on the overlay and neither does overlay with `Wall=true` allow buildings to be placed on itself regardless of other settings. + +In `rulesmd.ini`: +```ini +[SOMEOVERLAY] ; OverlayType +CanBeBuiltOn=false ; boolean +CanBeBuiltOn.Remove=true ; boolean +``` + +### More than 255 OverlayTypes + +- Game now supports more than 255 distinct OverlayTypes, up to 65535. For map file/editor support, see [Increased Overlay Limit](AI-Scripting-and-Mapping.md#increased-overlay-limit). + +### Custom palette + +- You can now specify custom palette for OverlayTypes in similar manner as TechnoTypes can. + +In `artmd.ini`: +```ini +[SOMETERRAINTYPE] ; TerrainType +Palette= ; filename - excluding .pal extension and three-character theater-specific suffix +``` + +### ZAdjust + +- OverlayTypes now read and use `ZAdjust`. + +In `artmd.ini`: +```ini +[SOMEOVERLAY] ; OverlayType image +ZAdjust=0 +``` ## Particle systems diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 95190b8f5c..57ce653454 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -543,6 +543,7 @@ New: - Allow jumpjet climbing ignore building height (by TaranDahl) - Allow draw SuperWeapon timer as percentage (by NetsuNegi) - Customize particle system of parasite logic (by NetsuNegi) +- [Buildable-upon OverlayTypes](Fixed-or-Improved-Logics.md#buildable-upon-overlaytypes) (by Starkku) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Ext/OverlayType/Body.cpp b/src/Ext/OverlayType/Body.cpp index 568f54e84e..72658ce58c 100644 --- a/src/Ext/OverlayType/Body.cpp +++ b/src/Ext/OverlayType/Body.cpp @@ -2,6 +2,37 @@ OverlayTypeExt::ExtContainer OverlayTypeExt::ExtMap; +bool OverlayTypeExt::CanBeBuiltOn(int overlayTypeIndex, BuildingTypeClass* pBuildingType, bool requireToBeRemovable) +{ + auto const pOverlayType = OverlayTypeClass::Array[overlayTypeIndex]; + auto const pTypeExt = OverlayTypeExt::ExtMap.Find(pOverlayType); + + if (!pTypeExt->CanBeBuiltOn) + return false; + + if (((pBuildingType && pBuildingType->Wall) || pOverlayType->Wall) && !pTypeExt->CanBeBuiltOn_Remove) + return false; + + return requireToBeRemovable ? pTypeExt->CanBeBuiltOn_Remove : true; +} + +void OverlayTypeExt::RemoveOverlayFromCell(int overlayTypeIndex, CellClass* pCell, HouseClass* pSource) +{ + if (overlayTypeIndex != -1 && OverlayTypeClass::Array[overlayTypeIndex]->Wall) + { + if (pSource && pCell->WallOwnerIndex == pSource->ArrayIndex) + pSource->SellWall(pCell->MapCoords, true); + else + pCell->DamageWall(-1); + } + else + { + pCell->OverlayTypeIndex = -1; + pCell->OverlayData = 0; + pCell->RecalcAttributes(-1); + } +} + // ============================= // load / save @@ -9,6 +40,8 @@ template void OverlayTypeExt::ExtData::Serialize(T& Stm) { Stm + .Process(this->CanBeBuiltOn) + .Process(this->CanBeBuiltOn_Remove) .Process(this->ZAdjust) .Process(this->PaletteFile) ; @@ -18,12 +51,15 @@ void OverlayTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); - //const char* pSection = pThis->ID; - // - //if (!pINI->GetSection(pSection)) - // return; - // - //INI_EX exINI(pINI); + const char* pSection = pThis->ID; + + if (!pINI->GetSection(pSection)) + return; + + INI_EX exINI(pINI); + + this->CanBeBuiltOn.Read(exINI, pSection, "CanBeBuiltOn"); + this->CanBeBuiltOn_Remove.Read(exINI, pSection, "CanBeBuiltOn.Remove"); auto pArtSection = pThis->ImageFile; INI_EX exArtINI(&CCINIClass::INI_Art); diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index 1acdffce48..62072ab806 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -15,11 +15,15 @@ class OverlayTypeExt class ExtData final : public Extension { public: + Valueable CanBeBuiltOn; + Valueable CanBeBuiltOn_Remove; Valueable ZAdjust; PhobosFixedString<32u> PaletteFile; DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. ExtData(OverlayTypeClass* OwnerObject) : Extension(OwnerObject) + , CanBeBuiltOn { false } + , CanBeBuiltOn_Remove { true } , ZAdjust { 0 } , PaletteFile {} , Palette {} @@ -50,4 +54,7 @@ class OverlayTypeExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); + + static bool CanBeBuiltOn(int overlayTypeIndex, BuildingTypeClass* pBuildingType, bool requireToBeRemovable); + static void RemoveOverlayFromCell(int overlayTypeIndex, CellClass* pCell, HouseClass* pSource); }; diff --git a/src/Ext/OverlayType/Hooks.cpp b/src/Ext/OverlayType/Hooks.cpp index 029ef78bde..e66b8399a2 100644 --- a/src/Ext/OverlayType/Hooks.cpp +++ b/src/Ext/OverlayType/Hooks.cpp @@ -44,3 +44,38 @@ DEFINE_HOOK(0x47F974, CellClass_DrawOverlay_Walls, 0x5) return SkipGameCode; } + +#pragma region CanBeBuiltOn + +DEFINE_HOOK(0x47C9A7, CellClass_IsClearToBuild_Overlays, 0x5) +{ + enum { ReturnFromFunction = 0x47C6D1, CheckTileLandType = 0x47C9CD }; + + GET(CellClass*, pThis, EDI); + GET_STACK(BuildingTypeClass*, pBuildingType, STACK_OFFSET(0x18, 0x8)); + + int overlayTypeIndex = pThis->OverlayTypeIndex; + + if (overlayTypeIndex != -1) + { + if (OverlayTypeExt::CanBeBuiltOn(overlayTypeIndex, pBuildingType, false)) + return CheckTileLandType; + } + + return ReturnFromFunction; +} + +DEFINE_HOOK(0x45EF11, BuildingTypeClass_FlushForPlacement_Overlays, 0x6) +{ + enum { Continue = 0x45EF2C }; + + GET(BuildingTypeClass*, pThis, EBX); + GET(int, overlayTypeIndex, ECX); + + if (OverlayTypeExt::CanBeBuiltOn(overlayTypeIndex, pThis, false)) + return Continue; + + return 0; +} + +#pragma endregion diff --git a/src/Ext/TerrainType/Hooks.Passable.cpp b/src/Ext/TerrainType/Hooks.Passable.cpp index bd6cb30821..b8530f3a4b 100644 --- a/src/Ext/TerrainType/Hooks.Passable.cpp +++ b/src/Ext/TerrainType/Hooks.Passable.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include constexpr bool IS_CELL_OCCUPIED(CellClass* pCell) { @@ -113,6 +114,16 @@ DEFINE_HOOK(0x5684B1, MapClass_PlaceDown_BuildableTerrain, 0x6) TerrainTypeExt::Remove(pTerrain); } } + + int overlayTypeIndex = pCell->OverlayTypeIndex; + + if (overlayTypeIndex != -1) + { + auto const pBuildingType = static_cast(pObject->GetType()); + + if (OverlayTypeExt::CanBeBuiltOn(overlayTypeIndex, pBuildingType, true)) + OverlayTypeExt::RemoveOverlayFromCell(overlayTypeIndex, pCell, pObject->GetOwningHouse()); + } } return 0;