Prepare fixtures

This commit is contained in:
dkanus 2026-07-14 20:27:09 +07:00
commit 9c94356263
6021 changed files with 722805 additions and 22 deletions

View file

@ -0,0 +1,9 @@
class BlockHitEmitter extends MetalHitEmitter;
defaultproperties
{
ImpactSounds(0)=None
ImpactSounds(1)=None
ImpactSounds(2)=None
RemoteRole=ROLE_SimulatedProxy
}

View file

@ -0,0 +1,14 @@
// for debugging
class BrainDeadFemaleFP extends FemaleFP;
event PostBeginPlay()
{
Super.PostBeginPlay();
SetMovementPhysics();
}
defaultproperties
{
ControllerClass=None
}

View file

@ -0,0 +1,52 @@
class BruteAvoidArea extends AvoidMarker;
var KFMonster KFMonst; // Should be the same as base and owner
state BigMeanAndScary
{
Begin:
StartleBots();
Sleep(1.0);
GoTo('Begin');
}
function InitFor(KFMonster V)
{
if (V != None)
{
KFMonst = V;
SetCollisionSize(KFMonst.CollisionRadius * 3, KFMonst.CollisionHeight + CollisionHeight);
SetBase(KFMonst);
GoToState('BigMeanAndScary');
}
}
function Touch( actor Other )
{
if ( (Pawn(Other) != None) && RelevantTo(Pawn(Other)) )
KFMonsterController(Pawn(Other).Controller).AvoidThisMonster(KFMonst);
}
function bool RelevantTo(Pawn P)
{
return ( KFMonst != none && VSizeSquared(KFMonst.Velocity) >= 75 && Super.RelevantTo(P)
&& KFMonst.Velocity dot (P.Location - KFMonst.Location) > 0 );
}
function StartleBots()
{
local KFMonster P;
if (KFMonst != None)
ForEach CollidingActors(class'KFMonster', P, CollisionRadius)
if (RelevantTo(P))
KFMonsterController(P.Controller).AvoidThisMonster(KFMonst);
}
defaultproperties
{
CollisionRadius=1000.000000
bBlockZeroExtentTraces=False
bBlockNonZeroExtentTraces=False
bBlockHitPointTraces=False
}

View file

@ -0,0 +1,69 @@
class BruteZombieController extends KFMonsterController;
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
var bool bDoneSpottedCheck;
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
function TimedFireWeaponAtEnemy()
{
if ( (Enemy == None) || FireWeaponAt(Enemy) )
SetCombatTimer();
else
SetTimer(0.01, True);
}
state ZombieCharge
{
function Tick(float Delta)
{
local ZombieBrute ZFP;
Global.Tick(Delta);
// Bored, get angry
if (RageFrustrationTimer < RageFrustrationThreshhold)
{
RageFrustrationTimer += Delta;
if (RageFrustrationTimer >= RageFrustrationThreshhold)
{
ZFP = ZombieBrute(Pawn);
if( ZFP != none && !ZFP.bChargingPlayer )
{
ZFP.StartCharging();
ZFP.bFrustrated = true;
}
}
}
}
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
{
return false;
}
function bool TryStrafe(vector sideDir)
{
return false;
}
function Timer()
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
}
function BeginState()
{
super.BeginState();
RageFrustrationThreshhold = default.RageFrustrationThreshhold + (Frand() * 5);
}
}
defaultproperties
{
RageFrustrationThreshhold=10.000000
}

View file

@ -0,0 +1,6 @@
class DamTypeEMP extends DamTypeTesla;
defaultproperties
{
DeathString="%o watched %k's Self-Destruct Sequence too close."
}

View file

@ -0,0 +1,10 @@
class DamTypeTesla extends DamTypeZombieAttack;
defaultproperties
{
bCheckForHeadShots=False
DeathString="%o accepts %k's shaft."
FemaleSuicide="%o rode the lighting."
MaleSuicide="%o rode the lighting."
bLocationalHit=False
}

View file

@ -0,0 +1,125 @@
//-----------------------------------------------------------
//Still behaves like a Fleshpound for the most part, although quicker to rage.
//Thanks to Scary for the work on this one. - Whisky
//-----------------------------------------------------------
class FFPController extends FleshpoundZombieController;
var bool bFindNewEnemy, bSmashDoor, bStartled, bAttackedTarget, bMissTarget;
var float prevRageTimer,prevRageThreshold;
//I fear nothing but you WILL fear me!!
//FFP will not bother trying to avoid nades when raged, prevents players from using their nades to stop her in a door.
//(I know because as a medic I was doing it all the time. - Whisky)
function FearThisSpot(AvoidMarker aSpot)
{
local FemaleFP FFP;
FFP = FemaleFP(Pawn);
if (!FFP.bChargingPlayer)
{
if ( Skill > 1 + 2.0 * FRand() )
super(Controller).FearThisSpot(aSpot);
}
}
function PostBeginPlay()
{
super.PostBeginPlay();
prevRageThreshold= default.RageFrustrationThreshhold + (Frand() * 5);
}
function bool FindNewEnemy()
{
bFindNewEnemy= true;
return super.FindNewEnemy();
}
function BreakUpDoor(KFDoorMover Other, bool bTryDistanceAttack)
{
bSmashDoor= true;
super.BreakUpDoor(Other,bTryDistanceAttack);
}
function Startle(Actor Feared)
{
bStartled= True;
super.Startle(Feared);
}
state SpinAttack
{
ignores EnemyNotVisible, GetOutOfTheWayOfShot;
function DoSpinDamage()
{
local Actor A;
//log("FLESHPOUND DOSPINDAMAGE!");
foreach CollidingActors(class'actor', A, (KFM.MeleeRange * 1.5)+pawn.CollisionRadius, pawn.Location)
FemaleFP(pawn).SpinDamage(A);
}
Begin:
WaitForAnim:
While( KFM.bShotAnim )
{
Sleep(0.1);
DoSpinDamage();
}
WhatToDoNext(152);
if ( bSoaking )
SoakStop("STUCK IN SPINATTACK!!!");
}
state ZombieCharge
{
function BeginState()
{
super.BeginState();
if (!bSmashDoor && ((bAttackedTarget && bMissTarget) || bFindNewEnemy || bStartled))
{
RageFrustrationTimer= prevRageTimer;
RageFrustrationThreshhold= prevRageThreshold;
}
bFindNewEnemy= false;
bSmashDoor= false;
bStartled= false;
bAttackedTarget= false;
bMissTarget= false;
}
function EndState()
{
prevRageTimer= RageFrustrationTimer;
prevRageThreshold= RageFrustrationThreshhold;
}
function Tick( float Delta )
{
local FemaleFP FFP;
Global.Tick(Delta);
// Make the FP rage if we haven't reached our enemy after a certain amount of time
if( RageFrustrationTimer < RageFrustrationThreshhold ) {
RageFrustrationTimer += Delta;
if( RageFrustrationTimer >= RageFrustrationThreshhold ) {
FFP = FemaleFP(Pawn);
if( FFP != none && !FFP.bChargingPlayer ) {
FFP.StartCharging();
FFP.bFrustrated = true;
}
}
}
}
}
defaultproperties
{
RageFrustrationThreshhold=30.000000
}

View file

@ -0,0 +1,61 @@
/*
This object is used to load bRagdolls config variable
Clients, who have no config (and probabaly have no karma data),
will have karma ragdoll death animations turned off.
@author PooSH
@author Marco
*/
Class FFPKarma extends Object
Config(FemaleFPMut)
PerObjectConfig;
var config bool bRagdolls;
var config bool bAutoDetect;
var private bool bHasInit,bRagdoll;
static final function InitConfig(LevelInfo Level)
{
local FFPKarma D;
Default.bHasInit = true;
if ( Level.NetMode == NM_DedicatedServer ) {
Default.bRagdoll = false;
}
else if ( Level.PlatformIsWindows() ) {
// DIR command doesn't work on Linux. Not sure about MacOS
Default.bRagdoll = Level.GetLocalPlayerController().ConsoleCommand("DIR ../KarmaData/FFPKarma.ka") ~= "FFPKarma.ka";
}
else {
// read from config. create config file, if it doesn't exist
D = New(None,"FemaleFPKarma") Class'FFPKarma';
Default.bRagdoll = D.bRagdolls;
if ( Level.PlatformIsWindows() ) {
if ( Level.GetLocalPlayerController().ConsoleCommand("DIR ../KarmaData/FFPKarma.ka") ~= "FFPKarma.ka" ) {
// karma file exists
if ( default.bAutoDetect )
Default.bRagdolls = true; // enable it in config for the next use, but don't enable it now. For cases, when karma file is obtained via MusicLoader
}
else {
// karma file doesn't exist - disable ragdoll animations
Default.bRagdolls = false;
Default.bRagdoll = false;
}
}
D.SaveConfig();
}
}
static final function bool UseRagdoll(LevelInfo Level)
{
if( !Default.bHasInit )
InitConfig(Level);
return Default.bRagdoll;
}
defaultproperties
{
bAutoDetect=True
}

View file

@ -0,0 +1,88 @@
class FFPVentEmitter extends VehicleExhaustEffect;
defaultproperties
{
MaxSpritePPSOne=20
MaxSpritePPSTwo=22
IdleSpritePPSOne=5
IdleSpritePPSTwo=7
Begin Object Class=SpriteEmitter Name=SpriteEmitter1
FadeOut=True
FadeIn=True
SpinParticles=True
UseSizeScale=True
UseRegularSizeScale=False
UniformSize=True
BlendBetweenSubdivisions=True
UseRandomSubdivision=True
UseVelocityScale=True
Acceleration=(X=70.000000,Z=20.000000)
ColorScale(0)=(Color=(B=255,G=255,R=255,A=50))
ColorScale(1)=(RelativeTime=1.000000,Color=(B=255,G=255,R=255))
FadeOutStartTime=0.100000
MaxParticles=20
UseRotationFrom=PTRS_Actor
SpinsPerSecondRange=(X=(Min=-0.075000,Max=0.075000))
SizeScale(0)=(RelativeSize=0.500000)
SizeScale(1)=(RelativeTime=0.070000,RelativeSize=0.500000)
SizeScale(2)=(RelativeTime=0.370000,RelativeSize=1.100000)
SizeScale(3)=(RelativeTime=1.000000,RelativeSize=2.500000)
StartSizeRange=(X=(Min=15.000000,Max=30.000000))
InitialParticlesPerSecond=5.000000
DrawStyle=PTDS_AlphaBlend
Texture=Texture'Effects_Tex.Vehicles.Gasoline_Smoke'
TextureUSubdivisions=2
TextureVSubdivisions=2
SecondsBeforeInactive=0.000000
LifetimeRange=(Min=0.200000,Max=0.300000)
StartVelocityRange=(X=(Min=45.000000,Max=300.000000),Y=(Min=-45.000000,Max=45.000000),Z=(Min=-45.000000,Max=45.000000))
VelocityScale(0)=(RelativeVelocity=(X=1.000000,Y=1.000000,Z=1.000000))
VelocityScale(1)=(RelativeTime=0.300000,RelativeVelocity=(X=0.200000,Y=1.000000,Z=1.000000))
VelocityScale(2)=(RelativeTime=1.000000,RelativeVelocity=(Y=0.400000,Z=0.400000))
End Object
Emitters(0)=SpriteEmitter'ScrnZedPack.FFPVentEmitter.SpriteEmitter1'
Begin Object Class=SpriteEmitter Name=SpriteEmitter2
FadeOut=True
FadeIn=True
AutoReset=True
SpinParticles=True
UseSizeScale=True
UseRegularSizeScale=False
UniformSize=True
BlendBetweenSubdivisions=True
UseRandomSubdivision=True
UseVelocityScale=True
Acceleration=(X=70.000000,Z=20.000000)
ColorScale(0)=(Color=(B=128,G=128,R=128,A=50))
ColorScale(1)=(RelativeTime=1.000000,Color=(B=255,G=255,R=255))
FadeOutStartTime=0.100000
MaxParticles=20
AutoResetTimeRange=(Min=5.000000,Max=10.000000)
UseRotationFrom=PTRS_Actor
SpinsPerSecondRange=(X=(Min=-0.075000,Max=0.075000))
SizeScale(0)=(RelativeSize=0.500000)
SizeScale(1)=(RelativeTime=0.070000,RelativeSize=0.500000)
SizeScale(2)=(RelativeTime=0.370000,RelativeSize=1.100000)
SizeScale(3)=(RelativeTime=1.000000,RelativeSize=2.500000)
StartSizeRange=(X=(Min=11.000000,Max=21.000000))
InitialParticlesPerSecond=5.000000
DrawStyle=PTDS_AlphaBlend
Texture=Texture'Effects_Tex.Vehicles.Deisel_Smoke'
TextureUSubdivisions=2
TextureVSubdivisions=2
SecondsBeforeInactive=0.000000
LifetimeRange=(Min=0.200000,Max=0.300000)
StartVelocityRange=(X=(Min=45.000000,Max=300.000000),Y=(Min=-10.000000,Max=10.000000),Z=(Min=-10.000000,Max=10.000000))
VelocityScale(0)=(RelativeVelocity=(X=1.000000,Y=1.000000,Z=1.000000))
VelocityScale(1)=(RelativeTime=0.400000,RelativeVelocity=(X=0.150000,Y=1.000000,Z=1.000000))
VelocityScale(2)=(RelativeTime=1.000000,RelativeVelocity=(Y=0.400000,Z=0.400000))
End Object
Emitters(1)=SpriteEmitter'ScrnZedPack.FFPVentEmitter.SpriteEmitter2'
AutoDestroy=True
bNetTemporary=True
LifeSpan=8.000000
bHardAttach=False
bDirectional=True
}

View file

