*8> $PQD-RRr9u>vB   =6|66668888888888888NoneEngine SkaarjPackCoreFlySetAnimAction HitSound WhatToDoNext DeathSoundChallengeSoundSystemMovementAnimsFloat AirAnimsWalkF WalkAnimsLungeDrag UnrealGame TakeoffAnimsSkinsJump WaveInfo WaveDurationWaveMaxMonstersWaveDifficultyNone MonsterClass StopFiringNoneWaves DodgeAnims WaveMask RangedAttackXGameUserPostBeginPlayMeleeDamageTarget TacticalMoveAddPrecacheMaterial SoakStop SwimAnims LandAnimsTimerSetCombatTimer WaitForAnim GetFireStart SpawnGiblet PlayVictory NotifyBumpCountDownSoundsCrawlBeginSwim ScoringValue MeleeAttack GetGibClass Idle_RestTeamExplodeOtherRotator SendMessageMesh EnemyVisibleDoubleJumpAnims BeginStateDamage CrouchAnimsSetMovementPhysics SeePlayerTimedFireWeaponAtEnemyForceLanded MaxSpeedGetSmallerFontForSpeed LightColorKarma PreferMelee RotationRate TryToDuck SetEnemy MeleeRange Lighting bLeadTargetPlayDirectionalHitPlayDirectionalDeathAnimEnd FireWeaponAtCollisionHeight AirStillAnimFireProjectile Movement AmbientSoundPickDestinationTakeoffStillAnimCrouchMassDoRangedAttackOnHealth CollisionRunFTakeHitGetHumanReadableNameDoTacticalMoveDoMove DamageRadiusFaceMoveTarget FaceActor LightHue StakeOutProjectileClassSameSpeciesAs EndStateCollisionRadiusEnemyNotVisible AdjustAimAmmunitionClassStyleLandCheckIfShouldCrouch ResetSkill OrderListGetCollisionExtent TakeDamage mSizeRange mSpeedRangeFollow mRegenRange mLifeRange NotifyKilledTestDirection CanAttackCancelCampFor SplashDamage FireSoundSetFallInAirIdleWeaponAnimShouldStrafeTobCanFlyTextureStopped bCanStrafe bCanDodgeRestFormationBump DestroyedEngageDirection LifeSpanPlayChallengeSoundVector DoCharge CreateGib Footstep DeathAnim FindNewEnemy ChangeEnemy AirSpeed SoundRadius SoundVolumeReceiveWarningJumpZTransientSoundVolume DefendMelee WaterSpeed GroundSpeed DeathAnims Buoyancy TryStrafe Gameplay TurnLeftAnimTurnRightAnimFallingMayFallbMeleeFighter DoStakeOutRoamingWarLordHunting IdleSwimAnim IsPlayerPawnbDynamicLight WanderOrCampFindNewStakeOutDir DrawTypeSkaarjLooking LightRadiusDodgeDying PlayTakeHitFindBestPathTowardPlayVictoryAnimationWhipLightBrightness LightEffectDeath LightTypebSplashDamage MyDamageTypeWarnTargetPct ScoreEventDoWaitForLandingRecommendSplashDamage GetEnemyNameGetOldEnemyNameMomentumTransferExplosionDecalEffectIsRelevant ProcessTouchSetMaxDesiredSpeedTransientSoundRadiusStrafeFromDamageRunR KilledBy PlayDying ClearShotmoving SetFocus UseLowGore TrailTex StartDeResThrowlthigh TeamAIType GetBotTeamrthigh SetFireYawheadIdleRifleAnim SetWalkingIdleHeavyAnim mGrowthRatexPawn mMassRange SetEnemyInfomPosDev EnemyChangedRunLmStartParticles XWeapons DoneRoamingBlowUpSetPeripheralVisionFinishedStrafe DoStrafeMove XInterface ProjectileGetMediumFontFor CheckReflect PistolWhipPunchExecuteWhatToDoNextEndGameFindPlayerStartTurnPoundRangedAttackTimeGetDamageRadius RecoverEnemyPickup ScoreKill FightEnemyAdjustFromWallDead2WaitingForLandingHit2bRecommendSplashDamage PickupClassShoot1NoneStepDeadLegLoss SwimFireChooseAttackMode AdjustTossSting AdjustAroundNotifyTakeHitPossessDirectedWander LandThump guncheck NeedToTurn NearWallAdjustAimErrorDeath2Jump2LightSaturationGetMessageIndexPhysics FormationFireFlyFire FindViewSpotCrouchTurnLeftAnimCrouchTurnRightAnim FDodgeUp CelebrateStab AddMonster SetupWaveSetupRandomWave ForceScale NotifyLandedbAlwaysStrafeDodgeSkillAdjust ForceRadius ForceType IsHunting DrawScaleHasRangedAttack IsStrafingbPhysicsAnimUpdate AmbientGlowbFixedRotationDir Charging TryToWalk AccelRateDestroyTrails TacticalTick WalkingPct RadarPosX RadarScaleTriggerHitWallTick HearNoise SeeMonsterbBoss bTryToWalkNotifyPhysicsVolumeChangeNotifyHitWallTeamScoreStringNoneInitWaveCountDown IncomingWaveWarlordRocket WarlordAmmoSkaarjSparkles SkaarjPupae SkaarjAmmobCanPickupInventoryScoreboardInvasionMonsterController MeleeDamage KrallAmmoInvasionTimerMessageInvasionTeamAIInvasionSquad bCrawlerInvasionPoint bCanCrouchInvasionMutator IdleRestAnim InvasionBot InvasionIceSkaarjProjectile IceSkaarj HUDInvasion GasbagBelch GasbagAmmoSkaarjProjectileReachedDestinationFireSkaarjProjectile FireSkaarj KrallBoltEliteKrallBolt EliteKrall BruteRocket BruteAmmoMonsterJumpOutOfWater BelchFlames Behemoth AddVelocityDead3 StaticMeshUpdateScoreBoardDiedBiteCheckWaterJumpFlapPlayDyingSoundDead2AStrikeDead1RunStep WalkStepClawDamageTargetSpinDamageTargetSpawnTwoShotsDeath5JogFireStrafeRightFr StrafeLeftFr DeathStringFemaleSuicide MaleSuicide HairFlipFiring FovAngleDeath4Death3BreathClaw RazorflyManta WingBeatKrall ThrowTargetThrowDamageTargetStrikeDamageTarget SpawnShotLaughTwirl LeglessDeathShoot3PlayerControllerStrike3Strike2Strike1GasbagPoundDamageTargetPunchDamageTarget SpawnBelchGrabDeflateBelch TwoPunchBruteAwardAdrenalineWhipDamageTargetSpawnLeftShotSpawnRightShotRestart WalkFire StillFire PawnIsInPain StillLook GameHasEndedClientGameEndedCockGunAllowVoiceMessageShowTeamScorePassCShowTeamScorePassAYouveLostTheMatchNone WaitForMover FearThisSpot DrawNetInfodamageAttitudeTo DrawMatchID DrawTitleSpin Idle_Rifle BreathTimerSwitchToBestWeaponCheckFutureSightServerReStartPlayerBroadcastLocalizedMessage Idle_BiggunNoneDesireabilityMonitoredPawnAlertDodgeBDodgeFClientPlaySoundIsDeadClientSetBehindViewDodgeLDodgeRNotifyMissedJumpspinelfarmrfarmSetInvisibility ProcessHitFXRemoveFlamingEffectsAssignInitialPoseNewRoundSound GibSoundCampingCheckReplacement SpecialNavigTeamScoreEventNone CheckScore RadarPosYMANDOWNRatePlayerStart INPOSITIONbHiddenSuggestDefenseStyle CheckEndGame PickTeam DrawScale3D PrePivotDesiredRotationMoveToGoalWithEnemyMoveToGoalNoEnemyNoGoal FindRoamDest DisplayDebugSetOverlayMaterialControllerClassNoneInitializeSkill ReduceDamageScoreBoardTypeHUDType ImpactSound CanSpectate MapListType CombatStyleKilled SoakPausemSpawningType SquadType GameNamemMaxParticlesCloseToLeader RemoveEnemy IsOnSquadRestartPlayer mPosRelative MaxLivesNone MutatorClass mAttenKamNumTileColumns mNumTileRowsCheckMaxLivesSoundsMatchInProgressPlayEndOfMatchMessageInitializeBot SpawnBotPlayWinMessageOverrideInitialBots InitialBotsbPlayersMustBeReady MoveToGoalPrecacheGameTexturesbotnamemyLevelbForcedGibSparksAdjustScriptedController bWonMatch directionCurrentGameProfileSquadAITeamAI UnrealPlayerGameObjective GameObject TeamGame DeathMatchUnrealMPGameInfo DMMutatorUnrealTeamInfoNone RosterEntry TimerMessage UnrealPawnEndTimeNoneNone SetDrawColorAdjustedDifficultySetPosNoneNoneDrawTileStretchedClipYNewBotGameRulesModifiers TeamBotsClipXChosenBotTeamNMTrail NextDist OtherPlayer ProbabilityLiving bNoneLeft OtherPRIInstigatorSkillPRINoneResultSquad SquadLeader SquadMembersEnemies NewEnemy StartSpotaPlayerPC BestEnemy OldEnemybSeeNew PawnClassValBestValNone HudClassInterfaceContentSkinA BoxMaterialOffsetYmRegenNumBots CurrentDir bDisabledDefenderTeamIndex StartTeam NumPlayersbDisplayWinnerKillerMAXSTAKEOUTDISTENEMYLOCATIONFUZZTACTICALHEIGHTADVANTAGEMINSTRAFEDIST MINVIEWDIST bCanFire bStrafeDir bChangeDirbInitLifeMessage bHasFiredbForcedDirectionbEnemyIsVisible bMustChargebRecommendFastMove HidingSpot AcquireTime AggressionLoseEnemyCheckTimeStartTacticalTime AccuracyStrafingAbility KilledPawn ReactionTime GoalString SoakStringNextSquadMemberChoosingAttackLevelChooseAttackTimeChooseAttackCounterEnemyVisibilityTime VisibleEnemyStopStartTimeLastRespawnTimeFailedHuntTimeFailedHuntEnemyNumRandomJumpsVelDirbOnlySpectator ViewTarget SeenPlayerGameDifficulty TargetLocbImmediateFire bSeeTarget bTeamGameStartPositionTargetPositionScorebNewEnemyVisible PlayerName instigatedBy bFindDestinjuredInSkillViewer AdjustedYaw HudScaleP TurqColor CallingByteframeXLYPosYL GoldColorGRI bCanCharge enemyDistAdjustedCombatStyle bFarAwaybOldForcedChargedist HurtRadius BestPath BlueColorHitLocNumDir OtherDirSideDirStart WanderDir DestinationbCheckedReach bAllowDetour LoudnesstargAimDirExtent walldist ViewSpot FutureLoc Momentum TargetDist bDefendMelee bInstantProjbLeadTargetNow FireRotation TargetLook FireDist FireSpot TargetVelrealYawbClean enemyDir HitLocationduckDir bReversed bDuckLeft NewVolume HitNormal WayPointEventInstigatorStrafingModifier RelativeDir DeltaTime bMayCrouch RedColorGetAnimParams BoneNameAnimRate WhiteColor PawnOwner SetDrawScaleEDoubleClickDir PlayerOwner bProjTarget bCanTeleport MaxLightsBot RefNormal Accelerationpickdir enemyPart strafeSize Instigator StrafeDir collspecMinDest bTearOffbReplicateMovement nextSpotposZbCanSeeLastSeenbAcceptsProjectorsbWorldGeometryWinnerBaseOwner SuicidedReasonScorerShadowProjector ScoreBoard bMeleeWeaponbSuperRelevantAngleNonejiS TeamIndexNoneNoneNoneNoneNone XEffectsFlakExplosionFlashExplosionLinkBoltScorchLinkProjEffectLinkProjSparksYellow RocketMarkRocketSmokeRingxPawnGibGroup XEffectMat link_muz_redlink_muz_greenshock_sparkleShockLinkProjYellowFBlink_muz_yellowlink_muz_blue RedShell WeaponSounds BExplosion3BaseImpactAndExplosionsShockRifleExplosionShockRifleProjectileRocketLauncherProjectileLinkGunProjectileRocketLauncherFireShockRifleAltFireRocketLauncher ShockRifle BioRifle BioRifleGoo2LinkGunGeneralAmbience firefx11GibGroupClassGibs ReturnValue bSuccessEmitterTextures MultiFrame LargeFlamesWeaponStaticMeshRocketAmmoPickupLinkProjectile RocketProj Velocity Rotation Location xTeamGameTeamsbGibbedSimHitFxTicker GibCountCalfGibCountForearm GibCountHeadGibCountTorsoGibCountUpperArmMinTimeBetweenPainSounds PlayerShadowRGbDeRes DeResTime DeResMat0 DeResMat1RagdollLifeSpan bTrySplashbTossedZ GibClassGibletDummy PRIArrayYXCDeathsBDamTypeRocketDamTypeFlakChunkA ScriptTextSkillbAdjustFromWallsnewdist ViewDist BestTarget NumLives bIsSpectatorbWaitingPlayerSetViewTargetLastPlaySpeech bOutOfLivesMaxDist projSpeed xEmittershooterWeaponDamTypeAssaultBulletNoneDamTypeMinigunBulletDamTypeShockBallDamTypeSniperHeadShotSeekingRocketProj SmokeTrailRefDirSeeking SparkleTrail LevelInfoKills GameInfoNoneNoneScoreBoardDeathMatch PlayerText PointsText DeathsTextNetTextOutText OutFireTextBestHeaderOffsetY OwnerPRIFontReduction OwnerPosNetXPos PlayerCount HeadFoot MessageFootPlayerBoxSizeY BoxSpaceY NameXPosBoxTextOffsetY OwnerOffset ScoreXPos DeathsXPosBoxXPos TitleYPos BoxWidth MaxScaling deathsXLscoreXLnetXL MaxNamePosbNameFontReductionDoubleClickMoveaSpotVel2DNone TimeSecondsNoneNoneNoneSeen NoiseMaker KillScoreSkaarjPack_rcSkaarjPackSkinsekralljBrute2GasBag1GasBag2jBrute1 JWarlord1jkrallJManta1 Skaarjw3JPupae1 Skaarjw1 Skaarjw2JFly1RadarPulseSoundBrute1 DetailMode PainVolume PlayerStartHUD bDropDetailPlayerReplicationInfoNetModeaPawnGameproblemInLatentExecutionwalk1br pwhip1br injur1br injur2bryell1bryell2br nearby2brwalk2brGasBagMFireDir BestDistbestAimMonitoredPawnGameReplicationInfo Ammunition AIController RouteGoal RouteCacheOldMessageTime LastSeenTime twopunch1ginjur1ginjur2gyell2gyell3g nearby1gdeath1ghit1gamb2gKrallM ControllerLastSeeingPos LastSeenPosTargetEnemy DamageTypePhysicsVolumeAcquisitionYawRateSmallNavigationPoint MinHitWallFocus FocalPoint MoveTarget MoveTimernextController AdjustLoc bAltFiredeath1kdeath2kinjur1kinjur2k strike1khit2k staflp4kchlng1kchlng2kManta1 TeamInfoNavigationPoint AvoidMarkerbFire bSoakingwhip1minjur1minjur2mcall1mcall2mfly1mdeath2msting1mFlyMbEnemyAcquiredbuzz3rf injur1rf injur2rf death1rfSkaarjwbEnemyInfoValid GameProfilebPreparingMove Projectors bAdjusting bIsPlayerGibPerterbation bInstantHitCanvasChannelLookDirFont bWaterVolumeGravitybAlwaysUseStrafingbNeverUseStrafing FearCostnextNavigationPointNavigationPointList NewActionLevelspin1sclaw2s clawhit1s injur1sk injur2sk injur3sk chalnge1s chalnge3s death1sk death2skroam11s hairflp2skCowwalkCThump WarlordMControllerList FinalBlendMask Modifier DrawColorbDisplayMessagesMoverPawn injur1WL injur2WL acquire1WL DeathCry1WLfly1WLroam1WL threat1WL breath1WLTitanstep1t laugh1WLPupae1WallPlayer HitActor MaterialLinkPupae injur1pp injur2pproam1pphiss1pphiss2pphiss3pp death1pp VertMesh messagetypejumpDirpick aimerror projStartFiredAmmunitionbShadowActive bJustLandedFbWantsToCrouch HitFxTickerHitFxrotDirbSeverdamtypeBone HitFXDataShield bIsCrouchedIdleCrouchAnim bCanJump bCanWalk bCanSwim bAvoidLedgesbStopAtLedgesbNoJumpAdjustbAutoActivatebAmbientCreature VisibilityMaxDesiredSpeed SightRadiusPeripheralVisionAnchor bHeavyAttackActorWaveMonsterClassNextMonsterTimeNone WaveEndTime NumMonsters MaxMonstersLastKilledMonsterClassWaveNumWaveNumClasses WaveMonstersFallbackMonsterbWaveInProgress bWaitForAnim InitialWaveYawRollPitch SoundGroupSound bPlayedDeath bWasOnGroundRoleClass NewMonsterNewMonsterClassPackageNewMaxMonstersConst EndSound bOneMessageFindAnchorFailedTimeTearOffMomentum bShotAnimHitDamageType bVictoryNextTakeHitLocation AnimActionLastPainSound LastPainTimeLastValidAnchorTime CrouchHeight EyeHeight TextBufferMyAmmoObjectEnum Function FireStartStateColorStruct StrProperty hitdamagepushdirStructProperty bLeftShotArrayPropertyClassPropertybAttackSuccess bLeglessbSuperAggressive NameProperty bStingingObjectProperty bLunging bRocketDirProjRotbDamagedMessage CurrentEnemyChallengeTimeFloatProperty bSeeBest BoolPropertybCanSeeNewEnemy bHateMonsterbNewMonsterEnemy IntProperty DodgeSkill ByteProperty RadarPulseBaseEyeHeight MaxFallSpeed GameRules RadarWidth PulseWidthPulseBrightnessDotSize OffsetScalebDisplayLoserNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneKDamageImpulseGetHitEffectsbDetonatesGoop WaveChange GetValueDamTypeBruteRocketDamTypeWarlordRocketWinTop DamTypeBelch SetValue__OnChange__DelegateInitComponentWinLeft ControlsCaption MaxValueInvasionDeathMessageDeathMessageClassDamTypeSkaarjProj FRIENDLYFIREDamTypeKrallBoltTAB_IAInvasionYellAtHint MinValue WinWidthSomeoneStringGUIMultiComponent GUISlider GUILabelIARulesGoalScorexDeathMessageIARulesFriendlyFireSliderMyOwnerIARulesBrightSkinsIARulesFriendlyFireLabelIARulesAllowWeaponThrowTab_IATeamDeathMatch IARulesBK3 HitSmokeGUIController VictemHealth moCheckBox HitFlameBig GUIImageLastDrawRadarmoNumericEdit HitFlame IARulesBK4IARulesAutoAdjustSkillIARulesWeaponStay IARulesBK2MutatorIARulesMaxLivesTab_IADeathMatch IARulesBK1IARulesGameSpeedLabelTab_InstantActionBaseRules HitEffectsIARulesTimeLimitMoron GUIComponent EndTimeDelaySender MyControllerIAInvasionInitialWaveIARulesTranslocatorStaticSaveConfigIARulesGameSpeedSliderNone InitGameErrorbForceRespawnOptionsNonebNoRadarSound next_wave_inAnnouncerEvilNone WinHeightbBoundToParentbScaleToParentComponentJustification CheckChangedTagNone CaptionWidthImage ImageStyle bNeverFocus SkillChanged WaveChanged StyleName TextAlign CloseClicked__OnClick__DelegateDurationChangedConfigClicked OpenMenu CloseMenu IsCheckedCheckedInvasionWaveConfigGUINumericEdit bInitializedMyNumericEdit GUIButtonMenu GUIEditBoxIAInvasionWaveConfigWCFGBackgroundmodWCFGBackground2 WCFGLabel1 WCFGWaveNoWCFGBackground3WCFGDifficulty WCFGDuration WCFGCreat01 WCFGCreat02 WCFGCreat03 WCFGCreat04 WCFGCreat05 WCFGCreat06 WCFGCreat07 WCFGCreat08 WCFGCreat09 WCFGCreat10 WCFGCreat11 WCFGCreat12 WCFGCreat13 WCFGCreat14 WCFGCreat15 WCFGCreat16 WCFGLabel2 WCFGClosewMaskWave bReadOnlyValueMyTimeMySkillWaveNoWCFGBackground1GUIGUIPage BorderBoxDNoneNoneNoneNoneNoneInvasionEndSound FormatTime OutMessageInvasionGameReplicationInfo WaveStringNoneMenuStateChangeInvasionMessageMessage GetStringPlayerWaiting StackModeGameReplicationInfoClassPosYMapNameRemainingTime FragLimit RelatedPRI_1Title FooterTextscoreinfostringSKAARJslaughter PlayerAreaY RelatedPRI_2five ScoreInfoXLSwitchNonetenthreenine ElapsedTime titlestringOptionalObjectSpacereightsevenfourSKAARJexterminationNoneNoneRestartString SkillLevel GoalScoreSKAARJterminationTitleXLNoneYouveWonTheMatch TimeLimitsixonetwoCriticalEventPlus WaveNumberSKAARJbloodbathSKAARJerradication LocalMessageNoneNoneReplenishWeapons PreBeginPlayNoneInv InventoryNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneInvasionBotNames ResetClickedAddWeaponKillGetServerDetailsDefaultMaxLivesLoginCreateRosterEntryCharacterMaplistSkaarjInvasionNoneNone SecondBotTPRI ServerState bNoTeamSkinsxBotbForceNoPlayerLights WCFGResetMapListServerResponseLine bNetInitialBaseDifficulty xRosterEntryNoneNoneNonePortalTeamPlayerReplicationInfoNoneNoneNoneAcronymNoneNone FinalWaveNoneNoneNoneNoneNoneNone__OnCreateComponent__DelegateInternalOnCreateComponentInvasionPropTextInvasionDescText TextColorGetDescriptionText FillPlayInfo__OnMousePressed__DelegateInternalOnMouseRelease__OnMouseRelease__DelegateScreenShotName Description__OnKeyEvent__DelegateUpdateAnnouncementsInternalOnMousePressedAcceptPlayInfoPropertyInternalOnClickNoneFallbackMonsterClassInternalOnKeyEvent __OnCapturedMouseMove__DelegateTurnOffInternalCapturedMouseMove PlayInfo MinEnemyDist INVPROPNUMPiNoneNoneNone PropNameNoneSKAARJannihilationDynamicLoadObjectNone AccelDir PropertyName bGameEndedNoneNonePrecacheSound InvasionEndGetDisplayTextEndGameSoundNameAddServerDetailCreateRosterEntry IsHeadShotUpdatePrecacheMaterialsInstantWarnTargetWaveConfigMenu GetTeamColor GetTeamNumPrecacheGameAnnouncementsGIPropsDisplayTextGIPropDescTextPlayStatusAnnouncement SPBotDescPlayRewardAnnouncementYou_Have_Won_the_MatchLoginMenuClassYou_Have_Lost_the_MatchPR HeadRadiusInfoDMRosterConfigureddiffHudCTeamDeathMatchVAdditionalScalePlayerRecords NewPlayer Distance AssaultRadar UT2K3GUIPageAltEndGameSoundNameSavedFirePropertiesbNoTeamChanges HeadScale AS_FX_TXFindPlayerRecordHeadLoc CharactersHudCDeathmatch RecordIndexDotMMrayFirePropertiesJakob_NewHead bCustomBots GameGroupHudBaset PlayerRecord AmmoClassDemoPlayerSkins HeadBoneAnnouncerVoiceNetUpdateTime MaxRangelocbRewardSoundsxUtilNone RedeemerPainterFillToInitialAmmoNoneNoneNoneNone CrouchedPctForceDefaultCharacterNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNonebN wBY65Kx̌3}Fo3}ZspZs3},e,eʁ),eʁ),eʁ),eʁ),eʁ),eʁ),eʁ)3}3},e\"hhZs,e\"\"\"\"\"\"\"\"\",e,e,e"#pQ6"i9g9dSZSS$BD$>D$>} lAmADaDaDaDa    *x*x*x*xAAAA[`kajaK9}U$BQQQQ[QQjQQ^$ X,w* r*aUw*Z#ai GQQlRa2&:\fF,e,ezVGB||||||||Kx|hKx| ]eTMNHT"QQXQQfQQQQgQQ"QQQQQQOQQOQQQQQQxQQMQQ@QQkQQ_ QQDQQQQ`QQQQQQ}QQoQQQQog! GQXQQ@hNQbQQrQQQrc gBG0ao[h&:3}zZ&:3}zZh&:&:&:3}3}"k#QQzQQTQQN@QQZQQkQQ~QQT@QQJQQDQQNQQQQiQQ QQF QQn 0 GQ@QQQQ}QQgQQQQsQQKQQDQQ^QQRQQQQeQQQQTQQ=QQ|QQwQQeQQKQQQQmQQdQQjQQWQQQQe QQx QQZQQaQQAQQ QQQQwQQHQQcQQTQQQQQQ_QQQQQQWQQNEo$ GQQQCQQWQ]R {BY6FKxʁ)ʁ)ʁ),e,e,eʁ),eʁ)\"\"\"\"\"\"\"\",e\"\"hh,e,e,e"#[[\F\a\W\V       vv6"iQgKS$pBc$ Dd" W W p)5)5)5)5 J J J J*I*I*I*IVUZ[[b`bu5K|fX? Yb$Ci$CP:= `QQYQQj QQbQQnQQCQ[Bn"Lbrmȵʁ)̄ʁ)ʁ)ʁ)BBBBB3}B L$zDJ$zDC$Ak$CP$GICQGEndcUD$C}$@zw^V$ ANy$L>Ywa`$pBQQQQMQQ8QQQQG QQoQQaQQQQOQQaQQwQQQQQQlQQmQQnQQDQQjQQTQQ,QQYQQAO.hI %ի?OT GQ^QQQQdQQZQQpQQQQUQQdQQvQQ;QQiQQqQQQQQQyQQ_QQdQQn QQ]QQQQQQsQQZQuQh6lzZYвввr3}3},e,e,e,e3}3}3},e3} , ,h,eh,e,e3}3}3}3},e,e,ezVGB,ezVGB,e,e,ezVGB,e,er3},e u,e,ezVGB u,e,ezVGB,ezVGB,e,ezVGB,e,e3},e u,e3},eG0ao||\fF|\fF,e||\fF,e||,eh@q@q@qqY@q3}@q,eɢbQ 3},e,e,ehɢ,eh,eh,eɢ,ehɢ,eh,eh,e3},e,e u,e3}3}ɢKxɢɢɢ;kɢɢ,e,e8fhKxhh ,hhG0aohhG0aoh,eh,eh3}h/$/$7575pp|3},e,e,ezVGB,ezVGB uzVGB uzVGB,e,ezVGB3},e,e,e,ezVGBzVGBzVGBJzVGBJ3}zVGB o>+,e,ezVGB,ezVGB3},ezVGBJ,ezVGBJ3},e,e,e,ezVGB,ezVGB3},ezVGBJ,ezVGBJ3},eKx,ezVGB,ezVGB,ezVGBJ,ezVGBJ3},e,ezVGB3},ezVGBJ3},e,e,e,ezVGB,ezVGB,e3}- =13},e uzVGB uzVGB- =1 u u,e,eʃ,e,e,ezVGB uzVGBhhzVGB uzVGBrr- =1- =1- =1_ ._ ._ ._ ._ ._ ._ .Y3}3}3}3}3}3}3}3}3}3}3}3}3}" ]eTMNHV\~s^ yFj Y  d  q V V\\G] GUI2K4.UT2K4InvasionWaveConfigh]SkaarjPack.EliteKrallO"X]Starting WaveX Final WaveXWave ConfigurationX  InvadersX  Wave NumberX Max InvadersX  DurationX DifficultyY]87Specify the first wave of incoming monsters for a map.YCASpecify the final wave which must be defeated to complete a map.Y)(Configure the properties for each wave.YSelect the wave to configureYHFPlace a check next to each monster which should be part of this wave.Y@?Maximum amount of monsters that may be in the map at one time.Y32Length of time (in seconds) the wave should last.Y32Adjusts the relative intelligence of the invadersS"nGorgen CannonballnAnnikanRikern  BlackJackn  SapphirenJakobn OthelloxarCBvZ " PZ$Q "< Z$Q "i Z$Q " Z$?Q " Z$?Q " Z$?Q " x$?Q " x$?Q " x$?Q "TG x$?Q "8 $?Q " $?Q " $?Q " $@Q " $@Q "$@QbPbPa}r"`"APARQ]GUI2K4.UT2K4InvasionLoginMenuN]DBSpecify the number of bots (max 2 for invasion) that should join.A]Skaarjpack.ScoreboardInvasionB]Skaarjpack.HudInvasionE]"!Skaarjpack.MapListSkaarjInvasionR"E]T]Skaarjpack.InvasionMutatorXrK] Invasiona]omAlong side the other players, you must hold out as long as possible against the waves of attacking monsters.`] UT2004Thumbnails.InvasionShotsL]INVK]Monster SkillL])(Set the skill of the invading monsters.QQ[QQGQQjQQ\QQB QQuQQCQQQQQQQQgQQJQQQQSQQLQQQQ-QQM QQVf Xg!Q7-A9:9:$-A(0YY--(-(f  GQBQQ QQxQQuQQm t @dVt , a,c$@ C GQ|QQQQDQQ QQVQQnQQ@QQ QQQQ|QQQQQQHQQu QQQQUQQlQQFQQQQQQtQQjQQiQQcQQQQQQI QQQQYQQUQQNQQTQQsQQvQQQQQQQQQQhQQ9QQ{QQtQQ~QQkQQMabWH._% H9?, r*.-r9:9:$9:9:$L>H> Fa   FlG=>  VWwKa  FkKVL? pW9?%W9?QW'QW( GQQQ}QQ@QQHQQQQVQQqQQQQXQQsQQQQQQSQQPQQNQQ\ QQIQQUQQtQZL XBY6 V:jKxʁ)ʁ),eʁ)ʁ)ʁ),eʁ),e,e,e"#hRhJh|t     bS$Ag$Cf$Cc$Cd"< 3 3 3 3l3m3)3)3)3)33333    AAAA[`u3? Ys$AZ$@Ab$BP:= QQZQQfQQcQQX QQYQ`X ZBY6 \eKxʁ)ʁ)ʁ),eʁ),e,e,e"#QStMS$ B_$CA$Dd"#|S^ ? Ys$AZ$0AP:=p QQiQQQQzQQQQ`QQxQQUQQeQQgQQr QQQQiQQZ QQBQQGQQHQQ{QQq QQhQQoQQDQfmr" r;ـȵʁ)̄ʁ)ʁ)B3}3}B ^L$DJ$DC$AP$PFIWQGEn(cdD$C}$@@z}w^V$@t:X33#@??u:X AY}wa`$HB~P:=8wv$Ar$@Qia ] A9:9:$GBY6sLP2Kx,eʁ),eʁ),e,eh,eʁ),eʁ),e,e,e,eʁ)ʁ)ʁ),e,e,e,e,e"#7i7h7g7`7`    HvB 6"PSc$ D f f f)5)5)5)5u5? YQQo QQvQQQQ}QQzQM| rBY6 yIw Kx,eʁ)ʁ)ʁ),e,e,e,e,e"#[ [ 7|7}7|7}QS     Hvn 6"PSS$Bg$Cf$Bc$Bd"    ))))uK~? Yd$333?U$HDs$y,q Rn$trnzH-I' GQq QQ QQHQQCQQFQQQQa QQJQQQQF QQb ` '0]Z-9:9:$` Za =Z9:9:$-' G QKQQ:QQQQQQhQQNQk`2P. jy ]eTMNHJ&{@{@{@{@QQn@QQA QQg^$MR#(_a Mw_*%_T99^6^a+^-9:9:$-($ GQQ QLQQw+ڜ G GQr nq!Gw.n*2anan GQQQhQQ_yVwGQAM \!$y - `\ \a $,z|  !y-'#-' GQQQ]QQ[QQI wbWGQaQQbQQcQQ} QQ`QQSQUU+f/ՎP.tr L$zDJ$zDC$Bk$HCQQQQQQQQkQQQQ$l$$-j? GQQQQQiQQvQQrQQMQQfQQu QQQQEQQQQaQQwQQyQQAQQ{QQ|QQJQQ%QQQQQQPQQLQQFQQHQQxQQEQQEQQQQL@QQQQQQTQQIQQOQQPQQhQQpQQvQQWQQLQQUQQQQlQQEQQ]QQdQQK M F*jQ GQQQmQQlQQQQP QQeQQdQQQQiQQgQQ{QQwQQQQKQQaQQQQpQQqQQ@QQAQQ[QQxQQQQA@QQzQQQQ3QQQQl QQOQQYQQPQQ^rO - GQ}QQQQIQQQQQLQQJQQEQQ`QQpQQCQQMQQGQQ| QQ w Zu.#l )w6Z*C6ZPr*Pr* D  r.*-6Z9P (VC 'a+9P(-z-6Z-e-Kr eD@9D`9?@D-K-6Z-z-zf S9:9:$76 6 6f6fë6 ?DCS6f6f?333??fDC66 6h9:9:$  ?9?X ,9:9:$6X%-99X 5?b$|-b$-> ? -(t-6Zw.*9?,-K9:9:$/6 9?,P6 V6 9?,6 #-K@?a=#?9?,(-r*E-=#@@-b$t-9:9:$b$-66 -b$=-6Z --a=(5w*-m(ի9?,-'-66 fff?-b$- r-6 66>a=(w*ի9?,.G 9?,a=(w*ի9?,-m(-6Z(VC'('9P(l6'FZ99'6'e6'@(99'VCV(a=( w*E 6333?6'el@(99'V(a=( w*9?, 9?,a=( w*('9P(a+'' GQQQ~QQC QQ[QQQQQcQQQQVQQQQXQQYQQ_QQc QQQQE@QQ^] s|Ww*f 6ff  9?% #w.*.( GQ dIg~V"-o$ J--(  b 6  # GQcQQY QQkQQgQQfQQ U `1S1Uv!1<.`Kr<*r<* r<(cR<$(tr<(X<F<( GQQQhQQQQgQQ#QQYQQ`QQpQQnQQQQ{ ]x( GQFQQTQQQQrQQQQ`QQRQQKQQQQEQQZ QQzQQ{QQpQQQA]Q\-;6 6 (6]-p-Df |ZaBX 9?,] (f-grZ*B 9?,-g-p-p]9?ZaBX 9?,] (f-grZ*B 9?,-g(rZ*B 9?,]ZaBXB B#?B(frZ*(?-p.$U.$.~.' GQQQQQ_ QQQQo CcECq!! GQQQQQCQQIQQQQQVMQTQQQQQQQF@QQTQQ QQ{QQWQQXQQq QQIQQKQQW z {*( GQQQ\QQb_\o liw*zyw_.zyw)-6Y6Y#6Y#6Y#6Y#-6Y#--6Y#--6Y#--6Y#--6Y'a# _ uY_Xag$ GQh QQQQQQj l L.7 q!K' GQSQQo QQdQQVQQSQQqQQnQyQ{ zBY6jQyIw ,e,e"#6"Pd"YQQQQQQQQQQy yxGWV2p}`\" MUHpfJ$fff?NQXp sBY6qyKx\"\"\"\"\"\"\"\",eP.t,eʁ)ʁ),e,eʁ),e,e,e,e,e,e,e,e,ehh"#pSMLu$@     Hvp6" MS$Bg$C_$Cd" f f fl9m9    [`? Yd$?U$Ds$B?A$AAAn:XAAA$HBHBQ~$ B~ Bj$e YwQQo~ f( GQoQQQ\Y2v)\" UpLQQ+',u!1 GQQQ__xGQQQzYQO qy q!o Gd]3}һp P]%o was eviscerated.QM%o was hacked.RM%o was hacked.Qek [BY6 ~Kx,eʁ)ʁ)ʁ)ʁ)ʁ)ʁ)ʁ)ʁ)ʁ)"#QS    MPSS$HCf$C_$CA$HD|S        [`? YZ$@Ab$Bi$BP:=@:QQQQV QQ\ Qh^2B ʃ^\" UprQQSE ;)3%R(a  E 9PG a a Gj_2D 6k k ]eTMNH2 2 2 2 2 2 2 2 2 2 QQG QQpQQQQK QQ_ QQQQLQLvi"L QiLbrm L$@DJ$@DC$AnNYQQJ QQT QQQ QK N VV|$ GQQQQ QQQQQM QQT QQH X Egu . EX  GQE QFwP jBY6Y pFȝ\""#YQQ W l /.-m( $ $ GQa Q~Pl* \ Tiȵ3}ʁ)ʁ)3}3}3}B L$"DJ$"DC$4Bk$ CP$PCGI[QGEnD$C}$@zpw^V$@Ny$>}`wa`$B~P:=Pv:=0uwv$Br$@Qm2] 82b\" UHp\ J$333?Q@~ jBY6^ <Kx,eʁ),eʁ),e,eʁ)ʁ),eʁ),eʁ)"#pSu$@    Hv] 6"M_$Cd"     l9m9D9D9D9D9         * * * *     [ ` k j Kn^ ? Ys$BL>A$CACn:X@@@@@@$l$l~$A~Aj$U$?V"W"e ^V$ AYwa`$BQQz QQm QQUQQ| v y%x- - q 9?,.M-( FJK:. .FJK::v  6:6:ff?OaCB:(QrO*-' 6:6:ff?OaCB:(rO*-( -' GQ~rZ% r9?(-Ub$r #?-x  -U--Ub$r կ#?0-U8b$r #?(.G. r.D8b$r#fff? (' GQs QQ @ } JJGy,qRs $ GQuQQA RJr*%r* r(-v-@,. q-- -v('-v b r* @^b'6b(r-@w.*-(u  u(u?  ^b'( GQC i ^%ci o-j  GQ6QQD B ]{-(-w-*q--w-*r6*6-J6  -Gb6qL-  q-G LJ-kb-q-k-G LJ6-JL-G-k--1r6(w6*^6b6'( GQZ 0{x 0O80 9=,9=,9=,0ap  (89?,O0@8w*0LapppEnemy Dist 9U   Acquired 9T-8O0@80apWeapons: _ (8O0@80>apppPERSONALITY: CombatStyle 9Uk Strafing 9UL8O0@8 GQQQ { V G8!6'rV % RT ' GQQQ J QY[} ]rQ*Q4rQ* wQ(Q. !( GQF S m?E"zZd-S   -'wz-( GQL GS 0$r* Y,-a=' GQI Q oQ#+-(m-Q .W GQM R DM "R U GQS ,H (a?Q= Av' GQO QQP QQQ QQQQ P N N^-%F^N -4-[[9:9:$o$AA9:9:$-o$Ao$A-26 6 9?% 66  AM xM( GQU O EB&#9?&@O  GQMQQm$:7,4.--' GQ R \wm'# -o$ GQV xZ}'x.̌?E9:9:$o$$u!1 GQ]QQW lX) l@z GQ\  200QY kz)-e9?,,uFv9?,6_v9?,69D9? N9?Xv69D9?0u9?vk@?L=v9?6 9DkT GQR 200Q[ T+URw*09?,?R?= GQ] uO, r* 29?,333?yW9?,y?L> GQ^ m-vn%pppppppCHOOSEATTACKAGAIN in state 9Wa enemy N old enemy O CALLING BYTE 9Rmqy y,pppppppCHOOSEATTACKSERIAL in state 9Wa enemy N old enemy O CALLING BYTE 9Rm+qy%}.-W9:9:$ n% y%@n~n GQb  1200Q_ h@61- r |Mahq #B (rwM*M- 6q333?o$ GQ 2000Q k pKjv*p-Fpgw*rv*.&Q.z>*!<$@A!:.&Q.zggwv,P bv$.&-N>*!<$@A!:<>*!<$@A!:.&-N.&-N GQQQd QQa g O2(rc*NONE%ch GQ@QQf i N"3(r*NONE%h GQ@QQh ~3-o( pWhatToDoNext at 9Uer*ph WhatToDoNext with no pawn -.- #q!- 9:9:$E9:9:$L 8w*(%r**- w*-*r* @]w*U pWhatToDoNext Wander or Camp at 9Ux' GQ je :<#9?,(7' GQQQbn @7h0H rz-IzH-Ib-I GQQQm GH.H9Er* r*pppHERE 3 Enemy 9V pawn 9VrhN-]rh FAILED HUNT - HANG OUT@-G(O+9?%9?,bOHx @$@a x կx#?@x' -F-T-T(T  |k9?L?9?,|    w*99?,qT9?9?9:9:$9:9:$[6 6 9?@99?|9?,Tff&?9?-D'9?6 6 9k?@ Enemy not visible*-G Stake Outq= Huntq!t .O-G-F ChargeY -G 9?,-D 99?& ? Charge closerY R.O{.>9?&eT Ranged Attackc -G99?& Charge 2Y - Ranged Attackc  Do tactical move.M.-r 333?9?, YIS Try to Duck ~?I9?QI'QI(i GQ^r D\ w*}|{D.}|{)-6Y6Y#6Y#6Y#6Y#-6Y#--6Y#--6Y#--6Y#--6Y'A uYDXj-m6A {6A -m-mua f DAuag$ GQQQbs V.I ,ի?VXW GQAt A!v- R - )9:9:$!hGA A-'#!E%,-/G a $G-o$!hG!g-'#-' GQVv 0jro$-'#-'a $!b  q!&!- GQBQQw u V^@B - %BPQ2B!l a&!gL= GQSy J@U$!C,2SJCL GQQQz x WM&9:9:$a!D#a!F GQ{ nH~o$ G"Q~ GHA,6G333?o$GE GQFUcF p ChooseAttackMode last seen 9Ur* r*pppHERE 1 Enemy 9V pawn 9V ChooseAttackMode FightEnemyH' GQ | J(GPJQJ Q /a9 LLL HL10 GQB BI;o$B GQuQQ} A zH9 pNo anchor 9U79?,-(NO PATH AVAILABLE!!!7i,%  '7i79:9:$o$ ի?6 (i% pFind roam dest 9U r*r ob @%  r* -9:9:$(COULDN'T FIND ROAM DESTINATION(%r@*@b(Dw@*@q!r'-9:9:$(pCOULDN'T FIND ROAM PATH TO 9V*( GQ@QQD P~#8uv49:P9:$pu[9:P9:$p9?uy9:P9:$pv9:P9:$p9?vo$- >-'!l 9?,pp q!&!j' GQ@ qDN>Uq9?,ȫ9?,,YaOT Uի?q (wY*UOTq9?,b$U (U UpU -9:9:$6pp9?, GQBE Eo$ GQG Bj a $ GQOQQH Q& a $ GQ$J $ $-o? GQQQWmnqc=w.n*(na/!{na/!s GQC w%Pzq!r!s GQAO N!2| - MN ;M9?^ -'a,h$MN?!@!n%,#-'# -g'u!S!9?N N#@?tMN9?,#6333?Mo$ GQQQ^cX1Q`k @k(c k6cec  6eee]ceL?- '6T6c6T6cTe9?%T9?  ի?c?cT GQQ kS$~p--g-g(%`mnp`!%, @F k G"QkQQVS 0|j-'#-'a!n?=  q!&!- GQQQV R V>a&!gL= GQQQN e[GTo pDIRECTED WANDER  --`Deq!R![lq!R!4 GQW T W a,b333?= GQBbExo$ GQA[ o!'Yo o-'a!O{%, `jFo a $ 9?,no 6 69?,F  q!&!j GQ<QQQQ_ Z V"a&!gL= GQQQ^ QQfQQb \ W a!R GQ]sA,Z&-Z bss5bs(Ew*'rs ws*Nh-9:9:$(pCOULDN'T FIND BEST PATH TO 9Vs( GQQQNf a X>&--(a!?L= GQd QQQQ` u ^ \e99 6eeedu  6ddded{n? GQh no$ G"QeQQBi E o$a! GQ$$K$a! GQe {_]Yb99P #?Db{|aO~PDP(r|*(D~#?{?D9?*b$PDP D'0b$PDP D' b9?,' GQm SSd--^-^(%cUVc!C c!W%,@F 9?69?%69?S G"Q|QQAn F!H.K - D66F F9?% F F333?F F  -'F #L?Fu!S-^'3?!Wa $H!Ca $ GQVp 0C!C GQQQr o V>a&!gL= GQQQv q W9\ a!F GQ j aN a?r*#r*(Z #Y Y  aw*Y ab$Y 9:9:$(:b$  aY'( GQFQQYQQNx t X P--(<-A9:9:$-A(0 %FEJqF!a&!>E? 33>M6M?a!a&!L?9?,a! GQs `c:w.*9:., @@@A9?,    ?   -D? #?OUIU9?,fff?@ ף=@U>333??-D33S@p>UA?-9?, ?&L>ff?m9:9:$9:9:$?Z??9?,?-L?9?9D9?, GQz \ a $ GQDQQB{ Eao$a! GQ$^$ $a! GQ} QQQQ k_A>%,#9?] a $ GQD lYA>%,9?X a $ GQQQ@ QQ'QQA QQbE P.T?P GQF mO \ GQVH 0Ia $!B GQQQJ G VA,?a&!gL=)a&!LL= GQQQK I W9.?a!o =+a!J = GQL n4o$ G"QBN E/jo$ GQtQQO M~tsT49:M9:$t[9:M9:$9?ty9:M9:$s9:M9:$9?s 9?, q!&!j' GQAj!_ - j jw#6w wa $?!q!B!p-' GQ$$>$# L  GQ$$?$# b  GQAS }!  - -'19:9:$!T} }333?!Ja $}  !Ya $-'#5#!T-'#%{hgh{!p!O{!W!N!M GQT I}> A>%,9?a a $ GQU Jw A>%,9?0u a $ GQ{V ~YRr*-(-(-'-' ~ _q!o$ r~ f6_6@? ? w~  w~  w~ a!L?L=Z!f~ X69?,D?a!F?L=Va!a?L= a,H?L= GQW HM!a,E$L> GQX GHa,E$ GQNY WX;tfW%%Lsr>L!|!]ffL!] ?!|NW GQ[ QK QOSh.QOS-6Y6Y#6Y#6Y#6Y#-6Y#--6Y#--6Y#--6Y#--6Y'w uYhXa# hwhhիff?O6wa# hw GQQQb] |./Hիfff?|իfff?{ի>z GQQQW^ Rq=w.R*(Ra/!{Ra/!s GQVE0om-'#-'a $!S  q!&!- GQBQQl$>l# e  GQb ^-lO ?a $w.*.U{PCG #? GQc _{ 2-l%,#/-la $ GQVd 0= x -H -'#-'a $!c  q!&!- GQIe a!S-(B-(-o'-H'aA #?9?,%% %,%!% !% !% !% !% !% !% !%n!!! ! ! ! ! ! ! ! ! !I G Q{g ]YW){]\&-Ha!dL= GQ A_Ct^r_m[r`*wh *h - r**] GQSi b@EM -H W, x,6b6Sbxa `9PbZ!aa`Z!da`-(!S GQ`QQq a> \ GQf k l)Kw GQQQj tL2r.-Y'0L9?,(wrtX.O(L9?,9?&r.t*X.trX*X- (X ,'X-  L?'w.t*L9?,9?&=L9?,9?&(Rr*>e@Y?? GQQQXQQm QQl fm[eH-t(Ar*#9?,flVr9?,fl @w.*P  #?6 6   #?6 6 xPY?P>w.*rL9?,l?P>L9?,f>PL?l GQbs e.9%fff?e?d GQPQQz `3A>%,9?R a $ GQp u xx q!R GQQQ t w S}ӓ  GQx QQQQ v iG?<% n9?% Ri'$ GQAln!+ - "-H!e@9:9:$!Tn na $,k-j-   !O-'#-' GQ[m!EN - @!x-' [m GQA QQOa,{$ GQQQQQosH R^b #*_HCaa@@b_v.-a? !-a '-9:9:$o$$ !4 -Q[Hf4QGQ^uF!2[ u  GQbJ a.Io<-ma333?b>cla333?b>c GQs@ C f' GQQQB +,u!1 GQD _"D D GQE BY*-(-'-'h'> GQF r@z *>ww*-(-(h(w9?%-' GQ G TF $ GQ mQB GQK vC--m(\ GQL u=-m'\ GQ[t79?<%,#@G a $ GQV@0c-'#-'  q!&!-!{ GQ[ _},9:9:$  $ $%&a(((A%7- (|w*- (b|*'-' GQyZP rqMbam? Lb$ U-(STUCK IN ROAMING! s(4QGXQo bN-w*/9:9:$a/!F GQU ++@ba=4 -(_=a?( GQ|Y#v:-7-i ll--i q! GQR i5 q!& GQS @ e=IEw*- @ 9? GQZ I~+"o$a%\ GQQQZT x ;9:9:$ bNr*$s.-a33> !-A('q!&am? L$-(STUCK IN CHARGING! [-N4QGQoJKw -bH- GQAX RQT<?8QR-A 9rjR GQ Lz* GQ^ Q P,G iQQQP Y I V^.I >k9?(;-[ 'H  #? H9?%H9?jH GQ` 1aD/9:9:$ ] br6*kk-616 6 !a ?!d/8$#?61/8$#?61,k G!_ !^/8$#?61/8$#?61k ,!f/8$#L?61k 4!] h!Q/8$61-'%/8$#L?61s%/8$#?61]%/8$#?61-'k 9D9?&9?,: GQ[ QQIU Z4Q ] 6U 6M -6'6J &,&% GQHQQf B" -A' GQ] qjp2Q |kaxz 9?,q (Qwk*q9?kaxz 9?,q (Qwk*(J9:9:$kaxz 9?,q B#? 9?,q(QJrk*( 9?,,qq!&!w' GQEQQQQQg k %rmw*O 33?9:9:$9:9:$r66 ?GaEa  (>wG*(}k Eh }'( GQ{CU1-ae$ @'C a,f$ @'C GQr QQQQc y YYy m AHF -h9:9:$R% mV9?AH' VV-h r9:9:$p?-ipo  #?6o իp333?oVL>-i-i GQi QQQQ Bl v7' GQo TP w#*#a GQp Wa,h$ GQWca&% GQQQt \9:9:$a  GQdj t1+rtc(81t GQ$s/;!rs* ] Ras fVOrR* R+R 9?LaG6V9D@aa6V9D@aa6V9D@aaViioR oD GQQQVs +-u!1G GQv t $ GQw rҭ74w*9?%-' GQQQQQ[[&&]a ף<C9:9:$ b_-.-a? !-aa&-W]L>9?,9?,-T'$3 RangedAttack from failed tacticalcr*@b$ .O- !v Recover Enemy] a=L>ի9?, aY #i.GaL=a=>u=9?,@?u!t]ի9?,]  !j$-(STUCK IN TACTICAL MOVE! vEw-cjI4CQG Q[} {' GQQQA| Zb1-m > ZKI GQy QQQQ L@Cd  GQ^~ I9 # 8I GQ BHO$9:9:$(0r*$(-S ?+  H9?%$-S' H9?' GQQQFQQQQC+ ѳ/u!1&w*G,, GQGtx@.b$ q!&!E6$v!t GQ HJzc/ 9?,  GQQQI\_*öBr*p9V Tactical move pick destination with no pawn -S(--- 9?,K#?69?,d n  Kn#?J9:9:$6K6nf6n6nX@ff&?333333?X>L>XjnX<-6 6 9?jj#?<jj#333?X?X\XK-i\9?-i-iUj\( Uj\( -W']Uj\' GQNDzc GQQQPMOa -o GQXQQrOgX~Ufff? PPIRPX,bUvR.PPIE!X GQKIUbK 9?,Ii-q] |6]@~a\CK (]w~*(i9:9:$6]`A?6]6]~a\CKծ B#?K(]ir~*C9?I(KI?9?,  HC' GQQQVRM GQ~QQQQXUDt GQQQYWGQ GQf2 GQQB<-W(89?,> ף=>-'-'-(-( GQZrEs-'r* T-(-(>p9?%-' GQQQQQZopt1ab&bFK.-a33> !-_i9?, i.Wr*aam A @? L$-(STUCK IN HUNTING! h-&4 IQGQo`x' GQQQ_o!N-r*9:9:$a/!F GQ~abFrbUi9?,i.WzH-I'$8Fb GQ\QQVb+ L, GQdF_6V2r*%*$ Y9?%-'b * g #?-z-b$gA'' --9:9:$(pCOULDN'T FIND PATH TO ENEMY 9V*P-*$ -(b$ g( F w6F 6Fw@b$FgFS-z| 6|| Stakeout 3 from huntq!o SSb$g.bgi9:9:$I+q!t!IS Stakeout 2 from huntq!o  GQ AcE GQQQ H~? u.%7RR-&R-R-rRu    ,  zaTest 9D> 9D?"9D9?,[9D?9?"x9? ?9?"9?  9D> 9D?x9? ?9?"9? E9? ?9?"9?  9D?x9? ?9?"9? KaTest 9D> 9D?"9D9?,x9? 9?"9? KaTest 9D> 9D?"9D9?,x9?19? 9?"9? KaTest 9D> 9D?"9D9?,9? 9D9?&9?"9?  9D9?"   &5,h@@@h@ 9D?+?9?[9? 9? 9? h-9? 9?[9? +9D9?,,9Dp?;9D?9?,,9D9?,;{9D9?;=9?,Z9D9?;?9?,l9D9?;0?9?,W9D9?;P?9?,$b(?%j )9?;9?+ 9?,9? 9appv 9S9D%x "9?;9?+0@,x>9?,?$I9?+9? &9? 9? "9?{9?+ @0appv 9S9D%'`9D9?+?axat9?{9?` a '"9?Z?x9?` a' &"9?l?t9?` a'[fff?9?Z{%  a [-R'   -R   a  [-R'E -RK&%  a [9D[9?}L    a  [  9D[9?} $&?9?+,T9D9?+?9? %    -9?{9? 9?9?Ta'=  -RK%  -9?Z9? 9?9?T%a9S9D' % { -9?l9? 9?9?T9- a'{{&%a9S9D'R E  T$b( ?)9?;9?+  9?,9? $rE  T9?{9?E-RK&a '-RK9?Z9?E%a9S9D '9?l9?E - a'&%a9S9D '9:9:$ as"9?W?s9?` a'%7 F+ T WH G QjBP/R.rw9= GQ+IB-~J9?,b9:6Cw*-w*x_x&b9:6CJ@,bJ9?,Zw*w. **. 9?,. bX 2I%-~(M,~ Ibon9?I?9?~L>~9?,I%F*TimeLimit MM,w*w*-(%]w*G-w.*q!VM, .rw9=w*qw.*.M!@$'qr*-.  w*-(%r*-OCM& M, P M&M&-~'pw*w.*. pw*w.*3..G3-J(3-(-y ff&?-y'w3*'3M3I>3!<3 d!oA!:3-( GQelimtRQNb$  9?,Q  @Q'b$  9?,Q  @Q'=?  @Qo  @Q' GQRQQNiZ?QGQ5QQqYn;D9:}%wY*Y-Y}YeY-w. Y*r. Y*5K59? w*w.*w*@ Y g@5$tI@9?59? N@9?  @9??99 Y  9?%559?@JJb$Y  5@F@66 @9?59?5@ GQkrB74w*9?%-' GQzt[c9Fa%aAR%a&aa,R,mtwt*ta/!f.tOa$'ttu GQQQVo;  #*y ?a.z ? 9?,9?, Z'Ya?-8b$ #?Vb$ կ#?-(a>L=9?&9?, $-(STUCK IN STAKEOUT! 4QGQW uE' GQvQQQQtxO' GQQQ~w\Fcr\zH-I'X?Y$R\r\zH-I'$ GQ}yq6}pA3,}9?,@X}}@9?,9?, b9D}9?&J9?,9?,?W%@Z&y%y,Z@%WEyvWZ9?,y GQ|yqV\s>Wb$#fff? #L?yq!o!4 GQ{BYnBICQ[k% C%crBZ$ GQ~up3V,q @%W%|9<6Cr,||@9?,9?, ,|9?,@b9D|J9<6~C6@C]&u%Su,>]6MC%WEuvW]9?,u GQEoc1 E@*$rE* _WEra_ E կ_E#?E rr*ra E կ E#?E  wr*@I GQAKGH/w. G*w.`*IwK*K-p.Kwp*p pH@`KG`H GQuQQH)@^q(0r?*)?opqdw.*^. q?%)9w. ?*u. ?,u?6u9?,r.* u9?,)9D9?)>L=u6)9D9?)>=u9?,^^r?)9D9?)?w.G*9.G-J),2.G-J'?>*!<$@A!:9>*!<$ @A!:9?,v r?9:9:$)?w. ?*u9?,)9D9?)>>uA9?)?opq9DA GQ|+u!1, GQMBuulmaakle(ywm*lի9?,kyb$lela+9Ple GQ^QQGQQlQQ~\]4#dc-7  b2lh   j C6h rj*k%\k&tkk,k%k&,,\ktj  t\/rj*jR^a Gw^*9?,\^dj9?,^-'^ GQQQIQQJQQCSy ei  SwS*jS  {j{9? {9?,Eij{-EE[ bS[EiSSS :bwi*i ի?#? GQPKsy $ GQQQNQQQOc . % GQRfP{+f- f GQS]C{|ddwd*Mw. d*dC]dedd]de GQG$^`w^*^^9?, ^9?, ^9?&^-'P C&^k*w*G`w. * r` r*w`*`9?&`9?&9?&9?&K`self_fragr*- r` `-9?, 9?&9?, 9?&K team_frag r@*D?D9?@jDD9?& sD9?&i?tdm_frag GQL\% .-+  GQTB- vU #-(\-Z(*9?, ?y GQUr7 74w*9?%-' GQsa! -o( Ranged attackabw*y  ?^ a-o'rGYa=.O r* w.-X$#Bw*y  ?a.CL>??>9?,$$-(STUCK IN RANGEDATTACK! 4QGQYO[ ' GQQQ^X[F` i GQQQZ Ze "8-o-o($  GQ\tp $! GQ]+u 7..O,$"4G GQF^tc tq!! GQKQQ_B U-o( #Nr*r*ph no target in ranged attack GQW~- 6GQsX L GQQQru}  GQvQQwQQxQQyQQQQAt{b  GQ|QQ}QQQQzxX 8 Xx5.-$c GQJuX%-l'KwK*wK*K-K-K--l(KK'-lwZ*ZLastManuLastMan'( GQqGK GQbBL  -U(B' GQQQ]Ays  -U'y$2( GQxQQQQC+ ~!rG, GQFB a-U(<w*r* wm?^r* w GQD@QQHQQKIr/@F%xwx*w.xww*w-^wrwwY'w}x|xxT*w*- (' GQO_, GQQQ4QQOrDp  r4*(w.4*8r.4*(w.4* w4r6r.4rw.4*.4v6r.4 r GQN!c3  c!! ' ! ' ! ' ! ' ! ' ! ' ! ' ! ' ! ' ! ' ! ' ! ' ! '  G Q vAwOO?O*O$g?9?,/C69CgSJ?*N?*&a **\DCC*/OSJ?*N?*&a **\DCC$H9=I>JN&a DD7JN&a DD7JN&a DD0JN&a DD GQQcC  rS* w*wS*rSOSw*wSw**>*!< d!m@!:j Na.c w*rr*w*|%|,ww|* |XQ%/a9 GG%3 G 9? 0 10>*!<$ @A!: GQQQg g$1w.*.-'w.*-'.-'.9=,.9=,a(((w.*(' GQ3class WarlordRocket extends SeekingRocketProj; Q Q@<T`?OcA9?@r*h h d;E/$^N`G;E/a9 [[%mh[ m9? smdaC9?,m9/aC9?,m9/9?&w.[*GGm69Ca69Ca6$X6$6$69CaY9P[ h[Om/9Y9?9D9?6Y6 @9?J[Y?c^[Y?c&a ccQDnCCC10 GQ*class WarlordAmmo extends Ammunition; QBCclass Warlord extends Monster; var bool bRocketDir; function bool SameSpeciesAs(Pawn P) { return ( (Monster(P) != None) && (P.IsA('Skaarj') || P.IsA('WarLord')) ); } function PostBeginPlay() { Super.PostBeginPlay(); bMeleeFighter = (FRand() < 0.5); } function Step() { PlaySound(sound'step1t', SLOT_Interact); } function Flap() { PlaySound(sound'fly1WL', SLOT_Interact); } function SetMovementPhysics() { SetPhysics(PHYS_Flying); } function bool Dodge(eDoubleClickDir DoubleClickMove) { local vector X,Y,Z,duckdir; GetAxes(Rotation,X,Y,Z); if (DoubleClickMove == DCLICK_Forward) duckdir = X; else if (DoubleClickMove == DCLICK_Back) duckdir = -1*X; else if (DoubleClickMove == DCLICK_Left) duckdir = Y; else if (DoubleClickMove == DCLICK_Right) duckdir = -1*Y; SetPhysics(PHYS_Flying); if ( !bShotAnim && (FRand() < 0.3) ) { bShotAnim = true; SetAnimAction('FDodgeUp'); } Controller.Destination = Location + 200 * duckDir; Velocity = AirSpeed * duckDir; Controller.GotoState('TacticalMove', 'DoMove'); return true; } event Landed(vector HitNormal) { SetPhysics(PHYS_Walking); Super.Landed(HitNormal); } event HitWall( vector HitNormal, actor HitWall ) { if ( HitNormal.Z > MINFLOORZ ) SetPhysics(PHYS_Walking); Super.HitWall(HitNormal,HitWall); } singular function Falling() { SetPhysics(PHYS_Flying); } simulated function PlayDirectionalDeath(Vector HitLoc) { if ( Physics == PHYS_Flying ) PlayAnim('Dead2A'); else PlayAnim('Dead1'); } function PlayTakeHit(vector HitLocation, int Damage, class DamageType) { if ( Damage > 50 ) Super.PlayTakeHit(HitLocation,Damage,DamageType); } simulated function PlayDirectionalHit(Vector HitLoc) { local name Anim; local float frame,rate; if ( bShotAnim ) return; GetAnimParams(0, Anim,frame,rate); if ( Anim == 'FDodgeUp' ) return; TweenAnim('TakeHit', 0.05); } function PlayVictory() { SetPhysics(PHYS_Falling); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; PlaySound(sound'laugh1WL',SLOT_Interact); SetAnimAction('Laugh'); Controller.Destination = Location; Controller.GotoState('TacticalMove','WaitForAnim'); } function RangedAttack(Actor A) { if ( bShotAnim ) return; if ( Physics == PHYS_Flying ) SetAnimAction('FlyFire'); else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) { Controller.bPreparingMove = true; Acceleration = vect(0,0,0); SetAnimAction('Strike'); if ( MeleeDamageTarget(45, (45000.0 * Normal(Controller.Target.Location - Location))) ) PlaySound(sound'Threat1WL', SLOT_Talk); } else if ( !Controller.bPreparingMove && Controller.InLatentExecution(Controller.LATENT_MOVETOWARD) ) { SetPhysics(PHYS_Flying); SetAnimAction('FlyFire'); } else { SetAnimAction('Fire'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } bShotAnim = true; } function vector GetFireStart(vector X, vector Y, vector Z) { return Location + 0.5 * CollisionRadius * (X+Z-Y); } function FireProjectile() { local vector FireStart,X,Y,Z; local rotator ProjRot; local SeekingRocketProj S; if ( Controller != None ) { GetAxes(Rotation,X,Y,Z); FireStart = GetFireStart(X,Y,Z); if ( !SavedFireProperties.bInitialized ) { SavedFireProperties.AmmoClass = MyAmmo.Class; SavedFireProperties.ProjectileClass = MyAmmo.ProjectileClass; SavedFireProperties.WarnTargetPct = MyAmmo.WarnTargetPct; SavedFireProperties.MaxRange = MyAmmo.MaxRange; SavedFireProperties.bTossed = MyAmmo.bTossed; SavedFireProperties.bTrySplash = MyAmmo.bTrySplash; SavedFireProperties.bLeadTarget = MyAmmo.bLeadTarget; SavedFireProperties.bInstantHit = MyAmmo.bInstantHit; SavedFireProperties.bInitialized = true; } ProjRot = Controller.AdjustAim(SavedFireProperties,FireStart,600); if ( bRocketDir ) ProjRot.Yaw += 3072; else ProjRot.Yaw -= 3072; bRocketDir = !bRocketDir; S = Spawn(class'WarlordRocket',,,FireStart,ProjRot); S.Seeking = Controller.Enemy; PlaySound(FireSound,SLOT_Interact); } } QQclass SkaarjSparkles extends xEmitter; #exec OBJ LOAD FILE=XEffectMat.utx QAclass SkaarjPupae extends Monster; var name DeathAnims[3]; var bool bLunging; function SetMovementPhysics() { SetPhysics(PHYS_Falling); } simulated function PlayDirectionalDeath(Vector HitLoc) { PlayAnim(DeathAnims[Rand(3)], 0.7, 0.1); } simulated function PlayDirectionalHit(Vector HitLoc) { TweenAnim('TakeHit', 0.05); } function PlayVictory() { Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; PlayAnim('Stab', 1.0, 0.1); Controller.Destination = Location; Controller.GotoState('TacticalMove','WaitForAnim'); } singular function Bump(actor Other) { local name Anim; local float frame,rate; if ( bShotAnim && bLunging ) { bLunging = false; GetAnimParams(0, Anim,frame,rate); if ( Anim == 'Lunge' ) MeleeDamageTarget(12, (20000.0 * Normal(Controller.Target.Location - Location))); } Super.Bump(Other); } function RangedAttack(Actor A) { local float Dist; if ( bShotAnim ) return; Dist = VSize(A.Location - Location); if ( Dist > 350 ) return; bShotAnim = true; PlaySound(ChallengeSound[Rand(4)], SLOT_Interact); if ( Dist < MeleeRange + CollisionRadius + A.CollisionRadius ) { if ( FRand() < 0.5 ) SetAnimAction('Bite'); else SetAnimAction('Stab'); MeleeDamageTarget(8, vect(0,0,0)); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); return; } // lunge at enemy bLunging = true; Enable('Bump'); SetAnimAction('Lunge'); Velocity = 500 * Normal(A.Location + A.CollisionHeight * vect(0,0,0.75) - Location); if ( dist > CollisionRadius + A.CollisionRadius + 35 ) Velocity.Z += 0.7 * dist; SetPhysics(PHYS_Falling); } Quclass SkaarjProjectile extends Projectile; var xEmitter SparkleTrail; simulated function PostBeginPlay() { Super.PostBeginPlay(); if ( Level.NetMode != NM_DedicatedServer ) { SparkleTrail = Spawn(class'SkaarjSparkles', self); SparkleTrail.Skins[0] = Texture; } Velocity = Speed * Vector(Rotation); } simulated function Destroyed() { if (SparkleTrail != None) { SparkleTrail.mStartParticles = 12; SparkleTrail.mLifeRange[0] *= 2.0; SparkleTrail.mLifeRange[1] *= 2.0; SparkleTrail.mRegen = false; } Super.Destroyed(); } simulated function DestroyTrails() { if (SparkleTrail != None) SparkleTrail.Destroy(); } simulated function ProcessTouch (Actor Other, vector HitLocation) { local Vector X, RefNormal, RefDir; if (Other == Instigator) return; if (Other == Owner) return; if (Other.IsA('xPawn') && xPawn(Other).CheckReflect(HitLocation, RefNormal, Damage*0.25)) { if (Role == ROLE_Authority) { X = Normal(Velocity); RefDir = X - 2.0*RefNormal*(X dot RefNormal); RefDir = RefNormal; Spawn(Class, Other,, HitLocation+RefDir*20, Rotator(RefDir)); } DestroyTrails(); Destroy(); } else if ( !Other.IsA('Projectile') || Other.bProjTarget ) { Explode(HitLocation, Normal(HitLocation-Other.Location)); } } simulated function Explode(vector HitLocation,vector HitNormal) { if ( Role == ROLE_Authority ) HurtRadius(Damage, DamageRadius, MyDamageType, MomentumTransfer, HitLocation ); PlaySound(ImpactSound, SLOT_Misc); Destroy(); } Q)class SkaarjAmmo extends Ammunition; QcFclass Skaarj extends Monster; var sound FootStep[2]; var name DeathAnim[4]; function PlayVictory() { Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; PlaySound(sound'hairflp2sk',SLOT_Interact); SetAnimAction('HairFlip'); Controller.Destination = Location; Controller.GotoState('TacticalMove','WaitForAnim'); } function bool SameSpeciesAs(Pawn P) { return ( (Monster(P) != None) && (P.IsA('Skaarj') || P.IsA('WarLord')) ); } function vector GetFireStart(vector X, vector Y, vector Z) { return Location + 0.9 * CollisionRadius * X + 0.9 * CollisionRadius * Y + 0.4 * CollisionHeight * Z; } function SpawnTwoShots() { local vector X,Y,Z, FireStart; local rotator FireRotation; GetAxes(Rotation,X,Y,Z); FireStart = GetFireStart(X,Y,Z); if ( !SavedFireProperties.bInitialized ) { SavedFireProperties.AmmoClass = MyAmmo.Class; SavedFireProperties.ProjectileClass = MyAmmo.ProjectileClass; SavedFireProperties.WarnTargetPct = MyAmmo.WarnTargetPct; SavedFireProperties.MaxRange = MyAmmo.MaxRange; SavedFireProperties.bTossed = MyAmmo.bTossed; SavedFireProperties.bTrySplash = MyAmmo.bTrySplash; SavedFireProperties.bLeadTarget = MyAmmo.bLeadTarget; SavedFireProperties.bInstantHit = MyAmmo.bInstantHit; SavedFireProperties.bInitialized = true; } FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600); Spawn(MyAmmo.ProjectileClass,,,FireStart,FireRotation); FireStart = FireStart - 1.8 * CollisionRadius * Y; FireRotation.Yaw += 400; spawn(MyAmmo.ProjectileClass,,,FireStart, FireRotation); } simulated function AnimEnd(int Channel) { local name Anim; local float frame,rate; if ( Channel == 0 ) { GetAnimParams(0, Anim,frame,rate); if ( Anim == 'looking' ) IdleWeaponAnim = 'guncheck'; else if ( (Anim == 'guncheck') && (FRand() < 0.5) ) IdleWeaponAnim = 'looking'; } Super.AnimEnd(Channel); } function RunStep() { PlaySound(FootStep[Rand(2)], SLOT_Interact); } function WalkStep() { PlaySound(FootStep[Rand(2)], SLOT_Interact,0.2); } simulated function PlayDying(class DamageType, vector HitLoc) { AmbientSound = None; bCanTeleport = false; bReplicateMovement = false; bTearOff = true; bPlayedDeath = true; HitDamageType = DamageType; // these are replicated to other clients TakeHitLocation = HitLoc; LifeSpan = RagdollLifeSpan; GotoState('Dying'); Velocity += TearOffMomentum; BaseEyeHeight = Default.BaseEyeHeight; SetPhysics(PHYS_Falling); if ( (DamageType == class'DamTypeSniperHeadShot') || ((HitLoc.Z > Location.Z + 0.75 * CollisionHeight) && (FRand() > 0.5) && (DamageType != class'DamTypeAssaultBullet') && (DamageType != class'DamTypeMinigunBullet') && (DamageType != class'DamTypeFlakChunk')) ) { PlayAnim('Death5',1,0.05); CreateGib('head',DamageType,Rotation); return; } if ( Velocity.Z > 300 ) { if ( FRand() < 0.5 ) PlayAnim('Death',1.2,0.05); else PlayAnim('Death2',1.2,0.05); return; } PlayAnim(DeathAnim[Rand(4)],1.2,0.05); } function SpinDamageTarget() { if (MeleeDamageTarget(20, (30000 * Normal(Controller.Target.Location - Location))) ) PlaySound(sound'clawhit1s', SLOT_Interact); } function ClawDamageTarget() { if ( MeleeDamageTarget(25, (25000 * Normal(Controller.Target.Location - Location))) ) PlaySound(sound'clawhit1s', SLOT_Interact); } function RangedAttack(Actor A) { local name Anim; local float frame,rate; if ( bShotAnim ) return; bShotAnim = true; if ( Physics == PHYS_Swimming ) SetAnimAction('SwimFire'); else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) { if ( FRand() < 0.7 ) { SetAnimAction('Spin'); PlaySound(sound'Spin1s', SLOT_Interact); Acceleration = AccelRate * Normal(A.Location - Location); return; } SetAnimAction('Claw'); PlaySound(sound'Claw2s', SLOT_Interact); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } else if ( Velocity == vect(0,0,0) ) { SetAnimAction('Firing'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); } else { GetAnimParams(0,Anim,frame,rate); if ( Anim == 'RunL' ) SetAnimAction('StrafeLeftFr'); else if ( Anim == 'RunR' ) SetAnimAction('StrafeRightFr'); else SetAnimAction('JogFire'); } } Qtclass ScoreBoardInvasion extends ScoreBoardDeathMatch; var localized string TeamScoreString; var localized string WaveString; function DrawTitle(Canvas Canvas, float HeaderOffsetY, float PlayerAreaY, float PlayerBoxSizeY) { local string titlestring,scoreinfostring,RestartString; local float TitleXL,ScoreInfoXL,YL; if ( Canvas.ClipX < 512 ) return; titlestring = SkillLevel[Clamp(InvasionGameReplicationInfo(GRI).BaseDifficulty,0,7)]@GRI.GameName@WaveString@(InvasionGameReplicationInfo(GRI).WaveNumber+1)$MapName$Level.Title; Canvas.StrLen(TitleString,TitleXL,YL); if ( GRI.MaxLives != 0 ) ScoreInfoString = MaxLives@GRI.MaxLives; else if ( GRI.GoalScore != 0 ) ScoreInfoString = FragLimit@GRI.GoalScore; if ( GRI.TimeLimit != 0 ) ScoreInfoString = ScoreInfoString@spacer@TimeLimit$FormatTime(GRI.RemainingTime); else ScoreInfoString = ScoreInfoString@spacer@FooterText@FormatTime(GRI.ElapsedTime); Canvas.DrawColor = HUDClass.default.GoldColor; if ( UnrealPlayer(Owner).bDisplayLoser ) ScoreInfoString = class'HUDBase'.default.YouveLostTheMatch; else if ( UnrealPlayer(Owner).bDisplayWinner ) ScoreInfoString = class'HUDBase'.default.YouveWonTheMatch; else if ( PlayerController(Owner).IsDead() ) { RestartString = Restart; if ( PlayerController(Owner).PlayerReplicationInfo.bOutOfLives ) RestartString = OutFireText; if ( Canvas.ClipY - HeaderOffsetY - PlayerAreaY >= 2.5 * YL ) { Canvas.StrLen(RestartString,ScoreInfoXL,YL); Canvas.SetPos(0.5*(Canvas.ClipX-ScoreInfoXL), Canvas.ClipY - 2.5 * YL); Canvas.DrawText(RestartString,true); } else ScoreInfoString = RestartString; } Canvas.StrLen(ScoreInfoString,ScoreInfoXL,YL); Canvas.SetPos(0.5*(Canvas.ClipX-TitleXL), ((HeaderOffsetY-PlayerBoxSizeY) - YL)*0.5 ); //Canvas.SetPos(0.5*(Canvas.ClipX-TitleXL), 0.5*YL); Canvas.DrawText(TitleString,true); Canvas.SetPos(0.5*(Canvas.ClipX-ScoreInfoXL), Canvas.ClipY - 1.5 * YL); Canvas.DrawText(ScoreInfoString,true); } simulated event UpdateScoreBoard(Canvas Canvas) { local PlayerReplicationInfo PRI, OwnerPRI; local int i, FontReduction, OwnerPos, NetXPos, PlayerCount,HeaderOffsetY,HeadFoot, MessageFoot, PlayerBoxSizeY, BoxSpaceY, NameXPos, BoxTextOffsetY, OwnerOffset, ScoreXPos, DeathsXPos, BoxXPos, TitleYPos, BoxWidth; local float XL,YL, MaxScaling; local float deathsXL, scoreXL, netXL, MaxNamePos; local string playername[MAXPLAYERS]; local bool bNameFontReduction; OwnerPRI = PlayerController(Owner).PlayerReplicationInfo; for (i=0; i (Canvas.ClipY - 1.5 * HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ) { BoxSpaceY = 0.125 * YL; PlayerBoxSizeY = 1.25 * YL; if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ) { if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ) PlayerBoxSizeY = 1.125 * YL; if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ) { FontReduction++; Canvas.Font = GetSmallerFontFor(Canvas,FontReduction); Canvas.StrLen("Test", XL, YL); BoxSpaceY = 0.125 * YL; PlayerBoxSizeY = 1.125 * YL; HeadFoot = 7*YL; if ( PlayerCount > (Canvas.ClipY - HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ) { FontReduction++; Canvas.Font = GetSmallerFontFor(Canvas,FontReduction); Canvas.StrLen("Test", XL, YL); BoxSpaceY = 0.125 * YL; PlayerBoxSizeY = 1.125 * YL; HeadFoot = 7*YL; if ( (Canvas.ClipY >= 768) && (PlayerCount > (Canvas.ClipY - HeadFoot)/(PlayerBoxSizeY + BoxSpaceY)) ) { FontReduction++; Canvas.Font = GetSmallerFontFor(Canvas,FontReduction); Canvas.StrLen("Test", XL, YL); BoxSpaceY = 0.125 * YL; PlayerBoxSizeY = 1.125 * YL; HeadFoot = 7*YL; } } } } } if ( Canvas.ClipX < 512 ) PlayerCount = Min(PlayerCount, 1+(Canvas.ClipY - HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ); else PlayerCount = Min(PlayerCount, (Canvas.ClipY - HeadFoot)/(PlayerBoxSizeY + BoxSpaceY) ); if ( OwnerOffset >= PlayerCount ) PlayerCount -= 1; if ( FontReduction > 2 ) MaxScaling = 3; else MaxScaling = 2.125; PlayerBoxSizeY = FClamp((1+(Canvas.ClipY - 0.67 * MessageFoot))/PlayerCount - BoxSpaceY, PlayerBoxSizeY, MaxScaling * YL); bDisplayMessages = (PlayerCount <= (Canvas.ClipY - MessageFoot)/(PlayerBoxSizeY + BoxSpaceY)); HeaderOffsetY = 5 * YL; BoxWidth = 0.9375 * Canvas.ClipX; BoxXPos = 0.5 * (Canvas.ClipX - BoxWidth); BoxWidth = Canvas.ClipX - 2*BoxXPos; NameXPos = BoxXPos + 0.0625 * BoxWidth; ScoreXPos = BoxXPos + 0.5 * BoxWidth; DeathsXPos = BoxXPos + 0.6875 * BoxWidth; NetXPos = BoxXPos + 0.8125 * BoxWidth; // draw background boxes Canvas.Style = ERenderStyle.STY_Alpha; Canvas.DrawColor = HUDClass.default.WhiteColor * 0.5; for ( i=0; i MaxNamePos ) { bNameFontReduction = true; break; } } if ( !bNameFontReduction && (OwnerOffset >= PlayerCount) ) { playername[OwnerOffset] = GRI.PRIArray[OwnerOffset].PlayerName; Canvas.StrLen(playername[OwnerOffset], XL, YL); if ( XL > MaxNamePos ) bNameFontReduction = true; } if ( bNameFontReduction ) Canvas.Font = GetSmallerFontFor(Canvas,FontReduction+1); for ( i=0; i MaxNamePos ) playername[i] = left(playername[i], MaxNamePos/XL * len(PlayerName[i])); } if ( OwnerOffset >= PlayerCount ) { playername[OwnerOffset] = GRI.PRIArray[OwnerOffset].PlayerName; Canvas.StrLen(playername[OwnerOffset], XL, YL); if ( XL > MaxNamePos ) playername[OwnerOffset] = left(playername[OwnerOffset], MaxNamePos/XL * len(PlayerName[OwnerOffset])); } Canvas.Style = ERenderStyle.STY_Normal; Canvas.DrawColor = HUDClass.default.WhiteColor; Canvas.SetPos(0.5 * Canvas.ClipX, HeaderOffsetY + 4); BoxTextOffsetY = HeaderOffsetY + 0.5 * (PlayerBoxSizeY - YL); Canvas.DrawColor = HUDClass.default.WhiteColor; for ( i=0; i= PlayerCount ) { OwnerPos = (PlayerBoxSizeY + BoxSpaceY)*PlayerCount + BoxTextOffsetY; // draw extra box Canvas.Style = ERenderStyle.STY_Alpha; Canvas.DrawColor = HUDClass.default.TurqColor * 0.5; Canvas.SetPos(BoxXPos, HeaderOffsetY + (PlayerBoxSizeY + BoxSpaceY)*PlayerCount); Canvas.DrawTileStretched( BoxMaterial, BoxWidth, PlayerBoxSizeY); Canvas.Style = ERenderStyle.STY_Normal; } else OwnerPos = (PlayerBoxSizeY + BoxSpaceY)*OwnerOffset + BoxTextOffsetY; Canvas.DrawColor = HUDClass.default.GoldColor; Canvas.SetPos(NameXPos, OwnerPos); if ( bNameFontReduction ) Canvas.Font = GetSmallerFontFor(Canvas,FontReduction+1); Canvas.DrawText(playername[OwnerOffset],true); if ( bNameFontReduction ) Canvas.Font = GetSmallerFontFor(Canvas,FontReduction); Canvas.SetPos(ScoreXPos, OwnerPos); Canvas.DrawText(int(GRI.PRIArray[OwnerOffset].Score),true); Canvas.SetPos(DeathsXPos, OwnerPos); if ( GRI.PRIArray[OwnerOffset].bOutOfLives ) Canvas.DrawText(OutText,true); else if ( GRI.MaxLives != 1 ) Canvas.DrawText(int(GRI.PRIArray[OwnerOffset].Deaths),true); if ( Level.NetMode == NM_Standalone ) return; Canvas.StrLen(NetText, NetXL, YL); Canvas.DrawColor = HUDClass.default.WhiteColor; Canvas.SetPos(NetXPos + 0.5*NetXL, TitleYPos); Canvas.DrawText(NetText,true); for ( i=0; i 1 + 4.5 * FRand() ) Super.FearThisSpot(aSpot); } function SetCombatTimer() { SetTimer(1.2 - 0.09 * FMin(10,Skill+ReactionTime), True); } function WaitForMover(Mover M) { Super.WaitForMover(M); StopStartTime = Level.TimeSeconds; } function TimedFireWeaponAtEnemy() { if ( (Enemy == None) || FireWeaponAt(Enemy) ) SetCombatTimer(); else SetTimer(0.1, True); } function bool FireWeaponAt(Actor A) { if ( A == None ) A = Enemy; if ( (A == None) || (Focus != A) ) return false; Target = A; Monster(Pawn).RangedAttack(Target); return false; } function bool CanAttack(Actor Other) { // return true if in range of current weapon return Monster(Pawn).CanAttack(Other); } function StopFiring() { Monster(Pawn).StopFiring(); bCanFire = false; bFire = 0; bAltFire = 0; } //=========================================================================== function DisplayDebug(Canvas Canvas, out float YL, out float YPos) { local string S; Super.DisplayDebug(Canvas,YL, YPos); Canvas.SetDrawColor(255,255,255); Canvas.DrawText(" "$GoalString, false); YPos += 2*YL; Canvas.SetPos(4,YPos); if ( Enemy != None ) { Canvas.DrawText("Enemy Dist "$VSize(Enemy.Location - Pawn.Location)$" Acquired "$bEnemyAcquired); YPos += YL; Canvas.SetPos(4,YPos); } Canvas.DrawText("Weapons: "$S, false); YPos += YL; Canvas.SetPos(4,YPos); Canvas.DrawText("PERSONALITY: CombatStyle "$CombatStyle$" Strafing "$StrafingAbility); YPos += YL; Canvas.SetPos(4,YPos); } function bool FindNewEnemy() { local Pawn BestEnemy; local bool bSeeNew, bSeeBest; local float BestDist, NewDist; local Controller C; if ( Level.Game.bGameEnded ) return false; for ( C=Level.ControllerList; C!=None; C=C.NextController ) if ( C.bIsPlayer && (C.Pawn != None) ) { if ( BestEnemy == None ) { BestEnemy = C.Pawn; BestDist = VSize(BestEnemy.Location - Pawn.Location); bSeeBest = CanSee(BestEnemy); } else { NewDist = VSize(C.Pawn.Location - Pawn.Location); if ( !bSeeBest || (NewDist < BestDist) ) { bSeeNew = CanSee(C.Pawn); if ( bSeeNew || (!bSeeBest && (NewDist < BestDist)) ) { BestEnemy = C.Pawn; BestDist = NewDist; bSeeBest = bSeeNew; } } } } if ( BestEnemy == Enemy ) return false; if ( BestEnemy != None ) { ChangeEnemy(BestEnemy,CanSee(BestEnemy)); return true; } return false; } function ChangeEnemy(Pawn NewEnemy, bool bCanSeeNewEnemy) { OldEnemy = Enemy; Enemy = NewEnemy; EnemyChanged(bCanSeeNewEnemy); } function bool SetEnemy( Pawn NewEnemy, optional bool bHateMonster ) { local float EnemyDist; local bool bNewMonsterEnemy; if ( (NewEnemy == None) || (NewEnemy.Health <= 0) || (NewEnemy.Controller == None) || (NewEnemy == Enemy) ) return false; bNewMonsterEnemy = bHateMonster && (Level.Game.NumPlayers < 4) && !Monster(Pawn).SameSpeciesAs(NewEnemy) && !NewEnemy.Controller.bIsPlayer; if ( !NewEnemy.Controller.bIsPlayer && !bNewMonsterEnemy ) return false; if ( (bNewMonsterEnemy && LineOfSightTo(NewEnemy)) || (Enemy == None) || !EnemyVisible() ) { ChangeEnemy(NewEnemy,CanSee(NewEnemy)); return true; } if ( !CanSee(NewEnemy) ) return false; if ( !bHateMonster && (Monster(Enemy) != None) && NewEnemy.Controller.bIsPlayer ) return false; EnemyDist = VSize(Enemy.Location - Pawn.Location); if ( EnemyDist < Pawn.MeleeRange ) return false; if ( EnemyDist > 1.7 * VSize(NewEnemy.Location - Pawn.Location)) { ChangeEnemy(NewEnemy,CanSee(NewEnemy)); return true; } return false; } function HearNoise(float Loudness, Actor NoiseMaker) { if ( ((ChooseAttackCounter < 2) || (ChooseAttackTime != Level.TimeSeconds)) && SetEnemy(NoiseMaker.instigator) ) WhatToDoNext(2); } event SeePlayer(Pawn SeenPlayer) { if ( ((ChooseAttackCounter < 2) || (ChooseAttackTime != Level.TimeSeconds)) && SetEnemy(SeenPlayer) ) WhatToDoNext(3); if ( Enemy == SeenPlayer ) { VisibleEnemy = Enemy; EnemyVisibilityTime = Level.TimeSeconds; bEnemyIsVisible = true; } } function bool ClearShot(Vector TargetLoc, bool bImmediateFire) { local bool bSeeTarget; if ( VSize(Enemy.Location - TargetLoc) > MAXSTAKEOUTDIST ) return false; bSeeTarget = FastTrace(TargetLoc, Pawn.Location + Pawn.EyeHeight * vect(0,0,1)); // if pawn is crouched, check if standing would provide clear shot if ( !bImmediateFire && !bSeeTarget && Pawn.bIsCrouched ) bSeeTarget = FastTrace(TargetLoc, Pawn.Location + (Pawn.Default.EyeHeight + Pawn.Default.CollisionHeight - Pawn.CollisionHeight) * vect(0,0,1)); if ( !bSeeTarget || !FastTrace(TargetLoc , Enemy.Location + Enemy.BaseEyeHeight * vect(0,0,1)) ); return false; if ( (Monster(Pawn).SplashDamage() && (VSize(Pawn.Location - TargetLoc) < Monster(Pawn).GetDamageRadius())) || !FastTrace(TargetLoc + vect(0,0,0.9) * Enemy.CollisionHeight, Pawn.Location) ) { StopFiring(); return false; } return true; } /* CheckIfShouldCrouch() returns true if target position still can be shot from crouched position, or if couldn't hit it from standing position either */ function CheckIfShouldCrouch(vector StartPosition, vector TargetPosition, float probability) { local actor HitActor; local vector HitNormal,HitLocation, X,Y,Z, projstart; if ( !Pawn.bCanCrouch || (!Pawn.bIsCrouched && (FRand() > probability)) || (Skill < 3 * FRand()) || Monster(Pawn).RecommendSplashDamage() ) { Pawn.bWantsToCrouch = false; return; } GetAxes(Rotation,X,Y,Z); projStart = Monster(Pawn).GetFireStart(X,Y,Z); projStart = projStart + StartPosition - Pawn.Location; projStart.Z = projStart.Z - 1.8 * (Pawn.CollisionHeight - Pawn.CrouchHeight); HitActor = Trace(HitLocation, HitNormal, TargetPosition , projStart, false); if ( HitActor == None ) { Pawn.bWantsToCrouch = true; return; } projStart.Z = projStart.Z + 1.8 * (Pawn.Default.CollisionHeight - Pawn.CrouchHeight); HitActor = Trace(HitLocation, HitNormal, TargetPosition , projStart, false); if ( HitActor == None ) { Pawn.bWantsToCrouch = false; return; } Pawn.bWantsToCrouch = true; } function Trigger( actor Other, pawn EventInstigator ) { if ( (Other == Pawn) || (Pawn.Health <= 0) ) return; SetEnemy(EventInstigator,true); } function SetEnemyInfo(bool bNewEnemyVisible) { AcquireTime = Level.TimeSeconds; if ( bNewEnemyVisible ) { LastSeenTime = Level.TimeSeconds; LastSeenPos = Enemy.Location; LastSeeingPos = Pawn.Location; bEnemyInfoValid = true; } else { LastSeenTime = -1000; bEnemyInfoValid = false; } } // EnemyChanged() called when current enemy changes function EnemyChanged(bool bNewEnemyVisible) { bEnemyAcquired = false; SetEnemyInfo(bNewEnemyVisible); Monster(Pawn).PlayChallengeSound(); } function bool StrafeFromDamage(float Damage, class DamageType, bool bFindDest); //********************************************************************** function bool NotifyPhysicsVolumeChange(PhysicsVolume NewVolume) { local vector jumpDir; if ( newVolume.bWaterVolume ) { if (!Pawn.bCanSwim) MoveTimer = -1.0; else if (Pawn.Physics != PHYS_Swimming) Pawn.setPhysics(PHYS_Swimming); } else if (Pawn.Physics == PHYS_Swimming) { if ( Pawn.bCanFly ) Pawn.SetPhysics(PHYS_Flying); else { Pawn.SetPhysics(PHYS_Falling); if ( Pawn.bCanWalk && (Abs(Pawn.Acceleration.X) + Abs(Pawn.Acceleration.Y) > 0) && (Destination.Z >= Pawn.Location.Z) && Pawn.CheckWaterJump(jumpDir) ) Pawn.JumpOutOfWater(jumpDir); } } return false; } event NotifyMissedJump() { if ( Pawn.bCanFly ) Pawn.SetPhysics(PHYS_Flying); } function Possess(Pawn aPawn) { Super.Possess(aPawn); InitializeSkill(DeathMatch(Level.Game).AdjustedDifficulty); Pawn.MaxFallSpeed = 1.1 * Pawn.default.MaxFallSpeed; // so bots will accept a little falling damage for shorter routes Pawn.SetMovementPhysics(); if (Pawn.Physics == PHYS_Walking) Pawn.SetPhysics(PHYS_Falling); WhatToDoNext(1); enable('NotifyBump'); } function InitializeSkill(float InSkill) { Skill = FClamp(InSkill, 0, 7); ReSetSkill(); } function ResetSkill() { local float AdjustedYaw; bLeadTarget = ( Skill >= 4 ); SetCombatTimer(); SetPeripheralVision(); if ( Skill + ReactionTime > 7 ) RotationRate.Yaw = 90000; else if ( Skill + ReactionTime >= 4 ) RotationRate.Yaw = 20000 + 7000 * (skill + ReactionTime); else RotationRate.Yaw = 30000 + 4000 * (skill + ReactionTime); AdjustedYaw = (0.75 + 0.05 * ReactionTime) * RotationRate.Yaw; AcquisitionYawRate = AdjustedYaw; SetMaxDesiredSpeed(); } function SetMaxDesiredSpeed() { if ( Pawn != None ) { if ( Skill > 3 ) Pawn.MaxDesiredSpeed = 1; else Pawn.MaxDesiredSpeed = 0.6 + 0.1 * Skill; } } function SetPeripheralVision() { if ( Pawn == None ) return; if ( Skill < 2 ) Pawn.PeripheralVision = 0.7; else if ( Skill > 5 ) Pawn.PeripheralVision = 0; else Pawn.PeripheralVision = 1.0 - 0.2 * skill; Pawn.SightRadius = Pawn.Default.SightRadius; } //============================================================================= function WhatToDoNext(byte CallingByte) { if ( ChoosingAttackLevel > 0 ) log("CHOOSEATTACKAGAIN in state "$GetStateName()$" enemy "$GetEnemyName()$" old enemy "$GetOldEnemyName()$" CALLING BYTE "$CallingByte); if ( ChooseAttackTime == Level.TimeSeconds ) { ChooseAttackCounter++; if ( ChooseAttackCounter > 3 ) log("CHOOSEATTACKSERIAL in state "$GetStateName()$" enemy "$GetEnemyName()$" old enemy "$GetOldEnemyName()$" CALLING BYTE "$CallingByte); } else { ChooseAttackTime = Level.TimeSeconds; ChooseAttackCounter = 0; } if ( Monster(Pawn).bTryToWalk && (Pawn.Physics == PHYS_Flying) && (ChoosingAttackLevel == 0) && (ChooseAttackCounter == 0) ) TryToWalk(); ChoosingAttackLevel++; ExecuteWhatToDoNext(); ChoosingAttackLevel--; } /* Check if just above ground - if so land */ function TryToWalk() { local vector HitLocation, HitNormal, Extent; local actor HitActor; if ( Pawn.PhysicsVolume.bWaterVolume ) return; Extent = Pawn.GetCollisionExtent(); HitActor = Trace(HitLocation, HitNormal, Pawn.Location - vect(0,0,100), Pawn.Location, false, Extent); if ( (HitActor != None) && HitActor.bWorldGeometry && (HitNormal.Z > MINFLOORZ) ) Pawn.SetPhysics(PHYS_Falling); } function string GetOldEnemyName() { if ( OldEnemy == None ) return "NONE"; else return OldEnemy.GetHumanReadableName(); } function string GetEnemyName() { if ( Enemy == None ) return "NONE"; else return Enemy.GetHumanReadableName(); } function ExecuteWhatToDoNext() { bHasFired = false; GoalString = "WhatToDoNext at "$Level.TimeSeconds; if ( Pawn == None ) { warn(GetHumanReadableName()$" WhatToDoNext with no pawn"); return; } if ( bPreparingMove && Monster(Pawn).bShotAnim ) { Pawn.Acceleration = vect(0,0,0); GotoState('WaitForAnim'); return; } if (Pawn.Physics == PHYS_None) Pawn.SetMovementPhysics(); if ( (Pawn.Physics == PHYS_Falling) && DoWaitForLanding() ) return; if ( (Enemy != None) && ((Enemy.Health <= 0) || (Enemy.Controller == None)) ) Enemy = None; if ( Level.Game.bGameEnded && (Enemy != None) && Enemy.Controller.bIsPlayer ) Enemy = None; if ( (Enemy == None) || !EnemyVisible() ) FindNewEnemy(); if ( Enemy != None ) ChooseAttackMode(); else { GoalString = "WhatToDoNext Wander or Camp at "$Level.TimeSeconds; WanderOrCamp(true); } } function bool DoWaitForLanding() { GotoState('WaitingForLanding'); return true; } function bool EnemyVisible() { if ( (EnemyVisibilityTime == Level.TimeSeconds) && (VisibleEnemy == Enemy) ) return bEnemyIsVisible; VisibleEnemy = Enemy; EnemyVisibilityTime = Level.TimeSeconds; bEnemyIsVisible = LineOfSightTo(Enemy); return bEnemyIsVisible; } function FightEnemy(bool bCanCharge) { local vector X,Y,Z; local float enemyDist; local float AdjustedCombatStyle, Aggression; local bool bFarAway, bOldForcedCharge; local NavigationPoint N; if ( (Enemy == None) || (Pawn == None) ) log("HERE 3 Enemy "$Enemy$" pawn "$Pawn); if ( (Enemy == FailedHuntEnemy) && (Level.TimeSeconds == FailedHuntTime) ) { if ( !Enemy.Controller.bIsPlayer ) FindNewEnemy(); if ( Enemy == FailedHuntEnemy ) { GoalString = "FAILED HUNT - HANG OUT"; if ( EnemyVisible() ) bCanCharge = false; else if ( (LastRespawnTime != Level.TimeSeconds) && ((LastSeenTime == 0) || (Level.TimeSeconds - LastSeenTime) > 15) && !Pawn.PlayerCanSeeMe() ) { LastRespawnTime = Level.TimeSeconds; EnemyVisibilityTime = 0; N = Level.Game.FindPlayerStart(self,1); Pawn.SetLocation(N.Location+(Pawn.CollisionHeight - N.CollisionHeight) * vect(0,0,1)); } if ( !EnemyVisible() ) { WanderOrCamp(true); return; } } } bOldForcedCharge = bMustCharge; bMustCharge = false; enemyDist = VSize(Pawn.Location - Enemy.Location); AdjustedCombatStyle = CombatStyle; Aggression = 1.5 * FRand() - 0.8 + 2 * AdjustedCombatStyle + FRand() * (Normal(Enemy.Velocity - Pawn.Velocity) Dot Normal(Enemy.Location - Pawn.Location)); if ( Enemy.Weapon != None ) Aggression += 2 * Enemy.Weapon.SuggestDefenseStyle(); if ( enemyDist > MAXSTAKEOUTDIST ) Aggression += 0.5; if ( (Pawn.Physics == PHYS_Walking) || (Pawn.Physics == PHYS_Falling) ) { if (Pawn.Location.Z > Enemy.Location.Z + TACTICALHEIGHTADVANTAGE) Aggression = FMax(0.0, Aggression - 1.0 + AdjustedCombatStyle); else if ( (Skill < 4) && (enemyDist > 0.65 * MAXSTAKEOUTDIST) ) { bFarAway = true; Aggression += 0.5; } else if (Pawn.Location.Z < Enemy.Location.Z - Pawn.CollisionHeight) // below enemy Aggression += CombatStyle; } if ( !EnemyVisible() ) { GoalString = "Enemy not visible"; if ( !bCanCharge ) { GoalString = "Stake Out"; DoStakeOut(); } else { GoalString = "Hunt"; GotoState('Hunting'); } return; } // see enemy - decide whether to charge it or strafe around/stand and fire Target = Enemy; if( Monster(Pawn).PreferMelee() || (bCanCharge && bOldForcedCharge) ) { GoalString = "Charge"; DoCharge(); return; } if ( bCanCharge && (Skill < 5) && bFarAway && (Aggression > 1) && (FRand() < 0.5) ) { GoalString = "Charge closer"; DoCharge(); return; } if ( !Monster(Pawn).PreferMelee() && (FRand() > 0.17 * (skill - 1)) && !DefendMelee(enemyDist) ) { GoalString = "Ranged Attack"; DoRangedAttackOn(Enemy); return; } if ( bCanCharge ) { if ( Aggression > 1 ) { GoalString = "Charge 2"; DoCharge(); return; } } if ( !Pawn.bCanStrafe ) { GoalString = "Ranged Attack"; DoRangedAttackOn(Enemy); return; } GoalString = "Do tactical move"; if ( !Monster(Pawn).RecommendSplashDamage() && Monster(Pawn).bCanDodge && (FRand() < 0.7) && (FRand()*Skill > 3) ) { GetAxes(Pawn.Rotation,X,Y,Z); GoalString = "Try to Duck "; if ( FRand() < 0.5 ) { Y *= -1; TryToDuck(Y, true); } else TryToDuck(Y, false); } DoTacticalMove(); } function DoRangedAttackOn(Actor A) { Target = A; GotoState('RangedAttack'); } /* ChooseAttackMode() Handles tactical attacking state selection - choose which type of attack to do from here */ function ChooseAttackMode() { GoalString = " ChooseAttackMode last seen "$(Level.TimeSeconds - LastSeenTime); // should I run away? if ( (Enemy == None) || (Pawn == None) ) log("HERE 1 Enemy "$Enemy$" pawn "$Pawn); GoalString = "ChooseAttackMode FightEnemy"; FightEnemy(true); } event SoakStop(string problem) { local UnrealPlayer PC; log(problem); SoakString = problem; GoalString = SoakString@GoalString; ForEach DynamicActors(class'UnrealPlayer',PC) { PC.SoakPause(Pawn); break; } } function bool FindRoamDest() { local actor BestPath; if ( Pawn.FindAnchorFailedTime == Level.TimeSeconds ) { // couldn't find an anchor. GoalString = "No anchor "$Level.TimeSeconds; if ( Pawn.LastValidAnchorTime > 5 ) { if ( bSoaking ) SoakStop("NO PATH AVAILABLE!!!"); else { if ( NumRandomJumps > 4 ) { Pawn.Health = 0; Pawn.Died( self, class'Suicided', Pawn.Location ); return true; } else { // jump NumRandomJumps++; if ( Physics != PHYS_Falling ) { Pawn.SetPhysics(PHYS_Falling); Pawn.Velocity = 0.5 * Pawn.GroundSpeed * VRand(); Pawn.Velocity.Z = Pawn.JumpZ; } } } } //log(self$" Find Anchor failed!"); return false; } NumRandomJumps = 0; GoalString = "Find roam dest "$Level.TimeSeconds; // find random NavigationPoint to roam to if ( (RouteGoal == None) || (Pawn.Anchor == RouteGoal) || Pawn.ReachedDestination(RouteGoal) ) { RouteGoal = FindRandomDest(); BestPath = RouteCache[0]; if ( RouteGoal == None ) { if ( bSoaking && (Physics != PHYS_Falling) ) SoakStop("COULDN'T FIND ROAM DESTINATION"); return false; } } if ( BestPath == None ) BestPath = FindPathToward(RouteGoal,false); if ( BestPath != None ) { MoveTarget = BestPath; GotoState('Roaming'); return true; } if ( bSoaking && (Physics != PHYS_Falling) ) SoakStop("COULDN'T FIND ROAM PATH TO "$RouteGoal); RouteGoal = None; return false; } function bool TestDirection(vector dir, out vector pick) { local vector HitLocation, HitNormal, dist; local actor HitActor; pick = dir * (MINSTRAFEDIST + 2 * MINSTRAFEDIST * FRand()); HitActor = Trace(HitLocation, HitNormal, Pawn.Location + pick + 1.5 * Pawn.CollisionRadius * dir , Pawn.Location, false); if (HitActor != None) { pick = HitLocation + (HitNormal - dir) * 2 * Pawn.CollisionRadius; if ( !FastTrace(pick, Pawn.Location) ) return false; } else pick = Pawn.Location + pick; dist = pick - Pawn.Location; if (Pawn.Physics == PHYS_Walking) dist.Z = 0; return (VSize(dist) > MINSTRAFEDIST); } function Restart() { Super.Restart(); ReSetSkill(); GotoState('Roaming','DoneRoaming'); } function CancelCampFor(Controller C); function bool AdjustAround(Pawn Other) { local float speed; local vector VelDir, OtherDir, SideDir; speed = VSize(Pawn.Acceleration); if ( speed < Pawn.WalkingPct * Pawn.GroundSpeed ) return false; VelDir = Pawn.Acceleration/speed; VelDir.Z = 0; OtherDir = Other.Location - Pawn.Location; OtherDir.Z = 0; OtherDir = Normal(OtherDir); if ( (VelDir Dot OtherDir) > 0.8 ) { bAdjusting = true; SideDir.X = VelDir.Y; SideDir.Y = -1 * VelDir.X; if ( (SideDir Dot OtherDir) > 0 ) SideDir *= -1; AdjustLoc = Pawn.Location + 1.5 * Other.CollisionRadius * (0.5 * VelDir + SideDir); } } function DirectedWander(vector WanderDir) { GoalString = "DIRECTED WANDER "$GoalString; Pawn.bWantsToCrouch = Pawn.bIsCrouched; if ( TestDirection(WanderDir,Destination) ) GotoState('RestFormation', 'Moving'); else GotoState('RestFormation', 'Begin'); } event bool NotifyBump(actor Other) { local Pawn P; Disable('NotifyBump'); P = Pawn(Other); if ( (P == None) || (P.Controller == None) || (Enemy == P) ) return false; if ( SetEnemy(P) ) { WhatToDoNext(4); return false; } if ( Enemy == P ) return false; if ( !AdjustAround(P) ) CancelCampFor(P.Controller); return false; } function SetFall() { if (Pawn.bCanFly) { Pawn.SetPhysics(PHYS_Flying); return; } if ( Pawn.bNoJumpAdjust ) { Pawn.bNoJumpAdjust = false; return; } else { Pawn.Velocity = EAdjustJump(Pawn.Velocity.Z,Pawn.GroundSpeed); Pawn.Acceleration = vect(0,0,0); } } function bool NotifyLanded(vector HitNormal) { local vector Vel2D; if ( MoveTarget != None ) { Vel2D = Pawn.Velocity; Vel2D.Z = 0; if ( (Vel2D Dot (MoveTarget.Location - Pawn.Location)) < 0 ) { Pawn.Acceleration = vect(0,0,0); if ( NavigationPoint(MoveTarget) != None ) Pawn.Anchor = NavigationPoint(MoveTarget); MoveTimer = -1; } } return false; } /* FindBestPathToward() Assumes the desired destination is not directly reachable. It tries to set Destination to the location of the best waypoint, and returns true if successful */ function bool FindBestPathToward(Actor A, bool bCheckedReach, bool bAllowDetour) { if ( !bCheckedReach && ActorReachable(A) ) MoveTarget = A; else MoveTarget = FindPathToward(A,false); if ( MoveTarget != None ) return true; else { if ( (A == Enemy) && (A != None) ) { FailedHuntTime = Level.TimeSeconds; FailedHuntEnemy = Enemy; } if ( bSoaking && (Physics != PHYS_Falling) ) SoakStop("COULDN'T FIND BEST PATH TO "$A); } return false; } function bool NeedToTurn(vector targ) { local vector LookDir,AimDir; LookDir = Vector(Pawn.Rotation); LookDir.Z = 0; LookDir = Normal(LookDir); AimDir = targ - Pawn.Location; AimDir.Z = 0; AimDir = Normal(AimDir); return ((LookDir Dot AimDir) < 0.93); } /* NearWall() returns true if there is a nearby barrier at eyeheight, and changes FocalPoint to a suggested place to look */ function bool NearWall(float walldist) { local actor HitActor; local vector HitLocation, HitNormal, ViewSpot, ViewDist, LookDir; LookDir = vector(Rotation); ViewSpot = Pawn.Location + Pawn.BaseEyeHeight * vect(0,0,1); ViewDist = LookDir * walldist; HitActor = Trace(HitLocation, HitNormal, ViewSpot + ViewDist, ViewSpot, false); if ( HitActor == None ) return false; ViewDist = Normal(HitNormal Cross vect(0,0,1)) * walldist; if (FRand() < 0.5) ViewDist *= -1; Focus = None; if ( FastTrace(ViewSpot + ViewDist, ViewSpot) ) { FocalPoint = Pawn.Location + ViewDist; return true; } if ( FastTrace(ViewSpot - ViewDist, ViewSpot) ) { FocalPoint = Pawn.Location - ViewDist; return true; } FocalPoint = Pawn.Location - LookDir * 300; return true; } // check for line of sight to target deltatime from now. function bool CheckFutureSight(float deltatime) { local vector FutureLoc; if ( Target == None ) Target = Enemy; if ( Target == None ) return false; if ( Pawn.Acceleration == vect(0,0,0) ) FutureLoc = Pawn.Location; else FutureLoc = Pawn.Location + Pawn.GroundSpeed * Normal(Pawn.Acceleration) * deltaTime; if ( Pawn.Base != None ) FutureLoc += Pawn.Base.Velocity * deltaTime; //make sure won't run into something if ( !FastTrace(FutureLoc, Pawn.Location) && (Pawn.Physics != PHYS_Falling) ) return false; //check if can still see target if ( FastTrace(Target.Location + Target.Velocity * deltatime, FutureLoc) ) return true; return false; } function float AdjustAimError(float aimerror, float TargetDist, bool bDefendMelee, bool bInstantProj, bool bLeadTargetNow ) { if ( (Pawn(Target) != None) && (Pawn(Target).Visibility < 2) ) aimerror *= 2.5; // figure out the relative motion of the target across the bots view, and adjust aim error // based on magnitude of relative motion aimerror = aimerror * FMin(5,(12 - 11 * (Normal(Target.Location - Pawn.Location) Dot Normal((Target.Location + 1.2 * Target.Velocity) - (Pawn.Location + Pawn.Velocity))))); // if enemy is charging straight at bot with a melee weapon, improve aim if ( bDefendMelee ) aimerror *= 0.5; if ( Target.Velocity == vect(0,0,0) ) aimerror *= 0.6; // aiming improves over time if stopped if ( Stopped() && (Level.TimeSeconds > StopStartTime) ) { if ( (Skill+Accuracy) > 4 ) aimerror *= 0.9; aimerror *= FClamp((2 - 0.08 * FMin(skill,7) - FRand())/(Level.TimeSeconds - StopStartTime + 0.4),0.7,1.0); } // adjust aim error based on skill if ( !bDefendMelee ) aimerror *= (3.3 - 0.37 * (FClamp(skill+Accuracy,0,8.5) + 0.5 * FRand())); // Bots don't aim as well if recently hit, or if they or their target is flying through the air if ( ((skill < 7) || (FRand()<0.5)) && (Level.TimeSeconds - Pawn.LastPainTime < 0.2) ) aimerror *= 1.3; if ( (Pawn.Physics == PHYS_Falling) || (Target.Physics == PHYS_Falling) ) aimerror *= 1.6; // Bots don't aim as well at recently acquired targets (because they haven't had a chance to lock in to the target) if ( AcquireTime > Level.TimeSeconds - 0.5 - 0.6 * (7 - skill) ) { aimerror *= 1.5; if ( bInstantProj ) aimerror *= 1.5; } return (Rand(2 * aimerror) - aimerror); } /* AdjustAim() Returns a rotation which is the direction the bot should aim - after introducing the appropriate aiming error */ function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror) { local rotator FireRotation, TargetLook; local float FireDist, TargetDist, ProjSpeed; local actor HitActor; local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal; local int realYaw; local bool bDefendMelee, bClean, bLeadTargetNow; if ( FiredAmmunition.ProjectileClass != None ) projspeed = FiredAmmunition.ProjectileClass.default.speed; // make sure bot has a valid target if ( Target == None ) { Target = Enemy; if ( Target == None ) return Rotation; } FireSpot = Target.Location; TargetDist = VSize(Target.Location - Pawn.Location); // perfect aim at stationary objects if ( Pawn(Target) == None ) { if ( !FiredAmmunition.bTossed ) return rotator(Target.Location - projstart); else { FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true); SetRotation(Rotator(FireDir)); return Rotation; } } bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget; bDefendMelee = ( (Target == Enemy) && DefendMelee(TargetDist) ); aimerror = AdjustAimError(aimerror,TargetDist,bDefendMelee,FiredAmmunition.bInstantHit, bLeadTargetNow); // lead target with non instant hit projectiles if ( bLeadTargetNow ) { TargetVel = Target.Velocity; // hack guess at projecting falling velocity of target if ( Target.Physics == PHYS_Falling ) { if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z ) TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0); else TargetVel.Z = FMin(0, TargetVel.Z); } // more or less lead target (with some random variation) FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed; FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z); if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) ) { // don't always lead far away targets, especially if they are moving sideways with respect to the bot TargetLook = Target.Rotation; if ( Target.Physics == PHYS_Walking ) TargetLook.Pitch = 0; bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) ); } else // make sure that bot isn't leading into a wall bClean = FastTrace(FireSpot, ProjStart); if ( !bClean) { // reduce amount of leading if ( FRand() < 0.3 ) FireSpot = Target.Location; else FireSpot = 0.5 * (FireSpot + Target.Location); } } bClean = false; //so will fail first check unless shooting at feet if ( FiredAmmunition.bTrySplash && (Pawn(Target) != None) && ((Skill >=4) || bDefendMelee) && (((Target.Physics == PHYS_Falling) && (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z) && (bDefendMelee || (skill > 6.5 * FRand() - 0.5)))) ) { HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 6), FireSpot, false); bClean = (HitActor == None); if ( !bClean ) { FireSpot = HitLocation + vect(0,0,3); bClean = FastTrace(FireSpot, ProjStart); } else bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) ); } if ( !bClean ) { //try middle FireSpot.Z = Target.Location.Z; bClean = FastTrace(FireSpot, ProjStart); } if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid ) { FireSpot = LastSeenPos; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != None ) { bCanFire = false; FireSpot += 2 * Target.CollisionHeight * HitNormal; } bClean = true; } if( !bClean ) { // try head FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight; bClean = FastTrace(FireSpot, ProjStart); } if ( !bClean && (Target == Enemy) && bEnemyInfoValid ) { FireSpot = LastSeenPos; if ( Pawn.Location.Z >= LastSeenPos.Z ) FireSpot.Z -= 0.4 * Enemy.CollisionHeight; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != None ) { FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal; if ( Monster(Pawn).SplashDamage() && (Skill >= 4) ) { HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != None ) FireSpot += 2 * Enemy.CollisionHeight * HitNormal; } bCanFire = false; } } // adjust for toss distance if ( FiredAmmunition.bTossed ) FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true); else FireDir = FireSpot - ProjStart; FireRotation = Rotator(FireDir); realYaw = FireRotation.Yaw; InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation)); FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror); FireDir = vector(FireRotation); // avoid shooting into wall FireDist = FMin(VSize(FireSpot-ProjStart), 400); FireSpot = ProjStart + FireDist * FireDir; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != None ) { if ( HitNormal.Z < 0.7 ) { FireRotation.Yaw = SetFireYaw(realYaw - aimerror); FireDir = vector(FireRotation); FireSpot = ProjStart + FireDist * FireDir; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); } if ( HitActor != None ) { FireSpot += HitNormal * 2 * Target.CollisionHeight; if ( Skill >= 4 ) { HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if ( HitActor != None ) FireSpot += Target.CollisionHeight * HitNormal; } FireDir = Normal(FireSpot - ProjStart); FireRotation = rotator(FireDir); } } SetRotation(FireRotation); return FireRotation; } function ReceiveWarning(Pawn shooter, float projSpeed, vector FireDir) { local float enemyDist, DodgeSkill; local vector X,Y,Z, enemyDir; // AI controlled creatures may duck if not falling DodgeSkill = Skill + Monster(Pawn).DodgeSkillAdjust; if ( (Pawn.health <= 0) || (DodgeSkill < 4) || (Enemy == None) || !Monster(Pawn).bCanDodge || (Pawn.Physics == PHYS_Falling) || (Pawn.Physics == PHYS_Swimming) || (FRand() > 0.2 * DodgeSkill - 0.4) ) return; // and projectile time is long enough enemyDist = VSize(shooter.Location - Pawn.Location); if (enemyDist/projSpeed < 0.11 + 0.15 * FRand()) return; // only if tight FOV GetAxes(Pawn.Rotation,X,Y,Z); enemyDir = (shooter.Location - Pawn.Location)/enemyDist; if ((enemyDir Dot X) < 0.8) return; if ( (FireDir Dot Y) > 0 ) { Y *= -1; TryToDuck(Y, true); } else TryToDuck(Y, false); } function bool TryToDuck(vector duckDir, bool bReversed) { local vector HitLocation, HitNormal, Extent; local actor HitActor; local bool bSuccess, bDuckLeft; if ( Pawn.PhysicsVolume.bWaterVolume || (Pawn.PhysicsVolume.Gravity.Z > Pawn.PhysicsVolume.Default.Gravity.Z) ) return false; duckDir.Z = 0; bDuckLeft = !bReversed; Extent = Pawn.GetCollisionExtent(); HitActor = Trace(HitLocation, HitNormal, Pawn.Location + 240 * duckDir, Pawn.Location, false, Extent); bSuccess = ( (HitActor == None) || (VSize(HitLocation - Pawn.Location) > 150) ); if ( !bSuccess ) { bDuckLeft = !bDuckLeft; duckDir *= -1; HitActor = Trace(HitLocation, HitNormal, Pawn.Location + 240 * duckDir, Pawn.Location, false, Extent); bSuccess = ( (HitActor == None) || (VSize(HitLocation - Pawn.Location) > 150) ); } if ( !bSuccess ) return false; if ( HitActor == None ) HitLocation = Pawn.Location + 240 * duckDir; HitActor = Trace(HitLocation, HitNormal, HitLocation - MAXSTEPHEIGHT * vect(0,0,1), HitLocation, false, Extent); if (HitActor == None) return false; if ( bDuckLeft ) UnrealPawn(Pawn).CurrentDir = DCLICK_Left; else UnrealPawn(Pawn).CurrentDir = DCLICK_Right; UnrealPawn(Pawn).Dodge(UnrealPawn(Pawn).CurrentDir); return true; } function NotifyKilled(Controller Killer, Controller Killed, pawn KilledPawn) { if ( Killer == self ) Celebrate(); if ( (KilledPawn == Enemy) || (Killed != None && Killed.bIsPlayer && (Enemy == None)) ) { Enemy = None; FindNewEnemy(); } } function Actor FaceMoveTarget() { if ( MoveTarget != Enemy ) StopFiring(); return MoveTarget; } function bool ShouldStrafeTo(Actor WayPoint) { local NavigationPoint N; if ( Monster(Pawn).bAlwaysStrafe ) return true; if ( Skill + StrafingAbility < 3 ) return false; if ( WayPoint == Enemy ) { if ( Monster(Pawn).PreferMelee() ) return false; return ( Skill + StrafingAbility < 5 * FRand() - 1 ); } else if ( Pickup(WayPoint) == None ) { N = NavigationPoint(WayPoint); if ( (N == None) || N.bNeverUseStrafing ) return false; if ( N.FearCost > 200 ) return true; if ( N.bAlwaysUseStrafing && (FRand() < 0.8) ) return true; } if ( Pawn(WayPoint) != None ) return ( Skill + StrafingAbility < 5 * FRand() - 1 ); if ( Skill + StrafingAbility < 7 * FRand() - 1 ) return false; if ( Enemy == None ) return ( FRand() < 0.4 ); if ( EnemyVisible() ) return ( FRand() < 0.85 ); return ( FRand() < 0.6 ); } function Actor FaceActor(float StrafingModifier) { local float RelativeDir; bRecommendFastMove = false; if ( (Enemy == None) || (Level.TimeSeconds - LastSeenTime > 6 - StrafingModifier) ) return FaceMoveTarget(); if ( MoveTarget == Enemy ) return Enemy; if ( Level.TimeSeconds - LastSeenTime > 4 - StrafingModifier) return FaceMoveTarget(); if ( (Skill > 2.5) && (GameObject(MoveTarget) != None) ) return Enemy; RelativeDir = Normal(Enemy.Location - Pawn.Location - vect(0,0,1) * (Enemy.Location.Z - Pawn.Location.Z)) Dot Normal(MoveTarget.Location - Pawn.Location - vect(0,0,1) * (MoveTarget.Location.Z - Pawn.Location.Z)); if ( RelativeDir > 0.85 ) return Enemy; if ( (RelativeDir > 0.3) && (Bot(Enemy.Controller) != None) && (MoveTarget == Enemy.Controller.MoveTarget) ) return Enemy; if ( skill + StrafingAbility < 2 + FRand() ) return FaceMoveTarget(); if ( (RelativeDir < 0.3) || (Skill + StrafingAbility < (5 + StrafingModifier) * FRand()) || (0.4*RelativeDir + 0.8 < FRand()) ) return FaceMoveTarget(); return Enemy; } function WanderOrCamp(bool bMayCrouch) { GotoState('RestFormation'); } event float Desireability(Pickup P) { return -1; } function DamageAttitudeTo(Pawn Other, float Damage) { if ( (Pawn.health > 0) && (Damage > 0) && SetEnemy(Other,true) ) WhatToDoNext(5); } //********************************************************************************** // AI States //======================================================================================================= // No goal/no enemy states state NoGoal { } function bool Formation() { return false; } state RestFormation extends NoGoal { ignores EnemyNotVisible; function CancelCampFor(Controller C) { DirectedWander(Normal(Pawn.Location - C.Pawn.Location)); } function bool Formation() { return true; } function Timer() { SetCombatTimer(); enable('NotifyBump'); } function PickDestination() { if ( TestDirection(VRand(),Destination) ) return; TestDirection(VRand(),Destination); } function BeginState() { Enemy = None; Pawn.bCanJump = false; Pawn.bAvoidLedges = true; Pawn.bStopAtLedges = true; Pawn.SetWalking(true); MinHitWall += 0.15; } function EndState() { MonitoredPawn = None; MinHitWall -= 0.15; if ( Pawn != None ) { Pawn.bStopAtLedges = false; Pawn.bAvoidLedges = false; Pawn.SetWalking(false); if (Pawn.JumpZ > 0) Pawn.bCanJump = true; } } event MonitoredPawnAlert() { WhatToDoNext(6); } Begin: WaitForLanding(); Camping: Pawn.Acceleration = vect(0,0,0); Focus = None; FocalPoint = VRand(); NearWall(MINVIEWDIST); FinishRotation(); Sleep(3 + FRand()); Moving: WaitForLanding(); PickDestination(); WaitForAnim: if ( Monster(Pawn).bShotAnim ) { Sleep(0.5); Goto('WaitForAnim'); } MoveTo(Destination,,true); if ( Pawn.bCanFly && (Physics == PHYS_Walking) ) SetPhysics(PHYS_Flying); WhatToDoNext(8); Goto('Begin'); } function Celebrate() { Pawn.PlayVictoryAnimation(); } //======================================================================================================= // Move To Goal states state MoveToGoal { function Timer() { SetCombatTimer(); enable('NotifyBump'); } } state MoveToGoalNoEnemy extends MoveToGoal { } state MoveToGoalWithEnemy extends MoveToGoal { function Timer() { TimedFireWeaponAtEnemy(); } } state Roaming extends MoveToGoalNoEnemy { ignores EnemyNotVisible; function MayFall() { Pawn.bCanJump = ( (MoveTarget != None) && ((MoveTarget.Physics != PHYS_Falling) || !MoveTarget.IsA('Pickup')) ); } Begin: SwitchToBestWeapon(); WaitForLanding(); MoveToward(MoveTarget,FaceActor(1),,ShouldStrafeTo(MoveTarget)); DoneRoaming: WaitForLanding(); WhatToDoNext(12); if ( bSoaking ) SoakStop("STUCK IN ROAMING!"); } //======================================================================================================================= // Tactical Combat states function DoStakeOut() { GotoState('StakeOut'); } function DoCharge() { if ( Enemy.PhysicsVolume.bWaterVolume ) { if ( !Pawn.bCanSwim ) { DoTacticalMove(); return; } } else if ( !Pawn.bCanFly && !Pawn.bCanWalk ) { DoTacticalMove(); return; } GotoState('Charging'); } function DoTacticalMove() { GotoState('TacticalMove'); } /* DefendMelee() return true if defending against melee attack */ function bool DefendMelee(float Dist) { return ( (Enemy.Weapon != None) && Enemy.Weapon.bMeleeWeapon && (Dist < 1000) ); } state Charging extends MoveToGoalWithEnemy { ignores SeePlayer, HearNoise; /* MayFall() called by engine physics if walking and bCanJump, and is about to go off a ledge. Pawn has opportunity (by setting bCanJump to false) to avoid fall */ function MayFall() { if ( MoveTarget != Enemy ) return; Pawn.bCanJump = ActorReachable(Enemy); if ( !Pawn.bCanJump ) MoveTimer = -1.0; } function bool TryToDuck(vector duckDir, bool bReversed) { if ( FRand() < 0.6 ) return Global.TryToDuck(duckDir, bReversed); if ( MoveTarget == Enemy ) return TryStrafe(duckDir); } function bool StrafeFromDamage(float Damage, class DamageType, bool bFindDest) { local vector sideDir; if ( FRand() * Damage < 0.15 * CombatStyle * Pawn.Health ) return false; if ( !bFindDest ) return true; sideDir = Normal( Normal(Enemy.Location - Pawn.Location) Cross vect(0,0,1) ); if ( (Pawn.Velocity Dot sidedir) > 0 ) sidedir *= -1; return TryStrafe(sideDir); } function bool TryStrafe(vector sideDir) { local vector extent, HitLocation, HitNormal; local actor HitActor; Extent = Pawn.GetCollisionExtent(); HitActor = Trace(HitLocation, HitNormal, Pawn.Location + MINSTRAFEDIST * sideDir, Pawn.Location, false, Extent); if (HitActor != None) { sideDir *= -1; HitActor = Trace(HitLocation, HitNormal, Pawn.Location + MINSTRAFEDIST * sideDir, Pawn.Location, false, Extent); } if (HitActor != None) return false; if ( Pawn.Physics == PHYS_Walking ) { HitActor = Trace(HitLocation, HitNormal, Pawn.Location + MINSTRAFEDIST * sideDir - MAXSTEPHEIGHT * vect(0,0,1), Pawn.Location + MINSTRAFEDIST * sideDir, false, Extent); if ( HitActor == None ) return false; } Destination = Pawn.Location + 2 * MINSTRAFEDIST * sideDir; GotoState('TacticalMove', 'DoStrafeMove'); return true; } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class damageType, vector Momentum) { local float pick; local vector sideDir; local bool bWasOnGround; Super.NotifyTakeHit(InstigatedBy,HitLocation, Damage,DamageType,Momentum); bWasOnGround = (Pawn.Physics == PHYS_Walking); if ( Pawn.health <= 0 ) return; if ( StrafeFromDamage(damage, damageType, true) ) return; else if ( bWasOnGround && (MoveTarget == Enemy) && (Pawn.Physics == PHYS_Falling) ) //weave { pick = 1.0; if ( bStrafeDir ) pick = -1.0; sideDir = Normal( Normal(Enemy.Location - Pawn.Location) Cross vect(0,0,1) ); sideDir.Z = 0; Pawn.Velocity += pick * Pawn.GroundSpeed * 0.7 * sideDir; if ( FRand() < 0.2 ) bStrafeDir = !bStrafeDir; } } event bool NotifyBump(actor Other) { if ( Other == Enemy ) { DoRangedAttackOn(Enemy); return false; } return Global.NotifyBump(Other); } function Timer() { enable('NotifyBump'); Target = Enemy; TimedFireWeaponAtEnemy(); } function EnemyNotVisible() { WhatToDoNext(15); } function EndState() { if ( (Pawn != None) && Pawn.JumpZ > 0 ) Pawn.bCanJump = true; } Begin: if (Pawn.Physics == PHYS_Falling) { Focus = Enemy; Destination = Enemy.Location; WaitForLanding(); } if ( Enemy == None ) WhatToDoNext(16); WaitForAnim: if ( Monster(Pawn).bShotAnim ) { Sleep(0.35); Goto('WaitForAnim'); } if ( !FindBestPathToward(Enemy, false,true) ) GotoState('TacticalMove'); Moving: MoveToward(MoveTarget,FaceActor(1),,ShouldStrafeTo(MoveTarget)); WhatToDoNext(17); if ( bSoaking ) SoakStop("STUCK IN CHARGING!"); } function bool IsStrafing() { return false; } state TacticalMove { ignores SeePlayer, HearNoise; function bool IsStrafing() { return true; } function ReceiveWarning(Pawn shooter, float projSpeed, vector FireDir) { if ( bCanFire && (FRand() < 0.4) ) return; Super.ReceiveWarning(shooter, projSpeed, FireDir); } function SetFall() { Pawn.Acceleration = vect(0,0,0); Destination = Pawn.Location; Global.SetFall(); } function bool NotifyHitWall(vector HitNormal, actor Wall) { if (Pawn.Physics == PHYS_Falling) return false; if ( Enemy == None ) { WhatToDoNext(18); return false; } if ( bChangeDir || (FRand() < 0.5) || (((Enemy.Location - Pawn.Location) Dot HitNormal) < 0) ) { Focus = Enemy; WhatToDoNext(19); } else { bChangeDir = true; Destination = Pawn.Location - HitNormal * FRand() * 500; } return true; } function Timer() { enable('NotifyBump'); Target = Enemy; if ( Enemy != None ) TimedFireWeaponAtEnemy(); else SetCombatTimer(); } function EnemyNotVisible() { StopFiring(); if ( FastTrace(Enemy.Location, LastSeeingPos) ) GotoState('TacticalMove','RecoverEnemy'); else WhatToDoNext(20); Disable('EnemyNotVisible'); } function PawnIsInPain(PhysicsVolume PainVolume) { Destination = Pawn.Location - MINSTRAFEDIST * Normal(Pawn.Velocity); } /* PickDestination() Choose a destination for the tactical move, based on aggressiveness and the tactical situation. Make sure destination is reachable */ function PickDestination() { local vector pickdir, enemydir, enemyPart, Y; local float strafeSize; if ( Pawn == None ) { warn(self$" Tactical move pick destination with no pawn"); return; } bChangeDir = false; if ( Pawn.PhysicsVolume.bWaterVolume && !Pawn.bCanSwim && Pawn.bCanFly) { Destination = Pawn.Location + 75 * (VRand() + vect(0,0,1)); Destination.Z += 100; return; } enemydir = Normal(Enemy.Location - Pawn.Location); Y = (enemydir Cross vect(0,0,1)); if ( Pawn.Physics == PHYS_Walking ) { Y.Z = 0; enemydir.Z = 0; } else enemydir.Z = FMax(0,enemydir.Z); strafeSize = FClamp((2 * FRand() - 0.65),-0.7,0.7); strafeSize = FMax(0.4 * FRand() - 0.2,strafeSize); enemyPart = enemydir * strafeSize; if ( Pawn.bCanFly ) { if ( Pawn.Location.Z - Enemy.Location.Z < 1000 ) enemyPart = enemyPart + FRand() * vect(0,0,1); else enemyPart = enemyPart - FRand() * vect(0,0,0.7); } strafeSize = FMax(0.0, 1 - Abs(strafeSize)); pickdir = strafeSize * Y; if ( bStrafeDir ) pickdir *= -1; bStrafeDir = !bStrafeDir; if ( EngageDirection(enemyPart + pickdir, false) ) return; if ( EngageDirection(enemyPart - pickdir,false) ) return; bForcedDirection = true; StartTacticalTime = Level.TimeSeconds; EngageDirection(EnemyPart + PickDir, true); } function bool EngageDirection(vector StrafeDir, bool bForced) { local actor HitActor; local vector HitLocation, collspec, MinDest, HitNormal; // successfully engage direction if can trace out and down MinDest = Pawn.Location + MINSTRAFEDIST * StrafeDir; if ( !bForced ) { collSpec = Pawn.GetCollisionExtent(); collSpec.Z = FMax(6, Pawn.CollisionHeight - Pawn.CollisionRadius); HitActor = Trace(HitLocation, HitNormal, MinDest, Pawn.Location, false, collSpec); if ( HitActor != None ) return false; if ( Pawn.Physics == PHYS_Walking ) { collSpec.X = FMin(14, 0.5 * Pawn.CollisionRadius); collSpec.Y = collSpec.X; HitActor = Trace(HitLocation, HitNormal, minDest - (Pawn.CollisionRadius + MAXSTEPHEIGHT) * vect(0,0,1), minDest, false, collSpec); if ( HitActor == None ) { HitNormal = -1 * StrafeDir; return false; } } } Destination = MinDest + StrafeDir * (0.5 * MINSTRAFEDIST + FMin(VSize(Enemy.Location - Pawn.Location), MINSTRAFEDIST * (FRand() + FRand()))); return true; } function BeginState() { bForcedDirection = false; if ( Skill < 4 ) Pawn.MaxDesiredSpeed = 0.4 + 0.08 * skill; MinHitWall += 0.15; Pawn.bAvoidLedges = true; Pawn.bStopAtLedges = true; Pawn.bCanJump = false; bAdjustFromWalls = false; } function EndState() { bAdjustFromWalls = true; if ( Pawn == None ) return; SetMaxDesiredSpeed(); Pawn.bAvoidLedges = false; Pawn.bStopAtLedges = false; MinHitWall -= 0.15; if (Pawn.JumpZ > 0) Pawn.bCanJump = true; } TacticalTick: Sleep(0.02); Begin: if (Pawn.Physics == PHYS_Falling) { Focus = Enemy; Destination = Enemy.Location; WaitForLanding(); } PickDestination(); DoMove: if ( !Pawn.bCanStrafe ) { StopFiring(); WaitForAnim: if ( Monster(Pawn).bShotAnim ) { Sleep(0.5); Goto('WaitForAnim'); } MoveTo(Destination); } else { DoStrafeMove: MoveTo(Destination, Enemy); } if ( bForcedDirection && (Level.TimeSeconds - StartTacticalTime < 0.2) ) { if ( Skill > 2 + 3 * FRand() ) { bMustCharge = true; WhatToDoNext(51); } GoalString = "RangedAttack from failed tactical"; DoRangedAttackOn(Enemy); } if ( (Enemy == None) || EnemyVisible() || !FastTrace(Enemy.Location, LastSeeingPos) || Monster(Pawn).PreferMelee() || !Pawn.bCanStrafe ) Goto('FinishedStrafe'); //CheckIfShouldCrouch(LastSeeingPos,Enemy.Location, 0.5); RecoverEnemy: GoalString = "Recover Enemy"; HidingSpot = Pawn.Location; StopFiring(); Sleep(0.1 + 0.2 * FRand()); Destination = LastSeeingPos + 4 * Pawn.CollisionRadius * Normal(LastSeeingPos - Pawn.Location); MoveTo(Destination, Enemy); if ( FireWeaponAt(Enemy) ) { Pawn.Acceleration = vect(0,0,0); if ( Monster(Pawn).SplashDamage() ) { StopFiring(); Sleep(0.05); } else Sleep(0.1 + 0.3 * FRand() + 0.06 * (7 - FMin(7,Skill))); if ( FRand() > 0.5 ) { Enable('EnemyNotVisible'); Destination = HidingSpot + 4 * Pawn.CollisionRadius * Normal(HidingSpot - Pawn.Location); Goto('DoMove'); } } FinishedStrafe: WhatToDoNext(21); if ( bSoaking ) SoakStop("STUCK IN TACTICAL MOVE!"); } function bool IsHunting() { return false; } state Hunting extends MoveToGoalWithEnemy { ignores EnemyNotVisible; /* MayFall() called by] engine physics if walking and bCanJump, and is about to go off a ledge. Pawn has opportunity (by setting bCanJump to false) to avoid fall */ function bool IsHunting() { return true; } function MayFall() { Pawn.bCanJump = ( (MoveTarget == None) || (MoveTarget.Physics != PHYS_Falling) || !MoveTarget.IsA('Pickup') ); } function SeePlayer(Pawn SeenPlayer) { if ( SeenPlayer == Enemy ) { if ( Level.timeseconds - ChallengeTime > 7 ) { ChallengeTime = Level.TimeSeconds; Monster(Pawn).PlayChallengeSound(); } VisibleEnemy = Enemy; EnemyVisibilityTime = Level.TimeSeconds; bEnemyIsVisible = true; Focus = Enemy; WhatToDoNext(22); } else Global.SeePlayer(SeenPlayer); } function Timer() { SetCombatTimer(); StopFiring(); } function PickDestination() { local vector nextSpot, ViewSpot,Dir; local float posZ; local bool bCanSeeLastSeen; // If no enemy, or I should see him but don't, then give up if ( (Enemy == None) || (Enemy.Health <= 0) ) { Enemy = None; WhatToDoNext(23); return; } if ( Pawn.JumpZ > 0 ) Pawn.bCanJump = true; if ( ActorReachable(Enemy) ) { Destination = Enemy.Location; MoveTarget = None; return; } ViewSpot = Pawn.Location + Pawn.BaseEyeHeight * vect(0,0,1); bCanSeeLastSeen = bEnemyInfoValid && FastTrace(LastSeenPos, ViewSpot); if ( FindBestPathToward(Enemy, true,true) ) return; if ( bSoaking && (Physics != PHYS_Falling) ) SoakStop("COULDN'T FIND PATH TO ENEMY "$Enemy); MoveTarget = None; if ( !bEnemyInfoValid ) { Enemy = None; WhatToDoNext(26); return; } Destination = LastSeeingPos; bEnemyInfoValid = false; if ( FastTrace(Enemy.Location, ViewSpot) && VSize(Pawn.Location - Destination) > Pawn.CollisionRadius ) { SeePlayer(Enemy); return; } posZ = LastSeenPos.Z + Pawn.CollisionHeight - Enemy.CollisionHeight; nextSpot = LastSeenPos - Normal(Enemy.Velocity) * Pawn.CollisionRadius; nextSpot.Z = posZ; if ( FastTrace(nextSpot, ViewSpot) ) Destination = nextSpot; else if ( bCanSeeLastSeen ) { Dir = Pawn.Location - LastSeenPos; Dir.Z = 0; if ( VSize(Dir) < Pawn.CollisionRadius ) { GoalString = "Stakeout 3 from hunt"; GotoState('StakeOut'); return; } Destination = LastSeenPos; } else { Destination = LastSeenPos; if ( !FastTrace(LastSeenPos, ViewSpot) ) { // check if could adjust and see it if ( PickWallAdjust(Normal(LastSeenPos - ViewSpot)) || FindViewSpot() ) { if ( Pawn.Physics == PHYS_Falling ) SetFall(); else GotoState('Hunting', 'AdjustFromWall'); } else { GoalString = "Stakeout 2 from hunt"; GotoState('StakeOut'); return; } } } } function bool FindViewSpot() { local vector X,Y,Z; GetAxes(Rotation,X,Y,Z); // try left and right if ( FastTrace(Enemy.Location, Pawn.Location + 2 * Y * Pawn.CollisionRadius) ) { Destination = Pawn.Location + 2.5 * Y * Pawn.CollisionRadius; return true; } if ( FastTrace(Enemy.Location, Pawn.Location - 2 * Y * Pawn.CollisionRadius) ) { Destination = Pawn.Location - 2.5 * Y * Pawn.CollisionRadius; return true; } if ( FRand() < 0.5 ) Destination = Pawn.Location - 2.5 * Y * Pawn.CollisionRadius; else Destination = Pawn.Location - 2.5 * Y * Pawn.CollisionRadius; return true; } function EndState() { if ( (Pawn != None) && (Pawn.JumpZ > 0) ) Pawn.bCanJump = true; } AdjustFromWall: MoveTo(Destination, MoveTarget); Begin: WaitForLanding(); if ( CanSee(Enemy) ) SeePlayer(Enemy); WaitForAnim: if ( Monster(Pawn).bShotAnim ) { Sleep(0.35); Goto('WaitForAnim'); } PickDestination(); if ( Level.timeseconds - ChallengeTime > 10 ) { ChallengeTime = Level.TimeSeconds; Monster(Pawn).PlayChallengeSound(); } SpecialNavig: if (MoveTarget == None) MoveTo(Destination); else MoveToward(MoveTarget,FaceActor(10),,(FRand() < 0.75) && ShouldStrafeTo(MoveTarget)); WhatToDoNext(27); if ( bSoaking ) SoakStop("STUCK IN HUNTING!"); } function bool Stopped() { return bPreparingMove; } state StakeOut { ignores EnemyNotVisible; function bool CanAttack(Actor Other) { return true; } function bool Stopped() { return true; } event SeePlayer(Pawn SeenPlayer) { if ( SeenPlayer == Enemy ) { VisibleEnemy = Enemy; EnemyVisibilityTime = Level.TimeSeconds; bEnemyIsVisible = true; if ( FRand() < 0.5 ) { Focus = Enemy; FireWeaponAt(Enemy); } WhatToDoNext(28); } else if ( SetEnemy(SeenPlayer) ) { if ( Enemy == SeenPlayer ) { VisibleEnemy = Enemy; EnemyVisibilityTime = Level.TimeSeconds; bEnemyIsVisible = true; } WhatToDoNext(29); } } /* DoStakeOut() called by ChooseAttackMode - if called in this state, means stake out twice in a row */ function DoStakeOut() { SetFocus(); if ( (FRand() < 0.3) || !FastTrace(FocalPoint + vect(0,0,0.9) * Enemy.CollisionHeight, Pawn.Location + vect(0,0,0.8) * Pawn.CollisionHeight) ) FindNewStakeOutDir(); GotoState('StakeOut','Begin'); } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class damageType, vector Momentum) { Super.NotifyTakeHit(InstigatedBy,HitLocation, Damage,DamageType,Momentum); if ( (Pawn.Health > 0) && (Damage > 0) ) { if ( InstigatedBy == Enemy ) AcquireTime = Level.TimeSeconds; WhatToDoNext(30); } } function Timer() { enable('NotifyBump'); SetCombatTimer(); } function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror) { local vector FireSpot; local actor HitActor; local vector HitLocation, HitNormal; FireSpot = FocalPoint; HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false); if( HitActor != None ) { FireSpot += 2 * Enemy.CollisionHeight * HitNormal; if ( !FastTrace(FireSpot, ProjStart) ) { FireSpot = FocalPoint; StopFiring(); } } SetRotation(Rotator(FireSpot - ProjStart)); return Rotation; } function FindNewStakeOutDir() { local NavigationPoint N, Best; local vector Dir, EnemyDir; local float Dist, BestVal, Val; EnemyDir = Normal(Enemy.Location - Pawn.Location); for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint ) { Dir = N.Location - Pawn.Location; Dist = VSize(Dir); if ( (Dist < MAXSTAKEOUTDIST) && (Dist > MINSTRAFEDIST) ) { Val = (EnemyDir Dot Dir/Dist); if ( Level.Game.bTeamgame ) Val += FRand(); if ( (Val > BestVal) && LineOfSightTo(N) ) { BestVal = Val; Best = N; } } } if ( Best != None ) FocalPoint = Best.Location + 0.5 * Pawn.CollisionHeight * vect(0,0,1); } function SetFocus() { if ( bEnemyInfoValid ) FocalPoint = LastSeenPos; else FocalPoint = Enemy.Location; } function BeginState() { StopStartTime = Level.TimeSeconds; Pawn.Acceleration = vect(0,0,0); Pawn.bCanJump = false; SetFocus(); if ( !bEnemyInfoValid || !ClearShot(FocalPoint,false) || ((Level.TimeSeconds - LastSeenTime > 6) && (FRand() < 0.5)) ) FindNewStakeOutDir(); } function EndState() { if ( (Pawn != None) && (Pawn.JumpZ > 0) ) Pawn.bCanJump = true; } Begin: Pawn.Acceleration = vect(0,0,0); Focus = None; CheckIfShouldCrouch(Pawn.Location,FocalPoint, 1); FinishRotation(); if ( Monster(Pawn).HasRangedAttack() && (FRand() < 0.5) && (VSize(Enemy.Location - FocalPoint) < 150) && (Level.TimeSeconds - LastSeenTime < 4) && ClearShot(FocalPoint,true) ) { FireWeaponAt(Enemy); } else StopFiring(); Sleep(1 + FRand()); // check if uncrouching would help if ( Pawn.bIsCrouched && !FastTrace(FocalPoint, Pawn.Location + Pawn.EyeHeight * vect(0,0,1)) && FastTrace(FocalPoint, Pawn.Location + (Pawn.Default.EyeHeight + Pawn.Default.CollisionHeight - Pawn.CollisionHeight) * vect(0,0,1)) ) { Pawn.bWantsToCrouch = false; Sleep(0.15 + 0.05 * (1 + FRand()) * (10 - skill)); } WhatToDoNext(31); if ( bSoaking ) SoakStop("STUCK IN STAKEOUT!"); } state RangedAttack { ignores SeePlayer, HearNoise, Bump; function bool Stopped() { return true; } function CancelCampFor(Controller C) { DoTacticalMove(); } function StopFiring() { Global.StopFiring(); if ( bHasFired ) { bHasFired = false; WhatToDoNext(32); } } function EnemyNotVisible() { //let attack animation complete WhatToDoNext(33); } function Timer() { if ( Monster(Pawn).PreferMelee() ) { SetCombatTimer(); StopFiring(); WhatToDoNext(34); } else TimedFireWeaponAtEnemy(); } function DoRangedAttackOn(Actor A) { Target = A; GotoState('RangedAttack'); } function BeginState() { StopStartTime = Level.TimeSeconds; bHasFired = false; Pawn.Acceleration = vect(0,0,0); //stop if ( Target == None ) Target = Enemy; if ( Target == None ) log(GetHumanReadableName()$" no target in ranged attack"); } Begin: bHasFired = false; GoalString = "Ranged attack"; Focus = Target; Sleep(0.0); if ( Enemy != None ) CheckIfShouldCrouch(Pawn.Location,Enemy.Location, 1); if ( NeedToTurn(Target.Location) ) { Focus = Target; FinishRotation(); } bHasFired = true; if ( Target == Enemy ) TimedFireWeaponAtEnemy(); else FireWeaponAt(Target); Sleep(0.1); if ( Monster(Pawn).PreferMelee() || (Target == None) || (Target != Enemy) || Monster(Pawn).bBoss ) WhatToDoNext(35); if ( Enemy != None ) CheckIfShouldCrouch(Pawn.Location,Enemy.Location, 1); Focus = Target; Sleep(FMax(Monster(Pawn).RangedAttackTime(),0.2 + (0.5 + 0.5 * FRand()) * 0.4 * (7 - Skill))); WhatToDoNext(36); if ( bSoaking ) SoakStop("STUCK IN RANGEDATTACK!"); } function GameHasEnded() { } State WaitForAnim { ignores SeePlayer, HearNoise, KilledBy, NotifyBump, HitWall, NotifyPhysicsVolumeChange, NotifyHeadVolumeChange, Falling, TakeDamage, ReceiveWarning; event AnimEnd(int Channel) { Pawn.AnimEnd(Channel); if ( !Monster(Pawn).bShotAnim ) WhatToDoNext(99); } } State WaitingForLanding { function bool DoWaitForLanding() { if ( bJustLanded ) return false; BeginState(); return true; } function bool NotifyLanded(vector HitNormal) { bJustLanded = true; Super.NotifyLanded(HitNormal); WhatToDoNext(50); return false; } function Timer() { if ( Focus == Enemy ) TimedFireWeaponAtEnemy(); else SetCombatTimer(); } function BeginState() { bJustLanded = false; if ( (MoveTarget != None) && ((Enemy == None) ||(Focus != Enemy)) ) FaceActor(1.5); if ( (Enemy == None) || (Focus != Enemy) ) StopFiring(); } } QQclass Monster extends xPawn; var bool bMeleeFighter; var bool bShotAnim; var bool bCanDodge; var bool bVictoryNext; var bool bTryToWalk; var bool bBoss; var bool bAlwaysStrafe; var float DodgeSkillAdjust; var sound HitSound[4]; var sound DeathSound[4]; var sound ChallengeSound[4]; var sound FireSound; var class AmmunitionClass; var Ammunition MyAmmo; var int ScoringValue; var FireProperties SavedFireProperties; // combat interface function RangedAttack(Actor A); function bool CanAttack(Actor A); function StopFiring(); function bool SplashDamage(); function float GetDamageRadius(); function bool RecommendSplashDamage(); simulated function TurnOff() { if ( Health > 0 ) bVictoryNext = true; } simulated function bool ForceDefaultCharacter() { return false; // never replace monsters w/ default character } function bool IsHeadShot(vector loc, vector ray, float AdditionalScale) { local vector HeadLoc, B, M, diff; local float t, DotMM, Distance; if ( HeadBone != '' ) return super.IsHeadShot(Loc,Ray,AdditionalScale); HeadRadius = 0.25 * CollisionHeight; HeadLoc = Location + CollisionHeight * vect(0,0,0.5); // Express snipe trace line in terms of B + tM B = loc; M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius); // Find Point-Line Squared Distance diff = HeadLoc - B; t = M Dot diff; if (t > 0) { DotMM = M dot M; if (t < DotMM) { t = t / DotMM; diff = diff - (t * M); } else { t = 1; diff -= M; } } else t = 0; Distance = Sqrt(diff Dot diff); return (Distance < (HeadRadius * HeadScale * AdditionalScale)); } function Fire( optional float F ) { local Actor BestTarget; local float bestAim, bestDist; local vector FireDir, X,Y,Z; bestAim = 0.90; GetAxes(Controller.Rotation,X,Y,Z); FireDir = X; BestTarget = Controller.PickTarget(bestAim, bestDist, FireDir, GetFireStart(X,Y,Z), 6000); RangedAttack(BestTarget); } function bool PreferMelee() { return bMeleeFighter; } function bool HasRangedAttack(); function float RangedAttackTime(); function vector GetFireStart(vector X, vector Y, vector Z) { return Location + 0.5 * CollisionRadius * (X+Y); } function FireProjectile() { local vector FireStart,X,Y,Z; if ( Controller != None ) { GetAxes(Rotation,X,Y,Z); FireStart = GetFireStart(X,Y,Z); if ( !SavedFireProperties.bInitialized ) { SavedFireProperties.AmmoClass = MyAmmo.Class; SavedFireProperties.ProjectileClass = MyAmmo.ProjectileClass; SavedFireProperties.WarnTargetPct = MyAmmo.WarnTargetPct; SavedFireProperties.MaxRange = MyAmmo.MaxRange; SavedFireProperties.bTossed = MyAmmo.bTossed; SavedFireProperties.bTrySplash = MyAmmo.bTrySplash; SavedFireProperties.bLeadTarget = MyAmmo.bLeadTarget; SavedFireProperties.bInstantHit = MyAmmo.bInstantHit; SavedFireProperties.bInitialized = true; } Spawn(MyAmmo.ProjectileClass,,,FireStart,Controller.AdjustAim(SavedFireProperties,FireStart,600)); PlaySound(FireSound,SLOT_Interact); } } event PostBeginPlay() { Super.PostBeginPlay(); if ( (ControllerClass != None) && (Controller == None) ) Controller = spawn(ControllerClass); if ( Controller != None ) { Controller.Possess(self); MyAmmo = spawn(AmmunitionClass); } } simulated function SpawnGiblet( class GibClass, Vector Location, Rotator Rotation, float GibPerterbation ) { local Gib Giblet; local Vector Direction, Dummy; if( (GibClass == None) || class'GameInfo'.static.UseLowGore() ) return; Instigator = self; Giblet = Spawn( GibClass,,, Location, Rotation ); if( Giblet == None ) return; Giblet.SetDrawScale(Giblet.DrawScale * (CollisionRadius*CollisionHeight)/1100); // 1100 = 25 * 44 GibPerterbation *= 32768.0; Rotation.Pitch += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation; Rotation.Yaw += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation; Rotation.Roll += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation; GetAxes( Rotation, Dummy, Dummy, Direction ); Giblet.Velocity = Velocity + Normal(Direction) * 512.0; } function LandThump() { // animation notify - play sound if actually landed, and animation also shows it if ( Physics == PHYS_None) PlaySound(sound'Thump'); } function bool SameSpeciesAs(Pawn P) { return ( (Monster(P) != None) && (ClassIsChildOf(Class,P.Class) || ClassIsChildOf(P.Class,Class)) ); } simulated function AssignInitialPose() { TweenAnim(MovementAnims[0],0.0); } function PlayChallengeSound() { PlaySound(ChallengeSound[Rand(4)],SLOT_Talk); } function Destroyed() { if ( MyAmmo != None ) MyAmmo.Destroy(); Super.Destroyed(); } function PlayVictory(); simulated function AnimEnd(int Channel) { AnimAction = ''; if ( bVictoryNext && (Physics != PHYS_Falling) ) { bVictoryNext = false; PlayVictory(); } else if ( bShotAnim ) { bShotAnim = false; Controller.bPreparingMove = false; } Super.AnimEnd(Channel); } function SetMovementPhysics() { SetPhysics(PHYS_Falling); } function bool IsPlayerPawn() { return true; // so can use movers } function PlayTakeHit(vector HitLocation, int Damage, class DamageType) { PlayDirectionalHit(HitLocation); if( Level.TimeSeconds - LastPainSound < MinTimeBetweenPainSounds ) return; LastPainSound = Level.TimeSeconds; PlaySound(HitSound[Rand(4)], SLOT_Pain,2*TransientSoundVolume,,400); } simulated function PlayDying(class DamageType, vector HitLoc) { AmbientSound = None; bCanTeleport = false; bReplicateMovement = false; bTearOff = true; bPlayedDeath = true; LifeSpan = RagdollLifeSpan; GotoState('Dying'); Velocity += TearOffMomentum; BaseEyeHeight = Default.BaseEyeHeight; SetInvisibility(0.0); PlayDirectionalDeath(HitLoc); SetPhysics(PHYS_Falling); } function PlayDyingSound() { if ( bGibbed ) { PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.5*TransientSoundVolume,true,500); return; } PlaySound(DeathSound[Rand(4)], SLOT_Pain,2.5*TransientSoundVolume, true,500); } function bool MeleeDamageTarget(int hitdamage, vector pushdir) { local vector HitLocation, HitNormal; local actor HitActor; // check if still in melee range If ( (Controller.target != None) && (VSize(Controller.Target.Location - Location) <= MeleeRange * 1.4 + Controller.Target.CollisionRadius + CollisionRadius) && ((Physics == PHYS_Flying) || (Physics == PHYS_Swimming) || (Abs(Location.Z - Controller.Target.Location.Z) <= FMax(CollisionHeight, Controller.Target.CollisionHeight) + 0.5 * FMin(CollisionHeight, Controller.Target.CollisionHeight))) ) { HitActor = Trace(HitLocation, HitNormal, Controller.Target.Location, Location, false); if ( HitActor != None ) return false; Controller.Target.TakeDamage(hitdamage, self,HitLocation, pushdir, class'MeleeDamage'); return true; } return false; } function PlayVictoryAnimation() { bVictoryNext = true; } simulated event SetAnimAction(name NewAction) { if ( !bWaitForAnim || (Level.NetMode == NM_Client) ) { AnimAction = NewAction; if ( PlayAnim(AnimAction,,0.1) ) { if ( Physics != PHYS_None ) bWaitForAnim = true; } } } function CreateGib( Name boneName, class DamageType, Rotator r ) { if ( class'GameInfo'.static.UseLowGore() ) return; HitFX[HitFxTicker].bone = boneName; HitFX[HitFxTicker].damtype = DamageType; HitFX[HitFxTicker].bSever = true; HitFX[HitFxTicker].rotDir = r; HitFxTicker = HitFxTicker + 1; if( HitFxTicker > ArrayCount(HitFX)-1 ) HitFxTicker = 0; } simulated function ProcessHitFX() { local float GibPerterbation; if( (Level.NetMode == NM_DedicatedServer) || class'GameInfo'.static.UseLowGore() ) return; for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) ) { if( HitFX[SimHitFxTicker].damtype == None ) continue; if( HitFX[SimHitFxTicker].bSever ) { GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation; switch( HitFX[SimHitFxTicker].bone ) { case 'lthigh': case 'rthigh': SpawnGiblet( GetGibClass(EGT_Calf), Location - CollisionHeight * vect(0,0,0.5), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); SpawnGiblet( GetGibClass(EGT_Calf), Location - CollisionHeight * vect(0,0,0.5), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); GibCountCalf -= 2; break; case 'rfarm': case 'lfarm': SpawnGiblet( GetGibClass(EGT_UpperArm), Location + CollisionHeight * vect(0,0,0.5), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); SpawnGiblet( GetGibClass(EGT_Forearm), Location + CollisionHeight * vect(0,0,0.5), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); GibCountForearm--; GibCountUpperArm--; break; case 'head': SpawnGiblet( GetGibClass(EGT_Head), Location + CollisionHeight * vect(0,0,0.8), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); GibCountTorso--; break; case 'spine': case 'none': SpawnGiblet( GetGibClass(EGT_Torso), Location, HitFX[SimHitFxTicker].rotDir, GibPerterbation ); GibCountTorso--; bGibbed = true; while( GibCountHead-- > 0 ) SpawnGiblet( GetGibClass(EGT_Head), Location + CollisionHeight * vect(0,0,0.8), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); while( GibCountForearm-- > 0 ) SpawnGiblet( GetGibClass(EGT_UpperArm), Location + CollisionHeight * vect(0,0,0.5), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); while( GibCountUpperArm-- > 0 ) SpawnGiblet( GetGibClass(EGT_Forearm), Location + CollisionHeight * vect(0,0,0.5), HitFX[SimHitFxTicker].rotDir, GibPerterbation ); bHidden = true; break; } } } } State Dying { ignores AnimEnd, Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer; function Landed(vector HitNormal) { SetPhysics(PHYS_None); if ( !IsAnimating(0) ) LandThump(); Super.Landed(HitNormal); } simulated function Timer() { if ( !PlayerCanSeeMe() ) Destroy(); else if ( LifeSpan <= DeResTime && bDeRes == false ) StartDeRes(); else SetTimer(1.0, false); } } simulated function StartDeRes() { if( Level.NetMode == NM_DedicatedServer ) return; AmbientGlow=254; MaxLights=0; Skins[0]=DeResMat0; Skins[1]=DeResMat1; // Turn off collision when we de-res (avoids rockets etc. hitting corpse!) SetCollision(false, false, false); // Remove/disallow projectors Projectors.Remove(0, Projectors.Length); bAcceptsProjectors = false; // Remove shadow if(PlayerShadow != None) PlayerShadow.bShadowActive = false; // Remove flames RemoveFlamingEffects(); // Turn off any overlays SetOverlayMaterial(None, 0.0f, true); bDeRes = true; } QVXIkX//?X/9?&-vTL>W '?CG//9?& GQ6class MeleeDamage extends DamageType abstract; Q[%//============================================================================= // Manta. //============================================================================= class Manta extends Monster; var bool bStinging; simulated function PostBeginPlay() { Super.PostBeginPlay(); PlayAnim('Fly'); } function SetMovementPhysics() { SetPhysics(PHYS_Flying); PlayAnim('Fly'); } function WingBeat() { PlaySound(sound'fly1m', SLOT_Interact); } simulated function AnimEnd(int Channel) { local name Anim; local float frame,rate; local vector AccelDir; if ( bShotAnim ) bShotAnim = false; if ( bVictoryNext && (Physics != PHYS_Falling) ) { bVictoryNext = false; PlayVictory(); return; } GetAnimParams(0, Anim,frame,rate); if ( Anim != 'Fly' ) TweenAnim('Fly',0.4); else if ( (frame > 0.5) && (FRand() < 0.35) ) { AccelDir = Normal(Acceleration); if ( AccelDir.Z > 0.5 ) PlayAnim('Fly'); else TweenAnim('Fly',0.8 + 2*FRand()+FRand()); } else PlayAnim('Fly'); } simulated function PlayDirectionalDeath(Vector HitLoc) { PlayAnim('Death'); } simulated function PlayDirectionalHit(Vector HitLoc) { TweenAnim('TakeHit', 0.05); } function PlayVictory() { SetAnimAction('Whip'); } function RangedAttack(Actor A) { if ( bShotAnim ) return; if ( Location.Z - A.Location.Z + A.CollisionHeight <= 0 ) return; if ( VSize(A.Location - Location) > MeleeRange + CollisionRadius + A.CollisionRadius - FMax(0, 0.7 * A.Velocity Dot Normal(A.Location - Location)) ) return; bShotAnim = true; Acceleration = AccelRate * Normal(A.Location - Location + vect(0,0,0.8) * A.CollisionHeight); Enable('Bump'); bStinging = true; if (FRand() < 0.5) { SetAnimAction('Sting'); PlaySound(sound'whip1m', SLOT_Interact); } else { SetAnimAction('Whip'); PlaySound(sound'sting1m', SLOT_Interact); } } singular function Bump(actor Other) { local name Anim; local float frame,rate; if ( bShotAnim && bStinging ) { bStinging = false; GetAnimParams(0, Anim,frame,rate); if ( (Anim == 'Whip') || (Anim == 'Sting') ) MeleeDamageTarget(18, (20000.0 * Normal(Controller.Target.Location - Location))); Velocity *= -0.5; Acceleration *= -1; if (Acceleration.Z < 0) Acceleration.Z *= -1; } Super.Bump(Other); } Qpclass KrallBolt extends Projectile; var xEmitter Trail; var texture TrailTex; simulated function Destroyed() { if (Trail != None) Trail.Destroy(); Super.Destroyed(); } simulated function PostBeginPlay() { local Rotator R; Super.PostBeginPlay(); if ( EffectIsRelevant(vect(0,0,0),false) ) { Trail = Spawn(class'LinkProjEffect',self); if ( Trail != None ) Trail.Skins[0] = TrailTex; } Velocity = Speed * Vector(Rotation); R = Rotation; R.Roll = Rand(65536); SetRotation(R); if ( Level.bDropDetail || Level.DetailMode == DM_Low ) { bDynamicLight = false; LightType = LT_None; } } simulated function Explode(vector HitLocation, vector HitNormal) { if ( EffectIsRelevant(Location,false) ) Spawn(class'LinkProjSparksYellow',,, HitLocation, rotator(HitNormal)); PlaySound(Sound'WeaponSounds.BioRifle.BioRifleGoo2'); Destroy(); } simulated function ProcessTouch (Actor Other, vector HitLocation) { local Vector X, RefNormal, RefDir; if (Other == Instigator) return; if (Other == Owner) return; if (Other.IsA('xPawn') && xPawn(Other).CheckReflect(HitLocation, RefNormal, Damage*0.25)) { if (Role == ROLE_Authority) { X = Normal(Velocity); RefDir = X - 2.0*RefNormal*(X dot RefNormal); Spawn(Class, Other,, HitLocation+RefDir*20, Rotator(RefDir)); } Destroy(); } else if ( Other.bProjTarget ) { if ( Role == ROLE_Authority ) Other.TakeDamage(Damage,Instigator,HitLocation,MomentumTransfer * Normal(Velocity),MyDamageType); Explode(HitLocation, vect(0,0,1)); } } Q_T| w_*_a GQ(class KrallAmmo extends Ammunition; Qc>//============================================================================= // Krall. //============================================================================= class Krall extends Monster; var bool bAttackSuccess; var bool bLegless; var bool bSuperAggressive; var name MeleeAttack[5]; replication { unreliable if( Role==ROLE_Authority ) bLegless; } function PostBeginPlay() { Super.PostBeginPlay(); bSuperAggressive = (FRand() < 0.5); } function RangedAttack(Actor A) { if ( bShotAnim ) return; if ( bLegless ) SetAnimAction('Shoot3'); else if ( Physics == PHYS_Swimming ) SetAnimAction('SwimFire'); else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) { PlaySound(sound'strike1k',SLOT_Talk); SetAnimAction(MeleeAttack[Rand(5)]); } else { if ( bSuperAggressive && !Controller.bPreparingMove && Controller.InLatentExecution(Controller.LATENT_MOVETOWARD) ) return; if ( Controller.InLatentExecution(501) ) // LATENT_MOVETO return; SetAnimAction('Shoot1'); } Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; } function StrikeDamageTarget() { if (MeleeDamageTarget(20, 21000 * Normal(Controller.Target.Location - Location))) PlaySound(sound'hit2k',SLOT_Interact); } function vector GetFireStart(vector X, vector Y, vector Z) { return Location + 0.9*X - 0.5*Y; } function SpawnShot() { FireProjectile(); } function PlayTakeHit(vector HitLocation, int Damage, class DamageType) { local rotator r; if ( bLegless ) return; if ( (Health > 30) || (Damage < 20) || (HitLocation.Z > Location.Z) ) { Super.PlayTakeHit(HitLocation, Damage, DamageType); return; } r = rotator(Location - HitLocation); CreateGib('lthigh',DamageType,r); CreateGib('rthigh',DamageType,r); bWaitForAnim = false; SetAnimAction('LegLoss'); } simulated function PlayDying(class DamageType, vector HitLoc) { Super.PlayDying(DamageType,HitLoc); if ( bLegless ) PlayAnim('LeglessDeath',0.05); } simulated event SetAnimAction(name NewAction) { local int i; if ( NewAction == 'LegLoss' ) { bWaitForAnim = false; GroundSpeed = 100; bCanStrafe = false; bMeleeFighter = true; bLegless = true; SetCollisionSize(CollisionRadius,16); PrePivot = vect(0,0,1) * (Default.CollisionHeight - 16); for ( i=0; i<3; i++ ) { MovementAnims[i] = 'Drag'; SwimAnims[i] = 'Drag'; CrouchAnims[i] = 'Drag'; WalkAnims[i] = 'Drag'; AirAnims[i] = 'Drag'; TakeOffAnims[i] = 'Drag'; LandAnims[i] = 'Drag'; DodgeAnims[i] = 'Drag'; } IdleWeaponAnim = 'Drag'; IdleHeavyAnim = 'Drag'; IdleRifleAnim = 'Drag'; IdleRestAnim = 'Drag'; IdleCrouchAnim = 'Drag'; IdleSwimAnim = 'Drag'; AirStillAnim = 'Drag'; TakeoffStillAnim = 'Drag'; TurnRightAnim = 'Drag'; TurnLeftAnim = 'Drag'; CrouchTurnRightAnim = 'Drag'; CrouchTurnLeftAnim = 'Drag'; } Super.SetAnimAction(NewAction); } function PlayVictory() { if ( bLegless ) return; Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; PlaySound(sound'staflp4k',SLOT_Interact); SetAnimAction('Twirl'); Controller.Destination = Location; Controller.GotoState('TacticalMove','WaitForAnim'); } function ThrowDamageTarget() { bAttackSuccess = MeleeDamageTarget(30, vect(0,0,0)); if ( bAttackSuccess ) PlaySound(sound'hit2k',SLOT_Interact); } function ThrowTarget() { if ( bAttackSuccess && (VSize(Controller.Target.Location - Location) < CollisionRadius + Controller.Target.CollisionRadius + 1.5 * MeleeRange) ) { PlaySound(sound'hit2k',SLOT_Interact); if (Pawn(Controller.Target) != None) { Pawn(Controller.Target).AddVelocity( (50000.0 * (Normal(Controller.Target.Location - Location) + vect(0,0,1)))/Controller.Target.Mass); } } } Q7class InvasionTimerMessage extends TimerMessage; Q,class InvasionTeamAI extends TeamAI; Qvclass InvasionSquad extends SquadAI; var int IncomingWave; var bool bHeavyAttack; function NotifyKilled(Controller Killer, Controller Killed, pawn KilledPawn) { local Bot B; local int i; local Monster P; if ( Killed == None ) return; // if teammate killed, no need to update enemy list if ( (Team != None) && (Killed.PlayerReplicationInfo != None) && (Killed.PlayerReplicationInfo.Team == Team) ) { if ( IsOnSquad(Killed) ) { for ( B=SquadMembers; B!=None; B=B.NextSquadMember ) if ( (B != Killed) && (B.Pawn != None) ) { B.SendMessage(None, 'OTHER', B.GetMessageIndex('MANDOWN'), 4, 'TEAM'); break; } } return; } RemoveEnemy(KilledPawn); B = Bot(Killer); if ( (B != None) && (B.Squad == self) && (B.Enemy == None) && (B.Pawn != None) ) { // if no enemies left, area secure for ( i=0; i<8; i++ ) if ( Enemies[i] != None ) return; IncomingWave = 0; ForEach DynamicActors(class'Monster',P) if ( (P.Health > 0) && VSize(B.Pawn.Location - P.Location) < 3000 ) return; B.SendMessage(None, 'OTHER', 11, 12, 'TEAM'); } } QC \S3'Hr\ "r\ \a/!k-.\{se9D>9:9:$dgd@edea\ sg9?,9PgaEE\--9:9:$\*}9Ds;s#? GQ>class InvasionPoint extends GameObjective notplaceable; nc@2oz=ؚ TMNHQQF class InvasionMutator extends DMMutator HideDropDown CacheExempt; function bool CheckReplacement( Actor Other, out byte bSuperRelevant ) { // set bSuperRelevant to false if want the gameinfo's super.IsRelevant() function called // to check on relevancy of this actor. bSuperRelevant = 0; if ( Pawn(Other) != None ) { Pawn(Other).bAutoActivate = true; } else if ( GameObjective(Other) != None ) { Other.bHidden = true; GameObjective(Other).bDisabled = true; GameObjective(Other).DefenderTeamIndex = 255; GameObjective(Other).StartTeam = 255; Other.SetCollision(false,false,false); } else if ( GameObject(Other) != None ) return false; return true; } pTe6q 22hʁ)ؚؚؚʁ) ]eTMNHQQzclass InvasionBot extends xBot; var bool bDamagedMessage; /* YellAt() Tell idiot to stop shooting me */ function YellAt(Pawn Moron) { if ( (Enemy != None) || (FRand() < 0.7) ) return; SendMessage(None, 'FRIENDLYFIRE', 0, 5, 'TEAM'); } function bool AllowVoiceMessage(name MessageType) { if ( Level.TimeSeconds - OldMessageTime < 3 ) return false; else OldMessageTime = Level.TimeSeconds; return true; } event SeeMonster(Pawn Seen) { local Pawn CurrentEnemy; CurrentEnemy = Enemy; if ( !Seen.bAmbientCreature ) SeePlayer(Seen); if ( Enemy != None ) { if ( CurrentEnemy == None ) { if ( InvasionSquad(Squad).IncomingWave != Invasion(Level.Game).WaveNum ) { SendMessage(None, 'OTHER', 14, 12, 'TEAM'); InvasionSquad(Squad).IncomingWave = Invasion(Level.Game).WaveNum; } } else if ( (CurrentEnemy != Enemy) && (Pawn.Health < 80) && LineOfSightTo(CurrentEnemy) ) { if ( InvasionSquad(Squad).bHeavyAttack ) SendMessage(None, 'OTHER', 21, 12, 'TEAM'); else SendMessage(None, 'OTHER', 22, 12, 'TEAM'); InvasionSquad(Squad).bHeavyAttack = !InvasionSquad(Squad).bHeavyAttack; } } } QS$S$S.% T GQC sH ; OAR(La  H 9PK %L a a GQbclass Invasion extends xTeamGame config; #exec OBJ LOAD FILE=SkaarjPackSkins.utx #exec OBJ LOAD FILE=AnnouncerEvil.uax var class MonsterClass[16]; var class WaveMonsterClass[16]; var class LastKilledMonsterClass; var class FallbackMonster; var float NextMonsterTime; var float WaveEndTime; var config string WaveConfigMenu; var config string FallbackMonsterClass; var config int InitialWave; var config int FinalWave; const INVPROPNUM = 8; var localized string InvasionPropText[INVPROPNUM]; var localized string InvasionDescText[INVPROPNUM]; var int NumMonsters, MaxMonsters; var int WaveNum; var int WaveNumClasses; var int WaveMonsters; var int SecondBot; var int WaveCountDown; var bool bWaveInProgress; var string InvasionBotNames[9]; var sound NewRoundSound; // OBSOLETE var sound InvasionEndSound[6]; // OBSOLETE var name InvasionEnd[6]; struct WaveInfo { var() int WaveMask; // bit fields for which monsterclasses var() byte WaveMaxMonsters; var() byte WaveDuration; var() float WaveDifficulty; }; var() config WaveInfo Waves[16]; // TODO Add support for structs & arrays to PlayInfo static function PrecacheGameTextures(LevelInfo myLevel) { class'xTeamGame'.static.PrecacheGameTextures(myLevel); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.jBrute2'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.jBrute1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.eKrall'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.Skaarjw3'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.Gasbag1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.Gasbag2'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.Skaarjw2'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.JManta1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.JFly1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.Skaarjw1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.JPupae1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.JWarlord1'); myLevel.AddPrecacheMaterial(Material'SkaarjPackSkins.jkrall'); } static function PrecacheGameAnnouncements(AnnouncerVoice V, bool bRewardSounds) { Super.PrecacheGameAnnouncements(V,bRewardSounds); if ( bRewardSounds ) { V.PrecacheSound('SKAARJtermination'); V.PrecacheSound('SKAARJslaughter'); V.PrecacheSound('SKAARJextermination'); V.PrecacheSound('SKAARJerradication'); V.PrecacheSound('SKAARJbloodbath'); V.PrecacheSound('SKAARJannihilation'); } else V.PrecacheSound('Next_wave_in'); } /* OBSOLETE UpdateAnnouncements() - preload all announcer phrases used by this actor */ simulated function UpdateAnnouncements() {} event InitGame( string Options, out string Error ) { Super.InitGame(Options, Error); FallbackMonster = class(DynamicLoadObject(FallbackMonsterClass, class'Class')); if (FallbackMonster == None) FallbackMonster = class'EliteKrall'; MaxLives = 1; bForceRespawn = true; } event PreBeginPlay() { Super.PreBeginPlay(); WaveNum = InitialWave; InvasionGameReplicationInfo(GameReplicationInfo).WaveNumber = WaveNum; InvasionGameReplicationInfo(GameReplicationInfo).BaseDifficulty = int(GameDifficulty); GameReplicationInfo.bNoTeamSkins = true; GameReplicationInfo.bForceNoPlayerLights = true; GameReplicationInfo.bNoTeamChanges = true; } function bool CanSpectate( PlayerController Viewer, bool bOnlySpectator, actor ViewTarget ) { if ( (ViewTarget == None) ) return false; if ( Controller(ViewTarget) != None ) { if ( Controller(ViewTarget).Pawn == None ) return false; return ( (Controller(ViewTarget).PlayerReplicationInfo != None) && (ViewTarget != Viewer) && (Controller(ViewTarget).PlayerReplicationInfo.Team == Viewer.PlayerReplicationInfo.Team) ); } return ( (Pawn(ViewTarget) != None) && Pawn(ViewTarget).IsPlayerPawn() && (Pawn(ViewTarget).PlayerReplicationInfo.Team == Viewer.PlayerReplicationInfo.Team) ); } function OverrideInitialBots() { InitialBots = Min(InitialBots,2); } function bool CheckEndGame(PlayerReplicationInfo Winner, string Reason) { local Controller P; local PlayerController Player; EndTime = Level.TimeSeconds + EndTimeDelay; if ( WaveNum >= FinalWave ) GameReplicationInfo.Winner = Teams[0]; for ( P=Level.ControllerList; P!=None; P=P.nextController ) { player = PlayerController(P); if ( Player != None ) { if ( !Player.PlayerReplicationInfo.bOnlySpectator ) PlayWinMessage(Player, (Player.PlayerReplicationInfo.Team == GameReplicationInfo.Winner)); player.ClientSetBehindView(true); player.ClientGameEnded(); } P.GameHasEnded(); } if ( CurrentGameProfile != None ) CurrentGameProfile.bWonMatch = false; return true; } // check if all other players are out function bool CheckMaxLives(PlayerReplicationInfo Scorer) { local Controller C; local PlayerReplicationInfo Living; local bool bNoneLeft; if ( MaxLives > 0 ) { bNoneLeft = true; for ( C=Level.ControllerList; C!=None; C=C.NextController ) if ( (C.PlayerReplicationInfo != None) && C.bIsPlayer && !C.PlayerReplicationInfo.bOutOfLives && !C.PlayerReplicationInfo.bOnlySpectator ) { bNoneLeft = false; break; } if ( bNoneLeft ) { if ( Living != None ) EndGame(Living,"LastMan"); else EndGame(Scorer,"LastMan"); return true; } } return false; } function ScoreKill(Controller Killer, Controller Other) { local PlayerReplicationInfo OtherPRI; local float KillScore; OtherPRI = Other.PlayerReplicationInfo; if ( OtherPRI != None ) { OtherPRI.NumLives++; OtherPRI.Score -= 10; OtherPRI.Team.Score -= 10; OtherPRI.Team.NetUpdateTime = Level.TimeSeconds - 1; OtherPRI.bOutOfLives = true; BroadcastLocalizedMessage(class'InvasionMessage', 1, OtherPRI); CheckScore(None); } if ( GameRulesModifiers != None ) GameRulesModifiers.ScoreKill(Killer, Other); if ( MonsterController(Killer) != None ) return; if( (killer == Other) || (killer == None) ) { if ( Other.PlayerReplicationInfo != None ) { Other.PlayerReplicationInfo.Score -= 1; Other.PlayerReplicationInfo.NetUpdateTime = Level.TimeSeconds - 1; Killer.PlayerReplicationInfo.Team.Score -= 1; Killer.PlayerReplicationInfo.Team.NetUpdateTime = Level.TimeSeconds - 1; ScoreEvent(Other.PlayerReplicationInfo,-1,"self_frag"); } } if ( (Killer == None) || !Killer.bIsPlayer || (Killer == Other) ) return; if ( Other.bIsPlayer ) { Killer.PlayerReplicationInfo.Score -= 10; Killer.PlayerReplicationInfo.NetUpdateTime = Level.TimeSeconds - 1; Killer.PlayerReplicationInfo.Team.Score -= 10; Killer.PlayerReplicationInfo.Team.NetUpdateTime = Level.TimeSeconds - 1; ScoreEvent(Killer.PlayerReplicationInfo, -10, "team_frag"); return; } if ( LastKilledMonsterClass == None ) KillScore = 1; else KillScore = LastKilledMonsterClass.Default.ScoringValue; Killer.PlayerReplicationInfo.Kills++; Killer.PlayerReplicationInfo.Score += KillScore; Killer.PlayerReplicationInfo.Team.Score += KillScore; Killer.PlayerReplicationInfo.Team.NetUpdateTime = Level.TimeSeconds - 1; Killer.AwardAdrenaline(KillScore); Killer.PlayerReplicationInfo.NetUpdateTime = Level.TimeSeconds - 1; TeamScoreEvent(Killer.PlayerReplicationInfo.Team.TeamIndex, 1, "tdm_frag"); } function NotifyKilled(Controller Killer, Controller Killed, Pawn KilledPawn) { local Controller C; for ( C=Level.ControllerList; C!=None; C=C.nextController ) if ( MonsterController(C) != None ) C.NotifyKilled(Killer, Killed, KilledPawn); Super.NotifyKilled(Killer,Killed,KilledPawn); } function RestartPlayer( Controller aPlayer ) { if ( aPlayer.PlayerReplicationInfo.bOutOfLives ) return; Super(GameInfo).RestartPlayer(aPlayer); } function UnrealTeamInfo GetBotTeam(optional int TeamBots) { return Teams[0]; } function byte PickTeam(byte num, Controller C) { return 0; } event PlayerController Login ( string Portal, string Options, out string Error ) { local PlayerController NewPlayer; local Controller C; NewPlayer = Super.Login(Portal,Options,Error); for ( C=Level.ControllerList; C!=None; C=C.NextController ) if ( (C.PlayerReplicationInfo != None) && C.PlayerReplicationInfo.bOutOfLives && !C.PlayerReplicationInfo.bOnlySpectator ) { NewPlayer.PlayerReplicationInfo.bOutOfLives = true; NewPlayer.PlayerReplicationInfo.NumLives = 1; } return NewPlayer; } /* Spawn and initialize a bot */ function Bot SpawnBot(optional string botName) { local Bot NewBot; local RosterEntry Chosen; local UnrealTeamInfo BotTeam; local array PlayerRecords; local xUtil.PlayerRecord PR; BotTeam = GetBotTeam(); if ( bCustomBots && (class'DMRosterConfigured'.Default.Characters.Length > NumBots) ) { class'xUtil'.static.GetPlayerList(PlayerRecords); PR = class'xUtil'.static.FindPlayerRecord(class'DMRosterConfigured'.Default.Characters[NumBots]); Chosen = class'xRosterEntry'.Static.CreateRosterEntry(PR.RecordIndex); } if ( Chosen == None ) { if ( SecondBot > 0 ) { BotName = InvasionBotNames[SecondBot + 1]; SecondBot++; if ( SecondBot > 6 ) SecondBot = 0; } else { SecondBot = 1 + 2 * Rand(4); BotName = InvasionBotNames[SecondBot]; } Chosen = class'xRosterEntry'.static.CreateRosterEntryCharacter(botName); } if (Chosen.PawnClass == None) Chosen.Init(); NewBot = Spawn(class'InvasionBot'); if ( NewBot != None ) { AdjustedDifficulty = AdjustedDifficulty + 2; InitializeBot(NewBot,BotTeam,Chosen); AdjustedDifficulty = AdjustedDifficulty - 2; NewBot.bInitLifeMessage = true; } return NewBot; } function int ReduceDamage( int Damage, pawn injured, pawn instigatedBy, vector HitLocation, out vector Momentum, class DamageType ) { local float InstigatorSkill, result; if ( instigatedBy == None ) return Super.ReduceDamage( Damage,injured,instigatedBy,HitLocation,Momentum,DamageType ); if ( Monster(Injured) != None ) { if ( Monster(Injured).SameSpeciesAs(InstigatedBy) ) return 0; return Damage; } if ( MonsterController(InstigatedBy.Controller) != None ) { InstigatorSkill = MonsterController(instigatedBy.Controller).Skill; if ( NumPlayers > 4 ) InstigatorSkill += 1.0; if ( (InstigatorSkill < 7) && (Monster(Injured) == None) ) { if ( InstigatorSkill <= 3 ) Damage = Damage * (0.25 + 0.05 * InstigatorSkill); else Damage = Damage * (0.4 + 0.1 * (InstigatorSkill - 3)); } } else if ( injured == instigatedBy ) Damage = Damage * 0.5; if ( InvasionBot(injured.Controller) != None ) { if ( !InvasionBot(injured.controller).bDamagedMessage && (injured.Health - Damage < 50) ) { InvasionBot(injured.controller).bDamagedMessage = true; if ( FRand() < 0.5 ) injured.Controller.SendMessage(None, 'OTHER', 4, 12, 'TEAM'); else injured.Controller.SendMessage(None, 'OTHER', 13, 12, 'TEAM'); } if ( GameDifficulty <= 3 ) { if ( injured.IsPlayerPawn() && (injured == instigatedby) && (Level.NetMode == NM_Standalone) ) Damage *= 0.5; //skill level modification if ( MonsterController(InstigatedBy.Controller) != None ) { if ( InstigatorSkill <= 3 ) Damage = Damage * (0.25 + 0.15 * InstigatorSkill); } } } result = Super.ReduceDamage( Damage,injured,instigatedBy,HitLocation,Momentum,DamageType ); return result; } function Killed( Controller Killer, Controller Killed, Pawn KilledPawn, class damageType ) { local TeamPlayerReplicationInfo TPRI; if ( (MonsterController(Killed) != None) || (Monster(KilledPawn) != None) ) { NumMonsters--; if ( (Killer != None) && Killer.bIsPlayer ) { TPRI = TeamPlayerReplicationInfo(Killer.PlayerReplicationInfo); if ( TPRI != None ) TPRI.AddWeaponKill(DamageType); } } LastKilledMonsterClass = class(KilledPawn.class); Super.Killed(Killer,Killed,KilledPawn,DamageType); } function AddMonster() { local NavigationPoint StartSpot; local Pawn NewMonster; local class NewMonsterClass; StartSpot = FindPlayerStart(None,1); if ( StartSpot == None ) return; NewMonsterClass = WaveMonsterClass[Rand(WaveNumClasses)]; NewMonster = Spawn(NewMonsterClass,,,StartSpot.Location+(NewMonsterClass.Default.CollisionHeight - StartSpot.CollisionHeight) * vect(0,0,1),StartSpot.Rotation); if ( NewMonster == None ) NewMonster = Spawn(FallBackMonster,,,StartSpot.Location+(class'EliteKrall'.Default.CollisionHeight - StartSpot.CollisionHeight) * vect(0,0,1),StartSpot.Rotation); if ( NewMonster != None ) { WaveMonsters++; NumMonsters++; } } function SetupWave() { local int i,j; local float NewMaxMonsters; if ( WaveNum > 15 ) { SetupRandomWave(); return; } WaveMonsters = 0; WaveNumClasses = 0; NewMaxMonsters = Waves[WaveNum].WaveMaxMonsters; if ( NumPlayers + NumBots <= 2 ) NewMaxMonsters = NewMaxMonsters * (FMin(GameDifficulty,7) + 3)/10; if ( NumPlayers > 4 ) NewMaxMonsters *= FMin(NumPlayers/4,2); MaxMonsters = NewMaxMonsters; WaveEndTime = Level.TimeSeconds + Waves[WaveNum].WaveDuration; AdjustedDifficulty = GameDifficulty + Waves[WaveNum].WaveDifficulty; j = 1; for ( i=0; i<16; i++ ) { if ( (j & Waves[WaveNum].WaveMask) != 0 ) { WaveMonsterClass[WaveNumClasses] = MonsterClass[i]; WaveNumClasses++; } j *= 2; } } function SetupRandomWave() { local int i,j, Mask; local float NewMaxMonsters; NewMaxMonsters = 15; if ( NumPlayers > 4 ) NewMaxMonsters *= FMin(NumPlayers/4,2); else NewMaxMonsters = NewMaxMonsters * (FMin(GameDifficulty,7) + 3)/10; MaxMonsters = NewMaxMonsters + 1; WaveEndTime = Level.TimeSeconds + 180; AdjustedDifficulty = GameDifficulty + 3; GameDifficulty += 0.5; WaveNumClasses = 0; Mask = 2048 + Rand(2047); j = 1; for ( i=0; i<16; i++ ) { if ( (j & Mask) != 0 ) { WaveMonsterClass[WaveNumClasses] = MonsterClass[i]; WaveNumClasses++; } j *= 2; } } function PlayEndOfMatchMessage() { local controller C; local name EndSound; if ( WaveNum >= FinalWave ) EndSound = EndGameSoundName[0]; else if ( WaveNum - InitialWave == 0 ) EndSound = AltEndGameSoundName[1]; else EndSound = InvasionEnd[Min(5,(WaveNum - InitialWave)/3)]; for ( C = Level.ControllerList; C != None; C = C.NextController ) if ( C.IsA('PlayerController') ) PlayerController(C).PlayRewardAnnouncement(EndSound,1,true); } /* Rate whether player/monster should choose this NavigationPoint as its start */ function float RatePlayerStart(NavigationPoint N, byte Team, Controller Player) { local float Score, NextDist; local Controller OtherPlayer; if ( (Team == 0) || ((Player !=None) && Player.bIsPlayer) ) return Super.RatePlayerStart(N,Team,Player); if ( N.PhysicsVolume.bWaterVolume ) return -10000000; //assess candidate if ( (SmallNavigationPoint(N) != None) && (PlayerStart(N) == None) ) return -1; Score = 10000000; Score += 3000 * FRand(); //randomize for ( OtherPlayer=Level.ControllerList; OtherPlayer!=None; OtherPlayer=OtherPlayer.NextController) if ( (PlayerController(OtherPlayer) != None) && (OtherPlayer.Pawn != None) ) { NextDist = VSize(OtherPlayer.Pawn.Location - N.Location); if ( NextDist < OtherPlayer.Pawn.CollisionRadius + OtherPlayer.Pawn.CollisionHeight ) Score -= 1000000.0; else if ( NextDist > 5000 ) Score -= 20000; else if ( NextDist < 3000 ) { if ( (NextDist > 1200) && (Vector(OtherPlayer.Rotation) Dot (N.Location - OtherPlayer.Pawn.Location)) <= 0 ) Score = Score + 5000 - NextDist; else if ( FastTrace(N.Location, OtherPlayer.Pawn.Location) ) Score -= (10000.0 - NextDist); if ( (Location.Z > OtherPlayer.Pawn.Location.Z) && (NextDist > 1000) ) Score += 1000; } } return FMax(Score, 5); } function ReplenishWeapons(Pawn P) { local Inventory Inv; for (Inv = P.Inventory; Inv != None; Inv = Inv.Inventory) if (Weapon(Inv) != None && !Inv.IsA('Painter') && !Inv.IsA('Redeemer')) { Weapon(Inv).FillToInitialAmmo(); Inv.NetUpdateTime = Level.TimeSeconds - 1; } } State MatchInProgress { function Timer() { local Controller C; local bool bOneMessage; local Bot B; local int PlayerCount; Super.Timer(); if ( bWaveInProgress ) { if ( (WaveEndTime - Level.TimeSeconds < 30) && (MaxMonsters < Waves[WaveNum].WaveMaxMonsters) ) { for ( C = Level.ControllerList; C != None; C = C.NextController ) if ( C.bIsPlayer && (C.Pawn != None) ) PlayerCount++; if ( PlayerCount > 1 ) MaxMonsters = Waves[WaveNum].WaveMaxMonsters; } if ( (Level.TimeSeconds > WaveEndTime) && (WaveMonsters >= 2*MaxMonsters) ) { if ( Level.TimeSeconds > WaveEndTime + 90 ) { for ( C = Level.ControllerList; C != None; C = C.NextController ) if ( (MonsterController(C) != None) && (Level.TimeSeconds - MonsterController(C).LastSeenTime > 30) && !MonsterController(C).Pawn.PlayerCanSeeMe() ) { C.Pawn.KilledBy( C.Pawn ); return; } } if ( NumMonsters <= 0 ) { bWaveInProgress = false; WaveCountDown = 15; WaveNum++; } } else if ( (Level.TimeSeconds > NextMonsterTime) && (NumMonsters < MaxMonsters) ) { AddMonster(); if ( NumMonsters < 1.5 * (NumPlayers + NumBots) ) NextMonsterTime = Level.TimeSeconds + 0.2; else NextMonsterTime = Level.TimeSeconds + 2; } } else if ( NumMonsters <= 0 ) { if ( WaveNum == FinalWave ) { EndGame(None,"TimeLimit"); return; } WaveCountDown--; if ( WaveCountDown == 14 ) { for ( C = Level.ControllerList; C != None; C = C.NextController ) { if ( C.PlayerReplicationInfo != None ) { C.PlayerReplicationInfo.bOutOfLives = false; C.PlayerReplicationInfo.NumLives = 0; if ( C.Pawn != None ) ReplenishWeapons(C.Pawn); else if ( !C.PlayerReplicationInfo.bOnlySpectator && (PlayerController(C) != None) ) C.GotoState('PlayerWaiting'); } } } if ( WaveCountDown == 13 ) { InvasionGameReplicationInfo(GameReplicationInfo).WaveNumber = WaveNum; for ( C = Level.ControllerList; C != None; C = C.NextController ) { if ( PlayerController(C) != None ) { PlayerController(C).PlayStatusAnnouncement('Next_wave_in',1,true); if ( (C.Pawn == None) && !C.PlayerReplicationInfo.bOnlySpectator ) PlayerController(C).SetViewTarget(C); } if ( C.PlayerReplicationInfo != None ) { C.PlayerReplicationInfo.bOutOfLives = false; C.PlayerReplicationInfo.NumLives = 0; if ( (C.Pawn == None) && !C.PlayerReplicationInfo.bOnlySpectator ) C.ServerReStartPlayer(); } } } else if ( (WaveCountDown > 1) && (WaveCountDown < 12) ) BroadcastLocalizedMessage(class'TimerMessage', WaveCountDown-1); else if ( WaveCountDown <= 1 ) { bWaveInProgress = true; SetupWave(); for ( C = Level.ControllerList; C != None; C = C.NextController ) if ( PlayerController(C) != None ) PlayerController(C).LastPlaySpeech = 0; for ( C = Level.ControllerList; C != None; C = C.NextController ) if ( Bot(C) != None ) { B = Bot(C); InvasionBot(B).bDamagedMessage = false; B.bInitLifeMessage = false; if ( !bOneMessage && (FRand() < 0.65) ) { bOneMessage = true; if ( (B.Squad.SquadLeader != None) && B.Squad.CloseToLeader(C.Pawn) ) { B.SendMessage(B.Squad.SquadLeader.PlayerReplicationInfo, 'OTHER', B.GetMessageIndex('INPOSITION'), 20, 'TEAM'); B.bInitLifeMessage = false; } } } } } } function BeginState() { Super.BeginState(); WaveNum = InitialWave; InvasionGameReplicationInfo(GameReplicationInfo).WaveNumber = WaveNum; } } function GetServerDetails( out ServerResponseLine ServerState ) { Super.GetServerDetails(ServerState); AddServerDetail( ServerState, "InitialWave", InitialWave ); AddServerDetail( ServerState, "FinalWave", FinalWave ); } static function FillPlayInfo(PlayInfo PI) { Super.FillPlayInfo(PI); PI.AddSetting(default.GameGroup, "InitialWave", GetDisplayText("InitialWave"), 50, 0, "Text", "2;0:"$(ArrayCount(default.Waves)-1) ); PI.AddSetting(default.GameGroup, "FinalWave", GetDisplayText("FinalWave"), 50, 0, "Text", "2;1:"$ArrayCount(default.Waves) ); PI.AddSetting(default.GameGroup, "Waves", GetDisplayText("Waves"), 60, 0,"Custom", ";;"$default.WaveConfigMenu,,,True); } static event string GetDisplayText( string PropName ) { switch (PropName) { case "InitialWave": return default.InvasionPropText[0]; case "FinalWave": return default.InvasionPropText[1]; case "Waves": return default.InvasionPropText[2]; case "Monsters": return default.InvasionPropText[3]; case "WaveNo": return default.InvasionPropText[4]; case "WaveMaxMonsters": return default.InvasionPropText[5]; case "WaveDuration": return default.InvasionPropText[6]; case "WaveDifficulty": return default.InvasionPropText[7]; } return Super.GetDisplayText( PropName ); } static event string GetDescriptionText(string PropName) { switch (PropName) { case "InitialWave": return default.InvasionDescText[0]; case "FinalWave": return default.InvasionDescText[1]; case "Waves": return default.InvasionDescText[2]; case "Monsters": return default.InvasionDescText[3]; case "WaveNo": return default.InvasionDescText[4]; case "WaveMaxMonsters": return default.InvasionDescText[5]; case "WaveDuration": return default.InvasionDescText[6]; case "WaveDifficulty": return default.InvasionDescText[7]; } return Super.GetDescriptionText(PropName); } static event bool AcceptPlayInfoProperty(string PropertyName) { if ( (PropertyName == "bBalanceTeams") || (PropertyName == "bPlayersBalanceTeams") || (PropertyName == "GoalScore") ) return false; return Super.AcceptPlayInfoProperty(PropertyName); } Q8class IceSkaarjProjectile extends SkaarjProjectile; QSclass IceSkaarj extends Skaarj; event PostBeginPlay() { Super.PostBeginPlay(); MyAmmo.ProjectileClass = class'IceSkaarjProjectile'; } Qx>class HUDInvasion extends HudCTeamDeathMatch config(user); var float RadarPulse,RadarScale; var config float RadarPosX, RadarPosY; var float LastDrawRadar; var float MinEnemyDist; var config bool bNoRadarSound; #EXEC OBJ LOAD FILE=InterfaceContent.utx #EXEC OBJ LOAD FILE=AS_FX_TX.utx simulated function UpdatePrecacheMaterials() { Level.AddPrecacheMaterial(Material'InterfaceContent.HUD.SkinA'); Level.AddPrecacheMaterial(Material'AS_FX_TX.AssaultRadar'); Super.UpdatePrecacheMaterials(); } simulated function ShowTeamScorePassA(Canvas C) { local float RadarWidth, PulseWidth, PulseBrightness; RadarScale = Default.RadarScale * HUDScale; RadarWidth = 0.5 * RadarScale * C.ClipX; PulseWidth = RadarScale * C.ClipX; C.DrawColor = RedColor; C.Style = ERenderStyle.STY_Translucent; PulseBrightness = FMax(0,(1 - 2*RadarPulse) * 255.0); C.DrawColor.R = PulseBrightness; C.SetPos(RadarPosX*C.ClipX - 0.5*PulseWidth,RadarPosY*C.ClipY+RadarWidth-0.5*PulseWidth); C.DrawTile( Material'InterfaceContent.SkinA', PulseWidth, PulseWidth, 0, 880, 142, 142); PulseWidth = RadarPulse * RadarScale * C.ClipX; C.DrawColor = RedColor; C.SetPos(RadarPosX*C.ClipX - 0.5*PulseWidth,RadarPosY*C.ClipY+RadarWidth-0.5*PulseWidth); C.DrawTile( Material'InterfaceContent.SkinA', PulseWidth, PulseWidth, 0, 880, 142, 142); C.Style = ERenderStyle.STY_Alpha; C.DrawColor = GetTeamColor( PawnOwner.GetTeamNum() ); C.SetPos(RadarPosX*C.ClipX - RadarWidth,RadarPosY*C.ClipY+RadarWidth); C.DrawTile( Material'AS_FX_TX.AssaultRadar', RadarWidth, RadarWidth, 0, 512, 512, -512); C.SetPos(RadarPosX*C.ClipX,RadarPosY*C.ClipY+RadarWidth); C.DrawTile( Material'AS_FX_TX.AssaultRadar', RadarWidth, RadarWidth, 512, 512, -512, -512); C.SetPos(RadarPosX*C.ClipX - RadarWidth,RadarPosY*C.ClipY); C.DrawTile( Material'AS_FX_TX.AssaultRadar', RadarWidth, RadarWidth, 0, 0, 512, 512); C.SetPos(RadarPosX*C.ClipX,RadarPosY*C.ClipY); C.DrawTile( Material'AS_FX_TX.AssaultRadar', RadarWidth, RadarWidth, 512, 0, -512, 512); } simulated function ShowTeamScorePassC(Canvas C) { local Pawn P; local float Dist, MaxDist, RadarWidth, PulseBrightness,Angle,DotSize,OffsetY,OffsetScale; local rotator Dir; local vector Start; LastDrawRadar = Level.TimeSeconds; RadarWidth = 0.5 * RadarScale * C.ClipX; DotSize = 24*C.ClipX*HUDScale/1600; if ( PawnOwner == None ) Start = PlayerOwner.Location; else Start = PawnOwner.Location; MaxDist = 3000 * RadarPulse; C.Style = ERenderStyle.STY_Translucent; OffsetY = RadarPosY + RadarWidth/C.ClipY; MinEnemyDist = 3000; ForEach DynamicActors(class'Pawn',P) if ( P.Health > 0 ) { Dist = VSize(Start - P.Location); if ( Dist < 3000 ) { if ( Dist < MaxDist ) PulseBrightness = 255 - 255*Abs(Dist*0.00033 - RadarPulse); else PulseBrightness = 255 - 255*Abs(Dist*0.00033 - RadarPulse - 1); if ( Monster(P) != None ) { MinEnemyDist = FMin(MinEnemyDist, Dist); C.DrawColor.R = PulseBrightness; C.DrawColor.G = PulseBrightness; C.DrawColor.B = 0; } else { C.DrawColor.R = 0; C.DrawColor.G = 0; C.DrawColor.B = PulseBrightness; } Dir = rotator(P.Location - Start); OffsetScale = RadarScale*Dist*0.000167; Angle = ((Dir.Yaw - PlayerOwner.Rotation.Yaw) & 65535) * 6.2832/65536; C.SetPos(RadarPosX * C.ClipX + OffsetScale * C.ClipX * sin(Angle) - 0.5*DotSize, OffsetY * C.ClipY - OffsetScale * C.ClipX * cos(Angle) - 0.5*DotSize); C.DrawTile(Material'InterfaceContent.Hud.SkinA',DotSize,DotSize,838,238,144,144); } } } simulated function Tick(float DeltaTime) { Super.Tick(DeltaTime); RadarPulse = RadarPulse + 0.5 * DeltaTime; if ( RadarPulse >= 1 ) { if ( !bNoRadarSound && (Level.TimeSeconds - LastDrawRadar < 0.2) ) PlayerOwner.ClientPlaySound(Sound'RadarPulseSound',true,FMin(1.0,300/MinEnemyDist)); RadarPulse = RadarPulse - 1; } } Q2$u[D9:9:$2a r%2 99 GQyTzd[w2*2 , %2@&2@2-( GQzB w2*2a GQ{>S(D8r> "r> >a/!k-.>{}B9D>9:9:$jCj@BjBCBa> }C9?,9PCBa55>a/!y>-;}}>  Gxck>?}0S\43} u3}ҽҽ uhҽҽhhҽҽҽҽҽҽh uҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽhҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽҽ3}3}x#F$L>E$fff?l$>B]The Invasion ContinuesQQt//============================================================================= // rocket. //============================================================================= class GasBagBelch extends Projectile; var xEmitter SmokeTrail; var vector Dir; simulated function Destroyed() { if ( SmokeTrail != None ) SmokeTrail.mRegen = False; Super.Destroyed(); } simulated function PostBeginPlay() { if ( Level.NetMode != NM_DedicatedServer) { if ( !Level.bDropDetail ) spawn(class'RocketSmokeRing',,,Location, Rotation ); SmokeTrail = Spawn(class'BelchFlames',self); } Dir = vector(Rotation); Velocity = speed * Dir; if ( Level.bDropDetail ) { bDynamicLight = false; LightType = LT_None; } Super.PostBeginPlay(); } simulated function Landed( vector HitNormal ) { Explode(Location,HitNormal); } simulated function ProcessTouch (Actor Other, Vector HitLocation) { if ( (Other != instigator) && (!Other.IsA('Projectile') || Other.bProjTarget) ) Explode(HitLocation,Vect(0,0,1)); } function BlowUp(vector HitLocation) { HurtRadius(Damage, DamageRadius, MyDamageType, MomentumTransfer, HitLocation ); MakeNoise(1.0); } simulated function Explode(vector HitLocation, vector HitNormal) { PlaySound(sound'WeaponSounds.BExplosion3',,2.5*TransientSoundVolume); spawn(class'FlakExplosion',,,HitLocation + HitNormal*16 ); spawn(class'FlashExplosion',,,HitLocation + HitNormal*16 ); if ( (ExplosionDecal != None) && (Level.NetMode != NM_DedicatedServer) ) Spawn(ExplosionDecal,self,,Location, rotator(-HitNormal)); BlowUp(HitLocation); Destroy(); } Q)class GasbagAmmo extends Ammunition; QI!class GasBag extends Monster; function RangedAttack(Actor A) { local vector adjust; if ( bShotAnim ) return; if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) { adjust = vect(0,0,0); adjust.Z = Controller.Target.CollisionHeight; Acceleration = AccelRate * Normal(Controller.Target.Location - Location + adjust); PlaySound(sound'twopunch1g',SLOT_Talk); if (FRand() < 0.5) SetAnimAction('TwoPunch'); else SetAnimAction('Pound'); } else SetAnimAction('Belch'); bShotAnim = true; } function bool Dodge(eDoubleClickDir DoubleClickMove) { local vector X,Y,Z,duckdir; GetAxes(Rotation,X,Y,Z); if (DoubleClickMove == DCLICK_Forward) duckdir = X; else if (DoubleClickMove == DCLICK_Back) duckdir = -1*X; else if (DoubleClickMove == DCLICK_Left) duckdir = Y; else if (DoubleClickMove == DCLICK_Right) duckdir = -1*Y; Controller.Destination = Location + 200 * duckDir; Velocity = AirSpeed * duckDir; Controller.GotoState('TacticalMove', 'DoMove'); return true; } function SetMovementPhysics() { SetPhysics(PHYS_Flying); } singular function Falling() { SetPhysics(PHYS_Flying); } simulated function PlayDirectionalDeath(Vector HitLoc) { if ( FRand() < 0.5 ) PlayAnim('Deflate',, 0.1); else PlayAnim('Dead2',, 0.1); } simulated function PlayDirectionalHit(Vector HitLoc) { if ( FRand() < 0.6 ) TweenAnim('TakeHit', 0.05); else TweenAnim('Hit2', 0.05); } function PlayVictory() { PlaySound(sound'twopunch1g',SLOT_Interact); SetAnimAction('Pound'); } function SpawnBelch() { FireProjectile(); } function vector GetFireStart(vector X, vector Y, vector Z) { return Location + 0.5*X; } function PunchDamageTarget() { if (MeleeDamageTarget(25, (39000 * Normal(Controller.Target.Location - Location)))) PlaySound(sound'Hit1g', SLOT_Interact); } function PoundDamageTarget() { if (MeleeDamageTarget(35, (24000 * Normal(Controller.Target.Location - Location)))) PlaySound(sound'Hit1g', SLOT_Interact); } Q9class FireSkaarjProjectile extends SkaarjProjectile; Q|c ;??/9:9:$~ c a $a GQUclass FireSkaarj extends Skaarj; event PostBeginPlay() { Super.PostBeginPlay(); MyAmmo.ProjectileClass = class'FireSkaarjProjectile'; } QQQCclass EliteKrallBolt extends KrallBolt; simulated function PostBeginPlay() { Super.PostBeginPlay(); FinalBlend(Skins[0]).Material = TrailTex; } simulated function Explode(vector HitLocation, vector HitNormal) { local xEmitter sparks; if ( EffectIsRelevant(Location,false) ) { sparks = Spawn(class'LinkProjSparksYellow',,, HitLocation, rotator(HitNormal)); sparks.Skins[0] = texture'Shock_Sparkle'; } PlaySound(Sound'WeaponSounds.BioRifle.BioRifleGoo2'); Destroy(); } QPclass EliteKrall extends Krall; event PostBeginPlay() { Super.PostBeginPlay(); MyAmmo.ProjectileClass = class'EliteKrallBolt'; } QqT )%wq*q-( GQG$P9:9:$A-a  qa w D99D--($ GQHg I$:;g  GQ*class BruteRocket extends RocketProj; QItS)VSwt(ta/!yt-;o #? GQ(class BruteAmmo extends Ammunition; QD//============================================================================= // Brute. //============================================================================= class Brute extends Monster; #exec OBJ LOAD FILE=SkaarjPackSkins.utx //----------------------------------------------------------------------------- // Brute variables. var bool bLeftShot; // Sounds var(Sounds) sound Footstep[2]; var name MeleeAttack[4]; function PlayTakeHit(vector HitLocation, int Damage, class DamageType) { if ( Damage > 15 ) Super.PlayTakeHit(HitLocation,Damage,DamageType); } function PlayVictory() { Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; Controller.Destination = Location; Controller.GotoState('TacticalMove','WaitForAnim'); SetAnimAction('StillLook'); } function RangedAttack(Actor A) { if ( bShotAnim ) return; if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) { PlaySound(sound'pwhip1br',SLOT_Talk); SetAnimAction(MeleeAttack[Rand(4)]); } else if ( Controller.InLatentExecution(501) ) // LATENT_MOVETO return; else SetAnimAction('StillFire'); Controller.bPreparingMove = true; Acceleration = vect(0,0,0); bShotAnim = true; } function WhipDamageTarget() { if ( MeleeDamageTarget(35, 40000.0 * Normal(Controller.Target.Location - Location)) ) PlaySound(sound'pwhip1br', SLOT_Interact); } function SpawnLeftShot() { bLeftShot = true; FireProjectile(); } function SpawnRightShot() { bLeftShot = false; FireProjectile(); } function vector GetFireStart(vector X, vector Y, vector Z) { if ( bLeftShot ) return Location + CollisionRadius * ( X + 0.7 * Y + 0.4 * Z); else return Location + CollisionRadius * ( X - 0.7 * Y + 0.4 * Z); } function Step() { PlaySound(FootStep[Rand(2)], SLOT_Interact); } Q Kp t/I*~ p b? GQ(class BelchFlames extends xEmitter; QNw;5a   @a  w}9?,a  w}9?,w*9:9:$a 9P}twa GQKclass Behemoth extends Brute; function RangedAttack(Actor A) { if ( bShotAnim ) return; if ( Controller.InLatentExecution(Controller.LATENT_MOVETOWARD) ) { SetAnimAction('WalkFire'); bShotAnim = true; return; } Super.RangedAttack(A); } Q`  320QZQQGQQVQQqQhdH3W]#p P]%o was zapped by a krall.vt$@FQQiQQfQQQoa|3[np P]%o was sauteed by a gasbag.Q]A Gasbag blew itself up.R]A Gasbag blew itself up.vt$@FQ`OI6 \!*+%ZzZzZnLnLnLnLzZ0珛0x#AY% rUQ^D6]iSyC+_"#]eTMNHN] a monsterQQ6class InvasionDeathMessage extends xDeathMessage; QBclass DamTypeWarlordRocket extends DamTypeRocket abstract; Qe// ==================================================================== // (C) 2002, Epic Games // ==================================================================== class TAB_IAInvasion extends Tab_IATeamDeathMatch; function InitComponent(GUIController MyController, GUIComponent MyOwner) { local moNumericEdit a; local GUINumericEdit b; Super.InitComponent(MyController, MyOwner); Controls[14].WinTop = 0.765899; Controls[15].WinTop = 0.823138; Controls[15].WinLeft = 0.121094; Controls[11].MenuStateChange(MSAT_Disabled); a = moNumericEdit(Controls[16]); a.SetValue(class'invasion'.default.InitialWave+1); b = a.MyNumericEdit; GUIEditBox(b.Controls[0]).bReadOnly = true; } function WaveChange(GUIComponent Sender) { class'invasion'.default.InitialWave = moNumericEdit(Controls[16]).GetValue()-1; class'invasion'.static.staticsaveconfig(); } function bool ConfigClicked(GUIComponent Sender) { Controller.OpenMenu("SkaarjPack.InvasionWaveConfig"); return true; } QSuR%S 6Z% L>&S OOL?&S  G _z3bܶq r P]('%o was utterly destroyed by a Warlord.Q])(A Warlord fired its rocket prematurely.R])(A Warlord fired its rocket prematurely.QQ~class DamTypeSkaarjProj extends DamageType abstract; static function GetHitEffects(out class HitEffects[4], int VictemHealth ) { HitEffects[0] = class'HitSmoke'; } QYu%Y  G cgF3e1"Y%p P]%o was scorched by a skaarj.vt$@FQQQQXu%X  G Q}class DamTypeKrallBolt extends DamageType abstract; static function GetHitEffects(out class HitEffects[4], int VictemHealth ) { HitEffects[0] = class'HitSmoke'; } QQQJkJ 6w* 333? >*!G$@!: GQQQ@class DamTypeBruteRocket extends DamTypeRocket abstract; QU pUV,D?,,R?, "=, R$q.,q} zR&sq.%s-' Gly3n  r P]#"%o was blasted by a Brute Rocket.Q]'&A brute fired its rocket prematurely.R]'&A brute fired its rocket prematurely.QQcclass DamTypeBelch extends DamageType abstract; static function GetHitEffects(out class HitEffects[4], int VictemHealth ) { HitEffects[0] = class'HitSmoke'; if( VictemHealth <= 0 && FRand() < 0.2 ) HitEffects[1] = class'HitFlameBig'; else if ( FRand() < 0.8 ) HitEffects[1] = class'HitFlame'; } Qmqw; zR.,x& z GQQL"C"B] Initial WaveV'rWK]Choose the initial wave.{$,R?@$?M$>C$u=~'\wQQt@QQ@QQTszY Sst O Ar* &-' GQ@QPAZ2 *wTIzZzZZZnLzZzZzZzZzZ: 8 zZ: 8 zZzZzZZZ0珛0x#Z " PZ$Q "< Z$Q "i Z$?Q " Z$?Q " Z$?Q " Z$?Q " x$?Q " x$?Q " x$?Q "TG x$?Q "8 $?Q " $@Q " $@Q " $@Q " $@Q "$@QAY7VWXYZ[\]^_`abcdefghijklmnoE{$L>@$>M$?C$ff&?QQ@QQxQQ{QQCQQ}QQBQQ@Q~QAQQwQQQQQQQQDQQzQQQQGQQJFO$s  -y -y({9=xx&@!}6@9:{ zCA#}9:6~9:{ zCC6M9:{ zCz,z,.z(Y9>C.zz-y' GQyEEDx.,@.,A.,.%x-'-y'x}& GQQQQMTN=W -y 6~xx& zC9=Ax z GQQQL|GZ:-yr.|* }%B,|B,r.BX}BB.6Mxx& zC} z GQpHU#3%VSkaarjPack.InvasionWaveConfig' GQP+// ==================================================================== // (C) 2002, Epic Games // ==================================================================== class InvasionWaveConfig extends UT2K3GUIPage DependsOn(Invasion); var bool bInitialized; var moNumericEdit WaveNo; var GUISlider MySkill; var moNumericEdit MyTime; var Invasion.WaveInfo Waves[16]; // Defaults function InitComponent(GUIController MyController, GUIComponent MyOwner) { Super.InitComponent(MyController, MyOwner); WaveNo = moNumericEdit(Controls[4]); MySkill = GUISlider(Controls[6]); MyTime = moNumericEdit(Controls[7]); GUIEditBox(WaveNo.MyNumericEdit.Controls[0]).bReadOnly = true; bInitialized = true; WaveNo.SetValue(1); } function WaveChanged(GUIComponent Sender) { local int i; local byte Wave; local int wMask; if (!bInitialized) return; /* struct WaveInfo { var int WaveMask; // bit fields for which monsterclasses var int WaveMaxMonsters; var float WaveMonsterInterval; var float WaveDifficulty; var float WaveDuration; var class WaveFallbackMonster; }; */ bInitialized = false; Wave = WaveNo.GetValue()-1; MySkill.SetValue(class'Invasion'.default.Waves[Wave].WaveDifficulty); MyTime.SetValue(class'Invasion'.default.Waves[Wave].WaveDuration); wMask = class'Invasion'.default.Waves[Wave].WaveMask; for (i=8;i<24;i++) moCheckBox(Controls[i]).Checked(bool(wMask & moCheckBox(Controls[i]).Tag)); bInitialized = true; } function SkillChanged(GUIComponent Sender) { if (!bInitialized) return; class'Invasion'.default.Waves[WaveNo.GetValue()-1].WaveDifficulty = MySkill.Value; class'Invasion'.static.staticsaveconfig(); } function DurationChanged(GUIComponent Sender) { if (!bInitialized) return; class'Invasion'.default.Waves[WaveNo.GetValue()-1].WaveDuration = MyTime.GetValue(); class'Invasion'.static.staticsaveconfig(); } function CheckChanged(GUIComponent Sender) { local int mask,mod; if (!bInitialized || moCheckBox(Sender)==None) return; Mask = 0; for (Mod=8;Mod<24;Mod++) if (moCheckBox(Controls[Mod]).IsChecked()) Mask += Controls[Mod].Tag; class'Invasion'.default.Waves[WaveNo.GetValue()-1].WaveMask = Mask; class'Invasion'.static.staticsaveconfig(); } function bool CloseClicked(GUIComponent Sender) { Controller.CloseMenu(); return true; } function bool ResetClicked(GUIComponent Sender) { local int i; for (i=0;i<16;i++) class'Invasion'.default.Waves[i] = Waves[i]; class'Invasion'.static.staticsaveconfig(); WaveNo.SetValue(1); return true; } QIKNDZT -y 6@xx& zC@  z GQNSRh W' GQTQQQB]Configure WavesP]SquareMenuButton{$n?@$>M$l>C$̌=S'\Ub'UiQKLC$?DEMQKLC$?DEMQKLC$?DEMQB]Configure each wave...QZ*~{$<@$;M$>C$BDQL"C"FJ$?B] Wave NoV'ZWK]Select which wave to adjust.{$>@$>M$>C$u=DE~'wOQKL{$k>@$@$=M$>DES'\f]'\d_'\^~'wNb'\ij'\lQL"C"FB] DurationV']WK]! How long should each wave last.{$>@$Y?M$>C$z=DE~'wTQFJ$fff?B]PupaeV'^WK]The Skaarj Pupae.{$PU>@$=M$>C$ #=H"DE~'wGQFJ$fff?B] Razor FlyV'_WKMThe Razor Fly.{$PU>@$ ?M$>C$ #=H"DE~'wGQFJ$fff?B]MantaV'`WK= The Manta.{$ђ>@$=M$>C$ #=H"DE~'wGQFJ$fff?B]KrallV'aWK= The Krall.{$ђ>@$ ?M$>C$ #=H"DE~'wGQFJ$fff?B] Elite KrallV'bWK]The Elite Krall.{$?@$=M$>C$ #=H"DE~'wGQFJ$fff?B]GasbagV'cWK] The Gasbag.{$?@$ ?M$>C$ #=H" DE~'wGQFJ$fff?B]BruteV'dWK= The Brute.{$!"?@$=M$>C$ #=H"@DE~'wGQFJ$fff?B]SkaarjV'eWK] The Skaarj.{$!"?@$ ?M$>C$ #=H"DE~'wGQFJ$fff?B] BehemothV'fWK]The Behemoth.{$#?@$=M$>C$ #=H"DE~'wGQFJ$fff?B= Ice SkaarjV'gWK] Ice Skaarj.{$#?@$ ?M$>C$ #=H"DE~'wGQFJ$fff?B] Fire SkaarjV'hWK]The Fire Skaarj.{$2?@$=M$>C$ #=H"DE~'wGQFJ$fff?B] WarlordV'iWK] The Warlord.{$2?@$ ?M$>C$ #=H"DE~'wGQFJ$fff?B]PupaeV'jWK]The Skaarj Pupae.{$@?@$=M$>C$ #=H"DE~'wGQFJ$fff?B]PupaeV'kWK]The Skaarj Pupae.{$@?@$ ?M$>C$ #=H" DE~'wGQFJ$fff?B] Razor FlyV'lWKMThe Razor Fly.{$O?@$=M$>C$ #=H"@DE~'wGQFJ$fff?B] Razor FlyV'mWKMThe Razor Fly.{$O?@$ ?M$>C$ #=H"DE~'wGQB= DifficultyQZ*~{$>@$>M$>C$BDQB]CloseP]SquareMenuButton{$JD`?@$?N?M$>C$BDES'wRb'oiQQ@QQt@QFwOw&-9:9:$9:9:$G6r- =1 ]eTMNHQQQQz@QQsQQq@QQr QQuQQmQQ}QQKQQNQQ~QQ@QQ@TDGS3Cbsx@zVGBk ]eTMNHN] is OUT!WY$ff&?QQPclass InvasionMessage extends CriticalEventPlus abstract; var(Message) localized string OutMessage; // // Messages common to GameInfo derivatives. // static function string GetString( optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject ) { switch (Switch) { case 1: return RelatedPRI_1.PlayerName@Default.OutMessage; break; } return ""; } Qclass InvasionGameReplicationInfo extends GameReplicationInfo; var byte WaveNumber,BaseDifficulty; replication { reliable if ( bNetInitial && (Role == ROLE_Authority) ) BaseDifficulty; reliable if(Role == ROLE_Authority) WaveNumber; } QB|Uv-| $&}B'  G Q vpI Mp9? vpp9:.rr%, A9S9:.rw& pavzs%q 9S  %q 9SR%qpq Mqq Mp.-q  .-q  .Xt G.-t p@~ @spatusp>?pup @sp at'qtpaqusp<?pz@Ns?p av'p>?pup?sp aq' GQ@QQIQQJQQQQLQQoMG8ALMwL*w.L*La/!~La/!}.LL9?&LL GQuHe$ R.rw9=.rr9=9D-'-'-' GQMQQQQ@QQQQsQQ QQQQU @QQvQQQQQNQy@QQz@QQ}@QQuQQmnqQJnBnInitialWave9SRBnFinalWave9SF GQ`QQMxs/!`xyz]w]*w]*]-]-`-'`&]]/` GQ= class MapListSkaarjInvasion extends MapList config; QSm{@$!u,S{uv GQRBop% am%=m,m zCmwm zx}&' GQCQQmQu6DQcH ]eTMNHQB]ResetP]SquareMenuButton{$JD`?@$ ?M$>C$BDES'wob'EiQQL@QQvQQS@QQQQX@QQI@QQK@QQQQvQQR@@QQpJ[VJ InitialWave%K 5FinalWave&K HWaves,K ^Monsters,K rWaveNo,K WaveMaxMonsters,K WaveDuration,K WaveDifficulty,K J G(QPHeWYcTzHbBalanceTeamszHbPlayersBalanceTeamszHGoalScore(H G(Q|I\[RIIAbInitialWave@InitialWave$2$Textp2;0:9S,&I:bFinalWave@FinalWave$2$Textp2;1:9S,I6bWaves@Waves$<$Customp;;n ' G QQQtcU  GQ Sk %-A' GQv 8Q W|  GQ@QQQQQQfQQbQQQQ\QQ^QQ]QQQQdQQ_QQQQo@QQQQsQQ@QQjQQgQQcQQh@QqQCQQO@@QQ@QQRe@[Te InitialWave%L 5FinalWave&L HWaves,L ^Monsters,L rWaveNo,L WaveMaxMonsters,L WaveDuration,L WaveDifficulty,L e G(Q@QQxiD. a% !Qijg>k#?ci_j@@^kc\_^$\9?%b__ \b\\b^^\_!\?^_/\d^^d g GQkQQP[JF[-f-f[ ~!x[ ~!a[ ~!r[ ~!C[ ~!B[ ~!v[ ~!@ G QeQQ/Ea1 '  '  GQQQUwE&(( GffctcLcOcyLOc{XckCM cJcCcLfxfLxfzcUcccScPf"LwLtLwckSb cdcYccSsSZcLSscwceLP[ecP Sp lCH cx cyCK L@cfCz cNf{cPfcsc@Lw ={A cycOctc} CL cFcbczc^SI SE f]CLcqf^ YQ fjfrcOUeCuzCcJYdf{EAcOL~c}fsL:f[cmY[Ef fg c{CvLBUhLHU~cqfaFh cnfrUSc[SSFcCvf`SifZLzfAUz fp LcUcFpLY LSYtcpSwLffi Ys [QcvLn[wc{c_S^ ~UM[bS]S@ LK U[ ScCp {l chc Se cV{TcXUa E{+cK{$cpc`CucPSe UoL~ U\SgUs{rCpcgSh {wcuFp}Z{U@g NL^SCfINYRfkfdcEcN {W{V{BN=j cjS}UPUAUBfNN;Ff_U|_hSdCPc`{Ycm_SU` UUUjc\SL{SL Yn Svcy_pcl Sg _ZYu _FcC_E_aUYcJjft__zr{D NmJqNh @oNn {;{ScHFISP[wY czfy Lq JKLt SxSASJFxS_fz c[SxUjUUU` {nUa U~ {tUO{oSJFsc^_JSIUu {@{ucUc[_YI|IBSGIANE[o SVIHSAU@ICLk _c_V_Q_PU]_E_B_}Sh_zfDcIcd_`_X_W{Z{w_V{s_UcF_M_L_H_G{$Y]Y\{E[wk _q[wZ _n_m_l_k_j_X{b_WIFN}N@Uq cID{{{D{H{DN~NUwJycO [GIGLJ UpCk UwCv {hISn {EUC Sk@n@T@SFQ@R@QUa~{Um L]SB{BUB YHS{LvtcYL^J LKEfU`cJ@Clchc_cEcocQF}SrcrUv STzs cvcwSuS^cDcJ L:LZ cH }{~{X{I{${TS_ U[ Y`LX YV {G{sYU LJ{Q+{QB{@LO {JYS UP Ejg{z_b{CYwLO {qLI cS{nc|SHSj {\{[{eNFj IEo Ni UCUfk f{}{DIU{^=| ^ w YRLG f{ Lmcc ca {HLO {JF)d {Agc_ c` {Pccc_@^[Kcb Ut cd ce cf Uefr _'q [r f} [q ^x Up Lu_$v _t _s {iU_ u UM{EUdUb UDNm UEIl @YK[Ff~ _ fD f E fG N F Uw Uv {[{G{s{c{DY|{rYi{P{_{Gc} I YkY[{XUbF@ YR {cYT {]J`SW JAJiJgSkS\ L] L^ Un{g{cU|{C{/{a{_SS|USS{{SSr{B{Y{@U}U{{C{EL\ {q+@Kc@cm cBYX cCcDL[ St{}LHH{X{I{XSD{r SsUASbSc[w LPCp[}FnCu{uSyLNUk{JlJmJ)JDS@JJ L,_YqJ{tS_@PJ*JLC@_J[@U@vLf @R@\@Z@w@|J`Jk{I{~@b{@bBJjCp {hA{h@x`kx`gx`ZxQlxPxx^fx^vxQWxPTxbRxboxbix^Xx^Vx`Bdx^BeUG UH C@ J}JuJfUlQ cL Ju{E{v{G{L{x Sy {TiJ_Ic_i^_V{N{cD_Z_[_\_]{wis{Y{G{C{k{K_o{F{J_r{SiN{{|{z{I_IcM_K{C{\{1i\_T{O{N{(Up_Yc|_[cQib_^chUrLbUqLt_x_yLg_{_|Ys_~__@_ASi_CfA UgiG_0FcKfX[RY{_RcN_T_U[D_Wi[_+Y_ZcN{zeCC_d_e_f_gccE_o_d_q_j_~_p_}_h_i_wC WkZV ji xZ HmWu fi u\D_R M ``oc~nMsty]u VTfg uk D] T`d] ri A<Qs amo^~P MY\j kd z\JPJeT]cxr$_B$V aaDoW s_CCSaaWph~i LQ=\[kj^izIHk W\ ge wa FQt VF e{ntfCo R_ a<<ph @O P!#^_ AQs a^ qn Ag\P S l A {=EJi Y J iSx=lG=FVP eg unEL Sbclq0"AUc} rL @^Pm`-<n@ ~T NQ<^tinR}Z Ki [D j[ zP I|Y | i| xc^HZ W} g^ v  F` UcE d|_ tn Di TU d L s ]A o P R ` T oagNg\yj` { JoYE)h| QS _Zn{b }x _3HnQPNA ]nPm} }n Ku [0h in wtd GGXE h U wtL Fg V,L fQ udDo S{ c` sF C2N Sgcbq.;@c{M JmYN iC yF H RWJ f FweF { T P cn rb A Q Od^-_ n,e ~V NQ]W lk{V JhVIY} oV ~<_ M^]QmmO|<y L<G\yke yD GTW]E fg u`ET Tr bopXlA{^mo}cN NV ^@lny Z|a iY w[FX T]dU t^C] QU `>Koe}K Mi ]mln {K Jn[qkMziHVWC gLwS F1NTo bpr} @C Pl `g ph @h Pg `ipL QV Nn \z kn yXIWXVi bbxQd Z|iMyy Jo YZh wGPGg Un e^t&KCg Qh a&Tqo @ } P B _ qnXk} @ hd wS G E VdeZjt D ^SY n|}'Y M'y\'_ m b }-h M-y ]0g m-G| X K Y [<h jc z3GI9DXWo gjE wjlG\FVrjf]bPK<ry BXQP_ _c_ or}cd |YqLC }Pd M Ft\{d PW`jCo{d ~{_ N\C ^f_ m{h }f^Mjy\lm{g Y{b i4hxkG W V V e`It N }Y` L2$v\2^R I bwZ+&qqWW#i nC |_ w3LA9!f[n e H tl C b2R`DAh SAg cAb s{ C URU~ab_jamte {3IJm Y$3$3gq Z:n itlxwHd XN htc xs<JW Zjb {'CJg Yh ib yg Ih YNi$vxSFn Wn fCu{ D[S| dd sd Cb STb^ q` @QQ_ Pg `h p_@b Pd `aph lOt ^^nR }f M_ \u l F$|A `n o~ ~] N<]GmK }_ MH\ m^ ~n Nd ^\ m^|>] L_ [R ky{QLl[jjXyPHE W\fjtF BO P^_:od  O)OJxX Gh UCeT tr Cn RGayq_ Bo Ru aU q ug@b g_ vh FyVh g<wb Gr Wo fMtLCtRI aR pd s\N IQju{<J_ Zhjy 1IJG Sbo sr AvP6`<o| r No ] x&k_Qfao pr ~n MN \`lb {lKd Yb ig yh IQQb Y i Qqvd gn va EHT c.cQ\Q~___ n~| ~yM ] kh Dr To cZqb @g Pd`h qg Ab Qh a~~ q {&A%A g%yv\TG&u [&K kK z L+Iu t1g D1h TtM dR t_ D9<TyzNd37rk @;n N9C] y1kV{\sI WWV` f&v C \@i kYgz_+)aRJ_ x2Y97K9[Y q(j]]R[UoUd DT} SYa b^gq2;GX_`_C_ jR OCd _4_ n]C~`Y M4d \LiDl`ypc ApR V$bf FM Tf df r`m A<P E4`W<TFjtd @X]i XlRgmryjTkW IvMfd [LpKkB_ vFqzFrs@@l@Id NfB]] mV}fAKu~Zj_ Xv{hK_ cN_ r{Q A;A P~x__ nL ~{c NyB^|` `jh o|B  ybN Zrp~K b JYqB J RQY ^8jB b ]Rp {bBWB d GJrB | YaJ msk GE^ o>c D8a ,BYPC[PyjPi {PB J NQX EAiB j $Gy \<@ ZM| G I :X z  R zZ] y  w TbB uTd vx w  n @mz v  g GK\rB Nj\ OCkB n NA|B } ~eK GNp"B ~ @fLB r HZ@^1\\ZB vb1.AEA1!VFV10n\A J1VSZS1@St{B oA>!P~V>0)NA w>V0FA v>W+E NCplsB BN>XPQ `Q a>\+rS ]B>E.l$>$*ZV D B S ^kPa ^lPq B A b P B ` g o b^.3 ^m'r V^01Y A J ^VJY A c ^WLr ^n)~ B^E)g B P ^~Q_ A^!Hp $Y$1x $d$1i A!BZ IP\ JPl {Y~| H5z G0o NXx_ KCW B Z b.Wi B @ WqRN V0i` B I l$3X 3^HK 3_IS V30r\ I3`N {3YAn Cio S3@VX &Gn 3a'~ l3e *B X LGg 1B n ,B } 1b L mD\ b3.?` 0B _ 3`Po x) 4j h S+v 6o a 6B r GS@ A3!}S [*!WP 9Q0g AB W 3B f o R_t ^?FCS b9.vV s?f'L BB s ?+*A ?_8k ?Bac ?r{D ?T) m-h 9v-U 9u-B 9tNo V90a} _X^ y rGv Oo[} [+XX Yzp i'j eXQ [I>i TB g Z `u WoVU AWQNk [L#y 3\ YB O P WVg] a[ D ]y_ ZFp ]B v B*E Wjxo fB g cB v %^E Cjc ;yM y^ WYbn CP mB _ v'm T9T W0M c1} WB n \6| dW1=r /No sB } VW+0K Wt){ WrKd B o B ~  &r L [{{&~ |B d A{bIr b { B K C%Y ^{II~ {OrG B y \G B W {+>e {tLc {zBo Lq {_h A z$i B M O)[ rD guS {UfH B n M$| B ` B o D$} B a G$o #S {BCv {rwy B p B ~ Z tJL ox&V B | o\J ~FJf n p V+)@ _G i E%p An U ~t3d B@X +GX i`_ B  Z3N B A ncO rKr [u} B r  os@ W E&s <Y B i O'w B ^ ~FXl qCD |qFG YmM pZz ohT Gb| B ^ @L m +*y MuIc QB l m| B K ]Q[ B l i { B K yf[ s(A B i H y c+H PBs Cxu Ga m \@N BXN rKf  !Cq O&t B Z ^F)h i Q Z 8` t)X +DA Fc/E B t BFC QQQQQQQQQQQQQQQ -1I X$z f ^ }%m CR f a _ q R A yQ Ab%a ~ F | V _e XGt Xj{  K2e bL3W B J ]s9X B Q B ` +7n Bhe M M I \ r|l _/h QB W [ e DOs cMB =Ah O &C[w B R 1gj` fo 4>J =@I H 0o 45Q 1o 4NCF 2o 4]T o 4Mq o 4A~ 6o 44 o 4oFs o 4Ab[Zco 4q} o 4}no 4^k=ISI=o 4A\>o 4g%]2o 4|D2T;@Bo 43{3o 4o>nDo 4B]Po 47_&o 4BV2SZX/o 4Irci{1o 4R degvGo 4F]Se$9cC e;[\o 4owLo 4CfYo 4_i=o 4D?H$_LTlkB4WSCKky N\o 4@G]o 44G^o 4U!{bo 4DP;PTdo 4adBd Eeo 4OTo 4\c\T>\$M}\I-Jlo 45w\Sglno 43S9o 4PF \t@Vwo 43V\;[I*o 4Wd x  {anF=aWmtfmUwHIHgnQdnba]s|GAIhHD|p]o 4Albo 4Nm\o 4q{[uelziQeo 4JzWu-DFLqd]}eu-KWo 4Ixg]AGJMOjp\no 4Lk\Cwy`z[o 4oZ\wNIpsW\ujgu}Qu{`zXn=FZMTwBaw\rV A}Ps`Jq@OwA^w@oc@|NU\tls}B Ns\wOfmwHSs[wTdksOwGm_\UPLwo 4\+\wNax wR1Y sJ B [ \aWi wb@ wC^ wd| weKZ wf|e wg3a whCT wiyW wjhP wkhx wl`` wm`@ wnm` wocM wp`p wqcP wrgs wsfZ wtm@ wuem wvhR wwhz wxhb wyhJ wz>r w{Up kE `U OKd u o u} eL l[ Aj QQy| LK f\ ]k b{ QWJ PY Ng SPu o 4\E Qo 4Ka UGl Ie s B X mf cv JF o V Gff H|L QQQQQQQQQQQQQQQQQQQQQQQQQQQxH QV W ze Qyu mn E BV mk g nw CF yU we Gu }D {S _ b q\r B N sC^ o 4Ha S9@