KF2-Server-Extension/ServerExt/Classes/VSZombie.uc
2017-10-19 21:12:29 -05:00

483 lines
13 KiB
Ucode

Class VSZombie extends KFPawn_Monster
abstract;
var() class<DamageType> MeleeHitDT;
var() string ZombieName;
var transient VSFPZedHands FPHandModel;
var() int MeleeDamage,HitsPerAttack;
var() float PropDamageScale;
var byte IsFiringMode[3];
var repnotify byte ServerAttackMode;
var() vector FPHandOffset;
var() name FPHandsIdle;
var() float HPScaler;
var bool bAttacking,bShowFPHands,bFPShowBody,bLockMovement;
var() bool bBoss;
replication
{
if( true )
ServerAttackMode;
}
// Bot AI.
function bool BotFire(bool bFinished)
{
StartFire(0);
return true;
}
function bool CanAttack(Actor Other)
{
return VSizeSq(Other.Location-Location)<62500.f; // 250.f
}
function bool IsFiring()
{
local byte i;
for( i=0; i<ArrayCount(IsFiringMode); ++i )
if( IsFiringMode[i]!=0 )
return true;
return false;
}
function bool StopFiring()
{
local byte i;
for( i=0; i<ArrayCount(IsFiringMode); ++i )
IsFiringMode[i] = 0;
return true;
}
function bool HasRangedAttack()
{
return false;
}
simulated function ModifyPlayerInput( PlayerController PC, float DeltaTime )
{
if( bLockMovement )
{
PC.PlayerInput.aForward = 0;
PC.PlayerInput.aStrafe = 0;
PC.bPressedJump = false;
}
}
function SetSprinting(bool bNewSprintStatus);
// Handle animation input.
function PlayAttackAnim( byte Num )
{
if( !bAttacking )
ServerAttackAnim(Num);
}
simulated function float StartAttackAnim( byte Num ) // Return animation duration.
{
return 0.2f;
}
simulated function AttackFinished()
{
local byte i;
bAttacking = false;
bLockMovement = false; // Usually at this point enable movement.
if( WorldInfo.NetMode!=NM_Client )
{
// Attack again.
for( i=0; i<ArrayCount(IsFiringMode); ++i )
{
if( IsFiringMode[i]!=0 )
{
PlayAttackAnim(i);
break;
}
}
}
}
// Handle fire input.
reliable server function ServerModeFire( bool bStart, byte Mode )
{
if( (bStart && bNoWeaponFiring) || Mode>=ArrayCount(IsFiringMode) || WorldInfo.NetMode==NM_Client )
return;
IsFiringMode[Mode] = byte(bStart);
if( bStart )
PlayAttackAnim(Mode);
}
simulated function StartFire(byte FireModeNum)
{
if( bNoWeaponFiring )
return;
ServerModeFire(true,FireModeNum);
}
simulated function StopFire(byte FireModeNum)
{
ServerModeFire(false,FireModeNum);
}
function DropProp();
// Handle animation replication.
function ServerAttackAnim( byte Num )
{
local float F;
bAttacking = true;
F = StartAttackAnim(Num);
SetTimer(F,false,'AttackFinished');
if( WorldInfo.NetMode!=NM_StandAlone )
{
Num+=1;
if( Num==ServerAttackMode )
ServerAttackMode = ServerAttackMode | 128;
else ServerAttackMode = Num;
SetTimer(F+0.5,false,'ResetAttackTimer');
}
}
function ResetAttackTimer()
{
ServerAttackMode = 0;
}
simulated event ReplicatedEvent(name VarName)
{
switch( VarName )
{
case 'ServerAttackMode':
if( ServerAttackMode!=0 )
{
ServerAttackMode = (ServerAttackMode & 127)-1;
bAttacking = true;
SetTimer(StartAttackAnim(ServerAttackMode),false,'AttackFinished');
}
break;
default:
Super.ReplicatedEvent(VarName);
}
}
// Handle melee damage.
simulated function MeleeImpactNotify(KFAnimNotify_MeleeImpact Notify)
{
if( WorldInfo.NetMode!=NM_Client )
MeleeDamageTarget(vector(Rotation)*MeleeDamage*500.f);
}
function bool MeleeDamageTarget( vector pushdir )
{
local Actor A;
local vector Dir,HL,HN,Start;
local byte T;
local bool bResult,bHitActors;
if( WorldInfo.NetMode==NM_Client || Controller==None )
Return False; // Never should be done on client.
Start = GetPawnViewLocation();
Dir = vector(GetAdjustedAimFor(None,Start));
T = GetTeamNum();
// First try if can hit with a small extent trace.
foreach TraceActors(Class'Actor',A,HL,HN,Start+Dir*120.f,Start,vect(8,8,8))
{
if( A.bWorldGeometry || A==WorldInfo )
{
if( KFDoorActor(A)!=None )
{
DamageDoor(KFDoorActor(A),HL,pushdir);
bHitActors = true;
}
bResult = true;
break;
}
else if( Pawn(A)!=None )
{
if( Pawn(A).Health>0 && (Pawn(A).GetTeamNum()!=T || WorldInfo.Game.bGameEnded) )
{
DealMeleeDamage(A,HL,pushdir);
bResult = true;
bHitActors = true;
}
}
}
if( !bHitActors )
{
// Then try with large extent.
HN.X = GetCollisionRadius()*0.75;
HN.Y = HN.X;
HN.Z = GetCollisionHeight()*0.5;
foreach TraceActors(Class'Actor',A,HL,HN,Location+Dir*120.f,Location,HN)
{
if( A.bWorldGeometry || A==WorldInfo )
{
if( KFDoorActor(A)!=None )
DamageDoor(KFDoorActor(A),HL,pushdir);
else DealMeleeDamage(A,HL,pushdir);
bResult = true;
break;
}
else if( Pawn(A)!=None )
{
if( Pawn(A).Health>0 && (Pawn(A).GetTeamNum()!=T || WorldInfo.Game.bGameEnded) )
{
DealMeleeDamage(A,HL,pushdir);
bResult = true;
}
}
else DealMeleeDamage(A,HL,pushdir);
}
}
return bResult;
}
function DealMeleeDamage( Actor Other, vector HL, vector pushdir )
{
if( KFPawn_Monster(Other)!=None && KFPawn_Monster(Other).GetTeamNum()==0 )
Other.TakeDamage(MeleeDamage*5, Controller, HL, pushdir, MeleeHitDT,,Self); // Almost insta-kill pet zeds.
else Other.TakeDamage(MeleeDamage, Controller, HL, pushdir, MeleeHitDT,,Self);
}
function DamageDoor( KFDoorActor D, vector HL, vector pushdir )
{
local bool bIsP;
// Hack method, fake player being a NPC while damage is dealt to allow destroy the door.
bIsP = Controller.bIsPlayer;
Controller.bIsPlayer = false;
D.TakeDamage(MeleeDamage, Controller, HL, pushdir, MeleeHitDT,,Self);
Controller.bIsPlayer = bIsP;
}
/** Apply damage to a specific zone (useful for gore effects) */
function TakeHitZoneDamage(float Damage, class<DamageType> DamageType, int HitZoneIdx, vector InstigatorLocation)
{
local float HeadHealthPercentage;
Super(KFPawn).TakeHitZoneDamage(Damage, DamageType, HitZoneIdx, InstigatorLocation);
if ( HitZoneIdx == HZI_Head && Health>0 ) // Don't remove head until zombie actually dies.
HitZones[HZI_Head].GoreHealth = Max(HitZones[HZI_Head].GoreHealth,1);
// When GoreHealth <= 0, check to see if this weapon can dismember limbs
if ( HitZones[HitZoneIdx].GoreHealth <= 0 && CanInjureHitZone(DamageType, HitZoneIdx) )
HitZoneInjured(HitZoneIdx);
// Handle head injuries
if ( HitZoneIdx == HZI_Head )
{
// Based on head health, calculate number of head chunks we're allowed to remove
if( !bPlayedDeath && !bIsHeadless && !bTearOff )
{
HeadHealthPercentage = GetHeadHealthPercent();
if( HeadHealthPercentage > 0.5 )
{
MaxHeadChunkGoreWhileAlive = 1;
}
else if ( HeadHealthPercentage > 0.25 )
{
MaxHeadChunkGoreWhileAlive = 2;
}
else if ( HeadHealthPercentage > 0.0 )
{
MaxHeadChunkGoreWhileAlive = 3;
}
}
}
}
simulated function SetMeshVisibility(bool bVisible)
{
Super.SetMeshVisibility(bVisible);
bShowFPHands = !bVisible;
}
// First person hands.
simulated function DrawHUD( HUD H )
{
Super.DrawHUD(H);
if( Health<=0 )
return;
if( !bShowFPHands )
{
if( FPHandModel!=None && !FPHandModel.bHidden )
FPHandModel.SetHidden(true);
}
else
{
if( FPHandModel==None )
{
FPHandModel = Spawn(class'VSFPZedHands',Self);
FPHandModel.InitHands(Mesh);
FPHandModel.Mesh.SetLightingChannels(PawnLightingChannel);
InitFPHands();
}
if( FPHandModel.bHidden )
FPHandModel.SetHidden(false);
FPHandModel.SetRotation(GetViewRotation());
FPHandModel.SetLocation(GetPawnViewLocation()+(FPHandOffset >> FPHandModel.Rotation));
}
}
simulated function InitFPHands()
{
FPHandModel.Mesh.HideBoneByName('RightUpLeg',PBO_Term);
FPHandModel.Mesh.HideBoneByName('LeftUpLeg',PBO_Term);
FPHandModel.Mesh.HideBoneByName('Neck',PBO_Term);
FPHandModel.SetIdleAnim(FPHandsIdle);
}
simulated function Destroyed()
{
DisableNightVision();
if( FPHandModel!=None )
FPHandModel.Destroy();
Super.Destroyed();
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
DisableNightVision();
if( FPHandModel!=None )
FPHandModel.Destroy();
Super.PlayDying(DamageType,HitLoc);
}
exec function TestFPOffset( vector V )
{
FPHandOffset = V;
}
simulated final function DisableNightVision()
{
local KFPlayerController KFPC;
if( IsLocallyControlled() )
{
KFPC = KFPlayerController(Controller);
if( KFPC!=None && KFPC.bNightVisionActive )
KFPC.SetNightVision(false);
}
}
function PossessedBy( Controller C, bool bVehicleTransition )
{
Super(KFPawn).PossessedBy( C, bVehicleTransition );
bReducedZedOnZedPinchPointCollisionStateActive = false;
bIgnoreTeamCollision = true;
}
// Removed insane damage multiplier for headshot.
function AdjustDamage(out int InDamage, out vector Momentum, Controller InstigatedBy, vector HitLocation, class<DamageType> DamageType, TraceHitInfo HitInfo, Actor DamageCauser)
{
local float TempDamage,DamageMod;
local int HitZoneIdx;
Super(KFPawn).AdjustDamage(InDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser);
TempDamage = InDamage; // Multiply by float value all the way for more precise result.
// is vulnerable?
DamageMod = 1.f;
if( IsVulnerableTo(DamageType,DamageMod) )
{
TempDamage *= DamageMod;
}
else if( IsResistantTo(DamageType,DamageMod) )
{
TempDamage *= DamageMod;
}
// Cached hit params
if( HitInfo.BoneName!='' && class<KFDT_Bludgeon>(DamageType)==None && class<KFDT_Slashing>(DamageType)==None )
{
HitZoneIdx = HitZones.Find('ZoneName', HitInfo.BoneName);
if( HitZoneIdx>=0 )
TempDamage *= HitZones[HitZoneIdx].DmgScale;
}
InDamage = FCeil( TempDamage );
if( InstigatedBy!=None && InstigatedBy!=Controller && InstigatedBy.GetTeamNum()==0 )
{
// Give credits to pets owner.
if( Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo)!=None )
InstigatedBy = Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo).OwnerController;
if( InstigatedBy!=None )
AddTakenDamage( InstigatedBy, FMin(Health, InDamage), DamageCauser, class<KFDamageType>(DamageType) );
}
}
event Landed(vector HitNormal, actor FloorActor)
{
Super.Landed(HitNormal, FloorActor);
if ( Velocity.Z < -200 )
{
// Slow down after a jump.
Velocity.X *= 0.02;
Velocity.Y *= 0.02;
}
}
function bool DoJump( bool bUpdating )
{
if( bJumpCapable && (Physics == PHYS_Walking || Physics == PHYS_Ladder || Physics == PHYS_Spider) )
{
if ( Physics == PHYS_Spider )
Velocity = Velocity + (JumpZ * Floor);
else if ( Physics == PHYS_Ladder )
Velocity.Z = 0;
else Velocity.Z = JumpZ;
if (Base != None && !Base.bWorldGeometry && Base.Velocity.Z > 0.f)
Velocity.Z += Base.Velocity.Z;
SetPhysics(PHYS_Falling);
return true;
}
return false;
}
// Nope.
function bool CanBeGrabbed(KFPawn GrabbingPawn, optional bool bIgnoreFalling, optional bool bAllowSameTeamGrab)
{
return false;
}
// UI stuff.
simulated function String GetHumanReadableName()
{
if ( PlayerReplicationInfo != None )
return PlayerReplicationInfo.PlayerName;
return ZombieName;
}
defaultproperties
{
bIgnoreBaseRotation=true
MeleeDamage=22
HitsPerAttack=1
bCanCrouch=true
FPHandOffset=(X=-40,Z=-70)
FPHandsIdle="Walk_B_taunt_V2"
MeleeHitDT=class'KFDT_ZombieHit'
AccelRate=800
HPScaler=1
InventoryManagerClass=None // No weapons for bots!
ZombieName="Zombie"
PropDamageScale=0.65
HitZones(0)=(ZoneName="head",BoneName="head",GoreHealth=20,Limb=BP_Head,DmgScale=2)
HitZones(1)=(ZoneName="neck",BoneName="neck",GoreHealth=20,Limb=BP_Head,DmgScale=2)
HitZones(2)=(ZoneName="chest",BoneName="Spine2",GoreHealth=150,Limb=BP_Torso)
HitZones(3)=(ZoneName="heart",BoneName="Spine2",GoreHealth=150,Limb=BP_Special)
HitZones(4)=(ZoneName="lupperarm",BoneName="LeftArm",Limb=BP_LeftArm,DmgScale=0.4)
HitZones(5)=(ZoneName="lforearm",BoneName="LeftForearm",GoreHealth=15,Limb=BP_LeftArm,DmgScale=0.4)
HitZones(6)=(ZoneName="lhand",BoneName="LeftHand",GoreHealth=20,Limb=BP_LeftArm,DmgScale=0.4)
HitZones(7)=(ZoneName="rupperarm",BoneName="RightArm",Limb=BP_RightArm,DmgScale=0.4)
HitZones(8)=(ZoneName="rforearm",BoneName="RightForearm",GoreHealth=15,Limb=BP_RightArm,DmgScale=0.4)
HitZones(9)=(ZoneName="rhand",BoneName="RightHand",GoreHealth=20,Limb=BP_RightArm,DmgScale=0.4)
HitZones(10)=(ZoneName="stomach",BoneName="Spine1",GoreHealth=150,Limb=BP_Torso)
HitZones(11)=(ZoneName="abdomen",BoneName="Hips",GoreHealth=150,Limb=BP_Torso)
HitZones(12)=(ZoneName="lthigh",BoneName="LeftUpLeg",GoreHealth=75,Limb=BP_LeftLeg,DmgScale=0.75)
HitZones(13)=(ZoneName="lcalf",BoneName="LeftLeg",GoreHealth=25,Limb=BP_LeftLeg,DmgScale=0.7)
HitZones(14)=(ZoneName="lfoot",BoneName="LeftFoot",GoreHealth=15,Limb=BP_LeftLeg,DmgScale=0.7)
HitZones(15)=(ZoneName="rthigh",BoneName="RightUpLeg",GoreHealth=75,Limb=BP_RightLeg,DmgScale=0.75)
HitZones(16)=(ZoneName="rcalf",BoneName="RightLeg",GoreHealth=25,Limb=BP_RightLeg,DmgScale=0.7)
HitZones(17)=(ZoneName="rfoot",BoneName="RightFoot",GoreHealth=15,Limb=BP_RightLeg,DmgScale=0.7)
Begin Object Name=KFPawnSkeletalMeshComponent
RBChannel=RBCC_Untitled3
End Object
}