Skip to content

Commit

Permalink
random anims
Browse files Browse the repository at this point in the history
  • Loading branch information
Coronia committed Sep 16, 2024
1 parent 0455a77 commit 2b28804
Show file tree
Hide file tree
Showing 17 changed files with 97 additions and 44 deletions.
39 changes: 24 additions & 15 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,10 @@ IdleAnimDamaged.ConditionYellow= ; Animation
IdleAnimDamaged.ConditionRed= ; Animation
IdleAnim.OfflineAction=Hides ; AttachedAnimFlag (None, Hides, Temporal, Paused or PausedTemporal)
IdleAnim.TemporalAction=Hides ; AttachedAnimFlag (None, Hides, Temporal, Paused or PausedTemporal)
BreakAnim= ; Animation
HitAnim= ; Animation
BreakAnim= ; list of Animation
BreakAnims= ; list of Animation, overrides BreakAnim
HitAnim= ; list of Animation
HitAnims= ; list of Animation, overrides HitAnim
HitFlash=false ; boolean
HitFlash.FixedSize= ; integer
HitFlash.Red=true ; boolean
Expand All @@ -315,8 +317,10 @@ ShieldType=SOMESHIELDTYPE ; ShieldType; none by default
[SOMEWARHEAD] ; WarheadType
Shield.Penetrate=false ; boolean
Shield.Break=false ; boolean
Shield.BreakAnim= ; Animation
Shield.HitAnim= ; Animation
Shield.BreakAnim= ; list of Animation
Shield.BreakAnims= ; list of Animation, overrides Shield.BreakAnim
Shield.HitAnim= ; list of Animation
Shield.HitAnims= ; list of Animation, overrides Shield.HitAnim
Shield.SkipHitAnim=false ; boolean
Shield.HitFlash=true ; boolean
Shield.BreakWeapon= ; WeaponType
Expand Down Expand Up @@ -372,8 +376,8 @@ Shield.InheritStateOnReplace=false ; boolean
- `Bouncer=true` and `IsMeteor=true` animations can exhibit irregular behaviour when used as `IdleAnim` and should be avoided.
- `IdleAnim.OfflineAction` indicates what happens to the animation when the shield is in a low power state.
- `IdleAnim.TemporalAction` indicates what happens to the animation when the shield is attacked by temporal weapons.
- `BreakAnim`, if set, will be played when the shield has been broken.
- `HitAnim`, if set, will be played when the shield is attacked, similar to `WeaponNullifyAnim` for Iron Curtain.
- `BreakAnim(s)`, if set, will be played when the shield has been broken. If more than one animation is listed, a random one is selected. If both are set, `BreakAnims` will override `BreakAnim`.
- `HitAnim(s)`, if set, will be played when the shield is attacked, similar to `WeaponNullifyAnim` for Iron Curtain. If more than one animation is listed, a random one is selected. If both are set, `HitAnims` will override `HitAnim`.
- `HitFlash`, if set to true, makes it so that a light flash is generated when the shield is attacked by a Warhead unless it has `Shield.HitFlash=false`. Size of the flash is determined by damage dealt, unless `HitFlash.FixedSize` is set to a number, in which case that value is used instead (range of values that produces visible effect are increments of 4 from 81 to 252, anything higher or below does not have effect). Color can be customized via `HitFlash.Red/Green/Blue`. If `HitFlash.Black` is set to true, the generated flash will be black regardless of other color settings.
- `BreakWeapon`, if set, will be fired at the TechnoType once the shield breaks.
- `AbsorbPercent` controls the percentage of damage that will be absorbed by the shield. Defaults to 1.0, meaning full damage absorption.
Expand All @@ -394,8 +398,8 @@ Shield.InheritStateOnReplace=false ; boolean
- Warheads have new options that interact with shields. Note that all of these that do not by their very nature require ability to target the shield (such as modifiers like `Shield.Break` or removing / attaching) still require Warhead `Verses` to affect the target unless `EffectsRequireVerses` is set to false on the Warhead.
- `Shield.Penetrate` allows the warhead ignore the shield and always deal full damage to the TechnoType itself. It also allows targeting the TechnoType as if shield doesn't exist.
- `Shield.Break` allows the warhead to always break shields of TechnoTypes. This is done before damage is dealt.
- `Shield.BreakAnim` will be displayed instead of ShieldType `BreakAnim` if the shield is broken by the Warhead, either through damage or `Shield.Break`.
- `Shield.HitAnim` will be displayed instead of ShieldType `HitAnim` if set when Warhead hits the shield.
- `Shield.BreakAnim(s)` will be displayed instead of ShieldType `BreakAnim(s)` if the shield is broken by the Warhead, either through damage or `Shield.Break`. If more than one animation is listed, a random one is selected. If both are set, `Shield.BreakAnims` will override `Shield.BreakAnim`.
- `Shield.HitAnim(s)` will be displayed instead of ShieldType `HitAnim(s)` if set when Warhead hits the shield. If more than one animation is listed, a random one is selected. If both are set, `Shield.HitAnims` will override `Shield.HitAnim`.
- If `Shield.SkipHitAnim` is set to true, no hit anim is shown when the Warhead damages the shield whatsoever.
- `Shield.BreakWeapon` will be fired instead of ShieldType `BreakWeapon` if the shield is broken by the Warhead, either through damage or `Shield.Break`.
- `Shield.AbsorbPercent` overrides the `AbsorbPercent` value set in the ShieldType that is being damaged.
Expand Down Expand Up @@ -431,7 +435,7 @@ Shield.InheritStateOnReplace=false ; boolean
- `CreateUnit.AlwaysSpawnOnGround`, if set to true, ensures the vehicle will be created on the cell at ground level even if animation is in air. If set to false, jumpjet units spawned on ground will take off automatically after being spawned regardless.
- `CreateUnit.SpawnParachutedInAir`, if set to true, makes it so that the vehicle is created with a parachute if it is spawned in air. Has no effect if `CreateUnit.AlwaysSpawnOnGround` is set to true.
- `CreateUnit.ConsiderPathfinding`, if set to true, will consider whether or not the cell where the animation is located is occupied by other objects or impassable to the vehicle being created and will attempt to find a nearby cell that is not. Otherwise the vehicle will be created at the animation's location despite these obstacles if possible.
- `CreateUnit.SpawnAnim` can be used to play another animation at created unit's location after it has appeared. This animation has same owner and invoker as the parent animation.
- `CreateUnit.SpawnAnim(s)` can be used to play another animation at created unit's location after it has appeared. This animation has same owner and invoker as the parent animation. If more than one animation is listed, a random one is selected. If both are set, `CreateUnit.SpawnAnims` will override `CreateUnit.SpawnAnim`.
- `CreateUnit.SpawnHeight` can be set to override the animation's height when determining where to spawn the created unit. Has no effect if `CreateUnit.AlwaysSpawnOnGround` is set to true.

