//============================================================================= // MissileScout. //============================================================================= class MissileScout extends FSStationaryPawn; var() sound FireSound; // to play when firing var() sound ActivateSound; // to play when alarmed var() sound DeActivateSound; // to play when disarmed var() sound ExplodeSound; // to play when itself exploding var() Class ProjectileType; // what to fire var() int MyTeam; // at who not to fire var() localized string PreKillMessage, PostKillMessage; // what to write when target is eleminated var() vector Canon1, Canon2, SpecialW; // location of the canons relative to the pivot point. If both are same there's only 1 canon // Z: how much lower the projectiles are fired to avoid projectile collisions with the carrying mover var float SampleTime; // How often we sample Instigator's location var int TrackingRate; // How fast Cannon tracks Instigator var bool bShoot; var rotator StartingRotation; // rotation / rotation delta related to base at startup var Actor StartingBase; // base (e.g. carrying mover) at startup function TakeDamage( int NDamage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { local vector X, Y, Z, SpecialStart, DamageOffset; Instigator = InstigatedBy; if (Health<0) Return; if ( Instigator != None ) MakeNoise(1.0); Health -= NDamage; if (NDamage > 1) { DamageOffset = 0.2 * CollisionRadius * Normal(HitLocation - Location); DamageOffset.Z = DamageOffset.Z * 0.5; if ( Level.bHighDetailMode ) { if (NDamage > 35) skinnedFrag(class'Fragment1', texture'Jscout1', Vect(20000,0,0),1.0, 5); else skinnedFrag(class'Fragment1', texture'Jscout1', Vect(20000,0,0),1.0, 1); } } if (Health <0) { PlaySound(ExplodeSound, SLOT_None, 5.0); skinnedFrag(class'Fragment1', texture'Jscout1', Momentum, 4.0, 7); //Spawn(class'FallingScout'); GetAxes(Rotation, X, Y, Z); SpecialStart = Location + X * SpecialW.X + Y * SpecialW.Y + Vect(0, 0, 1) * SpecialW.Z; Spawn(class'FallingScout', , , SpecialStart, DesiredRotation); Spawn(class'scoutexplosion'); Destroy(); } } function PostBeginPlay() { Super.PostBeginPlay(); if (Base != None) { // remark our base for later turnings back StartingBase = Base; StartingRotation = Rotation - StartingBase.Rotation; } else StartingRotation = Rotation; } function string KillMessage( name damageType, pawn Other ) { return (PreKillMessage@Other.PlayerReplicationInfo.PlayerName@PostKillMessage); } function SetTeam(int TeamNum) { MyTeam = TeamNum; } function bool SameTeamAs(int TeamNum) { return (MyTeam == TeamNum); } function Trigger( actor Other, pawn EventInstigator ) { GotoState('DeActivated'); } function StartDeactivate() { SetPhysics(PHYS_Rotating); if (StartingBase != None) DesiredRotation = StartingRotation + StartingBase.Rotation; else DesiredRotation = StartingRotation; } function PlayDeactivate() { PlaySound(ActivateSound, SLOT_None,5.0); TweenAnim('Scout', 1.5); } function PlayActivate() { PlayAnim(AnimSequence); PlaySound(ActivateSound, SLOT_None, 2.0); } function ActivateComplete(); function Name PickAnim() { return AnimSequence; } function Shoot() { local vector X, Y, Z, ProjStart1, ProjStart2; if (DesiredRotation.Pitch < -20000) Return; PlaySound(FireSound, SLOT_None, 5.0); PlayAnim(PickAnim()); GetAxes(Rotation, X, Y, Z); ProjStart1 = Location + X * Canon1.X + Y * Canon1.Y + Vect(0, 0, 1) * Canon1.Z; ProjStart2 = Location + X * Canon2.X + Y * Canon2.Y + Vect(0, 0, 1) * Canon2.Z; Spawn(ProjectileType, , , ProjStart1, DesiredRotation); if (ProjStart1 != ProjStart2) Spawn(ProjectileType, , , ProjStart2, DesiredRotation); bShoot = False; SetTimer(1.0, True); } auto state Idle { ignores EnemyNotVisible; function SeePlayer(Actor SeenPlayer) { if ( SeenPlayer.bCollideActors && ((Pawn(SeenPlayer).PlayerReplicationInfo.Team != MyTeam) || !Level.Game.bTeamGame) ) { Enemy = Pawn(SeenPlayer); GotoState('ActiveCannon'); } } simulated event Tick(float DeltaTime) { local rotator Desired; if (StartingBase != None) Desired = StartingRotation + StartingBase.Rotation; else Desired = StartingRotation; if (Desired != Rotation) GotoState('Idle', 'TurnBack'); else GotoState('Idle', 'Turned'); } function BeginState() { Disable('Tick'); Enemy = None; } Begin: TweenAnim(AnimSequence, 0.25); Sleep(5.0); PlayDeactivate(); Enable('Tick'); TurnBack: StartDeactivate(); Sleep(SampleTime + 0.2); Turned: Disable('Tick'); SetPhysics(PHYS_None); } state DeActivated { ignores SeePlayer, EnemyNotVisible, TakeDamage; Begin: Health = -1; Enemy = None; StartDeactivate(); Sleep(0.0); PlayDeactivate(); FinishAnim(); Sleep(6.0); SetPhysics(PHYS_None); } state ActiveCannon { ignores SeePlayer; function EnemyNotVisible() { local Pawn P; Enemy = None; for ( P=Level.PawnList; P!=None; P=P.NextPawn ) if ( P.bCollideActors && P.bIsPlayer && (!Level.Game.bTeamGame || !SameTeamAs(P.PlayerReplicationInfo.Team)) && (P.Health > 0) && !P.IsA('TeamCannon') && LineOfSightTo(P) ) { Enemy = P; return; } GotoState('Idle'); } function Killed(pawn Killer, pawn Other, name damageType) { if ( Other == Enemy ) EnemyNotVisible(); } function Timer() { DesiredRotation = rotator(Enemy.Location - Location); DesiredRotation.Yaw = DesiredRotation.Yaw & 65535; if ( bShoot && (DesiredRotation.Pitch < 2000) && ((Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) < 1000) || (Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) > 64535)) ) Shoot(); else { TweenAnim(PickAnim(), 0.25); bShoot=True; SetTimer(SampleTime,True); } } function BeginState() { Target = Enemy; } Begin: Disable('Timer'); FinishAnim(); PlayActivate(); FinishAnim(); ActivateComplete(); Enable('Timer'); SetTimer(SampleTime,True); RotationRate.Yaw = TrackingRate; SetPhysics(PHYS_Rotating); bShoot=True; FaceEnemy: TurnToward(Enemy); Goto('FaceEnemy'); } state TrackWarhead { ignores SeePlayer, EnemyNotVisible; function Timer() { if ( (Target == None) || Target.bDeleteme ) { FindEnemy(); return; } DesiredRotation = rotator(Target.Location - Location); DesiredRotation.Yaw = DesiredRotation.Yaw & 65535; if ( bShoot && (DesiredRotation.Pitch < 2000) && ((Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) < 2000) || (Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) > 63535)) ) Shoot(); else { TweenAnim(PickAnim(), 0.25); bShoot=True; } SetTimer(SampleTime,True); } function FindEnemy() { local Pawn P; Target = None; Enemy = None; for ( P=Level.PawnList; P!=None; P=P.NextPawn ) if ( P.bCollideActors && P.bIsPlayer && ((P.PlayerReplicationInfo.Team != MyTeam) || !Level.Game.bTeamGame) && (P.Health > 0) && !P.IsA('TeamCannon') && LineOfSightTo(P) ) { Enemy = P; GotoState('ActiveCannon'); } GotoState('Idle'); } Begin: Disable('Timer'); FinishAnim(); PlayActivate(); FinishAnim(); ActivateComplete(); Enable('Timer'); SetTimer(SampleTime,True); RotationRate.Yaw = TrackingRate; SetPhysics(PHYS_Rotating); bShoot=True; FaceEnemy: if ( (Target == None) || Target.bDeleteme ) FindEnemy(); TurnToward(Target); Goto('FaceEnemy'); } state GameEnded { ignores SeePlayer, HearNoise, KilledBy, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, TakeDamage, WarnTarget, Died; function BeginState() { Destroy(); } } function skinnedFrag(class FragType, texture FragSkin, vector Momentum, float DSize, int NumFrags) { local int i; local actor A, Toucher; local Fragment s; if (Event!='') foreach AllActors( class 'Actor', A, Event ) A.Trigger( Toucher, pawn(Toucher) ); for (i=0 ; i