Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3392074
Initial commit
FS-21 May 12, 2025
54a141e
More code
FS-21 May 12, 2025
2398e31
Small additions
FS-21 May 14, 2025
c69bb45
- Now AI-controlled super weapons managed by Ares dll will obey the p…
FS-21 May 15, 2025
d440d4e
Documentation and bugfixes
FS-21 May 15, 2025
6f78cd0
small fix
FS-21 May 16, 2025
fb8f5f0
doc tweaks
FS-21 May 16, 2025
4a5d9dd
Merge remote-tracking branch 'origin/develop' into feature/batte-points
FS-21 May 16, 2025
b65fb81
tweak
FS-21 May 16, 2025
bc96d8f
renamed tag
FS-21 May 17, 2025
384ee39
bugfix
FS-21 May 17, 2025
a58c346
By mistake I interted the sign of the operations
FS-21 May 17, 2025
762cbd5
Merge remote-tracking branch 'origin/develop' into feature/batte-points
FS-21 May 18, 2025
b4edaaf
Merge remote-tracking branch 'origin/develop' into feature/batte-points
FS-21 May 18, 2025
655e2ec
Code improvements
FS-21 May 19, 2025
fd57aef
Code tweaks
FS-21 May 19, 2025
5c81621
Merge branch 'develop' into feature/batte-points
FS-21 May 23, 2025
e74c242
Merge branch 'develop' into feature/batte-points
FS-21 May 29, 2025
2837e63
Merge branch 'develop' into feature/batte-points
FS-21 May 30, 2025
44df416
Merge branch 'develop' into feature/batte-points
FS-21 Jun 7, 2025
db1560f
Merge branch 'develop' into feature/batte-points
FS-21 Jun 8, 2025
f448d09
Merge branch 'develop' into feature/batte-points
FS-21 Jul 5, 2025
0cba2bd
Merge branch 'develop' into feature/batte-points
FS-21 Aug 7, 2025
2c23dca
Merge branch 'develop' into feature/batte-points
FS-21 Sep 2, 2025
e325abc
Merge branch 'origin/develop' into feature/batte-points
FS-21 Aug 25, 2026
3882e73
rewrite
FS-21 Aug 26, 2026
0a0560c
Repair stuff & production
FS-21 Aug 26, 2026
6a356fb
more tags, money & power supported
FS-21 Aug 27, 2026
3b8c6c6
Horizontal bar improvements and Weeds & harvesters support
FS-21 Aug 27, 2026
b667e96
remove fallbacks
FS-21 Aug 27, 2026
817c439
Merge remote-tracking branch 'origin/develop' into feature/multi-reso…
FS-21 Aug 27, 2026
430f72a
missing docs
FS-21 Aug 27, 2026
a043bec
Merge remote-tracking branch 'origin/develop' into feature/multi-reso…
FS-21 Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ This page lists all the individual contributions to the project by their author.
- Warhead activation target health thresholds enhancements
- Event 606: AttachEffect is attaching to a Techno
- Linked superweapons
- Custom Resource Types system
- Unit & infantry auto-conversion on ammo change
- Restore the ScriptType action#24 `Play speech` from Tiberian Sun
- Modify ammo on impact
Expand Down
3 changes: 3 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@
<ClCompile Include="src\New\Type\InsigniaTypeClass.cpp" />
<ClCompile Include="src\New\Type\LaserTrailTypeClass.cpp" />
<ClCompile Include="src\New\Type\RadTypeClass.cpp" />
<ClCompile Include="src\New\Type\ResourceTypeClass.cpp" />
<ClCompile Include="src\New\Type\SelectBoxTypeClass.cpp" />
<ClCompile Include="src\New\Type\ShieldTypeClass.cpp" />
<!-- src\New\Type\Affiliated -->
Expand Down Expand Up @@ -313,6 +314,7 @@
<ClInclude Include="src\Commands\DeselectObject.h" />
<ClInclude Include="src\Commands\DeselectObject5.h" />
<ClInclude Include="src\Commands\FireTacticalSW.h" />

