Skip to content
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ This page lists all the individual contributions to the project by their author.
- Fix a bug where passengers created by the InitialPayload logic or TeamType with `Full=true` would fail to fire when the transport unit with `OpenTopped=yes` moved to an area that the passengers' `MovementZone` cannot move into
- Fix a bug where game will crash after loading if a techno with `AlphaImage` converts to a type without it, or an anim with `AlphaImage` changes to a type without it through `Next`
- Fix a bug where updating the `OpenTopped` attribute during convert did not update the coordinates of passengers
- Aux technos of superweapon
- **Apollo** - Translucent SHP drawing patches
- **ststl**:
- Customizable `ShowTimer` priority of superweapons
Expand Down
9 changes: 9 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,15 @@ In `rulesmd.ini`:
TabIndex=1 ; integer
```

### Aux technos of superweapon

In `rulesmd.ini`:
```ini
[SOMESW] ; SuperWeaponType
SW.AuxTechnos= ; List of TechnoTypes
SW.NegTechnos= ; List of TechnoTypes
```

### EMPulse settings

- It is possible to customize which weapon a building with `EMPulseCannon=true` fires when an associated `Type=EMPulse` superweapon (**only** if `EMPulse.TargetSelf=false` or omitted) is fired by setting `EMPulse.WeaponIndex`.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ Fixes / interactions with other extensions:
- Fixed the issue that `BombSight` not being updated correctly in techno conversion (by TaranDahl)
- `EVA.Tag` already supports being set for specific countries, and `EVAIndex` is no longer reset after load game (by FlyStar)
- `DisableWeapons.Duration` now makes `Gattling=yes` rate tick down and stops the sounds from playing, no longer interferes with target acquisition and works together with Phobos' `OpenTopped.CheckTransportDisableWeapons` (by Starkku)
- Aux technos of superweapon (by NetsuNegi)
```

### 0.4.0.3
Expand Down
4 changes: 4 additions & 0 deletions docs/locale/zh_CN/LC_MESSAGES/CREDITS.po
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,10 @@ msgid ""
"not update the coordinates of passengers"
msgstr "修复了类型转换会更新 `OpenTopped` 属性却不更新乘客坐标的 Bug"

msgid ""
"Aux technos of superweapon"
msgstr "超级武器的辅助单位"

msgid "**Apollo** - Translucent SHP drawing patches"
msgstr "**Apollo** - 半透明 SHP 绘制补丁"

Expand Down
3 changes: 3 additions & 0 deletions docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,9 @@ msgid ""
" (vehicle tab)."
msgstr "有效值为:0(建筑栏)、1(防御栏)、2(步兵栏)、3(载具栏)。"

msgid "Aux technos of superweapon"
msgstr "超级武器的辅助单位"

msgid "EMPulse settings"
msgstr "EMPulse 设置"

Expand Down
4 changes: 4 additions & 0 deletions docs/locale/zh_CN/LC_MESSAGES/Whats-New.po
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,10 @@ msgstr ""
"现在 `DisableWeapons.Duration` 会使 `Gattling=yes` 的速率降低并停止播放音效,不再干扰目标获取并且可以和"
" Phobos 的 `OpenTopped.CheckTransportDisableWeapons` 共用(by Starkku)"

msgid ""
"Aux technos of superweapon (by NetsuNegi)"
msgstr "超级武器的辅助单位(by NetsuNegi)"

msgid "0.4.0.3"
msgstr "0.4.0.3"

