Skip to content

Commit

Permalink
Spawn crates /w Warheads & minor crate improvements (#1227)
Browse files Browse the repository at this point in the history
* Crate improvements

* Add feature to spawn crates on Warhead impact

* Improve INI parsing code
  • Loading branch information
Starkku committed May 14, 2024
1 parent d700c3b commit e359f70
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 43 deletions.
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ This page lists all the individual contributions to the project by their author.
- Projectile return weapon
- Aircraft landing / docking direction
- `DeploysInto` cursor desync fix
- Minor crate logic improvements
- **Morton (MortonPL)**:
- `XDrawOffset` for animations
- Shield passthrough & absorption
Expand Down
1 change: 1 addition & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<ClCompile Include="src\Misc\Hooks.AssignHouses.cpp" />
<ClCompile Include="src\Misc\Hooks.Gamespeed.cpp" />
<ClCompile Include="src\Misc\Hooks.Ares.cpp" />
<ClCompile Include="src\Misc\Hooks.Crates.cpp" />
<ClCompile Include="src\Misc\Hooks.VeinholeMonster.cpp" />
<ClCompile Include="src\Misc\PhobosToolTip.cpp" />
<ClCompile Include="src\Misc\TextInput.cpp" />
Expand Down
21 changes: 16 additions & 5 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- `PowerUpN` building animations can now use `Powered` & `PoweredLight/Effect/Special` keys.
- Fixed a desync potentially caused by displaying of cursor over selected `DeploysInto` units.
- Skipped drawing rally point line when undeploying a factory.

## Fixes / interactions with other extensions

- All forms of type conversion (including Ares') now correctly update `OpenTopped` state of passengers in transport that is converted.
Expand Down Expand Up @@ -1158,15 +1158,26 @@ In `rulesmd.ini`:
RadialIndicatorVisibility=allies ; list of Affected House Enumeration (owner/self | allies/ally | enemies/enemy | all)
```

## Crate generation
## Crate improvements

The statistic distribution of the randomly generated crates is now more uniform within the visible map region by using an optimized sampling procedure.
- You can now limit the crates' spawn region to land only.
There are some improvements on goodie crate logic:
- The statistic distribution of the randomly generated crates is now more uniform within the visible map region by using an optimized sampling procedure.
- You can now limit the crates' spawn region to land only by setting `[CrateRules]` -> `CreateOnlyOnLand` to true.
- The limit of vehicles a player can own before unit crates start giving money instead can now be customized by setting `UnitCrateVehicleCap`. Negative numbers disable the cap entirely.
- `FreeMCV` setting is now actually respected and can be used to disable the forced unit selected from `[General]` -> `BaseUnit` that is given if player picks a crate and has enough credits but no existing buildings or `BaseUnit` vehicles.
- The previously hardcoded credits threshold that must be passed can also now be customized via `FreeMCV.CreditsThreshold`.
- It is possible to influence weighting of units given from crates (`CrateGoodie=true`) via `CrateGoodie.RerollChance`, which determines the chance that if this type of unit is rolled, it will reroll again for another type of unit.

In `rulesmd.ini`:
```ini
[CrateRules]
CrateOnlyOnLand=no ; boolean
CrateOnlyOnLand=false ; boolean
UnitCrateVehicleCap=50 ; integer
FreeMCV=true ; boolean
FreeMCV.CreditsThreshold=1500 ; integer

[SOMEVEHICLE] ; VehicleType
CrateGoodie.RerollChance=0.0 ; floating point value, percents or absolute (0.0-1.0)
```

## DropPod
Expand Down
14 changes: 14 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,20 @@ In `rulesmd.ini`:
BigGap=false ; boolean
```

### Spawn powerup crate

- Warheads can now spawn powerup crates of specified type(s) on their impact cells (if free, or nearby cells if occupied something other than a crate) akin to map trigger action 108 ('Create Crate').
- `SpawnsCrateN` where N is a number starting from 0, parsed until no key is found can be used to define the type of crate spawned.
- `SpawnsCrateN.Weight` is a number that determines relative weighting of spawning corresponding crate type vs. other listed ones (0 is no chance, higher means higher probability) defaulting to 1 if not defined.
- `SpawnsCrate.Type/Weight` is an alias for `SpawnsCrate0.Type/Weight` if latter is not set.

In `rulesmd.ini`:
```ini
[SOMEWARHEAD] ; Warhead
SpawnsCrate(N).Type= ; Powerup crate type enum (money|unit|healbase|cloak|explosion|napalm|squad|reveal|armor|speed|firepower|icbm|invulnerability|veteran|ionstorm|gas|tiberium|pod)
SpawnsCrate(N).Weight=1 ; integer
```

### Trigger specific NotHuman infantry Death anim sequence
- Warheads are now able to trigger specific `NotHuman=yes` infantry `Death` anim sequence using the corresponding tag. It's value represents sequences from `Die1` to `Die5`.

Expand Down
7 changes: 6 additions & 1 deletion docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ You can use the migration utility (can be found on [Phobos supplementaries repo]

### From vanilla

- `[CrateRules]` -> `FreeMCV` now controls whether or not player is forced to receive unit from `[General]` -> `BaseUnit` from goodie crate if they own no buildings or any existing `BaseUnit` vehicles and own more than `[CrateRules]` -> `FreeMCV.CreditsThreshold` (defaults to 1500) credits.- Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto`/`UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[SOMETECHNOTYPE]/[CombatDamage]->IronCurtain.KeptOnDeploy=no`.
- Translucent RLE SHPs will now be drawn using a more precise and performant algorithm that has no green tint and banding. Can be disabled with `rulesmd.ini->[General]->FixTransparencyBlitters=no`.
- Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto`/`UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[SOMETECHNOTYPE]/[CombatDamage]->IronCurtain.KeptOnDeploy=no`.
- The obsolete `[General] WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `WarpOut`.
- - The obsolete `[General] WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `WarpOut`.
- Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both default to `TiltsWhenCrushes`.

### From older Phobos versions
Expand Down Expand Up @@ -394,6 +395,10 @@ New:
- Toggleable height-based shadow scaling for voxel air units (by Trsdy & Starkku)
- User setting toggles for harvester counter & power delta indicator (by Starkku)
- Shrapnel weapon target filtering toggle (by Starkku)
- Restore functionality of `[CrateRules]` -> `FreeMCV` with customizable credits threshold (by Starkku)
- Allow customizing the number of vehicles required for unit crates to turn into money crates (by Starkku)
- Per-VehicleType reroll chance for `CrateGoodie=true` (by Starkku)
- Warheads spawning powerup crates (by Starkku)
Vanilla fixes:
- Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy)
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->IronCurtain_KillOrganicsWarhead.Read(exINI, GameStrings::CombatDamage, "IronCurtain.KillOrganicsWarhead");

this->CrateOnlyOnLand.Read(exINI, GameStrings::CrateRules, "CrateOnlyOnLand");
this->UnitCrateVehicleCap.Read(exINI, GameStrings::CrateRules, "UnitCrateVehicleCap");
this->FreeMCV_CreditsThreshold.Read(exINI, GameStrings::CrateRules, "FreeMCV.CreditsThreshold");

this->ROF_RandomDelay.Read(exINI, GameStrings::CombatDamage, "ROF.RandomDelay");

Expand Down Expand Up @@ -287,6 +289,8 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->DisplayIncome_AllowAI)
.Process(this->DisplayIncome_Houses)
.Process(this->CrateOnlyOnLand)
.Process(this->UnitCrateVehicleCap)
.Process(this->FreeMCV_CreditsThreshold)
.Process(this->RadialIndicatorVisibility)
.Process(this->DrawTurretShadow)
.Process(this->IsVoiceCreatedGlobal)
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class RulesExt
Valueable<float> ToolTip_Background_BlurSize;

Valueable<bool> CrateOnlyOnLand;
Valueable<int> UnitCrateVehicleCap;
Valueable<int> FreeMCV_CreditsThreshold;
Valueable<AffectedHouse> RadialIndicatorVisibility;
Valueable<bool> DrawTurretShadow;
ValueableIdx<ColorScheme> AnimRemapDefaultColorScheme;
Expand Down Expand Up @@ -185,6 +187,8 @@ class RulesExt
, DisplayIncome_AllowAI { true }
, DisplayIncome_Houses { AffectedHouse::All }
, CrateOnlyOnLand { false }
, UnitCrateVehicleCap { 50 }
, FreeMCV_CreditsThreshold { 1500 }
, RadialIndicatorVisibility { AffectedHouse::Allies }
, DrawTurretShadow { false }
, IsVoiceCreatedGlobal { false }
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->Convert_HumanToComputer.Read(exINI, pSection, "Convert.HumanToComputer");
this->Convert_ComputerToHuman.Read(exINI, pSection, "Convert.ComputerToHuman");

this->CrateGoodie_RerollChance.Read(exINI, pSection, "CrateGoodie.RerollChance");

// Ares 0.2
this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius");

Expand Down Expand Up @@ -605,6 +607,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->DroppodType)
.Process(this->Convert_HumanToComputer)
.Process(this->Convert_ComputerToHuman)

