Skip to content

[New feature]Tiberium eater logic #1619

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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 @@ -387,6 +387,7 @@ This page lists all the individual contributions to the project by their author.
- Fix the bug that buildings will always be tinted as airstrike owner
- Fix the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it
- Fix the bug that infantry ignored `Passengers` and `SizeLimit` when entering buildings
- Tiberium eater logic
- **Apollo** - Translucent SHP drawing patches
- **ststl**:
- Customizable `ShowTimer` priority of superweapons
Expand Down
2 changes: 2 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<ClCompile Include="src\Ext\Unit\Hooks.Sinking.cpp" />
<ClCompile Include="src\Misc\Hooks.AlphaImage.cpp" />
<ClCompile Include="src\New\Entity\AttachEffectClass.cpp" />
<ClCompile Include="src\New\Type\Affiliated\TiberiumEaterTypeClass.cpp" />
<ClCompile Include="src\New\Type\AttachEffectTypeClass.cpp" />
<ClCompile Include="src\Commands\Commands.cpp" />
<ClCompile Include="src\Commands\DamageDisplay.cpp" />
Expand Down Expand Up @@ -197,6 +198,7 @@
<ClInclude Include="src\Blowfish\blowfish.h" />
<ClInclude Include="src\Ext\Cell\Body.h" />
<ClInclude Include="src\New\Entity\AttachEffectClass.h" />
<ClInclude Include="src\New\Type\Affiliated\TiberiumEaterTypeClass.h" />
<ClInclude Include="src\New\Type\AttachEffectTypeClass.h" />
<ClInclude Include="src\Commands\FrameByFrame.h" />
<ClInclude Include="src\Commands\FrameStep.h" />
Expand Down
29 changes: 29 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,35 @@ IsVoiceCreatedGlobal=false ; boolean
VoiceCreated= ; Sound entry
```

### Tiberium eater

- TechnoTypes can convert the ores underneath them into credits in real time, like GDI's MARV in Command & Conquer 3 Kane's Wrath.
- `TiberiumEater.TransDelay` specifies the interval frames between two eating processes, 0 means eat in every frame. When it's below 0, the logic will be turned off for this TechnoType.
- `TiberiumEater.CellN` set a list of cells that'll process tiberium eating, where `N` is 0-based and the values are offset related to the TechnoType's current cell. If not set, only the ores on the TechnoType's current cell will be eaten.
- `TiberiumEater.AmountPerCell` controls the amount of ores that can be eaten at each cell at once. No limit when it's below 0.
- By default, ores mined in this way worth the same as regular harvesting. This can be adjusted by `TiberiumEater.CashMultiplier`.
- `TiberiumEater.Display`, if set to true, will create a flying text to display the total credits received in each eating process. `TiberiumEater.Display.Houses` determines which houses can see the credits display.
- An animation will be played at each mined cell in an eating process. If `TiberiumEater.Anims` contains 8 entries, entry from position matching the TechnoType's current facing will be chosen. Otherwise, an entry will be chosen randomly.
- `TiberiumEater.Anims.TiberiumN`, if set, will override `TiberiumEater.Anims` when eating corresponding tiberium type.
- If `TiberiumEater.AnimMove` set to true, the animations will move with the TechnoType.

In `rulesmd.ini`:
```ini
[SOMETECHNO] ; InfantryType, VehicleType or AircraftType
TiberiumEater.TransDelay=-1 ; integer
TiberiumEater.CellN= ; X,Y - cell offset
TiberiumEater.CashMultiplier=1.0 ; floating point value
TiberiumEater.AmountPerCell=0 ; integer
TiberiumEater.Display=true ; boolean
TiberiumEater.Display.Houses=all ; AffectedHouse enumeration
TiberiumEater.Anims= ; List of AnimationTypes
TiberiumEater.Anims.Tiberium0= ; List of AnimationTypes
TiberiumEater.Anims.Tiberium1= ; List of AnimationTypes
TiberiumEater.Anims.Tiberium2= ; List of AnimationTypes
TiberiumEater.Anims.Tiberium3= ; List of AnimationTypes
TiberiumEater.AnimMove=true ; boolean
```

### Weapons fired on warping in / out

- It is now possible to add weapons that are fired on a teleporting TechnoType when it warps in or out. They are at the same time as the appropriate animations (`WarpIn` / `WarpOut`) are displayed.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ New:
- [Make harvesters do addtional scan after unload](Fixed-or-Improved-Logics.md#make-harvesters-do-addtional-scan-after-unload) (by TaranDahl)
- [Passenger-based insignias](Fixed-or-Improved-Logics.md#customizable-veterancy-insignias) (by Ollerus)
- [Use `InsigniaType` to set the properties of insignia in a batch](Miscellanous.md#insignia-type) (by Ollerus)
- [Tiberium eater logic](New-or-Enhanced-Logics.md#tiberium-eater) (by NetsuNegi)
Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down
92 changes: 92 additions & 0 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <SpawnManagerClass.h>
#include <ParticleSystemClass.h>
#include <Conversions.h>

#include <Ext/Anim/Body.h>
#include <Ext/Bullet/Body.h>
Expand Down Expand Up @@ -338,6 +339,97 @@ void TechnoExt::ExtData::EatPassengers()
}
}

void TechnoExt::ExtData::UpdateTiberiumEater()
{
const auto pEaterType = this->TypeExtData->TiberiumEaterType.get();

if (!pEaterType)
return;

const int transDelay = pEaterType->TransDelay;

if (transDelay && this->TiberiumEater_Timer.InProgress())
return;

const auto pThis = this->OwnerObject();
const auto pOwner = pThis->Owner;
bool active = false;
const bool displayCash = pEaterType->Display && pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer);
int facing = pThis->PrimaryFacing.Current().GetFacing<8>();

if (facing >= 7)
facing = 0;
else
facing++;

const int cellCount = static_cast<int>(pEaterType->Cells.size());

for (int idx = 0; idx < cellCount; idx++)
{
const auto& cellOffset = pEaterType->Cells[idx];
const auto pos = TechnoExt::GetFLHAbsoluteCoords(pThis, CoordStruct { cellOffset.X, cellOffset.Y, 0 }, false);
const auto pCell = MapClass::Instance.TryGetCellAt(pos);

if (!pCell)
continue;

if (const int contained = pCell->GetContainedTiberiumValue())
{
const int tiberiumIdx = pCell->GetContainedTiberiumIndex();
const int tiberiumValue = TiberiumClass::Array[tiberiumIdx]->Value;
const int tiberiumAmount = static_cast<int>(static_cast<double>(contained) / tiberiumValue);
const int amount = pEaterType->AmountPerCell > 0 ? std::min(pEaterType->AmountPerCell.Get(), tiberiumAmount) : tiberiumAmount;
pCell->ReduceTiberium(amount);
const float multiplier = pEaterType->CashMultiplier * (1.0f + pOwner->NumOrePurifiers * RulesClass::Instance->PurifierBonus);
const int value = static_cast<int>(std::round(amount * tiberiumValue * multiplier));
pOwner->TransactMoney(value);
active = true;

if (displayCash)
{
auto cellCoords = pCell->GetCoords();
cellCoords.Z = std::max(pThis->Location.Z, cellCoords.Z);
FlyingStrings::AddMoneyString(value, pOwner, pEaterType->DisplayToHouse, cellCoords, pEaterType->DisplayOffset);
}

const auto& anims = pEaterType->Anims_Tiberiums[tiberiumIdx].GetElements(pEaterType->Anims);
const int animCount = static_cast<int>(anims.size());

if (animCount == 0)
continue;

AnimTypeClass* pAnimType = nullptr;

switch (animCount)
{
case 1:
pAnimType = anims[0];
break;

case 8:
pAnimType = anims[facing];
break;

default:
pAnimType = anims[ScenarioClass::Instance->Random.RandomRanged(0, animCount - 1)];
break;
}

if (pAnimType)
{
const auto pAnim = GameCreate<AnimClass>(pAnimType, pos);
AnimExt::SetAnimOwnerHouseKind(pAnim, pThis->Owner, nullptr, false, true);

if (pEaterType->AnimMove)
pAnim->SetOwnerObject(pThis);
}
}
}

if (active && transDelay)
this->TiberiumEater_Timer.Start(pEaterType->TransDelay);
}

void TechnoExt::ExtData::UpdateShield()
{
auto const pTypeExt = this->TypeExtData;
Expand Down
1 change: 1 addition & 0 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ void TechnoExt::ExtData::Serialize(T& Stm)
.Process(this->IsBeingChronoSphered)
.Process(this->KeepTargetOnMove)
.Process(this->LastSensorsMapCoords)
.Process(this->TiberiumEater_Timer)
.Process(this->AirstrikeTargetingMe)
;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class TechnoExt
bool IsBeingChronoSphered; // Set to true on units currently being ChronoSphered, does not apply to Ares-ChronoSphere'd buildings or Chrono reinforcements.
bool KeepTargetOnMove;
CellStruct LastSensorsMapCoords;
CDTimerClass TiberiumEater_Timer;

AirstrikeClass* AirstrikeTargetingMe;

Expand Down Expand Up @@ -112,6 +113,7 @@ class TechnoExt
, IsBeingChronoSphered { false }
, KeepTargetOnMove { false }
, LastSensorsMapCoords { CellStruct::Empty }
, TiberiumEater_Timer {}
, AirstrikeTargetingMe { nullptr }
{ }

Expand All @@ -121,6 +123,7 @@ class TechnoExt
bool CheckDeathConditions(bool isInLimbo = false);
void DepletedAmmoActions();
void EatPassengers();
void UpdateTiberiumEater();
void UpdateShield();
void UpdateOnTunnelEnter();
void UpdateOnTunnelExit();
Expand Down
1 change: 1 addition & 0 deletions src/Ext/Techno/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ DEFINE_HOOK(0x4DA54E, FootClass_AI, 0x6)
pExt->UpdateTypeData_Foot();

pExt->UpdateWarpInDelay();
pExt->UpdateTiberiumEater();

return 0;
}
Expand Down
16 changes: 16 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,20 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->PassengerDeletionType->LoadFromINI(pINI, pSection);
}

Nullable<int> transDelay;
transDelay.Read(exINI, pSection, "TiberiumEater.TransDelay");

if (transDelay.Get(-1) >= 0 && !this->TiberiumEaterType)
this->TiberiumEaterType = std::make_unique<TiberiumEaterTypeClass>();

if (this->TiberiumEaterType)
{
if (transDelay.isset() && transDelay.Get() < 0)
this->TiberiumEaterType.reset();
else
this->TiberiumEaterType->LoadFromINI(pINI, pSection);
}

Nullable<bool> isInterceptor;
isInterceptor.Read(exINI, pSection, "Interceptor");

Expand Down Expand Up @@ -996,6 +1010,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->LandingDir)
.Process(this->DroppodType)

.Process(this->TiberiumEaterType)

.Process(this->Convert_HumanToComputer)
.Process(this->Convert_ComputerToHuman)

Expand Down
3 changes: 3 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <New/Type/DigitalDisplayTypeClass.h>
#include <New/Type/SelectBoxTypeClass.h>
#include <New/Type/Affiliated/DroppodTypeClass.h>
#include <New/Type/Affiliated/TiberiumEaterTypeClass.h>

class Matrix3D;
class ParticleSystemTypeClass;
Expand Down Expand Up @@ -62,6 +63,7 @@ class TechnoTypeExt
Valueable<ShieldTypeClass*> ShieldType;
std::unique_ptr<PassengerDeletionTypeClass> PassengerDeletionType;
std::unique_ptr<DroppodTypeClass> DroppodType;
std::unique_ptr<TiberiumEaterTypeClass> TiberiumEaterType;

Nullable<float> HarvesterDumpAmount;

Expand Down Expand Up @@ -556,6 +558,7 @@ class TechnoTypeExt
, SpawnHeight {}
, LandingDir {}
, DroppodType {}
, TiberiumEaterType {}

, Convert_HumanToComputer { }
, Convert_ComputerToHuman { }
Expand Down
80 changes: 80 additions & 0 deletions src/New/Type/Affiliated/TiberiumEaterTypeClass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "TiberiumEaterTypeClass.h"

#include <Utilities/SavegameDef.h>
#include <Utilities/TemplateDef.h>

void TiberiumEaterTypeClass::LoadFromINI(CCINIClass* pINI, const char* pSection)
{
INI_EX exINI(pINI);
char tempBuffer[32];

this->TransDelay.Read(exINI, pSection, "TiberiumEater.TransDelay");
this->CashMultiplier.Read(exINI, pSection, "TiberiumEater.CashMultiplier");
this->AmountPerCell.Read(exINI, pSection, "TiberiumEater.AmountPerCell");

for (size_t idx = 0; ; ++idx)
{
Nullable<Vector2D<int>> cell;
_snprintf_s(tempBuffer, sizeof(tempBuffer), "TiberiumEater.Cell%d", idx);
cell.Read(exINI, pSection, tempBuffer);

if (idx >= this->Cells.size())
{
if (!cell.isset())
break;

this->Cells.emplace_back(cell.Get().X * Unsorted::LeptonsPerCell, cell.Get().Y * Unsorted::LeptonsPerCell);
}
else
{
if (!cell.isset())
continue;

this->Cells[idx] = Vector2D<int> { cell.Get().X * Unsorted::LeptonsPerCell, cell.Get().Y * Unsorted::LeptonsPerCell };
}
}

this->Display.Read(exINI, pSection, "TiberiumEater.Display");
this->DisplayToHouse.Read(exINI, pSection, "TiberiumEater.DisplayToHouse");
this->DisplayOffset.Read(exINI, pSection, "TiberiumEater.DisplayOffset");
this->Anims.Read(exINI, pSection, "TiberiumEater.Anims");

for (size_t idx = 0; idx < 4; ++idx)
{
_snprintf_s(tempBuffer, sizeof(tempBuffer), "TiberiumEater.Anims.Tiberium%d", idx);
this->Anims_Tiberiums[idx].Read(exINI, pSection, tempBuffer);
}

this->AnimMove.Read(exINI, pSection, "TiberiumEater.AnimMove");
}

template <class T>
bool TiberiumEaterTypeClass::Serialize(T& stm)
{
return stm
.Process(this->TransDelay)
.Process(this->CashMultiplier)
.Process(this->AmountPerCell)
.Process(this->Cells)
.Process(this->Display)
.Process(this->DisplayToHouse)
.Process(this->DisplayOffset)
.Process(this->Anims)
.Process(this->Anims_Tiberiums)
.Process(this->AnimMove)
.Success();
}

#pragma region(save/load)

bool TiberiumEaterTypeClass::Load(PhobosStreamReader& stm, bool registerForChange)
{
return this->Serialize(stm);
}

bool TiberiumEaterTypeClass::Save(PhobosStreamWriter& stm) const
{
return const_cast<TiberiumEaterTypeClass*>(this)->Serialize(stm);
}

#pragma endregion
31 changes: 31 additions & 0 deletions src/New/Type/Affiliated/TiberiumEaterTypeClass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <Utilities/Constructs.h>
#include <Utilities/Enum.h>
#include <Utilities/Template.h>

class TiberiumEaterTypeClass
{
public:
Valueable<int> TransDelay { -1 };
Valueable<float> CashMultiplier { 1.0 };
Valueable<int> AmountPerCell { 0 };
std::vector<Vector2D<int>> Cells { std::vector<Vector2D<int>>(1) };
Valueable<bool> Display { true };
Valueable<AffectedHouse> DisplayToHouse { AffectedHouse::All };
Valueable<Point2D> DisplayOffset { Point2D::Empty };
ValueableVector<AnimTypeClass*> Anims {};
NullableVector<AnimTypeClass*> Anims_Tiberiums[4] {};
Valueable<bool> AnimMove { true };

TiberiumEaterTypeClass() = default;

void LoadFromINI(CCINIClass* pINI, const char* pSection);
bool Load(PhobosStreamReader& stm, bool registerForChange);
bool Save(PhobosStreamWriter& stm) const;

private:

template <typename T>
bool Serialize(T& stm);
};