*EA@G&{JyNANonedebugsnameSLV2 StrangeShellEngineCore SLWeaponSystemBotpackSetPosPostBeginPlayASSTickpix DrawTextAmmoFire PlayerPawn BeginState ammoLeftEjectAltFireUtilgetAmmo hasViewPortSLHUD LifeSpan TakeDamageReplace getPilotBeginMesh Destroyed contrailTimersetDebugLevelBotplayAss drawFrameTexturesvSkyNetSkyNodechopf ejectedbrass RemoteRoleIdle UnrealShare clearRideralarmOnReloadStrangeMutatorerr ClientFireInitPutDown SLBotBrain HitSounds ReplaceWithchunksUseAmmohandStyle DrawType DrawScale FuelcoreUpdatesf getMaxAmmoAnimEndHitWallgetFont DownWeaponExplode deathStrClientAltFireRenderVector PositionfnamesSLAmmoPawnFindInventoryTypekeysCollisionHeight MFVariations MoveRocketsetDamageStringsprites StrangeWavecenterRotComp PlayIdleAnim getClipsbounces getRoundssetTargetNode PostRender closestNodeemptygetNextinrange hitSLDamStrround detonate Konglauncherdistto SLWallhitSidearm goShootMode ItemNameEndshotArmed PlaySelectFinishBringUp AmbientGlowCollisionRadiusSetHand BeginPlaysetRemoteRole needReload ClientActive updateSound hullHitsVismyFlag Register NullWeaponPickupViewMesh teamSLClass stopHover spawnSkyNet LogPickup slaveFireClosemarks TweenToStill updateRealRotator Reloading InventoryPickup Viewport ScaleGlowspawnMuzzleFlashfstylesOpenColor setOffsets PlayFiringparseKillStringClientSetRotationPhysicspossPlayAltFiring autoSkyNodegunnerBehavior GiveAmmoLanded EndStatesrPickupMessageRenderOverlaysSetDisplayProperties PreBeginPlayHandlePickupQuery SetRespawnAddAmmo spawnGlowsturnRam AmmoName NextNodeHitSLsetEndTextureProjectileFire LightRadiusMaxAmmo enforcer setTimesWarheadLauncherSinkout playHitSound RisingRate TraceFire sameTeam adjustRotSelect resetPhysics targetActor updateRiderlos findTrailers SoundRadiusLightBrightness handleEjectsgetGoal AlwaysKeepctick handleInv getNextFont adjustGlow setupRider spawnBlastguided burnFuel gameOvernoMorereflect fromFrontdisarmalarmstartContrailfadegunnerMountStr2gunnerMountStr useAltFire addLaunch expiredBeta getFuncName waitover spawnBrassviewFX setPlayer setCanvasFAFire getMaxRounds GunnerEjectActive setSkelMeshspin SetRadiusFontlib HUDLevel LaunchPuff StrangeExplroofisGoalNavPointaddClip alarmColor addNewClips drawTargetFadeOutPockTexmaxOutpaSlide setRetMsgsmateInNewClip pickedUp playFAFiring startHoverassFire assSelect assAltFire assReload RefireRateIcon StopFiringClientAdjustPositionPlayerViewOffsetSetDefaultDisplayPropertiesIdle2ProcessTraceHitDownSpecialDamageMass ShakeView PlayRecoil DropFromMaxDesireabilitybNetTemporaryGiveTo ServerMoveRise BecomeItemCheckReplacementInventoryGroupSwitchToBestWeapon RateSelfClientUpdatePosition OwnerJumpedClientAdjustGlow WeaponSetChangedWeapon RotationRate DeathMessage GetFreeMove LightTypesetPVO NoUpdate properGame validActor touchCheckshort getViewPortviewAlignChain rotateChain roottick vaporizeboomslife NewOwner sinkfudge landSoundNumbLevelclearJDgiveJDshakeupdateTrailers updateRiders drawKeysclientSendThrot adjustThrot accelerateisRelic getTrailers updateMarchjumped teleportedaddHUDcreateNullWeapon getGunner setPilot setGunnerspawnWreckageadjustGuideRot bOwnerNoSee autoMoveupdateSimProxynoFuel afterburnarccos wasEjected storeEjectspecialHurtRadius setLaunchVelautoArmarmswitchWarheadfindSLspawnDamPuffs spawnPuffs smoketickexhaustPuffClassrefuel insertLink hideBeams showBeamslinkUp idleRandom randomLink launchWeight setWeldLocweld getWeightloadLeftMeshesgetVersionStringnoMaptheTimesetOutputLevellegacyassDown leftWeaponsblendc useRoundfullGunnerBoardingWarninggetGuidedRotationgetFlag avoidNode nodeReachedtargetNodeExpireddeathBysetGoallinedup StatusIconbCoronapilotBehaviorsetBot riseSkyNode linkNodes insertClip goodNodefindEnemyFlagGoalfindLaunchNodeFollow setMapType drawLabels spawnRandSLsetSkew drawTargetsdrawIndicators toHeading drawHRot drawReticule SpentClip drawLogoSLClip fakeAmmo clearWarning spawnPuffCarcass pitchAdjustbrassvrecoil switchModereplicateSound playReload remoteReloadSlaveFireWait drawXhair drawRetMsgsupdateMaxAmmomimicPriorityOfxhairrotate idleLapseassIdle Rotation HUDMutatorhover spinRate leftMesh RecoilInfo ClientSleepPlayerViewMeshbCollideActorsThirdPersonMeshNormWeaponpickupMessages SpawnEffects defaultClipweaponSpawned PickupSound WreckageReceiveLocalizedMessage StrangeAmmoClientAltFiring ClientFiring SidearmAmmo ProjectileSN JumpDetectorIntroWeaponDescriptionSLSmokeFire2 SSpritesAssaultCTFGameSparks FlagBaseDirectionalAttach MyDamageType SetUpSlave FireOffset WeaponName FiringSpeedPickupAmmoCount SoundVolumeProjectileClassAltProjectileClass bSpecialIcon MaxSpeed SelectSoundspeedbDrawMuzzleFlashbSplashDamageFlashYFlashOFlashS MFTexture bWarnTargetDefaultWeapon AmmoAmountbOwnsCrosshairbHidden SpawnSound bInstantHitExplosionDecalPlayerWalkingbAltWarnTarget AdjustAim EffectSound1bReplaceVials RocketCrashSwitchPriority ZonePain StrangeraceSkill StrangewargunnerDisplay bDDShortrotRate sparkClass chipClassAttachToSurface bCanTeleport puffClassClientInstantFlash deathSelfStrmeshr ActorClass MuzzleScale pockClassStillDeathMatchPlusbRotVDefend GiveWeapon FortStandardTournamentPlayer TeamGamePlus shockMult WhatToDoNextdetonateSoundexrate FireEffect ProcessTouchbRandHitSoundnoBounceChanceInstFog smokeClass Sleeping WeaponString InstFlash sinktime AmmoString maxBouncesEndZoom warnBlink teleportDistwarnLvlbUnlit MaxSparks ClientDownsndDropNumSets sndGunnerEjcol4 ChipOddslucenthitWall2DamStrhitWallDamStrGetStaticBigFontGetStaticHugeFontDrop hitSLDamStr2shotSelfDamStrMedBox WarnCannonsshootCR HealthVialUpdateRealWeapon SendAltFire SendFirecol1ramSelfDamStr MiniammoramSelf2DamStrGetStaticMediumFontGetStaticSmallFontGetStaticSmallestFontGetStaticAReallySmallFont#GetStaticACompletelyUnreadableFont BulletBox RiderInfonormSelfDamStr MaxChips normDamStrcol3 touchLapsedtypeArmor ramDamStr bCanOverride WH_Disarmed WH_Armedlvlimit refuelSound BlastMark WarHeadAmmoWH_Auto abKickSound abLoopSound abFadeSound warnSound crashSound crushAngle puffOffset gunnerSound shotDamStrdamagePuffClass HUDClass baseFuelRatethrate accelmax fullspeed decelRatio bNoSmooth SpawnCopy bNeverStaysetConsoleOutputLevel defaultDLconsoleOutputDL outputDL DL_Verbose DL_Normal DL_Error AmbientSound DL_Default expiredMsgexpyexpdexpmwhenstrwlinehversion AltFiring NormalFirebBounceMessage TeleportersuggestedSpeed shotDeter EnemyFlagejectDistFlag ejectDist wallDeter Controller goodAngle linkRadiusassModelvlRate AIRatingtitle9title18techno8 techno16leg8leg16fitFontAutoSwitchPriority BobDamping FS_Title FS_SlickrandSpawnDist yjAmmoStr shaketime weightNodesSetWeaponStay fireLoop slaveAltWait slaveAccFeigningDeath AddVelocity drawVRotbFixedRotationDir bCanThrow slaveWait ModifyPlayerClientPlaySound burstRounds shakeRate AttachDecal sndWahoou2m sndTotalComm burstWait FlashLength sndMarchLoop sndPilotEjcol2rpmcontrailClass detClassafterburnRate minspeed pilotOffset bFirearmhitdam limitMult hoverRateLength hoverTimer hoverRadius LightEffect BuildSkyNet bProjTargetFlyingautotag paFlashYAltRefireRate paFlashOclosingopeningbStatic bubbleRate FS_NormalyjStrClientMessageBuildDeleteInventory RespawnTimerocket2SetSwitchPriority slaveNameBroadcastMessagevstr bShowKeyssetDefaultWarheadconfigMaxAmmofuelbAltSwitchModeFM_Auto BlackCloud explodedSLInfo DamagePuffDScar ExhaustPuffExhaustPuffBlueSuggestAttackStyle FM_Burst FM_Single FuelglowFuelpod brassClassAdjustDesireForTouch wallhitClass shakevert maxClips AddInventory TweenDown riseSound casingSoundMax SkyNodeBeamBotDesireability glowClass SLHitPufftargetExpireTime SLMutatorSLMuzzleFlash SLPockmark SLSparker targetLoc hoverDist randomGoal StrangeArena shakemag weldRadiusSpawnNotificationStylus ThermoWaverandomLinkAdjust assEmpty bClipped clipClass Activated flagAtBase bNoCenterbAkimboendTex recoilData contrailM sarmhandLeftclips RespawnSoundPickupMessageClass bPointingScalechunk5PendingWeaponchunk8StempdesireTime bUseAltModeExp SLV2ModelsrelicsRand ReturnValuebmADynamicLoadObjectCopyWarnGMutatorBobChargeinstdt bTossedOut HandednessRjbYawTS bFakeReload sarmclipclipnumB lastProjspawnProjOffset EFireMode AmmoTypebrass contrailEnd fireModebStored storeMaxbRotateChunk3Chunk2bClear storeAmmo sarmpickuplimit returnRate smoothRate pvoScale accScaleslhandrecoilv smoothrvfcorefpod rsarmviewslview slpickupRocketfreshi LevelInfoVarateMaster FlashCountOldFlashCountchunk6 RealLocationCurrentNetSpeed bGuiding CannonTimer bWeaponUpchunk7bAltSkin PrePivotthermo GameInfowhereGameEndedCommentscc LastFiredretbarretmsgnoretmsg3retFontyy bIsAnArmor PickupTime myMarkerpod Projectile NavPoint markedItemClearnextNavigationPointpickdroundsNextRHUDMutatorNPbClosedpvo ZoneInfobUseAlt hoverLoc bHuntingo OrderObjectbHoverRollForthlvolv1 DrawRect bSkipNext AnimFrameHUD TeamInfobigacchnhitn aimerrorejectv bCanWalk FlagListChipBRIbSetUp Location SavedMovelaunchv launchsn StartLocWeightsl bHasBouncedbForceHeadVictimbAmmo aClassNameXLtHPlayerReplicationInfobSetProjOwnerStatLoghittex bExpiredbNoYJhoYLInfo SLV2TexturesexhaustSN redex_a00 explo1_a00DirCalcDrawOffsetmf0mf1mf2mf3mf4mf5 kongicon levelpixellogo sidearmiconvisorbigscar bigscar2blackcloud_a00LasttResized ScriptTextChunk1Chunk4DirectionalBlast FontInfoBounce bHideWeaponASMDClipSpark AdjustedAimPitchbInAirdamomelapsed screenFade burstCountshakeybInitFAskewxskewybWarned bHeldItemslave keysFade keysSlideGameReplicationInfokeymaxkeysUpthroLvlveloLvlhullLvlfuelLvlcoreLvlretwreth retScaleindwindhlgsm bIsPlayer HurtRadiuspodsakimboSwitchPriorityUTexProjectileSpeedseprstrPuffAltProjectileSpeedcxcy MoveTargetbChangeWeapon bWarpingdppFCValueMoScalebSuperRelevantOldShockDistanceZanameExplo ChallengeHUDMomentumTransferDamdyaw ShockSizemaxdmaxskew SpawnNotifysevGlowtween RealVelocity SmoothRollCurXOldRoll bSkipOwner bDestroyed WorldLogfbpn spawnindexFStyle ServerUpdate ClientBufferReadoutFontLevel currindex currstyleMover SoundPitch bBringUp bBringingUp bForceAltbCanClientFire AffectorbAdvancedTacticsFlagrealLvldispLvl qualifierlFontsFontbubxbuby bCountJumpsretmsg2labely labelFontroyroFontqxqy qualFontbBorder bNoFitLabelsnextlvllvlmxpartial origColorbIsPawnc1newc LocalLogActor buildStampbCTFbAssSizeautonpfirstsnautosngoalnumgoals goalNodes maxGoalDistnodeSumSoundDampeningbUpdate PathNodebBuilt bShowNodes bShowLinksbDebugbNoAutoWeightNodeshaslaps ESoundSlot bBehindViewHealthbExexgoalpnum SkeletalMesh bLightinglngoalcwPlayOwnedSound bEnviroMapdefSpecialDamageStringtonPlayerclosestbWaitCountlastsnMovedistbCoopWeaponModeupdesired bIsPilotbEject targetNode lastNode launchNode bNoExpire bReachEject targetAcq Attacker numFortsAnimSpriteEffect BestFortStr nukeTarget bReplicateNavigationPointListnetNetModeSoundInventorySpotv2 bDropDetailDesiredRotationClipY MillisecondoldgSecondMinuteErrorHourcurrandom bNextNextRLAnncurwDayavoidMonthfowhonewroldrYear TimeSeconds EPhysics PointRegionOwnerConsole RealOrders bPainZonexoffyoffFRoletruexyreTagpoHolderLocc2 bWeaponStayGRI TeamIndexGeneralk bExpiresSkins EDebugLevelVapourSmokeflakLabelYPosoldhandclearanceStampfstampfnamebLeftMeshesLoadedStartTypeppEventcompClassZonePackagemyHUDXPosOutdlostrNavigationPoint Instigatormsmssmefunc DamageType PainTimerbSizekong TextBufferObjectnochopEnumBase FunctionStatebNamesRegion blueex_a00GamebOffensiveNode bGateNode bFinalNodelinksldistsbeams bWeighed weighDepth launches launchSxspasses teamPasses wallDeaths shotDeaths nextSkyNode prevSkyNodeweldLocweldSumNextPRI bWasForward bsmoke_a00 bWasBackWait bIsSpectatorbNoneHasFlaghiloEnemy PlayerName SLV2Soundscasingkonglwahooafterburner_fadeafterburner_kickafterburner_loopbounce2bounce3bounce4bounce5crashStruct dropalarmengines gunnermounthullhit hullhit2 hullhit3 hullhit4march piloteject bInvertMouse totalcomm brasshit1 brasshit2 brasshit3slcdumbExpl04 teamDamage numBouncesGuidedRotationKickTop bSuccess bAfterburn ChunkHitbeambNoFuel UT_ShellCaseArenaItem bRapidFireCTFReplicationInfo bNoContrailsPickupMessagePlusrootct puffRandBotReplicationInfobHome dropSoundGuidedWarshell NuclearMark throtSound ShockWaveScorch UT_Spark UT_WallHitRangeNameUnrealI realAccel EWarStat bAttachedCrosshairColorCTFFlagwarstatSlide bDetonate errorSumbClientNoFuel bBlastedejected ejectTime bMeleeWeapontstamp smokeTimer closeTimerTournamentPickupTournamentAmmoUT_SpriteSmokePuffkl WarShellPock ViewRotationpock2dscar2pock1 StrPropertypock3 bSputter sputtime launchedwhitecloud_a00TurnrammedlastTP lastTPRotpilotgunner bShootMode addedHUD throttlethrprevthlvl bThrotSent throtDirbHighDetailModebOldJumpStatusbDelayedEjectbrain trailerslastLocpInfogInfoHitW bMuzzleFlash fogResetbFognw lastWeaponlgFontbBotsObeyOrders marchWait bMarchUnderbMarchPlaying debugFontbInit savedBob bTakeovertoTime bWahooPlayedshakexbRevYbFlipYbAlarmTeambCollideWorldnowMLbUpdatePositiondamf Velocity AccelerationEffects ERenderStylebRisingfillstatStructPropertylvbMeshEnviroMap startSpeedzerolr bIsFemaleraddscalesds bJumpStatusWeaponPriority AnimSequenceisparksdb DrawColorsoftenthetanvrelvbDamageTriggeredbSlaveDelayResetfrbCanFlyRateClassProperty mindeltaPrevnm HomeBasenlxnlynlznvxnvynvzcurmdlocbNeverSwitchOnPickupgrrrcvdeltavmaxrolloldvTexthavevhitlEventInstigator FreeMovesDamagesret NameProperty HitLocationcolObjectPropertyOrdersLastUpdateTimeRollMagslhretcol edgeFadeIndex MomentumFloatProperty damageScaleshieldsNwhsKilleramaxPMsgthr bNetOwnermyYphysSlotbForced BoolProperty UT_JumpbootsVolumejdaClassOldAmmop2HitLocDeltaTournamentWeapon TimeStamp ProjClass RandSpinWallXrealhn ExploWallOutvish IntPropertysmokinonoffCfadet ClientLoc bShorted ByteProperty bForceFire bNoBlackWidth SkelAnim bWaterZoneNextHUDMutator bSlaveDown SavedMovesDCurY NextMutator DamagePerSecfxbRoot bDDStart bDDRestartbDDEndNewRot bViewAligndpitch bDDShorted bDDEndedbEndbDeadCanvasvpOtherslownernewlocRadiusCurrentTimeStampuupr clientErrbReplacePacksAccelClipXreversebOpenbFiredbForceAltFire bMaxAmmoH NextMovebDuck bAltFirebFirebRandomPlacement explo2_a00muzf0|S*0U"u ԗ ԊԝXtԝXtԝXt{#U㼊ԝX> YR@GI&/> YԝX> Y> Yp ]200K ] RELEASE-2.00m ]Wed Aug 29 01:35:40 2001D "4l "k "j "i ]ljThis Strangelove BETA version has expired. Visit http://www.planetunreal.com/0fus/ for a release version.c b a o] SLV2.SidearmoSLV2.KonglauncherVzZ::$::$-::$ -::$::$-#6k7uÊttt{#Ut{#U㼊tГГГtYPYPt1t㼈t> Y㼑> Y> Yt㼈tnevInevIttt㼊ԝXԝXԝXԝXtԝXtԝXttttt> Y> Y> Ytttttttt㼈t> Y> Y> Y㼈tԝXtГГГГtt> Ytt㼈tt㼑> Y> Y11t> Y> Y㼑> Y> Y㼑> Yr$58㼈t㼈ttX'tttt㼈tr$58tt㼈ttX'ttX'tr$58㼈ttt> Y> Y777777t> YtnevIt㼈t> Y> Yttttw6㼊㼊㼊ֲ㼊㼈ttt㼊㼊㼬R~R~R~R~R~R~R~㼈t> YR@GR~R~R~R~R~R~R~R~R~R~'n 6t㼊nevInevIt *ל *ל'n 6㼃ԝXԝX㼈tyrүԝXo}o}fް<7ԝX㼃ԝX77㼃ԝXԝXԝXԝXԝXԝX㼊ԝXԝXԝXԝXt㼃ԝXԝXԝXԝX7ԝX㼃ԝXԝX㼃ԝXnevIԝXֲtttX't,M[]t,M[]t㼈tt,M[]㼊㼊ԝX7777yէ1@A@At㼊 C $BO $pBm :NpAl $C[ $aDZ $C\ $>k $AX $?Y $?W f\@V yj ii S $BR $333?T Q P  O N M I }}}}^^^^^E A $@]87%os got more holes in %oop than a horse trader's mule.}]54%k shouldn'ta bought %kpa rocket from the Rooskies.D ]10%o lost the nuclear combat, toe to toe with %k.s]! %k should have bought American!u]87That's what %k gets for horsing around on %kpa rocket!U ]0/%k takes out the Strangelove under %os crotch.j]65%k learns that good aim is pretty doggone important.e] %k needs some driving lessons.d] %k needs to lay off the sauce.f]%$%k smacked into an unmanned rocket.f-,%k needs to watch out for unmanned rockets.f43%k knocked %kpa fool self into an unmanned rocket.f! %k smacked into a rogue rocket.f"!%k collided with a rogue rocket.i]43In the game of chicken, %k and %o are BOTH losers.m$AH $Df `^Y$zCe b ` Y]&%%k rides shotgun on %os Strangelove!Y! %k hops aboard %os Strangelove!Y.-%k gets taken for a ride on %os Strangelove!X]$#%k leaps onto a rogue Strangelove!X%$%k commandeers a rogue Strangelove.X"!%k tames a maverick Strangelove.W$CU$DKLebw$g  C Qv$4BU$@As X l+'ֿw4'pv''pvpv'pvpvpvpv''v $ S?(p0Kny LV0U"uxxxxxxxxxxxxx 9-vMB::$--[ ::$-::$3^ Ԝ&̻ԝX> Y> Y> Y> Y    ԑ> Y 㼊㼃ԝXttԝXԝXt Ԋ> YR@G> Y> YXu6 X 㼈tttttԝXԝXzFUֽԝXԝXԝX_ j֚ԝX㼃ԝXԝX 㼈tt㼈ttt㼊ԝX> Y> YR@Gtxxtxxxx㼈t 㼈t㼊 㼈t     X X X  X X X X㼃ԝX> Y           㼔Г ԈtX'X'X' X  X ԨѸBX'tX'Гtt ԔГГ& & ГГ  ԑ> Y> YԝXt㼋xx!> Yxxx> Yxxxxxxxx!x X X XLVg;xxxxxxxxLVxxLVxxxxxxxx> Yx! Ԉtt       ԨѸB  ԈttX' X X XѸBѸB X X XttѸBѸB X X XѸB X X 㼗X' ^ za _ c $L=] "D ZmHCzD@zDC=o:Z $L>S $L>T $@J ??AJ ??@Z }??nZ K?L=?BZ 3?@gJ /??| Z c? Z c?f$Ab$=A VVVVVVw$@P"aZ] ST  0KukC JV0U"u JVLVxxxxxxxxxxxxxxLVxLVLVxxLVLVxLVLVfް< JV JVxxfް<LVxLVLVLVLVLVLVxLVLVxxLVLVxLVxx JV JV JV JV JV JV JV JV JV@ $HB@ $@@fsQ#- ::$7 XL#!& & ГtГtГГГГГ X XГ `"Z\kj"-ֲGɊֲֲֲ7ֲ> YR@G> YR@Gֲ> Yֲֲֲֲֲֲֲ777ֲ7ֲֲֲ7ֲֲֲֲֲ77ֲֲֲֲ7ԝXA$@r $Bp$Ars$@EC K.@? Q$[DrU7 9I QXB6 *ל X}m t77}m m m }m m } s $pBt $HDq $4Cs $?k }g f s"c]You picked up a fuel core.n] FuelcoreBQ$ ף>xZ C@v$AU$@w Dr`:L0^[S  ::$0U"u Xh "VV0mH*v=70U"uԝXԝXԝX'''''ֿw4w4w4'ֿw4w4''''㼊㼊w43e 'ֿw4''֊ ''''㼃ԝX'''''''''''㼊'''֙L]''''''''֊rj\L]{#UL]{#U '֙L]㼙L]''㼊㼊 y ]w $C~ $D} $L>{ $L=qz GRc `LL[ `f  9aX\R G{i::$-6D,M[] ԊԝXԝX> Y777ԝXԝXL]L]tL]L]tL]㼙L]t *ל *ל㼙L]L]L]#fIn L]#fIn#fInL]777ԝX=7=7'փԝX'փԝX'փԝXt㼮{#U{#U _ C]SLV2.StrangeShellC SLV2.Rocket2CSLV2.StrangeShellC SLV2.Rocket2l]SLV2Models.lslviewZ L>=@@AZ @@Z }>?nZ K>L=?BJ 3??gJ /fff??f$?b$ ףb $AA $?C$>{ $>Va]%o was nuked by %k's %w.I  X c]You picked up a Strangelove.n] StrangeloveF $pBG:N oJ $Hz?Bq}Q$?xDh:L U$@M$ BRdVT 0U"uU[L!@DEG@] E  9 vl  7UCI 6| 9ako`s!nQvCwyk EHCMlDmI 0y$_".(} uA`k F  !.deOfnCpCv@N QAWWDEfް Y> Y5I㼡=$:e> Y5I=$:e=$:e㼡=$:e=$:e=$:e=$:e=$:e77LV7 JV JV77 JV777 JV777 JV7777 JV7 *ל7 *ל7 *ל JV JV77LVLVLVLV777LV7777㼋xxxLVLVL]L]L]L]L]ԝX7ԝXLVLVxLVLVLV> Yx77xxxxLVxxLV7LVLVLVLV7xxLVLV7xxLVxLVxx7xLVLVxLVxLVLVxLVxLVxLVxLVLVLVLVLVx7LVxLVLVxxxxxLVxxLVxxxxLVxLVxLVxLVxxLVfް<ő> YLV7x77LVxLVLVLVxxLVxxx㼋x㼋xxLVLVLVLVLVLVLVLVx JV JV JV JV77 *לx JVLVLVLVLV=$:e77 JV JV JV JV JV JV> Y7㼋xxxx JV JV JV JV JV77g;g;g;g; JV JV JV JV JV JVr*UKdg *U2@ *Ua*Uc$L>W$?X$?Z$>a $9<^ $@AL T]SLV2 ControlsTJump/AltFire (Eject)TForward/Back (Throttle)TNext Weapon (Warhead Status)TFire (Afterburner)TDuck (Shoot Mode)SKjg;flUxx PG PF PE PD PC PB SSSS}D @ fDn|ji pvGRS$pA@? ub yn!@ i@tXwM`kU yէMlDm 0y$ A$??A$?[T-; zFUֽ}M11 usT$@r$@V"N$?M:: : t"$@ cZs@|V@[ KoZm|IdJ u}} cd TPEY | iIcz 6Z"IiN  .uA~J^ m@Lk`evF@Q ZR_oC@Lz] m G㼃ԝXttt{#U *ל *ל *ל *ל *ל *ל *ל *ל.@ 0_Z9bnevI0U"u=7'7L]7L]L]L]777=7L]{#U㼙L]L]7L]7L]L]7L]ԝXL]ԝXL]L]X'L]X'ѸB㼙L]7rL]{#Uj\''''''''7=7'''''''֙L]{#U''=7L]㼊'=7''''֙L]{#UL]{#U''=7=7L]ԝXL]7ԝXL]㼃ԝXL]=7ԝXL]=7rL]{#UL]L]L]j\77L]=777L]777777777777=7'777 *ל7L]7m $@@z "y "{ $?w $?o$?`:L''W N_ }JA2r$58 7t㼃ԝX7㼃ԝX7tX',M[],M[],M[] Y Sn]'bpSk@[ }Nrobr@"T LMPq\ }MlDm 0Z $(A$L?ujyYW @AhW0.$L>@?(A$?c~SF `ZQMQ{T}  |\@g@m\ ~eh f e d SVy F Q Y_dqEH@l|C ^Lx krwNFW oC`|i i ex@w |Jyl EUL Y Mg"osq K O lNV GTLcWi*nFrrz mX]OMowzlj_ FNr_ZJ a pOYAmw[hhWyէ0ooi.$L?@?(A$@uu  r ~SqLA L K RmL4tx ha@U _V xY^-_V 9+V #?V \, W s q aj @\cRc Jw IR`d;pppfire( ,w ): bAlt: T-%$_r*_.zfire(): needs reload3-v '-Dq!cDw <fire(): no ammo<::$.::$ &t bw * G-%-v   du fZ+ppaltFire( ,u )E- [ -%'u  I @TrN _+M\ o^w9wok hKe D1 !\tg \us@T Z f fvAd =GTR Lb IhSF { }Ii u QJnBxlAG `lI C| dR`Z6#iN ylT㼃ԝX㼃ԝXԝXԝXt㼃ԝXԝXԝXԝXԝXԝXw[hZ"IZ"IZ"IZ"IZ"I㼊Z"I IG$ A{$HBK]*)%o went out in %ks big nuclear fireball.z] %k blew %kopself to high hell.B L$? uCqfz{kdCwyB C^NhpNDaNPAUB@Ggila @Qb @yy~Z@U}YnjlEVQ@Qpn  W(dm=$:e=$:e=$:e=$:e> Y5I> Y5I> Y5IJ@2 YPXu6ttt7Гt z bJL DE |BHe^M "djC g zy zmzeL6U@pppclientFire( ,L): alt: T-%$-k&t wz-D%clientFire(): going to FAFire}'q!c-%\q!|Wq!}-x'w * G-%-x-x MqW y\o\VB@n [fVVo`@} jABg oY @^tGJbringUp()$Pa/!w>.Wjw * t-o'-(rq!f W@YX@UXws TREHQktOp_ sPsSLQUG~D qz{by;w6zFUֽGɊo}o} <<<<<<<<PyuSr$ B:::::t".$A v$@U$@htyvw|p x}J f { 'fI JHdVVr^hd @l` smwik_ j֚/ԝXԝXԝX ~} xhut{ ~"\"b$?@Yj AZ] Km hK P hp N Q e t w u @d{{A Ay S s f^ @U xkn ^wB u LL Q Q@4K @ B2 w2.@ euy  ql ic{m 0iX| d 0U"u77R.G @ TXgj b eB }s ~ go JL|Vu xr3mԝX77ԝXԝX7ԝXԝX7ԝXԝXԝX7㼃ԝXp $?|u@TuJ2ruaD u @\P\wu wu wu*u+T.@;EJu-ua/!WaD F u @\P\ua  @ B X Dq { W<x (\ ITp } aW-< uuuZxuIL! - (-%'6I G X _ q T] ^sV H o} IIBN K apE NL|{\wX/MMM]x*h F a ]Ur]*]-G rW* ]-Gl?W_?Wdhprnds SW_-@Fpclips S]]l??Ehprnds Se-D -@= 9$asemie N$aburste b$aautoe a!Mhreloadinga  [ ^ zuQ;` o_zFUֽ s yQ` |_dLQ ea $$postBeginPlay()Z !Q pBotpack.WZ  wQ *Q -[ :I I h::$  h i j f [WW@4+m {uW(dmԝXԝXԝXԝXԝXԝXГГҗOГГԝXԝXԝXԝXГԝXГГԝXԝXԝXԝXԝXԝXԝXԝXԝXԝXԝXԝX Ԋ-%n3e 3e 2їjB ] SLV2.SidearmN ]SLV2.SidearmAmmoM "v#h\ tB@aM/i@-('.Yv!F 0v Fn }j \z~Qm c D _j{@"H @"q@" UYwO {IZyW s UZ #b q!w NQ`K&- ,aR6- wc *xo$-_f6f%6f%a+fa aa (q!w XY{^m@*H|G.-aH-b ^-(n6-x[ |||>|[ ?%|??,||k-' `-X+a -]RdgM@ioNor t q wT{|uvv gD| zt@4uFe OM HH e@bJ"nRL@iAuyo@_CD[Boc uPdh i kqSaf_p ymYkV{{ x LFTJ9MxiR   }MlDm 0J$?EE.$@?(A$AUFOB@jrMv J@S~M!S DRAbj^55 -'-(-(^r* a!eq!e(b$ #?h/gunnerBehavior(): over 360 off ground$Z+-^w*ev?H3gunnerBehavior(): eject! close to flag basew* we?G-|<!G ra/!sa/!w*A-$%\pgunnerBehavior(): eject! within range of movetarget:  ]^\ [@Up@Ymhw P^WQ p d @llm@n@op stuh@mTe4FKjFy {EHdJf fv kZj{!MjP @4 @4P@4R@4p @W@IWN [_@pO@a@Q@`@@F b@gX*xk *n"_ @*Mzll]TMlDm 0A$>p$@.$@(Q$n@*o*r*I @"p@"J @" qKl2sGs   K]%o was taken out by %ks %w.K%o got capped by %ks %w.K%o had a big taste of %ks %w.K43%o was reduced to a stain on the ground by %ks %w.K0/%o was splattered against the floor by %ks %w.n o " P h "XB I ]dual Yellow Jacketsl]SLV2Models.sarmviewZ @@=?AZ @=?@Z }??BZ 3??dw$?z $Q8=| $;ctB]Hmm.mRdO$@M:N@@KpC$?a]%o was taken out by %ks %w.X}$?Z$̌>[$ #=d $ #=\"]Xc]%$You picked up an RS1 Yellow Jacket.n]Yellow JacketG:NAoBq}Dh:L v$AU$@M$pAuw@e@hkz]f@"SB MK~o ~_T2MlDm 0YYYYYYK$L=(A$>Q$CDR k \{a {$$k aP O #?,}(~wk *a P a+az nw *Z -oCprenderOverlays(): bringing up slave:    -o( r dz wX*XY X  B@aDG9H9~ 9EK9e9{e@HDIajr8͟`playSelect()-(-(-(^a6JE&E OT D zD[ Z Q WH\|@a[Z ]]g`_^Ugmf\{jD tmQnvq@ @t@@v@zM ` {  t@sDL }wELHd t LKqHL| o`GLT]@P@ n~6RV X } ~ c " Q` s",c]%$You picked up a Yellow Jacket clip.n]Yellow Jacket clipB v$AU$@@F k} v@t xflHa-q!I-'-'/-@::$ w*a/!Ew.*".a/!P:.%.q/:.%.p//-.os 8KWay$w*Q..wQ*W-(`-@`QWxaWb&?,Qw * aW A@mYmBk.Active:beginState()-( ]CO  siyg@eiUbJPjbF@[QwA?=pppsetHand(SDQ): slave:   h-\  Q?%QQ?,-'66=-(w * wQ{w Q?&g.w g.dQ66Q r o h{o~x zV3_S`V^V-GLpppreload(): rounds: SW_ clips: SV]$wy*ya!MV]& reload(): mate reloading^^V]%2w*w. a *_. a ?]q!M^ ^ LmxIZzz|P W@F[ DPV+PDQ R SRKWUI @@ZZ r Ehx@FYlfJ b &\- IIdS~ lRT PJ::$iC<wi*iiC<ii-Q i@? M[|l h ZMlDm 0Ay$A$L?(A$ff>Q$>j@k@[@WX lkf@A0(ZU\@O X\ak2Active:endState()-o( l BN(XIa!ClientActive:beginState()w>-o(_w * q!{ xfs]8*l*Active:putDown() @ MJfa_i URE[X+? E?E?==E?E? HB$n8 XB CDb\wD*Da/!w.D*w..D* @}6G|rY-pFAFire:clientFire(): clear: T-wU-wq!86}W( EKmi>a/!pa/!n VDolsz No q k74#|] kllVmQnnog ?A&h?6q p \rp VQp"( ?Q?R P_ wR'-V(?_?w P( ?_a?w P TIaJ92/a0 V-,V\V0'10( ~B5B$ERROR! U A::$a iEV/aEPF P^r  ]gS^, yK%zK,pzKXSnyK[Y K[y'p(KK%K,zKXKXSK[y(K5ppppwait(S, DY ): no tags slots( FTes5FAFire:beginState()-w(g% @\q,h+'M%MO')( gX8 ;w]*]W-(.*' u_ Rzw*M@ _ ^ [ a ~  6M_ 6M^ >6M[ w * R _UZ|U6U6eX.U6gX6.6.6{X6{X6.6.6{X6{X6.6.6{X6{X&precoil:  ). `q o.::$::RO .wO *.?...6fXq LU` x ZU k8~R 88 i@xV8c 88 ouMw caeh<4a?"w* $qu-35 \zzcp$ LS?S?\b'b(c?Sb(dS\ DrV l/B@w*a+a<kDVe?6 mIv-J:. n 5i-Idle.beginState()aH' NkdIp\h#!a K&G @n [|^n #(a^n an  sOat }yo  $(aTEST[r r  QX42(((aX bC 7:B:$BXWA :N:$^]CA 5:B:NzPPDEBUG::$PPkPPUNPin :w*NpppppN().CZNppNunknown().CN5:B:Z::$5w\*r\*aA:N:$ :Z:$^]CA \t \N! ]UZ|<qM'pFAFire:fire(): clear: T-wK-wq!8| Mrr 4$|Pr  uU J@cY[lzY&s''s uS {9r_rV'=__*w_*_/r*'pilotBehavior(): no target nodeSb+X'wSV`S/-^'-O w*e?3pilotBehavior(): within range of eject nodeT-^'w*e@| #<R?'??ew*-7,pilotBehavior(): going to shoot modemR?'=-72pilotBehavior(): returning from shoot modemR?%8R?'R^ RHB R'='=''?w* -OS:m+@ZcwS* AS<ppilotBehavior(): new beeline:  S`S-O'-B'-By7ptimer(): target expired:  `n'w* Z{}tQc@H u \Jt w\*@\@aUv u v p  u r p v r u \z \\Jt z  ~EE0/a0 +10.r+*+a  LI@PHIVPHHeI@@L>L?-UrI-U-V'+WEq!sa??e #<'pppppsetBot(): brain set for:   skill:  ,e launch node:  H }Oi*ppppi.Sk built on j  @UKOh!-WO%O,{O\ a O\ wa*ploadLeftMeshes(): loading: awI.aw O-W' aI z!/"w *-@I vI  UB'@3F&Zq,+Zq& B&`-DF,&& Bq&`-D&& B&`o&-D&& Bq&`o&-D&&TF,ZZ&F,Z B&`o&-DHF,ZZ& B`&-DF&ZqZq&-MZo,&o,F, BZ&`&-D&o, d K+W[9mq YIL9|q @^$&#B]NpppplayAss(): aname: W6J$ arate: D6} $w6M$* w*d6{$.-L\6M$6{ $d-P -6A$6M$6{ $da6M$6{ $d6J$!6} $?%xa6J$a6J$6} $6y$')pplayAss(): no anim: W6J$( q][$-F &R "&N NrOBMAt6uQrRsY bz l] J\.-Y:] :z Z%z ] Z,|:d M:z .pSLV2Fonts.Wd O Z.Z%* ch@LX!w * L@ Stk}4},dDp0S}@DS}ppppppS:S:S.D j~6%X!w * 6~ (Ab#=(AD(?,U(?,-]T(T(`(-] kCW!w * C qyYy[կ>>yծL>?m>ծ>>b?,x EDH>D :$(-y ctechno167ctechno8 k$Z-y ctitle18hctitle9 -y cleg16cleg8.pSLV2Fonts.c   `;MVLql;(fdT.feQ`R6&} lNl2NqN.f-}f?N;#pNqN'f?f?f[6Df?6lb(NT[&; e&`; A&[6; e&`6&; A&1[&;$ e`6&[&;A6&[&-Of?6?Olb(b(NOT^ e[%[6&^ ; ^ `[; A&-h?[Qh?[lb(b(NOTmlb(NT;" Dg ?eD?`h; &,*-h-lb(b(b(N>?fT;''e`6&A&6lb(NTI;e`l}  w3PW b ;Sa  ozg@h:zC y }_ԅz?%z?::$ yW wqa}  ?%y?wwq*q"YyW wz sik 8i fxgS 8x }@vZcat<FAFire.begin:.%:.% :=:$:=:$ g` g%ypppppFAFire:fireLoop: fire loop: burstCount: Sg bFire/Alt: R./R.,z -:=:$. O}::$::$R::$z.6jX=&Z_TapB?Zgaa-w'a::$s"q!/ R $U~}&!pD-i -i (1+:=:$W1\B-~-i ' _lyd`?x &?z & x{cg!}@,2r@&* @%@ S4prandomLink(): S@&$@& @cjO407C?sQN6P  mQ?_?C-b(b(d''jq_v&,'wDm?,,?wQmQ?wmXQ?wCZX?,X?X?, AeX?+ D?j?wmCq&`?w?,-?%b(b(b(>X &,aSwtB : D?j?wmCt?q, Swb(b(b(dX &,w,aSDQtB 3 D?j?_@t?q,b( SDQ mZO}d'7N KVJ?  YW ]O ~W (W O  g]#!w * q!I e~z6A#.SLV2Fonts.leg8   TzG~O/ppppslaveFire(T-%, T-f )-%-zX-f q!_::$}-%L6-% bpadnF*v]d #?tUd!as y]d (nwU*s t] TBtj  L[_B[%@[,6w[]*[]a[ O @Ks^`T,,jTs`wT&*T&&T&T&`T`T s&ms`k FGClientActive:animEnd()-o(-@w * a!{a! a!l q! qa Rc_"M TPrMN NScn M% gGB(3/a0 j0jGGGj10 o~gEM!ppsetSkelMesh(V~)Kw~*~ U!' $kdestroyed() Cx)'-hM p vsize=,xppppppp(x=,6x y=,6x z=,6xM ) `!wi=destroyed()Ww+*T+-_ Tw*~(#destroyed(): skynet == none YBLw@VT B-@'6-D=B=eBBB-B-_.-ltq!I ]Lhp</ H!! T^f< M @Tfs];$a@@q! o g^y2#.SLV2Fonts.leg8   T^[; p~ o (@-_~ a/!D-^~ a/!I kjJ)a&!>'w * J Y\bBpppppp(p=S6\ y=S6\ r=S6\) UW{fv mHis8=5-I-I'-w * H-I( nP e^k7iK-I-I'Cw * eP E -q -~ -I(P E -q -~  ] oxS ] G-k f?hno clearance!vrY*Y.. S wY*Y%Fpcores SY iE~x 0/a0 _10.r_*_a  kW]N%oW/Wa6 kgRpppautoSkyNode(): welded  W to  kkfW 0E10ka  !]W )rk*>5pautoSkyNode(): spawn failed for  WEC-kk-(kUWkGEwE*Eok=pautoSkyNode(): new skynode for  Wk kgs ZWfinish()-^--]--(-(-%(]-q!I g.zrg* Hr.*gEq!/gY-q!IE:g% 8E/:g% 8EgEq!/UJq!/ C tv@XYDe40wY*HppppgoodNode( Y,  | )A Y |  %0,&wY&*/b$Y& Y >A Y Y& i'( jXDb^@UX6z@&0@@}@& Vz@&.@p@0@ [~PE:@%8@:A.@:P=@@ @^x VL--::$ x FEa l  x ?,P @{ F7x{ e-^Qw{ *gxbgEx33-_h. wh*w.:g|h%:Ar|hg=3*5setGoal(): No best fort. Ejecting.(' BIk 2nv6I6Mt6I6M|%y?v?|v%?t?|t%vvvvtttt:~%vD?v?~tD?t?~6iv6it6i%i @@qF|Q0+a=FkP F%@ DVU}C2c UC0we*V WE}Q-B @-B A F-X 'e*W}-B we*.eG._JUf-k we*A#-X ( ye,ReOUeXUeI ~X.XXI ,X bXXj Eb. X SNX_ iwb*n---iwb *?, B-rb*!bbErb*>Nib%>Nb?>?bE?brX*X>@NN rv P_"rdropFrom()\r *G-G*%T *v p Pv  sai+Idle.endState()a( vRyt T%iT:A_wT|*-yT:zc| TcTc%=c| (5randomGoal(): no possible goals!$ FU:8a/!.\  C (' LWvՎ0playFiring()`&@ gefqu^%a/!G-^'Ja/!F-_'-^-_ uPh6!T#uh X@Az Rvm.`rm*m-k"tick(): No SL. Destroying.atm Hz A@ T,E@ .. S ,Cw@ *@  OSz;2-GWc Gr-WNwr*?ra/!R.rKjrUnone Zpj,Ga~Vp.*aVpEVp}Vpa& `\E-E Gg^(_ W[Rgg&J'= #$=$F 3$=$F C$=$F ew * == UEf8Mfire()$Ka/!% w]*]O gq _u r*  H-p r* %.q Sr*aq d Vq!Iw-GWv i-p b a@k`w.*9::$ .a/!Pz.y.Nw*K- i]=CUG >G -GwW*WqSG =]  jh(r*_..  e !w * a Q\~?3playAltFiring()`&D xxw/,3-v?xt?x{J6?:wxq?:wxyK*-vpppppavoidNode( x) death ratios: wall: U?xt?x{ shot: U?:wxq?:wxyT-v vxo8\ 88 {zax>$xvra-pnodeReached():  adwaPP['`na' lCuf"$.SLV2Fonts.leg16   A J q$ppprojectileFire(VJ )e*a.baa L K N A#] A] 6L 6K 6N a hB]D '-}E-X a*eaJ a ]re*'5projectileFire(): Spawn failed!e tan&*~ ?%{~ .{+o\%\,n-HA\a&r\a&VM:m\a&@C-EM{Mv rY*Y\a&vM\5"rY*v:ma@C#wYpppppppnextNode( a): ejecting: next:  Y current/next weight: U:ma@C/UM-H-^'"Y ~`m!V1ppsetTargetNode( )w*ARK+-_ wV*V~( ~':m{:y yb#%+b  HG|yht!AF {[KX8 yppppppsetDamageString(K, J, v )XKJv  k "ka k$$ ^Og/&.SLV2Fonts.title18   E^ c "Wa W$$ JBzU6 6wV*jVj^A^VD !p:D^q !G^t  NCAI{@gCm+Fm:m:CFpsetGoal(): new goal is: :m+@|'-^'( y iS JPX,jS (y S |TFE Xl|&C? l  m Lp-t-|Pp   o /a $$6ppostBeginPlay(): owner:  }/a0 `10Va_p0'j DR-7(P'-u(-E (-r(6o6oB6op% NVE|z::$- -e*postBeginPlay(): spawning rootct$Uag Ox#"rxxkR?%T xRRx`r@* -AHT [T Rx %Pk[9 =-t #<:-t=Ht>ckrctctt -A w* Pa/!% ::$@I!eJ#? t:?l?@ ::$Zt -Pkt k?-A[ onb$[ @-P'r*-P'k? -Pa!H>L> \f_x w_ FYx mYwm*/a0 Nwm*1rNmrNmlY cC Y N 9C ?%Y Y N C 9Y lY Y lWpppppppspawnNotification(): proj: VY shellv: WDN  newv: WDY  limit: W Dl10Y t ap/^^EAT)pplos( E)$b$E   SiZ~&P6--P:-ONiM-O'NX-O(^ l b ~-O--O N??N{^ VW{l W@W{b W@WD ac {~-OD D ?KKFF333?Y^ ?,l b ?FF wF;sk::$-o wanimEnd(): bringing up-o(rq ha| eLJ5F Aj| F+XHj| F Ya Y)*$WuW@/u@a cu@ua ^u@uZ ae a Z ]@ Ubj c#b   @5XF iwG*Ga/!R.G-5%kpaher5%kopher5%kpahis5%kophimwQ*Qa/!R.Q-5%opaher5%oopher*5%opahis5%oophim~-\5%kG5%kspG[G5%ksp%k[G5%ospQ[Qpu.parseKillString(): returning: 5$5 U~DoWc+)ppfire2( ,~))N-%q!q Uq!r -'-'- ?%. O-z=&-%q-q-#- we*-%\W=&)s mGbUQ+-^:.* `X X+findSL()$/a0 twt*rtX ,wt*rtX 0t10* 8tHF|4gW,EC-L1 $1$C 0$1$C @$1$C  kT--L1$ lV*.-L1$ moq.:1:$ nqU/:1:$1$'( pV M /7V &1$5-V ,1$51$  q ,q r~y~0::$::$ -A w~*~a/!%0::$ ~ -P$$KpppsetRemoteRole( ~): remoteRole set: R M#l s?,4rB*B%#timer(): No fuel! Ejecting!-U'?-^'-U^-^ tLx K0xS2[pppFlying.beginState(): owner:   inst:  a!T?w*::$.::$ @a d NN::$-A'1$իp L>9K9y TDy<pFlying:beginState(): set v:  )$ ywST@7;::$-w*I G/a6 KF@wK*Ka/!i$.,K TCICI.,K TGK ICpppppppsetLaunchVel(): found p:  K lv:  )I lr:  bG delta loc: )K $.,K TC10{IlU 6IIIl6IU l?>psetLaunchVel(): lv scaled to:  )I$G"9G?SI F Q-r.* \q!OB-q!O\q! @FPx\e~Pj ZePppPeMP}Pe}j   ;a m= DM !@HM . S @rM *.-( W:; .8:a/!N: #( v w *8a :: : :a/!Nw.:*.:.8kw.8*.8.:8@a 8 կ8:#?.:*8a/!N.8-'.8*w8*8:8:8 : '( zl` p= Uppptouch(): actor:  l rotation:  b$la/!+w6\D*6\Dx.l la/!u  rt*t.lg wU*Uo l AL9A::$a/!B r ra/!W'a/!RQ..r}@ a/!R.-rX.*-A{. r*FpprocessTouch(): sticking new gunner on:  H. q S-processTouch(): detonating from fronthII a/!rrL.}@}@ s. ST RRT rXb/XwCD >I?a,l $@DL=I?!l EB 9(.-' M\(Ze ownerJumped()>wr *r &Xw*\ F"@Q-r.* \q!OB-q!O\q! Pe DptweenDown()ca !a%6JEa&>n&R  CQh|H-M'JQO ]X\@_aX\[ NS\2 -YSZdSZS[0S[Smp'(  I~ J#I@pppppppexplode( )~ ,  )S ): detonate: T-M instigator:  ::$ -Maf ~ S ?,";-::$a w a  SCI?~ a U (.-' GvPhL QOOSYM%pspecialHurtRadius(): Wn/a8 "R Q w""a/!R`Tn!e r"`vn!l r"`en!G r"?`Q`Pn!ohrs*rs*er"`,]r"s`_r"` n!pr"`ar" r"``X`"s" Q ] ?sss] c ?] "R "lDc O" ի?""sիc Ns10 dC v*/C _ B#?-wC * r (e a e $$ h+apWm -e'\-P \pa/!tZp #;p (pa/!v;pSLV2.Konglauncher(-Rpa/!n-Spa/!k;pSLV2.Fuelcore(' Z!gt95pppdestroyed(): state: Wa phys: RUwM*MalwH*Ha a p` ^ Vfa ` /` a3 j ]\rj ` j B.a 10a  [RP R1StJXr&d*J&^_&dP &^Jr%dP %^J \]N Q@Us9r%dN %^_'oor&dN &^_'( dH G]L -e(>-QH a/!. H vH  `L SNbVZVwL *J L  PH HDJ H 333?X( vy[ VSQa/!@ -P .Cy  J w 1.,w@@@ cf-c  \d-c!v!K BotPack.WarHeadLauncher {a -H m -P -H sa u  i+&+cGH_WIG::$[ ,G,G[ %a { [ * n{rW!w * { _j}q+ZppppppppptakeDamage(): dam: S} inst:  R hl:  )\  armor: UF dtype: WKwJ *J xD?}>F?%,takeDamage(): armor depleted already H}::$ JwR{R}nF?}vF?%%q SR >wR* wRR q+takeDamage(): detonating from fronth\ #?sLK!G K!o K!lI!paa$AJ\ #?a,K$@ ln5 ~oJ7t9Explode:begin-'a w %CzC!S HaL=h dK}%|  mAG_JhitWall()qM'A::$w.L*.L-L*D?!kb?hAAo$HF RALH::$Oa H HwO*OF <OJA tyG JAW/a0 M L%/a6 L ?G M  CL10K LL%/a6 L ?G M  K La , L  L1010 usE R>dCKK?<e a/!I!oGI!GRE R e  V@RWR zD@@BK~pppppppppreflect(): actor:  e  theta: UR relv: UR  soften: UK dam: UVi a,E ?>RK??Ki Ki  ::$DV?, ,?I w* YPP'-Pha,l $@V E#7-PJ} wzR{!e BrQ*4aaa>q!O $@AR CD2 N~ xD P Aj ?D?D ,D  V I *$-ea u a=(V  DOjU -P *-L-L('f H-L'f ''?| GC q'k?,ppnewOwner( C )aC  Sf$;#-e  \ HKP/ql"w`*{` J@O=m' P ::$ sP ja-LP kssP @s] ::$-P'%-P%-i #burnFuel(): Uh oh, no fuel.|yo$Nh-i ' s|q9pickedUp()2wM*MaowH*a H oH-vHH LNzGs%'%' Ohs?'' PR]tjfw*fwB*B%J::$B=&aR$?ss'( QN Wv~ -P !-AON  ]w`*{`o$# ::$-a/!% M ::$ ::$ -[KN7w6\D*6\D-V6\D uK g::$LN |WN  \R a=( SM{3KAa #rKa+P#Kqqq# UB K~1B #B | ghG3>-clha/-Hha/!ljha/!. hv'ha/!{'-Hha/!~.h.h'ha/'w* Gh( pCfEmu C UeZs VBL2?%BWB jBB?.r*b44w4*444b4444B4 4a+WBT64 64  q# -q!Oa=( [_NY.w* -7% ^zFCgzxL{IED3pclientAdjustPosition(): new v: U$ Bl[C{VmGqnGF-H6n=,6n=,6n=,6n=,6n=,:6n6n=,6n=,n nXeK+)m%a u ?m? WYcZ| `a["C-(awa*aaaaaWaa aa7pclientUpdatePosition(): updated v: U$ ] h]T=] /?%\ ]]6@6B{\ ?%W\ >GpserverMove(): sec since last update: U$a@FGG)\=` Aa` ` } aa?,@pserverMove(): sending updated pos: clientErr: Ua$F]666666 BifZ[U-Zx z  zL az -B'b ?%L b /a6 ZL wZb$Z CZ -B(r%r,Cr`r`?%^rZCr10-B kop jg Jj{'Na/!%g E ?6 ?6@ U ?6g  [E  [U Bg 6B6@ DE @6BDU @B VTnm}@4E vwrWJ wt*g CpmoveRocket(): teleported! new rot:  b$v9vNt*VA6y v} 9JNPv?vv} v| D?>Nvx uyD?, x y y%6N| y6N| y6NAAAAZ ?@6ND?6NZ ?A?&Z a+N::$0::$ a/!% w*o::$q C^R3 7|yo9QpppPickup:takeDamage(): dam: Sy instigator: 9 @ -[-r@ q!J ~D9)pPickup:stopHover(): v: U-[(#v! a!h-'o$$wM*MW ~G9Pickup:startHover()-['a!Uaw$?#?r o$-\'u! $j? AF!Ua!j zO@ -\z6l 6z@9!Pickup:tick(): v set to 0#o$av$Y?-\(zZ?#?r zZ?::$-v! |q!n-\v!  W?WRy-c?a/!sX?a/!A-H?a/!l'-H?a/!t;?SLV2.Sidearm({?a/-(;?(y?a/!Q-H(?a/!t?a/!{;?SLV2.SidearmAmmo(yy?a/;?(C$' F_QD-%aq$asG-%( 4P=w*a/![.H[j-\  [?%[-@ [?%[[[ RN`ws >9Pickup:landed()o:a H  S@g[MD?@ GA Iwq ,, A %uA q s a w Ps &??A  [ }r*9Pickup:beginState()I-ra!jj}[-[Dn`#?Hh T~xS)M/- -7V?DV?~,Z iBV<K/VVVBg[ -L V?,V@@V?%k D?DVV@j D?DVV@k %j %K%Vk Op j s  QDH#DDa$@DLDkaw*x ,uqx ,tJ Xx D' W{O 9YjRq (wM*MaMau UgG5,ppsetPilot( g)gLgDvxga/!` .g.gga/!%'=B.g S -7'm Hb 6-9a 9$$a+"%r*m 6m Zb a|f m (-rrb *%-rl |#?ZHa m |f ao?-r'.]Lo fm::$ goShootMode()-7-7ka/!K Y-7afaw*I( Dgo I<rhppppphandleInv(T-o ): weapon: last: FY-7-o Vw*a/!A* E ZF_F*VF5handleInv(): Switching weapon back, but current is not a null.pFrZ*ZEZ SZ_rppppphandleInv(): new weapon:V state:Wa pending:Vpa/!A.rT h> D Q @-]  Q -LiC>?100i.C> PSD P?,dxDC>   q SDG   i9C> F FSD FD9C> s sSD sw B*[XC>? B? BES BD2i2q!t -7w 2?OTODT?OT@@> O%OD?OT@>"-w }%}%D?}>C""u?%uu>?^O% ??>?q>e    K@q?qV> hkEG'U a rU ]U  U rMj}D wmw}*ApppaddHUD( }): hud:V}waf-E ' TYit.w.*i.>pbringUp(): setting proj class: team: Ri:ip T t n_/Y.mbH *Ww*Wr@*::$ @a d 2w* -w -v.-.--v'-^ K?, aC$@@-^ '2-E  D.::$EDEkm6-@ b@ ?6#!m@ ?::$ w*a/!:.:c .$c $%tick(): calling goShootMode()mw*}b|b-L w*w.*.NbC@::$~bObz QpxEPwx*7x%1xb!pXCb1xbt-6ubx::$Bxbx yRtzHw*FRTw*FRTrR*TC R!OT%zR%**R criCe}6oi?%)6ni 6oib $6ni b <pteleported(): delta: U6ni '( Mr~V!-r_`h uhBqGwh*ha/!".h--6e s Bo{_caocMcdc_cOc xY|J*rd $@=,$@ :AY C<$?,(-P}Y?%am $@*-LY?%aQ$@S$=,Y?%am $@$ =,D?,?,7 uwq6H( |jA= r*(t%zqwz*Zz-tzzz(, t, FF-a -['tt@az$?ttjx=,$@'9-a 't-[(( v!?Sdestroyed()$=wo*oGG tWrdrFcr?%6J6Ga?6JL66J6)\?6J66J6r6J6J?,d>pppsetPVO(SDr): new pvo:  )JJ Gir=;o q qbp{io  ~@DIG::$::$BDBk CEB72ppfindTrailers( E)$j%j6fn *wE*/E a1 N:N:$ j6fn Njj,10 PKpCS\gq,Bgr g&LgLgr Lpq WDJ~4-Lp"pq 7w*p?,dLw*p?,dl`'?Kp_p `l_-L_?l`?#9l`HB?_ pMM pK b^qbM cEM rWqLW%CM W'( Tcg? -Grcqbtc.cacr. cn. cch'r*(c Im }BLJa/!w..* -L.- p& P?PPm o ?-u(#.- p PPPm o -u(##-u|P-u'JP P?p% V j@j2*rj wj*wdj*]djprdj jdj522 Ol |]G%ppclientSendThrot(Ul )'l ? DQvzlv^TELw*a/!.a!U .q!f.ZEv6FE^6FET6FET?, 7a/!% -7 ::$ Y@DEw*:w*E |oz?6"F|zo`|z`oz`?&Ev`EEv6FE^6FET6F@kE UNv {n Ww % ZAi 73A%'-GrnA3A( l3g|;:handlePickupQuery()r3P.-[  r * aPW> Lr..3-0.3-.3-'U-Gw. 3W*p. 3Ww*V i.3 V %(wP3P-^PPz %**3a3sw*F3.w*F3.3h'r*( g3 ]r= -Gr( pSk@d"::$J$*J$6ok k ka ik ykk koJ c[3tdQM-GGq%pq&y pLpL*qy * PA>Aa/!i n #?q'n %a/!%Z.4pIdle:timer(): skynet:  _w_*_-k J_F.wJ* b$J  J["a+PJ  %E \bySiV%gV,wV6fK*]wV6fK*V6fKoV Kf`]jq aS+ppeject( S)$wS*A%$eject(): delaying one tick$rS-6uD'-6uk'::$~.SS S gh dS h ?,drSr-i d S$a_$@.SU1D-i S$aF$@.S1k odey {rlKAuFCDT?,F?6P  ?6  l?6  Y l?6P  - [F- [Y - [lFF?lu?p DFu?C?CCs DY u?C?CC-IpppppsetSkew(): skewx: Sp  dyaw: UF pitch: Ul oOZc `pppppuseAltFire(): ammo: T enemy: VO orders: WO{.Oa/!GVa/!F7:{.( wO*(T{!B{!G(/O)a6 p F@rpOrpFO0(10' ~v 3:2BuildSkyNet:begin: Building sky network.$^ C\& \,1BuildSkyNet:begin: Auto-spawning nodes.$]ow]*-^]a/!I:.]|]P\&$\,!+]]f]]a~:pBuildSkyNet:begin: firstsn:  ~.BuildSkyNet:begin: Rising sky nodes.$~Cw*eAGa-_%:A|.@S3BuildSkyNet:linkNodes: Linking sky nodes.$~Tw*5 aX)r~~GaGaW-n7BuildSkyNet:weightNodes: Weighting sky nodes.$%w:Aw|*Zb| XrpppBuildSkyNet:weightNodes: new goal node: Z for: |~w*%:AwZ*@CZ  xpppppBuildSkyNet:weightNodes:  : distance to goal node S: U@C@CF F @CaGTpBuildSkyNet:weightNodes: dividing distances: firstsn:  ~~Ww*%8:A.wZ*@C?@CF opppppBuildSkyNet:weightNodes:  : goal: S weight: U@CXaGFappBuildSkyNet:begin: Built network in D^  seconds.$-k ' P TBed1 - rd* .ppclearRider( d)%rdI'a/!.`  -v.-.--v(-7 Y -rKq*$*z>rd*z15clearRider(): Tried to clear a non-rider! da/!%w6\Q*g !6\Q zg 6\Qad-(d-'d-(~dvdRd snE`< ^A dC<+A a/!i.A :A  mf ~L F+6f  -R$3R$f oR MX qo?X X J X AU?,(pppppppppbot gunner: health: Sh orders: Wh order object: Vh state: Wha move target: Vh Gf-,>U a U $$<H%H qPw] /Pa/!G::$.::$ P*ppgiveJD( P)Ya YY SPYr  U ute vl +ppclearJD( e )::$]e w]*]a/!@]a ]]_ vws{| ws* wD *a/!Fs-D -3:s:D  e // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Wreckage.uc,v 1.3 2001/05/31 15:57:37 yrns Exp $ /** Bits 'o rocket. */ class Wreckage extends EjectedBrass; var Mesh chunks[8]; var bool smokin; var() Class smokeClass; simulated function postBeginPlay() { // No detail drop. //super.postBeginPlay(); mesh = chunks[rand(8)]; drawScale = 1.0 + (frand() * 2.0); smokin = true; setTimer(0.01, false); } simulated function hitWall(vector hn, Actor wall) { local DirectionalBlast db; super.hitWall(hn, wall); // Spawn some marks of our passage. if (!level.bDropDetail && level.netmode != NM_DedicatedServer) { db = spawn(Class'SLV2.DScar', self); if (db != none) { db.drawScale = drawScale * 0.25 + 0.25 * frand(); db.directionalAttach(velocity, hn); } } } simulated function landed(vector hn) { // Clear smoke timer. smokin = false; setTimer(0.0, false); super.landed(hn); } simulated function timer() { local Effects puff; if (smokin) { if (!level.bDropDetail) { puff = spawn(smokeClass,,, location + vrand() * 15); puff.drawScale += (lifespan / default.lifespan) - 1.2 * frand(); } if (vsize(velocity) != 0) // The faster we're going, the faster we emit smoke. setTimer(fmax(0.9 - vsize(velocity) / 600.0, 0.01), false); } else { super.timer(); } } // end l // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Util.uc,v 1.24 2001/08/24 08:18:14 yrns Exp $ /** Utility object. Handles debugging, release info, and beta expiration functions. */ class Util extends SLInfo config (SLV2); // DATA // var() string version; var() string vstr; var() string when; var() int build; // Expiration date. var() int expm; var() int expd; var() int expy; var() config bool bExpires; var() string expiredMsg; // The player who's the viewport. var PlayerPawn player; /** This is how the debugging system works. Each object spawns its own util. The default debugging level is default.defaultDL. Each util can have its own defaultDL as well (set via setDebugLevel()). The outputDL is the level of verbosity at which we actually output. Again, there is a default and each util can a separate output level. Console logging has a separate outputDL that works similarly, consoleOutputDL. */ enum EDebugLevel { DL_Default, DL_Error, DL_Normal, DL_Verbose }; var() config EDebugLevel outputDL; var() config EDebugLevel consoleOutputDL; var() config EDebugLevel defaultDL; /** Keeps track of when the last debug was called for each function. */ var float fstamp[4]; var string fname[4]; var() bool bLeftMeshesLoaded; var() string leftWeapons[2]; // METHODS // function postBeginPlay() { if (level.netmode != NM_DedicatedServer) setPlayer(); } function bool setPlayer() { local PlayerPawn pp; // Get the viewport for console logging. foreach allActors(class'PlayerPawn', pp) { if (hasViewport(pp)) { player = pp; return true; } } return false; } /** Instead of 0 to 65536, it makes it -16384 to 16384. */ function centerRotComp(out float comp) { if (comp < -32768) comp += 65536; else if (comp > 32768) comp -= 65536; } /** Useful function to keep those if statements short. */ function bool hasViewPort(Actor a) { return (a != none && a.isA('PlayerPawn') && PlayerPawn(a).player != none && ViewPort(PlayerPawn(a).player) != none); } /** Are we playing a legacy (non-Strangelove) gametype? */ function bool legacy() { return (!(level.game.isA('Strangewar') || level.game.isA('Strangerace'))); } function setDebugLevel(EDebugLevel l) { defaultDL = l; } function setOutputLevel(EDebugLevel l) { outputDL = l; } function setConsoleOutputLevel(EDebugLevel l) { consoleOutputDL = l; } /** Pass an actor reference and debug string (usually function name). */ function debug(string s, optional EDebugLevel dl, optional float wait, optional string tag) { local Actor a; local string msg; local string ostr; if (dl == DL_Default) dl = defaultDL; // No waiting if verbose. if (wait > 0.0 && outputDL != DL_Verbose && !waitover(getFuncName(s), wait)) return; if (dl <= outputDL) { a = self.owner; if (tag == "") tag = "DEBUG"; if (level.netmode != NM_Standalone) tag = tag @ theTime(); else tag = tag @ level.timeseconds; msg = tag @ "in "; if (a != none) msg = msg $ sname(a) $ "(" $ sname(a.owner) $ ")." $ s; else msg = msg $ "unknown()." $ s; log(msg); if (dl <= consoleOutputDL && level.netmode != NM_DedicatedServer) { if (player != none || (player == none && setPlayer())) { // This could be done better. FIX. if (wait > 0.0 && (outputDL == DL_Verbose && consoleOutputDL != DL_Verbose) && !waitover(getFuncName(s), wait)) return; player.player.console.message(player.playerReplicationInfo, msg, 'debug'); } } } } function err(string s) { debug(s, DL_Error, 0.0, "ERROR!"); } function string theTime() { local int ms; local string mss; ms = level.millisecond; if (ms < 100) mss = "0" $ ms; else mss = string(ms); return (level.hour $ ":" $ level.minute $ ":" $ level.second $ "." $ mss); } /** Only print debugs for this func every w seconds. */ function bool waitover(string func, float w) { local int i; local float t; t = level.timeseconds; for (i = 0; i < arrayCount(fstamp); i++) { if (fname[i] == func) { if (t - fstamp[i] > w) { fstamp[i] = t; return true; } else { return false; } } } // Didn't find the func name, insert it. for (i = 0; i < arrayCount(fstamp); i++) { if (fname[i] == "") { fname[i] = func; fstamp[i] = t; // Don't debug initially. return false; } } // Need to increase the fstamp and fname arrays... err("wait(" $ func $ ", " $ sf(w) $ "): no tags slots"); return false; } function string getFuncName(string s) { local int i; i = instr(s, "("); return left(s, i); } function string sv(vector v, optional bool bSize) { local string size; if (bSize) size = " vsize=" $ chopf(vsize(v)); return "(x=" $ chopf(v.x) $ " y=" $ chopf(v.y) $ " z=" $ chopf(v.z) $ size $ ")"; } function string sr(rotator r) { return "(p=" $ r.pitch $ " y=" $ r.yaw $ " r=" $ r.roll $ ")"; } /** Cuts off trailing 0's. */ function string sf(float f) { local string s; s = string(f); while (right(s, 1) == "0") { s = left(s, len(s) - 1); } if (right(s, 1) == ".") { s = s $ "0"; } return s; } /** Chops off anything after the tenths place. */ function string chopf(float f, optional float nochop) { local string s; local int i; if (abs(f) < nochop) return string(f); s = string(f); i = instr(s, "."); s = left(s, i + 2); return s; } function string sname(Actor a) { if (a != none) { if (a.isA('Pawn')) { return Pawn(a).playerReplicationInfo.playername; } else { return noMap(a); } } else { return "none"; } } function string noMap(Actor a) { local int index; index = instr(a, "."); if (index == -1) { return string(a); } else { return right(a, len(a) - index - 1); } } /** Parses the damage string and sets it in the GameInfo. */ function setDamageString(string s, Actor k, Actor o) { debug("setDamageString(" $ s $ ", " $ sname(k) $ ", " $ sname(o) $ ")"); level.game.specialDamageString = parseKillString(s, k, o); } /** Handles pronouns and such that parseKillMessage() doesn't. pkm() still handles %k, %o, and %w, though. This has to be called first. If we are passing this to pkm(), bNames should be false. Note pkm() silently fails if you don't use %k. */ function string parseKillString(string s, Actor killer, Actor other, optional bool bNames) { if (killer != none && killer.isA('Pawn') && Pawn(killer).bIsFemale) { replace(s, "%kpa", "her"); replace(s, "%kop", "her"); } else { replace(s, "%kpa", "his"); replace(s, "%kop", "him"); } if (other != none && other.isA('Pawn') && Pawn(other).bIsFemale) { replace(s, "%opa", "her"); replace(s, "%oop", "her"); } else { replace(s, "%opa", "his"); replace(s, "%oop", "him"); } if (bNames) { replace(s, "%k", sname(killer)); replace(s, "%ks", sname(killer) $ poss(sname(killer))); } else { // Leave the %k in for pkw()! replace(s, "%ks", "%k" $ poss(sname(killer))); } // We can always do %o. replace(s, "%os", sname(other) $ poss(sname(other))); debug("u.parseKillString(): returning: " $ s, DL_Verbose); return s; } /** Takes a proper name and makes it possessive. E.g. Drewbacca becomes Drewbacca's and yrns becomes yrns'. Update: Only returns what we'd tack on the end. */ function string poss(string name) { if (right(name, 1) == "s") return "'"; else return "'s"; } /** Replace in with out in str. */ function replace(out string str, string in, string out) { local int i; i = instr(str, in); if (i != -1) { str = left(str, i) $ out $ right(str, len(str) - (i + len(in))); } } static function string getVersionString() { //return default.vstr $ " build: " $ default.build $ " compiled: " $ default.when; return default.vstr $ "." $ default.build $ " built on " $ default.when; } // Beta expiration check. static function bool expiredBeta(LevelInfo level) { if (default.bExpires && !(level.year < default.expy || (level.year == default.expy && level.month < default.expm || (level.month == default.expm && level.day < default.expd)))) { log(default.expiredMsg); return true; } return false; } /** Preloads the left meshes for the necessary weapons. Called from SLWeapon.postBeginPlay(). */ function loadLeftMeshes() { local int i; local Class c; local SkeletalMesh m; if (!bLeftMeshesLoaded) { for (i = 0; i < arrayCount(leftWeapons); i++) { if (leftWeapons[i] != "") { c = Class(dynamicLoadObject(leftWeapons[i], Class'Class')); if (c != none) { debug("loadLeftMeshes(): loading: " $ c.default.leftMesh); m = SkeletalMesh(dynamicLoadObject(c.default.leftMesh, Class'SkeletalMesh')); } } } default.bLeftMeshesLoaded = true; } } // end x\b%Jf/a6 r]\]\r~*(r ~ a~r10~ sV G `] D-a -$$-postBeginPlay() . J 2. @OG g H$'M g H$(sMJ  gH$B  gH$ia i7THRO%GMxa x%7VELOm/sGMiia i#7L>HULL%GMxDa D"7L>FUELGMi[a [#7CORESGMD[-d 'a  rHE2-_]  A nQ -s-( XH x";-[H YZ*H c aH $? y // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: ThermoWave.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** This is an f/x cl_ass used with the StrangeWave. It does not do any damage. */ class ThermoWave extends StrangeWave; function postBeginPlay() { disable('tick'); disable('timer'); loopAnim('rotate', 0.5); } function tick(float delta) { // Nothing. } // end @R7v-init()FOODR}Q,DR #>D?Q>_,Fv, jD?DR?Q?kD?DR???,ry,iOjkryx'Ojr&&kryi(Ojr&,kryD(Ojr&,kry[,Ojr&,kr,y-] ' u) // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Stylus.uc,v 1.6 2001/07/21 07:02:18 yrns Exp $ /** This is a wrapper over canvas that adds our own drawing functions. */ class Stylus extends SLInfo; // DATA // /** Our canvas. */ var Canvas c; /** setPos() offsets. */ var int xoff, yoff; /** Center x and y coords. */ var float cx, cy; var float clipx, clipy; /** Resets the canvas. Stores center coords. Checks for a resize. */ function bool setCanvas(Canvas newc) { local bool resized; c = newc; cx = c.clipx / 2; cy = c.clipy / 2; resized = !(clipx == c.clipx); clipx = c.clipx; clipy = c.clipy; return resized; } function setOffsets(int x, int y) { xoff = x; yoff = y; } /** Styles: 0: normal. 1: rounded. 2: missing upper right wing. 3: missing lower right wing. 4: No top or bot lines. 5: No right side. */ function drawFrame(int x, int y, int w, int h, optional int style, optional bool fill, optional bool truexy) { local int re; if (style == 1) re = w - 2; else re = w - 1; setPos(x + 1, y, truexy); if (style == 4) { pix(1, 1); setPos(x + w - 1, y, truexy); pix(1, 1); setPos(x + 1, y + h - 1, truexy); pix(1, 1); setPos(x + w - 1, y + h - 1, truexy); pix(1, 1); } else { if (style == 2) re--; pix(re, 1); if (style == 2) re++; setPos(x + 1, y + h - 1, truexy); if (style == 3) re--; pix(re, 1); } setPos(x, y + 1, truexy); if (style == 1) re = w; else re = w - 1; if (fill) { pix(re, h - 2); } else { pix(1, h - 2); if (style != 5) { setPos(x + re - 1, y + 1, truexy); pix(1, h - 2); } } } /** Fill wrapper. */ function pix(int w, int h) { c.drawRect(Texture'SLV2Textures.slhud.levelpixel', w, h); } /** Set position with offsets. */ function setPos(int x, int y, optional bool truexy) { if (truexy) { c.setPos(x, y); } else { c.setPos(x + xoff, y + yoff); } } /** We don't normally want text to wrap, so we make a wrapper for drawText(). */ function drawText(coerce string text) { if (c.curx < c.clipx ) { c.drawTextClipped(text); } } /** Return the line height. */ function float lineh() { local float xl, yl; c.textSize("TEST", xl, yl); return yl; } /** Return the string width. */ function float strw(String s) { local float xl, yl; c.textSize(s, xl, yl); return xl; } function Color blendc(Color c1, Color c2, float x) { local Color newc; if (x > 1.0) x = 1.0; newc = c1 * (1.0 - x) + c2 * x; return newc; } [ c\ s<f\    G ffff fXf ףp?\ a/!NC z C hn D?T?&33?fIB D?`?&33?fJb(b(b(C f ףp?u=;EG zD?'n ,B ,,,& n ,B , pSDG G m ` OkD? JSrfJD // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: StrangeWave.uc,v 1.14 2001/08/29 04:35:56 yrns Exp $ /** Shockwave that eminates from a Strangelove detonation. */ class StrangeWave extends ShockWave config (SLV2); var() Sound detonateSound; var config float shockMult; // Mesh radius. var() float meshr; var() localized string deathStr, deathSelfStr; var() name dtype; var ThermoWave slave; // Slave is upside down? var bool bSlaveDown; var Util u; simulated function postBeginPlay() { local Actor a; local vector hl, hn; u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); a = trace(hl, hn, (location + vect(0, 0, -1) * 125), location, false); if (a != none) { // Set it on the ground. Looks stupid hovering! setLocation(hl); setRotation(rotator(hn) + rot(-16384, 0, 0)); } if (!level.bDropDetail && level.netmode != NM_DedicatedServer) { slave = spawn(Class'ThermoWave'); // If we're above flat ground, generate another hemisphere. if (a == none) { // Turn it upside down. slave.setRotation(rot(0, 0, -32768)); //slave.setLocation(location + vect(0, 0, 1) * 20); bSlaveDown = true; } } loopAnim('rotate', 0.7); super.postBeginPlay(); } /** Called from postBeginPlay(). */ simulated function spawnEffects() { local float v; v = 9.0 * (shockMult / default.shockMult); playSound(detonateSound, SLOT_None, v); //playSound(detonateSound, SLOT_Interface, v); playSound(detonateSound, SLOT_Misc, v); //playSound(detonateSound, SLOT_Talk, v); } simulated function setRadius() { shockSize = cos(lifespan) * shockMult; } /** This is really for looks. The dedicated server calculates the radius in timer (that's when the real work is done). */ simulated function tick(float delta) { if (level.netmode != NM_DedicatedServer) { setRadius(); scaleGlow = lifespan; ambientGlow = default.ambientGlow * (lifespan / default.lifespan); drawScale = shockSize; if (slave != none) { slave.scaleGlow = scaleGlow; slave.ambientGlow = slave.default.ambientGlow * (lifespan / default.lifespan); slave.drawScale = drawScale; if (!bSlaveDown) slave.drawScale *= 0.75; } } } simulated function timer() { local int i; local vector dir; local StrangeExpl exp; local DamagePuff puff; if (level.netmode == NM_DedicatedServer) { setRadius(); } else { // Effects. if (level.bHighDetailMode && !level.bDropDetail) { for (i = 0; i < (shockMult * 0.5); i++) { dir = vrand(); dir.z *= 0.2; exp = spawn(class'StrangeExpl',,, location + (dir * meshr * shocksize)); exp.remoteRole = ROLE_None; } } } //if (role == ROLE_Authority) vaporize(); } /** Carcass chunks are the authority on the client. */ simulated function vaporize() { local Actor victim; local float damageScale, dist, moscale; local vector dir; u.debug("vaporize(): shock size: " $ shockSize); foreach visibleCollidingActors(class 'Actor', victim, shockSize * meshr, location) { if (victim.role == ROLE_Authority) { // Maybe someday I'll figure this out. if (level.netmode == NM_Client) u.debug("vaporize(): victim has authority: " $ u.sname(victim)); dir = victim.location - location; dist = fmax(1, vsize(dir)); dir = dir/dist + vect(0, 0, 0.3); if ((dist > oldShockDistance) || (dir dot victim.velocity <= 0)) { moscale = fmax(0, 1100 - 1.1 * dist); // Non-pawns don't need death messages. if (role == ROLE_Authority && victim.bIsPawn) { Pawn(victim).addVelocity(dir * (moscale + 20)); if (victim == instigator) u.setDamageString(deathSelfStr, instigator, victim); else u.setDamageString(deathStr, instigator, victim); } else { victim.velocity = victim.velocity + dir * (moscale + 20); } victim.takedamage(moscale, instigator, victim.location - 0.5 * (victim.collisionHeight + victim.collisionRadius) * dir, (1000 * dir), dtype); } } } oldShockDistance = shockSize * meshr; } simulated function destroyed() { local vector loc; if (level.netmode != NM_DedicatedServer) { if (!level.bDropDetail) spawn(Class'BlackCloud'); } super.destroyed(); } // end 4-Norm:beginState()-}(-o ( Y Uwm nn -a "n`{6{na {{#6{n{-(o${#6{n{ Y // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: StrangeShell.uc,v 1.30 2001/08/28 07:24:29 yrns Exp $ /** Ridable Strangelove rocket. Watch out: autonomous proxy here. */ class StrangeShell extends GuidedWarShell config (SLV2); // IMPORTS // #exec OBJ LOAD FILE=..\Sounds\SLV2Sounds.uax PACKAGE=SLV2Sounds // DATA // var Controller slc; // Our controller. var bool dumb; var() float armor; // Damage the hull can withstand. var() config float fuel; // Amount of fuel (in seconds). var() config float teamDamage; // Rocket friendly-fire. var() vector pilotOffset; // Relative offset from the rocket location. var() float minspeed; var() float fullspeed; // Max speed without burners. var() float accelmax; var() float decelRatio; // The max deceleration. var bool bAfterburn; // Is the afterburner on? var() float afterburnRate; // Extra fuel ratio you burn. var() float baseFuelRate; // Fuel per second you burn at cruising speed. var bool bNoFuel; // Flag for setting stuff when the fuel runs out. var float accel; // Current acceleration rate. var() float thrate; // Fastest you can move the throttle. var() class HUDClass; var() class exhaustPuffClass; var() class damagePuffClass; var() class detClass; var config bool bNoContrails; var() class contrailClass; var Contrail rootct; var() float puffRand; // Random x/y offset. var() float puffOffset; // Distance the puffs start behind. /** The impact angle the hull can survive at max. velocity. This is a percentage of 90, so 45 degrees would be 0.5. See reflect(). */ var() float crushAngle; // Sounds. var() Sound dropSound; var() Sound gunnerSound; var() Sound crashSound; var() Sound throtSound; var() Sound warnSound; var() Sound abFadeSound; var() Sound abLoopSound; var() Sound abKickSound; var() Sound refuelSound; var() Sound hullHits[4]; var() Sound bounces[5]; var vector realAccel; // Just like GuidedWarshell's realLocation, etc. // Warhead status. Goddamn enums. var() config enum EWarStat { WH_Auto, WH_Armed, WH_Disarmed } warstat; var config bool bCanOverride; // The pilot can toggle the warhead status manually. var GameReplicationInfo GRI; // Used to detect the end of the game. var bool bDetonate; // Set just before calling explode(). var float errorSum; // Total client error on serverMove()'s. var int start; // Seconds since level began at begin play. var bool bClientNoFuel; // We use this to run initial no fuel code once. var bool bBlasted; // Placed blast decal? /** This keeps track of the last two pawns to eject and when, so that we don't pick them up again in touch(). */ var Pawn ejected[2]; var float ejectTime[2]; var() float touchLapse; var float tstamp; // When the last timer() Was called. var float smokeTimer; // Keep track of the old timer(). var float closeTimer; // Dumbfires' wings close before impact. var bool bClosed; // True if already closed. /** These are used to display our special death messages. Possible dtypes: NoUpdate, Ram, HitWall, HitSL, Shot. */ var name dtype; var() localized string normDamStr; var() localized string normSelfDamStr; var() localized string ramDamStr; var() localized string ramSelfDamStr; var() localized string ramSelf2DamStr; var() localized string shotDamStr; var() localized string shotSelfDamStr; var() localized string hitWallDamStr; var() localized string hitWall2DamStr; var() localized string hitSLDamStr[5]; var() localized string hitSLDamStr2; // See spawnPuffs(). These control sputtering when out of fuel, etc. var bool bSputter; var float sputtime; var float launched; // The time we were spawned. var() float shootCR; var() float lvlimit; // Limit on velocity we can maintain at launch. See setLaunchVel(). var StrangeShell rammed; // The shell we just touched. /** Used when handling teleportation. See touch(). */ var Teleporter lastTP; var rotator lastTPRot; // CONTROLLER DATA // var Pawn pilot; var Pawn gunner; var bool bShootMode; var bool addedHUD; var SLHUD HUD; var float throttle; // Percent throttle value. var float thrprev; // Throttle level before afterburner was set off. var float thlvl; var bool bThrotSent; var int throtDir; // For the mutate throttle commands. struct RiderInfo { var Pawn pawn; var bool bOldJumpStatus; var bool bDelayedEject; var SLBotBrain brain; var Actor trailers[5]; var vector lastLoc; }; var RiderInfo pInfo, gInfo; var() Sound sndPilotEj; var() Sound sndGunnerEj; var() Sound sndDrop; var byte bDuck; var vector fogReset; // Reset info to get rid of the fog. var bool bFog; // Is the client fogged? var NullWeapon nw; // The glue gun. var Weapon lastWeapon; // Weapon to switch back to in shoot mode or eject. var float teleportDist; // Minimum distance to kick in teleport hack. var Fuelcore fc; // Our fuel core inventory object. var config bool bBotsObeyOrders; // Bots follow orders or stick as gunners? var float marchWait; // Time to wait before playing total comm. sound. var bool bMarchUnder; // Have we been under the march limit yet? var bool bMarchPlaying; var() Sound sndMarchLoop; var() Sound sndTotalComm; var float savedBob; // Preserve their bob and set it to zero. var bool bTakeover; // In a takeover sequence. var float toTime; // Time left until takeover by gunner. var bool bWahooPlayed; // Once we achieve min speed, Major Kong plays. var Sound sndWahoo; var() config bool bRevY; // Reverse the mouse Y axis while flying? var bool bFlipY; // Have we already flipped it? var() localized string gunnerMountStr[3]; var() localized string gunnerMountStr2[3]; var float painTimer; var Util u; // REPLICATION // replication { reliable if (role == ROLE_Authority) armor, dumb, bAfterburn, warstat, bNoFuel; // The client gets it initially and goes with it. For takeovers? reliable if (role == ROLE_Authority && bNetOwner) // && bNetInitial) fuel; reliable if (role == ROLE_Authority && !bNetOwner) realAccel; // From the controller... reliable if (role == ROLE_Authority) pilot, gunner, bShootMode, throttle, fc; reliable if (role < ROLE_Authority && bNetOwner) clientSendThrot, goShootMode; } // METHODS /** Called on auto proxy and clients. */ simulated function postBeginPlay() { u = spawn(Class'Util', self); u.setDebugLevel(DL_Normal); u.debug("postBeginPlay(): owner: " $ u.sname(owner)); launched = level.timeseconds; // Find our GRI so we know when the match is over. foreach allActors(Class'GameReplicationInfo', GRI) break; startContrail(); // Start up smoke, etc. //setTimer(0.1, false); setTimer(0.000000001, true); start = level.timeseconds; tstamp = level.timeseconds; // Old controller init. bShootMode = false; thlvl = 0.0; throttle = 0.0; bThrotSent = false; addedHUD = false; bFog = false; fogReset.x = 0; //greenie.r * 0.0; fogReset.y = 100; //greenie.g * 0.5; fogReset.z = 0; //greenie.b * 0.0; throtDir = 0; } simulated function startContrail() { // Set myself as the owner and it becomes the root segment. if (level.netmode != NM_DedicatedServer && level.bHighDetailMode && !bNoContrails) { u.debug("postBeginPlay(): spawning rootct", DL_Verbose); rootct = spawn(contrailClass, self); } } /** This is our new tick! */ simulated function timer() { local float now; local float delta; now = level.timeseconds; if (tstamp != 0) { delta = now - tstamp; tstamp = now; if (slc == none && !dumb) ctick(delta); smoketick(delta); } tstamp = now; } /** Called on auto proxy clients. This is the old timer(). */ simulated function smoketick(float delta) { local float rate; local vector x, y, z; // Set the timer rate. if (level.bHighDetailMode) { rate = 0.01; if (level.bDropDetail) rate = 0.1; } else { rate = 0.15; } // This simulates the old timer code. smokeTimer += delta; if (smokeTimer > rate) smokeTimer -= rate; else return; // If we haven't received an update from the client in 4 seconds, explode. if (!dumb && pilot != none && !gameOver() && !instigator.isA('Bot') && role == ROLE_Authority && (level.timeseconds - serverUpdate > 4.0)) { dtype = 'NoUpdate'; explode(location, vect(0, 0, 1)); return; } // Warn cannons so they can shoot us down. cannonTimer += rate; if (cannonTimer > 0.6) { warnCannons(); cannonTimer -= 0.6; // Just stuck this in here so I don't have to create another timer. getTrailers(); } // This section just for f/x. if (level.netmode != NM_DedicatedServer) { spawnPuffs(rate); // Close the wings? if (!bClosed) { closeTimer += rate; if (closeTimer > 1.0) { // Check for impacts on dumbfires, and pilotlessness on !dumb. if (dumb) { getAxes(rotation, x, y, z); if (!fastTrace(location + x * vsize(velocity) * 5.0)) bClosed = true; } else if (pilot == none) { bClosed = true; } closeTimer -= 1.0; if (bClosed) playAnim('close', 0.4, 0.2); } } } //setTimer(rate, false); } /** Spawn exhaust puffs here. Rate is the timer rate. */ simulated function spawnPuffs(float rate) { local ExhaustPuff puff; local vector x, y, z, loc; // Handle sputtering. if (region.zone.bWaterZone || bNoFuel) { if (bSputter) { sputtime += rate; } else { bSputter = true; sputtime = 0; } } else { bSputter = false; } // Do the puffs. getAxes(rotation, x, y, z); // If bSputter is on, sputter out slowly for 2 secs. if (!bSputter || (bSputter && sputtime < 1.0 && (frand() * 1.0) > sputtime)) { loc = location - (x * puffOffset); //collisionRadius); if (puffRand > 0.0) { loc += (y * ((frand() * puffRand * 2.0) - puffRand)); loc += (z * ((frand() * puffRand * 2.0) - puffRand)); } puff = spawn(exhaustPuffClass,,, loc); if (!bSputter) puff.drawScale = puff.default.drawScale + 0.5 * ((vsize(velocity) - minspeed) / (maxspeed - minspeed)); } if ((armor / default.armor) < 0.70) spawnDamPuffs(location - (x * 20), y, z, 1.0 - armor / default.armor); } /** Spawn damage puffs here. */ simulated function spawnDamPuffs(vector loc, vector y, vector z, float damf) { local DamagePuff puff; local float rand; // Hull puffs slightly more jumpy. if (puffRand > 0.0) { rand = puffRand * 2.0; } else { rand = 4.0; } loc += y * ((frand() * rand * 2.0) - rand); loc += z * ((frand() * rand * 2.0) - rand); // Size and scale rate depend on the damage factor. puff = spawn(damagePuffClass,,, loc); puff.drawScale = damf * 2.0; } function StrangeShell findSL(Pawn p) { local StrangeShell s; u.debug("findSL()", DL_Verbose); foreach allActors(class'StrangeShell', s) { if ((s.pilot != none && s.pilot == p) || ((s.gunner != none) && s.gunner == p)) { return s; } } return none; } /** Cycles the warhead status. */ function switchWarhead() { if (bCanOverride) { switch (warstat) { case WH_Auto: warstat = WH_Armed; break; case WH_Armed: warstat = WH_Disarmed; break; case WH_Disarmed: warstat = WH_Auto; break; } } } /** Disarm the warhead. For bots, mainly. */ function disarm() { if (bCanOverride) warstat = WH_Disarmed; } /** Arm the warhead. For bots. */ function arm() { if (bCanOverride) warstat = WH_Armed; } /** Returns true if we're armed. */ simulated function bool armed() { return (warstat == WH_Armed); } /** If we are set to auto, arm us. */ function bool autoArm() { if (warstat == WH_Auto) { warstat = WH_Armed; return true; } return false; } /** Called from the options screen. */ static function setDefaultWarhead(int stat) { if (stat == 1) default.warstat = WH_Armed; else if (stat == 2) default.warstat = WH_Disarmed; else default.warstat = WH_Auto; } function setRemoteRole(Actor a) { if (level.netmode != NM_Standalone) { if (role == ROLE_Authority && !dumb && a != none && !a.isA('Bot') && (level.netmode != NM_ListenServer || !u.hasViewPort(a)) && !bNoFuel) { remoteRole = ROLE_AutonomousProxy; } else { remoteRole = ROLE_SimulatedProxy; } u.debug("setRemoteRole(" $ u.sname(a) $ "): remoteRole set: " $ remoteRole); } } auto state Flying { simulated function beginState() { local Pawn p; local float lv; u.debug("Flying.beginState(): owner: " $ u.sname(owner) $ " inst: " $ u.sname(instigator)); playAnim('open', 0.5); // Only the server has the owner set. if (owner != none) { serverUpdate = level.timeSeconds; guidedRotation = rotation; if (level.netmode == NM_Standalone || (level.netmode == NM_ListenServer && u.hasViewPort(owner))) slc = spawn(Class'Controller', self); } else { // The owner is always none on the client in a network // game. (At least for another few ticks.) if (role == ROLE_Authority) { dumb = true; warstat = WH_Armed; // Set acceleration. Catch this on the client with net initial...? acceleration = (accelmax * 0.20) * vector(rotation); // Don't do the launch v. velocity = minspeed * vector(rotation); setRemoteRole(owner); return; } } setLaunchVel(speed); setRemoteRole(owner); u.debug("Flying:beginState(): set v: " $ u.sv(velocity), DL_Verbose); } /** We want to maintain the instigator's velocity when they launch. On the server, we know it already. On the client the vel. doesn't get updated for a few ticks (or more), so we guess it on our end. The launcher sets the lv and we look for it here. */ simulated function setLaunchVel(int startSpeed) { local Pawn p; local vector lv, zero; local rotator lr; local float z; if (role == ROLE_Authority) { if (instigator != none) lv = instigator.velocity; lr = rotation; } else { // This is on the client. dumb == unknown. // Major kludge. This is all to save us from two or // three ticks of the wrong velocity. Hopefully it will smooth launches // out with maintained velocity. If there are two launches in the radius, // set them all to 0. That's better than having the wrong velocity set. foreach radiusActors(Class'Pawn', p, vsize(pilotOffset) * 2.0) { if (p.weapon != none && p.weapon.isA('Konglauncher') && Konglauncher(p.weapon).launchv != zero) { // Not set yet. if (lv == zero) { lv = Konglauncher(p.weapon).launchv; lr = p.rotation; } else { lv = zero; } u.debug("setLaunchVel(): found p: " $ u.sname(p) $ " lv: " $ u.sv(lv) $ " lr: " $ u.sr(lr) $ " delta loc: " $ u.sv(p.location - location), DL_Verbose); // Clear it. Konglauncher(p.weapon).launchv = zero; } } } // Now limit the velocity. Give a little on the z component, though. if (vsize(lv) > lvlimit) { z = lv.z; lv = normal(lv) * lvlimit; lv.z = fmin(z, lvlimit * 1.5); u.debug("setLaunchVel(): lv scaled to: " $ u.sv(lv), DL_Verbose); } // Merge it with the shell v. Only if we have a launch rotation. if (lr != rot(0, 0, 0)) velocity = vector(lr) * startSpeed + lv; } /** Intercept sky node touches. */ simulated function touch(Actor other) { u.debug("touch(): actor: " $ u.sname(other) $ " rotation: " $ u.sr(rotation), DL_Verbose); if (other.isA('SkyNode') && (pInfo.brain != none)) { pInfo.brain.nodeReached(SkyNode(other)); } else if (other.isA('Teleporter') && lastTP == none) { lastTP = Teleporter(other); lastTPRot = rotation; if (rootct != none) rootct.end(); } else { super(Projectile).touch(other); } } /** Someone has run into us! For shame! This handles detonations as well as new gunners. */ function processTouch(actor a, vector hl) { local vector x, y, z; // 1. Ignore processTouch() on the auto proxy client. // 2. Ignore fuel cores. (We have to pick them up.) // 3. The touching actor is the pilot or gunner. // 4. Ignore carcasses. Is this right? // 5. Ignore those recently ejected. // 6. Ignore the instigator if recently fired. if (role < ROLE_Authority || a.isA('Fuelcore') || a == pilot || a == gunner || a.isA('Carcass') || (a.isA('Pawn') && wasEjected(Pawn(a))) || (a == instigator && ((level.timeseconds - launched) < 2.0))) return; //u.debug("processTouch(): actor: " $ u.sname(a)); // See if we're a rider on another rocket. if (a.isA('Pawn') && Pawn(a).bIsPlayer && findSL(Pawn(a)) == none) { if (!dumb && sameTeam(Pawn(a), instigator) && gunner == none) { u.debug("processTouch(): sticking new gunner on: " $ u.sname(a)); setGunner(Pawn(a)); return; } // Nope? Run them over... } if (armed() && fromFront(a)) { u.debug("processTouch(): detonating from front"); detonate(hl, normal(hl - a.location)); } else { if (a.isA('StrangeShell')) { // Don't detonate our own freshly-launched rockets. Or the gunner's. if ((a.instigator == instigator || a.instigator == gunner) && ((level.timeseconds - StrangeShell(a).launched) < 2.0 || (level.timeseconds - launched) < 2.0)) return; // Remember who we rammed. rammed = StrangeShell(a); // This will give us an inverse normal 50% of the time. getAxes(a.rotation, x, y, z); reflect(y, a); //, 0.75); } else { // Print the correct death message. if (a == instigator) u.setDamageString(ramSelf2DamStr, a, a); else u.setDamageString(ramDamStr, instigator, a); a.takeDamage(vsize(velocity - a.velocity) * 0.15, instigator, hl, normal(velocity) * momentumTransfer, myDamageType); playSound(bounces[rand(ArrayCount(bounces))], SLOT_None, 4.0); // We take a fraction of the damage. takeDamage(vsize(velocity) * 0.05, instigator, hl, normal(velocity) * momentumTransfer, 'Ram'); } } } /** Wrapper for detonation. */ function detonate(vector hl, vector n) { bDetonate = true; explode(hl, n); } /** Either detonate, spawning a shock wave, or blow up due to damage. */ function explode(vector hl, vector n) { u.debug("explode(" $ u.sv(hl) $ ", " $ u.sv(n) $ "): detonate: " $ bDetonate $ " instigator: " $ u.sname(instigator)); if (role < ROLE_Authority) return; if (bDetonate) { spawn(detClass,,, hl + n * 16, rot(0, 0, 0)); } else { // This looks silly dropped, but oh well. if (!level.bDropDetail && level.netmode != NM_DedicatedServer) { spawn(class'SLV2.StrangeExpl',,, location); spawn(class'SLV2.BlackCloud',,, location); } // Use our dtype, previously set in takeDamage(). specialHurtRadius(damage, 300.0, dtype, momentumTransfer, hl); } // Why do I need this? //remoteRole = ROLE_SimulatedProxy; destroy(); } } // End Flying. function detonate(vector hl, vector n) { // Nothing. } /** I had to redo this to get better control over the death messages. */ function specialHurtRadius(float dam, float rad, name dtype, float mom, vector hl) { local Actor v; local float dscale, dist; local vector dir; local string sds; //if (bHurtEntry) //return; //bHurtEntry = true; u.debug("specialHurtRadius(): " $ dtype); foreach visibleCollidingActors(Class'Actor', v, rad, hl) { if (v != self) { if (v.isA('Pawn')) { // Set our "special" damage string. sds = normDamStr; if (dtype == 'NoUpdate' && v == instigator) { sds = normSelfDamStr; } else if (dtype == 'Ram' && v == instigator) { sds = ramSelfDamStr; } else if (dtype == 'HitWall' && v == instigator) { if (frand() < 0.5) sds = hitWallDamStr; else sds = hitWall2DamStr; } else if (dtype == 'HitSL') { // No rammed or ram pilot? if (rammed == none || rammed.pilot == none) { if (v == instigator) sds = hitSLDamStr[rand(arrayCount(hitSLDamStr))]; } else { if (v == rammed.pilot) { sds = hitSLDamStr2; } else if (v == instigator) { // This will print a blank line. Oh well. sds = " "; } } } else if (dtype == 'Shot') { if (v == instigator) { sds = shotSelfDamStr; } else if (v == pilot || v == gunner) { sds = shotDamStr; } } u.setDamageString(sds, instigator, v); } dir = v.location - hl; dist = fmax(1, vsize(dir)); dir = dir / dist; dscale = 1 - fmax(0, (dist - v.collisionRadius) / rad); v.takeDamage(dscale * dam, instigator, v.location - 0.5 * (v.collisionHeight + v.collisionRadius) * dir, dscale * mom * dir, myDamageType); } } //bHurtEntry = false; } /** Store this guy. He just ejected. If we have 3 ejects within the eject lapse, then we lose whoever was in the first slot. So be it. */ function storeEject(Pawn p) { local float time; time = level.timeseconds; if (ejected[1] == none || (time - ejectTime[1]) > touchLapse) { ejected[1] = p; ejectTime[1] = time; } else { ejected[0] = p; ejectTime[0] = time; } } /** Was this guy recently ejected? */ function bool wasEjected(Pawn p) { if (ejected[0] == p && (level.timeseconds - ejectTime[0]) <= touchLapse) return true; else if (ejected[1] == p && (level.timeseconds - ejectTime[1]) <= touchLapse) return true; return false; } /** Returns true if they are in front of us. Roughly. */ function bool fromFront(Actor a) { local vector dir, x, y, z; if (a != none) { dir = normal(a.location - location); getAxes(rotator(velocity), x, y, z); // Is this like 45 degrees? Not sure... return ((dir dot x) > 0.7); } else { return false; } } /** Spit out some sparks. */ simulated function sparks(int dam) { local int isparks; if (level.netmode != NM_DedicatedServer) { for (isparks = min(15, dam / 2); isparks > 0; isparks--) spawn(Class'SLSparker'); } } /** If taking damage from the front, there should be a chance of detonation - if armed. Should this be singular? No. As an example, imagine our rocket bumps into another rocket. We take a slight bit of damage, but the other rocket explodes. Their explosion causes us massive damage. If this function were singular, we'd be left unscathed. What we do is, if our armor's already depleted, we return so we don't explode again. */ function takeDamage(int dam, Pawn i, vector hl, vector m, name dtype) { u.debug("takeDamage(): dam: " $ dam $ " inst: " $ u.sname(i) $ " hl: " $ u.sv(hl) $ " armor: " $ armor $ " dtype: " $ dtype); if (HUD != none) { HUD.shake(dam * 0.25); } if (armor < 0) { u.debug("takeDamage(): armor depleted already"); return; } sparks(dam); if (role < ROLE_Authority) return; // The instigator is what team we're on. if (i != instigator && sameTeam(i, instigator)) dam *= teamDamage; armor -= dam; // Explode, either by shot damage or accidental (heh) detonation. if (armor <= 0 || (armed() && fromFront(i) && frand() < 0.40)) { // Reset the instigator so that whoever shot us gets credit. if (i != none && i != pilot) instigator = i; if (armed()) { u.debug("takeDamage(): detonating from front"); detonate(hl, vect(0, 0, 1)); } else { // If it's not one of these, we were shot down. if (dtype != 'HitWall' && dtype != 'HitSL' && dtype != 'Ram') self.dtype = 'Shot'; playSound(crashSound, SLOT_None, 8.0); explode(hl, vect(0, 0, 1)); } } else { // Just a scratch. Play the dent sound. //if (type != 'HitWall') playSound(hullHits[rand(arrayCount(hullHits))], SLOT_None, 6.0); } } /** This will get called on the client when we switch to sim proxy. */ simulated function hitWall(vector hn, Actor wall) { local float dam; local DirectionalBlast db; u.debug("hitWall()"); if (armed()) { // Blast decal. spawnBlast(true, location, hn); if (role == ROLE_Authority) { if ((Mover(wall) != none) && Mover(wall).bDamageTriggered) // Bogus damage type. wall.takeDamage(damage, instigator, location, normal(velocity) * momentumTransfer, 'RocketCrash'); makeNoise(1.0); detonate(location + explowallout * hn, hn); } // Don't call hitwall again. setPhysics(PHYS_None); // warstat == 1 code adapted from Projectile.hitWall(). //super.hitWall(hn, wall); } else { dam = reflect(hn, wall); if (level.netmode != NM_DedicatedServer) { db = spawn(Class'SLV2.DScar', self); if (db != none) { db.drawScale *= (dam * 0.03); db.directionalAttach(velocity, hn); } } } } /** We have hit something. hn is the hit normal. Returns the amount of damage inflicted. In SL-SL collisions, this resets the velocity, which affects the second rocket's reflect() call. FIX. */ simulated function float reflect(vector hn, Actor a, optional float soften) { local float theta; local vector nv, x, y, z; local float relv; local float dam; if (soften == 0.0) soften = 1.0; // Default is hit wall. if (a.isA('StrangeShell')) dtype = 'HitSL'; else dtype = 'HitWall'; // Angle of impact. 1 is head on. Near 0 is losing a coat of // paint. Sometimes we're passed an inverse normal for simplicity, so // take the abs(). theta = abs(normal(velocity) dot hn); relv = vsize(velocity - a.velocity); // Average the angle and speed components. 100.0 is usually the // default armor (max. damage). 1000.0 is usually maxspeed. The // angle counts twice as much. dam = ((2.0 * (theta / crushAngle) + (relv / 1000.0)) / 3.0) * 100.0 * soften; u.debug("reflect(): actor: " $ u.sname(a) $ " theta: " $ theta $ " relv: " $ relv $ " soften: " $ soften $ " dam: " $ dam); // Theta causes up to a 40% reduction in speed. nv = mirrorVectorByNormal(velocity, hn) * (1.0 - 0.40 * theta); if (soften < 1.0) velocity = (velocity * (1.0 - soften) + nv * soften); else velocity = nv; // Use the new velocity. Could we find a better hit location to use? if (role == ROLE_Authority) { takeDamage(dam + (rand(10) - 5), instigator, location, normal(velocity) * momentumTransfer, dtype); // Don't piss off CSHP. Server only. if (pilot != none) { pilot.clientSetRotation(rotator(velocity)); } } guidedRotation = rotator(velocity); // This'll never happen. if (bNoFuel) spin(); playSound(bounces[rand(ArrayCount(bounces))], SLOT_None, 4.0); return dam; } simulated function float arccos(float x) { return atan((sqrt(1 - x ^ 2)) / x); } /** Toggles the afterburners. */ function afterburn() { // Need fuel to burn. if (bNoFuel) return; if (bAfterburn) { bAfterburn = false; throttle = thrprev; } else { // Burners now on! bAfterburn = true; thrprev = throttle; throttle = 1.5; } // Trigger kick and fade sounds. updateSound(0); } function newOwner(Actor o) { u.debug("newOwner(" $ u.sname(o) $ ")"); setOwner(o); } /** Returns true if the game's over. */ simulated function bool gameOver() { return (GRI != none && GRI.gameEndedComments != ""); } /** Handles fuel usage. Called from the controller's tick() function. Midair (after no-fuel condition) refueling does not work yet. FIX. */ simulated function burnFuel(float delta) { local float fr; //u.debug("burnFuel(): current fuel: " $ fuel, DL_Verbose); // Don't burn fuel after game's over. if (gameOver()) return; // Burn fuel. Only do this on the server and owner client. The // owner client only does this to render the fuel gauge. if (role == ROLE_Authority || u.hasViewport(pilot)) { if (fuel > 0.0) { fr = baseFuelRate; if (bAfterburn) fr *= afterburnRate; fuel = fmax(0.0, fuel - fr * delta); } // Try to refuel. The client refuels, but doesn't ditch a pod. if (fuel == 0.0) { if (!refuel() && role == ROLE_Authority) bNoFuel = true; else /* if (bNoFuel) // Somehow we found a core. bNoFuel = false; */ } } // We don't really know we're out of fuel until the server sets // bNoFuel to true. We only run this "out of fuel" code once. if (bNoFuel) { if (!bClientNoFuel) { u.debug("burnFuel(): Uh oh, no fuel."); // Cut the engines yo. updateSound(0); // We should be falling down... setRemoteRole(pilot); setPhysics(PHYS_Falling); noFuel(); spin(); bClientNoFuel = true; } /* } else { if (bClientNoFuel) { // Somehow we picked up a core, after bNoFuel was set. u.debug("burnFuel(): Found some fuel!"); ambientSound = default.ambientSound; setRemoteRole(pilot); bClientNoFuel = false; } */ } } /** This gets called when the shell runs out of fuel. */ function noFuel() { eject(pilot, 0, true); eject(gunner, 0, true); } simulated function spin() { randspin(rand(10000) + 10000); } /** This handles refueling from carried fuel cores. It is called on both (owner) client and server, but the "no fuel" condition is passed to the client from bNoFuel on the rocket. */ simulated function bool refuel() { if (pilot != none) { if (fc != none && fc.ammoAmount > 0) { if (role == ROLE_Authority) fc.useAmmo(1); playSound(refuelSound, SLOT_None, 1.0); fuel = default.fuel; return true; } } return false; } /** Tick. Not called on server with an auto proxy! */ simulated function tick(float delta) { //u.debug("tick(): rotation: " $ u.sr(rotation), DL_Normal, 5.0); // Dropping like a rock. if (bNoFuel) return; if (dumb) { // No controller, so burn fuel ourselves. /* Acceleration is static. if (role == ROLE_Authority) accelerate(delta); */ burnFuel(delta); return; } // If the game's over... freeze. Damnit this only works for players and guided rockets (fine). if (GRI != none && GRI.gameEndedComments != "") { setPhysics(PHYS_None); velocity = vect(0, 0, 0); return; } // The role is still auto on non-viewport clients. Duh. Took me // this long to figure that out. Can't test for sim proxy on the client. if (level.netmode == NM_Client && (instigator.isA('Bot') || !u.hasViewPort(instigator))) { updateSimProxy(); return; // WTF is this? When does this happen? FIX. } else if ((level.netmode != NM_Standalone) && (remoteRole == ROLE_AutonomousProxy)) { return; } // if server updated client position, client needs to replay moves after the update if (bUpdatePosition) clientUpdatePosition(); // Keep goin straight if we're dead! if (guided()) if (pInfo.brain != none && pInfo.brain.bGuiding) guidedRotation = pInfo.brain.getGuidedRotation(); else guidedRotation = pilot.viewRotation; if (role == ROLE_AutonomousProxy) autoMove(delta); else moveRocket(delta, velocity, guidedRotation); } simulated function updateSimProxy() { if (updateReal(realLocation, location, 20.0)) { //u.debug("updateSimProxy: real loc: delta: " $ vsize(realLocation - location), DL_Verbose); setLocation(realLocation); realLocation = vect(0, 0, 0); } if (updateReal(realVelocity, velocity, 0.0)) { //u.debug("updateSimProxy: real vel: delta: " $ vsize(realVelocity - velocity), DL_Verbose); velocity = realVelocity; // Aproximate the rotation on sim proxies (no roll). setRotation(rotator(velocity)); realVelocity = vect(0, 0, 0); } if (updateReal(realAccel, acceleration, 0.0)) { //u.debug("updateSimProxy: real acc: delta: " $ vsize(realAccel - acceleration), DL_Verbose); acceleration = realAccel; realAccel = vect(0, 0, 0); } } simulated function bool updateReal(vector v1, vector v2, float mindelta) { return (v1 != vect(0, 0, 0) && vsize(v1 - v2) > mindelta); } /** Auto proxy movement. Called from tick(). Calls serverMove(). */ function autoMove(float delta) { local SavedMove nm; // Send the move to the server. Skip move if too soon. if (clientBuffer < 0) { clientBuffer += delta; moveRocket(delta, velocity, guidedRotation); return; } else { clientBuffer = clientBuffer + delta - 80.0 / PlayerPawn(instigator).player.currentNetSpeed; } // I'm a client, so I'll save my moves in case I need to replay // them. Get a SavedMove actor to store the movement in. if (savedMoves == none) { savedMoves = getFreeMove(); nm = savedMoves; } else { nm = savedMoves; while (nm.NextMove != none) nm = nm.nextMove; nm.nextMove = getFreeMove(); nm = nm.nextMove; } nm.timestamp = level.timeseconds; nm.delta = delta; nm.velocity = velocity; nm.setRotation(guidedRotation); moveRocket(delta, velocity, guidedRotation); serverMove(level.timeseconds, location, nm.rotation.pitch, nm.rotation.yaw); } /** Is there a pilot and is he conscious? */ simulated function bool guided() { return (pilot != none && !bShootMode && pilot.health > 0); } /** Server replicates this to the client. */ simulated function clientAdjustPosition(float ts, float nlx, float nly, float nlz, float nvx, float nvy, float nvz) { super.clientAdjustPosition(ts, nlx, nly, nlz, nvx, nvy, nvz); u.debug("clientAdjustPosition(): new v: " $ vsize(velocity), DL_Verbose); } /** Had to modify this slightly. */ simulated function clientUpdatePosition() { local SavedMove curm; bUpdatePosition = false; curm = savedMoves; while (curm != none) { if (curm.timestamp <= currentTimeStamp) { savedMoves = curm.nextMove; curm.nextMove = freeMoves; freeMoves = curm; freeMoves.clear(); curm = savedMoves; } else { // Use the current velocity, not the stored one! moveRocket(curm.delta, velocity, curm.rotation); curm = curm.nextMove; } } u.debug("clientUpdatePosition(): updated v: " $ vsize(velocity), DL_Verbose); //super.clientUpdatePosition(); } /** Client replicates this to the server. Called only from tick(). */ function serverMove(float ts, vector clientloc, int pitch, int yaw) { local float clientErr, delta; local vector dloc; if (currentTimeStamp >= ts) return; if (currentTimeStamp > 0) delta = ts - currentTimeStamp; currentTimeStamp = ts; guidedRotation.pitch = pitch; guidedRotation.yaw = yaw; if (delta > 0) moveRocket(delta, velocity, guidedRotation); if (level.timeseconds - lastUpdateTime > 0.4) { //0.3) { u.debug("serverMove(): sec since last update: " $ (level.timeseconds - lastUpdateTime), DL_Verbose); clientErr = 10000; } else if (level.timeseconds - lastUpdateTime > 0.07) { dloc = location - clientloc; clientErr = dloc dot dloc; errorSum += clientErr; } // If client has accumulated a noticeable positional error, correct him. if (clientErr > 3) { lastUpdateTime = level.timeseconds; u.debug("serverMove(): sending updated pos: clientErr: " $ clientErr, DL_Verbose); clientAdjustPosition(ts, location.x, location.y, location.z, velocity.x, velocity.y, velocity.z); } //super.serverMove(ts, clientloc, pitch, yaw); } /** This clamps the guide rotation to 90 degree turns. We don't want exact 180 turns else the rocket may shoot straight up or down. Also clamp pitch to below 16384. The yaw goes wild past that. */ simulated function rotator adjustGuideRot(rotator gr, rotator rr) { local float dyaw, pitch; local rotator newr; // This returns the last known guide rotation, i.e. straight. :) if (!guided() || pilot.isA('Bot')) return gr; dyaw = (pilot.viewRotation.yaw & 65535) - (rr.yaw & 65535); pitch = gr.pitch; u.centerRotComp(dyaw); u.centerRotComp(pitch); newr = gr; newr.yaw = rr.yaw + clamp(dyaw, -16384, 16384); newr.pitch = clamp(pitch, -16384, 16384); return newr; } /** Actually moves the rocket based on the passed criteria. Eventually calls autonomousPhysics(). */ simulated function moveRocket(float delta, vector cv, rotator gr) { local vector deltav; local int maxroll, oldroll, rollmag; local float smoothRoll; local vector oldv, x, y, z; //u.debug("moveRocket(): delta: " $ delta $ " cv: " $ vsize(cv) $ " gr: " $ u.sr(gr), DL_Verbose); if (lastTP != none) { if (lastTPRot != rotation) { u.debug("moveRocket(): teleported! new rot: " $ u.sr(rotation), DL_Verbose); cv = vsize(velocity) * vector(rotation); velocity = cv; gr = rotation; } lastTP = none; startContrail(); } serverUpdate = level.timeseconds; oldroll = (rotation.roll & 65535); oldv = cv; // This is our steering component. Its strength is based on the // current velocity. At higher speeds it's lower. deltav = vector(adjustGuideRot(gr, rotator(cv))) * ((maxspeed + 700) - vsize(cv)) * delta; // Here, we blend the current velocity (momentum) and the steering // vector from above to get the sliding effect. velocity = normal(cv + deltav) * vsize(cv); // Handle the rolling. maxroll = 65536 * 0.25; getAxes(gr, x, y, z); rollmag = int(10 * (y dot (velocity - oldv)) / delta); if (rollmag > 0) gr.roll = min(maxroll, rollmag); else gr.roll = max(65536 - maxroll, 65536 + rollmag); // Smoothly change the rotation. if (gr.roll > 32768) { if (oldroll < 32768) oldroll += 65536; } else if (oldroll > 32768) oldroll -= 65536; smoothroll = fmin(1.0, 2.0 * delta); gr.roll = gr.roll * smoothroll + oldroll * (1 - smoothroll); setRotation(gr); /** Normally an auto proxy does not move based on its physical properties like acceleration and velocity unless you tell it to. We only want to call autoPhysics() if it's a network game (where the auto proxy role is used). And, on listen servers, only if it's not the local viewport. The auto proxy role shows up on all the clients, so we can't specifically test for it. So we test netmodes, viewport (if a listen server), and pilot (for dumb fires and ejected pilots). */ // Do autonomous physics if: // We're not playing standalone and... if ((level.netmode != NM_Standalone) && // If we're a listen server, there's no viewport (remote player) and... (level.netmode != NM_ListenServer || !u.hasViewPort(instigator)) && (!instigator.isA('Bot')) && // We have a pilot. (pilot != none)) { //u.debug("moveRocket(): auto physics", DL_Verbose); autonomousPhysics(delta); } if (role == ROLE_Authority) { realLocation = location; realVelocity = velocity; realAccel = acceleration; } } /** Called on auto proxy and other clients. */ simulated function destroyed() { u.debug("destroyed()"); if (level.netmode != NM_DedicatedServer && !level.bDropDetail) spawnWreckage(); if (role == ROLE_Authority) { if (level.netmode != NM_Standalone) u.debug("destroyed(): client err/sec: " $ (errorSum / (level.timeseconds - start))); // Let the bot brain know how we died. clearRider(pilot, pInfo, dtype); clearRider(gunner, gInfo); } if (rootct != none) rootct.end(); spawnBlast(); super.destroyed(); } /** Two cases: 1. We're calling this from hitwall() - use the supplied hit location and normal. 2. On the client, destroyed() has been called before a hitwall. In the off chance (likely on a LAN) that the destroy notification arrives from the server before the hitwall is called on the client, we need to spawn our own decal. */ simulated function spawnBlast(optional bool havev, optional vector hitl, optional vector hitn) { local vector x, y, z, hl, hn; local Actor wall; u.debug("spawnBlast()", DL_Verbose); if (bBlasted || level.netmode == NM_DedicatedServer) return; if (havev) { hl = hitl; hn = hitn; } else if (role < ROLE_Authority && level.netmode != NM_Standalone) { getAxes(rotation, x, y, z); wall = trace(hl, hn, location + x * 200, location, false); if (wall == none) return; } spawn(explosionDecal, self,, hl, rotator(hn)); bBlasted = true; } simulated function spawnWreckage() { local int i; local Wreckage w; local int num; num = 5 + rand(10); for (i = 0; i < num; i++) { w = spawn(class'Wreckage',,, location, rotator(vrand())); w.eject(velocity * 1.0 + (vrand() * vsize(velocity) * 1.0)); } } // Controller methods... /** Set the gunner. */ function setGunner(Pawn g) { local string s; gunner = g; g.playSound(gunnerSound, SLOT_None, 4.0 * g.soundDampening); setupRider(g, gInfo); if (pilot != none) s = gunnerMountStr[rand(arrayCount(gunnerMountStr))]; else s = gunnerMountStr2[rand(arrayCount(gunnerMountStr2))];; broadcastMessage(u.parseKillString(s, g, pilot, true)); } /** Sets up the controller for a new pilot. Usually called just after the controller is spawned. */ function setPilot(Pawn p, optional SkyNet skynet, optional SkyNode launchsn) { u.debug("setPilot(" $ u.sname(p) $ ")"); pilot = p; // The owner is set during launch. This is for takeovers. /* if (owner != pilot) newOwner(p); instigator = p; */ setupRider(p, pInfo, skynet, launchsn); if (p.isA('PlayerPawn')) { savedBob = PlayerPawn(p).bob; PlayerPawn(p).bob = 0; // So we can fire while ducking and not go straight to shoot mode. // Uh, okay. It's a "feature" now. //bDuck = PlayerPawn(p).bDuck; } else if (p.isA('Bot')) { // Bot need to go slow. throttle = 0.1; } // Save fc for the HUD. fc = Fuelcore(p.findInventoryType(class'Fuelcore')); // We do this just to init the shoot mode stuff. bShootMode = true; goShootMode(); } function setupRider(Pawn p, out RiderInfo info, optional SkyNet skynet, optional SkyNode launchsn) { local StrangeShell sl; u.debug("setupRider(" $ u.sname(p) $ ")"); info.pawn = p; // Check if we were previously the pilot or gunner of another SL. foreach radiusActors(class'StrangeShell', sl, 250) { if (sl != self) { if (sl.pilot == p) sl.clearRider(p, sl.pInfo); if (sl.gunner == p) sl.clearRider(p, sl.gInfo); } } if (p.isA('PlayerPawn')) info.bOldJumpStatus = PlayerPawn(p).bJumpStatus; info.bDelayedEject = false; if (p.isA('Bot')) { info.brain = spawn(Class'SLBotBrain'); info.brain.setBot(Bot(p), self, skynet, launchsn); } else { info.brain = none; } info.lastLoc = vect(0, 0, 0); p.bWarping = true; //p.bCollideWorld = false; giveJD(p); } /* Not used. function SLBotBrain getBrain(Bot b) { if (b == pilot) return pInfo.brain; else if (b == gunner) return gInfo.brain; else return none; } */ /** Pilot accessor. */ simulated function Pawn getPilot() { return pilot; } /** Gunner accessor. */ simulated function Pawn getGunner() { return gunner; } // Holy crap. The client glow doesn't seem to work reliably unless all // the values are over 100. function adjustGlow() { if (pilot != none && pilot.isA('PlayerPawn')) { if (bFog) { bFog = false; PlayerPawn(pilot).clientAdjustGlow(0.2, fogReset * -1); } else { bFog = true; PlayerPawn(pilot).clientAdjustGlow(-0.2, fogReset); } } } /** This should probably be called toggleShootMode(). */ function goShootMode() { if (role < ROLE_Authority) return; u.debug("goShootMode()"); bShootMode = !bShootMode; if (pilot.isA('PlayerPawn')) { adjustGlow(); pilot.clientSetRotation(rotation); } if (bShootMode) setCollisionSize(shootCR, default.collisionHeight); else setCollisionSize(default.collisionRadius, default.collisionHeight); if (pilot != none) handleInv(false); } /** While riding the rocket you can't shoot unless you're in shoot mode, so we give you a useless 'null weapon' that doesn't really do anything. **/ function handleInv(bool ejected) { u.debug("handleInv(" $ ejected $ "): weapon:" $ u.sname(pilot.weapon) $ " last:" $ u.sname(lastWeapon)); if (bShootMode || ejected) { // I don't know in what case it's none. If they're dead? if (pilot.weapon != none) { // Hopefully this is always right. if (pilot.weapon.isA('NullWeapon')) { pilot.weapon = none; pilot.deleteInventory(nw); pilot.pendingWeapon = lastWeapon; pilot.changedWeapon(); lastWeapon = none; } else { u.err("handleInv(): Switching weapon back, but current is not a null."); } } } else { // Store the previous weapon. lastWeapon = pilot.weapon; if (nw == none) nw = createNullWeapon(); // Give it to them. nw.giveTo(pilot); pilot.pendingWeapon = nw; //pilot.weapon.gotoState('DownWeapon'); pilot.changedWeapon(); //pilot.weapon = nw; u.debug("handleInv(): new weapon:" $ pilot.weapon $ " state:" $ pilot.weapon.getStateName() $ " pending:" $ pilot.pendingWeapon); // Fake the ammo display. if (pilot.weapon.isA('NullWeapon')) NullWeapon(pilot.weapon).fakeAmmo(); } } /** We don't need to check for arena mutators anymore. Mine takes care of it - screw everyone else's. :) */ function NullWeapon createNullWeapon() { local NullWeapon nw; nw = spawn(class'NullWeapon', self); nw.sl = self; return nw; } simulated function addHUD(PlayerPawn pp) { local SLHUD slh; // This is effectively Mutator.registerHUDMutator(). if (pp.myHUD != none) { u.debug("addHUD(" $ u.sname(pp) $ "): hud:" $ pp.myHUD); slh = spawn(HUDClass, self); } addedHUD = true; } /** This is the old controller tick(). */ simulated function ctick(float delta) { //u.debug("ctick(): loc: " $ u.sv(location) $ " ticked: " $ (bTicked == level.bTicked) $ " pilot: " $ u.sname(pilot) $ " gunner: " $ u.sname(gunner), DL_Normal, 5.0); if (gunner != none) { if (slc == none && level.netmode == NM_Client && u.hasViewPort(gunner)) slc = spawn(Class'Controller', self); } if (pilot != none) { if (u.hasViewPort(pilot) && bRevY && !bFlipY) { PlayerPawn(pilot).bInvertMouse = !(PlayerPawn(pilot).bInvertMouse); bFlipY = true; } // -10 is a fudge factor... if (!bWahooPlayed && vsize(velocity) >= (minspeed - 10)) { playSound(sndWahoo, SLOT_None, 3.0); bWahooPlayed = true; } // Here so client will get this call. if (!addedHUD && u.hasViewPort(pilot)) addHUD(PlayerPawn(pilot)); } if (role == ROLE_Authority) { handleEjects(pilot, pInfo); handleEjects(gunner, gInfo); // Zone pain. We should add our own pain zone types. if (u.legacy() && region.zone.bPainZone) { painTimer += delta; if (painTimer > 1.0) { takeDamage(region.zone.damagePerSec, instigator, location, vect(0, 0, 0), 'ZonePain'); painTimer -= 1.0; } } } // The dedicated server receives goShootMode() calls from the client. if (level.netmode != NM_DedicatedServer && pilot != none && pilot.isA('PlayerPawn')) { if (PlayerPawn(pilot).bDuck != bDuck) { PlayerPawn(pilot).bDuck = 0; bDuck = 0; u.debug("tick(): calling goShootMode()"); goShootMode(); } } if (pilot != none) adjustThrot(delta); updateSound(delta); if (bAfterburn && gunner != none && PlayerPawn(gunner) != none) { PlayerPawn(gunner).shakeView(delta, 400, 7.5); } if (role >= ROLE_AutonomousProxy) accelerate(delta); // Limit this to being executed on pilot and gunner clients only? FIX. // This is broken. Can't switch owners and auto proxies on the fly. //takeover(delta); burnFuel(delta); // Last but not least, the heart and soul... updateRiders(); } function handleEjects(Pawn p, RiderInfo info) { if (p != none) { if (p.health <= 0) { clearRider(p, info, 'Shot'); } else if (teleported(info)) { clearRider(p, info); } else if (info.bDelayedEject) { eject(p); } else if (level.netmode != NM_Standalone && jumped(p, info)) { eject(p); } } } /** A general hack to allow teleportation effects (e.g. translocator). To account for warp zones we must make sure they are far from their last location AND far from the rocket. On slow computers, or servers with slomo jacked up, this may cause problems. May need to raise teleportDist in those cases. FIX? */ function bool teleported(RiderInfo info) { if ((vsize(info.lastLoc) != 0) && (vsize(info.pawn.location - info.lastLoc) > teleportDist) && (vsize(info.pawn.location - location) > teleportDist)) { u.debug("teleported(): delta: " $ vsize(info.pawn.location - location)); return true; } return false; } /** Returns true in network games if they've jumped. */ function bool jumped(Pawn p, RiderInfo info) { return (p != none && p.isA('PlayerPawn') && PlayerPawn(p).bJumpStatus != info.bOldJumpStatus); } /** If something happens to the pilot, the gunner will takeover the controls in 5 seconds. simulated function takeover(float delta) { //local Pawn save; if (pilot == none && gunner != none) { if (!bTakeover) { u.debug("takeover(): initiating takeover"); bTakeover = true; toTime = 5.0; if (PlayerPawn(gunner) != none) { addHUD(PlayerPawn(gunner)); } } else { toTime -= delta; if (toTime <= 0.0) { u.debug("takeover(): takeover: toTime: " $ toTime); if (role == ROLE_Authority) { setPilot(gunner); // Return to normal sound. No alarm. ambientSound = default.ambientSound; // Only on server. Client waits for data to propagate. bTakeover = false; setRemoteRole(pilot); } } } } else { bTakeover = false; } } */ /** d == 0 means this we're switching modes. */ simulated function updateSound(float d) { // The drop sound overrides all others. We set it manually in eject(). if (ambientSound == sndDrop) { soundRadius = 64.0; soundVolume = 255; soundPitch = 64.0; return; } if (updateMarch(d)) return; // Adjust ambient pitch and volume. soundPitch = 24 + 40 * (vsize(velocity) / maxspeed); if (bNoFuel) { if (d == 0) playSound(abFadeSound, SLOT_None, 6.0); ambientSound = none; } else if (bAfterburn) { if (d == 0) playSound(abKickSound, SLOT_None, 2.0); ambientSound = abLoopSound; soundRadius = 128; soundVolume = 255; } else { if (d == 0) playSound(abFadeSound, SLOT_None, 2.0); soundRadius = 32; soundVolume = Min(255, 150 + 55 * (vsize(velocity) / maxspeed)); ambientSound = default.ambientSound; } } /** Decide when to play the "total commitment" march loop. */ simulated function bool updateMarch(float delta) { local int shields; local Inventory i; if (pilot == none) return false; // Calculate the shields. shields = 0; for (i = pilot.inventory; i != none; i = i.inventory) { if (i.bIsAnArmor) { shields += i.charge; } } if ((pilot.health >= 199) && (shields >= 150) && (armor >= default.armor)) { if (bMarchUnder) { bMarchPlaying = true; // Wait for 4.5 seconds and then play the "total commitment" sound once. if (marchWait != -1.0) { if (marchWait > 4.5) { playSound(sndTotalComm, SLOT_None, 1.0); marchWait = -1.0; } else { marchWait += delta; } } ambientSound = sndMarchLoop; soundVolume = 255; soundPitch = 64; // We're marching! return true; } } else { bMarchUnder = true; marchWait = 0.0; bMarchPlaying = false; } return false; } /** Loop for updating trailers. */ simulated function getTrailers() { if (role == ROLE_AutonomousProxy || level.netmode == NM_StandAlone) { findTrailers(pilot, pInfo); findTrailers(gunner, gInfo); } } simulated function findTrailers(Pawn p, out RiderInfo info) { local Actor t; local int i; u.debug("findTrailers(" $ u.sname(p) $ ")", DL_Verbose); i = 0; info.trailers[i] = none; if (p != none) { foreach p.childActors(class'Actor', t) { if (t.physics == PHYS_Trailer) { info.trailers[i] = t; i++; } // We can only hold so many... if (i == arrayCount(info.trailers)) break; } } } /** Set the acceleration based on the throttle level. */ simulated function accelerate(float delta) { local float max, amax; local float desired, speed; if (bAfterburn) max = maxspeed; else max = fullspeed; // Both the pilot and gunner add drag and reduce the max speed. if (pilot != none) max -= 100; if (gunner != none) max -= 100; speed = vsize(velocity); desired = lerp(fmin(throttle, 1.0), minspeed, max); amax = accelmax; if (desired < speed) // No reverse jets! amax *= -0.5; else if (bAfterburn) amax *= 1.5; // If we're close, just zero it. if (abs(speed - desired) < 0.5) acceleration = vect(0, 0, 0); else // Within 50, slow down. acceleration = vector(rotation) * (fmin(abs(speed - desired) / 50.0, 1.0) * amax); //u.debug("accelerate(): new accel: " $ u.sv(acceleration) $ " des/curr: " $ u.chopf(desired) $ "/" $ u.chopf(speed), DL_Verbose); } /** Called on the client to determine what's up with the throttle. Adjustments are sent to the server with clientSendThrot() when let go. */ simulated function adjustThrot(float delta) { if (pilot.isA('PlayerPawn') && ViewPort(PlayerPawn(pilot).player) != none && !bAfterburn) { if ((PlayerPawn(pilot).bWasForward || throtDir == 1) && thlvl < 1.0) { thlvl = FMin(thlvl + delta * thrate, 1.0); bThrotSent = false; } else if ((PlayerPawn(pilot).bWasBack || throtDir == -1) && thlvl > 0.0) { thlvl = FMax(thlvl - delta * thrate, 0.0); bThrotSent = false; } else { // Did they let go? if (!bThrotSent) { //if (role < ROLE_Authority) { clientSendThrot(thlvl); //} else { //throttle = thlvl; //} bThrotSent = true; } } if (thlvl == 0.0 || thlvl == 1.0) throtDir = 0; } } /** Client sends the adjusted throttle result to the server. */ function clientSendThrot(float thr) { u.debug("clientSendThrot(" $ thr $ ")"); // Make sure they're sending a valid throttle. throttle = FClamp(thr, 0.0, 1.0); } /** This updates the status and positions of the riders. */ simulated function updateRiders() { local vector x, y, z, loc; // Minimum safe distance. local float d, mx, my, th; // This positions the rider slightly behind and above the // rocket's center, so it looks between their legs. getAxes(rotation, x, y, z); loc = location; if (pilot != none) { // Hmm, this sorta works. FIX. if (pilot.isA('PlayerPawn') && PlayerPawn(pilot).isInState('FeigningDeath')) { //PlayerPawn(pilot).rise(); PlayerPawn(pilot).gotoState('PlayerWalking'); PlayerPawn(pilot).weapon = nw; } loc += x * pilotOffset.x; loc += y * pilotOffset.y; loc += z * pilotOffset.z; // Adjust z for speed. Hug the rocket. loc -= z * (10 * vsize(velocity) / maxspeed); // Brain controls the rotation, not the bot. // CSHP - Only update rotation if the authority. if (pilot.isA('Bot') && !bShootMode && role == ROLE_Authority) { pilot.clientSetRotation(rotation); } updateRider(pilot, pInfo, loc); } // The closest way to pack two vertically oriented cylinders along // a line, depending on the the angle of the line: // // case 1: side by side (x is fixed - mx) // case 2: stacked (y is fixed - my). // // We have theta, solve for the hypotenuse. if (gunner != none) { if (pilot != none) { loc = pilot.location; // Min x and y distances. mx = (pilot.collisionRadius + gunner.collisionRadius); my = (pilot.collisionHeight + gunner.collisionHeight); th = rotation.pitch / 10430.2192; if ((mx * tan(th)) < my) { // Side by side. d = mx / cos(th); } else { // Stacked. d = my / sin(th); } d += 1; // FUDGE FACTOR loc += x * -d; } else { // Just use the gunner's collision radius to guesstimate the pilot's. loc = location; loc += x * (pilotOffset.x - gunner.collisionRadius); loc += y * pilotOffset.y; loc += z * pilotOffset.z; } updateRider(gunner, gInfo, loc); } } simulated function updateRider(Pawn p, RiderInfo info, vector loc) { local EPhysics phys; // In order to intercept jump commands (with JumpDetector), we // have to set this to walking. Only for standalone games. if (level.netmode == NM_Standalone) { phys = PHYS_Walking; } else { phys = PHYS_None; } info.lastLoc = p.location; p.setLocation(loc); //p.move(loc - p.location); p.velocity = velocity; updateTrailers(p, info); p.setPhysics(phys); } /** We need to update any trailers because we just moved their owner. We don't need to update the trailer if it hasn't been ticked yet, though. */ simulated function updateTrailers(Pawn p, RiderInfo info) { local int i; for (i = 0; i < arrayCount(info.trailers) && info.trailers[i] != none; i++) if (info.trailers[i] != none) // && info.trailers[i].bTicked) info.trailers[i].autonomousPhysics(0); } /** Ejects somebody. In standalone games we have to suppress the jump velocity to keep the behavior consistent with net games. We can't do that here because it's set in PlayerPawn.doJump() *after* Inventory.ownerJumped() is called. So we wait one tick and do the eject which resets the velocity. */ function eject(Actor a, optional int delay, optional bool bForced) { local vector x, y, z, loc; u.debug("eject(" $ u.sname(a) $ ")", DL_Normal); if (a != none) { if (delay > 0) { u.debug("eject(): delaying one tick", DL_Normal); if (a == pilot) pInfo.bDelayedEject = true; else gInfo.bDelayedEject = true; } else { // We aren't setting the velocity on eject // anymore. Every tick. // In standalone games, the rider has PHYS_Walking // set, which limits their velocity. We need to reset // it here, and the velocity. if (level.netmode == NM_Standalone) { resetPhysics(Pawn(a)); a.velocity = velocity; } // Add a little sideways dismount motion. getAxes(a.rotation, x, y, z); a.velocity += y * 100; if (a == pilot) { if (!bForced) { ambientSound = sndDrop; a.playSound(sndPilotEj, SLOT_None, 4.0 * Pawn(a).soundDampening); // We're ejecting. Time to arm the donuts. autoArm(); } clearRider(pilot, pInfo); } else { // Gunner. if (!bForced) a.playSound(sndGunnerEj, SLOT_None, 4.0 * Pawn(a).soundDampening); clearRider(gunner, gInfo); } } } } /** Removes the pilot. Normally called from eject(). If called from StrangeShell.destroy(), we are passed the damage type when we were destroyed. */ function clearRider(Pawn p, RiderInfo info, optional name dtype) { if (p == none) return; u.debug("clearRider(" $ u.sname(p) $ ")"); if (p == pilot) { handleInv(true); if (pilot.isA('PlayerPawn')) { PlayerPawn(pilot).bob = savedBob; if (!u.hasViewPort(pilot) && bFlipY) { PlayerPawn(pilot).bInvertMouse = !(PlayerPawn(pilot).bInvertMouse); bFlipY = false; } } // If they're in shoot mode, let them keep the same rotation. if (!bShootMode) pilot.clientSetRotation(rotation); // Reset the fog is it's on. Might not be if we're in shoot mode. if (bFog) adjustGlow(); // Hmm, iffy. Need to unset the owner so we can get the "real" data. newOwner(none); remoteRole = ROLE_SimulatedProxy; pilot = none; } else if (p == gunner) { gunner = none; } else { u.err("clearRider(): Tried to clear a non-rider!"); return; } if (p.isA('Bot')) { if (info.brain != none) { if (dtype != '') info.brain.deathBy(dtype); info.brain.destroy(); } p.bCanFly = false; p.bCanWalk = true; } p.bWarping = false; //p.bCollideWorld = true; resetPhysics(p); clearJD(p); storeEject(p); } /** Reset a pawn's physics based on what type of zone it's in. */ function resetPhysics(Pawn a) { local EPhysics phys; if (a.region.zone.bWaterZone) { phys = PHYS_Swimming; } else { phys = PHYS_Falling; } a.setPhysics(phys); } /** Only used for standalone games. Gives the pawn an inventory item which detects jumps and calls eject. Other netmodes use bJumpStatus. */ function giveJD(Pawn p) { local JumpDetector jd; if (p.isA('PlayerPawn') && (level.netmode == NM_Standalone || (level.netmode == NM_ListenServer && u.hasViewPort(p)))) { u.debug("giveJD(" $ u.sname(p) $ ")"); jd = spawn(class'JumpDetector', self); jd.giveTo(p); jd.sl = self; } } /** Clears the jump detector. */ function clearJD(Pawn p) { local Inventory i; u.debug("clearJD(" $ u.sname(p) $ ")"); if (level.netmode == NM_Standalone) { i = p.inventory; while (i != none) { if (i.isA('JumpDetector')) { i.destroy(); return; } i = i.inventory; } } } simulated function bool sameTeam(Pawn p, Pawn p2) { return (p != none && p2 != none && level.game.isA('TeamGamePlus') && p.bIsPlayer && p2.bIsPlayer && p.playerReplicationInfo.team == p2.playerReplicationInfo.team); } // end T> // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: StrangeMutator.uc,v 1.6 2001/07/29 23:58:33 yrns Exp $ /** "Strangelove V2" mutator. Replaces the Redeemer with Konglauncher. Optionally can replace medboxes and/or health vials with fuelpods. */ class StrangeMutator extends SLMutator config (SLV2); // These are set in the client options window. var() config bool bReplacePacks; var() config bool bReplaceVials; var() config bool bRandomPlacement; /** Take out the Yellow Jacket? */ var() config bool bNoYJ; /** Max ammo, like in the arena mutator? */ var() config bool bMaxAmmo; var() string yjStr, yjAmmoStr; // This is the maximum distance from the flag we'll spawn. var() int randSpawnDist; var bool bExpired; function preBeginPlay() { bExpired = Class'Util'.static.expiredBeta(level); super.preBeginPlay(); } function postBeginPlay() { if (!bExpired) { // This maintains the velocity of projectiles launched from a Strangelove. spawn(Class'ProjectileSN'); // Wait ten seconds. Hrm, let's try this... setTimer(0.1, false); } super.postBeginPlay(); } function timer() { if (bRandomPlacement) spawnRandSL(randSpawnDist); } function spawnRandSL(int dist) { local FlagBase fb; local PathNode pn; local int num, spawnindex; foreach AllActors(Class'FlagBase', fb) { num = 0; foreach RadiusActors(Class'PathNode', pn, dist, fb.location) { num++; } // Pick a random node and spawn it at that point. spawnindex = rand(num); num = 0; foreach RadiusActors(Class'PathNode', pn, dist, fb.location) { if (spawnindex == num) { spawn(Class'Konglauncher',,, pn.location); break; } num++; } } } function modifyPlayer(Pawn o) { if (level.game.isA('DeathMatchPlus') && !bNoYJ) { DeathMatchPlus(level.game).giveWeapon(o, yjStr); } } function bool alwaysKeep(Actor o) { if (bExpired) return false; if (bMaxAmmo && o.isA('SLWeapon')) SLWeapon(o).maxOut(); /* Hmm, now that might be rude. if (o.isA('Konglauncher')) return true; */ return super.alwaysKeep(o); } function bool checkReplacement(Actor o, out byte bSuperRelevant) { if (bExpired) return true; if (!bNoYJ) { if (o.isA('Enforcer')) { // Chances are it's default inventory if its loc is 0. if (o.location != vect(0, 0, 0)) replaceWith(o, yjStr); return false; } /* Why not? FIX. if (o.isA('EClip')) { replaceWith(o, yjAmmoStr); return false; } */ } if (o.isA('WarheadLauncher')) { replaceWith(o, "SLV2.Konglauncher"); return false; } if ((bReplaceVials && o.isA('HealthVial')) || (bReplacePacks && o.isA('MedBox'))) { replaceWith(o, "SLV2.Fuelcore"); return false; } return true; } // Added rotation rate fix. Otherwise exactly the same as the super. function bool ReplaceWith(actor Other, string aClassName) { local Actor A; local class aClass; if ( Other.IsA('Inventory') && (Other.Location == vect(0,0,0)) ) return false; aClass = class(DynamicLoadObject(aClassName, class'Class')); if ( aClass != None ) A = Spawn(aClass,,Other.tag,Other.Location, Other.Rotation); if ( Other.IsA('Inventory') ) { if ( Inventory(Other).MyMarker != None ) { Inventory(Other).MyMarker.markedItem = Inventory(A); if ( Inventory(A) != None ) { Inventory(A).MyMarker = Inventory(Other).MyMarker; A.SetLocation(A.Location + (A.CollisionHeight - Other.CollisionHeight) * vect(0,0,1)); } Inventory(Other).MyMarker = None; } else if ( A.IsA('Inventory') ) { Inventory(A).bHeldItem = true; Inventory(A).Respawntime = 0.0; } } if ( A != None ) { A.event = Other.event; A.tag = Other.tag; // All that for this! A.rotationRate = Other.rotationRate; return true; } return false; } // end {T@ O9:T.- { // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: StrangeExpl.uc,v 1.7 2001/08/28 07:24:59 yrns Exp $ /** Explosion object. */ class StrangeExpl extends AnimSpriteEffect config (SLV2); var() Texture booms[2]; var() config bool bNoBlack; simulated function postBeginPlay() { local DamagePuff puff; super.postBeginPlay(); if (!level.bHighDetailMode) drawscale = 1.9; //playSound(effectSound1,, 12.0,, 3000); playSound(effectSound1, SLOT_None, 1.0); drawScale = default.drawScale + (frand() * 1.0); texture = booms[rand(arrayCount(booms))]; if (!bNoBlack && !level.bDropDetail) { puff = spawn(Class'DamagePuff'); puff.drawScale = drawScale * 3.0; } } // end FNZEN%E.VrE* c @jZE?rE*=E E ? )b$E E IprateSelf(): enemy past det range: E@ X` y9,`?@-O 'a #<( ` [X G ?` X U-::$@a H w@*@>>@JX  PCtZlY  `) // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: StrangeArena.uc,v 1.6 2001/07/29 23:58:33 yrns Exp $ /** "Strangelove V2 Arena" mutator object. Replaces all weapons with Konglaunchers and all ammo with fuelpods. Except the default weapon is a sidearm and mini ammo becomes sidearm clips. */ class StrangeArena extends Arena; var bool bExpired; var bool bNoYJ; var Util u; function preBeginPlay() { bExpired = class'Util'.static.expiredBeta(level); if (bExpired) { weaponName = 'WarHeadLauncher'; ammoName = 'WarHeadAmmo'; weaponString = "BotPack.WarHeadLauncher"; defaultWeapon = Class'BotPack.WarHeadLauncher'; } u = spawn(Class'Util', self); // Get "no Yellow Jacket" from the StrangeMutator. bNoYJ = class'StrangeMutator'.default.bNoYJ; if (!bNoYJ) defaultWeapon = Class'SLV2.Sidearm'; /* level.game.damageMutator = self; level.game.messageMutator = self; */ spawn(Class'ProjectileSN'); super.preBeginPlay(); } function bool alwaysKeep(Actor other) { if (!bExpired) { if (other.isA(weaponName) || (!bNoYJ && other.isA('Sidearm'))) { if (other.isA('SLWeapon')) SLWeapon(other).maxOut(); return true; } // These are never spawned but for inventory. if (other.isA('StrangeAmmo')) { return true; } if (!bNoYJ && other.isA('SidearmAmmo')) { Ammo(other).ammoAmount = Ammo(other).maxAmmo; return true; } // Fuel core. Keep these, but don't fill them up to max. if (other.isA(ammoName)) return true; } if (nextMutator != none) return (nextMutator.alwaysKeep(other)); return false; } function bool checkReplacement(Actor other, out byte bSuperRelevant) { if (!bExpired) { if (other.isA('Weapon')) { if (other.isA('NullWeapon') || (!bNoYJ && other.isA('Sidearm'))) return true; if (!bNoYJ && other.isA('Enforcer')) { replaceWith(other, "SLV2.Sidearm"); return false; } // Else, replace it with a Konglauncher. if ((weaponString != "") && !other.isA(weaponName)) { // From Botpack.Arena. ... ? level.game.bCoopWeaponMode = false; replaceWith(other, weaponString); return false; } } if (other.isA('Ammo')) { // Replace all UT firearms ammo with sidearm clips. if (!bNoYJ && (other.isA('MiniAmmo') || other.isA('BulletBox'))) { replaceWith(other, "SLV2.SidearmAmmo"); return false; } else if (!other.isA(ammoName)) { replaceWith(other, ammoString); return false; } } } bSuperRelevant = 0; return true; } // end Z \c`/-O (a(Z c EY `U #8+-O b-U ad ?,U ??%afff?D #<(Y  pV L:2 s t w s/ a8 V ;E 'nsV s t w 10/ a8 W ;E 'sW s t w 10 x_Bu e$?dԱԝXԝXԝXԝXԝXԝXԝXԝXԝXԝXԝXԝX ԃԝXԝXѸBѸBԝX=$:e㼗m {u NimBR]SLV2.KonglauncherU]SLV2.Fuelcore_,} // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: StrangeAmmo.uc,v 1.3 2001/06/30 22:37:49 yrns Exp $ /** Odd, invisible replacement ammo you never see. */ class StrangeAmmo extends SLAmmo; // end Fux..#.SLV2Fonts.leg8   HLx5CH?,66aA?( hM ML CS C&_VCWg-hLS gh?6h?g ?A&yd_d_d_dv S d_dv S   ? ^ % tGH Mw5kiw.G*f%^f,2Tf.GZ =f fG GIW)la!tq!t pst<0 ^ IrJ 5 C>^?^J?^?Aa Lol/2-@ 1a/!ZGOLw *<I wqr.w< Z // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SpentClip.uc,v 1.3 2001/06/28 05:34:32 yrns Exp $ /** Empty clip. */ class SpentClip extends EjectedBrass; // end d // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLWeapon.uc,v 1.22 2001/08/29 02:18:25 yrns Exp $ /** Strangelove weapon superclass. */ class SLWeapon extends TournamentWeapon abstract; // DATA // var bool bFirearm; // Uses firing modes, etc. var() int hitdam; // Hit damage for firearms. var SLClip clip; // Current clip, if ammo().bClipped. var() bool bFakeReload; // There's no reload anim. Use down/select. //var SLClipFix clipfix; // Our clip fix HUD mutator. var() Class brassClass; var() Class wallhitClass; var bool bSetProjOwner; // If true, the owner will be set on new projectiles. var Projectile lastProj; // Keep track of the last projectile fired. var vector spawnProjOffset; // Optional projectile offset, replaces fireOffset/calcDrawOffset(). enum EFireMode { FM_Single, FM_Burst, FM_Auto, }; var EFireMode fireMode; // 0=single, 1=burst, 2=auto var() bool bAltSwitchMode; // Alt-fire changes the fire mode. var() int rpm; // Rounds per minute. var() float burstWait; // How long we sleep after a burst. var() int burstRounds; // # of rounds in a burst. var() int burstCount; // Burst counter. var bool bClear; // Can refire while in a burst cycle? var bool bInitFA; // See clientFire(). // Recoil data. struct RecoilInfo { var float base; // The base recoil increase per shot. var float limit; // Max recoil in any direction. var float kick; // Additional pitch factor. See recoil(). var float returnRate; // Rate at which recoil is reduced per sec. var float smoothRate; // Rate at which recoil is smoothed out. var float pvoScale; // Produces PVO offset due to recoil. //var float pitchScale; // Produces pitch offset due to recoil. var float accScale; // Scales the recoil to produce an accuracy figure for traceFire(). }; var() RecoilInfo recoilData; var vector recoilv; // Current recoil. var vector smoothrv; // Current, smoothed recoil. var bool bAlt; // This affects reuse of functions for alt-fire. // Akimbo config. var() bool bAkimbo; // Can be used akimbo? var SLWeapon slave; // The slave weapon. var SLWeapon master; // The master weapon (only set on slave). var bool bSlave; // Is this weapon a slave? var bool bSetup; // Used for setting display props. var bool bBringingUp; // Brings up the slave on the client. var() localized string slaveName; // The name of the slave weapon. var() byte akimboSwitchPriority; // Switch priority of akimbo pair. var() string leftMesh; // The other mesh. var() float slaveWait; var() float slaveAltWait; var() float slaveAcc; // Accuracy drop for akimbo weapons. /** Animation and Sound Struct (tm). :) Useful for putting all anim and sound info in one place, and not having to override a bunch of functions. */ struct ASS { var() name aname; var() float arate; var() float tween; var() Sound sound; var() float volume; var() ESoundSlot slot; var() bool bSkipOwner; }; var() ASS assFire; var() ASS assAltFire; var() ASS assSelect; var() ASS assDown; var() ASS assReload; var() ASS assIdle; var() ASS assEmpty; var() ASS assMode; /** Idle properties. */ var() float idleLapse; var() float idleRandom; var() Texture xhair; /** This weapon has no "center" handedness. Either because it has no center model (faces removed on left/right), or we specifically don't want it because this weapon can be used akimbo. Default is true. */ var() bool bNoCenter; var() Texture MFVariations[6]; /** The max amount the gun will slide depending on pitch. */ var() float paSlide; /** These are similar to flashY/O except these values are applied at a sliding amount, depending on the pitch adjustment. See pitchAdjust(). */ var() float paFlashY, paFlashO; /** Used to mimic another weapon's switch priority. See setSwitchPriority(). Also used to set autoSwitchPriority in postBeginPlay(). */ var() name mimicPriorityOf; // Info that appears next to the reticule. var float retbar; var string retmsg; var string retmsg2; var string retmsg3; var Font retFont; var Stylus yy; var Util u; // REPLICATION // replication { reliable if (role == ROLE_Authority && bNetOwner && bAkimbo) slave, master, bSlave, bBringingUp; reliable if (role == ROLE_Authority && bNetOwner) clip; reliable if (role == ROLE_Authority) remoteReload, updateMaxAmmo, fireMode; } // METHODS // /** Starts fully loaded. */ simulated function postBeginPlay() { local Class c; u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); u.debug("postBeginPlay()"); // Set switch priorities from mimic weapon. if (mimicPriorityOf != '') { c = Class(dynamicLoadObject("Botpack." $ mimicPriorityOf, Class'Class')); if (c != none) { autoSwitchPriority = c.default.autoSwitchPriority; // The default is -1, which means if we don't specify it // in the subclass, it gets assigned here to the auto value. if (bAkimbo && akimboSwitchPriority == -1) akimboSwitchPriority = autoSwitchPriority; } } u.loadLeftMeshes(); if (role < ROLE_Authority) return; super.postBeginPlay(); } function destroyed() { if (slave != none) slave.destroy(); super.destroyed(); } /** Wrapper to get ammoType as a SLAmmo object. */ simulated function SLAmmo ammo() { if (ammoType == none) giveAmmo(Pawn(owner)); return SLAmmo(ammoType); } function bool useAmmo(int num) { local SLAmmo a; a = ammo(); if (a.bClipped) { return (clip != none && clip.useRound()); } else { return a.useAmmo(num); } } /** This is where the client actually gets the ammoType, so let's update the maxAmmo, if necessary. */ function giveAmmo(Pawn p) { local int max; local bool bUpdate; if (ammoName == none) return; max = Class(ammoName).default.configMaxAmmo; bUpdate = (ammoType == none && max > 0); //super.giveAmmo(p); ammoType = Ammo(p.findInventoryType(ammoName)); if (ammoType == none) { ammoType = spawn(ammoName); p.addInventory(ammoType); ammoType.becomeItem(); ammoType.gotoState('Idle2'); ammo().weaponSpawned(); } /* Normally the pickup count for a new, clipped weapon is 1. Nothing else really makes sense unless you're running an arena mutator which pumps up the count. The following code allows up to start the weapon loaded and max out the clips in that situation. */ if (ammo().bClipped) { clip = ammo().defaultClip(); pickupAmmoCount--; } ammoType.addAmmo(pickupAmmoCount); if (bUpdate) updateMaxAmmo(max); } /** This is called from StrangeArena to give us max ammo. This is the only place that maxAmmo is used as a class default var. We need to take into account configMaxAmmo. */ function maxOut() { local int max; max = Class(ammoName).default.configMaxAmmo; if (max > 0) { pickupAmmoCount = max; } else { pickupAmmoCount = Class(ammoName).default.maxAmmo; } } /** Server can update max ammo on the client. */ simulated function updateMaxAmmo(int max) { if (ammoType != none) ammoType.maxAmmo = max; ammoName.default.maxAmmo = max; } /** Returns the total ammo, including clip rounds. */ simulated function int getAmmo() { return ammo().getAmmo(); } simulated function int getMaxAmmo() { return ammo().getMaxAmmo(); } /** Do we have any ammo left, clipped or otherwise? */ simulated function bool ammoLeft() { return (getAmmo() > 0); } /** Returns true if the clip is empty, but we have ammo left. */ simulated function bool needReload() { return (ammo().bClipped && clip.empty() && ammoLeft()); } /** This is based on the enforcer botDesireability(). It supports akimbo weapons. */ event float botDesireability(Pawn bot) { local SLWeapon has; local float desire; has = SLWeapon(bot.findInventoryType(class)); desire = maxDesireability + bot.adjustDesireFor(self); if (has != none) { if ((!bHeldItem || bTossedOut) && bWeaponStay) return 0; if (has.slave != none) { if ((respawnTime < 10) && (bHidden || (has.ammoType == none) || (has.getAmmo() < has.getMaxAmmo()))) return 0; // WTF does this mean? We haven't equipped it yet? if (has.ammoType == none) return 0.25 * desire; if (has.getAmmo() > 0) return fmax( 0.25 * desire, has.ammoType.maxDesireability * fmin(1, 0.15 * has.getMaxAmmo() / has.getAmmo()) ); } } if ((bot.weapon == none) || (bot.weapon.AIRating <= 0.4)) return 2 * desire; return desire; } /** Called from the clip fix, after drawWeapons() has been called in ChallengeHUD. Or before, if bSet == true. simulated function ammoRenderFix(bool bSet) { if (bSet) { ammo().setRenderMax(clip, bSlave); } else { ammo().unsetRenderMax(clip, bSlave); } } */ /** Sometimes the owner isn't set (initial network transfers). This is called before drawWeapons() in ChallengeHUD. */ simulated function postRender(Canvas c) { local ChallengeHUD hud; /* if (clipfix != none) { clipfix.register(); clipfix.runFix(true); } */ if (owner != none) { hud = ChallengeHUD(PlayerPawn(owner).myHUD); if (hud != none) { c.bNoSmooth = false; if (!bSlave) drawXhair(hud, c); setRetMsgs(); drawRetMsgs(c, 15 * hud.crosshairColor); if (slave != none) slave.postRender(c); } } } simulated function drawRetMsgs(Canvas c, Color color) { local int x, y, dist; local float w, h, xl, yl; // Nothing to display. if (retmsg == " ") return; if (yy == none) yy = spawn(Class'Stylus', self); yy.setCanvas(c); // Distance from ret. dist = 48; if (bSlave) dist = 56; if (retFont == none) retFont = class'Fontlib'.static.getFont(FS_Slick); if (retFont == none) return; c.font = retFont; c.style = ERenderStyle.STY_Translucent; c.drawColor = color; c.textSize(retmsg, xl, yl); w = xl; h = yl; c.textSize(retmsg2, xl, yl); w = fmax(w, xl); w = fmax(w, 50); if (hand() < 0) { x = c.clipx / 2.0 + dist; } else { x = c.clipx / 2.0 - (dist + w); } y = c.clipy / 2.0 - 20; if (retbar >= 0.0) { //c.drawColor = color * 0.7; yy.drawFrame(x, y, w + 2, yl + 2, 1, false); c.setPos(x + 1, y + 1); c.drawColor = color * 0.8; yy.pix(w * retbar, yl); } c.drawColor = color; c.setPos(x + 2, y + 2); c.drawText(retmsg); c.setPos(x + 2, y + yl + 3); c.drawText(retmsg2); if (retmsg3 != " ") { c.textSize(retmsg3, xl, yl); c.setPos(x + w - xl + 1, y + 2); c.drawText(retmsg3); } } simulated function setRetMsgs() { local SLAmmo a; retmsg = " "; retmsg2 = " "; retmsg3 = " "; a = ammo(); // Client doesn't have ammo yet. if (a == none || (a.bClipped && clip == none)) return; if (a.bClipped) { retbar = (float(clip.getRounds()) / clip.getMaxRounds()); retmsg = "rnds " $ clip.getRounds(); // If we're a a slave, only report the rounds. if (!bSlave) retmsg2 = "clips " $ a.getClips(); } else { retbar = (float(getAmmo()) / getMaxAmmo()); retmsg = "rnds " $ getAmmo(); } if (bFirearm && !bSlave) { switch (fireMode) { case FM_Single: retmsg3 = "semi"; break; case FM_Burst: retmsg3 = "burst"; break; case FM_Auto: retmsg3 = "auto"; break; } } if (isInState('Reloading')) { retmsg = "reloading"; // Otherwise, it'll overlap. retmsg3 = " "; } } simulated function drawXhair(ChallengeHUD hud, Canvas c) { local float scale, w, pickd; if (xhair == none) return; if (c.clipx < 512) scale = 0.5; else scale = fmax(1, int(0.1 + c.clipx / 640.0)); pickd = level.timeSeconds - hud.pickupTime; if (pickd < 0.4) { if (pickd < 0.2) scale *= (1 + 5 * pickd); else scale *= (3 - 5 * pickd); } scale += fmin(1.0, vsize(recoilv) / 1000.0); w = 64.0 * scale; if (PlayerPawn(owner).handedness == -1) c.setPos(0.503 * (c.clipx - w), 0.504 * (c.clipy - w)); else if (PlayerPawn(owner).handedness == 1) c.setPos(0.497 * (c.clipx - w), 0.496 * (c.clipy - w)); else c.setPos(0.5 * (c.clipx - w), 0.5 * (c.clipy - w)); c.style = ERenderStyle.STY_Translucent; c.drawColor = 15 * hud.crosshairColor; c.drawTile(xhair, w, w, 0, 0, 64, 64); } function bool weaponSet(Pawn p) { u.debug("weaponSet(" $ u.sname(p) $ ")"); if (bSlave) return false; else super.weaponSet(p); } /** This is called when we want to know which weapon we prefer. Usually returns the autoSwitchPriority. Based on Weapon.uc and enforcer.uc. */ function float switchPriority() { local int temp; // Never switch to the slave. if (bSlave) return -10; // Bots have their own scheme. if (!owner.isA('PlayerPawn')) return rateSelf(temp); if (ammoLeft()) { if (slave != none) return akimboSwitchPriority; } else { // Keeps us from switching through weapons without ammo. if (Pawn(owner).weapon == self) return -0.5; else return -1; } return autoSwitchPriority; } /** This is called when a weapon is spawned (in spawnCopy()). Most weapons look for a priority and set it, and if there's none, they insert one. We just want to mimic a UT weapon priority and leave it at that, for now. */ function setSwitchPriority(Pawn p) { local int i; if (PlayerPawn(p) != none) { for (i = 0; i < ArrayCount(PlayerPawn(p).weaponPriority); i++) { // Normally this is class.name. if (PlayerPawn(p).weaponPriority[i] == mimicPriorityOf) { autoSwitchPriority = i; return; } } // If, for whatever reason, they don't have our mimicked // (Botpack) class, just stick us in there. super.setSwitchPriority(p); } } function setDisplayProperties(ERenderStyle style, texture tex, bool bLighting, bool bEnviroMap) { if (!bSetup) { bSetup = true; if (slave != none) slave.setDisplayProperties(style, tex, bLighting, bEnviromap); bSetup = false; } super.setDisplayProperties(style, tex, bLighting, bEnviromap); } function setDefaultDisplayProperties() { if (!bSetup) { bSetup = true; if (slave != none) slave.setDefaultDisplayProperties(); bSetup = false; } super.setDefaultDisplayProperties(); } /** Hmm, the "this will annoy people" part really messes up the bots. They try to pick up weapons they don't even need. Need to address that before messing with it here. */ function bool handlePickupQuery(Inventory i) { local Pawn p; local int oldAmmo; local int rounds; u.debug("handlePickupQuery()"); if (i.class == class) { // NEVER return false past this point. You'll end up with doubles. p = Pawn(owner); if (bAkimbo && slave == none) { // Spawn the slave weapon. slave = spawn(class, p); itemName = slaveName; // FIX. Generalize. AIRating = 0.4; slave.setupSlave(self, Pawn(owner).weapon == self); } else { // Can't cheat with weapon stay... if (Weapon(i).bWeaponStay && (!Weapon(i).bHeldItem || Weapon(i).bTossedOut)) return true; /** With clipped weapons that are dropped, you can take the clip out. */ if (ammo().bClipped && SLWeapon(i).clip != none) { ammo().addClip(SLWeapon(i).clip); } else { // WTF is all this????? if (ammoType != none) { oldAmmo = getAmmo(); if (ammoType.addAmmo(Weapon(i).pickupAmmoCount) && (oldAmmo == 0) && (p.weapon.class != i.class) && !p.bNeverSwitchOnPickup) { weaponSet(p); } } } } p.receiveLocalizedMessage(class'PickupMessagePlus', 0, none, none, self.class); i.playSound(i.pickupSound); if (level.game.LocalLog != none) level.game.LocalLog.logPickup(i, Pawn(owner)); if (level.game.WorldLog != none) level.game.WorldLog.logPickup(i, Pawn(owner)); i.setRespawn(); return true; } // Same class. // Continue down the chain. if (inventory == none) return false; return inventory.handlePickupQuery(i); } /** bBringUp is true if we picked up this slave with another one already equipped. */ function setupSlave(SLWeapon p, bool bBringup) { becomeItem(); master = p; bSlave = true; if (bFirearm) fireMode = p.fireMode; setDisplayProperties(p.style, p.texture, p.bUnlit, p.bMeshEnviromap); giveAmmo(Pawn(owner)); if (bBringUp) bringUp(); else gotoState('Idle2'); } simulated function setHand(float hand) { u.debug("setHand(" $ int(hand) $ "): slave: " $ u.sname(slave)); // No center? Use right. if (bNoCenter && hand == 0) hand = -1; if (hand == 2) { // Hidden! bHideWeapon = true; playerViewOffset.y = 0; fireOffset.Y = 0; } else { // Not hidden! bHideWeapon = false; if (slave != none) slave.setHand(-hand); // Set the mesh. if (leftMesh != "" && hand == 1) setSkelMesh(SkeletalMesh(dynamicLoadObject(leftMesh, Class'SkeletalMesh'))); else setSkelMesh(SkeletalMesh(default.playerViewMesh)); // We don't care about the PVO if it's not being used. //if (owner.isA('PlayerPawn') && Viewport(PlayerPawn(owner).player) != none) setPVO(hand, default.playerViewOffset); fireOffset.y = default.fireOffset.y * hand; } } /** If you don't clear this skelAnim var, it tries to use the same animations, which screws things up. This must be called on the client. */ simulated function setSkelMesh(SkeletalMesh m) { u.debug("setSkelMesh(" $ m $ ")"); if (mesh != m) { skelAnim = none; mesh = m; } } /** Sets the playerViewOffset based on the hand. */ simulated function setPVO(float hand, vector def) { local vector pvo; if (hand == 0) { pvo.x = def.x * 0.88; pvo.y = -0.2 * def.y; pvo.z = def.z * 1.12; } else { pvo.x = def.x; pvo.y = def.y * hand; pvo.z = def.z; } pvo *= 100; // Set it. u.debug("setPVO(" $ int(hand) $ "): new pvo: " $ u.sv(pvo)); playerViewOffset = pvo; } state ClientActive { simulated function animEnd() { u.debug("ClientActive:animEnd()"); bBringingUp = false; if (!bSlave) { super.animEnd(); if (slave != none && !isInState('ClientActive')) { // Heh. FIX this. if ((getStateName() == 'None') || (getStateName() == 'Sidearm')) slave.gotoState(''); else slave.gotoState(getStateName()); } } } simulated function beginState() { u.debug("ClientActive:beginState()"); //clipHUDFix(); setHand(hand()); super.beginState(); bBringingUp = false; if (slave != none) slave.gotoState('ClientActive'); } } state ClientDown { simulated function animEnd() { if (!bSlave) super.animEnd(); } simulated function EndState() { if (slave != none) slave.gotoState(''); } } function bringUp() { u.debug("bringUp()", DL_Normal); //clipHUDFix(); if (owner.isA('PlayerPawn')) { setHand(hand()); PlayerPawn(owner).endZoom(); } if (slave != none) slave.bringUp(); bBringingUp = true; bWeaponUp = false; playSelect(); gotoState('Active'); //super.bringUp(); } /** Spawns a clip fix HUD. If another weapon already has one, use it. simulated function clipHUDFix() { local PlayerPawn pp; local Inventory i; if (u.legacy()) { if (clipfix != none) { clipfix.register(); } else { pp = PlayerPawn(owner); if (pp != none && pp.myHUD != none && pp.myHUD.isA('ChallengeHUD')) { // Look for other SLWeapons to use their clip fix HUD. for (i = pp.inventory; i != none; i = i.inventory) { if (i.isA('SLWeapon') && SLWeapon(i).clipfix != none) { clipfix = SLWeapon(i).clipfix; return; } } // Need to spawn one. clipfix = spawn(Class'SLClipFix', pp); } } } } */ /** This is called after the master has fired. If bWait is true, there is a slight pause before it fires the slave. */ simulated function slaveFire(bool bUseAlt, bool bWait) { u.debug("slaveFire(" $ bAlt $ ", " $ bWait $ ")"); // Yes, this gets run twice for bWait == true. Tough. bAlt = bUseAlt; if (bWait) { gotoState('SlaveFireWait'); } else { // Yes, this is silly. We want to pickup subclass behavior in the alt methods. if (role < ROLE_Authority) { if (bAlt) clientAltFire(0); else clientFire(0); } else { if (bAlt) altFire(0); else fire(0); } } } /** This state causes a slight random pause before the slave fires (after the master). See slaveFire(). */ simulated state SlaveFireWait { begin: if (bAlt) { sleep(slaveAltWait * frand()); } else { sleep(slaveWait * frand()); } slaveFire(bAlt, false); } /** Replicated reload function for remote clients. */ simulated function remoteReload() { reload(); } /** Tests for ammo, starts an animation, and switches to the reload state. */ simulated function reload() { local SLAmmo a; a = ammo(); if (a.bClipped) { u.debug("reload(): rounds: " $ clip.getRounds() $ " clips: " $ a.getClips(), DL_Normal); // If the other weapon is reloading and and we don't have more than one spare, return. if (mate() != none && mate().isInState('Reloading') && (a.getClips() == 1)) { u.debug("reload(): mate reloading"); } else if (a.getClips() > 0) { // This ejects a spent clip. if (ammoType != none && SLAmmo(ammoType).spentClip != none) spawnBrass(SLAmmo(ammoType).spentClip, 0.5); // Again, must initiate animation before changing states. See // clientFire() comments. playReload(); gotoState('Reloading'); // Handle remotes. if (!u.hasViewPort(owner)) remoteReload(); } } } /** The animation has been initiated in reload(). This state waits for it to finish. the client/Alt/Fire() functions are for passing fire commands to the slave while the primary is reloading. */ simulated state Reloading { ignores reload; function fire(float v) { if (slave != none) slave.fire(v); } function altFire(float v) { if (slave != none) slave.altFire(v); } simulated function bool clientFire(float v) { if (slave != none) slave.clientFire(v); } simulated function bool clientAltFire(float v) { if (slave != none) slave.clientAltFire(v); } /* Debugging. simulated function beginState() { u.debug("Reloading:beginState()"); } simulated function endState() { u.debug("Reloading:endState()"); } */ begin: u.debug("Reloading:begin"); // Finish reload anim from reload(). finishAnim(); if (bFakeReload) { playAss(assSelect); finishAnim(); } if (role == ROLE_Authority) { clip = ammo().reload(); finish(); } else { gotoState('Idle'); } } /** This initiates the anim in reload(). Reloading:begin finishes it. */ simulated function playReload() { if (bFakeReload) { playAss(assDown); } else { playAss(assReload); } } /** Play ASS struct. bReplicate means playSound will be called from a non-simulated function, which means it will be replicated to the relevant clients. bSkipOwner means the sound is played in a simulated function on the client as well as the server, so when we're replicating it, don't send it to the originating client. Returns true if the animation played. */ simulated function bool playAss(ASS a, optional bool bReplicate, optional bool bSkipOwner) { local float v; u.debug("playAss(): aname: " $ a.aname $ " arate: " $ u.sf(a.arate)); if (a.sound != none && owner != none) { v = a.volume * Pawn(owner).soundDampening; if (bReplicate) replicateSound(a.sound, a.slot, v); else if (bSkipOwner || a.bSkipOwner) playOwnedSound(a.sound, a.slot, v); else playSound(a.sound, a.slot, v); } if (a.aname != '' && a.arate > 0) { if (hasAnim(a.aname)) { playAnim(a.aname, a.arate, a.tween); return true; } else { u.debug("playAss(): no anim: " $ a.aname); } } return false; } /** Not simulated, so it gets replicated. */ function replicateSound(Sound s, ESoundSlot slot, float vol) { playSound(s, slot, vol); } /** Primary fire. */ function fire(float v) { local bool bWait; u.debug("fire(" $ u.chopf(v) $ "): bAlt: " $ bAlt, DL_Normal); if (ammoType == none) giveAmmo(Pawn(owner)); if (ammoLeft()) { if (needReload()) { u.debug("fire(): needs reload"); // Reload may fail silently in the the master/slave is // reloading. No empty sound. FIX? reload(); } else { bWait = true; if (bFirearm) { gotoState('FAFire'); } else { fire2(v); } } } else { u.debug("fire(): no ammo"); // clientFire() isn't called, so... if (level.netmode == NM_Standalone || (level.netmode == NM_ListenServer && u.hasViewPort(owner))) playAss(assEmpty); } if (slave != none) slave.slaveFire(bAlt, bWait); } /** Primary fire for non-firearms. */ function fire2(float v) { u.debug("fire2(" $ u.chopf(v) $ ")"); if (ammoLeft()) { if (bAlt) gotoState('AltFiring'); else gotoState('NormalFire'); bCanClientFire = true; bPointing = true; if (bRapidFire || (firingSpeed > 0)) Pawn(owner).playRecoil(firingSpeed); if (bInstantHit) { traceFire(0.0); useAmmo(1); } else { if (bAlt) projectileFire(altProjectileClass, altProjectileSpeed, bWarnTarget); else projectileFire(projectileClass, projectileSpeed, bWarnTarget); } // Run this after projectileFire() in case of spawn problems. if (bInstantHit || lastProj != none) { if (bAlt) playAltFiring(); else playFiring(); // Don't use ammo if the spawn failed! useAmmo(1); } else { finish(); } } } function altFire(float v) { u.debug("altFire(" $ u.chopf(v) $ ")"); if (bAltSwitchMode) { switchMode(); return; } bAlt = true; fire(v); } /** Cycles through the fire modes. */ function switchMode() { playAss(assMode, true); switch (fireMode) { case FM_Single: fireMode = FM_Burst; break; case FM_Burst: fireMode = FM_Auto; break; case FM_Auto: fireMode = FM_Single; break; } if (slave != none) slave.fireMode = fireMode; } state Idle { simulated function playIdleAnim() { if (!isAnimating() && frand() < idleRandom) playAss(assIdle); } function timer() { playIdleAnim(); } simulated function beginState() { u.debug("Idle.beginState()"); setTimer(idleLapse, true); } simulated function endState() { u.debug("Idle.endState()"); setTimer(0.0, false); } begin: bPointing=false; // Switch to another weapon when empty. if (!ammoLeft()) Pawn(owner).switchToBestWeapon(); /* Don't refire. if (Pawn(owner).bFire != 0) fire(0.0); if (Pawn(owner).bAltFire != 0) altFire(0.0); */ disable('animEnd'); //playIdleAnim(); } /** FIX? */ state Active { function beginState() { u.debug("Active:beginState()"); //super.beginState(); bChangeWeapon = false; } function endState() { u.debug("Active:endState()"); super.endState(); bBringingUp = false; } function bool putDown() { u.debug("Active:putDown()"); return super.putDown(); /* if (bWeaponUp || (animFrame < 0.75)) gotoState('DownWeapon'); else bChangeWeapon = true; return true; */ } begin: finishAnim(); if (bChangeWeapon) gotoState('DownWeapon'); bWeaponUp = true; bCanClientFire = true; if (!bSlave && (level.netmode != NM_Standalone) && (owner != none) && owner.isA('TournamentPlayer') && (PlayerPawn(owner).player != none) && !PlayerPawn(owner).player.isA('ViewPort')) { if (Pawn(owner).bFire != 0) TournamentPlayer(owner).sendFire(self); else if ( Pawn(owner).bAltFire != 0 ) TournamentPlayer(owner).sendAltFire(self); else if ( !bChangeWeapon ) TournamentPlayer(owner).updateRealWeapon(self); } finish(); } /* WTF was I doing with this? function timer() { u.debug("Global.timer()"); } */ /** This may not work for all subclasses, but it does for the sidearm. FIX? */ simulated function playFAFiring(optional bool bSkipNext) { if (bInitFA) { bInitFA = false; } else { if (fireMode == FM_Single) { playFiring(); } else { playAltFiring(); } } if (bSkipNext) bInitFA = true; } /** Firing mode for firearms. This is a dual client/server state. */ simulated state FAFire { // Can't switch modes mid-burst! ignores altFire, clientAltFire; //, animEnd; function fire(float v) { u.debug("FAFire:fire(): clear: " $ bClear); if (bClear) { // Must clear the state. Not sure why. gotoState(''); global.fire(v); } } simulated function bool clientFire(float v) { u.debug("FAFire:clientFire(): clear: " $ bClear); if (bClear) { // Must clear the state. gotoState(''); return global.clientFire(v); } else { return false; } } simulated function beginState() { u.debug("FAFire:beginState()"); bClear = false; burstCount = 0; } /* Debugging. simulated function endState() { u.debug("FAFire:endState()"); } */ begin: u.debug("FAFire.begin"); fireLoop: while (((Pawn(owner).bFire != 0 || Pawn(owner).bAltFire != 0) && fireMode == FM_Auto) || (fireMode == FM_Burst && burstCount < burstRounds) || burstCount == 0) { // Debugging. u.debug("FAFire:fireLoop: fire loop: burstCount: " $ burstCount $ " bFire/Alt: " $ Pawn(owner).bFire $ "/" $ Pawn(owner).bAltFire); if (needReload() || !ammoLeft()) break; /* Recoil animation. Note that the recoil animations keep going after the fire loop is done if we hold down the fire button. FIX, eventually. Also the recoil movement causes the recoil anim to temporarily stop. FIX, eventually. */ bRapidFire = (fireMode > FM_Single); Pawn(owner).playRecoil(firingSpeed); playFAFiring(); if (role == ROLE_Authority) { if (level.netmode != NM_DedicatedServer) spawnMuzzleFlash(); if (level.netmode != NM_Standalone) // For clients. flashCount++; traceFire(vsize(recoilv) * recoilData.accScale); // We already checked for ammo above. useAmmo(1); } // Recoil should be done *after* traceFire(), otherwise the // first shot will always be off. recoil(); spawnBrass(brassClass); sleep(60 / rpm); burstCount++; } // Fin. sleep(burstWait); //u.debug("FAFire:fireLoop: burst wait over"); bClear = true; finishAnim(); if (role == ROLE_Authority) finish(); else gotoState('Idle'); } /** Rather than use the undocumented 3rd person flash from Inventory, we'll just use a simple sprite. I think it looks better, anyway. */ simulated function spawnMuzzleFlash() { local vector x, y, z, fo; if (owner != none) { // This seems to look adequate. fo = fireOffset * 2.0; getAxes(owner.rotation, x, y, z); spawn(Class'SLMuzzleFlash', owner,, owner.location + fo.x * x + fo.y * y * -hand() + fo.z * z); // Hmm, this might not be appropriate for reloading, etc. There is // no way to tell currently if the weapon is firing or not. if (slave != none) { slave.spawnMuzzleFlash(); } } } /** Recoil alters the view rotation of the player. The delta is kept track of in recoilv. */ simulated function recoil() { local vector v; v = (vrand() - vrand()); v.z += frand() * recoilData.kick; recoilv += v * recoilData.base; recoilv.x = fclamp(recoilv.x, -recoilData.limit, recoilData.limit); recoilv.y = fclamp(recoilv.y, -recoilData.limit, recoilData.limit); recoilv.z = fclamp(recoilv.z, -recoilData.limit, recoilData.limit); u.debug("recoil: " $ u.sv(recoilv)); } /** Recoil is slowly brought under control at recoilRate. */ simulated function tick(float delta) { local Pawn p; local float x, y, z; local int dir; /* if (PlayerPawn(owner) != none) //&& level.netmode == NM_Client) u.debug("tick(): state: " $ getStateName() $ " seq: " $ animSequence $ " weap: " $ u.sname(PlayerPawn(owner).weapon) $ " bBringingUp: " $ bBringingUp $ " slave: " $ u.sname(slave) $ " weapup: " $ TournamentPlayer(owner).weaponUpdate); */ // Normally these are used for the flash mesh, but we're using sprites. if (role < ROLE_Authority && flashCount > oldFlashCount) { spawnMuzzleFlash(); oldFlashCount++; } p = PlayerPawn(owner); if (p != none) { if (vsize(recoilv) > 1.0) { recoilv = normal(recoilv) * fmax(vsize(recoilv) - (recoilData.returnRate * delta), 0); } } } /** If you're wielding two weapons, your accuracy drops. Note the recoil is also doubled with slaves. */ function traceFire(float acc) { if (slave != none || bSlave) { acc *= slaveAcc; } super.traceFire(acc); } /** Find out where the bullet went and deal the damage. */ function processTraceHit(Actor who, vector hitloc, vector hitn, vector x, vector y, vector z) { if (who == level) { //if (bSlave || slave != none) Light wall hit? Nah. spawn(wallhitClass, who,, hitloc + hitn, rotator(hitn)); } else if ((who != self) && (who != owner) && (who != none)) { /* WTF is this? FIX. if (frand() < 0.2) x *= 5; */ who.takeDamage(hitdam, Pawn(owner), hitloc, 3000.0 * x, myDamageType); if (!who.bIsPawn && !who.IsA('Carcass')) spawn(wallhitClass.default.puffClass, who,, hitloc + hitn, rotator(hitn)); else who.playSound(Sound'ChunkHit',, 4.0,, 100); } } /** Server-spawned. */ function spawnBrass(Class c, optional float ejectv) { local vector x, y, z; local EjectedBrass s; // Default is 1.0. if (ejectv == 0) ejectv = 1.0; if (level.netmode != NM_DedicatedServer) { getAxes(owner.rotation, x, y, z); s = spawn(c,,, owner.location + calcDrawOffset() + (0 * x) + (-6 * z)); // + (fireOffset.y * y * -hand())); if (s != none) s.eject(brassv(x, y, z) * ejectv); } } /** Override this to set the brass eject velocity. */ simulated function vector brassv(vector x, vector y, vector z) { return ((frand() * 0.3 - 0.15) * x + (frand() * 0.2 + 1.0) * y * -hand() + (frand() * 0.3 + 0.3) * z) * 120; } /** Finish a firing sequence. */ function finish() { local Pawn po; local bool bForce, bForceAlt; u.debug("finish()"); bForce = bForceFire; bForceAlt = bForceAltFire; bForceFire = false; bForceAltFire = false; // Reset this. bAlt = false; if (bChangeWeapon) { gotoState('DownWeapon'); return; } po = Pawn(owner); if (po == none) return; if (PlayerPawn(owner) == none) { // Pawns. if (!ammoLeft()) { po.stopFiring(); gotoState('Idle'); po.switchToBestWeapon(); if (bChangeWeapon) gotoState('DownWeapon'); } else if ((po.bFire != 0) && (frand() < refireRate)) { global.Fire(0); } else if ((po.bAltFire != 0) && (frand() < altrefireRate)) { global.AltFire(0); } else { po.stopFiring(); gotoState('Idle'); } } else { // Players. // Some of our animations don't zero. FIX. tweenToStill(); gotoState('Idle'); /* Can't hold down trigger. Make this an option? FIX. if (!ammoLeft() || (po.weapon != self)) { gotoState('Idle'); } else if (po.bFire != 0 || bForce) { //global.fire(0); } else if (po.bAltFire != 0 || bForceAlt) { //global.altFire(0); } else { gotoState('Idle'); } */ } } /** Throw the slave first, then us. */ function dropFrom(vector startLoc) { u.debug("dropFrom()"); if (slave == none) { // We don't transfer clips with dropped weapons. if (ammo().bClipped) { // This keeps the super call from setting ammo. ammoType = none; // Keep the clip. Unset pickup ammo. pickupAmmoCount = 0; } // Unset the master. master = none; super.dropFrom(startLoc); } else { slave.dropFrom(startLoc); } } simulated function playFiring() { u.debug("playFiring()"); viewFX(); playAss(assFire); } simulated function playAltFiring() { u.debug("playAltFiring()"); viewFX(); playAss(assAltFire); } simulated function viewFX() { if ((PlayerPawn(owner) != none) && ((level.netmode == NM_Standalone) || PlayerPawn(owner).player.isA('ViewPort'))) { if (instFlash != 0.0) PlayerPawn(owner).clientInstantFlash(instflash, instfog); PlayerPawn(owner).shakeView(shaketime, shakemag, shakevert); } if (affector != none) affector.fireEffect(); if (bDrawMuzzleFlash) bMuzzleFlash++; } /** This handles fake firing on the client. OK, here's the deal. The player (TournamentPlayer), in replicateMove(), will call gotoState('') and then tweenToStill() on us if we're not currently animating, almost every tick. What that means is that you *must* start an animation here before going to another state. I used to just go to FAFire here, but due to this issue, I had to start playing the anim here. playFAFiring(true) skips the next anim so it doesn't play twice. */ simulated function bool clientFire(float v) { local bool bFired; u.debug("clientFire(" $ u.chopf(v) $ "): alt: " $ bAlt, DL_Normal); if (bCanClientFire) { if (!ammoLeft()) { playAss(assEmpty); } else if (needReload()) { // Server will send reload. //reload(); } else { if (bFirearm) { u.debug("clientFire(): going to FAFire"); playFAFiring(true); gotoState('FAFire'); } else { // Do we need a role check here? FIX. if (bAlt) { playAltFiring(); gotoState('ClientAltFiring'); } else { playFiring(); gotoState('ClientFiring'); } } bFired = true; } if (slave != none) // Did we fire? Then pause before slave fire. slave.slaveFire(bAlt, bFired); } return bFired; } simulated function bool clientAltFire(float value) { // Server will send new mode. if (bAltSwitchMode) return false; bAlt = true; return clientFire(value); } /** This is just like projectileFire() from Engine.Weapon, except the owner is set in the call to spawn(). Used for the Konglauncher. */ function Projectile projectileFire(Class projClass, float speed, bool warn) { local vector start, x, y, z; local Pawn po; u.debug("projectileFire(" $ projClass $ ")"); // Clear it. lastProj = none; po = Pawn(owner); owner.makeNoise(po.soundDampening); getAxes(po.viewRotation, x, y, z); if (spawnProjOffset != vect(0, 0, 0)) { start = owner.location + spawnProjOffset; } else { start = owner.location + calcDrawOffset() + fireoffset.x * x + fireoffset.y * y + fireoffset.z * z; } adjustedAim = po.adjustAim(speed, start, aimError, true, warn); if (!bSetProjOwner) po = none; // Will setting the owner cause problems for other weapons? lastProj = spawn(projClass, po,, start, adjustedAim); if (lastProj == none) u.err("projectileFire(): Spawn failed!"); return lastProj; } /** For akimbo weapons. The server says when to bring up the slave (us). Er... */ simulated function animEnd() { if (level.netmode == NM_Client && bBringingUp && mesh != pickupViewMesh) { u.debug("animEnd(): bringing up"); bBringingUp = false; playSelect(); } else { super.animEnd(); } } /** FIX. We can clean up these states if we decide to definitely not go with refiring. */ state ClientFiring { simulated function animEnd() { if (Pawn(owner) == none || !ammoLeft()) { playIdleAnim(); gotoState(''); } else if (!bCanClientFire) { gotoState(''); /* Don't refire. else if (Pawn(owner).bFire != 0) global.ClientFire(0); else if (Pawn(owner).bAltFire != 0) global.ClientAltFire(0); */ } else { playIdleAnim(); gotoState(''); } } } state ClientAltFiring { simulated function animEnd() { if (Pawn(owner) == none || !ammoLeft()) { playIdleAnim(); gotoState(''); } else if (!bCanClientFire) { gotoState(''); /* Don't refire. else if (Pawn(owner).bFire != 0) global.ClientFire(0); else if (Pawn(owner).bAltFire != 0) global.ClientAltFire(0); */ } else { playIdleAnim(); gotoState(''); } } } simulated function playSelect() { u.debug("playSelect()"); bForceFire = false; bForceAltFire = false; bCanClientFire = false; if (!isAnimating() || (animSequence != assSelect.aname)) playAss(assSelect); } simulated function tweenDown() { u.debug("tweenDown()"); if (isAnimating() && (animSequence != '') && (getAnimGroup(animSequence) == assSelect.aname)) tweenAnim(animSequence, animFrame * 0.4); else playAss(assDown); } /* We'll replace this with a new gametype. state Sleeping { begin: sleep(respawnTime); playSound(respawnSound); spawn(class'RespawnEffect', self,, location); sleep(0.3); gotoState('Pickup'); } */ state DownWeapon { ignores fire, altFire, animEnd; function beginState() { super.beginState(); if (slave != none) slave.gotoState('DownWeapon'); } } /** This handles rendering the slave. What happens if we are viewing through a bot with akimbo weapons? ... FIX. */ simulated function renderOverlays(Canvas c) { local vector pvo; local PlayerPawn po; local float oldhand; // Let save us some effort here. if (bHideWeapon || owner == none) return; po = PlayerPawn(owner); // Store it for later. pvo = playerViewOffset; if (po != none) { pitchAdjust(po.rotation.pitch); playerViewOffset += (recoilv * recoilData.pvoScale); } // Pick a random flash. if (bDrawMuzzleFlash && arrayCount(MFVariations) > 0) { MFTexture = MFVariations[rand(arrayCount(MFVariations))]; } if (po != none) { // This is a hack to get it to render in the right hand. oldhand = po.handedness; po.handedness = hand(); } // Do it. super.renderOverlays(c); // Render the slave. if (slave != none) { // Is this not ever caught in animEnd()? FIX. if (slave.bBringingUp) { u.debug("renderOverlays(): bringing up slave: " $ u.sname(slave)); slave.bBringingUp = false; slave.playSelect(); } slave.renderOverlays(c); } if (po != none) // Restore the hand. po.handedness = oldhand; // Restore the true PVO. playerViewOffset = pvo; } /** While rotation is free (18000 up and 49752 down), viewRotation only goes from 3072 (up) to 62464 (down). Except underwater, so we'll lock it to the normal viewRotation pitch range. */ simulated function pitchAdjust(int pitch) { if (pitch >= 49752) pitch = max(62464, pitch); if (pitch < 18000) pitch = min(3072, pitch); if (pitch > 3072) { // Looking down. pitch -= 65535; } playerViewOffset.x = 100 * (default.playerViewOffset.x + paSlide * (pitch / 3072.0)); if (bDrawMuzzleFlash) { flashY = default.flashY - paFlashY * (pitch / 3072.0); flashO = default.flashO - paFlashO * (pitch / 3072.0); } } /** Sometimes the slave needs to refer back to the master. */ simulated function SLWeapon mate() { if (bSlave) // Obey... your... return master; else return slave; } /** The handedness is Botpack.PlayerPawn is not always the value that we want to use for displaying and firing. Certain weapons may lack models for either left, right, or center; or may have a slave counterpart, which is used in the opposite hand. This function returns the correct value for this weapon instance. Sometimes during ClientActive the owner is not set. Default to right-handed. */ simulated function float hand() { local float h; if (owner != none && owner.isA('PlayerPawn')) h = PlayerPawn(owner).handedness; else // Default to right handed (bots). h = -1; // No center? Use right. if (bNoCenter && h == 0) h = -1; // Generally akimbo weapons are bNoCenter, but what the hay. if (bSlave && h != 0) h = -h; return h; } // end b // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLWallhit.uc,v 1.1 2001/06/23 21:40:26 yrns Exp $ /** Bullet hits wall f/x. */ class SLWallhit extends UT_WallHit; var() Class pockClass; var() Class puffClass; var() Class chipClass; var() Class sparkClass; simulated function spawnEffects() { local Actor fx; local Texture tex; local int i; local int n; spawnSound(); if (level.bDropDetail) return; if (level.bHighDetailMode) { fx = spawn(pockClass); /* This does not work. tex = Pockmark(fx).hittex; log("!!! pock: " $ fx $ " tex: " $ tex); */ } n = rand(maxChips / 2) + maxChips / 2; for (i = 0; i < n; i++) { if (frand() < chipOdds) { fx = spawn(chipClass); if (fx != none) { /* This does not work. if (tex != none) { fx.texture = tex; } log("!!! chip tex: " $ fx.texture); */ fx.remoteRole = ROLE_None; fx.drawScale *= 0.75; n--; } } } if (level.bHighDetailMode) { // If it's anything whose normal points up, use up. Anything // down, use the hitnormal. if ((vector(rotation) dot vect(0, 0, 1)) < 0) fx = spawn(puffClass,,,, rotation); else fx = spawn(puffClass,,,, rotator(vect(0, 0, 1))); fx.remoteRole = ROLE_None; /* Sparks? Nah... if (!region.zone.bWaterZone && (n > 0)) { for (i = 0; i < n; i++) { spawn(sparkClass,,, location + 8 * vector(rotation)); } } */ } } // end %6-Warning:beginState()UaQ ( HGx{ 9pAzi yn &K- -T i@z@G@-T 'ziGizGziG??C<znz?%a HxL>H [ *,[ }ObF ~=Fb~:~Z b X@*findLaunchNode(): in range of goal*/ba6 VX[ZVd?ZDVb:~SV[ZmV g~5rS*SVzmZZmzSVzm10wS*6pfindLaunchNode(): node:  SS'findLaunchNode(): no good nodes* ZS j%-33?a$??,Z-Y-S a yS @@ x#U BU'-lpppppWarning:alarm(): bAlarm: T-} alarms: fuel: TD2 hull: Ti2D2i2-}-}-}-o i2[_?%2a C $?-o 'aQ (%q!r C // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLSparker.uc,v 1.1 2001/06/23 21:40:26 yrns Exp $ class SLSparker extends UT_Spark; // end U // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLSmoke.uc,v 1.2 2001/06/23 21:40:26 yrns Exp $ /** My smoke cl_ass. */ class SLSmoke extends UT_SpriteSmokePuff; /** Use the rotation as the rise direction? */ var() bool bRotV; simulated function beginPlay() { local vector dir; if (bRotV) dir = vector(rotation); else // Up. dir = vect(0, 0, 1); velocity = dir * risingRate; if (numsets > 2) texture=ssprites[rand(numsets)]; } // end mw*#.SLV2Fonts.leg8   ] Z `[.u r| * 4/?d?_d??D=/ DcZ c>cL>d?&?,cd?,?,cd?.zDOBde.?/>?/O%?/O.?&/>v>/O>/O/>?/O?/O/$/b&?,Z /&a| OOBB E // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLPockmark.uc,v 1.1 2001/06/23 21:40:26 yrns Exp $ /** A pock mark. */ class SLPockmark extends Pock; //var Texture hittex; simulated function attachToSurface() { local Texture hittex; hittex = attachDecal(100, vect(0,0,1)); bAttached = (hittex != none); } // end ]NQd`r *r * --noMore(): destroyinga'( i // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLMuzzleFlash.uc,v 1.2 2001/06/27 06:40:36 yrns Exp $ /** Muzzle flashes. (3rd person.) */ class SLMuzzleFlash extends SLSmoke; var Texture sprites[6]; simulated function postBeginPlay() { texture=sprites[rand(arrayCount(sprites))]; } // end HT ^x1Q-ppweaponSet( T )D-@(OT  W#a]&=zh  )rK*Ka K b#G,0V-@G,8xrm*m gH$rm* #m#$#`#ahSLIS|L#aFSLIISIIHBb>?%ED#@?GED#@?GIQD#@?,MlK('EQDI?,DL?,&(#?E&?Q&#b(`L?KDIlDL#`#?E,?Q,#ah#"?E,?QL?,#aF;{a #aaSL#(?EIS?&?Q,#aa J)a4 Q )/2- ^ b) -] 7)~ -[n )$ -7w 2?OT -)b(n 333?V DD?Q@?_?,(DU?@@)$)6 T)?@-)$)$ -Y)b(n Y pNOT FOR PUBLIC RELEASE. AUTHORIZED USE ONLY.  i)B )aY d dA DT?,d DU?,?, Y -w {MDD?Q,?_DU?@?vODD?Q,?_DU?@)i Mx Mi MD M^w B* B%)z [ MPDD?Q@DU?@LRDD?Q,?_DU?,w* a) l u>k9l AIIaR $l aR $l  N // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLMutator.uc,v 1.1 2001/07/09 10:00:16 yrns Exp $ /** Right now all this does it make sure we're ahead of any Relic mutators, which screw up the HUD mutator chain. */ class SLMutator extends Mutator; var PlayerPawn ho; // HUD owner. function postBeginPlay() { ho = PlayerPawn(owner); register(); } /** This checks to make sure we're in the HUD mutator list. If we reach any Relic HUD mutators before us, just put us at the front. The Relic HUD mutators don't allow non-Relic HUD mutators below them. */ function register() { local Mutator m; local bool move; m = ho.myHUD.HUDMutator; if (m == self) return; if (m != none) { while (getNext(m) != none) { if (isRelic(getNext(m))) // We're lost. Put us at the front. break; if (getNext(m) == self) // We're in there. Return. return; /* setNext(m, getNext(getNext(m))); self.nextHUDMutator = none; break; */ m = getNext(m); } } // Put it in front. nextHUDMutator = ho.myHUD.HUDMutator; ho.myHUD.HUDMutator = self; } function bool isRelic(Mutator m) { return (m.isA('HUDMutator')); } function Mutator getNext(Mutator m) { if (m.isA('HUDMutator')) return HUDMutator(m).nextRHUDMutator; else return m.nextHUDMutator; } /* Not used. function Mutator setNext(Mutator m, Mutator n) { if (m.isA('HUDMutator')) // So stupid. HUDMutator(anything) returns none unless it's a relic. HUDMutator(m).nextRHUDMutator = HUDMutator(n); else m.nextHUDMutator = n; } */ // end PiHtI go #hi  ::$i--o %?o I?p 6p L>N a w p b N $o On o // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLInfo.uc,v 1.1 2001/06/21 04:46:11 yrns Exp $ /** Empty - used for grouping Strangelove Infos. */ class SLInfo extends Info; // end y // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLHUD.uc,v 1.20 2001/08/28 07:23:45 yrns Exp $ /** HUD overlay when piloting. Inserted as a HUD mutator at launch. To anyone reading this: This is sludge layered upon sludge. No attempt has been made to make it readable or configurable. For the next version... */ class SLHUD extends SLMutator config (SLV2); // IMPORTS // #exec OBJ LOAD FILE=..\Textures\SLV2Textures.utx PACKAGE=SLV2Textures // DATA // var StrangeShell sl; // The controller. var() color col1; // Main HUD color. var() color col2; // Bluish color for the gauges. var() color col3; // White color for debugging, etc. var() color col4; // Reddish color to draw the keys in. var() float lucent; // Factor to lighten some parts of the HUD. var() float fade; // Fade time in seconds for the reticule. var() float warnBlink; // Timer rate for warning blink. var() float warnLvl; // Warnings go off at this level. /* Various fonts. */ var Font font, lgFont; var Font debugFont; var() float u2m; var bool bInit; var float screenFade; // Screen (green) fades in. //var bool in; // Fading in or out? //var float reticuleFade; //var float hudFadeIn; var float shakemag; // Magnitude of shaking, in pixels. var int shakex; // For afterburner shakes. var int shakey; // For afterburner shakes. var() float shakeRate; // Rate at which shaking subsides. var int skewx, skewy; var bool bWarned; // Single alarm bell for fuel low with cores. var bool bAlarm; // Flips the color in timer(). var() config bool bShowKeys; // Show the keys initially? var float keysFade; // Fading out. var int keysSlide; // Slide in. var() string keys[6]; // Key texts. var float keymax; // Max key string length. var int keysUp; // If they don't fit on the right, knock them up a bit. /** Level gauges. */ var HUDLevel throLvl, veloLvl, hullLvl, fuelLvl, coreLvl; var int retw, reth; var float retScale; var int indw, indh; var int slide; // Used for sliding in and out of shoot mode. //var bool bNoShoot; //var() color noShootColor; //var float nsFade; var Canvas c; var Stylus yy; var Util u; // METHODS // function postBeginPlay() { local Font lg, sm; u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); u.debug("postBeginPlay()"); sl = StrangeShell(owner); sl.HUD = self; ho = PlayerPawn(sl.getPilot()); register(); // Gunner may get a (takeover) HUD if the rider ejects. /* Takeovers off for now. if (ho == none && sl.gunner != none) { ho = PlayerPawn(sl.gunner); } */ //reticuleFade = 0.0; //in = true; screenFade = 0.0; slide = -1; lg = class'Fontlib'.static.getFont(FS_Slick, true); sm = class'Fontlib'.static.getFont(FS_Slick, false); font = sm; lgFont = class'Fontlib'.static.getFont(FS_Title); debugFont = class'Fontlib'.static.getFont(FS_Normal); throLvl = spawn(class'HUDLevel', self); throLvl.init(0, "THRO", "%", lg, sm); veloLvl = spawn(class'HUDLevel', self); veloLvl.init(0, "VELO", "m/s", lg, sm, throLvl); hullLvl = spawn(class'HUDLevel', self); hullLvl.init(0.20, "HULL", "%", lg, sm, veloLvl); fuelLvl = spawn(class'HUDLevel', self); fuelLvl.init(0.20, "FUEL", "", lg, sm, hullLvl); //fuelLvl.label2 = "CORES"; coreLvl = spawn(class'HUDLevel', self); coreLvl.init(0, "CORES", "", lg, sm, fuelLvl); coreLvl.bNoFitLabels = true; yy = spawn(Class'Stylus', self); } function init(Canvas c) { local int x, y; local int w, h; u.debug("init()"); // Only slide in from first init() call. if (slide == -1) { slide = c.clipx; keysSlide = -1; } /* if (clipx < 640) { retw = 64; reth = 24; } else if (clipx <= 800) { retw = 128; reth = 32; } else { retw = 256; reth = 48; } */ retw = max(128, c.clipx * 0.16); reth = retw * 0.25; indw = 70; // Reset in drawIndicators(). indh = reth - 13; x = (c.clipx + retw) * 0.5; y = (c.clipy - reth) * 0.5 + 2; w = reth; //32; h = reth - 4; //28; throLvl.position(x, y, w, h); veloLvl.position(x + (w + 1) * 1, y, w, h); hullLvl.position(x + (w + 1) * 2, y, w, h); fuelLvl.position(x + (w + 1) * 3, y, w, h); coreLvl.position(x + (w + 1) * 4, y, w / 4, h); bInit = true; } auto state Norm { function beginState() { u.debug("Norm:beginState()"); bAlarm = false; bWarned = false; } } state Warning { function beginState() { u.debug("Warning:beginState()"); alarm(); setTimer(warnBlink, false); } function timer() { alarm(); } function alarm() { u.debug("Warning:alarm(): bAlarm: " $ bAlarm $ " alarms: fuel: " $ fuelLvl.alarmOn() $ " hull: " $ hullLvl.alarmOn()); if (fuelLvl.alarmOn() || hullLvl.alarmOn()) { bAlarm = !bAlarm; if (bAlarm) { // If we're low on fuel and we have cores, only play the alarm once. if (!bWarned || hullLvl.alarmOn() || coreLvl.realLvl == 0) { ho.playSound(sl.warnSound, SLOT_None, 1.0); bWarned = true; } } setTimer(warnBlink, false); } else { gotoState('Norm'); } } } /** Destroys us. */ function bool noMore() { if (sl == none || sl.getPilot() == none || sl.bDestroyed) { u.debug("noMore(): destroying"); destroy(); return true; } return false; } function postRender(Canvas c) { local int i; local vector x, y, z; // Screen center point. local int pods; local string vstr; local float xl, yl; if (noMore()) return; // Store the canvas! We use it throughout, without passing it // between functions. self.c = c; // Behind view? No HUD. if (ho.bBehindView) return; // Takeover HUD? Don't need to be init'ed. /* Off for now. if (ho == sl.gunner) { yy.setCanvas(c); c.font = lgFont; // Fade the last second. c.drawColor = col4 * fmin(1.0, sl.toTime); vstr = "EMERGENCY TAKEOVER IN " $ left(sl.toTime, 3); c.textSize(vstr, xl, yl); yy.drawFrame(yy.cx - 2, yy.cy - 82, xl + 4, yl + 2, 1, true, true); c.setPos(yy.cx, yy.cy - 80); yy.drawText(vstr); // Kind of a hack, but not bad. //reinit(); return; } */ // setCanvas returns true on a resize. if (yy.setCanvas(c) || !bInit) init(c); // Total commitment! if (sl.bMarchPlaying) { col1 = col3; } else { col1 = default.col1; } // TAKE THIS OUT LATER. FIX. /* c.setPos(100, 200); c.font = debugFont; c.drawText("dot dot: " $ normal(vector(sl.getPilot().viewRotation)) dot normal(sl.velocity)); c.drawText("dyaw: " $ (sl.getPilot().viewRotation.yaw & 65535) - (rotator(sl.velocity).yaw & 65535)); */ c.style = ERenderStyle.STY_Translucent; // No need to draw the HUD if it's off screen. if ((sl.bShootMode || sl.getPilot() != ho) && slide == yy.clipx) { /* if (nsFade > 0.0) { c.drawColor = (15 * ChallengeHUD(ho.myHUD).crosshairColor) * nsFade; c.setPos(yy.cx - 32, yy.cy - (32 + 64)); c.drawIcon(Texture'SLV2Textures.slhud.noshoot', 1.0); } */ return; } // Visor is high detail only. if (level.bHighDetailMode) { c.drawColor = col3 * 0.7; yy.setPos(yy.cx - retw / 2.0 - indw - 40, yy.cy - (reth * 2.0) / 2.0); c.style = ERenderStyle.STY_Modulated; c.drawRect(Texture'SLV2Textures.slhud.visor', yy.clipx - c.curx, reth * 2.0); } if (level.bHighDetailMode) { c.style = ERenderStyle.STY_Translucent; } else { c.style = ERenderStyle.STY_Normal; } if (Class'Util'.default.bExpires) { c.drawColor = col3 * screenFade; vstr = "NOT FOR PUBLIC RELEASE. AUTHORIZED USE ONLY. " $ Class'Util'.static.getVersionString(); c.font = debugFont; c.textSize(vstr, xl, yl); yy.setPos(yy.clipx - (6 + xl), yy.cy + reth / 2 + 4); yy.drawText(vstr); } // Draw the hijacked keys. if (bShowKeys) drawKeys(); // Indicator lights! drawIndicators(yy.cx - retw / 2 - indw, yy.cy + reth / 2.0 - indh); drawHRot(yy.cx - retw / 2 - indw, yy.cy - reth / 2.0); //drawVRot(yy.cx - retw / 2 - indw - 32, yy.cy - reth / 2.0); // The levels get their first color choice from the existing // canvas color. Need to set it here. c.drawColor = col1; // Throttle level. throLvl.render(yy); veloLvl.render(yy); hullLvl.render(yy); fuelLvl.render(yy); if (sl.fc != none && sl.fc.getAmmo() > 0) { // Normally this would fuck things up, but we're the last gauge. c.drawColor = col2; coreLvl.render(yy); } drawReticule(yy.cx - retw / 2.0, yy.cy - reth / 2.0); /* if (sl.gunner != none && sl.gunner.isA('Bot')) { gunnerDisplay(c, Bot(sl.gunner), yy.cx, yy.cy); } */ // Targets! drawTargets(); drawLogo(yy, yy.cx - retw / 2 - indw, yy.cy - reth / 2); // Keep going down the chain. if (nextHUDMutator != none) nextHUDMutator.postRender(c); } function drawLogo(Stylus s, int x, int y) { local int style; c.drawColor = col1 * screenFade; s.setPos(x - 32, y); c.drawRect(Texture'SLV2Textures.slhud.logo', 32, 32); c.drawColor = col1 * screenFade * 0.20; if (reth > 32) // Missing lower right. style = 3; s.drawFrame(x - 21, y, 21, 32, style); } /** Let the player know what the hijacked key settings are. The box initially slides in from the left and then fades out. This has been hacked around so much. It needs to be cleaned out. */ function drawKeys() { local int x, y, i; local float xl, yl; local bool fill; local int sep; local String rstr; if (keysFade > 0 || keysSlide == -1) { x = -1 - keysSlide; y = (c.clipy - reth) / 2.0 - keysUp; c.font = lgFont; // get the yl c.textSize(keys[0], xl, yl); for (i = 0; i < arrayCount(keys); i++) { // If we haven't set the max yet, don't draw anything. if (keysSlide >= 0) { c.setPos(x + 6, y + 2 + (yl - 1) * i); sep = instr(keys[i], "("); c.drawColor = col4 * fmin(1.0, keysFade); if (sep == -1) { // Title. yy.drawText(keys[i]); } else { // Key name. c.drawColor = c.drawColor * 0.6; yy.drawText(left(keys[i], sep)); // Function. c.drawColor = col4 * fmin(1.0, keysFade); rstr = right(keys[i], len(keys[i]) - sep); c.textSize(left(keys[i], sep), xl, yl); c.setPos(x + 6 + xl, c.cury); yy.drawText(rstr); } } else { c.textSize(keys[i], xl, yl); if (xl > keymax) keymax = xl; } } // We'll miss the first pass, but that's alright, as we'll be // offscreen. FIX? Doesn't take into account max skew. if (((yy.clipx - retw) / 2.0) - indw - 30 < keymax) { keysUp = (yl - 1) * arrayCount(keys) + 4 + reth / 2.0; } else { keysUp = 0; } if (keysSlide >= 0) { fill = level.bHighDetailMode; if (fill) c.drawColor = col4 * (fmin(1.0, keysFade) / 4.0); yy.drawFrame(x, y, keymax + 8, (yl - 1) * arrayCount(keys) + 2, 1, fill, true); } else { keysSlide = keymax + x + 6; // This makes is so it sticks longer. keysFade = 7.0; } } } function drawReticule(int x, int y) { local int sret; local int cx, cy; local color col, retcol; local int w; cx = x + retw / 2; cy = y + reth / 2; if (level.bHighDetailMode) { col = col1 * 0.2 * screenFade; } else { col = col1 * screenFade; } retcol = col1 * (1.0 - retScale) * screenFade; c.drawColor = col; yy.setPos(cx, cy - 8); yy.pix(1, 6); yy.setPos(cx, cy + 2); yy.pix(1, 6); yy.drawFrame(x, y, retw, reth, 4); c.drawColor = retcol; if (sl.bMarchPlaying) { yy.setPos(x + 4, y + 4); c.font = font; yy.drawText("TOTAL COMMITMENT"); return; } // The rounding makes it looks nicer - more centered. We take the // sides in by one pixel so it doesn't overlap the frame in low detail mode. w = retw - 2; yy.drawFrame(x + 1 + round(((1.0 - retScale) * w) * 0.5), y + round(((1.0 - retScale) * reth) * 0.5), round(w * retScale), round(reth * retScale), 1); } function drawHRot(int x, int y) { local float dpp; local float xl, yl; local float head, left; local int i; local float edgeFade; dpp = 1.0; c.font = font; head = toHeading(rotator(sl.velocity).yaw); left = head - indw * 0.5 * dpp; if (level.bHighDetailMode) { c.drawColor = col1 * lucent * screenFade; yy.drawFrame(x, y, indw, reth - indh - 1, 3, true); } for (i = (left / 15) * 15; i < ((head - left) + head); i += 15) { if (i >= left) { //edgeFade = 1.0 - (abs(head - i) / (indw * 0.5)); edgeFade = (abs(head - i) / dpp); // Ugh. This part is hardcoded for indw. if (edgeFade > 20) { edgeFade = fmax(0.0, 1.0 - ((edgeFade - 20) / 10.0)); } else { edgeFade = 1.0; } yy.setPos(x + (i - left) * dpp, y + 1); if (i % 45 == 0) { c.drawColor = col1 * 0.4 * screenFade * edgeFade; yy.pix(1, 3); c.textSize(i, xl, yl); yy.setPos(x + (i - left) * dpp - (xl * 0.5), y + 6); yy.drawText(i); } else { c.drawColor = col1 * lucent * screenFade * edgeFade; yy.pix(1, 2); } } } c.textSize(int(head), xl, yl); yy.setPos(x + indw / 2.0 - xl * 0.5, y + 6); c.drawColor = col1 * screenFade; yy.drawText(int(head)); } function float toHeading(int yaw) { if (yaw < 0) { yaw = 65535 + yaw; } return (yaw / 65535.0) * 360.0; } function drawVRot(int x, int y) { c.drawColor = col1 * lucent * screenFade; yy.drawFrame(x, y, 32, reth, 0, true); c.drawColor = col1 * screenFade; } function drawIndicators(int x, int y) { local float xl, yl; local string whs; local color on, off; whs = "WARHEAD: "; c.font = font; c.textSize("h", xl, yl); /* FIX? if (indh < yl * 3) { c.font = font; c.textSize("h", xl, yl); } */ c.textSize(whs $ "DISARMED", xl, yl); indw = xl + 20; if (level.bHighDetailMode) { on = col1 * screenFade; off = col1 * lucent * screenFade; c.drawColor = off; yy.drawFrame(x, y, indw, indh, 2, true); } else { on = col1 * screenFade; off = col1 * 0.5 * screenFade; } // Vertically center them. y += (indh - yl * 3) / 2; c.textSize(whs $ "DISARMED", xl, yl); c.drawColor = on; yy.setPos(x + 4, y + 1); switch (sl.warstat) { case WH_Auto: yy.drawText(whs $ "AUTO"); break; case WH_Armed: yy.drawText(whs $ "ARMED"); break; case WH_Disarmed: yy.drawText(whs $ "DISARMED"); break; } yy.setPos(x + 4, y + yl); if (sl.gunner == none) { c.drawColor = off; } else { c.drawColor = on; } yy.drawText("GUNNER ONBOARD"); yy.setPos(x + 4, y + yl * 2 - 1); if (sl.bAfterburn) { c.drawColor = on; } else { c.drawColor = off; } yy.drawText("AFTERBURNERS ON"); } function drawTargets() { local Pawn p; local Inventory i; local vector x, y, z; getaxes(ho.viewRotation, x, y, z); c.font = font; foreach sl.visibleCollidingActors(class'Pawn', p, 3000,, true) { drawTarget(p, x, y, z); } foreach sl.visibleCollidingActors(class'Inventory', i, 3000,, true) { drawTarget(i, x, y, z); } } /** Inspired by Botpack.GuidedWarshell. */ function drawTarget(Actor a, vector x, vector y, vector z) { local float da, dist; local int xpos, ypos; local vector dir; local Color color; dir = a.location - sl.location; dist = vsize(dir); dir = normal(dir); // 1.0 is the middle, 0.7 is edge of screen. da = dir dot x; if (da > 0.94) { if (a.isA('Inventory')) { color = col2; } else { color = col4; } xpos = 0.5 * yy.clipx * (1 + 1.4 * (dir dot y)); ypos = 0.5 * yy.clipy * (1 - 1.4 * (dir dot z)); // Closer is brighter. c.drawColor = color * screenFade * ((da - 0.94) / 0.06) * fclamp((3000.0 - dist) / 1000.0, 0.0, 1.0); yy.drawFrame(xpos - 8, ypos - 8, 16, 16, 1); yy.setPos(xpos - 8, ypos + 9); yy.drawText(int((dist * u2m)) $ "m"); //drawText("x:" $ (dir dot y) $ " y:" $ (dir dot z)); } } function gunnerDisplay(Canvas c, Bot b) { c.drawColor = col1; c.font = lgFont; c.setPos(20, yy.cy + 40); yy.drawText("bot gunner: health: " $ b.health $ " orders: " $ b.orders $ " order object: " $ b.orderObject $ " state: " $ b.getStateName() $ " move target: " $ b.moveTarget); } /** This takes the difference between the direction of the rocket and the rotation, and skews the HUD. We scale the yaw delta based on the pitch. The pitch is locked in StrangeShell.adjustGuideRotation() to 16384. That 18000 jive messes with this. */ function setSkew() { local float pitch; local float dyaw, dpitch; local float maxd; local int maxskew; maxd = 16384.0; maxskew = yy.clipx / 8; dyaw = (rotator(sl.velocity).yaw & 65535) - (sl.rotation.yaw & 65535); pitch = sl.rotation.pitch & 65535; dpitch = pitch - (rotator(sl.velocity).pitch & 65535); u.centerRotComp(dyaw); u.centerRotComp(dpitch); u.centerRotComp(pitch); // This is unecessary since we're clamping the guide rotation to // below 16384, but it looks nicer anyway. dyaw = dyaw * (1.0 - fmin(abs(pitch) / maxd, 1.0)); skewx = clamp(int(((dyaw / maxd) * maxskew) + 0.5), -maxskew, maxskew); skewy = clamp(int(((dpitch / maxd) * maxskew) + 0.5), -maxskew, maxskew); u.debug("setSkew(): skewx: " $ skewx $ " dyaw: " $ dyaw $ " pitch: " $ pitch); } /** Tick. Updates timers and stuff. */ function tick(float delta) { if (noMore()) return; register(); // Don't go any further. if (!bInit) return; // Update gauges. if (sl.bAfterburn) throLvl.update(delta, 1.5, "100"); else throLvl.update(delta, sl.thlvl, string(int(sl.thlvl * 100))); veloLvl.update(delta, vsize(sl.velocity) / sl.fullspeed, int(u2m * vsize(sl.velocity))); hullLvl.update(delta, sl.armor / sl.default.armor, int(sl.armor)); fuelLvl.update(delta, sl.fuel / sl.default.fuel, int(sl.fuel)); if (sl.fc != none) coreLvl.update(delta, float(sl.fc.getAmmo()) / sl.fc.getMaxAmmo(), sl.fc.getAmmo()); // Alarm check. if (fuelLvl.alarmOn() || hullLvl.alarmOn()) gotoState('Warning'); if (sl.bShootMode || sl.getPilot() != ho) { if (slide < yy.clipx) { slide = fmin(yy.clipx, slide + yy.clipx * 3.0 * delta); } else { // This was a hack and a crutch anyway. //updateNoShoot(delta); // No need to update anything else - the HUD is off screen. return; } } else { // Reset this. //nsFade = 0.0; if (slide > 0) { slide = fmax(0, slide - yy.clipx * 2.0 * delta); } } // Keys. if (bShowKeys) { if (keysSlide > 0) { keysSlide = max(0, keysSlide - delta * 300.0); // keys slide rate } else if (keysFade > 0) { keysFade = fmax(0.0, keysFade - delta * 0.5); // keys fade rate } } if (slide == 0 && screenFade < 1.0) { screenFade = fmin(1.0, (screenFade + delta * 0.5)); // + frand() * 0.01); } retScale += delta * fade * (vsize(sl.velocity) / ((sl.maxSpeed + sl.minSpeed) / 2.0)); if (retScale > 1.0) retScale = 0.0; setOffsets(delta); } function setOffsets(float delta) { // Handle HUD shaking. if (shakemag > 0.0) { shakemag = fmax(0.0, shakemag - delta * shakeRate); } if (sl.bAfterburn && shakemag < 3) { shakemag = 3.0; } if (shakemag > 0) { shakex = (rand(shakemag) - shakemag / 2.0); shakey = (rand(shakemag) - shakemag / 2.0); } else { shakex = 0; shakey = 0; } setSkew(); yy.setOffsets(shakex + slide + skewx, shakey + skewy); } function shake(int sev) { if (!sl.bShootMode) { // Sometimes the shaking gets out of control... shakemag = min(shakemag + sev, 90); } } function int round(float f) { return int(0.5 + f); } /** Destroy us. But first take us out of the HUD mutator list. */ function destroyed() { local Mutator m; u.debug("destroyed()", DL_Normal); if (ho != none && ho.myHUD != none) { m = ho.myHUD.HUDMutator; if (m != none) { u.debug("first hudmut: " $ m); if (m.isA('SLHUD')) { if (m == self) { ho.myHUD.HUDMutator = m.nextHUDMutator; } } else { while (m.nextHUDMutator != none) { u.debug("next hudmut: " $ m); if (m.nextHUDMutator == self) { m.nextHUDMutator = m.nextHUDMutator.nextHUDMutator; } m = m.nextHUDMutator; } } } } super.destroyed(); } // end zyU*n {(pvaporize(): shock size: U/a8 *b :*:$::${@pvaporize(): victim has authority: { *B* [?BBB[#>[B* ?%bĎ?[::$*-.*V Bb?,r*{X@*{XA*.* * Bb?,*_Db* ի?**B?BB10b  L // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLHitPuff.uc,v 1.2 2001/06/23 21:51:35 yrns Exp $ /** See Wallhit.uc. */ class SLHitPuff extends SLSmoke; simulated function beginPlay() { drawScale = default.drawScale + frand() * 0.2; super.beginPlay(); } // end [TX // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLClip.uc,v 1.1 2001/07/06 07:20:46 yrns Exp $ /** Clip object. */ class SLClip extends SLInfo; var() int max; var int rounds; var SLAmmo ammo; replication { reliable if (role == ROLE_Authority) rounds, max; } simulated function bool empty() { return (rounds == 0); } simulated function bool full() { return (rounds == max); } simulated function int getRounds() { return rounds; } simulated function int getMaxRounds() { return max; } function bool useRound() { if (rounds > 0) { rounds--; ammo.ammoAmount--; return true; } else { return false; } } ` // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLBotBrain.uc,v 1.13 2001/08/28 07:23:34 yrns Exp $ /** Contains bot behavior for riding rockets. Spawned on the server when the pilot or gunner is set. */ class SLBotBrain extends SLInfo; var StrangeShell sl; var Bot bot; var bool bIsPilot; var bool bGuiding; // The bot is controlling the rocket thru gr. var bool bEject; // An eject is recommended. /** Current goal index. For CTF, this is the flag base index. For Assault, it's the fort standard index. Both in goals[] in SkyNet. */ var byte goal; var SkyNode targetNode; // The node we're heading for. var SkyNode lastNode; // The previous node. var SkyNode launchNode; // Our first node. var bool bNoExpire; // Our target node doesn't expire. var bool bReachEject; // Eject when we reach our target node. var float targetAcq; // Time we acquired this node. var() float targetExpireTime; // Time to give up on this node and find a reachable one. var() int ejectDist; // If we get this close to our objective, eject. var() int ejectDistFlag; // Same, but just for flag carriers and flag bases. var() float randomLinkAdjust; // Random factor for nextNode(). var() float wallDeter; // The % of wall deaths which will deter us. var() float shotDeter; // The % of shot deaths which will deter us. var() float skill; // 0.0 to 1.0. Affects reflexes and turning speed. /** This only happens when there are no enemy flags at base. NOT IMPLEMENTED. FIX. */ var bool bHunting; /** E.g. somebody carrying our flag. :) */ var Actor nukeTarget; var SkyNet skynet; var float suggestedSpeed; var Util u; function postBeginPlay() { u = spawn(Class'Util', self); // Invalid goal. goal = -1; } /** The launcher usually handles this, but just in case. */ function spawnSkyNet() { foreach allActors(Class'SkyNet', skynet) break; if (skynet == none) skynet = spawn(Class'SkyNet'); } /** Called from the Strangelove, right after spawn. */ function setBot(Bot b, StrangeShell s, optional SkyNet net, optional SkyNode ln) { bot = b; sl = s; launchNode = ln; targetNode = ln; // They need all the help they can get... //skill = b.skill / 3.0; skill = (b.skill / 3.0) * 0.2 + 0.8; bIsPilot = (b == sl.getPilot()); if (bIsPilot) { bGuiding = true; skynet = net; spawnSkyNet(); } else { gotoState('GunnerBoarding'); } // Skill == reaction time. Minimum timer is 0.01. setTimer(fmax(1.0 * (1.0 - skill), 0.01), true); u.debug("setBot(): brain set for: " $ u.sname(bot) $ " skill: " $ u.chopf(skill) $ " launch node: " $ u.sname(ln)); } function timer() { // Eject if fuel low! Max throttle first. if (sl.fuel < 3 && (sl.fc == none || !(sl.fc.getAmmo() > 0))) { u.debug("timer(): No fuel! Ejecting!"); if (bIsPilot) sl.throttle = 1.0; bEject = true; } else { if (bIsPilot) { pilotBehavior(); } else { gunnerBehavior(); } } if (bEject) eject(); } /** First, set the goal. If it's valid, decide where and what to do. */ function pilotBehavior() { local SkyNode temp; if (setGoal()) { // First check for nuke targets. if (bot.moveTarget == myFlag()) { nukeTarget = bot.moveTarget; } else if (bot.enemy == myFlag().holder) { // Ramming speed!!! sl.arm(); sl.throttle = 0.1; nukeTarget = bot.enemy; } else { nukeTarget = none; } if (nukeTarget != none) { targetActor(nukeTarget); } else { if (targetNode == none) { // This might happen at a dead end node. u.debug("pilotBehavior(): no target node"); temp = closestNode(skynet.linkRadius); if (temp != lastNode) { setTargetNode(temp); } else { bEject = true; } } if (bReachEject && targetNode != none) { if (inrange(targetNode, 0.5)) { u.debug("pilotBehavior(): within range of eject node"); // So we don't kill outselves. FIX! sl.disarm(); bEject = true; } } else { /* if (inrange(skynet.goalNodes[goal], 1.5)) { u.debug("pilotBehavior(): within range of goal node"); bEject = true; } */ } // Throttle! Shoot mode! if (targetNode != none && !inrange(targetNode, 2.0) && linedup(targetNode, 0.01)) { if (suggestedSpeed == -1) sl.throttle = 0.5 + 0.5 * skill; if (bot.enemy != none) { // Switch to shoot mode! if (!sl.bShootMode) { u.debug("pilotBehavior(): going to shoot mode"); sl.goShootMode(); } } } else { if (suggestedSpeed == -1) sl.throttle = 0.1; // Man the wheel! if (sl.bShootMode) { u.debug("pilotBehavior(): returning from shoot mode"); sl.goShootMode(); } } // Handle speed. if (suggestedSpeed >= 0) { if (suggestedSpeed < 1.0) { sl.throttle = suggestedSpeed; } else { if (abs(vsize(sl.velocity) - suggestedSpeed) < 50.0) { // Hold. } else { if (vsize(sl.velocity) < suggestedSpeed) { sl.throttle += 0.1; } else { sl.throttle -= 0.1; } sl.throttle = fclamp(sl.throttle, 0.0, 1.0); } } } // Can we see the goal node? Go for it! Did we already // spot it earlier? Keep going. if (targetNode != none && !bReachEject) { temp = skynet.goalNodes[goal].randomLink(); if (temp != none && los(temp)) { u.debug("pilotBehavior(): new beeline: " $ u.sname(temp)); setTargetNode(temp); bReachEject = true; bNoExpire = true; } } // Expire the node if it's been too long. Usually means // we're doing loop de loops. if (!bNoExpire && targetNodeExpired()) { u.debug("timer(): target expired: " $ u.sname(targetNode)); setTargetNode(nextNode(targetNode, true)); } // Target it. if (targetNode != none) targetActor(targetNode); } } } /** My flag? */ function CTFFlag myFlag() { if (skynet.bCTF) { return CTFReplicationInfo(level.game.gameReplicationInfo).flagList[bot.playerReplicationInfo.team]; } return none; } /** Returns true if this flag is an enemy flag. */ function bool enemyFlag(CTFFlag flag) { return (flag != myFlag()); } /** Returns the distance to the specified actor. */ function float distto(Actor a) { return vsize(a.location - bot.location); } /** Returns true if the specified actor is within the specified range. If the range is < 10.0, it's a factor of the link radius. */ function bool inrange(Actor a, float range) { if (range < 10.0) { return (distTo(a) < range * skynet.linkRadius); } else { return (distTo(a) < range); } } /** Returns true if the bot has LOS. */ function bool los(Actor a) { u.debug("los(" $ u.sname(a) $ ")", DL_Verbose); return fastTrace(a.location, bot.location); } /** Lined up. */ function bool linedup(Actor a, optional float error) { return ((1.0 - (normal(sl.velocity) dot normal(a.location - bot.location))) <= error); } /** Turns us towards the specified actor. */ function targetActor(Actor a) { desiredRotation = adjustRot(rotator(a.location - bot.location)); } /** Same as above, but for a raw location vector. */ function targetLoc(vector l) { desiredRotation = adjustRot(rotator(l - bot.location)); } /** This sets the goal index. We may set bEject, so this needs to happen just before we test for that in pilotBehavior(). Returns true if we have a valid goal. */ function bool setGoal() { local byte oldg; // We need to compare with the new one for debugging. oldg = goal; if (skynet.getGoal(bot, goal)) { if (goal != oldg) u.debug("setGoal(): new goal is: " $ u.sname(skynet.goals[goal])); return true; } bEject = true; return false; } /** When the pilot is cleared (and a damage type is specified), it notifies us so we can update the node counts. */ function deathBy(name type) { local SkyNode n; local byte team; // Find the closer node. We'll always have a targetNode. Right? if (lastNode != none && distto(lastNode) > distto(targetNode)) n = targetNode; else n = lastNode; team = bot.playerReplicationInfo.team; switch (name) { case 'Shot': n.shotDeaths[team]++; break; case 'HitWall': n.wallDeaths++; break; } } function bool targetNodeExpired() { return ((level.timeseconds - targetAcq) > targetExpireTime); } function setTargetNode(SkyNode sn) { lastNode = targetNode; u.debug("setTargetNode(" $ u.sname(sn) $ ")"); targetNode = sn; if (targetNode != none) { targetAcq = level.timeseconds; suggestedSpeed = sn.suggestedSpeed; if (skynet.bShowLinks) { if (lastNode != none) lastNode.vis(false); targetNode.vis(true, goal); } targetNode.passes++; targetNode.teamPasses[bot.playerReplicationInfo.team]++; } } function SkyNode closestNode(float r) { return skynet.closestNode(bot.location, r); } /** The Strangelove reports a touch on the sky node. */ function nodeReached(SkyNode sn) { if (sn == targetNode) { u.debug("nodeReached(): " $ u.sname(sn)); if (sn != launchNode) // Success! Or so we think... launchNode.addLaunch(true); setTargetNode(nextNode(sn, true)); } } /** Decide which node to go to next from the current one. If random is true, we add a slight random factor in, which essentially makes close calls go either way, rather than always to the higher weight. */ function SkyNode nextNode(SkyNode cur, optional bool random, optional bool bNextNext) { local float RLA; local int i; local SkyNode next, nn; local float w, curw; // Use ours if non-zero. if (randomLinkAdjust > 0) RLA = randomLinkAdjust; else RLA = skynet.randomLinkAdjust; for (i = 0; i < arrayCount(cur.links); i++) { if (bNextNext && !los(cur.links[i])) continue; // Skip it if we just came from there? if (cur.links[i] == lastNode) continue; curw = cur.links[i].weight[goal]; if (random) curw += frand() * RLA; if (curw > w || next == none) { next = cur.links[i]; w = curw; } } // If the next node is weighted lower than the current, eject. if (next == none || w < cur.weight[goal] || avoidNode(next, bot.playerReplicationInfo.team)) { u.debug("nextNode(" $ u.sname(cur) $ "): ejecting: next: " $ u.sname(next) $ " current/next weight: " $ cur.weight[goal] $ "/" $ curw); if (!bNextNext) bEject = true; } else { /** FIX. Infinite recursion. // Skip to the next next one if we can see it. if (next != none) { nn = nextNode(next, random, true); if (nn != none) { u.debug("nextNode(): skipping " $ u.sname(next) $ " to " $ u.sname(nn)); // This is so it doesn't expire too soon. targetAcq += targetExpireTime; next = nn; } } */ } return next; } /** Return true if we don't want to go to this node, because everytime someone's tried, they've hit a wall or gotten shot down. */ function bool avoidNode(SkyNode n, byte team) { local bool avoid; avoid = ((n.wallDeaths / float(n.passes)) > wallDeter || (n.shotDeaths[team] / float(n.teamPasses[team])) > shotDeter); if (avoid) { u.debug("avoidNode(" $ u.sname(n) $ ") death ratios: wall: " $ (n.wallDeaths / float(n.passes)) $ " shot: " $ (n.shotDeaths[team] / float(n.teamPasses[team]))); // Turn off the warhead, if we can. sl.disarm(); } return avoid; } function FlagBase getFlag() { return CTFReplicationInfo(level.game.gameReplicationInfo).flagList[bot.playerReplicationInfo.team].homeBase; } /** This takes the desired rotation and slowly moves to meet it. Basically our own home brewed bRotateToDesired (which doesn't seem to work in this instance). Note we could use the full rotation rate, but this uses the yaw rate for both yaw and pitch. */ function tick(float delta) { if (sl != none) { setRotation(rotation + sturn(desiredRotation, rotation, int(delta * skill * rotationRate.yaw))); } } /** This compensates for the sliding of the rocket. */ function rotator adjustRot(rotator r) { return (r - sturn(rotator(sl.velocity), r, 0, 16384)); } /** Given a new and old rotation, it figures the smallest deltas to get to the new rotation. If max is given, it limits the turn by that much. The limit optional arg is used when trying to compensate for momentum. I can't explain it in less than 500 words, but if you don't drop the compensation when the delta is over 90 degrees, it goes in the direct opposite direction. */ function rotator sturn(rotator newr, rotator oldr, optional int max, optional int limit) { local int dyaw, dpitch; local rotator turn; dyaw = (newr.yaw & 65535) - (oldr.yaw & 65535); dpitch = (newr.pitch & 65535) - (oldr.pitch & 65535); // If it's more than the limit, zero that component. if (limit > 0) { if (abs(dyaw) > limit) dyaw = 0; if (abs(dpitch) > limit) dpitch = 0; } if (dyaw < -32768) dyaw += 65536; else if (dyaw > 32768) dyaw -= 65536; if (dpitch < -32768) dpitch += 65536; else if (dpitch > 32768) dpitch -= 65536; if (max > 0) { dyaw = fmin(dyaw, max); dpitch = fmin(dpitch, max); } turn.yaw = dyaw; turn.pitch = dpitch; turn.roll = 0; return turn; } function rotator getGuidedRotation() { return rotation; } /** Decide what the bot does, as far as ejecting, etc., as a gunner. */ function gunnerBehavior() { bot.bCanFly = true; bot.bCanWalk = false; bot.bAdvancedTactics = false; //bot.bCanDuck = true; //bot.moveTarget = -1; // If the rider ejects or gets killed, eject! if (sl.getPilot() == none && !isInState('GunnerEject')) gotoState('GunnerEject'); // Don't jump off unless we're less than 30' (360 uu) off the ground. if (sl.fastTrace(sl.location + vect(0, 0, -1) * 360)) { u.debug("gunnerBehavior(): over 360 off ground", DL_Verbose); } else { // We have the flag! Even if we're set not to obey orders, eject if we get within flag base dist. if (skynet.bCTF && bot.playerReplicationInfo.hasFlag != none && inrange(getFlag(), ejectDistFlag)) { u.debug("gunnerBehavior(): eject! close to flag base"); eject(); // We're in eject distance of something we want. } else if (bot.moveTarget != none && bot.moveTarget != sl.getPilot() && inrange(bot.moveTarget, ejectDist)) { // Eject if we're obeying orders (and they're not 'follow' // orders) or we're out of ammo and our movetarget is a weapon or ammo. if ((sl.bBotsObeyOrders && !(bot.orders == 'Follow' && bot.orderObject == sl.getPilot())) || ((bot.moveTarget.isA('Weapon') || bot.moveTarget.isA('Ammo')) && bot.weapon != none && (bot.weapon.bMeleeWeapon || bot.weapon.ammoType.ammoAmount == 0))) { u.debug("gunnerBehavior(): eject! within range of movetarget: " $ u.sname(bot.moveTarget)); eject(); } } } } /** This state makes it so they don't jump off immediately. */ state GunnerBoarding { ignores gunnerBehavior; begin: sleep(3.0); gotoState(''); } /** Give them some response time for ejecting. This only happens if the rider ejects or is killed. */ state GunnerEject { ignores gunnerBehavior; begin: sleep(1.5); if (sl.gunner != none) eject(); } function eject() { sl.eject(bot); //bot.gotoState('Roaming', 'PreBegin'); bot.whatToDoNext('', ''); } function destroyed() { u.debug("destroyed()"); if (skynet != none) { if (skynet.bShowLinks) { if (targetNode != none) { targetNode.vis(false); } } } else { u.debug("destroyed(): skynet == none"); } super.destroyed(); } // end Za!z;3::$3-a    3v! v!#a!e? gbd  YG // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SLAmmo.uc,v 1.6 2001/07/09 10:01:21 yrns Exp $ /** This ext_ends the UT ammo concept by using clips of ammo which are stored as separate objects in an array. This allows easier handling of different ammo types. */ class SLAmmo extends TournamentAmmo abstract config (SLV2); // DATA // var() bool bClipped; var() Class clipClass; var SLClip clips[32]; var int clipnum; var() int maxClips; var() config int configMaxAmmo; var() Class spentClip; var bool bStored; var int storeAmmo, storeMax; var Util u; // REPLICATION // replication { reliable if (bNetOwner && role == ROLE_Authority) clipnum, clips; } // METHODS // function preBeginPlay() { super.preBeginPlay(); u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); if (configMaxAmmo > 0) maxAmmo = configMaxAmmo; } /** This shows, on a ChallengeHUD, the clip ammo. The master always calls this first. simulated function setRenderMax(SLClip c, bool slave) { if (bClipped && c != none) { if (bStored) { u.err("setRenderMax(): Already stored ammo counts!"); } else { if (!slave) { storeAmmo = ammoAmount; storeMax = maxAmmo; ammoAmount = c.getRounds(); maxAmmo = c.getMaxRounds(); bStored = true; } else { ammoAmount += c.getRounds(); maxAmmo += c.getMaxRounds(); } } } } */ /** Resets the true ammo counts. simulated function unsetRenderMax(SLClip c, bool slave) { if (bClipped && c != none && !slave) { if (bStored) { ammoAmount = storeAmmo; maxAmmo = storeMax; bStored = false; } else { u.err("unsetRenderMax(): No stored ammo counts!"); } } } */ simulated function int getMaxAmmo() { return maxAmmo; } simulated function int getAmmo() { return ammoAmount; } simulated function int getClips() { return clipnum; } /** Takes a clip off the back of the array and returns it to the weapon. */ function SLClip reload() { local int i; local SLClip fresh; if (bClipped) { if (clipnum > 0) { // The last clip is the best one. i = clipnum - 1; fresh = clips[i]; clips[i] = none; clipnum--; } return fresh; } return none; } /** Make sure we don't use this on a clipped weapon. */ function bool useAmmo(int num) { if (!bClipped) return super.useAmmo(num); return false; } function bool addAmmo(int count) { if (count > 0) { if (bClipped) { addNewClips(clipClass, count); } else { return super.addAmmo(count); } } return false; } function weaponSpawned() { ammoAmount = 0; } function SLClip defaultClip() { return newClip(clipClass); } function bool handlePickupQuery(Inventory i) { if (bClipped) { if (class == i.class) { if (clipnum < maxClips) { pickupMessages(i, Pawn(owner)); i.playSound(i.pickupSound); addNewClips(SLAmmo(i).clipClass, SLAmmo(i).ammoAmount); i.setRespawn(); } return true; } } if (inventory == none) return false; return super.handlePickupQuery(i); } /** This is used to add a clip. Non-full clips go to the beginning of the array and will be used last. */ function bool addClip(SLClip clip) { local int where; if (clipnum < maxClips && !clip.empty()) { if (clip.full()) // Last. where = clipnum; else where = 0; insertClip(clip, where); return true; } return false; } function insertClip(SLClip clip, int index) { local int i; for (i = (clipnum - 2); i >= index; i--) { clips[i + 1] = clips[i]; } clips[index] = clip; clipnum++; } function addNewClips(Class cc, int no) { local int i; while (i < no && clipnum < maxClips) { addClip(newClip(cc)); i++; } } function SLClip newClip(Class cc) { local SLClip clip; clip = spawn(cc, owner); clip.rounds = clip.getMaxRounds(); // Legacy. ammoAmount += clip.getRounds(); clip.ammo = self; return clip; } function pickupMessages(Inventory item, Pawn p) { if (level.game.localLog != none) level.game.localLog.logPickup(item, p); if (level.game.worldLog != none) level.game.worldLog.logPickup(item, p); if (item.pickupMessageClass == none) p.clientMessage(item.pickupMessage, 'Pickup'); else p.receiveLocalizedMessage(item.pickupMessageClass, 0, none, none, item.class); } // end otH_YH/a0 t\,0 10/a0 u!+>\,0 10\& kSduAc- :-daSQ ,,O %O Q daxwd*d$d@?Q O ]?-9#??%daF .daF P#?d$ j // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SkyNodeBeam.uc,v 1.4 2001/07/12 08:06:16 yrns Exp $ /** Shows sky node links, for debugging. */ class SkyNodeBeam extends Effects; O iD // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SkyNode.uc,v 1.8 2001/08/06 23:21:58 yrns Exp $ /** Nav point in the sky. */ class SkyNode extends NavigationPoint; // DATA // var() float linkRadius; var() float randomLinkAdjust; var() float suggestedSpeed; var() bool bOffensiveNode; // Used in racing. var() bool bGateNode; var() bool bFinalNode; var SkyNode links[5]; var float ldists[5]; var SkyNodeBeam beams[5]; var float weight[16]; var bool bWeighed; var int weighDepth; /** Keep track of launches so we can decide whether or not to try on this one again. We keep the same launch data for both teams. It's an anti-stupidity thing. Same for wall deathes, but not shots. */ var int launches; var int launchSxs; var int passes; // No. of times somebody's passed us. var int teamPasses[4]; // Same as above, but for teams. var int wallDeaths; // Times we've ran into a wall on this node. var int shotDeaths[4]; // Times we've been shot down, per team. var SkyNode nextSkyNode; var SkyNode prevSkyNode; var NavigationPoint navPoint; // Track nodes we're welding to us, to avg. our position. var vector weldLoc; var int weldSum; var Util u; // METHODS // function postBeginPlay() { u = spawn(Class'Util', self); } function float getWeight(byte team) { return weight[team]; } function weld(vector l) { weldSum++; weldLoc += l; } function setWeldLoc() { if (weldSum > 0) { setLocation((location + weldLoc) / (weldSum + 1.0)); } } /** Gets called by the launcher at launch. If they reached their second node, they call us a success. */ function addLaunch(optional bool bSuccess) { if (bSuccess) launchSxs++; else launches++; } /** Returns the perc. of launch successes with this node. We give the node the benefit of the doubt if it hasn't been used yet. */ function float launchWeight() { return ((launchSxs + 1) / float(launches + 1)); } function SkyNode randomLink() { local int i; i = rand(arrayCount(links)); // The links may be none, but they're sorted. while (links[i] == none && i > 0) i--; u.debug("randomLink(): " $ u.sname(links[i]), DL_Verbose); return links[i]; } /** Returns true on success. False if no links were created. */ function bool linkUp(float r) { local SkyNode sn; local int i; local float dist; local bool bNone; bNone = true; // Use mine if it's set. if (linkRadius > 0) r = linkRadius; foreach radiusActors(Class'SkyNode', sn, r) { if (sn != self && fastTrace(sn.location, location)) { dist = vsize(location - sn.location); bNone = false; for (i = 0; i < arrayCount(links); i++) { if (dist < ldists[i] || ldists[i] == 0) { insertLink(i, sn, dist); break; } } } } return !bNone; } /** Used for debugging. */ function showBeams(int goal) { local int i; local SkyNodeBeam beam; local float w, hi, lo; for (i = 0; i < arrayCount(links); i++) { if (beams[i] == none) { w = links[i].weight[goal]; if (i == 0) { hi = w; lo = w; } else if (w < lo) { lo = w; } else if (w > hi) { hi = w; } beams[i] = spawn(Class'SkyNodeBeam',,,location, rotator(links[i].location - location)); beams[i].drawScale = ldists[i] / 100.0; } } for (i = 0; i < arrayCount(links); i++) { if (beams[i] != none) { beams[i].ambientGlow = 255 * ((links[i].weight[goal] - lo) / (hi - lo)); beams[i].scaleGlow = beams[i].ambientGlow; beams[i].lightBrightness = beams[i].ambientGlow; } } } /** Used for debugging. */ function hideBeams() { local int i; for (i = 0; i < arrayCount(links); i++) { if (beams[i] != none) beams[i].destroy(); } } function insertLink(int index, SkyNode n, float dist) { local int i; for (i = arrayCount(links) - 2; i >= index; i--) { if (links[i] != none) { links[i + 1] = links[i]; ldists[i + 1] = ldists[i]; } } links[index] = n; ldists[index] = dist; } function vis(bool on, optional int goal) { if (!on) hideBeams(); else showBeams(goal); } function destroyed() { u.debug("destroyed()", DL_Verbose); if (prevSkyNode != none) prevSkyNode.nextSkyNode = nextSkyNode; } // end j // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SkyNet.uc,v 1.11 2001/08/28 07:23:35 yrns Exp $ /** SkyNet. Spawned on the server to build the sky node network. Built slowly over time in latent code so we don't choke. */ class SkyNet extends SLInfo config (SLV2); // DATA // // When we started. var float buildStamp; // Set based on gametype in postBeginPlay(). var bool bCTF, bAss; // Tag given to auto-spawned nodes. var() name autotag; // Used during auto-spawning. var NavigationPoint autonp; var SkyNode firstsn, autosn; var int i; /** Map type. 0 == no sky nodes. 1 == tagged path nodes. 2 == sky nodes. */ var int type; var byte goalnum; var Actor goals[16]; var SkyNode goalNodes[16]; var float maxGoalDist[16]; var int nodeSum; var() float weldRadius; var() float linkRadius; // The angle between a launch node and the next one that is "launchable". See goodNode(). var() float goodAngle; // Set when we've finished building. var bool bBuilt; // For visual debugging. var() config bool bShowNodes; var() config bool bShowLinks; // So we can do debugging just for the sky net. var() config bool bDebug; var() bool bNoAutoWeightNodes; var() float randomLinkAdjust; var() int laps; var Util u; // METHODS // function postBeginPlay() { u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); if (bDebug) u.setOutputLevel(DL_Verbose); if (!properGame()) { u.err("postBeginPlay(): Only usable in CTF or Assault. Destroying."); destroy(); } if (bAss) // 16 possible fort standards. goalnum = Assault(level.game).numForts; else // Only 4 flags. goalnum = 4; setMapType(); u.debug("postBeginPlay(): map type: " $ type); } function setMapType() { local SkyNode sn; local NavigationPoint np; foreach allActors(Class'SkyNode', sn) { type = 3; return; } foreach allActors(Class'NavigationPoint', np, 'SKYNODE') { type = 2; return; } // No SL support. type = 1; } /** Currently only works with CTF and Assault. */ function bool properGame() { if (level.game.isA('CTFGame')) bCTF = true; if (level.game.isA('Assault')) bAss = true; return (bCTF || bAss); } /** Pick a random goal. The Optional args are for excluding a specific goal. Not currently used. */ function byte randomGoal(optional bool bEx, optional byte exgoal) { local int i, pnum; local int poss[16]; for (i = 0; i < goalnum; i++) { if (goals[i] != none && !(bEx && i == exgoal)) { poss[pnum] = i; pnum++; } } if (pnum > 0) { return poss[rand(pnum)]; } else { u.err("randomGoal(): no possible goals!"); return 0; } } /** Returns the first node near us that has a positive launch record. */ function SkyNode findLaunchNode(Bot b) { local int team; local SkyNode sn, ln; local byte goal; local float cw, w; // Init. goal = -1; // If we are close to where we want to be, don't launch! if (getGoal(b, goal)) { if (vsize(goalNodes[goal].location - b.location) < linkRadius * 2.0) { u.debug("findLaunchNode(): in range of goal"); return none; } } // Find the heaviest, good node. foreach b.radiusActors(Class'SkyNode', sn, linkRadius) { if (sn.launchWeight() > 0.50) { // Don't use this one if we'll have to do a 180 once we reach it. if (goodNode(sn, b)) { if (goal == -1) { // No goal? Just use the first one. ln = sn; break; } else { cw = sn.getWeight(goal); if (ln == none) { ln = sn; w = cw; } else { if (cw > w) { ln = sn; w = cw; } } } } } } if (ln != none) { u.debug("findLaunchNode(): node: " $ u.sname(ln)); return ln; } else { u.debug("findLaunchNode(): no good nodes"); return none; } } /** This sets the goal index. We may set bEject, so this needs to happen just before we test for that in pilotBehavior(). Returns true if we have a valid goal. */ function bool getGoal(Bot bot, out byte goal) { local byte team; local FortStandard fort; local int i; team = bot.playerReplicationInfo.team; // Set our goal! if (bCTF) { // Are we carrying a flag? if (bot.playerReplicationInfo.hasFlag != none) { goal = team; } else { // Go for an enemy flag! //goal = randomGoal(true, team); goal = findEnemyFlagGoal(team); } } else if (bAss) { fort = Assault(level.game).bestFort; if (fort != none) { if (FortStandard(goals[goal]) != fort) { // Find the new fort index. for (i = 0; i < goalnum; i++) { if (goals[i] == fort) { goal = i; break; } } } } else { u.err("setGoal(): No best fort. Ejecting."); return false; } } return true; } /** FIX this. */ function byte findEnemyFlagGoal(byte team) { local int i; for (i = 0; i < goalnum; i++) { if (i != team) { // && flagAtBase(i)) { return i; } } } /** Returns true if the flag is safe and sound. */ function bool flagAtBase(byte team) { return CTFReplicationInfo(level.game.gameReplicationInfo).flagList[team].bHome; } /** Return true if there is a node after n which is roughly in a continuous direction from the bot and not blocked. */ function bool goodNode(SkyNode n, Bot b) { local vector ton; if (n != none) { u.debug("goodNode(" $ u.sname(n) $ ", " $ u.sname(b) $ ")"); ton = normal(n.location - b.location); for (i = 0; i < arrayCount(n.links); i++) { if (n.links[i] != none && fastTrace(n.links[i].location, n.location) && (ton dot normal(n.location - n.links[i].location)) > goodAngle) { return true; } } } return false; } function SkyNode closestNode(vector loc, float radius) { local SkyNode sn, closest; local float dist; foreach radiusActors(Class'SkyNode', sn, radius, loc) { if (closest == none || vsize(sn.location - closest.location) < dist) closest = sn; } return closest; } /** Build our sky network. */ auto state BuildSkyNet { begin: u.debug("BuildSkyNet:begin: Building sky network.", DL_Normal); buildStamp = level.timeseconds; // Need to auto-spawn sky nodes? if (type == 1 || type == 2) { u.debug("BuildSkyNet:begin: Auto-spawning nodes.", DL_Normal); autonp = level.navigationPointList; while (autonp != none) { // Catch our CTF goals here. if (bCTF && autonp.isA('FlagBase')) goals[FlagBase(autonp).team] = autonp; // If type 2, only spawn nodes for tagged path nodes. if (type == 1 || (type == 2 && autosn.tag == 'SKYNODE')) { autosn = autoSkyNode(autonp, autosn); nodeSum++; } autonp = autonp.nextNavigationPoint; sleep(0.0); } // Keep track of the last (which will be first). firstsn = autosn; u.debug("BuildSkyNet:begin: firstsn: " $ u.sname(firstsn)); u.debug("BuildSkyNet:begin: Rising sky nodes.", DL_Normal); autosn = firstsn; while (autosn != none) { autosn.setWeldLoc(); riseSkyNode(autosn); autosn = autosn.nextSkyNode; sleep(0.0); } } // Assault goals done separately here. Copy them over. if (bAss) { for (i = 0; i < goalnum; i++) { goals[i] = Assault(level.game).fort[i]; } } linkNodes: u.debug("BuildSkyNet:linkNodes: Linking sky nodes.", DL_Normal); autosn = firstsn; while (autosn != none) { // Destroy it if it has no links. if (!autosn.linkUp(linkRadius)) { // Need to keep a first one. if (autosn == firstsn) firstsn = autosn.nextSkyNode; autosn.destroy(); } autosn = autosn.nextSkyNode; sleep(0.0); } weightNodes: // Weigh the links, depending on gametype. if (!bNoAutoWeightNodes) { u.debug("BuildSkyNet:weightNodes: Weighting sky nodes.", DL_Normal); // Assign goal nodes. for (i = 0; i < goalnum; i++) { if (goals[i] != none) goalNodes[i] = closestNode(goals[i].location, linkRadius); u.debug("BuildSkyNet:weightNodes: new goal node: " $ u.sname(goalNodes[i]) $ " for: " $ u.sname(goals[i])); } // Set the weight to the distance first. autosn = firstsn; while (autosn != none) { for (i = 0; i < goalnum; i++) { if (goalNodes[i] != none) { autosn.weight[i] = vsize(goalNodes[i].location - autosn.location); u.debug("BuildSkyNet:weightNodes: " $ u.sname(autosn) $ ": distance to goal node " $ i $ ": " $ autosn.weight[i]); if (autosn.weight[i] > maxGoalDist[i]) maxGoalDist[i] = autosn.weight[i]; } } sleep(0.0); autosn = autosn.nextSkyNode; } // Now divide by the max and subtract from 1.0 to get the weight. u.debug("BuildSkyNet:weightNodes: dividing distances: firstsn: " $ u.sname(firstsn)); autosn = firstsn; while (autosn != none) { for (i = 0; i < goalnum; i++) { if (goalNodes[i] != none) { autosn.weight[i] = 1.0 - (autosn.weight[i] / maxGoalDist[i]); u.debug("BuildSkyNet:weightNodes: " $ u.sname(autosn) $ ": goal: " $ i $ " weight: " $ autosn.weight[i]); } } sleep(0.0); autosn = autosn.nextSkyNode; } } // End. u.debug("BuildSkyNet:begin: Built network in " $ u.sf(level.timeseconds - buildStamp) $ " seconds.", DL_Normal); bBuilt = true; } // End BuildSkyNet. function SkyNode autoSkyNode(NavigationPoint np, SkyNode lastsn) { local SkyNode n; // Don't weld goal nodes. if (!isGoalNavPoint(np)) { foreach np.radiusActors(Class'SkyNode', n, weldRadius) { u.debug("autoSkyNode(): welded " $ u.sname(np) $ " to " $ u.sname(n)); n.weld(np.location); // Return the last one, for setting firstsn. return lastsn; } } n = spawn(Class'SkyNode',, 'AutoSkyNode', np.location); if (n == none) { u.err("autoSkyNode(): spawn failed for " $ u.sname(np)); return lastsn; } if (bShowNodes) n.bHidden = false; n.navPoint = np; n.nextSkyNode = lastsn; if (lastsn != none) lastsn.prevSkyNode = n; u.debug("autoSkyNode(): new skynode for " $ u.sname(np)); return n; } function bool isGoalNavPoint(NavigationPoint np) { return ((bASS && np.isA('FortStandard')) || (bCTF && np.isA('FlagBase'))); } function linkNodes() { local SkyNode last, n; foreach allActors(Class'SkyNode', n) { n.nextSkyNode = last; last = n; } } function riseSkyNode(SkyNode n) { local vector down, up; // Don't rise goal nodes. if (!isGoalNavPoint(n.navPoint)) { down = roof(n.navPoint, -500); up = roof(n.navPoint, 500); n.setLocation(down + (up - down) * 0.5); } } function vector roof(Actor a, float dist) { local Actor hit; local vector end, hl, hn; end = a.location + vect(0, 0, 1) * dist; hit = a.trace(hl, hn, end, a.location, false); if (hit != none) return hl; else return end; } // end \ // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: SidearmAmmo.uc,v 1.3 2001/07/09 10:01:21 yrns Exp $ /** Sidearm ammo. */ class SidearmAmmo extends SLAmmo; // end ^ DZa $$:-ml$fC5postBeginPlay(): Only usable in CTF or Assault. Destroying.a-_A=.A$H+ppostBeginPlay(): map type: S\ vKMW61|WARHEAD: sahL~ap|DISARMEDL~_DL?,-xb(qb(b(dq'Ky_v,'!xb(qb(b(?yD?v~?,?,ap|DISARMEDL~x K,y& 1 $p|AUTO $p|ARMED $p|DISARMED  K,D?y~pr *qxGUNNER ONBOARD& K,D?y~?,?& -LxqAFTERBURNERS ON Y // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Sidearm.uc,v 1.12 2001/07/13 18:00:29 yrns Exp $ /** Racing sidearm. AKA "yellow jacket". */ class Sidearm extends SLWeapon; var() string deathStr[5]; /** Some of the sidearm anims don't return to zero. FIX this. Should be included in SLWeapon? */ simulated function tweenToStill() { tweenAnim('still', 0.3); if (slave != none) slave.tweenToStill(); } /** We randomize the death messages here. This is a little overkill. FIX. */ function processTraceHit(Actor a, vector hl, vector hn, vector x, vector y, vector z) { // Leave in the %k. Goddamn needs the default. default.deathMessage = u.parseKillString(deathStr[rand(arrayCount(deathStr))], owner, a, false); super.processTraceHit(a, hl, hn, x, y, z); } // end * // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Rocket2.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** Team2 rocket. Sports different exhaust and mesh. */ class Rocket2 extends StrangeShell; // end sG #6td7 \l j!Bwq *q a ~KW K(5db(b(d'Kf, %'b( WbH@?ZaltFire()$Xa/!% w]*]. FA ScL 0?;A cfT '=( X // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: ProjectileSN.uc,v 1.5 2001/07/12 08:13:12 yrns Exp $ /** Projectiles launched from the back of a Strangelove get their velocities modified to be relative to the speed of the rocket. This SpawnNotify catches Projectile spawns and does so. */ class ProjectileSN extends SpawnNotify; var() float limitMult; var Util u; function postBeginPlay() { u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); } function Actor spawnNotification(Actor a) { local Pawn inst; local StrangeShell s; local float limit; local float da; inst = a.instigator; // No instigator? Don't bother. if (inst != none) { foreach allActors(Class'StrangeShell', s) { // From the rider or gunner? if (inst != none && (s.getPilot() == inst || s.gunner == inst)) { // We limit the final vel. with this. limit = vsize(a.velocity) * limitMult; // 1.0 is aligned. -1.0 is pointing directly away from each other. da = normal(a.velocity) dot normal(s.velocity); if (da > 0) { a.velocity += normal(a.velocity) * vsize(s.velocity) * da; if (vsize(a.velocity) > limit) a.velocity = normal(a.velocity) * limit; } u.debug("spawnNotification(): proj: " $ a $ " shellv: " $ u.sf(vsize(s.velocity)) $ " newv: " $ u.sf(vsize(a.velocity)) $ " limit: " $ u.sf(limit)); break; } } } return a; } // end |) // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: NullWeapon.uc,v 1.12 2001/07/13 18:00:41 yrns Exp $ /** Invisible weapon that makes is so the pilot can't shoot while steering, and intercepts fire/altfire commands. */ class NullWeapon extends SLWeapon; // DATA // var StrangeShell sl; // METHODS // simulated function postBeginPlay() { // Use the Konglauncher defaults to tidy up the HUD. Is there a // way to avoid this? FIX. autoSwitchPriority = 10; inventoryGroup = 10; statusIcon = Class'Konglauncher'.default.statusIcon; projectileClass = Class'Konglauncher'.default.projectileClass; altProjectileClass = Class'Konglauncher'.default.altProjectileClass; super.postBeginPlay(); } /** This function fakes the ammo display to show SL ammo. What about takeover situations where the rider has no Konglauncher? No takeovers yet. FIX. */ function fakeAmmo() { local Weapon kong; kong = Weapon(Pawn(owner).findInventoryType(Class'SLV2.Konglauncher')); if (kong != none) ammoType = kong.ammoType; } simulated function postRender(Canvas c) { bOwnsCrosshair = true; } function fire(float v) { u.debug("fire()", DL_Normal); if (!owner.isA('Bot') && sl != none) sl.afterburn(); } function altFire(float v) { u.debug("altFire()", DL_Normal); if (!owner.isA('Bot') && sl != none) sl.eject(Pawn(owner)); } simulated function bool clientFire(float v) { return false; } simulated function renderOverlays(Canvas canvas) { // Nothing. } state ClientActive { simulated function beginState() { // Nothing. } } state Idle { function bool putDown() { return global.putDown(); } } auto state Active { function bool putDown() { return global.putDown(); } function fire(float v) { global.fire(v); } function altFire(float v) { global.altfire(v); } function beginState() { // Nothing. } function endState() { // Nothing. } begin: // Nothing. } state DownWeapon { function bool putDown() { return global.putDown(); } } simulated function setHand(float hand) { // Nothing. } /** Next/prev weapon toggles the warhead status. */ function bool putDown() { if (sl != none) sl.switchWarhead(); bChangeWeapon = false; Pawn(owner).pendingWeapon = none; return true; } // These keep those accessed none warnings at bay. simulated function playSelect() { // Nothing. No mesh. No ammo. } simulated function tweenToStill() { // No mesh. Nothing. } function bringUp() { // Nothing. } // end |JNB44J%JJ?JGC w // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: LaunchPuff.uc,v 1.4 2001/06/30 22:37:49 yrns Exp $ /** This gets spawned on the client when the Konglauncher is fired. */ class LaunchPuff extends SLSmoke; // DATA // // The fade intro time in seconds. var() float intro; // Our own substitute for the builtin lifeSpan stuff. Used also in // Contrail.uc. We do this so we can change the lifeSpan on the fly. var() float life; var float elapsed; // This means our lifespan has been cut short. var bool bShorted; // METHODS // simulated function beginPlay() { velocity = vector(rotation) * 15.0; elapsed = life; } simulated function tick(float d) { if (level.bDropDetail && !bShorted) { life /= 4.0; elapsed /= 4.0; intro /= 2.0; bShorted = true; } if (elapsed > (life - intro)) { // For the intro time we are expanding and solidifying. scaleGlow = (life - elapsed) / intro; drawScale = default.drawScale * scaleGlow; } else { // The rest we are slowly expanding and fading out. scaleGlow = elapsed / (life - intro); drawScale = default.drawScale + 1.0 * (1.0 - scaleGlow); } lightBrightness = scaleGlow * default.lightBrightness; elapsed -= d; if (elapsed < 0) destroy(); } // end e ] T$ $  , , ,e  H AA?w*?a/!-M '-'U. BoOV1w*-G p'LwU*UU*-j' Cdmd $w*-ja @-^@-J o g--q-^' -I kj-H ld-j r*-^r* wU*F U-r -J -o -^(1w*ea F PU U _E D (a U g_p(uwe*e  U  g?,e _E D c  g_m PU c -H  -I 6m 6U -(a  c m eu WWVa3 -' HUc // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Konglauncher.uc,v 1.21 2001/08/05 22:41:55 yrns Exp $ /** Konglauncher. Launches ridable Stranglove rockets. Weapon class. */ class Konglauncher extends SLWeapon config (SLV2); // DATA // var Fuelcore fc; // Keeps track of fuel cores for the ret HUD. var vector launchv; // Owner velocity at launch. var float lastFired; // Bots should only fire every so often... var SkyNet skynet; var SkyNode launchsn; // Our onramp onto the network. var() config bool bNeverStay; var bool bClear; var float clearanceStamp; var() config string teamSLClass[4]; // REPLICATION replication { reliable if (role == ROLE_Authority && bNetOwner) bClear; } // METHODS // /** Set the projectile classes based on team. */ function bringUp() { local PlayerReplicationInfo pri; // Teams alternate rocket types. if (Pawn(owner) != none) { pri = Pawn(owner).playerReplicationInfo; u.debug("bringUp(): setting proj class: team: " $ pri.team); projectileClass = Class(dynamicLoadObject(teamSLClass[pri.team], Class'Class')); altProjectileClass = projectileClass; } super.bringup(); } state Idle { /** Check for entrances onto the sky network. */ simulated function timer() { // Do the idle anim thing. super.timer(); // Only alt-fire if are aren't defending or following, // and we're not under attack. if (owner.isA('Bot') && useAltFire(Bot(owner))) { u.debug("Idle:timer(): skynet: " $ u.sname(skynet)); if (skynet != none) { if (skynet.bBuilt) { launchsn = skynet.findLaunchNode(Bot(owner)); // Do we have a clear path to it? if (launchsn != none && fastTrace(launchsn.location, owner.location)) { launchsn.addLaunch(); // There might be a smoother way to adjust the rotation. FIX? owner.setRotation(rotator(launchsn.location - owner.location)); altFire(0); } } } else { spawnSkyNet(); } } } } /** Function for bots. Returns true if they should ride a rocket. Not simulated so we don't need the role check. */ function bool useAltFire(Bot b) { local BotReplicationInfo bri; local StrangeShell sl; u.debug("useAltFire(): ammo: " $ ammoLeft() $ " enemy: " $ b.enemy $ " orders: " $ b.orders); bri = BotReplicationInfo(b.playerReplicationInfo); // Only used in CTF and assault gametypes. And for assault, only if they're attacking. if (!(level.game.isA('CTFGame') || (level.game.isA('Assault') && bri.team == Assault(level.game).attacker.teamIndex))) return false; if (!ammoLeft() || b.enemy != none) return false; // Which? FIX. //if (b.orders == 'Defend' || b.orders == 'Follow') if (bri.realOrders == 'Defend' || bri.realOrders == 'Follow') return false; // Not if we're already riding one. foreach b.radiusActors(Class'StrangeShell', sl, vsize(Class(projectileClass).default.pilotOffset) * 2.0) { if (sl.getPilot() == b || sl.getGunner() == b) return false; } return true; } function spawnSkyNet() { foreach allActors(Class'SkyNet', skynet) break; // Spawn it! if (skynet == none) skynet = spawn(Class'SkyNet'); } /** Needed to add fuel core count to the HUD. */ simulated function setRetMsgs() { super.setRetMsgs(); if (!bClear && (level.timeseconds - clearanceStamp < 1.5)) { retmsg = "no clearance!"; } if (fc == none) fc = Fuelcore(Pawn(owner).findInventoryType(class'Fuelcore')); if (fc != none && fc.ammoAmount > 0) retmsg2 = "cores " $ fc.ammoAmount; } /** The Redeemer never stays. */ function setWeaponStay() { if (bNeverStay) bWeaponStay = false; else super.setWeaponStay(); } /** From Botpack.WarheadLauncher. */ function float rateSelf(out int bUseAltMode) { local Bot me; // Alt-fire handled separately in the idle loop. bUseAltMode = 0; me = Bot(owner); if (me == none || !ammoLeft() || (level.timeseconds - lastFired) < 2.0) return -2.0; // Keep the weapon out until we're under fire. We may alt-fire to // get on the sky network. if (useAltFire(me)) return 1.0; if (me.enemy == none) return -2.0; if (vsize(me.enemy.location - me.location) > 800 && fastTrace(me.enemy.location, me.location)) { u.debug("rateSelf(): enemy past det range: " $ u.sname(me.enemy)); return 2.0; } return -1.0; } function float suggestAttackStyle() { return -1.0; } /** Launch puffs spawned on the server. */ function spawnPuff() { local vector x, y, z; if (level.bHighDetailMode && !level.bDropDetail && level.netmode != NM_DedicatedServer) { // Place it in view. getAxes(owner.rotation, x, y, z); spawn(class'LaunchPuff',,, owner.location + x * 80, rotation); } } /** If we had a successful launch, spawn the puff. */ function fire(float v) { lastFired = level.timeseconds; super.fire(v); if (lastProj != none) spawnPuff(); } /** Special behavior for the Konglauncher alt-fire. */ function altFire(float v) { local bool bAmmo; bAmmo = ammoLeft(); if (bAmmo) { spawnProjOffset = -(Class(altProjectileClass).default.pilotOffset); bSetProjOwner = true; lastProj = none; } // All just to get the click... super.altFire(v); if (bAmmo) { if (lastProj != none) { StrangeShell(lastProj).setPilot(Pawn(owner), skynet, launchsn); } else { clearWarning(); clearanceStamp = level.timeseconds; } bClear = (lastProj != none); // Unset these. Dumbs don't need them. spawnProjOffset = vect(0, 0, 0); bSetProjOwner = false; } } /** Plays the clearance warning on the client. Is there a way to quiet this? FIX. */ function clearWarning() { if (owner.isA('PlayerPawn')) PlayerPawn(owner).clientPlaySound(Class'StrangeShell'.default.warnSound, false, true); } simulated function bool clientFire(float v) { // Reset it. launchv = vect(0, 0, 0); return super.clientFire(v); } simulated function bool clientAltFire(float v) { if (super.clientAltFire(v)) { clearanceStamp = level.timeseconds; launchv = owner.velocity; return true; } else { return false; } } // end IY Z // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: JumpDetector.uc,v 1.4 2001/07/07 06:20:36 yrns Exp $ /** Used for jump detection, so we can eject riders and gunners. Checks for jump boots in the inventory and tries to manage the bCountJumps variable which we need to be set to true. After jump boots where off they set it to false. Only used in standalone games. */ class JumpDetector extends TournamentPickup; var StrangeShell sl; var Util u; function postBeginPlay() { u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); super.postBeginPlay(); } state Activated { begin: Pawn(owner).bCountJumps = true; } /** This is called from PlayerPawn.doJump(), normally used for jump boots. bCountJumps is set for the rider every tick so any jump boots don't get in the way. Once we've ejected, we look for jump boots. If none, set it to false. */ function ownerJumped() { u.debug("ownerJumped()"); if (sl != none) { sl.eject(owner, 1); } if (inventory != none) { inventory.ownerJumped(); } } /** We could make this a timer but it's for standalone games only, and even for just that one split second you wouldn't have been able to eject, it'll be worth it. */ function tick(float delta) { Pawn(owner).bCountJumps = true; } function destroyed() { local Inventory jb; jb = Pawn(owner).findInventoryType(class'UT_JumpBoots'); if (jb == none) { Pawn(owner).bCountJumps = false; } super.destroyed(); } // end Ng // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: HUDLevel.uc,v 1.5 2001/07/21 07:02:07 yrns Exp $ /** A gauge on the SLHUD. */ class HUDLevel extends SLInfo; /** Linked list. */ var HUDLevel next; var int x, y; var int w, h; var float alarm; var float realLvl; var float dispLvl; var string label; var string readout; var string qualifier; var Font lFont, sFont; var float bubx, buby; var() float bubbleRate; var() float lvlRate; /** Label info. These are adjusted based on screen size and font choice. Store all this garbage so we don't have to init every time. */ var int labely; var Font labelFont; var int roy; var Font roFont; var int qx, qy; /** This is the "qualifier" font, used to write "m" for meters, "kg" for kilograms, etc. */ var Font qualFont; var bool bBorder; // Draw the border around the gauge? var bool bNoFitLabels; function init(float alarm, string label, string qualifier, Font lFont, Font sFont, optional HUDLevel nextlvl) { self.alarm = alarm; self.label = label; self.qualifier = qualifier; self.lFont = lFont; self.sFont = sFont; bubx = rand(w - 1); buby = rand(h); next = nextlvl; } function position(int x, int y, int w, int h) { self.w = w; self.h = h; self.x = x; self.y = y; // Reset label info. labely = -1; } function render(Stylus s) { local Canvas c; local float lvl; local int x, y, mx; local int i, slide, top; local float partial; local float xl, yl; local Color color, origColor; local float fade; c = s.c; // This buffers the display level. lvl = dispLvl; fade = SLHUD(owner).screenFade; // I fucked this all up and for some reason reversed the y axis. x = self.x; y = self.y + h - 1; /* if (lvl > 1.0) { // Used to have a secondary level. drawLevel(c, pos, lvl - 1.0, 0, col, scale, alarm); lvl = 1.0; } */ // Grab the current color from the parent (HUD) and store it. origColor = c.drawColor; color = c.drawColor; // Flash the level from red to bright green. if (alarmOn()) { color = alarmColor(color, SLHUD(owner).bAlarm); } else if (lvl > 1.0) { color = s.blendc(color, alarmColor(color, true), lvl - 0.5); lvl = 1.0; } if (lvl > 0.0) { // This top function rounds down all the time, so if we take the modulo of it // we can draw the fraction that's lopped off as a partially faded extra line at // the top. It gives it a smooth look. top = Min(h, lvl * h); c.drawColor = color * fade; // The top and bottom lines are offset by one pixel, so we have to // handle them separatelsy. This is the bottom line. if (top >= 1) { s.setPos(x + 1, y); s.pix(w, 1); } // Top line. if (top == h) { s.setPos(x + 1, y - (h - 1)); s.pix(w, 1); } // The rest. if (top > 1) { // Line h is already done, if necessary, hence the min() func. s.setPos(x, y - (min(h - 1, top) - 1)); s.pix(w, (min(h - 1, top) - 1)); } if (level.bHighDetailMode) { // The "partial" line. partial = (lvl * h) % 1.0; if (partial > 0.0) { c.drawColor = color * partial * fade; mx = x; if (top == 0 || top == (h - 1)) { mx++; } s.setPos(mx, y - top); s.pix(w, 1); } } } // Draw bubbles. if (level.bHighDetailMode && buby < top) { //if (buby == top - 1) { if (buby == top) { // We're at the top - draw it faded to the partial line. c.drawColor = color * partial * fade; } else { c.drawColor = color * fade; } s.setPos(bubx + x, y - buby); s.pix(1, 2); } if (bBorder && level.bHighDetailMode) { // Draw a frame. Gets fainter as the level approaches 100%. c.drawColor = color * 0.25 * (1.0 - lvl) * fade; s.drawFrame(x, y - h + 1, w + 1, h); } c.drawColor = color * fade; drawLabels(s, x, y); // Restore the original color. The next gauge will pick the canvas // color up and use it, and if we've tainted it... c.drawColor = origColor; } /** Check the HUD and see if the alarm loop is "on". Alter the color if so. */ function Color alarmColor(Color c1, bool on) { local Color newc; newc = c1; if (!on) { newc.r /= 3; newc.g /= 3; newc.b /= 3; } else { newc.r *= 3; newc.r = min(255, newc.r); newc.g /= 3; newc.b /= 3; } return newc; } // Draw labels. function drawLabels(Stylus s, int x, int y) { local Canvas c; local float xl, yl; c = s.c; if (label != "" && readout != "") { // Initialize for this canvas. if (labely == -1) { if (label != "") { c.font = lFont; c.textSize(label, xl, yl); if (xl >= w) { c.font = sFont; c.textSize(label, xl, yl); if (xl >= w && !bNoFitLabels) { // Chop it off and re-init. label = left(label, w / xl - 2); labely = -1; return; } } labely = y - yl + 1; labelFont = c.font; if (readout != "") { // Gah. Hardcoded. if (w < 32 && !bNoFitLabels) { c.font = sFont; } else { c.font = lFont; } c.textSize(readout, xl, yl); roy = labely - yl + 1; qx = x + 1 + xl; roFont = c.font; if (qualifier != "") { c.font = lFont; c.textSize(readout, xl, yl); if (xl + qx >= w) { c.font = sFont; c.textSize(readout, xl, yl); } qy = labely - yl; qualFont = c.font; } } } } s.setPos(x + 2, labely); c.font = labelFont; s.drawText(label); if (readout != "") { s.setPos(x + 1, roy); c.font = roFont; c.textSize(readout, xl, yl); s.drawText(readout); if (qualifier != "") { s.setPos(x + 1 + xl, qy); c.font = qualFont; s.drawText(qualifier); } } } } function bool alarmOn() { return (realLvl < alarm); } /** Called from the HUD in tick(). */ function update(float delta, float l, coerce string s) { realLvl = l; readout = s; if (level.bHighDetailMode) { // Bubbles! buby += bubbleRate * delta; if (buby >= h) { buby = 1; // 2 pixels tall. bubx = rand(w - 1); } } if (dispLvl == realLvl) { // Nothing. } else if (dispLvl < realLvl) { dispLvl = fmin(realLvl, dispLvl + lvlRate * delta); } else { dispLvl = fmax(realLvl, dispLvl - lvlRate * delta); } /* if (next != none) next.update(delta); */ } function destroyed() { if (next != none) next.destroy(); } // end ] // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Fuelpod.uc,v 1.8 2001/07/09 09:52:55 yrns Exp $ /** Fuel core riser. Client-only in network games. */ class Fuelpod extends Effects; var Fuelcore core; var bool bOpen; var Util u; function postBeginPlay() { u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); core = Fuelcore(owner); if (core != none) setTimer(core.hoverTimer, true); super.postBeginPlay(); } /** Returns true if there is a valid pawn in the core's hover radius. */ function bool in() { local Pawn p; if (core != none) { foreach radiusActors(class'Pawn', p, core.hoverRadius) { if (validActor(p)) { return true; } } } return false; } /** Check to see if we should open or close. */ function timer() { if (in()) { if (core != none && !core.bHover && !core.bHidden && !bOpen) open(); } else { if (core.bHover && !core.bHidden && bOpen) close(); } } /** Core has just entered the pickup state. See if we should pop it up. */ function touchCheck() { local Pawn p; if (!bOpen && in()) open(); } function open() { u.debug("open()"); bOpen = true; playAnim('opening'); core.startHover(); } function close() { u.debug("close()"); bOpen = false; playAnim('closing'); core.stopHover(); } function bool validActor(Actor a) { return (a.bIsPawn && Pawn(a).bIsPlayer && (Pawn(a).health > 0) && !Pawn(a).playerReplicationInfo.bIsSpectator); } function destroyed() { u.debug("destroyed()"); } // end D^lsmpkwp*W p 6W D{^pa+W pp  ]Y4- % W // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Fuelglow.uc,v 1.1 2001/06/02 20:10:57 yrns Exp $ /** Makes the fuel cores glow. */ class Fuelglow extends SLSmoke; var float fadet; function postBeginPlay() { //setTimer(0.5, false); } /* function timer() { if (Fuelcore(owner).bHidden) gotoState('Fadeout'); else setTimer(0.5, false); } */ function tick(float delta) { if (fadet < 0.5) { fadet = fmin(0.5, fadet + delta); drawScale = default.drawScale * fadet * 2; } else { disable('tick'); } } function fade () { if (!isInState('Fadeout')) gotoState('Fadeout'); } state Fadeout { function beginState() { fadet = 0; } function tick(float delta) { if (fadet < 0.5) { fadet += delta; scaleGlow = default.scaleGlow * (1.0 - fadet / 0.5); } else { destroy(); } } } C // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Fuelcore.uc,v 1.12 2001/08/05 22:42:13 yrns Exp $ /** Fuel core pickup. It floats in the air and provides extra fuel. Here's how it works. When spawned, it determines if it's in air or not. If it is, it will loop the hovering anim and sit there. If not, it will spawn a fuel pod on the client side. When a player approaches, it will pop up out of the pod and hover. When they walk away it falls down. The complication of network play is that when the core is picked up, the client doesn't know, except via bHidden. So all our behavior is client side. The client core monitors bHidden to find out when it's sleeping and when it's picked up, via tick() in the Pickup state and timer() in the ClientSleep state, respectively. This scheme may need to be revised, either by making pickedUp() or start/stopHover() replicated calls from the server, depending on how badly it's possible for this scheme to get desynced. */ class Fuelcore extends SLAmmo; var Fuelpod pod; var() float hoverDist; // Distance off the ground we hover. var() float hoverRadius; // Range we inititate hovering. var() float hoverRate; // Speed we rise at. var() float hoverTimer; // Timer lapse in Fuelpod.uc. //var() Sound sndExplode; var vector hoverLoc; var bool bHover; var bool bInAir; var bool bRising; var BlastMark bm; var Fuelglow glow; var Class glowClass; var Sound casingSound, riseSound; var Util u; /** Initialize. Set bInAir and hoverLoc. On client, too. */ simulated function postBeginPlay() { local Actor a; local vector hl, hn, end; u = spawn(Class'Util', self); u.setDebugLevel(DL_Verbose); // Always level us out. We hover. setRotation(rot(0, 0, 0)); // If we have an owner, we are an inventory spawn copy. if (owner == none) { // Establish how high off the ground we are. end = location; end.z -= hoverDist; a = trace(hl, hn, end, location, false); bInAir = (a == none); // Only used if not hanging in the air all the time. if (!bInAir) { hoverLoc = hl + vect(0, 0, 1) * hoverDist; pod = spawn(class'Fuelpod', self,, hl + hn, rotator(hn) + rot(-16384, 0, 0)); u.debug("postBeginPlay(): hover loc: " $ u.sv(hl)); } } super.postBeginPlay(); } simulated function spawnGlow() { if (glow != none) glow.destroy(); glow = spawn(glowClass, self); } auto state Pickup { simulated function beginState() { // Inventory copy. if (owner == none) { u.debug("Pickup:beginState()"); if (bInAir) { loopAnim('hover'); spawnGlow(); } else { if (bHover) stopHover(); else // This starts the core hidden. landed(vect(0, 0, 1)); // Anyone in the pod radius? pod.touchCheck(); } } super.beginState(); } /** This is not inventory that can be tossed out and what not, so skip the super call. Once we've hit the ground (after stopHover()), hide us. We can't use bHidden because that will screw other stuff up. */ simulated function landed(vector hn) { u.debug("Pickup:landed()"); // Hidden, sorta. drawScale = 0.001; // Not sure this is necessary. setLocation(pod.location); //super.landed(hn); } /** Enabled so we know when to stop the pod's rising. Also used to detect pickups on net clients. */ simulated function tick(float delta) { local float d; if (bRising) { d = hoverLoc.z - location.z; if (abs(d) < 5.0) { u.debug("Pickup:tick(): v set to 0"); velocity = vect(0, 0, 0); // See destroyed(). setPhysics(PHYS_None); playSound(casingSound, SLOT_None, 0.85); bRising = false; } else if (abs(d) < (hoverDist * 0.5)) { velocity = vect(0, 0, 1) * hoverRate * (d / (hoverDist * 0.5)); } } // On the client, register pickups via bHidden. if (role < ROLE_Authority) { if (bHidden) { disable('tick'); pickedUp(); gotoState('ClientSleep'); } } else { // No need to keep tick running. if (!bRising) { disable('tick'); } } } /** Once we're done the rising animation, just hover. */ simulated function animEnd() { if (animSequence == 'rise') { loopAnim('hover'); } } simulated function startHover() { u.debug("Pickup:startHover()"); bHover = true; playAnim('rise'); playSound(riseSound, SLOT_Misc, 1.0); velocity = vect(0, 0, 1) * hoverRate; setPhysics(PHYS_Projectile); bRising = true; enable('tick'); lightEffect = LE_FastWave; // Damnit. un/touch() get called too fast. spawnGlow(); // Restore its scale from landed(). drawScale = 1.0; } simulated function stopHover() { u.debug("Pickup:stopHover(): v: " $ vsize(velocity)); bHover = false; // Keeps it from popping up in net games where stop and start // get called too closely. Need to figure out why and FIX that. velocity = vect(0, 0, 0); disable('tick'); playAnim('drop'); bCollideWorld = true; setPhysics(PHYS_Falling); lightEffect = LE_None; if (glow != none) glow.fade(); } /** The pod protects us when we're not hovering. You can't shoot the core while it's falling, but I can live with that. FIX? */ function takeDamage(int dam, Pawn inst, vector hl, vector mom, name dt) { u.debug("Pickup:takeDamage(): dam: " $ dam $ " instigator: " $ u.sname(inst)); if (bHover || bInAir) { instigator = inst; gotoState('Explode'); } } begin: // Nothing. Why does Inventory call becomePickup() twice? } // End pickup state. state ClientSleep { ignores touch, takeDamage; simulated function timer() { if (!bHidden) { gotoState('Pickup'); } else { setTimer(0.1, false); } } simulated function beginState() { setTimer(0.1, false); } } simulated function pickedUp() { u.debug("pickedUp()"); if (glow != none) glow.destroy(); // This calls back stopHover(). if (pod != none) { setLocation(pod.location); if (pod.bOpen) pod.close(); } } /** So the fuel pod compiles. */ simulated function startHover() { // Not in pickup mode? Ignore. } /** So the fuel pod compiles. */ simulated function stopHover() { // Not in pickup mode? Ignore. } state Sleeping { ignores touch, takeDamage; function beginState() { // Stick it back in the pod. pickedUp(); super.beginState(); } begin: sleep(respawnTime); playSound(respawnSound); // Looks stupid coming from inside the pod. //spawn(class'RespawnEffect', self,, location); sleep(0.3); gotoState('Pickup'); } state Explode { ignores touch, takeDamage; begin: u.debug("Explode:begin"); bHidden = true; spawn(class'StrangeExpl',,, location); //playSound(effectSound1,, 12.0,, 3000); hurtRadius(165, 250, 'exploded', 140000, location); sleep(0.05); // If we're close to the ground, spawn a blast mark. GAH. FIX. /* if (level.netmode != NM_DedicatedServer && !bInAir) { bm = spawn(class'Botpack.BlastMark',,, location, rot(-16384, 0, 0)); if (bm == none) { u.debug("Explode:begin: failed to spawn blast"); } } */ setRespawn(); } /** Need to notify the controller that we've pickup up our first pod so the HUD will reflect it. */ function inventory spawnCopy(Pawn p) { local inventory copy; local StrangeShell sl; copy = super.spawnCopy(p); foreach p.TouchingActors(class'StrangeShell', sl) { if (sl.getPilot() == p) { sl.fc = Fuelcore(copy); } } return copy; } /** Note that if we're PHYS_Projectile, the StrangeWave destroys us somehow. It is a mystery to me. So it's possible we will be destroyed, which we shouldn't be unless there's no respawns (unlikely). Watch for it. May need to get bProjTarget to false while rising. FIX? */ simulated function destroyed() { u.debug("destroyed(): state: " $ getStateName() $ " phys: " $ physics); if (glow != none) glow.destroy(); if (pod != none) pod.destroy(); super.destroyed(); } // end Nh P/!?h h h  p+ // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Fontlib.uc,v 1.2 2001/06/13 02:37:57 yrns Exp $ /** Provides dynamically loaded fonts for various HUDs. Not fully used yet. */ class Fontlib extends FontInfo; enum FStyle { FS_Normal, FS_Slick, FS_Title }; var() name fnames[6]; var() FStyle fstyles[6]; var int currindex; var FStyle currstyle; // Overridden Botpack.FontInfo functions. We can get rid of these // eventually. static function Font getStaticHugeFont(float width) { return Font(dynamicLoadObject("SLV2Fonts.title18", class'Font')); } static function Font getStaticBigFont(float width) { return Font(dynamicLoadObject("SLV2Fonts.leg16", class'Font')); } static function Font getStaticMediumFont(float width) { return Font(dynamicLoadObject("SLV2Fonts.leg16", class'Font')); } static function Font getStaticSmallFont(float width) { return Font(dynamicLoadObject("SLV2Fonts.leg8", class'Font')); } static function Font getStaticSmallestFont(float width) { return Font(dynamicLoadObject("SLV2Fonts.leg8", class'Font')); } static function font GetStaticAReallySmallFont(float Width) { return Font(dynamicLoadObject("SLV2Fonts.leg8", class'Font')); } static function font GetStaticACompletelyUnreadableFont(float Width) { return Font(dynamicLoadObject("SLV2Fonts.leg8", class'Font')); } // Mine. static function Font getFont(optional FStyle style, optional bool big) { local String name; switch (style) { case FS_Slick: if (big) name = "techno16"; else name = "techno8"; break; case FS_Title: if (big) name = "title18"; else name = "title9"; break; // case FS_Normal: default: if (big) name = "leg16"; else name = "leg8"; } return Font(dynamicLoadObject("SLV2Fonts." $ name, class'Font')); } function Font getNextFont(FStyle style, optional bool reset) { local int i; if (reset || style != currstyle) { currindex = 0; currstyle = style; } while (currindex < arrayCount(fnames)) { if (fstyles[i] == currstyle) { return Font(dynamicLoadObject("SLV2Fonts." $ fnames[i], class'Font')); } currindex++; } // At the end. currindex = 0; return none; } /** Find a font with the specified style that will fit in the specified space. */ function Font fitFont(Canvas c, string s, out float w, out float h, FStyle style) { local Font font, last; local float xl, yl; font = getNextFont(style); while (font != none) { c.font = font; c.textSize(s, xl, yl); if (xl <= w && yl <= h) { w = xl; h = yl; return font; } last = font; font = getNextFont(style); } // No font fit. return last; } // end RA kT rA * @ G@H@~w~*j~ z?6j@ jA  ~ 6j6jz6jz6j6jz6jz6jD6j6jG@H@?@pppsetting roll: S6j rotv: Xj~a+j~~1 Og R#b(g  h , i  BBb(b(L>, j ,g 'h ,i ,, j  f // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: ExhaustPuffBlue.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** It's blue. */ class ExhaustPuffBlue extends ExhaustPuff; // end ]*q // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: ExhaustPuff.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** Rocket exhaust sprite. */ class ExhaustPuff extends AnimSpriteEffect; // end VI{%u?% }I}W D?D?@?_ J a%sCE^%^,D}%-?I,?W ,E?&?^J~^s(b(h?uGJ^sAb(?^sJb(h?us ^s}^sJaր^sJCE%?I,C s a^sCECkkC^T?Q@?_?,k_ D?DE?&,,?@_ %}%-L -D-L b(h?u@5'IW Dk?,DE?&,,&-L '}Dk?I?,u@ - // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: EjectedBrass.uc,v 1.3 2001/06/28 05:34:32 yrns Exp $ /** Ejected brass from firearms. Also the super cl_ass for similar objects. */ class EjectedBrass extends UT_ShellCase; var() bool bLevel; // Zero the pitch and roll on landing? var() Sound landSound; // Sound played on landing. var() float sinktime; // Time it takes to sinkout. var() float sinkfudge; // Sink height fudge factor. var() int maxBounces; // Don't bounce past this. var() float noBounceChance; // Chance we'll stop bouncing on any given hitWall(). var() bool bRandHitSound; var() Sound hitSounds[10]; // Random hitsounds. var() int num; // # of hitsounds. simulated function postBeginPlay() { if (level.bDropDetail && (level.netmode != NM_DedicatedServer) && (level.netmode != NM_ListenServer)) lifeSpan = 1.5; } simulated function playHitSound() { local Sound s; if (bRandHitSound) s = hitSounds[rand(num)]; else s = landSound; playSound(s, SLOT_Misc, 1.0); } simulated function hitWall(vector hn, Actor wall) { local vector realhn; if (level.bDropDetail) { // || numBounces > 3) { destroy(); } else { if (bHasBounced && ((numBounces > maxBounces) || (frand() < noBounceChance))) // || (velocity.z > -50))) bBounce = false; numBounces++; if (!region.zone.bWaterZone) playHitSound(); realhn = hn; hn = normal(hn + 0.4 * vrand()); if ((hn dot realhn) < 0) hn *= -0.5; velocity = 0.5 * (velocity - 2 * hn * (velocity dot hn)); randspin(spinRate()); bHasBounced = true; } } simulated function float spinRate() { return (rand(100000) + 100000); } simulated function landed(vector hn) { local rotator r; if (level.bDropDetail || numBounces > 3) { destroy(); } else { if (!region.zone.bWaterZone && landSound != none) playHitSound(); setPhysics(PHYS_None); if (bLevel) { r = rotation; r.pitch = 0; r.roll = 0; setRotation(r); } if (lifespan > sinktime) setTimer(lifespan - sinktime, false); else gotoState('Sinkout'); } } simulated function timer() { gotoState('Sinkout'); } /** Rather than just disappearing or fading out, we sink into the ground. */ state Sinkout { simulated function beginState() { local float vish; local vector v; if (level.bDropDetail) { destroy(); return; } rotationRate = rot(0, 0, 0); // The constant is a fudge factor. Er, not. vish = (collisionHeight + sinkfudge) * drawScale; v = location; v.z += vish; setLocation(v); v = vect(0, 0, 0); v.z = -vish; prePivot = v; bCollideWorld = false; setPhysics(PHYS_Projectile); // Fast enough to disappear before we are destroy()'ed. v = vect(0, 0, 0); v.z -= vish / lifespan; velocity = v; } } // end U]jdxprn*na/!Pn*/a0 S onS a/!PnS o10n S l // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: DScar.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** My own textures. */ class DScar extends DirectionalBlast; var Texture marks[2]; simulated function postBeginPlay() { texture = marks[rand(arrayCount(marks))]; super.postBeginPlay(); } // end \XumXyyXyXHDU HX-f-f k // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: DamagePuff.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** Puffs the rocket emits when damaged. */ class DamagePuff extends SLSmoke; simulated function postBeginPlay() { super.postBeginPlay(); drawScale = default.drawScale + frand() * 0.5; } // end _i b.y@@H@-f' a 0-p -f-iF-M m   fa yHyHyHC< ZoPl,@h oQ,K k,\-I b(b(L>oI b({ b(b(?qI  h K , &, h K , &,'okQ,{  -[ o,k,sTOTAL COMMITMENT }Q,'o&g?q?}?kg?q??g?}qg?q& bep:-G 'j-e~ \[a ~ gN 6N D?6??a+N  cuvMu H2u%uK  h ex b5w*x x  7G A // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Controller.uc,v 1.17 2001/07/04 11:59:13 yrns Exp $ /** Used in standalone games (or listen servers where the rider has the viewport) to get a tick() *after* the rocket position has been updated. If we don't use this, the rider and rocket become desynced slightly and it looks bad. */ class Controller extends SLInfo; var Util u; function postBeginPlay() { u = spawn(Class'Util', self); u.setDebugLevel(DL_Normal); } function tick(float d) { local StrangeShell sl; sl = StrangeShell(owner); if (sl == none || sl.bDestroyed) { u.debug("tick(): No SL. Destroying."); destroy(); } else { sl.ctick(d); } } function destroyed() { u.debug("destroyed()"); super.destroyed(); } d!I+ -j' Li // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Contrail.uc,v 1.6 2001/07/21 07:07:32 yrns Exp $ /** Exhaust contrail from a Strangelove. View-aligned contrails don't work yet. FIX. */ class Contrail extends Effects config (SLV2); // DATA // /** Intro time. */ var() float intro; var() float length; /** The root contrail is hidden and is the master for the chain. Don't need to worry about relevancy as it's client-only. */ var bool bRoot; var StrangeShell sl; var Contrail last; // We keep track of our own lifespan for flexibility. var float elapsed; var() config float life; /** Detail drop settings. */ var() config bool bDDStart; // If DD is on, do we... even start? var() config bool bDDRestart; // Restart when DD goes off? var() config bool bDDEnd; // End when DD goes on? var() config bool bDDShort; // Shorten life span when DD is on? var() config bool bViewAlign; var() config bool bRotate; var() config float rotRate; var bool bDDShorted; var bool bDDEnded; var bool bEnd; // Is this an end segment? var bool bDead; // Destroyed()? /** End texture which can be rotated to be a "start" texture. */ var() Texture endTex; // NOT USED YET. var PlayerPawn vp; // METHODS // /** Detect root segment. */ simulated function postBeginPlay() { // This is the root segment? if (owner != none) { if (owner.isA('StrangeShell')) { bRoot = true; bHidden = true; sl = StrangeShell(owner); } } } /** Only used for root segment. */ simulated function end() { // Reverse the last one. Unless it already was. if (last != none && !last.bEnd) last.setEndTexture(true); if (sl != none) sl.rootct = none; // Destroy when the chain is gone. //destroy(); bDead = true; } /** This is tick() for the root node. */ simulated function roottick(float d) { local Pawn slowner; local vector x, y, z, newloc; local rotator newrot; local Contrail prev; local PlayerPawn pp; if (last != none && last.bDead) { destroy(); return; } if (bDDEnded) { if (!bDDRestart) { end(); return; } } if (level.bDroPDetail && bDDEnd) { bDDEnded = true; //if (!bDDRestart) //end(); return; } if (bViewAlign) viewAlignChain(getViewPort()); else if (bRotate) rotateChain(d); // For the root node this means the chain is stopped. if (bDead) return; if (last == none || bDDEnded) { // We do this so the owner doesn't see the first segment. if (last == none && sl != none) slowner = sl.getPilot(); if (bDDStart && !bDDRestart && level.bDropDetail) { end(); return; } // Restart. bDDEnded = false; if (last != none) prev = last; last = spawn(Class'Contrail', slowner,,, rotator(sl.velocity)); getAxes(sl.rotation, x, y, z); last.setLocation(sl.location + last.length * x); // This is the beginning. last.setEndTexture(false); setTimes(last); if (prev != none) last.last = prev; } else { if (vsize(sl.location - last.location) > (last.length * 2)) { prev = last; getAxes(last.rotation, x, y, z); newloc = last.location + last.length * x; newrot = rotator(sl.location - newloc); if (!bRotate && !bViewAlign) newrot.roll = sl.rotation.roll; // They can see it now. last.bOwnerNoSee = false; last = spawn(Class'Contrail', self,, newloc, newrot); last.last = prev; setTimes(last); } } } /** Slowly rotate each segment in the chain. */ simulated function rotateChain(float d) { local Contrail next; local rotator r; next = last; while (next != none) { r = next.rotation; r.roll += rotRate * d; next.setRotation(r); next = next.last; } } simulated function float arccos(float a) { return atan(sqrt(1 - a * a) / a); } /** Align each segment in the chain so that the camera lies in a plane perpedicular to the segment plane. */ simulated function viewAlignChain(PlayerPawn vp) { local Contrail next; local rotator r; local vector v; local float uupr, a; if (vp == none) return; // Unreal units per radian. uupr = 65536.0 / (2.0 * 3.14); next = last; while (next != none) { r = next.rotation; a = r.yaw / uupr; // Make the contrail the origin. v = vp.location - next.location; // Rotate around the z axis. v.x = v.x * cos(a) - v.y * sin(a); v.y = v.y * cos(a) + v.x * sin(a); // Calculate the angle that puts the camera in the plane, // and rotate 90'. r.roll = atan(v.z / v.y) * (65536.0 / (2.0 * 3.14)) + 16384; log("setting roll: " $ r.roll $ " rotv: " $ v); next.setRotation(r); next = next.last; } } /** Get, and store, the camera. */ simulated function PlayerPawn getViewPort() { local PlayerPawn pp; // If view port is not set, or has changed, reset it. if (vp == none || !(vp.player.isA('ViewPort'))) { vp = none; foreach allActors(Class'PlayerPawn', pp) { if (pp.player.isA('ViewPort')) { vp = pp; break; } } } return vp; } /** Init lifespan stuff. */ simulated function setTimes(Contrail c) { c.life = life; c.elapsed = life; //c.intro = intro; c.intro = (600 / vsize(sl.velocity)) * default.intro; c.bDDShorted = bDDShorted; } /** Shorten the life of this segment. */ simulated function short() { life /= 4.0; elapsed /= 4.0; intro /= 2.0; bDDShorted = true; } /** Segment fading is handled here. */ simulated function tick(float d) { if (bDDShort && !bDDShorted && level.bDropDetail) short(); if (bRoot) { roottick(d); return; } elapsed -= d; if (elapsed <= 0.0) { destroy(); return; } if (elapsed > (life - intro)) { // For the intro time we are expanding and solidifying. scaleGlow = (life - elapsed) / intro; } else { // The rest we are slowly expanding and fading out. scaleGlow = elapsed / (life - intro); // OK, we can clear this now. They won't see it. //bOwnerNoSee = false; } lightBrightness = scaleGlow * default.lightBrightness; } /** Swaps the texture with the "end" texture, possibily rotating it depending on which end is the end. */ simulated function setEndTexture(bool reverse) { local vector x, y, z; local rotator newrot; bEnd = true; skin = endTex; if (reverse) { // Rotate and move. getAxes(rotation, x, y, z); setLocation(location + x * length); newrot = rotation; newrot.pitch = rotation.pitch + (65535 * 0.5); setRotation(newrot); } } simulated function destroyed() { bDead = true; } // end h // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: Blastmark.uc,v 1.1.1.1 2001/05/29 05:01:33 al Exp $ /** My own blast texture. */ class Blastmark extends NuclearMark; // Two random ones. var Texture marks[2]; simulated function postBeginPlay() { texture = marks[rand(arrayCount(marks))]; drawScale = 5 + frand() * 3.0; super.postBeginPlay(); } // end v_v&.$.SLV2Fonts.leg16   H @U|a |$$H.MwH*aHt' npz#i@<wH*/a6 t Hn;:gt 0'10( t weMzYReloading:begina:-F &Eag::$W3snq!/ po#2UVzSwH*H-[H- -vTH-[H--vH rthAx-vzT sTG8|open()-v'a!~ H~ uHP!9|close()-v(a!} HD vsgYjs-.s-.s% .s- g // SLV2. Copyright (C) Al McElrath 2001. // http://www.planetunreal.com/0fus/ // $Id: BlackCloud.uc,v 1.3 2001/06/13 02:37:18 yrns Exp $ /** Residual smoke from crashes, etc. */ class BlackCloud extends SLSmoke; /** Expansion rate. */ var float exrate; simulated function beginPlay() { super.beginPlay(); velocity = vrand() * 15; velocity.z = abs(velocity.z); // Half step off... setTimer((lifespan / 20.0) * 0.5, false); } simulated function tick(float delta) { drawScale += delta * exrate; } /* No double spawn. simulated function timer() { if (owner == none && level.netmode != NM_DedicatedServer) { spawn(Class'BlackCloud', self,, location + vrand() * 20); } } */ // end w!^2|destroyed() QQOIOfObO\Q OROeON OhWNOcOuOGOsO\bcUON\ic\OOOdO%WLOO~chOaQKOKO` QmOaOpcr\XOOOO_ eAefOVQV OdOn QK OgeEWU\k\r OL\seqO~\aeCuOuwQ@OWf\ Gi GP\dOBf eQOtG@OYeseQAO^f!OFOy f \UOsOUckOH~Of O(G`\p\N\RQseU\IOwOH\|OIOpcGOMOIeQubO.OHev~m\g cMO\OfeUQ0\WQOuuo\zO^fO|eLc]\Pf#WhOEujOEGPciQMO?u}O`ef \(OI ~RGsGIeVOCg{en\l ~SODYo\iGbgfOX\ \Hfe_YscSc`O}e^uTOOcU \N \YOOuoOQOeOfO|ffOg fn ODuaf!Odf c`gUOea u\eF uxgQcP\`OWOfuEuu(t(u(AQGQnfl O2iL3QW eX uKYNeOYmuXOCfEuPuwe^gfgIdR\QO_GjY]OZ\Vg]f6fLGedgeUfzfAuY eA ~_uvYKeWcOe[f=u~G`d`g}fFLcODeZLUfeuK LSuiu|uPfHLRfGQlLy}u^LTOPOlQB\Z(v(7qud\}fi(}(N(x(yOMOu dva Qd\F\xfdg|fI umOR OS Ou efFf OIfafQ fJfAgx e}f&af!f uXfmFfmYMf%g/feQeDuZfe8gx fgfOZLfFLcue G~~fsfH (wfFO_ On O@fff} \~ fF O[fFO{ODfE O@f^G_f` eSfPcLf!Yk~J \kuVf^ (zu\LJ\Af#\Bfq \jeWf;gOuDf8\ufF~egIeGf8fj f\\]O?c] u[uluj ueus uKGTfW\p OvYnGReYeO QHfGfYx GCfpfWdnf~uf#eEe{ f gU dc\oYK\I \iOX\Sfeftf_f8feuaf`f`dae~bunKfedJfkfJek fxuwfwdUf\GWfWfaf[fY GV fgfufLfJ\\ dnQ{fyfvOAeo\Ef\frfzfeyuVuaGb fs\BQLf Oyff f#fxf L3@LChf"vulGzQxBL&pORuKfq\4u YGQ~fF GUfK fG fG uBfJ fFf^ u}fH (t fs fC f Ffz G[fy fGfM fT fZ fO OvfauRwu@fWfG\I((A(7(@fw(6\ (V (b(l(c(a(e(i(6j(~flev fe eCd_fZG^(6pfY \B({(|(]\r (lcOeb LXOjf[ LLLOLyLyLyOeyLyNu] LOLPLQffOJ fgOUeuLVLhLYLeLZL[L\L]L^L_L`L]LbLtO fGeNLdLefT\E0| pSJl+Ds C0y MPn]fi  l s y`/F ]f0kr vf[ hQ~ufK sM  @s M"hZ%BP P[ ]s jBwoEC[ T Bub0SpW|GfU U0*Sbfi ufrBM  PK]RO kKy][ G [  VocrqK@KN_\*j iyxfs q D ~KLT YZNsj DM QA _#P  l y ypF@[ Us c@jpmP ~X L u Y} jjv xD @ S@_Z o}|vKC Y } hv:dDlRM  `omCr|mUnfO |t Hfh Ub a%[ ns |[ Ifv  V Mc D s[ AVr Omm [ozhqwM  Ffk SRl `I l^yP G-lS o aWo)oje^jCfeQO _h mmKzi HG U|bi sw m jJ@ yO Fc UbbK p|M  }hJMU tX-qLUO  }RJf_ XO ek rP  ~QJr X ` effrZ[ @m Mz[m i@ xx  E:lR `QtLC@ Qr^> mv  yGQ  EM  R{ ^}kv y?tGhUiz ccy FK RN_L m,*ymq G g  T[ a M n\|mC JmA Wld e  r]XN~\ C k] x09xw hoW} ~fj L A}YfT V} cf^ pN}lKlZp hT us B LOM\ v]~ST aW ~nglVC {(N HVq TC aV? n#j{ei IB  VF cxs pk }@n I>m V)n c>s q|^ ~mM KhYmW g so@{ N|b [ w gStfgBM  P[ ]K j n wVq DXQ@ _lG l o xP EMrSta t ofc|JC JJq WAj d|G  qRB  ~Vy K~XV@ fM  sRr@'P  N X  [u g {  tfdAJy OM  [d~  hi ufT Bk Om\zM  jV vlCb Qz ^k k(K x@mEA S T `,Xml  {vt Ht UP df rlq MuZb hM  uzB!M  Ps \~  i~vDD  SKa`7M  n{ {L Hp Ut b} oM {L  H@Uo{c V q2n  ~pKg Yfa g(P tc Ai OB\ h  jJ wC DfaQH _u lfl ymsFIyq G[  Tg] `s jmJ wiDM  RM  ^sLj} x| D_ Qt_ v m:hzMxcHC kv x i  E H R] bR  nSM  z=F G#jTW bKbo,W} _K[  j \wP  S,@`onM  |pH[  VU cG py}p LlYs  gTso AnM  NM  ZH flrM  AlM d  [ h hoRvo| Dv QI^Q mkzlHoy WS d l qW ~_ KX Xv dBqh 4[  L} Yff p  uc B:gO n  ] @jEyv GG TCBa]S  omB  {sBGLUg csqpw  ~HKxt Ys fl so @xJ NKZ%hX vs BJ P` ]j  je w k D y QSK^n me {]O  H=M  Tg  `imA{ZHJ|C Rq _ W lj xUF EC R[ _|q m[ z[ H|b V(M cCpU~O LC Zc f)SrC AgY N>q[>[ i\qw>C E>UR>P  a[ nN |hHAV N dG ri  C Lb Ya fc smC B4lO q]JKk.C yEFz T p an wnK e@drgc Vv cMpJ~hLCZX ho uB Beh Ofc \h id ur  BfaOj ]g js w~  D[ Q r^p lM  yqP F> Tc ag n 6s{@ nqE{v IqCUqO c[ pB }@ JXVo d|qs E Lv XgZ elqF nlLMh ZMn gru!lCE  RP _Cl@ z~G-lU1rd\ r t| O  {L H `  UH a4xnb |G Kg XofqtA  BUOc ] c iX w[ Do R,F _Kllz Hh UY bH  o} | ~ I{Y)[ gy t>oA:lOx ]h  j_ vzyFBUHw V@ cv o~  |LIh  Wh  d` q0X~` LMU X:{eq sT @[  MHP  Z[gHu{ Ca P M]} k7H w{ DKPxv ^j} k} xDDHr Rq _2KlK  z c Gjp T} `Rv mmB zkGZlFZ] T{bRk p  }x[ ILW s ez rxuZ[ M[ ZO  ho tf@ AM  Or\ ~j[xcCFcqTKk bKb ncE {P  HlJ TgdaspP P  LLBY@yg^ uM  AMD NR [xgK v_xCMv Ta} a rofF}LKv[YvZha wM  Di Qh ^BjL x Q EP U 'bg  I e V(c cT p B  }mBIfL WCcfb qJ ~ vKEYlg0| Mus BMR OL\[  j X wfW DfQ Qq^alMS zf[ GfY Tg ac nfZ { | HQWY hfL  uh  B J`N?Knq _| d[p mjl W P  cc p L4~fKrTg@ a Nt^ k  mfa  zV gGTK n@zqICW?h  eEr!l@?NfX ] nj [ ylG s Uz bqqo?x }?E KHWEe A  s xZ@qZChzQcv QY[ j,c  xb E BRr TP `C nq {? H,{ T{ cW4Cq#!tM  UW almq {w I /VUWO  kUr wUO  CCPL  ^fg kay[ GQ SC`X no ziG|k UP bEpq~kLf Zx  gb t_ Ai Ng  Z] gU tL CBPV ^IkxzN HbxTclbcM p\L~z#"Lz`fn D TM[ ac?oJ ~[ KzGTXzT l~  yzsFcM  T7l`1[ n-B  |%qH%t V=z clpn } KlWT fK@ rt Ky K@XK?gB v}  CCPq^QlKBz%Hn VOdv rB  @MMo [Dh}  vC CxLPsJ ^vv kcCxM  FcrRv\ `c[ lM z`lFM  T]l`RB nRj {Lh HCqU:E c2b oP  {'lH@ V xcCro @ U LP Yx fM sI @Y M{Z^hox vs Cm PS ]R jJ wY D^QO _:b mY  zZ  G[  T<k aBml {|H @ Vq du q x~t L y Y[ fmDt{  BMR FOKKUA c p S }U J[ Wg eBsZ AcN|\P  j zwdEh S cn p C } q J { W^u dYW  gI ta A} Pj  ]l  j.G wiD D }PC ^Q km w | Dp Qk _fW kq xf^  D ~ QrO  ^K jU wC Dp Qq^rl@ zIG`C U`q b`@ o`y |@a HIUsO  cd}  oX |es I BVM  dP q C}o KXX} fCrl?@ E O q\FjI xMqEMESC a @ ngS|q LgPX~ fx sj @q M LZKP  hjY ujP  BMCNcz \n iEuqCf QX ^i kU  xj Eu RO  _X ls yU Fw  SO  `}  mC zZ IMlLV\ b] o{  |r I` V^ c lZ poJv  X} eD  r_uc Pd]s ky xO  EMo `R `rzRK`B nO  |J HE Sq`tQ  n } z TFs} Z}fJ ti} @ZT  L {gYU@M NI \N jXUx\BFS  TR  az Xnzt Fz:RzM `>vm>m {zN Hzu Uzr b>v ozV {\H%EXV fW v%CFi Tb `} ov | {-Ihvt Db R,C aXn*|,_ Jv Xk eg uf  CX PL ]z  jEwqEMSTa dEoP tMAi  OY \H iG um  Bz  Oy  \F i{  vw  CD P=T ] I-jIh W redIBICE WCCd ^ r q  BMP MMJ[JP iFG w h  DJC QJs] [ l EymG ]  V c  c![ p q~ mLdl ad  nde {dA  H} UdB  b} n [ { V IE Vqc [  q S  ~AY K Z  XX eY r T  R Le YR gb tw BG O^  \$O  ig  u BW QW ]` j AxY G`T gb f qA ~  K b ZO  g Vt BCc Rd _e l ~Ly_~E V f[c1} ~ aRJo \7Q i7ovYF@Ti T] `MA m7<zQ IkEW`L e7Prx Bj Oi __ nv{X  Ik  Vg ckqoi } wEJMOf  ]I jlwS FE SC` 3lnC ZkCgj  uE B OqK\vF lNx} FiGSP  ajO  mcE yC Fq SjJ _cqlSXzCHv  V]H cVa pRS }RJ J]W W@ dy q:f } o  J"YWq e I  q=R ~ z  J |  W<b d#h qt ~:P  Kf X w f:k sJ g.Ff.wtt Bm O p\Ml @LK  Lm  YD  f@X _sk  RzX _ Yki  y!} Ft SaD`s d,er*W`e8>sJ qpU~kO  L@O  XO  dhO  qT ~eO  L%y YYf[St$$G`ku6cKY  nmJzY  DY  QY  ]l%i'> N 7vZ\P2m U_a@t52t 2fV,XPrDiY  bY  o^g{uFbq?hY  gY  s8G RUF Zs[ AN` %OY  t58!@p  ax8!nowOePnkY  ~u!K$Y  l.Y xmM  FpXSY  k!C w IDvQM@^ZY  ^NI-j\7WY  N:Y  Z<Y  gn 4tsaho 7F@}M}Y JUuWXr 4oY  cuo!q K#Y  X[8dt\ }-PZA }!E JgH UWEV V &b ]3a eY  T OV` r v s C gJIP lY Y L4g hY  [ kDg 62k b{] 2X YnJ gHwx Mj o w3Y  'u g'\ _jC $m $Q gY  u cZB }Q\ |  m d/z xY  i czu zY o fOd } Y  a R m Y  z d"G Y  i Jv ]3T N4G gz8{ Gcs qV nte h Y Ttf _PD Y  T ^~a ,FU_ _"t Y  V Y  b r'n Y  U c%a B@F &Y  F gWR !8i )wa !HX LK` t  k Y  w R D \  Q 5^ T(^S Y  p sF} r C gy8P T^H oPe Y u J;C bR~ Uf:P  HIJ  efS ] ,xhy ,E?a ]~` *Y  ^  s[k AF gY  T gR a Y n Y  | DwH Df mO e EHr Y z ,V|H FID kxM Y  E Y  R |;_ Y Z U h U,>u W,ms ,W` O  w 2Y C Y  Q  j r^  PoP aA Y  @ t rM ,UB  W?A fb@ u,67b Y  Y  yf K _ TGl zBs Y  u _A jU` Y  u E*A Y  k R w (D [el UWQ  _]h Y  E `hR O  z =ZF Y  ` 2m  !2_  \BQ Y  S wJ` \j Y  x 8!E Y  f Y r Y  @ xsM R @ gf9M  qOF n@U `QU b4f Y  Z Y  g mb s y1@ R q Xv~  3t K g gg;t N o O |  3I zV| mk R Y  _ {Jl y JQv |LG :S  rM V #p~ Y  n E  z [kG KY r x (@ x Yh t /,A AWm ZxD  Fw| Y  s d@   Y  L eSX !Y  k Ybw j4Y &Y M XV[ Dsq Y d Yr XMK @Y  X 'Y d  |-r WR_ T)q V)Z q'C .Y  j U2v 0Y  h M Ft  -z ygg #yN x -G 6Pt 7lD 7d R 6TT^ HFTr FY  F _R  /q !L` ;Vl 6` cB D} e 6L q  ,p \a\ *FT} WY  Q  e r] 6h.O \*} \Tg 6JQ{  EL WCQ h_ OB} Oh K S~X ]Y  V vCc  7f l ] W_j !}I ZY F ^ dT o] x odD RpR aY  B oZN Qs\ GUO `Y  d S]p [ XM cY  e  @r ef\r m N n [ HUg 2| oUn i| oW e pr JCO mXR ~%` GHE }] M }dY J]g }ZD RTR #+f }UQ }W _ xY  l ~y QTV xCj xqx xEF DT ~q P8N V  9F Y   OYK qKd f3o P8b Y  Z OAf pY  g |ss N+f h0Q ]oA Y  p  {| &w M]] KEz eGg f+f UNQ Y  n Lgz #7a ] X N?d Y  c Fmo l{-\ qJI O  S Y _ O  m e=y WNv [`T dt TGB ZI [2W Y  I aGV U] W k x JIU n,^ N  J Y W d  e Sq B WZ M ^"g CI DKL Y  W ~Jc F/m } \  {i eWCd _\g f!bC XHe !am MRN IV ` y/v Y  e Y  r `W~ >LU L a `Mn fg*{ Iwe D\ fx?` fVP_ HHo O:w j;q GBl  }n Y  k dx LRW Y i  ]-w a0d F*T A}~  { Y  [ F!h Y  I K{V mSQ Itd f ] X E9u Y n Z } DwL T,t[C t ,/,^ Ha J O  k EUx tOM Y \ Cbj ~5L Y  A BQM {`^ Y  ~ Y  J |~V u6 T ARt !MF Y  S dM` rIm@MvBlCY  oY  | C^H Y  f~qs } d pfq Y  W g^cY  A}\N@gj|XQu izhu v&] w"CY  e iFqY  w gkC =1nY _@~m 3Vk,An #mm#Y  Zymf!lS ]"a&Y  CiO !x)Y  YfKqf,Z{WY  Rv ^^%v |1nI/Y  w E"CdJe~RofqsA fHtwn| w  j |  wvYD0Y  ] v  j{Rw7E4pIE4xy:Y  qbe}fZ bV f xo>Y g~u 2&CY  iCY  v,Q 2BXY  tzxI@E4DIf7HME4@*U BUfseczk.HNY  vfr,BLnE4UD|PDQzw,UTyAE4ezE4_>_@ E~7E4F C,Z[IX7 Kd` 7GLoft,{,Y %geE4k)LZ 7`5wZY  lY 7#WyfLRPY  bu voE4Hegx8mHxLe *q Cn[y 0I^Y  y E H jbtY  LW-Yt,Fq%rIq SW l~jE4eh E4pM_E4m}]FjHx2p SbY  uHx1AY  r )R hFTu7 WI]#!` ]UGA E4NH ME4`V gw8v Y  n `{ E4Pz fQlJ E4tv  ^Zj as D Y  w farC uHu E4Y} i+V Y  A } M #rY 0E4zK fE4EE M  J#xW#nLe#E4Wq#f H#Y U#E4c c#E4lF#!Gr%v y% /F%g u%} S%Y  _% E4dGk%HQO&_t`&_uSp&pE4uC&tx&E4tDG&E4v{&E4gq' mX(fMc E(E4d h(_ L(E4JX(G bb( !0D(fW Yt(W]M(Y  j(Y  v(CB(A ,LHP(E4cX(E4G*{(fNHB(E4BJ(Y  L)e TX) Gl)o\s)m}O)Wa%L)qq),E4`c)E_)q m)E4ez) E4Yg_*E4hx*L `+lhm+ $U+E4by+E4O[+P7j,gE4{+a,Y  \,k@h,fR[h,E4qC,y  t,E4|A,f{K },zE4J.H,jyR-Y K-E4wY-ulP-yE4v|-iAr- os-fPkb-pwM- vQD-X U- bCb-Y  e-E4L r-!#~-E4Wia-E4sx.gv9k. Vd.zMz.Y  G. MHT.#P\.h4l.l `.TFm.HGs.gkz.E4r e.Y  W.!4c.