diff --git a/CREDITS.md b/CREDITS.md index 84e88ce0fb..1ddaaf1b51 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -251,6 +251,7 @@ This page lists all the individual contributions to the project by their author. - Build area customizations - `Scorch` / `Flamer` fire animation customization - EM Pulse cannon logic improvements + - `` as owner for pre-placed objects - **Morton (MortonPL)**: - `XDrawOffset` for animations - Shield passthrough & absorption @@ -363,7 +364,6 @@ This page lists all the individual contributions to the project by their author. - Flashing Technos on selecting - Promotion animation - **ZivDero** - - Allow giving ownership of buildings to players in Skirmish and MP using - Re-enable the Veinhole Monster and Weeds from TS - Recreate the weed-charging of SWs like the TS Chemical Missile - Allow to change the speed of gas particles diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 9571aaf413..2e424c81c2 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -18,6 +18,7 @@ + @@ -62,7 +63,6 @@ - @@ -114,7 +114,6 @@ - diff --git a/YRpp b/YRpp index 55764af23e..a6c07ac165 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 55764af23ef01aae9f7598a3410d0a1ed61de63d +Subproject commit a6c07ac1656034406d038e04ec6dcadddd071024 diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 4156cc423f..574a9bae33 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -126,7 +126,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`. -- You can now specify houses named `` through `` as the owner of TechnoTypes preplaced on the map in the editor, and they will be correctly given to players starting on points 1-8. Originally, it was only possible to use these house names in events, actions and teams. - Wall overlays are now drawn with the custom palette defined in `Palette` in `artmd.ini` if possible. - `Secondary` will now be used against walls if `Primary` weapon Warhead has `Wall=false`, `Secondary` has `Wall=true` and the firer does not have `NoSecondaryWeaponFallback` set to true. - 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. @@ -174,6 +173,8 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Certain global tileset indices (`ShorePieces`, `WaterSet`, `CliffSet`, `WaterCliffs`, `WaterBridge`, `BridgeSet` and `WoodBridgeSet`) are now correctly parsed for Lunar theater. - Fixed infantry `SecondaryFire` / `SecondaryProne` sequences being displayed in water instead of `WetAttack`. - Fixed objects with ally target and `AttackFriendlies=true` having their target reset every frame, particularly AI-owned buildings. +- `` can now be used as owner for pre-placed objects on skirmish and multiplayer maps. +- Follower vehicle index for preplaced vehicles in maps is now explicitly constrained to `[Units]` list in map files and is no longer thrown off by vehicles that could not be created or created vehicles having other vehicles as initial passengers. ## Fixes / interactions with other extensions @@ -342,20 +343,20 @@ AircraftDockingDir(N)= ; Direction type (integers from 0-255) ### Unit repair customization -- It is now possible to customize the repairing of units by `UnitRepair=true` and `UnitReload=true` buildings. +- It is now possible to customize the repairing of units by `UnitRepair=true`, `UnitReload=true` and `Hospital=true` buildings. - `Units.RepairRate` customizes the rate at which the units are repaired. This defaults to `[General]`->`ReloadRate` if `UnitReload=true` and if overridden per AircraftType (Ares feature) can tick at different time for each docked aircraft. Setting this overrides that behaviour. For `UnitRepair=true` buildings this defaults to `[General]`->`URepairRate`. - On `UnitReload=true` building setting this to negative value will fully disable the repair functionality. - `Units.RepairStep` how much `Strength` is restored per repair tick. Defaults to `[General]`->`RepairStep`. - `Units.RepairPercent` is a multiplier to cost of repairing (cost / (maximum health / repair step)). Defaults to `[General]`->`RepairPercent`. Note that the final cost is set to 1 if it is less than that. - - `Units.DisableRepairCost` if set to true disables the repair cost entirely. + - `Units.UseRepairCost` can be used to customize if repair cost is applied at all. Defaults to false for infantry, true for everything else. In `rulesmd.ini`: ```ini -[SOMEBUILDING] ; BuildingType -Units.RepairRate= ; floating point value, ingame minutes -Units.RepairStep= ; integer -Units.RepairPercent= ; floating point value, percents or absolute -Units.DisableRepairCost=false ; boolean +[SOMEBUILDING] ; BuildingType +Units.RepairRate= ; floating point value, ingame minutes +Units.RepairStep= ; integer +Units.RepairPercent= ; floating point value, percents or absolute +Units.UseRepairCost= ; boolean ``` ### Airstrike target eligibility @@ -1454,6 +1455,17 @@ AmbientDamage.Warhead= ; WarheadType AmbientDamage.IgnoreTarget=false ; boolean ``` +### Charge turret delays + +- It is now possible to customize the delay of `IsChargeTurret=true` unit turret animation per weapon, per `Burst` shot instead of defaulting to weapon's rearm timer (`ROF`, `BurstDelays` etc). Delay in the list corresponding to burst shot is used, or last delay listed if number of listed values is lower than the current burst index. Delay of 0 or less means previous delay, if applicable, is not restarted. + - Note that unlike the default rearm timer that uses `ROF`, any modifiers are not applied to explicitly set charge turret delays. + + In `rulesmd.ini`: +```ini +[SOMEWEAPON] ; WeaponType +ChargeTurret.Delays= ; list of integers - game frames +``` + ### Customizable disk laser radius ![image](_static/images/disklaser-radius-values-01.gif) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 54b7b58d3c..2e8c80f3fd 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -34,7 +34,8 @@ This page describes all the engine features that are either new and introduced b - `Tint.Color` & `Tint.Intensity` can be used to set a color tint effect and additive lighting increase/decrease on the object the effect is attached to, respectively. - `Tint.VisibleToHouses` can be used to control which houses can see the tint effect. - `FirepowerMultiplier`, `ArmorMultiplier`, `SpeedMultiplier` and `ROFMultiplier` can be used to modify the object's firepower, armor strength, movement speed and weapon reload rate, respectively. - - If `ROFMultiplier.ApplyOnCurrentTimer` is set to true, `ROFMultiplier` is applied on currently running reload timer (if any) when the effect is first applied. + - `ArmorMultiplier.AllowWarheads` and `ArmorMultiplier.DisallowWarheads` can be used to restrict which Warheads the armor multiplier is applied to when dealing damage. + - If `ROFMultiplier.ApplyOnCurrentTimer` is set to true, `ROFMultiplier` is applied on currently running reload timer (if any) when the effect is first applied. - If `Cloakable` is set to true, the object the effect is attached to is granted ability to cloak itself for duration of the effect. - `ForceDecloak`, if set to true, will uncloak and make the object the effect is attached to unable to cloak itself for duration of the effect. - `WeaponRange.Multiplier` and `WeaponRange.ExtraRange` can be used to multiply the weapon firing range of the object the effect is attached to, or give it an increase / decrease (measured in cells), respectively. `ExtraRange` is cumulatively applied from all attached effects after all `Multiplier` values have been applied. @@ -103,6 +104,8 @@ Tint.Intensity= ; floating point value Tint.VisibleToHouses=all ; list of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) FirepowerMultiplier=1.0 ; floating point value ArmorMultiplier=1.0 ; floating point value +ArmorMultiplier.AllowWarheads= ; list of WarheadTypes +ArmorMultiplier.DisallowWarheads= ; list of WarheadTypes SpeedMultiplier=1.0 ; floating point value ROFMultiplier=1.0 ; floating point value ROFMultiplier.ApplyOnCurrentTimer=true ; boolean diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a0265a6137..fe6adaa3d9 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -21,6 +21,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] #### From post-0.3 devbuilds +- `Units.DisableRepairCost` was changed to `Units.UseRepairCost` (note inverted expected value) as it no longer has discrete default value and affects `Hospital=true` buildings, infantry do not have repair cost by default. - Critical hit animations created by `Crit.AnimOnAffectedTargets=true` Warheads no longer default to `AnimList.PickRandom` if `Crit.AnimList.PickRandom` is not set. - `SelfHealGainType` value `none` has been changed to `noheal` due to `none` being treated as a blank string and not parsed by the game. - Affected target enum (`CanTarget`, `Crit.Affects` et al) now considers buildings considered vehicles (`ConsideredVehicle=true` or not set in conjunction with `UndeploysInto` & 1x1 foundation) as units instead of buildings. @@ -373,7 +374,6 @@ New: - Unhardcoded timer blinking color scheme (by Starkku) - Customizing shield self-healing timer restart when shield is damaged (by Starkku) - Customizing minimum & maximum amount of damage shield can take from a single hit (by Starkku) -- Players can now be given ownership of preplaced buildings in Skirmish and Multiplayer in maps using houses of the format where X goes from A to H for spawn positions 1-8 (by ZivDero) - `AutoDeath.Technos(Dont)Exist` can optionally track limboed (not physically on map, e.g transports etc) technos (by Starkku) - Wall overlay `Palette` support (by Starkku) - Show designator & inhibitor range (by Morton) @@ -466,6 +466,8 @@ New: - `Scorch` / `Flamer` fire animation customization (by Starkku) - Warheads parasite removal customization (by Starkku) - Allow infantry to use land sequences in water (by Starkku) +- `` can now be used as owner for pre-placed objects on skirmish and multiplayer maps (by Starkku) +- Allow customizing charge turret delays per burst on a weapon (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) @@ -546,6 +548,7 @@ Vanilla fixes: - Certain global tileset indices (`ShorePieces`, `WaterSet`, `CliffSet`, `WaterCliffs`, `WaterBridge`, `BridgeSet` and `WoodBridgeSet`) are now correctly parsed for Lunar theater (by Starkku) - Fixed infantry `SecondaryFire` / `SecondaryProne` sequences being displayed in water instead of `WetAttack` (by Starkku) - Fixed objects with ally target and `AttackFriendlies=true` having their target reset every frame, particularly AI-owned buildings (by Starkku) +- Follower vehicle index for preplaced vehicles in maps is now explicitly constrained to `[Units]` list in map files and is no longer thrown off by vehicles that could not be created or created vehicles having other vehicles as initial passengers (by Starkku) Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) diff --git a/src/Commands/FrameStep.h b/src/Commands/FrameStep.h index 370cc742ef..caf432425c 100644 --- a/src/Commands/FrameStep.h +++ b/src/Commands/FrameStep.h @@ -17,93 +17,16 @@ class FrameStepCommandClass : public CommandClass template inline const char* FrameStepCommandClass::GetName() const { - // "StepXXFrames" - class to_string_t - { - public: - char buffer[Frame >= 10 ? 13 : 12]; - - public: - constexpr to_string_t() noexcept - : buffer { "Step" } - { - size_t idx = 4; - if constexpr (Frame < 10) - { - buffer[idx++] = Frame + '0'; - buffer[idx++] = 'F'; - buffer[idx++] = 'r'; - buffer[idx++] = 'a'; - buffer[idx++] = 'm'; - buffer[idx++] = 'e'; - buffer[idx++] = 's'; - buffer[idx++] = '\0'; - } - else - { - buffer[idx++] = Frame / 10 + '0'; - buffer[idx++] = Frame % 10 + '0'; - buffer[idx++] = 'F'; - buffer[idx++] = 'r'; - buffer[idx++] = 'a'; - buffer[idx++] = 'm'; - buffer[idx++] = 'e'; - buffer[idx++] = 's'; - buffer[idx++] = '\0'; - } - } - - constexpr operator char* () noexcept { return buffer; } - }; - static to_string_t ret; - return ret; + _snprintf_s(Phobos::readBuffer, Phobos::readLength, "Step%dFrames", Frame); + return Phobos::readBuffer; } template inline const wchar_t* FrameStepCommandClass::GetUIName() const { - // L"Step forward XX frames." - class to_string_t - { - public: - wchar_t buffer[Frame >= 10 ? 23 : 22]; - - public: - constexpr to_string_t() noexcept - : buffer { L"Step forward " } - { - size_t idx = 13; - if constexpr (Frame < 10) - { - buffer[idx++] = Frame + '0'; - buffer[idx++] = ' '; - buffer[idx++] = 'f'; - buffer[idx++] = 'r'; - buffer[idx++] = 'a'; - buffer[idx++] = 'm'; - buffer[idx++] = 'e'; - buffer[idx++] = 's'; - buffer[idx++] = '\0'; - } - else - { - buffer[idx++] = Frame / 10 + '0'; - buffer[idx++] = Frame % 10 + '0'; - buffer[idx++] = ' '; - buffer[idx++] = 'f'; - buffer[idx++] = 'r'; - buffer[idx++] = 'a'; - buffer[idx++] = 'm'; - buffer[idx++] = 'e'; - buffer[idx++] = 's'; - buffer[idx++] = '\0'; - } - } - - constexpr operator wchar_t* () noexcept { return buffer; } - }; - static to_string_t ret; - return StringTable::TryFetchString("TXT_STEP_XX_FORWARD", ret); + const wchar_t* csfString = StringTable::TryFetchString("TXT_STEP_XX_FORWARD", L"Step Forward %d Frames"); + _snwprintf_s(Phobos::wideBuffer, std::size(Phobos::wideBuffer), csfString, Frame); + return Phobos::wideBuffer; } template @@ -115,48 +38,9 @@ inline const wchar_t* FrameStepCommandClass::GetUICategory() const template inline const wchar_t* FrameStepCommandClass::GetUIDescription() const { - // L"Frame Step Only: Step forward XX frames." - class to_string_t - { - public: - wchar_t buffer[Frame >= 10 ? 41 : 40]; - - public: - constexpr to_string_t() noexcept - : buffer { L"Frame Step Only: Step forward " } - { - size_t idx = 30; - if constexpr (Frame < 10) - { - buffer[idx++] = Frame + '0'; - buffer[idx++] = ' '; - buffer[idx++] = 'f'; - buffer[idx++] = 'r'; - buffer[idx++] = 'a'; - buffer[idx++] = 'm'; - buffer[idx++] = 'e'; - buffer[idx++] = 's'; - buffer[idx++] = '\0'; - } - else - { - buffer[idx++] = Frame / 10 + '0'; - buffer[idx++] = Frame % 10 + '0'; - buffer[idx++] = ' '; - buffer[idx++] = 'f'; - buffer[idx++] = 'r'; - buffer[idx++] = 'a'; - buffer[idx++] = 'm'; - buffer[idx++] = 'e'; - buffer[idx++] = 's'; - buffer[idx++] = '\0'; - } - } - - constexpr operator wchar_t* () noexcept { return buffer; } - }; - static to_string_t ret; - return StringTable::TryFetchString("TXT_STEP_XX_FORWARD_DESC", ret); + const wchar_t* csfString = StringTable::TryFetchString("TXT_STEP_XX_FORWARD_DESC", L"Frame Step Only: Step forward %d frames."); + _snwprintf_s(Phobos::wideBuffer, std::size(Phobos::wideBuffer), csfString, Frame); + return Phobos::wideBuffer; } template diff --git a/src/Ext/Aircraft/Hooks.cpp b/src/Ext/Aircraft/Hooks.cpp index f77ecdb2f8..1c9f10bd7b 100644 --- a/src/Ext/Aircraft/Hooks.cpp +++ b/src/Ext/Aircraft/Hooks.cpp @@ -298,31 +298,6 @@ DEFINE_HOOK(0x44402E, BuildingClass_ExitObject_PoseDir2, 0x5) #pragma endregion -DEFINE_HOOK(0x4CF68D, FlyLocomotionClass_DrawMatrix_OnAirport, 0x5) -{ - GET(ILocomotion*, iloco, ESI); - __assume(iloco != nullptr); - auto loco = static_cast(iloco); - auto pThis = static_cast(loco->LinkedTo); - if (pThis->GetHeight() <= 0) - { - REF_STACK(Matrix3D, mat, STACK_OFFSET(0x38, -0x30)); - auto slope_idx = MapClass::Instance->GetCellAt(pThis->Location)->SlopeIndex; - mat = Matrix3D::VoxelRampMatrix[slope_idx] * mat; - float ars = pThis->AngleRotatedSideways; - float arf = pThis->AngleRotatedForwards; - if (std::abs(ars) > 0.005 || std::abs(arf) > 0.005) - { - mat.TranslateZ(float(std::abs(Math::sin(ars)) * pThis->Type->VoxelScaleX - + std::abs(Math::sin(arf)) * pThis->Type->VoxelScaleY)); - R->ECX(pThis); - return 0x4CF6AD; - } - } - - return 0x4CF6A0; -} - DEFINE_HOOK(0x415EEE, AircraftClass_Fire_KickOutPassengers, 0x6) { enum { SkipKickOutPassengers = 0x415F08 }; diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 08ee345871..20c6fece68 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -271,7 +271,7 @@ void AnimExt::SpawnFireAnims(AnimClass* pThis) pAnim->SetOwnerObject(pThis->OwnerObject); }; - auto LoopAnims = [&coords, SpawnAnim](std::vector const& anims, std::vector const& chances, std::vector const& distances, + auto LoopAnims = [&coords, SpawnAnim](std::span const& anims, std::span const& chances, std::span const& distances, int count, AnimTypeClass* defaultAnimType, double defaultChance0, double defaultChanceRest, int defaultDistance0, int defaultDistanceRest, bool constrainToCellSpots, bool attach) { double chance = 0.0; @@ -313,9 +313,9 @@ void AnimExt::SpawnFireAnims(AnimClass* pThis) if (IsLandTypeInFlags(disallowedLandTypes, pThis->GetCell()->LandType)) return; - std::vector anims = pTypeExt->SmallFireAnims; - std::vector chances = pTypeExt->SmallFireChances; - std::vector distances = pTypeExt->SmallFireDistances; + std::span anims = pTypeExt->SmallFireAnims; + std::span chances = pTypeExt->SmallFireChances; + std::span distances = pTypeExt->SmallFireDistances; bool constrainToCellSpots = pTypeExt->ConstrainFireAnimsToCellSpots; bool attach = pTypeExt->AttachFireAnimsToParent.Get(pType->Scorch); int smallCount = pTypeExt->SmallFireCount.Get(1 + pType->Flamer); @@ -383,7 +383,7 @@ void AnimExt::InvalidateTechnoPointers(TechnoClass* pTechno) if (!pExt) { auto const ID = pAnim->Type ? pAnim->Type->get_ID() : "N/A"; - Debug::FatalErrorAndExit("AnimExt::InvalidateTechnoPointers: Animation of type [%s] has no ExtData!", ID); + Debug::FatalErrorAndExit(__FUNCTION__": Animation of type[%s] has no ExtData!", ID); } if (pExt->Invoker == pTechno) @@ -403,7 +403,7 @@ void AnimExt::InvalidateParticleSystemPointers(ParticleSystemClass* pParticleSys if (!pExt) { auto const ID = pAnim->Type ? pAnim->Type->get_ID() : "N/A"; - Debug::FatalErrorAndExit("AnimExt::InvalidateParticleSystemPointers: Animation of type [%s] has no ExtData!", ID); + Debug::FatalErrorAndExit(__FUNCTION__": Animation of type[%s] has no ExtData!", ID); } if (pExt->AttachedSystem == pParticleSystem) @@ -437,26 +437,28 @@ DEFINE_HOOK(0x421EA0, AnimClass_CTOR_SetContext, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x422126, AnimClass_CTOR, 0x5) -DEFINE_HOOK_AGAIN(0x4226F6, AnimClass_CTOR, 0x6) -DEFINE_HOOK(0x4228D2, AnimClass_CTOR, 0x5) +DEFINE_HOOK(0x422126, AnimClass_CTOR_NullType, 0x5) +{ + Debug::Log("Attempting to create animation with null Type (Caller: %08x)!\n", CTORTemp::callerAddress); + return 0; +} + +DEFINE_HOOK(0x4228D2, AnimClass_CTOR_Load, 0x5) { GET(AnimClass*, pItem, ESI); - if (!Phobos::IsLoadingSaveGame) - { - auto const callerAddress = CTORTemp::callerAddress; + AnimExt::ExtMap.Allocate(pItem); + + return 0; +} - // Do this here instead of using a duplicate hook in SyncLogger.cpp - if (!SyncLogger::HooksDisabled && pItem->UniqueID != -2) - SyncLogger::AddAnimCreationSyncLogEvent(CTORTemp::coords, callerAddress); +DEFINE_HOOK(0x4226F6, AnimClass_CTOR, 0x6) +{ + GET(AnimClass*, pItem, ESI); - if (pItem && !pItem->Type) - { - Debug::Log("Attempting to create animation with null Type (Caller: %08x)!\n", callerAddress); - return 0; - } - } + // Do this here instead of using a duplicate hook in SyncLogger.cpp + if (!SyncLogger::HooksDisabled && pItem->UniqueID != -2) + SyncLogger::AddAnimCreationSyncLogEvent(CTORTemp::coords, CTORTemp::callerAddress); AnimExt::ExtMap.Allocate(pItem); diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index a8a728086b..f7fd7ea2c5 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -453,6 +453,19 @@ DEFINE_HOOK(0x44C836, BuildingClass_Mission_Repair_UnitReload, 0x6) return 0; } +DEFINE_HOOK(0x44B8F1, BuildingClass_Mission_Repair_Hospital, 0x6) +{ + enum { SkipGameCode = 0x44B8F7 }; + + GET(BuildingClass*, pThis, EBP); + + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + double repairRate = pTypeExt->Units_RepairRate.Get(RulesClass::Instance->IRepairRate); + __asm { fld repairRate } + + return SkipGameCode; +} + DEFINE_HOOK(0x44BD38, BuildingClass_Mission_Repair_UnitRepair, 0x6) { enum { SkipGameCode = 0x44BD3E }; @@ -485,7 +498,7 @@ DEFINE_HOOK(0x6F4D1A, TechnoClass_ReceiveCommand_Repair, 0x5) double repairPercent = pTypeExt->Units_RepairPercent.Get(RulesClass::Instance->RepairPercent); int repairCost = 0; - if (!pTypeExt->Units_DisableRepairCost) + if (pTypeExt->Units_UseRepairCost.Get(pThis->WhatAmI() != AbstractType::Infantry)) { auto const pType = pThis->GetTechnoType(); repairCost = static_cast((pType->GetCost() / (pType->Strength / static_cast(repairStep))) * repairPercent); diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index e05e4330ae..105956e045 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -155,7 +155,7 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Units_RepairRate.Read(exINI, pSection, "Units.RepairRate"); this->Units_RepairStep.Read(exINI, pSection, "Units.RepairStep"); this->Units_RepairPercent.Read(exINI, pSection, "Units.RepairPercent"); - this->Units_DisableRepairCost.Read(exINI, pSection, "Units.DisableRepairCost"); + this->Units_UseRepairCost.Read(exINI, pSection, "Units.UseRepairCost"); this->NoBuildAreaOnBuildup.Read(exINI, pSection, "NoBuildAreaOnBuildup"); this->Adjacent_Allowed.Read(exINI, pSection, "Adjacent.Allowed"); @@ -279,7 +279,7 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->Units_RepairRate) .Process(this->Units_RepairStep) .Process(this->Units_RepairPercent) - .Process(this->Units_DisableRepairCost) + .Process(this->Units_UseRepairCost) .Process(this->NoBuildAreaOnBuildup) .Process(this->Adjacent_Allowed) .Process(this->Adjacent_Disallowed) diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 15fd63554b..f7b00ae5e4 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -72,7 +72,7 @@ class BuildingTypeExt Nullable Units_RepairRate; Nullable Units_RepairStep; Nullable Units_RepairPercent; - Valueable Units_DisableRepairCost; + Nullable Units_UseRepairCost; Valueable NoBuildAreaOnBuildup; ValueableVector Adjacent_Allowed; @@ -122,7 +122,7 @@ class BuildingTypeExt , Units_RepairRate {} , Units_RepairStep {} , Units_RepairPercent {} - , Units_DisableRepairCost { false } + , Units_UseRepairCost {} , NoBuildAreaOnBuildup { false } , Adjacent_Allowed {} , Adjacent_Disallowed {} diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index e4834ce1a3..15b0f72e2a 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -8,7 +8,7 @@ #include "BombardTrajectory.h" #include "StraightTrajectory.h" -TrajectoryTypePointer::TrajectoryTypePointer(TrajectoryFlag flag) +TrajectoryTypePointer::TrajectoryTypePointer(TrajectoryFlag flag, double speed) { switch (flag) { @@ -20,6 +20,9 @@ TrajectoryTypePointer::TrajectoryTypePointer(TrajectoryFlag flag) return; } _ptr.reset(); + + if (speed > 0.0) + _ptr->Trajectory_Speed = speed; } namespace detail @@ -56,8 +59,10 @@ void TrajectoryTypePointer::LoadFromINI(CCINIClass* pINI, const char* pSection) flag.Read(exINI, pSection, "Trajectory");// I assume this shit is parsed once and only once, so I keep the impl here if (flag.isset()) { - if (!_ptr || _ptr->Flag() != flag.Get()) + if (!_ptr) std::construct_at(this, flag.Get()); + else if (_ptr->Flag() != flag.Get()) + std::construct_at(this, flag.Get(), _ptr->Trajectory_Speed);// inherit speed } if (_ptr) { diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.h b/src/Ext/Bullet/Trajectories/PhobosTrajectory.h index d4e6d1bd9c..b2ca24eba5 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.h +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.h @@ -1,13 +1,10 @@ #pragma once -#include #include #include #include -class BulletTypeExt; - enum class TrajectoryFlag : int { Invalid = -1, @@ -33,7 +30,7 @@ class PhobosTrajectoryType virtual bool Save(PhobosStreamWriter& Stm) const; virtual TrajectoryFlag Flag() const = 0; virtual void Read(CCINIClass* const pINI, const char* pSection) = 0; - virtual std::unique_ptr CreateInstance() const = 0; + [[nodiscard]] virtual std::unique_ptr CreateInstance() const = 0; Valueable Trajectory_Speed { 100.0 }; }; @@ -129,7 +126,7 @@ class TrajectoryTypePointer { std::unique_ptr _ptr {}; public: - explicit TrajectoryTypePointer(TrajectoryFlag flag); + explicit TrajectoryTypePointer(TrajectoryFlag flag, double speed = 0.0); explicit TrajectoryTypePointer() { } TrajectoryTypePointer(const TrajectoryTypePointer&) = delete; TrajectoryTypePointer& operator=(const TrajectoryTypePointer&) = delete; diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index eb67f996b2..5c4b25b69f 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -361,28 +361,6 @@ void HouseExt::GetAIChronoshiftSupers(HouseClass* pThis, SuperClass*& pSuperCSph } } -// Gives player houses names based on their spawning spot -void HouseExt::SetSkirmishHouseName(HouseClass* pHouse) -{ - int spawn_position = pHouse->GetSpawnPosition(); - - // Default behaviour if something went wrong - if (spawn_position < 0 || spawn_position > 7) - { - if (pHouse->IsHumanPlayer) - sprintf(pHouse->PlainName, ""); - else - sprintf(pHouse->PlainName, "Computer"); - } - else - { - const char letters[9] = "ABCDEFGH"; - sprintf(pHouse->PlainName, "", letters[spawn_position]); - } - - Debug::Log("%s, %ls, position %d\n", pHouse->PlainName, pHouse->UIName, spawn_position); -} - // Ares HouseClass* HouseExt::GetHouseKind(OwnerHouseKind const kind, bool const allowRandom, HouseClass* const pDefault, HouseClass* const pInvoker, HouseClass* const pVictim) { diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index 072a80cd68..0b0cca3a1c 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -144,7 +144,6 @@ class HouseExt static HouseClass* GetHouseKind(OwnerHouseKind kind, bool allowRandom, HouseClass* pDefault, HouseClass* pInvoker = nullptr, HouseClass* pVictim = nullptr); static CellClass* GetEnemyBaseGatherCell(HouseClass* pTargetHouse, HouseClass* pCurrentHouse, CoordStruct defaultCurrentCoords, SpeedType speedTypeZone, int extraDistance = 0); static void GetAIChronoshiftSupers(HouseClass* pThis, SuperClass*& pSuperCSphere, SuperClass*& pSuperCWarp); - static void SetSkirmishHouseName(HouseClass* pHouse); static bool IsDisabledFromShell( HouseClass const* pHouse, BuildingTypeClass const* pItem); diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 0c57e70790..54a61b1250 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -32,7 +32,7 @@ void RulesExt::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) DigitalDisplayTypeClass::LoadFromINIList(pINI); RadTypeClass::LoadFromINIList(pINI); ShieldTypeClass::LoadFromINIList(pINI); - LaserTrailTypeClass::LoadFromINIList(&CCINIClass::INI_Art.get()); + LaserTrailTypeClass::LoadFromINIList(&CCINIClass::INI_Art); AttachEffectTypeClass::LoadFromINIList(pINI); Data->LoadBeforeTypeData(pThis, pINI); @@ -411,7 +411,7 @@ void RulesExt::ExtData::ReplaceVoxelLightSources() { needCacheFlush = true; auto source = this->VoxelLightSource.Get().Normalized(); - Game::VoxelLightSource = Matrix3D::VoxelDefaultMatrix.get() * source; + Game::VoxelLightSource = Matrix3D::VoxelDefaultMatrix() * source; } /* @@ -420,7 +420,7 @@ void RulesExt::ExtData::ReplaceVoxelLightSources() { needCacheFlush = true; auto source = this->VoxelShadowLightSource.Get().Normalized(); - Game::VoxelShadowLightSource = Matrix3D::VoxelDefaultMatrix.get() * source; + Game::VoxelShadowLightSource = Matrix3D::VoxelDefaultMatrix() * source; } */ diff --git a/src/Ext/Scenario/Hooks.cpp b/src/Ext/Scenario/Hooks.cpp index fdd6bbec0f..dd20388715 100644 --- a/src/Ext/Scenario/Hooks.cpp +++ b/src/Ext/Scenario/Hooks.cpp @@ -8,7 +8,7 @@ DEFINE_HOOK(0x6870D7, ReadScenario_LoadingScreens, 0x5) LEA_STACK(CCINIClass*, pINI, STACK_OFFSET(0x174, -0x158)); - auto const pScenario = ScenarioClass::Instance.get(); + auto const pScenario = ScenarioClass::Instance(); auto const scenarioName = pScenario->FileName; auto const defaultsSection = "Defaults"; @@ -25,3 +25,37 @@ DEFINE_HOOK(0x6870D7, ReadScenario_LoadingScreens, 0x5) return SkipGameCode; } + +#pragma region PlayerAtX + +// Map as object owner name to correct HouseClass index. +DEFINE_HOOK(0x50C186, GetHouseIndexFromName_PlayerAtX, 0x6) +{ + enum { ReturnFromFunction = 0x50C203 }; + + GET(const char*, name, ECX); + + // Bail out early in campaign mode or if the name does not start with < + if (SessionClass::IsCampaign() || *name != '<') + return 0; + + int playerAtIndex = HouseClass::GetPlayerAtFromString(name); + + if (playerAtIndex != -1) + { + auto const pHouse = HouseClass::FindByPlayerAt(playerAtIndex); + + if (pHouse) + { + R->EDX(pHouse->ArrayIndex); + return ReturnFromFunction; + } + } + + return 0; +} + +// Skip check that prevents buildings from being created for local player. +DEFINE_JUMP(LJMP, 0x44F8D5, 0x44F8E1); + +#pragma endregion diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index 98d6242541..dabcc3df6c 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -80,7 +80,7 @@ CoordStruct TechnoExt::GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FL auto const pExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); auto pInf = abstract_cast(pThis); - auto pickedFLHs = pExt->WeaponBurstFLHs; + std::span> pickedFLHs = pExt->WeaponBurstFLHs; if (pThis->Veterancy.IsElite()) { @@ -148,11 +148,11 @@ void TechnoExt::ExtData::InitializeAttachEffects() { if (auto pTypeExt = this->TypeExtData) { - if (pTypeExt->AttachEffects.AttachTypes.size() < 1) + if (pTypeExt->AttachEffects->AttachTypes.size() < 1) return; auto const pThis = this->OwnerObject(); - AttachEffectClass::Attach(pThis, pThis->Owner, pThis, pThis, pTypeExt->AttachEffects); + AttachEffectClass::Attach(pThis, pThis->Owner, pThis, pThis, pTypeExt->AttachEffects.get()); } } diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 18a9eab8c9..8d490ccf4b 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -738,14 +738,14 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, Anim } default: //must be AutoDeathBehavior::Kill - if (IS_ARES_FUN_AVAILABLE(SpawnSurvivors)) + if (AresFunctions::SpawnSurvivors) { switch (pThis->WhatAmI()) { case AbstractType::Unit: case AbstractType::Aircraft: AresFunctions::SpawnSurvivors(static_cast(pThis), nullptr, false, false); - default:break; + default:; } } pThis->ReceiveDamage(&pThis->Health, 0, RulesClass::Instance->C4Warhead, nullptr, true, false, pThis->Owner); @@ -888,7 +888,7 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() auto const attachEffect = it->get(); auto const pType = attachEffect->GetType(); bool selfOwned = attachEffect->IsSelfOwned(); - bool remove = selfOwned && !pTypeExt->AttachEffects.AttachTypes.Contains(pType); + bool remove = selfOwned && !pTypeExt->AttachEffects->AttachTypes.Contains(pType); if (remove) { @@ -916,7 +916,7 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() } // Add new ones. - int count = AttachEffectClass::Attach(pThis, pThis->Owner, pThis, pThis, pTypeExt->AttachEffects); + int count = AttachEffectClass::Attach(pThis, pThis->Owner, pThis, pThis, pTypeExt->AttachEffects.get()); if (!count) this->RecalculateStatMultipliers(); @@ -983,6 +983,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers() bool hasTint = false; bool reflectsDamage = false; bool hasOnFireDiscardables = false; + bool hasRestrictedArmorMultipliers = false; for (const auto& attachEffect : this->AttachedEffects) { @@ -1001,6 +1002,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers() hasTint |= type->HasTint(); reflectsDamage |= type->ReflectDamage; hasOnFireDiscardables |= (type->DiscardOn & DiscardCondition::Firing) != DiscardCondition::None; + hasRestrictedArmorMultipliers |= (type->ArmorMultiplier != 1.0 && (type->ArmorMultiplier_AllowWarheads.size() > 0 || type->ArmorMultiplier_DisallowWarheads.size() > 0)); } this->AE.FirepowerMultiplier = firepower; @@ -1014,6 +1016,7 @@ void TechnoExt::ExtData::RecalculateStatMultipliers() this->AE.HasTint = hasTint; this->AE.ReflectDamage = reflectsDamage; this->AE.HasOnFireDiscardables = hasOnFireDiscardables; + this->AE.HasRestrictedArmorMultipliers = hasRestrictedArmorMultipliers; if (forceDecloak && pThis->CloakState == CloakState::Cloaked) pThis->Uncloak(true); diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index fac56be0ee..dd34bf636a 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -225,7 +225,7 @@ bool TechnoExt::AllowedTargetByZone(TechnoClass* pThis, TechnoClass* pTarget, Ta // BTW, who said it was merely a Type pointer replacement and he could make a better one than Ares? bool TechnoExt::ConvertToType(FootClass* pThis, TechnoTypeClass* pToType) { - if (IS_ARES_FUN_AVAILABLE(ConvertTypeTo)) + if (AresFunctions::ConvertTypeTo) return AresFunctions::ConvertTypeTo(pThis, pToType); // In case not using Ares 3.0. Only update necessary vanilla properties AbstractType rtti; @@ -482,6 +482,7 @@ void TechnoExt::ExtData::Serialize(T& Stm) .Process(this->PassengerDeletionTimer) .Process(this->CurrentShieldType) .Process(this->LastWarpDistance) + .Process(this->ChargeTurretTimer) .Process(this->AutoDeathTimer) .Process(this->MindControlRingAnimType) .Process(this->Strafe_BombsDroppedThisRound) @@ -492,6 +493,7 @@ void TechnoExt::ExtData::Serialize(T& Stm) .Process(this->DeployFireTimer) .Process(this->SkipTargetChangeResetSequence) .Process(this->ForceFullRearmDelay) + .Process(this->LastRearmWasFullDelay) .Process(this->CanCloakDuringRearm) .Process(this->WHAnimRemainingCreationInterval) .Process(this->FiringObstacleCell) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index fd222baf6c..dd958380bd 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -33,6 +33,7 @@ class TechnoExt CDTimerClass PassengerDeletionTimer; ShieldTypeClass* CurrentShieldType; int LastWarpDistance; + CDTimerClass ChargeTurretTimer; // Used for charge turrets instead of RearmTimer if weapon has ChargeTurret.Delays set. CDTimerClass AutoDeathTimer; AnimTypeClass* MindControlRingAnimType; int DamageNumberOffset; @@ -44,6 +45,7 @@ class TechnoExt CDTimerClass DeployFireTimer; bool SkipTargetChangeResetSequence; bool ForceFullRearmDelay; + bool LastRearmWasFullDelay; bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon. int WHAnimRemainingCreationInterval; bool CanCurrentlyDeployIntoBuilding; // Only set on UnitClass technos with DeploysInto set in multiplayer games, recalculated once per frame so no need to serialize. @@ -68,6 +70,7 @@ class TechnoExt , PassengerDeletionTimer {} , CurrentShieldType { nullptr } , LastWarpDistance {} + , ChargeTurretTimer {} , AutoDeathTimer {} , MindControlRingAnimType { nullptr } , DamageNumberOffset { INT32_MIN } @@ -79,6 +82,7 @@ class TechnoExt , DeployFireTimer {} , SkipTargetChangeResetSequence { false } , ForceFullRearmDelay { false } + , LastRearmWasFullDelay { false } , CanCloakDuringRearm { false } , WHAnimRemainingCreationInterval { 0 } , CanCurrentlyDeployIntoBuilding { false } diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 0888cf6a78..28e1004ac8 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -588,6 +588,51 @@ DEFINE_HOOK(0x6FF4CC, TechnoClass_FireAt_ToggleLaserWeaponIndex, 0x6) return 0; } +static inline void SetChargeTurretDelay(TechnoClass* pThis, int rearmDelay, WeaponTypeClass* pWeapon) +{ + pThis->ChargeTurretDelay = rearmDelay; + auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + + if (pWeaponExt->ChargeTurret_Delays.size() > 0) + { + size_t burstIndex = pWeapon->Burst > 1 ? pThis->CurrentBurstIndex - 1 : 0; + size_t index = burstIndex < pWeaponExt->ChargeTurret_Delays.size() ? burstIndex : pWeaponExt->ChargeTurret_Delays.size() - 1; + int delay = pWeaponExt->ChargeTurret_Delays[index]; + + if (delay <= 0) + return; + + pThis->ChargeTurretDelay = delay; + TechnoExt::ExtMap.Find(pThis)->ChargeTurretTimer.Start(delay); + } +} + +DEFINE_HOOK(0x6FE4A4, TechnoClass_FireAt_ChargeTurret1, 0x6) +{ + enum { SkipGameCode = 0x6FE4AA }; + + GET(TechnoClass*, pThis, ESI); + GET(int, rearmDelay, EAX); + GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0xB4, -0x70)); + + SetChargeTurretDelay(pThis, rearmDelay, pWeapon); + + return SkipGameCode; +} + +DEFINE_HOOK(0x6FF29E, TechnoClass_FireAt_ChargeTurret2, 0x6) +{ + enum { SkipGameCode = 0x6FF2A4 }; + + GET(TechnoClass*, pThis, ESI); + GET(int, rearmDelay, EAX); + GET(WeaponTypeClass*, pWeapon, EBX); + + SetChargeTurretDelay(pThis, rearmDelay, pWeapon); + + return SkipGameCode; +} + #pragma endregion #pragma region TechnoClass_GetFLH @@ -705,9 +750,10 @@ DEFINE_HOOK(0x6FD0B5, TechnoClass_RearmDelay_ROF, 0x6) GET(WeaponTypeClass*, pWeapon, EDI); auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::ExtMap.Find(pThis); auto range = pWeaponExt->ROF_RandomDelay.Get(RulesExt::Global()->ROF_RandomDelay); - double rof = pWeapon->ROF * pTechnoExt->AE.ROFMultiplier; + double rof = pWeapon->ROF * pExt->AE.ROFMultiplier; + pExt->LastRearmWasFullDelay = true; R->EAX(GeneralUtils::GetRangedRandomOrSingleValue(range)); __asm { fld rof }; @@ -721,17 +767,17 @@ DEFINE_HOOK(0x6FD054, TechnoClass_RearmDelay_ForceFullDelay, 0x6) GET(TechnoClass*, pThis, ESI); + auto const pExt = TechnoExt::ExtMap.Find(pThis); + pExt->LastRearmWasFullDelay = false; + // Currently only used with infantry, so a performance saving measure. if (pThis->WhatAmI() == AbstractType::Infantry) { - if (const auto pExt = TechnoExt::ExtMap.Find(pThis)) + if (pExt->ForceFullRearmDelay) { - if (pExt->ForceFullRearmDelay) - { - pExt->ForceFullRearmDelay = false; - pThis->CurrentBurstIndex = 0; - return ApplyFullRearmDelay; - } + pExt->ForceFullRearmDelay = false; + pThis->CurrentBurstIndex = 0; + return ApplyFullRearmDelay; } } diff --git a/src/Ext/Techno/Hooks.Misc.cpp b/src/Ext/Techno/Hooks.Misc.cpp index 67e5a3e61a..76dc27cb00 100644 --- a/src/Ext/Techno/Hooks.Misc.cpp +++ b/src/Ext/Techno/Hooks.Misc.cpp @@ -141,6 +141,18 @@ DEFINE_HOOK(0x6B7282, SpawnManagerClass_AI_PromoteSpawns, 0x5) #pragma region WakeAnims +DEFINE_HOOK_AGAIN(0x69FEDC, Locomotion_Process_Wake, 0x6) // Ship +DEFINE_HOOK_AGAIN(0x4B0814, Locomotion_Process_Wake, 0x6) // Drive +DEFINE_HOOK(0x514AB4, Locomotion_Process_Wake, 0x6) // Hover +{ + GET(ILocomotion* const, iloco, ESI); + __assume(iloco != nullptr); + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(static_cast(iloco)->LinkedTo->GetTechnoType()); + R->EDX(pTypeExt->Wake.Get(RulesClass::Instance->Wake)); + + return R->Origin() + 0xC; +} + namespace GrappleUpdateTemp { TechnoClass* pThis; diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 2665f281b5..88e9183347 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -85,6 +85,38 @@ DEFINE_HOOK(0x6F9FA9, TechnoClass_AI_PromoteAnim, 0x6) return aresProcess(); } +DEFINE_HOOK(0x6FA540, TechnoClass_AI_ChargeTurret, 0x6) +{ + enum { SkipGameCode = 0x6FA5BE }; + + GET(TechnoClass*, pThis, ESI); + + if (pThis->ChargeTurretDelay <= 0) + { + pThis->CurrentTurretNumber = 0; + return SkipGameCode; + } + + auto const pType = pThis->GetTechnoType(); + auto const pExt = TechnoExt::ExtMap.Find(pThis); + int timeLeft = pThis->RearmTimer.GetTimeLeft(); + + if (pExt->ChargeTurretTimer.HasStarted()) + timeLeft = pExt->ChargeTurretTimer.GetTimeLeft(); + else if (pExt->ChargeTurretTimer.Expired()) + pExt->ChargeTurretTimer.Stop(); + + int turretCount = pType->TurretCount; + int turretIndex = Math::max(0, timeLeft * turretCount / pThis->ChargeTurretDelay); + + if (turretIndex >= turretCount) + turretIndex = turretCount - 1; + + pThis->CurrentTurretNumber = turretIndex; + + return SkipGameCode; +} + #pragma endregion #pragma region Init @@ -274,15 +306,51 @@ DEFINE_HOOK(0x4DB218, FootClass_GetMovementSpeed_SpeedMultiplier, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x6FDC87, TechnoClass_ArmorMultiplier, 0x6) // TechnoClass_AdjustDamage -DEFINE_HOOK(0x701966, TechnoClass_ArmorMultiplier, 0x6) // TechnoClass_ReceiveDamage +static int CalculateArmorMultipliers(TechnoClass* pThis, int damage, WarheadTypeClass* pWarhead) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + double mult = pExt->AE.ArmorMultiplier; + + if (pExt->AE.HasRestrictedArmorMultipliers) + { + for (auto const& attachEffect : pExt->AttachedEffects) + { + if (!attachEffect->IsActive()) + continue; + + auto const type = attachEffect->GetType(); + + if (type->ArmorMultiplier_DisallowWarheads.Contains(pWarhead)) + continue; + + if (type->ArmorMultiplier_AllowWarheads.size() > 0 && !type->ArmorMultiplier_AllowWarheads.Contains(pWarhead)) + continue; + + mult *= type->ArmorMultiplier; + } + } + + return static_cast(damage / mult); +} + +DEFINE_HOOK(0x6FDC87, TechnoClass_AdjustDamage_ArmorMultiplier, 0x6) +{ + GET(TechnoClass*, pTarget, EDI); + GET(int, damage, EAX); + GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0x18, 0x8)); + + R->EAX(CalculateArmorMultipliers(pTarget, damage, pWeapon->Warhead)); + + return 0; +} + +DEFINE_HOOK(0x701966, TechnoClass_ReceiveDamage_ArmorMultiplier, 0x6) { - TechnoClass* pThis = R->Origin() == 0x701966 ? R->ESI() : R->EDI(); + GET(TechnoClass*, pThis, ESI); GET(int, damage, EAX); + GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0xC4, 0xC)); - auto const pExt = TechnoExt::ExtMap.Find(pThis); - damage = static_cast(damage / pExt->AE.ArmorMultiplier); - R->EAX(damage); + R->EAX(CalculateArmorMultipliers(pThis, damage, pWarhead)); return 0; } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 94ada02927..e54406b6f0 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -32,12 +32,6 @@ void TechnoTypeExt::ExtData::ApplyTurretOffset(Matrix3D* mtx, double factor) mtx->Translate(x, y, z); } -void TechnoTypeExt::ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor) -{ - if (auto ext = TechnoTypeExt::ExtMap.Find(pType)) - ext->ApplyTurretOffset(mtx, factor); -} - // Ares 0.A source const char* TechnoTypeExt::ExtData::GetSelectionGroupID() const { @@ -578,16 +572,20 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) } // Parasitic types - this->AttachEffects.LoadFromINI(pINI, pSection); + if (!this->AttachEffects) + this->AttachEffects = std::make_unique(); + + this->AttachEffects->LoadFromINI(pINI, pSection); - auto [canParse, resetValue] = PassengerDeletionTypeClass::CanParse(exINI, pSection); + Nullable passengerDeletionRate; + passengerDeletionRate.Read(exINI, pSection, "PassengerDeletion.Rate"); - if (canParse && !this->PassengerDeletionType) + if (passengerDeletionRate.Get(0) > 0 && !this->PassengerDeletionType) this->PassengerDeletionType = std::make_unique(this->OwnerObject()); if (this->PassengerDeletionType) { - if (resetValue) + if (passengerDeletionRate.isset() && passengerDeletionRate.Get(0) <= 0) this->PassengerDeletionType.reset(); else this->PassengerDeletionType->LoadFromINI(pINI, pSection); diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 2b06116e3d..b39683919a 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -213,7 +213,7 @@ class TechnoTypeExt Valueable RevengeWeapon; Valueable RevengeWeapon_AffectsHouses; - AEAttachInfoTypeClass AttachEffects; + std::unique_ptr AttachEffects; ValueableVector BuildLimitGroup_Types; ValueableVector BuildLimitGroup_Nums; diff --git a/src/Ext/TechnoType/Hooks.Drawing.cpp b/src/Ext/TechnoType/Hooks.MatrixOp.cpp similarity index 63% rename from src/Ext/TechnoType/Hooks.Drawing.cpp rename to src/Ext/TechnoType/Hooks.MatrixOp.cpp index 4439750317..5a3238a2c5 100644 --- a/src/Ext/TechnoType/Hooks.Drawing.cpp +++ b/src/Ext/TechnoType/Hooks.MatrixOp.cpp @@ -7,74 +7,19 @@ #include #include #include - -#include "Body.h" -#include -#include #include +#include -#pragma region Misc - -DEFINE_HOOK(0x73D223, UnitClass_DrawIt_OreGath, 0x6) -{ - GET(UnitClass*, pThis, ESI); - GET(int, nFacing, EDI); - GET_STACK(RectangleStruct*, pBounds, STACK_OFFSET(0x50, 0x8)); - LEA_STACK(Point2D*, pLocation, STACK_OFFSET(0x50, -0x18)); - GET_STACK(int, nBrightness, STACK_OFFSET(0x50, 0x4)); - - auto const pType = pThis->GetTechnoType(); - auto const pData = TechnoTypeExt::ExtMap.Find(pType); - - ConvertClass* pDrawer = FileSystem::ANIM_PAL; - SHPStruct* pSHP = FileSystem::OREGATH_SHP; - int idxFrame; - - auto idxTiberium = pThis->GetCell()->GetContainedTiberiumIndex(); - auto idxArray = pData->OreGathering_Tiberiums.size() > 0 ? pData->OreGathering_Tiberiums.IndexOf(idxTiberium) : 0; - if (idxTiberium != -1 && idxArray != -1) - { - auto const pAnimType = pData->OreGathering_Anims.size() > 0 ? pData->OreGathering_Anims[idxArray] : nullptr; - auto const nFramesPerFacing = pData->OreGathering_FramesPerDir.size() > 0 ? pData->OreGathering_FramesPerDir[idxArray] : 15; - auto const pAnimExt = AnimTypeExt::ExtMap.Find(pAnimType); - if (pAnimType) - { - pSHP = pAnimType->GetImage(); - if (auto const pPalette = pAnimExt->Palette.GetConvert()) - pDrawer = pPalette; - } - idxFrame = nFramesPerFacing * nFacing + (Unsorted::CurrentFrame + pThis->WalkedFramesSoFar) % nFramesPerFacing; - } - else - { - idxFrame = 15 * nFacing + (Unsorted::CurrentFrame + pThis->WalkedFramesSoFar) % 15; - } - - DSurface::Temp->DrawSHP( - pDrawer, pSHP, idxFrame, pLocation, pBounds, - BlitterFlags::Flat | BlitterFlags::Alpha | BlitterFlags::Centered, - 0, pThis->GetZAdjustment() - 2, ZGradient::Ground, nBrightness, - 0, nullptr, 0, 0, 0 - ); - - R->EBP(nBrightness); - R->EBX(pBounds); +#include "Body.h" - return 0x73D28C; -} -#pragma endregion +constexpr reference const Pixel_Per_Lepton {}; #pragma region FLH_Turrets -DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) +void TechnoTypeExt::ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor) { - GET(TechnoClass*, pThis, ECX); - auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); - if (pTypeData && pTypeData->HealthBar_Hide) - return 0x6F6AB6; - - return 0; + TechnoTypeExt::ExtMap.Find(pType)->ApplyTurretOffset(mtx, factor); } DEFINE_HOOK(0x6F3C56, TechnoClass_GetFLH_TurretMultiOffset, 0x0) @@ -109,7 +54,6 @@ DEFINE_HOOK(0x73B780, UnitClass_DrawVXL_TurretMultiOffset, 0x0) return 0x73B790; } -constexpr reference const Pixel_Per_Lepton {}; DEFINE_HOOK(0x73BA4C, UnitClass_DrawVXL_TurretMultiOffset1, 0x0) { @@ -146,8 +90,7 @@ DEFINE_HOOK(0x73CCE1, UnitClass_DrawSHP_TurretOffest, 0x6) GET(UnitClass*, pThis, EBP); REF_STACK(Point2D, pos, STACK_OFFSET(0x15C, -0xE8)); - Matrix3D mtx; - mtx.MakeIdentity(); + Matrix3D mtx = Matrix3D::GetIdentity(); mtx.RotateZ(static_cast(pThis->PrimaryFacing.Current().GetRadian<32>())); TechnoTypeExt::ApplyTurretOffset(pThis->Type, &mtx); @@ -166,9 +109,140 @@ DEFINE_HOOK(0x73CCE1, UnitClass_DrawSHP_TurretOffest, 0x6) #pragma endregion -#pragma region Voxel_Drawing +// Hi there copycat + +#pragma region draw_matrix + +DEFINE_HOOK(0x4CF68D, FlyLocomotionClass_DrawMatrix_OnAirport, 0x5) +{ + GET(ILocomotion*, iloco, ESI); + __assume(iloco != nullptr); + auto loco = static_cast(iloco); + auto pThis = static_cast(loco->LinkedTo); + if (pThis->GetHeight() <= 0) + { + REF_STACK(Matrix3D, mat, STACK_OFFSET(0x38, -0x30)); + auto slope_idx = MapClass::Instance->GetCellAt(pThis->Location)->SlopeIndex; + mat = Matrix3D::VoxelRampMatrix[slope_idx] * mat; + float ars = pThis->AngleRotatedSideways; + float arf = pThis->AngleRotatedForwards; + if (std::abs(ars) > 0.005 || std::abs(arf) > 0.005) + { + mat.TranslateZ(float(std::abs(Math::sin(ars)) * pThis->Type->VoxelScaleX + + std::abs(Math::sin(arf)) * pThis->Type->VoxelScaleY)); + R->ECX(pThis); + return 0x4CF6AD; + } + } + + return 0x4CF6A0; +} + +// Just rewrite this completely to avoid headache +Matrix3D* __stdcall JumpjetLocomotionClass_Draw_Matrix(ILocomotion* iloco, Matrix3D* ret, int* pIndex) +{ + __assume(iloco != nullptr); + auto const pThis = static_cast(iloco); + auto linked = pThis->LinkedTo; + // no more TiltCrashJumpjet, do that above svp + bool const onGround = pThis->State == JumpjetLocomotionClass::State::Grounded; + // Man, what can I say, you don't want to stick your rotor into the ground + auto slope_idx = MapClass::Instance->GetCellAt(linked->Location)->SlopeIndex; + *ret = Matrix3D::VoxelRampMatrix[onGround ? slope_idx : 0]; + auto curf = pThis->LocomotionFacing.Current(); + ret->RotateZ((float)curf.GetRadian<32>()); + float arf = linked->AngleRotatedForwards; + float ars = linked->AngleRotatedSideways; + + if (std::abs(ars) >= 0.005 || std::abs(arf) >= 0.005) + { + if (pIndex) *pIndex = -1; + + if (onGround) + { + double scalex = linked->GetTechnoType()->VoxelScaleX; + double scaley = linked->GetTechnoType()->VoxelScaleY; + Matrix3D pre = Matrix3D::GetIdentity(); + pre.TranslateZ(float(std::abs(Math::sin(ars)) * scalex + std::abs(Math::sin(arf)) * scaley)); + ret->TranslateX(float(Math::sgn(arf) * (scaley * (1 - Math::cos(arf))))); + ret->TranslateY(float(Math::sgn(-ars) * (scalex * (1 - Math::cos(ars))))); + ret->RotateX(ars); + ret->RotateY(arf); + *ret = pre * *ret; + } + else + { + // No more translation because I don't like it + ret->RotateX(ars); + ret->RotateY(arf); + } + } + + if (pIndex && *pIndex != -1) + { + if (onGround) *pIndex = slope_idx + (*pIndex << 6); + *pIndex *= 32; + *pIndex |= curf.GetFacing<32>(); + } + + return ret; +} +DEFINE_JUMP(VTABLE, 0x7ECD8C, GET_OFFSET(JumpjetLocomotionClass_Draw_Matrix)); + + +// Visual bugfix : Teleport loco vxls could not tilt +Matrix3D* __stdcall TeleportLocomotionClass_Draw_Matrix(ILocomotion* iloco, Matrix3D* ret, VoxelIndexKey* pIndex) +{ + __assume(iloco != nullptr); + auto const pThis = static_cast(iloco); + auto linked = pThis->LinkedTo; + auto slope_idx = MapClass::Instance->GetCellAt(linked->Location)->SlopeIndex; + + if (pIndex && pIndex->Is_Valid_Key()) + *(int*)(pIndex) = slope_idx + (*(int*)(pIndex) << 6); + + *ret = Matrix3D::VoxelRampMatrix[slope_idx] * pThis->LocomotionClass::Draw_Matrix(pIndex); + + float arf = linked->AngleRotatedForwards; + float ars = linked->AngleRotatedSideways; + + if (std::abs(ars) >= 0.005 || std::abs(arf) >= 0.005) + { + if (pIndex) + pIndex->Invalidate(); + + double scalex = linked->GetTechnoType()->VoxelScaleX; + double scaley = linked->GetTechnoType()->VoxelScaleY; + + Matrix3D pre = Matrix3D::GetIdentity(); + pre.TranslateZ(float(std::abs(Math::sin(ars)) * scalex + std::abs(Math::sin(arf)) * scaley)); + ret->TranslateX(float(Math::sgn(arf) * (scaley * (1 - Math::cos(arf))))); + ret->TranslateY(float(Math::sgn(-ars) * (scalex * (1 - Math::cos(ars))))); + ret->RotateX(ars); + ret->RotateY(arf); -// 2nd order Pade approximant just in case someone complains about performance + *ret = pre * *ret; + } + return ret; +} + +DEFINE_JUMP(VTABLE, 0x7F5024, GET_OFFSET(TeleportLocomotionClass_Draw_Matrix)); + +// Visual bugfix: Tunnel loco could not tilt when being flipped +DEFINE_HOOK(0x729B5D, TunnelLocomotionClass_DrawMatrix_Tilt, 0x8) +{ + GET(ILocomotion*, iloco, ESI); + GET_BASE(VoxelIndexKey*, pIndex, 0x10); + GET_BASE(Matrix3D*, ret, 0xC); + R->EAX(TeleportLocomotionClass_Draw_Matrix(iloco, ret, pIndex)); + return 0x729C09; +} + +#pragma endregion + + +#pragma region shadow_matrix +// just in case any retard complains about performance constexpr double Pade2_2(double in) { const double s = in - static_cast(in); @@ -176,16 +250,12 @@ constexpr double Pade2_2(double in) * (12. - 6 * s + s * s) / (12. + 6 * s + s * s); } -// We need to handle Ares turrets/barrels/waterimage/nospawnalt -struct DummyExtHere // TODO: move it +Matrix3D* __fastcall sub7559B0(Matrix3D* ret, int idx) { - char _[0xA4]; - std::vector ChargerTurrets; - std::vector ChargerBarrels; - char __[0x120]; - UnitTypeClass* WaterImage; - VoxelStruct NoSpawnAltVXL; -}; + *ret = Matrix3D::VoxelRampMatrix[idx] * Matrix3D { 1,0,0,0,0,1,0,0,0,0,0,0 }; + return ret; +} +DEFINE_JUMP(CALL, 0x55A814, GET_OFFSET(sub7559B0)); Matrix3D* __stdcall TunnelLocomotionClass_ShadowMatrix(ILocomotion* iloco, Matrix3D* ret, VoxelIndexKey* key) { @@ -223,25 +293,34 @@ Matrix3D* __stdcall TunnelLocomotionClass_ShadowMatrix(ILocomotion* iloco, Matri break; default:break; } - ret->ScaleX((float)Math::cos(theta));// I know it's ugly + ret->RotateY((float)theta);// I know it's ugly } return ret; } - DEFINE_JUMP(VTABLE, 0x7F5A4C, GET_OFFSET(TunnelLocomotionClass_ShadowMatrix)); +struct DummyTypeExtHere +{ + char _[0xA4]; + std::vector ChargerTurrets; + std::vector ChargerBarrels; + char __[0x120]; + UnitTypeClass* WaterImage; + VoxelStruct NoSpawnAltVXL; +}; + DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) { GET(UnitClass*, pThis, EBP); enum { SkipDrawing = 0x73C5C9 }; auto const loco = pThis->Locomotor.GetInterfacePtr(); - if (pThis->Type->NoShadow || !loco->Is_To_Have_Shadow()) + if (pThis->CloakState != CloakState::Uncloaked || pThis->Type->NoShadow || !loco->Is_To_Have_Shadow()) return SkipDrawing; REF_STACK(Matrix3D, shadow_matrix, STACK_OFFSET(0x1C4, -0x130)); GET_STACK(VoxelIndexKey, vxl_index_key, STACK_OFFSET(0x1C4, -0x1B0)); - LEA_STACK(RectangleStruct*, bounding, STACK_OFFSET(0x1C4, 0xC)); - LEA_STACK(Point2D*, floor, STACK_OFFSET(0x1C4, -0x1A4)); + LEA_STACK(RectangleStruct*, bnd, STACK_OFFSET(0x1C4, 0xC)); + LEA_STACK(Point2D*, pt, STACK_OFFSET(0x1C4, -0x1A4)); GET_STACK(Surface* const, surface, STACK_OFFSET(0x1C4, -0x1A8)); GET(UnitTypeClass*, pType, EBX); @@ -251,7 +330,7 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) const auto uTypeExt = TechnoTypeExt::ExtMap.Find(pType); const auto jjloco = locomotion_cast(loco); const auto height = pThis->GetHeight(); - const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; // -ln(baseScale) precomputed + const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; if (RulesExt::Global()->HeightShadowScaling && height > 0) { @@ -288,10 +367,10 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) { if (pType->NoSpawnAlt && pThis->SpawnManager && pThis->SpawnManager->CountDockedSpawns() == 0) { - if (CAN_USE_ARES && AresHelper::CanUseAres) + if (AresHelper::CanUseAres) { vxl_index_key.Invalidate();// I'd just assume most of the time we have spawn - return &reinterpret_cast(pType->align_2FC)->NoSpawnAltVXL; + return &reinterpret_cast(pType->align_2FC)->NoSpawnAltVXL; } return &pType->TurretVoxel; } @@ -300,12 +379,9 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) auto const main_vxl = GetMainVoxel(); - // TODO : adjust shadow point according to height - // There was a bit deviation that I cannot decipher, might need help with that - // But it turns out it has basically no visual difference auto shadow_point = loco->Shadow_Point(); - auto why = *floor + shadow_point; + auto why = *pt + shadow_point; float arf = pThis->AngleRotatedForwards; float ars = pThis->AngleRotatedSideways; @@ -316,18 +392,16 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) vxl_index_key.Invalidate(); shadow_matrix.TranslateX(float(Math::sgn(arf) * pType->VoxelScaleX * (1 - Math::cos(arf)))); shadow_matrix.TranslateY(float(Math::sgn(-ars) * pType->VoxelScaleY * (1 - Math::cos(ars)))); - shadow_matrix.ScaleX((float)Math::cos(arf)); - shadow_matrix.ScaleY((float)Math::cos(ars)); + shadow_matrix.RotateY(arf); + shadow_matrix.RotateX(ars); } auto mtx = Matrix3D::VoxelDefaultMatrix() * shadow_matrix; - { - auto& arr = mtx.row; - arr[0][2] = arr[1][2] = arr[2][2] = arr[2][1] = arr[2][0] = 0; - } + if (height > 0) shadow_point.Y += 1; + if (!pType->UseTurretShadow) if (uTypeExt->ShadowIndices.empty()) { if (pType->ShadowIndex >= 0 && pType->ShadowIndex < main_vxl->HVA->LayerCount) @@ -336,7 +410,7 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) pType->ShadowIndex, vxl_index_key, &pType->VoxelShadowCache, - bounding, + bnd, &why, &mtx, true, @@ -350,9 +424,9 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) pThis->DrawVoxelShadow( main_vxl, index, - index == pType->ShadowIndex ? vxl_index_key : std::bit_cast(-1), + index == pType->ShadowIndex ? vxl_index_key : VoxelIndexKey(-1), &pType->VoxelShadowCache, - bounding, + bnd, &why, &mtx, index == pType->ShadowIndex, @@ -361,10 +435,9 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) ); } - if (!uTypeExt->TurretShadow.Get(RulesExt::Global()->DrawTurretShadow) || main_vxl == &pType->TurretVoxel) + if (main_vxl == &pType->TurretVoxel || (!pType->UseTurretShadow && !uTypeExt->TurretShadow.Get(RulesExt::Global()->DrawTurretShadow))) return SkipDrawing; - auto GetTurretVoxel = [pType](int idx) ->VoxelStruct* { if (pType->TurretCount == 0 || pType->IsGattling || idx < 0) @@ -373,9 +446,9 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) if (idx < 18) return &pType->ChargerTurrets[idx]; - if (CAN_USE_ARES && AresHelper::CanUseAres) + if (AresHelper::CanUseAres) { - auto* aresTypeExt = reinterpret_cast(pType->align_2FC); + auto* aresTypeExt = reinterpret_cast(pType->align_2FC); return &aresTypeExt->ChargerTurrets[idx - 18]; } @@ -390,61 +463,66 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) if (idx < 18) return &pType->ChargerBarrels[idx]; - if (CAN_USE_ARES && AresHelper::CanUseAres) + if (AresHelper::CanUseAres) { - auto* aresTypeExt = reinterpret_cast(pType->align_2FC); + auto* aresTypeExt = reinterpret_cast(pType->align_2FC); return &aresTypeExt->ChargerBarrels[idx - 18]; } return nullptr; }; - Matrix3D rot = Matrix3D::GetIdentity(); - uTypeExt->ApplyTurretOffset(&rot, Pixel_Per_Lepton); - rot.RotateZ(static_cast(pThis->SecondaryFacing.Current().GetRadian<32>() - pThis->PrimaryFacing.Current().GetRadian<32>())); - auto tur_mtx = mtx * rot; // unfortunately we won't have TurretVoxelScaleX/Y given the amount of work - { - auto& arr = tur_mtx.row; - arr[0][2] = arr[1][2] = arr[2][2] = arr[2][1] = arr[2][0] = 0; - } - auto tur = GetTurretVoxel(pThis->CurrentTurretNumber); + uTypeExt->ApplyTurretOffset(&mtx, Pixel_Per_Lepton); + mtx.RotateZ(static_cast(pThis->SecondaryFacing.Current().GetRadian<32>() - pThis->PrimaryFacing.Current().GetRadian<32>())); - // sorry but you're fucked - if (tur && tur->VXL && tur->HVA) - pThis->DrawVoxelShadow( - tur, - 0, - std::bit_cast(-1), // no cache, no use for valid key - nullptr, // no cache atm - bounding, - &why, - &tur_mtx, - false, - surface, - shadow_point - ); + auto tur = GetTurretVoxel(pThis->CurrentTurretNumber); + if (!(tur && tur->VXL && tur->HVA)) + return SkipDrawing; auto bar = GetBarrelVoxel(pThis->CurrentTurretNumber); + auto haveBar = bar && bar->VXL && bar->HVA && !bar->VXL->Initialized; + if (vxl_index_key.Is_Valid_Key()) + vxl_index_key.TurretWeapon.Facing = pThis->SecondaryFacing.Current().GetFacing<32>(); - // and you are utterly fucked - if (bar && bar->VXL && bar->HVA) + auto* cache = &pType->VoxelShadowCache; + if (!pType->UseTurretShadow) + { + if (haveBar) + cache = nullptr; + else + cache = tur != &pType->TurretVoxel ? + nullptr // man what can I say, you are fucked, for now + : reinterpret_cast(&pType->VoxelTurretBarrelCache) // excuse me + ; + } + + pThis->DrawVoxelShadow( + tur, + 0, + vxl_index_key, + cache, + bnd, + &why, + &mtx, + cache != nullptr, + surface, + shadow_point + ); + + if (haveBar)// you are utterly fucked, for now pThis->DrawVoxelShadow( bar, 0, - std::bit_cast(-1), // no cache, no use - nullptr,//no cache atm - bounding, + VoxelIndexKey(-1), + nullptr, + bnd, &why, - &tur_mtx, + &mtx, false, surface, shadow_point ); - // Add caches in Ext if necessary, remember not to serialize these shit - // IndexClass VoxelTurretShadowCache {}; - // IndexClass VoxelBarrelShadowCache {}; - return SkipDrawing; } @@ -458,7 +536,7 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) enum { FinishDrawing = 0x4148A5 }; const auto loco = pThis->Locomotor.GetInterfacePtr(); - if (pThis->Type->NoShadow || !loco->Is_To_Have_Shadow() || pThis->IsSinking) + if (pThis->Type->NoShadow || pThis->CloakState != CloakState::Uncloaked || pThis->IsSinking || !loco->Is_To_Have_Shadow()) return FinishDrawing; auto shadow_mtx = loco->Shadow_Matrix(&key); @@ -488,25 +566,21 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) double arf = pThis->AngleRotatedForwards; if (flyLoco->CurrentSpeed > pThis->Type->PitchSpeed) arf += pThis->Type->PitchAngle; - double ars = pThis->AngleRotatedSideways; + float ars = pThis->AngleRotatedSideways; if (key.Is_Valid_Key() && (std::abs(arf) > 0.005 || std::abs(ars) > 0.005)) key.Invalidate(); - shadow_mtx.ScaleY((float)Math::cos(ars)); - shadow_mtx.ScaleX((float)Math::cos(arf)); + shadow_mtx.RotateX((float)ars); + shadow_mtx.RotateY((float)arf); } else if (height > 0) { // You must be Rocket, otherwise GO FUCK YOURSELF - shadow_mtx.ScaleX((float)Math::cos(static_cast(loco)->CurrentPitch)); + shadow_mtx.RotateY(static_cast(loco)->CurrentPitch); key.Invalidate(); } shadow_mtx = Matrix3D::VoxelDefaultMatrix() * shadow_mtx; - { - auto& arr = shadow_mtx.row; - arr[0][2] = arr[1][2] = arr[2][2] = arr[2][1] = arr[2][0] = 0; - } auto const main_vxl = &pThis->Type->MainVoxel; // flor += loco->Shadow_Point(); // no longer needed @@ -545,8 +619,9 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) return FinishDrawing; } -// Shadow_Point of RocketLoco was forgotten to be set to {0,0}. It was an oversight. -DEFINE_JUMP(VTABLE, 0x7F0B4C, 0x4CF940); +DEFINE_JUMP(VTABLE, 0x7F0B4C, 0x4CF940);// Shadow_Point of RocketLoco was forgotten to be set to {0,0}. It was an oversight. +DEFINE_JUMP(LJMP, 0x706BDD, 0x706C01); // I checked it a priori + /* //TO TEST AND EXPLAIN: why resetting height when drawing aircrafts? DEFINE_JUMP(CALL6, 0x4147D5, 0x5F4300); @@ -554,9 +629,9 @@ DEFINE_JUMP(CALL6, 0x4148AB, 0x5F4300); DEFINE_JUMP(CALL6, 0x4147F3, 0x5F4300); */ -DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x6) +DEFINE_HOOK(0x7072A1, cyka707280_WhichMatrix, 0x6) { - GET(FootClass*, pThis, EBX);//Maybe Techno later + GET(FootClass*, pThis, EBX);//Maybe Techno later, for GTGCAN GET(VoxelStruct*, pVXL, EBP); GET_STACK(Matrix3D*, pMat, STACK_OFFSET(0xE8, 0xC)); GET_STACK(int, shadow_index_now, STACK_OFFSET(0xE8, 0x18));// it's used later, otherwise I could have chosen the frame index earlier @@ -580,7 +655,7 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x6) else { // guess what, someone actually has a multisection nospawnalt - if (!(AresHelper::CanUseAres && pVXL == &reinterpret_cast(pType->align_2FC)->NoSpawnAltVXL)) + if (!(AresHelper::CanUseAres && pVXL == &reinterpret_cast(pType->align_2FC)->NoSpawnAltVXL)) return pThis->TurretAnimFrame % hva->FrameCount; } // you might also be WaterImage or sth else, but I don't want to care anymore, go fuck yourself @@ -613,13 +688,12 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x6) return pThis->WalkedFramesSoFar % hva->FrameCount; }; + matRet = *pMat * hva->Matrixes[shadow_index_now + hva->LayerCount * ChooseFrame()] * Matrix3D { 1,0,0,0,0,1,0,0,0,0,0,0 }; - Matrix3D hvamat = hva->Matrixes[shadow_index_now + hva->LayerCount * ChooseFrame()]; - { - auto& arr = hvamat.row; - arr[0][2] = arr[1][2] = arr[2][2] = arr[2][1] = arr[2][0] = arr[2][3] = 0; - } - matRet = *pMat * hvamat; + double l2 = 0; + auto& arr = matRet.row; + for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) l2 += arr[i][j] * arr[i][j]; + if (l2 < 0.03) R->Stack(STACK_OFFSET(0xE8, 0x20), true); // Recover vanilla instructions if (pThis->GetTechnoType()->UseBuffer) @@ -634,21 +708,8 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x6) Matrix3D* __fastcall BounceClass_ShadowMatrix(BounceClass* self, void*, Matrix3D* ret) { Matrix3D::FromQuaternion(ret, &self->CurrentAngle); - *ret = Matrix3D { 1, 0, 0 , 0, 0, 0.25f, -0.4330127018922194f , 0, 0, 0, 0 , 0 } **ret; + *ret = Matrix3D { 1, 0, 0 , 0, 0, 0.25f, -0.4330127f , 0,0, -0.4330127f, 0.75f , 0 }**ret * Matrix3D { 1,0,0,0,0,1,0,0,0,0,0,0 }; return ret; } DEFINE_JUMP(CALL, 0x749CAC, GET_OFFSET(BounceClass_ShadowMatrix)); - -DEFINE_HOOK_AGAIN(0x69FEDC, Locomotion_Process_Wake, 0x6) // Ship -DEFINE_HOOK_AGAIN(0x4B0814, Locomotion_Process_Wake, 0x6) // Drive -DEFINE_HOOK(0x514AB4, Locomotion_Process_Wake, 0x6) // Hover -{ - GET(ILocomotion* const, pILoco, ESI); - __assume(pILoco != nullptr); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(static_cast(pILoco)->LinkedTo->GetTechnoType()); - R->EDX(pTypeExt->Wake.Get(RulesClass::Instance->Wake)); - - return R->Origin() + 0xC; -} - #pragma endregion diff --git a/src/Ext/TechnoType/Hooks.Teleport.cpp b/src/Ext/TechnoType/Hooks.Teleport.cpp index c50761f139..cfaccf439f 100644 --- a/src/Ext/TechnoType/Hooks.Teleport.cpp +++ b/src/Ext/TechnoType/Hooks.Teleport.cpp @@ -106,58 +106,6 @@ DEFINE_HOOK(0x719973, TeleportLocomotionClass_ILocomotion_Process_ChronoDelay, 0 #undef GET_LOCO -// Visual bugfix : Teleport loco vxls could not tilt -Matrix3D* __stdcall TeleportLocomotionClass_Draw_Matrix(ILocomotion* iloco, Matrix3D* ret, VoxelIndexKey* pIndex) -{ - __assume(iloco != nullptr); - auto const pThis = static_cast(iloco); - auto linked = pThis->LinkedTo; - auto slope_idx = MapClass::Instance->GetCellAt(linked->Location)->SlopeIndex; - - if (pIndex && pIndex->Is_Valid_Key()) - *(int*)(pIndex) = slope_idx + (*(int*)(pIndex) << 6); - - *ret = Matrix3D::VoxelRampMatrix[slope_idx] * pThis->LocomotionClass::Draw_Matrix(pIndex); - - float arf = linked->AngleRotatedForwards; - float ars = linked->AngleRotatedSideways; - - if (std::abs(ars) >= 0.005 || std::abs(arf) >= 0.005) - { - if (pIndex) - pIndex->Invalidate(); - - double scalex = linked->GetTechnoType()->VoxelScaleX; - double scaley = linked->GetTechnoType()->VoxelScaleY; - - Matrix3D pre = Matrix3D::GetIdentity(); - pre.TranslateZ(float(std::abs(Math::sin(ars)) * scalex + std::abs(Math::sin(arf)) * scaley)); - ret->TranslateX(float(Math::sgn(arf) * (scaley * (1 - Math::cos(arf))))); - ret->TranslateY(float(Math::sgn(-ars) * (scalex * (1 - Math::cos(ars))))); - ret->RotateX(ars); - ret->RotateY(arf); - - *ret = pre * *ret; - } - return ret; -} - -DEFINE_JUMP(VTABLE, 0x7F5024, GET_OFFSET(TeleportLocomotionClass_Draw_Matrix)); -// DEFINE_JUMP(VTABLE, 0x7F5028, 0x5142A0);//TeleportLocomotionClass_Shadow_Matrix : just use hover's to save my time - -// Visual bugfix: Tunnel loco could not tilt when being flipped -DEFINE_HOOK(0x729B5D, TunnelLocomotionClass_DrawMatrix_Tilt, 0x8) -{ - GET(ILocomotion*, iloco, ESI); - GET_BASE(VoxelIndexKey*, pIndex, 0x10); - GET_BASE(Matrix3D*, ret, 0xC); - R->EAX(TeleportLocomotionClass_Draw_Matrix(iloco, ret, pIndex)); - return 0x729C09; -} - -// DEFINE_JUMP(VTABLE, 0x7F5A4C, 0x5142A0);//TunnelLocomotionClass_Shadow_Matrix : just use hover's to save my time -// Since I've already invalidated the key for tilted vxls when reimplementing the shadow drawing code, this is no longer necessary - DEFINE_HOOK(0x7197E4, TeleportLocomotionClass_Process_ChronospherePreDelay, 0x6) { GET(TeleportLocomotionClass*, pThis, ESI); diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 272ad0752f..ca9784f642 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -1,5 +1,63 @@ #include "Body.h" #include +#include + +DEFINE_HOOK(0x73D223, UnitClass_DrawIt_OreGath, 0x6) +{ + GET(UnitClass*, pThis, ESI); + GET(int, nFacing, EDI); + GET_STACK(RectangleStruct*, pBounds, STACK_OFFSET(0x50, 0x8)); + LEA_STACK(Point2D*, pLocation, STACK_OFFSET(0x50, -0x18)); + GET_STACK(int, nBrightness, STACK_OFFSET(0x50, 0x4)); + + auto const pData = TechnoTypeExt::ExtMap.Find(pThis->Type); + + ConvertClass* pDrawer = FileSystem::ANIM_PAL; + SHPStruct* pSHP = FileSystem::OREGATH_SHP; + int idxFrame; + + auto idxTiberium = pThis->GetCell()->GetContainedTiberiumIndex(); + auto idxArray = pData->OreGathering_Tiberiums.size() > 0 ? pData->OreGathering_Tiberiums.IndexOf(idxTiberium) : 0; + if (idxTiberium != -1 && idxArray != -1) + { + auto const pAnimType = pData->OreGathering_Anims.size() > 0 ? pData->OreGathering_Anims[idxArray] : nullptr; + auto const nFramesPerFacing = pData->OreGathering_FramesPerDir.size() > 0 ? pData->OreGathering_FramesPerDir[idxArray] : 15; + auto const pAnimExt = AnimTypeExt::ExtMap.Find(pAnimType); + if (pAnimType) + { + pSHP = pAnimType->GetImage(); + if (auto const pPalette = pAnimExt->Palette.GetConvert()) + pDrawer = pPalette; + } + idxFrame = nFramesPerFacing * nFacing + (Unsorted::CurrentFrame + pThis->WalkedFramesSoFar) % nFramesPerFacing; + } + else + { + idxFrame = 15 * nFacing + (Unsorted::CurrentFrame + pThis->WalkedFramesSoFar) % 15; + } + + DSurface::Temp->DrawSHP( + pDrawer, pSHP, idxFrame, pLocation, pBounds, + BlitterFlags::Flat | BlitterFlags::Alpha | BlitterFlags::Centered, + 0, pThis->GetZAdjustment() - 2, ZGradient::Ground, nBrightness, + 0, nullptr, 0, 0, 0 + ); + + R->EBP(nBrightness); + R->EBX(pBounds); + + return 0x73D28C; +} + +DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) +{ + GET(TechnoClass*, pThis, ECX); + auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + if (pTypeData->HealthBar_Hide) + return 0x6F6AB6; + + return 0; +} // Issue #503 // Author : Otamaa diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index bd59a0dcfc..1be3eb24b4 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -176,58 +176,6 @@ DEFINE_HOOK(0x70B649, TechnoClass_RigidBodyDynamics_NoTiltCrashBlyat, 0x6) return 0; } -// Just rewrite this completely to avoid headache -Matrix3D* __stdcall JumpjetLocomotionClass_Draw_Matrix(ILocomotion* iloco, Matrix3D* ret, int* pIndex) -{ - __assume(iloco != nullptr); - auto const pThis = static_cast(iloco); - auto linked = pThis->LinkedTo; - // no more TiltCrashJumpjet, do that above svp - bool const onGround = pThis->State == JumpjetLocomotionClass::State::Grounded; - // Man, what can I say, you don't want to stick your rotor into the ground - auto slope_idx = MapClass::Instance->GetCellAt(linked->Location)->SlopeIndex; - *ret = Matrix3D::VoxelRampMatrix[onGround ? slope_idx : 0]; - auto curf = pThis->LocomotionFacing.Current(); - ret->RotateZ((float)curf.GetRadian<32>()); - float arf = linked->AngleRotatedForwards; - float ars = linked->AngleRotatedSideways; - - if (std::abs(ars) >= 0.005 || std::abs(arf) >= 0.005) - { - if (pIndex) *pIndex = -1; - - if (onGround) - { - double scalex = linked->GetTechnoType()->VoxelScaleX; - double scaley = linked->GetTechnoType()->VoxelScaleY; - Matrix3D pre = Matrix3D::GetIdentity(); - pre.TranslateZ(float(std::abs(Math::sin(ars)) * scalex + std::abs(Math::sin(arf)) * scaley)); - ret->TranslateX(float(Math::sgn(arf) * (scaley * (1 - Math::cos(arf))))); - ret->TranslateY(float(Math::sgn(-ars) * (scalex * (1 - Math::cos(ars))))); - ret->RotateX(ars); - ret->RotateY(arf); - *ret = pre * *ret; - } - else - { - // No more translation because I don't like it - ret->RotateX(ars); - ret->RotateY(arf); - } - } - - if (pIndex && *pIndex != -1) - { - if (onGround) *pIndex = slope_idx + (*pIndex << 6); - *pIndex *= 32; - *pIndex |= curf.GetFacing<32>(); - } - - return ret; -} - -DEFINE_JUMP(VTABLE, 0x7ECD8C, GET_OFFSET(JumpjetLocomotionClass_Draw_Matrix)); - FireError __stdcall JumpjetLocomotionClass_Can_Fire(ILocomotion* pThis) { __assume(pThis != nullptr); diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 5a5e2febef..df63361e9d 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -270,7 +270,10 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) TypeConvertGroup::Parse(this->Convert_Pairs, exINI, pSection, AffectedHouse::All); // AttachEffect - this->AttachEffects.LoadFromINI(pINI, pSection); + if (!this->AttachEffects) + this->AttachEffects = std::make_unique(); + + this->AttachEffects->LoadFromINI(pINI, pSection); #ifdef LOCO_TEST_WARHEADS // Enable warheads parsing this->InflictLocomotor.Read(exINI, pSection, "InflictLocomotor"); @@ -315,9 +318,9 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) || this->Convert_Pairs.size() > 0 || this->InflictLocomotor || this->RemoveInflictedLocomotor - || this->AttachEffects.AttachTypes.size() > 0 - || this->AttachEffects.RemoveTypes.size() > 0 - || this->AttachEffects.RemoveGroups.size() > 0 + || this->AttachEffects->AttachTypes.size() > 0 + || this->AttachEffects->RemoveTypes.size() > 0 + || this->AttachEffects->RemoveGroups.size() > 0 ); char tempBuffer[32]; diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index b5331a9ada..76434cbf77 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -133,7 +133,7 @@ class WarheadTypeExt ValueableVector DetonateOnAllMapObjects_IgnoreTypes; std::vector Convert_Pairs; - AEAttachInfoTypeClass AttachEffects; + std::unique_ptr AttachEffects; Valueable InflictLocomotor; Valueable RemoveInflictedLocomotor; diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index b0884359ce..248571e646 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -167,7 +167,7 @@ void WarheadTypeExt::ExtData::DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* if (this->Convert_Pairs.size() > 0) this->ApplyConvert(pHouse, pTarget); - if (this->AttachEffects.AttachTypes.size() > 0 || this->AttachEffects.RemoveTypes.size() > 0 || this->AttachEffects.RemoveGroups.size() > 0) + if (this->AttachEffects->AttachTypes.size() > 0 || this->AttachEffects->RemoveTypes.size() > 0 || this->AttachEffects->RemoveGroups.size() > 0) this->ApplyAttachEffects(pTarget, pHouse, pOwner); #ifdef LOCO_TEST_WARHEADS @@ -463,7 +463,7 @@ void WarheadTypeExt::ExtData::ApplyAttachEffects(TechnoClass* pTarget, HouseClas return; std::vector dummy = std::vector(); - auto const& info = this->AttachEffects; + auto const info = this->AttachEffects.get(); AttachEffectClass::Attach(pTarget, pInvokerHouse, pInvoker, this->OwnerObject(), info); AttachEffectClass::Detach(pTarget, info); AttachEffectClass::DetachByGroups(pTarget, info); diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index df292a8d83..3e82a2b5bc 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -95,6 +95,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->FeedbackWeapon.Read(exINI, pSection, "FeedbackWeapon"); this->Laser_IsSingleColor.Read(exINI, pSection, "IsSingleColor"); this->ROF_RandomDelay.Read(exINI, pSection, "ROF.RandomDelay"); + this->ChargeTurret_Delays.Read(exINI, pSection, "ChargeTurret.Delays"); this->OmniFire_TurnToTarget.Read(exINI, pSection, "OmniFire.TurnToTarget"); this->FireOnce_ResetSequence.Read(exINI, pSection, "FireOnce.ResetSequence"); this->ExtraWarheads.Read(exINI, pSection, "ExtraWarheads"); @@ -139,6 +140,7 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->FeedbackWeapon) .Process(this->Laser_IsSingleColor) .Process(this->ROF_RandomDelay) + .Process(this->ChargeTurret_Delays) .Process(this->OmniFire_TurnToTarget) .Process(this->FireOnce_ResetSequence) .Process(this->ExtraWarheads) diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 554cab5645..2a979bece1 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -40,6 +40,7 @@ class WeaponTypeExt Valueable FeedbackWeapon; Valueable Laser_IsSingleColor; Nullable> ROF_RandomDelay; + ValueableVector ChargeTurret_Delays; Valueable OmniFire_TurnToTarget; Valueable FireOnce_ResetSequence; ValueableVector ExtraWarheads; @@ -80,6 +81,7 @@ class WeaponTypeExt , FeedbackWeapon {} , Laser_IsSingleColor { false } , ROF_RandomDelay {} + , ChargeTurret_Delays {} , OmniFire_TurnToTarget { false } , FireOnce_ResetSequence { true } , ExtraWarheads {} diff --git a/src/Misc/Hooks.AssignHouses.cpp b/src/Misc/Hooks.AssignHouses.cpp deleted file mode 100644 index eeefdae32a..0000000000 --- a/src/Misc/Hooks.AssignHouses.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include - -DEFINE_HOOK(0x68804A, AssignHouses_PlayerHouses, 0x5) -{ - GET(HouseClass*, pPlayerHouse, EBP); - - HouseExt::SetSkirmishHouseName(pPlayerHouse); - - return 0x68808E; -} - -DEFINE_HOOK(0x688210, AssignHouses_ComputerHouses, 0x5) -{ - GET(HouseClass*, pAiHouse, EBP); - - HouseExt::SetSkirmishHouseName(pAiHouse); - - return 0x688252; -} - -// Skips checking the gamemode or who the player is when assigning houses -DEFINE_JUMP(LJMP, 0x44F8CB, 0x44F8E1) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 2e7efa2def..638eab3548 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -1007,3 +1007,76 @@ DEFINE_HOOK(0x6FA467, TechnoClass_AI_AttackFriendlies, 0x5) return 0; } + +// Starkku: These fix issues with follower train cars etc) indices being thrown off by preplaced vehicles not being created, having other vehicles as InitialPayload etc. +// This fix basically works by not using the global UnitClass array at all for setting the followers, only a list of preplaced units, successfully created or not. +#pragma region Follower + +namespace UnitParseTemp +{ + std::vector ParsedUnits; + bool WasCreated = false; +} + +// Add vehicles successfully created to list of parsed vehicles. +DEFINE_HOOK(0x7435DE, UnitClass_ReadFromINI_Follower1, 0x6) +{ + GET(UnitClass*, pUnit, ESI); + + UnitParseTemp::ParsedUnits.push_back(pUnit); + UnitParseTemp::WasCreated = true; + + return 0; +} + +// Add vehicles that were not successfully created to list of parsed vehicles as well as to followers list. +DEFINE_HOOK(0x74364C, UnitClass_ReadFromINI_Follower2, 0x8) +{ + REF_STACK(TypeList, followers, STACK_OFFSET(0xD0, -0xC0)); + + if (!UnitParseTemp::WasCreated) + { + followers.AddItem(-1); + UnitParseTemp::ParsedUnits.push_back(nullptr); + } + + UnitParseTemp::WasCreated = false; + + return 0; +} + +// Set followers based on parsed vehicles. +DEFINE_HOOK(0x743664, UnitClass_ReadFromINI_Follower3, 0x6) +{ + enum { SkipGameCode = 0x7436AC }; + + REF_STACK(TypeList, followers, STACK_OFFSET(0xCC, -0xC0)); + auto& units = UnitParseTemp::ParsedUnits; + + for (size_t i = 0; i < units.size(); i++) + { + auto const pUnit = units[i]; + + if (!pUnit) + continue; + + int followerIndex = followers[i]; + + if (followerIndex < 0 || followerIndex >= static_cast(units.size())) + { + pUnit->FollowerCar = nullptr; + } + else + { + auto const pFollower = units[followerIndex]; + pUnit->FollowerCar = pFollower; + pFollower->IsFollowerCar = true; + } + } + + units.clear(); + + return SkipGameCode; +} + +#pragma endregion diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index 8b2a350c91..8c33114518 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -101,8 +101,11 @@ void AttachEffectClass::AI() { double ROFModifier = this->Type->ROFMultiplier; auto const pTechno = this->Techno; + auto const pExt = TechnoExt::ExtMap.Find(this->Techno); pTechno->RearmTimer.Start(static_cast(pTechno->RearmTimer.GetTimeLeft() * ROFModifier)); - pTechno->ChargeTurretDelay = static_cast(pTechno->ChargeTurretDelay * ROFModifier); + + if (!pExt->ChargeTurretTimer.HasStarted() && pExt->LastRearmWasFullDelay) + pTechno->ChargeTurretDelay = static_cast(pTechno->ChargeTurretDelay * ROFModifier); } if (this->Type->HasTint()) @@ -483,9 +486,9 @@ bool AttachEffectClass::IsFromSource(TechnoClass* pInvoker, AbstractClass* pSour /// Source object for the attachment e.g a Warhead or Techno. /// AttachEffect attach info. /// Number of AttachEffect instances created and attached. -int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker, AbstractClass* pSource, AEAttachInfoTypeClass const& attachEffectInfo) +int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker, AbstractClass* pSource, AEAttachInfoTypeClass* const attachEffectInfo) { - auto const& types = attachEffectInfo.AttachTypes; + auto const& types = attachEffectInfo->AttachTypes; if (types.size() < 1 || !pTarget) return false; @@ -499,7 +502,7 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T for (size_t i = 0; i < types.size(); i++) { auto const pType = types[i]; - auto const params = attachEffectInfo.GetAttachParams(i, selfOwned); + auto const& params = attachEffectInfo->GetAttachParams(i, selfOwned); if (auto const pAE = AttachEffectClass::CreateAndAttach(pType, pTarget, pTargetExt->AttachedEffects, pInvokerHouse, pInvoker, pSource, params)) { @@ -522,7 +525,9 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T if (ROFModifier != 1.0) { pTarget->RearmTimer.Start(static_cast(pTarget->RearmTimer.GetTimeLeft() * ROFModifier)); - pTarget->ChargeTurretDelay = static_cast(pTarget->ChargeTurretDelay * ROFModifier); + + if (!pTargetExt->ChargeTurretTimer.HasStarted() && pTargetExt->LastRearmWasFullDelay) + pTarget->ChargeTurretDelay = static_cast(pTarget->ChargeTurretDelay * ROFModifier); } if (attachedCount > 0) @@ -639,12 +644,14 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy /// Target techno. /// AttachEffect attach info. /// Number of AttachEffect instances removed. -int AttachEffectClass::Detach(TechnoClass* pTarget, AEAttachInfoTypeClass const& attachEffectInfo) +int AttachEffectClass::Detach(TechnoClass* pTarget, AEAttachInfoTypeClass* const attachEffectInfo) { - if (attachEffectInfo.RemoveTypes.size() < 1 || !pTarget) + auto const& types = attachEffectInfo->RemoveTypes; + + if (types.size() < 1 || !pTarget) return 0; - return DetachTypes(pTarget, attachEffectInfo, attachEffectInfo.RemoveTypes); + return DetachTypes(pTarget, attachEffectInfo, types); } /// @@ -653,9 +660,9 @@ int AttachEffectClass::Detach(TechnoClass* pTarget, AEAttachInfoTypeClass const& /// Target techno. /// AttachEffect attach info. /// Number of AttachEffect instances removed. -int AttachEffectClass::DetachByGroups(TechnoClass* pTarget, AEAttachInfoTypeClass const& attachEffectInfo) +int AttachEffectClass::DetachByGroups(TechnoClass* pTarget, AEAttachInfoTypeClass* const attachEffectInfo) { - auto const& groups = attachEffectInfo.RemoveGroups; + auto const& groups = attachEffectInfo->RemoveGroups; if (groups.size() < 1 || !pTarget) return 0; @@ -681,13 +688,13 @@ int AttachEffectClass::DetachByGroups(TechnoClass* pTarget, AEAttachInfoTypeClas /// AttachEffect attach info. /// AttachEffect types. /// Number of AttachEffect instances removed. -int AttachEffectClass::DetachTypes(TechnoClass* pTarget, AEAttachInfoTypeClass const& attachEffectInfo, std::vector const& types) +int AttachEffectClass::DetachTypes(TechnoClass* pTarget, AEAttachInfoTypeClass* const attachEffectInfo, std::vector const& types) { auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); int detachedCount = 0; bool markForRedraw = false; - auto const& minCounts = attachEffectInfo.CumulativeRemoveMinCounts; - auto const& maxCounts = attachEffectInfo.CumulativeRemoveMaxCounts; + auto const& minCounts = attachEffectInfo->CumulativeRemoveMinCounts; + auto const& maxCounts = attachEffectInfo->CumulativeRemoveMaxCounts; size_t index = 0, minSize = minCounts.size(), maxSize = maxCounts.size(); for (auto const pType : types) diff --git a/src/New/Entity/AttachEffectClass.h b/src/New/Entity/AttachEffectClass.h index 08b103a1c8..b4fcee5f51 100644 --- a/src/New/Entity/AttachEffectClass.h +++ b/src/New/Entity/AttachEffectClass.h @@ -36,9 +36,9 @@ class AttachEffectClass bool Load(PhobosStreamReader& Stm, bool RegisterForChange); bool Save(PhobosStreamWriter& Stm) const; - static int Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker, AbstractClass* pSource, AEAttachInfoTypeClass const& attachEffectInfo); - static int Detach(TechnoClass* pTarget, AEAttachInfoTypeClass const& attachEffectInfo); - static int DetachByGroups(TechnoClass* pTarget, AEAttachInfoTypeClass const& attachEffectInfo); + static int Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker, AbstractClass* pSource, AEAttachInfoTypeClass* const attachEffectInfo); + static int Detach(TechnoClass* pTarget, AEAttachInfoTypeClass* const attachEffectInfo); + static int DetachByGroups(TechnoClass* pTarget, AEAttachInfoTypeClass* const attachEffectInfo); static void TransferAttachedEffects(TechnoClass* pSource, TechnoClass* pTarget); private: @@ -49,7 +49,7 @@ class AttachEffectClass static AttachEffectClass* CreateAndAttach(AttachEffectTypeClass* pType, TechnoClass* pTarget, std::vector>& targetAEs, HouseClass* pInvokerHouse, TechnoClass* pInvoker, AbstractClass* pSource, AEAttachParams const& attachInfo); - static int DetachTypes(TechnoClass* pTarget, AEAttachInfoTypeClass const& attachEffectInfo, std::vector const& types); + static int DetachTypes(TechnoClass* pTarget, AEAttachInfoTypeClass* const attachEffectInfo, std::vector const& types); static int RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass* pTarget, int minCount, int maxCount); template @@ -94,6 +94,7 @@ struct AttachEffectTechnoProperties bool HasTint; bool ReflectDamage; bool HasOnFireDiscardables; + bool HasRestrictedArmorMultipliers; AttachEffectTechnoProperties() : FirepowerMultiplier { 1.0 } @@ -107,5 +108,6 @@ struct AttachEffectTechnoProperties , HasTint { false } , ReflectDamage { false } , HasOnFireDiscardables { false } + , HasRestrictedArmorMultipliers { false } { } }; diff --git a/src/New/Entity/LaserTrailClass.h b/src/New/Entity/LaserTrailClass.h index f5d54f937e..0fb61bde96 100644 --- a/src/New/Entity/LaserTrailClass.h +++ b/src/New/Entity/LaserTrailClass.h @@ -1,12 +1,10 @@ #pragma once #include -#include #include #include -#include class LaserTrailClass { diff --git a/src/New/Type/Affiliated/PassengerDeletionTypeClass.cpp b/src/New/Type/Affiliated/PassengerDeletionTypeClass.cpp index 2762ca9f0a..810a67d98c 100644 --- a/src/New/Type/Affiliated/PassengerDeletionTypeClass.cpp +++ b/src/New/Type/Affiliated/PassengerDeletionTypeClass.cpp @@ -3,18 +3,6 @@ #include #include -std::pair PassengerDeletionTypeClass::CanParse(INI_EX exINI, const char* pSection) -{ - Nullable rate; - rate.Read(exINI, pSection, "PassengerDeletion.Rate"); - Nullable useCost; - useCost.Read(exINI, pSection, "PassengerDeletion.UseCostAsRate"); - - bool canParse = rate.Get(0) > 0 || useCost.Get(false); - bool shouldResetValue = rate.isset() && rate.Get() == 0 && !(useCost.isset() && useCost.Get()); - return std::make_pair(canParse, shouldResetValue); -} - PassengerDeletionTypeClass::PassengerDeletionTypeClass(TechnoTypeClass* pOwnerType) : OwnerType(pOwnerType) , Rate { 0 } diff --git a/src/New/Type/Affiliated/PassengerDeletionTypeClass.h b/src/New/Type/Affiliated/PassengerDeletionTypeClass.h index fe18b28913..5491ccc26b 100644 --- a/src/New/Type/Affiliated/PassengerDeletionTypeClass.h +++ b/src/New/Type/Affiliated/PassengerDeletionTypeClass.h @@ -34,8 +34,6 @@ class PassengerDeletionTypeClass bool Load(PhobosStreamReader& stm, bool registerForChange); bool Save(PhobosStreamWriter& stm) const; - static std::pair CanParse(INI_EX exINI, const char* pSection); - private: template diff --git a/src/New/Type/AttachEffectTypeClass.cpp b/src/New/Type/AttachEffectTypeClass.cpp index 8942355920..5a8b96d0f1 100644 --- a/src/New/Type/AttachEffectTypeClass.cpp +++ b/src/New/Type/AttachEffectTypeClass.cpp @@ -123,6 +123,8 @@ void AttachEffectTypeClass::LoadFromINI(CCINIClass* pINI) this->FirepowerMultiplier.Read(exINI, pSection, "FirepowerMultiplier"); this->ArmorMultiplier.Read(exINI, pSection, "ArmorMultiplier"); + this->ArmorMultiplier_AllowWarheads.Read(exINI, pSection, "ArmorMultiplier.AllowWarheads"); + this->ArmorMultiplier_DisallowWarheads.Read(exINI, pSection, "ArmorMultiplier.DisallowWarheads"); this->SpeedMultiplier.Read(exINI, pSection, "SpeedMultiplier"); this->ROFMultiplier.Read(exINI, pSection, "ROFMultiplier"); this->ROFMultiplier_ApplyOnCurrentTimer.Read(exINI, pSection, "ROFMultiplier.ApplyOnCurrentTimer"); @@ -184,6 +186,8 @@ void AttachEffectTypeClass::Serialize(T& Stm) .Process(this->Tint_VisibleToHouses) .Process(this->FirepowerMultiplier) .Process(this->ArmorMultiplier) + .Process(this->ArmorMultiplier_AllowWarheads) + .Process(this->ArmorMultiplier_DisallowWarheads) .Process(this->SpeedMultiplier) .Process(this->ROFMultiplier) .Process(this->ROFMultiplier_ApplyOnCurrentTimer) diff --git a/src/New/Type/AttachEffectTypeClass.h b/src/New/Type/AttachEffectTypeClass.h index ee1d2f709c..bb54e9b57f 100644 --- a/src/New/Type/AttachEffectTypeClass.h +++ b/src/New/Type/AttachEffectTypeClass.h @@ -64,6 +64,8 @@ class AttachEffectTypeClass final : public Enumerable Valueable Tint_VisibleToHouses; Valueable FirepowerMultiplier; Valueable ArmorMultiplier; + ValueableVector ArmorMultiplier_AllowWarheads; + ValueableVector ArmorMultiplier_DisallowWarheads; Valueable SpeedMultiplier; Valueable ROFMultiplier; Valueable ROFMultiplier_ApplyOnCurrentTimer; @@ -113,6 +115,8 @@ class AttachEffectTypeClass final : public Enumerable , Tint_VisibleToHouses { AffectedHouse::All } , FirepowerMultiplier { 1.0 } , ArmorMultiplier { 1.0 } + , ArmorMultiplier_AllowWarheads {} + , ArmorMultiplier_DisallowWarheads {} , SpeedMultiplier { 1.0 } , ROFMultiplier { 1.0 } , ROFMultiplier_ApplyOnCurrentTimer { true } @@ -201,7 +205,7 @@ class AEAttachInfoTypeClass ValueableVector DurationOverrides; ValueableVector Delays; ValueableVector InitialDelays; - NullableVector RecreationDelays; + ValueableVector RecreationDelays; void LoadFromINI(CCINIClass* pINI, const char* pSection); bool Load(PhobosStreamReader& stm, bool registerForChange); diff --git a/src/Phobos.h b/src/Phobos.h index 720f48e95a..9ba1a63879 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -4,8 +4,6 @@ #include -#define CAN_USE_ARES 1 - class CCINIClass; class AbstractClass; diff --git a/src/Utilities/AresAddressTable.cpp b/src/Utilities/AresAddressTable.cpp index d7093860ee..5c7ea1b2e7 100644 --- a/src/Utilities/AresAddressTable.cpp +++ b/src/Utilities/AresAddressTable.cpp @@ -1,24 +1,35 @@ #pragma once #include "AresFunctions.h" +#include "AresHelper.h" +#include "Patch.h" +#define NOTE_ARES_FUN(name,reladdr) AresFunctions::name = reinterpret_cast(AresHelper::AresBaseAddress + reladdr) -const AresHelper::AresVersionFunctionMap AresHelper::AresFunctionOffsets = +decltype(AresFunctions::ConvertTypeTo) AresFunctions::ConvertTypeTo = nullptr; +decltype(AresFunctions::SpawnSurvivors) AresFunctions::SpawnSurvivors = nullptr; + +void AresFunctions::InitAres3_0() { + NOTE_ARES_FUN(ConvertTypeTo, 0x043650); + if constexpr (AresFunctions::AresWasWrongAboutSpawnSurvivors) { - AresHelper::Version::Ares30, - { - { ARES_FUN(ConvertTypeTo), 0x043650 }, - { ARES_FUN(SpawnSurvivors), 0x0464C0 }, - { ARES_FUN(HasFactory), 0x0217C0 }, - { ARES_FUN(CanBeBuiltAt), 0x03E3B0 }, - } - }, + Patch::Apply_RAW(AresHelper::AresBaseAddress + 0x4C0EB, { 0x5C }); + Patch::Apply_RAW(AresHelper::AresBaseAddress + 0x48C69, { 0x30 }); + } + else + NOTE_ARES_FUN(SpawnSurvivors, 0x0464C0); +} + +void AresFunctions::InitAres3_0p1() +{ + NOTE_ARES_FUN(ConvertTypeTo, 0x044130); + if constexpr (AresFunctions::AresWasWrongAboutSpawnSurvivors) { - AresHelper::Version::Ares30p, - { - { ARES_FUN(ConvertTypeTo), 0x044130 }, - { ARES_FUN(SpawnSurvivors), 0x047030 }, - { ARES_FUN(HasFactory), 0x0217C0 }, - { ARES_FUN(CanBeBuiltAt), 0x03E3B0 }, - } - }, -}; + Patch::Apply_RAW(AresHelper::AresBaseAddress + 0x4CD4B, { 0x5C }); + Patch::Apply_RAW(AresHelper::AresBaseAddress + 0x498B9, { 0x30 }); + } + else + NOTE_ARES_FUN(SpawnSurvivors, 0x047030); +} + +#undef NOTE_ARES_FUN + diff --git a/src/Utilities/AresFunctions.h b/src/Utilities/AresFunctions.h index 583d734320..7aecf0bef5 100644 --- a/src/Utilities/AresFunctions.h +++ b/src/Utilities/AresFunctions.h @@ -1,53 +1,22 @@ #pragma once -#include "AresHelper.h" - -#include -#include -#include - class TechnoClass; class TechnoTypeClass; class FootClass; class HouseClass; class BuildingTypeClass; -class AresTechnoTypeExt -{ -public: - __declspec(noinline) bool __thiscall CanBeBuiltAt(BuildingTypeClass* pBuildingType) { return 0; } -}; - - class AresFunctions { public: -#define CALL_ARES(name, ...) std::invoke(reinterpret_cast(AresHelper::AresFunctionOffsetsFinal[ARES_FUN(name)]), __VA_ARGS__) -#define THIS_CALL_ARES(name, ...) std::invoke(*reinterpret_cast(AresHelper::AresFunctionOffsetsFinal[ARES_FUN_M(name)]), __VA_ARGS__) - - // ??? - static bool ConvertTypeTo(TechnoClass* pFoot, TechnoTypeClass* pConvertTo) - { - return CALL_ARES(ConvertTypeTo, pFoot, pConvertTo); - } - + static void InitAres3_0(); + static void InitAres3_0p1(); // TechnoExt - static void SpawnSurvivors(FootClass* const pThis, TechnoClass* const pKiller, const bool Select, const bool IgnoreDefenses) - { - CALL_ARES(SpawnSurvivors, pThis, pKiller, Select, IgnoreDefenses); - } - - // HouseExt - static int HasFactory(int buffer, HouseClass* pOwner, TechnoTypeClass* pType, bool skipAircraft, bool requirePower, bool checkCanBuild, bool unknown) - { - return CALL_ARES(HasFactory, buffer, pOwner, pType, skipAircraft, requirePower, checkCanBuild, unknown); - } + static bool(__stdcall* ConvertTypeTo)(TechnoClass* pFoot, TechnoTypeClass* pConvertTo); - // TechnoTypeExt - static bool CanBeBuiltAt(AresTechnoTypeExt* pExt, BuildingTypeClass* pBuildingType) - { - return THIS_CALL_ARES(&AresTechnoTypeExt::CanBeBuiltAt, pExt, pBuildingType); - } + static void(__stdcall* SpawnSurvivors)(FootClass* pThis, TechnoClass* pKiller, bool Select, bool IgnoreDefenses); +private: + static constexpr bool _maybe = false; -#undef CALL_ARES -#undef THIS_CALL_ARES + static constexpr bool AresWasWrongAboutSpawnSurvivors = _maybe; + static constexpr bool AresWasWrongAboutAduction = true; }; diff --git a/src/Utilities/AresHelper.cpp b/src/Utilities/AresHelper.cpp index 0b180588c6..409bd2226b 100644 --- a/src/Utilities/AresHelper.cpp +++ b/src/Utilities/AresHelper.cpp @@ -1,5 +1,5 @@ #include "AresHelper.h" - +#include "AresFunctions.h" #include #include #include @@ -15,7 +15,6 @@ uintptr_t AresHelper::AresBaseAddress = 0x0; HMODULE AresHelper::AresDllHmodule = nullptr; AresHelper::Version AresHelper::AresVersion = AresHelper::Version::Unknown; bool AresHelper::CanUseAres = false; -AresHelper::AresFunctionMap AresHelper::AresFunctionOffsetsFinal; const AresHelper::AresTimestampMap AresHelper::AresTimestampBytes = { @@ -96,22 +95,14 @@ void AresHelper::Init() { case Version::Ares30: Debug::LogDeferred("[Phobos] Detected Ares 3.0.\n"); + AresFunctions::InitAres3_0(); break; case Version::Ares30p: Debug::LogDeferred("[Phobos] Detected Ares 3.0p1.\n"); + AresFunctions::InitAres3_0p1(); break; default: Debug::LogDeferred("[Phobos] Detected a version of Ares that is not supported by Phobos. Disabling integration.\n"); break; } - - constexpr const wchar_t* ARES_DLL = L"Ares.dll"; - if (CanUseAres && GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, ARES_DLL, &AresDllHmodule)) - { - for (auto x : AresFunctionOffsets.at(AresVersion)) - if (x.second > 0) - AresFunctionOffsetsFinal[x.first] = AresBaseAddress + x.second; - else - AresFunctionOffsetsFinal[x.first] = 0; - } } diff --git a/src/Utilities/AresHelper.h b/src/Utilities/AresHelper.h index 7d7e28c125..07837b8765 100644 --- a/src/Utilities/AresHelper.h +++ b/src/Utilities/AresHelper.h @@ -4,10 +4,6 @@ #include #include -#define ARES_FUN(name) std::string(NAMEOF(AresFunctions::name)) -#define ARES_FUN_M(name) std::string(NAMEOF_MEMBER(name)) -#define IS_ARES_FUN_AVAILABLE(name) AresHelper::CanUseAres && (AresHelper::AresFunctionOffsetsFinal[ARES_FUN(name)] != 0) - class AresHelper { public: @@ -31,13 +27,6 @@ class AresHelper static uintptr_t AresBaseAddress; static uintptr_t PhobosBaseAddress; - typedef std::unordered_map AresFunctionMap; - typedef std::unordered_map AresVersionFunctionMap; - - // offsets of function addresses for each version - static const AresVersionFunctionMap AresFunctionOffsets; - // storage for absolute addresses of functions (module base + offset) - static AresFunctionMap AresFunctionOffsetsFinal; // numeric id of currently used version, zero-indexed, -1 is unknown or missing static Version AresVersion; // is Ares detected and version known? diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index a26669270e..a6d43d0eb8 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -344,19 +344,16 @@ class Container if constexpr (HasOffset) ResetExtensionPointer(key); - if (auto const val = new extension_type(key)) - { - val->EnsureConstanted(); + auto const val = new extension_type(key); - if constexpr (HasOffset) - SetExtensionPointer(key, val); + val->EnsureConstanted(); - this->Items.insert(key, val); + if constexpr (HasOffset) + SetExtensionPointer(key, val); - return val; - } + this->Items.insert(key, val); - return nullptr; + return val; } extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) diff --git a/src/Utilities/Helpers.Alex.h b/src/Utilities/Helpers.Alex.h index 9dc439a546..2cf70ad297 100644 --- a/src/Utilities/Helpers.Alex.h +++ b/src/Utilities/Helpers.Alex.h @@ -245,7 +245,7 @@ namespace Helpers // Starkku: Reimplemented using AircraftTrackerClass. if (includeInAir) { - auto const airTracker = &AircraftTrackerClass::Instance.get(); + auto const airTracker = &AircraftTrackerClass::Instance; airTracker->FillCurrentVector(MapClass::Instance->GetCellAt(coords), Game::F2I(spread)); for (auto pTechno = airTracker->Get(); pTechno; pTechno = airTracker->Get()) @@ -413,13 +413,13 @@ namespace Helpers } template - inline bool is_any_of(Value&& value, Option&& option) + constexpr bool is_any_of(Value&& value, Option&& option) { return value == option; } template - inline bool is_any_of(Value&& value, Option&& first_option, Options&&... other_options) + constexpr bool is_any_of(Value&& value, Option&& first_option, Options&&... other_options) { return value == first_option || is_any_of(std::forward(value), std::forward(other_options)...); }