<ClInclude Include="src\Commands\FrameByFrame.h" />
<ClInclude Include="src\Commands\FrameStep.h" />
<ClInclude Include="src\Commands\NextIdleHarvester.h" />
Expand Down Expand Up @@ -414,6 +416,7 @@
<ClInclude Include="src\New\Type\InsigniaTypeClass.h" />
<ClInclude Include="src\New\Type\LaserTrailTypeClass.h" />
<ClInclude Include="src\New\Type\RadTypeClass.h" />
<ClInclude Include="src\New\Type\ResourceTypeClass.h" />
<ClInclude Include="src\New\Type\SelectBoxTypeClass.h" />
<ClInclude Include="src\New\Type\ShieldTypeClass.h" />
<!-- src\New\Type\Affiliated -->
Expand Down
33 changes: 33 additions & 0 deletions docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,24 @@ ID=ActionCount,[Action1],611,4,[Map Filename],0,0,0,0,A,[ActionX]
...
```

### `620-622` Modify Custom Resource

- Modifies the stored amount of a custom resource for a house.
- `620`: Sets the resource amount to a specific value.
- `621`: Adds the specified amount to the current resource amount.
- `622`: Subtracts the specified amount from the current resource amount.
- `HouseIndex` specifies the target house (`-1` or omitted uses the trigger owner / current player).

In `mycampaign.map`:
```ini
[Actions]
...
ID=ActionCount,[Action1],620,4,[ResourceTypeID],[Amount],[HouseIndex],0,0,A,[ActionX]
ID=ActionCount,[Action1],621,4,[ResourceTypeID],[Amount],[HouseIndex],0,0,A,[ActionX]
ID=ActionCount,[Action1],622,4,[ResourceTypeID],[Amount],[HouseIndex],0,0,A,[ActionX]
...
```

### `800-802` Display Banner

- Display a 'banner' at a fixed location that is relative to the screen.
Expand Down Expand Up @@ -1039,6 +1057,21 @@ ID=EventCount,...,606,2,0,[AttachEffectType],...
...
```

### `610-611` House Has Custom Resource Greater/Less Than

- Checks if the trigger owner house has more or less than the specified amount of a custom resource.
- `610`: Springs when the house owns strictly greater than the specified amount of the resource.
- `611`: Springs when the house owns strictly less than the specified amount of the resource.

In `mycampaign.map`:
```ini
[Events]
...
ID=EventCount,...,610,2,[Amount],[ResourceTypeID],...
ID=EventCount,...,611,2,[Amount],[ResourceTypeID],...
...
```

## Teams