@ -0,0 +1,982 @@
////////////////////////////////////////////////////////////////////////////
// Female Flesh Pound
// A more dainty and gentle fleshpound... pfft! NOT!
// Written by Whisky & Poosh, (Although it's mostly TWI's code in the first place.)
// Scary Ghost's code used with permission.
////////////////////////////////////////////////////////////////////////////
class FemaleFP extends KFMonster;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
var transient bool bChargingPlayer,bClientCharge,bFrustrated,bNeedVent;
var transient bool bRageFirstHit; //did she hit a player during current rage run
var float RageEndTime;
var FleshPoundAvoidArea AvoidArea;
var name ChargingAnim;
var() class<VehicleExhaustEffect> VentEffectClass; // Effect class for the vent emitter
var() VehicleExhaustEffect VentEffect,VentEffect2; //Dual venting baby!
var() float BlockDamageReduction;
var() vector RotMag;
var() vector RotRate;
var() float RotTime;
var() vector OffsetMag;
var() vector OffsetRate;
var() float OffsetTime;
var() int RageDamageThreshold;
replication
{
reliable if(Role==ROLE_Authority)
bChargingPlayer,bFrustrated, bNeedVent;
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
if( ROLE==ROLE_Authority ) {
StunsRemaining = fmax(7 - Level.Game.GameDifficulty, 1);
}
}
simulated function PostNetBeginPlay()
{
if(AvoidArea==None)
AvoidArea=Spawn(class'FleshPoundAvoidArea',self);
if(AvoidArea!=None)
AvoidArea.InitFor(Self);
EnableChannelNotify(1,1);
AnimBlendParams(1,1.0,0.0,,SpineBone2);
super.PostNetBeginPlay();
}
// This zed has been taken control of. Boost its health and speed
function SetMindControlled(bool bNewMindControlled)
{
if( bNewMindControlled )
{
NumZCDHits++;
// if we hit him a couple of times, make him rage!
if( NumZCDHits > 1 )
{
if( !IsInState('ChargeToMarker') )
{
GotoState('ChargeToMarker');
}
else
{
NumZCDHits = 1;
if( IsInState('ChargeToMarker') )
{
GotoState('');
}
}
}
else
{
if( IsInState('ChargeToMarker') )
{
GotoState('');
}
}
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
}
else
{
NumZCDHits=0;
}
bZedUnderControl = bNewMindControlled;
}
// Handle the zed being commanded to move to a new location
function GivenNewMarker()
{
if( bChargingPlayer && NumZCDHits > 1 )
{
GotoState('ChargeToMarker');
}
else
{
GotoState('');
}
}
function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
{
if( Level.TimeSeconds - LastPainAnim < MinTimeBetweenPainAnims )
return;
if ( Controller.IsInState('WaitForAnim') )
return; // Don't interrupt the controller if its waiting for an animation to end
if( StunsRemaining > 0 && Damage>=100 )
PlayDirectionalHit(HitLocation);
LastPainAnim = Level.TimeSeconds;
if( Level.TimeSeconds - LastPainSound < MinTimeBetweenPainSounds )
return;
LastPainSound = Level.TimeSeconds;
PlaySound(HitSound[0], SLOT_Pain,1.25,,400);
}
//Taken from ScaryGhost's SuperFP.
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{
local bool didIHit;
didIHit= super.MeleeDamageTarget(hitdamage, pushdir);
FFPController(Controller).bMissTarget=
FFPController(Controller).bMissTarget || !didIHit;
return didIHit;
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
local int OldHealth;
local bool bIsHeadShot;
local float HeadShotCheckScale;
local class<KFWeaponDamageType> KFDamType;
// optimizes typecasting and make code comre compact -- PooSH
KFDamType = class<KFWeaponDamageType>(damageType);
oldHealth= Health;
if ( KFDamType != none )
{
HeadShotCheckScale = 1.0;
// Do larger headshot checks if it is a melee attach
if( class<DamTypeMelee>(damageType) != none )
HeadShotCheckScale *= 1.25;
bIsHeadShot = IsHeadShot(Hitlocation, normal(Momentum), 1.0);
// She takes less damage to small arms fire (non explosives)
// Frags and LAW rockets will bring her down way faster than bullets and shells.
if ( !KFDamType.default.bIsExplosive ) // all explosives: current, future and custom -- PooSH
{
// Don't reduce the damage so much if its a high headshot damage weapon
if( bIsHeadShot && KFDamType.default.HeadShotDamageMult >= 1.5 )
{
Damage *= 0.75;
}
else if ( Level.Game.GameDifficulty >= 5.0 && bIsHeadshot
&& (ClassIsChildOf(KFDamType, class'DamTypeCrossbow')
|| ClassIsChildOf(KFDamType, class'DamTypeM99SniperRifle')) )
{
Damage *= 0.35; // 65% damage reduction from xbow/m99 headshots
}
else
{
Damage *= 0.5;
}
}
// include subclasses = care about modders (c) PooSH
// Can't check subclasses of DamTypeFrag, because LAW is one of them
else if ( KFDamType == class'DamTypeFrag' || ClassIsChildOf(KFDamType, class'DamTypePipeBomb') )
{
Damage *= 1.25;
}
// M32 and SP are subclasses of DamTypeM79Grenade, so no need to additionally check those -- PooSH
else if( ClassIsChildOf(KFDamType, class'DamTypeM79Grenade')
|| ClassIsChildOf(KFDamType, class'DamTypeM203Grenade') )
{
Damage *= 1.25;
}
}
// Shut off his "Device" when dead
if (Damage >= Health)
PostNetReceive();
if (damageType == class 'DamTypeVomit')
Damage = 0; // nulled
// fixes none-reference erros when taking enviromental damage -- PooSH
if (InstigatedBy == none || KFDamType == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, damageType); // skip NONE-reference error
else
super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, damageType);
Damage = OldHealth - Health;
// Starts charging if single damage > 300 or health drops below 50% on hard difficulty or below,
// or health <75% on Suicidal/HoE
// -- PooSH
if ( Health > 0 && !bDecapitated && !bChargingPlayer
&& !bZapped && (!(bCrispified && bBurnified) || bFrustrated)
&& (Damage > RageDamageThreshold || Health < HealthMax*0.5
|| (Level.Game.GameDifficulty >= 5.0 && Health < HealthMax*0.75)) )
StartCharging();
}
function RangedAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( CanAttack(A) )
{
bShotAnim = true;
SetAnimAction('Claw');
return;
}
}
//Need to add sound!
simulated function SpawnVentEmitter()
{
if ( VentEffectClass != none )
{
if ( VentEffect == none ) {
VentEffect = Spawn(VentEffectClass, self);
AttachToBone(VentEffect, 'BODY_Piston_top_Camisa_l');
VentEffect.SetRelativeRotation(rot(0, 0, 0));
}
if ( VentEffect2 == none ) {
VentEffect2 = Spawn(VentEffectClass, self);
AttachToBone(VentEffect2, 'BODY_Piston_top_Camisa_r');
}
}
else
bNeedVent = false;
}
//Clean up of effects.
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
StopVenting();
super.Died( Killer, damageType, HitLocation );
}
simulated function VentMe()
{
//Level.GetLocalPlayerController().ClientMessage("VentMe");
//Log("AAAAAARRRRRRROOOOOOOOOOOOOOGGGGGAAAAAAAAA!!");
bNeedVent = true;
SpawnVentEmitter();
}
simulated function StopVenting()
{
bNeedVent = false;
if ( VentEffect != none )
{
VentEffect.Destroy();
VentEffect = none;
}
if ( VentEffect2 != none )
{
VentEffect2.Destroy();
VentEffect2 = none;
}
}
// Sets the FFP in a berserk charge state until she either strikes her target, or hits timeout
function StartCharging()
{
local float RageAnimDur;
if( Health <= 0 )
{
return;
}
SetAnimAction('Rage_Start');
Acceleration = vect(0,0,0);
bShotAnim = true;
Velocity.X = 0;
Velocity.Y = 0;
Controller.GoToState('WaitForAnim');
KFMonsterController(Controller).bUseFreezeHack = True;
RageAnimDur = GetAnimDuration('Rage_Start');
FleshpoundZombieController(Controller).SetPoundRageTimout(RageAnimDur);
GoToState('BeginRaging');
}
state BeginRaging
{
Ignores StartCharging;
// Set the zed to the zapped behavior
simulated function SetZappedBehavior()
{
Global.SetZappedBehavior();
GoToState('');
}
function bool CanGetOutOfWay()
{
return false;
}
simulated function bool HitCanInterruptAction()
{
return false;
}
function Tick( float Delta )
{
Acceleration = vect(0,0,0);
global.Tick(Delta);
}
Begin:
Sleep(GetAnimDuration('Rage_Start'));
GotoState('RageCharging');
}
simulated function SetBurningBehavior()
{
if( bFrustrated || bChargingPlayer )
{
return;
}
super.SetBurningBehavior();
}
//Next two States are Scary's Code.
state RageCharging
{
Ignores StartCharging;
// Set the zed to the zapped behavior
simulated function SetZappedBehavior()
{
Global.SetZappedBehavior();
GoToState('');
}
function PlayDirectionalHit(Vector HitLoc)
{
if( !bShotAnim )
{
super.PlayDirectionalHit(HitLoc);
}
}
function bool CanGetOutOfWay()
{
return false;
}
// Don't override speed in this state
function bool CanSpeedAdjust()
{
return false;
}
function BeginState()
{
local float DifficultyModifier;
bRageFirstHit = false;
if( bZapped )
{
GoToState('');
}
else
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
// Scale rage length by difficulty
if( Level.Game.GameDifficulty < 2.0 )
{
DifficultyModifier = 0.85;
}
else if( Level.Game.GameDifficulty < 4.0 )
{
DifficultyModifier = 1.0;
}
else if( Level.Game.GameDifficulty < 5.0 )
{
DifficultyModifier = 1.25;
}
else // Hardest difficulty
{
DifficultyModifier = 3.0; // Doubled Fleshpound Rage time for Suicidal and HoE in Balance Round 1
}
RageEndTime = Level.TimeSeconds + DifficultyModifier * (10.0 + 6.0*FRand());
NetUpdateTime = Level.TimeSeconds - 1;
}
}
function EndState()
{
bChargingPlayer = False;
bFrustrated = false;
FleshPoundZombieController(Controller).RageFrustrationTimer = 0;
StopVenting();
if( Health>0 && !bZapped )
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
NetUpdateTime = Level.TimeSeconds - 1;
}
function Tick( float Delta )
{
if( !bShotAnim )
{
SetGroundSpeed(OriginalGroundSpeed * 2.3);//2.0;
if( !bFrustrated && !bZedUnderControl && Level.TimeSeconds>RageEndTime )
{
GoToState('');
}
}
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=None )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
global.Tick(Delta);
}
function Bump( Actor Other )
{
local float RageBumpDamage;
local KFMonster KFMonst;
KFMonst = KFMonster(Other);
// Hurt/Kill enemies that we run into while raging
if( !bShotAnim && KFMonst!=None && KFMonst.Health>0 && !SameSpeciesAs(self) )
{
// Random chance of doing obliteration damage
if( FRand() < 0.4 )
{
RageBumpDamage = 501;
}
else
{
RageBumpDamage = 450;
}
RageBumpDamage *= KFMonst.PoundRageBumpDamScale;
Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'DamTypePoundCrushed');
}
else Global.Bump(Other);
}
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{
local bool RetVal,bWasEnemy, bCalmDown;
local float oldEnemyHealth;
local bool bAttackingHuman;
local KFPawn KFP;
//Only rage again if he was attacking a human
KFP = KFPawn(Controller.Target);
bAttackingHuman = KFP != none;
if (bAttackingHuman)
oldEnemyHealth= KFP.Health;
bWasEnemy = (Controller.Target==Controller.Enemy);
if ( !bRageFirstHit )
hitdamage *= 1.5; // first rage hit doesn more damage, down from 1.75 to 1.5 in v091 -- PooSH
RetVal = Super(KFMonster).MeleeDamageTarget(hitdamage, pushdir*3);
if ( bAttackingHuman || bWasEnemy )
bRageFirstHit = true; //she had a chance to do a greater damage. If she missed - her fault
if(RetVal && bWasEnemy)
{
// On Hard and below she always calms down, no matter of was hit successful or not
// On Suicidal she calms down after successfull hit
// On HoE she calms down only if killed a player.
if ( bAttackingHuman && Level.Game.GameDifficulty >= 5.0 ) {
if ( Level.Game.GameDifficulty >= 7.0 )
bCalmDown = KFP == none || KFP.Health <= 0;
else
bCalmDown = KFP == none || KFP.Health < oldEnemyHealth;
}
else {
bCalmDown = true;
}
if (bCalmDown)
GoToState('');
}
return RetVal;
}
}
state ChargeToMarker extends RageCharging
{
Ignores StartCharging;
function Tick( float Delta )
{
if( !bShotAnim )
{
SetGroundSpeed(OriginalGroundSpeed * 2.3);
if( !bFrustrated && !bZedUnderControl && Level.TimeSeconds>RageEndTime )
{
GoToState('');
}
}
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=None )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
global.Tick(Delta);
}
}
simulated function PostNetReceive()
{
super.PostNetReceive();
if( bClientCharge!=bChargingPlayer && !bZapped )
{
bClientCharge=bChargingPlayer;
if( bChargingPlayer ) {
MovementAnims[0]=ChargingAnim;
MeleeAnims[0]='attack3';
MeleeAnims[1]='attack3';
MeleeAnims[2]='attack3';
DeviceGoRed();
}
else {
MovementAnims[0]=default.MovementAnims[0];
MeleeAnims[0]=default.MeleeAnims[0];
MeleeAnims[1]=default.MeleeAnims[1];
MeleeAnims[2]=default.MeleeAnims[2];
DeviceGoNormal();
StopVenting();
}
}
}
simulated function ClientChargingAnims()
{
PostNetReceive();
}
function ClawDamageTarget()
{
local vector PushDir;
local KFHumanPawn HumanTarget;
local KFPlayerController HumanTargetController;
local float UsedMeleeDamage;
local name Sequence;
local float Frame, Rate;
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
if( MeleeDamage > 1 )
{
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
}
else
{
UsedMeleeDamage = MeleeDamage;
}
// Reduce the melee damage for anims with repeated attacks, since it does repeated damage over time
if( Sequence == 'Attack1' )
{
UsedMeleeDamage *= 0.5;
}
else if( Sequence == 'Attack2' )
{
UsedMeleeDamage *= 0.25;
}
if(Controller!=none && Controller.Target!=none)
{
//calculate based on relative positions
PushDir = (damageForce * Normal(Controller.Target.Location - Location));
}
else
{
//calculate based on way Monster is facing
PushDir = damageForce * vector(Rotation);
}
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
{
HumanTarget = KFHumanPawn(Controller.Target);
if( HumanTarget!=None )
HumanTargetController = KFPlayerController(HumanTarget.Controller);
if( HumanTargetController!=None )
HumanTargetController.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
}
}
function SpinDamage(actor Target)
{
local vector HitLocation;
local Float dummy;
local float DamageAmount;
local vector PushDir;
local KFHumanPawn HumanTarget;
if(target==none)
return;
PushDir = (damageForce * Normal(Target.Location - Location));
damageamount = (SpinDamConst + rand(SpinDamRand) );
HumanTarget = KFHumanPawn(Target);
if ( HumanTarget != none ) {
// FLING DEM DEAD BODIEZ!
if ( HumanTarget.Health <= DamageAmount ) {
HumanTarget.RagDeathVel *= 3;
HumanTarget.RagDeathUpKick *= 1.5;
}
if (HumanTarget.Controller != none)
HumanTarget.Controller.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
//TODO - line below was KFPawn. Does this whole block need to be KFPawn, or is it OK as KFHumanPawn?
HumanTarget.TakeDamage(DamageAmount, self ,HitLocation,pushdir, class 'KFmod.ZombieMeleeDamage');
if ( HumanTarget.Health <=0 ) {
HumanTarget.SpawnGibs(rotator(pushdir), 1);
HumanTarget.HideBone(HumanTarget.GetClosestBone(HitLocation,Velocity,dummy));
}
}
else if ( Target.IsA('KFDoorMover') ) {
Target.TakeDamage(DamageAmount , self ,HitLocation,pushdir, class 'KFmod.ZombieMeleeDamage');
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
}
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='Attack1' || AnimName=='Attack2' || AnimName=='Attack3' )
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
Return 1;
}
Return Super.DoAnimAction(AnimName);
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' )
Return;
if(NewAction == 'Claw')
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
CurrentDamtype = ZombieDamType[meleeAnimIndex];
}
else if( NewAction == 'DoorBash' )
{
CurrentDamtype = ZombieDamType[Rand(3)];
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{
bWaitForAnim = true;
}
if( Level.NetMode!=NM_Client )
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
FFPController(Controller).bAttackedTarget=
(NewAction == 'Claw') || (NewAction == 'DoorBash');
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'Rage_Start' || TestAnim == 'DoorBash' )
{
return true;
}
return false;
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=None )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
if ( Level.NetMode != NM_DedicatedServer ) {
if ( bNeedVent && Health > 0 )
SpawnVentEmitter();
//DebugHead();
}
}
simulated function DebugHead()
{
local coords C;
local vector HeadLoc;
local float Radius;
if ( Level.NetMode == NM_DedicatedServer || Health <= 0 )
return;
Radius = HeadRadius * HeadScale;
// based on head bone
C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * C.XAxis);
DrawDebugSphere(HeadLoc, Radius, 16, 0, 100, 255);
// based on OnlineHeadshotOffset
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
Radius *= OnlineHeadshotScale;
DrawDebugSphere(HeadLoc, Radius, 16, 255, 0, 0);
}
function bool FlipOver()
{
Return False;
}
function bool SameSpeciesAs(Pawn P)
{
return ZombieFleshPound(P) != none || FemaleFP(P) != none;
}
simulated function Destroyed()
{
if( AvoidArea!=None )
AvoidArea.Destroy();
StopVenting();
Super.Destroyed();
}
simulated function DeviceGoRed()
{
Skins[3]=Shader'ScrnZedPack_T.FFPLights_Red_shader';
Skins[1]=Combiner'ScrnZedPack_T.FFP_Metal_R_cmb';
}
simulated function DeviceGoNormal()
{
Skins[3]=Shader'ScrnZedPack_T.FFPLights_Yellow_shader';
Skins[1]=Combiner'ScrnZedPack_T.FFP_Metal_cmb';
}
//Metal does not burn like flesh!
simulated function ZombieCrispUp()
{
bAshen = true;
bCrispified = true;
SetBurningBehavior();
if ( Level.NetMode == NM_DedicatedServer || class'GameInfo'.static.UseLowGore() )
{
Return;
}
Skins[4]=Combiner'PatchTex.Common.BurnSkinEmbers_cmb';
}
simulated function PlayDirectionalDeath(Vector HitLoc)
{
if( Level.NetMode==NM_DedicatedServer )
{
SetCollision(false, false, false);
return;
}
// lame hack, but it is better than leaving her staying for those, who have no karma data
// -- PooSH
SetCollision(false, false, false);
bHidden = true;
}
// Player must have bRagdolls=True in FemaleFPMut.ini to see karma death animation.
// If there is no FemaleFPMut.ini file on client side, then usually there is no FFPKarma.ka file too,
// so non-ragdoll death fallback is used.
// WARNING! Trying to use karma without karma file causes game crash!
// -- PooSH
simulated function PlayDyingAnimation(class<DamageType>DamageType,vector HitLoc)
{
if(Level.NetMode!=NM_DedicatedServer && Class'FFPKarma'.Static.UseRagdoll(Level) ) {
// karma death
super.PlayDyingAnimation(DamageType, HitLoc);
}
else {
if(MyExtCollision!=None)
MyExtCollision.Destroy();
// non-ragdoll death fallback
Velocity+=GetTearOffMomemtum();
BaseEyeHeight=Default.BaseEyeHeight;
SetTwistLook(0,0);
SetInvisibility(0.0);
PlayDirectionalDeath(HitLoc);
SetPhysics(PHYS_Falling);
}
}
function RemoveHead()
{
Super.RemoveHead();
// FFP has different bone names. Since most bones are hardcoded in KFMonster,
// there is no easy way to implement decapitation
KilledBy(LastDamagedBy);
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(Texture'ScrnZedPack_T.FFP.BraTexture');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.FFP.FFP_Metal_cmb');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.FFP.FFP_Metal_R_cmb');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFPLights_Yellow_shader');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFPLights_Red_shader');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFP_Skin1_Sdr');
}
defaultproperties
{
ChargingAnim="Rage_Run"
VentEffectClass=Class'ScrnZedPack.FFPVentEmitter'
RageDamageThreshold=300
MeleeAnims(0)="Attack1"
MeleeAnims(1)="Attack2"
MeleeAnims(2)="Attack3"
HitAnims(0)="Hit"
HitAnims(1)="Hit"
HitAnims(2)="Hit"
MoanVoice=SoundGroup'ScrnZedPack_S.FFP.FFPG_Talk'
KFHitFront="Hit"
KFHitBack="Hit"
KFHitLeft="Hit"
KFHitRight="Hit"
StunsRemaining=3
BleedOutDuration=7.000000
ZapThreshold=1.750000
ZappedDamageMod=1.250000
ZombieFlag=3
MeleeDamage=30
damageForce=15000
bFatAss=True
KFRagdollName="FFPKarma"
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_HitPlayer'
JumpSound=SoundGroup'ScrnZedPack_S.FFP.FFPG_Jump'
SpinDamConst=15.000000
SpinDamRand=10.000000
bMeleeStunImmune=True
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=47.000000)
ColRadius=40.000000
ColHeight=40.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredLegAttachScale=1.100000
SeveredHeadAttachScale=1.200000
DetachedArmClass=Class'ScrnZedPack.SeveredArmFFP'
DetachedLegClass=Class'ScrnZedPack.SeveredLegFFP'
PlayerCountHealthScale=0.300000
OnlineHeadshotOffset=(X=25.000000,Z=61.000000)
OnlineHeadshotScale=1.300000
HeadHealth=600.000000
PlayerNumHeadHealthScale=0.300000
MotionDetectorThreat=5.000000
HitSound(0)=SoundGroup'ScrnZedPack_S.FFP.FFPG_Pain'
DeathSound(0)=SoundGroup'ScrnZedPack_S.FFP.FFPG_Death'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.FFP.FFPG_Challenge'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.FFP.FFPG_Challenge'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.FFP.FFPG_Challenge'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.FFP.FFPG_Challenge'
ScoringValue=200
IdleHeavyAnim="Idle"
IdleRifleAnim="Idle"
FireRootBone="BODY_Spine2"
GroundSpeed=140.000000
HealthMax=1100.000000
Health=1100
HeadRadius=8.000000
HeadHeight=2.500000
HeadScale=1.300000
MenuName="Female FleshPound"
ControllerClass=Class'ScrnZedPack.FFPController'
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkF"
MovementAnims(2)="WalkL"
MovementAnims(3)="WalkR"
WalkAnims(2)="WalkL"
WalkAnims(3)="WalkR"
AirStillAnim="Jump"
TakeoffStillAnim="Jump"
IdleCrouchAnim="Idle"
IdleWeaponAnim="Idle"
IdleRestAnim="Idle"
RootBone="Armature"
HeadBone="BODY_Head"
SpineBone1="BODY_Spine2"
SpineBone2="BODY_Spine3"
AmbientSound=SoundGroup'ScrnZedPack_S.FFP.FFPG_Idle'
Mesh=SkeletalMesh'ScrnZedPack_A.FFPMesh'
Skins(0)=Texture'ScrnZedPack_T.FFP.BraTexture'
Skins(1)=Combiner'ScrnZedPack_T.FFP.FFP_Metal_cmb'
Skins(2)=Combiner'ScrnZedPack_T.FFP.FFP_Metal_cmb'
Skins(3)=Shader'ScrnZedPack_T.FFP.FFPLights_Yellow_shader'
Skins(4)=Shader'ScrnZedPack_T.FFP.FFP_Skin1_Sdr'
Mass=500.000000
RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,127 @@
class FemaleFP_MKII extends FemaleFP;
struct SDamageResistance
{
var class<KFWeaponDamageType> DamType;
var byte ResistanceLevel;
var float AdaptTime;
};
var array<SDamageResistance> DamageRes;
var array<float> ResistanceLevels;
var float AdaptInterval; // how long does it takes for FFP to adapt to received damage
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
local int OldHealth;
local class<KFWeaponDamageType> KFDamType;
local int i;
// optimizes typecasting and make code comre compact -- PooSH
KFDamType = class<KFWeaponDamageType>(damageType);
oldHealth= Health;
if ( KFDamType != none ) {
if ( KFDamType.default.bIsExplosive ) {
if ( KFDamType == class'DamTypeFrag' )
Damage *= 2.00;
else if ( !ClassIsChildOf(KFDamType, class'DamTypeLAW'))
Damage *= 1.25;
}
else if ( ClassIsChildOf(KFDamType, class'DamTypeCrossbow') )
KFDamType = class'DamTypeCrossbowHeadShot'; // apply same resistance on similar damage types
else if ( ClassIsChildOf(KFDamType, class'DamTypeM99SniperRifle') ) {
KFDamType = class'DamTypeM99HeadShot';
Damage *= 0.75; // additional damage resistance to M99
}
else if ( ClassIsChildOf(KFDamType, class'DamTypeCrossbuzzsaw') ) {
KFDamType = class'DamTypeCrossbuzzsawHeadShot';
Damage *= 0.75;
}
for ( i=0; i<DamageRes.length; ++i ) {
if ( DamageRes[i].DamType == KFDamType ) {
if ( Level.TimeSeconds < DamageRes[i].AdaptTime ) {
if ( DamageRes[i].ResistanceLevel > 0 )
Damage *= ResistanceLevels[DamageRes[i].ResistanceLevel-1]; // skin is not adapted to this damage type yet
}
else {
Damage *= ResistanceLevels[DamageRes[i].ResistanceLevel];
if ( DamageRes[i].ResistanceLevel + 1 < ResistanceLevels.length ) {
DamageRes[i].ResistanceLevel++;
DamageRes[i].AdaptTime = Level.TimeSeconds + AdaptInterval;
}
}
break;
}
}
if ( i == DamageRes.length ) {
// new damage type
DamageRes.insert(i, 1);
DamageRes[i].DamType = KFDamType;
DamageRes[i].AdaptTime = Level.TimeSeconds + AdaptInterval;
}
}
// Shut off her "Device" when dead
if (Damage >= Health)
PostNetReceive();
// fixes none-reference erros when taking enviromental damage -- PooSH
if (InstigatedBy == none || KFDamType == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, damageType); // skip NONE-reference error
else
super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, damageType);
Damage = OldHealth - Health;
// Starts charging if single damage > 300 or health drops below 50% on hard difficulty or below,
// or health <75% on Suicidal/HoE
// -- PooSH
if ( Health > 0 && !bDecapitated && !bChargingPlayer
&& !bZapped && (!(bCrispified && bBurnified) || bFrustrated)
&& (Damage > RageDamageThreshold || Health < HealthMax*0.5
|| (Level.Game.GameDifficulty >= 5.0 && Health < HealthMax*0.75)) )
StartCharging();
}
simulated function DeviceGoRed()
{
Skins[3]=Shader'ScrnZedPack_T.FFP.FFPLights_Blue_shader';
Skins[1]=Combiner'ScrnZedPack_T.FFP.FFP_Metal_B_cmb';
Skins[4]=Shader'ScrnZedPack_T.FFP.FFP_Skin1_B_Sdr';
}
simulated function DeviceGoNormal()
{
Skins[3]=Shader'ScrnZedPack_T.FFP.FFPLights_Green_shader';
Skins[1]=Combiner'ScrnZedPack_T.FFP.FFP_Metal_G_cmb';
Skins[4]=Shader'ScrnZedPack_T.FFP.FFP_Skin1_Sdr';
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(Texture'ScrnZedPack_T.FFP.BraTexture');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.FFP.FFP_Metal_G_cmb');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.FFP.FFP_Metal_B_cmb');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFPLights_Green_shader');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFPLights_Blue_shader');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFP_Skin1_Sdr');
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.FFP.FFP_Skin1_B_Sdr');
}
defaultproperties
{
DamageRes(0)=(damtype=Class'KFMod.DamTypeCrossbowHeadShot')
DamageRes(1)=(damtype=Class'KFMod.DamTypeM99HeadShot')
ResistanceLevels(0)=0.500000
ResistanceLevels(1)=0.250000
ResistanceLevels(2)=0.150000
AdaptInterval=0.500000
HeadHealth=800.000000
PlayerNumHeadHealthScale=0.175000
MenuName="Female FleshPound MKII"
Skins(1)=Combiner'ScrnZedPack_T.FFP.FFP_Metal_G_cmb'
Skins(2)=Combiner'ScrnZedPack_T.FFP.FFP_Metal_G_cmb'
Skins(3)=Shader'ScrnZedPack_T.FFP.FFPLights_Green_shader'
}

View file