Expand Down
8 changes: 8 additions & 0 deletions src/Ext/SWType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ void SWTypeExt::ExtData::Serialize(T& Stm)
.Process(this->SW_ManualFire)
.Process(this->SW_ShowCameo)
.Process(this->SW_Unstoppable)
.Process(this->SW_AllowPlayer)
.Process(this->SW_AllowAI)
.Process(this->SW_Inhibitors)
.Process(this->SW_AnyInhibitor)
.Process(this->SW_Designators)
Expand All @@ -39,6 +41,8 @@ void SWTypeExt::ExtData::Serialize(T& Stm)
.Process(this->SW_ForbiddenHouses)
.Process(this->SW_AuxBuildings)
.Process(this->SW_NegBuildings)
.Process(this->SW_AuxTechnos)
.Process(this->SW_NegTechnos)
.Process(this->SW_InitialReady)
.Process(this->SW_PostDependent)
.Process(this->SW_MaxCount)
Expand Down Expand Up @@ -116,6 +120,8 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->SW_ManualFire.Read(exINI, pSection, "SW.ManualFire");
this->SW_ShowCameo.Read(exINI, pSection, "SW.ShowCameo");
this->SW_Unstoppable.Read(exINI, pSection, "SW.Unstoppable");
this->SW_AllowPlayer.Read(exINI, pSection, "SW.AllowPlayer");
this->SW_AllowAI.Read(exINI, pSection, "SW.AllowAI");
this->SW_Inhibitors.Read(exINI, pSection, "SW.Inhibitors");
this->SW_AnyInhibitor.Read(exINI, pSection, "SW.AnyInhibitor");
this->SW_Designators.Read(exINI, pSection, "SW.Designators");
Expand All @@ -126,6 +132,8 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->SW_ForbiddenHouses = pINI->ReadHouseTypesList(pSection, "SW.ForbiddenHouses", this->SW_ForbiddenHouses);
this->SW_AuxBuildings.Read(exINI, pSection, "SW.AuxBuildings");
this->SW_NegBuildings.Read(exINI, pSection, "SW.NegBuildings");
this->SW_AuxTechnos.Read(exINI, pSection, "SW.AuxTechnos");
this->SW_NegTechnos.Read(exINI, pSection, "SW.NegTechnos");
this->SW_InitialReady.Read(exINI, pSection, "SW.InitialReady");
this->SW_PostDependent.Read(exINI, pSection, "SW.PostDependent");
this->SW_MaxCount.Read(exINI, pSection, "SW.MaxCount");
Expand Down
10 changes: 9 additions & 1 deletion src/Ext/SWType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class SWTypeExt
Valueable<bool> SW_ManualFire;
Valueable<bool> SW_ShowCameo;
Valueable<bool> SW_Unstoppable;
Valueable<bool> SW_AllowPlayer;
Valueable<bool> SW_AllowAI;
ValueableVector<TechnoTypeClass*> SW_Inhibitors;
Valueable<bool> SW_AnyInhibitor;
ValueableVector<TechnoTypeClass*> SW_Designators;
Expand All @@ -40,6 +42,8 @@ class SWTypeExt
DWORD SW_ForbiddenHouses;
ValueableVector<BuildingTypeClass*> SW_AuxBuildings;
ValueableVector<BuildingTypeClass*> SW_NegBuildings;
ValueableVector<TechnoTypeClass*> SW_AuxTechnos;
ValueableVector<TechnoTypeClass*> SW_NegTechnos;
Valueable<bool> SW_InitialReady;
ValueableIdx<SuperWeaponTypeClass> SW_PostDependent;
Valueable<int> SW_MaxCount;
Expand Down Expand Up @@ -119,6 +123,8 @@ class SWTypeExt
, SW_ManualFire { true }
, SW_ShowCameo { true }
, SW_Unstoppable { false }
, SW_AllowPlayer { true }
, SW_AllowAI { true }
, SW_Inhibitors {}
, SW_AnyInhibitor { false }
, SW_Designators { }
Expand All @@ -129,6 +135,8 @@ class SWTypeExt
, SW_ForbiddenHouses { 0u }
, SW_AuxBuildings {}
, SW_NegBuildings {}
, SW_AuxTechnos {}
, SW_NegTechnos {}
, SW_InitialReady { false }
, SW_PostDependent {}
, SW_MaxCount { -1 }
Expand Down Expand Up @@ -242,5 +250,5 @@ class SWTypeExt
static bool SaveGlobals(PhobosStreamWriter& Stm);

