983 lines
26 KiB
Ucode
983 lines
26 KiB
Ucode
////////////////////////////////////////////////////////////////////////////
|
|
// 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)
|
|
}
|