@ -0,0 +1,612 @@
Class HardPat extends ZombieBoss;
var transient float GiveUpTime;
var byte MissilesLeft;
var bool bEndGameBoss,bMovingChaingunAttack;
var(Sounds) sound SaveMeSound;
replication
{
reliable if( ROLE==ROLE_AUTHORITY )
bMovingChaingunAttack;
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
SyringeCount = 3; // no healing for mid-game bosses
Health *= 0.75; // less hp for mid-game bosses
}
function bool MakeGrandEntry()
{
Health = HealthMax; // restore original hp
bEndGameBoss = true;
SyringeCount = 0; // restore healing for end-game boss
return Super.MakeGrandEntry();
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
Super(KFMonster).Died(Killer,damageType,HitLocation);
if ( bEndGameBoss )
KFGameType(Level.Game).DoBossDeath();
}
simulated function bool HitCanInterruptAction()
{
return (!bWaitForAnim && !bShotAnim);
}
function RangedAttack(Actor A)
{
local float D;
local bool bOnlyE;
local bool bDesireChainGun;
// Randomly make him want to chaingun more
if( Controller.LineOfSightTo(A) && FRand() < 0.15 && LastChainGunTime<Level.TimeSeconds )
{
bDesireChainGun = true;
}
if ( bShotAnim )
{
if( !IsAnimating(ExpectingChannel) )
bShotAnim = false;
return;
}
D = VSize(A.Location-Location);
bOnlyE = (Pawn(A)!=None && OnlyEnemyAround(Pawn(A)));
if ( IsCloseEnuf(A) )
{
bShotAnim = true;
if( Health>1500 && Pawn(A)!=None && FRand() < 0.5 )
{
SetAnimAction('MeleeImpale');
}
else
{
SetAnimAction('MeleeClaw');
//PlaySound(sound'Claw2s', SLOT_None); KFTODO: Replace this
}
}
else if( Level.TimeSeconds>LastSneakedTime )
{
if( FRand() < 0.3 )
{
// Wait another 20-40 to try this again
LastSneakedTime = Level.TimeSeconds+20.f+FRand()*20;
Return;
}
SetAnimAction('transition');
GoToState('SneakAround');
}
else if( bChargingPlayer && (bOnlyE || D<200) )
Return;
else if( !bDesireChainGun && !bChargingPlayer && (D<300 || (D<700 && bOnlyE)) &&
(Level.TimeSeconds - LastChargeTime > (5.0 + 5.0 * FRand())) ) // Don't charge again for a few seconds
{
SetAnimAction('transition');
GoToState('Charging');
}
else if( LastMissileTime<Level.TimeSeconds && (D>500 || SyringeCount>=2) )
{
if( !Controller.LineOfSightTo(A) || FRand() > 0.75 )
{
LastMissileTime = Level.TimeSeconds+FRand() * 5;
Return;
}
LastMissileTime = Level.TimeSeconds + 10 + FRand() * 15;
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('PreFireMissile');
HandleWaitForAnim('PreFireMissile');
GoToState('FireMissile');
}
else if ( !bWaitForAnim && !bShotAnim && LastChainGunTime<Level.TimeSeconds )
{
if ( !Controller.LineOfSightTo(A) || FRand()> 0.85 )
{
LastChainGunTime = Level.TimeSeconds+FRand()*4;
Return;
}
LastChainGunTime = Level.TimeSeconds + 5 + FRand() * 10;
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('PreFireMG');
HandleWaitForAnim('PreFireMG');
if ( bEndGameBoss )
MGFireCounter = Rand(60) + 35*(SyringeCount+1);
else
MGFireCounter = Rand(60) + 60;
GoToState('FireChaingun');
}
}
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'FireMG' )
return !bMovingChaingunAttack;
return Super.AnimNeedsWait(TestAnim);
}
simulated final function rotator GetBoneTransRot()
{
local rotator R;
R.Yaw = 24576+Rotation.Yaw;
R.Pitch = 16384+Rotation.Pitch;
return R;
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='FireMG' && bMovingChaingunAttack )
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone, True);
//SetBoneDirection(FireRootBone,GetBoneTransRot(),,1,1);
PlayAnim('FireMG',, 0.f, 1);
return 1;
}
else if( AnimName=='FireEndMG' )
{
//SetBoneDirection(FireRootBone,rot(0,0,0),,0,0);
AnimBlendParams(1, 0);
}
return Super.DoAnimAction( AnimName );
}
simulated function AnimEnd(int Channel)
{
local name Sequence;
local float Frame, Rate;
if( Level.NetMode==NM_Client && bMinigunning )
{
GetAnimParams( Channel, Sequence, Frame, Rate );
if( Sequence != 'PreFireMG' && Sequence != 'FireMG' )
{
//SetBoneDirection(FireRootBone,rot(0,0,0),,0,0);
Super(KFMonster).AnimEnd(Channel);
return;
}
if( bMovingChaingunAttack )
DoAnimAction('FireMG');
else
{
PlayAnim('FireMG');
bWaitForAnim = true;
bShotAnim = true;
IdleTime = Level.TimeSeconds;
}
}
else
{
//SetBoneDirection(FireRootBone,rot(0,0,0),,0,0);
Super(KFMonster).AnimEnd(Channel);
}
}
// Fix: Don't spawn needle before last stage.
simulated function NotifySyringeA()
{
if( Level.NetMode!=NM_Client )
{
if( SyringeCount<3 )
SyringeCount++;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
if( Level.NetMode!=NM_DedicatedServer )
DropNeedle();
}
simulated function NotifySyringeC()
{
if( Level.NetMode!=NM_DedicatedServer )
{
CurrentNeedle = Spawn(Class'BossHPNeedle');
CurrentNeedle.Velocity = vect(-45,300,-90) >> Rotation;
DropNeedle();
}
}
simulated function ZombieCrispUp() // Don't become crispy.
{
bAshen = true;
bCrispified = true;
SetBurningBehavior();
}
state KnockDown
{
Ignores RangedAttack;
Begin:
if ( Health > 0 )
{
Sleep(GetAnimDuration('KnockDown'));
CloakBoss();
PlaySound(SaveMeSound, SLOT_Misc, 2.0,,500.0);
if ( KFGameType(Level.Game).FinalSquadNum == SyringeCount )
{
KFGameType(Level.Game).AddBossBuddySquad();
}
GotoState('Escaping');
}
else
{
GotoState('');
}
}
state FireChaingun
{
function BeginState()
{
Super.BeginState();
bMovingChaingunAttack = (SyringeCount>=2);
bChargingPlayer = bEndGameBoss && SyringeCount>=3 && FRand()<0.4f;
bCanStrafe = true;
}
function EndState()
{
bChargingPlayer = false;
Super.EndState();
bMovingChaingunAttack = false;
bCanStrafe = false;
}
function Tick( float Delta )
{
Super(KFMonster).Tick(Delta);
if( bChargingPlayer )
GroundSpeed = OriginalGroundSpeed * 2.3;
else GroundSpeed = OriginalGroundSpeed * 1.15;
}
function AnimEnd( int Channel )
{
if( MGFireCounter <= 0 )
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
GoToState('');
}
else if( bMovingChaingunAttack )
{
if( bFireAtWill && Channel!=1 )
return;
if( Controller.Target!=None )
Controller.Focus = Controller.Target;
bShotAnim = false;
bFireAtWill = True;
SetAnimAction('FireMG');
}
else
{
if ( Controller.Enemy != none )
{
if ( Controller.LineOfSightTo(Controller.Enemy) && FastTrace(GetBoneCoords('tip').Origin,Controller.Enemy.Location))
{
MGLostSightTimeout = 0.0;
Controller.Focus = Controller.Enemy;
Controller.FocalPoint = Controller.Enemy.Location;
}
else
{
MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35);
Controller.Focus = None;
}
Controller.Target = Controller.Enemy;
}
else
{
MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35);
Controller.Focus = None;
}
if( !bFireAtWill )
{
MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5);
}
else if ( FRand() < 0.03 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none )
{
// Randomly send out a message about Patriarch shooting chain gun(3% chance)
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 9, "");
}
bFireAtWill = True;
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireMG');
bWaitForAnim = true;
}
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
local float EnemyDistSq, DamagerDistSq;
global.TakeDamage(Damage,instigatedBy,hitlocation,Momentum,damageType);
if( bMovingChaingunAttack || Health<=0 )
return;
// if someone close up is shooting us, just charge them
if( InstigatedBy != none )
{
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
if( (ChargeDamage > 200 && DamagerDistSq < (500 * 500)) || DamagerDistSq < (100 * 100) )
{
SetAnimAction('transition');
GoToState('Charging');
return;
}
}
if( Controller.Enemy != none && InstigatedBy != none && InstigatedBy != Controller.Enemy )
{
EnemyDistSq = VSizeSquared(Location - Controller.Enemy.Location);
DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
}
if( InstigatedBy != none && (DamagerDistSq < EnemyDistSq || Controller.Enemy == none) )
{
MonsterController(Controller).ChangeEnemy(InstigatedBy,Controller.CanSee(InstigatedBy));
Controller.Target = InstigatedBy;
Controller.Focus = InstigatedBy;
if( DamagerDistSq < (500 * 500) )
{
SetAnimAction('transition');
GoToState('Charging');
}
}
}
Begin:
While( True )
{
if( !bMovingChaingunAttack )
Acceleration = vect(0,0,0);
if( MGLostSightTimeout > 0 && Level.TimeSeconds > MGLostSightTimeout )
{
Acceleration = vect(0,0,0);
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
GoToState('');
}
if( MGFireCounter <= 0 )
{
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMG');
HandleWaitForAnim('FireEndMG');
GoToState('');
}
// Give some randomness to the patriarch's firing (constantly fire after first stage passed)
if( Level.TimeSeconds > MGFireDuration && SyringeCount==0 )
{
if( AmbientSound != MiniGunSpinSound )
{
SoundVolume=185;
SoundRadius=200;
AmbientSound = MiniGunSpinSound;
}
Sleep(0.5 + FRand() * 0.75);
MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5);
}
else
{
if( bFireAtWill )
FireMGShot();
Sleep(0.05);
}
}
}
state FireMissile
{
function RangedAttack(Actor A)
{
if( SyringeCount>=2 )
{
Controller.Target = A;
Controller.Focus = A;
}
}
function BeginState()
{
if ( bEndGameBoss )
MissilesLeft = SyringeCount+Rand(SyringeCount);
else
MissilesLeft = 1+Rand(3);
Acceleration = vect(0,0,0);
}
function AnimEnd( int Channel )
{
local vector Start;
local Rotator R;
Start = GetBoneCoords('tip').Origin;
if( Controller.Target==None )
Controller.Target = Controller.Enemy;
if ( !SavedFireProperties.bInitialized )
{
SavedFireProperties.AmmoClass = MyAmmo.Class;
SavedFireProperties.ProjectileClass = Class'BossLAWProj';
SavedFireProperties.WarnTargetPct = 0.15;
SavedFireProperties.MaxRange = 10000;
SavedFireProperties.bTossed = False;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInitialized = true;
}
SavedFireProperties.bInstantHit = (SyringeCount<1);
SavedFireProperties.bTrySplash = (SyringeCount>=2);
R = AdjustAim(SavedFireProperties,Start,100);
PlaySound(RocketFireSound,SLOT_Interact,2.0,,TransientSoundRadius,,false);
Spawn(Class'BossLAWProj',,,Start,R);
bShotAnim = true;
Acceleration = vect(0,0,0);
SetAnimAction('FireEndMissile');
HandleWaitForAnim('FireEndMissile');
// Randomly send out a message about Patriarch shooting a rocket(5% chance)
if ( FRand() < 0.05 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 10, "");
}
if( MissilesLeft==0 )
GoToState('');
else
{
--MissilesLeft;
GoToState(,'SecondMissile');
}
}
Begin:
while ( true )
{
Acceleration = vect(0,0,0);
Sleep(0.1);
}
SecondMissile:
Acceleration = vect(0,0,0);
Sleep(0.5f);
AnimEnd(0);
}
State Escaping // Added god-mode if bEndGameBoss
{
Ignores RangedAttack;
function BeginState()
{
GiveUpTime = Level.TimeSeconds+20.f+FRand()*20.f;
Super.BeginState();
if ( bEndGameBoss ) {
bBlockActors = false;
// While escaping Pat doesn't blow up pipebombs -- PooSH
MotionDetectorThreat=0;
}
}
function EndState()
{
Super.EndState();
if( Health>0 )
bBlockActors = true;
MotionDetectorThreat = default.MotionDetectorThreat;
}
function Tick( float Delta )
{
if( Level.TimeSeconds>GiveUpTime )
{
BeginHealing();
return;
}
if( !bChargingPlayer )
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
GroundSpeed = OriginalGroundSpeed * 2.5;
Global.Tick(Delta);
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
if ( !bEndGameBoss )
global.TakeDamage(Damage,instigatedBy,hitlocation,Momentum,damageType);
}
}
State SneakAround
{
function BeginState()
{
super.BeginState();
SneakStartTime = Level.TimeSeconds+10.f+FRand()*15.f;
}
function EndState()
{
super.EndState();
LastSneakedTime = Level.TimeSeconds+20.f+FRand()*30.f;
if( Controller!=None && Controller.IsInState('PatFindWay') )
Controller.GoToState('ZombieHunt');
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
global.TakeDamage(Damage,instigatedBy,hitlocation,Momentum,damageType);
if( Health<=0 )
return;
// if someone close up is shooting us, just charge them
if( InstigatedBy!=none && VSizeSquared(Location - InstigatedBy.Location)<62500 )
GoToState('Charging');
}
Begin:
CloakBoss();
if( SyringeCount>=2 && FRand()<0.6 )
HardPatController(Controller).FindPathAround();
While( true )
{
Sleep(0.5);
if( !bCloaked && !bShotAnim )
CloakBoss();
if( !Controller.IsInState('PatFindWay') )
{
if( Level.TimeSeconds>SneakStartTime )
GoToState('');
if( !Controller.IsInState('WaitForAnim') && !Controller.IsInState('ZombieHunt') )
Controller.GoToState('ZombieHunt');
}
else SneakStartTime = Level.TimeSeconds+30.f;
}
}
defaultproperties
{
SaveMeSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_SaveMe'
RocketFireSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_FireRocket'
MiniGunFireSound=Sound'KF_BasePatriarch.Attack.Kev_MG_GunfireLoop'
MiniGunSpinSound=Sound'KF_BasePatriarch.Attack.Kev_MG_TurbineFireLoop'
MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Impale'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Fist'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPatriarch'
DetachedLegClass=Class'KFChar.SeveredLegPatriarch'
DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch'
DetachedSpecialArmClass=Class'KFChar.SeveredRocketArmPatriarch'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Death'
ScoringValue=1000
MenuName="Hard Pat"
ControllerClass=Class'ScrnZedPack.HardPatController'
AmbientSound=Sound'KF_BasePatriarch.Idle.Kev_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Patriarch_Freak'
LODBias=4.000000
Skins(0)=Combiner'KF_Specimens_Trip_T.gatling_cmb'
Skins(1)=Combiner'KF_Specimens_Trip_T.patriarch_cmb'
}

View file

@ -0,0 +1,160 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class HardPatController extends BossZombieController;
var NavigationPoint MidGoals[2];
var byte ReachOffset;
var Actor OldPathsCheck[3];
function Debugf( string S )
{
Level.GetLocalPlayerController().ClientMessage(S);
Log(S);
}
final function FindPathAround()
{
local Actor Res;
local NavigationPoint N;
local NavigationPoint OldPts[12];
local byte i;
local bool bResult;
if( Enemy==None || VSizeSquared(Enemy.Location-Pawn.Location)<360000 )
return; // No can do this.
// Attempt to find an alternative path to enemy.
/* This works by:
- finding shortest path to enemy
- block middle path point
- if the path is still about same to enemy, try block the new path and repeat up to 6 times.
*/
for( i=0; i<ArrayCount(OldPts); ++i )
{
Res = FindPathToward(Enemy);
if( Res==None )
break;
if( i>0 && CompareOldPaths() )
{
bResult = true;
break;
}
N = GetMidPoint();
if( N==None )
break;
N.bBlocked = true;
OldPts[i] = N;
if( i==0 )
SetOldPaths();
}
// Unblock temp blocked paths.
for( i=0; i<ArrayCount(OldPts); ++i )
if( OldPts[i]!=None )
OldPts[i].bBlocked = false;
if( !bResult )
return;
// Fetch results and switch state.
GetMidGoals();
if( ReachOffset<2 )
GoToState('PatFindWay');
}
final function NavigationPoint GetMidPoint()
{
local byte n;
for( n=0; n<ArrayCount(RouteCache); ++n )
if( RouteCache[n]==None )
break;
if( n==0 )
return None;
return NavigationPoint(RouteCache[(n-1)*0.5]);
}
final function bool CompareOldPaths()
{
local byte n,i;
for( i=0; i<6; ++i )
{
if( RouteCache[i]==None )
break;
for( n=0; n<ArrayCount(OldPathsCheck); ++n )
if( RouteCache[i]==OldPathsCheck[n] )
return false;
}
return true;
}
final function SetOldPaths()
{
local byte n;
for( n=0; n<ArrayCount(OldPathsCheck); ++n )
OldPathsCheck[n] = RouteCache[n+1];
if( RouteCache[1]==None )
OldPathsCheck[0] = RouteCache[0];
}
final function GetMidGoals()
{
local byte n;
for( n=0; n<ArrayCount(RouteCache); ++n )
if( RouteCache[n]==None )
break;
if( n==0 )
{
ReachOffset = 2;
return;
}
--n;
MidGoals[0] = NavigationPoint(RouteCache[n*0.5]);
MidGoals[1] = NavigationPoint(RouteCache[n]);
if( MidGoals[0]==MidGoals[1] )
ReachOffset = 1;
else ReachOffset = 0;
}
function bool CanKillMeYet()
{
return false;
}
state PatFindWay
{
Ignores Timer,SeePlayer,HearNoise,DamageAttitudeTo,EnemyChanged,Startle,Tick;
final function PickDestination()
{
if( ReachOffset>=2 )
{
GotoState('ZombieHunt');
return;
}
if( ActorReachable(MidGoals[ReachOffset]) )
{
MoveTarget = MidGoals[ReachOffset];
++ReachOffset;
}
else
{
MoveTarget = FindPathToward(MidGoals[ReachOffset]);
if( MoveTarget==None )
++ReachOffset;
}
}
function BreakUpDoor( KFDoorMover Other, bool bTryDistanceAttack )
{
Global.BreakUpDoor(Other,bTryDistanceAttack);
Pawn.GoToState('');
}
Begin:
PickDestination();
if( MoveTarget==None )
Sleep(0.5f);
else MoveToward(MoveTarget,MoveTarget,,False);
GoTo'Begin';
}
defaultproperties
{
}

View file

@ -0,0 +1,157 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class HardPat_CIRCUS extends HardPat;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd_CIRCUS.uax
simulated function CloakBoss()
{
local Controller C;
local int Index;
if ( bSpotted )
{
Visibility = 120;
if ( Level.NetMode==NM_DedicatedServer )
{
Return;
}
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
Skins[2] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
}
Visibility = 1;
bCloaked = true;
if ( Level.NetMode!=NM_Client )
{
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
if ( C.bIsPlayer && C.Enemy==Self )
{
C.Enemy = None; // Make bots lose sight with me.
}
}
}
if( Level.NetMode==NM_DedicatedServer )
{
Return;
}
Skins[1] = Shader'KF_Specimens_Trip_T.patriarch_invisible_gun';
Skins[0] = Shader'KF_Specimens_Trip_T.patriarch_invisible';
// Invisible - no shadow
if(PlayerShadow != none)
{
PlayerShadow.bShadowActive = false;
}
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(FinalBlend'KF_Specimens_Trip_T.patriarch_fizzle_FB', 1.0, true);
// Randomly send out a message about Patriarch going invisible(10% chance)
if ( FRand() < 0.10 )
{
// Pick a random Player to say the message
Index = Rand(Level.Game.NumPlayers);
for ( C = Level.ControllerList; C != none; C = C.NextController )
{
if ( PlayerController(C) != none )
{
if ( Index == 0 )
{
PlayerController(C).Speech('AUTO', 8, "");
break;
}
Index--;
}
}
}
}
// Speech notifies called from the anims
function PatriarchKnockDown()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_KnockedDown', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchEntrance()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_Entrance', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchVictory()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_Victory', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchMGPreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_WarnGun', SLOT_Misc, 2.0,true,1000.0);
}
function PatriarchMisslePreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_WarnRocket', SLOT_Misc, 2.0,true,1000.0);
}
// Taunt to use when doing the melee exploit radial attack
function PatriarchRadialTaunt()
{
if( NumNinjas > 0 && NumNinjas > NumLumberJacks )
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_TauntNinja', SLOT_Misc, 2.0,true,500.0);
}
else if( NumLumberJacks > 0 && NumLumberJacks > NumNinjas )
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_TauntLumberJack', SLOT_Misc, 2.0,true,500.0);
}
else
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_TauntRadial', SLOT_Misc, 2.0,true,500.0);
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_CIRCUS_T.Patriarch_Circus.Patriarch_Circus_CMB');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gatling_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gatling_D');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.PatGungoInvisible_cmb');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible_gun');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_fizzle_FB');
}
defaultproperties
{
SaveMeSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_SaveMe'
RocketFireSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_FireRocket'
MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_HitPlayer_Impale'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_HitPlayer_Fist'
JumpSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPatriarch_CIRCUS'
DetachedLegClass=Class'KFChar.SeveredLegPatriarch_CIRCUS'
DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch_CIRCUS'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_Death'
MenuName="Hard Ringmaster"
AmbientSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Patriarch.Kev_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip_CIRCUS.Patriarch_Circus'
Skins(0)=Combiner'KF_Specimens_Trip_CIRCUS_T.Patriarch_Circus.Patriarch_Circus_CMB'
Skins(1)=Combiner'KF_Specimens_Trip_T.gatling_cmb'
}

View file

@ -0,0 +1,8 @@
class HardPat_GRITTIER extends HardPat;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
defaultproperties
{
MenuName="Hard Grittier Pat"
Skins(1)=Combiner'ScrnZedPack_T.patriarch_grittier.patriarch_grittier_cmb'
}

View file

@ -0,0 +1,152 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class HardPat_HALLOWEEN extends HardPat;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd_HALLOWEEN.uax
simulated function CloakBoss()
{
local Controller C;
local int Index;
// No cloaking if zapped
if( bZapped )
{
return;
}
if ( bSpotted )
{
Visibility = 120;
if ( Level.NetMode==NM_DedicatedServer )
{
Return;
}
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
Skins[2] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
}
Visibility = 1;
bCloaked = true;
if ( Level.NetMode!=NM_Client )
{
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
if ( C.bIsPlayer && C.Enemy==Self )
{
C.Enemy = None; // Make bots lose sight with me.
}
}
}
if( Level.NetMode==NM_DedicatedServer )
{
Return;
}
Skins[1] = Shader'KF_Specimens_Trip_HALLOWEEN_T.Patriarch.Patriarch_Halloween_Invisible';
Skins[0] = Shader'KF_Specimens_Trip_T.patriarch_invisible_gun';
// Invisible - no shadow
if(PlayerShadow != none)
{
PlayerShadow.bShadowActive = false;
}
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(FinalBlend'KF_Specimens_Trip_T.patriarch_fizzle_FB', 1.0, true);
// Randomly send out a message about Patriarch going invisible(10% chance)
if ( FRand() < 0.10 )
{
// Pick a random Player to say the message
Index = Rand(Level.Game.NumPlayers);
for ( C = Level.ControllerList; C != none; C = C.NextController )
{
if ( PlayerController(C) != none )
{
if ( Index == 0 )
{
PlayerController(C).Speech('AUTO', 8, "");
break;
}
Index--;
}
}
}
}
// Speech notifies called from the anims
function PatriarchKnockDown()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_KnockedDown', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchEntrance()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_Entrance', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchVictory()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_Victory', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchMGPreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_WarnGun', SLOT_Misc, 2.0,true,1000.0);
}
function PatriarchMisslePreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_WarnRocket', SLOT_Misc, 2.0,true,1000.0);
}
// Taunt to use when doing the melee exploit radial attack
function PatriarchRadialTaunt()
{
if( NumNinjas > 0 && NumNinjas > NumLumberJacks )
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_TauntNinja', SLOT_Misc, 2.0,true,500.0);
}
else if( NumLumberJacks > 0 && NumLumberJacks > NumNinjas )
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_TauntLumberJack', SLOT_Misc, 2.0,true,500.0);
}
else
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_TauntRadial', SLOT_Misc, 2.0,true,500.0);
}
}
defaultproperties
{
SaveMeSound=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_SaveMe'
RocketFireSound=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_FireRocket'
MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_HitPlayer_Impale'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_HitPlayer_Fist'
JumpSound=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPatriarch_HALLOWEEN'
DetachedLegClass=Class'KFChar.SeveredLegPatriarch_HALLOWEEN'
DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch_HALLOWEEN'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd_HALLOWEEN.Patriarch.Kev_Death'
MenuName="Hard Sheriff"
AmbientSound=Sound'KF_BasePatriarch_HALLOWEEN.Kev_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip_HALLOWEEN.Patriarch_Halloween'
Skins(1)=Combiner'KF_Specimens_Trip_HALLOWEEN_T.Patriarch.Patriarch_RedneckZombie_CMB'
}

View file

@ -0,0 +1,154 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class HardPat_XMAS extends HardPat;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd_Xmas.uax
#exec OBJ LOAD FILE=KF_Specimens_Trip_XMAS_T.utx
simulated function CloakBoss()
{
local Controller C;
local int Index;
// No cloaking if zapped
if( bZapped )
{
return;
}
if ( bSpotted )
{
Visibility = 120;
if ( Level.NetMode==NM_DedicatedServer )
{
Return;
}
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
}
Visibility = 1;
bCloaked = true;
if ( Level.NetMode!=NM_Client )
{
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
if ( C.bIsPlayer && C.Enemy==Self )
{
C.Enemy = None; // Make bots lose sight with me.
}
}
}
if( Level.NetMode==NM_DedicatedServer )
{
Return;
}
Skins[1] = Shader'KF_Specimens_Trip_T.patriarch_invisible_gun';
Skins[0] = Shader'KF_Specimens_Trip_XMAS_T.patriarch_Santa.patriarch_santa_invisible_shdr';
// Invisible - no shadow
if(PlayerShadow != none)
{
PlayerShadow.bShadowActive = false;
}
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(FinalBlend'KF_Specimens_Trip_T.patriarch_fizzle_FB', 1.0, true);
// Randomly send out a message about Patriarch going invisible(10% chance)
if ( FRand() < 0.10 )
{
// Pick a random Player to say the message
Index = Rand(Level.Game.NumPlayers);
for ( C = Level.ControllerList; C != none; C = C.NextController )
{
if ( PlayerController(C) != none )
{
if ( Index == 0 )
{
PlayerController(C).Speech('AUTO', 8, "");
break;
}
Index--;
}
}
}
}
// Speech notifies called from the anims
function PatriarchKnockDown()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_KnockedDown', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchEntrance()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_Entrance', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchVictory()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_Victory', SLOT_Misc, 2.0,true,500.0);
}
function PatriarchMGPreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_WarnGun', SLOT_Misc, 2.0,true,1000.0);
}
function PatriarchMisslePreFire()
{
PlaySound(SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_WarnRocket', SLOT_Misc, 2.0,true,1000.0);
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_XMAS_T.gatling_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gatling_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gatling_D');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.PatGungoInvisible_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_XMAS_T.patriarch_Santa.patriarch_Santa_cmb');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_XMAS_T.patriarch_Santa_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_XMAS_T.patriarch_Santa_Diff');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_invisible_gun');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.patriarch_fizzle_FB');
//myLevel.AddPrecacheMaterial(Texture'kf_fx_trip_t.Gore.Patriarch_Gore_Limbs_Diff');
//myLevel.AddPrecacheMaterial(Texture'kf_fx_trip_t.Gore.Patriarch_Gore_Limbs_Spec');
}
defaultproperties
{
SaveMeSound=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_SaveMe'
RocketFireSound=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_FireRocket'
MiniGunFireSound=Sound'KF_BasePatriarch_xmas.Attack.Kev_MG_GunfireLoop'
MiniGunSpinSound=Sound'KF_BasePatriarch_xmas.Attack.Kev_MG_TurbineFireLoop'
MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_HitPlayer_Impale'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_HitPlayer_Fist'
JumpSound=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPatriarch_XMas'
DetachedLegClass=Class'KFChar.SeveredLegPatriarch_XMas'
DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch_XMas'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_Death'
MenuName="Hard Santa"
AmbientSound=SoundGroup'KF_EnemiesFinalSnd_Xmas.Patriarch.Kev_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip_Xmas.SantaClause_Patriarch'
Skins(0)=Combiner'KF_Specimens_Trip_XMAS_T.patriarch_Santa.patriarch_Santa_cmb'
Skins(1)=Combiner'KF_Specimens_Trip_XMAS_T.gatling_cmb'
}