static bool Activate(SuperClass* pSuper, CellStruct cell, bool isPlayer);

static SuperClass* __stdcall IsSuperAvailable(int swIdx, HouseClass* pHouse);
};
74 changes: 49 additions & 25 deletions src/Ext/SWType/SWHelpers.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "Body.h"
#include <MessageListClass.h>

#include <Ext/House/Body.h>

// Universal handler of the rolls-weights system
std::vector<int> SWTypeExt::ExtData::WeightedRollsHandler(ValueableVector<float>* rolls, std::vector<ValueableVector<int>>* weights, size_t size)
{
Expand Down Expand Up @@ -170,45 +172,54 @@ std::pair<double, double> SWTypeExt::ExtData::GetLaunchSiteRange(BuildingClass*
bool SWTypeExt::ExtData::IsAvailable(HouseClass* pHouse) const
{
const auto pThis = this->OwnerObject();
const int shots = this->SW_Shots;

if (shots >= 0 && HouseExt::ExtMap.Find(pHouse)->SuperExts[pThis->ArrayIndex].ShotCount >= shots)
return false;

if (pHouse->IsControlledByHuman() ? (!this->SW_AllowPlayer) : (!this->SW_AllowAI))
return false;

auto IsTechnoPresent = [pHouse](TechnoTypeClass* pType)
{
const auto pBuildingType = abstract_cast<BuildingTypeClass*, true>(pType);

if (pBuildingType && (!BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.empty() || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding)))
return BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse) > 0;

return HouseExt::ExtMap.Find(pHouse)->CountOwnedPresentAndLimboed(pType) > 0;
};

// check whether the optional aux building exists
if (pThis->AuxBuilding)
{
if ((BuildingTypeExt::ExtMap.Find(pThis->AuxBuilding)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pThis->AuxBuilding->PowersUpBuilding))
&& BuildingTypeExt::GetUpgradesAmount(pThis->AuxBuilding, pHouse) <= 0)
Comment thread
Coronia marked this conversation as resolved.
return false;
else if (pHouse->CountOwnedAndPresent(pThis->AuxBuilding) <= 0)
return false;
}
if (pThis->AuxBuilding && !IsTechnoPresent(pThis->AuxBuilding))
return false;

// allow only certain houses, disallow forbidden houses
const auto OwnerBits = 1u << pHouse->Type->ArrayIndex;
const auto ownerBits = 1u << pHouse->Type->ArrayIndex;

if (!(this->SW_RequiredHouses & OwnerBits) || (this->SW_ForbiddenHouses & OwnerBits))
if (!(this->SW_RequiredHouses & ownerBits) || (this->SW_ForbiddenHouses & ownerBits))
return false;

// check that any aux building exist and no neg building
auto IsBuildingPresent = [pHouse](BuildingTypeClass* pType)
{
if (pType)
{
if (BuildingTypeExt::ExtMap.Find(pType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pType->PowersUpBuilding))
return BuildingTypeExt::GetUpgradesAmount(pType, pHouse) > 0;
Comment thread
Coronia marked this conversation as resolved.
else
return pHouse->CountOwnedAndPresent(pType) > 0;
}

return false;
};
const auto& auxBuildings = this->SW_AuxBuildings;

if (!auxBuildings.empty() && std::ranges::none_of(auxBuildings, IsTechnoPresent))
return false;

const auto& negBuildings = this->SW_NegBuildings;

if (std::ranges::any_of(negBuildings, IsTechnoPresent))
return false;

const auto& Aux = this->SW_AuxBuildings;
const auto& auxTechnos = this->SW_AuxTechnos;

if (!Aux.empty() && std::none_of(Aux.begin(), Aux.end(), IsBuildingPresent))
if (!auxTechnos.empty() && std::ranges::none_of(auxTechnos, IsTechnoPresent))
return false;