In `artmd.ini`:
Expand All @@ -449,7 +453,8 @@ CreateUnit.InheritTurretFacings=false ; boolean
CreateUnit.AlwaysSpawnOnGround=false ; boolean
CreateUnit.SpawnParachutedInAir=false ; boolean
CreateUnit.ConsiderPathfinding=false ; boolean
CreateUnit.SpawnAnim= ; Animation
CreateUnit.SpawnAnim= ; list of Animation
CreateUnit.SpawnAnims= ; list of Animation, overrides CreateUnit.SpawnAnim
CreareUnit.SpawnHeight= ; integer, height in leptons
```

Expand Down Expand Up @@ -907,7 +912,7 @@ Spawner.AttackImmediately=false ; boolean
- `PassengerDeletion.SoylentMultiplier` is a direct multiplier applied to the refunded amount of credits.
- `PassengerDeletion.SoylentAllowedHouses` determines which houses passengers can belong to be eligible for refunding.
- `PassengerDeletion.DisplaySoylent` can be set to true to display the amount of credits refunded on the transport. `PassengerDeletion.DisplaySoylentToHouses` determines which houses can see this and `PassengerDeletion.DisplaySoylentOffset` can be used to adjust the display offset.
- `PassengerDeletion.ReportSound` and `PassengerDeletion.Anim` can be used to specify a sound and animation to play when a passenger is erased, respectively.
- `PassengerDeletion.ReportSound` and `PassengerDeletion.Anim(s)` can be used to specify a sound and animation to play when a passenger is erased, respectively. If more than one animation is listed, a random one is selected. If both are set, `PassengerDeletion.Anims` will override `PassengerDeletion.Anim`.

In `rulesmd.ini`:
```ini
Expand All @@ -926,7 +931,8 @@ PassengerDeletion.DisplaySoylent=false ; boolean
PassengerDeletion.DisplaySoylentToHouses=All ; Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
PassengerDeletion.DisplaySoylentOffset=0,0 ; X,Y, pixels relative to default
PassengerDeletion.ReportSound= ; Sound
PassengerDeletion.Anim= ; Animation
PassengerDeletion.Anim= ; list of Animation
PassengerDeletion.Anims= ; list of Animation, overrides PassengerDeletion.Anim
```

### Automatic passenger owner change to match transport owner
Expand Down Expand Up @@ -1109,7 +1115,7 @@ Both `InitialStrength` and `InitialStrength.Cloning` never surpass the type's `S
- `vanish`: The object will be directly removed from the game peacefully instead of actually getting killed.
- `sell`: If the object is a **building** with buildup, it will be sold instead of destroyed.