View file

@ -0,0 +1,72 @@
class HuskFireProjectileSE extends KFChar.HuskFireProjectile;
simulated singular function Touch(Actor Other)
{
local vector HitLocation, HitNormal;
//Don't touch bulletwhip attachment. Taken from HuskFireProjectile
if ( Other == None || KFBulletWhipAttachment(Other) != none )
return;
if ( Other.bProjTarget || Other.bBlockActors ) {
LastTouched = Other;
if ( Velocity == vect(0,0,0) || Other.IsA('Mover') ) {
ProcessTouch(Other,Location);
LastTouched = None;
return;
}
if ( Other.TraceThisActor(HitLocation, HitNormal, Location, Location - 2*Velocity, GetCollisionExtent()) )
HitLocation = Location;
ProcessTouch(Other, HitLocation);
LastTouched = None;
if ( (Role < ROLE_Authority) && (Other.Role == ROLE_Authority) && (Pawn(Other) != None) )
ClientSideTouch(Other, HitLocation);
}
}
// Don't hit Zed extra collision cylinders
simulated function ProcessTouch(Actor Other, Vector HitLocation)
{
if ( ExtendedZCollision(Other) != none /* || Other.IsA('KFMonster') */ )
{
return;
}
// Don't let it hit this player, or blow up on another player
if (Other == none || Other == Instigator || Other.Base == Instigator)
return;
// Don't collide with bullet whip attachments
if (KFBulletWhipAttachment(Other) != none) {
return;
}
// Use the instigator's location if it exists. This fixes issues with
// the original location of the projectile being really far away from
// the real Origloc due to it taking a couple of milliseconds to
// replicate the location to the client and the first replicated location has
// already moved quite a bit.
if (Instigator != none) {
OrigLoc = Instigator.Location;
}
if (!bDud && ((VSizeSquared(Location - OrigLoc) < ArmDistSquared) || OrigLoc == vect(0,0,0))) {
if( Role == ROLE_Authority ) {
AmbientSound=none;
PlaySound(Sound'ProjectileSounds.PTRD_deflect04',,2.0);
Other.TakeDamage( ImpactDamage, Instigator, HitLocation, Normal(Velocity), ImpactDamageType );
}
bDud = true;
Velocity = vect(0,0,0);
LifeSpan=1.0;
SetPhysics(PHYS_Falling);
}
if (!bDud) {
Explode(HitLocation,Normal(HitLocation-Other.Location));
}
}
defaultproperties
{
}

View file

@ -0,0 +1,215 @@
// Code originally was taken from Scary Ghost's Super Zombies mutator
class HuskZombieControllerSE extends HuskZombieController;
var float aimAtFeetZDelta;
function bool DefendMelee(float Dist)
{
return (Dist < 1000);
}
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
{
local rotator FireRotation, TargetLook;
local float FireDist, TargetDist, ProjSpeed;
local actor HitActor;
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
local int realYaw;
local bool bDefendCloseRange, bClean, bLeadTargetNow;
local bool bWantsToAimAtFeet;
if ( FiredAmmunition.ProjectileClass != None )
projspeed = FiredAmmunition.ProjectileClass.default.speed;
// make sure bot has a valid target
if ( Target == None ) {
Target = Enemy;
if ( Target == None )
return Rotation;
}
FireSpot = Target.Location;
TargetDist = VSize(Target.Location - Pawn.Location);
// perfect aim at stationary objects
if ( Pawn(Target) == None ) {
if ( !FiredAmmunition.bTossed )
return rotator(Target.Location - projstart);
else {
FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);
SetRotation(Rotator(FireDir));
return Rotation;
}
}
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
bDefendCloseRange = ( (Target == Enemy) && DefendMelee(TargetDist) );
aimerror = AdjustAimError(aimerror,TargetDist,bDefendCloseRange,FiredAmmunition.bInstantHit, bLeadTargetNow);
// lead target with non instant hit projectiles
if ( bLeadTargetNow ) {
TargetVel = Target.Velocity;
// hack guess at projecting falling velocity of target
if ( Target.Physics == PHYS_Falling) {
if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z ) {
TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
} else {
TargetVel.Z = FMin(0, TargetVel.Z);
}
}
// more or less lead target (with some random variation)
FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
/**
* If the target is within 1000uu, offset the Z coordinate of the
* FireSpot vector with aimAtFeetZDelta. Otherwise, the husk will
* aim at behind the target, not at his feet.
*/
if (aimAtFeetZDelta != 0.0 && Target.Physics == PHYS_Falling && bDefendCloseRange) {
FireSpot.Z= Pawn.Location.Z + aimAtFeetZDelta;
}
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) ) {
// don't always lead far away targets, especially if they are moving sideways with respect to the bot
TargetLook = Target.Rotation;
if ( Target.Physics == PHYS_Walking )
TargetLook.Pitch = 0;
bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
}
else // make sure that bot isn't leading into a wall
bClean = FastTrace(FireSpot, ProjStart);
if ( !bClean) {
// reduce amount of leading
if ( FRand() < 0.3 )
FireSpot = Target.Location;
else
FireSpot = 0.5 * (FireSpot + Target.Location);
}
}
bClean = false; //so will fail first check unless shooting at feet
// Randomly determine if we should try and splash damage with the fire projectile
if( FiredAmmunition.bTrySplash ) {
if( Skill < 2.0 ) {
if(FRand() > 0.85) {
bWantsToAimAtFeet = true;
}
}
else if( Skill < 3.0 ) {
if(FRand() > 0.5) {
bWantsToAimAtFeet = true;
}
}
else if( Skill >= 3.0 ) {
if(FRand() > 0.25) {
bWantsToAimAtFeet = true;
}
}
}
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != None) && (((Target.Physics == PHYS_Falling)
&& (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z)
&& (bDefendCloseRange || bWantsToAimAtFeet))) ) {
HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
bClean = (HitActor == None);
//So if we're too close, and not jumping, bClean is false
//same distance but jumping, bClean is true
if ( !bClean ) {
FireSpot = HitLocation + vect(0,0,3);
bClean = FastTrace(FireSpot, ProjStart);
}
else
bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
/**
* Update the aimAtFeetZDelta variable with the appropriate offset
* once the Husk decides to aim at the target's feet. Update the
* default property so all Super Husks can access it
*/
if (bClean && TargetDist > 625.0) {
aimAtFeetZDelta= FireSpot.Z - Pawn.Location.Z;
}
}
if ( !bClean ) {
//try middle
FireSpot.Z = Target.Location.Z;
bClean = FastTrace(FireSpot, ProjStart);
}
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid ) {
FireSpot = LastSeenPos;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != None ) {
bCanFire = false;
FireSpot += 2 * Target.CollisionHeight * HitNormal;
}
bClean = true;
}
if( !bClean ) {
// try head
FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
bClean = FastTrace(FireSpot, ProjStart);
}
if ( !bClean && (Target == Enemy) && bEnemyInfoValid ) {
FireSpot = LastSeenPos;
if ( Pawn.Location.Z >= LastSeenPos.Z )
FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != None ) {
FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
if ( Monster(Pawn).SplashDamage() && (Skill >= 4) ) {
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != None )
FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
}
bCanFire = false;
}
}
// adjust for toss distance
if ( FiredAmmunition.bTossed ) {
FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
}
else {
FireDir = FireSpot - ProjStart;
}
FireRotation = Rotator(FireDir);
realYaw = FireRotation.Yaw;
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
FireDir = vector(FireRotation);
// avoid shooting into wall
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
FireSpot = ProjStart + FireDist * FireDir;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != None ) {
if ( HitNormal.Z < 0.7 ) {
FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
FireDir = vector(FireRotation);
FireSpot = ProjStart + FireDist * FireDir;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
}
if ( HitActor != None ) {
FireSpot += HitNormal * 2 * Target.CollisionHeight;
if ( Skill >= 4 ) {
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != None )
FireSpot += Target.CollisionHeight * HitNormal;
}
FireDir = Normal(FireSpot - ProjStart);
FireRotation = rotator(FireDir);
}
}
//Make it so the Husk always shoots the ground it the target is close
SetRotation(FireRotation);
return FireRotation;
}
defaultproperties
{
}

View file

@ -0,0 +1,69 @@
class JasonZombieController extends KFMonsterController;
// Custom Zombie Thinkerating
// By : Alex
var bool bDoneSpottedCheck;
state ZombieHunt
{
event SeePlayer(Pawn SeenPlayer)
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Scrake saying something
if ( !KFGameType(Level.Game).bDidSpottedScrakeMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 13, "");
KFGameType(Level.Game).bDidSpottedScrakeMessage = true;
}
bDoneSpottedCheck = true;
}
super.SeePlayer(SeenPlayer);
}
}
function TimedFireWeaponAtEnemy()
{
if ( (Enemy == None) || FireWeaponAt(Enemy) )
SetCombatTimer();
else SetTimer(0.01, True);
}
state ZombieCharge
{
// Don't do this in this state
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
{
return false;
}
function bool TryStrafe(vector sideDir)
{
return false;
}
function Timer()
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
}
WaitForAnim:
While( Monster(Pawn).bShotAnim )
Sleep(0.25);
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
Moving:
MoveToward(Enemy);
WhatToDoNext(17);
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
}
defaultproperties
{
}

View file

@ -0,0 +1,6 @@
class SeveredArmBrute extends SeveredArm;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.Brute.Brute_Right_Arm'
}

View file

@ -0,0 +1,7 @@
class SeveredArmFFP extends SeveredArm;
defaultproperties
{
DrawType=DT_Mesh
Mesh=SkeletalMesh'ScrnZedPack_A.ffp_arm'
}

View file

@ -0,0 +1,6 @@
class SeveredArmShiver extends SeveredArm;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.Shiver.Shiver_Arm'
}

View file

@ -0,0 +1,6 @@
class SeveredArmTeslaHusk extends SeveredArm;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.TeslaHusk.TeslaHusk_arm_gore'
}

View file

@ -0,0 +1,6 @@
class SeveredGunTeslaHusk extends SeveredArm;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.TeslaHusk.TeslaHusk_gun_gore'
}

View file

@ -0,0 +1,16 @@
//=============================================================================
// SeveredHeadPound
//=============================================================================
// Detached head gib class for the FleshPound
//=============================================================================
// Killing Floor Source
// Copyright (C) 2009 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class SeveredHeadBrute extends SeveredHead;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.Brute.Brute_head'
}

View file

@ -0,0 +1,6 @@
class SeveredHeadShiver extends SeveredHead;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.Shiver.Shiver_Head'
}

View file

@ -0,0 +1,6 @@
class SeveredHeadTeslaHusk extends SeveredHead;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.TeslaHusk.TeslaHusk_head_gore'
}

View file

@ -0,0 +1,6 @@
class SeveredLegBrute extends SeveredLeg;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.Brute.Brute_Left_Leg'
}

View file

@ -0,0 +1,7 @@
class SeveredLegFFP extends SeveredLeg;
defaultproperties
{
DrawType=DT_Mesh
Mesh=SkeletalMesh'ScrnZedPack_A.ffp_leg'
}

View file

@ -0,0 +1,6 @@
class SeveredLegShiver extends SeveredLeg;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.Shiver.Shiver_Leg'
}

View file

@ -0,0 +1,6 @@
class SeveredLegTeslaHusk extends SeveredLeg;
defaultproperties
{
StaticMesh=StaticMesh'ScrnZedPack_SM.TeslaHusk.TeslaHusk_leg_gore'
}

View file

@ -0,0 +1,235 @@
// most of the code c&p from ZEDBeamEffect
class TeslaBeam extends xEmitter;
var array<TeslaBeam> ChildBeams;
var Actor StartActor, EndActor; // actors to chain with beam
var name StartBoneName, EndBoneName; // bone names of StartActor/EndActor to use for beam ending locations
var transient float EffectEndTime; // time when beam should be hidden. 0 - don't hide
var ZedBeamSparks Sparks;
var Vector PrevLoc;
var Rotator PrevRot;
var Vector StartEffect, EndEffect;
var transient protected bool bInstigatorReplicated; // not sure if we need this
var transient protected float DestroyTime;
replication
{
reliable if ( Role == ROLE_Authority && bNetDirty )
StartActor, EndActor, StartBoneName, EndBoneName,
EffectEndTime;
}
simulated function PostNetReceive()
{
bInstigatorReplicated = bInstigatorReplicated || Instigator != none;
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
if ( TeslaHusk(Owner) != none )
Instigator = TeslaHusk(Owner);
}
// returns item index of ChildBeams array, which has ChainedActor as an EndActor
// returns -1, if ChainedActor isn't chained to EndActor
function int ChildIndex(Actor ChainedActor)
{
local int i;
if ( ChainedActor != none ) {
for ( i=0; i<ChildBeams.Length; ++i ) {
if ( ChildBeams[i] != none && ChildBeams[i].EndActor == ChainedActor)
return i;
}
}
return -1;
}
function bool IsChainedTo(Actor Other, optional byte ChainLevel)
{
local int i;
if ( Other == none || ChainLevel > 100 ) // prevents circular links
return false;
for ( i=0; i<ChildBeams.Length; ++i ) {
if ( ChildBeams[i] != none && (ChildBeams[i].EndActor == Other || ChildBeams[i].IsChainedTo(Other, ChainLevel+1)) )
return true;
}
return false;
}
function DestroyChildBeams()
{
local int i;
for ( i=0; i<ChildBeams.Length; ++i ) {
if ( ChildBeams[i] != none ) {
ChildBeams[i].Instigator = none;
}
}
ChildBeams.Length = 0;
}
simulated function Destroyed()
{
if ( Sparks != None )
{
Sparks.SetTimer(0, false);
Sparks.mRegen = false;
Sparks.LightType = LT_None;
}
if ( ChildBeams.Length > 0 )
DestroyChildBeams();
Super.Destroyed();
}
simulated function SetBeamLocation()
{
if ( StartBoneName == '' )
StartEffect = StartActor.Location;
else {
StartEffect = StartActor.GetBoneCoords(StartBoneName).Origin;
}
if ( EndBoneName == '' )
EndEffect = EndActor.Location;
else {
EndEffect = EndActor.GetBoneCoords(EndBoneName).Origin;
}
SetLocation(StartEffect);
bHidden = false;
}
simulated function Vector SetBeamRotation()
{
SetRotation(Rotator(EndEffect - StartEffect));
return Normal(EndEffect - StartEffect);
}
simulated function bool CheckMaxEffectDistance(PlayerController P, vector SpawnLocation)
{
return !P.BeyondViewDistance(SpawnLocation,1000);
}
simulated function Tick(float dt)
{
local float LocDiff, RotDiff, WiggleMe;
local Vector BeamDir;
local PlayerController LocalPC;
if ( Instigator == none ) {
if ( Role == ROLE_Authority ) {
if ( Level.NetMode != NM_Standalone && DestroyTime == 0 )
DestroyTime = Level.TimeSeconds + 1.0; // give some time to replicate
else if ( DestroyTime < Level.TimeSeconds )
Destroy();
}
else if ( bInstigatorReplicated )
Destroy();
bHidden = true;
}
else {
bHidden = StartActor == none || EndActor == none || (EffectEndTime != 0 && EffectEndTime < Level.TimeSeconds);
}
if ( Sparks != none )
Sparks.bHidden = bHidden;
if ( bHidden ) {
if ( ChildBeams.Length > 0 )
DestroyChildBeams(); // destroy them, because they are comming from the EndActor
return;
}
// set beam start location
SetBeamLocation();
BeamDir = SetBeamRotation();
if ( Level.NetMode != NM_DedicatedServer )
{
if ( Sparks == None && EffectIsRelevant(EndEffect, false) )
{
LocalPC = Level.GetLocalPlayerController();
if ( LocalPC != none && CheckMaxEffectDistance(LocalPC, Location) )
Sparks = Spawn(class'ZedBeamSparks', self);
}
}
/*
if ( Level.bDropDetail || Level.DetailMode == DM_Low )
{
bDynamicLight = false;
LightType = LT_None;
}
else if ( bDynamicLight )
LightType = LT_Steady;
*/
mSpawnVecA = EndEffect;
LocDiff = VSize((Location - PrevLoc) * Vect(1,1,5));
RotDiff = VSize(Vector(Rotation) - Vector(PrevRot));
WiggleMe = FMax(LocDiff*0.02, RotDiff*4.0);
mWaveAmplitude = FMax(1.0, mWaveAmplitude - mWaveAmplitude*1.0*dt);
mWaveAmplitude = FMin(16.0, mWaveAmplitude + WiggleMe);
PrevLoc = Location;
PrevRot = Rotation;
if ( Sparks != None )
{
Sparks.SetLocation( EndEffect - BeamDir*10.0 );
Sparks.SetRotation( Rotation);
Sparks.mRegenRange[0] = Sparks.DesiredRegen;
Sparks.mRegenRange[1] = Sparks.DesiredRegen;
Sparks.bDynamicLight = true;
}
}
defaultproperties
{
mParticleType=PT_Beam
mMaxParticles=3
mRegenDist=65.000000
mSpinRange(0)=45000.000000
mSizeRange(0)=6.000000
mColorRange(0)=(B=240,G=240,R=240)
mColorRange(1)=(B=240,G=240,R=240)
mAttenuate=False
mAttenKa=0.000000
mWaveFrequency=0.060000
mWaveAmplitude=8.000000
mWaveShift=100000.000000
mBendStrength=3.000000
mWaveLockEnd=True
LightHue=100
LightSaturation=100
LightBrightness=255.000000
LightRadius=4.000000
bNetTemporary=False
bReplicateInstigator=True
bReplicateMovement=False
RemoteRole=ROLE_SimulatedProxy
NetUpdateFrequency=5.000000
LifeSpan=10.000000
Skins(0)=FinalBlend'KFZED_FX_T.Energy.ZED_FX_Beam_FB'
Style=STY_Additive
bNetNotify=True
}

View file