const auto& Neg = this->SW_NegBuildings;
const auto& negTechnos = this->SW_NegTechnos;

if (std::any_of(Neg.begin(), Neg.end(), IsBuildingPresent))
if (std::ranges::any_of(negTechnos, IsTechnoPresent))
return false;

return true;
Expand Down Expand Up @@ -309,3 +320,16 @@ void SWTypeExt::ExtData::PrintMessage(const CSFText& message, HouseClass* pFirer
// print the message
MessageListClass::Instance.PrintMessage(message, RulesClass::Instance->MessageDelay, color);
}

SuperClass* __stdcall SWTypeExt::IsSuperAvailable(int swIdx, HouseClass* pHouse)
{
if (const auto pSuper = pHouse->Supers.GetItemOrDefault(swIdx))
{
const auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type);

if (pExt->IsAvailable(pHouse))
return pSuper;
}

return nullptr;
}
3 changes: 3 additions & 0 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,9 @@ DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5)
{
GET(TechnoClass*, pItem, ECX);

if (pItem->AbstractFlags & AbstractFlags::Foot)
pItem->Owner->RecheckTechTree = true; // for SW.AuxTechons and SW.NegTechnos

TechnoExt::ExtMap.Remove(pItem);

return 0;
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Techno/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2)
pThis->TargetingTimer.Start(ScenarioClass::Instance->Random.RandomRanged(0, 15));
}

if (pThis->AbstractFlags & AbstractFlags::Foot)
pThis->Owner->RecheckTechTree = true; // for SW.AuxTechons and SW.NegTechnos

return 0;
}

Expand Down
14 changes: 14 additions & 0 deletions src/Misc/Hooks.Ares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <Ext/Building/Body.h>
#include <Ext/Sidebar/Body.h>
#include <Ext/EBolt/Body.h>
#include <Ext/SWType/Body.h>

#include <New/Entity/Ares/RadarJammerClass.h>

Expand Down Expand Up @@ -68,6 +69,11 @@ static bool __fastcall CameoIsVeteran(TechnoTypeClass** pTypeExt_Ares, void*, Ho
return TechnoTypeExt::ExtMap.Find(*pTypeExt_Ares)->CameoIsVeteran(pHouse);
}

static bool __fastcall SW_IsAvailable(SuperWeaponTypeClass** pExt_Ares, void*, HouseClass* pHouse)
{
return SWTypeExt::ExtMap.Find(*pExt_Ares)->IsAvailable(pHouse);
}

namespace PermaMCTemp
{
bool Selected = false;
Expand Down Expand Up @@ -181,6 +187,10 @@ void Apply_Ares3_0_Patches()
// Redirect Ares's TechnoTypeExt::ExtData::CameoIsElite() to our implementation:
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x3D800, &CameoIsVeteran);

// Redirect Ares's SWTypeExt::ExtData::IsAvailable to our implementation:
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x32BE0, &SW_IsAvailable);
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x329E0, &SWTypeExt::IsSuperAvailable);

// Remove Ares check for houses for Psychedelic=yes Warheads.
Patch::Apply_RAW(AresHelper::AresBaseAddress + 0x4AAAA, { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 });

Expand Down Expand Up @@ -263,6 +273,10 @@ void Apply_Ares3_0p1_Patches()
// Redirect Ares's TechnoTypeExt::ExtData::CameoIsElite() to our implementation:
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x3E210, &CameoIsVeteran);

// Redirect Ares's SWTypeExt::ExtData::IsAvailable to our implementation:
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x335E0, &SW_IsAvailable);
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x333E0, &SWTypeExt::IsSuperAvailable);

// Remove Ares check for houses for Psychedelic=yes Warheads.
Patch::Apply_RAW(AresHelper::AresBaseAddress + 0x4B70A, { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 });

Expand Down
Loading