If this option is not set, the self-destruction logic will not be enabled. `AutoDeath.VanishAnimation` can be set to animation to play at object's location if `vanish` behaviour is chosen.
If this option is not set, the self-destruction logic will not be enabled. `AutoDeath.VanishAnimation(s)` can be set to animation to play at object's location if `vanish` behaviour is chosen. If more than one animation is listed, a random one is selected. If both are set, `AutoDeath.VanishAnimations` will override `AutoDeath.VanishAnimation`.

```{note}
Please notice that if the object is a unit which carries passengers, they will not be released even with the `kill` option **if you are not using Ares 3.0+**.
Expand All @@ -1121,7 +1127,8 @@ In `rulesmd.ini`:
```ini
[SOMETECHNO] ; TechnoType
AutoDeath.Behavior= ; enumeration (kill | vanish | sell), default not set
AutoDeath.VanishAnimation ; Animation
AutoDeath.VanishAnimation= ; list of Animation
AutoDeath.VanishAnimations= ; list of Animation, overrides AutoDeath.VanishAnimation
AutoDeath.OnAmmoDepletion=no ; boolean
AutoDeath.AfterDelay=0 ; positive integer
AutoDeath.TechnosDontExist= ; list of TechnoType names
Expand Down Expand Up @@ -1279,11 +1286,13 @@ Convert.ComputerToHuman = ; TechnoType
### Destroy animation & sound

- You can now specify a destroy animation and sound for a TerrainType that are played when it is destroyed.
- If more than one animation is listed in `DestroyAnim(s)`, a random one is selected. If both are set, `DestroyAnims` will override `DestroyAnim`.

In `rulesmd.ini`:
```ini
[SOMETERRAINTYPE] ; TerrainType
DestroyAnim= ; Animation
DestroyAnim= ; list of Animation
DestroyAnims= ; list of Animation, overrides DestroyAnim
DestroySound= ; Sound
```

Expand Down
13 changes: 8 additions & 5 deletions src/Ext/Anim/Hooks.AnimCreateUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,17 @@ DEFINE_HOOK(0x424932, AnimClass_AI_CreateUnit_ActualAffects, 0x6)

