[Setup] Product=Poltergeist Version=100 Archive=poltergeist.umod SrcPath=. MasterPath=.. Requires=UnrealTournamentRequirement Group=SetupGroup Group=umodINIGroup Group=PoltergeistGroup [UnrealTournamentRequirement] Product=UnrealTournament Version= [SetupGroup] Copy=(Src=System\Manifest.ini,Master=System\Manifest.ini,Size=1423,Flags=3) Copy=(Src=System\Manifest.int,Master=System\Manifest.int,Size=597,Flags=3) [umodINIGroup] AddIni=UnrealTournament.ini,Engine.GameEngine.ServerPackages=Poltergeist AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.AttractRadius=1500.000000 AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.AttractStrength=0.007500 AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.bAnnounceDestroyed=False AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.bAnnounceNewOther=False AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.bAnnounceNewSelf=True AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.bAnnounceSurvivedOther=True AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.bAnnounceSurvivedSelf=True AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.bRotateProjectiles=False AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.SurvivalBonus=3 AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.TargetPickType=PT_TotalRandom AddIni=UnrealTournament.ini,Poltergeist.PoltergeistMut.TargetStayTime=15 [PoltergeistGroup] File=(Src=System\Poltergeist.u,Size=388997) File=(Src=System\Poltergeist.int,Size=271) File=(Src=System\Poltergeist.txt,Size=3004) [Setup] LocalProduct=Poltergeist ReadMe=System\Poltergeist.txt SetupWindowTitle=Poltergeist Setup AutoplayWindowTitle=Poltergeist Options ProductURL=http://people.ce.mediaone.net/serpwidgets/poltergeist.html VersionURL=http://people.ce.mediaone.net/serpwidgets/poltergeist.html Developer=Serpentine Widgets DeveloperURL=http://people.ce.mediaone.net/serpwidgets/UT.html [UnrealTournamentRequirement] LocalProduct=UnrealTournament ProductURL=http://www.unrealtournament.com/ VersionURL=http://unreal.epicgames.com/ Developer=Epic Games, Inc. DeveloperURL=http://www.epicgames.com/ *E3@# ,f P,53NoneSetTextCreateControl SetHelpTextTick PoltergeistSystemCore SetValue DoFXStuffEngineReceiveLocalizedMessageWAVClientPlaySoundSetNumericOnly Register GetValueSpawnAGhostNowAttractProjectiles SetMaxLengthSetNumericFloatAddItem PlayerPawnBroadcastMessageSubtractTarget GetHumanNamePickNewTargetUWindow BeginStateTimer IsActiveSetOwnerLightingTextureResetTheTargetLocationSetBotEnemiesisIdlePoltergeistHeld ImAServerPostBeginPlayBotpackBot LightType ModifyPlayer ScoreKill DrawTypebCenterSurvivalBonusPoltergeistEffectPoltergeistGhostAttractRadiusPoltergeistClientWindowPoltergeistMessagePlusAttractStrengthPoltergeistUMenuModMenuItemPoltergeistConfigWindowTargetStayTimebAnnounceDestroyedbRotateProjectilesbAnnounceNewSelfbAnnounceSurvivedOtherbAnnounceNewOtherPT_TotalRandomCollisionRadiusCollisionHeightbAnnounceSurvivedSelfHitWallPT_FragWeightedRandom AddInventoryPT_FlyingPoltergeistPhysicsCreatedLightBrightnessNotifyYPosServerReStartPlayerTouch DrawColor GetString FontSizeStyle AmbientGlowPoltergeistMut MenuHelp LightRadius RotationRatebBounce MenuCaption SetEnemyExecute bIsSpecial ScaleGlow bIsUnique bFadeMessage bSizableRotator GetValue2 FearThisSpot GetOffset GetFontSize ClientClass ClientFlashbBeepTargetPickTypePT_KillerBecomesTarget SetEditableColor LightHue StatusIconUMenuSetFont RemoteRole Destroyedspeed WindowTitleScorebWaitingPlayer bIsSpectatorHasFlag Velocity PlayerName nextPawnGameKillerP NewLocation HitNormalRegion Instigator DeltaTimemyHUD NextMutatorZoneVictimOwnerFatness LocationbReplicateInstigator LocalMessageLightSaturation RelatedPRI_2 RotationInvOther RelatedPRI_1OptionalObjectNavigationPoint DrawScaleSwitchNavigationPointList ZoneGravity TextBufferObjectEnum FunctionStateHealth bGameEnded PointRegiondist PawnListYLVectorStruct UnrealShareUWindowEditControlUWindowPulldownMenuItemUWindowSmallCloseButtonUWindowSmallButtonUWindowDialogClientWindow bIsPlayerUWindowComboControlUWindowRootWindowUWindowCheckboxUWindowDialogControlUWindowWindowUWindowPulldownMenuUWindowLabelControl CreateWindowbIsPawnAlign NotifyWindow EditBoxWidthRootpHIcons StrProperty LightEffect ShieldBelt NumPointsBelt_fx BlueShield SpawnLocnextNavigationPointGameReplicationInfo VisibilityClipYUMenuModMenuItem MenuItemLevelPlayerReplicationInfoMeshUT_ShieldBeltEffectUTTeleportEffectLocalMessagePlus TargetVelTGTPGuidedXFlareFX utflare5ACStructPropertyObjectPropertyFloatProperty BoolProperty IntProperty BytePropertyActorPawnRole FireTextureSoundClassHUDPackageEFire ZoneInfokStaticSaveConfigiC ReturnValueNWall ScriptTextA GameInfoMutator LevelInfo Inventory Projectile bCheckedUWindowFramedWindow ServerTarget TargetPosQuarterSecondGravtDist ANewTarget Package0TargetDestroyed Package1TargetSurvived Package2 URTarget Package3 YouGotBonus Package4 EPickType TargetTimeTargetPassedTimeCurrentTarget PrevTarget TheKillerAttractEffecttHeld HasAHeldCTLoc TargetDiedpPnPawnspList GrossFragstScore FragChoose FragFraction RotateProj AN_NewSelf AN_NewOtherAN_Dest AN_SurvSelf AN_SurvOther AttractRad AttractStr StayTime FragBonus tPickModeAnnounceLabeltClosetCancelToggler TimeRoamed TheTarget ClosestDist FoundOneServerResetMeLastResetStateAcc RandPoint LastTarget TargetedTimetTime TheGhost HasAVictim UsePrevLocServerDestroyedZEQFYY l9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }.;9 }.;9 }.;9 }.;=9 }9 }.;9 }9 }F9 }F9 }F9 }=======F9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }.;9 }9 }9 }___________RùR9 }9 }I+\9 } q Rw4w4Rw4w4w49 }9 }9 } q q q 9 }مم9 }م9 }9 }9 }م9 } 9 }9 }9 }j@j@j@9 }ممممم9 }م9 }مlمممl9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }.;9 }.;9 }.;9 }.;=9 }9 }.;9 }9 }F9 }F9 }F9 }=======F9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }9 }.;9 }9 }9 }___________RùR9 }9 }I+\9 } q Rw4w4Rw4w4w49 }9 }9 } q q q 9 }aa9 }a9 }9 }9 }a9 } 9 }9 }9 }j@j@j@9 }aaaaa9 }a9 }alaaal ."7$pA1$D4$;8<:;@9fD\}$ ::$2aj@___________5I9 }9 }9 } kE,)GT:^R3@kan0C ::$# q _Rw4w4Rw4w4w49 }9 }9 }.;9 }.;9 }9 }I+\9 }=9 }9 } p$Dn,O P>$A?$A)jSUE@ h/FL\'  Z$? b /@(5_ 1 ;`@)[D3#7j`=.;.;' N"Y[\eL*iI$C- "*Y&'c hSV W9Zg7@F@@O%$NpJ@C@I@B@\OP@6@>?f8@, 4 XK[ t] iQ pLT-= b ad MVy2Jj: ~r"I|.Xڅr"I|.Xڅr"I|.Xڅr"I|.Xڅr"I|.Xڅr"I|.Xڈ_4r"Ir"Ir"Ir"Ir"Itr"Itr"Itr"Itr"I|.Xچ|.Xچ|.Xڅr"I|.Xچ|.Xچ|.Xڅr"I|.Xچ|.Xچ|.Xڅr"I|.Xچ|.Xچ|.Xڅr"I|.Xچ|.Xچ|.Xڅr"I_4_4_4_4_4_4_4_4_4r"I_4r"I_4r"I_4_4|.Xچ|.Xڅr"Itttr"Ittttttttr"Ittttttttr"Ittttttttr"Itttttt7XÐ7XÙВ`Y|.Xڅ`Y|.Xڅ`Y|.Xڅ`Y|.Xڅ`Y|.Xڅ`Y|.Xڈ_4`Y`Y`Y`Y`Yt`Yt`Yt`Yt`Y|.Xچ|.Xچ|.Xڅ`Y|.Xچ|.Xچ|.Xڅ`Y|.Xچ|.Xچ|.Xڅ`Y|.Xچ|.Xچ|.Xڅ`Y|.Xچ|.Xچ|.Xڅ`Y_4_4_4_4_4_4_4_4_4`Y_4`Y_4`Y_4_4|.Xچ|.Xڅ`Yttt`Ytttttttt`Ytttttttt`Ytttttttt`Ytttttt7XÐ7XÙВ\t|.Xڅ\t|.Xڅ\t|.Xڅ\t|.Xڅ\t|.Xڅ\t|.Xڈ_4\t\t\t\t\tt\tt\tt\tt\t|.Xچ|.Xچ|.Xڅ\t|.Xچ|.Xچ|.Xڅ\t|.Xچ|.Xچ|.Xڅ\t|.Xچ|.Xچ|.Xڅ\t|.Xچ|.Xچ|.Xڅ\t_4_4_4_4_4_4_4_4_4\t_4\t_4\t_4_4|.Xچ|.Xڅ\tttt\ttttttttt\ttttttttt\ttttttttt\ttttttt7XÐ7XÙВq|.Xڅq|.Xڅq|.Xڅq|.Xڅq|.Xڅq|.Xڈ_4qqqqqtqtqtqtq|.Xچ|.Xچ|.Xڅq|.Xچ|.Xچ|.Xڅq|.Xچ|.Xچ|.Xڅq|.Xچ|.Xچ|.Xڅq|.Xچ|.Xچ|.Xڅq_4_4_4_4_4_4_4_4_4q_4q_4q_4_4|.Xچ|.Xڅqtttqttttttttqttttttttqttttttttqtttttt7XÐ7XÙВZ!dž|.XڅZ!dž|.XڅZ!dž|.XڅZ!dž|.XڅZ!dž|.XڅZ!dž|.Xڈ_4Z!DžZ!DžZ!DžZ!DžZ!DŽtZ!DŽtZ!DŽtZ!DŽtZ!Ǘ|.Xچ|.Xچ|.XڅZ!dž|.Xچ|.Xچ|.XڅZ!dž|.Xچ|.Xچ|.XڅZ!dž|.Xچ|.Xچ|.XڅZ!dž|.Xچ|.Xچ|.XڅZ!Lj_4_4_4_4_4_4_4_4_4Z!Lj_4Z!Lj_4Z!Lj_4_4|.Xچ|.XڅZ!DŽtttZ!DŽttttttttZ!DŽttttttttZ!DŽttttttttZ!DŽtttttt7XÐ7XÙВw|.Xڅw|.Xڅw|.Xڅw|.Xڅw|.Xڅw|.Xڈ_4wwwwwtwtwtwtw|.Xچ|.Xچ|.Xڅw|.Xچ|.Xچ|.Xڅw|.Xچ|.Xچ|.Xڅw|.Xچ|.Xچ|.Xڅw|.Xچ|.Xچ|.Xڅw_4_4_4_4_4_4_4_4_4w_4w_4w_4_4|.Xچ|.Xڅwtttwttttttttwttttttttwttttttttwtttttt7XÐ7XÙВ6u|.Xڅ6u|.Xڅ6u|.Xڅ6u|.Xڅ6u|.Xڅ6u|.Xڈ_46u6u6u6u6ut6ut6ut6ut6u|.Xچ|.Xچ|.Xڅ6u|.Xچ|.Xچ|.Xڅ6u|.Xچ|.Xچ|.Xڅ6u|.Xچ|.Xچ|.Xڅ6u|.Xچ|.Xچ|.Xڅ6u_4_4_4_4_4_4_4_4_46u_46u_46u_4_4|.Xچ|.Xڅ6uttt6utttttttt6utttttttt6utttttttt6utttttt7XÐ7XÙВS|.XڅS|.XڅS|.XڅS|.XڅS|.XڅS|.Xڈ_4SSSSStStStStS|.Xچ|.Xچ|.XڅS|.Xچ|.Xچ|.XڅS|.Xچ|.Xچ|.XڅS|.Xچ|.Xچ|.XڅS|.Xچ|.Xچ|.XڅS_4_4_4_4_4_4_4_4_4S_4S_4S_4_4|.Xچ|.XڅStttSttttttttSttttttttSttttttttStttttt7XÐ7XÙВb|.Xڅb|.Xڅb|.Xڅb|.Xڅb|.Xڅb|.Xڈ_4bbbbbtbtbtbtb|.Xچ|.Xچ|.Xڅb|.Xچ|.Xچ|.Xڅb|.Xچ|.Xچ|.Xڅb|.Xچ|.Xچ|.Xڅb|.Xچ|.Xچ|.Xڅb_4_4_4_4_4_4_4_4_4b_4b_4b_4_4|.Xچ|.Xڅbtttbttttttttbttttttttbttttttttbtttttt7XÐ7XÙВw/ݑ|.Xڅw/ݑ|.Xڅw/ݑ|.Xڅw/ݑ|.Xڅw/ݑ|.Xڅw/ݑ|.Xڈ_4w/ݑw/ݑw/ݑw/ݑw/ݑtw/ݑtw/ݑtw/ݑtw/ݑ|.Xچ|.Xچ|.Xڅw/ݑ|.Xچ|.Xچ|.Xڅw/ݑ|.Xچ|.Xچ|.Xڅw/ݑ|.Xچ|.Xچ|.Xڅw/ݑ|.Xچ|.Xچ|.Xڅw/ݑ_4_4_4_4_4_4_4_4_4w/ݑ_4w/ݑ_4w/ݑ_4_4|.Xچ|.Xڅw/ݑtttw/ݑttttttttw/ݑttttttttw/ݑttttttttw/ݑtttttt7XÐ7XÙВTT|.XڅTT|.XڅTT|.XڅTT|.XڅTT|.XڅTT|.Xڈ_4TTTTTTTTTTtTTtTTtTTtTT|.Xچ|.Xچ|.XڅTT|.Xچ|.Xچ|.XڅTT|.Xچ|.Xچ|.XڅTT|.Xچ|.Xچ|.XڅTT|.Xچ|.Xچ|.XڅTT_4_4_4_4_4_4_4_4_4TT_4TT_4TT_4_4|.Xچ|.XڅTTtttTTttttttttTTttttttttTTttttttttTTtttttt7XÐ7XÙВYY |.XڅYY |.XڅYY |.XڅYY |.XڅYY |.XڅYY |.Xڈ_4YY YY YY YY YY tYY tYY tYY tYY |.Xچ|.Xچ|.XڅYY |.Xچ|.Xچ|.XڅYY |.Xچ|.Xچ|.XڅYY |.Xچ|.Xچ|.XڅYY |.Xچ|.Xچ|.XڅYY _4_4_4_4_4_4_4_4_4YY _4YY _4YY _4_4|.Xچ|.XڅYY tttYY ttttttttYY ttttttttYY ttttttttYY tttttt7XÐ7XÙВA=gDB:H2 l0PU62u}oMcjq]#"Poltergeist Mutator Configuration]z!"6~'"-v!!w!*{!a/!( w*.! W!!6 @:::$  /a0 --- ,?10::$wD*D   ,  ;::$3r   ::$%` ?&P%.T ?&T`?,d$?D?D&%%Z,s?%X ,s?&q<%r r*X,pWarning: Snake is a dork! FragWeightedRandom has failed to pick a different target! S  lG4)T -ca?=:$,d-a---a--a ba =w@ $B$?G$?'$@F@ 'w *\  \G$$??RR?>G WqHl $q 4Close button hit -8<- -F)- -B"- -C%- -I*- -P&-:J_:, $o>:& $o^:, $o $ 6L 7L JL  AJ    Go "X/a0 ffa10-ofw*5=g5w*55gM M a MVr*Warning: Ghost couldn't be spawned, switching to Random mode!$ Hi[2,tw*a$r* b$-i/a0 p collected the Target Survival BonusZr #,W-P  g ( #,-I  o (10?AJ/a0 Ip was an excellent target... #,H-F  e (10r::$k-i'r( Bn/a7 zDw2 W[2]2nW 2   -Qa+P 10 X|3xj -L(?;E/a7 D-%--N N??N_-L'10X-L _=+ a/!(. ` U::$ w @p h::$  *=?=?,a ! ~!!=w*/h/w*//h^----b  {5RQ-.- w.* a    $$ =,$$$$ I*eQa/!(iw(*R(a/!$-Y'i(((-Ya   r*WARNING: Couldn't Spawn PoltergeistHeld! This will explain the following Accessed none...-'[6]7-Q-8 Ca  aw* * drO) pr.*r.*$w.r.* r*q!# .% a   1?,da 1V,r,>,, e.K~`^.-..-..%-e'd.. LmMrH*m S&S{HH is the new Target q%You are now the Target! ,Survival Bonus! ,Target Survived! ,Target Destroyed!   w ELJigw*T%a/!.J,d,,p is the Target!/a0 f#r #% -C  k (e #&e-B  j (10 ^aA@Dt  c+3+=3J r*%nw*g%'n(::$9+9@@93*:*9r*(::-ed-c'3wa/!.dL#HC/a0 'B'Aw*' V ' 1  +A::$' 16  +10;+;> w*=,;;-O-O-O" vsFn)0E /a7 zDE-,w 0E  S6070s S  0        ,-8 a+P  10 kU+Ip rKDU/a0 >edr>Kr>*>a10w*+UK @JFMb%  NK^A2#իff?^^ %T]52SOͷ;aGնmo lV]Poltergeist ConfigR]! Sets up the Poltergeist Mutatoru//============================================================================= // PoltergeistUMenuModMenuItem. //============================================================================= class PoltergeistUMenuModMenuItem expands UMenuModMenuItem; function Execute() { MenuItem.Owner.Root.CreateWindow(class'PoltergeistConfigWindow', 100,100,220,300); } @//============================================================================= // PoltergeistConfigWindow. //============================================================================= class PoltergeistConfigWindow expands UWindowFramedWindow; - -sy//============================================================================= // PoltergeistClientWindow. //============================================================================= class PoltergeistClientWindow expands UWindowDialogClientWindow; // checkbox; //var UWindowHSliderControl slider; var UWindowCheckBox RotateProj, AN_NewSelf, AN_NewOther, AN_Dest, AN_SurvSelf, AN_SurvOther; var UWindowEditControl AttractRad, AttractStr, StayTime, FragBonus; //SetNumericFloat, SetNumericOnly, SetValue var UWindowComboControl tPickMode; var UWindowLabelControl AnnounceLabel; var UWindowSmallButton tClose; var UWindowSmallCloseButton tCancel; // called after object has been created... // this is where you add all the buttons and crap function Created() { Super.Created(); // announcement, label AnnounceLabel = UWindowLabelControl(CreateControl(class'UWindowLabelControl', 10,5,110,10)); AnnounceLabel.SetText("Voice Announcements"); // announcements - checkboxes AN_NewSelf = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10,18,110,10)); AN_NewSelf.SetText("Self Targeted"); AN_Newself.SetHelpText("Plays a sound when you are targeted"); /// AN_NewSelf.bChecked = class'Poltergeist.PoltergeistMut'.default.bAnnounceNewSelf; AN_NewOther = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10,31,110,10)); AN_NewOther.SetText("Other Targeted"); AN_NewOther.SetHelpText("Plays a sound when others are targeted"); /// AN_NewOther.bChecked = class'Poltergeist.PoltergeistMut'.default.bAnnounceNewOther; AN_Dest = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10,44,110,10)); AN_Dest.SetText("Target Destroyed"); AN_Dest.SetHelpText("Plays a sound when target is killed"); /// AN_Dest.bChecked = class'Poltergeist.PoltergeistMut'.default.bAnnounceDestroyed; AN_SurvSelf = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10,57,110,10)); AN_SurvSelf.SetText("You Survived"); AN_SurvSelf.SetHelpText("Plays a sound when you get the survival bonus"); /// AN_SurvSelf.bChecked = class'Poltergeist.PoltergeistMut'.default.bAnnounceSurvivedSelf; AN_SurvOther = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10,70,110,10)); AN_SurvOther.SetText("Other Survived"); AN_SurvOther.SetHelpText("Plays a sound when others get the survival bonus"); /// AN_SurvOther.bChecked = class'Poltergeist.PoltergeistMut'.default.bAnnounceSurvivedOther; tPickMode = UWindowComboControl( CreateControl(class'UWindowComboControl', 10,115,200,10) ); tPickMode.SetText("Pick Targets"); tPickMode.SetHelpText("Determines how the next target is chosen"); /// tPickMode.SetFont(F_Normal); tPickMode.SetEditable( False ); tPickMode.EditBoxWidth = 115; tPickMode.AddItem("Totally Random", "0"); tPickMode.AddItem("Killer Becomes", "1"); tPickMode.AddItem("Poltergeist", "2"); tPickMode.AddItem("Weighted Random","3"); if (class'Poltergeist.PoltergeistMut'.default.TargetPickType == PT_FragWeightedRandom) tPickMode.SetValue("Weighted Random", "3"); else if (class'Poltergeist.PoltergeistMut'.default.TargetPickType == PT_KillerBecomesTarget) tPickMode.SetValue("Killer Becomes", "1"); else if (class'Poltergeist.PoltergeistMut'.default.TargetPickType == PT_FlyingPoltergeist) tPickMode.SetValue("Poltergeist", "2"); else tPickMode.SetValue("Totally Random", "0"); RotateProj = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10,165,100,10)); RotateProj.SetText("Rotate Projectiles"); RotateProj.bChecked = class'Poltergeist.PoltergeistMut'.default.bRotateProjectiles; // Projectile Gravitation AttractRad = UWindowEditControl( CreateControl(class'UWindowEditControl', 10,180,150,10) ); AttractRad.SetText("Attract Radius"); AttractRad.SetHelpText("Max distance of projectile attraction"); /// AttractRad.SetValue(string(class'Poltergeist.PoltergeistMut'.default.AttractRadius)); AttractRad.EditBoxWidth = 50; AttractRad.SetMaxLength(8); AttractRad.SetNumericOnly(True); AttractRad.SetNumericFloat(True); AttractRad.Register(self); AttractStr = UWindowEditControl( CreateControl(class'UWindowEditControl', 10,195,150,10) ); AttractStr.SetText("Attract Strength"); AttractStr.SetHelpText("How hard the projectiles are pulled"); /// AttractStr.SetValue(string(class'Poltergeist.PoltergeistMut'.default.AttractStrength)); AttractStr.EditBoxWidth = 50; AttractStr.SetMaxLength(8); AttractStr.SetNumericOnly(True); AttractStr.SetNumericFloat(True); AttractStr.Register(self); StayTime = UWindowEditControl( CreateControl(class'UWindowEditControl', 10,210,150,10) ); StayTime.SetText("StayTarget Time"); StayTime.SetHelpText("How long each player stays as the target (seconds)"); /// StayTime.SetValue(string(class'Poltergeist.PoltergeistMut'.default.TargetStayTime)); StayTime.EditBoxWidth = 50; StayTime.SetMaxLength(8); StayTime.SetNumericOnly(True); StayTime.SetNumericFloat(True); StayTime.Register(self); FragBonus = UWindowEditControl( CreateControl(class'UWindowEditControl', 10,225,150,10) ); FragBonus.SetText("Survival Bonus"); FragBonus.SetHelpText("Frags awarded for not dying while target"); /// FragBonus.SetValue(string(class'Poltergeist.PoltergeistMut'.default.SurvivalBonus)); FragBonus.EditBoxWidth = 50; FragBonus.Align = TA_Left; FragBonus.SetMaxLength(8); FragBonus.SetNumericOnly(True); FragBonus.SetNumericFloat(True); FragBonus.Register(self); tClose = UWindowSmallButton(CreateWindow(class'UWindowSmallButton', 80 , 250, 48, 16)); tClose.SetText("Save"); tClose.NotifyWindow = Self; tCancel = UWindowSmallCloseButton(CreateWindow(class'UWindowSmallCloseButton', 10, 250, 48, 16)); tCancel.SetText("Cancel"); } //when a control changes, Notify is called with the changed control // and the message sent. function Notify(UWindowDialogControl C, byte E) { local int i; //log ("Poltergeist Config Notify"); switch(E) { case DE_Click: // the message sent by the controls switch(C) { case tClose: // someone hit the save button... // set all of the variables now log("Close button hit"); //checkboxes class'Poltergeist.PoltergeistMut'.default.bRotateProjectiles = RotateProj.bChecked; class'Poltergeist.PoltergeistMut'.default.bAnnounceDestroyed = AN_Dest.bChecked; class'Poltergeist.PoltergeistMut'.default.bAnnounceNewOther = AN_NewOther.bChecked; class'Poltergeist.PoltergeistMut'.default.bAnnounceNewSelf = AN_NewSelf.bChecked; class'Poltergeist.PoltergeistMut'.default.bAnnounceSurvivedOther = AN_SurvOther.bChecked; class'Poltergeist.PoltergeistMut'.default.bAnnounceSurvivedSelf = AN_SurvSelf.bChecked; // dropdown combo box i=int(tPickMode.GetValue2()); if (i==3) class'Poltergeist.PoltergeistMut'.default.TargetPickType = PT_FragWeightedRandom; else if (i==1) class'Poltergeist.PoltergeistMut'.default.TargetPickType = PT_KillerBecomesTarget; else if (i==2) class'Poltergeist.PoltergeistMut'.default.TargetPickType = PT_FlyingPoltergeist; else class'Poltergeist.PoltergeistMut'.default.TargetPickType = PT_TotalRandom; // textboxes class'Poltergeist.PoltergeistMut'.default.AttractRadius = float(AttractRad.getValue()); class'Poltergeist.PoltergeistMut'.default.AttractStrength = float(AttractStr.getValue()); class'Poltergeist.PoltergeistMut'.default.TargetStayTime = float(StayTime.getValue()); class'Poltergeist.PoltergeistMut'.default.SurvivalBonus = int(FragBonus.getValue()); // write it to the ini class'Poltergeist.PoltergeistMut'.static.StaticSaveConfig(); return; } } } <FPo R.  A@B ARVoice Announcements%.  AAB A%Self Targeted%+Plays a sound when you are targeted%- -C".  AAB A"Other Targeted".Plays a sound when others are targeted"- -B).  A0BB A)Target Destroyed)+Plays a sound when target is killed)- -F&.  AdBB A&You Survived&5Plays a sound when you get the survival bonus&- -P*.  ABB A*Other Survived*8Plays a sound when others get the survival bonus*- -I.  ABHC APick Targets0Determines how the next target is chosenm%h(BTotally Random0Killer Becomes1Poltergeist2Weighted Random3: :$Weighted Random3,: :$Killer Becomes1e: :$Poltergeist2Totally Random0<.  A%CB A<Rotate Projectiles<- -8.  A4CC AAttract Radius-Max distance of projectile attractionU 6HB,''.  ACCC AAttract Strength+How hard the projectiles are pulledU 7HB,'' .  ARCC A StayTarget Time :How long each player stays as the target (seconds) U J HB , ' '  .  AaCC A Survival Bonus 0Frags awarded for not dying while target S A HB $ , ' ' 4. BzC@BA4 Save4Z.  AzC@BAZCancel R_#16"}::$q!%u! pK8~//============================================================================= // PoltergeistMut. // // Gives each player the Held item each time they respawn // // Destroys the one they have when they are killed, because all possessions // are apparently detached from their owners when they die. If it's not // destroyed they will continue to pile up in the level. // // Notifies all Held items of who is the target // // Affects projectiles // handles buttloads of stuff... // need to know: // how to know if game is over // how to determine who is spectator // how to force a player to respawn //============================================================================= class PoltergeistMut expands Mutator; #exec AUDIO IMPORT FILE="Sounds\ANewTarget.WAV" NAME="ANewTarget" GROUP="" #exec AUDIO IMPORT FILE="Sounds\TargetDestroyed.WAV" NAME="TargetDestroyed" GROUP="" #exec AUDIO IMPORT FILE="Sounds\TargetSurvived.WAV" NAME="TargetSurvived" GROUP="" #exec AUDIO IMPORT FILE="Sounds\URTarget.WAV" NAME="URTarget" GROUP="" #exec AUDIO IMPORT FILE="Sounds\YouGotBonus.WAV" NAME="YouGotBonus" GROUP="" var config int SurvivalBonus; // Number of frags to award for surviving as target var config float TargetStayTime; // amount of time a player stays as the target // note: these two must be replicated to the client Held objects!!! var config float AttractRadius; // max distance of projectile attraction var config float AttractStrength; // Amount of attraction // voice announcements upon events. // this can get messy and annoying if they are always going on... // 95% of traffic is Other becoming target and Other being destroyed... var config bool bAnnounceDestroyed; //False var config bool bAnnounceNewOther; //False var config bool bAnnounceNewSelf; //True var config bool bAnnounceSurvivedOther; //True var config bool bAnnounceSurvivedSelf; //True var config bool bRotateProjectiles; // rotates all projectiles when attracted // mainly to make rockets and redeemer warheads look better. // this is more realism at the cost of execution speed... default is False (turned off) var config enum EPickType { PT_TotalRandom, PT_KillerBecomesTarget, PT_FlyingPoltergeist, PT_FragWeightedRandom } TargetPickType; var float TargetTime, TargetPassedTime; var pawn CurrentTarget, PrevTarget, TheKiller; var PoltergeistEffect AttractEffect; var float QuarterSecond; //ticks the attract function every .25 seconds // this still creates realistic attraction, but doesn't eat up so much performance. var bool Toggler; // flipflops to tick bots and set their enemy every half second var PoltergeistGhost TheGhost; // must get rid of the Held Inventory item manually for some reason // otherwise they will pile up in the level all over the place function ScoreKill(Pawn Killer, Pawn Other) { local PoltergeistHeld tHeld; if ( Other == PrevTarget ) TheKiller = Killer; // hold onto who killed the target foreach AllActors(class 'PoltergeistHeld', tHeld) { If ( (tHeld.Owner == Other) || (tHeld.Owner == None) ) { //log ("PoltergeistHeld Destroyed upon Owner's Death " $tHeld); tHeld.Destroy(); } } // called by GameInfo.ScoreKill() if ( NextMutator != None ) NextMutator.ScoreKill(Killer, Other); } // Every time a player respawns, this is called. // This gives every new player one of these items. function ModifyPlayer(Pawn Other) { local Inventory Inv; local bool HasAHeld; // I'll ask im, but I don't think eel be very keen, // e's already got one, you see? local PoltergeistHeld tHeld; if ( Other.IsA('PlayerPawn') ) {// don't spawn these for bots, they are already on the server // make sure this player ends up with a PoltergeistHeld... for (Inv = Other.Inventory; Inv != None; Inv = Inv.Inventory) { if ( Inv.IsA('PoltergeistHeld') ) { HasAHeld = True; break; } } if ( !HasAHeld ) { // give it to them, vars will be replicated normally //log("Spawning PoltergeistHeld for player " $Other); tHeld = Spawn(class 'PoltergeistHeld', Other,,Other.Location ); if ( tHeld == None ) log("WARNING: Couldn't Spawn PoltergeistHeld! This will explain the following Accessed none..."); tHeld.bReplicateInstigator = True; tHeld.Instigator = Other; tHeld.AttractRadius = AttractRadius; tHeld.AttractStrength = AttractStrength; tHeld.bRotateProjectiles = bRotateProjectiles; Other.AddInventory( tHeld ); tHeld.SetLocation(Other.Location); tHeld.SetOwner(Other); } } // called by GameInfo.RestartPlayer() if ( NextMutator != None ) NextMutator.ModifyPlayer(Other); } ///////////////////////////////////////////////////////////////////////////// ///stuff that is done on a regular basis, not really mutator stuff... // function PostBeginPlay() { enable('Tick'); // testing Config Window stuff. /* log("Survival Bonus " $survivalbonus); log("TargetStayTime " $TargetStayTime); log("AttractRadius " $AttractRadius); log("AttractStrength " $AttractStrength); log("bAnnounceDestroyed " $bAnnounceDestroyed); log("bAnnounceNewOther " $bAnnounceNewOther); log("bAnnounceNewSelf " $bAnnounceNewSelf); log("bAnnounceNewOther " $bAnnounceNewOther); log("bAnnounceSurvivedOther " $bAnnounceSurvivedOther); log("bAnnounceSurvivedSelf " $bAnnounceSurvivedSelf); log("bRotateProjectiles " $bRotateProjectiles); log("TargetPickType " $TargetPickType); */ } function Tick(float DeltaTime) { local PoltergeistHeld pH; TargetTime += DeltaTime; if ( (TargetTime >= TargetStayTime) || (CurrentTarget == None) || (CurrentTarget.Health <= 0) ) ////////////////////////////THERE IS NO ACTIVE TARGET////////////////////////////////////// {// if timer expires, target dies, or target leaves the game if ( CurrentTarget != None ) // turn off the effects { if ( CurrentTarget.Health <= 0 ) { SubtractTarget(True); } else { SubtractTarget(False); } } // ensure that a new target is picked, if it's anything other than // poltergeist mode, we need to act on a timer, otherwise we just sit // and wait for the poltergeist to intercept someone if ( TargetPickType != PT_FlyingPoltergeist ) { //log("Picktarget Timer is active " $TargetPassedTime); TargetPassedTime += DeltaTime; if ( TargetPassedTime > 3.0 ) { // pause for 3 seconds between targets, // allows clients to clear stuff out TargetPassedTime = 0.0; TargetTime = 0.0; PickNewTarget(); //log ("New Target is " $CurrentTarget); } else { CurrentTarget = None; } } else // otherwise hang out and check to see if the poltergeist has caught anyone { CurrentTarget = None; TargetPassedTime = 0.0; // keep this timer from going off // this holds tick in the "between targets" limbo state if (TheGhost == None) { // log("Poltergeist not found, spawning one the old fashioned way"); SpawnAGhostNow(False); } else { if ( TheGhost.HasAVictim ) { // this is true if the Ghost has caught someone PrevTarget = TheGhost.Victim; CurrentTarget = PrevTarget; theGhost.ServerDestroyed = True; // will Clients know this and destroy it too? TargetTime = 0.0; PickNewTarget(); } } } } ///////////////////////////THERE IS AN ACTIVE TARGET/////////////////////////////////////////////////// else //otherwise there is a valid target { if (CurrentTarget.isa('PlayerPawn')) PlayerPawn(CurrentTarget).ClientFlash(-0.05,vect(0,0,200)); // notify all client objects of changes // server should SET the values of these vars // predict general location of target for next frame foreach AllActors (class 'PoltergeistHeld', pH) { pH.ServerTarget = CurrentTarget; if ( CurrentTarget != None ) { pH.TargetVel = CurrentTarget.Velocity; pH.TargetPos = CurrentTarget.Location + (CurrentTarget.Velocity * DeltaTime); if ( CurrentTarget.Physics == Phys_Falling ) {pH.TargetPos += CurrentTarget.region.zone.ZoneGravity * DeltaTime;} } } // this is a server, there will be no player Held item affecting projectiles // so we need to force this to happen on the server now QuarterSecond += DeltaTime; if ( (QuarterSecond > 0.25) && (CurrentTarget != None) ) { CurrentTarget.Visibility = 255; // makes them more visible to bots AttractProjectiles(QuarterSecond); QuarterSecond = 0; // reset the counter Toggler = !Toggler; if (Toggler) SetBotEnemies(); } } ////////////////////////////////// } function SpawnAGhostNow(bool UsePrevLoc) { local NavigationPoint N; local int NumPoints, RandPoint; local vector SpawnLoc; local PoltergeistGhost tG; foreach AllActors(class'PoltergeistGhost', tG) { tG.Destroy(); //log("SnakeWarning: Extra Ghost detected and destroyed!"); } // if there was no given location, pick a navpoint if ( !UsePrevLoc ) {// grabs a random navpoint and places it there. for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint ) { NumPoints++;} // count the number of points in this level RandPoint = Rand(NumPoints); // pick a random number for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint ) { NumPoints--; // reiterate back through to the randomly picked one if ( Numpoints <= RandPoint ) break; } SpawnLoc = N.Location; } else { SpawnLoc = PrevTarget.Location; } TheGhost = Spawn(class'PoltergeistGhost',,,SpawnLoc); if ( TheGhost == None ) { log("Warning: Ghost couldn't be spawned, switching to Random mode!"); TargetPickType = PT_TotalRandom; } } // twice per second, set all bots' enemy to the current target // makes bots go after the target more aggressively // also check for end of game by fraglimit or timelimit function SetBotEnemies() { local pawn P; if ( level.game.bGameEnded ) disable('Tick'); // turn off all the crap if the game has ended for (P=level.Pawnlist; P != None; P=P.NextPawn) { if ( (P.Isa('Bot')) && (CurrentTarget != None) ) bot(P).SetEnemy(CurrentTarget); } } // conceptually copied from the Held item function AttractProjectiles(float QuarterSecond) { local vector Grav; local float tDist; local Projectile tP; local vector CTLoc; // CurrentTargetLocation //Log("Attracting Projectiles " $TimeRoamed); CTLoc = CurrentTarget.Location; foreach VisibleActors(class'Projectile', tP, 1000, CTLoc) { if (CurrentTarget != tp.Instigator )//don't attract owner's projectiles { Grav = Normal(CTLoc - tP.Location); tDist = (AttractRadius - vSize(Grav)) * AttractStrength; Grav *= QuarterSecond * tP.Speed * tDist; tP.Velocity += Grav; // set terminal velocity... if ( VSize(tp.Velocity) > tp.Speed ) tP.Velocity = Normal(tP.Velocity) * tP.Speed; if ( bRotateProjectiles ) tp.SetRotation(rotator(tp.Velocity)); } } } // this is called when the target is killed or has survived past the allotted time. function SubtractTarget(bool TargetDied) {local PlayerPawn pP; if ( AttractEffect != None ) AttractEffect.Destroy(); if ( PrevTarget == None ) return; // no previous, nothing to say yet.. // reset the ex-targets stuff that was changed If ( PrevTarget.Health > PrevTarget.Default.Health ) PrevTarget.Health = PrevTarget.Default.Health; PrevTarget.AmbientGlow = PrevTarget.Default.AmbientGlow; PrevTarget.LightType = LT_None; PrevTarget.Visibility = PrevTarget.Default.Visibility; // if the player has an inviso, it will take care of itself in its timer function. if ( !TargetDied ) // if this is true, the target succeeded at surviving { foreach AllActors(class 'PlayerPawn', pP) {// need to send a message to all players, this is the big flashy one // this is the console/chat message BroadcastMessage(PrevTarget.GetHumanName()$" collected the Target Survival Bonus"); If ( PrevTarget == pP ) // if this is the target guy, send "you get the frag bonus..." { pP.ReceiveLocalizedMessage(class'PoltergeistMessagePlus', 2, PrevTarget.PlayerReplicationInfo); if ( bAnnounceSurvivedSelf ) pP.ClientPlaySound(sound'YouGotBonus',,false); } else //otherwise it will send "soandso is the new target" {// see Info.LocalMessage.LocalMessagePlus.PoltergeistMessage for the code pP.ReceiveLocalizedMessage(class'PoltergeistMessagePlus', 3, PrevTarget.PlayerReplicationInfo); if ( bAnnounceSurvivedOther ) pP.ClientPlaySound(sound'TargetSurvived',,false); } } // give the player the fragbonus PrevTarget.PlayerReplicationInfo.Score += SurvivalBonus; } else { // otherwise, send "TargetDestroyed" to all players. foreach AllActors(class 'PlayerPawn', pP) {// need to send a message to all players, this is the big flashy one // this is the console/chat message BroadcastMessage(PrevTarget.GetHumanName()$" was an excellent target..."); pP.ReceiveLocalizedMessage(class'PoltergeistMessagePlus', 4, PrevTarget.PlayerReplicationInfo); if ( bAnnounceDestroyed ) pP.ClientPlaySound(sound'TargetDestroyed',,false); } } // if Poltergeist mode is enabled, spawn the flying entity if ( TargetPickType == PT_FlyingPoltergeist ) { if (TargetDied) SpawnAGhostNow(True); else SpawnAGhostNow(False); // don't spawn on top of survivor... } } function SetOwnerLighting(actor Owner) { local PoltergeistHeld pH; if ( Owner.bIsPawn && Pawn(Owner).bIsPlayer && (Pawn(Owner).PlayerReplicationInfo.HasFlag != None) ) return; // Spawn the ShellFX and turn on lighting AttractEffect = Spawn(class 'PoltergeistEffect', CurrentTarget,,CurrentTarget.Location, CurrentTarget.Rotation); AttractEffect.Mesh = CurrentTarget.Mesh; AttractEffect.DrawScale = CurrentTarget.DrawScale; AttractEffect.Fatness = CurrentTarget.Fatness; /* foreach AllActors (class 'PoltergeistHeld', pH) { pH.AttractEffect = AttractEffect; }*/ // ripped from UDamage powerup, changed purple to blue... Owner.AmbientGlow = 254; Owner.LightEffect=LE_NonIncidence; Owner.LightBrightness=255; Owner.LightHue=160; Owner.LightRadius=16; Owner.LightSaturation=0; Owner.LightType=LT_Flicker; } /* This is the big mofo of a function that picks players... I know this could be divided up into several functions. And your point is? This is all in one function because I don't feel like passing arrays or copying the same code over and over... */ Function PickNewTarget() { // pick a player from the list... local int i, k, nPawns; local Pawn tP, pList[64]; local float GrossFrags, tScore; // used for picking weighted by frags local pawn FragChoose[116]; // me too local float FragFraction[64]; //log("PickNewTarget at " $Level.Timeseconds); // this mode skips all the crap if ( TargetPickType == PT_FlyingPoltergeist ) { DoFXStuff(); return; } ///////////////////////////////////////////////////////////////////// // create a list of all players & bots // level pawnlist may contain something other than players & bots (?) foreach AllActors(class 'Pawn', tP) {// if it's an active player... if ( tP.bIsPlayer && !tP.PlayerReplicationInfo.bIsSpectator && !tP.PlayerReplicationInfo.bWaitingPlayer ) {// ensure that these are not spectators... pList[nPawns] = tP; nPawns++; if ( nPawns > 63 ) break; } } //log(nPawns$" Pawns Added"); //// list is constructed // When we are done picking, PrevTarget will be set to whomever is gonna be the target, // no matter what mode is played. // tP, PrevTarget, and CurrentTarget are all used in funky ways... // by the end of DoFXStuff, Prevtarget and CurrentTarget will both point // to the actual current target. ///////////////////////////////////////////////////////////////////////// if ( TargetPickType == PT_KillerBecomesTarget) { if ( TheKiller != None ) // account for first case... or if player leaves { PrevTarget = TheKiller; } else { PrevTarget = pList[Rand(nPawns)]; } DoFXStuff(); return; // if player suicides in this mode, too bad... heh heh heh } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// tP = PrevTarget; if ( nPawns < 2 ) { DoFXStuff(); return; // can't pick different if there's only one } if ( TargetPickType == PT_TotalRandom ) { while (PrevTarget == tP) // keep going till we have a different one... { PrevTarget = pList[Rand(nPawns)]; } //log("Picked a Target: " $PrevTarget); DoFXStuff(); return; } ///////////////////////////////////////////////////////////////////////// // this is the tricky one, we need to create a weighted pick // Create an array of 100 and fill it based on the proportion of // frags each player has... you'll see. if ( TargetPickType == PT_FragWeightedRandom ) { for (i=0; i FragFraction[i] ) FragFraction[i] = int(FragFraction[i])-1; // too lazy to look and see if int truncates and if min is an int function... } // now put the names into a hat (the FragChoose array) k=0; // why not? // put one into the hat for each player's frag percentage points for (i=0; i115 ) break; for(FragFraction[i]=FragFraction[i]; FragFraction[i]>=0; FragFraction[i] -=1) { FragChoose[k] = pList[i]; k++; // k tracks the FragChoose array index if ( k>115 ) break; } } i=0; // now our hat is stuffed so we just pick a number until we get somthing we like. while ( (PrevTarget == tP) || (PrevTarget == None) )// keep going till we have a different one... { PrevTarget = FragChoose[Rand(k)]; k++; if ( i > 30 ) { PrevTarget = tP; // 30 is enough times to make sure... don't want recursion log("Warning: Snake is a dork! FragWeightedRandom has failed to pick a different target! " $k); break; } } } //////////////////////////////////////////////////////////////////////////// DoFXStuff(); } function DoFXStuff() { local PlayerPawn pP; // Turn Off FX for old player, and set FX for new player. CurrentTarget = PrevTarget; if ( CurrentTarget != None ) { // make sure someone who is dead doesn't become the target. // if they are dead, force them to respawn. if ( PrevTarget.Health <= 0 && PrevTarget.IsA('PlayerPawn') ) PlayerPawn(PrevTarget).ServerReStartPlayer(); // give them a buttload extra health PrevTarget.Health += 100; if ( PrevTarget.Health > 200 ) PrevTarget.Health = 200; // this is the console/chat message BroadcastMessage(CurrentTarget.GetHumanName()$" is the Target!"); SetOwnerLighting(CurrentTarget); foreach AllActors(class 'PlayerPawn', pP) {// need to send a message to all players, this is the big flashy one If ( CurrentTarget == pP ) // if this is the target guy, send "you are targeted" { pP.ReceiveLocalizedMessage(class'PoltergeistMessagePlus', 0, CurrentTarget.PlayerReplicationInfo); if ( bAnnounceNewSelf ) pP.ClientPlaySound(sound'URTarget',,false); } else //otherwise it will send "soandso is the new target" {// see Info.LocalMessage.LocalMessagePlus.PoltergeistMessage for the code pP.ReceiveLocalizedMessage(class'PoltergeistMessagePlus', 1, CurrentTarget.PlayerReplicationInfo); if ( bAnnounceNewOther ) pP.ClientPlaySound(sound'ANewTarget',,false); } } } } /* doesn't work... // ripped from LMS code, manually makes the player respawn so that dead // players cannot remain dead to aviod becoming the target, or remain // dead while they are the target in order to collect the bonus. function MakePlayerRespawn() { local NavigationPoint startSpot; local bool foundStart; startSpot = level.game.FindPlayerStart(None, PrevTarget.PlayerReplicationInfo.Team); if( startSpot == None ) return; foundStart = PrevTarget.SetLocation(startSpot.Location); if( foundStart ) { startSpot.PlayTeleportEffect(PrevTarget, true); PrevTarget.SetRotation(startSpot.Rotation); PrevTarget.ViewRotation = PrevTarget.Rotation; PrevTarget.Acceleration = vect(0,0,0); PrevTarget.Velocity = vect(0,0,0); PrevTarget.Health = PrevTarget.Default.Health; PrevTarget.ClientSetRotation( startSpot.Rotation ); PrevTarget.bHidden = false; PrevTarget.SoundDampening = PrevTarget.Default.SoundDampening; } } */ \//============================================================================= // PoltergeistMessagePlus. //============================================================================= class PoltergeistMessagePlus expands LocalMessagePlus; static function float GetOffset(int Switch, float YL, float ClipY ) { return (Default.YPos/768.0) * ClipY; // - YL; } static function int GetFontSize( int Switch ) { return 0; } // switch is 1 if the player sent is the target, // otherwise it is 0 // ignores PRI_2, only uses PRI_1 static function string GetString( optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject ) { if (RelatedPRI_1 == None) return ""; switch (Switch) { case 1: if (RelatedPRI_1.PlayerName != "") return RelatedPRI_1.PlayerName@" is the new Target"; case 0: return "You are now the Target!"; case 2: return "Survival Bonus!"; case 3: return "Target Survived!"; case 4: return "Target Destroyed!"; } return ""; } +qyH//============================================================================= // PoltergeistHeld. // the item that is held by players, this keeps it relevant to them so the // clients work. //============================================================================= class PoltergeistHeld expands Inventory; var pawn ServerTarget; // the player who is the current target var vector TargetPos; // position/movement data on the targeted player var vector TargetVel; var float AttractRadius, AttractStrength; // replicated from mutator var bool bRotateProjectiles; // replicated from mutator var float QuarterSecond; // used as a timer //var PoltergeistEffect AttractEffect; replication {// the server needs to be sure these are sent to the proper clients! reliable if ( Role == Role_Authority ) ServerTarget, TargetPos, TargetVel, AttractRadius, AttractStrength, bRotateProjectiles; } state ImAServer{} //does nothing...like it should :) // when nobody is the target Auto state isIdle { simulated function BeginState() { // if it's any kind of server, this object has already served its purpose If ( Role == Role_Authority ) GotoState('ImAServer'); // sends it to nop land //log("*****Client BeginState Idle " $Self); //log(" At " $Level.TimeSeconds); Enable('Tick'); // turn off lighting FX and shell // if ( AttractEffect != None ) AttractEffect.Destroy(); } simulated function Tick(float DeltaTime) { // log("*****Client Idle Tick " $Self); if ( ServerTarget != None ) GotoState('isActive'); } } // when any player is the target state isActive { simulated function BeginState() { //log("*****Client BeginState Active " $Self); //log(" At " $Level.TimeSeconds); //log ("My owner is " $Owner); //PlayerPawn(owner).viewtarget = ServerTarget; // testing relevance... Enable('Tick'); } simulated function Tick(float DeltaTime) { // only perform tick if this object's owner also owns a HUD // This way only one per HUD = one per machine. // If the owner is not a playerpawn (it's a bot) OR // If the owner's hud cannot be referenced OR // If the Hud's owner is different than my owner OR // If the Owner has no PRI (not sure who has who's PRI, assuming it's only the local player) if ( PlayerPawn(Owner) == None || PlayerPawn(Owner).MyHud == None || PlayerPawn(Owner).MyHud.Owner != Owner || PlayerPawn(Owner).GameReplicationInfo == None ) return; //log("*****Client Active Tick " $Self); // ensure we are in the right state if ( ServerTarget == None ) { GotoState('isIdle'); return; } if ( Pawn(Owner).Health <= 0 ) {// when player is killed, server will destroy this! // log("Dead Client Owner Destroyed " $Self); // Destroy(); return; } // try to keep it near the target... /*log("******** begin locationdump ********"); log("Owner's Location " $Owner.Location); log("My Location " $Location); log("ServerTargetLocation " $ServerTarget.Location); log("TargetPos " $TargetPos);*/ SetLocation(ServerTarget.Location); Velocity = ServerTarget.Velocity; if ( vsize(Location - TargetPos) > 100 ) {// only correct if too far off //log("Correcting Position of Target..." $vsize(Location - TargetPos)); SetLocation(TargetPos); Velocity = TargetVel; } QuarterSecond += DeltaTime; if ( QuarterSecond > 0.25 ) { AttractProjectiles(QuarterSecond); QuarterSecond = 0; // reset the counter } } simulated function AttractProjectiles(float QuarterSecond) { local vector Grav; local float tDist; local Projectile tP; //Log("Client Attracting Projectiles " $Self); //log(" At " $Level.TimeSeconds); // attracts projectiles based on their normal top speed. // Combined with setting a low terminal velocity, this will make them // more likely to come into a decaying orbit... foreach VisibleActors(class'Projectile', tP, 1000, Location) { if (ServerTarget != tp.Instigator )//don't attract Target's projectiles { Grav = Normal(Location - tP.Location); tDist = (AttractRadius - vSize(Grav)) * AttractStrength; //ensure these numbers are replicated! Grav *= QuarterSecond * tP.Speed * tDist; tP.Velocity += Grav; // set terminal velocity... if ( VSize(tp.Velocity) > tp.Speed ) tP.Velocity = Normal(tP.Velocity) * tP.Speed; // rotate projectiles for more realism if ( bRotateProjectiles ) tp.SetRotation(rotator(tp.Velocity)); } } } } X< uBB\CC fY`/w*q! v {`//============================================================================= // PoltergeistGhost. //============================================================================= class PoltergeistGhost expands Projectile; var bool HasAVictim; var Pawn Victim; var bool LastResetState, ServerResetMe, ServerDestroyed; //last is used by client to track changes // when server is changed, it is replicated to clients who can then figure it out // when ServerDestroy is set, all copies (client and server) will destroy themselves var vector NewLocation; // Server sends this when ghost is teleported var float TimeRoamed, TargetedTime; var Pawn TheTarget, LastTarget, ServerTarget; //LastTarget is used by clients to notice changes replication // TheTarget should be transferred from the server to the client. { // if clients are allowed to choose targets, it could be a problem when they differ reliable if ( Role == Role_Authority ) ServerTarget, NewLocation, ServerResetMe, ServerDestroyed; } function Touch( actor Other ) // make sure all clients are notified of this { // if it touches a live player... if( Other.bIsPawn && Pawn(Other).bIsPlayer && (Pawn(Other).Health > 0) ) { // log("Ghost touched " $Other); HasAVictim = True; Victim = Pawn(Other); } } simulated function Destroyed() { super.Destroyed(); } simulated function Tick(float DeltaTime) { local vector Acc; local float tTime, i; //if ( role 10000 ) TargetedTime = 10000; } LastTarget = TheTarget; if ( TheTarget != None ) // accelerate toward target { // Make it accelerate at a larger amount than its top speed // this will make it home in a lot better by overcorrecting // the velocity, forming a quickly decaying orbit. Acc = Normal(TheTarget.Location - Location); // gives a direction Velocity += Acc * DeltaTime * TargetedTime; // multiplies it by speed if ( VSize(Velocity) > 1000 ) Velocity = Normal(Velocity) * 1000; // limit speed } else { // otherwise come to a stop if ( vsize(velocity) > 0.0 ) Velocity *= 1.0 - ( 0.25 * DeltaTime); } } // The Timer function searches for a player or bot for the Target to fly after simulated function Timer() // pick real targets on the server! { local Pawn A, aC; local bool FoundOne; local float ClosestDist, Dist; //log ("Timer Fired"); FoundOne = False; // locate any visible players and target the closest one ClosestDist = 3000; foreach VisibleActors(class'Pawn', A, 2000, Location) { if ( A.bIsPlayer && (A.Health > 0) && !A.PlayerReplicationInfo.bIsSpectator && !A.PlayerReplicationInfo.bWaitingPlayer) // only go after live players { Dist = vSize(a.location - location); if (Dist < ClosestDist) { ClosestDist = Dist; aC = A; // hold on to the reference for this actor FoundOne = True; } } } if ( FoundOne ) { //log("Ghost Found Target " $aC); TheTarget = aC; TimeRoamed = 0; if ( TheTarget.IsA('Bot') ) { Bot(TheTarget).FearThisSpot(TheTarget); //warn bots if they are targeted } // have the server notify the client of authoritative target if ( (Role == Role_Authority) && (TheTarget != LastTarget) ) { ServerTarget = TheTarget; } } else { //log("Ghost didn't find target"); if ( Role < Role_Authority ) return; // don't let client do this. TheTarget = None; TimeRoamed += 0.5; if ( TimeRoamed >= 15 ) // nobody in sight for 15 seconds straight... booooring! { Spawn(class 'UTTeleportEffect'); //FX at point of exit ResetTheTargetLocation(); // place it at a navpoint } } } // note that this is very inefficient code to accomplish what it does, // but since this only happens at most once every 15 seconds, it is not // a noticable drain on the game. function ResetTheTargetLocation() {// grabs a random navpoint and places it there. local NavigationPoint N; local int NumPoints, RandPoint; for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint ) { NumPoints++;} // count the number of points in this level RandPoint = Rand(NumPoints); // pick a random number for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint ) { NumPoints--; // reiterate back through to the randomly picked one if ( Numpoints <= RandPoint ) break; } ServerResetMe = !ServerResetMe; // toggle this var, which when replicated will notify the clients NewLocation = N.Location; } // when it hits a wall, it will bounce off with some loss of velocity // note that this will only work properly if bBounce is set to True in Movement! simulated function HitWall (vector HitNormal, actor Wall) { // Log("Roaming HitWall"); Velocity -= 1.8 * (Velocity dot HitNormal) * HitNormal; } simulated function PostBeginPlay() { // Log("Roaming BeginPlay"); // log ("At " $Level.TimeSeconds); Enable('Tick'); Enable('Timer'); SetTimer(0.5,True); // loop through timer } A&G u! Ai u!  2nRIFF&WAVEfmt "VDdata  )0  -08GA@GLK9 /.+>OOFBKab`zz\bhQ3&?21EV][Wm~mja?50 (#Bg /\ty.m({ /Wla8zq*a"3@ZB/h6 etQ:W3&9}S1YmcME}y,/jpB>33E%2EOM>EQ;Ti2 p<# |)$P{>m{^|bF>R X=gQU>CIdDD`Vg$:=l k=3jziڴulDPr=Mپi ԧ^0ރX:.=bpD *B  VzC t  V 9!hu}Z @%} # + X o yc ] ^Xo` f9ib % H"^ \^Fk/B '2x#1(Y)7Rs$U) H. !x*Cm ;m0#:i + N Q F KZ [2ghhS"&#J s *_!. !e"X- a g &\ ? F:g"fv#&I# z-A|_jE`$0 ! =[[_ /   $ Y@ia R Q54JW58Nn $a\ ^ \{p;'0x `k9)c"#R!C8 6{2!#:  ` @ Q& $ 239[ 2E  `h f _Ze>ND,2 c "isX Yz FR; c-9 ] Q &m i Kt# !Li B;84 M(o>e4kv9\(67 ?RCAXPs N<rU)jH{Q$F3.3NTH|u cYX #A 6B@ L{` i9hl5 %<"/+ r Bn;G:; | bi ; hwq7gQM^G X\hdSrUGyIqjw > i4gE!le$tGs&_V#>(DmT[fP=$ P#o\i2_m;^a^zO>Ao[vl^sox_S49"2aefs.-mZ82(DaAK%Ft4;0r{u@% @{2}u5bK7@crj}{\%XawH#28s= eHD29+IeZ_W]=hMd`0}w]8Ks& &B9?:Rw8K+6NJ`$<7* OuS@E2N>>mf v SOaj 4 fUMv R 7 >M} 9Kd`ySr7q\8}%n%1K1:4Ep*T_pBAg_7sfGthv(IA,ge~ 'Xb*I;07<5?ZL_JG}  + b#aU8 A&+38<6.$U <܇x{tB `"+S01 0-'\rڭ֬G }"# * %?qfޛ6& iY hfx#K6,=pg rA:BT. W OeHxO3[ # P /LI1DFfG8] F'?1H""'&()*)(#'@!3 4+YڡS$)+E-%0/7*  lL".&*+S)F#m~K9фҼ}cUh %&%!%{/-Qްk`^4TnA kh@qO ?I^he R  LCx4)@CM}+PZ0Fӷԙ,+_eRb2HAI ;c ^ S v y }Qgm$ !o)0Z5;A=?ACrB974/l( ֶŻK ˦l#<&7cOYgDpn7h?^rReA ( @݆׏Әi%ʉC %@JxY>`v_mWE1 ڔXĦgud^*6M]2jobo@hY>E)N A٤Ǿ\XxѥײKuE(;wJTVRF6" ݱ̯/| y?0k=FzKID7#a Jݤ.̢B9w` +*6-*D%l߆Ґǎĵs?mr&)/)$ky|a;Y mr. =FNSSENG=14W*$n-;nQ՞=^![ -~4=MM4MQTM?9-c'ͫ.AU ,:>1E>6$(_֬EνпIvZJ֧ 0)AZNX`]YI8i%-NG\=W#- 3140-$0 m0Tol*9O [l%.674." 7ئŸeyKǫњܹJe { ? /Aݦ ۊ+RԈ<2t] ;6$3&+K/00U1.;7v4i12N3s#` Ѣ<Va֓Q%3?jYEWQTVX0PWFj->UsNЛJ7.ެ+/D*ImNJmCh59# /.#L s0BO\^^WK}<)SN1¶;ɽڅ7!,4585".$ (;;L`ɿ&,#%3= AA<5*1 ]'W8Wk#p}'&?[yt^OT $i*0g1365d3j2.0.x0.#-r-+.uAUǮLٳݑ E 3F1>L=^XTSaSFJ=% ELJfgN$:8LyORYLRD6$ ]%?趮q.>HsQPZP{I;) ).#&KK  %y-t6-85-$n'ӈǠ¥_Ҫ }8)2w8p;1(ƵNغӶظԒW<YV3 .T%/{0߶f_ 2.4EPG$;=ME JMGB\lswF}xh*G*%XjY<3[jRsvv oYx8̄^,Z(>4wQ>gv{?|yn(ZGA&tVw|ۊ C$L:LfX^1\#VJZ8]#\> iR0 }1b@VL4UZWUM>,ځdԧ0^/6GӛQh/(+&,)#S _QUX73}Oi1*L`M ah +X8<@EI^NOPC([Qk˰LUֈ> /OfRmZlqxAu&iO0.|(ɏ~Ë#I,K_f'ijij]/Cg!8 һGY8I/4K`RoOvlx\xt+hPQ 4| =HȚ$b$8iHT\M] YHQBt,Yϵ§ƿۃ A 0E=1I{ROWVQ7F\5! {+6`$㢍ZU?& *(y%&kMПƠ ϭCٗxF \6<{mt %_1:?mBEI J6(Ɉȳڪ߸i0L|`\d*k*q7q_jXB7<<1ԋ͐S}~n05ڝ,-HV]bd_NP0gȻ^\Iۓ*Db~#BJ`#t]G|@r_^E^( жL@E:Shlle{VB,)TÙw'˟hݹr1^G+[msyNz#ucrH`(șՃψK׷&/5;7X-*! zӘ ϕw7~c.7z>AB>5 %m}Z3+iDWcjgb0[N;!Ɉ|*4\ԡ-6~JTTXCUG5, - ɽs]ɂV4ބs3#+8+"\Qx1j}y8 *6!AJ+T.Z8ZSUOJKE- %1ͦ\M68P _cWaWH/*P~Yr/V,N >&>Um\2 ~➭V`ٰZ̛3OXPD6Y' 7F˰דI7TUhksxwwgiT=% 8{7]Q*6HRiw(|Ix'jdR3,kx4ӫK8q.@S6L]vi b|T@:O%FQ՟lU57)x@.PLU\SNF|5 KԶٶЯKO)(9H$Y0it0zztv7jXF/ v-ߠR"Աl[1^yjC5 rڕ !C@dZ 2p˷_Ka?3_=(0Z sxo`P@7&q޲e0^ >b$%/B4&1" }WdמҘPΦ #V6CKH)>^3'& bűs-᧱ƌّS**02/%`?2G ǜ$ߔFs  & "=!&w%+i.5:?B8FKUzR8cYOb uܽ«yz7U/fp|pxZ9~V3tݭa\ e0F etiH$@(ۙwڵI76We]aD("{RDH:&D_hY 9o$!pc%ɛȱsϝhr*AOK?E5k#,v_Ƭm{lCxd Oj 0!#?&g^j\|׷њS3C,r Ir!'$/;kKY-c dc;\?RJH:2!..^(aJh_h81^̛#u 4Rxy`PW!Kچh Πl 77D>#% Ch׶"Y(c43]!ۢc(̐،h p0ELF/pj%<|ζb~ߪ0-/g% / CSSs=o#284 3P7A9%2$OOfPȶ҂ k!+-5:-%8 7 .%!'t+, *&&())%4 scKY|B[(_3;>60* KgB}%5=?j<5+"53֙mŝԫT0 4!'(l(v&D}jGP)|#hۯ=ڏR^F! X q `eћ{78Җ[\k 6 ={JC)AWU"+1/.,S04N64#0,,`(U :% Ni&.2J9@TC>K4%(xB@ԆqSUe'7?s@>;5$(><5+ O"Eh$.6 :9C98Z2'3 T:Н:gP|`J iCt2<чϑv `ڔ>  )?*R 1*2ۙ Φ4Pըz Q3 wZ(n@U8 w G p s z T Wn} [l!~#a%Q(C(%l# l;M $V(r3u9:< <[6,!{ )o4 {*6T?lCCBU?[:0#5R?ܛޡ2$!$>$f!(2 Qoa3ӿԧڭE5 j!Cp mE̛ ķį)ٿ S  G%8dJe% i :6 v +Su,\<Գ2%؇ڱ7O n & (e&"|tlե'߀r #%E(R&|  q]/^Ԩؾ O*@%-9UACB@~N֒˦+Uk$)('} GW1χ+ū~3V?^"L'+-- (T>69Ϲ_Р(x3 J3e '2ld= 4 pUTM + U   ! t]Xߨ ;rte B*0-,S.-f'R?gչђԇxy;(-,&"iϬǍŜf/ 1s+64<*; 4)8# :BP=g L-"'e(t)y)&l@FqIؼ?}%$&1$! u:g ^COС)DK9M۽: ՜3X ܈M l U\'./,,/|*&"i*{[ Ku'<069C;zB>2N%Z61yAc /`!*/-$s* 8_Ⱥi|gG^ nA$#~ Oтƽ\`a( -}S "!P^<)A M$X!w- o i69:Ɂ,p_ zD}1dX<>fO clܞ  K {G&+3%1&]Jѩ\%4[E-&.;DKSNMC3zquOh !@RWQF<;>' ![.),LT-֜N4BWM ~#(&mn7ث ܪzzzl(/7M92&{[b Nޑϋ83\:4 C\anmک\yne/w  P~Y5to nSF2,׀گ <!d+c03753.%E#y/2B."$jʖJ2XlVu3v-S500)@L2 "e>GSH_$6Ki 4Nd^8]þ6<;QI~- \?2 ^'I ʮ! o;OMN3 PM# fދɉ*&y'p~ 7JS!!S |6’ך߽Wd gTv;(zMdz̳#:C^e$ %[8;.Y~ %/]ĦdJd'p<P_d[I39UwKeVOqtq5  .Ba?s!vK*t?p7?!A1k2}ɶt1'W 7;Ҳ)MA4ƶm~JJ w'e-S^O.D p TMA֨e\* |.ۉԫ*$# ,-׹m yղѧiBRCҺڭdۻQ-HG~]iSo5  lu!=Sr21  )w@QNuNF. 1 5#CrAv@4.0Go{c4f +0SGyI-/㨡eI D*- PN'P5{0 /$].-)o'JA>$4Y b rߌN~oJ ͵v=^S5Hj؜)Eݸ>ܷp  $ Z 6 KSKǴ _!,2070Q*V;% ' O v$s3H^mq}M~&{&c%x'6Bs8|5{H`rf(ZI EzY0A=.OI(s)E.-Tתk4tE^Y4mdH| 6Y^bw H[cW*4yU>,0lB˵n5 ۍ%)ճW!&#B&xwB  h|-eEWIgAl o e}  F !q cP$/m8/'8.agf]Qyc1 <"a 0^Kg6ƧxɚΡ$- R*oF<>lcx3@e^b]3bO&^*'T³Қ."<<5+qY<',׮˷2"n؟FN{ FZa jNEįښ$AQMݩ5[ΉƤMף =_Agr  H g#SP %*U.142\,#Ai(O6J>t?E*J}MI..3п$NvnnKI-w6!ZJ= fc:cQBRƼ]{ >G=x9:Fg'6Սݖ(4VrG]"`%4.1%S3@@(jթGLֹ#[2Q1&" J8{e In*(' @Ԑި!5@\%D\ɵȲڜ<= D#c)BPE?YQ , Nc [ 'X+, --9,!^'G:>59}QuycF(Y3`# v;&2#+ѫڄ=M) " F\$?iC3H ##" 3<'.c5O(? n "'w@Li0t- = L'|!:uCO>x X k[sJ\N| ]U^B!"!J( W, ) (t 9  v Uu2FBjA`qCI_xS$  'pN=9dP(x  w A'1DY2l#"z{5V   Uh4OD u H?kzVn%fBI{k ' 6 nM+%.z] U3h /B  "wci0    .G= GI Y L2y0m' OD K"/#k&.4o6MT.Y+"|Dfi{h.8wn2S;SIaHo]I!F\v_c P/| K& i V vF P K#, hH Jo ?  @%9HG ~ Fi + )VqyHZA jp7tVqe$aalSunjwM5\) & 5(Vv 2> <,I wknU# !EU:B@x)%(0+&m ]+rmR(-O%^j mv &{ .(,#vu;&YHZV? l 7+;QHI{67J  n ;; r8Ta4~vVp2 @Dbi x X &J'|B't/@`Y]#x%#$"4  Q  )P Z, #),=+O-l$6$i22ook(g[ * }3) n~ k n~)"%JgJ  B9  jx=  Qߐ U#(@iRz Rmb^=l ou;Z}e\ [Q2NXK$ Q1"R(߫ݺ߯M  &q MF6h q!&*+*}"2_n A;28O0)  .%*?8r1) O +m _p$aQ ((u( X&l=G+# Rg-u=z# !hoܭq$"^/Y 2?; T{jtZd0S W'i{)`xAu_o  YA ST%<&$A- r"5%RxN+a3#KebAŢ ^% U$  <Ghw;%' da]x7  H}EqTd?H>*6"74`K1)Oo-+l7H.0)D:i # _5*|,]QP f] <z} 0_ {# t ;V+)L-t3Z iT`:0/hUAvŮێ/J,L#)C7 hdh w*, q A ;!KHn?\$P d5(KOD%2H~LJxd5me# F LH4-$k-*Ex &8] U Ih)!"$&("j! ':./K-Af 6 [/.p) 74m{E@' (^! -Y8 &" %0$[O 5(uD(E ~F^ ^TxF)U^xC` )  }jBiuH+ < l M"%')f)"J7(+,%6 Fy4n%.-|0ޞbYfn,  X= WRn OI.Q y?L9clTqS!NZUl/ A9ptR$y9p= ZJ9CR,|tA(a`K[5 )g~sW F (1,3,c*) n# '%O0 LsJAj{%*r(e[ܐy x X7] Es F  !*3M/;Jb%{k G(9)D+Gy.JiFqG&&73}rO07:!Ij)j f Df1Sx "l!8lfH{ h(I ! "@<bK$!E./  q{e p , [ 5?OW}ptxN" MO ~^ Lylb%b0S zfT0- Q _S&zB]u(.qU+2K<;E5V"zz7 KFCmI1 I$ v R Gx [U7h {fINqo .M:Y6&a\?ecAaqEJwLnP@orl(]-fuXh&.EcLV3Y= QE9, Y'^?w7!7QXxjoY#hMn+  eY73RN$HX*& B p D f R1   O &Ebd!`f 1^uZ(hLn5~6`zyC:_c c9m=!'6QYiJ[nEKoN,?>g?h :-B 4mW#D42B(qM,jxE^g*j  2#Y%'S^4;  dh2 3W:a! jv7j gi>"mZM='\6 AWb X|~ m]$;,N/(H em> S/ {BQ@_DLZ5H6D/%4r :=t] dwi {cz r,P_'VkF $NzAlL bw )0   #ydM\X M+1K_eE{ vBbjc P/6w-(Y'w!s4!1"* ] $LBM*Fi0 6:43!J "W",OJ.oh'H- )eۜM S  1[{q@tD -) 6<]7 ?Uh 3yKJ5s QS;HsE 8Uyb c .-OM Z[ g ] u RB@X^ BD-VW+(138n;a?9G5NlLH@]"g!MQU_]N/+Aj!EUR5 * !:; IC2*ê{n o >=fx*+" P,73"ͣηٱ_3 u-o)hCr_l.?BX6wo+U5.@2 qB~varH%ur oh.Fj @VS'FG+2Pi& .+r @=Z:'2f9f* o$'-0|m7]+EFJ* t\'+%A>FMFM:( CNjz wMȴã{ $%%As%.97&t }a@Es^,@~BE9b-# t<re$+Y0"+.> z5̸#'41Z vaۅ ef8ޜ f!x(:fաbB.e}"e2<* ^gx! `    5H }0=gDEGG>>+^V )3IB EFG.%Ż`95S[UvH6$,!F(hO)"]bV (';PC@?3ͦI2 /<>+ bYe;NESN@ &d 4fN$.B6.]%!1n oc4A?2ZAќ0 i[!NHLچrrm=W>!/[%ٞن_ :F9F:eibE;5f`fN={G?i v(6zDwLwI1@26#ߴ-KnKFLL2X8f08.xI үvQ+V2:2&/7o܅c(2>1#) I6/!37J0#;E" ] #&X%4 !mӝ[U~$ؾRz `އ$74/YHME}7hx}pYdHX17 3NR#'- 47k60) #7)GQMN?6 *C2Rzc0dqU;${ lH6['S~ xMqwM1:x:k/ 'Ů+, Z[މ(.*+" f}+,!vt sf;]V#iU ]"8& &%NS,gcr{=rhۯ0}}u^6WCb/9ha N_ rq%0.=6|:q9k4&(Mr(5FJKKt*Ae$$@I\^2Q;#d ZiZ yQ "& ͬتR3A "1}7z3%nk " DםlՆؠAM RC~Q  g T x+!a r vw6nL[~yki]kR$5$AܒgV% Q#kb $< jS7$/98?2)) $<>NHyHL<6 kӶ:NeSH.4"f P \ b zY%(#$"1Ĵ|*<>b&0/?$^wM м Q PrsADc 2Xt7 t/%/7  HHSKl C3(`p#X::jf,Xن^Cu)-6ll{ P/5ksHd/%  n *{37!<9 %'+M 10$56a$8OaFwE~ݠGޥF\rj\f$YlD{9"gVv lC=E{X\lL. o.@PSYUUG2Ae$GXTS WL ,2DKBn/R !(/1*Nޮ")H,21uNΠƎjo#F0-j R'. H3BH5 b   IM {  |BvC3 v [  Zb.~-9? T>-~K;[Q& B/A x>\ X'La$/b% t E'2&'bש5%08JD%IV+\6:z4$3sT0IEMKG+^*9PV_^H&\k@ f$*+#5  !VrјX,3s+r'!,: b fJ8V5jkYpz"t#`uF!>I7ZH*@/N>H6Ae!  3"]>TpNK L tr/3߱`XuǷ\NY"  [O O<,w. .0'"N  "7CdF:q%S%WU"M,0.%~@v( ]QH$%40ZFY:"^ {!wܢT.d I9C 5CtMjW:Tc&VKc%(a qLd  5m [Я+M ߞWr5cq;%wvAq @ńĀ]DR%a :2yUXw" >3Ӆ"',%q `ִY $um?7 6/ , A)yel1L6twP  G> ) j/vR WdJv NHvYq_hug@u 9r[ (-[` )</$ pmјco2Ro R-o/1X8uCY6(f/+$*(+&_*070A7G;5-I `Fۭ) -֛Bďf#J. +;:f=} $<z.-~/ Jhf8+ #& p,( 0}[ :Q; o vLm0D  |: u[l"}%K *bCS O/R <4 *)3~(Cwu ;+7\#H|  ~2B jxaM  $05 Ry%(2 #8~)r"Wi1B&D pN  NP ` ۽4 !r&yQUXl7j4,gJ ; *>h 3p#y~,=cH!W _1; ].ug 9,uYL)-  g* 9k=A$"9{AP-Eg 3/Qڒ[%bc;P;?C  juR>'۫ \JWܾ X5וt j= !6DHIXp@kѩ r-N0Hj''? Oh w[ F%qg.d\1 ?)!0 O a Er}  k{O|tQB/|Kkd;,N+~+s. .(<G"0 1^"z ns NCtU p,$?~+ )Ho(u.\Eި'E혿]() mm(`m f O2Uq p :0  ><)z]k9N bQm! %@~ZC]G7mI >#?G|2|M iAB%+֭Uu O#&]%0n /j !M\3_TCg j !p 64 ^*^ޱbZ܏  Q T2݊ cQ )0ed#bR)|' ^3 B'0 $p =ƣ%5:p-/ L8 !{$p&0ˈ#}d/߾`RF $Bk:W*2\j 62tm "f ;+-q"DQ (, E  B 2;`& KFeo,  Q2w\ {[U **+iyr :i&  ~ !H*N h C w OxE#i[E_R;p'tqފ%.Mm_ z- oaf!50. aQ  > [ 5_G # KoX!9 $. a*- Va8 - an5: 0:RrLrUWaQ'M/ 5a6?Bh9' CT=   D6\&H@8e!  fMF E `o=%s q<Yn' ?/pA aN<TGdHHnc{ JtU7xqI+^ `c!}f6 n i d`  s {U1." Oo^H8 NPo8l>_^RLJ lJ ! @Q+xf0 BS{~s P  Q:c>J_; Ii \fQBWeL0X^ m)lL}#z  gd-OR0?N$ dE VqG 5 w>  )[eH] f ,s ~ UP\ %6 ]hO<ieB |B[: H"391R r^p}v  z AC\$f'  } _m 6E - -+Yo e 8LEN VL: -n R5 \\}5/0L CG#o {  ~ abW5625bXu2#"WGXH00J @ g&kN6%.?jW;>E x GV'33#iF#+ Mn 9nRb31q4 [ vLu` T/#cr H c PE  FgX,qt 7  9N` ZR@uKhwfV^N_x$Z[)+1.p# ~DddizRe3%Zt c@mSz` ] (N q |B  X DZE6 '9 VYE >1dQ# 8Xf(@Jt/b 0`v34) <-Na3 mh>IE'  5i tUr5%KzZ8 Bs AcWAS ` V9Ba %( K c ,7H+c8_ 38lMxi f%$ iv 2j{/6!cxsif~ k$+ [&WD" W * }u{N Y:  CSD 0D4 }B&I  XN>p [{YG@r*q;8\o n  z'.`E# {{LV"R JpNW/vh h(,LP %?,i+ !X( a~+h- ]^Af0Q0' (wVrJ [pPܐ's6~.:NYt*"QweAM_D  \! W$T PC'VVtZg )zV\,$R TP 4m<22oU' GKP( T c 2sW Z (F&^Z1}#_|P uDW K /8<Q'p%1.%72j &s / t l+2"W#{c  bu - (+D#('01 &^"R8. Z 3I)!H' 8B5839 z00)S*b8.306pPS$x;?(&03"A%oBO=fxC-^;7""e;?Bm/T:)%XY rD{6R{ [T WGN)@?}l# q `!6!j"$&W*.-d&*(,z)n#x   2u.#1 '"*}ItO 7c':UsW  UUE|;b8NVz~{3[|C4d?T|Qjk#W^8Pٲv0v<  {!!h&)*i*.23, !*qlR<23 ߱xd^ y N* `{דՂF؆w\!$&'E%#"w i'},D):U M   DNO J5lڑ֢ز:oؼ\_r52zo0DrRTݒٺתպӛP$+-irsn % S f [S&"+"*5+5i;;4P3>C=.&#$vunbԶw[ "= 5/0(&*Z32)  "ےёKֶF;iY>$`1c8@DELBA@<6+ -+ξ^qA ұOv+G4(0+52%11.V)"[>obSּīŌdWj;؟hR %  y۞4ۄ՝ly-։~ҧ؈yc |Qj!*078G9A6"+1'l,9/' y&%Qʲܼö ų/0_ )4 5"W Q .\43c&ԕݼ{\ %/4>-?A@FD@:7+D+[Z7 q1ʆCC'j"1'7]5C536v5@,#I $sޕMǿ9]ƆV̾z!O ^ d  rM2-hV:=˝V ޮvL" >#*,2hCBLG?vAFE?M1824`..(((x#YtpԹdʷԾD׊ۭ c$C"$*n.2-)!t|}߷ρ&5ˊKOH ]#2e<76 6VF2E 5O-t+"Bs |kz'KјׇO .2$'0 6iDD=7/ )(j*pǫ(ήHɫ׷B//8 \m VTW1ٻ9ۥykN5 !.T?JIADZMMF>&?@SB>d79_@-,յ~n|$&$+7BZE?KMTL]@(3nfA#%"v"/FyMJDJMKG@q;V0~*^"k"c@*QΥ`3<ЅMR}->9GWQ7X\XUUnL;4>+Mjߏ~ڶ+xA3!(&C; d}rv"%!G*!ܑ*,o ;t'X778ANTPMZTxWZW_]2VTV[VLlH|C71!{VSU!,Tx &1v/7F.VZ[ZLdn[qyZI+::15 *UU^ +D'0 %z@MsF@CMOCT0/.X5H5g&g!U8Rŕ :&"{%?-SJac[~Z`Efi^J =;6)@(i86pόQ z7$j)&~=8v ]*F5 ]&* $6EGFLU[]dXZebAd_qXWUS:tӔl#ԩ!-B B*H;FV`kz|8dL9NމET@%'&)0:d>>2AAJF~F8(<:ʑ 欅ػŲЯG j-o;JX?f^cbg^f]KUL;F;:)?Ⱥްֳǣďʋ!8˭  D".."S!-׭!ޱݺ}t%#X%5|HbN{EHBOD[W@J@$C[JFW5`b\kŠ7v<hH*;Vks|TjYD) JާQR4)? NLFKSRO?Ec&T7I&!+AMRSW_g]dXPAMqE4A+}‡›D$∹ӷ{ݐUM .Q56 #(R  pRIWRX̠ ۳ ,,5d6; EJHc@CN!PIDU@3(ŊAQB&ס%D\{Tl:U s&8NA\i({mz[j}S6JN~†pTZ-$ǀ#kz !J1Z;UKO[B2C ߹\]0tæz"y 3CxRE^1i9v{'iO0%k'W׺O2a+59?JSYXQI,=)ivKϪүLҰԵ8@("2?N ^QiYorwltojbYD+1'>+:"1Ϙal8i ד*&]C_Ot{sjuS2z0k\x֯7ZZ]hE+&&-jq(mO;ƥ}շ2(W}e'07IeWXdq|0r^[G/:? O8ة+ͥ!<#q/7*Ov {IޯqE̦ӽJ? !%->{O]fdo@qhS8 ] ڱ'/ϵ#T)P5ANZ5c if\P?0 $2#ΐ/ȻWڌr,~<@dH $H`q|_pW{>G+ 2 ˪'kM۽"U2A HQ\et vT)iy׳['59:6u-+%u,Z]چ[]ᜥ9W fC15:k\t}viO`4*"׊͐Uڵ0Pkx |lXD.z1}ǰ>:V' %4BT_ohr}yX@6>ϳמa33Ν\"$)5BA?MDMoWYOw=$T /}wtTT:5ʿη׽xv4s&4@eHmLJE1HLN!KFCGEC>32O"r{uAONIQ% gؗ5/ Y|]{fJ0f+7V 2JbsRq[m<_DJp7I'hvq9hRDT;2?_O^eel|yhkPJ"/2^i\~H"-52:S: > C>F@0իDA( ɦ^?Ԧ9,3 | "cf p.7:l7R53:7FNPWNJ<>2N'ghhgv`3ϒA㘻@Πh/Aovp g1\zx-pdB[-MY9)(x R1Ҭ>x ت3 F&6>;(7(B#~l sسz#% w<9TZ^k:qp~rpnlcAU@L"ۣ._ӆ=@3*&/3*l(z'b)8--(#>oki Ъ!Jǻۧ[zcx\ITzV"N+0d,<#v!'S+&$. Wlge-'q󢳤I;Sqz?<a;QFTQcXSj^gpw$~}s`$YI5Z-em]ȝڣZf;ʓ[m%0! [ 8Ax +}=KzLI,OO6A-)/43+: $xn%ߞ뢐Wj׈#"7߿R _ '~* !@4#O')>+$#y43hQp!Tb)ܐ۝u݄ |_ڟZ:ځ!O]&3>EbBH>D(_n)vzjVA;'(F3帾זgM_ײ4'Y17:7"32164/) H)DF# Y ?3G'13) a>wqljͯyD4$+_782}27'AOT$VePB2$'$Vf$<ۢUTHdbn3p7D;S<AE'DGT^;fe[.WN3?2(C7c\ gFsp+*z޻hf~6OAb.a]a^,`paa ]VHS[XS(H=]/+X(TJn&njtaɌ; v@O އd=` #_1:nCE@i7.)x(K'$ ؓ!khSџ2EcQ.5;/EBQ\a^]HUwG'9C2* & IyՔ׷aԳnį ̧< %9$*,<(eT(e9tC! Y;$))+!3530K/67:zC5 ;٭p"%u^‹[0\w_H#~Nq[ϓ | 8#EC>0G $6@ g?= 9 '"Yd "%R) ,)%= )IҷľST{o&0 mL &$#!6 "b%. ,r)'E"Py E] *̲6;!ԘYHOy s&W0M2+<&%%_'&!+ 85'2B9:DB-WPR8_lVݵ2sr=ű*>e&5~FOTT[_(eZmtpzk~EwX/5c] ̡YiФ2ue﨡92E;9k5E0)%SC#( 1[:w;-b H!\Ly  J D"nТ{Ο+1&ѿڠ>u4'Uhmia"[-TNF=72-}/͑gL2ɹ W MtK : !+.,C@)Ja`P a!!"!#3*03{7v95;\<7X'2%;$HJƆøvLNG}х#7R}eqy}wedJ&h Ϋ #&ĨW;C's),-W,)>#)!!kw[QZMT|1 uk["#'C-13+ J6ۅ!Ճˋ\笪׫ ˜ӣܘ߫tl"! #(&%j ѴҥٗѿʈxͻzNPUJY ~`F; Zw:Wm /yӆԺz܃޳޷X]zx'J09]?I0VdwMa7t *_:W`& 3O3^iqUybNRsw٬3wՋ#՚ϰ{D+@H.LGC)??EINy\ib?߇؃(P3eRQ:4Chӱgɸ~Q>z+$q) #3a0)#!Q TxC3H};~ `^?f>7]d؍JW K.< ?oq<S_#_-"svX k"+-97s;7(4)53a4|8:t<9 i9%ªQIll䷾(8%GF1eT{NgծĂmB;cnּ޳"&25~.Y&! R%85KafH#4$T V-o`߼ĺr.q,ٷ?ʈ{6PODi??BHY[lgd)_U]J{D@+/&&tEYҢE'׬МQC> `)38?>:l6]22J70/*XȊȇ`]wJޞWԫNt ^ g 1 G[#+g14x2s2/-1:GT]9\V@ -ݕ!l9ЕVb͖ds0}F [5cjmouh+v :PJؔ ŽѤ2KKa g Y; !0_MVPOKE)A6+&;2ؖz6^d tUam6-`y}zrj fZ8J}7"7"Uُԩ{l&8"=-TD!ɷTҮ:Nvn&35.'luQ<"} 24 ;X +UI1d˭Ц϶ɇ}v9-1U(a餴ڡv<iȬ{λ³z K.\F0huq\5HNjㅱ˃aV"@;MN-MIITJIINZYlx1xPf@  mn% K;c,atħήYp[.65#(]v&%d.4>yJbI[ahgd7b8]WWSmJ>50]H(J˴ 2vE#ƴ!:mUw= !"~#)%5-p>VVl{r_H=8 &pڥsr=;xO M O$ K"m\sW;:*i$(X0=9xBI!B) ?TY3_bհ'lĘ׋ƺGٔdW<0Lc2xyj\Os@2&a &lŧȍS-vʞ/bNTfs(umrnj5hefqhj^li]*O=0!)wJڠ?!CIWұ߸9(BWq~uvew>>o/Ȁ _‰Xkҫ?-cx I5CDSfiY;22#9?@@.#-;]h&В w]E&fhkh]F.#&6K\lhkz[=#H H Mܓ5]ֳjmV@10?F"FDq>70)" n%+-y' Ӕپ[ٯ!Bʜ!J(-9RD IE7=1V%l1'!^).0132J0[$&0Zt9qߠ#mnmfG`\X#TPPQSPD*=93Eޥυޤ֤:)ʮGv>U,I3H9@AqM\lm h\Gz( B$ #'[*%˓΋UںƊдٿu(BAZBdTaVFM4, 0@tUn~\9< !,ϲ-)F7ۑڂq"R} N $  <)[Ix},cJ7" ~LTO&]Āȷsνԑ4 ";SQage6cc`>WI3>4^.( t>P[c3zu3B1h/dV B ~A>QW^egPif`\ ZZ[X PHA>3 *\ a0=ۀ௻w$" ^Q*6@+R]_jK|%rS3dk!05v/m칒LX 仆Χ1 8rDAER?y2wA,nKrLZ43|DSႫp?S:nbrG&JYl}pNa,Jx4# 2IAH ;,=52>/(fzz+|Z!%% j)38<:9630*(P긽MU@E@AJV 7WKYM^adinv}{rciZSH;0b&p @Cd΄z4v\p#/NHmHpb_-Oo;7mݜ{Ӫw?ѸkӀj (u3JJX]\;VMbJW5z+x [G ,{A**,˻}RbL>9wg| +%=rLP*J?w*C# M寮 0ɩL'e T"1APY ]ZR>%riTKƿظD#o o&84=CJkUcD[8ytJY[XzYQֺ8)4(4-+ i WOɽ+;HT^gnt{rM[D5"_Ci x_y@lP(ZZXLW2T;JIJ^SZcmu~|?|zdC=  wZ5al:cB*[>KUn]>` elt|}oRd3#X!'u2q=?\5=\ڙR:5{ݗ"jz &/E_u