Class HopMineProj extends Projectile; #exec obj load file="KF_GrenadeSnd.uax" #exec OBJ LOAD FILE=ScrnWeaponPack_T.utx #exec OBJ LOAD FILE=ScrnWeaponPack_SND.uax #exec OBJ LOAD FILE=ScrnWeaponPack_A.ukx var PanzerfaustTrail SmokeTrail; var vector RepAttachPos,RepAttachDir,RepLaunchPos; var HopMineLight DotLight; var HopMineLchr WeaponOwner; var bool bDisintegrated; // This nade has been disintegrated by a siren scream. var() sound DisintegrateSound;// The sound of this projectile disintegrating var bool bWarningTarget,bCriticalTarget,bPreLaunched,bNeedsDetonate; var float MinSpread; // min distance between placed mines. If placed closer, they will trigger each other var int PlacedTeam; // TeamIndex of the team that placed this projectile replication { // Variables the server should send to the client. reliable if( Role==ROLE_Authority ) RepAttachPos,RepAttachDir,bWarningTarget,bCriticalTarget,RepLaunchPos; } simulated function AddSmoke() { if( SmokeTrail==None ) SmokeTrail = Spawn(class'PanzerfaustTrail',self); SmokeTrail.SetBase(self); SmokeTrail.SetRelativeRotation(rot(32768,0,0)); } simulated function PostBeginPlay() { Velocity = speed * vector(Rotation); Super.PostBeginPlay(); } simulated function Destroyed() { if( WeaponOwner!=None ) WeaponOwner.RemoveMine(Self); if ( SmokeTrail != None ) SmokeTrail.HandleOwnerDestroyed(); if( DotLight!=None ) DotLight.Destroy(); if( Level.NetMode==NM_Client ) BlowUp(Location); Super.Destroyed(); } simulated function Landed( vector HitNormal ) { HitWall(HitNormal,Base); } simulated function HitWall(vector HitNormal, actor Wall) { if( Level.NetMode==NM_Client ) { SetPhysics(PHYS_None); return; } AttachTo(Location,HitNormal,Wall); } simulated final function AttachTo( vector Pos, vector Dir, Actor Wall ) { SetPhysics(PHYS_None); SetLocation(Pos + Dir*2); SetRotation(rotator(Dir)-rot(16384,0,0)); RepAttachPos = Location; RepAttachDir = Dir*1000.f; if( Wall!=None && !Wall.bStatic ) SetBase(Wall); GoToState('OnWall'); } simulated function PostNetBeginPlay() { bNetNotify = true; if( Physics==PHYS_None ) GoToState('OnWall'); else if( Physics==PHYS_Projectile || RepLaunchPos!=vect(0,0,0) ) GoToState('LaunchMine'); else if ( Level.NetMode!=NM_DedicatedServer ) { AddSmoke(); TweenAnim('Up',0.01f); PlaySound(Sound'ScrnWeaponPack_SND.mine.blade_in'); RandSpin(65000.f); } } simulated function PostNetReceive() { if( RepAttachPos!=vect(0,0,0) ) { ClientAttachProj(); RepAttachPos = vect(0,0,0); } if( bHidden && !bDisintegrated ) { Disintegrate(Location, vect(0,0,1)); } } simulated final function ClientAttachProj() { local vector HL,HN,D; local Actor A; D = Normal(RepAttachDir); A = Trace(HL,HN,RepAttachPos-D*10.f,RepAttachPos+D,false); if( A==None ) { HN = D; HL = RepAttachPos; } AttachTo(HL,HN,A); } simulated function BlowUp(vector HitLocation) { if( Level.NetMode!=NM_DedicatedServer ) { PlaySound(ImpactSound,,2.0); if ( EffectIsRelevant(Location,false) ) Spawn(class'KFNadeLExplosion',,,HitLocation, rot(16384,0,0)); } HurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation ); if ( Role == ROLE_Authority ) MakeNoise(1.0); } simulated function Touch(Actor Other); simulated function ClientSideTouch(Actor Other, Vector HitLocation); simulated function ProcessTouch(Actor Other, Vector HitLocation); state OnWall { simulated function BeginState() { local HopMineProj Other; if ( SmokeTrail != None ) { SmokeTrail.HandleOwnerDestroyed(); SmokeTrail = None; } bCollideWorld = False; bFixedRotationDir = false; RotationRate = rot(0,0,0); if( Level.NetMode!=NM_DedicatedServer ) { TweenAnim('Down',0.05f); PlaySound(Sound'ScrnWeaponPack_SND.mine.blade_cut'); DotLight = Spawn(Class'HopMineLight',,,Location + (vect(0,0,5) << Rotation)); if( DotLight!=None ) { DotLight.SetBase(Self); DotLight.SetRelativeLocation(vect(0,0,5)); } } if( Level.NetMode!=NM_Client ) { NetUpdateFrequency = 1.f; SetTimer(0.25+FRand()*0.25,true); } // destroy other mines around -- PooSH foreach VisibleCollidingActors( class'HopMineProj', Other, MinSpread, Location ) { if ( Other != self && !Other.bDeleteMe && Other.IsInState('OnWall') ) { Other.RepLaunchPos = Location; Other.bCriticalTarget = true; Other.GoToState('LaunchMine'); Other.NetUpdateTime = Level.TimeSeconds-1; } } } simulated function EndState() { if( Level.NetMode!=NM_Client ) { SetTimer(0,false); NetUpdateFrequency = Default.NetUpdateFrequency; NetUpdateTime = Level.TimeSeconds-1; } } function Timer() { local Controller C; local vector X,Y,Z; local float DotP; local int ThreatLevel; local bool bA,bB; GetAxes(Rotation,X,Y,Z); for( C=Level.ControllerList; C!=None; C=C.nextController ) if( C.Pawn!=None && C.Pawn.Health>0 && VSizeSquared(C.Pawn.Location-Location)<1000000.f ) { X = C.Pawn.Location-Location; DotP = (X Dot Z); if( DotP<0 ) continue; DotP = VSizeSquared(X - (Z * DotP)); if( DotP>90000.f || !FastTrace(C.Pawn.Location,Location) ) continue; if( Monster(C.Pawn)!=None ) { bB = true; if( DotP<35500.f ) { Y = C.Pawn.Location; ThreatLevel+=C.Pawn.HealthMax; } } else bA = true; } if( bA!=bWarningTarget || bB!=bCriticalTarget ) { bWarningTarget = bA; bCriticalTarget = bB; if( DotLight!=None ) DotLight.SetMode(bA,bB); NetUpdateTime = Level.TimeSeconds-1; } if( bB && ThreatLevel>400 ) { bWarningTarget = false; bCriticalTarget = false; RepLaunchPos = Y; GoToState('LaunchMine'); } else if( InstigatorController==None || bNeedsDetonate || (WeaponOwner!=None && WeaponOwner.NumMinesOut>WeaponOwner.MaximumMines) ) { bWarningTarget = false; bCriticalTarget = false; RepLaunchPos = Location + Z*(150.f+FRand()*250.f); GoToState('LaunchMine'); } } simulated function PostNetReceive() { if( RepLaunchPos!=vect(0,0,0) ) GoToState('LaunchMine'); else if( DotLight!=None ) DotLight.SetMode(bWarningTarget,bCriticalTarget); } } state LaunchMine { simulated function BeginState() { if( WeaponOwner!=None ) WeaponOwner.RemoveMine(Self); bNeedsDetonate = true; if( DotLight!=None ) DotLight.SetMode(true,true); if( Physics!=PHYS_Projectile ) { bFixedRotationDir = false; RotationRate = rot(0,0,0); if( Level.NetMode!=NM_DedicatedServer ) { TweenAnim('Up',0.1f); PlaySound(Sound'ScrnWeaponPack_SND.mine.blade_in'); PlaySound(Sound'ScrnWeaponPack_SND.mine.combine_mine_deploy1',SLOT_Talk); } SetTimer(0.6,false); } bNetNotify = false; } function HitWall(vector HitNormal, actor Wall) { BlowUp(Location); Destroy(); } simulated function Timer() { if( !bPreLaunched ) { bPreLaunched = true; if( Level.NetMode!=NM_DedicatedServer ) PlaySound(Sound'ScrnWeaponPack_SND.mine.rmine_blip3',SLOT_Misc); SetTimer(0.2,false); } else if( Level.NetMode!=NM_Client && Physics==PHYS_Projectile ) { BlowUp(Location); Destroy(); } else { if( DotLight!=None ) DotLight.Destroy(); if ( Level.NetMode != NM_DedicatedServer) { AddSmoke(); bFixedRotationDir = true; RandSpin(45000.f); } bCollideWorld = true; SetPhysics(PHYS_Projectile); Velocity = (RepLaunchPos-Location)*2.f; if( Level.NetMode!=NM_Client ) SetTimer(0.5,false); } } } simulated function HurtRadius( float DamageAmount, float DamageRadius, class DamageType, float Momentum, vector HitLocation ) { local actor Victims; local float damageScale, dist; local vector dir; if ( bHurtEntry ) return; bHurtEntry = true; foreach VisibleCollidingActors( 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( Pawn(Victims)!=None && Monster(Victims)==None && Pawn(Victims).Controller!=InstigatorController ) 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 ); if( Monster(Victims)==None ) damageScale*=0.15; // Make it a lot less lethal to player self inflicted damage. Victims.TakeDamage ( damageScale * DamageAmount, Instigator, 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; } function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class damageType, optional int HitIndex) { local Class KFDamType; if ( InstigatedBy == none ) return; if ( damageType == class'SirenScreamDamage' ) { Disintegrate(HitLocation, vect(0,0,1)); return; } else if ( Monster(InstigatedBy) != none ) { if ( Damage >= 5 ) Explode(HitLocation, vect(0,0,1)); return; } if ( Damage < 30 ) return; KFDamType = Class(damageType); if ( KFDamType != none && (KFDamType.default.bIsExplosive || KFDamType.default.bIsMeleeDamage) ) return; // check same team if ( InstigatorController != InstigatedBy.Controller ) { if ( InstigatedBy.PlayerReplicationInfo != none && InstigatedBy.PlayerReplicationInfo.Team != none && InstigatedBy.PlayerReplicationInfo.Team.TeamIndex == PlacedTeam ) { return; } } Explode(HitLocation, vect(0,0,1)); } state Destroying { simulated function BeginState() { SetTimer(0.1, true); } simulated function Timer() { Destroy(); } } // Make the projectile distintegrate, instead of explode simulated function Disintegrate(vector HitLocation, vector HitNormal) { bDisintegrated = true; bHidden = true; if( Role == ROLE_Authority ) { GotoState('Destroying'); NetUpdateTime = Level.TimeSeconds - 1; } PlaySound(DisintegrateSound,,2.0); if ( EffectIsRelevant(Location,false) ) { Spawn(Class'KFMod.SirenNadeDeflect',,, HitLocation, rotator(vect(0,0,1))); } } defaultproperties { DisintegrateSound=Sound'Inf_Weapons.panzerfaust60.faust_explode_distant02' MinSpread=150.000000 Speed=5000.000000 MaxSpeed=8000.000000 Damage=450.000000 DamageRadius=400.000000 MomentumTransfer=75000.000000 MyDamageType=Class'KFMod.DamTypePipeBomb' ImpactSound=SoundGroup'KF_GrenadeSnd.Nade_Explode_1' bNetTemporary=False bAlwaysRelevant=True bSkipActorPropertyReplication=True Physics=PHYS_Falling LifeSpan=0.000000 Mesh=SkeletalMesh'ScrnWeaponPack_A.HopMineM' DrawScale=0.600000 AmbientGlow=25 bUnlit=False TransientSoundVolume=2.000000 TransientSoundRadius=350.000000 bBounce=True bFixedRotationDir=True }