324 lines
13 KiB
Ucode
324 lines
13 KiB
Ucode
// Alternate Burning Mechanism
|
|
// Big thanx to Scary Ghost for inspiring me to do it this way
|
|
|
|
class ScrnBurnMech extends Info;
|
|
|
|
struct BurningMonster {
|
|
var KFMonster Victim;
|
|
var Pawn Instigator;
|
|
var int BurnDown;
|
|
var int BurnDamage;
|
|
var class<DamageType> FireDamageClass;
|
|
var float NextBurnTime;
|
|
var int BurnTicks; //how many ticks zed is already burning
|
|
var int FlareCount; //how many flares are burning inside a zed
|
|
var int TotalInputDamage; // total amount of incoming damage
|
|
};
|
|
var array<BurningMonster> Monsters;
|
|
|
|
var array<ScrnFlareCloud> FlareClouds;
|
|
|
|
var int BurnDuration; // tick count from ignition till the end of burning
|
|
var int BurnInCount; // tick count from ignition till reaching the maximum burn damage
|
|
var float BurnPeriod; // how often zeds will receive damage from burning
|
|
|
|
var(Sound) Sound FlareSound;
|
|
var string FlareSoundRef;
|
|
|
|
var bool bOutputDamage;
|
|
|
|
function PostBeginPlay()
|
|
{
|
|
FlareSound = sound(DynamicLoadObject(FlareSoundRef, class'Sound', true));
|
|
}
|
|
|
|
function int FindMonsterIndex(KFMonster Monster )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Monsters.Length; ++i ) {
|
|
if ( Monsters[i].Victim == Monster )
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
function int GetAvgBurnDamage(int BurnDown, int InitialDamage)
|
|
{
|
|
local float AvgTickInc;
|
|
|
|
// Fire damage is increasing by (3-4 points per tick) * 1.5. 10 ticks total. Average = sum / 2 = 18.
|
|
|
|
// Ignition takes 2 ticks, then average, constant damage is applied till the end of burning process
|
|
// Total DoT is weaker comparing to original game due to 2 less ticks and burn-in damage decrement
|
|
if ( class'ScrnBalance'.default.Mut.bHardcore || BurnDown >= BurnDuration )
|
|
AvgTickInc = 6;
|
|
else if ( BurnDown > (BurnDuration - BurnInCount) )
|
|
AvgTickInc = 12;
|
|
else
|
|
AvgTickInc = 18;
|
|
|
|
return InitialDamage + AvgTickInc;
|
|
}
|
|
|
|
function FlareMonster(int idx)
|
|
{
|
|
local ScrnPlayerController InstigatorController;
|
|
local ScrnPlayerController PC;
|
|
local Controller C;
|
|
|
|
Monsters[idx].Victim.AmbientSound = FlareSound;
|
|
|
|
if ( Monsters[idx].Instigator != none )
|
|
InstigatorController = ScrnPlayerController(Monsters[idx].Instigator.Controller);
|
|
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController ) {
|
|
PC = ScrnPlayerController(C);
|
|
if ( PC != none && (PC == InstigatorController || PC.LineOfSightTo(Monsters[idx].Victim)) ) {
|
|
PC.ClientFlareDamage(Monsters[idx].Victim, Monsters[idx].BurnDamage/4, Monsters[idx].BurnDown);
|
|
}
|
|
}
|
|
}
|
|
|
|
function StopFlareFX(KFMonster Monster)
|
|
{
|
|
if ( Monster.AmbientSound == FlareSound )
|
|
Monster.AmbientSound = none;
|
|
}
|
|
|
|
function StopBurningBehavior(KFMonster Monster)
|
|
{
|
|
Monster.SetTimer(0, false);
|
|
Monster.bSTUNNED = false; // maybe timer was set to turn it off?
|
|
Monster.bBurnified = false;
|
|
Monster.BurnDown = 0;
|
|
//if monster is raged don't slow him down
|
|
Monster.GroundSpeed = max(Monster.GroundSpeed, Monster.default.GroundSpeed);
|
|
|
|
Monster.UnSetBurningBehavior();
|
|
Monster.RemoveFlamingEffects();
|
|
Monster.StopBurnFX();
|
|
|
|
StopFlareFX(Monster);
|
|
}
|
|
|
|
function MakeBurnDamage(KFMonster Victim, int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum,
|
|
class<DamageType> DamType, optional int HitIndex)
|
|
{
|
|
local bool bIncDamage;
|
|
local int OldHealth;
|
|
local int i, FreeSlot;
|
|
local class<ScrnDamTypeFlare> FlareDamType;
|
|
local float AdjustedFlareCount;
|
|
|
|
// ScrnDamTypeHuskGun_Alt required for achievement
|
|
bIncDamage = ClassIsChildOf(DamType, class'DamTypeMAC10MPInc') || DamType == class'ScrnBalanceSrv.ScrnDamTypeHuskGun_Alt';
|
|
FlareDamType = class<ScrnDamTypeFlare>(DamType);
|
|
|
|
// check if zed is already burning
|
|
FreeSlot = Monsters.Length;
|
|
for( i = 0; i < Monsters.Length; ++i ) {
|
|
if ( Monsters[i].Victim == none ) {
|
|
if ( FreeSlot == Monsters.Length )
|
|
FreeSlot = i;
|
|
}
|
|
else if ( Monsters[i].Victim == Victim
|
|
// Flare and regular burn damages occupy different slots
|
|
&& (FlareDamType == none) == (Monsters[i].FlareCount == 0) )
|
|
{
|
|
if ( FlareDamType != none ) {
|
|
// Flares make incremental damage, i.e. each next flare raises damage per tick
|
|
|
|
// Adjust flare count for this amount of damage.
|
|
// For instance, monster was hit by 4 small flares (15 damage each) and now hit by
|
|
// a big flare which causing 60 damage. Total flare count = 5, but if scaled by current damage,
|
|
// then adjusted flare count = 2.
|
|
AdjustedFlareCount = float(Monsters[i].TotalInputDamage) / Damage;
|
|
if ( bOutputDamage && InstigatedBy != none && PlayerController(InstigatedBy.Controller) != none )
|
|
PlayerController(InstigatedBy.Controller).ClientMessage(
|
|
"AdjustedFlareCount="$AdjustedFlareCount
|
|
@ "TotalInputDamage="$Monsters[i].TotalInputDamage
|
|
@ "Damage="$Damage
|
|
);
|
|
|
|
Monsters[i].BurnDown += FlareDamType.default.BurnTimeBoost;
|
|
Monsters[i].BurnDown = max(Monsters[i].BurnDown,
|
|
FlareDamType.default.MinBurnTime + AdjustedFlareCount * FlareDamType.default.BurnTimeInc);
|
|
Monsters[i].BurnDown = min(Monsters[i].BurnDown, FlareDamType.default.MaxBurnTime);
|
|
Monsters[i].BurnDamage += max(Damage * FlareDamType.default.iDoT_MinBoostRatio,
|
|
Damage / (1.0 + AdjustedFlareCount * FlareDamType.default.iDoT_FadeFactor));
|
|
Monsters[i].FlareCount++;
|
|
}
|
|
else if ( Damage >= Monsters[i].BurnDamage * 0.8 ) { //lower fire damage can't increase burn ticks, e.g. shooting with MAC10 after Husk gun
|
|
//received extra burn damage while still burning
|
|
Monsters[i].BurnDown = max(Monsters[i].BurnDown, BurnDuration - BurnInCount); //increase burn time to the maximum again (excluding 2 burn-in ticks)
|
|
Monsters[i].BurnDamage = max(Damage, Monsters[i].BurnDamage);
|
|
if ( bIncDamage )
|
|
Monsters[i].FireDamageClass = DamType;
|
|
else
|
|
Monsters[i].FireDamageClass = class'DamTypeFlamethrower';
|
|
}
|
|
Monsters[i].Instigator = instigatedBy;
|
|
Monsters[i].TotalInputDamage += Damage;
|
|
Victim.BurnDown = Monsters[i].BurnDown;
|
|
Victim.TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType, HitIndex);
|
|
if ( Victim.Health <= 0 )
|
|
StopBurningBehavior(Victim);
|
|
else if ( FlareDamType != none )
|
|
FlareMonster(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we've reached here, zed isn't burning yet.
|
|
// First we need to check if zed can be set on fire, otherwise don't apply burning mechanism
|
|
// on it (e.g. some Doom3 monsters are immune to fire).
|
|
|
|
if ( FlareDamType != none ) {
|
|
Victim.HeatAmount = 5; // just to be sure this gay feature don't block zed's ignition
|
|
}
|
|
OldHealth = Victim.Health;
|
|
//damage zed and check if it's burning
|
|
Victim.TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType, HitIndex);
|
|
if ( bOutputDamage && InstigatedBy != none && PlayerController(InstigatedBy.Controller) != none )
|
|
PlayerController(InstigatedBy.Controller).ClientMessage(
|
|
"Initial Damage: " $ Damage $ "/" $ String(OldHealth - Victim.Health)
|
|
@ " -> "@ Victim.MenuName);
|
|
//store burned damage in this variable. It is of Byte type, so can't write big values into it
|
|
//Victim.HeatAmount = min(127, Victim.HeatAmount + OldHealth - Victim.Health);
|
|
|
|
if ( !Victim.bBurnified && Victim.BurnDown == 0 )
|
|
return; // can't be set on fire
|
|
|
|
if ( Victim.Health <= 0 ) {
|
|
StopBurningBehavior(Victim);
|
|
return;
|
|
}
|
|
|
|
// if we reached here, zed is ignited and should burning - OUR WAY
|
|
|
|
// KFMonster.Timer() makes fire damage based on LastBurnDamage + 3 + rand(2)
|
|
// if player is lucky, monster will take another +1 damage per tick :)
|
|
Victim.SetTimer(0, false); //disable timer, cuz we'll be using our own
|
|
Victim.LastBurnDamage = -3;
|
|
Victim.BurnDown = BurnDuration;
|
|
|
|
//add new record
|
|
i = FreeSlot;
|
|
if ( i == Monsters.Length ) {
|
|
Monsters.insert( i, 1 );
|
|
}
|
|
Monsters[i].Victim = Victim;
|
|
Monsters[i].Instigator = InstigatedBy;
|
|
Monsters[i].NextBurnTime = Level.TimeSeconds + BurnPeriod;
|
|
Monsters[i].BurnTicks = 0;
|
|
Monsters[i].BurnDamage = Damage;
|
|
Monsters[i].TotalInputDamage = Damage;
|
|
if ( FlareDamType != none ) {
|
|
Monsters[i].FireDamageClass = FlareDamType;
|
|
Monsters[i].BurnDown = FlareDamType.default.MinBurnTime;
|
|
Monsters[i].FlareCount = 1;
|
|
FlareMonster(i);
|
|
}
|
|
else {
|
|
if ( bIncDamage )
|
|
Monsters[i].FireDamageClass = DamType;
|
|
else
|
|
Monsters[i].FireDamageClass = class'DamTypeFlamethrower';
|
|
Monsters[i].BurnDown = BurnDuration;
|
|
Monsters[i].FlareCount = 0;
|
|
}
|
|
|
|
if ( !bTimerLoop ) {
|
|
SetTimer(0.1, true);
|
|
}
|
|
}
|
|
|
|
|
|
function Timer()
|
|
{
|
|
local int i, EmptyTailBegin;
|
|
local int OldHealth;
|
|
local int Damage;
|
|
local KFMonster M;
|
|
|
|
if ( Monsters.Length == 0 ) {
|
|
SetTimer(0, false);
|
|
return;
|
|
}
|
|
|
|
EmptyTailBegin = Monsters.Length;
|
|
for ( i = 0; i < Monsters.Length; ++i ) {
|
|
if ( Monsters[i].Victim != none && Monsters[i].Victim.Health <= 0 ) {
|
|
StopBurningBehavior(Monsters[i].Victim);
|
|
Monsters[i].Victim = none;
|
|
}
|
|
|
|
if ( Monsters[i].Victim == none ) {
|
|
if ( EmptyTailBegin == Monsters.Length ) {
|
|
EmptyTailBegin = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
EmptyTailBegin = Monsters.Length;
|
|
M = Monsters[i].Victim;
|
|
M.SetTimer(0, false); // ensure that timer is disabled
|
|
if ( Monsters[i].NextBurnTime < Level.TimeSeconds ) {
|
|
M.bSTUNNED = false; // manualy remove flinching effect here, cuz we disabled the timer
|
|
M.BurnDown = max(Monsters[i].BurnDown, 2); //can't be less, or TakeFireDamage() will decrease it to 0 and turn off burning
|
|
|
|
if ( Monsters[i].FlareCount > 0 )
|
|
Damage = Monsters[i].BurnDamage; // flares don't use burn in
|
|
else
|
|
Damage = GetAvgBurnDamage(Monsters[i].BurnDown, Monsters[i].BurnDamage);
|
|
|
|
OldHealth = Monsters[i].Victim.Health;
|
|
M.TakeDamage(Damage*BurnPeriod, Monsters[i].Instigator, M.Location,
|
|
vect(0, 0, 0), Monsters[i].FireDamageClass);
|
|
M.LastBurnDamage = -3; //reset to default
|
|
|
|
if ( bOutputDamage && Monsters[i].Instigator != none && PlayerController(Monsters[i].Instigator.Controller) != none)
|
|
PlayerController(Monsters[i].Instigator.Controller).ClientMessage(
|
|
"Fire DoT: " $ String(Damage)$ "/" $ String(OldHealth - M.Health)
|
|
@ " -> "@ M.MenuName @ "("$String(i+1)$"/"$Monsters.Length$"). Burns = " $ String(Monsters[i].BurnDown)
|
|
);
|
|
|
|
Monsters[i].BurnDown--;
|
|
Monsters[i].BurnTicks++;
|
|
Monsters[i].NextBurnTime += BurnPeriod;
|
|
|
|
if ( Monsters[i].FlareCount == 0 && Monsters[i].BurnTicks * BurnPeriod > 10 - M.CrispUpThreshhold )
|
|
M.ZombieCrispUp(); // Melt em' :)
|
|
|
|
if ( M.Health <= 0 ) {
|
|
Monsters[i].Victim = none;
|
|
StopBurningBehavior(M);
|
|
}
|
|
else if ( Monsters[i].BurnDown <= 0 ) {
|
|
Monsters[i].Victim = none;
|
|
if ( FindMonsterIndex(M) == -1 ) {
|
|
StopBurningBehavior(M);
|
|
}
|
|
else if ( Monsters[i].FlareCount > 0 ) {
|
|
StopFlareFX(M); // flares burned down, but other burning continues
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( EmptyTailBegin != Monsters.Length ) {
|
|
// Remove empty records from the end of the monsters array.
|
|
// Shouldn't do it in the middle of array to prevent record shifts in memory.
|
|
Monsters.Length = EmptyTailBegin;
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
BurnDuration=8
|
|
BurnInCount=2
|
|
BurnPeriod=1.000000
|
|
FlareSoundRef="KF_IJC_HalloweenSnd.KF_FlarePistol_Projectile_Loop"
|
|
}
|