@ -0,0 +1,893 @@
class TeslaHusk extends ZombieHusk;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd_CIRCUS.uax
#exec OBJ LOAD FILE=ScrnZedPack_A.ukx
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
#exec OBJ LOAD FILE=ScrnZedPack_SM.usx
var class<TeslaBeam> BeamClass;
var TeslaBeam PrimaryBeam;
var float MaxPrimaryBeamRange; // how far Husk can shoot with his tesla gun?
var float MaxChildBeamRange; // max distance from primary target for a chain reaction
var float ChainedBeamRangeExtension; // if pawn is already chained, chain max range is multiplied by this value
// used for optimization
var private float MaxPrimaryBeamRangeSquared, MaxChildBeamRangeSquared;
var float Energy;
var float EnergyMax;
var float EnergyRestoreRate;
var() float DischargeDamage; // beam damage per second
var() float ChainDamageMult; // how much damage drops on the next chain
var() class<DamageType> MyDamageType;
var() byte MaxChainLevel;
var() int MaxChainActors;
var ZedBeamSparks DecapitationSparks;
var bool bChainThoughZED; // chain tesla beam though a zed, which is between target and the husk, to extend firing range
var transient float NextChainZedSearchTime;
var transient float ChainRebuildTimer; // how many seconds between rebuilding chained actors
var float DischargeRate; // time between damaging chained pawns. Do not set it too low due to rounding precission of DischargeDamage
var transient float LastDischargeTime;
// Tesla Husks are able to heal each other.
var() float HealRate; // Percent of HealthMax to heal per second
var() float HealEnergyDrain; // amount of energy required to heal 1%
var transient float NextHealAttemptTime, LastHealTime;
struct SActorDamages
{
var Actor Victim;
var int Damage;
};
var protected array<SActorDamages> DischargedActors;
simulated function PostBeginPlay()
{
// Difficulty Scaling
if (Level.Game != none && !bDiffAdjusted)
{
if( Level.Game.GameDifficulty < 2.0 ) {
// beginner
DischargeDamage *= 0.50;
EnergyMax *= 0.50;
EnergyRestoreRate *= 0.50;
}
else if( Level.Game.GameDifficulty < 4.0 ) {
// normal
}
else if( Level.Game.GameDifficulty < 5.0 ) {
//hard
DischargeDamage *= 1.35;
EnergyMax *= 1.35;
EnergyRestoreRate *= 1.35;
MaxPrimaryBeamRange *= 1.35;
MaxChildBeamRange *= 1.35;
}
else if( Level.Game.GameDifficulty < 7.0 ) {
// suicidal
DischargeDamage *= 1.55;
EnergyMax *= 1.55;
EnergyRestoreRate *= 1.55;
MaxPrimaryBeamRange *= 1.55;
MaxChildBeamRange *= 1.55;
}
else {
// HoE
DischargeDamage *= 1.75;
EnergyMax *= 1.75;
EnergyRestoreRate *= 1.75;
MaxPrimaryBeamRange *= 1.75;
MaxChildBeamRange *= 1.75;
// disabled due to bugs
//bChainThoughZED = true;
}
}
Energy = EnergyMax;
MaxPrimaryBeamRangeSquared = MaxPrimaryBeamRange * MaxPrimaryBeamRange;
MaxChildBeamRangeSquared = MaxChildBeamRange * MaxChildBeamRange;
NextHealAttemptTime = Level.TimeSeconds + 5.0;
super.PostBeginPlay();
BurnDamageScale = 1.0; // no fire damage resistance
}
simulated function HideBone(name boneName)
{
local int BoneScaleSlot;
local bool bValidBoneToHide;
if( boneName == LeftThighBone )
{
boneScaleSlot = 0;
bValidBoneToHide = true;
}
else if ( boneName == RightThighBone )
{
boneScaleSlot = 1;
bValidBoneToHide = true;
}
else if( boneName == RightFArmBone )
{
boneScaleSlot = 2;
bValidBoneToHide = true;
}
else if ( boneName == LeftFArmBone )
{
boneScaleSlot = 3;
bValidBoneToHide = true;
}
else if ( boneName == HeadBone )
{
// Only scale the bone down once
if( SeveredHead == none )
{
bValidBoneToHide = true;
boneScaleSlot = 4;
}
else
{
return;
}
}
else if ( boneName == 'spine' )
{
bValidBoneToHide = true;
boneScaleSlot = 5;
}
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
if( bValidBoneToHide )
{
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
}
}
function TeslaBeam SpawnTeslaBeam()
{
local TeslaBeam beam;
beam = spawn(BeamClass, self);
if ( beam != none ) {
beam.Instigator = self;
}
return beam;
}
function TeslaBeam SpawnPrimaryBeam()
{
if ( PrimaryBeam == none ) {
PrimaryBeam = SpawnTeslaBeam();
PrimaryBeam.StartActor = self;
PrimaryBeam.StartBoneName = 'Barrel';
}
return PrimaryBeam;
}
function FreePrimaryBeam()
{
if ( PrimaryBeam != none ) {
//PrimaryBeam.DestroyChildBeams();
PrimaryBeam.EndActor = none;
PrimaryBeam.EffectEndTime = Level.TimeSeconds - 1;
PrimaryBeam.Instigator = none; // mark to delete
PrimaryBeam = none;
}
}
function RangedAttack(Actor A)
{
local float Dist;
local KFMonster M;
if ( bShotAnim || bDecapitated )
return;
Dist = VSize(A.Location - Location);
if ( Physics == PHYS_Swimming )
{
SetAnimAction('Claw');
bShotAnim = true;
}
else if ( Energy < 25 && Dist < MeleeRange + CollisionRadius + A.CollisionRadius ) // do melee hits only when low on energy
{
bShotAnim = true;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
else if ( Energy > 25 && Dist < MaxPrimaryBeamRange*(0.7 + 0.3*frand()) )
{
bShotAnim = true;
SetAnimAction('ShootBurns');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
}
else if ( bChainThoughZED && Energy > 50 && Controller.Enemy == A && KFPawn(A) != none
&& NextChainZedSearchTime < Level.TimeSeconds
&& Dist < MaxPrimaryBeamRange + MaxChildBeamRange )
{
NextChainZedSearchTime = Level.TimeSeconds + 3.0;
foreach VisibleCollidingActors(class'KFMonster', M, MaxPrimaryBeamRange*0.9) {
if ( !M.bDeleteMe && M.Health > 0 && TeslaHusk(M) == none
&& VSizeSquared(M.Location - A.Location) < MaxChildBeamRangeSquared*0.9
&& M.FastTrace(A.Location, M.Location) )
{
Controller.Target = M;
bShotAnim = true;
SetAnimAction('ShootBurns');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
return;
}
}
}
}
function n_SpawnBeam()
{
SpawnPrimaryBeam();
if ( PrimaryBeam == none ) {
log("Unable to spawn Primary Beam!", 'TeslaHusk');
return;
}
PrimaryBeam.EndActor = Controller.Target;
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
GotoState('Shooting');
}
function SpawnTwoShots()
{
warn("TeslaHuks.SpawnTwoShots() must not be called");
}
simulated function Tick( float Delta )
{
if ( Role < ROLE_Authority ) {
if ( bDecapitated && Health > 0 && DecapitationSparks == none ) {
SpawnDecapitationEffects();
}
}
Super(KFMonster).Tick(Delta);
if ( Role == ROLE_Authority ) {
if ( Energy < EnergyMax )
Energy += EnergyRestoreRate * Delta;
if ( Energy > 50 && NextHealAttemptTime < Level.TimeSeconds ) {
NextHealAttemptTime = Level.TimeSeconds + 3.0;
TryHealing();
}
}
}
function TryHealing()
{
local Controller C;
local KFMonsterController MyMC;
local KFMonster BestPatient, Candidate;
local bool bAnyPatients;
MyMC = KFMonsterController(Controller);
if ( MyMC == none )
return; // just in case
if ( MyMC.Enemy != none && VSizeSquared(MyMC.Enemy.Location - Location) < MaxPrimaryBeamRangeSquared )
return; // no healing when need to fight
for ( C=Level.ControllerList; C!=none; C=C.NextController ) {
Candidate = KFMonster(C.Pawn);
if ( Candidate != none && Candidate != self && Candidate.Health > 0
&& (Candidate.IsA('TeslaHusk') || Candidate.IsA('ZombieFleshpound') || Candidate.IsA('FemaleFP')) )
{
bAnyPatients = true;
if ( (BestPatient == none || Candidate.Health/Candidate.HealthMax < BestPatient.Health/BestPatient.HealthMax )
&& VSizeSquared(C.Pawn.Location - Location) < MaxPrimaryBeamRangeSquared )
BestPatient = Candidate;
}
}
if ( !bAnyPatients )
NextHealAttemptTime = Level.TimeSeconds + 10.0; // no other husks on the map - so no need to do searching so often
else if ( BestPatient != none ) {
if ( BestPatient.Health < BestPatient.HealthMax * 0.99 ) {
MyMC.Target = BestPatient;
MyMC.ChangeEnemy(BestPatient, MyMC.CanSee(BestPatient));
MyMC.GotoState('RangedAttack');
LastHealTime = Level.TimeSeconds + 3.0; // do not heal until beam is spawned
GotoState('Healing');
}
else
NextHealAttemptTime = Level.TimeSeconds + 1.0; // if there are other Tesla Husks nearby, then check their health each second
}
else
NextHealAttemptTime = Level.TimeSeconds + 3.0; // there are other Tesla Husks on the map, but not too close
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
if ( PrimaryBeam != none )
PrimaryBeam.Instigator = none;
if ( DecapitationSparks != none )
DecapitationSparks.Destroy();
AmbientSound = none;
super.Died(Killer, damageType, HitLocation);
}
simulated event PlayDying(class<DamageType> DamageType, vector HitLoc)
{
super.PlayDying(DamageType, HitLoc);
if ( DecapitationSparks != none )
DecapitationSparks.Destroy();
AmbientSound = none;
}
// instead of roaming around, activate self-destruct
function RemoveHead()
{
Intelligence = BRAINS_Retarded; // Headless dumbasses!
bDecapitated = true;
DECAP = true;
DecapTime = Level.TimeSeconds;
bShotAnim = true;
SetAnimAction('KnockDown');
Acceleration = vect(0, 0, 0);
Velocity.X = 0;
Velocity.Y = 0;
Controller.GoToState('WaitForAnim');
KFMonsterController(Controller).bUseFreezeHack = True;
SetGroundSpeed(0);
AirSpeed = 0;
WaterSpeed = 0;
// No more raspy breathin'...cuz he has no throat or mouth :S
AmbientSound = MiscSound;
// super.TakeDamage(LastDamageAmount) isn't called yet, so set self-destruct sequence only if
// zed can survive the hit
if( Health > LastDamageAmount && Energy > 25 ) {
SpawnDecapitationEffects();
BleedOutTime = Level.TimeSeconds + BleedOutDuration;
GotoState('SelfDestruct');
}
PlaySound(DecapitationSound, SLOT_Misc,1.30,true,525);
}
// Tesla Husk doesn't get stunned by weak sniper shots
// function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx )
// {
// super(KFMonster).PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx );
// }
simulated function Destroyed()
{
if ( PrimaryBeam != none )
PrimaryBeam.Destroy();
if ( DecapitationSparks != none )
DecapitationSparks.Destroy();
super.Destroyed();
}
simulated function SpawnDecapitationEffects()
{
if (Level.NetMode == NM_DedicatedServer)
return;
DecapitationSparks = spawn(class'ZedBeamSparks', self,, GetBoneCoords(HeadBone).Origin, Rotation);
}
simulated function int DoAnimAction( name AnimName )
{
// faked animation with forces to play the end of shoot animation (where Huks is putting his gun down)
if ( AnimName == 'ShootBurnsEnd' ) {
PlayAnim('ShootBurns');
SetAnimFrame(120, 0, 1);
return 0;
}
return super.DoAnimAction(AnimName);
}
function float RangedAttackTime()
{
return 5.0;
}
// Overridden so that anims don't get interrupted on the server if one is already playing
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
{
local coords C;
local vector HeadLoc, B, M, diff;
local float t, DotMM, Distance;
local int look;
local bool bUseAltHeadShotLocation;
local bool bWasAnimating;
if (HeadBone == '')
return False;
// If we are a dedicated server estimate what animation is most likely playing on the client
if (Level.NetMode == NM_DedicatedServer)
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
// Only play the idle anim if we're not already doing a different anim.
// This prevents anims getting interrupted on the server and borking things up - Ramm
if( !IsAnimating(0) && !IsAnimating(1) )
{
if (bIsCrouched)
{
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
}
else
{
bUseAltHeadShotLocation=true;
}
}
else
{
bWasAnimating = true;
}
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
if( !bWasAnimating )
{
SetAnimFrame(0.5);
}
}
if( bUseAltHeadShotLocation )
{
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
AdditionalScale *= OnlineHeadshotScale;
}
else
{
C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis) -5.0*C.XAxis - 2.0*C.YAxis;
}
//ServerHeadLocation = HeadLoc;
// Express snipe trace line in terms of B + tM
B = loc;
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
// Find Point-Line Squared Distance
diff = HeadLoc - B;
t = M Dot diff;
if (t > 0)
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
}
else
t = 0;
Distance = Sqrt(diff Dot diff);
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
}
state Healing
{
ignores TryHealing;
function BeginState()
{
//log("Entering Healing State @ " $ Level.TimeSeconds, 'TeslaHusk');
LastHealTime = Level.TimeSeconds + 3.0; // do not heal until beam is spawned
}
function EndState()
{
local name SeqName;
local float AnimFrame, AnimRate;
//log("Exiting Healing State @ " $ Level.TimeSeconds, 'TeslaHusk');
NextHealAttemptTime = Level.TimeSeconds + 5.0;
FreePrimaryBeam();
// end shooting animation
GetAnimParams(0, SeqName, AnimFrame, AnimRate);
if ( SeqName == 'ShootBurns' && AnimFrame < 120 )
SetAnimAction('ShootBurnsEnd'); // faked anim
Controller.Enemy = none;
KFMonsterController(Controller).FindNewEnemy();
}
function n_SpawnBeam()
{
SpawnPrimaryBeam();
if ( Controller != none )
PrimaryBeam.EndActor = Controller.Target;
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
LastHealTime = Level.TimeSeconds - 1.0; // start healing
}
function bool FlipOver()
{
if ( global.FlipOver() ) {
GotoState('');
return true;
}
return false;
}
function Tick(float DeltaTime)
{
local KFMonster Patient;
local int HealthToAdd, HeadHealthToAdd;
local int HeadHealthMax;
super.Tick(DeltaTime);
if ( Level.TimeSeconds >= LastHealTime+0.25 ) {
if ( PrimaryBeam == none || Energy <= 0 || Level.TimeSeconds > PrimaryBeam.EffectEndTime ) {
GotoState('');
return;
}
NextHealAttemptTime = Level.TimeSeconds + 3.0;
Patient = KFMonster(PrimaryBeam.EndActor);
if ( Patient == none || Patient.Health <= 0 || Patient.Health >= Patient.HealthMax )
GotoState('');
else {
HealthToAdd = min(Patient.HealthMax*HealRate*(Level.TimeSeconds-LastHealTime), Patient.HealthMax - Patient.Health);
if ( HealthToAdd*HealEnergyDrain > Energy )
HealthToAdd = Energy / HealEnergyDrain;
if ( !Patient.bDecapitated && Patient.HeadHealth > 0 ) {
HeadHealthMax = Patient.default.HeadHealth * Patient.DifficultyHeadHealthModifer() * Patient.NumPlayersHeadHealthModifer();
HeadHealthToAdd = min(HeadHealthMax*HealRate*2.0*(Level.TimeSeconds-LastHealTime), HeadHealthMax - Patient.HeadHealth);
Patient.HeadHealth += HeadHealthToAdd;
}
Patient.Health += HealthToAdd;
Energy -= HealthToAdd*HealEnergyDrain;
LastHealTime = Level.TimeSeconds;
}
}
}
}
// todo: make custom controller, moving states there
state Shooting
{
ignores TryHealing;
function BeginState()
{
//log("Entering Shooting State @ " $ Level.TimeSeconds, 'TeslaHusk');
if ( PrimaryBeam == none ) {
SpawnPrimaryBeam();
PrimaryBeam.EndActor = Controller.Target;
}
PrimaryBeam.EffectEndTime = Level.TimeSeconds + 2.5;
BuildChain();
LastDischargeTime = Level.TimeSeconds;
Discharge(DischargeRate);
}
function EndState()
{
local name SeqName;
local float AnimFrame, AnimRate;
//log("Exiting Shooting State @ " $ Level.TimeSeconds, 'TeslaHusk');
FreePrimaryBeam();
// end shooting animation
GetAnimParams(0, SeqName, AnimFrame, AnimRate);
if ( SeqName == 'ShootBurns' && AnimFrame < 120 )
SetAnimAction('ShootBurnsEnd'); // faked anim
}
function BuildChain()
{
local int ChainsRemaining;
ChainRebuildTimer = default.ChainRebuildTimer;
ChainsRemaining = MaxChainActors;
if ( PrimaryBeam != none && KFDoorMover(PrimaryBeam.EndActor) == none )
SpawnChildBeams(PrimaryBeam, 1, ChainsRemaining);
}
function SpawnChildBeams(TeslaBeam MasterBeam, byte level, out int ChainsRemaining)
{
local Pawn P;
local TeslaBeam ChildBeam;
local int i;
if ( MasterBeam == none || level > MaxChainLevel || ChainsRemaining <= 0 )
return;
for ( i=0; i<MasterBeam.ChildBeams.length; ++i ) {
if ( MasterBeam.ChildBeams[i] == none )
MasterBeam.ChildBeams.remove(i--, 1);
}
if ( MasterBeam.EndActor == none || MasterBeam.EndActor.bDeleteMe )
return;
foreach MasterBeam.EndActor.VisibleCollidingActors(class'Pawn', P, MaxChildBeamRange, MasterBeam.EndActor.Location) {
if ( !P.bDeleteMe && P.Health > 0 && P != MasterBeam.StartActor && P != MasterBeam.EndActor
&& TeslaHusk(P) == none && !PrimaryBeam.IsChainedTo(P) )
{
i = MasterBeam.ChildIndex(P);
if ( i != -1 ) {
ChildBeam = MasterBeam.ChildBeams[i];
ChildBeam.EffectEndTime = PrimaryBeam.EffectEndTime;
}
else {
ChildBeam = SpawnTeslaBeam();
if ( ChildBeam != none ) {
ChildBeam.StartActor = MasterBeam.EndActor;
ChildBeam.EndActor = P;
ChildBeam.EffectEndTime = PrimaryBeam.EffectEndTime;
MasterBeam.ChildBeams[MasterBeam.ChildBeams.length] = ChildBeam;
}
}
if ( --ChainsRemaining <= 0 )
return;
}
}
for ( i=0; i<MasterBeam.ChildBeams.length && ChainsRemaining > 0; ++i ) {
SpawnChildBeams(MasterBeam.ChildBeams[i], level+1, ChainsRemaining);
}
}
function ChainDamage(TeslaBeam Beam, float Damage, byte ChainLevel)
{
local int i;
local float DmgMult;
if ( Beam == none || Beam.EndActor == none || Energy < 0)
return;
Damage = clamp(Damage, 1, Energy);
// look if actor already received a damage during this discharge
for ( i=0; i<DischargedActors.length; ++i ) {
if ( DischargedActors[i].Victim == Beam.EndActor ) {
if ( DischargedActors[i].Damage >= Damage )
return; // Victim already received enough damage
else {
Damage -= DischargedActors[i].Damage;
DischargedActors[i].Damage += Damage;
break;
}
}
}
if ( i == DischargedActors.length ) {
// new victim
DischargedActors.insert(i, 1);
DischargedActors[i].Victim = Beam.EndActor;
DischargedActors[i].Damage = Damage;
}
// deal higher damage to monsters, but do not drain energy quicker
DmgMult = 1.0;
if ( KFMonster(Beam.EndActor) != none ) {
DmgMult = 10.0;
}
// if pawn is already chained, then damage him until he gets out of the primary range
Beam.SetBeamLocation(); // ensure that StartEffect and EndEffect are set
if ( KFDoorMover(Beam.EndActor) != none ) {
Beam.EndActor.TakeDamage(Damage*10.0,Self,Location,vect(0,0,0), class'DamTypeUnWeld');
}
else if ( VSizeSquared(Beam.EndEffect - Beam.StartEffect) <= MaxPrimaryBeamRangeSquared * ChainedBeamRangeExtension
&& FastTrace(Beam.EndEffect, Beam.StartEffect) )
{
Beam.EndActor.TakeDamage(Damage*DmgMult, self, Beam.EndActor.Location, Beam.SetBeamRotation(), MyDamageType);
Energy -= Damage;
Damage *= ChainDamageMult;
ChainLevel++;
if ( Damage > 0 && ChainLevel <= MaxChainLevel) {
for ( i=0; i<Beam.ChildBeams.length && Energy > 0; ++i )
ChainDamage(Beam.ChildBeams[i], Damage, ChainLevel);
}
}
else {
Beam.Instigator = none; // delete beam
}
}
function Discharge(float DeltaTime)
{
LastDischargeTime = Level.TimeSeconds;
DischargedActors.length = 0;
ChainDamage(PrimaryBeam, DischargeDamage*DeltaTime, 0);
if (Energy <= 0)
GotoState(''); // out of energy - stop shooting
}
function bool FlipOver()
{
if ( global.FlipOver() ) {
GotoState('');
return true;
}
return false;
}
function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
//log("Shooting.Tick @ " $ Level.TimeSeconds $ ":" @"Energy="$Energy @"PrimaryBeam="$PrimaryBeam, 'TeslaHusk');
ChainRebuildTimer -= DeltaTime;
if ( Energy <= 0 || PrimaryBeam == none || PrimaryBeam.EndActor == none || Level.TimeSeconds > PrimaryBeam.EffectEndTime ) {
GotoState('');
}
else if ( LastDischargeTime + DischargeRate <= Level.TimeSeconds ) {
if ( ChainRebuildTimer <= 0 )
BuildChain();
Discharge(Level.TimeSeconds - LastDischargeTime); // damage chained actors
}
}
}
state SelfDestruct
{
ignores PlayDirectionalHit, RangedAttack, MeleeDamageTarget;
function BeginState()
{
local TeslaHuskEMPCharge MyNade;
MyNade = spawn(class'TeslaHuskEMPCharge', self,, Location);
if ( MyNade != none ) {
AttachToBone(MyNade, RootBone);
MyNade.SetTimer(3.0, false);
MyNade.bTimerSet = true;
MyNade.Damage = max(30, Energy * 0.65);
MyNade.Killer = LastDamagedBy;
}
else {
// wtf?
Died(LastDamagedBy.Controller,class'DamTypeBleedOut',Location);
}
}
function bool CanGetOutOfWay()
{
return false;
}
// takes only 25% of damage
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
global.TakeDamage(Damage*0.25,instigatedBy,hitlocation,momentum,damageType,HitIndex);
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(Shader'ScrnZedPack_T.TeslaHusk.TeslaHusk_SHADER');
myLevel.AddPrecacheMaterial(FinalBlend'KFZED_FX_T.Energy.ZED_FX_Beam_FB');
}
defaultproperties
{
BeamClass=Class'ScrnZedPack.TeslaBeam'
MaxPrimaryBeamRange=300.000000
MaxChildBeamRange=150.000000
ChainedBeamRangeExtension=1.350000
Energy=100.000000
EnergyMax=100.000000
EnergyRestoreRate=20.000000
DischargeDamage=10.000000
ChainDamageMult=0.850000
MyDamageType=Class'ScrnZedPack.DamTypeTesla'
MaxChainLevel=5
MaxChainActors=20
DischargeRate=0.250000
HealRate=0.250000
HealEnergyDrain=0.100000
ProjectileFireInterval=5.000000
BurnDamageScale=1.000000
MoanVoice=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Talk'
BleedOutDuration=5.000000
MiscSound=Sound'KF_FY_ZEDV2SND.foley.WEP_ZEDV2_Projectile_Loop'
DecapitationSound=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Explode'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Jump'
ColHeight=30.000000
ProjectileBloodSplatClass=None
DetachedArmClass=Class'ScrnZedPack.SeveredArmTeslaHusk'
DetachedLegClass=Class'ScrnZedPack.SeveredLegTeslaHusk'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadTeslaHusk'
DetachedSpecialArmClass=Class'ScrnZedPack.SeveredGunTeslaHusk'
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkB"
BurningWalkAnims(1)="WalkL"
BurningWalkAnims(2)="WalkR"
OnlineHeadshotOffset=(X=22.000000,Z=50.000000)
HeadHealth=250.000000
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd_CIRCUS.Husk.Husk_Challenge'
ScoringValue=25
GroundSpeed=140.000000
WaterSpeed=120.000000
HealthMax=800.000000
Health=800
MenuName="Tesla Husk"
AmbientSound=Sound'KF_BaseHusk_CIRCUS.Husk_IdleLoop'
Mesh=SkeletalMesh'ScrnZedPack_A.TeslaHuskMesh'
Skins(0)=Shader'ScrnZedPack_T.TeslaHusk.TeslaHusk_SHADER'
}

View file

@ -0,0 +1,170 @@
// used for Tesla Husk self-destruct explosion on decapitation
class TeslaHuskEMPCharge extends Nade;
var() class<Emitter> ExplosionEffect;
var Pawn Killer;
function Timer()
{
if ( bHidden )
Destroy();
else if ( Instigator != none && Instigator.Health > 0 )
Explode(Location, vect(0,0,1));
else
Disintegrate(Location, vect(0,0,1));
}
// don't get destroyed by a Siren
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
}
simulated function Explode(vector HitLocation, vector HitNormal)
{
local PlayerController LocalPlayer;
bHasExploded = True;
BlowUp(HitLocation);
PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],,2.0);
if ( EffectIsRelevant(Location,false) )
{
Spawn(ExplosionEffect,,, HitLocation, rotator(vect(0,0,1)));
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
}
// Shake nearby players screens
LocalPlayer = Level.GetLocalPlayerController();
if ( (LocalPlayer != None) && (VSize(Location - LocalPlayer.ViewTarget.Location) < (DamageRadius * 1.5)) )
LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
if ( Instigator != none ) {
// blow up the instigator
Instigator.TakeDamage(1000000, Killer, Instigator.Location, vect(0,0,1), MyDamageType);
}
Destroy();
}
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
{
local actor Victims;
local float damageScale, dist;
local vector dir;
local KFMonster KFMonsterVictim;
local Pawn P;
local KFPawn KFP;
local array<Pawn> CheckedPawns;
local int i;
local bool bAlreadyChecked;
if ( bHurtEntry )
return;
bHurtEntry = true;
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
{
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
&& ExtendedZCollision(Victims)==None )
{
if( (Instigator==None || Instigator.Health<=0) && KFPawn(Victims)!=None )
Continue;
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
if ( Instigator == None || Instigator.Controller == None )
{
Victims.SetDelayedDamageInstigatorController( InstigatorController );
}
P = Pawn(Victims);
if( P != none )
{
for (i = 0; i < CheckedPawns.Length; i++)
{
if (CheckedPawns[i] == P)
{
bAlreadyChecked = true;
break;
}
}
if( bAlreadyChecked )
{
bAlreadyChecked = false;
P = none;
continue;
}
KFMonsterVictim = KFMonster(Victims);
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
KFMonsterVictim = none;
}
KFP = KFPawn(Victims);
if( KFMonsterVictim != none )
{
// 20x more damage zeds
damageScale *= 20.0 * KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
if ( ZombieFleshpound(KFMonsterVictim) != none )
damageScale *= 2.0; // compensate 50% dmg.res.
}
else if( KFP != none )
{
damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
}
CheckedPawns[CheckedPawns.Length] = P;
if ( damageScale <= 0)
{
P = none;
continue;
}
else
{
//Victims = P;
P = none;
}
}
Victims.TakeDamage(damageScale * DamageAmount, Killer,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius)
* dir,(damageScale * Momentum * dir),DamageType);
if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0)
{
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
}
}
bHurtEntry = false;
}
defaultproperties
{
ExplosionEffect=Class'KFMod.ZEDMKIISecondaryProjectileExplosion'
ShrapnelClass=None
ExplodeSounds(0)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
ExplodeSounds(1)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
ExplodeSounds(2)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
Speed=0.000000
Damage=50.000000
DamageRadius=400.000000
MyDamageType=Class'ScrnZedPack.DamTypeEMP'
DrawType=DT_None
bCollideActors=False
bBlockZeroExtentTraces=False
bBlockNonZeroExtentTraces=False
}