### Adjust recruitable status on team member liberate
Expand Down
160 changes: 160 additions & 0 deletions docs/New-or-Enhanced-Logics.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ HideShakeEffects=false ; boolean
#### New:
- [Customized transport plane for teams](AI-Scripting-and-Mapping.md#customized-transport-plane-for-teams) (by FlyStar)
- [Modify ammo on impact](New-or-Enhanced-Logics.md#modify-ammo-on-impact) (by FS-21)
- [Custom Resource Types](New-or-Enhanced-Logics.md#custom-resource-types) (by FS-21)
- [Customize whether mind-controlled Insignificant technos can be auto-targeted](Fixed-or-Improved-Logics.md#customize-whether-mind-controlled-Insignificant-technos-can-be-auto-targeted) (by Noble_Fish)
- [AutoDeath based on player power status and player credits](New-or-Enhanced-Logics.md#kill-object-automatically) (by Flactine)
- [Roof production anim](New-or-Enhanced-Logics.md#roof-production-anim) (by Noble_Fish)
Expand Down
35 changes: 35 additions & 0 deletions scratch/disasm_credits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import capstone

with open(r'E:\Juegos\CnCReloaded - 2.7 - development\gamemd.exe', 'rb') as f:
exe_data = f.read()

# PE header parsing to find RVA mapping
pe_offset = int.from_bytes(exe_data[0x3C:0x40], 'little')
num_sections = int.from_bytes(exe_data[pe_offset+6:pe_offset+8], 'little')
opt_header_size = int.from_bytes(exe_data[pe_offset+20:pe_offset+22], 'little')
sec_table_offset = pe_offset + 24 + opt_header_size

sections = []
for i in range(num_sections):
sec = exe_data[sec_table_offset + i*40 : sec_table_offset + (i+1)*40]
name = sec[:8].strip(b'\x00').decode('latin1')
vsize = int.from_bytes(sec[8:12], 'little')
vaddr = int.from_bytes(sec[12:16], 'little')
rsize = int.from_bytes(sec[16:20], 'little')
rptr = int.from_bytes(sec[20:24], 'little')
sections.append((vaddr, vsize, rptr, rsize))

def va_to_offset(va):
rva = va - 0x400000
for vaddr, vsize, rptr, rsize in sections:
if vaddr <= rva < vaddr + vsize:
return rptr + (rva - vaddr)
return None

md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32)
for va in [0x4A24D0, 0x4A25D0]:
start_off = va_to_offset(va)
code = exe_data[start_off:start_off+25]
for insn in md.disasm(code, va):
print(f"0x{insn.address:X}: {insn.mnemonic} {insn.op_str}")
print()
130 changes: 120 additions & 10 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <BitFont.h>
#include <Misc/FlyingStrings.h>
#include <Utilities/AresHelper.h>
#include <Ext/House/Body.h>
#include <New/Type/ResourceTypeClass.h>

BuildingExt::ExtContainer BuildingExt::ExtMap;

Expand All @@ -11,7 +13,17 @@ BuildingExt::ExtContainer::~ExtContainer() = default;

void BuildingExt::DisplayIncomeString()
{
if (this->AccumulatedIncome)
bool hasResourceIncome = false;
for (float val : this->AccumulatedResources)
{
if (val >= 1.0f || val <= -1.0f)
{
hasResourceIncome = true;
break;
}
}

if (this->AccumulatedIncome != 0 || hasResourceIncome)
{
int const delay = this->GetTypeExtData()->DisplayIncome_Delay.Get(RulesExt::Global()->DisplayIncome_Delay.Get());
if (Unsorted::CurrentFrame % delay == 0)
Expand All @@ -22,15 +34,90 @@ void BuildingExt::DisplayIncomeString()
if ((RulesExt::Global()->DisplayIncome_AllowAI || pThis->Owner->IsControlledByHuman())
&& pTypeExt->DisplayIncome.Get(RulesExt::Global()->DisplayIncome))
{
FlyingStrings::AddMoneyString(
this->AccumulatedIncome,
pThis,
pThis->Owner,
pTypeExt->DisplayIncome_Houses.Get(RulesExt::Global()->DisplayIncome_Houses.Get()),
pThis->GetRenderCoords(),
pTypeExt->DisplayIncome_Offset
);
std::wstring combinedStr;
ColorStruct displayColor = ColorStruct { 0, 255, 0 };
bool hasCustomColor = false;

if (this->AccumulatedIncome != 0)
{
const bool isPositive = this->AccumulatedIncome > 0;
wchar_t moneyBuf[32];
swprintf_s(moneyBuf, L"%ls%ls%d", isPositive ? L"+" : L"-", Phobos::UI::CostLabel, std::abs(this->AccumulatedIncome));
combinedStr += moneyBuf;
if (!isPositive)
displayColor = ColorStruct { 255, 0, 0 };
}

for (size_t i = 0; i < this->AccumulatedResources.size(); ++i)
{
const int amount = static_cast<int>(this->AccumulatedResources[i]);
if (amount != 0 && i < ResourceTypeClass::Array.size())
{
if (const auto pResource = ResourceTypeClass::Array[i].get())
{
if (!combinedStr.empty())
combinedStr += L" ";

const bool isPositive = amount > 0;
wchar_t resBuf[32];
const wchar_t* label = pResource->Display_Label.Get();
const bool useSpace = pResource->Display_Label_UseSpace.Get();
if (label && *label)
{
if (pResource->Display_Label_InvertPosition.Get())
swprintf_s(resBuf, useSpace ? L"%ls%d %ls" : L"%ls%d%ls", isPositive ? L"+" : L"-", std::abs(amount), label);
else
swprintf_s(resBuf, useSpace ? L"%ls%ls %d" : L"%ls%ls%d", isPositive ? L"+" : L"-", label, std::abs(amount));
}
else
{
swprintf_s(resBuf, L"%ls%d", isPositive ? L"+" : L"-", std::abs(amount));
}
combinedStr += resBuf;

if (this->AccumulatedIncome == 0 && !hasCustomColor)
{
const ColorStruct resColor = pResource->Display_Color.Get();
if (resColor != ColorStruct { 0, 0, 0 })
{
displayColor = resColor;
hasCustomColor = true;
}
else if (!isPositive)
{
displayColor = ColorStruct { 255, 0, 0 };
}
}
}
}
}

if (!combinedStr.empty() && !MapClass::Instance.IsLocationShrouded(pThis->GetCoords()))
{
const auto displayHouses = pTypeExt->DisplayIncome_Houses.Get(RulesExt::Global()->DisplayIncome_Houses.Get());
if (displayHouses == AffectedHouse::All || EnumFunctions::CanTargetHouse(displayHouses, pThis->Owner, HouseClass::CurrentPlayer))
{
if (pThis->VisualCharacter(false, nullptr) != VisualType::Hidden)
{
Point2D offset = pTypeExt->DisplayIncome_Offset;
int width = 0, height = 0;
if (BitFont::Instance)
{
BitFont::Instance->GetTextDimension(combinedStr.c_str(), &width, &height, 120);
offset.X -= (width / 2);
}
FlyingStrings::Add(combinedStr.c_str(), pThis->GetCoords(), displayColor, offset);
}
}
}
}

for (size_t i = 0; i < this->AccumulatedResources.size(); ++i)
{
const int amount = static_cast<int>(this->AccumulatedResources[i]);
this->AccumulatedResources[i] -= static_cast<float>(amount);
}

this->AccumulatedIncome = 0;
}
}
Expand Down Expand Up @@ -270,6 +357,23 @@ bool BuildingExt::DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechn
pExt->AccumulatedIncome += refund;
pExt->GrindingWeapon_AccumulatedCredits += refund;

if (const auto pHouseExt = HouseExt::TryFetch(pBuilding->Owner))
{
const size_t resCount = ResourceTypeClass::Array.size();
for (size_t i = 0; i < resCount; ++i)
{
const int resRefund = TechnoExt::GetResourceRefund(pTechno, static_cast<int>(i), true);
if (resRefund > 0)
{
if (i >= pExt->AccumulatedResources.size())
pExt->AccumulatedResources.resize(i + 1, 0.0f);

pExt->AccumulatedResources[i] += static_cast<float>(resRefund);
pHouseExt->UpdateResourceAmount(static_cast<int>(i), resRefund);
}
}
}

if (pTypeExt->Grinding_Weapon
&& Unsorted::CurrentFrame >= pExt->GrindingWeapon_LastFiredFrame + pTypeExt->Grinding_Weapon->ROF
&& pExt->GrindingWeapon_AccumulatedCredits >= pTypeExt->Grinding_Weapon_RequiredCredits)
Expand Down Expand Up @@ -596,14 +700,15 @@ void BuildingExt::Serialize(T& Stm)
.Process(this->GrindingWeapon_AccumulatedCredits)
.Process(this->CurrentAirFactory)
.Process(this->AccumulatedIncome)
.Process(this->AccumulatedResources)
.Process(this->CurrentLaserWeaponIndex)
.Process(this->PoweredUpToLevel)
.Process(this->CurrentEMPulseSW)
//.Process(this->IsFiringNow) It is set and reset within a same function.
.Process(this->TurretAnimIdleFrame)
.Process(this->TurretAnimFiringFrame)
.Process(this->TurretAnimRateTick)
.Process(this->ConstructionStartFacing)
.Process(this->ConstructionStartFacing)
;
}

Expand Down Expand Up @@ -675,6 +780,11 @@ DEFINE_HOOK(0x43C0B6, BuildingClass_DTOR, 0x9)
{
GET(BuildingClass*, pItem, ESI);

if (const auto pExt = BuildingExt::TryFetch(pItem))
{
pExt->ApplyCollectorRegistration(false);
}

BuildingExt::ExtMap.Remove(pItem);

return 0;
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Building/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class BuildingExt final : public TechnoExt, public Detach::Listener<BuildingClas
int GrindingWeapon_AccumulatedCredits;
BuildingClass* CurrentAirFactory;
int AccumulatedIncome;
std::vector<float> AccumulatedResources;
std::optional<int> CurrentLaserWeaponIndex;
int PoweredUpToLevel; // Distinct from UpgradeLevel, and set to highest PowersUpToLevel out of applied upgrades regardless of how many are currently applied to this building.
SuperClass* CurrentEMPulseSW;
Expand All @@ -40,13 +41,15 @@ class BuildingExt final : public TechnoExt, public Detach::Listener<BuildingClas
, GrindingWeapon_AccumulatedCredits { 0 }
, CurrentAirFactory { nullptr }
, AccumulatedIncome { 0 }
, AccumulatedResources {}
, CurrentLaserWeaponIndex {}
, PoweredUpToLevel { 0 }
, CurrentEMPulseSW {}
, IsFiringNow { false }
, TurretAnimIdleFrame { 0 }
, TurretAnimFiringFrame { -1 }
, TurretAnimRateTick { 0 }
, ConstructionStartFacing { 0 }
, IsPlayingRoofProductionAnim { false }
{ }

Expand Down
Loading
Loading