486 lines
18 KiB
Ucode
486 lines
18 KiB
Ucode
// FreezeRules controls Zed freezing on server side.
|
|
// FreezeReplicationInfo is used to replciate freezing effects to clients.
|
|
|
|
class FreezeRules extends GameRules;
|
|
|
|
var ScrnGameRules ScrnGameRules;
|
|
|
|
var FreezeReplicationInfo FreezeRI;
|
|
var float FreezeTreshold; // % of HealthMax to be done to fully freeze zed
|
|
var float FrozenDamageResistance; // frozen zed gets less damage
|
|
var float FrozenDamageMult; // but it is possible to break the ice and instant kill zed
|
|
var float FrozenDamageMultHS; // damage that can do headshots does more damage to ice
|
|
var float FrozenDamageMultSG; // shotgun-specific damage mult
|
|
|
|
var class<Emitter> ShatteredIce;
|
|
var transient PlayerController LastShatteredBy; //last player who shattered zed
|
|
var transient float LastShaterTime;
|
|
var transient int InstantShatterCounter;
|
|
var transient float NextChainsawTime;
|
|
|
|
var bool bDosh;
|
|
|
|
struct SFrozen {
|
|
var KFMonster M;
|
|
var KFPlayerController LastFrozenBy;
|
|
var class<DamTypeFreezerBase> LastFrozenDamType;
|
|
var bool bFrozen, bWasFrozen;
|
|
var float CurrentFreeze;
|
|
var float FreezeTreshold; // when CurrentFreeze reaches this value, zed becomes completely frozen
|
|
// FoT: Freeze over Time
|
|
var float FoT;
|
|
var float FoT_Remaining;
|
|
var float WarmTime; // time after which zed should get warmed up
|
|
|
|
var int Damage; // damage done during freeze
|
|
var vector FocalPoint;
|
|
};
|
|
var transient array<SFrozen> Frozen;
|
|
|
|
|
|
final static function FreezeRules FindOrSpawnMe(GameInfo Game)
|
|
{
|
|
local GameRules GR;
|
|
local FreezeRules FR;
|
|
|
|
FR = FreezeRules(Game.GameRulesModifiers);
|
|
if ( FR == none ) {
|
|
|
|
for ( GR = Game.GameRulesModifiers; GR != none && FR == none; GR = GR.NextGameRules )
|
|
FR = FreezeRules(Game.GameRulesModifiers);
|
|
|
|
// Freeze rules not found. Spawn it now
|
|
if ( FR == none ) {
|
|
FR = Game.Spawn(Class'FreezeRules', Game);
|
|
if( Game.GameRulesModifiers==None )
|
|
Game.GameRulesModifiers = FR;
|
|
else
|
|
Game.GameRulesModifiers.AddGameRules(FR);
|
|
}
|
|
}
|
|
|
|
return FR;
|
|
}
|
|
|
|
|
|
function PostBeginPlay()
|
|
{
|
|
FreezeRI = spawn(class'FreezeReplicationInfo', self);
|
|
class'ZombieSirenCryo'.default.FreezeRules = self;
|
|
}
|
|
|
|
function AddGameRules(GameRules GR)
|
|
{
|
|
if ( GR.class != self.class ) //prevent adding same rules more than once
|
|
Super.AddGameRules(GR);
|
|
}
|
|
|
|
function int NetDamage( int OriginalDamage, int Damage, pawn injured, pawn instigatedBy,
|
|
vector HitLocation, out vector Momentum, class<DamageType> DamageType )
|
|
{
|
|
local class<KFWeaponDamageType> KFDamType;
|
|
local class<DamTypeFreezerBase> FreezeDT;
|
|
local KFPlayerController PC;
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
local class<KFVeterancyTypes> Perk;
|
|
local KFMonster ZedVictim;
|
|
local int idx;
|
|
|
|
KFDamType = class<KFWeaponDamageType>(damageType);
|
|
ZedVictim = KFMonster(injured);
|
|
if ( instigatedBy != none )
|
|
PC = KFPlayerController(instigatedBy.Controller);
|
|
if ( PC != none )
|
|
KFPRI = KFPlayerReplicationInfo(PC.PlayerReplicationInfo);
|
|
if ( KFPRI != none )
|
|
Perk = KFPRI.ClientVeteranSkill;
|
|
else
|
|
Perk = class'KFVeterancyTypes';
|
|
|
|
|
|
if ( KFDamType != none && ZedVictim != none && ZedVictim.Controller != none && ZedVictim.Health > 0 )
|
|
{
|
|
FreezeDT = class<DamTypeFreezerBase>(damageType);
|
|
if ( FreezeDT != none ) {
|
|
if ( ZedVictim.bBurnified ) {
|
|
ZedVictim.BurnDown /= 2;
|
|
if ( ZedVictim.BurnDown == 0 ) {
|
|
ZedVictim.bBurnified = false;
|
|
ZedVictim.Timer(); // stop burning behavior
|
|
}
|
|
}
|
|
|
|
if ( !ZedVictim.bBurnified ) {
|
|
idx = FrozenIndex(ZedVictim, true);
|
|
if ( KFPRI != none && KFPRI.ClientVeteranSkill != none )
|
|
Frozen[idx].CurrentFreeze += Damage * FreezeDT.default.FreezeRatio;
|
|
if ( Frozen[idx].bFrozen ) {
|
|
Frozen[idx].WarmTime = fmax(Frozen[idx].WarmTime, Level.TimeSeconds + 0.5);
|
|
// allow shattering for ice darts
|
|
if ( FreezeDT.default.bCheckForHeadShots ) {
|
|
Damage *= FrozenDamageResistance * FreezeDT.default.HeadShotDamageMult
|
|
* FreezeDT.default.ShatteringDamageMult;
|
|
Frozen[idx].Damage += Damage * FrozenDamageMultHS;
|
|
if ( ZedVictim.Health <= Damage + Frozen[idx].Damage ) {
|
|
Damage += Frozen[idx].Damage;
|
|
ShatterZed(ZedVictim, idx, instigatedBy.Controller, DamageType);
|
|
}
|
|
}
|
|
else
|
|
Damage = 0;
|
|
}
|
|
else {
|
|
Frozen[idx].LastFrozenBy = PC;
|
|
Frozen[idx].LastFrozenDamType = FreezeDT;
|
|
// Freeze over Time
|
|
if ( FreezeDT.default.FoT_Duration > 0 && (Frozen[idx].FoT_Remaining < 0.5
|
|
|| Frozen[idx].FoT < Damage * FreezeDT.default.FoT_Ratio) )
|
|
{
|
|
Frozen[idx].FoT_Remaining = FreezeDT.default.FoT_Duration;
|
|
Frozen[idx].FoT = Damage * FreezeDT.default.FoT_Ratio;
|
|
}
|
|
if ( Frozen[idx].CurrentFreeze >= Frozen[idx].FreezeTreshold ) {
|
|
// enough damage to freeze zed
|
|
FreezeZed(ZedVictim, idx);
|
|
Frozen[idx].bFrozen = true;
|
|
Frozen[idx].WarmTime = fmax(Frozen[idx].WarmTime, Level.TimeSeconds + 2.0);
|
|
}
|
|
else
|
|
Frozen[idx].WarmTime = fmax(Frozen[idx].WarmTime, Level.TimeSeconds + 1.0);
|
|
}
|
|
}
|
|
//debug
|
|
// Level.GetLocalPlayerController().ClientMessage(Frozen[idx].CurrentFreeze $" / "$ Frozen[idx].FreezeTreshold);
|
|
}
|
|
else {
|
|
idx = FrozenIndex(ZedVictim, false);
|
|
if ( idx != -1 ) {
|
|
if ( KFDamType.default.bDealBurningDamage ) {
|
|
// fire instantly unfreezes zed
|
|
if ( Frozen[idx].bFrozen )
|
|
UnfreezeZed(ZedVictim, idx);
|
|
Frozen.remove(idx, 1);
|
|
}
|
|
else if ( Frozen[idx].bFrozen ) {
|
|
if ( KFDamType.default.bCheckForHeadShots && class<DamTypeCrossbuzzsaw>(KFDamType) == none ) {
|
|
Damage *= KFDamType.default.HeadShotDamageMult;
|
|
if ( KFDamType.default.bIsPowerWeapon || KFDamType.default.bIsMeleeDamage ) {
|
|
Frozen[idx].Damage += Damage * FrozenDamageMultSG;
|
|
if ( ClassIsChildOf(KFDamType, class'DamTypeChainsaw')
|
|
&& Level.TimeSeconds > NextChainsawTime )
|
|
{
|
|
ProgressAchievement(PC, 'Freeze_Chainsaw', 1);
|
|
ProgressAchievement(Frozen[idx].LastFrozenBy, 'Freeze_Chainsaw', 1);
|
|
NextChainsawTime = Level.TimeSeconds + 1;
|
|
}
|
|
}
|
|
else
|
|
Frozen[idx].Damage += Damage * FrozenDamageMultHS;
|
|
}
|
|
else
|
|
Frozen[idx].Damage += Damage * FrozenDamageMult;
|
|
Damage *= FrozenDamageResistance;
|
|
if ( ZedVictim.Health <= Damage + Frozen[idx].Damage ) {
|
|
Damage += Frozen[idx].Damage;
|
|
ShatterZed(ZedVictim, idx, instigatedBy.Controller, DamageType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( NextGameRules != None )
|
|
return NextGameRules.NetDamage( OriginalDamage,Damage,injured,instigatedBy,HitLocation,Momentum,DamageType);
|
|
|
|
return Damage;
|
|
}
|
|
|
|
function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation)
|
|
{
|
|
local KFMonster M;
|
|
local PlayerController Zapper;
|
|
|
|
if ( (NextGameRules != None) && NextGameRules.PreventDeath(Killed,Killer, damageType,HitLocation) )
|
|
return true;
|
|
|
|
M = KFMonster(Killed);
|
|
if ( M != none && M.ZappedBy != none )
|
|
Zapper = PlayerController(M.ZappedBy.Controller);
|
|
// killing zapped zed gives perk progression to zapper
|
|
if ( Zapper != none && SRStatsBase(Zapper.SteamStatsAndAchievements) != none
|
|
&& SRStatsBase(Zapper.SteamStatsAndAchievements).Rep != none )
|
|
SRStatsBase(Zapper.SteamStatsAndAchievements).Rep.ProgressCustomValue(Class'HTecProg', min(1, M.default.ZapThreshold * 4.01));
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool IsFrozen(KFMonster M)
|
|
{
|
|
local int i;
|
|
|
|
i = FrozenIndex(M, false);
|
|
return i >= 0 && Frozen[i].bFrozen;
|
|
}
|
|
|
|
function int FrozenIndex(KFMonster M, bool bCreate)
|
|
{
|
|
local int i;
|
|
|
|
for ( i=0; i < Frozen.length; ++i )
|
|
if ( Frozen[i].M == M )
|
|
return i;
|
|
|
|
if ( bCreate ) {
|
|
Frozen.insert(i, 1);
|
|
Frozen[i].M = M;
|
|
Frozen[i].FreezeTreshold = M.HealthMax * FreezeTreshold;
|
|
Frozen[i].WarmTime = Level.TimeSeconds + 2.0;
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int GetFrozenCount()
|
|
{
|
|
local int i, c;
|
|
|
|
for ( i=0; i < Frozen.length; ++i )
|
|
if ( Frozen[i].bFrozen && Frozen[i].M != none && Frozen[i].M.Health > 0 )
|
|
++c;
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
|
|
function Tick(float DeltaTime)
|
|
{
|
|
local int i;
|
|
local bool bRemove;
|
|
local byte FrozenCount;
|
|
|
|
while ( i < Frozen.length ) {
|
|
bRemove = Frozen[i].M == none || Frozen[i].M.Health <= 0;
|
|
|
|
if ( !bRemove ) {
|
|
if ( Frozen[i].FoT_Remaining > 0 ) {
|
|
Frozen[i].CurrentFreeze += Frozen[i].FoT * DeltaTime;
|
|
Frozen[i].FoT_Remaining -= DeltaTime;
|
|
Frozen[i].WarmTime = fmax(Frozen[i].WarmTime, Level.TimeSeconds + 1.0);
|
|
if ( !Frozen[i].bFrozen && Frozen[i].CurrentFreeze >= Frozen[i].FreezeTreshold ) {
|
|
FreezeZed(Frozen[i].M, i);
|
|
Frozen[i].bFrozen = true;
|
|
}
|
|
}
|
|
else {
|
|
if ( Frozen[i].CurrentFreeze > 0 && Level.TimeSeconds >= Frozen[i].WarmTime ) {
|
|
Frozen[i].CurrentFreeze -= fmax(Frozen[i].CurrentFreeze * 0.2, 20.0) * DeltaTime;
|
|
if ( Frozen[i].bFrozen && Frozen[i].CurrentFreeze < Frozen[i].FreezeTreshold ) {
|
|
UnfreezeZed(Frozen[i].M, i);
|
|
Frozen[i].bFrozen = false;
|
|
Frozen[i].Damage = 0;
|
|
Frozen[i].CurrentFreeze *= 0.80;
|
|
}
|
|
if ( Frozen[i].CurrentFreeze <= 0 )
|
|
bRemove = true;
|
|
}
|
|
}
|
|
|
|
if ( Frozen[i].bFrozen ) {
|
|
FrozenCount++;
|
|
if ( Frozen[i].M.IsAnimating(0) || Frozen[i].M.IsAnimating(1) )
|
|
FreezeZed(Frozen[i].M, i);
|
|
else if ( Frozen[i].M.Controller != none ) {
|
|
// prevent rotating
|
|
Frozen[i].M.Controller.FocalPoint = Frozen[i].FocalPoint;
|
|
Frozen[i].M.Controller.Enemy = none;
|
|
Frozen[i].M.Controller.Focus = none;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bRemove )
|
|
Frozen.Remove(i, 1);
|
|
else
|
|
i++;
|
|
}
|
|
if ( FrozenCount != FreezeRI.FrozenCount ) {
|
|
FreezeRI.FrozenCount = FrozenCount;
|
|
FreezeRI.NetUpdateTime = Level.TimeSeconds - 1;
|
|
}
|
|
}
|
|
|
|
// call it only if zed isn't frozen yet
|
|
function FreezeZed(KFMonster M, int FrozenIndex)
|
|
{
|
|
if ( !Frozen[FrozenIndex].bWasFrozen ) {
|
|
Frozen[FrozenIndex].bWasFrozen = true;
|
|
if ( ZombieCrawler(M) != none && M.Physics == PHYS_Falling )
|
|
ProgressAchievement(Frozen[FrozenIndex].LastFrozenBy, 'Freeze_CR', 1);
|
|
else if ( ZombieFleshpound(M) != none )
|
|
ProgressAchievement(Frozen[FrozenIndex].LastFrozenBy, 'Freeze_FP', 1);
|
|
else if ( ZombieBoss(M) != none )
|
|
ProgressAchievement(Frozen[FrozenIndex].LastFrozenBy, 'Freeze_Pat', 1);
|
|
ProgressAchievement(Frozen[FrozenIndex].LastFrozenBy, 'Freeze_IceAge', 1);
|
|
}
|
|
if ( Frozen.length >= 20 && GetFrozenCount() >= 20 )
|
|
ScrnGameRules.ProgressAchievementForAllPlayers('Freeze_Festival', 1, true);
|
|
|
|
M.SetOverlayMaterial(FreezeRI.FrozenMaterial, 999, true);
|
|
Frozen[FrozenIndex].FocalPoint = M.Location + 512*vector(M.Rotation);
|
|
M.Velocity = M.PhysicsVolume.Gravity;
|
|
M.AnimAction = '';
|
|
M.bShotAnim = true;
|
|
M.bWaitForAnim = true;
|
|
M.Disable('AnimEnd');
|
|
class'FreezeReplicationInfo'.static.RemoveAnimations(M);
|
|
M.Acceleration = vect(0, 0, 0);
|
|
M.SetTimer(0, false);
|
|
M.StopAnimating();
|
|
|
|
if ( M.HeadRadius > 0 )
|
|
M.HeadRadius = -M.HeadRadius; // disable headshots
|
|
|
|
if ( M.Controller != none ) {
|
|
M.Controller.FocalPoint = Frozen[FrozenIndex].FocalPoint;
|
|
M.Controller.Enemy = none;
|
|
M.Controller.Focus = none;
|
|
if ( !M.Controller.IsInState('WaitForAnim') )
|
|
M.Controller.GoToState('WaitForAnim');
|
|
KFMonsterController(M.Controller).bUseFreezeHack = True;
|
|
}
|
|
}
|
|
|
|
function UnfreezeZed(KFMonster M, int FrozenIndex)
|
|
{
|
|
if ( M == none || M.Controller == none || M.Health <= 0 )
|
|
return;
|
|
|
|
M.SetOverlayMaterial(none, 0.1, true);
|
|
|
|
if ( M.HeadRadius < 0 )
|
|
M.HeadRadius = -M.HeadRadius; //enable headshots
|
|
M.bShotAnim = false;
|
|
M.bWaitForAnim = false;
|
|
M.Enable('AnimEnd');
|
|
class'FreezeReplicationInfo'.static.RestoreAnimations(M);
|
|
M.AnimEnd(1);
|
|
M.AnimEnd(0);
|
|
//KFMonsterController(M.Controller).WhatToDoNext(99);
|
|
M.Controller.GotoState('ZombieHunt');
|
|
M.GroundSpeed = M.GetOriginalGroundSpeed();
|
|
// this should "wake up" raged scrakes
|
|
M.TakeDamage(1, M, M.Location, vect(0,0,0), class'DamTypeFreezerBase');
|
|
}
|
|
|
|
function bool ShatterZed(KFMonster ZedVictim, int FrozenIdx, Controller Killer, class<DamageType> DamageType)
|
|
{
|
|
local vector loc;
|
|
local rotator r;
|
|
local FrozenDosh Dosh, PrevDosh, HeadDosh;
|
|
local int i;
|
|
local int MaxDosh;
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
local SRStatsBase FreezerStats;
|
|
|
|
if ( FrozenIdx < 0 )
|
|
FrozenIdx = FrozenIndex(ZedVictim, false);
|
|
if ( FrozenIdx < 0 )
|
|
return false; // zed is not frozen
|
|
|
|
if ( Killer != none )
|
|
KFPRI = KFPlayerReplicationInfo(Killer.PlayerReplicationInfo);
|
|
FreezerStats = SRStatsBase(Frozen[FrozenIdx].LastFrozenBy.SteamStatsAndAchievements);
|
|
loc = ZedVictim.Location;
|
|
|
|
if ( bDosh && !ZedVictim.bDecapitated && DamageType != class'SirenScreamDamage' ) {
|
|
if ( KFPRI != none && KFPRI.ClientVeteranSkill != none )
|
|
MaxDosh = KFPRI.ClientVeteranSkill.static.GetCostScaling(KFPRI, class'FrozenDosh')
|
|
* 0.2 * ZedVictim.ScoringValue;
|
|
else
|
|
MaxDosh = ZedVictim.ScoringValue / 5;
|
|
}
|
|
|
|
if ( LastShatteredBy == Killer && LastShaterTime == Level.TimeSeconds ) {
|
|
if ( ++InstantShatterCounter == 10 ) {
|
|
ProgressAchievement(PlayerController(Killer), 'Freeze_Storm', 1);
|
|
InstantShatterCounter = 0;
|
|
}
|
|
}
|
|
else {
|
|
LastShatteredBy = PlayerController(Killer);
|
|
LastShaterTime = Level.TimeSeconds;
|
|
InstantShatterCounter = 1;
|
|
}
|
|
|
|
if ( FreezerStats != none && !ZedVictim.bDecapitated ) {
|
|
if (ZedVictim.default.Health >= 1500)
|
|
i = 4;
|
|
if (ZedVictim.default.Health >= 1000)
|
|
i = 3;
|
|
else if (ZedVictim.default.Health >= 500)
|
|
i = 2;
|
|
else
|
|
i = 1;
|
|
FreezerStats.Rep.ProgressCustomValue(Class'HTecProg', i);
|
|
|
|
if ( LastShatteredBy != none && LastShatteredBy != Frozen[FrozenIdx].LastFrozenBy ) {
|
|
class'ScrnAchievements'.static.ProgressAchievementByID(FreezerStats.Rep, 'Freeze_ShatterZeds', 1);
|
|
ProgressAchievement(LastShatteredBy, 'Freeze_ShatterZeds', 1);
|
|
}
|
|
}
|
|
if ( ZombieBoss(ZedVictim) != none )
|
|
ScrnGameRules.ProgressAchievementForAllPlayers('Freeze_ShatterPat', 1, true);
|
|
|
|
Spawn(ShatteredIce,,,loc);
|
|
ZedVictim.bHidden = true;
|
|
ZedVictim.Died(Killer, DamageType, loc );
|
|
|
|
// DO$H DO$H DO$H DO$H DO$H
|
|
if ( MaxDosh > 0 ) {
|
|
r = ZedVictim.GetViewRotation();
|
|
for ( i=0; i<5; ++i ) {
|
|
Dosh = Spawn(class'FrozenDosh',,, loc);
|
|
if ( Dosh != none ) {
|
|
// link all doshes together
|
|
if ( HeadDosh == none )
|
|
HeadDosh = Dosh;
|
|
Dosh.HeadDosh = HeadDosh;
|
|
if ( PrevDosh != none )
|
|
PrevDosh.NextDosh = Dosh;
|
|
|
|
Dosh.CashAmount = 1 + rand(MaxDosh);
|
|
Dosh.RespawnTime = 0;
|
|
Dosh.bDroppedCash = True;
|
|
if ( Killer == Frozen[FrozenIdx].LastFrozenBy )
|
|
Dosh.DroppedBy = Killer;
|
|
Dosh.Velocity = Vector(r) * 100;
|
|
Dosh.Velocity.Z += 500;
|
|
Dosh.InitDroppedPickupFor(None);
|
|
PrevDosh = Dosh;
|
|
}
|
|
r.Yaw += 13107;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function bool ProgressAchievement(PlayerController PC, name ID, int Prog)
|
|
{
|
|
if ( PC != none && SRStatsBase(PC.SteamStatsAndAchievements) != none )
|
|
return class'ScrnAchievements'.static.ProgressAchievementByID(SRStatsBase(PC.SteamStatsAndAchievements).Rep, ID, Prog);
|
|
return false;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
FreezeTreshold=0.333333
|
|
FrozenDamageResistance=0.500000
|
|
FrozenDamageMult=1.500000
|
|
FrozenDamageMultHS=4.500000
|
|
FrozenDamageMultSG=2.500000
|
|
ShatteredIce=Class'ScrnHTec.IceChunkEmitter'
|
|
bDosh=True
|
|
}
|