View file

@ -0,0 +1,108 @@
// Code originally was taken from Scary Ghost's Super Zombies mutator
class ZombieBloat_GRITTIER extends ZombieBloat;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
var bool bAmIBarfing;
var float NextExtraBileTime;
function TakeDamage(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector momentum, class<DamageType> DamType, optional int HitIndex)
{
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
if( bAmIBarfing && !bDecapitated && Level.TimeSeconds > NextExtraBileTime ) {
SpawnTwoShots();
NextExtraBileTime = Level.TimeSeconds + default.NextExtraBileTime;
}
}
function Touch(Actor Other)
{
super.Touch(Other);
if (Other.IsA('ShotgunBullet')) {
ShotgunBullet(Other).Damage= 0;
}
}
function RangedAttack(Actor A)
{
local int LastFireTime;
if ( bShotAnim )
return;
if ( Physics == PHYS_Swimming ) {
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
}
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) {
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated ) {
bShotAnim = true;
SetAnimAction('ZombieBarfMoving');
RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
bMovingPukeAttack=true;
// Randomly send out a message about Bloat Vomit burning(3% chance)
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none ) {
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
}
}
}
//ZombieBarf animation triggers this
function SpawnTwoShots() {
super.SpawnTwoShots();
bAmIBarfing= true;
}
simulated function AnimEnd(int Channel) {
local name Sequence;
local float Frame, Rate;
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
super.AnimEnd(Channel);
if(Sequence == 'ZombieBarf')
bAmIBarfing= false;
}
defaultproperties
{
NextExtraBileTime=1.000000
BileExplosion=Class'KFMod.BileExplosion'
BileExplosionHeadless=Class'KFMod.BileExplosionHeadless'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump'
DetachedArmClass=Class'KFChar.SeveredArmBloat'
DetachedLegClass=Class'KFChar.SeveredLegBloat'
DetachedHeadClass=Class'KFChar.SeveredHeadBloat'
HeadHealth=50.000000
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
MenuName="Bloat.se"
AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak'
Skins(0)=Combiner'ScrnZedPack_T.bloat_grittier.bloat_grittier_cmb'
}

View file

@ -0,0 +1,24 @@
class ZombieBoss_GRITTIER extends ZombieBoss;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
defaultproperties
{
RocketFireSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_FireRocket'
MiniGunFireSound=Sound'KF_BasePatriarch.Attack.Kev_MG_GunfireLoop'
MiniGunSpinSound=Sound'KF_BasePatriarch.Attack.Kev_MG_TurbineFireLoop'
MeleeImpaleHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Impale'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_HitPlayer_Fist'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPatriarch'
DetachedLegClass=Class'KFChar.SeveredLegPatriarch'
DetachedHeadClass=Class'KFChar.SeveredHeadPatriarch'
DetachedSpecialArmClass=Class'KFChar.SeveredRocketArmPatriarch'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Patriarch.Kev_Death'
MenuName="Grittier Patriarch"
AmbientSound=Sound'KF_BasePatriarch.Idle.Kev_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Patriarch_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.gatling_cmb'
Skins(1)=Combiner'ScrnZedPack_T.patriarch_grittier.patriarch_grittier_cmb'
}

View file

@ -0,0 +1,738 @@
class ZombieBrute extends KFMonster;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
#exec OBJ LOAD FILE=KFWeaponSound.uax
var bool bChargingPlayer;
var bool bClientCharge;
var bool bFrustrated;
var int MaxRageCounter; // Maximum amount of players we can hit before calming down
var int RageCounter; // Decreases each time we successfully hit a player
var float RageSpeedTween;
var int TwoSecondDamageTotal;
var float LastDamagedTime;
var int RageDamageThreshold;
var int BlockHitsLanded; // Hits made while blocking or raging
var float BlockMeleeDmgMul; //Multiplier for melee damage taken, when Brute is blocking (no matter where the hit was landed)
var float HeadShotgunDmgMul; //Multiplier for shotgun damage taken into UNBLOCKED head
var float HeadBulletDmgMul; //Multiplier for non-sniper bullet damage taken into UNBLOCKED head
var name ChargingAnim;
var Sound RageSound;
// View shaking for players
var() vector ShakeViewRotMag;
var() vector ShakeViewRotRate;
var() float ShakeViewRotTime;
var() vector ShakeViewOffsetMag;
var() vector ShakeViewOffsetRate;
var() float ShakeViewOffsetTime;
var float PushForce;
var vector PushAdd; // Used to add additional height to push
var float RageDamageMul; // Multiplier for hit damage when raging
var float RageBumpDamage; // Damage done when we hit other specimens while raging
var float BlockAddScale; // Additional head scale when blocking
var bool bBlockedHS;
var bool bBlocking;
var bool bServerBlock;
var bool bClientBlock;
var float BlockDmgMul; // Multiplier for damage taken from blocked shots
var float BlockFireDmgMul;
var float BurnGroundSpeedMul; // Multiplier for ground speed when burning
replication
{
reliable if(Role == ROLE_Authority)
bChargingPlayer, bServerBlock;
}
simulated function PostNetBeginPlay()
{
super.PostNetBeginPlay();
EnableChannelNotify(1,1);
EnableChannelNotify(2,1);
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
}
function ServerRaiseBlock()
{
bServerBlock = true;
SetAnimAction('BlockLoop');
}
function ServerLowerBlock()
{
local name Sequence;
local float Frame, Rate;
bServerBlock = false;
GetAnimParams(1, Sequence, Frame, Rate);
if (Sequence == 'BlockLoop')
AnimStopLooping(1);
}
simulated function PostNetReceive()
{
local name Sequence;
local float Frame, Rate;
if(bClientCharge != bChargingPlayer)
{
bClientCharge = bChargingPlayer;
if (bChargingPlayer)
{
MovementAnims[0] = ChargingAnim;
MeleeAnims[0] = 'BruteRageAttack';
MeleeAnims[1] = 'BruteRageAttack';
MeleeAnims[2] = 'BruteRageAttack';
}
else
{
MovementAnims[0] = default.MovementAnims[0];
MeleeAnims[0] = default.MeleeAnims[0];
MeleeAnims[1] = default.MeleeAnims[1];
MeleeAnims[2] = default.MeleeAnims[2];
}
}
if (bClientBlock != bServerBlock)
{
bClientBlock = bServerBlock;
if (bClientBlock)
SetAnimAction('BlockLoop');
else
{
GetAnimParams(1, Sequence, Frame, Rate);
if (Sequence == 'BlockLoop')
AnimStopLooping(1);
}
}
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
if (Role == ROLE_Authority)
{
// Lock to target when attacking (except on beginner!)
if (bShotAnim && Level.Game.GameDifficulty >= 2.0)
if (LookTarget != none)
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
// Block according to rules
if (Role == ROLE_Authority && !bServerBlock && !bShotAnim)
if (Controller != none && Controller.Target != none)
ServerRaiseBlock();
}
}
// Override to always move when attacking
function RangedAttack(Actor A)
{
if (bShotAnim || Physics == PHYS_Swimming)
return;
else if (CanAttack(A))
{
if (bChargingPlayer)
SetAnimAction('AoeClaw');
else
{
if (Rand(BlockHitsLanded) < 1)
SetAnimAction('BlockClaw');
else
SetAnimAction('Claw');
}
bShotAnim = true;
return;
}
}
function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
{
local float D;
local float AddScale;
local bool bIsBlocking;
bBlockedHS = false;
if (bServerBlock && !IsTweening(1))
{
bIsBlocking = true;
AddScale = AdditionalScale + BlockAddScale;
}
else
AddScale = AdditionalScale + 1.0;
if (Super.IsHeadShot(Loc, Ray, AddScale))
{
if (bIsBlocking)
{
D = vector(Rotation) dot Ray;
if (-D > 0.20) {
bBlockedHS = true;
return false;
}
else
return true;
}
else
return true;
}
else
return false;
}
// Damage, which doen't make headshots, always does full damage to Brute
// Melee weapon resistance is applied only while blocking
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
{
local bool bIsHeadShot;
local class<KFWeaponDamageType> KFDamType;
KFDamType = class<KFWeaponDamageType>(DamType);
// damage, which doen't make headshots, always does full damage to Brute -- PooSH
if (KFDamType != none && KFDamType.default.bCheckForHeadShots) {
bIsHeadShot = IsHeadShot(HitLocation, normal(Momentum), 1.0);
if (!bIsHeadShot && bBlockedHS)
{
if (class<KFProjectileWeaponDamageType>(DamType) != none)
PlaySound(class'MetalHitEmitter'.default.ImpactSounds[rand(3)],, 128);
else if (class<DamTypeChainsaw>(DamType) != none)
PlaySound(Sound'KF_ChainsawSnd.Chainsaw_Impact_Metal',, 128);
else if (class<DamTypeMelee>(DamType) != none)
PlaySound(Sound'KF_KnifeSnd.Knife_HitMetal',, 128);
if ( KFDamType.default.bDealBurningDamage && !KFDamType.default.bIsPowerWeapon )
Damage *= BlockFireDmgMul; // Fire damage isn't reduced as much, excluding TrenchGun
else
Damage *= BlockDmgMul; // Greatly reduce damage as we only hit the metal plating
}
else if ( bServerBlock && class<DamTypeMelee>(DamType) != none )
Damage *= BlockMeleeDmgMul; // Give Brute higher melee damage resistance, but apply it only if Brute is blocking
else if ( bIsHeadShot ) {
if ( KFDamType.default.bIsPowerWeapon )
Damage *= HeadShotgunDmgMul; // Give Brute's head resistance to stotguns
else if ( !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage )
Damage *= HeadBulletDmgMul; // Give damage bonus to commando and gunslinger weapons
}
}
// Record damage over 2-second frames
if (LastDamagedTime < Level.TimeSeconds)
{
TwoSecondDamageTotal = 0;
LastDamagedTime = Level.TimeSeconds + 2;
}
TwoSecondDamageTotal += Damage;
// If criteria is met make him rage
if (!bDecapitated && !bChargingPlayer && TwoSecondDamageTotal > RageDamageThreshold)
{
StartCharging();
if (InstigatedBy != None)
if (Controller.Target != InstigatedBy)
MonsterController(Controller).ChangeEnemy(InstigatedBy, Controller.CanSee(InstigatedBy));
}
if (InstigatedBy == none || KFDamType == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
if (bDecapitated)
Died(InstigatedBy.Controller, DamType, HitLocation);
}
function TakeFireDamage(int Damage, Pawn Instigator)
{
Super.TakeFireDamage(Damage, Instigator);
// Adjust movement speed if not charging
if (!bChargingPlayer)
{
if (bBurnified)
GroundSpeed = GetOriginalGroundSpeed() * BurnGroundSpeedMul;
else
GroundSpeed = GetOriginalGroundSpeed();
}
}
function ClawDamageTarget()
{
local KFHumanPawn HumanTarget;
local float UsedMeleeDamage;
local Actor OldTarget;
local name Sequence;
local float Frame, Rate;
local bool bHitSomeone;
if (MeleeDamage > 1)
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
else
UsedMeleeDamage = MeleeDamage;
GetAnimParams(1, Sequence, Frame, Rate);
if (Controller != none && Controller.Target != none)
{
if (Sequence == 'BruteRageAttack')
{
OldTarget = Controller.Target;
foreach VisibleCollidingActors(class'KFHumanPawn', HumanTarget, MeleeRange + class'KFHumanPawn'.default.CollisionRadius)
{
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, HumanTarget);
}
Controller.Target = OldTarget;
if (bHitSomeone)
BlockHitsLanded++;
}
else if (Sequence != 'BruteAttack1' && Sequence != 'BruteAttack2' && Sequence != 'DoorBash') // Block attack
{
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
if (bHitSomeone)
BlockHitsLanded++;
}
else
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
if (bHitSomeone)
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
}
}
function bool ClawDamageSingleTarget(float UsedMeleeDamage, Actor ThisTarget)
{
local Pawn HumanTarget;
local KFPlayerController HumanTargetController;
local bool bHitSomeone;
local float EnemyAngle;
local vector PushForceVar;
EnemyAngle = Normal(ThisTarget.Location - Location) dot vector(Rotation);
if (EnemyAngle > 0)
{
Controller.Target = ThisTarget;
if (MeleeDamageTarget(UsedMeleeDamage, vect(0, 0, 0)))
{
HumanTarget = KFHumanPawn(ThisTarget);
if (HumanTarget != None)
{
EnemyAngle = (EnemyAngle * 0.5) + 0.5; // Players at sides get knocked back half as much
PushForceVar = (PushForce * Normal(HumanTarget.Location - Location) * EnemyAngle) + PushAdd;
if (!bChargingPlayer)
PushForceVar *= 0.85;
// (!) I'm sure the VeterancyName string is localized but I'm not sure of another way compatible with ServerPerks
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill != none)
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill
.default.VeterancyName == "Berserker")
PushForceVar *= 0.75;
if (!(HumanTarget.Physics == PHYS_WALKING || HumanTarget.Physics == PHYS_NONE))
PushForceVar *= vect(1, 1, 0); // (!) Don't throw upwards if we are not on the ground - adjust for more flexibility
HumanTarget.AddVelocity(PushForceVar);
HumanTargetController = KFPlayerController(HumanTarget.Controller);
if (HumanTargetController != None)
HumanTargetController.ShakeView(ShakeViewRotMag, ShakeViewRotRate, ShakeViewRotTime,
ShakeViewOffsetMag, ShakeViewOffsetRate, ShakeViewOffsetTime);
bHitSomeone = true;
}
}
}
return bHitSomeone;
}
function StartCharging()
{
// How many times should we hit before we cool down?
if (Level.Game.NumPlayers <= 3)
MaxRageCounter = 2;
else
MaxRageCounter = 3;
RageCounter = MaxRageCounter;
PlaySound(RageSound, SLOT_Talk, 255);
GotoState('RageCharging');
}
state RageCharging
{
Ignores StartCharging;
function PlayDirectionalHit(Vector HitLoc)
{
if (!bShotAnim)
super.PlayDirectionalHit(HitLoc);
}
function bool CanGetOutOfWay()
{
return false;
}
function bool CanSpeedAdjust()
{
return false;
}
function BeginState()
{
bFrustrated = false;
bChargingPlayer = true;
RageSpeedTween = 0.0;
if (Level.NetMode != NM_DedicatedServer)
ClientChargingAnims();
NetUpdateTime = Level.TimeSeconds - 1;
}
function EndState()
{
bChargingPlayer = false;
BruteZombieController(Controller).RageFrustrationTimer = 0;
if (Health > 0)
{
GroundSpeed = GetOriginalGroundSpeed();
if (bBurnified)
GroundSpeed *= BurnGroundSpeedMul;
}
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
NetUpdateTime = Level.TimeSeconds - 1;
}
function Tick(float Delta)
{
if (!bShotAnim)
{
RageSpeedTween = FClamp(RageSpeedTween + (Delta * 0.75), 0, 1.0);
GroundSpeed = OriginalGroundSpeed + ((OriginalGroundSpeed * 0.75 / MaxRageCounter * (RageCounter + 1) * RageSpeedTween));
if (bBurnified)
GroundSpeed *= BurnGroundSpeedMul;
}
Global.Tick(Delta);
}
function Bump(Actor Other)
{
local KFMonster KFM;
KFM = KFMonster(Other);
// Hurt enemies that we run into while raging
if (!bShotAnim && KFM != None && ZombieBrute(Other) == None && Pawn(Other).Health > 0)
Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'DamTypePoundCrushed');
else Global.Bump(Other);
}
function bool MeleeDamageTarget(int HitDamage, vector PushDir)
{
local bool DamDone, bWasEnemy;
bWasEnemy = (Controller.Target == Controller.Enemy);
DamDone = Super.MeleeDamageTarget(HitDamage * RageDamageMul, vect(0, 0, 0));
if (bWasEnemy && DamDone)
{
ChangeTarget();
CalmDown();
}
return DamDone;
}
function CalmDown()
{
RageCounter = FClamp(RageCounter - 1, 0, MaxRageCounter);
if (RageCounter == 0)
GotoState('');
}
function ChangeTarget()
{
local Controller C;
local Pawn BestPawn;
local float Dist, BestDist;
for (C = Level.ControllerList; C != none; C = C.NextController)
if (C.Pawn != none && KFHumanPawn(C.Pawn) != none)
{
Dist = VSize(C.Pawn.Location - Location);
if (C.Pawn == Controller.Target)
Dist += GroundSpeed * 4;
if (BestPawn == none)
{
BestPawn = C.Pawn;
BestDist = Dist;
}
else if (Dist < BestDist)
{
BestPawn = C.Pawn;
BestDist = Dist;
}
}
if (BestPawn != none && BestPawn != Controller.Enemy)
MonsterController(Controller).ChangeEnemy(BestPawn, Controller.CanSee(BestPawn));
}
}
// Override to prevent stunning
function bool FlipOver()
{
return false;
}
// Shouldn't fight with our own
function bool SameSpeciesAs(Pawn P)
{
return (ZombieBrute(P) != none);
}
// ------------------------------------------------------
// Animation --------------------------------------------
// ------------------------------------------------------
function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
{
if (Level.TimeSeconds - LastPainAnim < MinTimeBetweenPainAnims)
return;
// Uncomment this if we want some damage to make him drop his block
/*if( !Controller.IsInState('WaitForAnim') && Damage >= 10 )
PlayDirectionalHit(HitLocation);*/
LastPainAnim = Level.TimeSeconds;
if (Level.TimeSeconds - LastPainSound < MinTimeBetweenPainSounds)
return;
LastPainSound = Level.TimeSeconds;
PlaySound(HitSound[0], SLOT_Pain,1.25,,400);
}
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
if (NewAction=='')
return;
if (NewAction == 'Claw')
{
NewAction = MeleeAnims[rand(2)];
CurrentDamType = ZombieDamType[0];
}
else if (NewAction == 'BlockClaw')
{
NewAction = 'BruteBlockSlam';
CurrentDamType = ZombieDamType[0];
}
else if (NewAction == 'AoeClaw')
{
NewAction = 'BruteRageAttack';
CurrentDamType = ZombieDamType[0];
}
else if (NewAction == 'DoorBash')
CurrentDamType = ZombieDamType[Rand(3)];
ExpectingChannel = DoAnimAction(NewAction);
if (AnimNeedsWait(NewAction))
bWaitForAnim = true;
else
bWaitForAnim = false;
if (Level.NetMode != NM_Client)
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
simulated function int DoAnimAction( name AnimName )
{
if (AnimName=='BruteAttack1' || AnimName=='BruteAttack2' || AnimName=='ZombieFireGun' || AnimName == 'DoorBash')
{
if (Role == ROLE_Authority)
ServerLowerBlock();
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
}
else if (AnimName == 'BruteRageAttack')
{
if (Role == ROLE_Authority)
ServerLowerBlock();
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
}
else if (AnimName == 'BlockLoop')
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
LoopAnim(AnimName,, 0.25, 1);
return 1;
}
else if (AnimName == 'BruteBlockSlam')
{
AnimBlendParams(2, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 2);
return 2;
}
return Super.DoAnimAction(AnimName);
}
// The animation is full body and should set the bWaitForAnim flag
simulated function bool AnimNeedsWait(name TestAnim)
{
if (TestAnim == 'DoorBash')
return true;
return false;
}
simulated function AnimEnd(int Channel)
{
local name Sequence;
local float Frame, Rate;
GetAnimParams(Channel, Sequence, Frame, Rate);
// Don't allow notification for a looping animation
if (Sequence == 'BlockLoop')
return;
// Disable channel 2 when we're done with it
if (Channel == 2 && Sequence == 'BruteBlockSlam')
{
AnimBlendParams(2, 0);
bShotAnim = false;
return;
}
Super.AnimEnd(Channel);
}
simulated function ClientChargingAnims()
{
PostNetReceive();
}
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx)
{
local Actor A;
if (bBlockedHS)
A = Spawn(class'BlockHitEmitter', InstigatedBy,, HitLocation, rotator(Normal(HitLocation - Location)));
else
Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx);
}
defaultproperties
{
RageDamageThreshold=50
BlockMeleeDmgMul=0.500000
HeadShotgunDmgMul=0.500000
HeadBulletDmgMul=1.250000
ChargingAnim="BruteRun"
RageSound=SoundGroup'ScrnZedPack_S.Brute.BruteRage'
ShakeViewRotMag=(X=500.000000,Y=500.000000,Z=600.000000)
ShakeViewRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
ShakeViewRotTime=6.000000
ShakeViewOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
ShakeViewOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
ShakeViewOffsetTime=3.500000
PushForce=860.000000
PushAdd=(Z=150.000000)
RageDamageMul=1.100000
RageBumpDamage=4.000000
BlockAddScale=2.500000
BlockDmgMul=0.100000
BlockFireDmgMul=1.000000
BurnGroundSpeedMul=0.600000
MeleeAnims(0)="BruteAttack1"
MeleeAnims(1)="BruteAttack2"
MeleeAnims(2)="BruteBlockSlam"
MoanVoice=SoundGroup'ScrnZedPack_S.Brute.BruteTalk'
BleedOutDuration=7.000000
ZombieFlag=3
MeleeDamage=20
damageForce=25000
bFatAss=True
KFRagdollName="FleshPound_Trip"
MeleeAttackHitSound=SoundGroup'ScrnZedPack_S.Brute.BruteHitPlayer'
JumpSound=SoundGroup'ScrnZedPack_S.Brute.BruteJump'
SpinDamConst=20.000000
SpinDamRand=20.000000
bMeleeStunImmune=True
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=35.000000
ColHeight=25.000000
SeveredArmAttachScale=1.300000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
DetachedArmClass=Class'ScrnZedPack.SeveredArmBrute'
DetachedLegClass=Class'ScrnZedPack.SeveredLegBrute'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadBrute'
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
OnlineHeadshotScale=1.300000
HeadHealth=210.000000
PlayerNumHeadHealthScale=0.150000
MotionDetectorThreat=5.000000
HitSound(0)=SoundGroup'ScrnZedPack_S.Brute.BrutePain'
DeathSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteDeath'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ScoringValue=60
IdleHeavyAnim="BruteIdle"
IdleRifleAnim="BruteIdle"
RagDeathUpKick=100.000000
MeleeRange=85.000000
GroundSpeed=140.000000
WaterSpeed=120.000000
HealthMax=1000.000000
Health=1000
HeadHeight=2.500000
HeadScale=1.300000
MenuName="Brute.se"
ControllerClass=Class'ScrnZedPack.BruteZombieController'
MovementAnims(0)="BruteWalkC"
MovementAnims(1)="BruteWalkC"
WalkAnims(0)="BruteWalkC"
WalkAnims(1)="BruteWalkC"
WalkAnims(2)="RunL"
WalkAnims(3)="RunR"
IdleCrouchAnim="BruteIdle"
IdleWeaponAnim="BruteIdle"
IdleRestAnim="BruteIdle"
AmbientSound=SoundGroup'ScrnZedPack_S.Brute.BruteIdle1Shot'
Mesh=SkeletalMesh'ScrnZedPack_A.BruteMesh'
PrePivot=(Z=0.000000)
Skins(0)=Combiner'ScrnZedPack_T.Brute.Brute_Final'
Mass=600.000000
RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,22 @@
class ZombieClot_GRITTIER extends ZombieClot;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
defaultproperties
{
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
DetachedArmClass=Class'KFChar.SeveredArmClot'
DetachedLegClass=Class'KFChar.SeveredLegClot'
DetachedHeadClass=Class'KFChar.SeveredHeadClot'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
MenuName="Grittier Clot"
AmbientSound=Sound'KF_BaseClot.Clot_Idle1Loop'
Mesh=SkeletalMesh'KF_Freaks_Trip.CLOT_Freak'
Skins(0)=Combiner'ScrnZedPack_T.clot_grittier.clot_grittier_cmb'
}

View file

@ -0,0 +1,41 @@
// Code originally was taken from Scary Ghost's Super Zombies mutator
class ZombieCrawler_GRITTIER extends ZombieCrawler;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
simulated function PostBeginPlay()
{
super.PostBeginPlay();
PounceSpeed= Rand(221)+330;
MeleeRange= Rand(41)+50;
}
function TakeDamage(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector momentum, class<DamageType> DamType, optional int HitIndex)
{
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
}
defaultproperties
{
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Jump'
DetachedArmClass=Class'KFChar.SeveredArmCrawler'
DetachedLegClass=Class'KFChar.SeveredLegCrawler'
DetachedHeadClass=Class'KFChar.SeveredHeadCrawler'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
GroundSpeed=190.000000
WaterSpeed=175.000000
MenuName="Crawler.se"
AmbientSound=Sound'KF_BaseCrawler.Crawler_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.Crawler_Freak'
Skins(0)=Combiner'ScrnZedPack_T.crawler_grittier.crawler_grittier_cmb'
}

View file

@ -0,0 +1,22 @@
class ZombieFleshpound_GRITTIER extends ZombieFleshpound;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
defaultproperties
{
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Jump'
DetachedArmClass=Class'KFChar.SeveredArmPound'
DetachedLegClass=Class'KFChar.SeveredLegPound'
DetachedHeadClass=Class'KFChar.SeveredHeadPound'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Fleshpound.FP_Challenge'
MenuName="Grittier Fleshpound"
AmbientSound=Sound'KF_BaseFleshpound.FP_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.FleshPound_Freak'
Skins(0)=Combiner'ScrnZedPack_T.fleshpound_grittier.fleshpound_grittier_cmb'
}

View file

@ -0,0 +1,295 @@
// Completely invisible Stalker
// (c) PooSH, 2014
// used 'ClawAndMove' code from Scary Ghost's SuperStaler
class ZombieGhost extends ZombieStalker;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
// max distance squared for player to see cloacked Stalkers.
// Beyond that distance Stalkers will appear completely invisible.
var float CloakDistanceSqr;
// Unclock distance squared for Commandos
var float UncloakDistanceSqr;
var const Material CloakMat;
var const Material InvisibleMat;
var const Material UncloakMat, UncloakFBMat;
var const Material GlowFX;
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
myLevel.AddPrecacheMaterial(default.CloakMat);
myLevel.AddPrecacheMaterial(default.InvisibleMat);
myLevel.AddPrecacheMaterial(default.UncloakMat);
myLevel.AddPrecacheMaterial(default.UncloakFBMat);
myLevel.AddPrecacheMaterial(default.GlowFX);
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
}
simulated function PostNetBeginPlay()
{
super.PostNetBeginPlay();
if ( LocalKFHumanPawn != none ) {
CloakDistanceSqr = fmax(CloakDistanceSqr, 3.0 * UncloakDistanceSqr * LocalKFHumanPawn.GetStalkerViewDistanceMulti());
UncloakDistanceSqr *= LocalKFHumanPawn.GetStalkerViewDistanceMulti();
}
}
function RenderOverlays(Canvas Canvas)
{
Canvas.SetDrawColor(0, 92, 255, 255);
super.RenderOverlays(Canvas);
}
// makes Stalker invisible or glowing (for commando)
simulated function CloakStalker()
{
// No cloaking if zapped
if( bZapped )
{
return;
}
if ( bSpotted ) {
if( Level.NetMode == NM_DedicatedServer )
return;
Skins[0] = GlowFX;
Skins[1] = GlowFX;
bUnlit = true;
}
else if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
{
Visibility = 1;
bCloaked = true;
if( Level.NetMode == NM_DedicatedServer )
Return;
Skins[0] = InvisibleMat;
Skins[1] = InvisibleMat;
bUnlit = false;
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
}
simulated function UnCloakStalker()
{
if( bZapped )
{
return;
}
if( !bCrispified )
{
LastUncloakTime = Level.TimeSeconds;
Visibility = default.Visibility;
bCloaked = false;
bUnlit = false;
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
if ( Skins[0] != UncloakMat )
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = UncloakMat;
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
}
}
function RemoveHead()
{
Super(KFMonster).RemoveHead();
if (!bCrispified)
{
Skins[1] = UncloakFBMat;
Skins[0] = UncloakMat;
}
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
Super(KFMonster).PlayDying(DamageType,HitLoc);
if(bUnlit)
bUnlit=!bUnlit;
LocalKFHumanPawn = none;
if (!bCrispified)
{
Skins[1] = UncloakFBMat;
Skins[0] = UncloakMat;
}
}
simulated function Tick(float DeltaTime)
{
local float DistanceSqr;
Super(KFMonster).Tick(DeltaTime);
// Keep the stalker moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim && !bZapped ) {
if( LookTarget!=None ) {
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
if( bZapped ) {
// Make sure we check if we need to be cloaked as soon as the zap wears off
NextCheckTime = Level.TimeSeconds;
}
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
{
NextCheckTime = Level.TimeSeconds + 0.5;
bSpotted = false;
if ( LocalKFHumanPawn != none ) {
DistanceSqr = VSizeSquared(Location - LocalKFHumanPawn.Location);
if( LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers()
&& DistanceSqr < UncloakDistanceSqr )
{
bSpotted = True;
if ( Skins[0] != GlowFX ) {
Skins[0] = GlowFX;
Skins[1] = GlowFX;
bUnlit = true;
}
}
else if ( DistanceSqr < CloakDistanceSqr ) {
if ( bCloaked && Skins[0] != CloakMat ) {
Skins[0] = CloakMat;
Skins[1] = CloakMat;
bUnlit = false;
}
}
else if ( Skins[0] != InvisibleMat ) {
CloakStalker();
}
}
}
}
function RangedAttack(Actor A)
{
if ( !bShotAnim && Physics != PHYS_Swimming && CanAttack(A) ) {
bShotAnim = true;
SetAnimAction('ClawAndMove');
}
}
// copied from ZombieSuperStalker (c) Scary Ghost
simulated event SetAnimAction(name NewAction)
{
if( NewAction=='' )
Return;
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
bWaitForAnim= false;
if( Level.NetMode!=NM_Client ) {
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
// copied from ZombieSuperStalker (c) Scary Ghost
simulated function int AttackAndMoveDoAnimAction( name AnimName )
{
local int meleeAnimIndex;
local float duration;
if( AnimName == 'ClawAndMove' ) {
meleeAnimIndex = Rand(3);
AnimName = meleeAnims[meleeAnimIndex];
CurrentDamtype = ZombieDamType[meleeAnimIndex];
duration= GetAnimDuration(AnimName, 1.0);
}
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') {
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
}
return super.DoAnimAction( AnimName );
}
defaultproperties
{
CloakDistanceSqr=62500.000000
UncloakDistanceSqr=360000.000000
CloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
InvisibleMat=Shader'KF_Specimens_Trip_T.patriarch_invisible'
UncloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
UncloakFBMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
GlowFX=FinalBlend'ScrnZedPack_T.Ghost.GhostGlow'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Jump'
DetachedArmClass=Class'KFChar.SeveredArmStalker'
DetachedLegClass=Class'KFChar.SeveredLegStalker'
DetachedHeadClass=Class'KFChar.SeveredHeadStalker'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
MenuName="Ghost"
AmbientSound=Sound'KF_BaseStalker.Stalker_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Stalker_Freak'
Skins(0)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
Skins(1)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
}

View file

@ -0,0 +1,91 @@
// Code originally was taken from Scary Ghost's Super Zombies mutator
class ZombieGorefast_GRITTIER extends ZombieGorefast;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
var float minRageDist;
function TakeDamage(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector momentum, class<DamageType> DamType, optional int HitIndex)
{
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
}
function RangedAttack(Actor A) {
Super(KFMonster).RangedAttack(A);
if( !bShotAnim && !bDecapitated && VSize(A.Location-Location)<=minRageDist )
GoToState('RunningState');
}
state RunningState {
// Don't override speed in this state
function bool CanSpeedAdjust() {
return super.CanSpeedAdjust();
}
function BeginState() {
super.BeginState();
}
function EndState() {
super.EndState();
}
function RemoveHead() {
super.RemoveHead();
}
function RangedAttack(Actor A) {
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( CanAttack(A) ) {
bShotAnim = true;
//Always do the charging melee attack
SetAnimAction('ClawAndMove');
RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0);
return;
}
}
simulated function Tick(float DeltaTime) {
super.Tick(DeltaTime);
}
Begin:
GoTo('CheckCharge');
CheckCharge:
if( Controller!=None && Controller.Target!=None && VSize(Controller.Target.Location-Location)<minRageDist ) {
Sleep(0.5+ FRand() * 0.5);
//log("Still charging");
GoTo('CheckCharge');
}
else {
//log("Done charging");
GoToState('');
}
}
defaultproperties
{
minRageDist=1400.000000
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
DetachedArmClass=Class'KFChar.SeveredArmGorefast'
DetachedLegClass=Class'KFChar.SeveredLegGorefast'
DetachedHeadClass=Class'KFChar.SeveredHeadGorefast'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
MenuName="Gorefast.se"
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.GoreFast_Freak'
Skins(0)=Combiner'ScrnZedPack_T.gorefast_grittier.gorefast_grittier_cmb'
}

View file

@ -0,0 +1,144 @@
// Code originally was taken from Scary Ghost's Super Zombies mutator
class ZombieHusk_GRITTIER extends ZombieHusk;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
var int ShotsRemaining;
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Texture'ScrnZedPack_T.husk_grittier.husk_grittier_diff');
myLevel.AddPrecacheMaterial(Texture'ScrnZedPack_T.husk_grittier.husk_grittier_emissive_mask');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.husk_grittier.husk_grittier_energy_cmb');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.husk_grittier.husk_grittier_env_cmb');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.husk_grittier.husk_grittier_fire_cmb');
myLevel.AddPrecacheMaterial(Material'ScrnZedPack_T.husk_grittier.husk_grittier_shdr');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.husk_grittier.husk_grittier_cmb');
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
ShotsRemaining = Rand(default.ShotsRemaining) + 1;
}
function TakeDamage(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector momentum, class<DamageType> DamType, optional int HitIndex)
{
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
}
function RangedAttack(Actor A) {
local int LastFireTime;
if ( bShotAnim )
return;
if ( Physics == PHYS_Swimming ) {
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
}
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) {
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
else if ( (KFDoorMover(A) != none ||
(!Region.Zone.bDistanceFog && VSize(A.Location-Location) <= 65535) ||
(Region.Zone.bDistanceFog && VSizeSquared(A.Location-Location) < (Square(Region.Zone.DistanceFogEnd) * 0.8))) // Make him come out of the fog a bit
&& !bDecapitated )
{
bShotAnim = true;
SetAnimAction('ShootBurns');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
if(--ShotsRemaining > 0) {
NextFireProjectileTime= Level.TimeSeconds;
}
else {
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
ShotsRemaining = Rand(default.ShotsRemaining) + 1;
}
}
}
function SpawnTwoShots() {
local vector X,Y,Z, FireStart;
local rotator FireRotation;
local KFMonsterController KFMonstControl;
if( Controller!=None && KFDoorMover(Controller.Target)!=None ) {
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
return;
}
GetAxes(Rotation,X,Y,Z);
FireStart = GetBoneCoords('Barrel').Origin;
if ( !SavedFireProperties.bInitialized ) {
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = Class'HuskFireProjectileSE';
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 65535;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = true;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = False;
SavedFireProperties.bInitialized = True;
}
// Turn off extra collision before spawning vomit, otherwise spawn fails
ToggleAuxCollision(false);
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
foreach DynamicActors(class'KFMonsterController', KFMonstControl) {
if( KFMonstControl != Controller ) {
if( PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75 ) {
KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation),FireStart);
}
}
}
Spawn(Class'HuskFireProjectileSE',Self,,FireStart,FireRotation);
// Turn extra collision back on
ToggleAuxCollision(true);
}
defaultproperties
{
ShotsRemaining=4
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Jump'
DetachedArmClass=Class'KFChar.SeveredArmHusk'
DetachedLegClass=Class'KFChar.SeveredLegHusk'
DetachedHeadClass=Class'KFChar.SeveredHeadHusk'
DetachedSpecialArmClass=Class'KFChar.SeveredArmHuskGun'
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkB"
BurningWalkAnims(1)="WalkL"
BurningWalkAnims(2)="WalkR"
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_Challenge'
MenuName="Husk.se"
ControllerClass=Class'ScrnZedPack.HuskZombieControllerSE'
AmbientSound=Sound'KF_BaseHusk.Husk_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks2_Trip.Burns_Freak'
Skins(0)=Texture'ScrnZedPack_T.husk_grittier.husk_grittier_tatters'
Skins(1)=Shader'ScrnZedPack_T.husk_grittier.husk_grittier_shdr'
}