.Process(this->CrateGoodie_RerollChance)
;
}
void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ class TechnoTypeExt
Valueable<TechnoTypeClass*> Convert_HumanToComputer;
Valueable<TechnoTypeClass*> Convert_ComputerToHuman;

Valueable<double> CrateGoodie_RerollChance;

struct LaserTrailDataEntry
{
ValueableIdx<LaserTrailTypeClass> idxType;
Expand Down Expand Up @@ -377,6 +379,8 @@ class TechnoTypeExt
, DroppodType {}
, Convert_HumanToComputer { }
, Convert_ComputerToHuman { }

, CrateGoodie_RerollChance { 0.0 }
{ }

virtual ~ExtData() = default;
Expand Down
41 changes: 41 additions & 0 deletions src/Ext/WarheadType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,44 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
|| this->InflictLocomotor
|| this->RemoveInflictedLocomotor
);

char tempBuffer[32];
Nullable<Powerup> crateType;
Nullable<int> weight;

for (size_t i = 0; ; i++)
{
crateType.Reset();
weight.Reset();

_snprintf_s(tempBuffer, sizeof(tempBuffer), "SpawnsCrate%u.Type", i);
crateType.Read(exINI, pSection, tempBuffer);

if (i == 0 && !crateType.isset())
crateType.Read(exINI, pSection, "SpawnsCrate.Type");

if (!crateType.isset())
break;

if (this->SpawnsCrate_Types.size() < i)
this->SpawnsCrate_Types[i] = crateType;
else
this->SpawnsCrate_Types.push_back(crateType);

_snprintf_s(tempBuffer, sizeof(tempBuffer), "SpawnsCrate%u.Weight", i);
weight.Read(exINI, pSection, tempBuffer);

if (i == 0 && !weight.isset())
weight.Read(exINI, pSection, "SpawnsCrate.Weight");

if (!weight.isset())
weight = 1;

if (this->SpawnsCrate_Weights.size() < i)
this->SpawnsCrate_Weights[i] = weight;
else
this->SpawnsCrate_Weights.push_back(weight);
}
}

