*~P@))X >AUaD9|-muPNoneVectoroffsetReqMinHitDistanceDamageEmitter BoneName bCompletedSubDamageBones DamageModbFatalAdvancedEmitterMinRangeMaxbAdded AnimNameObjRef UpdateFocus RForearm DebugLog LForearmEngineCoreBD2004ZombiesRunF DOTZEngineBeginAdvancedEngineUserMeshXDOTZCharacters RUpperArmAdditionalAnimPkgControllerClass RelativeTime LUpperArm BodyTexturesPerform_Error_Stop HeadTexturesUnClaimPositionbAlwaysRelevant ShouldStrafeDONE GroundSpeedX RangeVector shouldWalkYZCollisionRadius BeginPlaySpecialDamageRunRRunBHealth bCanStrafeBaseMovementRateJawRunLJumpFNamesRCalfJumpBLCalfHeadJump BeginState WalkingPctWalksAnimWalkForward AnimWalkBack AnimWalkLeftJumpLJumpRAnimJumpRight AnimJumpLeft AnimJumpBackAnimJumpForward AnimJumpAnimWalkRight SameTeamAs KeepGoingEnemyIsVisible RelativeSize SeePlayerMove ReqBonesMeleeAttackAnimFindBestPathToward bFunctional DamageDelay EndState HunterOtisClaimPosition DeathSoundsHunterJackSladeExclaimSystem TimeElapsedSetInventoryLimit StopFiringEnemyNotVisible Swimming GiveWeaponInfectionEffect isBehindMe AttackingReachedDestination TakeDamageSetCombatTimerEngaged_ChargeDEBAIT TrySpotEnemyPerform_MoveToLocation SetPawnClassTimedFireWeaponAtEnemyRelativeVelocityTextureWeaponFireAgainMaxShotPeriodFadeOutStartSizeRange MaxAimRange IsFiringMaxSecondsOfLOSSetInfectionStateMinShotPeriod MaxNumShotsPossessLifetimeRangeSpinParticles GameplaySecondsBeforeInactiveInitialParticlesPerSecondAutomaticInitialSpawningRespawnDeadParticlesSetFocalPointNearLocation UseSizeScaleUseRegularSizeScale SizeScale AcquireEnemyEngaged_OutFromCover UniformSize ZombieOtis OnHeardNoisePlayerControllerReport_EyeSighted NeedToTurnWillFriendlyFireBDHunterAIControllerNotEngaged_WanderOnThreatSpottedMoveToLocationEngaged_GetLOSDoInfectionDamageEngaged_HideFromEnemyVerifyShootingPositionWeightStagePositionTryClearObstacleTimerEngaged_HuntEnemyEngaged_AdvanceMove iRandRangeUseRandomSubdivisionTrySeekTarget MultiTimer CanAttackGetLocalPlayerControllerSeeking RefireRateBotFire MaxParticles ZombieJackDMColorStartSpinRangeSpinsPerSecondRangeReport_UnSightedEngaged_RecoverEnemyContinue_Engaged_GetLOSStartLocationShapecalculateStagePosnLOSIsBlockedContinueWanderFadeOutStartTime DrawStyleCleanupNotEngagedWanderFailedPerform_MeleeAttack ChanceToHitUpdateEnemyInfoContinue_Engaged_TakeCoverEngaged_TakeCoverContinue_Engaged_OutFromCoverContinue_EngagedChargeContinue_Engaged_HuntEnemyAttackTwoHandcalculateStagePosnBlocksLOSCalculateMissVectorScalePickDestinationContinue_Engaged_AdvanceMoveGetMaxFiringRange FireWeaponAtWanderSetRandomFocalPointLocationFollowOrder_NonePositionHasLOFToEnemy Destroyed DoSpecialStartVelocityRange EndChargeDoDisinfectionInitWorldToScreenPerform_Engaged_StandGround DisplayDebug ChargeFailed SelectActiongetAttackPointOnBumpFixFOVPerform_Engaged_TakeCoverPositionProvidesCoverFromEnemyPursue bJumpCapableIncrementFlashCountRequest_HidingPositionReport_EnemySpotted AttackRightB Scripting OnKilledTakeFallingDamage PlayTakeHitTouchingWaterVolumeSetRelaxAttributesDormantFollowOrder_TakePositionFollowOrder_HoldPosition OnHitMover leaveStage OnHitWall ShakeViewFollowOrder_TakeCoverWeaponStopFireCrouch PlayFiringBringUp AdjustAroundDistanceFromMeMinimumDistanceToThreatDistanceToDeadBuddiesDeadZombiePawnBaseChangedWeapon GetLOSFailedTakeCoverFailed debugTickDiedbCanPickupInventoryNextOnTakingDamage OnLostSightPutDownOnWitnessedKillIsHumanControlledNumberOfLinesOfSightToThreatsOnThreatHeardRUNXDOTZPlayerControllerProperDistanceToBuddiesNumberOfLinesOfSightToBuddiesDistanceToNearestFreeCoverSpot AmmoNameAltFireAnimName HitAnimNameCrouchIdleAnimNameStandIdleAnimName FireAnimNamePawnAnimationPackageBlockingLineOfFireOfBuddiesBlockedLineOfFireByBuddies"ProjDistToBuddiesAsSeenFromThreatbDamageKicksViewbAlwaysPlayDying AimToMissGetTweakedFireSpotRagdollLifeSpan AimToHit ItemNameNotEngaged_AtRest BloodEmitterPerform_NotEngaged_Wander InitAIRoleUseColorScaleSpawnExclaimManagersetLastHitTime AttackLeftBPLAYER_INFECTEDPerform_MoveTowardPositionStartLocationPolarRangeMoveToPositionContinue_MoveToLocationEngaged_StandGroundUseRotationFromisTargetDestroyedNotEngaged_FireAtTargetPerform_Engaged_RecoverEnemyInfectedEventGETLOSSTANDUP PostLoadFinishedStrafeFLEEEngaged_SupressionFireTextureUSubdivisionsTextureVSubdivisionsPostNetBeginPlayTravelPostAcceptUseVelocityScaleVelocityScalegetWeaponFireRateTestDirectionPickStrafeDirEngaged_StrafeMoveKeepPanickingEngaged_Panic Error_Stop drawCoverHitHeadbInfiniteAmmoPickClosestBetterSpotSetCombatAttributesiMultiPlayerCorpseSecsbNoDamageEffectsPath DontBeStupid EAT_STUFFSMASH_OBSTACLE StartChargegetSmashActorgetChargeDurationBDHunterAIRole ShouldCharge OfficialName GetHitAnimFall StandIdlePostBeginPlay PlayHitSound ViewKick FallDownSetTeamAttachments CalcHitPart NearWall DeSelectPostNetReceive CheckShadowSetBoredomTimer WaterVolumeAdvancedPlayAnimHidePhysicsVolumeChangePerform_Engaged_HideFromEnemyBDPlayerPawnBaseBDLeadPipeHunter DestroyPawnLeaveScriptingBDHuntedInvasionbLightChanged bSelectedBDGlockHunter BDBatHunterZoneiLeaf ZoneNumber DebugFlags BDAxeHunterRegion MeleeWeaponGiveDefaultInventoryBD2004ZombiePawnBaseBD2004YoungAdultMaleBD2004YoungAdultFemaleBD2004TeenagerMaleStdDamageAmountAltDamageAmountGetAttackLocationBD2004TeenagerFemaleBD2004SkeletonMaledoSmashBD2004SeniorMale CrouchIdle CrawlIdleBD2004SeniorFemaleBD2004NurseFemaleBD2004MiddleAgedMaleBD2004MiddleAgedFemaleBD2004HospitalPatientMaleBD2004HoboMaleBD2004HoboFemaleBD2004GoreMaleBD2004GoreFemaleBD2004CopMaleZombieFistWeaponOtisZombieFistWeaponZombieFistAmmunition AttackLeftC AttackLeftA AttackRightC AttackRightA CrawlBoredinfectionFreqinfectionDamageBoredCrawlingMeleeAttacks MeleeAttacksSpawningSoundProbabilityTick HearNoise NotifyBumpNotifyHitWall PreBeginPlay DOTZGameMinWanderDelay AttackRangeMaxWanderDelaySpawningSoundSoundsLastSeekRefreshTimeMaxChargingZombiesInAttackRangebAllowedToChargeMaxTimeBetweenCharges ColorScale ProbabilityWeightBoredStandingAnimListBoredCrawlAnimListMinTimeBetweenChargesMinChargeDistMaxChargeDistSoundFootStepSound FootStepDirtFootStepMetalFootStepPlant FootStepRockFootStepWater FootStepWood bDoFootsteps HitSoundsPitchCrawlAttackRightCrawlAttackBiteCrawlAttackFall AttackBiteBDMiniExplosionAIType PreSaveGame SetCreator ActorToFace Perform_NotEngaged_FireAtTargetmaxDistToCrouchForCover RecoverEnemySO_AttackTarget SO_TakeCoverSO_HoldPositionSO_TakeUpPositionSO_NonePerform_Engaged_SupressionFire lastHitTimePerform_Engaged_ChargePerform_Engaged_StrafeMovePerform_Engaged_HuntEnemyPerform_Engaged_PanicDOTZGrenadeWeaponDOTZGunWeaponPickDestinationOldCLAIMED_POSITION_PENALTYPerform_Engaged_AdvanceMovem_NeedForNearbyGoalm_NeedForClosingInm_NeedToAvoidCorpsesm_NeedForIntelm_NeedCohesionm_NeedForContactm_NeedForCoverm_BlockingPenaltym_BlockedPenaltym_BunchPenaltyEnemyDistractDistanceEnemyDistractDurationOnRegainedSight DrawHUDDebugMaxAimVelocityOnEnemyAcquiredTakePositionSuceedederrorStopSucceededadvanceMoveSucceededPanicSucceededHuntSucceededstrafeMoveSucceededVolumeChargeSucceededTakeCoverSucceededsupressionFireSucceededoutFromCoverSucceededHideFromEnemySucceededGetLOSSucceededRecoverEnemySucceededFireAtTargetSucceededStandGroundSucceededMoveToPositionSucceededNotEngagedWanderSucceededNotEngagedAtRestSucceededacquireEnemySucceededawakenSucceeded MaxSkillOdds MinNumShotsbRangedAttackCapabledrawEnemyTargetPerform_Eating GlockWeapon M16WeaponRevolverWeapon RifleWeaponShotgunWeapon SniperWeapon ReflexTimeMaxLostContactTime drawPosnLOSdrawPositionWeightsdrawExperimentalGriddebugDrawVerifiedPositionsdebugDrawFireConedebugDrawBlockedLOF BBParticlesdebugDrawBlockingLOFReport_KilledReport_EnemyLost OpponentDiedVGSPAIControllerfOddsOfStrafeMoveMinTimeBetweenStrafeMove FallSoundbUseNotifyFootStep KilledByNotifyHitMoverPrimaryFleshImpactSecondaryFleshImpactPerform_Engaged_OutFromCoverBotSelectAction TriggerEvent SoakStop LocationOrder_TakeUpPosition joinStageOrder_HoldPositionOrder_TakeCoverOrder_AttackTarget Order_NoneRadiusStageOrder_AlertNewEnemyPerform_NotEngaged_AtRestStageOrder_JoinStageStageOrder_TakeUpPositionStageOrder_AwakenReport_InPositionMoveToLocationSucceededMoveToLocationFailedKarma Lighting CollisionForce LightColor MovementResetHasAmmo PawnDiedStageOrder_HoldPositionRestartPlayerRequest_ShootingPositionbUnlit FreeScriptStageOrder_TakeCoverNoLongerOnGoodPosition FollowOrderRequest_PercentEyeSighted AdjustAim Emitters AutoResetRestart AssessThreatTimeTillResetRange NotifyKilledStageOrder_AttackTargetStageOrder_NonegetLastHitTimeReceiveWarningdamageAttitudeTo InitRoleInitGameReplicationInfo LoseEnemy WarnTarget WasKilledBy LostContactClientSwitchToBestWeapon PickTeam ClientFlashStageOrder_Hibernate PointRegion Acceleration configurePerform_Engaged_GetLOSReport_TargetDestroyedTeambAllowedToSwitchTeamsfellYPosSetPosDrawDebugLine SetDrawColorAnimBlendParams PositionbAdjustFromWalls DrawText bCanJumpbStasis SightRadius DefaultFOVJumpZ MinHitWallShakeStagePosition MaxFallSpeedBaseEyeHeight aimerrorKilledOldPos PathList FearCostDestBotLookDir bAdjustingbEnemyInfoValidfPhysicsbEnemyAcquired bSoakingbHiddenCollisionHeightbFire bAltFire AdjustLoc MoveTimer MoveTarget FocalPointFocusEnemyTarget LastSeenPosFireLastSeeingPosWall LastSeenTime RouteGoal RouteDistMonitoredPawn HitActorEffectiveSpeed bestDistFireDirpick ScriptTextACWjiS projStart Rotation Velocity ReturnValue RandRangeTimeFiredAmmunitionIdaPawn KungFuModeiStateWeapon LevelInfo GameInfo ReachSpec ProjectilePlayerReplicationInfo bInfectedthreatGameReplicationInfo Ammunition VGSPAIBaseOpponentFactoryExclaimManager AIControllerDebaitAdaptersScheduledExclamationAIRole SpecialFXSmoke ExplosionSunspotDOTZXDestructionDOTZXCharactersPlayerPainSounds NoiseMakerSeenshooter projSpeed Controller DamageTypePhysicsVolume LastAnchorAnchorbStopAtLedges TeamInfo bAvoidLedgesbForcebWantsToCrouch ViewDistflagsSkillDOTZAIController DOTZAIRoleDOTZInfectionDamageDOTZMeleeAmmunitionDOTZPlayerControllerBase SkelHeadbFinishedFireXDOTZPawnBaseNetModeDOTZMeleeWeapon bLeadTargetProjectileClassbDebugLogging bDoAltFire lastState bMeleeWeaponAccuracyIndicator FireAnimbCanDamageSelf TimeSeconds PlayerOwnerCurrentVictim KilledPawn HUD_HACKNavigationPointCoronasHitAnimKillerhitPartbSuppressRoleNotificationsbCanSeePotEnemy newFocus bImmediatepotentialEnemyMaxTime NewStrength ThreatValuebThreatVisible NewThreatSideDir OtherDir superResult oldEnemytargAimDir returnVal skillOdds relVelocitylosTime enemyRight HitFriendtmpValtotal FireSpot MissVectorMissDirtargetDistance bFireSuccess newStage oldPoint oldDirVector wallDist ViewSpotbCanSeebCheckedReach bAllowDetouroldMoveTargetbogie numAvailLOST_SIGHT_TIMERUNGHOST_TIMERFOCUS_UPDATE_INTERVAL FOCUS_TIMERSIGHT_CHECK_TIMERslop exclaimMgrchargeEndTime moveSlopmoveDestinationPanicStartTimebHavePanickedwanderDestinationLastWanderTimeLastTakeCoverTime LastHideTime bStrafeDir strafeTargetLastStrafeMoveTimeTakeUpPositioncurStageOrder EStageOrderclaimedPosition currentStagenumMoveAttemptstimerID shootDesttmpDist myAIRole curBehaviourlastPawnPhysics myCreator pendingFocus maxDuration tmpTarget ShootTarget AcquireTimeminDistLoseEnemyCheckTime strafeDir VisibleEnemyEnemyVisibilityCacheTimebEnemyIsVisibleCacheEnemyInSightTime NumShotsToGobStopFireAnimationbFireAtLastLocationRandFocusChangeDuration panicDestLastFocusChangeTime bestPositionspecdpbestDP numPathsSpeed fromSpot DEBUG_FIRING oldPosition oldWeightoldDist AIDebugFlags FEAR_PENALTY PanicRangeLastAdvanceMoveTime screenPosMissVectorScalebMeleeAttackCapablegridNumcolSizesignxSignySign GoalScript baseWeight baseDist newPosition StageName bHibernatingStagePositions StageAgentsEnemies InitialStagefProjDistToBuddiesfDistToBuddies fDistToCover bIsClaimed StandLOF HoldSpot bSchedulednextExclamationLastMsgoutHitLocation BBPGibLeg BBPGibJawBBPGibForearm BBPGibBicepBBPBloodFistsPrimaryBBPDestructionBBPBloodFistsSecondary ExplosionsExplosionsGrenade DOTZTHumans JackSladeOtisBodyShaderCOtisBodyShaderBOtisJackSladeBodyShaderBJackSladeBodyShaderCOtisHeadShaderCOtisHeadShaderBJackSladeHeadShaderCJackSladeHeadShaderBOtisBodyShaderAOtisHeadShaderAJackSladeHeadShaderAJackSladeBodyShaderA DOTZTZombiesFemaleNurseBodyShaderA FemaleNurseFemaleNurseHeadShaderAFemaleYoungAdultMaleMiddleAgedFemaleYoungAdultBodyShaderAFemaleYoungAdultHeadShaderAFemaleHoboHeadShaderAFemaleHoboBodyShaderAMaleYoungAdultHeadShaderAMaleGoreHeadShaderAMaleGoreBodyShaderAFemaleGoreHeadShaderAFemaleGoreBodyShaderAMaleSkeletonHeadShaderAMaleCopHeadShaderAMaleCopBodyShaderAMaleSkeletonBodyShaderA FemaleHoboMaleYoungAdult FemaleGore MaleGore MaleSkeletonMaleCopMaleMiddleAgedBodyShaderA MaleSeniorFemaleMiddleAgedBodyShaderAFemaleMiddleAgedHeadShaderAFemaleMiddleAgedMaleMiddleAgedHeadShaderAMaleSeniorBodyShaderAMaleSeniorHeadShaderAMaleHospitalPatientHeadShaderAMaleHospitalPatientBodyShaderAMaleHoboHeadShaderAMaleHoboBodyShaderAFemaleTeenagerHeadShaderAFemaleTeenagerBodyShaderAMaleHospitalPatient MaleHoboFemaleTeenager MaleTeenagerMaleTeenagerHeadShaderAMaleTeenagerBodyShaderA FemaleSeniorFemaleSeniorBodyShaderAFemaleSeniorHeadShaderAMaleYoungAdultBodyShaderAMaleYoungAdultBodyShaderCMaleMiddleAgedHeadShaderCFemaleNurseBodyShaderBFemaleNurseBodyShaderCFemaleNurseHeadShaderBFemaleNurseHeadShaderCFemaleNurseBodyShaderDFemaleNurseBodyShaderEFemaleNurseHeadShaderDFemaleNurseHeadShaderEFemaleNurseHeadShaderFFemaleNurseHeadShaderGFemaleNurseHeadShaderHFemaleNurseHeadShaderIFemaleNurseHeadShaderJFemaleYoungAdultBodyShaderBFemaleYoungAdultBodyShaderCFemaleYoungAdultBodyShaderDFemaleYoungAdultBodyShaderEFemaleYoungAdultHeadShaderBFemaleYoungAdultHeadShaderCFemaleYoungAdultHeadShaderDFemaleYoungAdultHeadShaderEFemaleYoungAdultHeadShaderFFemaleYoungAdultHeadShaderGFemaleYoungAdultHeadShaderHFemaleYoungAdultHeadShaderIFemaleYoungAdultHeadShaderJMaleMiddleAgedHeadShaderBMaleMiddleAgedHeadShaderDMaleMiddleAgedHeadShaderEMaleMiddleAgedHeadShaderFMaleMiddleAgedHeadShaderGMaleMiddleAgedHeadShaderHMaleMiddleAgedHeadShaderIMaleMiddleAgedHeadShaderJMaleMiddleAgedBodyShaderBMaleMiddleAgedBodyShaderCMaleMiddleAgedBodyShaderDMaleMiddleAgedBodyShaderEMaleSeniorHeadShaderBMaleSeniorHeadShaderCMaleSeniorHeadShaderDMaleSeniorHeadShaderEMaleSeniorHeadShaderFMaleSeniorHeadShaderGMaleSeniorHeadShaderHMaleSeniorHeadShaderIMaleSeniorHeadShaderJMaleSeniorBodyShaderBMaleSeniorBodyShaderCMaleSeniorBodyShaderDMaleSeniorBodyShaderEFemaleMiddleAgedHeadShaderBFemaleMiddleAgedHeadShaderCFemaleMiddleAgedHeadShaderDFemaleMiddleAgedHeadShaderEFemaleMiddleAgedHeadShaderFFemaleMiddleAgedHeadShaderGFemaleMiddleAgedHeadShaderHFemaleMiddleAgedHeadShaderIFemaleMiddleAgedHeadShaderJFemaleMiddleAgedBodyShaderBFemaleMiddleAgedBodyShaderCFemaleMiddleAgedBodyShaderDFemaleMiddleAgedBodyShaderEFemaleTeenagerBodyShaderBFemaleTeenagerBodyShaderCFemaleTeenagerBodyShaderDFemaleTeenagerBodyShaderEFemaleTeenagerHeadShaderBFemaleTeenagerHeadShaderCFemaleTeenagerHeadShaderDFemaleTeenagerHeadShaderEFemaleTeenagerHeadShaderFFemaleTeenagerHeadShaderGFemaleTeenagerHeadShaderHFemaleTeenagerHeadShaderIFemaleTeenagerHeadShaderJMaleHoboBodyShaderBMaleHoboBodyShaderCMaleHoboBodyShaderDMaleHoboBodyShaderEMaleHoboHeadShaderBMaleHoboHeadShaderCMaleHoboHeadShaderDMaleHoboHeadShaderEMaleHoboHeadShaderFMaleHoboHeadShaderGMaleHoboHeadShaderHMaleHoboHeadShaderIMaleHoboHeadShaderJMaleHospitalPatientBodyShaderBMaleHospitalPatientBodyShaderCMaleHospitalPatientBodyShaderDMaleHospitalPatientBodyShaderEMaleHospitalPatientHeadShaderBMaleHospitalPatientHeadShaderCMaleHospitalPatientHeadShaderDMaleHospitalPatientHeadShaderEMaleHospitalPatientHeadShaderFMaleHospitalPatientHeadShaderGMaleHospitalPatientHeadShaderHMaleHospitalPatientHeadShaderIMaleHospitalPatientHeadShaderJMaleTeenagerBodyShaderBMaleTeenagerBodyShaderCMaleTeenagerBodyShaderDMaleTeenagerBodyShaderEMaleTeenagerHeadShaderBMaleTeenagerHeadShaderCMaleTeenagerHeadShaderDMaleTeenagerHeadShaderEMaleTeenagerHeadShaderFMaleTeenagerHeadShaderGMaleTeenagerHeadShaderHMaleTeenagerHeadShaderIMaleTeenagerHeadShaderJFemaleSeniorBodyShaderBFemaleSeniorBodyShaderCFemaleSeniorBodyShaderDFemaleSeniorBodyShaderEFemaleSeniorHeadShaderBFemaleSeniorHeadShaderCFemaleSeniorHeadShaderDFemaleSeniorHeadShaderEFemaleSeniorHeadShaderFFemaleSeniorHeadShaderGFemaleSeniorHeadShaderHFemaleSeniorHeadShaderIFemaleSeniorHeadShaderJMaleSkeletonBodyShaderBMaleSkeletonBodyShaderCMaleSkeletonBodyShaderDMaleSkeletonBodyShaderEMaleCopBodyShaderBMaleCopBodyShaderCMaleCopBodyShaderDMaleCopBodyShaderEMaleCopHeadShaderBMaleCopHeadShaderCMaleCopHeadShaderDMaleCopHeadShaderEMaleCopHeadShaderFMaleCopHeadShaderGMaleCopHeadShaderHMaleCopHeadShaderIMaleCopHeadShaderJMaleSkeletonHeadShaderBMaleSkeletonHeadShaderCMaleSkeletonHeadShaderDMaleSkeletonHeadShaderEMaleSkeletonHeadShaderFMaleSkeletonHeadShaderGMaleSkeletonHeadShaderHMaleSkeletonHeadShaderIMaleSkeletonHeadShaderJFemaleGoreBodyShaderBFemaleGoreBodyShaderCFemaleGoreBodyShaderDFemaleGoreBodyShaderEFemaleGoreHeadShaderBFemaleGoreHeadShaderCFemaleGoreHeadShaderDFemaleGoreHeadShaderEFemaleGoreHeadShaderFFemaleGoreHeadShaderGFemaleGoreHeadShaderHFemaleGoreHeadShaderIFemaleGoreHeadShaderJMaleGoreBodyShaderBMaleGoreBodyShaderCMaleGoreBodyShaderDMaleGoreBodyShaderEMaleGoreHeadShaderBMaleGoreHeadShaderCMaleGoreHeadShaderDMaleGoreHeadShaderEMaleGoreHeadShaderFMaleGoreHeadShaderGMaleGoreHeadShaderHMaleGoreHeadShaderIMaleGoreHeadShaderJMaleYoungAdultBodyShaderBMaleYoungAdultBodyShaderDMaleYoungAdultBodyShaderEMaleYoungAdultHeadShaderBMaleYoungAdultHeadShaderCMaleYoungAdultHeadShaderDMaleYoungAdultHeadShaderEMaleYoungAdultHeadShaderFMaleYoungAdultHeadShaderGMaleYoungAdultHeadShaderHMaleYoungAdultHeadShaderIMaleYoungAdultHeadShaderJFemaleHoboBodyShaderBFemaleHoboBodyShaderCFemaleHoboBodyShaderDFemaleHoboBodyShaderEFemaleHoboHeadShaderBFemaleHoboHeadShaderCFemaleHoboHeadShaderDFemaleHoboHeadShaderEFemaleHoboHeadShaderFFemaleHoboHeadShaderGFemaleHoboHeadShaderHFemaleHoboHeadShaderIFemaleHoboHeadShaderJZombieDeathSoundsPlayerPainDeath1PlayerPainDeath2PlayerPainDeath3PlayerPainDeath4PlayerPainDeath5PlayerPainGrunt1PlayerPainGrunt2PlayerPainGrunt3PlayerPainGrunt4ZombieMovementSoundsMovementFootstepGeneric1MovementFootstepGeneric2MovementFootstepGeneric3MovementFootstepWater1MovementFootstepWater2MovementFootstepWater3 DeathMale16 DeathMale01 DeathMale02 DeathMale03 DeathMale05 DeathMale08 DeathMale09 DeathMale11 DeathMale12 DeathMale14DeathFemale09DeathFemale03DeathFemale04DeathFemale05DeathFemale07DeathFemale08VelDirCanvas InteractionLevelShaderMoverPawnResult StartTimebOnlySpectatorFists SkeletalMeshSpriteEmitterActorPlayerCountYawMatchID PRIArrayConsoleRoleClassPackage TimeLimitHitLeft HitRightRemainingTime bTeamGame PawnClass TeamIndex instigatedByHumanPawnBase E_HitPartWEAPON_NAME_YWEAPON_NAME_CENTRE bForceRun numLooks SeekLocationLastSeekInstigatorCurrentObstacleCurrentAttackPointNumClearAttemptsClearStartTime EatTarget MAX_EAT_DISTFailedEatAttemptsDistToEatTarget EatingTime MAX_EAT_TIMELastHeardEnemyTimeREACQUIRE_DELAYNumChargingZombiesLastChargeTimeChargeStartTime bIsChargingChargeDuration bIgnoreBumps bActivatedSTART_REACQUIRE_TIMER CHARGE_TIMERPRE_CHARGE_TIMERTHINK_AGAIN_TIMER MELEE_RANGEDONT_BE_STUPID_TIMER obstacle BlueCountDOTZMPGameBase DOTZInvasionConst RedCountdt Smashable AttackPointAdvancedMPGameBase TextBufferObjectStandardMaleBaseStandardFemaleBase PlayerOtisPSpotEnum FunctionStateYL DurationdistRotatorEndHitLocStruct StrPropertyDirHitBackStructPropertyStartArrayPropertydisinfectioncounterInfectedFOVMaxINFECTIONTIMERDISINFECTIONTIMERWEAPON_UP_TIMERWEAPON_DOWN_TIMERDESTROY_TIMER bWasInfected DOTZAZombies DOTZAHumansHuntedClassProperty NamePropertyObjectPropertyFloatPropertyFriendZombieAIController HitFrontFIRINGBLENDBONE DestinationIZombieWeaponszattack BoolPropertyViewKickMagnitude hitblend LoudnessExtent Momentum HitLocationDamage NewVolume HitNormalEventInstigatorOtherposStageSingleFireRateDelta AltFireAnim LatentFloatAdvancedPlayerController IntProperty InstigatorAdvancedWeapon EPhysics ByteProperty AdvancedPawn enemyDirBurned HunterBot BD3PZombiesSpriteEmitter53SpriteEmitter54SpriteEmitter55SpriteEmitter56SpriteEmitter57Xv@I@W N UB!@Tq GsH$sH$v}v}L9v}L9JqL9ebL9sH$sH$sH$sH$v}sH$v}sH$sH$sH$sH$v}sH$v}L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9sH$sH$sH$v}sH$v}sH$v}sH$v}L9JesH$sH$v}v}sH$sH$v}v}v}sH$v}v}v}v}sH$sH$sH$v}v}v}v}JqL9L9L9L9L9L9v}33sH$33L9L93333ɰ{ʅL9L9333ɰ{ʨ3333܈Jq܈3܈Jq Ic  L9L9ɰ{ʅL9sH$CѣL9L9JeJeɰ{ʒsH$sH$v}L9v}L9L9Jqɰ{ʘv}L9sH$sH$L9L9L9L9JeJeɇ$ɇ$Jeɇ$JesH$sH$v}L9v}L9ɰ{ʙJq܈L9L9ɰ{ʅL9L9L9L9L9L9L9JqL9L9L9ɰ{ʅL9L9L9L9L9L9L9v}v}v}L9L9L9L9L9L9L9L9L9ɰ{ʙJqɰ{ʏɰ{ʙJqL9L9a"L9a"L9ɰ{ʅL9L9ɰ{ʒsH$L9sH$sH$v}sH$v}sH$L9sH$L9L9Jqɰ{ʏɰ{ʅL9ebL9sH$L9L9sH$L9sH$L9sH$L9L9Jqɰ{ʒsH$܈L9L9ɰ{ʏɰ{ʒsH$L9ɰ{ʅL9L9L9a"sH$ɰ{ʅL9L9ɰ{ʅL9L9ebɰ{ʆebL9L9L9L9ɰ{ʅL9ɰ{ʅL9L9ɰ{ʙJqɰ{ʏɰ{ʒsH$sH$v}sH$L9JeJeJeɇ$L9L9v}L9ɰ{ʅL9L9L9L9L9JqL9L9L9L9JqL9L9v}v}L9a"L9a"eb܈L9a"L9a"L9a"L9a"-3-3eb-3lJebebebebL9ebebL9ebL9ebebebeb-3eb-3lJebL9L9L9L9L9L9L9L9L9L9L9JqL9L9L9a"L9L9a"L9a"L9a"L9L9L9JqL9L9a"L9L9L9sH$sH$sH$L9L9ɰ{ʅL9L9sH$ɰ{ʏɰ{ʙJqɰ{ʒsH$܈sH$JqJqJqL9܈ɰ{ʏɰ{ʅL9L9ɰ{ʏɰ{ʒsH$JqJqJqL9L9JqsH$L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9L9ɰ{ʏɰ{ʏɰ{ʏɰ{ʒsH$܈L9L9܈L9L9܈sߊsߊsߊɰ{ʒsH$ɰ{ʅL9L9܈܈ɰ{ʅL9L9v}L9v}v}v}sH$v}sH$L9v}sH$v}sH$sH$sH$sH$sH$sH$sH$L9v}sH$L9v}sH$L9v}L9L9L9ɰ{ʅL9L9L9L9sH$sH$ɰ{ʏɰ{ʏɰ{ʏɰ{ʏɰ{ʒsH$ɰ{ʏɰ{ʼ܈ɰ{ʏɰ{ʼ܈Jqɰ{oCaO$?N$?ED"" ~$?w$AC$L>z$@El$C|$A\$>]$@@i$@h$zDg$?f$?e$?d$?c$?b$?a$?`$?_$?^$?\"'T$GOL$C[T gGPi FL ~MiqbrfV {@{d _\~#hLQm(UOiluCO-]W7ZWO VbvKUB\xLTM)EYHYlbP\wbg d=%`aGvKf1w@ {iBY6hOrt@pj IcJqa"a"Jqpjpj Ic Ic Ic Icpj鉪a"ZsߊsߊJqpjJqpjZ"Y"qgJ"K^`\$@Y ^Y GSYX^@+$C!(1$ARA`@n|DK6w w zBY7u"t@pj IcJqa"a"Jqpj鉪a"ZsߊsߊJqpjJqpjqgJ"K_ YM]DOTZAHumans.HumanStandardS]DOTZAZombies.ZombieStandardSrY[sYX`\$xYyYzY{Y|Y}Y~YGSYX^@+$C!(1$AONl?W%0-T'9?S9?, =% Vrq }'ttrq NN,r\.-( .-'NN,N\}q { UE R@\CQStICDP|UKJ{TVz^Wx} .rjaQu~8akYoY<M[IyIB~FX lCDH4MgWAXNF[Prpejc}as_vc kNlmnopqrstHezx S2wfȜ-abc"P~"(^{*US bE tL  cu+S~TJOQX 5Wb  r5* N65 6 9? z5a/!_5a/!\ w5 L5'q!j ] ` P r"* 9"a/!\"a/!_ er"T"a/!l"a/!O"a/!zd"q!h! hcG Z kQ nbd dX9r*,rM9?&a/!la/!Oa/!zM. '5   ..  '( zI _BIl g b ] U gSmOWLڙJq333HL9ebTJqTJqJq IcL9L9L9HL9HHHHHHHHHHHL9a"HHJqJqJqHHHJqzkHH63HzkHzkHzkHzkHzkHHHHL9HebHL9THebHL9HL9THHebHL9q GebHL9HL9HL9ebHHHL9HL9HL9HJqHebebL9HHL9a"HHHHHHHL9HL9HL9HL9HL9HHHL9HL9HL9HL9HHHHHebebHL9HJqebebebJqebHL9ebHL9ebHebebHJqebebebL9HL9L9L9HHJqL9L9L9Hoaf"e$?g$?j$@k" v$Du$Bt$pAn$Amu"}PQM SI E `@ZX Vch 'aS cce{ iaGj`mn3poiVQ^]q|LxXqACwRQPONAdGHJNyU0LmhnOQRfT^ZZ\w^_`q]nkm9fpe[ qZC#r*r*'?bEe'(  idX @[r* r*']%w*bDL>(D Yd'( [2 8A%,A&,  z>N IU3r>*P> g9>  99? L>99? L>99? L>99? L>-Z 9?&fw> w*-Z 9?,Z9333?   > L>>F >pppassess threat 9U  for 9V>  [sRJ]g0` rr-mr`-mb-m r:p`@r:r:*P: -\k-] W: H:w* a: 2 6A%,A&, jZtULcrU EU )m- (-[z-( `$m|IA 2 4A%,A&, ]bu.R^j @j (8 j68-b  6---\8-L?- '6U686U68U-9?%U9?  ի9?,b?8U x|gf)9?)rrb?jw*I 9?% `$w* R-'r-m'` {sBj\-YsZAwt* rsZtsa"?( 2 3A%,A&, J|Ik hcl @K"mW Aj+n' kCX(']h訨9VCOULDN'T FIND PATH TO:9VFrom 9VNoPathToTakeUpI$%I$w*] un BkPrW CpVs[ DH ^sMI $KK #$jK 1$kK ?$pK KK EGG wu r*'-L'vw*W C9?%'v t9?%'\'  G'( FIGx@F HSe8yS _!\w*1   e* |"tt* W u!e S 2 8A%,A&, J_|$Timer!&-[ s Lm}# -[ aa`' MscTimedFireWeaponAtEnemy() at9Us&Qr* )9?b)fa=' { r* Hma=' NR_8R"-i-i(d6-H~R 2 2A%,A&, ORq 2 2A%,A&, eD A'?Z?%Z T;HG9r;*;jr;* w;Can't fire at9V;with focus9V&(;w*{Rbc]Dvi(7Unable to fire weapon:9V&( 2 4A%,A&, UXf7[-t fXWeapon.CanAttack() returned9T-t&-t it2 2A%,A&, XWS?E99 6EEEVW 6VVVEV{n? jiaPmi YiJ.i<rJ*(pJ'YrJ(iuJ(( E2 2A%,A&, 2 0A%,A&, \b{P'r* r*u_ 9?,<  <?<^?<7   @   77?17nRO)OOZ?x?u<7OChanceToHit:9Uxskill:9Uurange:9U<relative velocity9U7los time9UO&x u2 4A%,A&, 2 9A%,A&, 2 .A%,A&, 2 .A%,A&, 2 -A%,A&, aVTtL#AAA0aGMTV'w0*w.0*w.0*P.0R.0OG'( SlUa O&Uf lf gqPbN4-gPh nh-g Gokh 16384sP IifEYw* Xjr*hwr* r ri `$ ?EPPf w* w**  9?  b`$ ??Pf w* w**  9?  b`$??w*w*pb  hEZ>r*AimToMiss on invalid target&9P4#?  6R|9P4AimToMiss on enemy that isn't visible:9Y|&|_@  m=Z  m=Z@69?,k @E-B 4CEk BCq9Pk4T4kHDq9PD#?H4AimToMiss on visible enemy:9Yq&q xe/Gr),r*C[w* pv!eaW @@( wzlXWya/!_z%6ya/!\z%wzyIJN vkp 0x00000001@xTVF&rx|$px' eUw}lbW~a/!_}%6~a/!\}%w}~FGH ~MH2a,(a,( }{1 BO0aV:?(a<( H|f1rD[D  DE`[E<PEd:w.d*p.d(  J]H=r*AimToHit on invalid target&9PW/[J-O WXJ/ OXTW/eY/  TW/eYp9P/WAimToHit enemy:9Yp&p KKSU C6% J9?%pSbelPS `$`$?BSJ F/I@s--(G%a,(.h(.Zi9:9:$a,9?t(@AC wTeb)T ,Z V<t.. V:.*D ,a T G[WT C:ppReceivedWarning from 9VW9VW NBhJ qhY #?Y{ e%]hueoZhue {Pjf ac(, ? _^ja jPw{ Z[w.*.VH!_ $9D9?7H $9D9?7H $9D9?7H #$9D9?7H ($ 9D9?7He?,  T{!-'a,9?a'  MSvr*Nr*No target to fire at&-i'd({Starting burst&G- 'S  fFiring weapon at9V&`$?R'- j-y--1 RSSupress A- j-y-Cannot fire.&R%Hr f(re)firing weapon at9V&- j-y--1 RSSupress B- j-y-CAN'T ATTACK&-i'd( yVQk-(G%a,(.h(.Z.^ L>#zDzDzD YZF-'Glg*## s.9?,x.9?,G,Q R'a %#w*-(* WB]j׮+ rB* B-' \@}K{.-!--@ .--@. h-y-'l-fh*a,9?a' XyJxܯbef ef?@*'|6|6|b #?99|y ezvRAVN z @N9? T?N@E _IgfIa/!dq!fYYa!f w*a<?(aV:(q!I ZwH $bef r* ef?@*']w #?}9P]6}6}b  #?99}] @`tOX}-'Va!fVw*a!`a<(aV:( bb{u  ]O_8YQ99+ #?(QOPauQ+(+(rP*((Q#?O?(9?*'b$+(+ ('1b$+(+ (' Q9?,' d]l  w*  PfeH369:9:$b?69?@6g@@9?,d@9?l9D9?`@*# %\#w.*.Ww*?6Po333>=TD#?`G# A?633b? lvwq?q@ v =@9? T@9?,o?@HC@E gz:d/a/!J(,(,(-T(S9D.h( hY6| ka+tYQ9:9:$Q-V--V-. h-   aXJY1-r bWb-ow.*rYzattempting to path find to9Vattempt #9Szto dest9Vz&z,Giving up on path through9Vto9V(&w*'p- 9:9:$gpCOULDN'T FIND BEST PATH TO 9VNo Path to 9V( o 26ysxhQ??s;D AY{ 25jq.p -T ps)' rq)Restq!_ tx_=b@w*H99 9? bW-HbJD_zDaa@@@@ B2: # # v_7 wmT:>8Tm* Y nG]>NA%DA,:9:n&A%\A9?\ uZaY^sWander3*w*Q%Q7Q-Z?9?ZLQQ'']L3LNDrD*7D&can't wander from hereN37DD'wandering to9V3u | 3849302} 3982834~ 26Q  24zu? k3p zw*'NoWander-Enemyy X3('a!Vq!V!Uq!V'NoWanderPathy% A Vh'aa=?'aa ).u U"P_ @ hMovingq!j B D JVCr* i-T C j4Vi- r*aa ).{a ).~ E kqkbjq!X ,B 39201F k5eW 9?,bq!X!*v cq!X!U I X4MoveToLocationadb moving directly to9Xa .bUr*can't find path to9X-w*:   falling back to9Va)\=>bUa>Ow*w !*w*moving to9V a ).a=>kDoneMovingToLocation # # *UJ z.| K T7=DStandGround0wH;q!l L P l^0d8 #a  .aa=} TBL -1' N [QA *-1( O _TW'8T_%r_ g 120M KoOPerform_NotEngaged_FireAtTargetFireAtTarget q!o R T nx S X oNotEngaged_FireAtTargetaa?'a?@nG rw* d  rG  *| _8"FireAtTarget timers V sr* u{ r * H 9Vfired at9V m~a=' W iT{8Ti   d eKJ<7&?,zDzDe,9?7, e ,,9?7?,9?zD,zD U p|RecoverEnemy? 9?,T [b q!q!] bw*q!q!L T  Z ^ qvxaa . !Aa ).=Qp{ A.L]P] Q:w* R 9?, \ pT"'8Tp%rp [ [c  NGetLOS['@w*]C r*][zRNoLOSSpotA%NoLOSSpotB% r _ ` r# V4X('*a!Yq!Y!r1q!YT'NoGetLOSPath% b |5 ,a=| E a LA< f QA z&w* k Vha uHide'3w*]`Xr*NoHideSpot% X('a![q![!f `$q!['NoHidePath% g l [ wp-'a )(1Qh|y fXj Q &w* k i B E" w* p k [ >+w*R R-( h [ YTakeCoverZ\%-'q!~!r -('w*]`r*NoCoverSpot-'% } m } vtX('Er  ex-'q!~!I'NoCoverPath% n } 0a=}%  o u ~ da= !*ei- r*aa )(a )(Qa=}qv *Irr Q {&w* k q B " w* p s [ I+w*R RGw*-( t e 8ed p Ed0 iOutFromCover\w*Lq!M!s-( 'UDE%(E7SES-LSPS PUUPMSEtAwM*]MaNoOutFromCoverSpot% z MvF*a,(a,( y {m) BdM(aV:?(a<( Q x fn)v ] \4X('*a!Mq!M!U1q!MZ'NoOutFromCoverPath% | ~ Mu Ri- r*'aa )(ra )(Qa=x *sU Qr &w* k } S *SupressionFireq!w @ G w {Xdaa=sa9?R~w HB  -1' B [  -1( C RT Z'8TR%rR U dM9;d D e 8e*' F s qr* 9{m R]Dmvi( A I ~  J H YU tUCharge&Y9?%Y AcY'q!n! H  39202K @ f  9?, kuq!n!*cVq!n!*bq!n!UX('q!n!UVq!n!* M n T"@\i- r*aa ).a ).@Charge-donea *UN V %Strafe@q!A  O Z( & Z^@^Kay^  ի?Z (wK* y^Z9?,b$  (   dw*!  9?     9?s  9:9:$6s-H9Uss^ R bMq%-(X%a,(.h(.Zi9:9:$a,9?Z(bce V Ne,!N ,Z V<t.. V:.*D ,a N fyt `U0`:[7&?f C`?f9?7& Z ll |0-P'9?N9?, =% Qrc  oorc ll,ra.-( .-' la_ac L  1.5^P p@M *p99#?-O-OJ-Op9?pp@@XNoStrafeDir% j f;ac(, ? L^a Pw^ Z[w.*'.VH'!L $'9D9?7H $'9D9?7H $'9D9?7H #$'9D9?7H ($ '9D9?7He'?,  Y ] Au 6.aaX.~cr*Rb$  !uStrafeRecoverda=L>ի9?, aa.s uMcBc ,=-'-'-(-( \ [k ;-l-'r* -(-( >j9?%-' [ W L2Hunting'A ^ A 2N4X('*a!`q!`!U1q!`LNoHuntPath% _ a ` 4Taa ).(QAr U<b Q ]4w* R ` d B 6k r*(9  Q'Pb@'gb`@@'( c X 7UPanic*-l'`' `$=q!C e z C@ :D`Ea )(E,B !vq *,v@]T 98T] gg oE :o%o7noln  n  l lAAl@noX@((NoPanicDir% k { l Q p Z  m i {[ >gA3rA*A v%M  c%4c7AgcAIgwg g  M*wv*?9?v[w{IceMw{*{eNoPanicDir% q n }  r FgfFa/!dq!fYYa!f w*a<?(aV:(q!F s tX}-'Va!fVw*a!`a<(aV:( u bw  D \V%9[7&?g t\?g9?7& v deM69:9:$b?69?K6gKK9?,dK9?l9D9?`K*# %\#w.*.Ww*d?6Po333>=dTDdd#?`G# A?633b? w ]B w*  x z0nd/a/!J(,(,(-P(N9D.h( | Y,& o B5 0C" w* p y [: C-+w*R R f B]V LFAdvanceMoveBBrB*roOGyr]BoB  'HroBWNoNewTacticalSpot%]WF  ~ a!YQ9:9:$Q-V--V-. h-  W  39203  26F  25{ Ft H[4X('*a!aq!a!U1q!aY'NoAdvanceMovePath% @ B a .KaZi- r*'aa ).a ).QFSp UC Q J&w* k A E Z SN r* Y%Y7XYbrX* r*HX  H9?X9?HY XG 3849302H 3982834J  26D m@ nP2wm*m@k.bwk*k@md*q!y K  24^  120I x Qpw* m$*Gw*r. *E d'nw6*6Z L k Sx M Q y S P ` vS O B Sa N i HTR % `UJwHAw* #q!D S U D V,aa@o B YVECw* # # T YS W3g  SY }  68695 V $k X-H r* S #?i6? $369?,},69?, $ $$$$a9Wa$-69?,},6+9?%$aE$aѨ9U$369?,},69?,-)6 9?&$a &XY U; \  -H P@ $$$STAGE:r*None9W  Stage order:I $SO_None $SO_TakeUpPosition9V 4$SO_HoldPosition W$SO_TakeCover Unrecognized order:9RIaP@Performing:7w*hitChance:9U{enemyDist9U  aP@pppEnemy:9VShootTarget:9V FocalPoint:9XaP@UP [ ] ` ~ld7/,<& c yP . c&!x. c&!Y. c&!H. c&!I. c&!J. c&!K. c&!L. c&!M. c,!Z1 a/!l &gBD2004Zombies.ZombieFistweapono a/!\ gDOTZWeapons.Fistweapon a/!O *gBD2004Zombies.ZombieFistweaponOtis a/!_ gDOTZWeapons.Fistweapon \ Ww b;9-H9`%`K%9VW![ W WPe l B 2-:a/!Q$&r %rBD2004Zombies.ZombieJackDM "-:a/!Q$&r #rBD2004Zombies.ZombieOtis -:a/!Q$%r #rBD2004Zombies.HunterOtis G-:a/!Q$%r (rBD2004Zombies.HunterJackSlade -(r %rBD2004Zombies.ZombieJackDM -(r #rBD2004Zombies.HunterOtis - ( a e ~ c;3w*  $$$E d F ew*RfPg # A $$$ # A $$$ # A$$$ f ]E h r* ]%]7=]\=%= = #?9?,2$$$= = #?9?,2$$$] \h  g ^P l^%^7#^%%%,9:#&%%# # #?9?,2#?9?, 9?%$$$Cw%  *# # #?9?,2#?9?, 9?%$$$# # #?9?,2#?9?, 9?%$$$%A^ i m k ] \%g\7V\%d]c\|dc$$ W _*- ( , j _Q qw*_%_7`_a]`aCa` ` #?9?,29Ca$$_ S// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class ZombieOtis extends BD2004ZombiePawnBase placeable; uo OBY6p ̕#u" Y]DOTZAHumans.HumanStandardS]DOTZAZombies.ZombieStandardS]DOTZAHumans.3PLeadPipeS]DOTZAHumans.3PFireAxeS]DOTZAHumans.3PBaseballBatS^Y &Y $Y  [// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class ZombieJackDM extends BD2004ZombiePawnBase placeable; n gRs,g,G%G,v&,G,z9D9?&9?,9?G9?,F%Fgb%bgF{#?9?9?F9?v#?9?9?b9?z* {6*9?9D6*?9?9?6*9?9D6*?9?9?**#?9?,2$$$bFnG% uq lBY6s =*iu" Y]DOTZAHumans.HumanStandardS]DOTZAZombies.ZombieStandardS]DOTZAHumans.3PLeadPipeS]DOTZAHumans.3PFireAxeS]DOTZAHumans.3PBaseballBatS^Y &Y $Y  W class ZombieFistWeaponOtis extends DOTZMeleeWeapon config(user); /***************************************************************** * PlayFiring * Normal fire has occured by this point, we just need to make it look * like it has now, so play an animation. ***************************************************************** */ simulated function PlayFiring(){ local DOTZPlayerControllerBase PlayerOwner; PlayerOwner = DOTZPlayerControllerBase(Instigator.Controller); if (bDoAltFire == true){ PlayAnim(AltFireAnim, SingleFireRate); } else { PlayAnim(FireAnim, SingleFireRate); } IncrementFlashCount(); } //=========================================================================== // DefaultProperties //=========================================================================== t o Q2u 0YϔJfL9OBT]DOTZAZombies.ZombieStandardSbRXQFPFU] Zombie HandsNw^] Zombie Hands S class ZombieFistWeapon extends DOTZMeleeWeapon config(user); /***************************************************************** * PlayFiring * Normal fire has occured by this point, we just need to make it look * like it has now, so play an animation. ***************************************************************** */ simulated function PlayFiring(){ local DOTZPlayerControllerBase PlayerOwner; PlayerOwner = DOTZPlayerControllerBase(Instigator.Controller); if (bDoAltFire == true){ PlayAnim(AltFireAnim, SingleFireRate); } else { PlayAnim(FireAnim, SingleFireRate); } IncrementFlashCount(); } //=========================================================================== // DefaultProperties //=========================================================================== v m R2w 苇JfL9OBT]DOTZAZombies.ZombieStandardSfRXQEPFU] Zombie HandsNw^] Zombie Hands O// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 class ZombieFistAmmunition extends DOTZMeleeAmmunition; [// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /***************************************************************** * Otis - * * The mesh specification for the lead action figure * * @version $1.0$ * @author Jesse (Jesse@digitalextremes.com) * @date 2004 ***************************************************************** */ class HunterOtis extends BDPlayerPawnBase placeable; /** */ function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class damageType) { if ( instigatedBy.isA( 'HunterJackSlade')) damage = 0; if ( instigatedBy.isA( 'HunterOtis')) damage = 0; super.TakeDamage( damage, instigatedBy, hitlocation, momentum, damageType ); } //=========================================================================== // DefaultProperties //=========================================================================== hy t\BY6z  EhOrL9L9 Yl]DOTZAHumans.3PFireAxeS]DOTZAHumans.3PBaseballBatS]DOTZAHumans.HumanStandardS&Y$Y!e// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /***************************************************************** * JackSlade - * * The mesh specification for the lead action figure * * @version $1.0$ * @author Jesse (Jesse@digitalextremes.com) * @date 2004 ***************************************************************** */ class HunterJackSlade extends BDPlayerPawnBase placeable; /** */ function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class damageType) { if ( instigatedBy.isA( 'HunterJackSlade')) damage = 0; if ( instigatedBy.isA( 'HunterOtis')) damage = 0; super.TakeDamage( damage, instigatedBy, hitlocation, momentum, damageType ); } //=========================================================================== // DefaultProperties //=========================================================================== r Su%7\  9?,2#?$$$  9?,2#?$$$ h{ z_BY6} HhOrL9L9 Yl]DOTZAHumans.3PFireAxeS]DOTZAHumans.3PBaseballBatS]DOTZAHumans.HumanStandardS&Y$Y! | T%wA?w*Y@@  m=  a    m=9P#?$$$  a    m=9P#$$$?  a  @@9P#?$$$  a  @@9P#$$$ ~ ?U8{?%?7t?9?%? ? 9?,2#?$$$? Z// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /***************************************************************** * JackSlade - * * The mesh specification for the lead action figure * * @version $1.0$ * @author Jesse (Jesse@digitalextremes.com) * @date 2004 ***************************************************************** */ class BDPlayerPawnBase extends HumanPawnBase; var int infectionDamage; var int infectionFreq; var name InfectedEvent; var int disinfectioncounter; const InfectedFOVMax = 120; const INFECTIONTIMER = 24; const DISINFECTIONTIMER = 26; const WEAPON_UP_TIMER = 3982834; const WEAPON_DOWN_TIMER = 3849302; const HUD_HACK = 25; const DESTROY_TIMER = 26; var int iMultiPlayerCorpseSecs; var bool bWasInfected; var bool bNoDamageEffects; var int StartTime; /***************************************************************** * PostNetReceive * Here we handle when data from the server comes in. ***************************************************************** */ simulated event PostNetReceive(){ if (Level.NetMode == NM_Client){ if (bWasInfected != bInfected){ bWasInfected = bInfected; DOTZPlayerControllerBase(Controller).InfectionEffect(bInfected); } } super.PostNetReceive(); } function PostBeginPlay(){ super.PostBeginPlay(); } simulated function PostNetBeginPlay(){ super.PostNetBeginPlay(); if (Controller.IsA('XdotzPlayerController') == false){ WEAPON_NAME_CENTRE += 40; WEAPON_NAME_Y += 40; } bNoDamageEffects = false; StartTime = Level.TimeSeconds; DOTZPlayerControllerBase(Controller).InfectionEffect(false); } function TakeFallingDamage(){ local float Shake, EffectiveSpeed; if (Velocity.Z < -0.5 * MaxFallSpeed) { if ( Role == ROLE_Authority ) { MakeNoise(1.0); if (Velocity.Z < -1 * MaxFallSpeed) { EffectiveSpeed = Velocity.Z; if ( TouchingWaterVolume() ) EffectiveSpeed = FMin(0, EffectiveSpeed + 100); if ( EffectiveSpeed < -1 * MaxFallSpeed ){ TakeDamage(-160 * (EffectiveSpeed + MaxFallSpeed)/MaxFallSpeed, None, Location, vect(0,0,0), class'Fell'); if (health > 0 ){ FallDown(vect(-1,0,0)); if(PlayerController(Controller) != none){ AdvancedPlayerController(Controller).Fall(); // camera effect } } } } } if ( Controller != None ) { Shake = FMin(1, -1 * Velocity.Z/MaxFallSpeed); Controller.ShakeView(0.175 + 0.1 * Shake, 850 * Shake, Shake * vect(0,0,1.5), 120000, vect(0,0,10), 1); } } else if (Velocity.Z < -1.4 * JumpZ) MakeNoise(0.5); } /***************************************************************** * Begin Play * This will take away the arrow above player, to stop server blog. ***************************************************************** */ simulated function SetTeamAttachments(){ if (PlayerReplicationInfo != none) { return; } } /***************************************************************** * CheckShadow * overridden to prevent shadowness on the player in a single player * game ***************************************************************** */ simulated function CheckShadow(){ /*if (Level.NetMode != NM_StandAlone){ super.CheckShadow(); } */ } /***************************************************************** * PostLoad * Reset the infection state ***************************************************************** */ function PostLoad() { super.PostLoad(); // log(self $ "postload"); SetInfectionState( bInfected, true ); if (IsInState('Swimming')){ if (Weapon != none){ Weapon.PlayAnim('Deselect'); SetMultiTimer( WEAPON_UP_TIMER, 0, false ); SetMultiTimer( WEAPON_DOWN_TIMER, 0, false ); } } } /***************************************************************** * PhysicsVolumeChange ***************************************************************** */ event PhysicsVolumeChange( PhysicsVolume NewVolume ){ // Log(NewVolume); if ( NewVolume.Isa('WaterVolume') ) { GotoState('Swimming'); } else if ( IsInState('Swimming') && Weapon !=None ) { SetMultiTimer( WEAPON_UP_TIMER, 0.5, false ); SetMultiTimer( WEAPON_DOWN_TIMER, 0, false ); GotoState(''); } super.PhysicsVolumeChange( NewVolume); } /***************************************************************** * SetInfectionState ***************************************************************** */ function SetInfectionState(bool iState, optional bool bForce ){ //no sense doing stuff if you already done it'afore //... unless you're re-initializing because of load game... if ( !bForce ) { if (bInfected == iState ){ return; } else { bInfected = iState; } } // Log(self $ " setinfectionstate: " $ iState); DOTZPlayerControllerBase(Controller).InfectionEffect(bInfected); //DOTZPlayerControllerBase(Controller).MotionBlurOn(); //always trigger events, and set timers on the client if (bInfected == true){ if (!bForce){ TriggerEvent( InfectedEvent, self, none); } SetMultiTimer(INFECTIONTIMER,infectionFreq,true); } } /***************************************************************** * DoInfectionDamage ***************************************************************** */ function DoInfectionDamage(){ if (bInfected == true){ disinfectioncounter++; TakeDamage(infectionDamage, none,vect(0,0,0),vect(0,0,0), class'DOTZInfectionDamage'); if (PlayerController(Controller).DefaultFOV < InfectedFOVMax ){ PlayerController(Controller).DefaultFOV += 6; } if (disinfectioncounter > 3){ DoDisinfection(); } } } /* function PreSave(){ super.PreSave(); log("PreSave"); DOTZPlayerControllerBase(Controller).InfectionEffect(false); } function PostSave(){ super.PostSave(); log("PostSave"); DOTZPlayerControllerBase(Controller).InfectionEffect(bInfected); } */ /***************************************************************** * ***************************************************************** */ function DoDisinfection(){ bInfected = false; disinfectioncounter=0; SetMultiTimer(INFECTIONTIMER,0,false); DOTZPlayerControllerBase(Controller).InfectionEffect(false); //DOTZPlayerControllerBase(Controller).MotionBlurOff(); PlayerController(Controller).FixFOV(); //DOTZHealthBar(MyLifeBar).SetInfected( bInfected ); PlayerController(controller).ClientFlash(0.2, vect(1000,1000,1000 )); } /***************************************************************** * TravelPostAccept ***************************************************************** */ function TravelPostAccept(){ super.TravelPostAccept(); // Log("Travel post accept called on the player"); if (bInfected == true){ SetMultiTimer(INFECTIONTIMER,infectionFreq,true); } } /***************************************************************** * PlayTakeHit * Overridden to prevent the hit noises from infection damage ***************************************************************** */ function PlayTakeHit(vector HitLoc, int Damage, class damageType) { local name HitAnim; local E_HitPart hitPart; //turn off the boredom timer SetBoredomTimer(false); // AnimBlendParams(TAKEHITCHANNEL,1); AnimBlendParams(TAKEHITCHANNEL,1,,,FIRINGBLENDBONE ); hitPart = calcHitPart( hitLoc ); Spawn(BloodEmitter,,,HitLoc); //this is the added line, the reason for overridding if (damageType != class'DOTZInfectionDamage'){ PlayHitSound(); } ViewKick(ViewKickMagnitude); //try and use a weapon specific hit animation first if (AdvancedWeapon(Weapon) != none){ HitAnim = AdvancedWeapon(Weapon).GetHitanim(); //Log("you win: " $ hitanim); } if (HitAnim == ''){ switch ( hitPart ) { case HIT_Back: HitAnim = HitBack[int(Frand()*HitBack.length)]; break; case HIT_Head: HitAnim = HitHead[int(Frand()*HitHead.length)]; break; case HIT_Chest: HitAnim = HitFront[int(Frand()*HitFront.length)]; break; case HIT_Left: HitAnim = HitLeft[int(Frand()*HitLeft.length)]; break; case HIT_Right: default: HitAnim = HitRight[int(Frand()*HitRight.length)]; break; } } // PlayAnim(HitAnim,1,hitblend,TAKEHITCHANNEL); AdvancedPlayAnim(HitAnim,1,hitblend,TAKEHITCHANNEL); } /***************************************************************** * TakeDamage ***************************************************************** */ function TakeDamage(int Damage,Pawn instigatedBy, Vector Hitlocation, vector momentum, class damageType){ // Log( self @ "" $ DamageType ) ; if (bNoDamageEffects == true || Level.TimeSeconds - StartTime < 5){ return; } //already dead igmore further damage so you don't //chunk up and prevent the death animation from playing if (Health < 0){ return; } if (DamageType == class'DOTZInfectionDamage') { SetInfectionState(true); } //fire should burn the zombies much worse else if (DamageType == class'Burned') { // Log( self @ "Damage was: " $ Damage ) ; Damage = Damage / 4; // Log( self @ "Damage reset to : " $ Damage ) ; } //can't hurt yourself with some weapons if (InstigatedBy == self && AdvancedWeapon(Weapon).bCanDamageSelf == false){ return; } //you are much tougher in kungfu mode if (DOTZPlayerControllerBase(Controller).KungFuMode == true){ Damage = Damage / 3; } Super.TakeDamage(Damage,instigatedBy, Hitlocation, momentum, damageType); } /***************************************************************** * MultiTimer ***************************************************************** */ function MultiTimer(int ID){ switch ( ID ) { case INFECTIONTIMER: DoInfectionDamage(); break; case WEAPON_UP_TIMER: Weapon.BringUp(); AdvancedWeapon(Weapon).AccuracyIndicator = AdvancedWeapon(Weapon).default.AccuracyIndicator; break; case WEAPON_DOWN_TIMER: AdvancedWeapon(Weapon).AccuracyIndicator = none; Weapon.PutDown(); break; case DESTROY_TIMER: Destroy(); break; default: Super.MultiTimer(ID); } } /**************************************************************** * Died **************************************************************** */ function Died( Controller killer, class damageType, vector hitLocation ) { //almost like do disinfection, but we leave the health bar, and //don't do the 'heal' flash bInfected = false; disinfectioncounter=0; SetMultiTimer(INFECTIONTIMER,0,false); //AdvancedPlayerController(Controller).MotionBlurOff(); DOTZPlayerControllerBase(Controller).InfectionEffect(false); PlayerController(Controller).FixFOV(); // Log(self $ " has ded"); if (Level.NetMode != NM_Standalone){ SetMultiTimer(DESTROY_TIMER,iMultiPlayerCorpseSecs,false); // Log(self $ " has ded"); // LifeSpan=5; } super.Died(killer, damageType, hitlocation); } state Swimming{ /***************************************************************** * BeginState ***************************************************************** */ function BeginState(){ SetMultiTimer( WEAPON_DOWN_TIMER, 0.5, false ); SetMultiTimer( WEAPON_UP_TIMER, 0, false ); } /***************************************************************** * ChangedWeapon ***************************************************************** */ function ChangedWeapon(){ } } /***************************************************************** * Destroyed ***************************************************************** */ function Destroyed(){ SetMultiTimer(INFECTIONTIMER,0,false); SetMultiTimer(DISINFECTIONTIMER,0,false); // Log(self $ " has been destroyed"); Super.Destroyed(); } //=========================================================================== // DefaultProperties //===========================================================================  2WD}2%27C29?%2 2 9?,d#?$$$2 U//============================================================================= // MyMiniExplosion. //============================================================================= class BDMiniExplosion extends BBPDestruction placeable; //Big thank you to Hawkzz for getting the size right. //Thank you to Trin for the Net Fix A ViR~:99 V  ?  P jW67&?ha#? &j&zD&9?7& j h&&9?7&&9??&zD C |b_|U|& B F@G /'~)IGVaK Y hijklL O Z $@ $pAn(D SowJ` rs"th:[ "@ b K S L F TbgTS l J RHs1aOGj% j7!j~]!r!P!- ~R(!  Q\!b!  baab}!j-r}*(' H e%L  \ ]\2%kklmnopqrstlkWlVmUnMoLpKqGrxswtvlk F// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /** * OtisAIController - * * @version $Rev: 4874 $ * @author Jesse LaChapelle (jesse@digitalextremes.com) * @date June 2004 */ class BDLeadPipeHunter extends BDHunterAIController; //=========================================================================== // //=========================================================================== /** */ function Possess( Pawn p ) { super.Possess( p ); p.GiveWeapon( "Hunted.HumanLeadPipeWeapon" ); } /** */ function bool SameTeamAs(Controller C) { // only zombies return (BDHunterAIController(c) != None); } //=========================================================================== // Otis-death game logic... //=========================================================================== /*function bool FireWeaponAt(Actor A) { //DebugLog( "Trying to fire at" @ A ); if ( A == None ) A = Enemy; Target = A; if ( (Pawn.Weapon != None) && Pawn.Weapon.HasAmmo() && !Pawn.Weapon.IsFiring() ) { NumShotsToGo = iRandRange(MinNumShots,MaxNumShots); CalculateMissVectorScale(); return WeaponFireAgain(Pawn.Weapon.RefireRate(),false); } else { DebugLog( "Unable to fire weapon:" @ Pawn.weapon, DEBUG_FIRING ); return false; } } */ //=========================================================================== // Default Properties //=========================================================================== !R j jB!T jbTL9oCa"~$L?w$?z$zD|$@@s// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /** * ZombieAIRole - * * @version $Rev: 5272 $ * @author Jesse LaChapelle (jesse@digitalextremes.com) * @author Neil Gower (neilg@digitalextremes.com) * @date June 2004 */ class BDHunterAIRole extends DOTZAIRole; //=========================================================================== // Settings //=========================================================================== var int AttackRange; var float MinWanderDelay; var float MaxWanderDelay; //=========================================================================== // internal data //=========================================================================== // wander var protected int numLooks; // seek var protected Vector SeekLocation; var protected Actor LastSeekInstigator; var protected float LastSeekRefreshTime; // obstacle smashing var protected Smashable CurrentObstacle; var protected AttackPoint CurrentAttackPoint; var protected int NumClearAttempts; var protected float ClearStartTime; // eating var protected Actor EatTarget; const MAX_EAT_DIST = 100; var protected int FailedEatAttempts; var protected float DistToEatTarget; var int EatingTime; const MAX_EAT_TIME = 10; // enemy mgmt var protected float LastHeardEnemyTime; const REACQUIRE_DELAY = 1.5; // charging var protected int NumChargingZombies; // using default value as a class var. var protected const int MaxChargingZombies; var protected const float MaxChargeDist; var protected const float MinChargeDist; var protected float MinTimeBetweenCharges; var protected float MaxTimeBetweenCharges; var protected float LastChargeTime; var protected float ChargeStartTime; var protected bool bIsCharging; var protected float ChargeDuration; var protected bool bIgnoreBumps; var() bool bAllowedToCharge; // other var protected bool bActivated; const START_REACQUIRE_TIMER = 68694; const CHARGE_TIMER = 8375093; const PRE_CHARGE_TIMER = 8375094; const THINK_AGAIN_TIMER = 92843; const MELEE_RANGE = 150; const DONT_BE_STUPID_TIMER = 29384; var BDHunterAIController HunterBot; //=========================================================================== // Events of interest... //=========================================================================== function OnTakingDamage(Pawn Other, float Damage) { super.OnTakingDamage( other, damage ); //in multiplayer, if some other human shoots you then go after them instead if (Level.NetMode != NM_StandAlone && BDHunterAIController(Other.Controller)==None){ if ( other.isA('HunterJackSlade') || other.isA('HunterOtis')) return; bot.enemy = Other; } } /** * Called whenever an enemy pawn is in sight. */ function OnThreatSpotted( Pawn threat ) { // // Log( self @ "OnThreatSpotted" @ threat ) ; if ( threat == None ) return; //TODO: this probably needs fancying up. Possibly only switch if // closer, or human controlled... // zombies have stiff necks, they can't look up and down very far... if ( abs(threat.location.z - bot.pawn.location.z) > 2048 ) { return; } if (threat.isA('HunterJackSlade') || threat.isA('HunterOtis')){ return; } if ( threat != bot.Enemy ) { bot.AcquireEnemy(threat, true); } GotoState( 'Attacking' ); } /** * */ function OnThreatHeard( Pawn threat ) { // // Log( self @ "OnThreatHeard not used by ZombieAIRole" ) ; } /** * Called when something does a MakeNoise(). Instead of directly * acquiring the enemy at this point (as the base class does), start * seeking, in an effort to cause an OnThreatSpotted(). */ function OnHeardNoise( float Loudness, Actor NoiseMaker ) { // // Log( self @ "OnHeardNoise" @ loudness @ noiseMaker ) ; if ( noiseMaker == None ) return; if ( NoiseMaker.isA('HunterOtis') || NoiseMaker.isA('HunterJackSlade')) return; if ( NoiseMaker == bot.Enemy ) lastHeardEnemyTime = Level.timeSeconds; if ( NoiseMaker.isA('ZombieJackDM') || NoiseMaker.isA('ZombieOtis')|| NoiseMaker.isA('ZombiePawnBase')) { //FIXME this may be a little wrong, don't want to reset seek state... if ( TrySeekTarget(NoiseMaker) ) GotoState( 'Seeking', 'BEGIN' ); } } /** * It's all over for this zombie... */ function OnKilled( Controller killer ) { super.OnKilled( killer ); EndCharge(); } /** * Mmmmm... tasty carrion to feed on... */ function OnWitnessedKill( Controller killer, Pawn victim ) { super.OnWitnessedKill( killer, victim ); } //=========================================================================== // High level AI scripts/plans... //=========================================================================== /** * Sets a to be the new seek target, if it is better than the current * target. * * @returns true - target is now a, false otherwise. */ function bool TrySeekTarget( Actor a ) { // do some easy checks... if ( LastSeekInstigator == none // if we don't have a seek target... || (LastSeekInstigator == a // or refreshing current target... && Level.TimeSeconds - lastSeekRefreshTime > 1) || a.IsA('ZombieJackDM') || a.isA('ZombieOtis') ||a.isA('ZombiePawnBase') ) { // or it's a hero... LastSeekInstigator = a; LastSeekRefreshTime = Level.TimeSeconds; SeekLocation = a.Location; return true; } // check if the new seek target is closer than the current seek // target... if ( VSize(bot.Pawn.Location - a.location) <= VSize(bot.Pawn.Location - SeekLocation) ) { LastSeekInstigator = a; SeekLocation = a.Location; bot.updateFocus( a ); return true; } // new target didn't meet any criteria to override the current one. return false; } /** */ function TryClearObstacle( Actor obstacle ) { local Pawn potentialEnemy; // if it's an enemy, kill it! potentialEnemy = Pawn(obstacle); if ( obstacle.isA('HunterJackSlade') || obstacle.isA('HunterOtis')) return; if ( potentialEnemy != None && !bot.SameTeamAs(potentialEnemy.controller) ) { bot.acquireEnemy(potentialEnemy, true); GotoState( 'Attacking' ); return; } // check if it's smashable... currentObstacle = Smashable(obstacle); if ( currentObstacle != None ) { // // Log( self @ "hulk smash!" ) ; numClearAttempts = 0; GotoState( 'DoSpecial', 'SMASH_OBSTACLE' ); } else { // // Log( self @ obstacle @ "not smashable" ) ; } //FIXME: need a way to resume after clearing the obstacle. } /** * Determines whether it's appropriate to charge at the enemy now. */ function bool ShouldCharge() { local bool result; return true; } //=========================================================================== // Wander - no (known) threats nearby, nothing in particular to do but // wander around in an undead stupor. //=========================================================================== auto state Wander { function BeginState() { // // Log( self @ "entering wander state" ) ; } function EndState() { // // Log( self @ "leaving wander state" ) ; } function NotEngagedWanderFailed() { GotoState( 'Attacking' ); } BEGIN: while ( true ) { // // Log( self @ "wandering" ) ; Sleep( 1 ); // look around randomly... numLooks = RandRange( 1, 5 ); while ( numLooks > 0 ) { bot.SetRandomFocalPointLocation( 2000 ); Sleep( RandRange(10, 30) ); --numLooks; } // // Log( self @ "doing the wander behaviour" ) ; // find a new place to stand around... bot.Perform_NotEngaged_Wander(); WaitForNotification(); // now just stand there for a while... Sleep( RandRange(MinWanderDelay, MaxWanderDelay) ); } } //=========================================================================== // Seeking - no enemy directly accessible, but something of interest // to seek towards, such as an attractor or the sound of a player. //=========================================================================== state Seeking { function BeginState() { // // Log( self @ "entering seeking state" ) ; // entering this state implies there is something to seek // towards... // if ( !(LastSeekInstigator != None ) ) { Log( "(" $ self $ ") assertion violated: (LastSeekInstigator != None )", 'DEBUG' ); assert( LastSeekInstigator != None ); }// ; } function EndState() { // // Log( self @ "leaving seeking state" ) ; // stop moving! bot.Perform_Error_Stop(); } /** * Bumping into things may interrupt the seeking... */ function OnBump( Actor other ) { // // Log( self @ "OnBump" @ other ) ; TryClearObstacle( other ); } /** */ function OnHitWall( Actor other ) { // // Log( self @ "OnHitWall" @ other ) ; TryClearObstacle( other ); } /** */ function OnHitMover( Actor other ) { // // Log( self @ "OnHitMover" @ other ) ; TryClearObstacle( other ); } BEGIN: // move to SeekLocation... // // Log( self @ "seeking to" @ seekLocation ) ; bot.Perform_MoveToLocation( seekLocation, 128 ); WaitForNotification(); //TODO: maybe we arrived at an eat point? chow down! //FIXME: assumes we made it to the location... LastSeekInstigator = None; // if we didn't end up in the attack state already, go back to // wandering around until something new comes up to seek... GotoState( 'Wander' ); } //=========================================================================== // Attacking - engaged with an enemy, give 'em what for! //=========================================================================== state Attacking { function BeginState() { // // Log( self @ "entering attack state" ) ; LastSeekInstigator = bot.enemy; bot.updateFocus( bot.enemy ); bot.SetCombatTimer(); } function EndState() { // // Log( self @ "leaving attack state" ) ; bot.SetTimer( 0, false ); } /** * Lost sight of the enemy, try to recover him... */ function OnLostSight() { // // Log( self @ "OnLostSight" @ bot.Enemy ) ; SetMultiTimer( START_REACQUIRE_TIMER, REACQUIRE_DELAY, false ); } /** * Attack isn't working. :-( */ function ChargeFailed() { //SetMultiTimer( START_REACQUIRE_TIMER, REACQUIRE_DELAY, false ); } /** */ function OnBump( Actor other ) { local Pawn enemy; if ( bIgnoreBumps ) return; if ( other.isA('HunterOtis') || other.isA('HunterJackSlade')) return; enemy = Pawn( other ); if ( enemy != none && enemy.IsHumanControlled() ) { bIgnoreBumps = true; // should set it back again after some time... bot.WeaponFireAgain(bot.Pawn.Weapon.RefireRate(),false); } } // // Zombies are very single-minded, and ignore many inputs when // attacking... // function OnThreatSpotted( Pawn threat ) { if ( threat == bot.Enemy ) { // // Log( self @ "reacquired" @ threat ) ; SetMultiTimer( START_REACQUIRE_TIMER, 0, false ); } else { // // Log( self @ "ignoring OnThreatSpotted" @ threat ) ; } } function OnHeardNoise( float Loudness, Actor NoiseMaker) { // // Log( self @ "ignoring HearNoise" @ loudness ) ; if ( NoiseMaker == bot.Enemy ) lastHeardEnemyTime = Level.timeSeconds; } function bool TrySeekTarget( Actor a ) { return false; } /** * @returns a little bit less than the amount of time it should take to * charge the enemy. */ function float getChargeDuration() { local float dist; dist = VSize(bot.enemy.location - bot.pawn.location) * 0.8; return dist/bot.pawn.GroundSpeed; } BEGIN: while ( true ) { // // Log( self @ "attacking" ) ; // Charge! if ( shouldCharge() ) { StartCharge(); chargeDuration = getChargeDuration(); // // Log( self @ "charge should take" @ chargeDuration @ "seconds" ) ; if ( chargeDuration > 1.5 ) { // some wild flailing along the way... SetMultiTimer( PRE_CHARGE_TIMER, 0.2, false ); } // timer will trigger an attack just before the bot reaches the // enemy, so that it overlaps with the running. SetMultiTimer( CHARGE_TIMER, chargeDuration, false ); bIgnoreBumps = false; bot.Perform_MoveToLocation( bot.pawn.location + ( (bot.enemy.location - bot.pawn.location)*0.9 ), MELEE_RANGE ); WaitForNotification(); EndCharge(); } // normal attack else { if ( VSize(bot.enemy.location - bot.pawn.location) < MELEE_RANGE ) { bIgnoreBumps = true; bot.Perform_MeleeAttack(); WaitForNotification(); bIgnoreBumps = false; } else { // move to a spot in front of the enemy... bot.Perform_MoveToLocation( bot.pawn.location + ( (bot.enemy.location - bot.pawn.location)*0.8 ), MELEE_RANGE ); // wait a bit and then re-think this action, since moves can // take a really long time... WaitForNotification( 2 ); } } //if you just killed enemy, eat 'im! //NOTE: should probably check range to the body here... if ( bot.Enemy.Health <= 0 ) { EatTarget = bot.Enemy; GotoState( 'DoSpecial', 'EAT_STUFF' ); } //FIXME: do we need this? Sleep( 0.3 ); } } //=========================================================================== // Performing some special activity... //=========================================================================== state DoSpecial { function BeginState() { EatingTime = Level.TimeSeconds; // // Log( self @ "entering special state" ) ; } function EndState() { // // Log( self @ "leaving special state" ) ; bot.target = none; bot.Focus = none; bot.updateFocus( None ); } // Zombies are very single-minded, and ignore many inputs when // doing special stuff... function OnThreatSpotted( Pawn threat ) { // // Log( self @ "ignoring OnThreatSpotted" @ threat ) ; if (Level.TimeSeconds - EatingTime > MAX_EAT_TIME){ super.OnThreatSpotted(threat); } } function OnHeardNoise( float Loudness, Actor NoiseMaker) { // // Log( self @ "ignoring HearNoise" @ loudness ) ; if ( NoiseMaker == bot.Enemy ) lastHeardEnemyTime = Level.timeSeconds; if (Level.TimeSeconds - EatingTime > MAX_EAT_TIME){ super.OnHeardNoise( Loudness, NoiseMaker); } } BEGIN: // if you didn't pick a specific thing to do, we're done! Goto( 'DONE' ); SMASH_OBSTACLE: // // Log( self @ "smashing" ) ; // get into position... currentAttackPoint = currentObstacle.getAttackPoint( bot.pawn ); if ( currentAttackPoint == None ) Goto( 'DONE' ); bot.Perform_MoveToLocation( currentAttackPoint.getAttackLocation() ); WaitForNotification(); // face the right way... bot.Target = currentObstacle.getSmashActor(); Bot.Focus = currentObstacle.getSmashActor(); Sleep( 1 ); // wait for rotation // start attacking ... for ( numClearAttempts = 0; numClearAttempts < 5; ++numClearAttempts ) { // // Log( self @ "Attacking obstacle" ) ; bot.Perform_MeleeAttack(); WaitForNotification(); if ( currentObstacle.getAttackPoint(bot.pawn) == none ) break; } if ( currentObstacle.getAttackPoint(bot.pawn) != none ) { // if the obstacle is still in place, smash it good! currentObstacle.doSmash( bot.pawn ); } bot.target = none; bot.Focus = none; goto( 'DONE' ); EAT_STUFF: // // Log( self @ "Eating stuff" ) ; failedEatAttempts = 0; while ( failedEatAttempts < 5 ) { if ( EatTarget == none && bot.enemy.Health <= 0 ) { EatTarget = bot.enemy; } if ( EatTarget != none ) { distToEatTarget = VSize(EatTarget.location - bot.pawn.location); if ( distToEatTarget > MAX_EAT_DIST) { // zombies love to eat, so they run towards food. HunterBot.bForceRun = true; Sleep( RandRange( 0.1, 0.7 ) ); // // Log( self @ "GRABBING A BITE:" @ eatTarget @ eatTarget.location ) ; bot.Perform_MoveToLocation( eatTarget.location, MAX_EAT_DIST ); WaitForNotification( 1 ); bot.Pawn.Velocity = vect( 0,0,0 ); bot.Pawn.Acceleration = vect( 0,0,0 ); HunterBot.bForceRun = false; } bot.target = eatTarget; bot.Focus = eatTarget; if (VSize(EatTarget.location - bot.pawn.location ) <= MAX_EAT_DIST){ DOTZAIController(bot).Perform_Eating(); } else { // bail out if you're not getting any closer to the target... if ( distToEatTarget - VSize(EatTarget.location - bot.Pawn.Location) < 128 ){ // a little hack to make the zombie walk away... LastSeekInstigator = EatTarget; SeekLocation = Vect(0,0,1) * bot.Pawn.Location + ( Vect(-1,-1,0) * Normal(bot.Pawn.Location - eatTarget.location) * RandRange(0.5, 1.5) * 512 ); goto( 'DONE' ); } ++failedEatAttempts; } Sleep( 0.3 ); } else goto( 'DONE' ); } Goto( 'DONE' ); DONE: // // Log( self @ "done DoSpecial" ) ; bot.Target = none; if ( bot.enemy != none ) GotoState( 'Attacking' ); else if ( LastSeekInstigator != none ) GotoState( 'Seeking' ); else GotoState( 'Wander' ); } //=========================================================================== // HELPERS //=========================================================================== /** */ function init( VGSPAIController c ) { super.init( c ); HunterBot = BDHunterAIController( c ); SetMultiTimer( DONT_BE_STUPID_TIMER, 6.0 + RandRange(0, 2.5), false ); } /** */ function MultiTimer( int timerID ) { switch ( timerID ) { //TODO: might be better to be in a separate state for "just lost contact", // which can more quickly reacquire... case START_REACQUIRE_TIMER: global.TrySeekTarget( bot.enemy ); GotoState( 'Seeking' ); break; case PRE_CHARGE_TIMER: case CHARGE_TIMER: // asynchronous attack, hopefully timed a little before reaching the // enemy... bot.WeaponFireAgain(bot.Pawn.Weapon.RefireRate(),false); break; case THINK_AGAIN_TIMER: // interrupt movement to allow bot a chance to reassess the situation. bot.MoveTimer = -1; break; case DONT_BE_STUPID_TIMER: DontBeStupid(); break; default: super.MultiTimer( timerID ); } } /** * Periodically override zombie behaviour, in hopes of avoiding stupid * situations. */ function DontBeStupid() { local Pawn player; // // Log( self @ "Checking for stupid situations... if you are not in multiplayer mode" ) ; if (Level.NetMode == NM_Standalone){ player = Level.GetLocalPlayerController().pawn; if ( player.isA('HunterJackSlade') || player.isA('HunterOtis')) return; if ( player == none ) return; // ignore the player if he's too far up... if ( abs(player.location.z - bot.pawn.location.z) < 2048 ) { // if the player IS the enemy, and you can attack, then get on it! if ( player == bot.Enemy && GetStateName() != 'Attacking' && bot.actorReachable(bot.Enemy)) { GotoState( 'Attacking' ); } // if the player isn't the enemy, but you can see and reach him, then // start attacking! if ( player != bot.Enemy && bot.CanSee(player) && bot.actorReachable(bot.Enemy)) { bot.AcquireEnemy(player, true); GotoState( 'Attacking' ); } } SetMultiTimer( DONT_BE_STUPID_TIMER, 4.0 + RandRange(0, 2.5), false ); } } /** * Bookkeeping for when a charge starts */ function StartCharge() { ChargeStartTime = Level.TimeSeconds; default.LastChargeTime = ChargeStartTime; HunterBot.bForceRun = true; bIsCharging = true; default.NumChargingZombies = default.NumChargingZombies + 1; } /** * Bookkeeping for when a charge ends. Safe to call even when not charging. */ function EndCharge() { if ( !bIsCharging ) return; default.NumChargingZombies = default.NumChargingZombies - 1; bIsCharging = false; HunterBot.bForceRun = True; } /** * InAttackRange */ function bool InAttackRange(actor enemy){ //NOTE: maybe migrate this to the ZombieGropeWeapon? if(VSize(bot.pawn.location - enemy.location) < 150){ return true; } else { return false; } } /** * Realtime onscreen debug info... */ function DisplayDebug( Canvas c, out float YL, out float YPos ) { super.DisplayDebug( c, YL, YPos ); c.SetPos(4, YPos); C.DrawText( "LastSeekInstigator:" @ LastSeekInstigator @ "SeekLocation:" @ SeekLocation ); YPos += YL; C.SetPos(4,YPos); } /** */ function PreBeginPlay() { super.PreBeginPlay(); // make sure charging is possibe at the beginning of the game... ChargeStartTime = Level.TimeSeconds - MinTimeBetweenCharges; LastChargeTime = ChargeStartTime; } //=============================================================== // Default Properties //=============================================================== O 2s ~ // Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /** * The main controller class for AI characters using the DEBAIT * framework. The controller is responsible for low level behaviours, * like aiming, dodging, and moving, as well as reporting information * to the stage. The stage handles higher level coordination, * especially between characters. The AIRole handles the intermediate * logic required to translate orders into sequences of behaviours. * * TODO: * - make sure all behaviours report completion properly, like * TakePosition does. * * * @author Mike Horgan (mikeh@digitalextremes.com) * @author Neil Gower (neilg@digitalextremes.com) * @author Jesse LaChapelle (jesse@digitalextremes.com) * @version $Revision: 1.118 $ * @date June 2003 */ class BDHunterAIController extends DOTZAIController; //---------------------- // Behaviour properties //---------------------- // Awareness settings // //How long must the enemy be out of sight before considering him lost var float MaxLostContactTime; //How long is the typical time between acquiring LOS and shooting var float ReflexTime; // Melee Attack settings (doesn't use the combat timer) // var bool bMeleeAttackCapable; // Ranged Attack settings // var bool bRangedAttackCapable; //How many bullets in a typical burst of fire. var int MinNumShots, MaxNumShots; //How long between bursts of fire var float MinShotPeriod, MaxShotPeriod; // maximum chance-to-hit factor due to skill... var float MaxSkillOdds; //AimRelated //aim approaches 0 as enemy's distance approaches this var float MaxAimRange; //aim approaches 0 as enemy's relative lateral velocity approaches this var float MaxAimVelocity; //aim approaches 1 as enemy stays in sight this long. var float MaxSecondsOfLOS; //The scale used to aim away from the targets center, which ought to //shrink with accuracy. var float MissVectorScale; // Data for StrafeMove behaviour var float fOddsOfStrafeMove; // don't tactical move until at least this much time has passed var float MinTimeBetweenStrafeMove; //Data for AdvanceMove var float LastAdvanceMoveTime; //how far the character will travel while panicking? var float PanicRange; // How long to focus on the enemy rather than the target when damaged var float EnemyDistractDuration; // Min distance between this character and current enemy before // switching focus from ShootTarget to Enemy. var float EnemyDistractDistance; //Position selection behaviour tweaks var float m_BunchPenalty; var float m_BlockedPenalty; var float m_BlockingPenalty; var float m_NeedForCover; var float m_NeedForContact; var float m_NeedCohesion; var float m_NeedForIntel; var float m_NeedToAvoidCorpses; var float m_NeedForClosingIn; var float m_NeedForNearbyGoal; // This is added to nearby navigation points when a pawn gets killed, // and is reduced every second by 5% by the GameInfo. Thus it's often // desirable to make this penalty bigger if you want it to last longer. const FEAR_PENALTY = 16384; //--------------------------- // Other editable properties //--------------------------- // enable debugging output var() bool bDebugLogging; // a bit-mask to reduce the quantity of debugging output, and related // consts... var() int AIDebugFlags; const DEBUG_FIRING = 0x00000001; // value added to path cost when passing through a claimed position var() const int CLAIMED_POSITION_PENALTY; //--------------------- // Internal properties //--------------------- // How often to change focus var float LastFocusChangeTime; var float RandFocusChangeDuration; // Firing Control related var bool bFireAtLastLocation; var bool bStopFireAnimation; var int NumShotsToGo; var bool bForceRun; // Enemy Management var float EnemyInSightTime; var bool bEnemyIsVisibleCache; var float EnemyVisibilityCacheTime; var pawn VisibleEnemy; var float LoseEnemyCheckTime; var float AcquireTime; // an actor to shoot at if there's no enemy var Actor ShootTarget; var Actor tmpTarget; var Actor pendingFocus; var private OpponentFactory myCreator; var private Name lastState; var private EPhysics lastPawnPhysics; var private float lastHitTime; // time (seconds) pawn last took damage. var String curBehaviour; //for debugging, should reflect current decision; var bool bHibernating; // AI Behaviour Modifier (Grunt, Leader, Medic) var AIRole myAIRole; var class AIType; // navigation var int numMoveAttempts; // Stage related var Stage currentStage; var StagePosition claimedPosition; // Order Management enum EStageOrder { SO_None, SO_TakeUpPosition, SO_HoldPosition, SO_TakeCover, SO_AttackTarget }; var EStageOrder curStageOrder; // Data for TakeUpPosition Order var StagePosition TakeUpPosition; // Data for StrafeMove behaviour var float LastStrafeMoveTime; //Last time at which we made a tactical move var Vector strafeTarget; var bool bStrafeDir; //Data for Hide behaviour var float LastHideTime; //Data for TakeCover behaviour var float maxDistToCrouchForCover; var float LastTakeCoverTime; //Data for Wander var float LastWanderTime; var Actor wanderDestination; // Data for Panic behaviour var bool bHavePanicked; //Only panic once. var float PanicStartTime; // data for move behaviour var vector moveDestination; var float moveSlop; // data for charge action var float chargeEndTime; //Exclamations var ExclaimManager exclaimMgr; const SIGHT_CHECK_TIMER = 39201; const FOCUS_TIMER = 39202; const FOCUS_UPDATE_INTERVAL = 1.5; const UNGHOST_TIMER = 39203; const LOST_SIGHT_TIMER = 68695; //NOTE this shouldn't really be here, but it breaks the scripted sequences // without it. function SelectAction() { myAIRole.botSelectAction(); } //---------------- // Implementation //---------------- /** */ function float getLastHitTime() { return lastHitTime; } /** */ function setLastHitTime() { lastHitTime = Level.TimeSeconds; } /** */ function BeginPlay() { Super.BeginPlay(); } function Possess(Pawn aPawn){ super.Possess(aPawn); SpawnExclaimManager(); } /** * Called from Pawn.possess(), which is in PostBeginPlay() for * statically placed pawns. */ function Restart() { Super.Restart(); initAIRole(); SetMultiTimer( SIGHT_CHECK_TIMER, 3, true ); } /** */ function SpawnExclaimManager() { exclaimMgr = Spawn(class'ExclaimManager',self); exclaimMgr.init(self); } /** * Ensures that this controller has an AI role object that's ready to * go. */ function InitAIRole() { if ( myAIRole == None ) { if ( AIType == None ) AIType = class'AIRole'; myAIRole = Spawn(AIType,self); } myAIRole.init(self); } /** */ function Destroyed() { Super.Destroyed(); Cleanup(); if(exclaimMgr != None) { exclaimMgr.Destroy(); } if(myAIRole != None) { myAIRole.Destroy(); } } /** */ state Scripting { /** */ function BeginState() { //log( "@@@@@@@@@@@@@@@ Starting scripting state!!" ); myAIRole.GotoState( 'Scripting' ); } /** */ function EndState() { //log( "@@@@@@@@@@@@@@@ Ending scripting state!!" ); super.EndState(); } function LeaveScripting() { super.LeaveScripting(); GoalScript = none; myAIRole.GotoState( 'InitRole' ); } } //=========================== // Opponent Factory interface //=========================== /** * this is called from the OpponentFactory when it sets up a new NPC */ function configure( OpponentFactory f, Stage initialStage ) { DebugLog( self $ " configured with " $ f $ ", " $ initialStage ); myCreator = f; if ( initialStage != None ) initialStage.joinStage( self ); ClientSwitchToBestWeapon(); } /** */ function SetCreator(OpponentFactory f) { if(myCreator != None) return; myCreator = f; } /** */ event PreSaveGame() { myCreator = None; currentStage = None; } //=========================================================================== // Stage Orders - these orders are the stage's interface to the bots. // By following the orders, the bots should appear to be // coordinated and intelligent in their actions. However, the // orders still leave lots of latitude for personality and // variations in *how* they are executed. // // Controllers communicate success and failure back to the stage // using the Report_* methods, as specified below... //=========================================================================== /** * Stage is going to maintain bookeeping, but bot is free to do what * it likes. * * success: N/A * failure: N/A */ function StageOrder_None() { myAIRole.Order_None(); } /** * Go to the specified position. Wandering away from the position * after arriving and reporting is okay, as long as it doesn't * contradict new orders. Staying in the general vicinity of the * position is prefered. * * success: Report_InPosition() * failure: Report_PositionUnreachable() */ function StageOrder_TakeUpPosition( StagePosition pos ) { myAIRole.Order_TakeUpPosition( pos ); } /** * Stay in the current position. Bot can shoot, hide, or whatever * else seems appropriate in the moment. * * success: N/A * failure: Report_AbandonedPosition() */ function StageOrder_HoldPosition() { myAIRole.Order_HoldPosition(); } /** * Get out of the line of fire by going to (or staying at) pos. May * require crouching at the destination. If no position is specified, * the bot should choose a location itself. * * success: Report_Covered() * failure: Report_Exposed() */ function StageOrder_TakeCover( optional StagePosition pos ) { myAIROle.Order_TakeCover( pos ); } /** * Attack the specified actor. The bot should not change targets * unless directed to do so by the stage. Thus it is important for * the bot to report if it is under attack from another enemy, so that * the stage will be able to promptly tell the bot to change enemies * (if it suits the strategy of the stage). * * success: Report_TargetDestroyed() * failure: Report_FailedAttack() */ function StageOrder_AttackTarget( Actor target ) { myAIRole.Order_AttackTarget( target ); } //NOTE The rest of these are kind of odd orders, maybe obselete? /** * Treat the specified pawn as your current enemy? * NOTE obselete? implied by attack target? */ function StageOrder_AlertNewEnemy(Pawn bogie, bool bCanSee) { AcquireEnemy(bogie, bCanSee); } /** * */ //NOTE right now, only newStage should call this. use newStage.joinStage() function StageOrder_JoinStage( Stage newStage ) { if ( currentStage != None ) { currentStage.leaveStage( self, RSN_JoinedOtherStage ); } currentStage = newStage; if ( Enemy != None && currentStage != None ) { currentStage.Report_EnemySpotted( Enemy, self ); } } /** * called to put NPC into a dormant (minimal performance hit) mode */ function StageOrder_Hibernate() { // do nothing if already asleep. if ( bHibernating ) return; // hibernate the pawn bHibernating = true; curBehaviour = "stasis"; lastPawnPhysics = Pawn.Physics; Pawn.SetPhysics(PHYS_None); Pawn.bStasis = true; pawn.bHidden = true; // hibernate the controller bStasis = true; GotoState('Dormant'); } /** * called to undo hibernate() */ function StageOrder_Awaken() { // do nothing if already awake if ( !bHibernating ) return; // awaken the pawn Pawn.bStasis = false; Pawn.SetPhysics( lastPawnPhysics ); // awaken the controller bStasis = false; pawn.bHidden = false; bHibernating = false; myAIRole.awakenSucceeded(); } //================= //Stage interfacing //================= /** * called from stage when providing a shooting position to make sure it * meets bot's requirements **/ function bool VerifyShootingPosition(StagePosition position) { if ( Enemy == None || position == None ) return false; if( VSize(Enemy.Location - position.Location) > GetMaxFiringRange() ) { return false; } else return true; } /** * Heigher weight == better **/ function float WeightStagePosition(StagePosition position) { local float weight; local float total; //if( !position.bLOSToPlayer) // return 0; total = m_BunchPenalty + m_BlockedPenalty + m_BlockingPenalty + m_NeedForCover + m_NeedForContact + m_NeedCohesion + m_NeedForIntel + m_NeedToAvoidCorpses + m_NeedForClosingIn + m_NeedForNearbyGoal; weight = m_BunchPenalty * ProjDistToBuddiesAsSeenFromThreat(position) + m_BlockedPenalty * BlockedLineOfFireByBuddies(position) + m_BlockingPenalty * BlockingLineOfFireOfBuddies(position) + m_NeedForCover * DistanceToNearestFreeCoverSpot(position) + m_NeedForContact * NumberOfLinesOfSightToBuddies(position) + m_NeedCohesion * ProperDistanceToBuddies(position) + m_NeedForIntel * NumberOfLinesOfSightToThreats(position) + m_NeedToAvoidCorpses * DistanceToDeadBuddies(position) + m_NeedForClosingIn * MinimumDistanceToThreat(position) + m_NeedForNearbyGoal * DistanceFromMe(position); return weight / total; } /** */ function float ProjDistToBuddiesAsSeenFromThreat(StagePosition position) { local float tmpVal; local vector enemyRight; if( currentStage.StageAgents.Length == 1) return 1.0; //Undo the inclusion of ourselves in the stage's calculations enemyRight = vect(0,1,0) >> Enemy.Rotation; tmpVal = position.fProjDistToBuddies; tmpVal *= 1000.0; tmpVal *= currentStage.StageAgents.Length; tmpVal -= abs( (Pawn.Location - position.Location) dot enemyRight ); tmpVal = tmpVal / (currentStage.StageAgents.Length - 1); if (tmpVal > 1000) return 1.0; else return tmpVal / 1000.0; } /** */ function float BlockedLineOfFireByBuddies(StagePosition position) { local float returnVal; if( currentStage.StageAgents.Length == 1) return 1.0; returnVal = currentStage.calculateStagePosnLOSIsBlocked(self, position); return 1.0 - (returnVal / (currentStage.StageAgents.Length - 1)); } /** */ function float BlockingLineOfFireOfBuddies(StagePosition position) { local float returnVal; if( currentStage.StageAgents.Length == 1) return 1.0; returnVal = currentStage.calculateStagePosnBlocksLOS(self, position); return 1.0 - (returnVal / (currentStage.StageAgents.Length - 1)); } /** */ function float DistanceToNearestFreeCoverSpot(StagePosition position) { return position.fDistToCover; } /** */ function float NumberOfLinesOfSightToBuddies(StagePosition position) { return 0; } /** */ function float ProperDistanceToBuddies(StagePosition position) { local float tmpVal; if( currentStage.StageAgents.Length == 1) return 1.0; tmpVal = 1000.0 - 1000.0 * position.fDistToBuddies; tmpVal *= currentStage.StageAgents.Length; tmpVal -= VSize(Pawn.Location - position.Location); tmpVal = tmpVal / (currentStage.StageAgents.Length - 1.0); if( tmpVal > 1000 ) return 0; else return (1000.0 - tmpVal) / 1000.0; } /** */ function float NumberOfLinesOfSightToThreats(StagePosition position) { local int i, count; //calculate weight of bitvector for( i=0; i<8; i++ ) { if( (position.StandLOF & (0x1 << i)) > 0) count++; } return count; } /** */ function float DistanceToDeadBuddies(StagePosition position) { return 1.0 - position.FearCost/750.0; } /** */ function float MinimumDistanceToThreat(StagePosition position) { local float returnVal; returnVal = VSize(Enemy.Location - position.Location); if ( returnVal > 2500 /*|| returnVal < 1000*/ ) { return 0; } if ( returnVal < 200) { return 0; } else { return 1.0 - ((returnVal-200.0) / 2500.0); } } function float DistanceFromMe(StagePosition position) { local float returnVal; returnVal = VSize(Pawn.Location - position.Location); if ( returnVal > 2500 ) { return 0; } else { return 1.0 - ( (returnVal) / 2500.0); } } //============== // Events // This is where "engine level" notifications go. //============== /** * Called when a shot is taken at this pawn (so it can dodge) **/ function ReceiveWarning(Pawn shooter, float projSpeed, vector FireDir) { DebugLog("ReceivedWarning from "$shooter$shooter.Controller); } /** * Called when the bot takes damage **/ function DamageAttitudeTo(Pawn Other, float Damage) { if ( (Pawn.health > 0) && (Damage > 0) ) { TrySpotEnemy(Other , CanSee(Enemy) ); } setLastHitTime(); if( SameTeamAs(Other.Controller) ) { exclaimMgr.Exclaim(EET_FriendlyFire, 0); } else { exclaimMgr.Exclaim(EET_Pain, RandRange(0, 0.5) ); } myAIRole.OnTakingDamage( Other, Damage); } /** */ event HearNoise( float Loudness, Actor NoiseMaker) { myAIRole.OnHeardNoise( loudness, noiseMaker ); if ( Pawn(NoiseMaker) != None ) { TrySpotEnemy(Pawn(NoiseMaker), false); } } /** * called when a player (bIsPlayer==true) pawn is seen **/ event SeePlayer( Pawn Seen ) { if ( Enemy == Seen ) UpdateEnemyInfo(); else TrySpotEnemy(Seen, true); } /** * Called as soon as Enemy is no longer visible (LOS) **/ event EnemyNotVisible() { //log("EnemyNotVisible"@Enemy); EnemyInSightTime = -1; //bEnemyAcquired = false; if ( Focus == Enemy ) { UpdateFocus( None ); FocalPoint = LastSeenPos; } myAIRole.OnLostSight(); if ( currentStage != None ) { currentStage.Report_UnSighted(self, Enemy); } // shut off these notifications for a while. Disable( 'EnemyNotVisible' ); SetMultiTimer( LOST_SIGHT_TIMER, 3.0, false ); } /** * This should not only check if our enemy was killed, * but also notice if we see a friendly die. **/ function NotifyKilled( Controller Killer, Controller Killed, pawn KilledPawn ) { local Pawn oldEnemy; oldEnemy = Enemy; Super.NotifyKilled(Killer, Killed, KilledPawn); myAIRole.OnWitnessedKill( killer, killedPawn ); if ( currentStage != None ) { currentStage.Report_Killed(self, Enemy); } if ( Enemy == None ) { SetRelaxAttributes(); } // exclaimations! if ( exclaimMgr == None ) return; if ( Killer == self && KilledPawn == oldEnemy ) { exclaimMgr.Exclaim(EET_KilledEnemy, 0.5); } else if ( SameTeamAs(Killer) && !SameTeamAs(Killed) && KilledPawn != None && Pawn != None && VSize(KilledPawn.Location - Pawn.Location) < 2500 && LineOfSightTo(KilledPawn) ) { exclaimMgr.Exclaim(EET_WitnessedKilledEnemy, RandRange(1.0, 1.5)); } else if ( SameTeamAs(Killed) && KilledPawn != None && Pawn != None && VSize(KilledPawn.Location - Pawn.Location) < 2500 && LineOfSightTo(KilledPawn) ) { exclaimMgr.Exclaim(EET_WitnessedDeath, RandRange(0.0, 1.0), 0.5); if ( Killer != None && Killer.Pawn != None ) { TrySpotEnemy(Killer.Pawn, CanSee(Killer.Pawn) ); } } } /** */ event bool NotifyHitWall(vector HitNormal, actor Wall) { local bool superResult; superResult = super.NotifyHitWall( hitNormal, wall ); myAIRole.OnHitWall( wall ); return superResult; } /** */ event NotifyHitMover(vector HitNormal, mover Wall) { super.NotifyHitMover( hitNormal, wall ); myAIRole.OnHitMover( wall ); } /** * If it's an enemy, notice it, if it's a friend, coordinate moving * out of each others way. **/ event bool NotifyBump(actor Other) { local Pawn P; super.NotifyBump( other ); myAIRole.OnBump( other ); P = Pawn(Other); if (P == None) return false; TrySpotEnemy(P, true); if ( Enemy == P ) return false; if ( AdjustAround(P) ) return false; return false; } /** * This is supposed to "interrupt" the regular movetowards stuff (by * setting bAdjusting) so that the bot moves around a dynamic obstacle **/ 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 + 2 * Other.CollisionRadius * (0.5 * VelDir + SideDir); } } //============== // Enemy Management //============== function float AssessThreat( Pawn NewThreat, bool bThreatVisible ) { local float ThreatValue, NewStrength, Dist; if( NewThreat.Controller == None || SameTeamAs(NewThreat.Controller) ) return -1; //NewStrength = RelativeStrength(NewThreat); ThreatValue = NewStrength; Dist = VSize(NewThreat.Location - Pawn.Location); if ( Dist < 2000 ) { ThreatValue += 0.2; if ( Dist < 1500 ) ThreatValue += 0.2; if ( Dist < 1000 ) ThreatValue += 0.2; if ( Dist < 500 ) ThreatValue += 0.2; } if ( bThreatVisible ) ThreatValue += 1; if ( (NewThreat != Enemy) && (Enemy != None) ) { if ( !bThreatVisible ) ThreatValue -= 5; if ( Dist > 0.7 * VSize(Enemy.Location - Pawn.Location) ) ThreatValue -= 0.25; ThreatValue -= 0.2; } if ( NewThreat.IsHumanControlled() ) ThreatValue += 0.25; DebugLog("assess threat "$ThreatValue$" for "$NewThreat); return ThreatValue; } /** * Has this bot been out of contact for MaxTime * the stage may call this to see if an enemy can be removed. **/ function bool LostContact(float MaxTime) { if ( Enemy == None || Enemy.Controller == None) return true; if ( TimeElapsed( FMax(LastSeenTime, AcquireTime) , MaxTime) ) return true; return false; } /* LoseEnemy() get rid of old enemy, if stage lets me */ function bool LoseEnemy(float Time) { if ( Enemy == None || currentStage == None) return true; if ( (Enemy.Health > 0) && (Enemy.Controller != None) && (!TimeElapsed(LoseEnemyCheckTime,0.2)) ) return false; LoseEnemyCheckTime = Level.TimeSeconds; if ( currentStage.Report_EnemyLost(self, Time) ) { return true; } // still have same enemy return false; } function bool SameTeamAs( Controller c ) { return (c != Level.GetLocalPlayerController()); } function bool EnemyIsVisible() { if ( (EnemyVisibilityCacheTime == Level.TimeSeconds) && (VisibleEnemy == Enemy) ) { return bEnemyIsVisibleCache; } VisibleEnemy = Enemy; EnemyVisibilityCacheTime = Level.TimeSeconds; bEnemyIsVisibleCache = LineOfSightTo(Enemy); return bEnemyIsVisibleCache; } /** * Manages the spotting of enemies, notifying the right objects and so on. * * @param potentialEnemy - the new possible enemy * @param bCanSeePotEnemy - the new enemy is visible? * @param bSuppressRoleNotifications - don't do any callbacks to the role (good * for avoiding infinite script recursion when the role wants to change * the enemy itself!) */ function TrySpotEnemy( Pawn potentialEnemy, bool bCanSeePotEnemy, optional bool bSuppressRoleNotifications ) { if( Enemy == potentialEnemy || potentialEnemy.Controller == none || SameTeamAs(potentialEnemy.Controller) ) { return; } if ( !bSuppressRoleNotifications ) { if ( bCanSeePotEnemy ) myAIRole.OnThreatSpotted( potentialEnemy ); //NOTE: if think if you can't see him, you must have heard him. else myAIROle.OnThreatHeard( potentialEnemy ); } //notify stage if(currentStage != None) { //Stage will assess threat, and assign new enemy currentStage.Report_EnemySpotted(potentialEnemy, self); } } /** * Inits enemy mg'mt with the new potential enemy, unless already acquired. */ function AcquireEnemy(Pawn potentialEnemy, bool bCanSeePotEnemy) { if ( potentialEnemy == Enemy ) return; AcquireTime = Level.TimeSeconds; Enemy = potentialEnemy; FocalPoint = Enemy.Location; EnemyInSightTime = -1; MoveTimer = -1; SetCombatTimer(); bEnemyAcquired = false; if(!bCanSeePotEnemy) { LastSeenTime = -1000; bEnemyInfoValid = false; } else { exclaimMgr.Exclaim(EET_AcquireEnemy, 0); myAIRole.OnEnemyAcquired(); UpdateEnemyInfo(); } SetCombatAttributes(); myAIRole.acquireEnemySucceeded(); } /** * Update the visibility info **/ function UpdateEnemyInfo() { // Enemy was not previously visible, update lastseen visibility info if (EnemyInSightTime == -1) { EnemyInSightTime = Level.TimeSeconds; // check if we're re-sighting the same enemy from earlier... if ( VisibleEnemy == Enemy && TimeElapsed(LastSeenTime, 1.0) ) { myAIRole.OnRegainedSight(); if( currentStage != none && currentStage.Request_PercentEyeSighted(self) == 0 ) { exclaimMgr.Exclaim(EET_NoticeEnemy, 0); } } //only need to update the stage if we were actually unsighted until now if (currentStage != None) { currentStage.Report_EyeSighted(self, Enemy); } } // freshen visibility stats... LastSeenTime = Level.TimeSeconds; bEnemyInfoValid = true; VisibleEnemy = Enemy; bEnemyIsVisibleCache = true; EnemyVisibilityCacheTime = Level.TimeSeconds; } /** * */ function UpdateFocus( Actor newFocus, optional bool bImmediate ) { if ( bImmediate ) { Focus = newFocus; } else if ( pendingFocus != None && newFocus == Enemy ) { // if focus is oscillating, prefer the enemy. Focus = Enemy; } else { PendingFocus = newFocus; SetMultiTimer( FOCUS_TIMER, FOCUS_UPDATE_INTERVAL, false ); } } /** * When we acquire an enemy, we should "heighten awareness" as it were. **/ function SetCombatAttributes(); /** * When we don't have an enemy, our awareness is "less" **/ function SetRelaxAttributes(); //============== // Decision logic. // Given a situation, decide what tactical behaviour is appropriate. // In general, we must follow orders first, however the bot may have discretion about how to handle some orders // (engage enemies first, or while on the way etc) //============== /** * We currently have no order, Let enemy strategy take over. **/ function FollowOrder_None() { myAIRole.SelectAction(); } /** * Try to get to our designated position, notify the stage when we get * there. **/ function FollowOrder_TakePosition() { UnClaimPosition(); if(!Pawn.ReachedDestination( TakeUpPosition ) ) { if( FindBestPathToward(TakeUpPosition, false, true) ) { ClaimPosition(TakeUpPosition); Perform_MoveTowardPosition(); } else { warn( self @ "COULDN'T FIND PATH TO:" @ TakeUpPosition @ "From " @ Pawn.Anchor ); curBehaviour = "NoPathToTakeUp"; curStageOrder = SO_None; //NOTEmyAIRole.TakePositionFailed(); Perform_Error_Stop(); } } else { curStageOrder = SO_None; //Pawn.Anchor = TakeUpPosition; if ( currentStage != None ) { ClaimPosition(TakeUpPosition); currentStage.Report_InPosition(self, TakeUpPosition); } myAIRole.TakePositionSuceeded(); } } /** * Stay put. Fire at will. **/ function FollowOrder_HoldPosition() { //TODO should make use of take-cover behaviours in hold position impl. myAIRole.SelectAction(); } /** */ function FollowOrder_TakeCover() { Perform_Engaged_TakeCover(); } /** * Call the appropriate order function **/ function FollowOrder() { switch (curStageOrder) { case SO_None: FollowOrder_None(); break; case SO_TakeUpPosition: FollowOrder_TakePosition(); break; case SO_HoldPosition: FollowOrder_HoldPosition(); break; case SO_TakeCover: FollowOrder_TakeCover(); break; default: FollowOrder_None(); break; } } /** * called during SelectCombatAction to see if we ought to pick a new position **/ function bool NoLongerOnGoodPosition() { if ( claimedPosition == None ) return true; if ( !currentStage.PositionHasLOFToEnemy(claimedPosition, Enemy) ) return true; if ( currentStage != None ) { if ( currentStage.calculateStagePosnBlocksLOS(self, claimedPosition) > 0) { return true; } if ( currentStage.calculateStagePosnLOSIsBlocked(self, claimedPosition) > 0 ) { return true; } } if ( !VerifyShootingPosition(claimedPosition)) return true; if ( VSize(Enemy.Location - Pawn.Location) > GetMaxFiringRange() ) { return true; } //NOTEfixme: Add time check for ability to orient towards LOF etc. return false; } /** * Override for non-standard weapon range checking. */ function float GetMaxFiringRange() { return 10000; //NOTEWarfare Codebase //return Pawn.Weapon.MaxRange; } /** */ function MultiTimer( int timerID ) { switch ( timerID ) { case SIGHT_CHECK_TIMER: if ( enemy != None && VSize( enemy.location - pawn.location ) > pawn.sightRadius ) { // make sure that we lose track of enemies that have moved // beyond our sight radius. EnemyNotVisible(); Enemy = None; } break; case FOCUS_TIMER: Focus = PendingFocus; PendingFocus = None; break; case LOST_SIGHT_TIMER: Enable( 'EnemyNotVisible' ); break; //TODO should probably migrate the loathsome firing control timer // into here some day... default: super.MultiTimer( timerID ); } } //============== // Firing Control (how I hate you) // In general, the timer is always on so that we are constantly checking if we have a shot. //============== function Timer() { DebugLog( "Timer!", DEBUG_FIRING ); if ( !bRangedAttackCapable ) return; TimedFireWeaponAtEnemy(); } /** * Combat timer is slow, so that the calls/recalls to FireWeaponAt appear * to have reflexes built in. **/ function SetCombatTimer() { if ( !bRangedAttackCapable ) return; //SetTimer(1.2 - 0.1 * FMin(10,Skill), True); SetTimer( RandRange(MinShotPeriod,MaxShotPeriod), true); } /** * If we don't have an enemy yet, or are currently shooting at one * set the timer to something that looks like reflexes. * The "reflex" delay will be upon first seeing an enemy I presume? **/ function TimedFireWeaponAtEnemy() { DebugLog( "TimedFireWeaponAtEnemy() at" @ Level.TimeSeconds @ "s", DEBUG_FIRING ); if ( Pawn.Weapon == None ) return; if ( (EnemyInSightTime == -1) || ( !TimeElapsed(EnemyInSightTime, ReflexTime) ) ) { SetTimer(0.1, True); //often enough... return; } if ( Pawn.Weapon.IsFiring() || (Enemy == None) || FireWeaponAt(Enemy) ) { SetCombatTimer(); //Timer between bursts of fire } else { SetTimer(0.1, True); //often enough... } } function Tick(float dT) { Super.Tick(dT); if(bStopFireAnimation) { bStopFireAnimation = false; StopFiring(); } if(bDebugLogging) debugTick(dT); } // code-base specific function WeaponStopFire( Weapon w ); //NOTE Pawn.Weapon.ServerStopFire(Pawn.Weapon.BotMode); function StopFiring() { if ( (Pawn != None) && (Pawn.Weapon != None) /*&& Pawn.Weapon.IsFiring()*/ ) { WeaponStopFire( Pawn.Weapon ); // bStoppedFiring = true; } // bCanFire = false; bFire = 0; bAltFire = 0; } /** * Find a spot on a side of the enemy so we'll miss **/ function CalculateMissVectorScale() { if(Frand() < 0.5) MissVectorScale = 1.0; else MissVectorScale = -1.0; } /** * Check ammo, and current focus, then attempt to fire weapon. **/ function bool FireWeaponAt(Actor A) { //DebugLog( "Trying to fire at" @ A ); if ( A == None ) A = Enemy; if ( (A == None) || (Focus != A) ) { DebugLog( "Can't fire at" @ A @ "with focus" @ Focus, DEBUG_FIRING ); /* if ( currentStage != None ) { //NOTE currentStage.Report_EnemyLost( self, Level.timeSeconds ); } */ return false; } Target = A; if ( (Pawn.Weapon != None) && Pawn.Weapon.HasAmmo() && !Pawn.Weapon.IsFiring() ) { NumShotsToGo = iRandRange(MinNumShots,MaxNumShots); CalculateMissVectorScale(); return WeaponFireAgain(Pawn.Weapon.RefireRate(),false); } else { DebugLog( "Unable to fire weapon:" @ Pawn.weapon, DEBUG_FIRING ); return false; } } /** */ function bool CanAttack(Actor Other) { local bool result; // return true if in range of current weapon result = Pawn.Weapon.CanAttack(Other); DebugLog( "Weapon.CanAttack() returned" @ result, DEBUG_FIRING ); return result; } 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); } /** * returns a number [0,1] to indicate odds of "shooting to hit" **/ function float ChanceToHit() { local float skillOdds, range, relVelocity, losTime, result; if ( Pawn == None || Enemy == None ) return 0; //skill (0-7) skillOdds = FMax(MaxSkillOdds, Skill/7); //target distance range = VSize(Pawn.Location - Enemy.Location); range = FClamp( 1.0 - (range/MaxAimRange), 0.0, 1.0); if(range == 0.0) return 0; //relative Velocity relVelocity = Normal(Enemy.Location - Pawn.Location) dot Normal( (Enemy.Location + 2.0*Enemy.Velocity) - (Pawn.Location + Pawn.Velocity) ); relVelocity = FClamp(relVelocity, 0.0, 1.0); if(relVelocity == 0.0) return 0; //how long visible if(EnemyIsVisible()) { losTime = Level.TimeSeconds - EnemyInSightTime; losTime = FMin(losTime / MaxSecondsOfLOS, 1.0); } result = 1.0 * skillOdds * range * relVelocity*losTime; DebugLog( "ChanceToHit:" @ result @ "skill:" @ skillOdds @ "range:" @ range @ "relative velocity" @ relVelocity @ "los time" @ losTime, DEBUG_FIRING ); return result; } function bool WillFriendlyFire(vector start, vector end, out Pawn HitFriend, out vector outHitLocation) { local vector HitLocation, HitNormal, extent; local actor HitActor; extent = vect(20, 20, 20); //HitActor = Trace(HitLocation, HitNormal, end, start, true, extent); HitActor = Trace(HitLocation, HitNormal, end, start, true); if(HitActor != None && Pawn(HitActor) != None && Pawn(HitActor).Controller != None && SameTeamAs(Pawn(HitActor).Controller)) { HitFriend = Pawn(HitActor); outHitLocation = HitLocation; return true; } return false; } /** */ function Rotator AimToMiss( Ammunition FiredAmmunition, vector projStart, int aimerror ) { local vector FireSpot, MissVector; local rotator MissDir, result; local float targetDistance, projSpeed; local vector outHitLocation; local Pawn HitFriend; if ( target == None ) { DebugLog( "AimToMiss on invalid target", DEBUG_FIRING ); return rotator( lastSeenPos - projStart ); } MissVector = vect(0,0,1) cross (Target.Location - Pawn.Location); MissVector.Z = 0; if( !EnemyIsVisible() ) { result = rotator(LastSeenPos - projStart); DebugLog( "AimToMiss on enemy that isn't visible:" @ result, DEBUG_FIRING ); return result; } //clamp miss cone to <= 7deg if( (Target.CollisionRadius*2.0) / VSize(Target.Location - Pawn.Location) > 0.123 ) // missCone > tan(7deg) { MissVector = MissVectorScale * Normal(MissVector) * VSize(Target.Location - Pawn.Location) * 0.123; } else { MissVector = MissVectorScale * Normal(MissVector) * Target.CollisionRadius*2.0; } MissVector.Z = RandRange( Target.CollisionHeight/4, Target.CollisionHeight ); FireSpot = Target.Location; if(FiredAmmunition.bLeadTarget) { targetDistance = VSize(Target.Location - projStart); projSpeed = FiredAmmunition.ProjectileClass.default.speed; FireSpot += Target.Velocity * targetDistance / projSpeed; } MissDir = rotator( (FireSpot + MissVector) - projStart ); //NOTE Q: friendly fire, what to do? // A: Rather have a really good shot than a really stupid one if( WillFriendlyFire( projStart, FireSpot+MissVector, HitFriend, outHitLocation ) ) { MissDir = rotator( outHitLocation + vect(0,0,1.0) * HitFriend.CollisionHeight - projStart); } DebugLog( "AimToMiss on visible enemy:" @ MissDir, DEBUG_FIRING ); return MissDir; } /** * Subclasses may override if you don't like the conservative * shoot-for-the-middle approach. */ function vector GetTweakedFireSpot( Actor target ) { return target.Location; } /** */ function Rotator AimToHit( Ammunition FiredAmmunition, vector projStart, int aimerror ) { local Vector FireSpot; local float targetDistance, projSpeed; local rotator result; local vector outHitLocation; local Pawn HitFriend; if ( target == None ) { DebugLog( "AimToHit on invalid target", DEBUG_FIRING ); return rotator( lastSeenPos - projStart ); } FireSpot = GetTweakedFireSpot( Target ); if(FiredAmmunition.bLeadTarget) { targetDistance = VSize(Target.Location - projStart); projSpeed = FiredAmmunition.ProjectileClass.default.speed; FireSpot += Target.Velocity * targetDistance / projSpeed; if( WillFriendlyFire(projStart, FireSpot, HitFriend, outHitLocation) ) { FireSpot = Target.Location; //log("FRIENDLY HITLEAD"@HitFriend); } } if( WillFriendlyFire( projStart, FireSpot, HitFriend, outHitLocation ) ) { //log("FRIENDLY HIT"@HitFriend); } result = Rotator(FireSpot - projStart); DebugLog( "AimToHit enemy:" @ result, DEBUG_FIRING ); return result; } /** * AdjustAim() * Returns a rotation which is the direction the bot should aim - * after introducing the appropriate aiming error **/ function rotator AdjustAim( Ammunition FiredAmmunition, vector projStart, int aimerror ) { FiredAmmunition.WarnTarget(Target,Pawn,vect(1,0,0)); if( FRand() <= ChanceToHit() || aimError <= 0 ) { return AimToHit(FiredAmmunition, projStart, aimerror); } else { return AimToMiss(FiredAmmunition, projStart, aimerror); } } /** * This is a callback from the weapon when it has finished firing. * bFinishedFire==true indicates a callback from the weapon, and is * passed into BotFire to indicate that we want to keep shooting * (otherwise, the gun would return false since the RefireTime on the * weapon hadn't been reached yet) * * In essence, when this is called, you call BotFire again * to keep the trigger pulled. * * the RefireRate is set on the weapon, and gives you a chance to * shoot in "bursts" it should really be called RefireOdds, since it's * really the chance that you'll stop the burst. **/ function bool WeaponFireAgain(float RefireRate, bool bFinishedFire) { local bool bFireSuccess; //LastFireAttempt = Level.TimeSeconds; if ( Target == None ) Target = Enemy; if ( Target == None ) { DebugLog( "No target to fire at", DEBUG_FIRING ); bStopFireAnimation = true; StopFiring(); return false; } // first shot... if ( !Pawn.Weapon.IsFiring() ) { DebugLog( "Starting burst", DEBUG_FIRING ); if ( Pawn.Weapon.bMeleeWeapon || (!NeedToTurn(Target.Location) && CanAttack(Target)) ) { DebugLog( "Firing weapon at" @ target, DEBUG_FIRING ); exclaimMgr.Exclaim(EET_Attacking, 0, 0.5); NumShotsToGo--; UpdateFocus( Target, true ); //bCanFire = true; //bStoppedFiring = false; bFireSuccess = Pawn.Weapon.BotFire(bFinishedFire); //DebugLog("@1@"@bFinishedFire@bFireSuccess); return bFireSuccess; } else if( bFireAtLastLocation && !EnemyIsVisible() && !NeedToTurn(LastSeenPos) ) //supression fire { debugLog("Supress A"); bFireSuccess = Pawn.Weapon.BotFire(bFinishedFire); //DebugLog("@3@"@bFinishedFire@bFireSuccess); return bFireSuccess; } else { DebugLog( "Cannot fire.", DEBUG_FIRING ); //bCanFire = false; } } // rest of a burst... else if ( NumShotsToGo-- > 0 ) { if ( Focus == Target && CanAttack(Target) ) { //bStoppedFiring = false; DebugLog( "(re)firing weapon at" @ target, DEBUG_FIRING ); bFireSuccess = Pawn.Weapon.BotFire(bFinishedFire); //DebugLog("@3@"@bFinishedFire@bFireSuccess); return bFireSuccess; } else if( bFireAtLastLocation && !EnemyIsVisible() && !NeedToTurn(LastSeenPos) ) //supression fire { debugLog("Supress B"); bFireSuccess = Pawn.Weapon.BotFire(bFinishedFire); //DebugLog("@3@"@bFinishedFire@bFireSuccess); return bFireSuccess; } else { DebugLog("CAN'T ATTACK", DEBUG_FIRING ); } } bStopFireAnimation = true; //DebugLog("@4@"@bFinishedFire@bFireSuccess); StopFiring(); return false; } //=============== // Helper functions //=============== function UnClaimPosition() { if(claimedPosition != None) { claimedPosition.bIsClaimed = false; claimedPosition = None; } } function ClaimPosition( StagePosition position ) { if(position == None) return; claimedPosition = position; claimedPosition.bIsClaimed = true; } /** * Sets a random direction to look at. **/ function SetRandomFocalPointLocation(float viewDist) { local Rotator LookDir; if( !TimeElapsed(LastFocusChangeTime, RandFocusChangeDuration ) ) return; LastFocusChangeTime = Level.TimeSeconds; RandFocusChangeDuration = RandRange(1.0, 2.0); UpdateFocus( None, true ); LookDir = Rotation; LookDir.Yaw = LookDir.Yaw + iRandRange(-32768, 32768); FocalPoint = Pawn.Location + Pawn.BaseEyeHeight * vect(0,0,1) + vector(LookDir)*viewDist; } function SetFocalPointNearLocation(vector oldPoint) { local vector oldDirVector; local rotator LookDir; if( !TimeElapsed(LastFocusChangeTime, RandFocusChangeDuration) || Pawn == None ) { return; } LastFocusChangeTime = Level.TimeSeconds; RandFocusChangeDuration = RandRange(1.0, 2.0); UpdateFocus( None, true ); oldDirVector = oldPoint - (Pawn.Location + Pawn.BaseEyeHeight * vect(0,0,1) ); LookDir = rotator(oldDirVector); LookDir.Yaw = LookDir.Yaw + iRandRange(-8192, 8192); //45deg FocalPoint = Pawn.Location + Pawn.BaseEyeHeight * vect(0,0,1) + vector(LookDir)*VSize(oldDirVector); } /** * 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; UpdateFocus( None, true ); 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; } /** * It tries to set MoveTarget to the location of the best waypoint, * and returns true if successful */ function bool FindBestPathToward( Actor A, bool bCheckedReach, bool bAllowDetour ) { local Actor oldMoveTarget; oldMoveTarget = moveTarget; // find the next waypoint along the path... if ( !bCheckedReach && ActorReachable(A) ) { MoveTarget = A; } else { MoveTarget = FindPathToward(A, (bAllowDetour && (NavigationPoint(A)!=None))); } // keep tabs on whether we might be stuck. //NOTE perhaps this should be based on time elapsed... if ( moveTarget == oldMoveTarget ) { numMoveAttempts++; DebugLog( "attempting to path find to" @ a @ "attempt #" @ numMoveAttempts @ "to dest" @ moveTarget ); } else numMoveAttempts = 1; if ( numMoveAttempts > 5 ) { DebugLog( "Giving up on path through" @ moveTarget @ "to" @ a ); return false; } else if ( MoveTarget != None ) { return true; } else if ( bSoaking && (Physics != PHYS_Falling) ) { SoakStop("COULDN'T FIND BEST PATH TO "$A); } else { DebugLog("No Path to "@A); } return false; } //===================== // Decision heuristics //===================== //NOTE we should collect all of our shouldX()-style methods here... /** * A heuristic for whether running or walking is appropriate in the * current situation. */ function bool shouldWalk() { return !bForceRun; } /** */ function bool ShouldStrafe() { return true; } //=========================================================================== // BEHAVIOURS // // Behaviours are the major features of this class. They combine low // level unreal tech into chunks of functionality that make building // high-level AI behaviour easier (in the AIRole). // // Behaviours are generally implemented like this: // // function Perform_X - the external interface to the behaviour, // called by the role (usually). // // function Continue_X - function that evaluates iterative behaviours // to see if they are complete. // // state X - state code that actually does the work. // // Some things to watch out for: // // - make sure that all code paths result in a succeeded() or // failed() call to the role, so that it doesn't get stuck waiting // for a notify(). // - don't do the completion call back in Perform_X, because the // caller won't have had a chance to call WaitForNotification() // yet. // // Some ideas for the future: // // - behaviours seem like SSeq actions, maybe they could be unified? // //=========================================================================== //============== // NotEngaged_AtRest // // Just idling, waiting for something to happen. //============== function Perform_NotEngaged_AtRest() { curBehaviour = "Rest"; GotoState('NotEngaged_AtRest'); } state NotEngaged_AtRest { function BeginState() { Pawn.Acceleration = vect(0,0,0); Pawn.Velocity = vect(0,0,0); } // Don't Shoot function Timer() {} event SeePlayer( Pawn Seen ) { Global.SeePlayer(Seen); UpdateFocus( None ); } BEGIN: WaitForLanding(); if(TakeUpPosition != None) SetFocalPointNearLocation( Vector(TakeUpPosition.Rotation)*2000 + Pawn.Location ); else if( bEnemyInfoValid ) SetFocalPointNearLocation( LastSeenPos ); else SetRandomFocalPointLocation(2000); NearWall(1000); FinishRotation(); Sleep( RandRange(2.0, 3.0) ); myAIRole.NotEngagedAtRestSucceeded(); } /** * BEHAVIOUR: Wander * * Pick a quasi-random location and move to it. Performs one * "wander-move". */ function Perform_NotEngaged_Wander() { local int numAvail, i; local StagePosition position; // local NavigationPoint currentNode; local NavigationPoint anchor; curBehaviour = "Wander"; wanderDestination = None; if ( currentStage != None ) { for(i=0; i< currentStage.StagePositions.Length; i++) { if( !currentStage.StagePositions[i].bIsClaimed ) { numAvail++; // odds are 1/1, 1/2, 1/3, 1/4 ... if( FRand() < 1.0f/float(numAvail) ) { position = currentStage.StagePositions[i]; } } } UnClaimPosition(); ClaimPosition(position); wanderDestination = position; } else { // no stage, find a nearby pathnode instead anchor = pawn.anchor; if ( anchor == None || anchor.pathList.length < 1 ) { debugLog( "can't wander from here" ); } else { wanderDestination = anchor.pathList[rand(anchor.pathList.length)].end; UnClaimPosition(); } } DebugLog( "wandering to" @ wanderDestination ); ContinueWander(); } /** * Called after each step of a "wander-move". */ function ContinueWander() { // check if this wander is already done... if ( Pawn.ReachedDestination(wanderDestination) ) { myAIRole.NotEngagedWanderSucceeded(); LastWanderTime = Level.TimeSeconds; return; } // stop wandering, there's an enemy! else if ( Enemy != None ) { UnClaimPosition(); curBehaviour = "NoWander-Enemy"; myAIRole.NotEngagedWanderFailed(); return; } // try to move towards the destination... else if ( FindBestPathToward(wanderDestination, false, true) ) { if ( IsInState('NotEngaged_Wander') ) { GotoState('NotEngaged_Wander', 'MOVE'); } else { GotoState('NotEngaged_Wander'); } } // ERROR else { UnClaimPosition(); curBehaviour = "NoWanderPath"; myAIRole.NotEngagedWanderFailed(); Perform_Error_Stop(); } } /** * State code for wandering... */ state NotEngaged_Wander { // Don't Shoot function Timer() {} BEGIN: UpdateFocus( MoveTarget, true ); FinishRotation(); sleep(RandRange(0.1, 0.5)); MOVE: UpdateFocus( MoveTarget, true ); FinishRotation(); MoveToward( MoveTarget,,, ShouldStrafe(), ShouldWalk() ); ContinueWander(); } //============== // NotEngaged_MoveTowardPosition // // MoveToward an actor, not currently paying attention to an enemy //============== function Perform_MoveTowardPosition() { curBehaviour = "Moving"; GotoState('MoveToPosition'); } /** */ function Actor ActorToFace() { if ( Enemy == None || isBehindMe(MoveTarget) || Pawn.bWantsToCrouch ) { UpdateFocus( MoveTarget ); return MoveTarget; } else { UpdateFocus( Enemy ); return Enemy; } } state MoveToPosition { //ignores EnemyNotVisible; BEGIN: if ( isBehindMe(MoveTarget) || Pawn.bWantsToCrouch || Enemy == None ) { UpdateFocus( MoveTarget ); FinishRotation(); MoveToward(MoveTarget,,, ShouldStrafe(), ShouldWalk() ); } else { UpdateFocus( Enemy ); MoveToward(MoveTarget, Enemy,, ShouldStrafe(), ShouldWalk() ); } //NOTE maybe too soon to say success/fail? myAIRole.MoveToPositionSucceeded(); } /** * BEHAVIOUR - MoveToLocation * * Move to the specified location. The is the most general * move-behaviour. */ function Perform_MoveToLocation( vector location, optional float slop ) { moveDestination = location; moveSlop = slop; GotoState( 'MoveToLocation' ); } function Continue_MoveToLocation() { if ( VSize(pawn.location - moveDestination) < pawn.CollisionRadius + 8 + moveSlop ) { // reached destination... GotoState( 'MoveToLocation', 'DONE' ); myAIRole.MoveToLocationSucceeded(); return; } else { //TODO: perhaps check if we're stuck? GotoState( 'MoveToLocation', 'MOVE' ); } } state MoveToLocation { BEGIN: curBehaviour = "MoveToLocation"; MOVE: FinishRotation(); if ( pointReachable(moveDestination) ) { debugLog( "moving directly to" @ moveDestination ); FocalPoint = moveDestination; moveTo( moveDestination,, ShouldWalk() ); } else { moveTarget = FindPathTo( moveDestination ); if ( moveTarget == none ) { debugLog( "can't find path to" @ moveDestination ); if ( RouteGoal != none && VSize(pawn.location - moveDestination) > VSize(pawn.location - routeGoal.location) ) { debugLog( "falling back to" @ RouteGoal ); //TODO: maybe insert a melee attack here? Sleep( RandRange(0.07, 0.15) ); moveTarget = FindPathToward( routeGoal ); } else { Sleep( 0.3 ); if (myAIRole != none){ myAIRole.MoveToLocationFailed(); } Goto( 'DONE' ); } } if (MoveTarget !=None){ // if we didn't hit the Goto, moveTarget should be okay now... debugLog( "moving to" @ moveTarget ); FocalPoint = moveTarget.location; moveToward( moveTarget,,,ShouldStrafe(), ShouldWalk() ); } } Sleep( RandRange(0.1, 0.3) ); Continue_MoveToLocation(); DONE: curBehaviour = "DoneMovingToLocation"; Pawn.Acceleration = vect(0,0,0); Pawn.Velocity = vect(0,0,0); } /** * Attempt a melee attack on the current enemy */ function Perform_MeleeAttack() { //TODO: this has to be implemented on a per-game basis. } //============== // Engaged_StandGround // // We are engaged with an enemy, and are standing our ground (human turret) //============== function Perform_Engaged_StandGround() { curBehaviour = "StandGround"; if(Focus != Enemy) //not visible { SetFocalPointNearLocation(LastSeenPos); } else { UpdateFocus( Enemy ); } //Pawn.Velocity = vect(0,0,0); GotoState('Engaged_StandGround'); } /** */ state Engaged_StandGround { function BeginState() { //Pawn.Acceleration = vect(0,0,0); //Pawn.Velocity = vect(0,0,0); bFireAtLastLocation = true; } function EndState() { MonitoredPawn = None; bFireAtLastLocation = false; } event SeePlayer( Pawn Seen ) { Global.SeePlayer( Seen ); if(Enemy == Seen) { UpdateFocus( Enemy ); } } BEGIN: if( Pawn.Acceleration != vect(0,0,0) ) { MoveTo(Pawn.Location,, ShouldWalk()); } FinishRotation(); Sleep( 0.1 ); myAIRole.StandGroundSucceeded(); } //============== // FireAtTarget // // Firing on the ShootActor, and ignoring the enemy. //============== function Perform_NotEngaged_FireAtTarget() { DebugLog( "Perform_NotEngaged_FireAtTarget" ); curBehaviour = "FireAtTarget"; UpdateFocus( ShootTarget ); GotoState( 'NotEngaged_FireAtTarget' ); } /** * May need to be overridden for particular games... */ function bool isTargetDestroyed(); /* { local Pawn p; // consider invalid targets destroyed if ( ShootTarget == None ) return true; // check for pawn-health p = Pawn( ShootTarget ); if ( p != None ) return p.Health <= 0; // for all other actors, it's only considered destroyed if it's // being deleted... return !ShootTarget.bDeleteMe; } */ state NotEngaged_FireAtTarget { function Timer() { DebugLog( "FireAtTarget timer" ); TimedFireWeaponAtEnemy(); } /** * Fire at the target, not the enemy. */ function TimedFireWeaponAtEnemy() { if ( Pawn.Weapon == None ) return; if ( Pawn.Weapon.IsFiring() || (ShootTarget == None) || FireWeaponAt(ShootTarget) ) { DebugLog( self @ "fired at" @ ShootTarget ); SetCombatTimer(); //Timer between bursts of fire } else { SetTimer(0.1, True); //often enough... } } /** */ event SeePlayer( Pawn Seen ) { Global.SeePlayer(Seen); UpdateFocus( ShootTarget ); } BEGIN: DebugLog( "NotEngaged_FireAtTarget" ); FinishRotation(); SetTimer( 1, true ); Sleep( RandRange(0.5,2.0) ); if ( isTargetDestroyed() ) { tmpTarget = ShootTarget; if ( currentStage != None ) { currentStage.Report_TargetDestroyed( self, ShootTarget ); } if ( tmpTarget == ShootTarget ) ShootTarget = None; } myAIRole.FireAtTargetSucceeded(); } //============== // Engaged_RecoverEnemy // // Enemy was recently seen, and went out of sight, // try to find him by going to the last position we saw him // under the assumption that he's probably near by. //============== function Perform_Engaged_RecoverEnemy() { curBehaviour = "RecoverEnemy"; if( VSize(Pawn.Location - LastSeenPos) < 16) { Perform_Engaged_StandGround(); return; } else if ( PointReachable(LastSeenPos) ) { GotoState('Engaged_RecoverEnemy', 'PURSUE'); return; } else { MoveTarget = FindPathTo(LastSeenPos); if( MoveTarget != None ) { GotoState('Engaged_RecoverEnemy', 'PATH'); return; } else { Perform_Engaged_StandGround(); return; } } } state Engaged_RecoverEnemy { //ignores EnemyNotVisible; function bool KeepGoing() { //log("Reached"@claimedPosition@Pawn.ReachedDestination(claimedPosition) ); return (Enemy!=None) && !EnemyIsVisible() && (VSize(Pawn.Location - LastSeenPos) > 16); } event SeePlayer( Pawn Seen ) { Global.SeePlayer(Seen); if(Enemy == Seen ) UpdateFocus( Enemy ); } BEGIN: FinishRotation(); PURSUE: MoveTo(LastSeenPos,, ShouldWalk() ); Goto('NEXT'); PATH: MoveToward(MoveTarget,,, ShouldStrafe(), ShouldWalk() ); NEXT: if( KeepGoing() ) Perform_Engaged_RecoverEnemy(); myAIRole.RecoverEnemySucceeded(); } //============== // Engaged_GetLOS // // Cheat and get LOS to Enemy //============== /** */ function Perform_Engaged_GetLOS() { local StagePosition oldPos; curBehaviour = "GetLOS"; oldPos = claimedPosition; UnClaimPosition(); if ( currentStage != None ) { ClaimPosition( currentStage.Request_ShootingPosition(self) ); } if(claimedPosition == None) { ClaimPosition( oldPos ); if( EnemyIsVisible() ) { curBehaviour = "NoLOSSpotA"; Perform_Error_Stop(); } else { curBehaviour = "NoLOSSpotB"; Perform_Error_Stop(); } //NOTEGotoState( 'GetLOSFailed' ); return; } Continue_Engaged_GetLOS(); } /** */ function Continue_Engaged_GetLOS() { if( FindBestPathToward(claimedPosition, false, true) ) { if( IsInState('Engaged_GetLOS') ) { GotoState('Engaged_GetLOS', 'GETLOS'); } else { GotoState('Engaged_GetLOS'); } } else { UnClaimPosition(); curBehaviour = "NoGetLOSPath"; Perform_Error_Stop(); } } state GetLOSFailed { BEGIN: Sleep(0.1); myAIRole.GetLOSFailed(); } state Engaged_GetLOS { //ignores EnemyNotVisible; function bool KeepGoing() { //log("Reached"@claimedPosition@Pawn.ReachedDestination(claimedPosition) ); return (Enemy!=None) && !Pawn.ReachedDestination(claimedPosition); } BEGIN: FinishRotation(); GETLOS: //if(EnemyIsVisible()) if ( isBehindMe(MoveTarget) || Pawn.bWantsToCrouch || Enemy == None ) { UpdateFocus( MoveTarget ); FinishRotation(); MoveToward(MoveTarget,,, ShouldStrafe(), ShouldWalk() ); } else { UpdateFocus( Enemy ); MoveToward(MoveTarget, Enemy,, ShouldStrafe(), ShouldWalk() ); } if( KeepGoing() ) { Continue_Engaged_GetLOS(); } myAIRole.GetLOSSucceeded(); } //============== // Engaged_HideFromEnemy // // try to get to a spot where the enemy can't shoot us //============== function Perform_Engaged_HideFromEnemy() { curBehaviour = "Hide"; UnClaimPosition(); if ( currentStage != None ) { ClaimPosition( currentStage.Request_HidingPosition(self) ); } if(claimedPosition == None) { curBehaviour = "NoHideSpot"; Perform_Error_Stop(); return; } if( FindBestPathToward(claimedPosition, false, true) ) { if(IsInState('Engaged_HideFromEnemy') ) GotoState('Engaged_HideFromEnemy', 'HIDE'); else { exclaimMgr.Exclaim(EET_Fear, 0); GotoState('Engaged_HideFromEnemy'); } } else { UnClaimPosition(); curBehaviour = "NoHidePath"; Perform_Error_Stop(); } } state Engaged_HideFromEnemy { //ignores EnemyNotVisible; function bool KeepGoing() { return (Enemy!=None) && !Pawn.ReachedDestination(claimedPosition); } function BeginState() { if(currentStage != None) currentStage.Report_Unsighted(self, Enemy); } function EndState() { if(currentStage != None && EnemyIsVisible() ) { currentStage.Report_Eyesighted(self, Enemy); } Pawn.bWantsToCrouch = false; } BEGIN: // FinishRotation(); Pawn.bWantsToCrouch = true; HIDE: MoveToward(MoveTarget,,,ShouldStrafe(), false); if( KeepGoing() ) Perform_Engaged_HideFromEnemy(); LastHideTime = Level.TimeSeconds; myAIRole.HideFromEnemySucceeded(); } //================= // Engage_TakeCover // hide as best as possible... //================= /** * Crouch, or move to somewhere nearby for cover. */ function Perform_Engaged_TakeCover() { curBehaviour = "TakeCover"; UpdateFocus( Enemy ); if(currentStage.PositionProvidesCoverFromEnemy(claimedPosition, Enemy) > 0) { Pawn.bWantsToCrouch = true; GotoState('Engaged_TakeCover', 'CROUCH'); return; } //need a new spot Pawn.bWantsToCrouch = false; UnClaimPosition(); if ( currentStage != None ) { ClaimPosition( currentStage.Request_HidingPosition(self) ); } if ( claimedPosition == None ) { curBehaviour = "NoCoverSpot"; Pawn.bWantsToCrouch = true; Perform_Error_Stop(); //NOTEGotoState( 'TakeCoverFailed' ); return; } Continue_Engaged_TakeCover(); } /** * Move towards cover... */ function Continue_Engaged_TakeCover() { if( FindBestPathToward(claimedPosition, false, true) ) { // run crouched to nearby cover... if(MoveTarget == claimedPosition) { RouteDist = VSize(Pawn.Location - MoveTarget.Location); } if(RouteDist < maxDistToCrouchForCover) { Pawn.bWantsToCrouch = true; } GotoState('Engaged_TakeCover', 'RUN'); } else { UnClaimPosition(); //NOTE crashes :-( myAIRole.TakeCoverFailed(); curBehaviour = "NoCoverPath"; Perform_Error_Stop(); } } state TakeCoverFailed { BEGIN: Sleep(0.1); myAIRole.TakeCoverFailed(); Perform_Error_Stop(); } state Engaged_TakeCover { function bool KeepGoing() { return (Enemy!=None) && !Pawn.ReachedDestination(claimedPosition); } function BeginState() { if(currentStage != None) { currentStage.Report_Unsighted(self, Enemy); } } function EndState() { if(currentStage != None && EnemyIsVisible() ) { currentStage.Report_Eyesighted(self, Enemy); } if ( pawn != None ) pawn.bWantsToCrouch = false; } function EnemyNotVisible() { Global.EnemyNotVisible(); StopFiring(); } BEGIN: // FinishRotation(); CROUCH: StopFiring(); Sleep(0.1); Goto('DONE'); RUN: if ( isBehindMe(MoveTarget) || Pawn.bWantsToCrouch || Enemy == None ) { UpdateFocus( MoveTarget ); FinishRotation(); MoveToward(MoveTarget,,, ShouldStrafe(), false ); } else { UpdateFocus( Enemy ); MoveToward(MoveTarget, Enemy,, ShouldStrafe(), false ); } if ( KeepGoing() ) { Sleep(0.1); Continue_Engaged_TakeCover(); } DONE: UpdateFocus( Enemy ); LastTakeCoverTime = Level.TimeSeconds; myAIRole.TakeCoverSucceeded(); } //==================== // Engage_OutFromCover // Come out of cover just long enough to shoot at enemy (whack-a-mole) // Do it fast for element of surprise //==================== function Perform_Engaged_OutFromCover() { local int i; local StagePosition position, shootDest; local float dist, tmpDist; curBehaviour = "OutFromCover"; if( claimedPosition != None && currentStage.positionHasLOFToEnemy(claimedPosition, Enemy) ) { GotoState('Engaged_OutFromCover', 'STANDUP'); Pawn.bWantsToCrouch = false; return; } UnClaimPosition(); tmpDist = 1200; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { position = currentStage.StagePositions[i]; if( position.bIsClaimed || !currentStage.positionHasLOFToEnemy(position, Enemy) ) { continue; } //dist = VSize(position.Location - Pawn.Location); dist = VSize(position.Location - LastSeeingPos); if( dist < tmpDist ) { tmpDist = dist; shootDest = position; } } if( shootDest != None) { ClaimPosition(shootDest); //MoveTarget = shootDest; } else { //NOTE: If we can't just come out of cover, we should be recovering, or // staking out curBehaviour = "NoOutFromCoverSpot"; Perform_Error_Stop(); } Continue_Engaged_OutFromCover(); } function Continue_Engaged_OutFromCover() { if( FindBestPathToward(claimedPosition, false, true) ) { if( IsInState('Engaged_OutFromCover') ) GotoState('Engaged_OutFromCover', 'MOVE'); else GotoState('Engaged_OutFromCover'); } else { UnClaimPosition(); curBehaviour = "NoOutFromCoverPath"; Perform_Error_Stop(); } } state Engaged_OutFromCover { function bool KeepGoing() { //log("Reached"@claimedPosition@Pawn.ReachedDestination(claimedPosition) ); return (Enemy!=None) && !Pawn.ReachedDestination(claimedPosition); } BEGIN: MOVE: if ( isBehindMe(MoveTarget) || Pawn.bWantsToCrouch || Enemy == None ) { UpdateFocus( MoveTarget, true ); FinishRotation(); MoveToward(MoveTarget,,, ShouldStrafe(), false ); } else { UpdateFocus( Enemy ); MoveToward(MoveTarget, Enemy,, ShouldStrafe(), false ); } if( KeepGoing() ) Continue_Engaged_OutFromCover(); STANDUP: Sleep(0.1); DONE: UpdateFocus( Enemy ); myAIRole.outFromCoverSucceeded(); } //======================= // Engage_SuppressionFire // The enemy isn't visible, but lay down fire at the last seen position to keep him hidden //======================= function Perform_Engaged_SupressionFire() { curBehaviour = "SupressionFire"; FocalPoint = LastSeenPos; GotoState('Engaged_SupressionFire'); } state Engaged_SupressionFire { function BeginState() { bFireAtLastLocation = true; } function EndState() { bFireAtLastLocation = false; } event SeePlayer( Pawn Seen ) { Global.SeePlayer( Seen ); if(Enemy == Seen) { UpdateFocus( Enemy ); } } event EnemyNotVisible() { Global.EnemyNotVisible(); UpdateFocus( None, true ); FocalPoint = LastSeenPos; } /** * Fire at the last seen position if we can't see enemy */ function TimedFireWeaponAtEnemy() { if ( Pawn.Weapon == None ) return; if( Pawn.Weapon.IsFiring() ) { SetCombatTimer(); return; } //FireWeaponAt(Enemy) NumShotsToGo = MaxNumShots; CalculateMissVectorScale(); SetCombatTimer(); //Timer between bursts of fire WeaponFireAgain(Pawn.Weapon.RefireRate(),false); } BEGIN: StopFiring(); FinishRotation(); Sleep(0.1); TimedFireWeaponAtEnemy(); Sleep( NumShotsToGo * getWeaponFireRate(Pawn.Weapon) ); myAIRole.supressionFireSucceeded(); } /** * Game specific override */ function float getWeaponFireRate( Weapon w ); //NOTEPawn.Weapon.FireMode[Max(0,Pawn.Weapon.BotMode)].FireRate /** * BEHAVIOUR: Charge * * Make a run at the current enemy */ function Perform_Engaged_Charge( optional float maxDuration ) { curBehaviour = "Charge"; if ( maxDuration <= 0 ) maxDuration = 10; chargeEndTime = Level.timeSeconds + maxDuration; UnClaimPosition(); GotoState( 'Engaged_Charge', 'BEGIN' ); } function Continue_EngagedCharge() { if ( VSize(Pawn.location - Enemy.Location) < (Pawn.CollisionRadius + Enemy.CollisionRadius + 8) || Pawn.ReachedDestination(Enemy) ) { myAIRole.ChargeSucceeded(); GotoState('Engaged_Charge', 'DONE'); } else if ( Level.timeSeconds > chargeEndTime ) { myAIRole.ChargeFailed(); GotoState('Engaged_Charge', 'DONE'); } else if ( ActorReachable(Enemy) ) { MoveTarget = Enemy; GotoState('Engaged_Charge', 'MOVE'); } else if ( FindBestPathToward(Enemy, false,true) ) { GotoState('Engaged_Charge', 'MOVE'); } else { myAIRole.ChargeFailed(); GotoState('Engaged_Charge', 'DONE'); } } state Engaged_Charge { BEGIN: Continue_EngagedCharge(); MOVE: if ( isBehindMe(MoveTarget) || Pawn.bWantsToCrouch || Enemy == None ) { UpdateFocus( MoveTarget ); FinishRotation(); MoveToward(MoveTarget,,, ShouldStrafe(), ShouldWalk() ); } else { UpdateFocus( Enemy ); MoveToward(MoveTarget, Enemy,, ShouldStrafe(), ShouldWalk() ); } Continue_EngagedCharge(); DONE: curBehaviour = "Charge-done"; UpdateFocus( Enemy ); FinishRotation(); } //============== //Engage_StrafeMove // do a little strafe to keep from looking like we're stuck in one spot. //============== function Perform_Engaged_StrafeMove() { curBehaviour = "Strafe"; PickStrafeDir(); GotoState('Engaged_StrafeMove'); } function bool TestDirection(vector dir, float minDist, out vector pick) { local vector HitLocation, HitNormal, dist; local actor HitActor; pick = dir * RandRange(minDist, 2 * minDist); 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; /* if( Pawn.Anchor != None && VSize(pick - Pawn.Anchor.Location) > 500 ) { pick = pick + Normal(Pawn.Anchor.Location - pick) * ( VSize(Pawn.Anchor.Location - pick) - 500 ) ; } */ if( claimedPosition != None && VSize(pick - claimedPosition.Location) > 500 ) { pick = pick + Normal(claimedPosition.Location - pick) * ( VSize(claimedPosition.Location - pick) - 500 ) ; } dist = pick - Pawn.Location; if (Pawn.Physics == PHYS_Walking) dist.Z = 0; if(bDebugLogging) curBehaviour = curBehaviour @ VSize(dist) / Pawn.CollisionRadius; return (VSize(dist) > minDist); } function PickStrafeDir() { local Vector strafeDir; strafeDir = Normal(Vector(Rotation) cross vect(0,0,1)); bStrafeDir = !bStrafeDir; if(bStrafeDir) strafeDir = -1 * strafeDir; if(!TestDirection(strafeDir, Pawn.CollisionRadius * RandRange(2.0, 5.0), strafeTarget) ) { curBehaviour = "NoStrafeDir"; Perform_Error_Stop(); } //drawdebugline(Pawn.Location, strafeTarget, 255,255,255); } state Engaged_StrafeMove { //Avoid falling off ledges while strafing function BeginState() { Pawn.bAvoidLedges = true; Pawn.bStopAtLedges = true; Pawn.bCanJump = false; bAdjustFromWalls = false; } function EndState() { bAdjustFromWalls = true; if ( Pawn == None ) return; Pawn.bAvoidLedges = false; Pawn.bStopAtLedges = false; MinHitWall -= 0.15; if (Pawn.JumpZ > 0) Pawn.bCanJump = true; } BEGIN: FinishRotation(); MoveTo( strafeTarget, Enemy, ShouldWalk() ); LastStrafeMoveTime = Level.TimeSeconds; //If it was *our* move that hid the enemy, we ought to recover it. if ( (Enemy == None) || EnemyIsVisible() || !FastTrace(Enemy.Location, LastSeeingPos) ) Goto('FinishedStrafe'); RecoverEnemy: curBehaviour = "StrafeRecover"; StopFiring(); Sleep(0.1 + 0.2 * FRand()); Destination = LastSeeingPos + 4 * Pawn.CollisionRadius * Normal(LastSeeingPos - Pawn.Location); FinishRotation(); MoveTo(Destination, Enemy, ShouldWalk() ); FinishedStrafe: myAIRole.strafeMoveSucceeded(); } //============== // Engaged_HuntEnemy // Hopefully this doesn't happen to often, but basically, chase the player since we can't rely on the // stage for LOS (or he's just too far) // NOTE Rather than pathfind right to the enemy, we could try AI Game Wisdon 2 article 2.6. //============== function Perform_Engaged_HuntEnemy() { curBehaviour = "Hunting"; UnClaimPosition(); Continue_Engaged_HuntEnemy(); } function Continue_Engaged_HuntEnemy() { if( FindBestPathToward(Enemy, false, true) ) { if( IsInState('Engaged_HuntEnemy') ) GotoState('Engaged_HuntEnemy', 'MOVE'); else GotoState('Engaged_HuntEnemy'); } else { curBehaviour = "NoHuntPath"; Perform_Error_Stop(); } } state Engaged_HuntEnemy { function bool KeepGoing() { return (Enemy!=None) && !EnemyIsVisible(); } BEGIN: FinishRotation(); MOVE: MoveToward(MoveTarget,,, ShouldStrafe(), ShouldWalk() ); if(KeepGoing()) Continue_Engaged_HuntEnemy(); myAIRole.HuntSucceeded(); } //============== // Engaged_Panic // This can be our basic "Panic" state.. we will just try to run away from the enemy. // if we gain enough distance, lose sight, or do it long enough, we can finish panicking // stage for LOS (or he's just too far) //============== function bool KeepPanicking() { if(Enemy == None) return false; if( VSize(Pawn.Location - Enemy.Location) < PanicRange ) return true; if( !TimeElapsed(LastSeenTime, 2.0) ) return true; if( !TimeElapsed(PanicStartTime, 3.0) ) return true; return false; } function Perform_Engaged_Panic() { curBehaviour = "Panic"; UpdateFocus( None ); bHavePanicked = true; PanicStartTime=Level.TimeSeconds; UnClaimPosition(); exclaimMgr.Exclaim(EET_Panic, 0.1); GotoState('Engaged_Panic'); } state Engaged_Panic { //ignores EnemyNotVisible; event SeePlayer( Pawn Seen ) { Global.SeePlayer(Seen); UpdateFocus( MoveTarget ); } /** * from our current anchor, pick a pathposition that goes away from enemy. * search locally since we're not really "thinking coherently", plus * getting cornered might look cool for a panic retreat. **/ function PickDestination() { local int i; local StagePosition position, panicDest; local float dist, tmpDist; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { position = currentStage.StagePositions[i]; dist = VSize(position.Location - Enemy.Location); if( VSize(position.Location - Pawn.Location) < dist && (dist > tmpDist) ) { tmpDist = dist; panicDest = position; } } if (! FindBestPathToward(panicDest, false, false) ) { //NOTE This should do something more interesting like cower, beg for mercy etc... curBehaviour = "NoPanicDir"; Perform_Error_Stop(); } } function PickDestinationOld() { local NavigationPoint bestPosition, position; local ReachSpec spec; local float dp, bestDP; local int i, numPaths; local vector enemyDir; local NavigationPoint fromSpot; fromSpot = Pawn.Anchor; if(fromSpot == None) fromSpot = Pawn.LastAnchor; numPaths = 0; enemyDir = (Pawn.Location - enemy.Location); for(i = 0; i < fromSpot.PathList.length; i++ ) { spec = fromSpot.PathList[i]; position = spec.End; dp = (spec.End.Location - spec.Start.Location) dot enemyDir; if( dp > 0.0 ) { numPaths++; //don't always pick the "most away" position, since then any bots in the area might go the same way. if( FRand() < 1.0f/float(numPaths) ) // odds are 1/1, 1/2, 1/3, 1/4 ... { bestDP = dp; bestPosition = position; } } } if( bestPosition != None ) { MoveTarget = bestPosition; } else { //NOTE This should do something more interesting like cower, beg for mercy etc... curBehaviour = "NoPanicDir"; Perform_Error_Stop(); } } function BeginState() { if(currentStage != None) currentStage.Report_Unsighted(self, Enemy); } function EndState() { if(currentStage != None && EnemyIsVisible() ) { currentStage.Report_Eyesighted(self, Enemy); } } BEGIN: PickDestination(); // FinishRotation(); FLEE: MoveToward(MoveTarget,,, ShouldStrafe(), false); PickDestination(); if( KeepPanicking() ) Goto('FLEE'); DONE: myAIRole.PanicSucceeded(); } //======================== // Engaged_AdvanceMove // Find a new position to engage our enemy from //======================== function Perform_Engaged_AdvanceMove() { local StagePosition oldPosition; local StagePosition bestPosition; local float oldWeight, oldDist; curBehaviour = "AdvanceMove"; oldPosition = claimedPosition; if(oldPosition == None) { oldWeight = 0; oldDist = 99999; } else { oldWeight = WeightStagePosition(oldPosition); oldDist = VSize(oldPosition.Location - Enemy.Location); } UnClaimPosition(); if(!PickClosestBetterSpot( oldWeight, oldDist, oldPosition, bestPosition) ) { curBehaviour = "NoNewTacticalSpot"; Perform_Error_Stop(); } ClaimPosition(bestPosition); Continue_Engaged_AdvanceMove(); return; } function Continue_Engaged_AdvanceMove() { if( FindBestPathToward(claimedPosition, false, true) ) { if( IsInState('Engaged_AdvanceMove') ) GotoState('Engaged_AdvanceMove', 'MOVE'); else GotoState('Engaged_AdvanceMove'); } else { UnClaimPosition(); curBehaviour = "NoAdvanceMovePath"; Perform_Error_Stop(); } } state Engaged_AdvanceMove { function bool KeepGoing() { //log("Reached"@claimedPosition@Pawn.ReachedDestination(claimedPosition) ); return (Enemy!=None) && !Pawn.ReachedDestination(claimedPosition); } BEGIN: FinishRotation(); MOVE: if ( isBehindMe(MoveTarget) || Pawn.bWantsToCrouch || Enemy == None ) { UpdateFocus( MoveTarget, true ); FinishRotation(); MoveToward( MoveTarget,,, ShouldStrafe(), ShouldWalk() ); } else { UpdateFocus( enemy ); MoveToward( MoveTarget, Enemy,, ShouldStrafe(), ShouldWalk() ); } if( KeepGoing() ) Continue_Engaged_AdvanceMove(); LastAdvanceMoveTime = Level.TimeSeconds; myAIRole.advanceMoveSucceeded(); } //============== // Dead // // Make sure we notify Stage if appropriate //============== function WasKilledBy(Controller Other) { //add fear local StagePosition position; local int i; local float tmpDist; if ( currentStage == None ) return; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { position = currentStage.StagePositions[i]; if ( position == None || Pawn == None ) continue; tmpDist = VSize(position.Location - Pawn.Location); if( tmpDist < 750) { position.FearCost += 750 - tmpDist; } } } /** */ //NOTE not sure why this works, but WasKilledBy() doesn't (in UW codebase)...? function PawnDied( Pawn p ) { local NavigationPoint dest; if ( p.anchor != None ) p.anchor.fearCost += FEAR_PENALTY; dest = NavigationPoint( moveTarget ); if ( dest != None ) dest.fearCost += FEAR_PENALTY; super.PawnDied( p ); myAIRole.OnKilled( none ); GotoState( 'Dead' ); } /** * Perform proper housekeeping before being destroyed. */ function Cleanup() { if ( currentStage != none ) currentStage.leaveStage(self, RSN_Died); Enemy = None; if ( (GoalScript != None) && (HoldSpot(GoalScript) == None) ) { FreeScript(); } StopFiring(); UnClaimPosition(); if ( myCreator != None ) myCreator.OpponentDied( self ); } /** */ function DestroyPawn() { Cleanup(); super.DestroyPawn(); } State Dead { ignores SeePlayer, HearNoise, KilledBy, EnemyNotVisible; //function Timer() {} function BeginState() { Destroy(); } } // for hibernating State Dormant { ignores EnemyNotVisible, SeePlayer, HearNoise; } //============== // Debugging //============== /** * Basically the same as standGround, but it relies on the caller to set "curBehaviour" to be used for unaccounted-for * situations like when a path can't be found, etc. **/ function Perform_Error_Stop() { if(Focus != Enemy) //not visible { SetFocalPointNearLocation(LastSeenPos); } if ( Pawn != none ) Pawn.Velocity = vect(0,0,0); GotoState('Error_Stop'); } state Error_Stop { function BeginState() { if ( pawn != none ) { Pawn.Acceleration = vect(0,0,0); Pawn.Velocity = vect(0,0,0); } } BEGIN: FinishRotation(); Sleep( 2.0 ); myAIRole.ErrorStopSucceeded(); } /** */ function vector WorldToScreen( vector w ) { //NOTE: I think this method is portable across codebases... return Level.GetLocalPlayerController().player.console.WorldToScreen( w ); } /** * When called, will draw debug text above the bots head */ function DrawHUDDebug(Canvas C) { local vector screenPos; if (!bDebugLogging || Pawn == None) return; screenPos = WorldToScreen( Pawn.Location + vect(0,0,1)*Pawn.CollisionHeight ); if (screenPos.Z > 1.0) return; C.SetPos(screenPos.X - 8*Len(curBehaviour)/2, screenPos.y-12); C.SetDrawColor(0,255,0); //C.Font = C.tinyFont; C.DrawText( myAIROle.GetStateName()); C.SetPos(screenPos.X - 8*Len(curBehaviour)/2, screenPos.y); if( LatentFloat == 0) { C.DrawText( curBehaviour ); } else { C.DrawText( curBehaviour @LatentFloat); } C.SetPos(screenPos.X - 8*Len(curBehaviour)/2, screenPos.y-24); if( !exclaimMgr.bScheduled && Level.TimeSeconds <= exclaimMgr.nextExclamation.time + 1 ) { C.DrawText( exclaimMgr.LastMsg ); } } function DisplayDebug(Canvas Canvas, out float YL, out float YPos) { local string drawText; if ( !bDebugLogging ) return; // canvas.font = canvas.tinyFont; Super.DisplayDebug(Canvas,YL, YPos); Canvas.SetPos(4,YPos); Canvas.SetDrawColor(0,255,0); drawText = "STAGE:"; if ( currentStage == None ) { drawText = drawText @ "None"; } else { drawText = drawText @ currentStage.StageName; } drawText = drawText @ " Stage order:"; switch (curStageOrder) { case SO_None: drawText = drawText @ "SO_None"; break; case SO_TakeUpPosition: drawText = drawText @ "SO_TakeUpPosition"@TakeUpPosition; break; case SO_HoldPosition: drawText = drawText @ "SO_HoldPosition"; break; case SO_TakeCover: drawText = drawText @ "SO_TakeCover"; break; default: drawText = drawText @ "Unrecognized order:" @ curStageOrder; break; } Canvas.DrawText(drawText); YPos += YL; Canvas.SetPos(4,YPos); drawText = "Performing:" @ curBehaviour; if(Enemy != None) { drawText = drawText @ "hitChance:" @ ChanceToHit() @ "enemyDist" @ VSize(Enemy.Location - Pawn.Location); } Canvas.DrawText(drawText); YPos += YL; Canvas.SetPos(4,YPos); drawText = "Enemy:" $ Enemy @ "ShootTarget:" $ ShootTarget @ "FocalPoint:" $ FocalPoint; Canvas.DrawText( drawText ); YPos += YL; Canvas.SetPos(4,YPos); myAIRole.DisplayDebug(Canvas,YL, YPos); } /** * Handy debugging helper. No flags means always output, otherwise * output is only generated when the flag bits are also set in * AIDebugFlags. */ function DebugLog( coerce String s, optional int flags ) { if ( bDebugLogging ) { if ( flags == 0 || (flags & AIDebugFlags) != 0 ) { Log( self @ s, 'VGSPAIController' ); } } } function debugTick(float dT) { if(claimedPosition != None) drawdebugline(Pawn.Location, claimedPosition.Location, 255,0,255); /* if(Pawn.Anchor != None) drawdebugline(Pawn.Location, Pawn.Anchor.Location, 0,255,0 ); */ //drawEnemyTarget(); drawCover(); //drawPosnLOS(); //drawPositionWeights(); //drawExperimentalGrid(); //green //debugDrawBlockingLOF(); //red //debugDrawBlockedLOF(); // blue //debugDrawFireCone(); //cone-shaped //debugDrawVerifiedPositions(); //white and black } /** */ function drawEnemyTarget() { if(Enemy != None) { if(EnemyIsVisible()) { if( SameTeamAs(Level.GetLocalPlayerController()) ) { drawdebugline( Pawn.Location+vect(0,0,10), Enemy.Location, 0, 0, 255 ); } else { drawdebugline( Pawn.Location+vect(0,0,10), Enemy.Location, 255, 0, 0 ); } } else { drawdebugline( Pawn.Location+vect(0,0,10), LastSeenPos, 179, 179, 255 ); } } } /** */ function drawCover() { local int i; local StagePosition position; if ( currentStage == None ) return; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { position = currentStage.StagePositions[i]; if( currentStage.PositionProvidesCoverFromEnemy(position, Enemy) > 0) { drawdebugline( position.Location, position.Location + vect(0,0,1)*50, 0,255,0 ); } //else if (position.bCrouchCover) { //if ( position.CrouchCover >= CT_PartialCover ) { //drawdebugline( position.Location, // position.Location + vect(0,0,1)*50, 255,0,0 ); //} else { drawdebugline( position.Location, position.Location + vect(0,0,1)*50, 255,0,0 ); } } } function drawPosnLOS() { local int i, j; local StagePosition position; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { position = currentStage.StagePositions[i]; //if(position.bLOSTOPlayer > 0) for(j=0;j<8;j++) { if( (position.StandLOF & (0x1< 0.123 ) { drawdebugline(Pawn.Location, Enemy.Location + (( Normal(Enemy.Location - Pawn.Location)*VSize(Pawn.Location - Enemy.Location)* 0.123) << rotator(vect(0,1,0))), 0,0,255); drawdebugline(Pawn.Location, Enemy.Location + (( Normal(Enemy.Location - Pawn.Location)*VSize(Pawn.Location - Enemy.Location)* 0.123) << rotator(vect(0,-1,0))), 0,0,255); } else { drawdebugline(Pawn.Location, Enemy.Location + (( Normal(Enemy.Location - Pawn.Location)*Enemy.CollisionRadius*3.0) << rotator(vect(0,1,0))), 0,255,0); drawdebugline(Pawn.Location, Enemy.Location + (( Normal(Enemy.Location - Pawn.Location)*Enemy.CollisionRadius*3.0) << rotator(vect(0,-1,0))), 0,255,0); } } } function debugDrawBlockedLOF() { local int i; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { if( currentStage.calculateStagePosnLOSIsBlocked(self, currentStage.StagePositions[i]) > 0) { drawdebugline(currentStage.StagePositions[i].Location, currentStage.StagePositions[i].Location + 50 * vect(0,0,1), 0,0,255); } } } function debugDrawBlockingLOF() { local int i; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { if( currentStage.calculateStagePosnBlocksLOS(self, currentStage.StagePositions[i]) > 0) { drawdebugline(currentStage.StagePositions[i].Location, currentStage.StagePositions[i].Location + 100 * vect(0,0,1), 255,0,0); } } } /** * Determines whether an actor is behind the pawn. */ function bool isBehindMe( Actor a ) { return Vector(pawn.rotation) dot (a.location - pawn.location) < 0.5; } //============= // Helpers //============= /** * integer version of RandRange, returns an random integer between Min and Max **/ function int iRandRange(int min, int max) { return Min + Rand((Max - Min) + 1); } /** * startTime must have been set to Level.TimeSeconds at some point in the past **/ function bool TimeElapsed(float startTime, float duration) { return (Level.TimeSeconds - startTime) >= duration; } /** * Pick closest better StagePosition **/ function bool PickClosestBetterSpot(float baseWeight, float baseDist, StagePosition oldPosition, out StagePosition newPosition) { local StagePosition position; local float weight, dist, bestDist; local int i; bestDist = 99999; for( i = 0; i < currentStage.StagePositions.Length; i++ ) { position = currentStage.StagePositions[i]; weight = WeightStagePosition(position); if( position == oldPosition || position.bIsClaimed || weight < baseWeight || VSize(position.Location - Enemy.Location) > baseDist || !VerifyShootingPosition(position) ) continue; dist = VSize(position.Location - Pawn.Location); if(dist < bestDist) { bestDist = dist; newPosition = position; } } if(newPosition == None) return false; return true; } o;class BDHuntedInvasion extends DOTZInvasion; /***************************************************************** * InitGameReplicationInfo * Something just for invasion so the switc teams stuff doesn't come * up in the menu ***************************************************************** */ function InitGameReplicationInfo(){ super.InitGameReplicationInfo(); GameReplicationInfo.bAllowedToSwitchTeams =false; GameReplicationInfo.MatchID = ID_INV; } /***************************************************************** * PickTeam * When starting a team match we assing teams alternately to each * side based upon order of entry. ***************************************************************** */ function byte PickTeam(byte Current, Controller C){ local int i; local int RedCount, BlueCount; //evaluate the current team distribution for (i=0; i < GameReplicationInfo.PRIArray.length; i++){ if (GameReplicationInfo.PRIArray[i].Team.TeamIndex == 0){ RedCount++; } else { BlueCount++; } } //put the new guy on the smaller team if (RedCount > BlueCount){ return 1; } else { return 0; } } function RestartPlayer( Controller P ) { if ((bTeamGame)&&( P.IsA('PlayerController') && P.PlayerReplicationInfo.Team.TeamIndex == 1)&&(P.PawnClass == class'XDOTZCharacters.JackSlade')) P.SetPawnClass("BD2004Zombies.ZombieJackDM", " "); else if ((bTeamGame)&&( P.IsA('PlayerController') && P.PlayerReplicationInfo.Team.TeamIndex == 1)&&(P.PawnClass == class'XDOTZCharacters.PlayerOtis')) P.SetPawnClass("BD2004Zombies.ZombieOtis", " "); else if ((bTeamGame)&&( P.IsA('PlayerController') && P.PlayerReplicationInfo.Team.TeamIndex == 0)&&(P.PawnClass == class'XDOTZCharacters.PlayerOtis')) P.SetPawnClass("BD2004Zombies.HunterOtis", " "); else if ((bTeamGame)&&( P.IsA('PlayerController') && P.PlayerReplicationInfo.Team.TeamIndex == 0)&&(P.PawnClass == class'XDOTZCharacters.JackSlade')) P.SetPawnClass("BD2004Zombies.HunterJackSlade", " "); else if ((bTeamGame == false) &&(P.PawnClass == class'XDOTZCharacters.JackSlade')) P.SetPawnClass("BD2004Zombies.ZombieJackDM", " "); else if ((bTeamGame == false) &&(P.PawnClass == class'XDOTZCharacters.PlayerOtis')) P.SetPawnClass("BD2004Zombies.HunterOtis", " "); super.RestartPlayer(P); P.PlayerReplicationInfo.bOnlySpectator = false; } function GiveDefaultInventory(Pawn P) { AdvancedPawn(P).SetInventoryLimit(1,'MeleeWeapon'); AdvancedPawn(P).SetInventoryLimit(1,'DOTZGrenadeWeapon'); AdvancedPawn(P).SetInventoryLimit(1,'GlockWeapon'); AdvancedPawn(P).SetInventoryLimit(1,'M16Weapon'); AdvancedPawn(P).SetInventoryLimit(1,'RevolverWeapon'); AdvancedPawn(P).SetInventoryLimit(1,'RifleWeapon'); AdvancedPawn(P).SetInventoryLimit(1,'ShotgunWeapon'); AdvancedPawn(P).SetInventoryLimit(1,'SniperWeapon'); AdvancedPawn(P).SetInventoryLimit(2,'DOTZGunWeapon'); // give the player default weaponry if ( P.IsA('ZombieJackDM') ) P.GiveWeapon("BD2004Zombies.ZombieFistweapon"); else if ( P.IsA('HunterOtis') ) P.GiveWeapon("DOTZWeapons.Fistweapon"); else if ( P.IsA('ZombieOtis') ) P.GiveWeapon("BD2004Zombies.ZombieFistweaponOtis"); else if ( P.IsA('HunterJackSlade') ) P.GiveWeapon("DOTZWeapons.Fistweapon"); } /***************************************************************** * Reset * Start the game again ***************************************************************** */ function Reset(){ if (TimeLimit == -1){ RemainingTime = -1; } else { RemainingTime = TimeLimit * 60 + 1; } super.Reset(); } A@7AA!gHunted.HumanFireAxeWeapon X _ m67Z y9=APAPAPAPAPAPAPAPAPL9L9L9L9L9L9L9L9sߊsߊ?>sߊsߊsߊsߊ?>sߊsߊsߊsߊ?>sߊsߊsߊsߊ?>sߊsߊsߊsߊsߊsߊsߊ?66?>66da}zy|x{[Y NPw.N* a n\0Nr* rn*(J n G(L' C@;CC%gHunted.HumanBaseBallBatWeapon @// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /** * OtisAIController - * * @version $Rev: 4874 $ * @author Jesse LaChapelle (jesse@digitalextremes.com) * @date June 2004 */ class BDGlockHunter extends BDHunterAIController; //=========================================================================== // //=========================================================================== /** */ function Possess( Pawn p ) { super.Possess( p ); p.GiveWeapon( "Hunted.HumanGlockWeapon" ); } /** */ function bool SameTeamAs(Controller C) { // only zombies return (BDHunterAIController(c) != None); } //=========================================================================== // Otis-death game logic... //=========================================================================== /*function bool FireWeaponAt(Actor A) { //DebugLog( "Trying to fire at" @ A ); if ( A == None ) A = Enemy; Target = A; if ( (Pawn.Weapon != None) && Pawn.Weapon.HasAmmo() && !Pawn.Weapon.IsFiring() ) { NumShotsToGo = iRandRange(MinNumShots,MaxNumShots); CalculateMissVectorScale(); return WeaponFireAgain(Pawn.Weapon.RefireRate(),false); } else { DebugLog( "Unable to fire weapon:" @ Pawn.weapon, DEBUG_FIRING ); return false; } } */ //=========================================================================== // Default Properties //=========================================================================== [^ MPw.M* f t.a -v -(oK-(-(-v(B F@5FFgHunted.HumanGlockWeapon _ e pB!d P ;TL9oCa"~$L?w$?z$zD|$@@[c LPw.L* k _ -v -v -v'stasisKo$-'-'-'q!i H@8HH"gHunted.HumanLeadPipeWeapon D// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /** * OtisAIController - * * @version $Rev: 4874 $ * @author Jesse LaChapelle (jesse@digitalextremes.com) * @date June 2004 */ class BDBatHunter extends BDHunterAIController; //=========================================================================== // //=========================================================================== /** */ function Possess( Pawn p ) { super.Possess( p ); p.GiveWeapon( "Hunted.HumanBaseBallBatWeapon" ); } /** */ function bool SameTeamAs(Controller C) { // only zombies return (BDHunterAIController(c) != None); } //=========================================================================== // Otis-death game logic... //=========================================================================== /*function bool FireWeaponAt(Actor A) { //DebugLog( "Trying to fire at" @ A ); if ( A == None ) A = Enemy; Target = A; if ( (Pawn.Weapon != None) && Pawn.Weapon.HasAmmo() && !Pawn.Weapon.IsFiring() ) { NumShotsToGo = iRandRange(MinNumShots,MaxNumShots); CalculateMissVectorScale(); return WeaponFireAgain(Pawn.Weapon.RefireRate(),false); } else { DebugLog( "Unable to fire weapon:" @ Pawn.weapon, DEBUG_FIRING ); return false; } } */ //=========================================================================== // Default Properties //=========================================================================== [h JPw.J* n Krz+Ww* m$KUw* w* a Gs JG.5-'aBa_ p Hp*LH-I Es JE.5-'aBa_ r FQ ) mF Cs DF ' lD H A ~<&k i ` qB!t kiTL9oCa"~$L?w$?z$zD|$@@@// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 /** * OtisAIController - * * @version $Rev: 4874 $ * @author Jesse LaChapelle (jesse@digitalextremes.com) * @date June 2004 */ class BDAxeHunter extends BDHunterAIController; //=========================================================================== // //=========================================================================== /** */ function Possess( Pawn p ) { super.Possess( p ); p.GiveWeapon( "Hunted.HumanFireAxeWeapon" ); } /** */ function bool SameTeamAs(Controller C) { // only zombies return (BDHunterAIController(c) != None); } //=========================================================================== // Otis-death game logic... //=========================================================================== /*function bool FireWeaponAt(Actor A) { //DebugLog( "Trying to fire at" @ A ); if ( A == None ) A = Enemy; Target = A; if ( (Pawn.Weapon != None) && Pawn.Weapon.HasAmmo() && !Pawn.Weapon.IsFiring() ) { NumShotsToGo = iRandRange(MinNumShots,MaxNumShots); CalculateMissVectorScale(); return WeaponFireAgain(Pawn.Weapon.RefireRate(),false); } else { DebugLog( "Unable to fire weapon:" @ Pawn.weapon, DEBUG_FIRING ); return false; } } */ //=========================================================================== // Default Properties //=========================================================================== u [ vB!v aTL9oCa"~$L?w$?z$zD|$@@[class BD2004ZombiePawnBase extends HumanPawnBase abstract notplaceable HideDropDown; var int infectionDamage; var int infectionFreq; var name InfectedEvent; var int disinfectioncounter; const InfectedFOVMax = 120; const INFECTIONTIMER = 24; const DISINFECTIONTIMER = 26; const WEAPON_UP_TIMER = 3982834; const WEAPON_DOWN_TIMER = 3849302; const HUD_HACK = 25; const DESTROY_TIMER = 26; var int iMultiPlayerCorpseSecs; var bool bWasInfected; var bool bNoDamageEffects; var int StartTime; /***************************************************************** * PostNetReceive * Here we handle when data from the server comes in. ***************************************************************** */ simulated event PostNetReceive(){ if (Level.NetMode == NM_Client){ if (bWasInfected != bInfected){ bWasInfected = bInfected; DOTZPlayerControllerBase(Controller).InfectionEffect(bInfected); } } super.PostNetReceive(); } function PostBeginPlay(){ super.PostBeginPlay(); } simulated function PostNetBeginPlay(){ super.PostNetBeginPlay(); if (Controller.IsA('XdotzPlayerController') == false){ WEAPON_NAME_CENTRE += 40; WEAPON_NAME_Y += 40; } bNoDamageEffects = false; StartTime = Level.TimeSeconds; DOTZPlayerControllerBase(Controller).InfectionEffect(false); } /***************************************************************** * Begin Play * This will take away the arrow above player, to stop server blog. ***************************************************************** */ simulated function SetTeamAttachments(){ if (PlayerReplicationInfo != none) { return; } } /***************************************************************** * Take Damage ***************************************************************** */ function TakeFallingDamage(){ local float Shake, EffectiveSpeed; if (Velocity.Z < -0.5 * MaxFallSpeed) { if ( Role == ROLE_Authority ) { MakeNoise(1.0); if (Velocity.Z < -1 * MaxFallSpeed) { EffectiveSpeed = Velocity.Z; if ( TouchingWaterVolume() ) EffectiveSpeed = FMin(0, EffectiveSpeed + 100); if ( EffectiveSpeed < -1 * MaxFallSpeed ){ TakeDamage(-160 * (EffectiveSpeed + MaxFallSpeed)/MaxFallSpeed, None, Location, vect(0,0,0), class'Fell'); if (health > 0 ){ FallDown(vect(-1,0,0)); if(PlayerController(Controller) != none){ AdvancedPlayerController(Controller).Fall(); // camera effect } } } } } if ( Controller != None ) { Shake = FMin(1, -1 * Velocity.Z/MaxFallSpeed); Controller.ShakeView(0.175 + 0.1 * Shake, 850 * Shake, Shake * vect(0,0,1.5), 120000, vect(0,0,10), 1); } } else if (Velocity.Z < -1.4 * JumpZ) MakeNoise(0.5); } /***************************************************************** * CheckShadow * overridden to prevent shadowness on the player in a single player * game ***************************************************************** */ simulated function CheckShadow(){ /*if (Level.NetMode != NM_StandAlone){ super.CheckShadow(); } */ } /***************************************************************** * PostLoad * Reset the infection state ***************************************************************** */ function PostLoad() { super.PostLoad(); // log(self $ "postload"); SetInfectionState( bInfected, true ); if (IsInState('Swimming')){ if (Weapon != none){ Weapon.PlayAnim('Deselect'); SetMultiTimer( WEAPON_UP_TIMER, 0, false ); SetMultiTimer( WEAPON_DOWN_TIMER, 0, false ); } } } /***************************************************************** * PhysicsVolumeChange ***************************************************************** */ event PhysicsVolumeChange( PhysicsVolume NewVolume ){ // Log(NewVolume); if ( NewVolume.Isa('WaterVolume') ) { GotoState('Swimming'); } else if ( IsInState('Swimming') && Weapon !=None ) { SetMultiTimer( WEAPON_UP_TIMER, 0.5, false ); SetMultiTimer( WEAPON_DOWN_TIMER, 0, false ); GotoState(''); } super.PhysicsVolumeChange( NewVolume); } /***************************************************************** * SetInfectionState ***************************************************************** */ function SetInfectionState(bool iState, optional bool bForce ){ return; } /***************************************************************** * DoInfectionDamage ***************************************************************** */ function DoInfectionDamage(){ return; } /* function PreSave(){ } function PostSave(){ } */ /***************************************************************** * ***************************************************************** */ function DoDisinfection(){ } /***************************************************************** * TravelPostAccept ***************************************************************** */ function TravelPostAccept(){ } /***************************************************************** * PlayTakeHit * Overridden to prevent the hit noises from infection damage ***************************************************************** */ function PlayTakeHit(vector HitLoc, int Damage, class damageType) { local name HitAnim; local E_HitPart hitPart; //turn off the boredom timer SetBoredomTimer(false); // AnimBlendParams(TAKEHITCHANNEL,1); AnimBlendParams(TAKEHITCHANNEL,1,,,FIRINGBLENDBONE ); hitPart = calcHitPart( hitLoc ); Spawn(BloodEmitter,,,HitLoc); //this is the added line, the reason for overridding if (damageType != class'DOTZInfectionDamage'){ PlayHitSound(); } ViewKick(ViewKickMagnitude); //try and use a weapon specific hit animation first if (AdvancedWeapon(Weapon) != none){ HitAnim = AdvancedWeapon(Weapon).GetHitanim(); //Log("you win: " $ hitanim); } if (HitAnim == ''){ switch ( hitPart ) { case HIT_Back: HitAnim = HitBack[int(Frand()*HitBack.length)]; break; case HIT_Head: HitAnim = HitHead[int(Frand()*HitHead.length)]; break; case HIT_Chest: HitAnim = HitFront[int(Frand()*HitFront.length)]; break; case HIT_Left: HitAnim = HitLeft[int(Frand()*HitLeft.length)]; break; case HIT_Right: default: HitAnim = HitRight[int(Frand()*HitRight.length)]; break; } } // PlayAnim(HitAnim,1,hitblend,TAKEHITCHANNEL); AdvancedPlayAnim(HitAnim,1,hitblend,TAKEHITCHANNEL); } /***************************************************************** * TakeDamage ***************************************************************** */ function TakeDamage(int Damage,Pawn instigatedBy, Vector Hitlocation, vector momentum, class damageType){ // Log( self @ "" $ DamageType ) ; if (bNoDamageEffects == true || Level.TimeSeconds - StartTime < 5){ return; } //already dead igmore further damage so you don't //chunk up and prevent the death animation from playing if (Health < 0){ return; } if (DamageType == class'DOTZInfectionDamage') { return; } //fire should burn the zombies much worse else if (DamageType == class'Burned') { // Log( self @ "Damage was: " $ Damage ) ; Damage = Damage / 4; // Log( self @ "Damage reset to : " $ Damage ) ; } //can't hurt yourself with some weapons if (InstigatedBy == self && AdvancedWeapon(Weapon).bCanDamageSelf == false){ return; } //you are much tougher in kungfu mode if (DOTZPlayerControllerBase(Controller).KungFuMode == true){ return; } Super.TakeDamage(Damage,instigatedBy, Hitlocation, momentum, damageType); } /***************************************************************** * MultiTimer ***************************************************************** */ function MultiTimer(int ID){ switch ( ID ) { case INFECTIONTIMER: DoInfectionDamage(); break; case WEAPON_UP_TIMER: Weapon.BringUp(); AdvancedWeapon(Weapon).AccuracyIndicator = AdvancedWeapon(Weapon).default.AccuracyIndicator; break; case WEAPON_DOWN_TIMER: AdvancedWeapon(Weapon).AccuracyIndicator = none; Weapon.PutDown(); break; case DESTROY_TIMER: Destroy(); break; default: Super.MultiTimer(ID); } } /**************************************************************** * Died **************************************************************** */ function Died( Controller killer, class damageType, vector hitLocation ) { //almost like do disinfection, but we leave the health bar, and //don't do the 'heal' flash bInfected = false; disinfectioncounter=0; SetMultiTimer(INFECTIONTIMER,0,false); //AdvancedPlayerController(Controller).MotionBlurOff(); DOTZPlayerControllerBase(Controller).InfectionEffect(false); PlayerController(Controller).FixFOV(); // Log(self $ " has ded"); if (Level.NetMode != NM_Standalone){ SetMultiTimer(DESTROY_TIMER,iMultiPlayerCorpseSecs,false); // Log(self $ " has ded"); // LifeSpan=5; } super.Died(killer, damageType, hitlocation); } state Swimming{ /***************************************************************** * BeginState ***************************************************************** */ function BeginState(){ SetMultiTimer( WEAPON_DOWN_TIMER, 0.5, false ); SetMultiTimer( WEAPON_UP_TIMER, 0, false ); } /***************************************************************** * ChangedWeapon ***************************************************************** */ function ChangedWeapon(){ } } /***************************************************************** * Destroyed ***************************************************************** */ function Destroyed(){ SetMultiTimer(INFECTIONTIMER,0,false); SetMultiTimer(DISINFECTIONTIMER,0,false); // Log(self $ " has been destroyed"); Super.Destroyed(); } //=========================================================================== // Default Properties //=========================================================================== P// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004YoungAdultMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } x V{B,Y6y  ~q.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AT// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004YoungAdultFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } z o|B,Y6| >6->%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$Aq  100N// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004TeenagerMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } ~ [}B,Y6 *Wh.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AR// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004TeenagerFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } }  10@ uAB,Y6B zdz>%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AA  1.5N// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004SkeletonMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } F G @Z Ass1% iA UD _BB,Y6J i.Y<9 Joe GrantD YO]BD3PZombies.BDZombieStandardS]BD3PZombies.ZombieStandardSEF5G:O4NAM;L>KHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AL// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004SeniorMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } K `DB,Y6L ˻3.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AP// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004SeniorFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } M yGB,Y6N `>%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AP SLC  68694Q 8375093R 8375094S  92843T  150U  29384O// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004NurseFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } V MBT M9:9:$r.M*pMa/!_Ma/!\ M W }HB,Y6Y >%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$Ac R f#n P// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004MiddleAgedMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } [ cIB,Y6\ F.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$A]^ H}; T// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004MiddleAgedFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } "_ KJB,Y6a k3>%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$A^~d~P e HNk 6** b |E{|z g }IE  w6* 6} M kba!@@' Fqb <dpppp9V configured with 9Vq, 9Vn6q\wn*nj\  U// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004HospitalPatientMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } yB^nC.B<Ba/!_Ba/!\ wC* PC LC'q!j  .Bw *x%q!N!O h dKB,Y6k  O.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y 7+$DC$?6"!8$C( 1$AJ// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004HoboMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } j n T' o m s IK'a?_9D?@`_% JDa AA_$aaaCB B q [ r y' q!j p { h8&<q.Ca*q!I ,B# u [G$% v xY&#% ^x z l-*q!V  w wn-% ^w | [)i y vl4y& ^v B"q!c t Mj2'TPKR<K?a6L>(a5K(-C([q   fff?CaP"  9?,-C'za-C([q   L?Ca@% q!N!Na> BO)Bm ~ [V* a(  C^+aV ?( @Vf, AJYl, -C 7Ja/!\Ja/!_ I.JwI*IF-C'(vi( l eLB,Y6C(.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$AP// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004HoboFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } ]BuW|/+)ruaV () Ix c1@^EHP0.,rtT tTM<x#w*a:w*a yGKd]1( LJNRD2`d  L?d dDPMB,Y6OV@>%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y 7+$DC$?6"!8$C( 1$A} UN@p !*\ X:r\* !*q\ @a Q Qa?x%x,zar X*x4w X* C** !*[%[,r *% w *W   W9?,dL-T'a=333?q  Ba? # #L-T(      9?,d.GW   9?, .a(#? a(#   ??9? !*[a> !*a !**)w*q!jE>w*q!hEq!I *NZOHB<V9D Q[_=2*** ]RsW>-+9?V9?, s abB0r*!rX*X aXR ^SfP?^,riT\9?V9?, fi c e YD aYi- r*aa ).~a ).Qrz rJ// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004GoreMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } PmRQN=mL.mar@ @( WfNB,Y6Y* .YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y 7+$DC$?6"!8$C( 1$AXoeYOo -V 8dq!h 56 q5(vi( j rM o N// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004GoreFemale extends StandardFemaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } ZM~S9:9:$gca/!_a/!\ pr* e6 6 9?r a!jbq!jewbb L'q!jar@ @( [SOB,Y6]f'C>%KHJI^Y 3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y 7+$DC$?6"!8$C( 1$A\PHXH~U~L-T'-R'{{& ^PY6 -R {{&-R(L-T' _rlZ;7 r 9?,'9( f d!a R `DU[DQFD@FD;aѨLastSeekInstigator:9VSeekLocation:9X.FQD@F I// Copyright (C) 2005 Brainbox Games. All Rights Reserved. // September 27, 2005 //----------------------------------------------------------- // //----------------------------------------------------------- class BD2004CopMale extends StandardMaleBase placeable; function BeginPlay() { Super.BeginPlay(); SpecialDamage.Remove( 0,5); SpecialDamage.Remove( 1,2); } QdG5w*w*q $ $ dgPB,Y6fx.YKHJI3iv#": S$? S": S$? S": S $> S": S $> S9": S $> S@":@ S $ ?": S $? S=": S $? S&Y $Y  7+$DC$?6"!8$C( 1$Acc\.~@U~ v$?xk"GSsiZ-4,Z $ $B/Z $ $B0Z $ $BBoZ-4,Z $ # $ #</Z $ $0Z $ $nZ-4,Z $ $?/Z $ $0Z $ $IJSKY"$L?S$?"$?S$?yZ-4,Z $pA $pA/Z $A $A0Z $A $ANE$ LFSwux"y"cD$AZ $ @ $ @OZ-4,Z $ $ A/Z $ $ A0Z $ $C|}YF"$t:???"$>t:@@@@?"$L?t:@@@@a : xk"GSmBIJSKY9"$S$?"$L>S$?"$L?S$?"$?S$?yZ-4,Z $A $A/Z $A $A0Z $A $ANE$ LFSwux"y"cD$AZ $? $?OZ-4,Z $ $C/Z $ $C0Z $ $|}Y/"$?t:???"$333?t:?cv$L>xGSsiZ-4,Z $ $B/Z $ $B0Z $ $BBoZ-4,Z $̽ $=/Z $ $0Z $ $nZ-4,Z $ $?/Z $ $0Z $ $IJSKY+"$?S$?"$L?S$?"$?S$?yZ-4,Z $ A $A/Z $A $A0Z $A $ANE$ LFSwucD$AZ $? $@?OZ-4,Z $ $ A/Z $ $ A0Z $ $HCxk"GSmBoZ-4,Z $̽ $=/Z $ $0Z $ $nZ-4,Z $ $?/Z $ $0Z $ $IJSKY"$?S$@"$?S$@yZ-4,Z $@ $HA/Z $A $A0Z $A $ANE$$tIFSuD$AZ $> $?coY!"$m*m"$?m*mv$L>xk"GSsBoZ-4,Z $̽ $=/Z $ $0Z $ $nZ-4,Z $ $?/Z $ $0Z $ $IJSKY"$S$"$?S$@yZ-4,Z $ A $A/Z $A $A0Z $A $ANiYVwoZ $C $CAZ $? $?q"tZ $ A $ ApZ $? $?h^Z $? $?E$ LFSu cD$AZ $? $@?NNNoMN M~MEM[MJNVf~VhNMCfQ MB MfMgM{NH MFNVNRMw Ms NWf@ NMnMVMAfN fr f{MMfN gn MCMBMyfR X3J2MzMAM@M~ Xp NlM|NkMKM_ Mr Mt Mb g0NaJk M~ Vi MQJj MDNcVS Mhnx M M` NJNCN~N[NjN@NsNNpNmN}NinW NnNXNZMEg1fP Mw NGVO M[ M{ gx ROM\ Mt Xq MT NI Ji Mu MdwmwjA6wnwpwiwqwhMO Mz wkNdMWwlwoMg/Vh Va g,M{NHJBNKwbwcfk fR MAEc nSdTwdKwKCMEMxMefe fw X} AUgW NC K~M] Kcnw MDXPXUXFXiXQJzJYg|M} JlJeJaJtM@fz JMVU gy nq Md JggI JAHK_J[Mv KoMP fX KlK[M KAEE J{KqnC gt MFMHMSfPf:j M| |egpu,D AoAZAYel d`J{IX|dejJUNlD |d|p|oM@|nEx |mJP |l|k|j|i|h|cnsh JfA{OA{RMHNCuE |une nFn |vA~ |w|x|y|zJa|{nS nU np g+JeJU||NlT Mg |}|f|ggLX df gs es~XJ|bgY |qJeJW JRJB VkJ@ NbNa|r|sw`|tnp gM J_gMCnm J}JanH JbJMJM Jl JcJBJbJEJWJk g~ fBM^ JeELJssJdJ@KDKyJkKaK`J]JYJPJ2Ji[JilJ{~NlKMMFfw |aKVKYJhJJJN_JNBJqJ)J.KjJkKXJ'JzJTJHBJH[JKJnJK_JKsJDJdJqJpJmJGJG JH JpJkJjJKJhJIJBQJ|JrJRK|JuJQJhJNTJGQJGBJG[J[J}K}JqJLJkQJkBJk[JtJ_ JkeJJ[JQJA JSJR JHJvBJKTJPJX Jv[JiBJbJdJ[ JeJveJS JWJvsJ~J@JyKnJVJxJMJnJlJrJEBJE[JWJAJpJQJBJXJuBJu[JFJQ JQJxJF JsJBKiJ%JIJBJFJTJ_JuTJSJiJbJbJJHgl JvTJ\JVJUJ~JT JeKcJc JVJCJBTJLJsJHTJ]N^Ew6FMBMDJJ gt gu gv S| MzJ-yJfJ_J] JJUJb w]w^w_wXwYwZw[w\Jv|T|Q|P|U|L|MJU |S|O|N|R|J|IJ`JHJwJJJGJZ JQJuEJ@JawewfwgJu[JdJ]nz JSJuJTJTJEJ{JUJpwswtwuwvwwwrCKJW|^|N|O|P|Q|R|S|T|U|V|_|J|K|L|MRA |w|g|h|i|j|k|l|m|n|o|x|c|d|e|fJH|r|||}|~||@|A|B|C|D|q|E|F|G|HJ]|t|c|H|d|e|f|g|h|i|j|o|k|l|m|nJK|E|A|B|C|D|E|F|G|H|I|D|}|~||@JH|e|[|\|]|^|_|`|a|b|c|h|J|K|L|MJX|]|Z|[|\|]|^|_|`|a|b|\|V|W|X|YJW|f|R|S|T|U|V|W|X|Y|Z|g|N|O|P|QJSJkJvJ_V{ JPJZJ]JRJJN en fr M]y u`F Nlu CkNG N@ fWnj AInf gCfQ gr gFZ fKfFgu VL nG nF ClnB |y|Z|[|\|]|^|_|`|a|b|z|V|W|X|YnA|Y|K|L|O|P|Q|R|S|T|U|W|I|J|M|NEK |v|o|p|q|r|s|t|u|v|w|u|x|y|z|{EJ |{|M|N|O|P|Q|R|S|T|U|||I|J|K|LfZ |A|t|u|v|w|x|y|z|{|||B|p|q|r|sID|`|A|B|C|D|E|F|G|H|I|F|~|G||@CHJ`MzM}CBCmMICnClC~C}C[CpCmC@CjCkCiCsCXCCCCZx  F} S~  aw no  |G HUfLUg  {G HOcU_ c@  qD ~yL9a Zf  hm ut CO^Qr _h  mF yR Fj T*a bb  pU  }w I@+Wv  Be  OO_[m jJ  xm  Eb  Rd  _O  kv  x!X EL  SH `!Z  mYO  zD FO] TQ a[  oO  |e  Im Ug cy  q@ ~K  Mx ZN g} sa A  Om  [B he  v$\  BY  N[ [Op i:R  vA C, Qg  ^g ku yG G\  TOI `Xymwu {L  HL U:e  aM  nvm {[ IC  UH  bg om }e  Kc  XP0ee  Ue  be  ne  zm Gq U/ ae  nv  {R  HP  UT aiJpN  ze  GQ  SN `K  mm ze  HH  US bI qNA  MzQZo  khlIwOa @a  My  ZOk fA sA AOm O$v  \%I  i9e  ujxBj}  Pb ]ve  kh  xhY D|  QQ  ]yjOo x:m Ev{ SI  aq  mvN z!A  GOn TyaOi ov|  |Og HOf UGbB  qT}Oe KO`XX gF  uO\ AJ  M]  ZN gO tJ  C z  P{  ]e  j m vyEX  S  _u lGygs  G]  T`wO o\  ~{ K YJ  eW  qU  ~ KVWu eS r@u OV\QjQ x|  FT  RM  _} lm zvHtVN  dm qm [  Nx [Giu@ wT  CuK PL ]~ jz  v n  B} Nu` \S iM  w} DA  RE _wVl zwGx UY  cVoy}y K} WN  dV qE  V KF  YQfg tf Ae Nd [c hb ua B` O_ \^ i g vh  DSxQK  ItVi  d} q  ~m Kq YV  fq  rG m  LG YG  fE  s#G C L(G Zvgu u AT POj \n  h!Y  t![ A!w OK  \h@ ihK uOl Bh` O L \r j*^  x:`  E`|  R9G _OWXkOPhCPK  kk  xhY Di  Qh  ^N jYm vI  DI  PM  \J  im vP  Dq Pb  ]m jz  x^K  DgD QQ `y  nJ  {y  H{y Uyy bwy oOdk|dq  go  tdw AeD Obw  ]Xu jOt vHz COg POe ]2z j/o  w0R  D-o  Q._ ^.[  l*b x+V  F%b S b ab oSx}[ ul  Ck  P M ] @ j  wa Df  RuY _c luJ zV  Gm TK  bO ot~m LsZw hO vm Ft Tq ahZ nhq {XK  HFf  UFhal  o8K  |9m I*]  Wn  do  p*\  },m JhJ X!te%m sW AL OX  \m iO wp  FwtSs  awsnw |O J v Zt iR vq DtQs_O mt|sJ^ Xr fwsO A} Pw^`  lN ya  GPTwbWpG ~yL|  Z| fz  sy  z  Kz XC ew r~ [b LD Zl  g~  tN A  NO Z[ Pgj  wj  CX IOd  Xj  e<2+q N d\[j  @RbLj  npKzB2+Ej  pl }LmJN2+wuMb|JocyY2+\j  GIThsK)RjP{k)Kp#tH ^WG cuj  XG&dj  JeUVa2+k_:Vm7PsBG_FI2+Oq zc  Z2+gD@RHDR92+Vj  AffM\ sj  @?2+MSxxajpj  ZJ2+gL2+R{l}j  i\2+vk2+a2+L2+w2+bTPM] ]a7jj  abAnj  oi |h I_  VP [b Z{}enxwl\fz  Bj OZ ^T9o\hwl\vhM9R{KB:jhf-d[*Q`E{j  @ ]DMi  QU Y^z  wj D ^  Rh{_heTZ M  nT N{j  IJ qVu GhfbTh{8v j n v{|i whQkDj  ohZE{'6@]=vh}{sJinvcWhgrzHbl%j  Nhte[hb@ _N_h]*m!j  WheDdw|hhzddhY#Hha]k,j  H X^Th_ rx9{9j  t*j  Ahs  N.&W0j  })!I2j  jq-v_ic5BKL5_W5T+vG\aap}h^mh]{h\ Ih[ Ru~[VsY@_Lh/jJUYCj  njO{q2JYj  |e  HkdTXTxzLTMklxxMB#pM[)SMT6|hZ rK^|n ZSj  zonFU_9tUsmUT,l K|XpKTq@_[QJ_\j  i[T6udj  kc Nwdm Er]S|MpL&}Q8cej  [hkg[yRhQ8Kij  ChB4Oh[IC[oL}C{}N~~PLpQ8\qj  TpB4`p[QTpe'edaLuM9mz{fzB:Euf-j  lcxMy[}Q8T~j  LS>XwlVB#B[$eT6IM+e/jslY~ Ec ej  sUZd  Y@ZenrV3q mdu{QueTLj  `j  mUdzul}^c  [u e@VrufbHAujBH_[ngW2UAXG`g_Q0Fj  vBpBj  rXX~CoVT+Ej  pE}u{|uQ[uZ yV  Yq  e[`ru}"RugrtutefubKVdjueDNu]*Ruzd|uY#`B4C[=w]vtua]jb  Gu_ Sus  \FbearGQ8yj  qZ n}yku^yu]Gu\ U@ A^u[ _uZ hxmrk'_y.F `tx SB"`i-B%RoDNABSOSDba  fj  r kuj  tuq @U_ MuY luZ y~JFyQPLaW)mB \ V~Er~ wFxCEJ{j Eb S Plap M] C[W <^QbZ` 4^|uOSZ` 4fm R}Sul[P` 4b kQQM` 4^ ^RQ|` 4ZM ` 4fg h\}M ` 4pJ SOz h_}I T|F UXB h` 4fZ WX@ ` 4`X iHx WN@ j  N b3Z FyM S $F j  j b3v j  i j  u j  B HqN e+ ]`j j  J ` 4QV j  g jNs O` 4A 2"@` 4J b` 4z;l6@JfmZ p[6P)J\Zsj  M4@NY$` 4Kg[4P)rtd[ j  $@HKpNS[$P)a_ tJ%j  ~@KJ4` 4OU[P)drZM*j  gsQsp+DsQoQ -@Of mF -zA )gqNP6` 4K^vNiu` 4gw9` 4[^{Y y<` 4_ROb q|i }Od f?` 4Yp}d IB` 4]mOh JAW SOj jJ` 4YtOk MOv ZOu gs-tOn aBz nL` 4WhD@ N` 4[GW ZOmqOq Or LOsXOtfOu tOv @Ow JY` 4ZVOBApHiqR )Z\` 4[CI~ ^OH \]y  |a` 4_Iq hJW tOd+KH'vOE*]I0GM 4wb nkk` 4`Y9j  yO^eEKO j` 4UyOT!Nmj  om |OIeHpBmp[Kpy$jOhUNtBct[(AtY'il3Ptn'C[#jtl'MB)tOjN]}BDk}[.o}C-]}VJ}YViLL ` 4[K]}WAfc.g^}Pb]W?`bK_^P^jYfH` 4UnORMCNK POeh[` 4YCOMv\OY ROPNkOPFyOlJd3Ij  |OUWH ` 4T_ dOs PQ B Oc;S KvN LYD Me] NYB O}[