View file

@ -0,0 +1,210 @@
class ZombieJason extends ZombieScrake;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
var int OriginalMeleeDamage; // default melee damage, adjusted by game's difficulty
var transient bool bFlippedOver;
var transient float LastFlipOverTime;
var float FlipOverDuration;
var bool bWasRaged; // set to true, if Jason is raged or was raged before
var float RageHealthPct;
simulated function PostBeginPlay()
{
super.PostBeginPlay();
OriginalMeleeDamage = MeleeDamage;
FlipOverDuration = GetAnimDuration('KnockDown');
}
simulated function Tick(float DeltaTime)
{
super.Tick(DeltaTime);
if ( bFlippedOver && Level.TimeSeconds > LastFlipOverTime + FlipOverDuration )
bFlippedOver = false;
}
// Machete has no Exhaust ;)
simulated function SpawnExhaustEmitter() {}
simulated function UpdateExhaustEmitter() {}
function bool FlipOver()
{
bFlippedOver= super.FlipOver();
if ( bFlippedOver ) {
LastFlipOverTime = Level.TimeSeconds;
// do not rotate while stunned
Controller.Focus = none;
Controller.FocalPoint = Location + 512*vector(Rotation);
}
return bFlippedOver;
}
function bool CanGetOutOfWay()
{
return !bFlippedOver; // can't dodge husk fireballs while stunned
}
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
{
local bool bIsHeadShot;
local float headShotCheckScale;
local class<KFWeaponDamageType> KFDamType;
local int OldHealth, DamageDone;
KFDamType = class<KFWeaponDamageType>(DamType);
if ( InstigatedBy != none && KFDamType != none ) {
if ( KFDamType.default.bCheckForHeadShots ) {
headShotCheckScale= 1.0;
if (class<DamTypeMelee>(DamType) != none) {
headShotCheckScale*= 1.25;
}
bIsHeadShot = IsHeadShot(Hitlocation, normal(Momentum), 1.0);
}
if ( ClassIsChildOf(DamType, class'DamTypePipeBomb') )
Damage *= 1.5;
else if ( bIsHeadShot && Level.Game.GameDifficulty >= 5.0 && class<DamTypeCrossbow>(DamType) != none )
Damage *= 0.5;
OldHealth = Health;
super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
DamageDone = OldHealth - Health;
if ( !bDecapitated ) {
if( bFlippedOver ) {
if ( Level.Game.GameDifficulty >= 7.0 && Health>0 && !bIsHeadShot && DamageDone<800
&& Level.TimeSeconds - LastFlipOverTime > 0.1
&& (DamageDone > 300 || (Damage > 50 && KFDamType.default.bSniperWeapon)) )
{
// [HoE only] Break stun if Jason is hit with sniper weapon to the body
bFlippedOver = false;
bWaitForAnim = false;
bShotAnim = false;
if ( Controller != none )
Controller.GoToState('ZombieHunt');
GoToState('RunningState');
}
}
else {
if ( Level.Game.GameDifficulty >= 5.0 && !IsInState('SawingLoop') && !IsInState('RunningState') && float(Health) / HealthMax < RageHealthPct )
RangedAttack(InstigatedBy);
}
}
}
else {
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
}
}
function RangedAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( CanAttack(A) )
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
CurrentDamType = ZombieDamType[0];
//PlaySound(sound'Claw2s', SLOT_None); KFTODO: Replace this
GoToState('SawingLoop');
}
if( !bShotAnim && !bDecapitated ) {
if ( bWasRaged || float(Health)/HealthMax < 0.5
|| (Level.Game.GameDifficulty >= 5.0 && float(Health)/HealthMax < RageHealthPct) )
GoToState('RunningState');
}
}
State SawingLoop
{
function RangedAttack(Actor A)
{
if ( bShotAnim )
return;
else if ( CanAttack(A) )
{
Acceleration = vect(0,0,0);
bShotAnim = true;
MeleeDamage = OriginalMeleeDamage*0.6; // fixed difficulty multiplier -- PooSH
SetAnimAction('SawImpaleLoop');
CurrentDamType = ZombieDamType[0];
if( AmbientSound != SawAttackLoopSound )
{
AmbientSound=SawAttackLoopSound;
}
}
else GoToState('');
}
}
simulated function float GetOriginalGroundSpeed()
{
local float result;
result = OriginalGroundSpeed;
if ( bWasRaged || bCharging )
result *= 3.5;
else if( bZedUnderControl )
result *= 1.25;
if ( bBurnified )
result *= 0.8;
return result;
}
state RunningState
{
function BeginState()
{
bWasRaged = true;
if( bZapped )
GoToState('');
else {
bCharging = true;
SetGroundSpeed(GetOriginalGroundSpeed());
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
NetUpdateTime = Level.TimeSeconds - 1;
}
}
function EndState()
{
bCharging = False;
if( !bZapped )
SetGroundSpeed(GetOriginalGroundSpeed());
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
}
}
defaultproperties
{
RageHealthPct=0.750000
SawAttackLoopSound=Sound'KF_BaseGorefast.Attack.Gorefast_AttackSwish3'
StunsRemaining=5
BleedOutDuration=7.000000
MeleeDamage=25
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
HeadHealth=800.000000
ScoringValue=300
HealthMax=1500.000000
Health=1500
MenuName="Jason.se"
AmbientSound=Sound'ScrnZedPack_S.Jason.Jason_Sound'
Mesh=SkeletalMesh'ScrnZedPack_A.JasonMesh'
Skins(0)=Shader'ScrnZedPack_T.Jason.Jason__FB'
Skins(1)=Texture'ScrnZedPack_T.Jason.JVMaskB'
Skins(2)=Combiner'ScrnZedPack_T.Jason.Machete_cmb'
}

View file

@ -0,0 +1,26 @@
class ZombieScrake_GRITTIER extends ZombieScrake;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
defaultproperties
{
SawAttackLoopSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Impale'
ChainSawOffSound=SoundGroup'KF_ChainsawSnd.Chainsaw_Deselect'
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Chainsaw_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Jump'
DetachedArmClass=Class'KFChar.SeveredArmScrake'
DetachedLegClass=Class'KFChar.SeveredLegScrake'
DetachedHeadClass=Class'KFChar.SeveredHeadScrake'
DetachedSpecialArmClass=Class'KFChar.SeveredArmScrakeSaw'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
MenuName="Grittier Scrake"
AmbientSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.Scrake_Freak'
Skins(0)=Shader'ScrnZedPack_T.scrake_grittier.scrake_grittier_FB'
Skins(1)=TexPanner'KF_Specimens_Trip_T.scrake_saw_panner'
}

View file