template <typename T>
Expand Down Expand Up @@ -316,6 +354,9 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm)
.Process(this->Shield_Respawn_Types)
.Process(this->Shield_SelfHealing_Types)

.Process(this->SpawnsCrate_Types)
.Process(this->SpawnsCrate_Weights)

.Process(this->NotHuman_DeathSequence)
.Process(this->LaunchSW)
.Process(this->LaunchSW_RealLaunch)
Expand Down
7 changes: 6 additions & 1 deletion src/Ext/WarheadType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class WarheadTypeExt
Valueable<int> Shield_SelfHealing_RestartInCombatDelay;
Valueable<bool> Shield_SelfHealing_RestartTimer;

std::vector<Powerup> SpawnsCrate_Types;
std::vector<int> SpawnsCrate_Weights;

ValueableVector<ShieldTypeClass*> Shield_AttachTypes;
ValueableVector<ShieldTypeClass*> Shield_RemoveTypes;
Valueable<bool> Shield_ReplaceOnly;
Expand Down Expand Up @@ -115,7 +118,6 @@ class WarheadTypeExt
Valueable<bool> InflictLocomotor;
Valueable<bool> RemoveInflictedLocomotor;


// Ares tags
// http://ares-developers.github.io/Ares-docs/new/warheads/general.html
Valueable<bool> AffectsEnemies;
Expand Down Expand Up @@ -204,6 +206,9 @@ class WarheadTypeExt
, Shield_Respawn_Types {}
, Shield_SelfHealing_Types {}

