Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ Credits
- **Otamaa (Fahroni, BoredEXE)** - help with CellSpread, ported and fixed custom RadType code, togglable ElectricBolt bolts, customizable Chrono Locomotor properties per TechnoClass, DebrisMaximums fixes, Anim-to-Unit, NotHuman anim sequences improvements, Customizable OpenTopped Properties, hooks for ScriptType Actions 92 & 93, ore stage threshold for `HideIfNoOre`, occupied building `MuzzleFlashX` bugfix,`EnemyUIName=` for other TechnoTypes, TerrainType `DestroyAnim` & `DestroySound`
- **E1 Elite** - TileSet 255 and above bridge repair fix
- **FS-21** - Dump Object Info enhancements, Powered.KillSpawns, Spawner.LimitRange, ScriptType Actions 71 to 113, MC deployer fixes, help with docs, Automatic Passenger Deletion, Fire SW At Location Trigger Action, Fire SW At Waypoint Trigger Action, Kill Object Automatically, Customize resource storage, Override Uncloaked Underwater attack behavior, AI Aircraft docks fix, Shared Ammo
- **AutoGavy** - interceptor logic, warhead critical damage system, Customize resource storage
- **AutoGavy** - interceptor logic, Warhead critical hit logic, Customize resource storage
- **ChrisLv_CN** - interceptor logic, LaserTrails, laser fixes, general assistance (work relicensed under [following permission](images/ChrisLv-relicense.png))
- **Xkein** - general assistance, YRpp edits
- **thomassneddon** - general assistance
- **Starkku** - Warhead shield penetration & breaking, strafing aircraft weapon customization, vehicle DeployFire fixes/improvements, stationary VehicleTypes, Burst logic improvements, TechnoType auto-firing weapons, Secondary weapon fallback customization, weapon target type filtering, AreaFire targeting customization, CreateUnit improvements, Attached animation & jumpjet unit layer customization, IsSimpleDeployer improvements, Shield modification warheads, Warhead decloaking toggle, Warp(In/Out)Weapon, Grinder improvements / additions, Attached animation position customization
- **Starkku** - Warhead shield penetration & breaking, strafing aircraft weapon customization, vehicle DeployFire fixes/improvements, stationary VehicleTypes, Burst logic improvements, TechnoType auto-firing weapons, Secondary weapon fallback customization, weapon target type filtering, AreaFire targeting customization, CreateUnit improvements, Attached animation & jumpjet unit layer customization, IsSimpleDeployer improvements, Shield modification warheads, Warhead decloaking toggle, Warp(In/Out)Weapon, Grinder improvements / additions, Attached animation position customization, Critical hit logic additions
- **SukaHati (Erzoid)** - Minimum interceptor guard range
- **Morton (MortonPL)** - XDrawOffset, Shield passthrough & absorption, building LimboDelivery, fix for Image in art rules, power delta counter
- **mevitar** - honorary shield tester *triple* award
Expand Down
38 changes: 27 additions & 11 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,20 +609,36 @@ In `rulesmd.ini`:
RemoveMindControl=no ; boolean
```

### Critical damage chance

- Warheads can now apply additional chance-based damage (known as "critical" damage) with the ability to customize chance, damage, affected targets, and animations of critical strike.

### Chance-based extra damage or Warhead detonation / 'critical hits'

- Warheads can now apply additional chance-based damage or Warhead detonation ('critical hits') with the ability to customize chance, damage, affected targets, affected target HP threshold and animations of critical hit.
- `Crit.Chance` determines chance for a critical hit to occur. By default this is checked once when the Warhead is detonated and every target that is susceptible to critical hits will be affected. If `Crit.ApplyChancePerTarget` is set, then whether or not the chance roll is successful is determined individually for each target.
- `Crit.ExtraDamage` determines the damage dealt by the critical hit. If `Crit.Warhead` is set, the damage is used to detonate the specified Warhead on each affected target, otherwise the damage is directly dealt based on current Warhead's `Verses` settings.
- `Crit.Affects` can be used to customize types of targets that this Warhead can deal critical hits against.
- `Crit.AffectsBelowPercent` can be used to set minimum percentage of their maximum `Strength` that targets must have left to be affected by a critical hit.
- `Crit.AnimList` can be used to set a list of animations used instead of Warhead's `AnimList` if Warhead deals a critical hit to even one target. If `Crit.AnimList.PickRandom` is set (defaults to `AnimList.PickRandom`) then the animation is chosen randomly from the list.
- `Crit.AnimOnAffectedTargets`, if set, makes the animation(s) from `Crit.AnimList` play on each affected target *in addition* to animation from Warhead's `AnimList` playing as normal instead of replacing `AnimList` animation.
- `ImmuneToCrit` can be set on TechnoTypes to make them immune to critical hits.

In `rulesmd.ini`:
```ini
[SOMEWARHEAD] ; Warhead
Crit.Chance=0.0 ; float, chance on [0.0-1.0] scale
Crit.ExtraDamage=0 ; integer, extra damage
Crit.Affects=all ; list of Affected Target Enumeration (none|land|water|empty|infantry|units|buildings|all)
Crit.AnimList= ; list of animations
[SOMEWARHEAD] ; Warhead
Crit.Chance=0.0 ; float, percents or absolute (0.0-1.0)
Crit.ApplyChancePerTarget=false ; boolean
Crit.ExtraDamage=0 ; integer
Crit.Warhead= ; Warhead
Crit.Affects=all ; list of Affected Target Enumeration (none|land|water|empty|infantry|units|buildings|all)
Crit.AffectBelowPercent=1.0 ; float, percents or absolute (0.0-1.0)
Crit.AnimList= ; list of animations
Crit.AnimList.PickRandom= ; boolean
Crit.AnimOnAffectedTargets=false ; boolean