@ -0,0 +1,626 @@
class ZombieShiver extends KFMonster;
#exec load obj file=ScrnZedPack_T.utx
#exec load obj file=ScrnZedPack_S.uax
#exec load obj file=ScrnZedPack_A.ukx
var name WalkAnim, RunAnim;
// Head twitch
var rotator CurHeadRot, NextHeadRot, HeadRot;
var float NextHeadTime;
var float MaxHeadTime;
var float MaxTilt, MaxTurn;
// Targetting, charging
var float TeleportBlockTime;
var float HeadOffsetY;
var transient bool bRunning, bClientRunning;
var bool bDelayedReaction;
var bool bCanSeeTarget;
var float SeeTargetTime;
var float RunUntilTime;
var float RunCooldownEnd;
var float PeriodSeeTarget;
var float PeriodRunBase;
var float PeriodRunRan;
var float PeriodRunCoolBase;
var float PeriodRunCoolRan;
// Teleporting
var byte FadeStage;
var byte OldFadeStage;
var float AlphaFader;
var bool bFlashTeleporting;
var float LastFlashTime;
var float MinTeleportDist, MaxTeleportDist;
var float MinLandDist, MaxLandDist; // How close we can teleport to the target (collision cylinders are taken into account)
var int MaxTeleportAttempts; // Attempts per angle
var int MaxTeleportAngles;
var ColorModifier MatAlphaSkin;
replication
{
reliable if (Role == ROLE_Authority)
FadeStage, bRunning;
}
simulated function PostBeginPlay()
{
Super.PostBeginPlay();
if (Level.NetMode != NM_DedicatedServer)
{
MatAlphaSkin = ColorModifier(Level.ObjectPool.AllocateObject(class'ColorModifier'));
if (MatAlphaSkin != none)
{
MatAlphaSkin.Color = class'Canvas'.static.MakeColor(255, 255, 255, 255);
MatAlphaSkin.RenderTwoSided = false;
MatAlphaSkin.AlphaBlend = true;
MatAlphaSkin.Material = Skins[0];
Skins[0] = MatAlphaSkin;
}
}
}
simulated function PostNetReceive()
{
super.PostNetReceive();
if( bClientRunning != bRunning )
{
bClientRunning = bRunning;
if( bRunning ) {
MovementAnims[0] = RunAnim;
}
else {
MovementAnims[0] = WalkAnim;
}
}
}
simulated function Destroyed()
{
if (Level.NetMode != NM_DedicatedServer && MatAlphaSkin != none)
{
Skins[0] = default.Skins[0];
Level.ObjectPool.FreeObject(MatAlphaSkin);
}
Super.Destroyed();
}
simulated function StopBurnFX()
{
if (bBurnApplied)
{
MatAlphaSkin.Material = Texture'PatchTex.Common.ZedBurnSkin';
Skins[0] = MatAlphaSkin;
}
Super.StopBurnFX();
}
function RangedAttack(Actor A)
{
if (bShotAnim || Physics == PHYS_Swimming)
return;
else if (CanAttack(A))
{
bShotAnim = true;
SetAnimAction('Claw');
return;
}
}
state Running
{
function BeginState()
{
bRunning = true;
RunUntilTime = Level.TimeSeconds + PeriodRunBase + FRand() * PeriodRunRan;
MovementAnims[0] = RunAnim;
}
function EndState()
{
bRunning = false;
GroundSpeed = global.GetOriginalGroundSpeed();
RunCooldownEnd = Level.TimeSeconds + PeriodRunCoolBase + FRand() * PeriodRunCoolRan;
MovementAnims[0] = WalkAnim;
}
function float GetOriginalGroundSpeed()
{
return global.GetOriginalGroundSpeed() * 2.5;
}
function Tick(float Delta)
{
Global.Tick(Delta);
if (RunUntilTime < Level.TimeSeconds)
GotoState('');
GroundSpeed = GetOriginalGroundSpeed(); }
function bool CanSpeedAdjust()
{
return false;
}
}
function TakeDamage(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector momentum, class<DamageType> DamType, optional int HitIndex)
{
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip NONE-reference error
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
}
// returns true also for KnockDown (stun) animation -- PooSH
simulated function bool AnimNeedsWait(name TestAnim)
{
if( TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
{
return true;
}
return ExpectingChannel == 0;
}
simulated function float GetOriginalGroundSpeed()
{
local float result;
result = OriginalGroundSpeed;
if( bZedUnderControl )
result *= 1.25;
if ( bBurnified )
result *= 0.8;
return result;
}
simulated function Tick(float Delta)
{
Super.Tick(Delta);
if (Health > 0 && !bBurnApplied)
{
// Handle targetting
if (Level.NetMode != NM_Client && !bDecapitated)
{
if (Controller == none || Controller.Target == none || !Controller.LineOfSightTo(Controller.Target))
{
if (bCanSeeTarget)
bCanSeeTarget = false;
}
else
{
if (!bCanSeeTarget)
{
bCanSeeTarget = true;
SeeTargetTime = Level.TimeSeconds;
}
else if (Level.TimeSeconds > SeeTargetTime + PeriodSeeTarget)
{
if (VSize(Controller.Target.Location - Location) < MaxTeleportDist)
{
if (VSize(Controller.Target.Location - Location) > MinTeleportDist || !Controller.ActorReachable(Controller.Target))
{
if (CanTeleport())
StartTelePort();
}
else
{
if (CanRun())
GotoState('Running');
}
}
}
}
}
}
// Handle client-side teleport variables
if (!bBurnApplied)
{
if (Level.NetMode != NM_DedicatedServer && OldFadeStage != FadeStage)
{
OldFadeStage = FadeStage;
if (FadeStage == 2)
AlphaFader = 0;
else
AlphaFader = 255;
}
// Handle teleporting
if (FadeStage == 1) // Fade out (pre-teleport)
{
AlphaFader = FMax(AlphaFader - Delta * 512, 0);
if (Level.NetMode != NM_Client && AlphaFader == 0)
{
SetCollision(true, true);
FlashTeleport();
SetCollision(false, false);
FadeStage = 2;
}
}
else if (FadeStage == 2) // Fade in (post-teleport)
{
AlphaFader = FMin(AlphaFader + Delta * 512, 255);
if (Level.NetMode != NM_Client && AlphaFader == 255)
{
FadeStage = 0;
SetCollision(true, true);
GotoState('Running');
}
}
if (Level.NetMode != NM_DedicatedServer && ColorModifier(Skins[0]) != none)
ColorModifier(Skins[0]).Color.A = AlphaFader;
}
}
/*
simulated function DebugHead()
{
local coords C;
local vector HeadLoc;
local float Radius;
Radius = HeadRadius * HeadScale;
ClearStayingDebugLines();
// based on head bone
C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * C.XAxis) + HeadOffsetY * C.YAxis;
DrawDebugSphere(HeadLoc, Radius, 16, 0, 100, 255);
DrawStayingDebugLine(HeadLoc - Radius*vect(1,0,0), HeadLoc + Radius*vect(1,0,0), 0, 100, 255);
DrawStayingDebugLine(HeadLoc - Radius*vect(0,1,0), HeadLoc + Radius*vect(0,1,0), 0, 100, 255);
DrawStayingDebugLine(HeadLoc - Radius*vect(0,0,1), HeadLoc + Radius*vect(0,0,1), 0, 100, 255);
// based on OnlineHeadshotOffset
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
Radius *= OnlineHeadshotScale;
DrawDebugSphere(HeadLoc, Radius, 16, 255, 0, 0);
DrawStayingDebugLine(HeadLoc - Radius*vect(1,0,0), HeadLoc + Radius*vect(1,0,0), 255, 0, 0);
DrawStayingDebugLine(HeadLoc - Radius*vect(0,1,0), HeadLoc + Radius*vect(0,1,0), 255, 0, 0);
DrawStayingDebugLine(HeadLoc - Radius*vect(0,0,1), HeadLoc + Radius*vect(0,0,1), 255, 0, 0);
}
*/
function bool CanTeleport()
{
return !bFlashTeleporting && !bBurnified && Physics == PHYS_Walking && Level.TimeSeconds > TeleportBlockTime
&& LastFlashTime + 7.5 < Level.TimeSeconds;
}
function bool CanRun()
{
return (!bFlashTeleporting && !IsInState('Running') && RunCooldownEnd < Level.TimeSeconds);
}
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
{
local coords C;
local vector HeadLoc, B, M, diff;
local float t, DotMM, Distance;
local int look;
local bool bUseAltHeadShotLocation;
local bool bWasAnimating;
if (HeadBone == '')
return False;
// If we are a dedicated server estimate what animation is most likely playing on the client
if (Level.NetMode == NM_DedicatedServer)
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
// Only play the idle anim if we're not already doing a different anim.
// This prevents anims getting interrupted on the server and borking things up - Ramm
if( !IsAnimating(0) && !IsAnimating(1) )
{
if (bIsCrouched)
{
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
}
else
{
bUseAltHeadShotLocation=true;
}
}
else
{
bWasAnimating = true;
}
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
if( !bWasAnimating )
{
SetAnimFrame(0.5);
}
}
if( bUseAltHeadShotLocation )
{
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
AdditionalScale *= OnlineHeadshotScale;
}
else
{
C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis)
+ HeadOffsetY * C.YAxis;
}
//ServerHeadLocation = HeadLoc;
// Express snipe trace line in terms of B + tM
B = loc;
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
// Find Point-Line Squared Distance
diff = HeadLoc - B;
t = M Dot diff;
if (t > 0)
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
}
else
t = 0;
Distance = Sqrt(diff Dot diff);
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
}
function StartTeleport()
{
FadeStage = 1;
AlphaFader = 255;
SetCollision(false, false);
bFlashTeleporting = true;
}
function FlashTeleport()
{
local Actor Target;
local vector OldLoc;
local vector NewLoc;
local vector HitLoc;
local vector HitNorm;
local rotator RotOld;
local rotator RotNew;
local float LandTargetDist;
local int iEndAngle;
local int iAttempts;
if (Controller == none || Controller.Target == none)
return;
Target = Controller.Target;
RotOld = rotator(Target.Location - Location);
RotNew = RotOld;
OldLoc = Location;
for (iEndAngle = 0; iEndAngle < MaxTeleportAngles; iEndAngle++)
{
RotNew = RotOld;
RotNew.Yaw += iEndAngle * (65536 / MaxTelePortAngles);
for (iAttempts = 0; iAttempts < MaxTeleportAttempts; iAttempts++)
{
LandTargetDist = Target.CollisionRadius + CollisionRadius +
MinLandDist + (MaxLandDist - MinLandDist) * (iAttempts / (MaxTeleportAttempts - 1.0));
NewLoc = Target.Location - vector(RotNew) * LandTargetDist; // Target.Location - Location
NewLoc.Z = Target.Location.Z;
if (Trace(HitLoc, HitNorm, NewLoc + vect(0, 0, -500), NewLoc) != none)
NewLoc.Z = HitLoc.Z + CollisionHeight;
// Try a new location
if (SetLocation(NewLoc))
{
SetPhysics(PHYS_Walking);
if (Controller.PointReachable(Target.Location))
{
Velocity = vect(0, 0, 0);
Acceleration = vect(0, 0, 0);
SetRotation(rotator(Target.Location - Location));
PlaySound(Sound'ScrnZedPack_S.Shiver.ShiverWarpGroup', SLOT_Interact, 4.0);
Controller.GotoState('');
MonsterController(Controller).WhatToDoNext(0);
goto Teleported;
}
}
// Reset location
SetLocation(OldLoc);
}
}
Teleported:
bFlashTeleporting = false;
LastFlashTime = Level.TimeSeconds;
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
// (!)
Super.Died(Killer, damageType, HitLocation);
}
function RemoveHead()
{
local class<KFWeaponDamageType> KFDamType;
KFDamType = class<KFWeaponDamageType>(LastDamagedByType);
if ( KFDamType != none && !KFDamType.default.bIsPowerWeapon
&& !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage
&& !KFDamType.default.bIsExplosive && !KFDamType.default.bDealBurningDamage
&& !ClassIsChildOf(KFDamType, class'DamTypeDualies')
&& !ClassIsChildOf(KFDamType, class'DamTypeMK23Pistol')
&& !ClassIsChildOf(KFDamType, class'DamTypeMagnum44Pistol') )
{
LastDamageAmount *= 3.5; //significantly raise decapitation bonus for Assault Rifles
//award shiver kill on decap for Commandos
if ( KFPawn(LastDamagedBy)!=None && KFPlayerController(LastDamagedBy.Controller) != none
&& KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements) != none )
{
KFDamType.Static.AwardKill(
KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements),
KFPlayerController(LastDamagedBy.Controller), self);
}
}
if (IsInState('Running'))
GotoState('');
Super.RemoveHead();
}
function bool FlipOver()
{
if ( super.FlipOver() ) {
TeleportBlockTime = Level.TimeSeconds + 4.0; // can't teleport during stun
// do not rotate while stunned
Controller.Focus = none;
Controller.FocalPoint = Location + 512*vector(Rotation);
}
return false;
}
function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamType)
{
if (Level.TimeSeconds - LastPainSound > MinTimeBetweenPainSounds)
{
LastPainSound = Level.TimeSeconds;
PlaySound(HitSound[0], SLOT_Pain, 1.25,,400);
}
if (!IsInState('Running') && Level.TimeSeconds - LastPainAnim > MinTimeBetweenPainAnims)
{
PlayDirectionalHit(HitLocation);
LastPainAnim = Level.TimeSeconds;
}
}
simulated function int DoAnimAction( name AnimName )
{
if (AnimName=='Claw' || AnimName=='Claw2' || AnimName=='Claw3')
{
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
}
return Super.DoAnimAction(AnimName);
}
defaultproperties
{
WalkAnim="ClotWalk"
RunAnim="Run"
MaxHeadTime=0.100000
MaxTilt=10000.000000
MaxTurn=20000.000000
HeadOffsetY=-3.000000
bDelayedReaction=True
PeriodSeeTarget=2.000000
PeriodRunBase=4.000000
PeriodRunRan=4.000000
PeriodRunCoolBase=4.000000
PeriodRunCoolRan=3.000000
AlphaFader=255.000000
MinTeleportDist=550.000000
MaxTeleportDist=2000.000000
MinLandDist=150.000000
MaxLandDist=500.000000
MaxTeleportAttempts=3
MaxTeleportAngles=3
MoanVoice=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
bCannibal=True
MeleeDamage=8
damageForce=5000
KFRagdollName="Clot_Trip"
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
CrispUpThreshhold=9
PuntAnim="ClotPunt"
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.800000
SeveredHeadAttachScale=0.800000
DetachedArmClass=Class'ScrnZedPack.SeveredArmShiver'
DetachedLegClass=Class'ScrnZedPack.SeveredLegShiver'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadShiver'
PlayerCountHealthScale=0.200000
OnlineHeadshotOffset=(X=19.000000,Z=39.000000)
OnlineHeadshotScale=1.400000
MotionDetectorThreat=0.340000
HitSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverPainGroup'
DeathSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverDeathGroup'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ScoringValue=15
GroundSpeed=100.000000
WaterSpeed=100.000000
AccelRate=1024.000000
JumpZ=340.000000
HealthMax=300.000000
Health=300
HeadRadius=8.000000
HeadHeight=3.000000
HeadScale=1.300000
MenuName="Shiver.se"
MovementAnims(0)="ClotWalk"
AmbientSound=SoundGroup'ScrnZedPack_S.Shiver.ShiverIdleGroup'
Mesh=SkeletalMesh'ScrnZedPack_A.ShiverMesh'
DrawScale=1.100000
PrePivot=(Z=5.000000)
Skins(0)=Combiner'ScrnZedPack_T.Shiver.CmbRemoveAlpha'
RotationRate=(Yaw=45000,Roll=0)
}

View file

@ -0,0 +1,17 @@
class ZombieSiren_GRITTIER extends ZombieSiren;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
defaultproperties
{
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Talk'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Jump'
DetachedLegClass=Class'KFChar.SeveredLegSiren'
DetachedHeadClass=Class'KFChar.SeveredHeadSiren'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Death'
MenuName="Grittier Siren"
AmbientSound=Sound'KF_BaseSiren.Siren_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Siren_Freak'
Skins(0)=FinalBlend'KF_Specimens_Trip_T.siren_hair_fb'
Skins(1)=Combiner'ScrnZedPack_T.siren_grittier.siren_grittier_cmb'
}

View file

@ -0,0 +1,176 @@
class ZombieStalker_GRITTIER extends ZombieStalker;
#exec OBJ LOAD FILE=ScrnZedPack_T.utx
simulated function Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
if( Level.TimeSeconds > NextCheckTime && Health > 0 )
{
NextCheckTime = Level.TimeSeconds + 0.5;
if( LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() &&
VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0 ) // 640000 = 800 Units
{
bSpotted = True;
}
else
{
bSpotted = false;
}
if ( !bSpotted && !bCloaked && Skins[0] != Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_cmb' )
{
UncloakStalker();
}
else if ( Level.TimeSeconds - LastUncloakTime > 1.2 )
{
// if we're uberbrite, turn down the light
if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' )
{
bUnlit = false;
CloakStalker();
}
else if ( Skins[0] != Shader'ScrnZedPack_T.stalker_grittier.stalker_grittier_invisible' )
{
CloakStalker();
}
}
}
}
simulated function CloakStalker()
{
if ( bSpotted )
{
if( Level.NetMode == NM_DedicatedServer )
return;
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
Skins[2] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
}
if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
{
Visibility = 1;
bCloaked = true;
if( Level.NetMode == NM_DedicatedServer )
Return;
Skins[0] = Shader'ScrnZedPack_T.stalker_grittier.stalker_grittier_invisible';
Skins[1] = Shader'ScrnZedPack_T.stalker_grittier.stalker_grittier_invisible';
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
}
simulated function UnCloakStalker()
{
if( !bCrispified )
{
LastUncloakTime = Level.TimeSeconds;
Visibility = default.Visibility;
bCloaked = false;
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
if ( Skins[0] != Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_cmb' )
{
Skins[1] = FinalBlend'ScrnZedPack_T.stalker_grittier.stalker_grittier_fb';
Skins[0] = Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_cmb';
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
}
}
function RemoveHead()
{
Super.RemoveHead();
if (!bCrispified)
{
Skins[1] = FinalBlend'ScrnZedPack_T.stalker_grittier.stalker_grittier_fb';
Skins[0] = Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_cmb';
}
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
Super.PlayDying(DamageType,HitLoc);
if(bUnlit)
bUnlit=!bUnlit;
LocalKFHumanPawn = none;
if (!bCrispified)
{
Skins[1] = FinalBlend'ScrnZedPack_T.stalker_grittier.stalker_grittier_fb';
Skins[0] = Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_cmb';
}
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_cmb');
myLevel.AddPrecacheMaterial(Combiner'ScrnZedPack_T.stalker_grittier.stalker_grittier_env_cmb');
myLevel.AddPrecacheMaterial(Texture'ScrnZedPack_T.stalker_grittier.stalker_grittier_diff');
myLevel.AddPrecacheMaterial(Texture'ScrnZedPack_T.stalker_grittier.stalker_grittier_spec');
myLevel.AddPrecacheMaterial(Material'ScrnZedPack_T.stalker_grittier.stalker_grittier_invisible');
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
myLevel.AddPrecacheMaterial(Material'ScrnZedPack_T.stalker_grittier.stalker_grittier_opacity_osc');
}
defaultproperties
{
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Jump'
DetachedArmClass=Class'KFChar.SeveredArmStalker'
DetachedLegClass=Class'KFChar.SeveredLegStalker'
DetachedHeadClass=Class'KFChar.SeveredHeadStalker'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
MenuName="Grittier Stalker"
AmbientSound=Sound'KF_BaseStalker.Stalker_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Stalker_Freak'
Skins(0)=Shader'ScrnZedPack_T.stalker_grittier.stalker_grittier_invisible'
Skins(1)=Shader'ScrnZedPack_T.stalker_grittier.stalker_grittier_invisible'
}

View file

@ -0,0 +1,55 @@
<html>
<head><title>Index of /kf_sources/ScrnZedPack/Classes/</title></head>
<body>
<h1>Index of /kf_sources/ScrnZedPack/Classes/</h1><hr><pre><a href="../">../</a>
<a href="BlockHitEmitter.uc">BlockHitEmitter.uc</a> 01-Jul-2020 18:13 193
<a href="BrainDeadFemaleFP.uc">BrainDeadFemaleFP.uc</a> 01-Jul-2020 18:13 200
<a href="BruteAvoidArea.uc">BruteAvoidArea.uc</a> 01-Jul-2020 18:13 1195
<a href="BruteZombieController.uc">BruteZombieController.uc</a> 01-Jul-2020 18:13 1785
<a href="DamTypeEMP.uc">DamTypeEMP.uc</a> 01-Jul-2020 18:13 137
<a href="DamTypeTesla.uc">DamTypeTesla.uc</a> 01-Jul-2020 18:13 263
<a href="FFPController.uc">FFPController.uc</a> 01-Jul-2020 18:13 3128
<a href="FFPKarma.uc">FFPKarma.uc</a> 01-Jul-2020 18:13 1877
<a href="FFPVentEmitter.uc">FFPVentEmitter.uc</a> 01-Jul-2020 18:13 3960
<a href="FemaleFP.uc">FemaleFP.uc</a> 01-Jul-2020 18:13 27072
<a href="FemaleFP_MKII.uc">FemaleFP_MKII.uc</a> 01-Jul-2020 18:13 5180
<a href="HardPat.uc">HardPat.uc</a> 01-Jul-2020 18:13 15970
<a href="HardPatController.uc">HardPatController.uc</a> 01-Jul-2020 18:13 3350
<a href="HardPat_CIRCUS.uc">HardPat_CIRCUS.uc</a> 01-Jul-2020 18:13 5047
<a href="HardPat_GRITTIER.uc">HardPat_GRITTIER.uc</a> 01-Jul-2020 18:13 223
<a href="HardPat_HALLOWEEN.uc">HardPat_HALLOWEEN.uc</a> 01-Jul-2020 18:13 4471
<a href="HardPat_XMAS.uc">HardPat_XMAS.uc</a> 01-Jul-2020 18:13 5116
<a href="HuskFireProjectileSE.uc">HuskFireProjectileSE.uc</a> 01-Jul-2020 18:13 2497
<a href="HuskZombieControllerSE.uc">HuskZombieControllerSE.uc</a> 01-Jul-2020 18:13 8456
<a href="JasonZombieController.uc">JasonZombieController.uc</a> 01-Jul-2020 18:13 1504
<a href="SeveredArmBrute.uc">SeveredArmBrute.uc</a> 01-Jul-2020 18:13 136
<a href="SeveredArmFFP.uc">SeveredArmFFP.uc</a> 01-Jul-2020 18:13 138
<a href="SeveredArmShiver.uc">SeveredArmShiver.uc</a> 01-Jul-2020 18:13 133
<a href="SeveredArmTeslaHusk.uc">SeveredArmTeslaHusk.uc</a> 01-Jul-2020 18:13 147
<a href="SeveredGunTeslaHusk.uc">SeveredGunTeslaHusk.uc</a> 01-Jul-2020 18:13 147
<a href="SeveredHeadBrute.uc">SeveredHeadBrute.uc</a> 01-Jul-2020 18:13 632
<a href="SeveredHeadShiver.uc">SeveredHeadShiver.uc</a> 01-Jul-2020 18:13 136
<a href="SeveredHeadTeslaHusk.uc">SeveredHeadTeslaHusk.uc</a> 01-Jul-2020 18:13 150
<a href="SeveredLegBrute.uc">SeveredLegBrute.uc</a> 01-Jul-2020 18:13 135
<a href="SeveredLegFFP.uc">SeveredLegFFP.uc</a> 01-Jul-2020 18:13 138
<a href="SeveredLegShiver.uc">SeveredLegShiver.uc</a> 01-Jul-2020 18:13 133
<a href="SeveredLegTeslaHusk.uc">SeveredLegTeslaHusk.uc</a> 01-Jul-2020 18:13 147
<a href="TeslaBeam.uc">TeslaBeam.uc</a> 01-Jul-2020 18:13 5855
<a href="TeslaHusk.uc">TeslaHusk.uc</a> 01-Jul-2020 18:13 28383
<a href="TeslaHuskEMPCharge.uc">TeslaHuskEMPCharge.uc</a> 01-Jul-2020 18:13 4911
<a href="ZombieBloat_GRITTIER.uc">ZombieBloat_GRITTIER.uc</a> 01-Jul-2020 18:13 3863
<a href="ZombieBoss_GRITTIER.uc">ZombieBoss_GRITTIER.uc</a> 01-Jul-2020 18:13 1320
<a href="ZombieBrute.uc">ZombieBrute.uc</a> 01-Jul-2020 18:13 21116
<a href="ZombieClot_GRITTIER.uc">ZombieClot_GRITTIER.uc</a> 01-Jul-2020 18:13 1109
<a href="ZombieCrawler_GRITTIER.uc">ZombieCrawler_GRITTIER.uc</a> 01-Jul-2020 18:13 1887
<a href="ZombieFleshpound_GRITTIER.uc">ZombieFleshpound_GRITTIER.uc</a> 01-Jul-2020 18:13 1187
<a href="ZombieGhost.uc">ZombieGhost.uc</a> 01-Jul-2020 18:13 8892
<a href="ZombieGorefast_GRITTIER.uc">ZombieGorefast_GRITTIER.uc</a> 01-Jul-2020 18:13 3160
<a href="ZombieHusk_GRITTIER.uc">ZombieHusk_GRITTIER.uc</a> 01-Jul-2020 18:13 5990
<a href="ZombieJason.uc">ZombieJason.uc</a> 01-Jul-2020 18:13 5997
<a href="ZombieScrake_GRITTIER.uc">ZombieScrake_GRITTIER.uc</a> 01-Jul-2020 18:13 1460
<a href="ZombieShiver.uc">ZombieShiver.uc</a> 01-Jul-2020 18:13 16674
<a href="ZombieSiren_GRITTIER.uc">ZombieSiren_GRITTIER.uc</a> 01-Jul-2020 18:13 758
<a href="ZombieStalker_GRITTIER.uc">ZombieStalker_GRITTIER.uc</a> 01-Jul-2020 18:13 6580
</pre><hr></body>
</html>