KF2-Server-Extension/ServerExt/Classes/VSZombie.uc

482 lines
12 KiB
Ucode
Raw Normal View History

2017-10-20 02:00:49 +00:00
Class VSZombie extends KFPawn_Monster
abstract;
var() class<DamageType> MeleeHitDT;
var() localized string ZombieName;
2017-10-20 02:00:49 +00:00
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
{
2020-11-28 20:04:55 +00:00
if(true)
2017-10-20 02:00:49 +00:00
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;
2020-11-28 20:04:55 +00:00
for(i=0; i<ArrayCount(IsFiringMode); ++i)
if(IsFiringMode[i]!=0)
2017-10-20 02:00:49 +00:00
return true;
return false;
}
function bool StopFiring()
{
local byte i;
2020-11-28 20:04:55 +00:00
for(i=0; i<ArrayCount(IsFiringMode); ++i)
2017-10-20 02:00:49 +00:00
IsFiringMode[i] = 0;
return true;
}
function bool HasRangedAttack()
{
return false;
}
2020-11-28 20:04:55 +00:00
simulated function ModifyPlayerInput(PlayerController PC, float DeltaTime)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(bLockMovement)
2017-10-20 02:00:49 +00:00
{
PC.PlayerInput.aForward = 0;
PC.PlayerInput.aStrafe = 0;
PC.bPressedJump = false;
}
}
function SetSprinting(bool bNewSprintStatus);
// Handle animation input.
2020-11-28 20:04:55 +00:00
function PlayAttackAnim(byte Num)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(!bAttacking)
2017-10-20 02:00:49 +00:00
ServerAttackAnim(Num);
}
2020-11-28 20:04:55 +00:00
simulated function float StartAttackAnim(byte Num) // Return animation duration.
2017-10-20 02:00:49 +00:00
{
return 0.2f;
}
simulated function AttackFinished()
{
local byte i;
bAttacking = false;
bLockMovement = false; // Usually at this point enable movement.
2020-11-28 20:04:55 +00:00
if(WorldInfo.NetMode!=NM_Client)
2017-10-20 02:00:49 +00:00
{
// Attack again.
2020-11-28 20:04:55 +00:00
for(i=0; i<ArrayCount(IsFiringMode); ++i)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(IsFiringMode[i]!=0)
2017-10-20 02:00:49 +00:00
{
PlayAttackAnim(i);
break;
}
}
}
}
// Handle fire input.
2020-11-28 20:04:55 +00:00
reliable server function ServerModeFire(bool bStart, byte Mode)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if((bStart && bNoWeaponFiring) || Mode>=ArrayCount(IsFiringMode) || WorldInfo.NetMode==NM_Client)
2017-10-20 02:00:49 +00:00
return;
IsFiringMode[Mode] = byte(bStart);
2020-11-28 20:04:55 +00:00
if(bStart)
2017-10-20 02:00:49 +00:00
PlayAttackAnim(Mode);
}
simulated function StartFire(byte FireModeNum)
{
2020-11-28 20:04:55 +00:00
if(bNoWeaponFiring)
2017-10-20 02:00:49 +00:00
return;
ServerModeFire(true,FireModeNum);
}
simulated function StopFire(byte FireModeNum)
{
ServerModeFire(false,FireModeNum);
}
function DropProp();
// Handle animation replication.
2020-11-28 20:04:55 +00:00
function ServerAttackAnim(byte Num)
2017-10-20 02:00:49 +00:00
{
local float F;
bAttacking = true;
F = StartAttackAnim(Num);
SetTimer(F,false,'AttackFinished');
2020-11-28 20:04:55 +00:00
if(WorldInfo.NetMode!=NM_StandAlone)
2017-10-20 02:00:49 +00:00
{
Num+=1;
2020-11-28 20:04:55 +00:00
if(Num==ServerAttackMode)
2017-10-20 02:00:49 +00:00
ServerAttackMode = ServerAttackMode | 128;
else ServerAttackMode = Num;
SetTimer(F+0.5,false,'ResetAttackTimer');
}
}
function ResetAttackTimer()
{
ServerAttackMode = 0;
}
simulated event ReplicatedEvent(name VarName)
{
2020-11-28 20:04:55 +00:00
switch(VarName)
2017-10-20 02:00:49 +00:00
{
case 'ServerAttackMode':
2020-11-28 20:04:55 +00:00
if(ServerAttackMode!=0)
2017-10-20 02:00:49 +00:00
{
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)
{
2020-11-28 20:04:55 +00:00
if(WorldInfo.NetMode!=NM_Client)
2017-10-20 02:00:49 +00:00
MeleeDamageTarget(vector(Rotation)*MeleeDamage*500.f);
}
2020-11-28 20:04:55 +00:00
function bool MeleeDamageTarget(vector pushdir)
2017-10-20 02:00:49 +00:00
{
local Actor A;
local vector Dir,HL,HN,Start;
local byte T;
local bool bResult,bHitActors;
2020-11-28 20:04:55 +00:00
if(WorldInfo.NetMode==NM_Client || Controller==None)
2017-10-20 02:00:49 +00:00
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))
{
2020-11-28 20:04:55 +00:00
if(A.bWorldGeometry || A==WorldInfo)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(KFDoorActor(A)!=None)
2017-10-20 02:00:49 +00:00
{
DamageDoor(KFDoorActor(A),HL,pushdir);
bHitActors = true;
}
bResult = true;
break;
}
2020-11-28 20:04:55 +00:00
else if(Pawn(A)!=None)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(Pawn(A).Health>0 && (Pawn(A).GetTeamNum()!=T || WorldInfo.Game.bGameEnded))
2017-10-20 02:00:49 +00:00
{
DealMeleeDamage(A,HL,pushdir);
bResult = true;
bHitActors = true;
}
}
}
2020-11-28 20:04:55 +00:00
if(!bHitActors)
2017-10-20 02:00:49 +00:00
{
// 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)
{
2020-11-28 20:04:55 +00:00
if(A.bWorldGeometry || A==WorldInfo)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(KFDoorActor(A)!=None)
2017-10-20 02:00:49 +00:00
DamageDoor(KFDoorActor(A),HL,pushdir);
else DealMeleeDamage(A,HL,pushdir);
bResult = true;
break;
}
2020-11-28 20:04:55 +00:00
else if(Pawn(A)!=None)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(Pawn(A).Health>0 && (Pawn(A).GetTeamNum()!=T || WorldInfo.Game.bGameEnded))
2017-10-20 02:00:49 +00:00
{
DealMeleeDamage(A,HL,pushdir);
bResult = true;
}
}
else DealMeleeDamage(A,HL,pushdir);
}
}
return bResult;
}
2020-11-28 20:04:55 +00:00
function DealMeleeDamage(Actor Other, vector HL, vector pushdir)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(KFPawn_Monster(Other)!=None && KFPawn_Monster(Other).GetTeamNum()==0)
2017-10-20 02:00:49 +00:00
Other.TakeDamage(MeleeDamage*5, Controller, HL, pushdir, MeleeHitDT,,Self); // Almost insta-kill pet zeds.
else Other.TakeDamage(MeleeDamage, Controller, HL, pushdir, MeleeHitDT,,Self);
}
2020-11-28 20:04:55 +00:00
function DamageDoor(KFDoorActor D, vector HL, vector pushdir)
2017-10-20 02:00:49 +00:00
{
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);
2020-11-28 20:04:55 +00:00
if (HitZoneIdx == HZI_Head && Health>0) // Don't remove head until zombie actually dies.
2017-10-20 02:00:49 +00:00
HitZones[HZI_Head].GoreHealth = Max(HitZones[HZI_Head].GoreHealth,1);
// When GoreHealth <= 0, check to see if this weapon can dismember limbs
2020-11-28 20:04:55 +00:00
if (HitZones[HitZoneIdx].GoreHealth <= 0 && CanInjureHitZone(DamageType, HitZoneIdx))
2017-10-20 02:00:49 +00:00
HitZoneInjured(HitZoneIdx);
// Handle head injuries
2020-11-28 20:04:55 +00:00
if (HitZoneIdx == HZI_Head)
2017-10-20 02:00:49 +00:00
{
// Based on head health, calculate number of head chunks we're allowed to remove
2020-11-28 20:04:55 +00:00
if(!bPlayedDeath && !bIsHeadless && !bTearOff)
2017-10-20 02:00:49 +00:00
{
HeadHealthPercentage = GetHeadHealthPercent();
2020-11-28 20:04:55 +00:00
if(HeadHealthPercentage > 0.5)
2017-10-20 02:00:49 +00:00
{
MaxHeadChunkGoreWhileAlive = 1;
}
2020-11-28 20:04:55 +00:00
else if (HeadHealthPercentage > 0.25)
2017-10-20 02:00:49 +00:00
{
MaxHeadChunkGoreWhileAlive = 2;
}
2020-11-28 20:04:55 +00:00
else if (HeadHealthPercentage > 0.0)
2017-10-20 02:00:49 +00:00
{
MaxHeadChunkGoreWhileAlive = 3;
}
}
}
}
simulated function SetMeshVisibility(bool bVisible)
{
Super.SetMeshVisibility(bVisible);
bShowFPHands = !bVisible;
}
// First person hands.
2020-11-28 20:04:55 +00:00
simulated function DrawHUD(HUD H)
2017-10-20 02:00:49 +00:00
{
Super.DrawHUD(H);
2020-11-28 20:04:55 +00:00
if(Health<=0)
2017-10-20 02:00:49 +00:00
return;
2020-11-28 20:04:55 +00:00
if(!bShowFPHands)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(FPHandModel!=None && !FPHandModel.bHidden)
2017-10-20 02:00:49 +00:00
FPHandModel.SetHidden(true);
}
else
{
2020-11-28 20:04:55 +00:00
if(FPHandModel==None)
2017-10-20 02:00:49 +00:00
{
FPHandModel = Spawn(class'VSFPZedHands',Self);
FPHandModel.InitHands(Mesh);
FPHandModel.Mesh.SetLightingChannels(PawnLightingChannel);
InitFPHands();
}
2020-11-28 20:04:55 +00:00
if(FPHandModel.bHidden)
2017-10-20 02:00:49 +00:00
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();
2020-11-28 20:04:55 +00:00
if(FPHandModel!=None)
2017-10-20 02:00:49 +00:00
FPHandModel.Destroy();
Super.Destroyed();
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
DisableNightVision();
2020-11-28 20:04:55 +00:00
if(FPHandModel!=None)
2017-10-20 02:00:49 +00:00
FPHandModel.Destroy();
Super.PlayDying(DamageType,HitLoc);
}
2020-11-28 20:04:55 +00:00
exec function TestFPOffset(vector V)
2017-10-20 02:00:49 +00:00
{
FPHandOffset = V;
}
simulated final function DisableNightVision()
{
local KFPlayerController KFPC;
2020-11-28 20:04:55 +00:00
if(IsLocallyControlled())
2017-10-20 02:00:49 +00:00
{
KFPC = KFPlayerController(Controller);
2020-11-28 20:04:55 +00:00
if(KFPC!=None && KFPC.bNightVisionActive)
2017-10-20 02:00:49 +00:00
KFPC.SetNightVision(false);
}
}
2020-11-28 20:04:55 +00:00
function PossessedBy(Controller C, bool bVehicleTransition)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
Super(KFPawn).PossessedBy(C, bVehicleTransition);
2017-10-20 02:00:49 +00:00
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;
2020-11-28 20:04:55 +00:00
if(IsVulnerableTo(DamageType,DamageMod))
2017-10-20 02:00:49 +00:00
{
TempDamage *= DamageMod;
}
2020-11-28 20:04:55 +00:00
else if(IsResistantTo(DamageType,DamageMod))
2017-10-20 02:00:49 +00:00
{
TempDamage *= DamageMod;
}
// Cached hit params
2020-11-28 20:04:55 +00:00
if(HitInfo.BoneName!='' && class<KFDT_Bludgeon>(DamageType)==None && class<KFDT_Slashing>(DamageType)==None)
2017-10-20 02:00:49 +00:00
{
HitZoneIdx = HitZones.Find('ZoneName', HitInfo.BoneName);
2020-11-28 20:04:55 +00:00
if(HitZoneIdx>=0)
2017-10-20 02:00:49 +00:00
TempDamage *= HitZones[HitZoneIdx].DmgScale;
}
2020-11-28 20:04:55 +00:00
InDamage = FCeil(TempDamage);
2017-10-20 02:00:49 +00:00
2020-11-28 20:04:55 +00:00
if(InstigatedBy!=None && InstigatedBy!=Controller && InstigatedBy.GetTeamNum()==0)
2017-10-20 02:00:49 +00:00
{
// Give credits to pets owner.
2020-11-28 20:04:55 +00:00
if(Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo)!=None)
2017-10-20 02:00:49 +00:00
InstigatedBy = Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo).OwnerController;
2020-11-28 20:04:55 +00:00
if(InstigatedBy!=None)
AddTakenDamage(InstigatedBy, FMin(Health, InDamage), DamageCauser, class<KFDamageType>(DamageType));
2017-10-20 02:00:49 +00:00
}
}
event Landed(vector HitNormal, actor FloorActor)
{
Super.Landed(HitNormal, FloorActor);
2020-11-28 20:04:55 +00:00
if (Velocity.Z < -200)
2017-10-20 02:00:49 +00:00
{
// Slow down after a jump.
Velocity.X *= 0.02;
Velocity.Y *= 0.02;
}
}
2020-11-28 20:04:55 +00:00
function bool DoJump(bool bUpdating)
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if(bJumpCapable && (Physics == PHYS_Walking || Physics == PHYS_Ladder || Physics == PHYS_Spider))
2017-10-20 02:00:49 +00:00
{
2020-11-28 20:04:55 +00:00
if (Physics == PHYS_Spider)
2017-10-20 02:00:49 +00:00
Velocity = Velocity + (JumpZ * Floor);
2020-11-28 20:04:55 +00:00
else if (Physics == PHYS_Ladder)
2017-10-20 02:00:49 +00:00
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.
2017-10-20 02:12:29 +00:00
function bool CanBeGrabbed(KFPawn GrabbingPawn, optional bool bIgnoreFalling, optional bool bAllowSameTeamGrab)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
return false;
2017-10-20 02:00:49 +00:00
}
// UI stuff.
simulated function String GetHumanReadableName()
{
2020-11-28 20:04:55 +00:00
if (PlayerReplicationInfo != None)
2017-10-20 02:00:49 +00:00
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!
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
}