[SOMETECHNO] ; TechnoType
ImmuneToCrit=no ; boolean
[SOMETECHNO] ; TechnoType
ImmuneToCrit=no ; boolean
```

```{warning}
If you set `Crit.Warhead` to the same Warhead it is defined on, or create a chain of Warheads with it that loops back to the first one there is a possibility for the game to get stuck in a loop and freeze or crash afterwards.
```

### Custom 'SplashList' on Warheads
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ New:
- New ways for self-killing objects under certaing cases (by FS-21)
- `ForceWeapon.Naval.Decloacked` for overriding uncloaked underwater attack behavior (by FS-21)
- Shared Ammo for transports to passengers (by FS-21)
- Additional critical hit logic customizations (by Starkku)

Vanilla fixes:
- Fixed laser drawing code to allow for thicker lasers in house color draw mode (by Kerbiter, ChrisLv_CN)
Expand Down
10 changes: 10 additions & 0 deletions src/Ext/BulletType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ double BulletTypeExt::GetAdjustedGravity(BulletTypeClass* pType)
return pType->Floater ? nGravity * 0.5 : nGravity;
}

BulletTypeClass* BulletTypeExt::GetDefaultBulletType()
{
BulletTypeClass* pType = BulletTypeClass::Find(NONE_STR);

if (pType)
return pType;

return GameCreate<BulletTypeClass>(NONE_STR);
}

// =============================
// load / save

Expand Down
1 change: 1 addition & 0 deletions src/Ext/BulletType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ class BulletTypeExt
static ExtContainer ExtMap;

static double GetAdjustedGravity(BulletTypeClass* pType);
static BulletTypeClass* GetDefaultBulletType();
};
43 changes: 43 additions & 0 deletions src/Ext/WarheadType/Body.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include "Body.h"

#include <BulletClass.h>
#include <HouseClass.h>

#include <Ext/BulletType/Body.h>

template<> const DWORD Extension<WarheadTypeClass>::Canary = 0x22222222;
WarheadTypeExt::ExtContainer WarheadTypeExt::ExtMap;

Expand All @@ -26,6 +29,36 @@ bool WarheadTypeExt::ExtData::CanTargetHouse(HouseClass* pHouse, TechnoClass* pT
return true;
}

void WarheadTypeExt::DetonateAt(WarheadTypeClass* pThis, ObjectClass* pTarget, TechnoClass* pOwner, int damage)
{
BulletTypeClass* pType = BulletTypeExt::GetDefaultBulletType();

if (BulletClass* pBullet = pType->CreateBullet(pTarget, pOwner,
damage, pThis, 0, false))
{
const CoordStruct& coords = pTarget->GetCoords();

pBullet->Limbo();
pBullet->SetLocation(coords);
pBullet->Explode(true);
pBullet->UnInit();
}
}

void WarheadTypeExt::DetonateAt(WarheadTypeClass* pThis, const CoordStruct& coords, TechnoClass* pOwner, int damage)
{
BulletTypeClass* pType = BulletTypeExt::GetDefaultBulletType();

if (BulletClass* pBullet = pType->CreateBullet(nullptr, pOwner,
damage, pThis, 0, false))
{
pBullet->Limbo();
pBullet->SetLocation(coords);
pBullet->Explode(true);
pBullet->UnInit();
}
}

// =============================
// load / save

Expand All @@ -52,9 +85,14 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)

// Crits
this->Crit_Chance.Read(exINI, pSection, "Crit.Chance");
this->Crit_ApplyChancePerTarget.Read(exINI, pSection, "Crit.ApplyChancePerTarget");
this->Crit_ExtraDamage.Read(exINI, pSection, "Crit.ExtraDamage");
this->Crit_Warhead.Read(exINI, pSection, "Crit.Warhead");
this->Crit_Affects.Read(exINI, pSection, "Crit.Affects");
this->Crit_AnimList.Read(exINI, pSection, "Crit.AnimList");
this->Crit_AnimList_PickRandom.Read(exINI, pSection, "Crit.AnimList.PickRandom");
this->Crit_AnimOnAffectedTargets.Read(exINI, pSection, "Crit.AnimOnAffectedTargets");
this->Crit_AffectBelowPercent.Read(exINI, pSection, "Crit.AffectBelowPercent");

this->MindControl_Anim.Read(exINI, pSection, "MindControl.Anim");

Expand Down Expand Up @@ -110,9 +148,14 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm)
.Process(this->DecloakDamagedTargets)

.Process(this->Crit_Chance)
.Process(this->Crit_ApplyChancePerTarget)
.Process(this->Crit_ExtraDamage)
.Process(this->Crit_Warhead)
.Process(this->Crit_Affects)
.Process(this->Crit_AnimList)
.Process(this->Crit_AnimList_PickRandom)
.Process(this->Crit_AnimOnAffectedTargets)
.Process(this->Crit_AffectBelowPercent)

.Process(this->MindControl_Anim)

Expand Down
17 changes: 16 additions & 1 deletion src/Ext/WarheadType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ class WarheadTypeExt
Valueable<bool> AnimList_PickRandom;
Valueable<bool> DecloakDamagedTargets;

Valueable<int> Crit_ExtraDamage;
Valueable<double> Crit_Chance;
Valueable<bool> Crit_ApplyChancePerTarget;
Valueable<int> Crit_ExtraDamage;
Nullable<WarheadTypeClass*> Crit_Warhead;
Valueable<AffectedTarget> Crit_Affects;
ValueableVector<AnimTypeClass*> Crit_AnimList;
Nullable<bool> Crit_AnimList_PickRandom;
Valueable<bool> Crit_AnimOnAffectedTargets;
Valueable<double> Crit_AffectBelowPercent;

Nullable<AnimTypeClass*> MindControl_Anim;

Expand All @@ -44,6 +49,7 @@ class WarheadTypeExt
Nullable<WeaponTypeClass*> Shield_BreakWeapon;

double RandomBuffer;
bool HasCrit;

Valueable<int> NotHuman_DeathSequence;

Expand Down Expand Up @@ -83,10 +89,16 @@ class WarheadTypeExt
, DecloakDamagedTargets { true }

, Crit_Chance { 0.0 }
, Crit_ApplyChancePerTarget { false }
, Crit_ExtraDamage { 0 }
, Crit_Warhead {}
, Crit_Affects { AffectedTarget::All }
, Crit_AnimList {}
, Crit_AnimList_PickRandom {}
, Crit_AnimOnAffectedTargets { false }
, Crit_AffectBelowPercent { 1.0 }
, RandomBuffer { 0.0 }
, HasCrit { false }

, MindControl_Anim {}

Expand Down Expand Up @@ -156,4 +168,7 @@ class WarheadTypeExt
static ExtContainer ExtMap;
static bool LoadGlobals(PhobosStreamReader& Stm);
static bool SaveGlobals(PhobosStreamWriter& Stm);

static void DetonateAt(WarheadTypeClass* pThis, ObjectClass* pTarget, TechnoClass* pOwner, int damage);
static void DetonateAt(WarheadTypeClass* pThis, const CoordStruct& coords, TechnoClass* pOwner, int damage);
};
29 changes: 25 additions & 4 deletions src/Ext/WarheadType/Detonate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse,
pHouse->TransactMoney(this->TransactMoney);
}

this->HasCrit = false;
this->RandomBuffer = ScenarioClass::Instance->Random.RandomDouble();

// List all Warheads here that respect CellSpread
Expand Down Expand Up @@ -180,8 +181,12 @@ void WarheadTypeExt::ExtData::ApplyRemoveDisguiseToInf(HouseClass* pHouse, Techn

void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner)
{
//auto& random = ScenarioClass::Instance->Random;
const double dice = this->RandomBuffer; //double(random.RandomRanged(1, 10)) / 10;
double dice;

if (this->Crit_ApplyChancePerTarget)
dice = ScenarioClass::Instance->Random.RandomDouble();
else
dice = this->RandomBuffer;

if (this->Crit_Chance < dice)
return;
Expand All @@ -190,6 +195,9 @@ void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget
{
if (pTypeExt->ImmuneToCrit)
return;

if (pTarget->GetHealthPercentage() > this->Crit_AffectBelowPercent)
return;
}

if (!EnumFunctions::IsCellEligible(pTarget->GetCell(), this->Crit_Affects))
Expand All @@ -198,7 +206,20 @@ void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget
if (!EnumFunctions::IsTechnoEligible(pTarget, this->Crit_Affects))
return;

auto Damage = this->Crit_ExtraDamage.Get();
this->HasCrit = true;

if (this->Crit_AnimOnAffectedTargets && this->Crit_AnimList.size())
{
int idx = this->OwnerObject()->EMEffect || this->Crit_AnimList_PickRandom.Get(this->AnimList_PickRandom) ?
ScenarioClass::Instance->Random.RandomRanged(0, this->Crit_AnimList.size() - 1) : 0;

GameCreate<AnimClass>(this->Crit_AnimList[idx], pTarget->Location);
}

auto damage = this->Crit_ExtraDamage.Get();

pTarget->ReceiveDamage(&Damage, 0, this->OwnerObject(), pOwner, false, false, pHouse);
if (this->Crit_Warhead.isset())
WarheadTypeExt::DetonateAt(this->Crit_Warhead.Get(), pTarget, pOwner, damage);
else
pTarget->ReceiveDamage(&damage, 0, this->OwnerObject(), pOwner, false, false, pHouse);
}
4 changes: 2 additions & 2 deletions src/Ext/WarheadType/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ DEFINE_HOOK(0x48A5B3, WarheadTypeClass_AnimList_CritAnim, 0x6)
GET(WarheadTypeClass* const, pThis, ESI);
auto pWHExt = WarheadTypeExt::ExtMap.Find(pThis);

if (pWHExt && !(pWHExt->Crit_Chance < pWHExt->RandomBuffer) && pWHExt->Crit_AnimList.size())
if (pWHExt && pWHExt->HasCrit && pWHExt->Crit_AnimList.size() && !pWHExt->Crit_AnimOnAffectedTargets)
{
GET(int, nDamage, ECX);
int idx = pThis->EMEffect || pWHExt->AnimList_PickRandom ?
int idx = pThis->EMEffect || pWHExt->Crit_AnimList_PickRandom.Get(pWHExt->AnimList_PickRandom) ?
ScenarioClass::Instance->Random.RandomRanged(0, pWHExt->Crit_AnimList.size() - 1) :
std::min(pWHExt->Crit_AnimList.size() * 25 - 1, (size_t)nDamage) / 25;
R->EAX(pWHExt->Crit_AnimList[idx]);
Expand Down
1 change: 0 additions & 1 deletion src/Ext/WeaponType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ class WeaponTypeExt

static int nOldCircumference;


static void DetonateAt(WeaponTypeClass* pThis, ObjectClass* pTarget, TechnoClass* pOwner);
static void DetonateAt(WeaponTypeClass* pThis, ObjectClass* pTarget, TechnoClass* pOwner, int damage);
static void DetonateAt(WeaponTypeClass* pThis, const CoordStruct& coords, TechnoClass* pOwner);
Expand Down