, SpawnsCrate_Types {}
, SpawnsCrate_Weights {}

, NotHuman_DeathSequence { -1 }
, LaunchSW {}
, LaunchSW_RealLaunch { true }
Expand Down
18 changes: 13 additions & 5 deletions src/Ext/WarheadType/Detonate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse,
}
}

if (this->SpawnsCrate_Types.size() > 0)
{
int index = GeneralUtils::ChooseOneWeighted(ScenarioClass::Instance->Random.RandomDouble(), &this->SpawnsCrate_Weights);

if (index < static_cast<int>(this->SpawnsCrate_Types.size()))
MapClass::Instance->PlacePowerupCrate(CellClass::Coord2Cell(coords), this->SpawnsCrate_Types.at(index));
}

for (const int swIdx : this->LaunchSW)
{
if (const auto pSuper = pHouse->Supers.GetItem(swIdx))
Expand Down Expand Up @@ -212,12 +220,12 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget)
if (pExt->Shield)
{
auto isShieldTypeEligible = [pExt](Iterator<ShieldTypeClass*> elements) -> bool
{
if (elements.size() > 0 && !elements.contains(pExt->Shield->GetType()))
return false;
{
if (elements.size() > 0 && !elements.contains(pExt->Shield->GetType()))
return false;

return true;
};
return true;
};

if (this->Shield_Break && pExt->Shield->IsActive() && isShieldTypeEligible(this->Shield_Break_Types.GetElements(this->Shield_AffectTypes)))
pExt->Shield->BreakShield(this->Shield_BreakAnim.Get(nullptr), this->Shield_BreakWeapon.Get(nullptr));
Expand Down
31 changes: 0 additions & 31 deletions src/Misc/Hooks.BugFixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,37 +569,6 @@ DEFINE_HOOK(0x70BCE6, TechnoClass_GetTargetCoords_BuildingFix, 0x6)
return 0;
}

DEFINE_HOOK(0x56BD8B, MapClass_PlaceRandomCrate_Sampling, 0x5)
{
enum { SpawnCrate = 0x56BE7B, SkipSpawn = 0x56BE91 };

int XP = 2 * MapClass::Instance->VisibleRect.X - MapClass::Instance->MapRect.Width
+ ScenarioClass::Instance->Random.RandomRanged(0, 2 * MapClass::Instance->VisibleRect.Width);
int YP = 2 * MapClass::Instance->VisibleRect.Y + MapClass::Instance->MapRect.Width
+ ScenarioClass::Instance->Random.RandomRanged(0, 2 * MapClass::Instance->VisibleRect.Height + 2);
CellStruct candidate { (short)((XP + YP) / 2),(short)((YP - XP) / 2) };

auto pCell = MapClass::Instance->TryGetCellAt(candidate);
if (!pCell)
return SkipSpawn;

if (!MapClass::Instance->IsWithinUsableArea(pCell, true))
return SkipSpawn;

bool isWater = pCell->LandType == LandType::Water;
if (isWater && RulesExt::Global()->CrateOnlyOnLand.Get())
return SkipSpawn;

REF_STACK(CellStruct, cell, STACK_OFFSET(0x28, -0x18));
cell = MapClass::Instance->NearByLocation(pCell->MapCoords,
isWater ? SpeedType::Float : SpeedType::Track,
-1, MovementZone::Normal, false, 1, 1, false, false, false, true, CellStruct::Empty, false, false);

R->EAX(&cell);

return SpawnCrate;
}

// Fixes C4=no amphibious infantry being killed in water if Chronoshifted/Paradropped there.
DEFINE_HOOK(0x51A996, InfantryClass_PerCellProcess_KillOnImpassable, 0x5)
{
Expand Down

0 comments on commit e359f70

Please sign in to comment.