if (success)
{
if (auto const pAnimType = pTypeExt->CreateUnit_SpawnAnim)
if (!pTypeExt->CreateUnit_SpawnAnim.empty())
{
if (auto const pAnim = GameCreate<AnimClass>(pAnimType, location))
if (auto const pAnimType = pTypeExt->CreateUnit_SpawnAnim[ScenarioClass::Instance->Random.RandomRanged(0, pTypeExt->CreateUnit_SpawnAnim.size() - 1)])
{
AnimExt::SetAnimOwnerHouseKind(pAnim, pInvokerHouse, nullptr, false, true);
if (auto const pAnim = GameCreate<AnimClass>(pAnimType, location))
{
AnimExt::SetAnimOwnerHouseKind(pAnim, pInvokerHouse, nullptr, false, true);

if (auto const pAnimExt = AnimExt::ExtMap.Find(pAnim))
pAnimExt->SetInvoker(pInvoker, pInvokerHouse);
if (auto const pAnimExt = AnimExt::ExtMap.Find(pAnim))
pAnimExt->SetInvoker(pInvoker, pInvokerHouse);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/Ext/AnimType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ void AnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI)
this->CreateUnit_SpawnParachutedInAir.Read(exINI, pID, "CreateUnit.SpawnParachutedInAir");
this->CreateUnit_ConsiderPathfinding.Read(exINI, pID, "CreateUnit.ConsiderPathfinding");
this->CreateUnit_SpawnAnim.Read(exINI, pID, "CreateUnit.SpawnAnim");
this->CreateUnit_SpawnAnim.Read(exINI, pID, "CreateUnit.SpawnAnims");
this->CreateUnit_SpawnHeight.Read(exINI, pID, "CreateUnit.SpawnHeight");
this->XDrawOffset.Read(exINI, pID, "XDrawOffset");
this->HideIfNoOre_Threshold.Read(exINI, pID, "HideIfNoOre.Threshold");
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/AnimType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class AnimTypeExt
Valueable<bool> CreateUnit_AlwaysSpawnOnGround;
Valueable<bool> CreateUnit_SpawnParachutedInAir;
Valueable<bool> CreateUnit_ConsiderPathfinding;
Valueable<AnimTypeClass*> CreateUnit_SpawnAnim;
ValueableVector<AnimTypeClass*> CreateUnit_SpawnAnim;
Nullable<int> CreateUnit_SpawnHeight;
Valueable<int> XDrawOffset;
Valueable<int> HideIfNoOre_Threshold;
Expand Down
18 changes: 13 additions & 5 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo)

// Self-destruction must be enabled
const auto howToDie = pTypeExt->AutoDeath_Behavior.Get();
const auto pVanishAnim = pTypeExt->AutoDeath_VanishAnimation;
AnimTypeClass* pVanishAnim = nullptr;

if (!pTypeExt->AutoDeath_VanishAnimation.empty())
pVanishAnim = pTypeExt->AutoDeath_VanishAnimation[ScenarioClass::Instance->Random.RandomRanged(0, pTypeExt->AutoDeath_VanishAnimation.size() - 1)];

// Death if no ammo
if (pType->Ammo > 0 && pThis->Ammo <= 0 && pTypeExt->AutoDeath_OnAmmoDepletion)
Expand Down Expand Up @@ -283,11 +286,16 @@ void TechnoExt::ExtData::EatPassengers()
if (pDelType->ReportSound >= 0)
VocClass::PlayAt(pDelType->ReportSound.Get(), pThis->GetCoords(), nullptr);

if (const auto pAnimType = pDelType->Anim.Get())
if (!pDelType->Anim.empty())
{
auto const pAnim = GameCreate<AnimClass>(pAnimType, pThis->Location);
pAnim->SetOwnerObject(pThis);
pAnim->Owner = pThis->Owner;
if (auto const pAnimType = pDelType->Anim[ScenarioClass::Instance->Random.RandomRanged(0, pDelType->Anim.size() - 1)])
{
if (auto const pAnim = GameCreate<AnimClass>(pAnimType, pThis->Location))
{
pAnim->SetOwnerObject(pThis);
pAnim->Owner = pThis->Owner;
}
}
}

// Check if there is money refund
Expand Down
1 change: 1 addition & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)

this->AutoDeath_Behavior.Read(exINI, pSection, "AutoDeath.Behavior");
this->AutoDeath_VanishAnimation.Read(exINI, pSection, "AutoDeath.VanishAnimation");
this->AutoDeath_VanishAnimation.Read(exINI, pSection, "AutoDeath.VanishAnimations");
this->AutoDeath_OnAmmoDepletion.Read(exINI, pSection, "AutoDeath.OnAmmoDepletion");
this->AutoDeath_AfterDelay.Read(exINI, pSection, "AutoDeath.AfterDelay");
this->AutoDeath_TechnosDontExist.Read(exINI, pSection, "AutoDeath.TechnosDontExist");
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class TechnoTypeExt
Valueable<int> Ammo_DeployUnlockMaximumAmount;

Nullable<AutoDeathBehavior> AutoDeath_Behavior;
Valueable<AnimTypeClass*> AutoDeath_VanishAnimation;
ValueableVector<AnimTypeClass*> AutoDeath_VanishAnimation;
Valueable<bool> AutoDeath_OnAmmoDepletion;
Valueable<int> AutoDeath_AfterDelay;
ValueableVector<TechnoTypeClass*> AutoDeath_TechnosDontExist;
Expand Down
9 changes: 7 additions & 2 deletions src/Ext/TerrainType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <TerrainClass.h>
#include <TerrainTypeClass.h>

#include <Ext/Scenario/Body.h>
#include <Utilities/GeneralUtils.h>

TerrainTypeExt::ExtContainer TerrainTypeExt::ExtMap;
Expand All @@ -23,8 +24,11 @@ void TerrainTypeExt::ExtData::PlayDestroyEffects(const CoordStruct& coords)
{
VocClass::PlayIndexAtPos(this->DestroySound, coords);

if (auto const pAnimType = this->DestroyAnim)
GameCreate<AnimClass>(pAnimType, coords);
if (!this->DestroyAnim.empty())
{
if (auto const pAnimType = this->DestroyAnim[ScenarioClass::Instance->Random.RandomRanged(0, this->DestroyAnim.size() - 1)])
GameCreate<AnimClass>(pAnimType, coords);
}
}

void TerrainTypeExt::Remove(TerrainClass* pTerrain)
Expand Down Expand Up @@ -78,6 +82,7 @@ void TerrainTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->SpawnsTiberium_CellsPerAnim.Read(exINI, pSection, "SpawnsTiberium.CellsPerAnim");

this->DestroyAnim.Read(exINI, pSection, "DestroyAnim");
this->DestroyAnim.Read(exINI, pSection, "DestroyAnims");
this->DestroySound.Read(exINI, pSection, "DestroySound");

this->MinimapColor.Read(exINI, pSection, "MinimapColor");
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/TerrainType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class TerrainTypeExt
Valueable<int> SpawnsTiberium_Range;
Valueable<PartialVector2D<int>> SpawnsTiberium_GrowthStage;
Valueable<PartialVector2D<int>> SpawnsTiberium_CellsPerAnim;
Valueable<AnimTypeClass*> DestroyAnim;
ValueableVector<AnimTypeClass*> DestroyAnim;
ValueableIdx<VocClass> DestroySound;
Nullable<ColorStruct> MinimapColor;
Valueable<bool> IsPassable;
Expand Down
2 changes: 2 additions & 0 deletions src/Ext/WarheadType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->Shield_Penetrate.Read(exINI, pSection, "Shield.Penetrate");
this->Shield_Break.Read(exINI, pSection, "Shield.Break");
this->Shield_BreakAnim.Read(exINI, pSection, "Shield.BreakAnim");
this->Shield_BreakAnim.Read(exINI, pSection, "Shield.BreakAnims");
this->Shield_HitAnim.Read(exINI, pSection, "Shield.HitAnim");
this->Shield_HitAnim.Read(exINI, pSection, "Shield.HitAnims");
this->Shield_SkipHitAnim.Read(exINI, pSection, "Shield.SkipHitAnim");
this->Shield_HitFlash.Read(exINI, pSection, "Shield.HitFlash");
this->Shield_BreakWeapon.Read<true>(exINI, pSection, "Shield.BreakWeapon");
Expand Down
4 changes: 2 additions & 2 deletions src/Ext/WarheadType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ class WarheadTypeExt

Valueable<bool> Shield_Penetrate;
Valueable<bool> Shield_Break;
Valueable<AnimTypeClass*> Shield_BreakAnim;
Valueable<AnimTypeClass*> Shield_HitAnim;
ValueableVector<AnimTypeClass*> Shield_BreakAnim;
ValueableVector<AnimTypeClass*> Shield_HitAnim;
Valueable<bool> Shield_SkipHitAnim;
Valueable<bool> Shield_HitFlash;
Nullable<WeaponTypeClass*> Shield_BreakWeapon;
Expand Down
9 changes: 8 additions & 1 deletion src/Ext/WarheadType/Detonate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,14 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget, TechnoE
};

if (this->Shield_Break && pTargetExt->Shield->IsActive() && isShieldTypeEligible(this->Shield_Break_Types.GetElements(this->Shield_AffectTypes)))
pTargetExt->Shield->BreakShield(this->Shield_BreakAnim, this->Shield_BreakWeapon);
{
AnimTypeClass* pAnimType = nullptr;

if (!this->Shield_BreakAnim.empty())
pAnimType = this->Shield_BreakAnim[ScenarioClass::Instance->Random.RandomRanged(0, this->Shield_BreakAnim.size() - 1)];

pTargetExt->Shield->BreakShield(pAnimType, this->Shield_BreakWeapon);
}

if (this->Shield_Respawn_Duration > 0 && isShieldTypeEligible(this->Shield_Respawn_Types.GetElements(this->Shield_AffectTypes)))
{
Expand Down
Loading

0 comments on commit 2b28804

Please sign in to comment.