Áƒ*žE% @ì‘6=}+kOÂL9E¡;ÜaÕbÍì% None addLabel Register hasRightColor Nexgen105Core addButtontrim GetValue splitRegionV SetValueSystemdivideRegionH splitRegionH SetMaxLength addEditBoxFormat addCheckBoxConsoleCommandUWindow InternalTime skipRegion stringHash broadcastMsgnscLogaddContentPanelSetTextsetRPCIMessageNotify setContentsignalConfigUpdate fixStrLen addPanelsplitdivideRegionVPaletteCloseVBitsVClampUClampUBitsMipZeroVSizegetUSizebMasked boolHashAddTextconfigChangedEngineshowMsg MaxColor bInvisible playerEvent addListBoxSetNumericOnlyCreatedgetControllerset addNewLine addComponentMutatepanelIdentifiercreateWindowRootRegionAppend setDisabled AddMessage fixByteRangeplayerSelected showPopupgameInfoChanged setValuesfixStrCreateControl selectRegion TeamColor getPropertydecodeaddRightDefiniton notifyEventencodegetClientByNumCaptionlfillClear setNexgenHUDColors TeamGamePlusaddAccountType addRegionclientInitializedsignalGameInfoUpdate addProperty bindCommand wrapLength removePlayerpjupdatePlayerInfoaddPlayerToListRemove SetTextColor PostRendergetUserAccountTitlegetAccountTypeTitleAC PreBeginPlaySetTeampl getClient NextPageReplacePaint FirstPageaddHistoryMsg isSpectatorNSCUWindowSmallButton getPanelsetEncryptionParams Reconnect baseColorsrenderMessageBoxloadMutatorList CountShowngetCurrentRightsclientCommand saveSettingsloadAccountTypesaccountSelectedGetSelectedIndexgetUserAccountIndexaccountTypeSelectedupdatePreviewNexgenPanelContainer GetString sendMessageUser InitializeDrawTypingPrompt selectPanelbanPeriodSelectedLocalizedMessageBotpackisMuted GetTeamName GameEndedaddPropertyLabelgetSelectedAccountNumcalcDynamicChecksum renderAlertupdateChecksum ChangeNameannouncePlayerAttrChangeCriticalEventreconnectTextmoveSelectedPlayerToplayerTeamChangedplayerNameChanged daysInMonth playerLeft playerJoinedsetPlayerTeam SavedMessage MD5StringSetFontKeyDownmakeKeyGetLookAndFeelTexture checkLoginloadBootControlSettingsMutatorBroadcastMessage PreventDeath!MutatorBroadcastLocalizedMessageMutatorTeamMessageSetSelectedIndex addPlayergetBanPeriodType updateBan checkConfigEventgetClientByID gameStarted TeamNameloadGameTypeListMutatorTakeDamageloadUserAccountscalcStaticChecksum formatGUID RemoteRoletoggleMatchMode addChatMsgTeamSaygetPlayerColorsetNexgenMessageHUD BalanceTeams ScoreKillloadMatchSettingscanEditAccount PlayerPawnremoveMessageColorTag ModifyPlayer isBlocked ModifyLoginupdateBootControlseparatePlayers sendPassword banPlayergetBoolloadAccountTypeInfoEndGameCorrodedMessage GetPlayer loadBanListloadIgnoredWeaponListSpectatorTextloadHUDReplacementList readDateselectionHasAccountserializeDatereceivePasswordNexgenControllerNexgenMainFrame BlockPlayercreatePanelRootRegionweaponSelectedhudReplacementSelectedUWindowCheckboxresetEncryptionConfig banSelectedreconnectPlayer isLeapYear getItemByID pauseGame isExpiredBan ServerTravel removeBanmoveAccountTypeaddDynamicTextArea addListComboaddUserAccountgetMutatorIndex ListClass daysInYearunblockPlayer execCommandmodifyServerStatesetupControlPanelbanPeriodTypeSelectedgetCurrentBanPeriodSetup getIPList getIDListgetBackgroundColorAddItemcanUseAccountTypeSortgetDisplayTextTimerupdateLoginOption showPanelgetDisplayFont setGameInfoFellinitializeConsoleWindow addImageBox setFlagTexBurnedgetWrapPositionswitchPreferredPlayergetSmallestTeamgetLargestTeam PasswordText canGetSlotSaydisconnectClient renderPanelgetPanelWidthremoveClientHandlergetLocalizedDateStrrenderMessagecheckPreventDamage startGamegetBanPeriodDescriptionsendMsggetPlayerJoinEventArgshandleMsgCommandaddEditControlclientCreated fixIntRange windowWidth windowHeightcheckMatchSettingsdoCompatibilityCheck forceEndGamemodifyLoginReject setLevelInfo checkBanListgameSpeedChangedplayerRespawnedupdateCountryCodesshowGameReadyToLaunchMessages virtualTimeraddColorizedMessagegetPlayerNameIndices parseBooladdRaisedButtoncheckAccountSystemSettingsaddMsggetCompactDateStrcheckBootControlSettingsrenderTypingPromt parseDategetServerStategetDelocalizedDateStr NetPriority clearDatagetClientStatecheckExtraServerSettingssetPlayerData SpectatorsetupTeamButtonsignoreWeaponFire rebootServer firePressed clientLogininitPreferredSwitcherscheckGlobalServerSettings hasUniqueKeysetAccountInfogiveJoinOverrideCodeisValidJoinOverrideCode newClientregisterPluginsetReplacementHUDClass initGameInfocheckEncryption applyConfig getFlagTexselectRandomBootMapcopyTo hasRightscompareSwitchDesirabilityinitializePopupWindow ShowWindowinitializeControlPanelbShowCountryFlag SetRangeexecPauseGame DrawItemupdateMapPrefixTickInsertexecStartGamegetLoginParametersaddController doNexgenBootupdatePlayerList FocusWindow isFirstGame CloseUWindowexecBalanceTeamscheckConfigUpdate SetEditableNexgenparseCommandStrexecJoinAsPlayer respawnedexecJoinAsSpecgetEncryptionParamscheckResidentConfigexecSwitchTeamLaunchUWindow computeDategetDisplayColorsetActiveMutatorListLoginsetServerSettingssendPM SuicidedDoubleClickItem getGameIndex receivePMRestartPlayer getAllRightsupdateAccountType loadBanInfoupdatePlayerTitlesdeleteAccountTypeupdateBanPeriodsdeleteAccountcleanExpiredBansupdateAccount addAccountgetPlayerByID getBanIndexsaveHUDReplacementClasstoggleTeamSwitchsendPlayerToURLinstalltoggleGlobalTeamSwitchtoggleGlobalTeamBalance addSubPaneltoggleLockedTeamspostInitialize deleteBanisValidIPAddressisValidClientIDaddBandelHUDReplacementClass loadKeyBindssaveIgnoredWeapon ItemHeightdelIgnoredWeaponupdateMatchSettings visitServertogglePlayerMutesetPlayerNamecontainsCommand kickPlayer bAlwaysTickPawnremoveKeybind addKeybind SetFrameshowAdminMessagereceiveMessageClientInitializetoggleGlobalMuteClientPlaySoundUMenutoggleGlobalNameChange RestartGamesetServerSettingsExt1setServerSettingsExt2getMessageColor isBanned AdminLoginNexgenControllerPawncountryFlagsPkginternalVersionversionTrigger DecToHexIIHHGGFF ROTATE_LEFTMD5Move MD5Final MD5Update MD5TransformMD5InitMD5_CTXpNum msgLabelTextsendButtonTextcloseButtonText slotMessagereplyButtonText messageText senderText noReasonText periodText reasonText loginText buttonSpace buttonHeightbuttonPanelHeightbuttonPanelBorderSizeeditControlLabelVOffseteditControlHeightautoCloseControlPanelhasCloseButtonrightNotDefined rightDenied rightGrantedPostBeginPlaySpawnNotificationGi PackageName ChangeTeamFbAlwaysRelevantbNetTemporary GetItemName GetIntOptionClientGameEndedPBT_Transparent PBT_Beveled PBT_Default LayoutRegionitemIDselectedAccountColor accountColor selectColorSetProgressTimeRegisterDamageMutatorWAV getFloatgetIntgetByteSetProgressMessageAttributeEntrySShot ActorClass GetLocalURLaddNewItemTxtreplacementHUDClassTxtoriginalHUDClassTxthudReplaceClassesTxtignoreAltFireTxtignorePrimaryFireTxtweaponClassTxtignoredWeaponsTxtautoDisableMatchTimeTxtteamKillPushProtectTxtteamKillDmgProtectTxtspawnProtectTimeTxtmaxIdleTimeCPTxtmaxIdleTimeTxtautoReconnectTimeTxtgameStartDelayTxtgameWaitTimeTxtdefaultAllowNameChangeTxtdefaultAllowTeamBalanceTxtdefaultAllowTeamSwitchTxtLogPrivateMessagesTxtlogChatMessagesTxtlogMessagesTxt logEventsTxtenableNexgenMessageHUDTxtannounceTeamKillsTxtUMenuRaisedButtonautoDelExpiredBansTxtautoUpdateBansTxtshowAdminMessageTxtallowNameChangeTxt ButtonWidth muteAllTxt banPlayerTxtkickPlayerTxtsetPlayerNameTxtmuteToggleTxtallPlayersTxtsendPasswordTxt stopMatchTxtstartMatchTxtmatchDoSeparateTxtmatchAutoTagSeparateTxtmatchSeparateByTagTxtmatchAutoPauseTxtmatchAutoLockTeamsTxtDeathMatchPlusmatchBootControlTxtmatchMuteSpecsTxt StartMatchmatchSpecNoPassTxtmatchCurrGameNumTxtGetStaticHugeFontGetStaticSmallestFontmatchNumOfGamesTxtmatchSettingsTxtidealPlayerCountTxt authorTxt titleTxtfileTxt levelTxt mutatorsTxtnameChangeAllowedTxtteamsLockedTxtteamBalanceEnabledTxtteamSwitchEnabledTxt gameSpeedTxtteamScoreLimitTxtscoreLimitTxt timeLimitTxt recordSetTxtFPHTxtbestPlayersTxttotalFlagsTxttotalDeathsTxttotalFragsTxttotalGamesTxtstatisticsTxt serverIDTxt BaseColormsgOfTheDayTxtcontactAddrTxtadministratorTxt rebootTxtpreSwitchCommandsTxt LabelHeightextraCmdLineOptTxt mapPrefixTxt gameTypeTxtbootCmdLineTxtexclMutatorsTxtinclMutatorsTxtrestartOnLastGameTxtenableBootCtrlTxt removeTxt delBanTxt updateBanTxt addBanTxt clientIDsTxtMD5HashNexgenAccountUpdatedDialogNexgenPopupDialog NexgenActorNexgenAdminLoginDialogNexgenBannedDialog NexgenClientNexgenClientControllerNexgenClientCoreNexgenClientLoginHandlerNexgenCommandHandler NexgenConfigNexgenConfigCheckerNexgenConfigExtNexgenConfigSysNexgenContentPanelipAddressesTxtNexgenCorePlugin NexgenPluginNexgenCPKeyBind NexgenPanelNexgenDummyComponentNexgenEditBoxNexgenEditControlNexgenGameInfo NexgenHUD NexgenHUDxASNexgenHUDxCTF NexgenHUDxDMNexgenHUDxDOMNexgenHUDxTDMNexgenIdleKickedDialogNexgenIDUsedDialogNexgenImageControlNexgenJustBannedDialog NexgenLangbanUntilDateTxtNexgenMainPanel banDaysTxtNexgenMainPanelBarNexgenNoPlayRightDialogNexgenPasswordDialogNexgenPlayerACListBoxNexgenPlayerListBoxNexgenPlayerACListItemNexgenPlayerListNexgenPlayerDataNexgenPopupFrameNexgenPrivateMsgDialogNexgenRCPAboutNexgenRCPAccountTypesNexgenRCPBanControlNexgenRCPBootControlNexgenRCPClientConfigNexgenRCPGameInfoNexgenRCPHomeNexgenRCPMatchControlNexgenRCPMatchSetNexgenRCPModerateNexgenRCPPrivateMsgNexgenRCPServerInfoNexgenRCPServerSettingsNexgenRCPServerSettings2NexgenRCPServerSettings3NexgenRCPUserAccountsNexgenResidentConfigDialogNexgenScrollPanelContainerNexgenServerFullDialogNexgenSimpleListBoxNexgenSimpleListItemNexgenSimplePlayerListBoxNexgenTeamBalancer NexgenUtilbanMatchesTxtbanForeverTxt banPeriodTxt banReasonTxtplayerNameTxt lockTeamsTxtallowTeamBalanceTxtallowTeamSwitchTxtrestartGameTxt endGameTxt pauseGameTxtdisableTeamSwitchTxtreconnectAsSpecTxtreconnectAsPlayerTxt sendToURLTxtswitchToTeamTxtcustomAccountTxt deleteTxtaddTxt updateTxt offlineTxt onlineTxt userTitleTxtaccountTypeTxt userNameTxt moveDownTxt moveUpTxtdelAccountTypeTxtaddAccountTypeTxt passwordTxtaccountTitleTxtaccountNameTxtreceivedMsgTxt sendMsgTxt unblockMsg blockMsg historyTxtsendWindowedPMTxtsendNormalPMTxt messageTxtblockAllPMsTxtblockToggleTxtblockedListTxtplayerListTxtautoSSMatchTxtautoSSNormalGameTxtmiscSettingsTxt pmSoundTxtshowPlayerLocationTxtmsgFlashEffectTxtenableMsgHUDTxtUISettingsTxtpauseGameBindTxtopenCPBindTxtopenMapVoteBindTxtsuicideBindTxtswitchGoldBindTxtswitchGreenBindTxtswitchBlueBindTxtswitchRedBindTxtbalanceBindTxt keyBindsTxtsaveTxt resetTxt advertiseTxt specSlotsTxtadminSlotsTxt vipSlotsTxtplayerSlotsTxtadminPasswordTxtserverPasswordTxtadminEmailTxt adminNameTxt MOTDLineTxtshortServerNameTxtserverNameTxt loginTxt startTxt mapVoteTxtexitTxtdisconnectTxt reconnectTxt spectateTxtplayTxt goldTeamTxt greenTeamTxt blueTeamTxt redTeamTxtteamBalanceTxtrightNotDefinedTxtrightsOverviewTxt welcomeTxt aboutTabTxtpluginsTabTxt bootTabTxtaccountTypesTabTxtaccountsTabTxtbanControlTabTxt infoTabTxt serverTabTxtmatchSetupTabTxtmatchControlTabTxtmoderatorTabTxtplayersTabTxt gameTabTxtprivateMessageTabTxtsettingsTabTxt homeTabTxt clientTabTxt matchState deadStateprotectedState mutedState idleState loginState pausedState endedStateofflineStateRCN offlineState onlineStatestartingState readyState waitingState idleAlert rebootAlertreconnectingAlertautoReconnectAlertadminLoginMsginvalidPasswordMsgadminRebootServerMsgadminDisableNameChangeMsgadminEnableNameChangeMsgadminUnmuteAllMsgadminMuteAllMsgadminBanPlayerMsgadminKickPlayerMsgadminSetNameMsgadminUnmutePlayerMsgadminMutePlayerMsgadminDisableMatchModeMsgadminEnableMatchModeMsgadminDeleteBanMsgadminAddBanMsgadminUnlockTeamsMsgadminLockTeamsMsgadminEnableTeamBalanceMsgadminDisableTeamBalanceMsgadminEnableTeamSwitchMsgadminDisableTeamSwitchMsgUWindowEditControladminSendToURLMsgadminReconnectAsSpecMsgadminReconnectAsPlayerMsgadminPlayerTeamSwitchEnableMsg adminPlayerTeamSwitchDisableMsgadminStopGameMsgadminRestartGameMsgAddPageadminResumeGameMsgadminPauseGameMsgadminTeamSwitchMsgCreateRootWindowreceivedPWMsgreceivedPMMsgsettingsSavedMsg SetHistory welcomeMsgSetSelectedItemSetEditTextColoradminLaunchGameMsglaunchGameMsg HideConsoleserverAdminRightDescaccountMngrRightDescNotifyBeforeLevelChangebanOpRightDescmoderatorRightDescmatchSetRightDescmatchAdminRightDesccanBeIdleRightDescneedsNoPWRightDescadminSlotAccessRightDescvipSlotAccessRightDescallowedToPlayRightDescprivateMessageLogTagteamSayMessageLogTagchatMessageLogTagmessageLogTag eventLogTagbUseExternalConfigcontrollerSystemLogTagteamKillAttemptMsgaccountTypeNameStrmutedReminderMsgdeathPreventedMsg specTitlerootAdminTitle forcedEndMsg launchMsg balanceMsgteamsAlreadyBalancedMsgteamBalanceDisabledMsgnotATeamGameMsgteamBalanceFailedMsgnoMorePlayerSlotsMsgnoPlayingRightsMsgnoMoreSpecSlotsMsgnoSpecsAllowedMsg sameTeamMsginvalidTeamMsgGotoTabplayerTeamSwitchDisabledMsgteamSwitchDisabledMsgspecTeamSwitchMsgteamSwitchFailedMsginternalErrorMsgcommandFailedMsginvalidCommandMsgnameChangeDisablednameChangeFailMsgteamsLockedMsgteamSwitchFailMsgplayerLeaveLogMsgplayerNameChangeMsgplayerLeaveMsgplayerJoinMsgloginRejectedMsgloginAcceptedMsgnoPlayRightMsgserverCapacityMsginvalidPassMsg bannedMsgduplicateIDMsgattrPasswordMsgattrClientIDMsgattrClientIPMsg duplicateidloginRequestMsgbannednoHUDReplacementClassMsg invalidpass regFailedMsg serverfullinitFailedMsg noplayrightloadingPluginMsgGetDesiredDimensionsComparenexgenActiveMsgbootLevelSwitchMsg BeforePaintexecCommandMsgnexgenBootFailMsgnexgenBootMsgattrServerIDMsginvalidConfigMsgautoInstallMsgcompatibilityModeMsgnoDedicatedServerMsgstartingControllerMsgdateFormatStrClearProgressMessages ipToCountrypluginVersion pluginAuthor pluginNamerfillmodifyClientStatectrlIDaddPluginConfigPanel csnormalcsdeadcsmuted csprotectedcsidlecsloginRegisterMessageMutator ssnormalssmatch sspausedssendedRegisterHUDMutator ssstaringssready sswaiting ssofflineH PanelInfo MessageInfo blankColor signalEvent BorderSizeBotbPreventDamage bResetPlayerMsgTypeHistory TypedStr logMsgTypeAmountstr1str2str3str4 Checksum SelectedItem PlayerZone TalkTextureSender bIsCommand bChecked bAllowSwitch PlayerIDGameEndedCommentslogType senderPRIbIsSpecMessagebIsNexgenCommandbIsLegacyCommand MOTDLine3 configType AdminName infoTypeRemainingTimeattributeNameNewItemPName bAllowChange argumentsGFXgrad32grad64 playerIcon playerIcon2 serverIcon serverIcon2 offlineIcon offlineIcon2 waitIcon waitIcon2 specIcon specIcon2 shieldIcon shieldIcon2 idleIcon idleIcon2 mutedIcon mutedIcon2 matchIcon matchIcon2lastSetupTime baseFontbFire baseHUDColor instigatedBybaseFontHeight bAltFirecolActiontextCol solidIcon chatMessages chatMsgCount msgCountfaceImg msgBoxWidthmsgBoxLineHeight msgBoxHeightminPanelWidthbFlashMessages lastResX lastResY lastTeamlastLevelTimeSecondsbShowPlayerLocation iconSizechatMessageLifeTimemessageLifeTimemessageBlinkTimemessageBlinkRatepanelBlinkRateblinkColorDampconnectionAlertDelay secPerMinutematchInfoSwitchTime hDefBarSizealertAnimTimealertAnimDistalertBorderSize newLineTokenC_REDC_BLUEC_GREEN C_YELLOWC_PINKC_CYANC_METAL C_ORANGE SS_OfflineClipX SS_WaitingWidth SS_ReadypDat SS_Starting ZoneName SS_EndedbBanned SS_Paused banIndex SS_MatchNextHUDMutator SS_Normal currClient CS_Login popupArgsCS_IdlepopupWindowClass CS_Protected rejectType CS_MutedallowSpecReconnectCS_Dead bRejected CS_NormalNextMessageMutatorbadConnectionTimetimeRemainingCriticalString LocationName HUDMutatorcxcy animIndex hBarSize textWidth maxTextWidth textHeight lineCountpri1pri2 playerColor messageColorbIsSpecSayMsgspecPRITeamNumfirstPlayerNamesecondPlayerName firstIndex secondIndexfirstPlayerColorsecondPlayerColor msgPart1 msgPart2 msgPart3 msgColor tempIndextempPlayerColorSourceindex1index2tmpPRI nameIndex tmpIndexStrcol1text1col2text2col3text3col4text4col5text5KeyNum bUpdateBaseClipY serverState clientStateYPos msgOffsetXPos msgIndex msgWidth msgHeight blinkFactorblinkIntensity bIsSpecialpInf stateInfo stateTypebComplexString protectTimeVictimseparatorWidthNextDamageMutator iconHeightText colorNum timer_tick nextClientgcscloginHandlerChecksum bNetLogin bNetWaitloginComplete ipAddress bHasAccount loginOptionsloginTimerFreq waitTimeOutcountry bSpectator bFirePressedbadConnectionSincebBadConnectionDetectedbMutedteamSwitchOverrideTimenameChangeOverrideTimelastRespawnTimelastSwitchTimespawnProtectionTimeXtkDmgProtectionTimeXtkDmgProtectionTimetkPushProtectionTimeXtkPushProtectionTimebScreenShotTakenbReconnecting originalHUDnewHUD gameEndTimebEncryptionParamsSet idleTime idleTimeCPidleTimeRemainingbUsingNexgenMessageHUD clientCtrlnscHUD popupWindow configUpdate nextChecksumnextUpdateCountgameInfoUpdateSSTR_ClientKeySSTR_ClientIDSSTR_ServerPasswordSSTR_OverrideClassSSTR_UseNexgenHUDSSTR_FlashMessagesSSTR_ShowPlayerLocationSSTR_PlayPMSoundSSTR_AutoSSNormalGameSSTR_AutoSSMatchSSTR_RunCount R_MayPlay R_VIPAccessR_AdminAccess R_NeedsNoPW R_CanBeIdle R_MatchAdmin R_ModerateR_BanOperatorR_AccountManagerR_ServerAdmin R_MatchSetRCN_ReconnectOnlyRCN_ReconnectAsPlayerRCN_ReconnectAsSpecreconnectCommanddisconnectCommand exitCommand startCommandspectatorClassPE_PlayerJoinedPortalPE_PlayerLeft Palette1PE_AttributeChanged PA_ClientID PA_IPAddressPA_Name PA_TitlePA_Team PA_CountrymaxIdleRadiusidleCountDelayidleTimeWarningmaxOverrideTimecancelSpawnProtectDelaynormalModeTimerFreqautoScreenShotDelayautoSSMinGameTimewelcomeMsgCountlastIdleTimeRemainingValuebIgnore bIgnoreFirebIgnoreAltFirecurrWeaponClassexcludedModesKey NextMutator runCount BaseMutatorbEnable Palette3 cleanMsg MapPrefixAdminPasswordpos rightsStr bHasRights bGameEndeddistwWidthwHeightwTopwLeft PawnListNetMode popupClass ScreenshotSummary optionNamePauserIdealPlayerCountcontrollerClasscreatorMinute controllerIDHour updateNumDayMonthcurrentChecksumcurrentChecksumSetYear TimeSecondsOptionalObject RelatedPRI_1Padding panelClass containerbBeep bInvalidbCanModifyHUDStatePanelOwnerbValid PasswordblockedPlayers bBlockAllopenURLCommand restartURL rebootDelay nextPawn bBlockedReason TimerRateshortServerNameMOTD1MOTD2MOTD3MOTD4 specSlotsbEnableUplinkmyHUD bWindowed targetCtrl senderTitleTarget senderIDbForcedpmPanel InstigatoraccountTypeNumHUDType newTitleBaseuserAccountsChangedRegion oldPosition newPositionGameNextPRIbAlreadyHasAccountContextbGameIsPaused StartTimeFramebFoundbSpecbWaitingPlayerfURL bIsSpectatorHasFlagOldNamebValidPassword bRootAdmin PlayerName deletedBan PRIArrayipListidList MOTDLine4 bLoadLastMap mutators extraOptions indexStr MOTDLine2 teamTags MOTDLine1 targetPlayer pwToSendpwReceivedMsg AdminEmailCommand teamTag0 teamTag1 teamTag2 teamTag3bBadConnectionAlert bTeamGame GameClassSecondsItembanPeriodDescML OldLocation bSelectedaPawn MutateStringbTyping currIndexDamageignorePrimaryFireignoreAltFireweaponConfigStr HitLocationIndexoriginalClassreplacementConfigStropenMapVoteCommand MomentumbInvalidCommand failMessage actualDamage bAllowedN numSpecs SpawnClassKillerP RelatedPRI_2MsgURL bNetOwner NewValue EventTypeOldTeam bShowScoresOptionmaxLenResultintVar lowerBound upperBound TimeStampbyteVar overrideCodeclientHandler ReceiverOther DeltaTime bSaveDefaultplugin PA_MutedPA_NoTeamSwitchMapName bCancelledipInfoC_Black oldClient tokenIndexhudReplaceClassName hudClassNameC_WhitekUWindowLookAndFeelrandomMapIndex mapCountbFirst shortMapNamebootURL randomMapsecondsPerMinuteRT_NoPlayRightRT_ServerFullRT_InvalidPassword RT_BannedRT_DuplicateIDLT_PrivateMsg LT_TeamSayLT_Say LT_Message LT_Event LT_SystemcorrodeDamageTypeburnDamageTypefallDamageTypesuicideDamageTyperebootCommand CMD_Pause CMD_OpenVote CMD_OpenCMD_Disconnect CMD_ExitCMD_StartGame CMD_Spectate CMD_PlayCMD_BalanceTeamsCMD_SwitchTeam CMD_PrefixserverPauserName timerFreqlogTagvirtualTimerCounterbUTPureEnabledlastTimeDilationlastPlayerLeftTimejoinOverrideCodeOptionmaxOverrideCodeLeaseTimeoverrideCodeLeaseTimesjoinOverrideCodesnextPlayerNum loginHandlerplayerDataList teamBalancerpluginslnggInf cmdHandler clientListsConf bSpecialModeIT_GlobalRights GS_Ended GS_Playing GS_Starting GS_Ready GS_WaitingrebootCountDownbNoNameChange bMuteAllmatchAdminCount gameStatebNoTeamBalancebNoTeamSwitch bTeamsLockedlbcinbckcsbIgnoreLDoubleClick TextColor currClassargs banPeriodStrWindowConsole customTitle customRights accountNameUWindowVScrollbar clientIDUWindowEditBox accountType accountNumUWindowPageWindowUWindowPageControl entryNumUWindowPageControlPage bBanDeletedcurrBanUWindowDynamicTextAreabanPeriodArgsbanPeriodType bExpiredUWindowDialogClientWindowidCountipCountcurrIDcurrIPUWindowControlFrame bIDMatch bIPMatch bNameMatch playerIP UWindowListcCheck bConfigOkUWindowListControlrightIDUWindowFramedWindowrights typeNameUWindowComboControlUWindowClientWindow bValidCharcbUWindowRootWindowmutatorInfoStrUWindowDialogControl gameInfoStrUWindowButtongameTypeClassactiveGameTypeClass remainingUWindowWindow bAlwaysOnTop UWindowBasehashbIgnoreMDoubleClickbIgnoreRDoubleClickUWindowListBox IW_AltFireIW_FirebActiveMutatorIndicesSetactiveMutatorIndicesactiveGameType mutatorInfo gameTypeInfogameInfoPanelClassserverInfoPanelClass BP_UntilDate BP_Matches BP_ForevermaxBanClientIDsmaxBanIPAddresses banPeriod banReason bannedIDs bannedIPs bannedName rightsDefpaCustomTitlepaCustomRightspaAccountType paPlayerName paPlayerID atPasswordatTitle atRights atTypeNameHUDReplacementClassreplacementClassuseNexgenMessageHUDtagsToSeparatematchAutoSeparatematchAutoPausematchAutoLockTeamsenableMatchBootControlmuteSpectatorsDuringMatchspectatorsNeedPasswordserverPassword currentMatchmatchesToPlaymatchModeActivatedlastServerURL bootCommands bootOptionsbootMutatorIndices bootMutatorsbootMapPrefix bootGameTyperestartOnLastGameenableBootControlspawnProtectExcludeWeaponsautoDisableMatchTimeallowNameChangeallowTeamBalanceallowTeamSwitchlogPrivateMessageslogChatMessageslogSystemMessages logEventsbroadcastTeamKillAttemptsteamKillPushProtectionTimeteamKillDamageProtectionTimespawnProtectionTimemaxIdleTimeCP maxIdleTimeautoReconnectTime waitTimeremoveExpiredBansautoUpdateBans MOTDLinespectatorSlots adminSlots vipSlots playerSlots enableUplinkglobalAdminPasswordglobalServerPasswordconfigCodeSchemeconfigEncryptionKey codeSchemeencryptionKey serverKey bInstalledCT_HUDReplacementListCT_ExclWeaponListCT_ExtraServerSettingsCT_MatchSettingsCT_BootControl CT_BanListCT_UserAccountsCT_AccountTypesCT_GlobalServerSettingsdynamicChecksumSaltstaticChecksumdynamicChecksum updateCount separatorcontrolserverControllerUWindowListBoxItemGetPlayerOwnerSetSize BringToFront serverIDSetAcceptsFocusGeneralConfig ServerConfig CreateWindowDrawClippedTexture NexgenCCRootWin TeamButton LeftOverDrawStretchedTexturebBottom ClipText LabelText DrawUpBevel GameTypeList Smallest Static_A00 LadrStatic ControllerTGRIMyFontsbPlayersBalanceTeamsWinEndTime bNetReadybRequireReadyBestRecordDate BestFPHs BestPlayers TotalFlags TotalDeaths TotalFrags TotalGames NumTeamsChallengeDominationHUD FontInfoChallengeCTFHUDTournamentGameReplicationInfo CountDown AssaultHUD GetParentChallengeTeamHUD ChallengeHUD MessagesWindowIsVisible WindowTitle bTopCentric PlayerNumbWindowVisible MutatorName FirstMapRestartButton MutatorList StartButtonPages bInitialized WeaponClass LabelWidth bPolling OwnerWindowUMenuFramedWindowUMenuLabelControlUMenuMapListFrameCW CHSpectatorTextYTransA3 PanelHeight PanelWidthNewName PlayerCountTempPlayerNameMinutesGRINewTeamGoalTeamScore TimeLimit FragLimitPickups Palette7 Palette11 Palette39 Palette19 Palette23 Palette27Dec Palette31 Palette35 Palette43 Palette45SizeNextSiblingWindow PlayerListdateStr MaxTeamsGetPlayerNetworkAddressFirstChildWindow attributesPanelbHighDetailMode bDisabledAlign ParentWindow maxTeamCount teamSizeminPlayersPerTeam targetTeampreferredSwitchersprefSwitchTeamOffsetsswitchedCountMaxSpectatorslargest bCanEditbStopCountDown nextOffset tempClientbSorted TotalPlayers preferred TimeDilationclient1client2 GameSpeed lastChecksavedMessagessavedMsgCountbHandleByParentmsgStrcanvcons LeftMessage noCountry GameTypeEnteredMessageoffsetXflagTexbackgroundColor MaxPlayers MutatorClass Lifetime ServerName ShortNameIcon SmallFontSetPos MainWindow CloseButtonHealth PageControl Location mainPaneldialog dialogClassbDoneregions regionCount currRegionEPanelBackType bSuccessPartImage panelBGType parentCPminRegionSize AL_CenterAL_LeftAL_Top AL_Right AL_BottomdefaultComponentDistdefaultButtonHeightdefaultLabelHeightdefaultEditBoxHeightdefaultCheckBoxHeightdefaultRaisedButtonHeightdefaultlistComboHeight bIsPlayer LocalMessageGetNextIntDesc DefaultValueTypeStyle bPercent splitPointregion1heightregion2height region1top region2top GameName region1width region2width region1left region2leftTeamtotalRegionHeight regionHeight carryOver currHeightcurrTop DrawColortotalRegionWidth regionWidth currWidth currLeftData NewValue2 LookAndFeel bByParent DamageType checkBoxEditBoxTextColor raisedButton WndClass imageBoxtempbgType contentPanelA listComboDX listBoxClasslistBoxC PropValue PropName playerItemZY bindButtonW selectedBindbindSeparatorgetKeyNameCommandgetKeyBindCommandsetKeyBindCommandLabel keyActionPage commandsKeyName actionStrjVAlign panelName ScriptText identifier newPanel parentPanel subPanelsS barHeight scrollBarpanels numPanelsclientAreaDesiredHeightnextPanelOffsetpanelDistanceborderDistancedefaultPanelHeightbNeedScrollBardesiredPanelHeightlogonumAccountTypesrpciaccountTypeListaddAccountTypeButtondeleteAccountTypeButton moveUpButtonmoveDownButtonaccountNameInpaccountTitleInpaccountPasswordInprightEnableInp resetButton saveButton rightDefBRaccountRightsHAlign ReturnValueLenbanList addBanButtonupdateBanButtondeleteBanButtonplayerNameInp banReasonInp banPeriodInpmatchCountInpdateInp addIPButton delIPButton addIDButton delIDButton ipAddressInp clientIDInpDynamicLoadObjectnumBansbDownFontsbanArgsMutatorWeapon LevelInfo GameInfoselectedIndexinclMutatorListexclMutatorList rebootButton mapPrefixInpextraOptionsInp commandsInpenableBootControlInprestartOnLastGameInp previewBox ZoneInfooldItem mutatorIndexHUDbootCmdPlayerReplicationInfoenableNexgenHUDInpuseMsgFlashEffectInpshowPlayerLocationInpplayPMSoundInpautoSSNormalGameInpautoSSMatchInptimeLimitLabelfragLimitLabelteamScoreLimitLabelgameSpeedLabelenableTeamSwitchInpenableTeamBalanceInpteamsLockedInpallowNameChangeInp fileLabel titleLabel authorLabel playersLabelInfoReplicationInfo levelFileteamBalanceButtonplaySpecButtonreconnectButtondisconnectButton exitButtonmapVoteButton loginButtonserverTitleLabelbRightbMoving ClientHeight rightPanelGameReplicationInfo teamButtons pauseButton endButtonsendToURLButtonreconnectAsPlayerButtonreconnectAsSpecButtondisableTeamSwitchButtonurlInpallowTeamSwitchInpallowTeamBalanceInp lockTeamsInpdefaultButtonColor LevelSummary locationidstartStopButtontagInp numGamesInp currGameInp passwordInpspecNeedPWInp muteSpecsInp autoLockInp autoPauseInpautoSeperateInpseparateButtonsendPasswordButton SpawnNotify ViewportmuteToggleButtonsetNameButton kickButton banButtonshowMsgButtonnumMatchesInp numDaysInp messageInpbanForeverInpbanMatchesInp banDaysInp muteAllInpCanvasblockedPlayerListblockToggleButtonsendNormalButtonsendWindowedButtonmsgInp blockAllInpFont ServerActorsselectedPlayerLevelActorserverNameInpshortServerNameInpMOTDInp adminNameInpadminEmailInpserverPasswordInpadminPasswordInpplayerSlotsInp vipSlotsInpadminSlotsInp specSlotsInp doUplinkInpautoUpdateBansInpautoDelExpiredBansInpannounceTeamKillsInpenableNexgenMessageHUDInp logEventsInplogMessagesInplogChatMessagesInpLogPrivateMessagesInpdefaultAllowTeamSwitchInpdefaultAllowTeamBalanceInpdefaultAllowNameChangeInpgameWaitTimeInpgameStartDelayInpautoReconnectTimeInpmaxIdleTimeInpmaxIdleTimeCPInpspawnProtectTimeInpteamKillDmgProtectInpteamKillPushProtectInpautoDisableMatchTimeInpignoredWeaponListhudReplacementListweapSaveButtonweapRemButtonhudReplSaveButtonhudReplRemButtonignorePrimaryFireInpignoreAltFireInpweaponClassInporiginalHUDClassInpreplacementHUDClassInpBitmapPlayerClientWindow accountList updateButton deleteButtoninvalidAccountTypecustomAccountTypedefaultAccountTypeList accountItemCountHeightAuthorbItemSelected Description rightStrbClientIsServerAdminOptionsSound ClientWidthTextureminWrapRetain wrapChars ClientAreanextButtonPos EditControlTailHead TextAreabUnique TextAligncw newButtonTitle fontTypewrapLentextStrlineStr newLinePoswrapStrConsole maxCharsCmdRoleStaticClasspasswordInputbSelectOnFocus reasonLabel periodLabel bAllSelected NotifyOwner CaretOffsetperiodspectatorButton allowSpecs msgLabel senderLabel replyButtonOutputCh SelectedfirstLineWrapLenlineNum lineEntry slotLabelWrapPos sendButtonRoot bShowConsolebLeaveOnscreen borderDistseparatorDistcomponentDisteditCtrlHeight sayCommandbQuickKeyEnablepTitle pIPAddress pClientID pCountrypTeam bFlagTexSet specTeamParentPackageInputConst displayTextWinTopbufferSwitchdigestHex WinWidthConsoleWindowBufblock WinHeight inputLenpartlentmpbuf NotifyWindowbitspadLenstrbitsbAcceptsFocussrc srcindex bufindexButton bCanDragEditBoxValue2 EditBoxWidthItemsbBold clientKey MaxLengthbBlink WhiteTexture keyChars keyFormat dateFormatnexgenCommand assignment escapeToken TextBufferoldStrnewStr subStrIndexstrLeft formattedStrObjectEnum minLengthfillStrPlayerLocation FunctioncmdStrcmdName argCountcclc recStartrecEndbRecStrStatepropStrformattedValueVectorpropDefaultValuesavedStruct daysToCountdaysRemainingName UnrealShare StrPropertyStructPropertycurrentSectionsectioncharClassProperty NamePropertyObjectPropertyFloatProperty BoolProperty rawGUIDStr IntProperty ByteProperty bStretchÈ@?m9g »p1š:Ë:$–:Ë:$ÿÿÿÿÿÿÿÿÿÿb,£¤ÈU[¥Ú+¾÷²s9·Çâ¥[qš¤+¾÷²s9·Çâ¥+¾÷²s9·Çâ¥u¦9>NõÔBQ÷Ö‰Ž”œ©Ù«Ž> £YŠ M± ZŽ> £YŠ M± Z‰Ž”œ©Ù«‰Ž”¼}"›#‰Ž”¼}"›#Q÷ÖŽ> £Y‰Ž”œ©Ù«‰Ž”¼}"›#Œ„£’ÈZ«Œ„£Q÷Ö+¾÷²s9·Çâ¥[qš¤Œ„£’ÈZ«’ÈZ«’ÈZ«+¾÷²s9·Çâ¥[qš¤Œ„£+¾÷²s9·Çâ¥[qš¤[qš¤[qš¤[qš¤[qš¤Ž> £YŽ> £YŽ> £YŽ> £YŽ> £Y+¾÷²s+¾÷²s+¾÷²s²/Tã2²/Tã2+¾÷²s9·Çâ¥E6‚E6‚E6‚E6‚²/Tã2²/Tã2²/Tã2²/Tã2Ão û”.ü'l”.ü'l”.ü'lŠ+¾÷²sŠ+¾÷²s²/Tã2Ão û²/Tã2Ão û²/Tã2Ão û+¾÷²s+¾÷²s9·Çâ¥[qš¤ŠE6‚ŠE6‚²/Tã2Ão û²/Tã2Ão û²/Tã2Ão ûE6‚E6‚E6‚E6‚E6‚Ž> £YÂR@G¢²/Tã2²/Tã2²/Tã2²/Tã2Š M± ZŒ„£Q÷Öu¦9Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Š M± ZŽ> £YÂR@G¢Ž> £YÂR@G¢ÔIò&/Ž> £YŽ> £YÜ5Iœ´Ž> £YÜ5Iœ´Q÷ÖŽ> £YŒ„£Ü5Iœ´Ü5Iœ´Ž> £YŽ> £Y´M ¡Œ„£[qš¤´M ¡´M ¡´M ¡u¦9Ž> £Y{#U¿Ž> £YŽ> £YQ÷ÖŠud2½ð’ÈZ«’ÈZ«Ž> £YŽ> £YŽ> £YŽ> £YÑX©'Œ„£Œ„£Š M± ZŒ„£Š M± ZŒ„£Ž> £YŒ„£Ž> £Y‰Ž”‰Ž”Ž> £YŽ> £YŽ> £YŽ> £YŒ„£Œ„£’ÈZ«’ÈZ«Ž> £YŽ> £Y‰Ž”+¾÷²s‰Ž”Œ„£Œ„£‰Ž”Œ„£Œ„£Ž> £YQ÷ÖŽ> £Y{#U¿Ž> £Y’ÈZ«’ÈZ«‰Ž”‰Ž”‰Ž”Ž> £YŠ M± ZŒ„£’ÈZ«Œ„£Œ„£Œ„£Œ„£’ÈZ«’ÈZ«Ž> £Y´M ¡´M ¡Œ„£Ž> £YQ÷ÖŽ> £Y{#U¿Q÷Ö3– eÓ~|$@×n>a2l ¯æ/ š:Ë:$ÿÿÿÿÿÿÿÿÿÿbŒ„£×YQ²Z§=$:eŠ M± ZŠ M± ZŠ M± Z‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”Š M± ZŠ M± ZŠ M± ZŠ M± ZseVsseVsŠ M± ZŠ M± Z‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«‰Ž”œ©Ù«‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6Š M± ZŠœ©Ù«Šœ©Ù«Šv©ÞªQ÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Q÷Öu¦9Š M± Z‰Ž”‰Ž”œ©Ù«Š M± ZœÔX3– e*§\>H5^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿE Q÷Ö§=$:e,£¤,£¤,£¤I”1¢‘u¦9u¦9I”1¢‘§=$:e‰Ž”œ©Ù«'n 6u¦9‰Ž”˜žtȘžtȘžtȘžtȘžtȉŽ”œ©Ù«‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«‰Ž”Î-¤%n‰Ž”‰Ž”œ©Ù«œÔX‰Ž”Š M± Z,£¤,£¤,£¤,£¤,£¤’ÈZ«,£¤,£¤’ÈZ«,£¤Œ„£Œ„£,£¤,£¤Œ„£Œ„£,£¤I”1¢‘Š M± Zt©PZÒt©PZÒI”1¢‘§=$:eœÔX˜žtȘžtȘžtȘžtÈŠ¸ùçEl{#U¿I”1¢‘§=$:e‰Ž”‰Ž”œ©Ù«‰Ž”œ©Ù«‰Ž”‰Ž”œ©Ù«‰Ž”œ©Ù«œÔX˜žtÈœÔX{#U¿{#U¿{#U¿,£¤Œ„£Œ„£’ÈZ«’ÈZ«,£¤,£¤,£¤,£¤,£¤u¦9˜žtȘžtȘžtÈ{#U¿I”1¢‘§=$:eœÔX,£¤,£¤u¦9˜žtȘžtȘžtÈ{#U¿I”1¢‘§=$:e‰Ž”Œ„£Œ„£Œ„£Œ„£’ÈZ«’ÈZ«’ÈZ«I”1¢‘Œ„£Œ„£Œ„£’ÈZ«’ÈZ«’ÈZ«I”1¢‘,£¤Œ„£’ÈZ«,£¤,£¤,£¤u¦9,£¤u¦9,£¤’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£’ÈZ«’ÈZ«’ÈZ«’ÈZ«,£¤,£¤,£¤Ž> £Y{#U¿,£¤,£¤Ž> £Y{#U¿,£¤‰Ž”œ©Ù«’ÈZ«’ÈZ«’ÈZ«’ÈZ«‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”,£¤,£¤Ž> £Y,£¤Š M± Z,£¤,£¤,£¤,£¤,£¤,£¤Š M± Z,£¤Š M± ZŠ M± Z,£¤,£¤,£¤Œ„£u¦9Œ„£u¦9Œ„£u¦9Œ„£u¦9Œ„£u¦9u¦9’ÈZ«’ÈZ«Œ„£’ÈZ«’ÈZ«’ÈZ«’ÈZ«’ÈZ«‰Ž”Î-¤%n‰Ž”Î-¤%n’ÈZ«’ÈZ«Œ„£’ÈZ«’ÈZ«’ÈZ«Œ„£Gè‚æ¨,£¤,£¤Ž> £Y{#U¿,£¤,£¤Ž> £Y{#U¿,£¤Ž> £Y,£¤,£¤,£¤,£¤Ž> £Y{#U¿,£¤Ž> £Y,£¤Ž> £Y‰Ž”œ©Ù«,£¤,£¤u¦9I”1¢‘§=$:e‰Ž”œ©Ù«,£¤,£¤u¦9‰Ž”œ©Ù«'n 6˜žtȘžtȘžtÈ{#U¿˜žtÈ{#U¿,£¤Œ„£,£¤Œ„£Œ„£u¦9˜žtÈ{#U¿˜žtÈ{#U¿˜žtÈI”1¢‘§=$:e,£¤,£¤,£¤’ÈZ«u¦9’ÈZ«u¦9,£¤u¦9,£¤,£¤Ž> £Y{#U¿,£¤I”1¢‘,£¤,£¤u¦9,£¤,£¤,£¤’ÈZ«u¦9,£¤,£¤,£¤Ž> £Y{#U¿u¦9,£¤,£¤Ž> £YI”1¢‘,£¤,£¤u¦9,£¤V^«¾S,£¤,£¤’ÈZ«I”1¢‘u¦9,£¤,£¤u¦9u¦9,£¤Œ„£Œ„£’ÈZ«’ÈZ«’ÈZ«,£¤‰Ž”‰Ž”’ÈZ«,£¤I”1¢‘,£¤,£¤‰Ž”œ©Ù«Œ„£Œ„£Œ„£‰Ž”¼}"›#,£¤Œ„£,£¤Ž> £Y{#U¿,£¤,£¤,£¤Ž> £Y{#U¿,£¤,£¤’ÈZ«’ÈZ«,£¤,£¤Œ„£,£¤,£¤’ÈZ«I”1¢‘’ÈZ«’ÈZ«Œ„£’ÈZ«,£¤,£¤,£¤u¦9,£¤u¦9u¦9,£¤,£¤,£¤Ž> £Y,£¤I”1¢‘I”1¢‘§=$:e,£¤,£¤,£¤,£¤,£¤,£¤Ž> £Y,£¤V^«¾S,£¤V^«¾SV^«¾S,£¤V^«¾S,£¤,£¤,£¤,£¤,£¤,£¤Œ„£Œ„£Œ„£,£¤,£¤Œ„£Œ„£Œ„£Œ„£,£¤,£¤,£¤Œ„£Œ„£,£¤,£¤Œ„£Œ„£u¦9Œ„£Œ„£,£¤,£¤,£¤Š M± Z,£¤,£¤u¦9u¦9,£¤u¦9u¦9,£¤u¦9u¦9Š M± Z,£¤u¦9u¦9u¦9u¦9Œ„£Œ„£Œ„£Œ„£,£¤,£¤,£¤Œ„£u¦9,£¤Œ„£u¦9Œ„£Œ„£Œ„£,£¤,£¤,£¤u¦9I”1¢‘I”1¢‘,£¤u¦9u¦9,£¤Œ„£,£¤u¦9,£¤‰Ž”œ©Ù«’ÈZ«I”1¢‘§=$:e,£¤Œ„£,£¤,£¤,£¤,£¤,£¤,£¤,£¤Š M± ZI”1¢‘‰Ž”œ©Ù«Œ„£Œ„£Œ„£Œ„£Œ„£‰Ž”œ©Ù«Œ„£u¦9u¦9’ÈZ«’ÈZ«’ÈZ«Œ„£’ÈZ«‰Ž”œ©Ù«‰Ž”œ©Ù«’ÈZ«‰Ž”¼}"›#’ÈZ«Œ„£’ÈZ«Œ„£’ÈZ«Œ„£’ÈZ«Œ„£Œ„£Œ„£Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«Œ„£Œ„£Œ„£‰Ž”œ©Ù«Œ„£Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«'n 6Œ„£‰Ž”œ©Ù«'n 6Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£‰Ž”Œ„£Œ„£Œ„£Š M± Zu¦9u¦9Œ„£Œ„£Œ„£u¦9u¦9‰Ž”u¦9‰Ž”u¦9Œ„£u¦9Œ„£Œ„£u¦9Œ„£Œ„£Œ„£u¦9u¦9Œ„£u¦9u¦9Š M± ZŒ„£‰Ž”Î-¤%n‰Ž”Î-¤%nŒ„£‰Ž”œ©Ù«‰Ž”œ©Ù«‰Ž”œ©Ù«‰Ž”œ©Ù«‰Ž”œ©Ù«Œ„£Œ„£Œ„£‰Ž”‰Ž”u¦93–mg ÓeÓåe>_*p ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªáÅåôŒÇ˪áŪáÅã—£l5àe•ÌzÃZݼԌŽÌzÃZã—£l5àe•½_ã‰4½_ã‰4½_ã‰4ªáŪáŪáÅc ¦éc ¦é‹|.XÚ‹|.XÚ‹|.XÚ[•’[[•’[¤á±˜¤á±˜¤á±˜×7XÃ×7XÖ ÈQ

D ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2u¦9ÈU[¥ÚŠ M± ZŠ M± ZŠ M± ZŠ M± ZŠ M± ZŠ M± ZŠ M± ZŠ M± ZŠ M± ZŠ M± Z3– n]dd/mm/yyyy hh:mmm]&%Starting Nexgen Server Controller...l]*)Failed, your server should be dedicated.k]65%1 has been detected, entering compatibility mode...j]32First time run, executing default installation...i]54Configuration file has been automatically repaired.h]ServerID = %1g]B@Server crash/reboot detected, executing Nexgen boot sequence...f]76Nexgen boot sequence failed, resuming to normal mode.e] EXEC %1c]Restarting server on %1b]%$Nexgen Server Controller is active._]Loading %1 %2...]]('Failed to initialize %1, destroying...[]&%Failed to register %1, destroying...Y]#"No replacement class found for %1W]Login request for %1U]IP = %1T]ClientID = %1S]Password = %1R]Client ID already in use.Q] Kicked/banned from the server.P]Invalid password.O]Server is full.N]No playing rights.M]Login accepted.L]Login denied. Reason: %1K](+%2) %1J](-%2) %1I]$#%1 has changed his/her name to %2.H]%1 has left the server.G]Failed to switch team, %1.F]the teams are lockedE]Failed to change name, %1.D]name change has been disabledC]'&Invalid Nexgen command or parameters.B] Failed to execute command, %1.AMinternal error@]Failed to switch team, %1. ]spectators can't switch team~ ]+*team switching is disabled on this server} ])(you are not allowed to change from team{ ]team doesn't existz ]you are already on %1y ]&%this server doesn't allow spectatorsx ]all spectator slots are takenw ]/.you do not have playing rights on this serverv ]all player slots are takenu ]Failed to balance teams, %1.t ]/.this command is only available for team gamess ]team balancing is disabledr ] the teams are already balancedq ]%1 has balanced the teams.p ]%1 has started the game.x]redxÝbluexÝgreenxÝyellowo ]%$The game has been forced to an end.n = Root Adminm ] Spectatorl ]*)The server has prevented you from dying.k ]-,Unable to send message, you are muted.j ]Account type %1i ]('%1 had a team kill attempt against %2.h ] [NSC-SYS]f ] [ EVENT ]e ] [MESSAGE]d ] [ SAY ]c ] [TEAMSAY]b ] [ PMSAY ]a ]Allowed to play on server.` ]VIP slot access._ ]Admin slot access.^ ]Password immune.] ] Can be idle.\ ]Game supervisor.[ MSetup matches.Z ] Moderate.Y ]Ban operator.W ]Manage accounts.V ]Server administrator.T ]&%Press [Fire] to start the game.S ]@?Please wait, an administrator is going to start the game.P ]?>Welcome to Nexgen, type !open to open the control panel.N ]$#New settings have been saved.M ][PM] %3 %1: %2L ]$#Match password received: '%1'J ]%1 has moved %2 to %3.I ]%1 has paused the game.H ]%1 has resumed the game.F ]! %1 has restarted the game.E ]%1 has stopped the game.D ]-,%1 has disabled team switching for %2.C ],+%1 has enabled team switching for %2.B ]'&%1 has reconnected %2 as player.A ]*)%1 has reconnected %2 as spectator.@ ]%1 has send %2 to %3.~ ]&%%1 has disabled team switching.} ]<| ]&%%1 has disabled team balancing.{ ]%$%1 has enabled team balancing.z ]%1 has locked the teams.y ]! %1 has unlocked the teams.x ]&%%1 has added %2 to the banlist.w ]*)%1 has removed %2 from the banlist.v ]+*%1 has set the server in match mode.u ].-%1 has reset the server in normal mode.t ]%1 has muted %2.s ]%1 has unmuted %2.r ]%1 has renamed %2 to %3.q ]('%1 has kicked %2 from the server.p ]('%1 has banned %2 from the server.o ] %1 has muted all players.n ]"!%1 has unmuted all players.m ]65%1 has allowed nickname changing on the server.l ]76%1 has disabled nickname changing on the server.k ]"!%1 has rebooted the server.j ]:9Unable to login as administrator, invalid password.i ]%1 has logged in as %2.h ])(Connection lost!\nReconnecting in %1...g ]'&Connection lost!\nReconnecting now...f ]@?Warning, reboot sequence activated!\nRebooting server in %1...e ]@?Idle / camper detection activated!\nMove or be kicked in %1...d ] Waiting [%1]c ] Ready...b ]Starting [%1]a ] Online [%1]` ] Offline_ ] Offline [%1]^ ]Ended] ]Paused\ = Logging in[ ] Idle [%1]Z ]MutedY MProtected [%1]X ]DeadW = Match [%1]V ]ClientU ]HomeT ] SettingsS ]Private messageR ]GameQ ] PlayersP ] ModeratorO ]Match controlN ] Match setupM ]ServerL ]InfoK ] Ban controlJ ] AccountsI ]Account typesH ] Boot controlG ] PluginsF ]AboutE ]&%Welcome %1, you are logged in as %2.D ]IGThe following privileges / rights are available to you on this server:C ]Privilege %1 (not defined).B ] Team balanceA ]Red@ ]Blue ]Green~ ]Yellow} ]Play| ] Spectate{ ] Reconnectz = Disconnecty ]Exitx ] Open mapvotew = Start gamev ] Admin loginu ] Server name:t ]Short server name:s ] MOTD line %1r ] Admin name:q ] Admin email:p ]Server password:o ]Admin password:n ]Player slots:m = VIP slots:l ] Admin slots:k ]Spectator slots:j ]Advertise server:i ]Reseth ]Saveg ] Keybindsf ]Balance teamse ]Switch to redd MSwitch to bluec ]Switch to greenb ]Switch to yellowa ] Suicide` ]Open map vote_ ]Open control panel^ = Pause game] MUser interface\ ]Enable Nexgen message HUD[ ]Enable message 'flash' effectZ ]*)Show player location on teamsay messagesY ]-,Play a sound when a private message arrivesX ]Miscellaneous settingsW ],+Auto screenshot at the end of normal gamesV ]'&Auto screenshot at the end of matchesU ] PlayersT ] BlockedS ]Block / UnblockR ]Block all private messagesQ ] MessageP MSend normal PMO ]Send windowed PMN ] HistoryM ]%1 has been blocked.L ]%1 has been unblocked.K MSend to %1: %2J ]%1: %2I ] Account nameH ]Account titleG ] PasswordF ]Add account typeE ]Delete account typeD ] Move upC ] Move downB ] User nameA ] Account type@ = User title ]Online~ ] Offline} ]Update| ]Add{ ]Deletez ] y ] Switch to %1x ] Send to URLw ]Reconnect as playerv ]Reconnect as spectatoru ](Dis)allow team switcht = Pause games ] End gamer ] Restart gameq ]Allow team switchingp ]Allow team balancingo ]Lock the gamen ] Player namem ]Ban/kick reasonl = Ban periodk ] Foreverj ] 'x' matchesG ] 'x' daysE ] Until 'date'q ] IP addresses` = Client IDs_ MCreate new ban^ = Update ban] = Remove ban\ ]Remove[ ]Enable Nexgen boot controlZ ]Restart game on last mapY ]Mutators usedX ]Mutators not usedW ]Server boot command lineV ] Game typeU = Map prefixT ]! Additional command line optionsR ]$#Pre switch server console commandsQ ]RebootP ]AdministratorO ]Contact addressN ]Message of the dayL ] Server IDK ]StatsJ ] Games hostedI ] Total fragsH ] Total deathsG = Total capsF ] Top playersE ]FPHD ]DateC = Time limitB ] Score limitA ]Team score limit@ = Game speed]Team switch enabled~]Team balancing enabled}]Teams are locked|]Name change allowed{] Mutatorsz]Levely]Filex]Titlew]Authorv] PlayersuMMatch settingst]Number of gamesq] Current gamep]76Allow spectators to enter the game without a passwordn]! Mute spectators during the gamem]54Switch back to last map when the server has crashedk]87Automatically lock teams at the beginning of each gamej]32Automatically pause the game when a player leavesi]Separate players by tagh]Automatically separate by tagg] Separate nowf] Start matche= Stop matchd]Send passwordc]b] (Un)mutea] Set name`]Kick_]Ban^]Mute all players\]$#Allow players to change their name[] Show messageZ]"!Automatically update ban entriesY]*)Automatically remove expired ban entriesW]Broadcast team kill attemptsV]Enable the Nexgen message HUDU]'&Write Nexgen events to the server logT])(Write system messages to the server logS]'&Write chat messages to the server logR]*)Write private messages to the server logQ] Team switch allowed by defaultP]! Team balance allowed by defaultO] Name change allowed by defaultN]Game wait time (sec)M]Game start delay (sec)L]Auto reconnect time (sec)K]Max idle time (sec)J]&%Max idle time in control panel (sec)I]Spawn protect time (sec)H]%$Team kill damage protect time (sec)G]#"Team kill push protect time (sec)F]$#Auto disable match mode time (min)E]%$Weapons ignored by spawn protector.D] Weapon classC]Ignore primary fireB]Ignore alternate fireA]HUD replacement classes.@]Original HUD class]Replacement HUD class~Mq<–{8h?i ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ M± Z–µÏxWŠ M± ZŠ M± Z– z$ff†?y">c] Nexgen105x]CountryFlags2] ' ¶W9U;f ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ EyE¾¶yŸ&. EyE¾ EyE¾– o"ÿÿÿÿ Žšb9X9e ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ yJ„š¤ÅÚãÌ~ EyE¾ EyE¾ EyE¾ EyE¾ŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxÃo û EyE¾– ]$PAs ×~=I2y / š:Ë:$ÿÿÿÿÿÿÿÿÿÿ’ÈZ«×YQ²Z3– ¶r;O;N ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷^®¶yŸ&.÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®Š M± Z– H"ÿÿÿÿ%[~>T1i š:Ë:$š:Ë:$ÿÿÿÿÿÿÿÿÿÿ@÷t×[qš¤,£¤,£¤Q÷ÖŒ„£Š M± ZŠ M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Š M± ZŠ M± ZQ÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷Ö’ÈZ«,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤Q÷Öu¦9,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷Ö,£¤Q÷Ö’ÈZ«Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷Ö,£¤Q÷Ö’ÈZ«Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤‰Ž”˜žtȘžtÈŽ> £YŽ> £YŽ> £Y˜žtÈ,£¤,£¤,£¤,£¤Q÷ÖQ÷ÖŒ„£Q÷ÖŒ„£‰Ž”‰Ž”‰Ž”‰Ž”‰Ž”Š M± ZŠ M± ZQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£,£¤Q÷ÖŒ„£,£¤Q÷ÖŒ„£,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷Ö,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YŠ M± Z,£¤,£¤Q÷Ö,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö,£¤,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £Y,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷ÖŒ„£Q÷Ö’ÈZ«Q÷ÖŒ„£Q÷Ö,£¤Q÷ÖQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤Q÷Öu¦9,£¤ÙÊM¶,£¤,£¤ÙÊM¶,£¤,£¤,£¤ÙÊM¶,£¤,£¤u¦9,£¤u¦9,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷Ö,£¤÷t×Q÷Ö,£¤,£¤÷t×,£¤,£¤,£¤‰Ž”œ©Ù«Q÷Ö,£¤,£¤Q÷ÖŒ„£‰Ž”¼}"›#,£¤Q÷ÖŒ„£,£¤Ž> £Y{#U¿,£¤,£¤,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£,£¤Œ„£Š M± Z,£¤Œ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Š M± Z,£¤Œ„£Š M± Z,£¤Œ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤Q÷Öu¦9,£¤,£¤Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷Öu¦9Q÷ÖŒ„£,£¤,£¤Q÷ÖQ÷Öu¦9,£¤Q÷ÖŒ„£,£¤Ž> £Y,£¤,£¤Ž> £YQ÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤Q÷Öu¦9,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷Ö,£¤Q÷Ö’ÈZ«Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷Ö,£¤Q÷Ö’ÈZ«Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷Ö,£¤Q÷Ö’ÈZ«Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £YŠ M± ZQ÷Ö,£¤,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷Ö,£¤,£¤,£¤,£¤,£¤Q÷Ö,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷Ö,£¤,£¤,£¤,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤,£¤Ž> £YQ÷Ö,£¤,£¤‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«‰Ž”¼}"›#,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤Q÷Öu¦9,£¤Ž> £Y,£¤,£¤‰Ž”Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«‰Ž”Q÷ÖQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö’ÈZ«Q÷Ö’ÈZ«‰Ž”‰Ž”‰Ž”,£¤Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £YQ÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y,£¤,£¤Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷Ö,£¤,£¤Ž> £YQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Š M± ZQ÷ÖQ÷ÖŒ„£,£¤,£¤Ž> £YQ÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Ž> £YQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£Q÷ÖQ÷ÖŒ„£Q÷Ö,£¤Q÷ÖŒ„£,£¤,£¤Q÷Öu¦9Q÷ÖŒ„£Q÷ÖŒ„£,£¤,£¤Q÷Ö,£¤,£¤,£¤,£¤Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷Öu¦9Q÷Öu¦9Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Š M± ZQ÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖQ÷ÖŒ„£,£¤´M ¡,£¤,£¤Ž> £Y,£¤+¾÷²s9·Ç⥊v3Týáv3Týá,£¤{#U¿,£¤,£¤u¦9,£¤u¦9{#U¿,£¤,£¤,£¤Q÷ÖQ÷ÖŒ„£Q÷ÖŒ„£Q÷Ö’ÈZ«Q÷Ö’ÈZ«,£¤,£¤,£¤,£¤,£¤,£¤,£¤,£¤,£¤÷t×,£¤,£¤Ž> £Y,£¤,£¤Q÷Ö,£¤,£¤Q÷Ö,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«'n 6‰Ž”œ©Ù«‰Ž”œ©Ù«Q÷ÖQ÷ÖŒ„£,£¤Q÷Öu¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤Œ„£,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤Œ„£,£¤,£¤,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤,£¤,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤,£¤,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤Œ„£,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤Œ„£,£¤,£¤,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤,£¤,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤,£¤,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤+¾÷²s9·Çâ¥,£¤+¾÷²s9·Çâ¥,£¤+¾÷²s9·Çâ¥,£¤u¦9,£¤+¾÷²s9·Çâ¥,£¤u¦93– v= ClientCoreB~;2u ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3¬—6ZªáŬ—6Z¬—6Z¬—6Z¬—6Z¬—6Z&; ë¡Š M± Z,£¤÷^®Š M± Z,£¤÷^®Š M± Z,£¤÷^®÷^®Š M± Z,£¤÷^®&; ë¡÷^®÷^®Š M± Z,£¤÷^®Š M± Z,£¤÷^®Š M± Z,£¤÷^®Š M± Z,£¤÷^®Š M± Z,£¤÷^®Š M± Z,£¤– “=K9x ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[•’[“tµðg9 CŸÄU K/ÄU K/ÄU K/ݼԌŽ– Q @@\qr CIšq;~9L ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&; ë¡š¤&; ë¡÷^®¶yŸ&.&; ë¡÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®÷^®ªK4èž÷^®÷^®÷^®÷^®Ão ûÅÚãÌ~ŒxŒx÷^®ŒxŒx– M *ÿÿÿr*úÈ–M*ÈMª–MªÈMªÈ–Mªddd[Ó]$PAs1 X @B(m@|ÞJG<@.F ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 9·Çâ¥>NõÔBŽ> £YÂR@G¢²/Tã2Ão û”.ü'l”.ü'l”.ü'lŽ> £YÂR@G¢²/Tã2– @]mainh @@F D9};U,Gÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2>NõÔB¬—6ZÍ´è7`³?{³?{³?{¬—6Z³?{Í´è7`³?{³?{³?{¬—6Z³?{Í´è7`³?{³?{³?{¬—6Z³?{Í´è7`³?{³?{³?{¬—6Z³?{Í´è7`³?{¬—6Z¬—6Z>NõÔBÍ´è7`³?{Í´è7`³?{>NõÔB³?{>NõÔBÍ´è7`³?{¬—6Z¬—6Z>NõÔB¬—6ZÍ´è7`³?{¬—6Z¬—6Z>NõÔB³?{– T @tJÅm;qc ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBêÓnÅÅÚãÌ~Ž> £YÂR@G¢²/Tã2Ão û”.ü'l”.ü'l”.ü'lŽ> £YÂR@G¢²/Tã2¤á±˜¤á±˜“tµð“tµð¤á±˜¤á±˜¤á±˜¤á±˜¤á±˜×7XÃŽ> £YÂR@G¢²/Tã2Ão û”.ü'l”.ü'l”.ü'l– [Ó`"PM$€AS $€AY$€AX$@@W$@V$°AU$€A]$@BT$€@§z=x2z ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUC»P£§=$:e{#U¿{#U¿{#U¿{#U¿{#U¿ŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒx,£¤,£¤u¦9,£¤,£¤,£¤u¦9,£¤u¦9,£¤,£¤,£¤,£¤,£¤,£¤,£¤u¦9,£¤u¦9,£¤,£¤,£¤’ÈZ«,£¤u¦9,£¤Ž> £Y,£¤u¦9,£¤,£¤,£¤,£¤,£¤[qš¤,£¤[qš¤,£¤Ž> £Y,£¤Œ„£,£¤Œ„£,£¤,£¤,£¤u¦9,£¤u¦9,£¤u¦9,£¤’ÈZ«,£¤’ÈZ«,£¤u¦9,£¤u¦9,£¤’ÈZ«,£¤’ÈZ«,£¤’ÈZ«,£¤u¦9,£¤’ÈZ«,£¤’ÈZ«,£¤u¦9,£¤u¦9,£¤’ÈZ«,£¤’ÈZ«,£¤’ÈZ«,£¤u¦9‰Ž”,£¤u¦9,£¤Œ„£,£¤Ž> £Y'n 6,£¤Œ„£,£¤Œ„£,£¤u¦9,£¤u¦9,£¤Ž> £Y'n 6‰Ž”‰Ž”,£¤u¦9,£¤u¦9,£¤,£¤,£¤[qš¤,£¤[qš¤ŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒx±ëŠZ!±ëŠZ!ŒxŒxŒxŒx±ëŠZ!±ëŠZ!,£¤Ž> £YÂR@G¢ÔIò&/,£¤Ž> £YÂR@G¢ÔIò&/,£¤Ž> £YÂR@G¢ÔIò&/Œx‰Ž”‰Ž”‰Ž”‰Ž”ŒxŒx,£¤Ž> £Y{#U¿ŒxÃŽ> £Y¹²‘¿!ÊÃflUŒxŒxŒx,£¤Ž> £Y{#U¿,£¤Ž> £Y{#U¿,£¤Ž> £Y{#U¿,£¤Ž> £Y{#U¿,£¤Ž> £Y{#U¿ŒxŒx,£¤Ž> £Y{#U¿Œx‰Ž”,£¤Ž> £Y{#U¿{#U¿{#U¿'n 6'n 6'n 6{#U¿{#U¿{#U¿{#U¿'n 6'n 6'n 6{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿Š M± ZŠ M± Z{#U¿{#U¿,£¤Ž> £Y'n 6'n 6'n 6{#U¿'n 6{#U¿'n 6{#U¿'n 6{#U¿{#U¿'n 6'n 6{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿{#U¿óÿF×z{#U¿{#U¿í1ÚÈ{#U¿{#U¿{#U¿{#U¿ŒxŒxÃŽ> £Y¹²‘¿!ÊÃflUŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒx‰Ž”,£¤ŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒxŒx§=$:e,£¤,£¤,£¤,£¤Œ„£,£¤’ÈZ«,£¤Œ„£,£¤u¦9,£¤u¦9,£¤u¦9,£¤’ÈZ«,£¤’ÈZ«,£¤u¦9,£¤u¦9,£¤,£¤,£¤,£¤,£¤u¦9,£¤u¦9,£¤´M ¡,£¤,£¤´M ¡,£¤3– K*ÿÿÿz*ÿzªÿzªúzªúúzª––ÿzªÿÿÿX*úZZXªZZÿXªZúZXªúúZXªÿÿÿXªú2ÈXª2úúXª––úXª ú–2Xª – ~xr E,õ~3i¶o6õih4wa *a ih Èo;l2s ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿI”1¢‘ÈU[¥ÚQ÷ÖQ÷ÖQ÷Öu¦9Q÷Öu¦9Q÷ÖQ÷ÖQ÷Öu¦9Q÷Öu¦9Q÷ÖQ÷Öu¦9Q÷Öu¦9Q÷ÖŒ„£Q÷ÖŒ„£Q÷ÖŒ„£3– Hq «I @rc [KO€j@o€s!€j @@g€bÈ>E8h ž š:Ë:$ÿÿÿÿÿÿÿÿÿÿ[qš¤ÈU[¥Ú3– ~|$À?u  @@/Ó%M6*'.",")"("+*ÿÿÿ"bÃ'ª¢Û A@××××××××××××××××Y @@f6uf5&x;p9K ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb³šÀ&; ë¡÷^®.âSÕ.âSÕÃo ûÃo û– q*–––p*È–d[Ss.G€Œhëw6y6:;Q Öëa¯¯ºh[ ;-G)cCloseÔ-C)^.²Æ×ÝÕÔw^*q^ÄÔwq*½qa/!_q&qqÅ f6^²[ @@o@yabw«X  @@_ @I@G@e €@±J0€l@ J B@bkK Àp o`± @N@bq@UD¾I¡D’’a$Gz#y#x#o#D’cDp#G  Þl;_8P ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2 E6‚Þ“Ú0³Š M± ZBêÓnÅBêÓnÅBêÓnÅBêÓnÅBêÓnÅŠ M± Zo– c$ÈCd$HC@ u X @@j]“DB~OI¡B’’^$Kg#f#d#[#B’cB^#K  M@}àI¡@’’S$LS#P#O#L#@’c@N#L  K@õeke6õkl4‚rkc š:l,& F @DFm+€ŒS$e@ &N¡e’’n$zK$J$H$}#eceB$¡ez  Ès;J>O ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ V^«¾SÈU[¥ÚŠ M± ZŠ M± ZŠ M± ZŠ M± Z3– ~#µ^ @@^ @J @@]CK @@ZQM&nH0BI  @@n/€{€Y@d+€l+€ŒiJ.S½G]D€`kZ @@w @@V5€R€¨R@|€Tr@[ j W{€q‹d DS @u @@|MO,€Žk€Œh J€o[@G @v  @@Ío@R€¨s @@w \Q\@x€w A0€JY]@C€sWO @@y &m€¨C€LYypE n. Bz€xBA @V  @`rK€D€@bS @^€˜N @o%zi32, u n @_#w @@ab wQ @K%_o @[pc,€f q @F@f @C$~VUL€¨/EP€xr E ms^d[€@_ @g$l€Œx €¨R @I &Sf Z‹g@Z @PJ€˜z€¨O€V  @@X N€ @Xt  f @z€j€XFH A oq |M€qñS€MDCUr@M0€@Cj€}d@@\%@€P0€O@H@y&«u€_@@mX}@€k€¨H@J@{HR½`@@;Z3\ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZv3Týᬗ6Z,£¤u¦9,£¤u¦9{#U¿6”`k6”`k6”`k6”`k“tµð,£¤,£¤Œ„£,£¤Œ„£,£¤’ÈZ«,£¤’ÈZ«,£¤÷^®,£¤,£¤,£¤,£¤,£¤u¦9,£¤u¦9÷^®“tµð÷t×÷^®“tµð“tµð,£¤‰Ž”,£¤‰Ž”ÌzÃZ÷t׋|.XÚ×7XÃ6”`k6”`k÷t×÷^®,£¤u¦9,£¤u¦9÷^®6”`k6”`k6”`k÷t×÷^®,£¤u¦9,£¤u¦9÷^®6”`k6”`k6”`k¶yŸ&.6”`k6”`k6”`k¶yŸ&.6”`k6”`k6”`k÷t×÷^®,£¤u¦9,£¤u¦9÷^®6”`k6”`k6”`k÷t×÷^®,£¤u¦9,£¤u¦9÷^®6”`k,£¤÷t׊ M± Z,£¤,£¤6”`k6”`k,£¤,£¤Š÷tתáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ6”`k6”`k×7XË|.XÚ×7XÃ×7XÃ×7XÃ,£¤,£¤“tµð“tµð– @= privatemsgd|Dr€Y&“biI@D@Qz@c Z O€@}€˜k@f€V@Mm5 {K“Q  @o%QCK%€e v @O@@H"y ³L€u@]€D‹~ iC_V[@g$c€@|s V@@~Ca @@C$@ ”e @@H HÒLTC_#˜N kCT U @N–c?O=a ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿv©Þª–µÏxW– T{ df d”|u2t NaY@qf  @E @F @Jz €c@w@T€e @; `~ \ @R €€RU€¨Y€¨L€Kdb@\e37H¶  _f@@^yg N  @bc2€@lo  @˜PW)€X)€v @Y  @S\W €]|€¨]”XI €ŒS rw^B~@b@kK K Go  L±[+tp  yNt¤W/@Y€@`@mM#€@JEAt~@U€hb @Kg€r0€@Nb @@@s@x”M@y€Iv ~c @@`€W@ G ‹ f±QW@K ‹j.€€MZD ‹AdB€BJ€MW@E @E6!M @oS €Z wOc @B(@¨k|:v ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿK ØGÞ”.ü'l– m @g@h@n€TRL±yY “U _ “EFÜ[ Ü^ €X€˜L “N@^ k Z €z @I€@f  X l z @m V€@TZH @X @q €U @|2€Ìn½Pg ‹D  @¤H  @x}F M @L  @Hh @^ “O  @@C @@ET  @G€{ @p @N€: ŠÜ}|zD“rÙm@iP)@TW@V@d @Q€l @_'€Œr@I,€t/€@@r@a€¨“v “x “|+ZQy “} “~ “ “Y@ “L@J ‹LB “Rw€¨[€ªL ‹W“‹K “M “[€@N “µ@@YL?€@@b m€¨Z \ ] S_ ‹P‹N€@@aLbS@eB“i @[ "vq MWs ‹v ‹€z { | NUw ‹e€Š¸ ‹t “bE‹‹e\A‹I‹‹€N€Š¸m€W€]+‹@jx€Š¸{c€Š¸sZ“@@ €@L“\€u€^“_“e“H@cB@P “\&K€Š¸a#€@q%€@j'€@`@g@{«pv^±C0@sŠÜRs@@y€ŠŽÌDUF€A€EFk d@G@cIJl @S@{€K@} @DV i|'€@[“X0€v€@bu`“b0€@e€b T €` [pEa fG)@g€y Šf€t Šv @}@@)Xi€t(iU JQP ?@@@X+€J{Ct@c k  @W@Q @hY  @d€b@Gg3H8Ô Cag ŠuBbE3€@kX¤r@S€C4\ e€d@e“H8L8ÈÌA sP I@zW'“A@u€yCL X@YVJ€¨Q€¨@€d,€@HG[€@U I`@PY@@\@€P%@€RC%@€aMaAñ;!ž”abÄa“, b  z€D#@dM gL b@G@j€H€@O€^ €@E€Èm>L/m ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQseVsÈU[¥ÚŒ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Š M± ZŒ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£Œ„£3– È|>V0k ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqt©PZÒÈU[¥Ú,£¤Š÷t×÷t×Q÷Ö’ÈZ«Q÷Ö’ÈZ«Q÷ÖŒ„£Q÷Ö’ÈZ«,£¤,£¤Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £Y‰Ž”œ©Ù«u¦9,£¤,£¤Q÷Ö’ÈZ«u¦9Q÷Ö’ÈZ«u¦9Q÷ÖQ÷Öu¦9,£¤,£¤Ž> £Yu¦9,£¤,£¤u¦9,£¤Q÷Öu¦9Q÷Ö’ÈZ«u¦9,£¤,£¤Q÷Ö,£¤,£¤Q÷ÖŒ„£u¦9Q÷ÖŒ„£,£¤u¦9,£¤,£¤,£¤u¦9Q÷Ö’ÈZ«u¦9,£¤u¦9Q÷Ö’ÈZ«u¦9‰Ž”œ©Ù«‰Ž”¼}"›#u¦9,£¤Ž> £Y{#U¿u¦9u¦9u¦9,£¤u¦9u¦9Q÷Öu¦9Q÷Öu¦9Q÷ÖQ÷ÖQ÷ÖQ÷ÖQ÷Ö,£¤,£¤Q÷Ö,£¤,£¤Q÷Ö,£¤Q÷Ö‰Ž”œ©Ù«§=$:e,£¤,£¤u¦9,£¤u¦9Q÷Ö3– –}>R=j ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)ud2½ð–µÏxW,£¤´M ¡,£¤,£¤´M ¡,£¤Š M± ZŠv©Þª,£¤´M ¡,£¤,£¤´M ¡,£¤,£¤´M ¡Šv©ÞªŠ M± ZŠv©Þª,£¤´M ¡,£¤,£¤´M ¡,£¤,£¤´M ¡,£¤ÙÊM¶,£¤,£¤Š M± Z,£¤– c€@mtc W@>Q€@Tlu@d?€@| bM>S?€dM?c!€@^ g?€@P@^€«@D @x>€fV"€@@@@Ae@i ÄF>w8w ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿg9 CŸÄU K/ŒxŒxŒx– L>E>n o nV#€f€E#@tdvb@z^w#€@O€x@J#€AL€JK€@G€y€Gu@d€K`@E%A%Bäpjaw$`]h–`’]r$ax$=íA%`&¥a^ša,@h¥`  pL€XRHO €@h EODŒªQS œÄ%6bO,,?M–¡%6bO”O ,”O ,¥&6bO¡&6bOÄO ,R“,@S ™O RMS%6OOS RE6HO6OOBR–’B,?O MSBI%,@E6HOI¡B,@ÇS %$B%MSB6OOS “O B  @P@@M]€w€^&€«X@r€TJ@(mlkk(Vw(Pr¤{n€p€¨KL@ÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ!!!ÿ"""ÿ###ÿ$$$ÿ%%%ÿ&&&ÿ'''ÿ(((ÿ)))ÿ***ÿ+++ÿ,,,ÿ---ÿ...ÿ///ÿ000ÿ111ÿ222ÿ333ÿ444ÿ555ÿ666ÿ777ÿ888ÿ999ÿ:::ÿ;;;ÿ<<<ÿ===ÿ>>>ÿ???ÿ@@@ÿAAAÿBBBÿCCCÿDDDÿEEEÿFFFÿGGGÿHHHÿIIIÿJJJÿKKKÿLLLÿMMMÿNNNÿOOOÿPPPÿQQQÿRRRÿSSSÿTTTÿUUUÿVVVÿWWWÿXXXÿYYYÿZZZÿ[[[ÿ\\\ÿ]]]ÿ^^^ÿ___ÿ```ÿaaaÿbbbÿcccÿdddÿeeeÿfffÿgggÿhhhÿiiiÿjjjÿkkkÿlllÿmmmÿnnnÿoooÿpppÿqqqÿrrrÿsssÿtttÿuuuÿvvvÿwwwÿxxxÿyyyÿzzzÿ{{{ÿ|||ÿ}}}ÿ~~~ÿÿ€€€ÿÿ‚‚‚ÿƒƒƒÿ„„„ÿ………ÿ†††ÿ‡‡‡ÿˆˆˆÿ‰‰‰ÿŠŠŠÿ‹‹‹ÿŒŒŒÿÿŽŽŽÿÿÿ‘‘‘ÿ’’’ÿ“““ÿ”””ÿ•••ÿ–––ÿ———ÿ˜˜˜ÿ™™™ÿšššÿ›››ÿœœœÿÿžžžÿŸŸŸÿ   ÿ¡¡¡ÿ¢¢¢ÿ£££ÿ¤¤¤ÿ¥¥¥ÿ¦¦¦ÿ§§§ÿ¨¨¨ÿ©©©ÿªªªÿ«««ÿ¬¬¬ÿ­­­ÿ®®®ÿ¯¯¯ÿ°°°ÿ±±±ÿ²²²ÿ³³³ÿ´´´ÿµµµÿ¶¶¶ÿ···ÿ¸¸¸ÿ¹¹¹ÿºººÿ»»»ÿ¼¼¼ÿ½½½ÿ¾¾¾ÿ¿¿¿ÿÀÀÀÿÁÁÁÿÂÂÂÿÃÃÃÿÄÄÄÿÅÅÅÿÆÆÆÿÇÇÇÿÈÈÈÿÉÉÉÿÊÊÊÿËËËÿÌÌÌÿÍÍÍÿÎÎÎÿÏÏÏÿÐÐÐÿÑÑÑÿÒÒÒÿÓÓÓÿÔÔÔÿÕÕÕÿÖÖÖÿ×××ÿØØØÿÙÙÙÿÚÚÚÿÛÛÛÿÜÜÜÿÝÝÝÿÞÞÞÿßßßÿàààÿáááÿâââÿãããÿäääÿåååÿæææÿçççÿèèèÿéééÿêêêÿëëëÿìììÿíííÿîîîÿïïïÿðððÿñññÿòòòÿóóóÿôôôÿõõõÿöööÿ÷÷÷ÿøøøÿùùùÿúúúÿûûûÿüüüÿýýýÿþþþÿÿÿÿÿvD€€@@~€r€~t)€A€¨U š` @ÈE9B=h ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGè‚æ¨ÈU[¥Ú,£¤,£¤,£¤Ž> £Y{#U¿,£¤Ž> £Y{#U¿,£¤Q÷Ö,£¤,£¤,£¤‰Ž”œ©Ù«‰Ž”¼}"›#Q÷Ö,£¤,£¤,£¤,£¤,£¤,£¤œÔX3– aH€Žv@h @Jg2–MNOW¤S@U+€@~@D(“Q€Š¸W@F8€ÃB@J|Ãp¤g z t7ky€q€Šl7€r@h@u,€@f€ m€˜w€˜aS[k€˜€[€hR ¤^m@S7€HCwK‹|‹}€@{€@Z(€@w@y+ vRw@ @o@A q@o+\@@B  @qÃ@b@|li@|e@I)e@]k€@xI6€p‹q‹s‹SR‹q5ŠœvFg0€@}€C€B€@A€@ }}€y@DL] sp@Z nu NOY Q€@u4€@K€^ “S €VW@YU[\\S€@Xa€^ ^€[gCpkK €|)€@GF ‹i‹j‹]k‹€o€n‹q‹r‹s‹t‹u‹v‹T€p€y€z€{€|€a @}€€W+ŠuAVBGtII€E€m  Fi@h s@U€h “{3€Q*€E x‹p3€VZ@_€l€a €iki @c el @d@_Ì]@@€@@p€q€u€@c3€@u[@Wh*€@€yw{@j@F§@€@@@A@G@Jîx@x @C€Jk @{KMOQP*@\ ~@SwL*€@A*€@X h@X]4@Y+€@@Q€«bY1€g€@O1€@e€Œi€@|hi)@€kc+¤owI@\s@€HvGwy.n0€@]€@a @r€@n@_€@@@h+@G@i+a o€@U@Q ¤N¤O¤P¤t@T@U@o¤W¤e @X¤Z¤[[6€kŠœl@€dC)gc _e(€@l€¨€B7pX@r r@f@@u@a(€@@J7€@zN€@|}LS/€@~X(€B€M/€IJ D€@G€J/€hr@K@L@LNz@{.€€U(€W€@T€@U€@V€@X€@\@R@Z€@_€îY€î]€@b`€@b€@@x-€în@iew}€tsM-€@F G-@p@C-@p€@x€@J-€@C i@I O(€Œa@AC/€@C`@H([€ŠE€@j/€@ëW:¿<ëW.ÍØ Í¯º?,¯¾?, S€¨H €Y0q€@iPf+€@I1Q €B€WV€¨T€y'¤` @i'@g'\€¨€b'_€¨€]'cF@j€¨s €X*€a*€Œ€h€¨€yEã{ ã@mÀy@u€˜¤L€˜@@v€@@L5z}€@J @FI'~>z9[-c ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ApˆàK>NõÔB¬—6Z¬—6Z¬—6Z¬—6Z¬—6Z¬—6Z>NõÔBÒ£½þˬ—6ZŠ M± Z¬—6Z>NõÔB>NõÔBŠ¬—6ZåôŒÇËåôŒÇˬ—6Z¬—6ZApˆàK¬—6Z¬—6Z¬—6Z>NõÔBåôŒÇËÒ£½þËÒ£½þËÒ£½þËÒ£½þËåôŒÇËÒ£½þËÒ£½þË– NE€¨B€U6s@I~r&¤l@l6OM€x€@q6Q€UK&€Œh6Ó/Ó%i(*'." ," )" (" +*"5*õòÙÿ"ê "¼¢Û ¶º@ =U}q®ÈÐÎÇÏâÝÉ>r°±Ë² o8`tmx©uej§¥eD4 F,-Pnled;.:Oz€2#&X•Ä”HIW[Zƒ'†0Œ øüíõúŸ¢Ôž¶3M+œÃÀ™˜“‘“˜˜Y¸9ŠK‘““’’‘~^Y¹b/¿)‘‘^]JÒwA× $‘_\%¾´A… ‘_QªE‰ –¼i"‡ ÔÁ{SGÖ—žšµ…p*›š¡ÓVRyC Ž £ìšš¢îï1@C(£ñÿøÆÁÅôïò0?<ŒÓó÷Úûýöðùï¢70½ÕÛÜÜÜÙØÕÂg505³‹‰ˆ|LA7hvT=Êêëæåã»f¨Þ«¯!6ªBáéþäçèѦàߤºh06„·¬­ÊÍÌkascN7  u66Ó/Ó%i(*'." ," )" (" +*5*õòÙÿ"ÃK ¾¢Û $¿@q®ÈÐÎÇÏâÝÉ>r°±Ë²o8`tmx©uej§¥eD4F,-Pnled;.:Oz€2#&X•Ä”HIX[Zƒ'†0Œ øüíõúŸ¢Ôž¶2MœÃÀ™˜“‘“˜˜¸9Š‘““’’‘~^¹b¿‘‘^]Òwב_[¾´…‘_Qª‰–¼i‡ÔÁ{SÖ—žšµ…q›š¡ÓRzC £ìšš¢îï1>C£ñÿøÆÁÅôïò0><ŒÓó÷Úûýöðùï¢70½ÕÛÜÜÜÙØÕÂg505³‹‰ˆ|LA7hvT=Êêëæåã»f¨Þ«¯"6ªCáéþäçèѦàߤºh06„·¬¬ÊÍÌl`rdN7 [ŠB@_Ù`´acm8€Œ€Q'€i€j€ªm€@G'€@o9€H&€Œd€&d«c€W:€_€oyt€_;€@z|W&«@~€@€@R&€@W@{€A€B€C€@e%`E1l¼ %`&`,`,`E%\%,@U%,x¤j×U&, V·ÇèU,,Ûp $U,,îνÁU,,¯|õU,, *ƇGU,,F0¨U,,•FýU,,ؘ€iU, , ¯÷D‹U, ,±[ÿÿU, ,¾×\‰U, ,"kU, , “q˜ýU,,ŽCy¦U,,!´ID&,b%öD,, @³@ÀD, ,QZ^&D%,ªÇ¶éD,,]/ÖD, , SDD,,æ¡ØD,,ÈûÓçD, ,æÍá!D,, Ö7ÃD,,‡ ÕôD,,íZED, ,éã©D,, ø£ïüD,,ÙogD, ,ŠL*M,,B9úÿM,, öq‡M, ,"amM,, 8åýM&,D꾤M,, ©ÏÞKM,,`K»öM, ,p¼¿¾M, ,Æ~›(M%, ú'¡êM,,…0ïÔM,,ˆM, ,9ÐÔÙM, , å™ÛæM,,ø|¢M,,eV¬ÄN%,D")ôN,, —ÿ*CN,,§#”«N,,9 “üN, ,ÃY[eN,, ’Ì N, ,}ôïÿN&,Ñ]„…N,,O~¨oN,, àæ,þN,,C£N, ,¡NN,,‚~S÷N, , 5ò:½N,,»Ò×*N, ,‘Ó†ë¡%`¡&`¡,`¡,`  VF@G–@i@QV%ZRÇ¿] %w%½–wP%wZ=œ] \,ÿ’w&Z=œ:=•] \,,ÿ’w,Z=œ:=•] \,,ÿ’w,Z=œ:=•] \,,ÿ¥] ¡w,  O€@N€€ŒFT€@r%€@RUW@J€Y€Z€[€@x$€L^@_–b€V<€Jl$€I€@f€€ŒPjkl@l€n€o€p€@Hs@t–`<€@FK$€E€@{€€Œj@A@g€C€D€E€@z#€CH@I–g#€S#€U$€@g|€@Q€€ŒZG$€@VWX@O€Z€[€\€@P”w`@a–_cunp€@h€€ŒIlVmo@A=€q€s€D@"€@mn@p@\ €jG#€q"€A#€j"€e"€@d€@mT"€@l"€@G@["€@D?€@BE?p8f ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2J¦&¿ùBêÓnŤ᱘¤á±˜¤á±˜– T]98Failed to login: you are banned/kicked from the server.]égThe server has refused to let you join the game because you have been banned or kicked, meaning you are probably not welcome for the time being. Please go play somewhere else, there are enough of other servers and games out there.R] Reason:Q] Period:P]`"OS @O"€@K@H"VN@RR€@@€@U€@F"€@SVWXB"€@Yl>€@]^_`as>€@f?dj?€@€@jR€@u€@k€@n€@o€@p€@q€@r€@l€@m€@s€@j€@t€@v€@y€@z€@{€@|€@}€@~€@Y€@V?€A €@B €@C €@D €@E €@F €@G €@H €@I €@J €@K €@L €@M €@N €@O €@P €@Q €@R €@S €@T €@U €@V €@W €@X €@Y €@Z €@[ €@\ €@] €@^ €@_ €@` €@a €@b €@c €@d €@e €@f €@g €@h €@i €@j €@k €@l €@m €@n €@o €@p €@q €@r €@s €@t €@u €@v €@w €@x €@y €@z €@{ €@| €@} €@~ €@ €@@!€@A!€@B!€@C!€@D!€@E!€@F!€@G!€@H!€@I!€@J!€@K!€@L!€@M!€@N!€@O!€@P!€@Q!€@R!€@S!€@T!€@h€@w€@€U!€@V!€X!€[!€@\!@]!@^!@Q?@Y!€_!€@N?€a!€W!€@b!€f!€g!€h!€i!€J?€d!€j!€m!€@n!€o!€p!€q!€z>€{>€@R€S"€@O€@u>€u!€@v!€w!€@y!€@z!€@{!€@|!€@}!€@q>€~!€@k>n ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿA"&,‘ôŒ„£3–m@"€@Z€@C"€@D"€@E"€@T€@e zG"€@I"€@J"€@K"€@L"€@B€M"€@g>o ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿP"$BEŒ„£3– R"€@f>@€@T€«L€@d>@X"€@a>\€@U"€@]€@]"€@Z>€Z"€@IX>L6r ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\`"iYI”1¢‘Q÷Ö,£¤,£¤Q÷Ö,£¤,£¤Q÷Ö,£¤,£¤,£¤,£¤,£¤,£¤V^«¾S,£¤,£¤,£¤V^«¾S,£¤,£¤V^«¾S,£¤,£¤,£¤Š M± Z,£¤,£¤Š M± Z,£¤,£¤Š M± Z,£¤,£¤Š M± Z,£¤,£¤Š M± Z,£¤Š M± Z,£¤,£¤Q÷Ö,£¤,£¤,£¤,£¤,£¤Q÷Ö,£¤,£¤,£¤,£¤,£¤Š M± Z,£¤V^«¾S,£¤Q÷Ö’ÈZ«,£¤,£¤,£¤,£¤V^«¾S,£¤,£¤V^«¾S,£¤Q÷Ö,£¤œÔX,£¤,£¤,£¤,£¤Q÷Ö,£¤,£¤,£¤Q÷ÖŒ„£Q÷ÖŒ„£3– s]Nexgen core controllerr] Zeropointq]1.05 build 1086b"€@V>T>U0t ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8c"Y>n·¬—6Z,£¤Ž> £YŠ M± Z,£¤Ž> £Y,£¤Ž> £Y,£¤Ž> £Y,£¤Ž> £YÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖ¨)æ›üÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖ,£¤Ž> £Y,£¤Ž> £YÀ¶ŒsÖ,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9À¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖÀ¶ŒsÖ– _]mutate nsc balanceteams_Ýmutate nsc setteam 0_Ýmutate nsc setteam 1_Ýmutate nsc setteam 2_Ýmutate nsc setteam 3_Ý suicide_Ý"!mutate nsc openvote,mutate hz010_Ý87mutate nsc openrcp,mutate asc#get#window,mutate hz0090_Ýmutate nsc pause_"€@R> €@g"€@O>A€@d"€@h€@i"€@I>€@D>€@k"€@@€@n"€@j€@}€@}=€@y=@p"€@t"@u"€@v"@x"@y"€@l=€@w"€@|"€@}"€@n=€@~"€@k=€v€@@#€@@fC#€@U=€T€@F#€@N=€N~|' fx%^–xJ#,}p0123456789ABCDEFœ~,&}~Ä~,¥x}  €I#€@L#€N#€O#€O €@Q#€@P#€h#€@R#€@W#€X# €t€U#€@Y#€@€Z#€@\#€@[#€pd]#€@`#€@h€@b#€@^#€c#€@d#€f#€j#@e#€@{#@i#€@k#€@l#€@m#€@€o#€n#€@q#€@r#€@s#€@t#€@u#€@v#€@p#€x#€y#€|#@@€{€@~#€@#€@@$€@}#€YdA$€@D$€@E$€@F$€@B$€U€@H$€J$€j<€I$€@L$€M$€@O$€@N$€g<€@^$Q$b³N$žQ$L$  e<€R$€@M€@V$€@W$€@X$€@Y$€@Z$€@T$€\$€a$]$Hb]$\$T$  v€_$€n$`$aûžœ`$vœ_$v  [$€@b$€@c$€@d$€@[<€Bde$€@h$€@i$€@j$€@f$€k$€@ceeö¤žœel$œef$  p$€@@%€@m$€@`€q$€@s$€@t$€@u$€@r$€w$@€v$€@y$€@z$€@{$€@|$€@}$€@~$€@]€@€@N€^@€MD%O×L‹N%p%‰–pC%ND%žžž:p^”:’p&^,”:’p,^,”:’p,^,¥N¡p,  $€@F%€@G%€@H%€@ÅC £Y¿üM»ï“tµðÄU K/Ž> £Y×7XÃ×7XÓtµð“tµð“tµð¤á±˜¤á±˜– K]CloseJ]SendI]Say:mdI%€@L%€@M%€@N%€@] €O%€@S%€Š[u;Q @€Q%€@UT%C«¶EYì,€i&7–i,@YpYì%¥iMJ6bQ ,WœÄ%6bQ ,,?†–W,8P“,8W•P“,xWUQ YPXi%ê–i,XpXì:iJ¥i¹UQ X,MT%6HQ ,i%C–i,@i6OQ $¥i  U%€@W%€@X%€@Y%€@g;_-R ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[%ôL¡h¬—6ZŠ M± ZŠ M± Z– @]about@€Z%€@]%€@^%€@_%€@`%€@a%€@b%€@c;L.S ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿWd%=ќﬗ6Z,£¤Œ„£,£¤Š÷t××7Xà yJ„ EyE¾×7XÃ÷t× yJ„ EyE¾“tµð“tµð“tµð×7XÃ÷tדtµð“tµð“tµð×7XÃ÷t× yJ„ EyE¾×7XÃ÷t× yJ„ EyE¾×7XÃ÷t× yJ„ EyE¾,£¤Œ„£‹|.XÚ yJ„×7XÃ×7XÃ×7XÃ×7XÃ×7Xà yJ„×7Xà EyE¾×7Xà EyE¾×7Xà EyE¾ EyE¾×7XÃ×7Xà yJ„ yJ„ EyE¾,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£,£¤Œ„£,£¤Œ„£,£¤Œ„£‹|.XÚ‹|.XÚ yJ„ªK4èž yJ„,£¤Œ„£,£¤Œ„£ yJ„ªK4èž EyE¾,£¤Œ„£ EyE¾ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáÅ,£¤Œ„£ªáÅ,£¤u¦9,£¤u¦9‹|.XÚªáÅ,£¤Œ„£ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9“tµð“tµð“tµð yJ„×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ×7XÖ @] accounttypes];I F$Ah%6bI %&6bI %%6HI #Eg&6HI ‰«Íï,6HI þܺ˜,6HI vT2  a;€@[;@/T ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzg%#Ȭ—6Z×7XÊ M± Z“tµð÷t׊ M± Z“tµð×7XÊ M± Z“tµð÷t× yJ„ EyE¾Š M± Z“tµð×7XÃ÷t× yJ„ EyE¾×7Xà yJ„ yJ„“tµð yJ„ EyE¾×7Xà yJ„ yJ„“tµð yJ„ EyE¾×7XÊ M± Z“tµðŠ M± Z yJ„ªK4èž EyE¾×7Xà yJ„ªK4èž,£¤Œ„£×7Xà yJ„¶yŸ&. yJ„×7XÃ×7Xà yJ„ªK4èž,£¤Œ„£×7XÊ M± Z“tµðŠ M± Z yJ„ªK4èž EyE¾×7Xà yJ„ªK4èž,£¤Œ„£×7Xà yJ„¶yŸ&. yJ„×7XÃ×7Xà yJ„ªK4èž,£¤Œ„£¨)æ›ü‹|.XÚ yJ„ EyE¾ EyE¾ EyE¾ EyE¾ EyE¾ yJ„ EyE¾ EyE¾ EyE¾ EyE¾ EyE¾,£¤Œ„£‹|.XÚ[•’[,£¤Œ„£‹|.XÚ,£¤u¦9[•’[,£¤Œ„£,£¤Š÷t×[•’[,£¤Œ„£‹|.XÚ[•’[,£¤Œ„£‹|.XÚ“tµð,£¤Œ„£“tµð,£¤Œ„£,£¤Œ„£,£¤Œ„£‹|.XÚ,£¤Œ„£[•’[[•’[[•’[[•’[,£¤Œ„£[•’[[•’[[•’[[•’[,£¤u¦9[•’[[•’[[•’[[•’[ yJ„ªK4èž yJ„,£¤Œ„£ yJ„ªK4èž EyE¾×7Xà yJ„ªK4èž,£¤Œ„£×7Xà yJ„ªK4èž yJ„,£¤Œ„£ yJ„ªK4èž EyE¾×7Xà yJ„ªK4èž,£¤Œ„£×7Xà yJ„×7XÃ×7XÃ×7XÃ×7Xà yJ„ EyE¾ yJ„ªK4èž yJ„,£¤Œ„£,£¤Œ„£ yJ„ªK4èž EyE¾,£¤Œ„£ EyE¾×7XÃ,£¤Œ„£ªáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9“tµð“tµð“tµð“tµð[•’[[•’[[•’[ yJ„ yJ„ yJ„×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ×7XË|.XÚ‹|.XÚ×7XÃ×7XÃ[•’[,£¤u¦9– @= bancontrolY;~/U ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuh%Ýçý⬗6Z,£¤Œ„£÷t× yJ„ yJ„¶yŸ&. yJ„ yJ„ yJ„¶yŸ&. yJ„ yJ„ yJ„ªK4èž EyE¾ yJ„ EyE¾ EyE¾ yJ„ EyE¾ yJ„ yJ„¶yŸ&. yJ„ EyE¾ yJ„¶yŸ&. yJ„ yJ„ yJ„ yJ„ªK4èž EyE¾ yJ„ EyE¾ EyE¾ yJ„ EyE¾ yJ„ yJ„¶yŸ&. yJ„ EyE¾ yJ„¶yŸ&. yJ„ yJ„ EyE¾ EyE¾ EyE¾ EyE¾ EyE¾÷t׋|.XÚ‹|.XÚ½_ã‰4Š M± Z[•’[Š M± Z[•’[Š M± Z[•’[,£¤Š÷t×½_ã‰4Š M± Z,£¤Œ„£Š M± Z[•’[½_ã‰4Š M± Z,£¤Œ„£ yJ„ EyE¾ EyE¾Š M± Z,£¤Œ„£ EyE¾ EyE¾Š M± Z[•’[Š M± Z[•’[ÌzÃZÌzÃZ,£¤Œ„£,£¤Œ„£½_ã‰4 yJ„ EyE¾ EyE¾ yJ„ªK4èž EyE¾ EyE¾ EyE¾ EyE¾ EyE¾ yJ„ªK4èž yJ„ yJ„ yJ„¶yŸ&. yJ„,£¤Œ„£Š M± Z yJ„ yJ„ªK4èž EyE¾ EyE¾ EyE¾ EyE¾ EyE¾ yJ„ªK4èž[•’[,£¤Œ„£[•’[,£¤Œ„£[•’[,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£,£¤Œ„£,£¤Œ„£Š M± Z,£¤Œ„£ yJ„ªK4èž EyE¾ EyE¾ yJ„ªK4èž,£¤Œ„£,£¤Œ„£Š M± Z,£¤Œ„£Š M± Z½_ã‰4,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9ªáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ[•’[[•’[[•’[×7Xà yJ„ yJ„½_ã‰4[•’[[•’[×7XÃÌzÃZ yJ„– @] bootcontrolX;€ªc%€@j%€@k%€@l%€@i%€ªXdm%€@p%€@i€@S€@S;I0V ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkt%œ¨>´¬—6Z,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤Œ„£‹|.XÚ,£¤,£¤,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤C»P£‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤C»P£‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ,£¤u¦9‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤‹|.XÚ,£¤´M ¡,£¤– @Mclientsettingss%€@Q;l0W ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEv%}O ¦¬—6Z,£¤,£¤,£¤&; ë¡,£¤,£¤Œ„£Š M± Z,£¤Œ„£Š M± Z yJ„ªK4èž EyE¾¤á±˜¤á±˜,£¤Ž> £Y‰Ž”ùvÅ?ï¤á±˜,£¤Ž> £Y‰Ž”ùvÅ?ï¤á±˜,£¤Ž> £Y‰Ž”ùvÅ?ï,£¤Ž> £Y¤á±˜µ;¬º(¤á±˜µ;¬º(¤á±˜µ;¬º(¤á±˜,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«,£¤Ž> £Y'n 6ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9,£¤u¦9ªáÅ,£¤Ž> £Y‰Ž”ªáŪáÅ,£¤Ž> £Y‰Ž”ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ– @] gameinfoH;H1X ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBw%i„ɬ—6Z,£¤Œ„£¤á±˜,£¤Œ„£¨)æ›ü×7XÃ,£¤Ž> £Y,£¤Ž> £Y,£¤Ž> £Y,£¤Ž> £Y,£¤Ž> £Y,£¤,£¤Ž> £Y,£¤Ž> £Y¿üM»ï,£¤Ž> £Y¿üM»ï,£¤Ž> £Y¿üM»ï,£¤Ž> £Y,£¤Ž> £Y¿üM»ï,£¤Ž> £Y,£¤,£¤,£¤Ž> £Y'n 6,£¤Ž> £Y'n 6×7XÃ×7XÃ×7XÃ,£¤’ÈZ«×7XÃ×7XêáÅ,£¤Œ„£ªáŪáŪáŪáÅ,£¤u¦9,£¤u¦9,£¤,£¤ªáÅ,£¤u¦9ªáÅ,£¤Œ„£ªáÅ,£¤Œ„£,£¤Œ„£ªáŪáÅ,£¤Œ„£¤á±˜,£¤u¦9,£¤u¦9¤á±˜¤á±˜,£¤Œ„£,£¤,£¤Œ„£¤á±˜ªáŤ᱘,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤u¦9,£¤×7XÃ,£¤u¦9×7XÃ,£¤u¦9,£¤’ÈZ«,£¤’ÈZ«×7XÖ M*ÈMªÈMªdMªúú^*`]*`\*@@@@] startpageF;j1Y ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUx%=d¨¬—6Z¨)æ›ü×7XÃ÷t×÷t×÷t×÷t×&; ë¡÷^®÷t×&; ë¡÷^®÷t×&; ë¡÷^®÷t×&; ë¡÷^®÷t×&; ë¡÷^®÷t×&; ë¡÷^®÷t×&; ë¡÷^®÷t×&; ë¡÷^®Š M± Z“tµð¨)æ›ü‹|.XÚ÷t×÷t×÷t×,£¤Š÷t×,£¤,£¤&; ë¡,£¤&; ë¡,£¤Ž> £Y'n 6×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ÷^®÷^®,£¤’ÈZ«×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ÷^®×7XÃ÷^®×7XÃ÷^®,£¤‹|.XÚ,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«ªáŪáŪáŪáÅ,£¤u¦9,£¤u¦9,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9“tµð×7XÃ×7XÃ×7XÃ×7XÃ&; ë¡×7XÃ×7XÃ×7XÃ×7XË|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ,£¤Ž> £Y'n 6‹|.XÚ,£¤Ž> £Y'n 6– M*ÈMªÈMªdMªúú@] matchcontrolu%€@D;X2Z ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJz%݆±¬—6Z,£¤Œ„£,£¤Š÷tר)æ›ü×7XÃ,£¤Œ„£¨)æ›ü¨)æ›ü×7XÃ,£¤,£¤&; ë¡,£¤÷t׊ M± Z“tµð÷tדtµð“tµð“tµð‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ÷t×&; ë¡÷^®Š M± Z“tµðŠ M± Z“tµð÷t××7XÃ&; ë¡“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£“tµð,£¤Œ„£,£¤Œ„£×7XÃ,£¤u¦9×7XÃ,£¤u¦9×7XêáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ“tµðªáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9,£¤u¦9“tµð“tµð“tµð“tµð“tµð&; ë¡÷^®÷^®,£¤u¦9÷^®×7XÃ,£¤Ž> £Y'n 6×7XÃ×7XÃ×7XÃ×7XÃ&; ë¡×7XÓtµð“tµð“tµð‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ“tµð– @= matchsetupy%€@{%€@|%€@}%€@A;}2[ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`%,:üF¬—6Z,£¤,£¤,£¤&; ë¡,£¤¨)æ›ü×7XÃ÷t×&; ë¡÷^®÷t×&; ë¡÷^®Š M± Z“tµð÷t×&; ë¡÷^®Š M± Z“tµð÷t׊ M± Z“tµð‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ÷t×÷t׋|.XÚ,£¤Œ„£Š M± Z[•’[‹|.XÚ,£¤Œ„£Š M± Z[•’[,£¤Œ„£÷t×&; ë¡÷^®Š M± Z“tµð,£¤Š÷t×[•’[‹|.XÚ[•’[‹|.XÚ&; ë¡×7XÃ×7XÃ×7XÃ×7XÃ,£¤,£¤“tµð“tµð÷^®‹|.XÚ,£¤’ÈZ«‹|.XÚ,£¤’ÈZ«ªáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ“tµð“tµð[•’[[•’[[•’[[•’[“tµð&; ë¡×7XÃ×7XÃ×7XÃ×7XÃ×7XË|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ[•’[[•’[– @] moderate€~%€@A&€@B&€@C&€@D&€@E&€@F&€@@&€x:a3] ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiI&¥ªE¬—6ZŠ M± Z,£¤Ž> £YªáÅ'n 6ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáÅ'n 6ªáÅ'n 6ªáÅ'n 6ªáÅ'n 6ªáÅ'n 6ªáÅ'n 6ªáÅŠ M± Z,£¤ªáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŵ;¬º(ªáŵ;¬º(ªáŵ;¬º(ªáŵ;¬º(ªáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáŵ;¬º(ªáŵ;¬º(ªáŵ;¬º(ªáŪáŪáÅ,£¤u¦9ªáŵ;¬º(ªáŵ;¬º(ªáŵ;¬º(ªáÅ,£¤u¦9ªáŵ;¬º(ªáŵ;¬º(ªáŵ;¬º(– @= serverinfo€J&€p:}3^ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpL&hÂô¬—6Z“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð‹|.XÚ÷tר)æ›ü×7XÃ,£¤Š÷t×,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£,£¤Œ„£“tµð,£¤Œ„£,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£‹|.XÚ,£¤Œ„£,£¤u¦9,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9,£¤u¦9ªáÅ,£¤u¦9,£¤u¦9ªáÅ,£¤u¦9,£¤u¦9ªáÅ,£¤u¦9,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáÅ“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð– @Mserversettingsn:k4_ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@M&j3a¬—6Z÷t׋|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ‹|.XÚ÷tדtµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð,£¤Š÷t×,£¤Œ„£¨)æ›ü×7XË|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£“tµð,£¤Œ„£,£¤u¦9,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáŪáÅ“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð“tµð– @]serversettings2G&€@N&€@O&€@P&€@Q&€@}€@S&€@€@U&€@X&«V&€@T&€@Z&€@[&€@\&€@_&€n€]&€@`&€@a&€@U:N5` ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqc&ôí‹Ë¬—6Z,£¤Œ„£,£¤Œ„£×7XÃ÷t× yJ„ EyE¾×7XÃ÷t× yJ„ EyE¾Š M± Z[•’[‹|.XÚ‹|.XÚ×7XÃ÷t× yJ„ EyE¾×7XÃ÷t× yJ„ EyE¾Š M± Z[•’[Š M± Z[•’[,£¤Š÷t× yJ„×7XÃ×7Xà EyE¾[•’[[•’[[•’[[•’[ EyE¾[•’[[•’[,£¤Œ„£ EyE¾[•’[[•’[ yJ„×7XÃ×7Xà EyE¾‹|.XÚ‹|.XÚ[•’[‹|.XÚ‹|.XÚ[•’[ EyE¾‹|.XÚ‹|.XÚ[•’[Š M± Z,£¤Œ„£ EyE¾‹|.XÚ,£¤Œ„£‹|.XÚ,£¤Œ„£[•’[ yJ„ yJ„ªK4èž,£¤Œ„£,£¤Œ„£ yJ„ªK4èž EyE¾,£¤Œ„£ EyE¾,£¤Œ„£ yJ„ªK4èž EyE¾,£¤u¦9 EyE¾ yJ„ yJ„ªK4èž,£¤Œ„£,£¤Œ„£Š M± Z,£¤Œ„£ yJ„ªK4èž EyE¾ EyE¾,£¤Œ„£ yJ„ªK4èž EyE¾,£¤u¦9 EyE¾ªáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáŪáÅ,£¤u¦9ªáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9[•’[[•’[[•’[– @]abouts&€`:Y:F]S&Ü(Î Z:@f&@e&sBk”,ppp[sY] sh b&€@g&@h&l&~z#l&-É{!m P:€€@m&€@M:€N:€@p&€@q&€@€@e€i&€@F€G:€9a6a ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrx&y#Z}¬—6Z,£¤Š÷t×,£¤Œ„£,£¤Œ„£b³šÀ.âSÕ,£¤Œ„£.âSÕ.âSÕ.âSÕ.âSÕ,£¤Œ„£.âSÕ.âSÕ.âSÕ,£¤Œ„£.âSÕ,£¤,£¤Œ„£,£¤,£¤½_ã‰4,£¤Œ„£,£¤Œ„£,£¤Œ„£×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ×7XÃ[•’[[•’[,£¤Œ„£,£¤Œ„£,£¤Œ„£,£¤Œ„£‹|.XÚ‹|.XÚ,£¤‹|.XÚ,£¤Œ„£,£¤Œ„£,£¤Œ„£,£¤Œ„£,£¤,£¤,£¤[•’[×7XÃ×7XÃ×7XÃ[•’[,£¤Œ„£½_ã‰4,£¤Œ„£[•’[b³šÀ.âSÕ½_ã‰4[•’[½_ã‰4b³šÀb³šÀ.âSÕ yJ„b³šÀb³šÀ.âSÕ yJ„ yJ„ EyE¾,£¤Œ„£‹|.XÚ yJ„ yJ„¶yŸ&. yJ„b³šÀb³šÀ¶yŸ&.b³šÀ×7XÃ÷t××7XÊ M± Z[•’[½_ã‰4Š M± Z[•’[÷t××7XÃb³šÀ.âSÕŠ M± Z[•’[½_ã‰4Š M± Z[•’[÷t×,£¤,£¤Œ„£.âSÕ.âSÕ.âSÕ yJ„ yJ„.âSÕb³šÀ yJ„ EyE¾,£¤b³šÀ.âSÕ yJ„ªK4èž EyE¾.âSÕ EyE¾,£¤Œ„£.âSÕ,£¤Œ„£.âSÕb³šÀ EyE¾ yJ„b³šÀb³šÀ,£¤ yJ„ªK4èž yJ„,£¤Œ„£,£¤Œ„£b³šÀ,£¤Œ„£ yJ„ªK4èž EyE¾ EyE¾,£¤Œ„£,£¤Œ„£½_ã‰4½_ã‰4,£¤u¦9,£¤Œ„£,£¤Œ„£½_ã‰4,£¤Œ„£ªáŪáŪáŪáŪáŪáŪáŪáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáŪáŪáŪáŪáŪáŪáÅ,£¤Œ„£ªáÅ,£¤u¦9,£¤u¦9ªáÅ,£¤Œ„£ªáÅ,£¤u¦9ªáÅ,£¤u¦9ªáÅ,£¤u¦9,£¤u¦9,£¤u¦9[•’[[•’[½_ã‰4b³šÀ yJ„×7XÃ×7Xý_ã‰4– @] useraccountsN«€@D:€@z&€@u&€@}€Š}&€@&€@x9@«A'@q9€€@E'€@@'€@F'€@H'€@d9€e9@€@L'€@l€@N'€@O'€@R'€€M'€@Y9€S'€@@ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿ ÿ!ÿ$ ÿ(ÿ+ÿ+ÿ+ÿ,ÿ%!ÿ%!ÿ1!ÿ2!ÿ3! ÿ5# ÿ6%ÿ=&ÿ>&ÿ:2ÿ:0ÿ""!ÿ''(ÿ.2:ÿ32+ÿ97-ÿ444ÿ>><ÿ>>>ÿF.ÿL/ÿD8 ÿA:ÿB<ÿX- ÿ^/ ÿQ6 ÿQ5 ÿV1 ÿW6ÿV: ÿY7ÿY8ÿZ8 ÿY? ÿU?ÿa/ÿc4 ÿc<ÿf?ÿh0ÿr!ÿI@ÿSBÿ^A ÿOI&ÿGC4ÿJE2ÿQD'ÿSG%ÿPL;ÿPM?ÿXP&ÿUP:ÿaBÿeAÿeB ÿdCÿbHÿaMÿkBÿiG ÿjOÿkK ÿhQÿmP ÿjQÿqG ÿtK ÿxJ ÿrRÿyUÿ}TÿlX1ÿo[6ÿvW ÿy^*ÿp]9ÿzaÿxk0ÿSYbÿQXdÿhaEÿqqMÿaehÿy|zÿ€^ÿ†[ÿ‡[ÿ‡\ÿ…bÿƒm ÿ‰aÿhÿ`ÿ‡qÿ‰q ÿŽu ÿŽ{ ÿ| ÿ‘dÿ•lÿ“dÿ˜hÿŒg"ÿ€r/ÿ‡p>ÿ}3ÿ’v?ÿŽtCÿ€ ÿ€9ÿª‘ ÿ¾ 4ÿ…‚Sÿ„^ÿ‘…Aÿž•]ÿµ Cÿ¿ªHÿËÃlÿÊÆ|ÿ˜™‘ÿ¨¤‹ÿ¯­šÿ°®¢ÿ¸µ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿU'€@S9xE|  €@Y'€@P'€@['€@Z'€@€@`'€A9€^'€@a'€@€@c'€@f'€@@d'€@\'€@s8@h'€@€@k'€@l'€@j€@p'=@ÿÿÿÿ ÿÿ ÿ ÿÿ ÿÿÿÿÿÿ-ÿ6ÿ(ÿ ÿ6ÿ$3ÿ! ÿ$#ÿ!! ÿ""$ÿ%#!ÿ%$"ÿ%%%ÿ'('ÿ(%"ÿ)("ÿ)(%ÿ,)$ÿ+))ÿ./-ÿ0.)ÿ0/;ÿ30+ÿ30,ÿ51-ÿ74/ÿ210ÿ315ÿ630ÿ542ÿ952ÿ986ÿ=:5ÿ<<3ÿ==:ÿZÿ$|ÿ! Mÿ=;EÿCÿK ÿ]ÿ@4ÿ@>9ÿJ>:ÿfÿjÿzÿn*ÿd ÿj><ÿ|(3ÿz@ÿx3WÿBD:ÿEA=ÿGD?ÿDEEÿDBKÿIFBÿMGDÿHGKÿJHBÿMIFÿOLGÿKKMÿOQMÿOQPÿQB^ÿRQGÿQPMÿURMÿXUOÿRRQÿUPPÿWUPÿZVQÿYYSÿ^[Rÿ^ZTÿ_\WÿZZXÿ\YYÿ^^ZÿB@cÿEClÿIG`ÿNGlÿIHlÿIDyÿNLpÿNIzÿcUPÿa^Yÿg_[ÿe_\ÿzEFÿba[ÿf`Yÿea\ÿkd\ÿ``bÿgceÿgfdÿjf`ÿjhcÿkigÿnhaÿmmfÿeczÿoopÿnlxÿslcÿrmlÿqrlÿvqjÿuvlÿztlÿvvrÿxwzÿzzuÿ{||ÿ72Ÿÿ<<¬ÿ97¿ÿ9;·ÿ9;äÿ:;ïÿ72õÿ59óÿ69úÿ94üÿ=9óÿ88ùÿ@=šÿD6¯ÿC1²ÿB;ÓÿU5ÉÿMGƒÿVQ–ÿb`ƒÿ~ÿ|{Šÿ~~‘ÿgo³ÿpr ÿCBÐÿZSÈÿebÂÿzzÍÿ…ÿÿ›ÿ•ÿžÿ­ÿ¯ÿ´ÿ¹ÿ¾ÿ­':ÿ½6<ÿFÿ­!Dÿº&Oÿ€{uÿ|tÿ€|ÿËÿÎ ÿßÿÚ ÿÈ1ÿÓ8ÿÐ!.ÿÞ)5ÿãÿå ÿêÿì ÿüÿùÿ÷ÿì++ÿñ..ÿú,-ÿ”~…ÿ¢fÿ„‚}ÿ‰†~ÿ…ƒÿ‡„ƒÿ„„„ÿ…ƒÿ‹ˆƒÿŠ…ÿ‰ˆÿŽ‹ÿŽÿ•ÿŒ‹žÿ’Žÿ–†Ÿÿ”‘Œÿ—•‘ÿ——˜ÿ˜›šÿÿ—œ¦ÿšš¦ÿ“”¼ÿ¡›ÿ¡ ›ÿ¡¤œÿ¤¡žÿ§¤žÿ¢£¤ÿ««¬ÿª¤³ÿ«¯´ÿ­ªµÿ¬­¶ÿ°®¬ÿ²°¬ÿ´³­ÿ±²²ÿ±³½ÿ¸µ²ÿ¹¸²ÿ¼¹¶ÿ¼¼·ÿÁ¾¹ÿÀÀºÿüÿÉƾÿÍÉÆÿÌÌÅÿÏÐÈÿÜÚÖÿÝÞÕÿm'€@t'bn'€@u'€@v'€@w'€@r'€@€@z'€@}'@~'@'@F@{'€@x'€@\n@ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ ÿ#ÿ%ÿ&ÿ)ÿ*ÿ!!/ÿ##/ÿ%%4ÿ((7ÿ))9ÿ)):ÿ**<ÿ--=ÿ22?ÿ..@ÿ33Dÿ22Gÿ33Hÿ88Mÿ99Sÿ::Rÿ::Tÿ<;Uÿ<;Vÿ==Qÿ==Uÿ<=Xÿ>>Xÿ@?[ÿAAPÿAAYÿ@@Zÿ@@\ÿBA]ÿBA^ÿCC\ÿBB^ÿDD\ÿDD_ÿFF]ÿFF_ÿLL\ÿCC`ÿDC`ÿDD`ÿFEbÿFF`ÿFFbÿFFdÿHGeÿHGfÿIIbÿHHeÿHHfÿJIdÿJIgÿJJeÿJJfÿLLfÿJIhÿJJiÿKJjÿLKjÿLLiÿLLjÿOOkÿMLlÿNMnÿOOlÿNNnÿPOoÿPOpÿQQkÿQPnÿRRmÿRRnÿVVlÿQPqÿQPrÿRQrÿSRpÿRRrÿSRtÿTSuÿTSvÿTTrÿWVpÿTTtÿUTwÿVUuÿVVtÿWWvÿVTxÿWVxÿXWtÿXVzÿYW|ÿZZsÿYXvÿ]\sÿ^^uÿYYxÿZY{ÿZZzÿYX|ÿZX}ÿ[Yÿ[[|ÿ[Zÿ\[|ÿ_^zÿ\\|ÿ\\~ÿ^^ÿ``uÿccyÿgg{ÿ\Z€ÿ][‚ÿ\\€ÿ]\‚ÿ^\ƒÿ^^€ÿ_^ƒÿ^]„ÿ_^…ÿ_b‡ÿ`^…ÿ`_†ÿ``ÿaa‚ÿbb‚ÿa`…ÿa`†ÿcc…ÿedÿdd…ÿee‡ÿff†ÿb`ˆÿcaŠÿcbŠÿdb‹ÿecŒÿee‹ÿffˆÿedŒÿfdÿgeŽÿgfŽÿhgÿih€ÿkk„ÿnm…ÿhhŠÿkjŠÿjjŒÿkkÿll‹ÿllŽÿgfÿhfÿig’ÿih“ÿjj’ÿki•ÿkj•ÿko–ÿlk‘ÿlj”ÿlj–ÿoo“ÿmk˜ÿnl˜ÿomšÿpn›ÿpnœÿtt‡ÿ~~Žÿqp’ÿqq•ÿss—ÿrpžÿus˜ÿtt™ÿuušÿvv›ÿwwœÿxwÿyxžÿty¡ÿzz ÿxx¤ÿ|z¢ÿ~~£ÿ~~¦ÿ}{¨ÿ††›ÿˆˆ˜ÿŽŽŸÿ€€¥ÿ€€§ÿ‚©ÿ‚‚ªÿƒƒ¬ÿ††®ÿ‰‰¡ÿŽŽ ÿƒŠ³ÿ‡‰²ÿ‰‰²ÿ‰‰´ÿŒŒ·ÿ‰“¼ÿ““¥ÿ””¢ÿ½ÿ——¸ÿ  ³ÿ››Ãÿš¡Åÿ  Æÿ§¶Üÿ³³Àÿ´´Áÿ´´Ãÿ··Áÿ¶¶Ðÿ´³Øÿ¿¿×ÿ¶ÂåÿÁÁØÿÉÉÔÿÕÕÚÿØØÝÿ@(€@€@@ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ)ÿ'!ÿ*#ÿ-#ÿ,&ÿ/&ÿ2&ÿ3,ÿ5,ÿ4* ÿ4,!ÿ4,#ÿ=2%ÿ@3'ÿD4%ÿC7*ÿA:.ÿD:-ÿG>.ÿM7)ÿI>/ÿX?0ÿMC3ÿSC-ÿTE3ÿTH4ÿ[F1ÿfN6ÿqT=ÿuZ?ÿ_TAÿcUBÿdXEÿj[Aÿyb@ÿbFÿ„fFÿ…mGÿ‹mJÿlJÿnLÿ†nQÿqNÿ–wKÿ˜tKÿ˜uNÿœrLÿœuNÿ•xSÿ™qPÿšvRÿwRÿŸvTÿ{Rÿž{Tÿž|Qÿž}Tÿ¢sMÿ¢tKÿ¢wNÿ¦vLÿª{Nÿ }Tÿ©}Tÿ­}Pÿ°Nÿ°PÿšVÿŸ‚Sÿ€aÿ˜†gÿ¢Tÿ¤‚Vÿ£Xÿ¥Yÿ¦…Xÿ«€Uÿ¬€Rÿ¯€Uÿ«…Zÿ¨‡\ÿ®…\ÿ©‰]ÿ®_ÿ²€Qÿ¶„Sÿµ†Yÿ·ˆ[ÿ¹†Tÿ¼‰VÿºˆXÿº‹]ÿ½ŠYÿ¿ŒYÿ¾_ÿ¤Žeÿªˆaÿ­‹cÿ­aÿ²ŒcÿµŽiÿºbÿ¸eÿ¼aÿ±‘cÿµ•fÿ±”kÿµhÿ·—lÿ·›mÿ¹“hÿ¹’mÿ½—mÿ¹›kÿ¹šmÿ¿™oÿ¼iÿ½mÿ½pÿ¾£oÿÀŽ_ÿ‘bÿÅ’`ÿÇ”eÿÃœnÿŘmÿÈ•cÿÏœjÿÏžmÿÁ™pÿÁœsÿÅ™rÿÆžqÿÊœrÿÑžlÿПqÿÁ¡sÿÁ¡vÿäqÿÄ¡sÿÄ¥qÿÅ¥tÿÇ¡xÿÇ©sÿŨvÿÉ sÿÈ¡vÿΡrÿÍ¢vÿË£yÿÊ¥yÿÊ®yÿˬ|ÿÍ­yÿέ}ÿϲzÿÒ¢rÿÖ£qÿÖ¥uÿÒ¥xÿЩ|ÿÒ¬ÿש}ÿÔ¯ÿئwÿÜ©vÿÚ¨xÿв}ÿÓ´~ÿá®|ÿÓ­ÿÒ¬…ÿ×­ÿܯ€ÿÒ±‚ÿÔ·‚ÿÕ¹ƒÿÙº…ÿÚ»†ÿؼ†ÿÛ¼‡ÿÝ»‰ÿß»Œÿá±€ÿà´‡ÿṆÿ赂ÿ빆ÿ칆ÿè¾ÿ쾎ÿÕÅ“ÿáÀŒÿåÂÿéÃÿæÂ’ÿæÆ‘ÿåÆ—ÿãÌšÿéÆ’ÿëÉ‘ÿíÉ“ÿíË™ÿóÂ’ÿòÆ”ÿôÑÿõÄ“ÿöÆ—ÿòÉ›ÿòÍ™ÿõÉ™ÿõÍšÿ÷ÌœÿøÉšÿòÒšÿôÑ™ÿ÷ÑžÿöÔ›ÿ÷ÕœÿùÖžÿö× ÿöÔ¤ÿöØ¡ÿ÷Ü£ÿùÔ¢ÿúÙ ÿøÙ§ÿúÝ¢ÿýÛ¡ÿûæ«ÿüã©ÿüå¨ÿüäªÿþä©ÿþå¬ÿýé­ÿýê°ÿüí°ÿþð°ÿþòµÿE(€@G(€@@ÿÿ ÿ ÿÿÿÿÿ ÿ ÿ!ÿ$ÿ(ÿ& ÿ)"ÿ-#ÿ3&ÿ2)ÿ7+ÿ5+ÿ9,ÿ=1ÿ"""ÿ%%%ÿ)%!ÿ*(&ÿ)))ÿ**-ÿ.-+ÿ---ÿ1,'ÿ7,!ÿ4/+ÿ20.ÿ;1'ÿ81*ÿ?4)ÿ=4.ÿ111ÿ224ÿ433ÿ666ÿ78:ÿ:6;ÿ999ÿ;;<ÿ?<9ÿ===ÿC2ÿ@6*ÿM=-ÿF=6ÿD?;ÿR=&ÿR>*ÿCA?ÿLA5ÿNE<ÿUA+ÿ^F.ÿRC3ÿPF;ÿfO/ÿkN-ÿ`I2ÿbQ?ÿkP2ÿlS:ÿoW>ÿx[6ÿ}\8ÿAABÿDCBÿEEEÿCCLÿHFEÿHHHÿIJLÿNLKÿMMMÿOPRÿQGDÿXOCÿTNRÿ\QGÿZTOÿQQPÿPPTÿUUUÿWWYÿXV_ÿ_XSÿYYYÿZZ\ÿ\[[ÿ\\\ÿ]]aÿ_`cÿ`]\ÿm_Rÿw_Gÿfa^ÿpg^ÿ{gSÿaaaÿbbfÿeeeÿffjÿhgeÿjhgÿnhcÿihhÿhinÿoljÿlllÿjkpÿkmrÿqqqÿvtsÿvvvÿuvyÿwx}ÿyxuÿzyzÿyy~ÿ}yxÿz|ÿ}}ÿ†ˆÿŽh>ÿ‚jNÿ‰hEÿ‰kJÿ™rFÿ™zWÿ‡zÿ“z`ÿ§yGÿ§~Lÿ§Rÿ“sÿ¬ˆ]ÿ³†Tÿ¬ˆbÿ«‹iÿ ˆpÿ¹ÿŸpÿÊ¥}ÿ€ÿ„ÿ†††ÿƒŠÿ„„‰ÿˆ‡‡ÿˆˆˆÿˆ‰ÿŒŒŽÿ‰ŠÿŠŒ‘ÿŒŽ–ÿŽ‘˜ÿŽ–šÿ•Œ†ÿ™ˆÿÿ‘‘”ÿ”””ÿ’”ÿ——™ÿ™˜—ÿ›š›ÿ››œÿŸÿ—š¡ÿ›ž§ÿŸŸ ÿ ©ÿ¨™Œÿ©Ÿ—ÿ ž¤ÿ¾£Šÿ»¥ÿ¢¡¡ÿ¦¦§ÿ¡¤­ÿ¨§§ÿ©¨ªÿ¬««ÿ¯®¯ÿ£§±ÿ¨¬¶ÿ¯¯°ÿ«¯ºÿ®±ºÿ±­«ÿ²±²ÿ²²´ÿµµ·ÿ°´¿ÿµ¶¼ÿ¼¼¾ÿ°µÁÿ·¼Çÿ·¼Éÿ¸½Çÿ»¿Êÿ¿ÀÇÿ¼ÁÍÿϳšÿؽ’ÿÀ±¤ÿÓ»£ÿÁÁÄÿÃÄÆÿÄÄÅÿÆÆÈÿÁÅÑÿÄÉÔÿÆÌÙÿËÌÓÿÉÏÝÿÏÒÝÿÔÈÄÿÑÑÒÿÒÒÔÿÖÖØÿÛÛÛÿÚÙÞÿÒÖãÿÕ×àÿÖÛçÿÝÜàÿÝâîÿßäñÿäÑÄÿáØ×ÿñæÜÿãäçÿäéãÿæêõÿçíùÿéíöÿìîõÿëîøÿññóÿðòõÿó÷ôÿõôóÿõ÷üÿøõõÿûûýÿúýýÿýûøÿýýýÿÿA(€@J8€@J(€@^|€N(€As7€@€w€@@€ïT(€S€@E€F€@p€@[(\(S@^(€_(€`(€E7€b(€@c(€@p€@m€@g(€@€h(€@€@@ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿ"ÿ&ÿ! ÿ$ ÿ) ÿ* ÿ&ÿ##ÿ, ÿ3# ÿ1& ÿ2$ÿ6%ÿ9$ÿ9(ÿ:-ÿ?,ÿ=)ÿ'$ ÿ+&!ÿ0*$ÿ>4$ÿ421ÿ:60ÿ897ÿ998ÿ@'ÿF( ÿL* ÿB-ÿD+ÿL,ÿD1 ÿE1 ÿO8 ÿE2ÿG2ÿL3ÿU7 ÿP8 ÿY0ÿ]: ÿP0ÿR5ÿR:ÿQ8ÿW9ÿT9ÿ];ÿ]9ÿ^>ÿF6!ÿJ3 ÿO;#ÿK:*ÿN>.ÿ@80ÿC>8ÿY?#ÿ]?)ÿ`> ÿj>ÿc;ÿe?ÿg?"ÿ]Aÿ_Aÿ^Gÿ@@<ÿO@2ÿNB6ÿLD:ÿRC3ÿRE6ÿUE4ÿYG6ÿ]J6ÿ_M:ÿcB ÿgB ÿbHÿiF ÿiD ÿoI ÿd@ÿdIÿjKÿkPÿpG ÿpKÿtI ÿtL ÿrGÿqMÿpLÿuNÿrPÿvP ÿ~U ÿuRÿqTÿtYÿ}UÿxUÿWÿcB ÿdG*ÿeI"ÿcO7ÿcO8ÿgP$ÿlP-ÿcQ;ÿhT7ÿsT#ÿuR(ÿt[ ÿxW*ÿrU0ÿuY3ÿ}[:ÿ~e4ÿCCCÿLKDÿLNNÿ^ZOÿ_`ZÿcRAÿhVCÿjXEÿk\Mÿj]PÿncPÿjg_ÿraLÿ|jVÿtmcÿtsjÿ|tgÿzvmÿ}ymÿwwxÿxwpÿ{ztÿ|{xÿ~}ÿ‹W ÿŠXÿŠ^ÿ[ÿ‚Pÿ[ÿƒ\ÿ•cÿ“iÿ”iÿ–mÿšmÿ›s ÿŸy ÿ—rÿg&ÿƒf(ÿ…c=ÿŽt(ÿ–o&ÿ”~.ÿ‘x9ÿ¡rÿ¯|ÿ‹lBÿ€pXÿuIÿ•xVÿ…t`ÿ„zlÿ~fÿŠ{hÿ|iÿ‚{ÿ‰}qÿ™ƒ'ÿ›„%ÿš‰&ÿ¬„ÿ·‡ÿ±Šÿ¹ÿ¢“1ÿ£•3ÿ£˜5ÿ¸˜+ÿ›Rÿ€€yÿŠ†|ÿ•dÿ ‚Oÿ¡‹fÿ¢‰iÿ§oÿ¢’~ÿ©•vÿ²œ{ÿ®§FÿÁ† ÿÂÿÇ‘ÿÍ¥#ÿÆ°=ÿê¾4ÿáÃ6ÿçÇ=ÿôÖ;ÿÛÊMÿÔÇYÿßÛzÿõèRÿïòvÿ‚‚€ÿ†…ˆÿŽˆÿ””Œÿœÿ–—˜ÿ—˜’ÿ™——ÿ˜˜šÿŸ¦´ÿ§—„ÿ­¨Ÿÿ¢£¥ÿ¬ª§ÿ¯¸Íÿ¸­ÿÀÀ¿ÿÊþÿââ”ÿÕÕÙÿL(€@Zj(€@hl(€@6Ó/Ó%t*'." ," )" (" +*3335*åååÿ"ÿ÷¹¢Û t@x…›® ž€qPnŽ°¾Ãú¡yl\z›ª¹·ÃÊÑÅ–¡Œyl•€¡¾Ä¥½¾½“œš€q ›ˆ• ½¿™ÌÑ™~–©—{y¤‘“šˆ•¡¼µ°Í´¡ƒ˜›•€‹±“«¦ˆ•¥ºŸŽ¥”šœ¢œ—ˆ‹–ƒ˜]«€‡œ«Ÿ‡ŽŽ”˜Ÿ‡‡€ ­nœ‘{}ƒŽ£¥~zŽ¤Žƒš¦¡p}ƒƒƒ~~”~zzmsŽ€}z˜¢€z‘semmmmz‡we‚¯š¯®µ†}gr‡cceemz~~mmsµåÒÒ»¸™‡U‚}j__emwsjmsÒÒÌǵ¸¡˜V‚iic_cmjcem~ºÌÒÇ»©¾¥†kŽ}s_cjccem†ÇÒÒÅż¯Žhk|‘ˆjcj___jzÐÐ×о¥pI_eq}ˆsccc__j›½¦{|}eSely…jbicb]]]PPUaLp_eq†]PV]]VH=F[WYemŽYHHPVO=;FLSWH==FO;;5;;5;; n(€@p(€@q(€@r(€@m(6Ó/Ó%t*'." ," )" (" +*5555*åååÿ"¹¸¸¢Û y@$5KG;'Mx…›® ž€qPnŽ°¾Ãú¡yl\5z›ª¹·ÃÊÑÅ–¡Œyl•?8€¡¾Ä¥½¾½“œš€q ›2/ˆ• ½¿™ÌÑ™~–©—{y¤‘ “šˆ•¡¼µ°Í´¡ƒ˜›•€‹±“«"G¦ˆ•¥ºŸŽ¥”šœ¢œ—ˆ‹–ƒ˜]«€‡œ«Ÿ‡ŽŽ”˜Ÿ‡‡€ ­n œ‘{}ƒŽ£¥~zŽ¤Žƒš¦¡p+}ƒƒƒ~~”~zzmsŽ€}z˜¢/€z‘semmmmz‡we‚¯š¯®µ†}++gr‡cceemz~~mmsµåÒÒ»¸™‡*(U‚}j__emwsjmsÒÒÌǵ¸¡˜(V‚iic_cmjcem~ºÌÒÇ»©¾¥†OkŽ}s_cjccem†ÇÒÒÅż¯Žh+k|‘ˆjcj___jzÐÐ×о¥pI _eq}ˆsccc__j›½¦{|}eS6ely…jbicb]]]PPUaL(Up_eq†]PV]]VH=F[?dWYemŽYHHPVO=;FM I?LSWH==FO;;5*;/;;5;;8 s(€@Sv(€@[ 6Ó/Ó%{(*'." ," )" (" +*0335*þäòÿ"ª!¶¢Û  }@CÍÙÒ¥a6)Bh‚ðòúúܦi^53t§öÛøûþüàÕq^5maœ³ùØóòóßæása6¬“kfj£Ú÷ãÿþâ\àŸ›_^¯ª—˜’fjèõñïýôë…äËag½©¸­fkz¤²çˆî‰åæíÓÌfg”w«7v®aop‘¡Ô]‡ˆˆ‰ƒçpcn{¬»DžŽ_blyêé]\][ˆìxkwn¢±·‹rUlw…\\‰\[][XYy{UVζŠuV„YWXXXX[‡ZW} Ñ×ݼ¿šŒ?S€OOWWX[\\XXYÖÊÇÇÂÀµ™H}URMMWXZYRXYÞÇÇÆÿÀ·¨I}QQOMOXROWX\ÅÆÇôÁ°•PxUYMOROOWX†ÃÇÇÄľ¹–@A`Ž|RORMMMRVÈÈÉÈÁ°e8%>26b|YOOOMMRϺ±Td2,e25^RNQONLLKGGH4$8/26mÐLGILLIE &.*120–JEEGIF &$,=E &F @ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ'ÿ3ÿ ÿ$2ÿ!*ÿ*!ÿ",ÿ?/ÿ$,)ÿ&'9ÿ.9 ÿ+0<ÿ0)&ÿ%*Gÿ'-Rÿ-3\ÿ66@ÿ07\ÿ-4fÿ19iÿ29vÿ5Dÿ3@ÿ?Lÿ>Sÿ?M;ÿ;CyÿK=ÿJLÿCVÿK]ÿ@P"ÿEZ$ÿYL ÿS_<ÿMgÿYvÿIa ÿSm#ÿXa%ÿRg<ÿYu%ÿ\z%ÿwV#ÿhv+ÿva8ÿFGPÿDPIÿ[TEÿIZdÿVcPÿVlcÿckWÿdnhÿiplÿe}{ÿ~iaÿ;DˆÿAL†ÿAK“ÿHS†ÿGR–ÿQW‚ÿPZ•ÿLX¥ÿMX´ÿQ]¬ÿQ]·ÿ\j˜ÿXf¬ÿXeºÿ`sžÿs}ˆÿk}ªÿgxºÿR^ÀÿYgÅÿ`oÃÿcsÇÿdtÓÿizÓÿk|Úÿcƒ%ÿd…*ÿi…*ÿhŠ+ÿg†7ÿn’6ÿp…+ÿ{„3ÿq“-ÿt–0ÿz—6ÿz™?ÿ|¡3ÿmˆAÿmŒNÿm†]ÿl‰Qÿl‰Yÿn‘Dÿx•Cÿ”Zÿ{™Rÿj€kÿo†xÿ}„eÿq‰rÿy–fÿx–zÿ{ Cÿoƒ“ÿsŒ˜ÿn„£ÿr„¦ÿs†±ÿs†¿ÿ{Ž°ÿ~‘ ÿž¢ÿ|’¿ÿm€Óÿw‚Ìÿq‚ÝÿxŠãÿ{‘åÿªu&ÿˆjOÿtCÿyzÿ„™:ÿŸ—9ÿ†¥7ÿ‚¨<ÿ‰¤8ÿ¬š;ÿ‹]ÿ“†Aÿ’‘Cÿ’Eÿ”–Xÿœ‚fÿš€pÿ¡Oÿ‰¬Bÿƒ¥Vÿ™ ^ÿ•³Kÿœ¹Kÿ‘·Yÿ‹¢gÿ‡©xÿŒ¯uÿ“¥dÿ”§|ÿ²mÿ¨—Bÿ¸ŠJÿ°›Gÿ¸‘@ÿ¥¤Cÿ¥¬Eÿ¥²Bÿ±¥Cÿ¸¤Sÿ´¦fÿ£ÆCÿ´Ælÿßš8ÿÅ@ÿ˘GÿǘQÿɦNÿƬZÿɾ]ÿЦMÿΦsÿÀ°lÿÞµgÿæ¨Fÿå­Pÿà·Zÿð­EÿúºKÿôºUÿà±nÿþÀNÿüÉZÿøÈeÿùÑqÿûäxÿ€›ÿƒœœÿ‘ŸÿŸ•œÿ˜‘ÿ€„§ÿ’š´ÿš£Œÿ‰¡¦ÿ‡¤±ÿ—¡¹ÿ®¹•ÿ´¯œÿ«¡¸ÿ©°£ÿ³¿½ÿ¾´»ÿ¾¸¸ÿ‹ÅÿÎÿŠÌÿƒ“Ûÿƒ›Òÿˆ—Ûÿ––Ãÿ€•çÿ„—èÿƒ™òÿ‰¢Åÿ‡¢Öÿ‹§ÔÿŠ¤ßÿ• Ñÿ‰¤åÿˆ¡ðÿ•£âÿŸ®íÿ«°Äÿ®´Ñÿ¾¾Áÿº¼Íÿ¥³èÿ°Á¬ÿÀ·ªÿľ­ÿÓ¿¨ÿϧÿÃÃÅÿÊÊÍÿÂÄÔÿËÍ×ÿÑÑÓÿÆÌâÿx(€@|(€@}(€@~(€@y(6Ó/Ó%{(*'." ," )" (" +*2555*þäòÿ"áÈ@´¢Û 9†@+#";CÍÙÒ¥a6) Bh‚ðòúúܦi^53 t§öÛøûþüàÕq^5'maœ³ùØóòóßæása6¬“kfj£Ú÷ãÿþâ\àŸ›_^¯ª—˜’fjèõñïýôë…äËag½©¸:­fkz¤²çˆî‰åæíÓÌfg”w«7v®aop‘¡Ô]‡ˆˆ‰ƒçpcn{¬»D žŽ_blyêé]\][ˆìxkwn¢±·‹ rUlw…\\‰\[][XYy{UVζŠ uV„YWXXXX[‡ZW} Ñ×ݼ¿šŒ?S€OOWWX[\\XXYÖÊÇÇÂÀµ™H}URMMWXZYRXYÞÇÇÆÿÀ·¨ I}QQOMOXROWX\ÅÆÇôÁ°• FPxUYMOROOWX†ÃÇÇÄľ¹–@A`Ž|RORMMMRVÈÈÉÈÁ°e8%>26b|YOOOMMRϺ±Td2, e25^RNQONLLKGGH4$<8/26mÐLGILLIE &.!9*120–JEEGIF &- (!$,=E &F   (€@aB)€@D)€@E)€@c@ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ"""ÿ"##ÿ###ÿ%%%ÿ&&&ÿ'''ÿ(((ÿ)))ÿ***ÿ+++ÿ,,,ÿ---ÿ...ÿ000ÿ111ÿ222ÿ333ÿ444ÿ776ÿ777ÿ787ÿ888ÿ999ÿ::9ÿ:::ÿ;=<ÿ===ÿPd?ÿBBBÿCCBÿCCCÿCDCÿDDCÿDEDÿFFFÿGGFÿHHGÿHHHÿIJHÿJJIÿJLKÿKLLÿNPOÿPPOÿPPPÿQQQÿRRRÿSSSÿUUSÿUUUÿVVVÿWWWÿYYYÿZZXÿZ[[ÿ[[Zÿ\\\ÿ]_^ÿ^^^ÿ^_^ÿ___ÿaaaÿbbbÿcccÿbdcÿddcÿeedÿeeeÿeefÿffeÿiihÿiiiÿjjiÿjjjÿkkjÿkkkÿlllÿmmlÿmmmÿmmnÿnnnÿoooÿylkÿppoÿv~oÿpppÿqqqÿrrrÿsssÿtttÿuutÿuuuÿvvuÿwwuÿ{{{ÿ{{|ÿ|||ÿ}}}ÿ~~}ÿ~~~ÿFc‹ÿmw‘ÿ‘1/ÿ¦{Rÿ»~Hÿ¡xwÿ€ÿÿ‚‚‚ÿ‚ƒƒÿƒƒ‚ÿƒƒƒÿ„„„ÿ††…ÿ†††ÿ‡‡†ÿ‡‡‡ÿ‡‡ˆÿ‡‡‰ÿ‡ˆˆÿˆˆ†ÿˆˆ‡ÿ‰ˆ‡ÿˆˆˆÿ‰‰‰ÿŠŠ‰ÿ‹‹‰ÿŒŒ‹ÿŒ‹ÿŒŒÿŒŽŽÿŽŽŒÿŽŽÿÿŽŽŽÿŽÿÿÿÿ““‘ÿ’’’ÿ“““ÿ””“ÿ”””ÿ••”ÿ–––ÿ——–ÿ˜˜–ÿ˜˜—ÿ™”ÿ˜˜˜ÿ››™ÿšššÿ›››ÿœœšÿ›ÿœœœÿžžÿžžžÿŸŸŸÿŸŸ ÿž«¼ÿ£˜’ÿ®›‘ÿ¹™—ÿ  žÿ¡¡Ÿÿ§¢ÿ£££ÿ££¤ÿ¤¤¢ÿ¤¤£ÿ¦§¢ÿ¥¥¥ÿ¦¦¤ÿ«¥ ÿ­§¦ÿªª§ÿ¨¨©ÿ©©¨ÿªª©ÿ««©ÿ¬¬®ÿ®®¬ÿ®®®ÿ®®°ÿ¬³¾ÿ±±¯ÿ½³©ÿ°±³ÿ²²°ÿ²²³ÿ¶¶´ÿ··µÿ¿¿¾ÿÀ¾ÿÃÃÁÿÅÅÃÿÆÆÅÿÇÇÅÿÇÇÆÿËËÉÿÕÔÓÿÖÖÔÿ××ÕÿÚÙØÿÝÝÛÿÞÞÜÿááßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_ ´F)€@J)€@/Ó%t*'."@,"@)"@("@+*²²²"Þ1®¢Û ýš@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóððððððððððððððððððððððððððððððððððððððððððððððððððððððððððððððððîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææääääääääääääääääääääääääääääääääääääääääääääääääääääääääääääääääááááááááááááááááááááááááááááááááááááááááááááááááááááááááááááááááßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸšššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššš˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••““““““““““““““““““““““““““““““““““““““““““““““““““““““““““““““““ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽ‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‰‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuussssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd@@MK)€@N)€@jO)€@Q)€@R)€@F6€N6€@U)€@D€@~€@Z)€@\)€@I€Š”j @_)€@b)€U5€@a)€@c)€@d)€@C5€@v)[ €g)@h)~4@Q€R€m)@n)q4ïp4€E€s)€@@Q €@h4€w)@@*j€z)ï{)@X4})€@~)€@)€@h€@V*@F*€@C*±D*E*R4G*€@H*€@€@N4fK*€x€M*€@O*€@€P€@B@S*€@| €@U*€|3€ŠšZ*€W*{*@R*€@€`*€ÐH\*gË¿q+)wa *a g\*]*^* ]*€^*€€ó[*fqÜØsVq 4$Ñfº¾iT <$T ÿÿófa*b* d*€b*€€m3€@g*€w €k*€i*€@j*€m*@l*€@n*€@`3@u*€@_\3QUÒ  €p*€@nt*€&y €S3€@n€@O3€@t€z*€&d€B,B3€@p €*€y €@A+€Š~2f€o€E+€€@G+€f2€@o€@J+€K+€M+€€N+€O+€P+€R+€E@S+€T+€V+€@2€@€Z P€Z+€` €€c«€«z0€_+«a+«b+«e+€@q €@Y€@€E@gb€T0€j+€h€@€[@v@p+HGw.ÃïB WrÙ‚-~  wB *[.B Ó¶w[*t|[@G-~ '¶¶[a/!G[.>[ wG-~ w[*Ö-~ B B oë-~ [í* q+@s+e/€It+@v+r+F"fý±{WP—~W,%_€W~W,BW’~W,&[_WVWré‚-` wV*J.>VÓÑ‚wJ*|J@_-`'æVVop-`y J"FE[B©VWG FE©wV*y .VÓ{[y @[y y y  u@a€Žw+@P@{+}+€@O/€~+€]€t@x €x+@M¢Èßv WrÖ‚-|  wv *G.v Ói|G@@-| 'Ga/!G-| .>G M@¾-| W | v 'Óv v o-|  D/@@B/~.KVw ' €T,€@GD,K,2Éius Wrsws *[w.s Ó*.s Ó 2K,s s o s €G,l.aeÅ \J,N,HÜ'ur Wrswr *[w.r Ó*.r Ó HN,r r o r €o.€@_M,Q,7ó!p Wr}wp *ew.p Ó*.p Ó7Q,R,S,p p o R,€S,€p @m.€@o*P,V,Q$zo Wrxwo *`w.o Ó*.o ÓQV,W,o o o W,€@o @l€].N.b›¢ €Y,H.\ªP A.€@c\,€@F.{-aÄ  _,y-^ÑÙ! }-€@`,u-_Þ°# C€˜f,@h-€g,@h,@i,@R s€V@m,€Œn,€x€k,€@[@p,€q,€t,€€w,@l€y,@y€z,@{,@,€o€@x+O-},"«ÛîÑ{r #rqp΂-o –F uÄ‚F Ha/!G!|.>F H@q-o'D.>F H"},oqpË¥F *ææ–u, ` oe*²` ?%` CD.} 4ØoÀ@l¯} º«?,À@` uHD{qD@qDD¸B` Ì—u%¸B€@¸l®` €@¥uD d €D-€˜@-€n-A-o[.' D€N-€€E-o>F-c-|,nù:Îsd <]‚-m wd *Frd Ñ|,-m'Zd d @ o-md q* z€D,~,nMí› Ç¾‚-G  –_ uE|_ H@n-G '……_ Ha/!G-G .>_ H Mn´-G kÿ_ HÉ»¥_ -G  H-€ @_ €X-€@r+U-vw:Ÿ—‚rz* –c uN|c H@vzc Hc Ha/!Gz.>c H wv¥c z J,K-x,2Ó$<y%:–yuyH 2x,¥y €Q-€M,P-v,H#ý&<l%:–lulH Hv,¥l R-€Œ_*W-T-qoÔ P,S-r,78E*F[%D–[u[H7r,q,p,¥[ È]-s,`_· ÷s,t, \-€I-e,S-Òsi <]‚-h wi *Fši _ e,-h'Zi i @ o-hi q* L-€˜U,V-o,QIÆ,AV%?–VuVHQo,k,¥V g-Õ^-l,d>* þx¯º?, C B8²C ¾C ¾@-w'} ÎxC ‘-wk#\C k¾ AÈk\kÿ} É©kÿÕl,m,n, Hd-:#X£ë} .åØ å¯º?,¯¾?,þk.ÒØ Ò¯º?,@A¯¾?,k-ÿ'B«@À@lÀ@ zÓÉA$,J a- , Nexgen Server Controller'$¨¨¨version€U q,buildS E# $Copyright © 2006-2008 Zeropoint productions $d.scheerens@gmail.com $Development'$Daan "Defrost" Scheerens $Credits and thanks to'$Mickaël "ATHoS" DEHEZ $Matthew "MSuLL" Sullivan $David "The_Dave" Schwartzstein $Zohar "SuB" Zada $ Z-€6Ó%b-*'.",")"("+*iy5*ýþÿÿ"ñù•¢Ü l¶@€Vdfwƒ‰¦õº¼úȽú¼Èö´­ô¯‚pnbZp‡ŽƒƒØÂÿ¿¬¾¿¿¿¿ŽnKNbånZXPXåXSSXZåXSKNKßIDDDKåXSSSSßKICAA""A"A"AAAQY\[EEE"RRRRUQWW[W\))****BBBDDV[[enunuwww|~|~ƒŒ……ŽŽŽddofpppp}}~}„~„~~}}pwpppfoÚæáæáßáááááá¿ÐÐÐÀîx~xwwwuGFFFFFFDFDDDDBBBBBBB***)*)DLKKKKKGKGGGGGEDDFSKJHeŒ§À­åÿæñÿÝóÿôñÿåíÿáåÜ×…Éßãáܹ·áßØØÃ{ÃÃÃØØØŽ·¬¬¬¿uy…£õ°¤££ó¬€£££ô©€zyíeyTXcñŽzŽñzUcUQQQGEUQGGEB‹\\EGD"QURUVBW[[[eBFLV[gxKŒŽŽ·ÃѾÑÑÑÑߎßÑÑÑþЮ¬Ž§¹Ãߧ®®¹ÂÚ§ÐÚàáááÐáåáàà¦ÚÏÀ¹®¦~àæææá®æáááááŽÐÐÃÃߎŽ¬¬ÀuwuwŒZeneeewLVVVRVTLLRLGgLgŽŒŒvŽŒx‹vWŒg[UKD[NJ5.f¬®Àá¬åÿæñÿÝñÿññÿååÿåãåå¨ãåññåã×ñæãßßzØßßãåå¥ØÁ¿®ÃŽ‡ƒ¨°ú°Ç«©©ô°‡©°»ý°££‡|íunSYyñŽyŽíxUcUQQGQEQQGGEA‹\\EG"BRUTUWAW[eggDELR[euDŒŒ·ÃìßÑÑÑÑwßÑÐÐЬо®¹¹Àà®®¹ÀÚ~®àÚààæÏááààà~Ðй®®ƒßæææææáæáááŽÐÑÐÃÑŒ¬¾uufw|Teee[[uLVVTRTLLLRLFWReŒŒxgŒ‹|vxUŒ\WRKCbJ.++wÀÀÚæ¹æÿññÿäæÿóñÿåíÿæíæóÛóôöõõññõôæåãßååññôÉåÞØÂØ£¥¨ÇâÿÝâÉ°°õǨÇÛâý⺩£‡íynXUyñŽcŽízT\SQQGQAUGGGEA‹\\EGQTUUWWBU\eguBDLR[guGŒƒŒ·ÃìÑÑÑÑÑ~ßÑÐÐÐÀÀ¹ÀŽÐÂÐߥ¸®¹¹ÐÏÚÚààáÏáàààÚ…ÐϹ®§§{áææææ¬æáåáááŽÐÐÐÃÃØ~¬¬Žufuu|Leee[[eKVTTLTLLLLLFWL[ŒŒvgŒxxvuQx\VLI*N.+ ƒÐáæñÚñÿññÿÉâÿññÿÞÓÿíñóúïÿÿÿÿýýÿùúôí嬰åñõýÿíååÜØΫ«ºâñÿíÕâÉÇöǯÇäóÿïÛ¯££íyXXXyíYnzzíYITQQKE"AGKGEAA\\\IARUUTD"D[geT))LRVefDgƒ|®ÀŽÃÐÃоwÚÃÂÐÀ~ÂÐÂÐŽÂÚÚØ­®®¹ÏÚoÐÙÚÚà„ÚàÚÚÙ~®¹®®§Žoæææ徧ÚáááჅÂÂÐÃ~{~«¥{wKnefdKFZZWVVDFTLLKKLKKB*KK[|gKTxxugKK\[TKC(H+#…¶½ÙãñåÿñíýñÉöäåýíÉõíñõÿÿÿÿÿÿÿÿÿÿÿõåÝåÎÞíÿÿÿÿýÛä×°»É´ýÿÿÿýìÆÉóºÛÕýÿÿÿüÛ¨€ínNDSNåybyyßSYKDID"GECAI"DKE\[)DBAKFA"VRKLKBBFFNK.),JNZ‚d{Á®Ž®z¥ÐwÀ½¬~ƒ{~ƒ…ƒ§ŽŠ…­}}­®¸¸…„§ÏŠÙÏ}¸×ÏŠ®„~¦§‰}~~§à®¹áá®ÚŽ®®p··~Ž~ƒ~Ž~ƒDFZVKVVDKPFKL.BFKDDKD*DKD)TLK|eTuXFTYKIKC+! ¤æóöúóúÿíäúääýÈÓúääúÕÓïÿÿÿÿÿÿÿÿÿÿÿïåâÝãíóÿÿÿÿÿâ⯫ºÆÓÿÿÿÿÿìâÅóÊÕìÿÿÿÿÿÕº¨ÜPNIHrãynbXXã[SKDC"EDDDA)DCIICL"CC*,"JHKLKNDY)DD.# '44¥¥…···¥§¬„¶ƒ¦®®¶}·¸¹½¤¦d}oŠ­¸½¸„¸ÏÏÏŠŠ¦¤­­¤}}o‚oÏÞ«¹ÏÂ×Ø×}¤«¬«¥ƒ~Šnƒdn{{~XVJKNLPLDHINKKK*.DD.)FIFFDFLn[ZTKFXVLKID., ²ôúÿÿýýÿìÖõâÖõ›µóÕÓõâÒüÿÿÿÿÿÿÿÿýÿÿóäËÉÓäüÿÿüÿÿóƯ¯²³ïÿýøÿÿïè´óÊÍõÿÿóÿÿ믗ÇPNP,bÜbbXXNÜSNIHC)DCCCCD)IHIC.! DJKN.Z).F( #334‡¥P¬«««¦…«3'M^s„_­­­¶²qŠ‚„ª¦­²²d²­²²²Pª¦ŠŠ›…_}om²Ï¤¸¶¶¶¶½Z¤‰‰‰Š~tƒ‚mZtLoorKPFJKJKMDFJFJHK,.DD).FD5D5DZXLNLSJLKPIH).( ÉýÿÿÿÿÿõëâóÊÊó´ñµÆïÓžëÿÿÿÿÿüÿÿÿÿÿÕÍÆ´ÆÓëÿÿýÿÿÕ¯¨—¨³ÿÿýÿÿÕÍÌðÅÍèÿÿÿÿÿÄ—•°OJM.NÇ_PPPMÇNIHC,))H)))!C!D.  ,HFJ.P(,D+ &'&3‚€m¤£¤£¤ts&''''3#4Mhˆ²}s‚Š›¤ª¦Pªªª¦ª_ˆˆ„}PoiP¦¸†«ªª¦ª­Pˆ‚‚}‚mtttmPpNZmZ9K.JHJHF.HHFHDJ!...)(F..*.DNLNKKJHKNHFD(- ÝýÿÿÿÿÿõìÕðŵìÊ–ì³³ìÅšÍòÿÿÿøîüÿÿÿïÒ´¯¯¯ÅÅìÿÿÿìÄ—–†—–™ëÿÿÿîÒÌÅïÌÅÍîÿÿÿÕ™•¨M9H59ÆPNNNH°MHHC(().)(!)(.,$HHJ,P!,.+ &&&0trP‰‰‰‰‰‰i&0'0''31'1kPs„ˆˆˆŠ›OŠ›Š—Šis}sOdPOŠ²t¤›¤¤›¤_ttstsZ_osNOPJPPZ9H,HH5HH-.5...H!,,,).,,,,,NJIJJF.NHJH,(( âÿÿÿÿÿÿýëÕÖ´žäÄ–Õ±Öž–šÌÍøéÖèÕéøëÅ™—ˆ›£›ÅðÊÄ——ia†•™ ïÌÌÅ¡ÌòÌÌÌÅ¡ðÄ™•k_¨55+5P¯NMJ5H£JH.!!(),!(!!C(  .(((N!+,$  %&&OJP€‚†‚†‰O�''11114li}‚‚ˆ—_is—ˆˆ9ssssiMOPM‰ªimt‰ˆ„PiOmmmPqPOmMMMJJPN.,,(-5.5)!(...((!(,,,+(,HJF.(.5JD,(, ÝõÿÿÿÿÿóÖÍì–Ö±žì›™â›ž—™ÅëÅÔÒÌÅìÅů›€™³ë™——†_kik•Õ ÄÅîÿÿÿéÅÄÄë–•kP]¯M55H]¯P9HNP¨J5)! !!,!)(!,!  (.J5.!!((    q††tt€‡‰;'0# #&#&&‚‚ˆii}—ssˆˆMoiiPP9JMmŠ—P}imttPP_mmssNJPMPPJM..5H+.(,5H!!.!+.,(!,,!((!HF5,.,NHHD.,., ÉýÿÿÿÿÿóÖÊì–žäž³äÄ•ä—Öž´Ä³Åâį››¨_†—ì—›—•€—_ii•–Ö™ÌÿÿøÿýÌęՕ–O]N¯JMH5P´JHONN¯H$  !(C()!!!J    HD.JJ+,$  &'s†‚‰‚‚‚‰—4'1#&0'#'1'4‚ˆˆˆˆ_08hPˆ—ˆˆOPi_PPO9oˆ‰ˆ¨_mssoˆJqmmsq€MMNOPdH5.5HH(....5H!(.,-H.,+,!,..JI,JNHJH.(K,,$ ºôýÿÿõùïÖÕힳ쳳䞖⛛kÕ››Õ³¨›——Ps†—톙—†•‡ik_k—ë™èÿòéüÿÔ™Í]OM¯JOH7MÆMqNM;—&  !(,((!!!D$ ...JM+(( %&0r‚†††}ˆ‰‰0'#0'0'111<„ˆˆˆˆˆ81'0#8i‚MPsPPOP9}ˆˆˆŠPqsqs‚JqmmqimMNMNPm55D5.J(-....J!,,,,.,+)+!.(.JJ,NHJHHD(J.,( ªôôúúôìÒâúųÅäijä³lä›››kÓ—›——›Ê‰†›Pqq†í—†–—k_–í–™–ÅÿÿøÿýÄ–ÊkkOOO¯MN9JMÆ_M713“&  !.("!!H(    +..HJ(+)$  %&0t‚†‰†‰t—s#&'01''11h›JOPˆˆˆˆ0'0'00';iMO_PPOO9sˆ„ˆ›PsoqqˆMdmqmmMJMNONmH.5.5F,....J!.,,,,+,+,(!,).JF.NMHHH.,J5,+ ¤æñôôñäâú–ų䳳ä³kÕ±›››a´–——–ˆÈ›—†ˆ_iqq–ñ———••ì•–•éÿÿÿÔÄ™–Êi_9P]¯M]JMNÆJ6444–&  (,((!!.) $,.H9(((( % 0s‡††‰‰iN9&&''1'4'4lŠii_OMˆŠ<1'0'#0&0&'5OPPOO5dˆˆˆ‰_qosmqmJ_smmiMJJNMNPH.5.55!(....,!!,-,+)+)(!(+.FM(PHHHH,,H5.+  ‰æñôñíñÿÓÒ™µÒ쳳ͱ͖•ªâ_ˆ䈛—PsONqq_kˆäììäìââ•k––ÄÕÄ™–™—ÕP^_^OÈMMMM_Êr^44;    !,((,,( ..5!(!! #&0P„†‡‰‰P‰<'0&'111#114—ss„ˆsOˆ410&&0#&&#&<9OOOJ9Pi„ˆtiOmoPimMPmP_ZJ9NONJ_+(...,((..!+.!(((((!+)!(.J,H,HHH.5J.J5+$ ŠÞãåäôÿäÓųÅôÅ´³Ö±³ë–—ñ——s–ñ—›q_m_q——–ˆ™›–aika–š™™™™ìki__íMOMM_ä€kk6;Í1&   !!!!C,) !,(5+!(  &0&mP†‰ˆ}q‰8''1 &&000'ˆ±ˆˆ„ˆ_s'10&000&##%0isPJJ9JiPsPmttiPPmmsJJPP_JMHMJNPH(!-,.5..(,,,.,((((,,)(!..NJHHHHMNNMHM33  ¤ææñåÿñÝÒÅÈóš–l•äijäžžñ—ˆsqsq›ñ—–ˆs—N_t†—–››–•k••–•™™š™ï—kkiì•O_]qâ€kqaÔ44  !)(!,,,  (J5(!($  &0&tPJMNq†‰301'0'''1;ˆsˆ†ˆˆ4000&0&&4osPMMJ9_‚__sostMmm_qoOmPPJNNJMPN.M!....H!+..,-.!((+.(+()-(XHIJJJHiP__]MM3   ªæôöÿÝíÖÓÒó³³Å³žä³³ä³–ñ—qsqis¯ì›—–ti_–¨ìäìììäâëÕâëäëìâìëì웕k•ì^q^_qä]kq–ì7%0   (+(,),  H(-((!  #&0&HP‚†ˆtˆ‚0''0'1141^ªO„ˆˆˆi'0'&0000&&OioPJM99dPioqq‚Md_m_mPPPPJJPHPON.J!.....)-,,,,(((.)((.(+NHJJMMJbâÛËÅ•>1 ¶óöÿõñÝâÕõű³³Êó›–³ì™ílkOOPOP__—ñ——i›í†—™™•iaiakk•k•lkka•kë>;>^ië_MP_kã_1  !),)  5(-  &&M€‚†ˆ„Ps0'0&'1'1#14h­ª_ˆˆˆO1000%0&0#OM95555-_$8simmtMPiPPsNPPPH55J5D9(+!!(!((!!!!!!!((.,.5HJMkã__^N;7&  ¼úÿöôó¼Èõš–³³ÅôÅ—lÓðkikMPOPPPi—ñ—–is—í—–•ˆ™a_aiak•kkl•kk“è?;;:aä_MM]ã__4 !)((+--  q‚‚„ˆ‡ˆ-&0''1114lª²sOJJP;000&0%'OO5555..i9#&Ji_mJNiPP_JJONH5HJ5.J(.!(((-!(!!!!((!..HJJMqåk__^^]60  ÿÿúõôóäÿÓ³–šÈ󳳚›óˆli__9hPPOP_s–â••â—•†•››™ÓÕÛÕâÕâëâÖâÕÔÔ`>>:?ë]M^qäiOM5 !!, -!.-   & O}€‚†ˆM90&&&0'1141›ªªisO#00000%&%5OM55.55.O#%##'MP_OMP_MNPJJ5HJ55.J.(++$.!(!(!!!(!!(+!5,9HPkãkk__kÆ•?1 ÝöúööóÿäÊÅžó´ž—âliMOMsOPPPs†—Ê—––ˆÕ•–†—ìk^OO^_kk••la``?`>;?è?]k__ãaN5.J   !)()+!5+ &&O‚‚‚†Pt4&'&&'1'1#16ªªis}}<##0&0#&&&#JMM555..9'####-PiPOJNNNMN5J5D.5M(,+)+.((,+((!!!(!((!!+(!,HJHPäkb_â_]7&  âääåíÿñÖÈÅÅól–žâlkOMOMP___–›Ë–—š—–â––ˆ–í•P_]MM]ikk•k````?^?`è>;;kqä_PM.NJ+  (!(! $!--  %%qZOJMt}&0&0'#%&1_s}4&0&&%8J9O.55.5-##&9POMMJMMF5J5.5.J,,,+,5(((().((!((!!++!,!,HJJãkqqì_]M6&  ÿÿÿÿÿìíâÒÓóž›l›žä–____O_iks•ˆ–›Õ——™›™Õ—™—–—ì–•O^M9MMaiÄÕÔÔÔÔÔÔÔÔ?;:;`äP]NH,J.5    !!!!5-   &&m9PoiNi&&&'%&010#1^¤ªssi&&%#&%&MMOO55..9&JJJJJJHJ5.5.J5,,,,.,(,)+(.(((()(,+!!,!.JMMÛqk•åa_];;?1 åýúýýñäììùÒÅÅÅųÒÒì–i_OOOik––—šžÕžìš™™™Õl•ks__hq—Æ–™’’’jj^??>;>?ÕkOH.+!!(  !   $HO5..+  (5JJJ99#### ##%&&<sisˆˆ^'%&&&0#0&#OJMJOMOPM &.HH55.-,.-,.+!H55J5H,....D!(.,,,5.,,!,,(,..¯M]_qä]OMM]– ñýÿýýýñõýÍÒÕÕèÕÕÕÔÍðÄž—s_i–šžšžžÅÅéžÄÅÌÅîÅ ÅÄ ë•–†k_q™í¯œ“““‘`^`>>^è;;_.)(!! ,  $MMJM..    .9FJJM+ ###%%&Oss}ª›'0'0'000&%&%9P_PPimii0 ..+.5D.J((,.(!$.HNJJJ,HJJH,N.,HHJ.HD()H,H(,+,¯MPaâ]MM;_Ä$ ýÿÿÿÿÿÿýÖÖÕééðéééÖéùÔÅž—lsl–ÄëÔÌÔÕÔøèÕÔÔèøèèÒÔÔóÌ—•q–ì–ÄÅÍÔœ˜’j```?è;6'5(H.(!)   .MPMH-   .9HJ9J# ##%&&isisˆs_00&0000&%&&#P_PPOmiP9#&   $59J+(-9...(.((.PNHHJNP..FJHNN,HHJH..JNHH,(!(O¯]JÊk_MMaÆ5& ñúýýýôóñìéÖòÿÿÿøîøÿÿÿé³—l—éÿÿÿòéòÿÿÿòîòÿÿÿòîîÿÿÿÕÒ–•ˆâÅ™Òîÿÿÿ̘’j^jé;640&M.,)(. !    PPPMH.     +55H9J%#%0_siPˆ<'1&0000&%&&'_PiPOoi_&#% 59HHN(J5..5+(((.Pm5JNNNPHHJJNJXJHNJDHKJNJ.(HJJ¯PMÆ_kJaÛ53$ñööúôõóïìéìÿÿýÿÿòÿÿüÿýÌš™ÅÿÿøÿÿòÿÿüÿÿòÿÿüÿÿòÿÿøÿýÍ–q™â³Íÿÿøÿýœ“j’è`443&.,,((!)$ !   MPOMJ.   +.J9J.'POOiˆˆ'00&000&%&&%9_PiOPms8#&# $HHH5J-....H+!+(.PZ.NNNNNZ.NKMJX.MJJ.HJNHJIJHM¯P]¨kaaâJ+3 ÿÿÿÿÿÿÿýýùüÿûòÿÿüÿüòüÿÕž—™ššëÿøîüÿøÿü÷ýÿüÿü÷üÿüÿøîøÿÔ`k—ë³ ìÿøìûÿòèèÔ?;40& .,()!,,  !+   5MOPJH     +++-,+  '_O9_O0&&&%&%%#%&&PPiPOPiM%%%  #-HH5HM-...5+5+!9NP,NNNNMZ+KMKPN5HJI,NNJIJJJHJ¯__ÄPÆM5+ýýýÿöôúõòðòÿÿýÿÿòÿÿüÿÿÒÅ›š™Åÿÿùÿÿîÿÿüÿÿòÿÿüÿÿòÿÿøÿÿ“j;ik—â™Íÿÿùÿÿ“’j>;: 0 $,(,,!  !!,   +H_PJ5  -HONP-% &%&&&&0OO´?666&1141#009sim_P'#&%  -.9HH5J(..,$ $(-.MN,HNNNNZ(NJPJX(.JJ(PJIHJJJHM¯_^¯_]¨5.$ýýÿýÿñôñðìÖøÿÿÿüòøÿÿÿîÒ³—— îÿÿÿòîòÿÿÿòéòÿÿÿòéòÿÿÿ¡’?;6i†ÕÄÆéÿÿÿÕ’`>74&%  +)((( .)   $.k_P+# # -MNPO-##%#&&&0&iˆis4441144110%'sˆmidiO%&&& #.,(55J5.+($  +!.NNZ5,KNJH.,NJNJH).)5DHHHD(.HM¯7MÆOM—.( úýýýýýíäÖÖÒìîøòîðîéîÕÒų›–ˆšžÅ¡óÔèÕèÕøèèÕééøÔÔÔÕÔÌ“‘j>;6:âÅÅÅÕÄÄ™>64&&& # !.)  ,!!C,    ,MJ5.   JOPOP'&&0&&}ˆˆ^'11&412'11'0_Pismi_'&%#  .9MN,.9.HH5  !(NPSmN.JJFPbH.HDJN(NNHD.).S.,5O¨kqÆ_]£5+-ýýýÿýôôñìäÒéìéììéÖèÖÕÅž›ˆ––›™ÌìÅ¡ÒÌÒìÌÒÒÌÒïÅÅÅ ““j`?;664^⛛ij—™›—]0&& & H)+)!+  ,.D  5__H,-4(-9.4$$'JOONP### %&&0&4ˆO84440%44111%9ˆqiqi8&%&#%# #JJJJP.5HH5 (P_NNXZ,PNNNb(HJHF_NHJHH(SM,5M,Æ__Ä_P—J. úýÿýÿôôñìÖÕÒìéìéÒÒÕÕÒÒ±——l›éž—žžÖÅÄžÅÖžšj`j>>>6644;Å—››¯——†q0 #   .,)+(.  +..$ $-5H,Mi_JHN_.OPHP55M59Odim9-8''##&0&8ˆˆ_44644064441%‚tims%&& %  -JJJJP(H5.#   ONNNPJHNNMNX(HHHNPNJJMJ,PN.JM7°__°_]¨.+ÿÿÿÿÿÿÿÿúõôóððïïóóóóðñð±–ˆih—šâš–—–қ͖–`?`?>;>6:64:k††Ptqq9#  ++)(),   .C+..HIH,,,.HN__JMHJ.HMJ_5HMJJ-...._mimmi;309_9OM<4444410%&%&8ˆsi3%#% % -JOJMJ(H5    #HNNX.HNN_JX!HINJNNNNNM5bNMMJ-ÆP_Ä__—.(úýýÿôôõñìÕÕµÒÒÒµžÊÅÅÅ´³ó—hˆ–Óš›k–—Õ–—š—âl??``?>>>;6666:k€aNmbPb  (,(.$ !.),)HHH,H5HHHN__NMHM.MMHP55MJ.5JMMJPimmisO–s}ˆs&64444&0000#h‚tsO%%&% 5.JJ9MJ,($   5NP..H_KHN(.JJJHJPb_NJb_MJ55¨__´aM—,+ úýýýýñúïìÖÒ´ÅÈŵžžÊµ³±³ñ›–s–ˆÕˆ–š–ˆÕšs––Ö``j’j???>6:8˜68OPqJ_NMM.    ((( !((HJ...,.IJIHJNP_NM._.MNJP5HPJ,JJMJJ5osi}Oi–ˆ&6464400000&m›ˆi'#&#&#%-JJ55H.JM-    +ODMNJ.JC.H5555JO_•••__J55N¯MaÆJ5—+!+úúýýýñöóÖÈÅҵŵŵ³±³ªžä–—Ó–iš›Ó—–_–—Õ³’˜¢˜’j??>:`¡6'3__tHPMNJ    .)!!(  !(!IHHDH,)HJJJJKM___DJP.MNMMH-.+9JJMMONJiosP–tˆˆsil;0&1000&8sO‚]%##%& &&$JJOH5.HH-    5.JJPN)-.DJHX,MPk•–ÕÄ—–qHOPaÜ^M£7M—5+ úúýýÿúâȵž±ÒÒÅÈŵž±›—ž›Ý–ˆklÓs—ˆšÕˆ––—âÅÒÔÿÿÿ̘j`?`è>40&3ZP†9NNJN.%   .(!!+ .,HD5HDMHHIJJN,9J_MJNP.HPMM(-9.HJJMMN55559iˆšs}–„ˆˆssi;4000&_sssiO0#%& &#& HJOJ(JHJ.   N.NHPHI5)HH.HN.Na†ÍÿÿÿÕ™•MabäaO]¨]M£+( úýýýýôíäÕÓžÒÓÒÒÒžÅÒ³³›ž›â—–——Õ—k———Õ———ÕÅÕÿÿòÿüŸ’jjè>:1&& 4PbMHPMJM+  (!!!!($(!+HDHDHHMJDJJKN,NJi_JJP.H_JM.J955JJMMO.NOPPO—–ˆˆˆOˆŠ››ˆik81's};## %%%% MJPJ9JJ5#   HJHHOHHJN,..C5N.Pk™ÿýøÿü —]€ì_MMM¨N]—.($ ÿÿÿÿÿÿÿýúôïóøõøóóóóðų›žªÖ›šˆš›âšˆˆÈ›šˆš—âÅîÿòè÷ÿòèÔÔ?:40% #5PiJJPMJJ  !!!!((++HD5DHHIPJ,,X.HMJq_M.b.J_HP.M5JJ.Omi.9OOPOihˆ_ˆˆ_››ˆ››s±ªOˆi&##&# %&#+NM(-MHH$  JJ,MDHJHM!,..DH.OÍÿîèøÿóìÍï_MJ7H^J¨-+$+ ôæíæñôíäÖÓÕÒÒÒÔÒÕÖÕðÕÒ³žâšÖllÈk›—–›Ö³Åÿÿøýÿœ’`;:61&&5PM5HN5H+   !!-!.D.DHHDP.,.,HIJNk_NHH9HOMP5J9JJ-..-JOPP_iOOJOiiMMMs›Š››—ˆisi_s8% ##&%%-55,5(JJ$   ,5HM,(5DH,,!),.,MM™ÿÿòÿý•kM7J.57©M]£53++& ôôööæíñäÖÖÕÕéìéìèéìòÕÒÅž³ÖšÅ³ÅìÅž™›Ò]i›š—Õž– ëÿÿÿ¡’j>6110& %#JPiHJHHJ   (!!!($,!HDHJX-HJJH,JNq_N5N9JPMP5M9J.9JMNJOOP_s^issssiiª—››ˆˆˆˆ‚iˆi0#%#%#%%#H.MJM.+-    5J.DJ(5)..D,(,.H__ëÿÿÿÍ™PJ5..MJÄ97™54733 ôööööäÞðäÕÒéðòòîîîòüîÖÒÒ³ÖžžžèÕùÒ̳ž– ;<–±Ýšš›Äé–`>:4&&&%%%#MNs5MJF+  !!((!!.(!DMKNHHJIJJJHPq5JNJJ_JOHMM55MMMOOMOdssOlsssPˆª›—‚ˆˆ‚ˆssˆ8## %&%&-.MHMHH   !J.,H,M(,,(H!,.MM•™Õ ™M7H5]5_ËJJ™M;];7: ôôöööôóäÕÕéøÿÿÿüòûÿÿÿîÖÒÒìžÅÅéÿÿÿÖÒj >::_kÅ›–šššškk440%% %##$MmJJM9H# 5!!!!!!,(!(!(.MIJJNN,J^NJNNMJ_PP.95HJMNONPPJPOi–sssª›—s›ˆssiP_P' %$+5MMHH-    -,,5,.D(( .!)5J]Pk–™––kJ593M_ÜJ7J]^a˜¢”>ôööööõäìäÖéÿÿýÿÿøÿÿýÿÿÖÕÒìÅÌÒÿÿøÿýÌ“j >:6;lÓl––hkq<& %##&NOHMHH5  (+.!!!!!+-).(HHHHMJJJN.HP_qNJPJMiPP9MMJ.O_OPPMMi__–ssˆ›ˆs›ˆˆsoiO_9#%%% O59JJH.    (,5).,. .H.J_a–™™••b9M55]Ü7559]a’ê¡`=/ ÿÿÿÿÿÿÿýúúüÿýøÿÿýÿüøýÿîÕÒìÅÌìÿøîùÿ¢“j¡>;:6;Åhs–_OMlP4#%+5MHJ5H$+.((,.!DHHIJNXXPXHNPikPNPNPqN_JPMOJPsmqiJ_s_kˆ—ˆˆˆˆˆOˆ››—sŠ—›ˆsqsmMdPM9##-PM.OMJ#    (.).((  +(.JPk–ÄÍ™••O7J7Ü5--.5ÄËÍéê˜=6/ ñæñæñññìäéìÿÿýÿÿûÿÿýÿÿÖÔÒìÅèÿÿøÿý˜’`œ::::8œ^ihMO^m9PiO&-MJ.J5,-!!($-$+!!,(DHHJN.H,H.JNPqqbbJPMi_rM_PPPMOMJPiissl_P_siP_Os››——›sis_isismPOMOO9' N_J5((    +!,(( (.9_–ÕÿÿÿÍ™k^MMÕ5+((+35]a•ê¡j=/ÞåååäåÝÓÒÕÔòÿÿÿøîøÿÿÿðÔÒÅìž Òîÿÿÿ¢“j` ;6:6:œ64Ph9M;MsPsP&#&#&%#MHMJJ.J.,+.+.!,(!!(!(!!+!!,HHIN.MPNNNNPbq•rPPbNkPiJOMNMM_ONOPPiisl_iPkOii_OPˆssidisi_iPiMJMJMO_OPM__.M.   ++(!J((   -.M•–ÿÿóÿý ™OPëM,+!!(5JM^kœ¢”=«×××ɱµ±±žÅÌÕîéèèèéòÔÅžšÖÅÒÅÖÖ霓’jj ?;6>>;14_sPisM59O5% #  J5-.,!NJJJNJ5q5D.H(ND.5..(,H.+5!!,.H.(JJHHJ.JPP–_JPOJiMPP€t‚ˆ‚†‚—›——›‰›–›Š›ˆl_i_OMOOOOM9O9.---.59.-JOJOM5### (JM,,.,H(!9_ÅÿîèòÿïëÕñ_MHH+NJJM_akkj>/ ²ÙÝÙÝɱªž¯žÅÔÒÔÕÔÔÕÔÅ¡ššÕ³ÒųŒ’jj`` >>8::œ:10'OkPi9599O.#  #   3!M-5JJPJJN+sH.D..J..,HJ(()M,5((!!),DH_JNMNNJP•_X__NsPiq†›‚‰›‰£—Š›¨››žªªªª—›ª›››PhiMOPOP^O5JM--,.5-55-.5-M5  (.9)(5,5, 5]•ÿÿïÿý™kPkNHH.),HHMOaaa?6 ²ÞÞäÞÛ´±šÅÅÌÒÌÒÔÅ¡ž šÕž³Åųj^???? >>:::œ410&%Ji_OM9JMO-  #+O(555!MNJNJN,bM55.5JD.HH5HC,H.5J!,).D.IJP_NXNNPq–€iqrb•mt—‰‰¨¯ª¨ª²›¤›³±´²µ±›±²ªªžss__M___OPOO55---9+...-95.-+ !.5+,5)5)  3MkÍÿÿÿÊ•aMrJM..,,HHMN_ka^6 ýÿÿÿÿýýõóðìðïóòóòðóîžÕž³ž³Åžh;>?? >;:::œ810&##8JOO59MO#   #..-+(9(-,JJPH.H5JOD,,J..J,JN5H5MHN((,JDHJJNbqbb_br€™—r€r—€††ª°¨«´²´¼¸ªµµÈȱȼҼªµµµ±±siP__s_PMM9J-,5..+.-9.9-5JH#   !.5(,H,5(   Ma•™Ì™–kM7MM..,(+.HM_akkj?% ²ÙÝÝÉÓȵ±žžÄ¡ÒÕÔÒÒÔðžž žÖÄž³››k:>>œ;::::œ610&%#8_JJ^95JO&  #..-,J.(9.JH5HMNMJJM..JJ.D.JMMPIH_JP,..NJNNSPbrbrqt€ˆ¨†‡—†‰¨›´»¼ºÈ×Ó²¼¼ÓÈÕÓÈʼÈȪ¼µ²±³—}sqi_^s_OJMJ--9+.+-5.9+(.M.9    -5((H,5, (H5]••••^M_H5,,(HHHJM_•™ ¢”= «Ù×ݲ×È´žžžžÌÌÒÌÕèèòÒÔÒÌÅîÒÒÒÈÅ´µÅš^?¡>:>;>œ;41&&#&$^JJO99MO#  -O9-9.-9H.95NJHPNHMHJ_NMHMHHNNP_PH_PbPINbbby€b£›£‡‡£¨¯Èƪ£ª¨´¯´¶¼Î×Óɼ¼É×ÓÝÝÖÝÝÈÓÓÓ´Ó¼¼´µ››isPO9_OMJ.9J9+(..MJ55NOPN_-$.J,(,D5(  ++]_k•–•k•aJJH.,JHHMO_•™œî¢j=/ ªÙÞÝɵɴ³ÅÅÒèîéèèìêùéèÕèèøÖÔÕÒµµ³³Ò³lÌ?;>>>œ:410&%#&#OJ9OJJMM# '5J999..-J-J5OJHNOJJMJqJJNNMJJ__b€Nb†rkSXb‡€‡y££°©°©«°ºÍË«ºÆ»ÊºÉ¼ÛÝâÙÝÝÞâÝâÞÖääÓÓäÝäµÝÝÓÓ±ª›ˆ†si_9POJ9J9.55M+PH5JJNNbPP. 5J,,.(H+  (+5M_k™Ì™–•OMMJ5.HHMNkíâëìêŸ?62  ªÞÞåÞµ¼ÊÅÅÅîÿÿÿ÷îøÿÿÿ÷îòÿÿÿðÔÖÕÒ±ÈÒÕô’?>?;>œ:610&###%%&9OJ^J9O^+O59999M..M-,OJOJPJNJNH__MPNPNPr†‡Xb—‡€ry—‡‡£©°ºÆ»»»ËÛÝâºÉËÉâÇÕÛÝíÛâäãäãääãììääàìääÓäÝÝÖȪ±›¤–smiOJP_O.95.J9M+MOJJmbPPNi. HM.,.,,   !!-]kÊÿÿÿÅ™__PMJ5JMMí•™ ê¡j=/  «ÞâÞää¼µÄÌÒÿÿüÿÿ÷ÿÿüÿÿòÿÿüÿÿÕÅž›ÕÕõÓÒl?;>>œ;:40&%#% 9OJ^JJO--99O5-5M.M-.5.5_HMJNONJMNsNPbbrPXqr—€€£¯†£‡‡££©°»»ÉÎÎκdzž˜k^?:2022216>`j–³äääíìäíìíäåÝäääÝݱª›——ˆs_PN5955.5J9J9,.!-JmPSNN+ ..J,5,,  !.]•ÿüòÿü™ka_PHJM_ãk–•• ¢”=  ýÿÿÿÿýýúõóüÿøòýÿüÿüøÿÿüÿü÷üÿðÕÕÓÒÅõÕÒÈÒl>?j¡:810&%#% 4OJ_99_9.9J95-95.M-,55MMPJMNMJP_PXsb†b€†—‡›‡£Æ£Æ‡£°°°°ÁÎÆ“`@2=/// 6‘ÕìñíííÝíäìäâµ´²ª›—tiPON_9.-5MJ9.+J5JO.bPNNP555J.5H,. !!5]ÄÿîÌòÿïëâí_MP]OÛabkik•’j?/  ªæååÞÉÓÈÒÒÕÿÿüÿÿòÿÿýÿÿòÿÿøÿÿÖÖÒÒÕôžÕÕèÈžjé?:61'0%%##4iJOJMiO559MMP5595-,JmNPMJJrmb_r—‡£€€‡‡¨©¯¯©Û¯Ë°°»ÇÆ ‘‘==222222////////// 2’Êñäññíää¼¼µ´¯ˆ››ˆ‚_OiO9.-9JM5.9HJ5NJHbb_HMMNJ,.H.   -_•ÿüòÿý™•i•í•qP_âk_k’^:%  ¦º½½²Ó¼µÅÅÔðÿÿÿòéøÿÿÿòîòÿÿÿééÕÕÕõÒ±›ÈÒój>640&%%%&#8Oi9Js999JM5P99^..MZOqP_N€qmm†s†—€©£©°°°Æ°ÉË°âïøꢟ”@@@=@=@@@@=222/2/22/2/  6“Öäääӽȵ´›‚POOm_P.559M,,.HJ5JJPJJ.H,ONMJHJ,(   !,9MkÍÿÿÿÊ–ka—–íqkP¯_P_ak•k`:% ¤××Û¶ªÉ³³žÅÌÕîééÕééòèèè̢硜ŸÅÕõÒȳÅÒªÒôµž^611&&%%19MO_JOOO99OHM99J^.J_PPsqP€m€†t£¨£¯¯«°°ÎÈÎÁÎòüÿÿÿÿÿꢟŸ””””‘””‘@=22/// 2lâÝÓɵ´—›ˆqO_N..M99-9(!OJ.JPqHJPJHPMHJ.+   ,+-J_’“Ì““•_aiÛ_Æ_i_••“j^/  ¤×ÙÙת¼µ¯³žÄÌÔÕÔÒÌéÖÔÒÌŸœœœ˜“˜îÅÈűµÈÒÒóÕÕµž<1'00%#10&%4MJOiiP5PM9JOM.J9M^J_mqqsq‡†‰¨€£¨²©º©ÎÛÜÜÉÕè÷ÿÿÿÿÿÿÿÿÿÿꢢŸŸ””@=2  ?ÒÓÈ´›¨—‚ss_s_5J5M..9,+MHHJqJNPPN5PNPHH      !!!(!+H>`’“’jaOkâ_aÆqak–Å¢”=  ¤ÙÝÞÛɪ›š–ž³ÅÅÒÔÅÅ¡¡ž˜““““““é’j“šµÅÈÓõÅ—±›h00&&%&#.OMMOO9O5.OO95O..MM9_Pmsb€‚—£¨°°È»ÛÇÇÎÍ¢˜”‘””ŸŸç÷ÿÿÿÿ÷¢Ÿ”@=2 <±¯±¨ˆ‚siJ.9595.9.55,.5sPNPPPNN,.,J),$   $4:`‘’“‘^]MP__ÆaqÆk—–™ê¢j=/ ýÿÿÿÿýúôñíÖìðìððððóòðìèèçèÔèèj^?>>˜µÓôÒ±ªˆs›k1&&&&##8OOMMJOOMOO5OM99_-.M_isbstˆ†£££¯°ººÛÎÛÎë¡‘2/ 2@”Ÿ”‘=2 %26:?h<;6:21& ^ªªˆ‚–m_OMM9.59.5.5++9(,_PPPNHHH,((H,    4>’“¢“’`]O_abÆ_Æ•âëëðêœ@:/ ¤ÙÝÞÎÈȯ››––›žÄÅÒ “jjj```?^??^>`“ðÈ´›››s›ˆ1&%0#'iOOO9JMO_9OP9_9JJiOO_i—qt¨†¯¨©¯«ÈÁÛââÕÔ‘2%; / ;l³äíððñíðíðññíìííìåâÓÓȵ——¤†ˆqOP_99-.59.5,-P.JMJXiPJ5H..,,..)  (- &:`Ìÿÿÿ¡’`_kbkÆaÊ•Û–™ ê¡‘=0  Š½¼½²º²¯ˆ–k›ž³žÅÒÒœ“’`>????66>>><>^jé?>;:6'6Oˆsˆˆsh#%MiOOO_O9iOOOJJhMOM9iMO_iq——›µ¯°Ê½ÛâÎÝÌ‘2  •ãÜÜâââÎâäë¨4ââëÝãääÝääâÖìÖäääÖÝääÖÓâÝÝռʴ±¨›†_Mq_O.5,55,,+OHH9HNOHNJH,J..,.5("! ()!( &:jÿÿîÿü“j`ÅËkkÈ––Èa•a;6 ¯××Éɪȴªšˆ•— ÅÅÿÿøÿü“j>::::?^jé?>;:44144:ssˆssªh<}<_iOO_MMi__MMMi9_MOO_sˆ‰ª¯´È°ÛÝäÜÕ˜= lãíãâÎâÜâÛÎìãì°%¯âäÛÕâÛääÓââÝÖÝÖÖäÖäÓÕÝâÖµÕÓÓÓŲ¯ª—€imOON9.5-5.,M!MMHP_HNMHHHHJKH5!(!!( .,++   4?¡ÿÿÿ ’`? –†Êq•Í•Ókk^]& úÿÿÿÿýöùìââääìððüÿòéøÿ j?::;>>^è>:>8641%%&#%8}–OMhPiOi_O_MMMJ9_OO_OiOiim—ª©±ÊÝËÉÝâÍ‘/ aÛââÛÛÛÎËÛËËÎÛÕâί œâÝââËÓÓÈÉÓÓÓÛÓÕÕÕµÒÈÒÓÝÓÕÓ³ÓÈÈÈÒ›—iˆbi__5.5+95,.],..!(..PMHFJ.(!+.!,(,+!!!(!(  &6?jœ’`??;ÅkqÍ•Í–•ÈM950 ‰¶»»×Ӫ훗k––—žÔÿÿøÿýÅ“?::>;`é`>4:6411%&&&&%1;ssˆ_i_OsiOi_OOiOOMJ_OOiPiˆ›´²ºÝÝÖÜâÌ@´ãâÜëÛÝÇÉÇËÆÍÆ°ëÛÎÁ¯&ÕìâËÓÕÆÈÊÆÓÊÈÈÊÈÓÕÒÒÈÒȵÊÒÓÒ±µ´µÅň——€ii_q_OJO..-..M-5+5HMH5NNHJH),JH(,.!!!,,".04:>;:;]]È•–Æ•—ÍkÉ_OM7+ }·¶»¶ªÆï—–lš›ÄÅîÿÿÿÖ™›–h;?`é?>;61841&&&&&&%&01OsiiPhsiiPhO_OOMmsPPiˆ—›ª¶Í¼Þââì @ ^ãäâÛÜÛâËÆÇ»»Æ°´°ÕÆջǴ?ââëÓËË˯ÅƱÆȱÈÊÓÒÅÅÊŵʳµÈÒÊ›´Å´Åª––s–_r•iiJO9H9(._5H,59M.JN,P_N(.M.)(((,)(,!,,!!!()$  0014]J_Ík•Õ•ˆ–ÆP]M5+ d¤¤§«‡›í›š•–—ÅÌéÅ¡š––kè^>:8161''&%&%&%#0&&&'PPiiOOisiPhPhOiOOi_Pii›‰¨Å¼ÉÝââì¡= ãäÞÜÛËÎÓÉÅ°´°¨¯Ä©ÆÆÍ°°Ë•kÛâÊëʺÆů¯Äª´³¯Å±´ÅÒŵű´³Ò±³—ªžž¯›si__kiiMOMM9,+^.,55.M.HM..SHHJ5.,)((()(((!+!(,+(   +-M]MÊ–•ÊkÊkÇOOJ5&TƒŽ¤«‡âk––ˆ–› ™™š—•ól;6000400&0#%#%#%0%&&0is9MOhiP_hiPOihPOsii_sst›—¯ª¼Õ×Ýäî¡=  ÈäìÞâÜ˺ƺ´©´¯¨‡¯¨™£Å¯Æ°ÆÆ;–ƺÈÊÕƯ³³¨››¯±±žÅÅšž›³´Åž±–ªž›iOOk___kNMM95-+P..9H9M(,+!,PJHNN.,!+(,,+(((  --..OaÆkqÊ•ÈÊ_J53 Z¥¬¶£µñ¯———™™³ÒÒž™›š¯ð_—^4'#%#%# #4Jˆ—ˆˆssslsisi‰›¨³²ÊµÉÕ×é¡= ÓââÝâ˺´º¯º´—™€—‡™£Ä¨Ä¯Æ¯ÆÇ° ™âÛËËÆÍʯ¯Ä››™››—–›kš–ˆ_–šˆlks—†i_q•q_PJqJM]JMNMP,_PNJM),.7,+(,+5 -9.+5]OMO^´__Å_O¯M59-+ w®···«Îúóñìììíìððóïïððìíìíli—sii8##&%##OOsˆ–ˆš—ˆsžªˆ››——ª‰›ª²µ¼ÝÝÓ×âêŸ2 4äíÝââÓÉ´¯©¯¨—›q—‡——¯©ÄÊÅ©°ÆÆ°ÆÍÉÉÆÓÍů¯Ä——i–—ilššššš–—lš—–h––•_ˆˆ™—t–†sPbMJP__iH_rPXJ.,HOH..((+!   -J5,HM9MM9¯__Æ^_Æ_^¯J9.+#u¬¾·¤Î½ó²¨™—–›—ÅÅÒÒÄžž›™ˆOiss__O&# #-9Miˆˆˆˆsˆ—ªš›‰›››ªª²µ¼ÓÓâÝÖé¡2 6äåìäÛÕÉ˯´¨¨——•b–€—¨™£Å¯¯—¯°¯°ÆÊÆÆÆÍÄ™¨—•i•–kk–˜–šš–š—–––hš–h_ˆ—ˆ—•_qPk€ibOb_biJP€XqbHH5].!!(((!-    +95,JJ5.5JOJ¯P_Å^´__¯M953e¬¥»½ñ¯›†–´™j™¡ÍÒÅÅž—kq___PiiPO4& &O99Pˆˆ–ˆs›ˆ‰›››ª±ª±´µÈ×ÝÝâé¡= 6äíäääâÈ͵´¯›——€k–q•‡‡¯Ä£™‡£¯°´¯¯¯Åņ——™™•_–l•lš™šˆššklslk–khiˆ––€š•‡–q€qs—PiqqaNrqbrbbNiN.(,.)!((!!(!(    $H.(9H(+,,.JOJ¯^_´k^´_^¯OM5-&e¿·À·°ºñ¯›—•›’’“˜¡èÌÅž™‰_OMPPPPO9ss;###OOJ9O}ˆ–››ˆˆš››¤ž›¼´µ¼ÓÓÞäåäîç= :ííìåâäââÓʯ¯¨—†kq•€—q•—™€ Ä£©Ä¯—¯Ä©¯Ä›™–—l_k–kl–š“lšš–šš–š–l–l_–——–s€†–Pq—bqrr†rqrr€NMS_JH+,+(+!+!((!!    -9J.MOJJ5D5H5XO9¯_^Ä;^ÊkÆM]M73 e¬¬··»­ñ¯›•›j’˜çÿÿÿÔ™›–iO9JOMJ.miiPO' 8PMJ9M}iˆˆ–—ˆ›››±³²µÓÕääääóê@ 0íííåìäââÕɵƛ›—†•Pk•_–†qk€™€——£¨††—™—¡——–––•^•il–šœšlšˆš––––l–ll•ikš€™_——†€–€†r•r†€€†€q†—_N__NINHH,!+,!(!!   995MOJNH,JJ.H9J_5:;œ;>ÆaiÆa_M;8& [¬¬¾·»²í£—–¨›–j“œÿÿòÿü“j^iOPO595-JPPPiiO-#MMOO9MPP_iˆ—ˆ—ˆsˆ››ª±ÓÓÓÝâääñ÷”äñííìãäâÝÕӳƛ™qii_k–r•_k–™‡————†™—™¡†–—–šh_akl–œ™š˜ll–š––lš’ikl––i—€——†™‡–r—‡†£‡‡‡†_PrXPPNM((.5+.  7J5__JMM5H5.,HHJ540–6:œ;>ÄlÊk^^]80 W¬¬·£½ýóñññìÕè¡çÿîç÷ÿ `;6&-+_iPPPJPPPP9o<%8OMJ9MOª›¤sssˆ–ˆ´±µ³µµÓÕÝääåðÿ¢= âññññíñäâÝÓÒʪ¯ž›†a_ia––——q ™†–€—™™›•— –—––™k^—lklšœšššššš k–lššh•lq•ki™›†—™†‡–©¨™£—£‡¨£—‡Æ°©‡ybrrb_CNNNM(!(+!.,,(,$&;bMaqPa_bMONJJ59+ 10˜;> >>¡kÒk–’h> W¬¬¥©»ñ³›——–^`’˜ÿÿòÿý“`:4&-O_P9NPOH.PPPM'OOOO95isssistª›µ±ÈÒ×ÝäìäíÿÿŸ ÅññæïóíìíÝâÓÈÊŨ›—™—_i_k—•––†––™•—†———™• ––––™khl•k–˜œ–œ—šš–k–œšklkl_–—ˆ—–™—£‡¨¯¯££¨£¯©©Æ´º©°‡‡€€†rNJXPM,,,5-((((5,7O_ki••s–_i_M5% #&00œ:> ;; lkÆl•• ¢”=  VŽŽ|»¶í¨›™—h:>`’çÿÿÿ¡j>61$5M9--.PMMMPO.MO99MOsssissq—ª›³È±µÓÝÖäíìùÿ÷@ –ññññæóñìäìÓÕÈȯś—–__a_•–••–—•••_•••™†™• •––•™k^•l–k˜“–˜š–š –šh––lkhlkq™——››†€™——°Ä°£¯©°¯°Æ°Ç»Ç°£©£€bSPqqJHDJ,(!((((,535_kq•—™—™™›–k?61 & &11œ>: ?> ^kÈ•–˜êÌj=/  V¾¾¾°¬°í£–q?>?`^’“¡“j?;40% 4PsP9MMMM5O_OOP9Mssiˆˆˆˆ›µ¼³Ê¼âäìåðÿ÷Ÿ2 œóñññíññðåìâÝÕËųž——kik—™k•––_ ••_–€™–™• ™š––˜ka–l•š  š˜š™œš–kœša–š•—››—¨Ê¯Æ³Æº°´°´ºÇÕÎÎÎËÁÛ¯—£©£¨›€PNPP5,.)!!!+((.PNMP__™Ä™ÕÅÅÅ¡ ¡“j?600&&041˜>>œ>; `–È•šÅêœ?6/ V¬¬¾£©í‰†_O446:?``j?>:4'& ##8.9MMJ.J99MJ9OsssPs—}ªˆžµÈÕÝääÖŸ”‘=0ÕñññííñìääâÓÕÈÅÅ›––qk™•––••š™™™•k••™•™– ™–š–œla–“lš ˜ lšœ–l™œ–ša––•–š›¨£Å¯ÆÆÆƺ˰ÇÇËâÛÛÛ´’= k°°©‡rXbN,.)(!((!(!--H]MP_–™Õÿÿÿé¢éÿÿÿ¡`>4&&1&14˜6> ;>œ?kÍl–œê¡‘=0  V¬¬·°¨å—_M013446;::410&%  ##4M.5J95559O_iiis–ˆˆˆ›«µÕÓÝÖÝ@// >ìññíäðìääÕÓÕ´ÅÄ—š—••k—–™k—•k—™•œ—kk•–– ™šš™˜kkl–“œ œ–šœš–šœœš–h–—•™™—¯—Å°ÆÊÍÈÆÉÇÉÇÛâÍÅœŸ‘@@2 ´Æ©£¨†qPJDH,,-!(!!-..JJMO_™ÿÿòÿÿèÿÿòÿü“`:1&&011˜::œ?> >aÊ•l• ¢”=  V¬®·°¤í†q400&003&4110&%  # $9M59555..ˆˆ—†›ªˆª²µÉâÖÝ2  šíñííììääÕÊÕµÒÍ™™™–—–—™–•k–™– ™•–ka™™™•  ™™šlkl“l œ œšœœš––œš™–kœ–š™†¯¯Å´ÊËÍËÛËÛâË颡Ÿ‘‘‘@22 6»ºº¨‡bXMJHH,,-(M5H-++JHJOkÌÿêèþÿ÷ÿ÷è÷ÿœ`;1&%414œ;>œ>> >?Ê•š“jj% V¬·»«Ëóª—_M4& &0&% # %&  9J-9M55..Ooisˆ´›ª²¼ÈÝÝäÖÕš:  2ÒíñíììÖäÓÊÍÈÒÅ›™—™™—™™™–™œ™•–™–™™™œ¡™œ™ ll™–œ  šš ššœœš™–š–™–™ÄƳÊÆÊËÕÛâÉâëéꢟŸ‘@/ ™Îº£€‡rbPMJH(,-(J5-+(!5HJPa?“ÿÿ÷ÿÿèÿÿ÷ÿÿ“?:'& 444˜:; >> >>Èkkka`? V¥®Á°ãâóÅ›–_iPJ3+% %   HMM-.999-Md_m_s——ˆ´´µµÓÝÝääðíññÕ^  ?äíìíìÖÖÓÓÍÔÊž—™ ™™˜™—  •–™–™œ™¡–œ¡–“šœœœ œ šœ œš™ ˜šš™š– –—Ì´ÍÆÍÊÕÕÛÛëîêçççŸ= »°££‡†rbMHHM-.9(-,(!5HJO?jÔÿÿÿêçêÿÿÿÌ’>610&441œ:;œ??¡asÅkkah6&&%&46˜:> klÊkkÆkia^>> [·Âåñÿÿÿÿë™qPNMHMJ+    +.-5.5-99-5iiPPmmi‚—›¯´±ÊÓÕâääääìííðåìðl  /˜äììÖëÕÖÈÒÍÄÄÅÄ›ž ™–œ™ ™™™–™ ¢™  ¡šœœ ¡¡¡¡  œ  œ ÄšÄžÄÄÅÆÕÕÕëÕäíû÷÷ê÷Ÿ2 •ãåñʯº¯£‡€–P_M,.-(+((!+-HHJHMa?j“ç““˜˜“ç’`;41%&&014œ^aÈakÊkÅkkah>> {°Âãôÿÿÿÿÿ›—kPNMJ.HJ...(  -.-(+(.4+9MOPiimqsˆs›¨³ÆÓÊÓÝäääåäåìððìðííì^///“ììäìÕÖÊÔÍÄÒÅijÄÄ ¡ –™   ¡Ì¡¡¡™ ¡¢ž¡ ¡¡ œ¡  ¡ ÄÄÅÅÆÕÖâëâäóÿÿÿÿ÷Ÿ/’íåíñíå•6É©¯¯—•q_P9,()+(.-()JM5_iq;??¡’jj’j¡^?61&0004;9ÊskÊkÈl•Êkka^? [¥ÜíÿÿÿøüÿÊ–qPMMIH.5.....,  3+((+(+((5-9iii_s£s—›²ÅâÈÕââäìíÝääåäìììäìðÕ; 22/ œìëìÕÖÕÕÔÍÒÌÄÅ ÅÅÄÄ¡¡Ì™ ¡ÅÅçÄÌÌÌ  ¡ž¢Ì¡Ì ¡¡ ¡¡ ž¡ÄÅ ´ÄÍÅÕÕÕëâìüÿÿÿÿ÷”Íííñííååã;ÄËƨ›†NJ5,,+.-(((+MqOO__];; `?`?`>:41&33MiMÓkkÊ•kÈ–•Ê–l•’`j%VŽ¶Üñÿÿÿÿÿ_PJJ5.JH......,(!$   -P__((((+(+5mPiqt¯†‰›«²ÝÊââÖäâíÖäâäÝääìììäìóÒ4 /==/ /ÅìîâèÕÖèÕÔÕÍÕÅÌÌÅÅÌÄÌÌÅÅÅÌÅÌèÌÌÌÔÄ¡Ì¡ÔÌÌ¡ÌÌ¡žÌ¡¡¡žÌ¡ÌžÅÍÒÕÍÖìëïûÿÿÿÿÿ” >ïíñññññíåíã 6ËËÈƇmPMHJ55.((.99PiJMPiP::œ>;;>>œ;643;_bis^ÈÒkÕ–Í––˜ ¢”=  Vƒ©»ÛýÿÿÿÕ™_OJ5.H5D.,...,,.,,$   J5H5O9-((++.PO_ss—›tˆ›¨¯É×¼ÓââÖäìÝÖäâÓäÝÕääìÖíððš /=@2 =ÖîâéééÖèëÕÕÕÌÔÍÅÌÌÔÔÅÌÌÔÌèèÔÔèÔÌÌÌÔçÔÔÌÌÌ¡¡ÔÌ¡ÄÌÄÌÌÅÔÍÕÍèììøÿÿÿÿÿÿ” ;ííññííñíñíååk ÊÛ°©£r_PN5..5.9..5_POJOP;4œ:::;>œ8?kj^>/  KQRTYDHN     -JJ.5JH_NNNPNJHMobNHJNMPJH+,.HMP—›£ª¨l°º¼Æʳ¯¯ª›ª››³´ÅÅÓÒÕâÕâììðóðì2”@`ò÷òîîìîòîîîîòîìîîê:2òòòòòò÷òòòòòîîîîîîîîîïòòòüÿÿÿÿÿÿÿç2âíííïííñóíóñåííñññ™2äãÛƲ²¨—‚PNMOMMO..99JO9O_´_kO__´__^P_klì“h``’éj`` `? ?>la]6&  GKQLFDK,   +-5JJMP_PHHmZPPPHJJNJJH+.5JMPr†‰¯Æ;º´¼ºÆ¯¯¯›—›—›¯³³ÍÕÍÕÖâÖììïòóœ 2”@ ¡øøòòïòòòòòò÷òòè2 ¢ò÷ò÷øò÷÷ò÷òò÷òòòòòîòïòòóüÿÿÿÿÿÿÿç@ 6ñññííïóïññíííñññññâ@2 lä⼺¨£‡sq_PMJOM595JO_MOiÅkO_iÅqh^^>`è???^’é```` ^? ;:h^;1  GEB"FD$    #$-J5ib_PNP.5H.HJH,.5JNsq†¨›©l6Èͼ¯ª¨››¨™—›—›ª³³ÊÍÔÕâÖÖìððøò6 =”@=òøòòòò÷òò÷øòç2 2÷ø÷÷ø÷ø÷÷ø÷÷ò÷òòòòòòòòòøÿÿÿÿÿÿÿÿç@Êïñïóïñïñåííññññññóó˜@åÜDz¨¨ˆ_P_POJ_59OM9M_O•Ý_^^?j¡=::>`è?>>>jÔj`?`jÌ?? :6>?`è?;;:?è`>??`éj```jé^??:6;_<4  QRUUT**    ##JJPP_NPNNt5_qbiP.NN_t‰£¨²º=/κ²º´´¯¯€›¯ž¨ž¯¯ÓÕÕéììîðïïó˜/=@2 @÷üøøøøî”2 /øûøøþøø¢¡êþ÷û÷øø÷ø÷øøøüÿÿÿÿÿÿÿÿç@/ ’óóóóïñííññíññóóóñóôóõ÷=käÞ⼺´¤›—m_ZOmMPM_k™ïžk>?`è`;>>`è????jé?j``ké`>?>œ848aM1  QQRURQ"     MNHNNPPPbb,PP_bPOP_q€—¨©ºº2Ûº´°¯¯£´——–tk¯Ä³ÅÓÈÓÕéììòïòóó /@=/  Ÿüûø¢‘/ ¢øûûøûøûþ¡ŸŸêêòþøøøûøø÷ÿÿÿÿÿÿÿ÷Ÿ@2ðóóóííñïííñóñóñññóóñôýÿ@/åäã´¼º¯£—mm_PPa–šï—a?`é``;?`é`>j`jé`?j`’Ô`?>?>œ641_;3 QQRRRD     KMJ(NNNNPqHP_bmMHP_s¨¨¨´º2ɺºº²©ª›€––¯››¯ÕÍÍÕâÖéðòóòó’2=/ /=22/  øûþûûûûûþþ¡Ÿê÷÷碢êèççê÷ÿÿÿÿÿ÷Ÿ=2 /óññññïíñóóññññññññôóùÿÿ@äåä²Ç¶ªª£ss—lkk—íškljè`?j>jé`??`jé`??j’é`?;;>>œ840M<1  KQQQQ"H    $JJD.HJNXXHHJNZPHO_Pb†‰¯ÉÈ 2 ›Îºº¯°ª¨qq—™¯››ªÄ³ÕÕÍÕìììîòóøé 222  `ûûûûûûûûüûþŸ˜êÿÿ÷¢Ÿ‘ŸŸêÿÿÿÿÿê”=// 6óóóóñóóóïñóñññññóóñõÿÿÿ=ÆåÛº»µÈ——›š—O_l훚–šéj````é`???jé`???jéj>;;>:;œ:40<5'  KQQQB.   +J.HJNHN,,JJJS5JJNPq†¯ºÇÉŸ/ šÛÛËɨ—£—€i——†Ä³Ä´´¯ÒÕëëìðìðòøø 22/  çûûûûûûûûûýþû‘‘èÿÿÿÿê¢êÿÿÿÿÿç‘=/ `óóóùóóñóññíññóñóóóôÿÿÿ÷/aÝââÕÓ¯ª›†‚miq–›ñ›•–š³ó’`??’èj??`’è`>`?’é^;;>;>>>œ;64<;&  G""QD+   $,HJJKJ,.JNJNP5XJMSmb‰°°ÉÓŸ2kâÛË⨳¯———††—›–¨¯³ÊÒͳâÖìîðïðóóøj /// ûûûûûûûûûûûûû÷==˜ÿÿÿÿÿÿÿÿÿ÷Ÿ‘2/ “óøóóóóñíññññóñóóóóÿÿÿÿç 2íääÓ×›¨¤ˆ†›—ó³l–³ól`?jéj```jéj^?`jé`?:?;>??` >>;^M0 GKQGAEFDC,),(  .,5IJJN,.MKJNZ5PJNNbq£¯ÇËÓŸ/?âÜÛίº¯¯£‡†—¨——Ư´ÆÊÌëëëìðòï÷óø¡ /22/    2ûûûûûûûþûûûûûûê /=¡ÿÿÿÿ÷êç”@2 ¡óõóóóñóóñóóóóóóñõÿÿÿÿÿ” íâÞÓרªª›——››ñ³š›ó–`jéj``j’éj`?>’è`?<6>;?k–Õž›–i<&%  GQGQGCDD.,),()()   .,DHJJJS.IJJNPHNJNPbr‰°ºÁèŸ26ââÜȺ´°¨¯›¨¯›—ƳÆÈÊ´ëâììðïòóøøð /22/   ”þûûûþþûûûûþûûûûŸ /@¡ê꟔@22 ëóóóóóóóóóóñññññùÿÿÿÿÿÿ=ååäÝÓ²ª¨››——ìž›Èðžš’èj```’éj`?`?é^?`h]›ª³ÈÒÕìÌšk<0% GGQGQE)C)),()()(( H,5DHNK...JKMX.NJNPbr£°°Îìç2ãââÇʺ´Æ¯›¯£¯Ä¯ÊÊÆÅÈËÕäììïòðóøóù/ /2@=/ èþûûþþþþþøþûûûûûþ 2@‘@=// óóóóóóóóóññóóñóüÿÿÿÿÿÿç/ÈäääÛÓ¯±›‰—ˆ›â™³Åñš›±Åèj?``’éj```lìlš›››µÊÒÖÖóÕÅl]0& GGGQGA)!)((((!)!!(C!.,$ (,D.H((,H,HJ,HHN.NNXr‡©ºÛêê/âÎÎËÉÇÆƯ¯—³´³´Æ¯ÄÕÊÕÕÍâìíìïðïøøø2/2=‘@/ ûþþûþþ÷þûþþþûûûþþ¡ /222 óøõóóóóóóóøõóøÿÿÿÿÿÿÿ÷@ –ää×¼²¯‰››ˆˆ—Ó›ž›ð›šðj```’îkll›šñ›i›s›ª–—ÓÖðÿÿÿÖžl<4 EGGGEEDD.H(,,.,!!((!C.C,(!!.D5.D(5D5D.).HJNHNJbt£©°°îê2ÛÎÜÜÛÉǨ°¯´™ÆÊÍÈÍÊÅÛÕâëìëìïïïóóóø=/=‘”@2 2þþþþþþ÷þ÷þøþ÷þûþþû/ //  óóóõóùóøóóñóûÿÿÿÿÿÿÿÿŸ/^åÓ×Ó¼²ª¨——±Ýl–쳞žžÒð³––š³ð›Åñ——_±›ª±³žÒÿÿùÿü –^4  (.H55.H.!,..C.H,D.H.H,HIJNHJNbr£©Á°÷ê2¯ÛâÜÛÛ۰ƺ°›ÇÉÉÍËÍÍëëëììììïóòóóøó@@‘Ÿ‘‘/ Ÿþþþ÷þ÷þ÷þ÷þ÷øþ÷þþûŸ   øùøóóóóñóñ¡”ŸŸŸç÷ÿÿê¡@4íÝÝÓ¼µ­´—››ÅÖ–ä´ó–³ó—š—󛛈_²›±³ÅÕìÿûîùÿÅœ^6%  ,5..55N.MMJMMJ5HHD.N(HHJNIHNbr£°»´êè=kÜããÜÛܺÉÇƯËËËËÛËËëâììììïóòïóøóò‘”ŸŸŸ@2 êûþþ÷þ÷þ÷÷÷þ÷þ÷þ÷þþ÷  /  ùóóóóóóóóóó 2222@”Ÿ”@/ íÝäÝÓµ²ªˆ››³Ýž–Öž³ó³–›³ñ³–›ž³ñžšˆˆk±ªª±ÅÒÕÿÿüÿý šh6% EDED)),)(!!!)),,D(),..CJ.HHMMPHNP__PMbrr£©Á¯çê=6ããâãâÜËËÎÇÆÛÛÛâÕÛââääìíìïóóøóøøòŸ¡¢çŸ=/  þûþ÷þ÷÷÷÷÷÷÷÷÷÷þ÷þþþ= /   õøùóóóóóññóó  /2/ ñÝâÝȼ´—›››´Õ±šâžžðÅžšì³³š››ì——›ss±ªªžµ³èóÿÿÿéžš`8 AAAA"   (!(((,C.!,,,D.(,.HJNNNbq—¯ÆËŸê@ãÜãããÛÎÎÛËËËãââââëâìíìíìïóóïóøóÿ÷÷÷èŸ=/ /ûþþþ÷þ÷÷÷÷÷÷÷÷÷÷÷þ÷þ¡// øóóóóóïóññóóól  íäÝâÓÓµ›ªµÖžšÖäl—––Õš–kklÒkslshOO_Oš—šžžèéîèÌœ’h:%  AAAA"   +5,,(.5.,,,(D+DJH,JNXr££©–Ÿ¢@ìíãíäëãââÛâÛÛãäâãëìäììïïïïïóóóüÿÿÿÿê”2/  2ûþþ÷÷÷÷÷÷÷÷÷÷÷÷÷÷þ÷þ÷ /// õóõóóóóóóóóñóñ: äåâÓ×±ªªª››´Õ–›ÖššÓl—–ˆhœ:6::;œ:6&4&%%&1046?j“Ÿ¡¡¡œ’>^6% AAAA   (H(.5H5H.!.HHHMHMJNbr€‡¯»“‘ç‘´ãåâããâãâââëìâëëîìéîèÔÌ œ˜ŸŸç÷ÿÿÿÿÿç@2 2ûþþ÷þ÷÷÷÷ê÷÷÷÷÷÷÷÷þøþ/ 22/ %øóõõóñóïóñóóóóð  íääÝȵµ²±›ªÕâÓš—–ˆhœ:::::œ641%%%&141;?‘˜ ¢è¢œ“``6% DAAA     !(!((((+,,5H5JNb€‡°Æj@¡”aåíãåãìãìâ`@@‘”Ÿê÷ÿÿ÷¢ŸŸ‘”Ÿ¢÷ÿÿÿÿÿŸ=/ 2þþ÷þ÷÷÷÷÷÷÷÷ê÷÷÷÷÷þ÷û2 /22// /õùøõóøóóóóñðóñóž ääÝȼ´±››››âÖ›Õ›–ˆ–l˜;:6::œ:41&&%0146>`’œéÿÿÿç˜@`:%  ,!(...-5..5HHMJMJN_†™°Ç?@ŸŸ %ïíííííãããëãÅj22@‘Ÿ¢÷ÿÿÿÿ÷êè÷ÿÿÿÿÿç‘2/ =þ÷þ÷÷÷÷÷÷ê÷ê÷÷÷÷÷÷÷ûþ@ /2=2 ùõõùóóóóðóñðññðñ6 ìääâÓµµž±žžÖžžÖžÖššššlœ:::::œ64100%&114>`“Ÿÿÿ÷ÿûœj`:/ EEGE,    (,((,+,,,(,,,.H5H5HNby‡°°==ŸŸâããåååííííííïí™//=‘Ÿêÿÿÿÿÿÿÿÿ÷¢‘=/ 2þþ÷þ÷÷÷÷ê÷ê÷÷÷ê÷÷÷þ÷û‘ /=@//  õùõóóóóðóðñðñðññÖ íäääÝÒµµžª³ÖÖÓšˆ–ˆh˜:666:œ640&#%&116>`“èÿ÷éþÿê¡œ`6/ EAEA!!!!!),),C(.IPb‡£©:2”ç/—ííåííåííííííñíí™ 2@Ÿçê÷ꢟ@2/ 2þ÷÷÷÷÷÷÷ê÷ê÷ê÷÷÷÷÷÷þøŸ /@@2/ øõøõóóóðóððñðññðô6 ðíäÖâÕÒÈųžµÖ›Ö›Õ–ˆkhœ66666˜610%%#&146;j“¡ÿÿûÿûŸ’j:0 EAEA)  !((!!("(!,HC+DJSb‡£°62‘ç24íåíåíåíåíííññññïñ/2@@22/ 2þþ÷÷÷÷÷ê÷ê÷ê÷ê÷÷÷÷÷÷þç2=@2/ óøõóòïòóðððððððñóÌ óììììÖÖÕÒÒÅÒÖÕšÖ—l–kœ:6666˜611&&%%%11:>`“¡êÿÿÿ韑j;/ QQQQC    !(!DHNby£©°6/‘¢@âíåíåíííåíííííííïñï™   2þ÷þ÷÷÷ê÷ê÷êê÷ê÷÷÷÷÷÷þç 2@@2/ øøóøøòóòóòððððñðóó ìììÖìéðèÒÅžžìÅžÖžÖ–––hœ::46:˜4400%%%&06?’ŸÌé÷ꢜj?6%  "v\\\A!!!)!HNXr€£©»2/=¡”kñíñíññññïïïïóóïóïóóï“  2þþ÷÷÷÷÷ê÷êê÷êê÷ê÷÷÷÷þç2=@2/ ïùùõóïððððððñðñðñó` ðììîùÿÿÿîÔÒžîÅÅÖžžÖ›š—ˆhœ:::::œ6410&%&%04>’ŸêÿÿÿèŸj`4/ "vv\QC    !!!(,.Prr‡©°Ëâ2/=”Ÿ ñóóóóóóóóóóóñóïóóóóóóë/ 6þ÷÷÷÷÷ê÷ê÷êê÷êê÷ò÷÷÷÷è 2@=2/ óøõøóðóóóòññíðñíðñÒ ðäððÿÿüÿÿÖÔÒìÕžìÒÅìÈž³l >>:>> :>66414%114>`“ŸÿÿûÿûŸ‘`6% "UQQDY !!!-..+-.H]Jbk€™´ÆÛË62”Ÿkñóñññññññññóñóóóóóóóóój  2þþ÷÷÷÷ê÷êêê÷ê÷ê÷ê÷÷÷þè2=@2/ øùøõøðóïïññðìððñðññ ìììùÿüòüÿòóòðÕÅìÅÅì´››k >>:;;œ;;6441'%%16:`’çÿ÷éþÿé¡œ`2% "‹‹‹\UIJH5!.H5H.,+5!.!(..(++.($,-+(+!++5HH_r†‡£°ºÎ:/@Ÿ/íñññññóííññññóóñóóøóøóùÔ  2þ÷÷÷÷÷÷ê÷ê÷êêê÷ê÷÷÷÷÷ê 2@@2/ ïóùóðïððððîìððìïììñ^ äìììÿÿüÿÿÕÅž³ž´ì›ÅÖ±žªšÖš–l–lÕ–kklihOO< =”= 6ññññóóññññóóóóóóóóóóóóõò/ / 2ûþ÷÷÷ê÷ê÷êêêêòê÷ê÷÷÷÷î2=@2/ óóïðóóðíÖâäìììíìäññ   äìììøÿÿÿîÒÅš³Åäś⳪ݖ›ªä±›–››_š›žÒîÿÿÿéž“^1 AvvcNCMH5NNPNNPN(NNNJJ!,(!(!,,.,H(!!!!!((.!,Nbr‡©£»Îa /@@ Åñóóñóóóñóóñóóóóóóóùøøøùó=//22/ 2þ÷÷÷÷÷ê÷ê÷êêòê÷ê÷÷÷÷÷ò 2@=2/ òùïóïïïðìëìììäìääííä ììëìèÖìÕ̳³–³³³ìÅÖ³žžª´Ýªª±ä±ª›—ª›i››ÅÒÔéÖ¡’<1& EŽIznSNIHKJKNNP,NPNNN+H()(((.!!!!,!!()5(,NZr‡©£ºÁ— =@/ìóóóóñññóóóñóóóùøùøóøóùøòœ”‘@2/ 6þ÷÷÷÷î÷ê÷ê÷êòêò÷ê÷÷÷÷î2=@2/ óóðïïðððâÖääääììíííí/  ìâÖäÕÕèÔÅž³–ž±žäś䯪ÅÕ±žžÅì³³±›—±k—šžÅÒ¡Ížš’<1/ A‹vvUQ[XSNNMNPPKNN!KJKMI!.()().!!!!!.((()5!,Nb€£—¯ÇÛÆ 2=2 ñóóóññññðéîêîêêêêòÿÿÿÿÿÿÿ꟔=/ 2þþ÷÷÷÷ê÷êòêê÷ê÷ê÷÷÷÷øê 2@=2/ ïøðïïíììÖâÖëäëääääííj  4äÝÕÓâ³³³ž³ÄìÒžÖij¯šÓ›±¯ÈìÅų›³³ˆ–ˆ›³¡žÅšk>1& AEACAvYXSNKHJHKNNI.HKNJ)H!! (!!((!(.H95.Pr€‡£©°Ûä  /=2;ññóó >@@‘”Ÿçê÷÷÷ÿÿÿÿÿÿÿÿÿê”=/ 2þ÷÷÷÷÷ê÷î÷ê÷ê÷ê÷÷÷ò÷÷è2=@2/ ïóîðìíìâÖÕÓââäÖâäìííÅ  ^äÝÕÝų›—–k››±äžâžžš> `j“œ³óïóðìíäñäääÖäìíðìðìðìÕ¡–?2% A‹‹‹‹ASXSNHIJKH.).NI,N!HJH,,,!(,-,+!!.,N—©ÆÉÎÎ& 22/ jùóóññÍ’22==‘Ÿ¢êÿÿÿÿÿÿÿÿÿ¢‘2/ 2þþ÷÷÷÷÷ê÷ê÷êòê÷÷ò÷÷÷øç/2@@2/ ìðïðëðÓââââÕÓÕÝääâäìÊ lâÖÕÊÍÅÅ›–›š›Õ›¯Ö››—h> ?``j”̘œ˜š–šˆ–l–žÅžššh>1 Avvv\\LSNNKNNNS(NKNJJ(   35HH!(()(.5MMJqbb€£©ÇÎÛ> /22 –óóóóñóí˜0 /2@Ÿ¢÷ÿÿÿ÷¢”@/ /ø÷÷÷÷÷÷î÷ê÷î÷î÷÷÷÷÷ø÷è//=@@2/ íïííðÕëëëâÕÝÊâââäìÞåä ÅâÓÊÓÕÅÌÌž™±—ä¯Ö›—ˆk? `jj“˜çœ”“j`?`:1466;`“œÅÔìÔÅœl?0 Bv\c\RITHH.+  #   #HHJJ(+(,.(5HJ5bPq€£©»ÉÛ /2/ •ñóóóóóóóóÅ/ //// ÷ø÷ø÷÷÷÷÷ò÷ò÷ò÷÷÷ø÷ûø¡2=‘”‘2/ òïðíìËâÕÛÓÓÕÆÖÕâääÝãì äâÖëéóÿÿÿéÒ¡›ÄÈñ³âž™`?¡j˜˜èÿÿÿÔ˜’?>:01146>`’çÿÿÿÔ˜“^1/  Bvgc\DU      ..HH(..5H5!.D,PXm‡ºÍâââ/// `óóóóñõóóõóë?  ¢þ÷øþ÷÷÷÷ò÷÷÷÷÷÷÷øþ÷øœ@‘ŸŸ‘2/ íïïîëÕÕÛÓÓË´ºÆÛÓâäëìå >äââÕìÿÿüÿÿÕÅÄÒ󞛪∖>?¡“˜Ÿÿÿ÷ÿýŸ’`;6&0016;`˜ÿÿòÿø““^1%  DUQGBKN     JM(,.-(.,.H+5.P––¨º©ºÛÜ /2/ 6ñóóóóñóóóóóë2  `þø÷÷ø÷÷÷÷÷÷÷÷ø÷÷øøøû””ŸçŸ‘2/ ìïìâëìÖÕÓÆ´ºÊºÈËÝÛâäì –äÓëìóÿøòüÿùòï󞛙䗖—;?¢’˜èÿòê÷ÿ¢“`;60%04;`¡ÿêç÷ÿèŘ?1 DRSx|DK+     $,HHF!.JMP_Mist‡£—°Ûâä: // Õñóóñóñóõóøó     ûøþø÷ø÷÷ø÷÷÷ø÷øøþ÷û꟟êꢑ2 ìíâëââëëÒÍÊÊËÊƼ´Ûâãä/ ÛÈÝÕëîÿÿüÿÿèÅ™³ž—͈›l>?¡’Ÿ¡ÿÿûÿÿ ’`>:11&02;`˜ÿýòÿø˜’;0 Dgg\\DU-     HHHHHM(MMOON(,Pb†›¨¯ºÉÛã•/ 6íóóñóóóóùùóøï  èþøø÷þø÷÷ø÷ø÷øøøûøûêèêÿ÷Ÿ@2 íììãëââËÆÕÕÕÊËËÈÛÛâãä6 2ÛÖââÖìüÿÿÿ÷ÖÒÄ›¨–â›k>`¡“œ¢êÿÿÿê “`?:0&&&1;`‘Ôÿÿÿ¡“k;& ETUUgD[5   #  #  HMJOPM(5.H5M5Jk€†‡©°Ç×ÞË  Õóóñóóóóõøóõøõøð   =üøûøøþøø÷þøøûøûøøû÷÷ÿÿ÷Ÿ@/ ìïìäââËÛ¯ÆÆÍÕÕÕËÉââäã= –ÓâÕÖëîîòüòîÔÌ™³¯›¨ä——]?`¡“œ¢èê÷ê硘j?;10&&&6:`j“¡˜‘lh;&  ELR[vFYJ  ## ## OJMHHH.JOM_..Pbt†‡¨°»Ûâå   äñññóóóóóóõóøõóùù¡  ÷øûøûøûøøøûøûøûüûÿÿÿÿÿê”2 ìïëìëâÕɯÇȺºÈËÕÕââãã=/ ÝÆËÝÕëîøÿÿÿòèÅž¨›³ñ›—ˆ;?`¡“Ÿ¢îÿÿÿꢘ’`>6100&16:?`?`>h]4#  EFEEEDD)      5HHHM5JMJ5HJ5)JNPmt†¨²ÈËÝ6 Íííñíïðïïñóðóóòõóóø/ // ‘øøûøøûøøø÷øøøøûÿÿÿÿÿÿŸ@/ âââÕÍÊÆƯ›£¯©¯Æââëäì?/0ÛÉËÉÍÕìÿÿüÿÿÕÌ–š––í•qkihhaÅ’œ¡ÿÿþÿÿ¡œ’?>1&%&&04466194&  EQW\gQeX #  #### $-.HH(HMPONJ+5H5HM,.Pbt—›¯°È×âã– ³ñóñóóóóóóóóóóõõóõøù` /2/ ”üøüûûøüøûüüüüÿÿÿÿÿÿê‘2/ ëìäâëâËÕÆÊƺºÆÈÆÉÎâäì=/—âºÈÈÊÕðÿøîüÿøïðïïìí¨–—†——›ÅìÅÒîÿøîþÿøêéèj;640&&&004141O5'  EWW\xF[Z+# #   # 3$(+,(!HHJMP-PMJJJJ!5HHHM,5i†—‚£²ÆÓÜãâ–ñóñóóóñóóðóóóõóóóùùù“ /2=// >øûüüüøüûüüÿÿÿÿÿÿÿ÷Ÿ=/ ÛåëâââÕ˯ÊÈÊƺººËÛÛÜä:  ÞÛ´ËÈÆÍëÿÿøÿÿÒÄ™›ž—–†——¯ó¯ÅÍéÿÿÿÿÿ衘˜é?:44000%&&%&3J4$  Ggg\xIeY5  # # '49JP,PH...(-PP_MJ!JJJJMJ!HHJMPM5imt†—£¯¶Éâää60óóñóóññóñóñóóóóóøùøõø 2@@2 ¡ûüûüûø÷÷ÿÿÿÿÿÿê”=/ âìÜââËÛÍÆÆÆÆÈËƺÇÎââÞ2 jäâƵ´ÈÒÕîÿÿÿéÅ–™›———››Äñ³›ÄÍèøÿÿÿøëÕÅÅÄík<610&&&%%49-$  GŒ‹ŒxFndJ# '39M5NZbZ_d.PH9MJ5+5JJMJ!5JMMMN9NOOP++_st€€‚¯ÈÕÛâݯäóóóñíñóñññðóóóóõøóõøõ˜/2=‘@2 /@˜˜‘”Ÿ÷ÿÿÿÿ÷Ÿ@2/ ëìÛâÜÛË˯ÆͺÆÈËÆÈÎÛÜâÛâÕÊÅÊÆÅÊÕÌëÌÄ™i——–†—k–ñÄÒèìòü÷îéÍž³ž›í_P^M8'#95-#  GGGKILIZb599H.PPDmHHdmbHMOMJ.,,((.J!.- 5N-5O55JM.M9Jmq›¨—¯Ç×ÛÞä–óõñíññóññïñóñïóóóóóøøù’2@‘”@2   /=”ç÷ÿÿê‘2  ëììËËÎÇȯ¯°ÆÍÇÆËÆÊÜÛ´ 6ÜË°ÆÅÄÒÍÅÄ Ä¡ž–••—–†^è?^•› ÅèøÿÿÿòèÒÅį––äkqM_P55M.55$# "RLQXHX..!,JJK.HJN-5OJJOH.JKHMHqH.& &&JPP_PbMsPObMHO__q‚†—«¼ÎÝl%óñññõóôñóññôóõóùùóõùùùó‘@‘Ÿ‘@2   //@”ŸŸ”2 Ëëëëͺ¯©©¯†™¯¯Æ¯âââãÆ´ÛÓÎÉË´ÍÄÍÌÅÅÄ^O_ai•è:646>kÌÿÿüÿÿÕÌ™––›³ñ›ˆtsm_OJ53#  "unnRB)KJCJJJJ(.J__NM(HJHHNPHNM-5  &&_5PPbiPNZiO5JO_o£—«¼ÉÛâ Óóññôóôóóôóôóôóõóõõùøùóò‘‘”˜”=/  /2==2 ËäëÛâʺ¯©©¨¨‡¯©ÆââÜä™6ãÆÎÓÍËóíñïóïòóîíìäëäÕâÛâÍ;44446?’Ôÿ÷îøÿùòì왈¯¯ñ›–†qi_P5M3  "WWUVLDH.JJJJP.5OJJMNJHF9NP.-& #%&& &4MbiPPm_mMH5JOZi}ˆ‰ª´½ÛÞh4óñóñôóôóôóôôóõùóõóùøõùù蔑‘‘=/       ÕäâââÄÍÊ©¨¨©‡¨©©ÛãÛã•ÅãÆËËËóͯ³´ÄžÄÌÄÄ™ks_kki6410116>j˜ÿÿ÷ÿýÔÄžžï›¯ñ›•†miP9M3  )eW[WV)FJKJONMK(JNJKM!JHJ.- &# && P_ZsP_dmMHJ9OPmsˆ†ª²¼ÉâÝ kóôñññôôóôóñõóõóõóõøôõù@2/    ÕìâÜÛÍÆËʯ£©—©©©ÎÜâä^6âäËËËõÆ´¯ÄÅÅÄÄÅ ž–•a___6400016;`’çÿÿÿÌ œ™—™ï³¯›¯ñ—ˆm_99-  *vwggVKNNJJKMJNHJMJN,(-#&##&&%&+is_ZJMPmO55.MPmst‰±ºÓÝâ–jñññññôóôôóóôôóõóóùóùõ2  ¡ý> ÕãìëΰÄÆÆÆ´©£©©©ÎÜ»Î2ÅÜâÆÕõƯ¯™ÄÍÌéÒÄ™—k•akk<4100116:?j’˜¢˜“’j`kk–ñ›™ñ—†qO993#  )KD[xZVXNNJ,JMKM(,J.-$  %& #%3JMPPPmqPPN.9OJO_m‰¨´µÝâä&Õññôóôôóóóõóóôóóùóùóî‘2 ˜ùüù“ ÓäâËâË°´ÆÆÆÌ©££©ºÁÜÇ6ËÕËâó´ÆĨÄÄëÿÿÿëÄ™—a_k_a6644404::>`j’’’’``?>;j™ñ—–›¯ï–†iiP93#  *gRG)FeXNNM.HNMNNJ ## & &%% P‚MbmmmMN9JMPMPm—ª´´ÊÓÝäÈ/ñôñôôñõõõóõóóùõóõõÌ6/  /ìüùüúÔ âãââââËǯƯ°Æ´©£°Îΰ ¨ÆÇâùÈÆ—¨Ä¯ÄÍÿÿøÿÿÍÄ™kiai^66::6:64:>?`jjj`??>???`’ë™›—ñ•›•i<4   *|xxuDnZPNJJNMJ- # #& &00# #5JmimqtPOH9MOMOsˆ²›¼ÓÕâähjññôõôùôôôóùóùóõó˜  ?ùüùüùùù ëíâëãÛë˯©ÆÆÆÊÊ©»ÁÁka¯°ÆíºÄÆ•£—ÌìÿòîüÿùóìëÕÛÕÊ ¡¡Ì¡¡Ì¡ÌÌÌÌÔÔÔÌÌÌÌÔ??``jék’—™¨ì¯i]7&%  *Œx|xKdbXPNH5$ #&&& &# # mMmmqi‚NP9JMOsOs–ˆ—²ˆµ¼ÝÝääÅóôôóôôõóõóóõùóÄ`?>:1 ˜ùùùùùùøùé âíâãââÜÛƯ°¯Æ¯ÆÄÎÁ»]»©¯°ã¯£¯•——ÄÕÿÿüÿÿÔÊ–•i__:446666;4::>;>;>>>;:`è`^`?’éj^`–—ñ–kO;&  BŒ|Œ|eF,(!,J# # %& &&# #%&MMoiqosOP9JO_i^sˆˆ±ˆ´ÊÓÓÝäž  ðôõôóôõõõõóõóôóóñôóóóóóíë˜6  ùõùøõøùóóøÅËåìãëâÜÛËƺ°¯Æ°©ËËÆ£»¨©¯â£—€_†–›ÄÔòÿÿÿòÍÅ–ka_O;114464646:6::6:::::>`é``>jjéj`••¯ì•h;' BŒŒ||KZZXN.O$  %##& &39_mPMmqm_PO5OM_PiOiˆ›—›²È×Ýää^ 2ñôóôóõùóóóôóôóñóóóóóóñíóóóïÊ: Õóøïøùøõøóùõõ¡’ååìÜãâÆËÆ°°´©¯Ä¯ÆǺ©‡‡—Ü—€PNk—ÄÅéîøîéÌ™_O_]N756446464166464444::>jé`?^`jék–›™›ÕÄ–?6 BŽŽŽx|ndPNJ.5### ##'9OZPPqsoP__m}_5MhP^msiiˆªªªµÊÓâää kõõõõõóôõóôóñôóóóóñóñíóóïíïìïä’%ìóóóóóóøóøóøõøõé%ïååìÛÜÛâÎÎÊ°©££Æ©¯ÆÄ©£Ûbqbqb_•™ÍòÿÿÿòÒÄ™^PMOPPMM7866:664664668:::>`éj`^?jï›—••a?4/ Bu\[De{ZZPNJ- ### #%&#-OqoOPmqmstPsssi_OOMPiikˆˆ‰›µªÈÓÝääÈ ¡ôõùôóõõôóôóñôóóóñíñïñïñðìïãïìí 4:îðóóóóîððððóøóóõóòlÆñååâäâÎâË°ºÆ¨££Æ¨™©¯£‡Ëbq_X_qk™ÅÿÿüÿÿÔÅ™kM]P^__O^ia]>;6:666;:::;;>`’éj```³ó›–•k?6% BŽŽŒŒC{mXPNMN' # ##-OMmmqmtMPmqo_t_siOO9_iisˆO››µªµÈÝÝììl ÕùôóõôôóôóñóôóñóñííïïïíïìäìãìììíÍ>>ððìóïïïìïòóðóòóóøðóôë0ííåÛãäÜÎÎÊ°¯¯£££—£–—™ÜP__N_bk™ëÿòéüÿøïìëÍÅËËËÛÕËËââââÕÒÅÌ¡¡Ì¡ÌÌÌ?;`^jéj`kÄÄíìëÕŘ^6 DŽŽŒeKdmPNMP5 # #-J_oMmmmqqqPiP3'ti_59M;>Õ60&00:?“ÿÿîÿÿ™–a__O]M•ì–—–››™™™™–’^6 FŒŒ~|fNdPPJJJJJJ5JMJ-----.JPmiP9'%&&&#&&0&0%&0&&'_sOiOOOOOisssi_ssˆ—–ˆš³²µÈÓÝâäÝ% ÄñññæïííååìââââÛËÊʺÆÊËÆÆÆÆËËÛËÉÕËâÕÆÈÆÅůÄÅʳµÈÒÊÒÕÒÖÖäâäìíðñìññôóôñóóñóóñññíæì:©¯†tqPrP__b9aÍ•—™–ÄÄÄÄ¡Ä –`>;4:Õ400&6>’Ìÿÿÿ¡’j]_OOMM__•í——•›™™™—™š–l]6/ F~||wwN....JJ9JJJ5.5-.-.-.O_PO4&# &%&&%#&&00#%&%&;‰OOMOOsi_isOO–ˆÅ±ª³Èµ¼Óãíä1 ’ñíñññíåìäåäââËÇÊÆÇÆʯ°ÊǺÆËÈÊËÕÇÍÍÈÆÅÅ´¨Ä³´¯ÈÅÆÒÊÕÈÊÓÒÖäìííñìñôñôóóñññññóñññíkª¯©‰›rXPk_PNMPbMÆ™–™ÄÄèèÌÌÍÔéÄ“a>;406Ô64406?j“`>;79M9i_]_—âëíììììëíììëÕĘ`6 F|wwwZKPPMH-J9J9J599-.-..-P_5#&%&&&%&&&00&0&00'%001sOPPOsˆissˆˆl—›±±µ²µÝäåìå? 6íññåíåííäãâÜÛÊ˺ÆÆÆ°Ê°ÆƺÆÆËÉËÉÍÊÆÅ´Å´›Ä´´¯³´µÒÈÒÒÝÕäÖÝìíìíðññññôñóñóñññññí?%°Æ©¨‡t•XNPr_NO_q•Ë•™ÌëÿÿÿòéòÿÿÿÕ’?;14&7Ô7464:?j¡’`>:666M_____a¨—į™™—•’`6& FfwfZPVNNJJ.9JJ9559H-.-.-.9& %%&&0#&&&0&1&0&00'%'<ˆsOP_hsMsssšsšˆ—³ˆª´¨ÝÝäãåð– ÅñíåíäíããäâÜÊ˺ÇËÆÆÆÆ°ÆƺËËÇËÊÉÍƴĴģij¯±¯´ÅÊÈÊÔÓÖâääÖäðæìñññôñóñóóñíñóì’1Ë°¯¨¯rrbqMNXOPM_PkÛ•Åÿÿüÿÿîÿÿøÿük]00 07Ô6464:> >;666616;^_aia¨™¨™—–Ä—™–l•^6/ IVLLKVNMJH95...-5555.-..+#%&&&&&#&&0000&0&0014ˆ„ˆsM_iOsMs–iˆ››‰ªª†ÓÝÝâìäâÓ   `åííãíãäãââÇËÉÆÍ°ºÆÆ°ÆƺÇÊËÊËÊË´³¯´³›³¯´¯±´³¯ÅÈÕÍÓÖääääìíìñññññññññññíé”2>Ëȯ£—‰†qXN_MXH__N_bÍ–ëÿøîøÿøÿøêøÿÄ•k50&3604047œ76014œ¡¡ÄÅÌÛâËíëììíììííëìëÊŘ`6 ®¾¾¿{V«‰€PbmqPMmPPPOJ'# #%#####'OOiiiisissOˆš—ˆˆ_ˆ››—¯m›ªª—µ²ÆÓÓËÈäÝ& /ÅâìãâââÛâËÎÛÛËÇÍÇË»ËËÆÇËÛËÓÎÊÇƵÊÅ´´¯¯¯›ÆµÒµÖÝÝÖäìÝííðæñññññôôõóõñê”/ •ÝÛÊƯ‰¨bPNO.!+MMH]Æa— ÿÿøÿÿéÿÿøÿü–_MMH$&03˜36 46™66&36™6;:667^9kq••a•–kkah;4 ØÑßÑڃ¶ª¤‰t‚‚‚_P‚tt9%## #####&<_POOOiiˆis›ªˆ›ª_—›››¤ˆ¨››¤¯´È°µµÎÛÝ]  ?ÛÜäÜÛâÎâÜâÜÛÎËËÎÛÎÇËÛÛâÜâËÓËÊËËÍÆÈ´µ±›µµÈÓÊÅÖäääääííñññóñôæóõôøîŸ@/³ãÝÝÇÆ´£‡›b†rb5J.!(M^OÆ]aèÿÿÿîÔîÿÿÿÌk]M.N_,34˜44&06˜64004˜1:666414__a^k†•kaa_^^;1  ØÑßßßЉ„tq_ˆ„„‚mOP###%%%%%####&'OiPssPi6'iˆ›ª±±–›ˆ†¯–‚ˆ›¤ˆ²—›—¤¯»È¼ÇÉ⪠ –ãÝÜäâãÜââÎâÜÜÎÛÎâãÜËâÎÕâÛÛËËÕÛµ´¯¯¯µÊ¼ÓÕÓÕÕÓÝÖäñíñññññôñôññî¡‘2 2ËÜÛ¼°È´¨›‡€–b_J(.M,!(^M´•_k¡òÌÅÌœŸî“••]HMNN_.5™31403˜31464˜74101040HMa¯ÛâëââÍËÍÊ•?1 Ñßßßß®«¤‰P‰††‚‚ˆP_o8#%&####%4iOssss—OO1'08s››±ª³—›ªss—ˆˆ¨±ª¨†‚—²´°º¶ÈââÛa /ããâããââããÜâÜÛâÜââÎäÜâäÜÛËÓâÕÈʺ´´ÇÊÓÓÕÝÓÖääìåðñññññóóññ韑2 žåäÛÓÇ°¨´¨†›trqNq,!.M5H5aÆk•–•–ë ™“˜’¢j>757PNONq©]4464–44447œ640& 317MM9Ë•qk_kJ^O]]7& Úßßá߬¹«¤›tO¤‰—‰†mts0&##%%##%#&OsssPssˆ8&00&&;›±›ª²iˆ›ˆ¤sq‚ˆ—¤ˆ›††¯¤ª¯º´ÛÓÛÝÊ4 2ÜããìÜãäãÜãÛãâãÛâââäââãËÛâÕËÓÈ˵´ÓÝâââÓìäìíìíñññññóðÔŸ@2’ååäÝÝÉÈ­‡£›†††PPqNM(!.MM5PÍk•–a–Í™’`?`¡>6&.bNNN__¯b_M67“64467œ643&&&JM]9Æ_aiPP5M9977%  À¥Ž¬Ú·­¨Šˆt_Pi_‚t<&####%%%%##&08isssM_sss<%0000&h›ª›«i›—ˆ—ˆªO€€ˆ›—›sqi£‰£¯´‡Ë»ÓÉâÛ™ 1™ãäÜãããâãÜãåÜâãããääÜäÛÛââÝÕÉÛÓ×ÊÓÊÉÖäìíåðñäìåìñé¡”=/ ?âäÝÝÉÛÓÈ°±‰€q—†iPP_.M!5MaJaÆ–•™k–Ík``;>œ>4&H___bM¯•__]–6667>761&&H5HM-Æ_OMP],55754;0 ßßááß­¨Š‰ioqsss—‚t4%%#%%&%&%%-OOPssssOMsss8&#00&0%0k››‰›ˆˆˆˆ¤Otti—ˆqiPtt††¨›—°µ°ËÉÛƼa  2’ÇãããããÜââããìããäíÜâÜâäâââÓÝÝÈÓÝâÝäìíðíññäóé蟔@/ 6ääääÝۺӼƯª‡tb_PbPNM.M.59aÍ–™™••Ôj`?;:œ44&&J_•iÆq7;;>? ?;4& 3N.H5.¯MOJJH-5..437  ßßááî׮¨Šˆm}ˆˆiˆ—ˆˆ_0&0&&00004ilssiOsOiˆ—_O1&&0&&&%0kˆ›—mit_smqsqiskPiMO_Nq—‡—¨¯Æ©Æ«°Ëº^  2–ÛÜãããåããäããåìÜìÜäääãÖÝââËÞâäâääíðíÖÔ¢¡”@2/ %ÈííâäââɶªÆ²¨¨rmPqHNN9H-_Oi•šÕ™™ÄŘ̒`?;:˜400% 3M™™Í••kÆ_^^?j¡`?60&.J,(+9¨5MH,(-53$6 ßáááá×¹¶ª_tˆˆ†ˆ›±ª—<10&0046:hsssiOs}0#%%&&%00%&1%&Ol‚—Šiiqmt_s_q5mPOP_i€—€—€£¨¯£¯°´°Ék :“ÛåãåãååíåìãåâãìãâäãäÝÝâäääìÖéÌŸŸ‘@2/ 2µññæåäÝâÓÓ°«²²¯—ZNm_iPH9MM••™žòÅÅÌšj?;Å:0 %014:a–ÄóÅ™kË•—••“ê’j^77JH,5H£.+J-,!37  ßááááÚ~mPPmˆˆˆ—›—ˆ–ˆ›–41144:^––šˆsissh&0%&%&1&%&&&0%0it›Pimssm—_sPi_5PJMPqHˆbt€—††¯‡‡¯£©ºÉÇÁ¨0 2`™ÆÕãäãíãíÜìåìãâÛâÓÕÍÒ¡ÌŸ˜”@=2  häíäÝÝ×ÓÞ×Óȼ²ˆ†q€miN_Zi_MP++Paèÿÿÿîèîÿÿÿ¡’?>?Í6& &6:>jÔÿÿÿÍ™•Ê–›–ëÿÿÿ j;NJHH.5£5,(,.(!+36 ßááá椈ˆi‚ˆ—››ÅÅÅ—›h66:^šžž–±—ˆs<&&0&%%&1%0&%&&&&<‚„OPss_s—_Pi_OHJJ9_OMq_ib–b††€††£‡°°º°ÇÆâ1 /226@``‘j`‘‘‘‘‘‘@‘@@=2;žåíääääÞÝȼӼȲ³‡‰††mq_MZPMPiPM-.–ÿÿøÿÿéÿÿ÷ÿü’`;6:Í6&06?`“ÿÿïÿÿ™•Ë•Äÿÿóÿü’aPJHH..—..(,!(+-&7 áááæᥫª‰‚siªª³±³ÒÕìÔÒ›<žäÝÝÓäӼȼÈâÝÓ¼¼²—›‚ˆ›€tqsq_MJPJPNi_5OOM_œÿÿøÿÿéÿÿøÿü’?61&&6Í;>?`“ÿÿîÿý™––Í••ÿÿòÿü–aN5,(,-‡(((! 7 ÐÚßᦹ¹¤‰ˆ}—›¯ÅÒÿÿùÿýÕžžÒÿÿøÿýÅÅ›—il'&&%&%%#1PssP_OMsmmi_PO_OPP.+559HJ9._,_OJHPJNNibXrq—£‡©¯°»»ÎλÛÎܯ<0  /4`›ÖäääÝÖääÝÝÝÓµ±ÝÓ¼µ´ªª¤››†ˆsqbiPPJiJMNP_M5O_k–èÿÿÿéÔîÿÿÿÅ^;1 %6Õ;?j’Ôÿÿÿ¡–•Ê••™ëÿÿÿÆkPH,H(!((†.,(3ÚááßÚƒ¶­Š†qˆ±ÊìÿøòüÿÖÅžžÖÿøòùÿÖ³—^%'0&&%%#%#3PsiimqM&%9iq_O_N_PPM,,9-M.M.M.HP.5JJJHMNON_rr—‡£¨©»»Î°ºÎÁÎÉÎÎÜâãÛÉÜÓÛÝâÜÝÛÛÝÈÝÝÝÝÝâÝÝÖâ¼ÝÝâÓÓÓȼț«Óµ²ª›¤›—ˆ–s†s_PPOOP_JbPqM99k_’’¢œœÄ ™Õ˜ki40 &:è?`j’“¡“j`a•¯k•–™Ê–•kM,MH!(+(£,+!3 àßàßàиŠ†sˆ—››±ÅÕÿÿùÿÿÕÒž›žÅÿÿøÿýÔ³h'&&%&%%8Oqmid_d&%#9_JPPPMPJJ,9(5.55+O(M5,.M.5,MHHHNX†‡y€‡€£¨°º°Ç»Ë°ººÈÇÎÎÉËÎÛÈȺ¼ÈÉÉÉÓÉÓÝÈÓÓâÈ×ÓÓÊÓµ¼µµ±µ±±›¤›—‰ˆˆiPMP_PMPP_H..-._Ma;;?`?j`–kk•_].$ %3?è^``j```>;^¯]Naka_PMJ,J.!(+(£++!!4 ÚßßáßÚ¦Š‰t›ˆ›±ÅÒðÿÿÿîÒű›žžðÿÿÿÖÒž^40%%#&%% #%9O_PsP_P4#%#8ZMOPMJPJJ..5+5.+9.55((.,.((95,HHbbNr•bb€¨£££©°©°°©ºÆÉ°°¼ºÆ¼ÉºÆ´°µ°º¼È¼Ë±µÆ¼ÅÓ¼Èȵµµ±²ª±‰›ª›¤ˆˆ‚tlsssOJNOO_PJPNN.8H]4114:;::7]OM_M-(% 06?è?^?>:7:66™MMMJ_]OH5,.H,!(++£-,(4ßæááᥤ¦››mˆ››±±ÅÒÖøÖÔ³³ž››žÒèøÒžžh:60&%%&%%%%# &MO_O_PPP8##'J9OJJMJM.-+(9(5-!.-(!,!,!+,,(..MNJrNyy‡€£‡°¯©©£¨´©´©©°ª°´¯²¯ª©±ª¯²´²µ›µ¯´Å±²µµµ´±±ªª››ªª›sŠˆimskPJPPM_MMPNM.(M&&0&&11003H9J](($(0 &067?è?;:64346•47H5H9H5+!(,5!(.-¨5-(!3®®æ®¼²Šˆs²›—µÒÒðÕÒž³›—›žÅÅìÅšj:0&&%&%%%#&OP_OPOON955H9H5O!-(++!9-(((-(+!MJ(.bHJbb€r‡b€r©€‡¨£¯£¨ˆ››¯¨¨›¨—¨›ˆ†›²¨›±¨¯³››²›ˆ——››sª››››i›ˆmPss_qOJMMMOPMPN-  %%$(+(](+.5611667?Ô;641004˜0&5,,((,+.((+,557¨95,!3 ¬§¥­…‚sqiiissšžžäž›—ššžÖš?:40&%0%%%& #9oiPiosmM##&%## 55JJ_HOH55(5+(]),!(.+(++(,+MC.NXbrNbbrr€—€›‡€€ˆ—€ˆ†ttsšt–—sslslii_issi_PPOO_OiOOM9HHPM_ONO$   !(]M9^_j;>667>Õ>46314˜:4&H,,(!H.H.5,MkkÆ_^5+,(!3À¹¹¬¦¤‰}ssss—›žð±›š››šš±Õ?;640&&%%&&#  &5MPiiPmi'% & &MOP9M,9,59O.!!(9(!-(!!5+!H,,NPXbJbbqbrPr€r—€m€qtiiq_sqsis–sks_ssiOˆsiii_POPPiOJO9H9PJM_NM-     !+9ka•Ä•’`>646>Ô>4444Ä:775.,(H,HHJO._•™ì–aqM-(!3¥ÏÂÐϧŠ¦¤ssˆˆˆ—ˆžµó›››–—––›Ök6600&&%-5.9PiPd_i&0##%&%## -N_JMH55+9.P!!!+5(((!((!(),H.JJ_PPPb_bq_Pbqq_qiPPiiP_isimsksi–ssholii_ˆˆsim__dMiiOOMM9HOPOM9PJ+    (-MaÔÿÿüÅ™`6444;Õ666:>Õ^MMH.((C,.MN]•ÍÿÿÿÄO.+!!4§ÑÚÐÐÏŠ‚simˆˆˆ—›—›ñž››š–—––Òs:40&&%%85599iP__m'&& %%0#M5_J_.HJ+9.,+J!+!+!-!!$(!!!)5.HMHNMNPNHM_NPP_PiP_PMOM_POPPi_iOhOOlsii^ssiiiski_ilsii_i__MO9OO99M9&OM.!+$   5]•ýüîÿù™™;4114>Í:76;]íMP^M,!H..Ha]ÄÿýòÿükaH+!3¥ÚÚÚØ­­¤‰sˆˆˆˆˆ—ˆ››žììììäääìää–h800%&%#999M5Oiimi8&#%%& #-NJMMM#.+9(-(!!+!$!(+!!(.,5HHJHJNHJO_MMNOO_MPMJ9MOMOOO_POOkPPhsiisOiksiOslsi_iqi_OPiJOMMM5'-OO+$5    4_ÄÿéÔòÿÅ•a;0311;Ô::;kâNMM5,..HMNaÍÿòèòÿ_7,!3§æá᮸®ª›Šs›ªªˆˆ››ªš››š›—l––Ólsh'0&0%%#%#1M9O99MOisq<0%&&0%%%% &JMP5M+ -(.-(((!(!,(.+.+H..95HJOJJJMMOMMM59MM9M_PiMOOiOPhiii9siOsitsOP_iJO9M4M9.(-$  &^•ÿüîÿù•M7&&03>Í;_]_ã]HPM.,HJ]•™ÿÿøÿý™aH+!4§ÐÐϮ׸²Šˆˆssss››±ž››››šsš››Õ›šk440&1108sO^OOOM_i401111400%% ##9JPJ_M#  9.!!((+((!(5,5HJ(J5MH.OM_NMN5MOJOM959MM-.599POhOOi^OOO:>>?>>;;>01%-+.5+9!  +-,J,.5.((!5HH(!!+!-++.,-$,J5.-59---5MiiJP__i_PsslPl_–ˆ–s9i_hOJ^OMM99O4   $OO_PP_J  %06?–•••këO9M+,7 +.JMâMMM]bâM59Ma_–ÅîÿÿÿéÄ–_MH5M537 p®½¹­¦~oiOOss–›–šš›–––›—›šâšÓÒÓÕÕ¡˜žÅÒÅÒÒÌÅÅĞœ‘’’’‘’j‘j?>60+5(9+9(    $,,9).(,!,(!!! (-(+.+.($!(--9+5$,M5OOP_Pi_POOk_ilh–9OM<9OOOOMM9-   '_iPP_O5   %&0:^qaM_ëM9,+,7,-5-OâMMM;aÜMJ7a•™ÌÿÿøÿÿÄ™•_O..3&7 pÏ®¸®¦„‚}sOsˆšžžÒë¡›–›››ÅäÅÅÖÕéðìéÖèéðèèÕèÕîÕ¢œœ ¢ Ÿœœœ¡“`;44H.M!+-    +,...((,(++$! ,((.-$+.+($+-7+-$+MOMOPPPi_Miik_PlslsO– }¸¹¸ƒ­¦ˆ‚ss_s–ÖÿøîøÿÕšl´Å³ìÍÕòÿø÷üÿýÿüøÿÿüÿûêûÿ÷ÿ÷êøÿ÷ÿ÷ç÷ÿÅ–M^.M+(     5,,J.+  ($+(--(+-+'+$+9M<99JMM]Oi_hiiMkii ¼åååÓ²Ó²›—lš—–š³óšks•›ñʳ³±³¯ÒóÈžÅÅóůž³ó™jj`jéj??k–í_OOM55&%&1114&  5(,H(.$    '$4-'  9;<88 #'#####  3333&$&$$ 3$$-+$(++59Õ9.--+(5!!(!+5H5NHO_a••••^;4 ×íæíåÓÝÕµž–—›››óšl›ñ›—µ´µµ³ÅôÅųÄÕõͳ™ž³ó—™–’éj??a–ìhM_kP94016:>?>60 -,)+JHH    # &&'3  8888'  &#  433'  + $$-+.$!+--MÕ95.--+7(!(!(.JJJ,HONPPP_aP;& ÿÿÿÿÿÿýöñìììðìððìšš–l•–›ð›ª››—–Ê󵳴ų󚗗žÄó——™›ì`>>a–ík_M^99744:?jj¡j?:0&,-,.,+,   # &  4'411       # $(+++-Mâ955-5.5$(,((,(,,.5JMJM5-3  âôôóôíÕÓųšžžžÄžššlˆ›ï—››ªÅñʳ³³Êóž¯ó›—›ñ–k^^—ìak_M_]M:6>`jÌÿÿÿ ?:&35(,H+(.      %&%&%    +$+($-5MëOMMM997,,(,.,)(,...M,,.(âôôõôíìÕÒÅžÌÒžžž›––šñš—›ª³³Êô¯šÍÊÍó¨³ï›í••ì–••__aOO]::`’˜ÿÿîÿüj>4HJ(H,,,(             +-+(--7_ëaaaa_9_+(.,,,(,C.H.,,+$ ÿÿÿÿÿÿÿýõóðððððððìðììëì—–›–³ÊóȳÈÍõ™³ñ™ñ™—l–ík•iÍÛÍÕÅÌÌÔèêÿîç÷ÿœ?7O.(H.!.  $$(+++5]kï––Ä•aP],5,,)((,,,C!-(! ÝôôôõñäÒÒÒÅ¡³ÅÄžž™š–šš—›ž¯Åó—ÈÒóų›³ñ–¯ñ–—kìk••i•ìi^_]6:^j“ ÿÿ÷ÿý˜?O].(HH((  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ +($+5Ma™øÿÿüÄaO.H.,(!)()(.!!(ÝñôõõïÖÖÅÄÅÅÅÅÅž ÅÅš™›™š³ÅóµÍʳÒóų³³ï–›ñ›™–¯™íkik••ìq_O^466?j“Ÿêÿÿÿç“a_M.+9,(! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ($$++5;a–ÿÿìÿù•^M.,,()(,()()$+ÿÿÿÿÿÿÿýúóððïðððìðððììììììðìóÕÅÈÅÒóʳ³Ä³õʳ—Äñ›™––´ï––lì_Oa<67;>`“ ¢è÷硘a_J..5,(( ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$$($+-MaÅÿéèîÿ™O5MJHH!()+()!,,(×óôôñððäÕÅÒžžžžÅžÅžžÊÊÅÅÓóų³¯Äóµ›Äñ›™––›ñ–k•ìa_]k466:>`˜ŸêÿÿÿÔ™k]J.7).( ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#+$(+9_•ÿüîÿù•]JJ..C5!)))(,,($ÓíñññóñÖÕÒÅ¡ ÄŞĜš™ÊÒÅÊÕóÒ›š—Äñųñ™—––›ï•–ík_^O_O;;:>j“ ÿÿ÷ÿü™kM95,.((    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(+-]aÅýÿüÌ_9HH5,,D((!((),(! ÿÿÿÿÿÿÿýùóðìðïóðððððìðììïððððóïóÒ³žÊÕõůó—Äž¯žíš—•–ík••ÕËÓÕÍÌÌèè÷ÿîç÷ÿÅaOJM,M!  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ #(++-+5]_k•Ä•a•5.H..,(,DHHC(,((ÉñôõôììÖÓÅžžÅÅžÅÄžÄÄ™ž ÅÅÄÒÒÒÅÊÍóÆÅ—³ó—›ñ™—•–ìkkiì_M]_OM`^‘˜ÿÿòÿü™kMMH+,,    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ $(--..M^_^OM-^(5.,,)+,,,.!,($ ÝôöúúõíÖÕÅÄÒÌÅÒÅÅÅž žžÅžÅžžš•–Åó™—–ìk—•—ð•sak–í•kˆk–ík_]PMMMk^a’ÌÿÿýÍkO55(ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷¢”@/öÿÿÿÿÿÿÿúõðóóóóóóðõóïòñïóïððëïïïïðìž™›—ﯙ™k—í•ka•ñ¯¯•—ïqP_qbiaPi••“Ì™™kM5,! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþêç”=2åúúýúõõðëÕÕÒÒÍÔÒÅÕÒÒÅÅžÅÒÅÄÄžž¯ž››ï›–—–—ð•kkí—–—í–q_MqNMqJOP_i•–•iPJ+-(ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿ 3+-+-....J-J9HH5MHHH.)-)/åúúýôýôðëÕÍÅÅÅÅžÅÍÒÒÅÅÅÅÄŜŞžžžž••šì—™–—ï•iai•ì–†––—í•iPMM_JJiHJMMPiqPaMJJ+!!ÿ€q_;3ÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ % ÿÿÿÿ4^—¨¨´ººÇ»×ÇÎÛÛÎÎÞÛÛÎÜ»ÁÁËÁÁ»»¿Á·»°©†q;/ÿÿÿÿÿÿÿÿúõóïóóóóðóóóðððóïóððððíðìðëïïñ—š–™›ï–•—ìik–†—í•iPOOMJJ_N55HJOPNMJH55!- ÿq_]M0ÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ %% ÿÿÿÿ6]k†››¨«°²»Ç°ººÉÇ»ÎÁǽ»©º·°º°©«©©©©©£€_72åúýýýõóðâÕÅÅÍÒÒÒÒÒÒÊÄÅÅÌÅÒžžžž™Äž–™›ï–k••íkai•›íitPNO.HN_....HP..J.,55!ÿbPM7&ÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ %%ÿÿÿÿ 4]q–†—£›¨°¨¨«©«©°´©»»«°£‰£©£°££©£©‡‡‡q]4/åúúýýúíìÖͳÒÒÒÒÒÒÅÒÅÄžÒÅÅÒ š™ž™šÄ³›ï—li•–ìa_a†ìaqPMOJHHJ-.H..H,,J.,..5!ÿNMM4&ÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ %&ÿÿÿÿ %8]^i†—‰—‡—£¨£¨¤©¯©££©¥‡‡€€€‡¨‡‡‡£yrrPJ42ÿÿÿÿÿÿÿúõóðóïóïóïððððïïïóïðììðëììððìïìïìë–•k•–ìka_kñqO_NMMHHN5D5.D5.,...,D9)! ÿJJ]-$ÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ &%0ÿÿÿÿ &8]_i€††‡†‰‡‰ˆ££‰£¨£†©£t€{rr€{—rrt€bNP7&/ãôöúöíñìÕÊÅÅÅÊÒÅÄÊÕÒÅÌÅÒÅÅž™Ä³ÅÄžžžž–•›—•–íki_kä—OMJMHH5_...D.J,,.,,,J.)!ÿJJH4$ ÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ %&&0 ÿÿÿÿ07P^bqs€†t€‚†€‰†‚€€‰†£€bbbbZmbb€bXPX_M5 /ÞôôöíôíäÍÒ´žÅųžÅÅÒÅÅÒÅÌÅ žÄž ž™žžžÄ™–™––—ìkkaikäqqkPMJJHHHJN,D...H,,,,,)O)(ÿ ..-.            &30   49^O_ibqq†qrtrt‚yttrt€‡tPXPNPXPNSkNNNJH-$/ÿÿÿÿÿÿÿúôñïíììììðñðóðòóïððìîìììììììëìëëäìÖââk_^PaäO^__NJ.H.HHNH.,.DJD+),,CH5,Cÿ-+++          # &-'&$&37MOMOP_rq_Z_bmtrqtmb_Z†rNMJIHPSMJIPHJH-+ 2ÏôôôæäÝÈ´›³³³›ÅÅÅÅîÅÌÅÄÄ¡žÄ  ž™––Êka_OOaäO9JaNMH5H.DM,,..,H((,.(,.5!!(ÿ(!($        %%  #& $'3&&$ $34999MOMPPMP_PPbNMNMZPZ_P_JH.H..,H..DM.,+$/¸ååå弸²›——–ˆ–—–šÄ¡ÅéÌÌÅ¡¡Å¡¡¡Ä ¡™š–kÅaaP61>0&&&6           ””   ” ” ””””” ” ”” ” ” ”#””# ” #””  ƒ¶«—s84&%%%%/0=====2=====2=>===%0 &              @ = @  = = =  = = =  = @ @  = = = = @  = = >«Š_<$ /2/////2///2/    /2//2//2/2////2 /2/2/2 /  2/2/2/ /2/2/2///22//2// /2/2/22//2/22/2/2//‰_M4%          iM8&     s_<4&                @ÿ ÿ ÿÿ #ÿ )ÿ 5ÿ%ÿ2ÿ =ÿ")ÿ%7ÿ&&'ÿ#,8ÿ(2;ÿ789ÿEÿSÿ "Kÿ &Rÿ)Gÿ,Tÿ1Mÿ3Yÿ +dÿ-`ÿ5dÿ;sÿ!-Aÿ)6Fÿ%9Xÿ3=Iÿ2>Qÿ$a•ÿE\‚ÿB^‘ÿKd†ÿHh—ÿLpÿTjˆÿTl“ÿYr‹ÿ[u•ÿJk¢ÿMp¢ÿSm ÿRs£ÿSt©ÿUy¤ÿUyªÿZu¢ÿYvªÿ[z£ÿZ|«ÿ\|´ÿan„ÿhv‡ÿez—ÿr|†ÿq~“ÿe}¢ÿ`µÿ^‚¤ÿ]­ÿ_ˆ¯ÿ]´ÿnÿi‚›ÿy‚‹ÿu…šÿ~‘Ÿÿe¡ÿb‚¬ÿeˆ¦ÿd‹¬ÿk„¢ÿl„«ÿm‰¢ÿmŠ«ÿa„³ÿb„»ÿeŠ´ÿe‰»ÿj„²ÿj…¸ÿjŒ´ÿjŒ»ÿk®ÿl’¶ÿu‹¥ÿuŒ³ÿy“ªÿu“²ÿr”»ÿt˜µÿt™¼ÿ{”³ÿ|”ºÿ}™²ÿ|›ºÿ| ¾ÿgŠÂÿl‘ÁÿqŽÀÿu˜Äÿ}¢Çÿ¢Ðÿ„…†ÿƒŒ—ÿŠ“œÿ’’“ÿŽ¥ÿ…–ªÿ†›´ÿ“œ§ÿ‘²ÿ ®ÿŠ¢¸ÿ˜¡«ÿ–§¹ÿ°¾ÿ¦§§ÿ¢¬¸ÿ¨²»ÿ¹»½ÿ…Ãÿƒ£Ãÿ‚¤Ëÿ„©ÆÿƒªËÿ‹£ÃÿŒ¤ÊÿŒ©ÃÿŒ«Ëÿ‡ªÓÿ°ÍÿŒ²Õÿ–«Æÿ–­Òÿ–°Åÿ”±Ëÿœ±Äÿ›³Ëÿ¸Íÿ“³Óÿ“´Üÿ“¸Ôÿ“¹Ûÿœ³Óÿœ´Úÿ¹Óÿ›ºÛÿ²àÿ’´áÿ”»ãÿŸ¶àÿ™¼ãÿš½éÿ¡®Âÿ¦¶Èÿ¢´Òÿ¡¶Úÿ¤ºÓÿ¤»Ûÿª¼Óÿª½Ûÿ³¼Èÿ±¾Ôÿ¦¼áÿ›ÁÝÿ›ÂçÿžÆñÿ®ÀÎÿ«ÂÙÿ¸ÂÌÿ¶ÆØÿ¾ÐÞÿ¤Ãâÿ¢Ãëÿ§Èçÿ£Êìÿ¬Ããÿ­Äéÿ­Éãÿ¬Ëêÿ¦Ëóÿ©Ðîÿ¬Ó÷ÿ¶Êåÿ¶Íòÿ»Òèÿ¹ÕöÿºáþÿÁÃÄÿÄÌ×ÿÈÑÜÿÖØÚÿÁÎâÿÆ×éÿÇÛõÿÔÜåÿÐÞóÿÎàîÿÌâùÿÙâêÿÕçúÿÕñÿÿÛòÿÿÜùÿÿåçéÿåìõÿãòýÿäüÿÿíñõÿëóûÿëýþÿðïñÿýþþÿi-j,]äîÊIs su,w,y,z,j,4s{, e- 288.0f- 6.0}  4.0€i €j-KÉÆ’K Kd,f,g,h, IK$R <wR *R 4Ki,R R @U k-O ¨;ÀS=š:W &:-Uç¨O&O !uQ{š:W ,x-Qç¨N&O !uQ¹š:W ,¶-k ç¨G&O !uQ÷š:W ,ô-k ç¨F&O !uQ5š:W ,2-k ç¨E&O !uQç¨P&O !u m-[ŽÕ¼:Ë„‚š:;&˜:t%3‚š:;,˜F%;$.Ε¢-Ý(.Ε¢ow88‚š:;&—:t%;$F:t €r-l-D{›º"rC*( CD p-`-lø)' zlD+  A @C,ÀB, 'DD €BD  ÀBD€A 'D ,D ,D $,,D  6CD D"'D C"'D Z'V DY D_ DD  ‘,,D  ‘,,D $,,Dx%´–x,|x Zgz|xL D@$ & {S’x&xL -ž'ªxL D.$|’~| ,&¥x½J D [K D O `.8 DD ,QD B"U D @"T D ~!S D }!V ,Y ,_ , `QU T S K J AE t-SôB& B-z-n7©4' E.VZQ¹³÷F›VQ:V¥Ç}@(u@( ã±VA?%}@'u@‚rV¥ÿ*R„„‚þC!H˜VÂj þC!L þC!Tõ;}}@„±Vk?%S‚±V^?%<„„„þC!H þC!L þC!T rc,*u@‚‚±V^?%$rV¥ÿ*R„„‚þC!H˜VÂj þC!L þC!T o-B AsóÙ`®V`›*Ì‚–B , "{B  :v. `® B v5B  :v$B ¥B (lB  b,wéû$ €s-f R‘Š&zf  : V # f  :Y # f  [_ ;  #Of  Jff  h %ˆ– ,n  ZAzn L -¡(~d€n~n, L -¡fd¥ ì Y-a,v=-Ösn <]‚-b wn *F|n qa,-b'Zn n @ o-bn q* @ €€-€€v-k E³Æ`lr`›*U -ž'T -ž'S -ž'K -ž'J -ž'(k . `›U -ž–k $&T -ž–k $,S -ž„–k $&š’k $&lK -ž(J -ž(^w`›*R. `›$ n €|-^,Ö1™~p^,,p\,,% W.€˜ÏI.],&<õc.²Æ×ÝÕµwc*|cĵ‚-d w|*-d‚‚w|Ö|ñ|-Ç||ÅKç-d.²Æ×ÝiÏ-], €~-C.~çy ·q %¯–q ,z q  Z¥‚q L -¡ {z ‚zn n €z ~z ,¥n ppn ,€z ~z ,¥q n  n €@€ÖO.xm!¬O@ nx‚w@ * ÿm!|Z@ wmŸ†giÂ;g@ Â,dÀ;i•¢@@ @ 4~'‚–w,ww%*w%mxwme'¥wÂ)rÚ*(MÚmxwme [,D.l·> HB.l]$þHlk:‚rl` š:k,E‚‚rlK K -ž š:k,R. `›$0‚‚‚rlJ J -ž š:k, ws*sVB=. `›$V  ~Y  _  ³‚‚‚rlQQ-ž š:k, ws*s9ZV  ~Y  _   ‚‚‚rlU U -ž š:k, ws*s#E=. `›$Ž‚‚‚rlT T -ž š:k, ws*s$n=. `›$(ü‚‚‚rlS S -ž š:k, ws*s$n=. `›$' €ìM.X (Áì& G.K.:M-C?rs*s.; Aws*A' €GJ.Z,2P 0,*š:Z, &AE HP.:meëW.ÍØ Íº¯¯¾?,?,Ø J%¯¯¾?,?,º°A €æQ.v zÛÔ¡šdnq ‚wd* ÿY!|ZdEYv ya­;yv %­;a•¢@dd4~‚‚‚‚wq E"•¢»-ç wE*(„Ea/!HEa/!N3š:q ¥Ç:E¥Çd^<g ;yv %dk<f -x Q&E¥¯q ¥¯ E¥`‚–b,wb%*b%$zv q E^_Y¥b˜wß*ß$zv q E^_Y  22U.v^¦3š›G²¯kvKv?vK-z'Êp-pSlÊ™-~SjÊÂv-KSmÊ-z']-zvQ:v¥Çv{kZ‚–W,wW%*W% ^v¥W™vl=vQ]vn'S €@R.€@S.€@V.C_xS”‡G²¯kCFC?CF-{'xp-x[h'x-{'J-{\CCC¥¯v'\C C¥G‚–R,wR%*R% _C¥R…CYC]Cj'[ X.aBÚÒW]}QFŒHWkŸ‚–D,wD%*D% a¥D\w'z"u'˜‚‚‚‚‚-?-Q  w<*š:;,—:H%-E•àserverÄ‚{•àš:H%a p.€Š¸Z.X,\/k‹EC‚–l,wl%*l% \X,¥l T.€\.Vbìö‚OV@<<V>‚‚‚V-E•¢a/!Y-?-T >‚‚-Y –o,$–o.¼•¢ö4™~ëVëo{%-Y'1›:V¥ÇoV l=o;¥ozVQ:V¥ÇV{kV-l 'æ‚š:;,V-EVA<EVVF‹Ho%^‚–o,wo%*o% bV¥oš:;&ç‚‚-?—:H%VVFV4TV4Sx'VVz V¥ Y.€è^.|Jį~Òkn|nwk*kpn‚–[,w[%*[% lk¥[+±‚–[,w[%*[% J|¥[nÐwÚ*Ú J| g.[.hŽ± a.hW¥){¢&r<h<h@ T < ‚-W wT *‰rT @h-W'T @h@T T @1 €zs 0™êA  C,B, ' \’  B,&  ÀB   ,  ,  l'  k'M  ^   L\ L €B,\’ !& €?  > K   ÀB  ,  ÀB,  u'    ,  ,%D $ j&D $ R,D $ y q p  L\ L €?, > K   ,,  `C, '  `C, ' `BS. 8  `BF. 8   ,   ,   z' $,,d   {' $,,` y  O\  Y}  O]  YB.8    ,@   |g   }f   ~d,`, M , ^ ,ÿq,q9'p,BSFy\}]@ g f [%–[,[D¥[UV%D-¡'y\-ž']-ž'p  S) e.}Tlx.W}}a}a _.€@b.€`.L V¦‚øB®VB›*Ì‚–L , "{L  ^A. B® B A5L  ^A$L ¥L (RL @ -ž™R, f k.@l`tÒ[Bf‚-~ w[*O|[Oq-~'c[[N ¼-~[a V[Oq[NBB[][ c.€L,f.jy%' d.fÃ9rB›*g -ž'f -ž'}g -ž(f -ž(C. B›$ h.i Câ ‚M # i  ^^ # i  K -ri  wVDm%×–m,mD-¡šm:V¥mž=š:V &qC(q  DpC'p þ¼š:V ,qC'q pC(p#  XDþqC'q pC'p S®VS›*Qi  Yð{Qm~Q,†–m%F QQ±F €QmQQ’m},W. S® B W5F Iy-ž™S®} ,\-ž'F®VF›*Qi  ^1{Qm~Q,Ç–m%F QQòF €QmQQ’m},W. F® B W5F Š}-ž™F®} ,]-ž' J €z.H RF#o,J <YwJ *B‚wJ H J -E¥ZJ J @ pH -E'*ÊH H C–Z’’:H:S:o*H H B–Z’:H:S*–Z:H €E €w-N_]ÃÙ£~ O,-a'~ 0!r [!red?¨¨NSCSETTEAMS%Nš b!b Ž!blue?¨¨NSCSETTEAMS&Nš •!g Ã!green?¨¨NSCSETTEAMS,Nš Ê!y Ö!yellow !gold?¨¨NSCSETTEAMS,Nš !t !team B!teams?¨NSCBALANCETEAMSNš I!p n!play?¨NSCPLAYNš u!s ž!spec?¨NSCSPECTATENš ¥!l Ì!start?¨NSCSTARTNš Ö!quit û!exit?¨NSCEXITNš !leave 0!bye?¨NSCDISCONNECTNš 7!o _!open?¨NSCOPENRCPNš f!v !vote?¨NSCOPENVOTENš ÿÿ-a(-a ~€u.i.y0÷&kq+C &D-¡p,C ,D-¡ q.s.A¨)C?rs *s .; Aws *A' €Gr.H,2Xh,'%š:H, ,V E/t.w.zf=.š> &D-¡pMq ˜• ,D-¡pU {p ˜ €@v.y.||"1 O. S®˜wO*|{O5\zo o O5|o ppo ,O5O. OÞo  O€@.m u"Éi E 0Lm m Xm q™E % -u  'sE m Xm q $I,E KT,\E w-Q kE -Q P€x.}.}—34 S. F®˜wS*|{S5\zp p S5|p ppp ,S5S. SÞp  S€@€F/OI'eƒ-P'<z‚-P w*c‚wO|qOq-P(@-P H|.y·B8ñHyR¾‚‚ry@  š:R,@ -žl M  ¾{ls ;Yl|} ^  zƒ‚‚ryg  š:R,g -žl M  ƒ{ls Xs=. B›$l|} ^  zã‚‚ryf  š:R,f -žs #V=. B›$‚ryB š:R,f…‚ryS š:R,\-žrS›*…wS›*d" . S›5‚ryF š:R,]-žrF›*wF›*`" . F›5ì‚‚ryy š:R,y-žr d ì WrM. S® B M5ry-ž™S®} ,—‚‚ry\ š:R,\-žS›eS›*\-ž'y-ž™S®} ,€‚‚ry} š:R,}-žr ` € XrM. F® B M5r}-ž™F®} ,+‚‚ry] š:R,]-žF›eF›*]-ž'}-ž™F®} ,ï‚š:R,ya/!dq ÿÿÿÿŸ‚–q % –J,•ryJDq Jœ¥JYï™q %J%é–J,JD-¡šJq ¥J±y Àn.C,`…¼àˆ1z•à¸k¬C,•Ïx <†wx *orx ×*ax x x @< ôGk#‚·/a0 GwG*10grG*èCRITICAL EXCEPTION, Nexgen controller not detected.a ~G-Ea G?G'G~(E,F,*K'GNG7GG|(VamG7GG}(VaGXGgG| v €F,@G/I/Sj7VçO 'NSiS Password"v(S"s(SX1r( }Sq {Oq(O]IS-S'z!Vop(fNexgenIDUsedDialogÝ‚-SuSJK-S'z!Xon(fNexgenBannedDialog%_J&_K‚‚‚‚‚-S-?{Z%{OOZSSD&„S-E-s-S'z!Zol(-L‚S-E-sfNexgenPasswordDialog%_T-L°‚-SRS-S'z!\oj(fNexgenServerFullDialog%_RH&_RS,_Ro9‚‚-SS-ESSA-S'z!^oL(fNexgenNoPlayRightDialog@S§‚‚-S –L,wL%*-SL%jSzo¥LD‚-SL%‚–L,wL%*L%hSzof_¥L·L{fS)Gf%_&_,_,_TSA(oå­S-crMNSxMNJ(bS H/–€Q/A,KcƒP­]‚-E –],S„–]Z %±¯k]u pA-E'Z¥]«-E]Z §ÿÿÿ]u kA,DNXOCS]Z  €W5@,u°Ùnw!–w,vw¥wiv&[–vw“v&BvB¥v(v“w&%6TvB~+&6TvB{+,6TvBv+,6TvBs+,6TvBp+%6[vB@,&6[vB}+,6[vBw+,6[vBt+,6[vBq+6w vB@ R/AET•3F!–:A:FAF'DB—:A:GAG'D( €q-\/?EÛ6 G€òA/RC¢ŸäoR<ªwR*T{RR¥¯_R“›RQ:R¥Ç^RRR@ ‚•¢-é›:;,;$S8µ•Ïhh•Ïk¸eÝm±e•Ïeo Y/+LAÂL¢™‚‚-G -H –h,šhZ +b²¯khu pA-G'j-H'hZ ÿÿÿÿhu –¥h-G T/Bb;‡/B–BABA'@>—BBBB'@( €_/M!%Á+,(—}M}M€M}'*( z_4*î3A A,$, A, '  A,  A,4 DC 'J$ @ 'K$ A 'H I ÀB$$,,C [I O 4 ‚BD«?,€@'{4!44 ,4 €A4 €A4 B 'R.4 8 4 C 'K.4 8 4 L{4L4 C,{4!44 €A4 D '|4o4 L{4L4  ,,4  €B4  €B4 €A4 €A4 E 't4p4 F 'w44 G 'd44 H 'K4w,d,ÿK,ÿIRKtwdHy|k|-Ü'R-û' P/oÎ]éÒ$™F%¦Fx‚šF%š:;%;$n³‚šF%š:;,[Ö-bx„‚‚‚-?—:M% r<*#±¯kW?,<:M-?(b $„-M-~(З:{%Œ{К:{%debug gpf V€U/z yù¬ª‚–z ,"{z  y -#z  y|+P #PPwtwSz ¥z  ýc/zLŸHâ rz* {•¢iNNXOCÿÿÿÿj‚-~L{z ó·‚–},w}%*}%LzyN¥}jàwÚ*ÚLzyN V/rnÿÚî·r<µwr*‚‚-?—:H%rrFr4Tžr4Srr@  X€^/€@X/| |–ÝÜ‚–| , "{|  A -#|  Ay+uQ. K® B Q5uQ$| ¥| K®A €Žk/i e <'ä-X„-XEi T$$d-X„-X%Ei v$i T—}i Z,€i Z-X'C%Û–C,-X„-X!Ci {,¥C—-X N/l/LT£9 Z/CwNñž^‚‚-?-M-~-~']$œ‚–C,wC%*C%w¥C^ ]/ak²V(u ~ ht pua. R®ÿwa*ã™a$%w. K® B w5a5w$a$a. aÞbR®VR›*ewK›*K›-É(K›*v fF{v #vsvuJsaK iuw. R® B w5a5w$a$ae‚K®Aw  ed  N K  O J-¡ -I K-¡ -L F i/z+MûQCaa z+alwata a'a a_ ¥W+a_Aag VA ‚–x,wx%*x% aa¥xÊ a/S,ôÊ"-?‹v~‚‚-?-M-~-~(]$¼‚–,w%*%S¥~b U€e€d/ZkK÷MÝ«¬€? A•ÏK‚–Z,wZ%*¥Z" b/HFít÷XtCV™X% -#X yVoA . R®wA *™A $% 6#A $ AWoçz@ @ W@ ppp@ , WA . A ÞoHp w -*.unrv{VHppH ?game=V¢{@ HppH ?mutator=@ HppH  d |V| 1H y/u+Nà]@YP‚-U –w,Frw%*-U'w%u+M¥w-U B €q/p jîf#"‚–Y, {Yp ^-e„-e!Yp ^, -e„-e!Yp Y,ÿ-e„-e!Yp ^,ÿ-e„-e!Yp K,ÿ-e„-e!Yp w, ¥Y-e m/€ŠŽo/€@|€€@h/Z_%•ZtCw™Z% -#Z yo+\ #\[\[w  [ }/ftÃ…`ù‚–k, {kf:-|„-|!kf:,-|„-|!kfh,ÿ-|„-|!kf[,ï—}kfJ,€kfJ-|'¥kk%W‚–k, {kfX-|„-|!kfX, -|„-|!kfj, -|„-|!kfI,ÿ-|„-|!kfu,M‚–kf€O%(z kfu-|'kfuPlayer*¥k-| Ôg/`olŒüUp‚‚‚w`*`a/!H r`x„þ] !S þ] !A-M_.Ž`H in`Ü‚wi*iQÚr`x½-M'Úi4(‚‚r`* wx*rxï*H $··‚‚w`* r`xwx¥*fþ] !Sg$ˆ€þ] !Ag$ˆg$ppx¥¯: H g‚–d,wd%*d%%o`xJH ] -K¥d·Qwñ*ñ%o`xJH ] -KS' K/k{Ø sïrN*N ãkæND´B•Ê ¸@¬¯•Ê|•Ï|•Ê-i„„µvk÷µyký*›:z:¥Çñ-iN.¹kÍÛêÈsk÷kæNkaÐOnline [00:00]uq΂¥-À$¥-Ûl,QI‚˜%:¥Ç&˜:¥Ç,l:¥ÇQl,dly&vk÷ykýz¥ÇL?D«k÷@?|?D®q€@`«|?,n‚—N%±¯@6w %{€Al&U–lN“l&{l{¥l"¦NnšN%P*Ù‚—w%±¯@6w %B@Al&Ò–lw“l&BlB¥lŸ¦wB•Ê d€p/v/0[(C?r_*_.; Aw_*A' €u/C MHð*;L. R®žwL*‚™L$%\zC C SL$‚C pppC , SL$L. LÞ_’MJ-¡K-¡tCC  w  d  K  Y€{/x/O°º9ƒYV•¢ë2‚‚-s –R,{Rpt~Rp=(‚—t%#|€RptYqRp’t&rÜÃq Š‚wr* ar•¢ë-s'%¥R/¥RR-sX rzx(Y-s Hw/lhM0CHlm:‚rlI š:m,kk‚‚rlC š:m, w_*M¥‚‚rlH š:m, w_*_D‚rlR š:m,wK›*K›-É(K›*``‚rlK š:m,`wR›*R›-É(R›*¥‚‚rlR š:m, wR›*C. K® B C5. R›5C$. R›$@wK›*K›-É(K›CC-É'R›eR›*KAFØØ‚‚rlK š:m, wK›*C. R® B C5. K›5C$. K›$…wR›*R›-É(R›CC-É'K›eK›*Fû‚rlt š:m&_‚rlw š:m&FA‚rld š:m&F |/P‘5+a ;$F:Y D•¢ì¶•¢a/!YJ=.¼•¢ö-p-B -N-D -x-H P& E0Rs‘/¨set Engine.GameInfo GamePasswordO{ ¨set Engine.GameInfo AdminPasswordOV •¢»ßV•¢»úv •¢»Ùw •¢»Øx •¢û’’:H:S:o•¢è:R-t set IpServer.UdpServerUplink DoUplink True9set IpServer.UdpServerUplink DoUplink False•¢»Ô%\•¢»Õ&\•¢»Ì,\•¢»Ò,\ B0n w¨±0—}n h,@-d'n h-d„-d!n e,-d„-d!n N ,ÿ-d„-d!n O ,ÿ-d Gz/n+2Á^<'%š:n+ ,k `/K0Ja|; s/3{)½}t{3O?D¬¯`@@@`zb~a õV`3OVb3O3á$3­b(dÍÌÌ>3·€?€?34aÒ L)¯L@¯`@€B€B3­b(dÍÌL?3·3&aÒ ]L€?€?€?3·¯`€?3&aÒ ]L€?€?€?3%·¯¯L€?a ®O€?3&aÒ ]a €?€?€?3·€?3-aÒ ]€?¯`@€?€?3·¯L€?€?3-aÒ ]€?¯`@€?€?3·¯`€?€?3-aÒ ]€?¯`@€?€?3·¯¯L@a €?3-aÒ ]€?¯`@€?€?U`3O¯¯La €?€?Ub3O¯¯La €?®O@íwP*3á$3­q3·€?€?3HaÒP¯`@¯`@?Pê?Pæ3á$3­b(d€>3·€?€?3HaÒ á¯`@¯`@? áê? áæY×Ý-Òx3pp(>×Ýù_X®`@i¬¯|q@i%ЖiNY3Xii{¸i|¥i‹X€?i®`@&×Ý-Ò¸i|i%r–iwY3XiiB¸iq¥i- T€L0c‡Ý-T„-TEcY $$<-T„-TEct$$-T„-TEcU$$<-T„-TbcG%'-T„-TbcF%'-T„-TEcE$$<-T„-TEcf $$-T„-TEcg $$<-T„-TEcM$$xj%É–j,-T„-T!jce,@¥j…j%–j,-T„-T!jcp,€¥jÐ-T M @C€@^0D0T=*½Cb%qC-M '¹‚{C„-M  {Cq-M (¥‚|€C’}e&pe-–~ëCTUTORIAL%¥dCbC&!Çšd%g§dCb%qC-M 'µ‚‚{C„-M  {Cq z_-M (¡‚|€C’}e&pe-–~ëCTUTORIAL%ššeg_C¥eCbC&õ_ zB c"º;êA DC,S c"ÀB,cc €Ac  p"'$c ,B c$ n"G c$ k"K c$ i"D c$ d"€B,cc €Ac  _"'$c ,J c$ Z"L c$ U"B G K D J L B -¡|1#-UseNexgenHUDtruetrueG -¡|1$-FlashMessagestruetrueK -¡|1$-ShowPlayerLoctruetrueD -¡|1"- PlayPMSoundtruetrueJ -¡|1(-AutoSSNormalGamefalsetrueL -¡|1"- AutoSSMatchtruetrue @0Tx}Š#Tá$T­b(dÍÌÌ>T·€?`T4aÒ P6¯L@¯|€?BBT­b(dÍÌL?T·¯®`|€?T&aÒ ]L€?€?€?T·`T-aÒ ]€?¯|€?€?€?T·¯L€?`T-aÒ ]€?¯|€?€?€?h¬¯|q@TæNTá$T­,NT·h®h`T aÑm+( HF0~Z!dH~|Ô‚r~B  š:|,1-<UseNexgenHUDTB -¡1bÔw X *ÄB -¡C'ÔC(o‚r~G  š:|,1.<FlashMessagesTG -¡1bc-oG -¡ ‚r~K  š:|,1.<ShowPlayerLocTK -¡1bc-XK -¡{‚r~D  š:|,1,< PlayPMSoundTD -¡1bñ‚r~J  š:|,11<AutoSSNormalGameTJ -¡1bb‚r~L  š:|,1,< AutoSSMatchTL -¡1b F€€˜O0xH^æ “HzxV-F'xV[NEXGEN] Another UT Server-F„-F!xV,€-F„-F!xv , -F„-F!xw ,@-F„-F!xx ,@-F„-F!%x\,À-F„-F!&x\,À-F„-F!,x\,À-F„-F!,x\,À-F„-FExH$$ -F„-FExS$$-F„-FExo$$-F„-FExR$$Š˜’’:xH:xS:xo%-F'xH$-F i€G0jYª°;g¯@6w bx‚-o °g€@b ¬®€?¼«««g?,ÛI@€@@i«¯€?b Cvl+jæNjá$v%9–v,j·vj+j­v6[bNà‚-o °g€@j­b(j­333?6éj­=D®«<6éj­b i6ìj­=D®«<6ìj­b i6èj­=D®«<6èj­b ijaÑv6Tb(jaÐv6TbIi+¸vI¥v¯ R0VQ?½ $-i„šV[%›}Vb, -iVeV{ V.Rget Engine.GameInfo GamePasswordVV V/Rget Engine.GameInfo AdminPasswordVZ]%–], ]VJ¥]î-i n€/d0ztE? at ’-n„„„„„„Qa Ha a wa ta ja ea‡-nab-n €S0€@w0Lh)%Ú^ ¨keybindingL[™~q,%|€q~q,f|qz ^ ^ |¦^ pp^ ||¨¨set inputL^  ]0k+]<Þ(0.k+; Aj' Q€W0€@M€zf2)ÓaA$,, C,ÀB,€A€A€A€AÐâ'$22 ¬HC?, '2 ,2 ,2 V '2 W '2 X '2 Y '2 Z '2 [ '2 \ '2 ] '[2Z2X2W2o2$p2$q2$s2$ ^ '$\.8  _ '$22  B,sr•Â*2> K €B€B$$­21J•Â'€B€B2  @B2 ,2 ,2 ` '2 a '2 b '2 c 'R2P2O2N2 L'$f.&8 &o-ž'p-ž'q-ž's-ž'Gi| _€Z0MG{°nMÐQ .µM’wQ *[SQ ðZSQ ôXSQ õW+pSD«ÈB'D%o-¡'-pp-¡'-Nq-¡'-~s-¡'-x e0ab %ü3›:A;A&›“‚‚A-?—:AH%aaF›-Q'ø-QA[A<J[&a a¥' c0[0eö"çr„-L $‚-Y -?QzC(•lC(ã_œÃh Š£r_*(kT½zk(lO T{l #lglD)gegQ{ep> eÑ`pppppk?game=h?mutator=aN B)k• l`(' \0R i”KéR VR p€R ~R ..unrR R P/•üÙO/•üØN/•ü× €_0J|¬ƒÊa cÈ{a #aHaGJH A #Gh+EJ. \® B J5E c€o0`0hⶠzb# m0€j0fjúF c6•¢a/!YeJa&Ë_ffF-c'Ë‘A-~eJjËÃA-NeJ`&Ë-c'O-c-uAD;-uA<J\&f f¥OeJ]&‚-c-u Òr/rl´ïÙ°„‚rr•¢:|ê}}•¢â•¢âT‚rr•¢:|ê}}•¢ç•¢ç(ê‚wr*ra/!fV.˜r¥-Z ‚‚wV*ra/!A0|€}’}V¯&pV¯:Ž‚-Z  rrD-R_.Žr}’}V¯&-Z Bnr„‚wB*BQ‰‚‚‚‚-?-vš:;,BBFBBGŽrrDq-R'ŽB4(º‚-Z  rrD}$EE‚‚-Z  wD*1„rDï*Dïa/!w"rV*}$EppV¯: }$‚–E,wE%*E% lrD}-NP¥EEÕwñ*ñ lrD}-NP×' x€_a0z7ÊUˆ-þ}!bdfzAYþ}!mf az†þ}!jcfzA N0k0z×ö–é$-ùE!GÍ—: U%mSú%D¯?’&: U¯]s6i) " h$mï6i) i$6o)%N6w) u(6@) o(æš:';'%E!F6i) 5 q$S'F6o)%N6w) Y6@) \†š:';'&E!E6i) m$6o), N-6`)'6w) Y6@) \Wš:';',E!D6i) 5 k$S'F6o),N-6`)'6w) Y6@) \ëš:';',E!B6i) e$6o),N6w) A)6@) z(p{•àE!A6i) d$6o),N-6`)'6w) u(6@) o(à -?E!@HÐÙ.‚—H%´­?‘H,?,?&o‘H,<pD­?H?,<mppêp0So,:êp0Sp,kmppR v/R T6i) " X$m6o),N6w) B96@) [9E!HÐÙk—H%o‘H,<pD­?H?,<mppêp0So,:êp0Sp,¨mppêp0S•ü,:êp0S•Ð,6i) " j$m6o),N6w) A)6@) z(F@-6`)Ï‚–] ,w] @Q*Å] @Q-q] @Q8wE6i)6o)6w)6@)F¥] ,-6`);F) q0toÙá7ttAyJu&µ{‚t-EA RtyJi&µ­A-~yJjµ-x'Ø-xtyt$-x )€@|\h0g+Hãeš:g+%G v0€˜~€Äp0`kl‘a R)R›:•û:$Q)a fs-Q+a A"‚a P"¹-IN)PátK)i‚h8„-I $‚-Y -?F)-EeU-E iE)U1J) }wRP.Ε¢-ò( -G.Ε¢ç&/‚-V  -GODa tj•¢a/!YCa GÚ•¢õ•¢õ•¢~•¢t`%–`,`Z ÿÿÿÿ¥`Ùa `"ÄXg|h•ÏA ua«¬€? A•Ï'( }0 60u0Aq²Ka A<Owa *8a -E¥@a a @„š:AR%}J&ÛÓ‚™@:ARA-E}J}&Û-~'þ-~AyA$-~ K€i0t0~Q3¨’6w7 d66@7 g6w-l \!}6i7 c$6o7,NÃ-™y%\!|tSy6i7 " b$t6o7,N6w7 \86@7 ]8-6`7'ÃT„„—:c %—:b %—:A%\!{s=úú:c :b :A6i7 # Z$Rs—:b %6o7, N16o7,N6w7 R86@7 S8Ãå„-J'-m \!z6i7 [$6o7%N6w7 ^86@7 v8ÃD˜Â%\!y6i7 Y$6o7%NÃ\!x6i7z°-E6o7,N6w7 I86@7 Q8Ã6o7,NJ@-6`7x‚–Z ,wZ @Q*nZ @Q-qZ @Q8w\6i76o76w76@7J¥Z Õ-6`7;J7 7€@|G1utƒGÁ)u-EgJN'š[A-pgJlš„u-KgJmš¶A-~gJjš-„„–f%C‚•¢a/!Y$™f.¼•¢ö —f,gJG'š’šf:u¥ÇgJ(J@'J Rfš-K'¸-Ku l=f-K x0€˜{0Ogí;eY¨keybindingOFëf+1{Fb~F,{–b%kFF¦k€FbFF’b},b~ëYk.™b%Yp€YbY’b}k.zYb}||Yp€YbY’b}|>¨¨set inputOY X1€îI€y0€[*e1`+gÊ¿¼I¨keynameS`+º‚-s {Igh}ìhKhIhKh}-ž(h}ìI-s( z@n/ (A ÀB, 'B, ,non  V'$nn B,n ,n $,,nR 9 #z'n ~#n ‘,,,n ‘,,,W %Û–W ,Ln$u L '$b W  Z+zb u > & {S’W &u f_+Ñu ,b ’~b  ,&½)€b ~b  ,u fb+Lq$Ñu fa+¥W a@ w#%} v#&} u#,} t#,} s#T{ n#y m#w l#v k#H i#p e#¹-ET r#ßT q#B&—:';'&H-ž' 0 'noplayright's0jV³þ´ÎA?D«qÍÌÌ>iæNiaÐ6ijQ[+QõuQ€r6wj*¸Q«@AƬ±ÀAe¸Q®e«@@AƸQ®ÀA«@@AQ @1 'serverfull'A1 'invalidpass'D1 'banned'|0C1B{âS¨„-E$Ð-çqÐ-ç@-ž'W%¥–W,W}-ž'¥WxúW%ú–W,W}-ž™W:'J¥W¯W%Q–W,GW}-žW}fWc+¥W WµF1 'duplicateid'HB1X  ¯ HX mž‚‚‚wX * š:m,X a/!v.X -žX Ÿ@mutate nsc balanceteamsž Û%}mutate nsc setteam 0ž &}mutate nsc setteam 1ž T,}mutate nsc setteam 2ž ‘,}mutate nsc setteam 3ž #TØ-Emutate nsc playmutate nsc spectate.¿Á ¿&ž l{reconnect.¿Á ¿&ž ¶ydisconnect.¿Á ¿&ž àw exitž 3vmutate nsc openvote.¿Á ¿&ž iHmutate nsc startž ›pGNexgenAdminLoginDialogž ÿÿ J1 5M1iv5µùxJ['}JP'ëd+ |A SETTEAM-wtiJ%e+}xJO'› ÄABALANCETEAMS-wji}xJb&› ðAPLAY-woi}› A SPECTATE-wqi}› MASTART-wbi}› zAPAUSE-w]i}› µAEXIT-w'iiExit› üA DISCONNECT-w'ii Disconnect› +A OPENRCP-w'iE› A OPENVOTE-w'•¢õ)?BDBMAPVOTE VOTEMENUi› ÿÿ-q'Ä-qi4J\'÷÷-wi4Jx} GE1^+2ÔþFDš:^+ %o V V€K1 4L1 3P1 2ôN1kÂ&A.ÑJA A "BDBMAPVOTE VOTEMENU"@Q1 1R1 0S1 'CorrodedMessage'U1 'Burned'c1GM*(6Mj„„„  J ™G,z fz g Æ–G%G%¸‚–G,"{Gp¥G|Æ™G, Gppp f= gb $  V1 'Fell'W1 'Suicided'[1 "debug gpf"_1€îA€zmP.]¼A @C,a.&8 &8C,PP  C,P ,P ,r%þ–r,rMPR : U! Rr¥rY P wZ P v\ P t] P sPPPPSP  C,PP ,MP mNP nOP oPP ,^ P$ p_ P$ qPP$ rS,€r%˜–r,rM¥rlMNOa] Z \ Y ^ _ P^ -žÐ-ç_ -žÐ-çFI \1 "PAUSE"a1 "OPENVOTE"Z1Ir7~^ -¡'-p_ -¡'-NP-¡'-~ \]1\+H™š:\+%I €^1`F“€C`.a›-ZÐ-çár`*_%š–_,_M-ž'_M fc¥_SY -ž'Z -ž'\ -ž'] -ž'A_%Ä–_,_M-ž„„„-Z`tš_:`s ™_:'Jš_M-ž_M fcº_Mf_]+¥_èY -ž(Z -ž`t\ -ž`t] -ž„`t -Z b1 "OPENRCP"k1 "DISCONNECT"p1TZ ®0ü?„„  J –:T% ™:T, S :T¢–S “,&S p’S &p¥S K“,&pb $  _`1_7Àü”-þa!bda_^_þa!ma a_F’þa!jca_^F HB2H™/HHQ-š:Q,‚wH*Ha/!XQ .ÀHî“‚-s šhQ h}-ž(-s(Ø-sh}-ž(Q }-ž'hQ Q }-ž'hQ -s'---sh}-ž(-s( Q0w1m„XB( Óf0] n¸‡:‚w] *] a/!fL.˜] ¥á‚w^ *1„r^ ï*^ ïa/!wJXHQONM¾rL*J$áppL¯: J$B‚–v,wv%*v%)n] ^ XQONM¥váƒwñ*ñ)n] ^ XQONM…' d1i1Þ›C?rm*m.; Awm*A' €Hh1]õÑ"|H]L¦‚‚‚‚w]* š:L,]a/!v.]-ž wm*] Mmj¦ ©NmS¦ ÃOmq¦ ü%Mm#c.a›d%¦ 5&Mm#c.a›d&¦ p,Mm$c.a›d,¦ «,Mm$c.a›d,¦ á] m"N.a›d¦ Z m#g.a›d(¦ O\ m#g.a›d'¦ £Y m@O.a›d S ¦ ÿÿV‚‚‚‚w]* š:L,]a/!d.‹]-ž wm*] ^ mQV 9_ mRV SPmTV ÿÿz‚r]a š:L,F l1 "EXIT"m1 "START"o1 "SPECTATE"~0`UÝjº>U-6`` ¬®€?¼«««@?,ÛI@@@P«¯€? CU?D«qÍÌÌ>E ®X+UÓw6w`*³±ÀAG~GxPà~ÀAx®P?D¬¯GÀA@cá$c­qc·E xc+aÒ6w`~~ÀAÀAÀw6@`*cá$c·E xc+aÒ6@`~~ÀAÀA¸E ®U~x®P¬¯Gq@cá$c­6o`ø-6``c­b(c­333?6éc­=D®«<6éc­ P6ìc­=D®«<6ìc­ P6èc­=D®«<6èc­ Pc·E xcæNcaÑ6i`( q1 "PLAY"x1Y\ÝÁ*{M„„  J ™Y,z ] ©–Y%Y%›‚–Y,"{Ye¥Y_©™Y, ` p ],ó-Y+` p` P-Z+` p` SYe` b $  r1 "BALANCETEAMS"s1 "SETTEAM"t1 "NSC"u1 "server"v1 10.0z1 'NSC'|1€˜y1f^»‡$ü?„„  J –:f% ™:f, K :f¢–K “,&K e’K &e¥K K“,&eb $  }1D¢S!Œ"  J E{$   ¥{$   {1 "NXOC"Q+ 15.0A2€˜^2@syY+"  J @=û:@%,<=û:%,}=û:}%,<|û|%'{û{%'z=û:z%,<y=û:y%,p=û:p%,<o=û:o%,xY @tU}G|F{Ezf yg pMob $ 4i  \ 4z~1p 2¿MA @C, '"" C," €Ar "!""  d '$" ,"$,"  ÀBJ"  g I"  h A"  i  "  j w "  k "  ÀB"  ÀB" ZP "" e M " @B$" f N " @B$" Lr "L" B," €?r "!"" > K " €A"  l '$"€A, '" ,"   A"$,"$,v "$"  @Cv%.–v,"& Rv $¥vév%…–v,vI " €B$vI ,¥v5" m "  B '"s " n " Lr "L" €?" > K "€A '"" $,,b" [["d" O €A, 'p.&8 6q  q M 9'M ,N 9'N ,P , p pqp dÿÿÿÿp h r p s$s -žÐ-çb[ds pq M N P JIA w v v%?–v,vI ¥vFF V€C2€Q2Ucuß±UëU~ëU+¨‚{~ -YD~~,d–D%V~~V€~D~~’D},-Y™~UV%-Y E2€î1l F¥IHM  R TN  R vP 5  O ZJ-¡ -sI-¡ -vA-¡ -Y  -¡ -Mw -¡ -Q v -¡ -T l %Ë–l ,l I # l  {¥l ƒ -?[ p 5[ o [-ž( €D2FÁù#q -žrp›* F2HNÐßp  k %Z–k ,k H k I  ¥k \ NH f1P2E’]D L2 0G2Oä7X  \@O.p›d P   J2j @÷”!  i %Z–i ,i j  i I  ¥i \­_JM  JN  P  J-¡I-¡A-¡ -¡w -¡v -¡%j &j ,j ,j  M2 4T2 3K2T%  \ _N2H7$‹(Ž-þI!bdpHE_þI!mp aHFŒþI!jcpHE R2€˜Y2[Sì Û%Ù–,ÿC¨keynameSÏ{CX¨keybindingC` %Ï–` , ÅcX` K` }ìC¥` †¥ €˜HO2m@R,UHmx º‚‚‚wm* š:x ,ma/!v.m-žm sdFº „b@º •s Nº ¦q Oº ·[º ÿÿÞ‚rmp š:x ,FS‚‚‚š:x & -? wm*(„ma/! ma/!d[-ž' U2 2Z2 1S2W2h¯1C?r\*\.; Aw\*A' €GV2L+2o4'%š:L+ ,F z[2J$.êa€A M#'$$, ,  ,  I# F# C# @# ~" }" |" w" u"J%&–J, J}sJ}û$J}-ö(J}-Æ'J}-Å'J}-Ä'J}îJJ}¥J[[ P 0\2 "set input"]2 "keybinding"e2 "keyname"`2I+rO§±"  J -u -I+-X-J+-x -K+-V -M+-U-N+-Q-O+-k -P+-]-R+-B -S+-D -T+-H -V+b $  M€@b2p%¼+"  G -x-x] $Û-xE|$   ¥)E}$   ¥  d2E O“¦ÕÎ5„„›}^,  zE ´­?}E ?,?&%W =œf,ÿ&W =œ:=Äf,,ÿ,W =œ:=Äf,,ÿ,W =œ:=Äf,,ÿi“‘}E ,&Æ™i%H ~^E ’i,&&â–H %îl=H H ~^E i,&–H %5l=ž:l”H ,rši“‘}E ,&L=:l:D­?i?,W ¼l=:l:L:D­?i?,W Mpì:lMLl¦i¥M t2m )+"  G -m -m ] $Û-m E$   ¥)E~$   ¥  O€@i2jRiÜЗ„›}^,  zj%f =œf,ÿ&f =œ:=Äf,,ÿ,f =œ:=Äf,,ÿ,f =œ:=Äf,,ÿs%˜s}jºšs}jeg g $$õšs“}j&e=íjs&g =§$e=íjs&g =íj’s&&e=:e:D­?s?,f :g OppO^œÄ:e,,&^œ:e,&¥s‚O } "|"€L€H2j22ž$F o2F+rT}ÎF+fG+^ €zt0-P¡A @C,k.&8 &`B,00 ,0  ÀB,0  ÀB,y 0 s 0z 0 t h 0àB,00 ,0  ÀB,0  ÀB,0  ÀB,0  ÀB,0  ÀB,0 kB0{ 0 u 0| 0 v 0  ÀB,00  ÀB,00  ÀB,h 0$ j0t0$ Rt 0Z0$ xj 0ŒB00 ,D0$ w E0$ x 0  ÀB,N0 y Q0h , B,út ,t 9'j ,j 9'Q,úky z { | Nh tZDEt-¡'t  3j  7FNI h2@3Q«@H k2IƒëTD-¡'-m E-¡'-x m2qF‘µÙq.k›y -žrq*z -žrq*{ -žrq*| -ž„rq*Hºrq*h  ×h  qh w2D+xHêÌfD+^E+ n2N¦rBt Ct-¡j CZ-¡ p2r2·ñC?rt*t.; Awt*A' €q2HPÍ7Tt-¡H $\J t  ¨Z-¡H $\J j  ÂH $tJP.k›dH\ B  {2H+jó‹µ"  G o•Ï³wo*œoa/!H.Žoo.Žo sÀ@.Žo yH+%ooï6   Hs2Yç±DHYRŸ‚‚‚‚wY* š:R,Ya/!v.Y-ž wt*Y «y t"a.k›dŸ ÿz t@b.k›d h  Ÿ S{ t@d.k›d B Ÿ d| PŸ œNt$j Q Ÿ ÿÿÂrYk š:R,F‚rYh  š:R,h -¡'t-¡(Z-¡(NÎw‚rYt š:R,h -¡(t-¡'Z-¡(NÎ΂rYZ š:R,h -¡(t-¡(Z-¡'N‚‚rYD š:R, wt*tmB‚‚rYE š:R, wt*tp F3|x-×Ƀc/a0 §|rFrV|åq™F%YzccSFqcppc,SF10-b'X n1z Bˆ3‚z -Àz -ÛT,€x‚˜%:z ǘ:z Ç,T:z Ç€T,T _v2^79'*”-þ_!bdk^l_þ_!mk a^F’þ_!jck^lF åg1i ?HÞIcnI-H ni bFÃwc*k-HDvcbFÃÄ|i asc#get#window|i hz0090DvcOPENRCPF-a'G‚-H -a#‚–`,w`%*`%?i I¥`ÛGwÚ*Ú?i I A3C+P­úªD„  G  H u SC+kru*  š:o&NpMSú&p||š:o,[•íZ•öV•ôx•üy•Ð vú&p[ZVNpU \[ZVxyt \Nu(GNexgenJustBannedDialogqtuaRF% u  ¥”‚-T –:w, Šz:w^-T'‘‹w@¡-T :w^u:wYuX:w^uq:wKq:wwNb $  T€\y2B+HTa-š:B+%I ~ €I3@+S²qj~ .>@+~ qA+~ a ~ ~ ~  D3€@G3~*d…Óóê"  G y  S~*Iry * y &GNexgenJustBannedDialog*-y aRG% y   ¥  d €u¦F1RIFF> WAVEfmt "VD¬dataÐ åþÔý#ýýþúÿôûâþy[ 9 MøýJ ü\çû4õ÷îçéµæìåøçýì¡äáˆà]ã²é[óßÿYìûã ƒá"ù*/Ã.ù)!ªçö æÙÉÎÉ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùζÚL̲ǨÈQÏNÛÙëÍÿ†âàøi Ï.28<+:÷2-'ô°õwä ÖÇÌëÇ–ÈùÎ)Û‹Í»ÉSË@ÒõÝŒíÒÿ¸å¼ù& ¹z'·./1ô.‡(®xÈ÷Ãëâ—ÛÏØòÙìÞJç]Þ ÜTÝâåéôãÿOï ü˜.@p§óð® ç]û¸ôï@ìëùëßîló.ï]îUïìñÕõ©úóÿæø^þ £ (  ^1ãºñþ®ýýêüLýÿýÒþÿLISTBINFOICRD 1997-04-25IENG J@KER !ISFTSound Forge 4.0@n€R3|*~ÔÆš€‚‚-b –d ,{d y]d y #]k]v|k|*-b'}¥d ’-bd ˜ÿÿÿÿ M3}*bdžïñp  p J„  G zp  V S}*qrV* hVV Yp MH% hp  ¥  zKL$sA @C,LL  ,,L €AL €AL  L'$_.6L 8 6L  O"'$LB, 'f.6L 8 6L ,fL M"KL$ L"€B,LL  ,,L  K"'$LLL  €CL $,,LeL J"c L I"LL €AL  G"'$jLo_ffKec c -žGLL,ÿ X3y*c„ñeny* Uz*]wn* Ndnamen{nh Ndtitle¸{nY Nd country{nonK Ndteam]{nsIn H3K3`¶ C?rK*K.; AwK*A' €_J3|7z‹‹þ`!bv‚wK*1K'K Na iddf|a ‹d_|a Ëþ`!m_ a|f a| þ`!jc_|a cf|a  N3x*aB ë7"  G t Sx*Irt* t-Jt-JÚt-JRL% t  ¥5RI% t  ¥  A4å"  F -?-?b:‚‚-M/š:;,3ó-~-?-~-?] $ $É-?EN%   ¥EM%   ¥  b €HL3y ¯›P‚‚ryK š:z, wK*K-}K-¡u‚rye š:z,I(š‚ryL š:z,I(Õ‚‚ryc  š:z,c -žI'E‚‚ryf š:z, wK*¤w_›*O._›K`OXs + TOh_ ]fEEwf›*O.f›KuOXs + UOhf ]_¡‚ry_ š:z,¡wf›*f›-É(f›*ý‚ryf š:z,ýw_›*_›-É(_›*Ë‚‚‚ry_ š:z, w_›* wK*O._›K`OXs + TOh_ ]f™‚‚‚ryf š:z, wf›* wK*O.f›KuOXs + UOhf ]_ ~ 2.0Y3w*rìØš€‚‚-a –b , {b Adb A #didv|iw*-a'}¥b ’-ab ˜ÿÿÿÿ P3v*sñN"vnpppp[êp0S•ü,:êp0S•Ð,]j1¨nv* I€Q3 "muted"T3r*Iç$ rK* =w_›*n._›jjwf›*n.f›‚‚‚wn*{L QÙ‚‚‚‚‚‚ -? -v/š:';',-EntFGs : F"nhL K){ndL -r*L  o*s*dgó^Is*qIdt*Ih Ny nameIY Ny titleI` Ny ipIX Ny idIo Ny  countryIsI Ny teamI _3` rÎÓ¾sz` N$q@|€` &MN$m` &qi|€` &UN$m` &qN$  W3q*k+"+Js 0 E"p*¯q* @[3€@zÒÐX.µAB,##ß'$C,##  C# ,# ,# J '# K '# L '#### M '#JÙ#JØ#JÔ#JÕ#JÌ#JÒ## }lŽwX*  C,##  A '#  N '$#  ÈB# ,# ,# O '# P '# Q '# R '#SXï#SXö#SX÷#SXø##  A '#  S '$#  C '# ,#  @B# l'#J%Xþ#J&Xþ#J,Xþ# ,# ,# T '#S%X î#S&X î#S,X î# U '#J%Xé#J&Xé#J,Xé €@k3^3A³„º’Š‚–,{Z]—%QppQ,€Z~Z,€Q€Z~Z,¥Q @€]3rJoa*"z r-(r  @€@b3€d3€z2d vÐ#Á¥j Xs <|ws *s '2dj fs s @*¿‚–Z,wZ%*Z% 2d¥Z| €V3 "noTeamSwitch"€f3f*]”ž'a¥Pw <_ww *w Hf*Pw w @ o3vqƒ¼µæ@Dv'™@%-[ 'n@%n‚-[  –@, dz@X-[ 'k¥@.Ì-[ @Xv@jh*@Oi*@Ij*@um*Þ-[ @äÿÿÿÿ j3eZ«§*} ^nX*R*| <{w| *| '7e_ e!jn| | @) T €G,h3q3K&sGv I&Gt v%/a0 g!p^~g^10a¬€?@'' t3c*DR̬qW‚‚-Y –T , {T XM|T Xc*-Y'T¥T i-YT oÿÿÿÿ €@g€òn3C>›w~*m zV*+.¾A A, '* DC '$,,u [s O * \B '*  ÈB* ðA,'* ,* ,*  ÈB*  ÈB* c#'* b#'*8  hS&'*9  hS,'*9  hS,'*9  hS,'W*Z*%L*&L*,L*,L** ,* ,* ,* ,* Z#'* Y#'* U#'* R#'* Q#'P * @B$N * @B$M * @B$K * @B$W*$* a#'* `#'* ]#'* \#'*]*^*_*e*W,€Z, %L,À&L,À,L,À,L,À],@^,@_,e,P 9'N 9'M 9'K 9'P ,N ,M ,K ,I J4Xi8¾¨2#z:X[:X:0:X[ s3I€³†W  VZ  v ]  w ^  x _5  O { e5  O V %L % \&L & \,L  , \,L  , \P  R HN  R SM  R oK  R RW-¡ -t  Gu3Y*2Ÿö&$š:Y* %I r3FmP¯ pFG<^‚wF* -DGzFV U ~hFXÃz€U &!ëU  ¤!BAD INPUTFV noneÀ ½!DISABLED-D'À ÿÿGG{U û|êU ,noneFV noneGFV êU ,G+ZFF countryFV FF@n-D~* v3y3±_C?rV*V.; AwV*A' €Hx3H È•‰HH w‡‚‚‚wH * š:w,H a/!v.H -žH ssI‡ „u@‡ ÿÿ €@l€šz3z@ëk È  zW {Z %j%L &j&L ,j,L ,j,L }] ^ @_ Ae GJP  JJN  KJM  MJK  -QW-¡V\zz{%j&j,j,j}@AGJKM-Q I4T*8£âkel.ã> ãE.šKT*ÈBÈBlð iEE L,w3N*a„/N* d  @ 5.0B4j_ðjßL"  F jú&jSûS&jT=jv=SZ RQ*-s-S*-v-U*-Y -Z*-M-`*-Q -d*-T -g*%{k*&{l*,{n*,{u*b $ 4i  E4~]Ðû×¾ o ` l o ,< l Password~ o b :4  "  p%~ Â@@4 20.0G4FO¯üÓ7"  F fzCxOZqxCÌ™F%I SFk.I ;Ak ]x5I<5wI*k.I ;Ak ]xII@à  I€@O4F4N„OÎkC„  F•¢a/!Y I<iwI*RI-EM%-G(R‚‚-G –M,$–M.¼•¢öH™~ëIëM{%-G'E›:I¥ÇMI l=MO¥M…II@W  m€ÌL4H4oŒ*h“m.ã> ã{ .ÌK ÌÈBÈB{ fäá{ -Ü(mð i{ {  Z4Rh%Á¥8"–:RO%:Ru6i=:RO } €½P4K4pw³dT} .½> ½ €A $} ü} º} l(}  l3og¿-k•¢»ÞT&m•Ï–wm*m-úmq!SmjmmïC•¢-é'•¢»-á'"•¢a/!l.Ε¢í®•Ê@@/•¢a0 o!Sho{•¢*10 ~ €T4J*MN!Çã"  J -I -J*-L -K*Ï‚˜%x –x, -#x  yPa hPéheL*fTaa TI{a  #a Na BJNF‚˜%B –B,  -#B  A~P* zaa~Fappa,~HN M*O O*b $ 4i  S4I*b³aQ~ .> ~ qI*~ a~ a ~  Y,3gb—Ù€r^gCG<†wC*owCgC'7g_ C!brCC@%y -g]-gname‚‚G-x {y{ygg Yyg-Jg] Qmutedg-Kg]QnoTeamSwitch O€c V4B*JMr^VO.c > c D*E*$$OD B*O-f-C*O Y4as$¿cu„„„  Hz U ™:a, #z:a^ :a^U:aYA*:a^F*:aKG*:awH*b $  p€À[4U4s7Z'p.À> À A $p äM4sEä|1nH‚–L,wL%*L%Esu¥LlwÚ*ÚEsu A €‹`4gYæ¸à?„  Hz g “‚-m –:h, ‰z:h^-m'‹h? -m :h^g:hY|):h^}):hK~):hw)b $IQ% g  ¥  \4A FM¢qo‚–A , {A ^rA wYXeš:Y&A wpMSú%“JX&¥A  o4y) uWhA .‹> ‹ PA $A z)A ûy)`-{)A f&A  _4x)mëšýjx)û–j, oš’j&, j^jYj^jKjwñj^’j&^jY’j&Yj^’j&^jK’j&Kjw’j&w¥j  { €r4^4HÑtšWN‚–{ , {{ ^Dk{ m{ -Z'K¥{ -Z b4NVÀ»²&X„„  H ™:N, #z:N^ [:N^ m:Nb $IO% [  ¥  zG ,5ÔˆA A, ', DC '$,,S [R O ,$,D«?,€@, , , €B 'w, $ z 'v, $ { 'u, $ | 't, $ } 's, $ ~ 'r, $  'q, $ @!'n, $ A!'k, $ B!'j, $ C!'i, $ D!', , , , , E!', F!', G!', H!', I!', J!', K!', L!', M!',,F ,B ,@ , ,~ ,} ,y ,x ,v ,,,,F ,B ,@ , ,~ ,} ,y ,x ,v ,F 9'B 9'@ 9' 9'~ 9'} 9'y 9'x 9'v 9'I e4]vxÆ©ù z]Xz]OV -S'-`'‚‚-S –Q, "{Q:‚{QJ=z#OQJ]-S'_=Q¥QXÑ-S -`QVq q ÿÿÿÿAS&àQ(q q :_ΙQ%by$  h=Q  ¥ "GNexgenAccountUpdatedDialog ab $÷ 4z$  a4I†Nw-¡ -u v-¡ -Xu-¡ -x t-¡ -V s-¡ -Ur-¡ -Qq-¡ -k n-¡ -]k-¡ -B j-¡ -D i-¡ -H F  R Y B  R t@  R U  S G~  S F}  R Ey  R f x  R g v  R M Hc4u ©ü‰Hu a‡‚‚‚wu * š:a,u a/!v.u -žu sRI‡ „S@‡ ÿÿ f4TU¸¤+"  F -~-~] $Û-~EW%   ¥)EU%   ¥  l4R:¡+"  F -N-N] $Û-NEY%   ¥)EX%   ¥  Gd4u)2 '%š:u) ,I M€g4j4Ô"C?rG *G .; AwG *A' €i4@è´$i  G «rw-¡v-¡u-¡t-¡s-¡r-¡q-¡n-¡k-¡j-¡i-¡G s=JF  =JB  =J@  J  J~  =J}  =Jy  =Jx  =Jv   s4Qd+"  F -p-p] $Û-pE]%   ¥)EZ%   ¥  D4 0[,Q4p)\ºâyEG<wwE*w^Ep)'7E_ E!bwEE@ v4r)ôR‡±@?%C@0C6p!(M.> @€At)$MÖCM  r)M @€C €¤y4o)kª‰•tro)wUV:š:U&-@˜JV%kcš:U, $ZVWY[\\-@„—•íWÌ‚š•íW²„—•öY˜‚š•öY~„—•ô[d‚š•ô[J„—•ü\0‚š•ü\™•Ð\k-@(-@ w4q)O÷™îT s)Q  Sq)q„„  F rQ * zT Q ¨openTV^% Q T Q ¥  m4 "ClientKey"J€D5l)å5NhC .¤> ¤ @A $C l)L-m)C f&C ûn)C  }4k)gÜÏ”}K Sk)e„„  F rK*òK-E-R ‹-RKyK$¹ KKKyK$ -RR_% K K¥{R`% K K¥  zx8(ÛØA°B,88  €C,S.8 8 8 ,8  N!'$8  ÀB8$,D«?,€@8  @C8 O!g8k 8$ P!n 8$ Q!8 $,,88 [D8 Y°B,88  €C,U.8 8 8 ,8  R!'$8  C8  C8  @C8 S!b88 T!^88 $,,8E8 [F8 Yg,@b,@^,@WY M5_sgâôc_Y†‚-J {c #cHcH H||HP-J'ƒ¥dè‚-J –d,Åz_Y_YPè_Ypp_Y,Pc_^n‚-I {c #cGcG Gd|GQ-I'k¥bùЂ-I –b,­z_^_^QÐ_^pp_^,Qð„-J -Ib'ò( x4C Wf`S›*S®Vê‚–C ,"{C  e -#C  eli)D . S® B D 5lD $C ¥C (X–C ,D . S® a D 5 hD $ÿÿÿÿb t4 "ClientID"W4yfú 4…yëget Engine.GameEngine ServerActors-G—~y.UTPURESA"%ƒ-GO)UTPure E5j)N» 7Q Sj)I„  F rQ* Q-KQ-KÚQ-KRb% Q Q¥5Ra% Q Q¥  K€z4F YBU›*U®VÌ‚–F ,"{F  pG . U® B G 5F  pG $F ¥F (:–F ,G . U® a G 5 hG $ÿÿÿÿc {4 "Password"@5 "OverrideClass"4bb³Bäb. S›-žrb*D-ž„rb*–b$%k -žrb*n -žrb*gCrb*çrb*k -¡(n -¡(g â1–b$%k -¡(n -¡(g â 6#b$ erIk -¡™~I P%n -¡™~I S%g  r Z€_5f)ÎeJSK.> g)€Ah)$K f)KK O5e)c:Š{S  Se)í„„„„„„  F rS *$•¢»-ç –[ % ™[ ,C‚•¢a/!Y$™[ .¼•¢öšS Q[  S l=[ nm% S  R[  S ¥  B5@cØÉg@. U›E-žr@*F-ž„r@*–@$%bCr@*^Cr@*½r@*b ^ eö–@$%b ^ et @$ pb €t ~t =^ t ’~t =& n4x^Ïx~xQ1x-E~,RR„–~% —~,~, &^Gxidxq &^GxipxX (^Gxnamex )^Gxtitlexz ^GxteamS~ +^Gx countryxV G |4|L €7J <FwJ *J Q|}J J @ Ž‚–H,wH%*H%Q|}¥HF F5J5úª"C?rx*x.; Awx*A' €HI5Wá%|HWU4‚rWS š:U,bX‚rWU š:U,c‚‚rWD š:U,D-žx#^=. S›$i‚‚‚rW š:U,-žx^\. S›$ g k -¡n -¡Ô‚‚‚rWF š:U,F-žx#Z=. U›$z‚‚‚rWE š:U,E-žx^M. U›$ b  ^  G €”S5`)L5¯‡Ì²‚‚-K –Z, {Z^-n|Z^`)-k™~ZYa)%-h™~Z^c)%¨„„-n-k-h-K'¯¥ZÄ-KZÊÿÿÿÿ GK5u2Cî-O(š:u ,WMMš:u ,Y P5qxö†Œ"  F •l?restart(Ej%   ¥  T5SbA„ÅU„  F/›:;, •àgEc%   ¥  A5 "UseNexgenHUD"x€X5R5taƒ:Ga s-xGtGa-x i5P jAv€aU„  F/›:;, -P {•à‹-P •à¨•à -P -P -P El%   ¥_Ek%   ¥  E€G€@k8^)@}œfw!–N,tN¥Nit&[–tN“t&{t{¥t(t“N&%6Tt{b)&6Tt{v),6Tt{@*,6Tt{W*,6Tt{B,%6[t{^)&6[t{d),6[t{w),6[t{V*,6[t{{*6w t{@ b5])PüÃf]‚-E –L,SzLZ-E'LZpp]),_)Z¥L-E Q5 "FlashMessages"| -1Z5 0[5 1z\5U,›4A @C, ,,UU €BU  ÀBU€A 'U ,U ,U $,,U  @CU |!'U {!'U z!'SUoUpe UU  ‘,,U  ‘,,U $,,U@%˜–@,N@ ZKzN@X U@$ & {S’@&Ž@X U.$N’~N ,&¥@¸aU u!BU Os U c!€A€A y!'$Z.b8 b w!'$.8 S, e ,ol(Zas o{AB _,G5eaî„!ýuG<awu*u"7e_ u!muu@e]#<enameee]<mutedTe-Je]$<noTeamSwitchTe-K c6[)>˜#Có>²IG 6\!(}6p!(å}Inš:{&G 6\!(å­š:{,G ¯®6\!(6p!(IåG ?D®6\!(¬¯6p!(I@#²JF 6U!(~6G!(Ê~JSš:z&F 6U!(Ê’š:z,F ¯®6U!(6G!(JÊF ?D®6U!(¬¯6G!(J@¥!Ø[)G F }~ k?i?Cî‡XtD„ræ**‚š:É%š:Æ%'MVæåk‚‚-d –I,{I e $#I eKS a|MK-d'S ë S -e„‚™~S  P%—:É%.‚™~S  S%—:Æ%h¥Ib-e ]5r Aož«oVo# W!Sÿÿÿÿ©‚–r , "{r  :o)r  :Sr ¥r ; d5Y)ZÝz{us‚-z –j , izj :-z'j :Y)j hZ)j [\)p¥j  a5}{†lA®V›*9‚–}, "{} X|..Z#K} X/r|*. ® B $}5ppp[ h=}] } j¥}(f k5w P|ÃlÝw  fr heZAPlayerZVIPA,BZLevel 3 AdminA,B,C,D,FL3 AdminZLevel 4 AdminA,B,C,D,F,GL4 AdminZLevel 5 AdminA,B,C,D,E,F,G,H,KL5 AdminZLevel 6 AdminA,B,C,D,E,F,G,H,I,KL6 AdminZLevel 7 AdminA,B,C,D,E,F,G,H,I,J,KL7 AdminV•¢»ßv •¢»úw •¢»Ùx •¢»Ø{ Rget Engine.GameInfo GamePasswordV Rget Engine.GameInfo AdminPasswordZ-t |get IpServer.UdpServerUplink DoUplinkTrue-s(H=•¢ûS$o$R=•¢è-u 'Y $ t$U$ G,(F,xE$ f $g $-x '%\•¢»Ô&\•¢»Õ,\•¢»Ì,\•¢»Ò%eBotpack.Translocator,PS&eBotpack.SniperRifle,S-I (C-L '-?(T$v$Z-s'-v'-Y '-M'-Q (-T (-B '-D '-H 'M$-V '%pppBotPack.ChallengeHUD=w .NexgenHUDxDM&pppBotPack.ChallengeTeamHUD=w .NexgenHUDxTDM,pppBotPack.ChallengeCTFHUD=w .NexgenHUDxCTF,pppBotPack.AssaultHUD=w .NexgenHUDxAS,pppBotPack.ChallengeDominationHUD=w .NexgenHUDxDOM-I'b Y5 "ShowPlayerLoc"_c5Q7°Ì  þ{!bn..dZQCS DnX ™S%n-U'nN=SJ iSür›Jn-É'Z›n›*Jeqþ{!mn..Z UQ]‚wn*n-UJ. ® B J$:nNJ5ppp[ hnN] :nN j]rZ›nJ-É'›JZ›*Z aQžþ{!jcZQC e5 "PlayPMSound"s5|JþlxÅÄ„„„„  Iz B –rÿÿÿÿ ™r, 3‚›rÿÿÿÿ"zr:*‚šrÿÿÿÿz ~ T‚‚-C -D –R, zRX-C'QJ|RX|-D'Q¥RÃl„-C-D ] v|Ñw]*]"GNexgenAccountUpdatedDialog]aRX|RjBR€Or‹–r%RIX)Ru~b $  h5 "AutoSSNormalGame"p5RedZiš[ž”§,§bˆ–}b, R=’§,],!-‚›:R,"–~bì:R%0-bpbì:R"x[b `,^5{^¶$4G)Z{{teamS{Q Sj5 "AutoSSMatch"Hg5X÷@"`HXYx‚rXZ š:Y,rw›*›-É(›*BÚ‚rX š:Y,ÔwZ›*Z›-É(Z›*Bý‚rXo š:Y&ET‚‚‚rXs  š:Y,s -ž w|*| G=UI‚‚‚rXa š:Y,a-ž w|*U S } “oC&–} %| ~r  e  | r |!I=UU} | r ^‚‚‚rXB š:Y,B-ž w|*L..Z›XU S } “oC&&–} %| ~r  e  6| r |JLU} | r  v5T|Hf¨ÚMutator%TK¦‚{T –w, [™~K,%K€K~K,FppT,KwAFÚMutator£wTK @n5 "RunCount"z5~Iº{n®„„„„„„„„  I –:~% ™:~, #z:~Xz K –oÿÿÿÿ ™o, 3‚›oÿÿÿÿ"zo:*‚šoÿÿÿÿz D _$v:~X…w_*_"GNexgenAccountUpdatedDialog_a:~j K:~€Oo2–o%:~IW):~uDt:~I:~ub $  r5 "A"t5 "B"A6j y$¸aöÚTournamentGameInfo%j l ô‚{j  –r,tœÃj Š‰z l l têSppppj ,tÚ,l rySÚTournamentGameInfo£rj l % o5x5~Cþ+·g %¯–g ,d g  Z¥‚g X -¡ {d ‚zh h €d ~d ,¥h pph ,€d ~d ,¥g h  h €@u5 "C"Z6O Ge'f„„„  I –:O % ™:O , #z:O X b$v:O Xäwb*b"GNexgenAccountUpdatedDialogban:O í–n, ­š’n&, nXnjn€O%nInuãnX’n&Xnj’n&jn€O’n&€OnI’n&Inu’n&u¥nðb $  y5 "D"w5}5U`÷/†:wZ›*^:..Z›N~sw›*^. ›$~^ÿÿÿÿ^ ^€{5 "E"|5@6[{Š3k;wZ›*-_..Z›-UbZw›*-_'b-_(-_ _€C6YUÇøVHw v erj &x[bPAAD&PBAC&PCAB&PDAA&PEA~%PFA}%PGA{%PKA|%PHAy%PIAu%PJAs%y|e a${e  #e Ye WrY!™W% zffSW!fppf,SW¨f •}f f ’~f /&Cf VV•¢åW%‚‚-T –W,{Wy #WyUe ú|VU-T'f=W¥Wƒ.-?.—:v:Tv$-?(=-XHFb 5zB˜ß6½R U-W [-z™R %SC-za-ž„„-z -W GR s -ž„„-z -W GR B-ž„-z-W B‚-W -zS# R  jo&p’R  €O&»–‚-W -zS" ..Z›hop&»S o pÿÿÿÿ ôD6k«ÐSB(„rÑ*Ña/!^a A.Ñ-P(' Q6Xœ0Rz§ÿÿÿfV R €B6mG¾µ=ÀI–m €O%km I„km €O h„JkJ ~5 "F"k€H6 "G"G6V)Ô:A™~pV),pU),% b,l5w_Æ&3G(Zwwnamew @ÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ33ÿ333ÿ33fÿ33™ÿ33Ìÿ33ÿÿf3ÿf33ÿf3fÿf3™ÿf3Ìÿf3ÿÿ™3ÿ™33ÿ™3fÿ™3™ÿ™3Ìÿ™3ÿÿÌ3ÿÌ33ÿÌ3fÿÌ3™ÿÌ3ÌÿÌ3ÿÿÿ3ÿÿ33ÿÿ3fÿÿ3™ÿÿ3Ìÿÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ3fÿ3f3ÿ3ffÿ3f™ÿ3fÌÿ3fÿÿffÿff3ÿfffÿff™ÿffÌÿffÿÿ™fÿ™f3ÿ™ffÿ™f™ÿ™fÌÿ™fÿÿÌfÿÌf3ÿÌffÿÌf™ÿÌfÌÿÌfÿÿÿfÿÿf3ÿÿffÿÿf™ÿÿfÌÿÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿ3™ÿ3™3ÿ3™fÿ3™™ÿ3™Ìÿ3™ÿÿf™ÿf™3ÿf™fÿf™™ÿf™Ìÿf™ÿÿ™™ÿ™™3ÿ™™fÿ™™™ÿ™™Ìÿ™™ÿÿÌ™ÿÌ™3ÿÌ™fÿÌ™™ÿÌ™ÌÿÌ™ÿÿÿ™ÿÿ™3ÿÿ™fÿÿ™™ÿÿ™Ìÿÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿ3Ìÿ3Ì3ÿ3Ìfÿ3Ì™ÿ3ÌÌÿ3ÌÿÿfÌÿfÌ3ÿfÌfÿfÌ™ÿfÌÌÿfÌÿÿ™Ìÿ™Ì3ÿ™Ìfÿ™Ì™ÿ™ÌÌÿ™ÌÿÿÌÌÿÌÌ3ÿÌÌfÿÌÌ™ÿÌÌÌÿÌÌÿÿÿÌÿÿÌ3ÿÿÌfÿÿÌ™ÿÿÌÌÿÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿ3ÿÿ3ÿ3ÿ3ÿfÿ3ÿ™ÿ3ÿÌÿ3ÿÿÿfÿÿfÿ3ÿfÿfÿfÿ™ÿfÿÌÿfÿÿÿ™ÿÿ™ÿ3ÿ™ÿfÿ™ÿ™ÿ™ÿÌÿ™ÿÿÿÌÿÿÌÿ3ÿÌÿfÿÌÿ™ÿÌÿÌÿÌÿÿÿÿÿÿÿÿ3ÿÿÿfÿÿÿ™ÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€K6EEé­Cx-mJEoCd U-P)™d %-j[yšEÿÿÿÿ` ýÖšE%³-j` d  uÓ`  i$ý`  i=“E&1šEÿÿÿÿB-ž'a-ž'ÅŠ-jB-ž'a-ž„Gd ‚-m@EÅB-ž‚-m@Ea-ž'e  ` e C›E% šEÿÿÿÿc ŒfšE%D-jc d  Icc % hŒc “E& hT%v–T,yT ZèzyTX -ž'lTX -ž„›E%0‚-m#€y~y,TX -¡c €y~y,¥T“ /Ó%t*'." ," )" (" +*²²²"àò¬¢Û Á2@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúúõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõõððððððððððððððððððððððððððððððððëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëææææææææææææææææææææææææææææææææááááááááááááááááááááááááááááááááÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ××××××××××××××××××××××××××××××××ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃþ¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥                                ››››››››››››››››››››››››››››››››––––––––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxssssssssssssssssssssssssssssssssnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnniiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiidddddddddddddddddddddddddddddddd T6T)0Œ†P-T)&%  J6 "H"]€W6t i¶LbZ{t ]’’’]tí€t &¹ít ‘}t ,&,íêt &}t ]  u€K€]6V6|K¬Hÿ|%5–|,¡K|Z¥|¡KVX |%}–|,¡K|y¥|O|%²–|, ¡K|A¥|„¡Kw¡KVv¡KVt¡K:fK €O6^@6„N?„š^%0&V“^& h h6Zn0vZ>»„„„„  J ™:Z, #z:Z:C‚-j8„z’:Z&:™’:Z&, ‚-j –:Z, x:Zâ-jB’:Z&ñB“:Z&Ô‚–k, "{kXxšk€Oxk€OB-l'ÊÊšk€OBk€Ox-l'¥kñWx:x:B:B:WWxhxhBhBhWWx[x[B[B[WWxJxJBJBJWb $<-l $  €Y6sfGØPs..Z®ws*n DsX±™n%s-U'sN=nsY h=nüs-U(sN$sY i$s..sÞ ^6X6VÔw7ï¡V¡v ¡w ¡x ¡{ ¡Z¡:H¡:S¡:o¡0-s¡0-t %î–,¡\¥À¡:U¡G%<–, ¡:¥%q–, ¡h¥C%¦–, ¡[¥x%Û–, ¡J¥­%–, ¡X¥â%E–, ¡j¥%t–, ¡O¥L%©–, ¡I¥{%Þ–, ¡u¥°%–, ¡^¥å%H–, ¡Y¥%}–, ¡^¥O%²–, ¡K¥„%ç–, ¡w¥¹¡j ¡0-I ¡0-L ¡h¡e¡f¡N ¡O ¡:E¡c¡0-?¡:T¡:v¡0-v¡0-Y ¡0-M¡0-Q ¡0-T %S–,¡{¥%¡:f ¡:g ¡0-x ¡0-U¡0-Q¡0-k ¡0-]¡0-u ¡0-X¡0-V ¡:Y ¡:t¡0-B ¡0-D ¡0-H ¡:M%š–,¡p¥l%Ï–,¡e¥¡¡z¡F e6 "S"G\6a2e®T]0š:a &{AB[š:a ,{B R6 "I"_6b6~«WC?r|*|.; Aw|*A' €p6R $[å9Qu¯6p!(?“R &Gv?D¬u?R D¬¯u«v?R ?R E6\!(F%B–FR P v¸BDÒ³B?&B¯B€?P ®P €?@[E6U!(P 6G!(šF%~@E®®EP ?G¥F|¥!~ 6Ó/Ó%H)*'." ," )" (" +* 5*ááßÿ"yU6°¢Û C@  Y–†Z5 +Ozek;;u²¾«Ÿ›ÂÍÌ¿º¨Ë6/‰ÓÏr`³ÙÜÚÔ€»ÈC]Ã*0§£’r‚‹bâ|}å¼~j ŠØ¦ hàãäµ¢Þãè¹Ò­Ç˜$#‘Ö߶xªœÝæçáœc—Û3lÓŸG=ƒ¬¯¨Æ< 5ØÜ^%sÄɽŠhUa{t„r?HM&‡¥F !W´©•m… n&fo{ˆ->8)ž®°-À±¤Lghcnƒ _('V·Å°ŠJSšÕ×Ðm9 [%miy”“{w©ÀÁ¸ŠxÊÎÑÎ\! R4vamqpdPEFFKToƒŠ™¡ŽBLIŒf[26cXQ1":Lƒq.7L@, D_G NA.  o6 "P"`6 "J"6Ó/Ó%H)*'." ," )" (" +*5*ááßÿ"e³²¢Û {G@Y–†Z5+Ozek;;u²¾«Ÿ›ÂÍÌ¿º¨Ë6/‰ÓÏr`³ÙÜÚÔ€»ÈC]Ã*0§£’r‚‹bâ|}å¼~jŠØ¦ hàãäµ¢Þãè¹Ò­Ç˜‘Ö߶xªœÝæçáœc—Û3lÓŸG=ƒ¬¯¨Æ< 5ØÜ^%sÄɽŠhUa{t„r?HM&‡¥F !W´©•m…n&fo{ˆ->8)ž®°-À±¤Lghcnƒ _('V·Å°ŠJSšÕ×Ðm9[%miy”“{w©ÀÁ¸ŠxÊÎÑÎ\!R4vamqpdPEFFKToƒŠ™¡ŽBLIŒf[26cXQ1":Lƒq.7L@,D_GNA. n6l EéPŠf„„„  J ™:l ,  –:l &#z:l : R‚–M, "{MXâšM€O:l M€O%HH™M€O:l M€O“M€O&¥MfM:l $‚–M, "{M:"š’M&, M:MhM[MJM:’M&:Mh’M&hM[’M&[MJ’M&J¥M^b $ˆ-M) $  f6 "K"f 0.60j6 " -,."k€i6 0~6HDÆÒK=H<;wH*cDHq«–c%ˆH-Eu }¨u i$Ïu  h=c${Hzu Hzu  ZHHtitleu HH@ r6 2t6W  2Qk¯6G!(?“W &Nr?D¬k?W K¬¯k«r?W ?W 6U!(@%B–@W U r¸~KÒ³~?&~¯~€?U ®U €?J[6\!(6p!(U š@%IJ®®U ?N¥@|¥!I Y €s6 1x6 0v6{ éî)“k-w(B-SY ?D¬«6p!(¯ÈB{ÈBhY ?D¬«6p!({ÈBŸ”-SY ¯6p!({ŸY {}õ¯Y ?D¬?i@€@iõ¯¯6p!(}?i€@j6\!(P®®j}?iO[j6U!(}6G!([P6U!(i6G!(¥!O \ €z6s°O “k-k(B-Z\ ?D¬«6G!(¯ÈBsÈBh\ ?D¬«6G!(sÈBŸ”-Z\ ¯6G!(sŸ\ szõ¯\ ?D¬?g@€@Xõ¯¯6G!(z?g€@h6U!(V®®hz?gU[6\!(h6p!(z[6\!(V6p!(X¥!U k6 "\\n"{6 6^¤|6‘$ ¥! V7 8@7f(L…’ !f( m6 1F7nB£:G4"  I a{j:n: j:nhe(:n[ g(:nJ Rh(b $D  Pd(| Ó±?mr ?m*r m(Tt(n¯ar p¯®¯¯¾|[ ¬¯|T@?,R.K npr TR d(¹a®r hR C7au÷Z![®€@@®€@@¯¯º«@€@€@¯¯¾«@€@€@ }6 2n€G7AdØZ![®€@@®€@@¯¯º«@€@A¯¯¾«@€@ A D7€N7tZxº@×H„  J"{“, &: v{p:¥pHÛztp:&R&S’p& p: tpha(p[ b(pJ Rc(b $  I7]([KÈwo–X ,@6\X (](6UX (^(6pX (_(6GX (`(¥X uÿÿÿÿ 6vœmxlzv|¯º«@lLy¸vL.¤K ¤xz|L K7 ","x€¤M7 16.0A7 "Reconnect"Q7 18.0T7Y(R+;‡(‚-[(„-} KY( †| 1"- PlayPMSoundtruetrue  n Í 'S.v /=w v@äwS*SkOp*-Z( 1GNexgenPrivateMsgDialogOp¯… R4  5  q%p¯O\(p ÂH7@T¸]Ù]lC@^FayB.¤K ¤]C^aB X(Bf&¸]F^¯¯º«@lF¸@a.¤K ¤]C^a L7 "Disconnect"R7 13.0Z7 16.0]€¤a7W({814A‚  G„ Q-D J  SW(6‚‚‚‚‚‚-?-v/š:;, -EJ -E  F  G \ -c~ z2wJ *.J ;AV q ¥E-D  G~Dpppp  -> J : E$  P7 "Exit"W7 8`7 7U7 "Mutate NSC START"O7V(1à#$·˜@%@dhleSk¯º«@ltyXV(ª{Xg~X\n™–g%uXXÅu€XgXX’g}\n§{uhMu@–h%ruu2r€u’h&uu’h&f.¤K ¤hektf rf fU(fûT(¸etÅ[Se \7 12.0X7 "Botpack.CHSpectator"]7 16.0j7 4.0[7 ","^7 'pj'c7 6b7vzÐ÷$£"  J KzvvV]–W%W%o–L%L%–T%T%“–R %R %§—W, W, »—L,L,Ï—T,T,ã—R ,R ,˜’’WLT%W,Vvv c%\b&\`,\],\Zw Xx V{  RUV  RTH=WS=Lo=TR=R -t -Pb•¢»ßv•¢»úc•¢»Ôb•¢»Õ`•¢»Ì]•¢»ÒZ•¢»ÙX•¢»ØV¨set Engine.GameInfo GamePasswordU¨set Engine.GameInfo AdminPasswordT•¢û’’WLT•¢èR $-Pset IpServer.UdpServerUplink DoUplink TrueXset IpServer.UdpServerUplink DoUplink False $ 4i  X'k7x‰Â… /=*"  W$ >client /=+"  V$ w% client /=+"  M t% client /=+"  U$ v client /=("  R$ >gameÍw  t* /=;"  U  t game/  G /=)"  P$ % game‘  F /=)"  O$ x% gameó  K /=)"  M$ z% game /=*"  I$ >server­w  v* /=="  U  v server  H /=+"  G$ g% serveru  I /=+"  F$ x& serverF  J /=+"  E$ d% server /=:"  M >serversettingsserver /=&"1 L& server,serversettings /=&"2 M& server,serversettings /=&"3 c& server,serversettings /=:"  D$ h% server,serversettings /=""  @$ [% d7 5e7 4f7 3h7 2_7 'pl'm7 1Y7sM&@,‚–}spPÿÿÿÿzP“p&l‚–~ -,.sP&%³?P«š™??p¦P-Pú&PP o7 2r7S(uušSQ‚-m –w, G|wI S(-m'wI N¥w P€x7 0i7R(=C¡/¸R(y u7 2n7n`Yg3No@)ll\ nÌ{yZ i¸\ y(e.¤K ¤l\ Z oe yef&¸li\ nZ ¯¯º«@liåZ ¯º«@ln®\ op.“K “l\ Z opÖZ p,úp g7 'ac'v7u`X’cKu a‚-t –x, WzxI -t'xI u^¥x j€l€“z7 1w7Q(K?É LC‚-k –j, 9|jI Q(-k'@¥j-k y7 10P( ","|7 ","}7 1óp7{qƒ=8‚ó{O(N(|[ ¯¯¾|[ A¯º«[ ?,C|Ñ{|ACi 7 "?restart"~7 0D8 4.0I  "open"q7 "id"@A8€@Ï{7M(&œq;õ^.²Æ×ÝÕµw^*^ĵ‚-] w*-]‚‚wÖñ-ÇÅKç-].²Æ×ÝiÏ-M( f 4.0[8B8QŸ„ €€C8K(r¹K?|K(true 6Ó/Ó%I(*'." ," )" (" +*,*)5*ýýýÿ"Jrá¿¢Û ‰l@/i~˜xaGiÐÞÞÝÙÌÁ± ]ËéñôòêåÛÎʾ¨~-,ÜðùüûùðçÚÍÇ¿¹¡yGÕðüþþþûóæϹ±¯®œp'­áöþþþõ¼}n[b{Ÿž¡ |WOÕèõþþàq‡³ÓŒc9Kx™–`)«Öäîûãe£ìøýëµg8.tyk,ÄÕáâóv¤ßú÷ïÃÑd"Ls{k,H½ÉØ×ÈU²í´¢€`Q„2`ri*VºÅÆÀ•f¶ÔSZJ+%C‹@Wa]'X·»¸­mT‘’34;…: MYVKª°¬¥l=ˆ”<$F5 IPI&š©§—v#A†6R‚‰?GI)z¦–u71DƒŽŠÒ“?0 (/,Nw›–uh :BEE>0 ))^w{uhX! &&)Vorj\OH!&&&GX__VL/,)(&/ILI/)& €G8€±M8@N8@O8@@K8€±6Ó/Ó%I(*'." ," )" (" +**('5*ýýýÿ"O„Â¢Û 9q@ÚÞÞÝÙÌÁ±¡ÛéñôòêåÛÎʾ¨ÜðùüûùðçÚÍÇ¿¹¡yØðüþþþûóæϹ±¯®œpÅáöþþþõ¼}n[b{Ÿž¡ |WÕèõþþàq‡³ÓŒc9Kx™–`¼Öäîûãe£ìøýëµg8.tyk,ÄÕáâóv¤ßú÷ïÃÑd"Ls{k,½ÉØ×ÈU²í´¢€`Q„2`ri*ºÅÆÀ•f¶ÔSZJ+%C‹@Wa]'·»¸­mT‘’34;…: MYVª°¬¥l=ˆ”<$F5 IPIš©§—v#A†6R‚‰?GI)z¦–u71DƒŽŠÒ“?0 (/,hw›–uh :BEE>0 ))^w{uhX! &&OVorj\OH!&&&)GX__VL/,)(&/ILI/)& 6Ó/Ó%F(*'." ," )" (" +*?3%5*þòµÿ"¿Ä¢Û Žu@ 0 )$"&.UÏùúÖV/! 1ÁÄÉó÷ÿþöíüýìÚž™™(oÁ£ÌèûÿýäåîõùÆu™9 ”¬ºßòûãâãßì消az}¦æáåàßï÷Üν´Å‘mp ¿ƒ¬ÉÞðøââæÛÕÓ®¡wgÃÔ¤ÊÜöóóÝÞÓóñ»–‰††ÀÔÐ¥æññôáÍóéáá“’‚§Â¾£ÑêéëáÈçÙÑדt˜|‡˜k®ÔçÒË̼ØÑ¢Žjqr S]f¨£¤ÐDzº¹°³‹sOM@'Q{·Œ­¹±Ñ¯—vnYQ*=eaŠ€Á‘´¤¶›f\M?5dPL=8 ,CIRC7 %AD3# 6Ó/Ó%F(*'." ," )" (" +*;0"5*þòµÿ"§ÉàÅ¢Û ãy@ÏùùÖÁÄÉóùÿÿöíüýíÚž™™Á£ÌèûÿýäåîùùÆu™ ”¬ºÞòûäâäÞí趈az}¦èäåâÞïùÞν´Æ‘mp¿ƒ¬ÉÞðùââèÛÕÓ®£ugƒÄÕ£ÊÞöóóÛÞÓóòº–‰††ÀÕÐ¥èòòòäÎóéää‘”ƒ§Á¾£ÐééëäÈèÙÐבt™z‡™k®ÕèÒÌ̺ÙУŽkprS]f§££ÐDzº¹²³‹sOM@Q{·Œ¬¹³Ð¯—umƒYQ=ea‰€Á‘´£¶™f]M=5=ia«¸·•µƒ§ pRK:S=x„ ·§t „xBF2[bcyuy„yaH6;W[aYkk]bcNFHWBdOYdKNHW=hBNRKFN>dOK=BIRBAB D "Reconnect"eT8t:!”Wetl1H(t&$=t1G(t%$DE(,@ àa8\`)JB:‚\a/!H wP*PM.Ž\\ PU8I;N RPISP‚rID š:S,ÆReconnect& P8€«X8€@Y8€o8Z8w‘\ 6Ó/Ó%C(*'." ," )" (" +*((65*ØØåÿ"‰ÙÇ¢Û ‹@+O]<" =áéàÒÍÐÔÔ˜ îèO8(-&0I›Îo>ú´yljHrfJ.*(D±2ü„ÛýïˆKRŸE95r-0›—ø˜ìÿôÚvLJƒQLF\700Ÿ`³ÝÆþ÷ܬeWZœcbZSG?0A±!í_õöë®wbgq°utqgZJ.zn O؇äã­sm“}ŠÈ”‘~m[¥6;¦Жaxk^\i¼Ó Ì¤¤¡”iUA0ž% çM:CESdtŽêåÒ¸¸¶¤•}bF1„MâCIhBWi•µóñÁÁ¿·£‹i™6pYàCBŒFZq‹¡¶æûÃÄ»©’qª9pV ÞD)5EZrŽ£·ùÙÅÅÄÁ«•rS9€NÊf(1EXq‹¢ßðÃÅËÅÁ¶”qR6˜,`²'@rSh òÂÂÄÅû§‹”J5¯Ï;Q«J\tÑÖ¶ºÁÁ»ªš}ÕBln›†(6BSgב¡¨ªª¤•~dP6¯'ÉT-6P½{tŠ‘•’‹¨dQ?†e=ÀD.6…R\grqg¾L?n– O´U05@Ff¹WRJW6‚—# 3¯†D/19d?91Y¦p!`žž|jfp†¯„3'Ujj^D$  6Ó/Ó%C(*'." ," )" (" +*%%25*ØØåÿ"üIäÉ¢Û àƒ@O]áéàÒÍÐÔÔ˜îèO8(-&0I›Îú´yljHrfJ.*(D±ü„ÛýïˆKRŸE95r-0›—ø˜ìÿôÚvLJƒQLF\700Ÿ³ÝÆþ÷ܬeWZœcbZSG?0A±í_õöë®wbgq°utqgZJ.z؇äã­sm“}ŠÈ”‘~m[¥6;¦Ð–axk^\i¼Ó Ì¤¤¡”iUA0žçM:CESdtŽêåÒ¸¸¶¤•}bF1„âCIhBWi•µóñÁÁ¿·£‹i™6pYàCBŒFZq‹¡¶æûÃÄ»©’qª9pVÞD)5EZrŽ£·ùÙÅÅÄÁ«•rS9€Êf(1EXq‹¢ßðÃÅËÅÁ¶”qR6˜²'@rSh òÂÂÄÅû§‹”J5¯Ï;Q«J\tÑÖ¶ºÁÁ»ªš}ÕBln›†(6BSgב¡¨ªª¤•~dP6¯ÉT-6P½{tŠ‘•’‹¨dQ?†eÀD.6…R\grqg¾L?n–´U05@Ff¹WRJW6‚—¯†D/19d?91Y¦pžž|jfp†¯„jj^ 6Ó/Ó%q'*'." ," )" (" +*(!5*üÞüÿ"²•ÏË¢Û 5ˆ@ …ïùùøð‡aõþô‡p\äý… !ÙûûxM.! EôXLÏUîb:#'-:+&ÛÎ"U}öüv,#(/:FJ/û) XbÕÿƒU%(/FMNNÓÔPtßå{'WJ('JOV^-M÷5~ÑRQµ¶_F GW]lF%ú)$žØíÞ×ÖçÍxO&:nswV&ô.HèêË›IciœŸÐA©K€‚[:ó1Xò¡³Å²IdhšjBÆ­;„„„[MõH룙ÁÂÀªSgf°É¬:„‚wV憥““–?®º¹D¾É>†qszVÍyí¢””ŠCÂÄÄÃ8 è^mz^rïᘒ‘ŽŒ•¼Ä¨4ŸT_Z[Üv ”˜´É­««7eèMMYUä· ऋ—½È= 9®­o10GuçÌ }Ú¿Ç6‰3±À¦kÌãæ`¯Ç6*ñ2âÊ»¸@Ò{¦Ã6Lì|ˆ<À>Héݧ1·  i8YG8Ì ÿ0–~Y.%Ypp f.Y^BÃY ŠF.BØ^€@€A¯º?,¯¾?,FI)aF_ `Fr_FfbF|'}'~''ëF eTN :"KŽeN l1D(N &$=N 1@(N %$=N W`N {'€BTz',@W,  ôPk2š:Ë:$Pa  P`8UDÈ ¯PUV­‚rUT š:V,&T W S.f; A­‚{T wS*S vT €c8€±d8€±e8€«f8€@g8€Æs': €OÆÔpNexgen Server Controller v€U q,-Ç'-ò' Æo':¥ŽÆÔpNexgen Server Controller v€U q,ëØ 9€@€A¯º?,¯¾?,=.9ë-Ç'-ò' U?Xqù¤T>E Ð{ÿÿÿÿ|ÿÿÿÿb‚wY*–~XY¯%Y*‚rY* wg*Ygg*ïï‚‚wY* wg*#—}g¯}Y¯rYYggrÀrY*L%½‚–L, wLE €Û*rLE €Ûa~Xr¯³‚™a%0„rY*#—}r¯}Y¯Yr{a¥LÛ{~XY¯b‚wY* rg*L%_‚–L, wLE €Û*rLE €Ûa~Xr¯‚‚‚™{% ™a% ˜{a–a’{}Y¯a~X’{}Y¯r¯™a%¡a’{}Y¯U‚™a%0„rg*#—}r¯}g¯gr|a¥Lú<<wg*a~Xg¯1‚‚‚™{% ™a% ˜{a–a’{}Y¯a~X’{}Y¯g¯1™a%¡a’{}Y¯|a d€l8€eRD:"…eDl1y'D&$=D1r'D%$=DRTDm'@BWTDl'@B ~8h8u± qn8Y<œ M#zYR k'7R YW i' @óD eq ¼¡ŸwD *;-fÈeº¾D Ÿd?D¬¯º?D ê@c?D¬¯¾?D æ@ÚedcD  t8@@q8€6Ó/Ó%q'*'." ," )" (" +*' 5*üÞüÿ"7õÍ¢Û Д@…ïùùøð‡aõþô‡p\äý…ÙûûxM.! EôXLÏUîb:#'-:+&ÛÎU}öüv,#(/:FJ/ûXbÕÿƒU%(/FMNNÓÔPtßå{'WJ('JOV^-M÷5~ÑRQµ¶_F GW]lF%ú)$žØíÞ×ÖçÍxO&:nswV&ô.HèêË›IciœŸÐA©K€‚[:ó1Xò¡³Å²IdhšjBÆ­;„„„[MõH룙ÁÂÀªSgf°É¬:„‚wV憥““–?®º¹D¾É>†qszVÍyí¢””ŠCÂÄÄÃ8 è^mz^rïᘒ‘ŽŒ•¼Ä¨4ŸT_Z[Üv ”˜´É­««7eèMMYUä·à¤‹—½È= 9®­o10GuçÌ}Ú¿Ç6‰3±À¦kÌãæ`¯Ç6*ñ2âÊ»¸@Ò{¦Ã6Lì|ˆ<À>Héݧ1· ÇJ9e' 6ßI÷e'úf'?„-É—Á}÷Á}÷$ Z "Reconnect"ey8u:!˜Weul1g'u&$=u1d'u%$Zc',@ s™/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenUtil * $VERSION 1.10 (10-3-2008 17:59) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Utility class. Contains some usefull general purpose functions. * **************************************************************************************************/ class NexgenUtil extends Object; var float version; // Nexgen version number. var int internalVersion; // Internal version number. var string packageName; // Name of the Nexgen package. var string countryFlagsPkg; // Package containing the flag textures. const keyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const keyFormat = "5-5-5-5-5-5-4"; const dateFormat = "mm/dd/yyyy"; const nexgenCommand = "NSC"; const separator = ","; const assignment = "="; const escapeToken = "\\"; /*************************************************************************************************** * * $DESCRIPTION Generates a new unique key. * $RETURN A string containing a new unique key. * **************************************************************************************************/ static function string makeKey() { local string key; local int index; local string cs; local int size; local int count; for (index = 0; index < len(keyFormat); index++) { cs = mid(keyFormat, index, 1); if ("0" <= cs && cs <= "9") { size = int(cs); for (count = 0; count < size; count++) { key = key $ mid(keyChars, rand(len(keyChars)), 1); } } else { key = key $ cs; } } return key; } /*************************************************************************************************** * * $DESCRIPTION Replaces a specified substring in a string with another substring. * $PARAM source The original string, which is to be filtered. * $PARAM oldStr Substring in the original string that is to be replaced. * $PARAM newStr Replacement for the substring to be replaced. * $RETURN The specified string where all occurrences of oldStr are replaced with newStr. * **************************************************************************************************/ static function string replace(string source, string oldStr, string newStr) { local bool bDone; local int subStrIndex; local string result; local string strLeft; strLeft = source; // Replace each occurrence of oldStr with newStr. while (!bDone) { // Find index of oldStr in the part not examined yet. subStrIndex = instr(strLeft, oldStr); // Update examined and unexamined parts. if (subStrIndex < 0) { bDone = true; result = result $ strLeft; } else { result = result $ left(strLeft, subStrIndex) $ newStr; strLeft = mid(strLeft, subStrIndex + len(oldStr)); } } // Return the filtered string. return result; } /*************************************************************************************************** * * $DESCRIPTION Formats the given string by inserting the specified strings into the proper * positions. The positions are indicated by the "%n" tags, where n is number of the * string to insert. * $PARAM source The string that is to be formatted. * $PARAM str1 String number 1 to insert. * $PARAM str2 String number 2 to insert. * $PARAM str3 String number 3 to insert. * $PARAM str4 String number 4 to insert. * $RETURN The formatted string. * **************************************************************************************************/ static function string format(string source, optional string str1, optional string str2, optional string str3, optional string str4) { local string formattedStr; formattedStr = replace(source, "%1", str1); formattedStr = replace(formattedStr, "%2", str2); formattedStr = replace(formattedStr, "%3", str3); formattedStr = replace(formattedStr, "%4", str4); return formattedStr; } /*************************************************************************************************** * * $DESCRIPTION Removes leading and trailing spaces from the given string. * $PARAM source The string for which the leading and trailing spaces are to be removed. * $RETURN The original string with all spaces removed from the front and back of the string. * $ENSURE len(result) > 0 ? left(result, 1) != " " && right(result, 1) != " " : true * **************************************************************************************************/ static function string trim(string source) { local int index; local string result; // Remove leading spaces. result = source; while (index < len(result) && mid(result, index, 1) == " ") { index++; } result = mid(result, index); // Remove trailing spaces. index = len(result) - 1; while (index >= 0 && mid(result, index, 1) == " ") { index--; } result = left(result, index + 1); // Return new string. return result; } /*************************************************************************************************** * * $DESCRIPTION Fills the given string from the left until it has a minimum length. * $PARAM source The original string. * $PARAM minLength Minimum length of the string. * $PARAM fillStr String used to fill up the original string. * $PARAM maxLength Maximum length of the string. * $REQUIRE minLength >= 0 && len(fillStr) > 0 * $RETURN The original string filled to a minimum length. * $ENSURE minLength <= len(result) && (maxLength >= minLength? len(result) <= maxLength : true) * **************************************************************************************************/ static function string lfill(coerce string source, int minLength, string fillStr, optional int maxLength) { local string result; // Add leading string until minLength is reached. result = source; while (len(result) < minLength) { result = fillStr $ result; } // Cut off. if (maxLength >= minLength && len(result) > maxLength) { result = right(result, maxLength); } // Return string. return result; } /*************************************************************************************************** * * $DESCRIPTION Fills the given string from the left until it has a minimum length. * $PARAM source The original string. * $PARAM minLength Minimum length of the string. * $PARAM fillStr String used to fill up the original string. * $PARAM maxLength Maximum length of the string. * $REQUIRE minLength >= 0 && len(fillStr) > 0 * $RETURN The original string filled to a minimum length. * $ENSURE minLength <= len(result) && (maxLength >= minLength? len(result) <= maxLength : true) * **************************************************************************************************/ static function string rfill(coerce string source, int minLength, string fillStr, optional int maxLength) { local string result; // Add trailing string until minLength is reached. result = source; while (len(result) < minLength) { result = result $ fillStr; } // Cut off. if (maxLength >= minLength && len(result) > maxLength) { result = left(result, maxLength); } // Return string. return result; } /*************************************************************************************************** * * $DESCRIPTION Parses the given command string. * $PARAM cmdStr The string containing the command and parameters. * $PARAM cmdName Name of the parsed command. * $PARAM args Arguments of the parsed command. * $RETURN True if the command is a Nexgen command, false if not. * **************************************************************************************************/ static function bool parseCommandStr(string cmdStr, out string cmdName, out string args[10]) { local string cmd; local int index; local int argCount; local string cc; local string lc; local int recStart; local int recEnd; local bool bRecStr; // Check command string. cmd = trim(cmdStr); index = instr(cmd, " "); if (index < 0 || caps(left(cmd, index)) != nexgenCommand) { // Not a valid command. return false; } // Get command name. cmd = trim(mid(cmd, index)); index = instr(cmd, " "); if (index < 0) { // No arguments, but it still is a commmand. cmdName = cmd; return true; } // Get command arguments. cmdName = left(cmd, index); cmd = mid(cmd, index) $ " "; // The extra space is to make sure the last argument gets stored. index = 0; recStart = -1; recEnd = -1; while (index < len(cmd) && argCount < arrayCount(args)) { // Fetch current character. cc = mid(cmd, index, 1); // Handle current character. if (cc == " ") { if (recStart >= 0 && !bRecStr) { // End of current argument. recEnd = index; } } else if (cc == "\"") { if (recStart < 0) { // Start recording a new argument. recStart = index + 1; bRecStr = true; } else if (lc != "\\") { // End of current argument. recEnd = index; bRecStr = false; } } else { if (recStart < 0) { // Start recording a new argument. recStart = index; } } // Store recorded strings. if (recEnd >= 0) { args[argCount++] = replace(mid(cmd, recStart, recEnd - recStart), "\\\"", "\""); recStart = -1; recEnd = -1; } // Continue with next character. lc = cc; index++; } // Yeah, it's a command. return true; } /*************************************************************************************************** * * $DESCRIPTION Adds a property to the given properties string. * $PARAM propStr The string where the property is to be added to. * $PARAM propName Name of the property to add. * $PARAM propValue The value of the property that is to be added. * $REQUIRE propName != "" * $ENSURE new.getProperty(propStr, propName) == propValue * **************************************************************************************************/ static function addProperty(out string propStr, string propName, coerce string propValue) { local string formattedValue; // Format value (in case it includes the separator tokens). formattedValue = replace(propValue, separator, escapeToken $ separator); // Update property string. if (propStr == "") { propStr = propName $ assignment $ formattedValue; } else { propStr = propStr $ separator $ propName $ assignment $ formattedValue; } } /*************************************************************************************************** * * $DESCRIPTION Reads a property from the given properties string. * $PARAM propStr The string where the property is to be read from. * $PARAM propName Name of the property to read. * $PARAM propDefaultValue The default value of the property to return if the property * string doesn't contain the specified property. * $REQUIRE propName != "" * $RETURN The value of the specified property in the given property string. * **************************************************************************************************/ static function string getProperty(string propStr, string propName, optional string propDefaultValue) { local int index; local bool bFound; local string remaining; local string saved; // Search until property is found or there are no properties left. remaining = propStr $ separator; index = instr(remaining, separator); while (!bFound && index >= 0) { // Check if the separator is preceeded by a escape token, i.e. a formatted value. if (index - len(escapeToken) >= 0 && mid(remaining, index - len(escapeToken), len(escapeToken)) == escapeToken) { // Escape token found, continue with next separator token. saved = saved $ left(remaining, index - len(escapeToken)) $ separator; remaining = mid(remaining, index + len(separator)); index = instr(remaining, separator); } else { // Property separator found, check name. saved = saved $ left(remaining, index); if (left(saved, len(propName) + len(assignment)) ~= (propName $ assignment)) { // Property found. bFound = true; } else { // Property name does not match continue search. saved = ""; remaining = mid(remaining, index + len(separator)); index = instr(remaining, separator); } } } // Return result. if (bFound) { return mid(saved, len(propName) + len(assignment)); } else { return propDefaultValue; } } /*************************************************************************************************** * * $DESCRIPTION Computes the date 'daysToCount' days after the specified date. * $PARAM daysToCount Number of days after the specified date. * $PARAM year Year of the starting date. * $PARAM month Month of the starting date. * $PARAM day Day of the starting date. * **************************************************************************************************/ static function computeDate(int daysToCount, out int year, out int month, out int day) { local int daysRemaining; daysRemaining = daysToCount; // Add years. while (daysRemaining > daysInYear(year)) { daysRemaining -= daysInYear(year); year++; } // Add months. while (daysRemaining > daysInMonth(year, month)) { daysRemaining -= daysInMonth(year, month); if (++month > 12) { month = 1; year++; } } // Add days. if (daysRemaining > daysInMonth(year, month) - day) { day = daysRemaining - daysInMonth(year, month) + day; if (++month > 12) { month = 1; year++; } } else { day += daysRemaining; } } /*************************************************************************************************** * * $DESCRIPTION Gives the number of days in the specified year. * $PARAM Year The year for which the number of days has to be returned. * $RETURN The number of days in the specified year. * $ENSURE result == isLeapYear(year) ? 366 : 365 * **************************************************************************************************/ static function int daysInYear(int year) { if (isLeapYear(year)) { return 366; } else { return 365; } } /*************************************************************************************************** * * $DESCRIPTION Gives the number of days in the specified year and month. * $PARAM Year The year for which the number of days has to be returned. * $PARAM Month The month for which the number of days has to be returned. * $RETURN The number of days in the specified year and month. * $ENSURE 28 <= result && result <= 31 * **************************************************************************************************/ static function int daysInMonth(int year, int month) { switch (month) { case 1: return 31; // January case 2: if (isLeapYear(year)) return 29; else return 28; // February case 3: return 31; // March case 4: return 30; // April case 5: return 31; // May case 6: return 30; // June case 7: return 31; // July case 8: return 31; // August case 9: return 30; // September case 10: return 31; // October case 11: return 30; // November case 12: return 31; // December }; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given year is a leap year. * $PARAM year The year for which has to be checked if it is a leap year or not. * $RETUN True if the specified year is a leap year, false if not. * **************************************************************************************************/ static function bool isLeapYear(int year) { return (year % 400 == 0) || (year % 4 == 0) && (year % 100 != 0); } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given IP address is valid. * $PARAM ipAddress The IP address that is to be checked. * $RETURN True if the specified IP address is valid, false if not. * **************************************************************************************************/ static function bool isValidIPAddress(string ipAddress) { local bool bValid; local string remaining; local int index; local int currentSection; local string section; local int char; // Check each section. bValid = true; currentSection = 1; remaining = ipAddress; while (bValid && currentSection <= 4) { // Get section split point. index = instr(remaining, "."); // Split head section from tail. if (currentSection == 4 && index > 0) { // Already at byte 4, but still some sections remaining. bValid = false; } else if (index < 0 && currentSection != 4) { // Premature end of address. bValid = false; } else if (index > 0) { section = left(remaining, index); remaining = mid(remaining, index + 1); } else { section = remaining; remaining = ""; } // Check section. if (bValid) { // Check section length. bValid = section != "" && len(section) <= 3; // Check section characters. index = 0; while (bValid && index < len(section)) { char = asc(caps(mid(section, index, 1))); bValid = asc("0") <= char && char <= asc("9"); index++; } // Check section value. bValid = bValid && int(section) < 256; // Section check completed. currentSection++; } } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given client ID is valid. * $PARAM clientID The client ID that is to be checked. * $RETURN True if the specified client ID is valid, false if not. * **************************************************************************************************/ static function bool isValidClientID(string clientID) { local bool bValid; local int index; local int char; // Check length. bValid = len(clientID) == 32; // Check characters. while (bValid && index < 32) { char = asc(caps(mid(clientID, index, 1))); if (!(asc("0") <= char && char <= asc("9")) && !(asc("A") <= char && char <= asc("F"))) { bValid = false; } else { index++; } } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Serializes the specified date to a compact date description string. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $REQUIRE (1 <= month && moth <= 12) && (1 <= day && day <= 31) && * (0 <= hour && hour <= 23) && (0 <= minute && minute <= 59) * $RETURN A description string of the serialized date. * **************************************************************************************************/ static function string serializeDate(int year, int month, int day, int hour, int minute) { return lfill(year, 4, "0") $ "_" $ lfill(month, 2, "0") $ "_" $ lfill(day, 2, "0") $ "_" $ lfill(hour, 2, "0") $ "_" $ lfill(minute, 2, "0"); } /*************************************************************************************************** * * $DESCRIPTION Parses the given date string. * $PARAM dateStr The date string to parse. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $RETURN True if the specified date string was valid, false if not. When false is returned * the outcome (the date) should be ignored. * **************************************************************************************************/ static function bool readDate(string dateStr, out int year, out int month, out int day, out int hour, out int minute) { local bool bValid; local string remaining; local int index; bValid = true; remaining = class'NexgenUtil'.static.trim(dateStr); // Parse year. index = instr(remaining, "_"); if (index >= 0) { year = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } // Parse month. if (bValid) { index = instr(remaining, "_"); if (index >= 0) { month = int(left(remaining, index)); remaining = class'NexgenUtil'.static.trim(mid(remaining, index + 1)); } else { bValid = false; } } // Parse day. if (bValid) { index = instr(remaining, "_"); if (index >= 0) { day = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse hour. if (bValid) { index = instr(remaining, "_"); if (index >= 0) { hour = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse minute. if (bValid) { minute = int(remaining); } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Splits the head element for the tail in given string list. Elements are separated * by the separator token. * $PARAM list The list that is to be split. * $PARAM head The first element in the list. * $PARAM tail The remaining elements in the list. * **************************************************************************************************/ static function split(string list, out string head, out string tail) { local int index; index = instr(list, separator); if (index < 0) { head = list; tail = ""; } else { head = left(list, index); tail = mid(list, index + len(separator)); } } /*************************************************************************************************** * * $DESCRIPTION Gives the formatted GUID string of the raw GUID. * $PARAM rawGUIDStr The raw GUID string. * $REQUIRE len(rawGUIDStr) == 32 * $RETURN The formatted GUID string. * $ENSURE len(result) == 36 * **************************************************************************************************/ static function string formatGUID(string rawGUIDStr) { return left(rawGUIDStr, 8) $ "-" $ mid(rawGUIDStr, 8, 4) $ "-" $ mid(rawGUIDStr, 12, 4) $ "-" $ mid(rawGUIDStr, 16, 4) $ "-" $ mid(rawGUIDStr, 20, 12); } /*************************************************************************************************** * * $DESCRIPTION Removes the leading color tag from the specified message. * $PARAM msg The message for which the color tag has to be removed. * $RETURN The original message without a leading color tag. * **************************************************************************************************/ static function string removeMessageColorTag(string msg) { if (left(msg, 2) ~= "") { return mid(msg, 5); } else { return msg; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the base color of the message. * $PARAM msg The message for which the base color is be determined. * $RETURN The base color of the message based on the leading color tag, or -1 if none is * present. * **************************************************************************************************/ static function int getMessageColor(string msg) { if (left(msg, 2) ~= "") { return int(mid(msg, 2, 2)); } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Gives the integer hash value of a string. * $PARAM str The string that is to be hashed. * $RETURN The hash value of the given string. * **************************************************************************************************/ static function int stringHash(coerce string str) { local int hash; local int index; for (index = 0; index < len(str); index++) { hash = 31 * hash + asc(mid(str, index, 1)); } return hash; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ Pz8[;R RP[\P‚r[Z š:\,ÆReconnect& u8€G9}87m €x8@96Ó/Ó%V'*'." ," )" (" +* 5*ËÆ¥ÿ"ÏãÏ¢Û @I LT¯H 5š™j6¡—z2“J ­›3”M.¢˜}K¹N.Ÿ–…"O¼m/ £†'Q½q-ž¤•7P»t&œ~pº‘y|„Y(4n€XgaZxs?r²okdc[`A0Žl’¸Š]wvhWS)+³·´uƒ{_e)$§ªµU ERB‹‰,¥=i¶®V«Œ 8¦D!1@¬ˆ,Cb<%‡> \‚:±©*9f^F°¨;G# ] "Reconnect"eC9g : ƒbeg l1b'g &$=g 1a'g %$=g ]^',@ ~Ÿ/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenTeamBalancer * $VERSION 1.03 (28-12-2007 21:16) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Team balancing support class. The sole purpose of this class is to provide team * balancing support. This support has been put in a separate class so plugins can * change the default team balancing routine to a custom routine. * **************************************************************************************************/ class NexgenTeamBalancer extends Info; var NexgenController control; // The Nexgen controller. const maxTeamCount = 4; // Maximum number of teams supported. /*************************************************************************************************** * * $DESCRIPTION Initializes the team balancer. * $OVERRIDE * **************************************************************************************************/ function preBeginPlay() { // Check owner. if (owner == none || !owner.isA('NexgenController')) { destroy(); return; } // Set controller. control = NexgenController(owner); } /*************************************************************************************************** * * $DESCRIPTION Attempts to balance the current teams. * $RETURN True if the teams have been balanced, false if they are already balanced. * **************************************************************************************************/ function bool balanceTeams() { local NexgenClient client; local int teamSize[4]; local int totalPlayers; local int numTeams; local int minPlayersPerTeam; local int index; local int targetTeam; local NexgenClient preferredSwitchers[32]; // Assume the game has at most 32 players. local int prefSwitchTeamOffsets[4]; local int switchedCount[4]; // Get number of teams. if (level.game.isA('TeamGamePlus')) { numTeams = TeamGamePlus(level.game).maxTeams; } else { // Not a team game, so we're unable to balance the teams. return false; } // Get current team sizes. for (client = control.clientList; client != none; client = client.nextClient) { if (!client.bSpectator && 0 <= client.team && client.team < maxTeamCount) { teamSize[client.team]++; totalPlayers++; } } // Check if teams are already balanced. if (teamSize[getLargestTeam(teamSize, numTeams)] - teamSize[getSmallestTeam(teamSize, numTeams)] < 2) { return false; } // Calculate minimum players per team. minPlayersPerTeam = totalPlayers / numTeams; // Get player switch desirability rankings. initPreferredSwitchers(preferredSwitchers, prefSwitchTeamOffsets); // Determine rebalance actions. for (index = 0; index < numTeams; index++) { while(teamSize[index] < minPlayersPerTeam || teamSize[index] > minPlayersPerTeam + 1) { // Switch a player. if (teamSize[index] < minPlayersPerTeam) { // Too few players, steal one from the largest team. targetTeam = getLargestTeam(teamSize, numTeams); teamSize[index]++; teamSize[targetTeam]--; switchPreferredPlayer(targetTeam, index, preferredSwitchers, prefSwitchTeamOffsets, switchedCount); } else { // Too many players, dump one at the smallest team. targetTeam = getSmallestTeam(teamSize, numTeams); teamSize[index]--; teamSize[targetTeam]++; switchPreferredPlayer(index, targetTeam, preferredSwitchers, prefSwitchTeamOffsets, switchedCount); } } } // Team balance complete. return true; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the index of the team with the most players. * $PARAM teamSize The current team sizes. * $PARAM numTeams The number of teams available in the current game. * $REQUIRE 0 < numTeams && numTeams <= maxTeamCount * $RETURN The team number of the biggest team. * $ENSURE 0 <= result && result < numTeams * **************************************************************************************************/ function int getLargestTeam(int teamSize[4], int numTeams) { local int largest; local int index; // Find largest team. for (index = 1; index < numTeams; index++) { if (teamSize[index] > teamSize[largest]) { largest = index; } } // Return result. return largest; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the index of the team with the least players. * $PARAM teamSize The current team sizes. * $PARAM numTeams The number of teams available in the current game. * $REQUIRE 0 < numTeams && numTeams <= maxTeamCount * $RETURN The team number of the smallest team. * $ENSURE 0 <= result && result < numTeams * **************************************************************************************************/ function int getSmallestTeam(int teamSize[4], int numTeams) { local int smallest; local int index; // Find smallest team. for (index = 1; index < numTeams; index++) { if (teamSize[index] < teamSize[smallest]) { smallest = index; } } // Return result. return smallest; } /*************************************************************************************************** * * $DESCRIPTION Initializes the list containing the players with the highest switch desirability. * $PARAM preferredSwitchers List containing the most preferred players for team * switching per team. Sorted in descending order. * $PARAM prefSwitchTeamOffsets Starting offsets in the preferredSwitchers array for each team. * **************************************************************************************************/ function initPreferredSwitchers(out NexgenClient preferredSwitchers[32], out int prefSwitchTeamOffsets[4]) { local int team; local int nextOffset; local NexgenClient client; local NexgenClient tempClient; local bool bSorted; local int index; // Add preferred players for each team. for (team = 0; team < maxTeamCount; team++) { prefSwitchTeamOffsets[team] = nextOffset; // Add each player belonging to this team. for (client = control.clientList; client != none; client = client.nextClient) { if (client.team == team && nextOffset < arrayCount(preferredSwitchers)) { // Add to end of list. preferredSwitchers[nextOffset] = client; // Sort list. bSorted = false; index = nextOffset - 1; while (!bSorted && index >= prefSwitchTeamOffsets[team]) { // Player is correctly positioned in the list? if (compareSwitchDesirability(preferredSwitchers[index], preferredSwitchers[index + 1]) >= 0) { // Yes, list sort done. bSorted = true; } else { // No, switch players. tempClient = preferredSwitchers[index]; preferredSwitchers[index] = preferredSwitchers[index + 1]; preferredSwitchers[index + 1] = tempClient; index--; } } // Update next player offset. nextOffset++; } } } } /*************************************************************************************************** * * $DESCRIPTION Switches the next preferred player from team oldTeam to newTeam. * $PARAM oldTeam The team from which a player is to be switched. * $PARAM newTeam Target team for the player. * $PARAM preferredSwitchers List containing the most preferred players for team * switching per team. Sorted in descending order. * $PARAM prefSwitchTeamOffsets Starting offsets in the preferredSwitchers array for each team. * $PARAM switchedCount Number of players that already have been switched for each team. * $REQUIRE 0 <= oldTeam && oldTeam <= maxTeamCount && * 0 <= newTeam && newTeam <= maxTeamCount && * oldTeam != newTeam * $ENSURE old.switchedCount[oldTeam] = old.switchedCount[oldTeam] + 1 * **************************************************************************************************/ function switchPreferredPlayer(int oldTeam, int newTeam, NexgenClient preferredSwitchers[32], int prefSwitchTeamOffsets[4], out int switchedCount[4]) { local NexgenClient preferred; // Get preferred player. preferred = preferredSwitchers[prefSwitchTeamOffsets[oldTeam] + switchedCount[oldTeam]]; // Switch the player. switchedCount[oldTeam]++; preferred.setTeam(newTeam); } /*************************************************************************************************** * * $DESCRIPTION Compares the switch desirability of two players. * $PARAM client1 The client of the first player. * $PARAM client2 The client of the second player. * $REQUIRE client1 != none && client2 != none && client1.team == client2.team * $RETURN -1 if the switch desirability of the first player is lower then the switch * desirability of the second player, 1 if it is higher and 0 if they are equal. * $ENSURE result == -1 || result == 0 || result == 1 * **************************************************************************************************/ function int compareSwitchDesirability(NexgenClient client1, NexgenClient client2) { if (client1.lastSwitchTime < client2.lastSwitchTime && client2.player.playerReplicationInfo.hasFlag == none || client1.player.playerReplicationInfo.hasFlag != none) { return -1; } else { return 1; } } PD9^;O RP^_P‚r^] š:_,ÆReconnect& I98H] €X'H92QL  Þgk q}†o-g6ék ­$À6ìk ­$À6èk ­$ÀÈk º¾ çÞk _'`' L9hC,Ž5.gø-g-hø-Â-h Ê:ywÀø.ÄØ gº¾øýø-Ã'Ö¬º?,R äá ` "OverrideClass"M9 "Botpack.CHSpectator"N9 "Reconnect"eO9e :$zbee l1]'e &$=e 1Z'e %$=e `Y',@ €¶PP9c># „Pcj‚‚rc` š:j,Æ(b"OverrideClassBotpack.CHSpectator'ÆReconnect& U9\:  AT9K.$ F/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenSimplePlayerListBox * $VERSION 1.00 (13-10-2007 18:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Simple player listbox GUI component. * **************************************************************************************************/ class NexgenSimplePlayerListBox extends NexgenPlayerListBox; /*************************************************************************************************** * * $DESCRIPTION Returns the text displayed for a list item. * $PARAM item The item for which its display text has to be determined. * $REQUIRE item != none * $RETURN The text that should be displayed for the specified item in the listbox. * $OVERRIDE * **************************************************************************************************/ function string getDisplayText(NexgenPlayerList item) { return item.pName; } u/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenSimpleListItem * $VERSION 1.00 (14-10-2007 22:36) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Simple list item description class. Stores only two properties a display name and * an item identifier. * **************************************************************************************************/ class NexgenSimpleListItem extends UWindowListBoxItem; var string displayText; var int itemID; /*************************************************************************************************** * * $DESCRIPTION Compares two UWindowList items. * $PARAM a First item to compare. * $PARAM b Second item to compare. * $REQUIRE a != none && b != none * $RETURNS -1 If the first item is 'smaller' then the second item, otherwise 1 is returned. * $OVERRIDE * **************************************************************************************************/ function int compare(UWindowList a, UWindowList b) { if (NexgenSimpleListItem(a).displayText < NexgenSimpleListItem(b).displayText) { return -1; } else { return 1; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ åZ9Q9}Tà wÓ*Ó $  C € \9T'i?T YC . ®UwC *9šC $T'C C . C Þ* 6Ó/Ó%V'*'." ," )" (" +* 5*ËÆ¥ÿ"NoÐÑ¢Û Î<@ILT¯Hš™j¡—z“­›”M¢˜}¹NŸ–…¼m £†Q½qž¤•P»tœ~pº‘y|„Yn€XgaZxsr²okdc[`Žl’¸Š]wvhWS³·´uƒ{_e§ªµUER‹‰¥i¶®V«Œ¦¬ˆb‡\‚±©f^F°¨G ôP^?YÉ. k-É6éP­$6ìP­$6èP­$€ÈPjiQ'¯R'?& ç6éP­=,ÿ6ìP­=,ÿ6èP­=,ÿ 6éP­$6ìP­$6èP­$Pæ%Ü(ÎÑP®j?,i. k5 k "Password"]9 "OverrideClass"^9 "Reconnect"_9 "Botpack.CHSpectator"e`9z:)]¡ezl1W'z&$=z1U'z%$=zz`zS'€ByL',@kM',@z, x4/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenSimpleListBox * $VERSION 1.01 (31-10-2007 13:44) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Simple listbox GUI component. * **************************************************************************************************/ class NexgenSimpleListBox extends UWindowListBox; /*************************************************************************************************** * * $DESCRIPTION Renders the specified listbox item. * $PARAM c The canvas object on which the rendering will be performed. * $PARAM item Item to render. * $PARAM x Horizontal offset on the canvas. * $PARAM y Vertical offset on the canvas. * $PARAM w Width of the item that is to be rendered. * $PARAM h Height of the item that is to be rendered. * $REQUIRE c != none && item != none * $OVERRIDE * **************************************************************************************************/ function drawItem(Canvas c, UWindowList item, float x, float y, float w, float h) { if(NexgenSimpleListItem(item).bSelected) { c.drawColor.r = 0; c.drawColor.g = 0; c.drawColor.b = 128; drawStretchedTexture(c, x, y, w, h - 1, Texture'WhiteTexture'); c.drawColor.r = 255; c.drawColor.g = 255; c.drawColor.b = 255; } else { c.drawColor.r = 0; c.drawColor.g = 0; c.drawColor.b = 0; } c.font = root.fonts[F_Normal]; clipText(c, x + 2, y, NexgenSimpleListItem(item).displayText); } /*************************************************************************************************** * * $DESCRIPTION Retrieves the item with the specified id number. * $PARAM itemID The id number of the item to return. * $RETURN The item that has the specified id number, or none if there is no item with the * specified id number. * **************************************************************************************************/ function NexgenSimpleListItem getItemByID(int itemID) { local NexgenSimpleListItem item; // Search for item. for (item = NexgenSimpleListItem(items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.itemID == itemID) { return item; } } // Item not found, return none. return none; } /*************************************************************************************************** * * $DESCRIPTION Called when an item was double clicked on. * $PARAM item The item which was double clicked. * $REQUIRE item != none * $OVERRIDE * **************************************************************************************************/ function doubleClickItem(UWindowListBoxItem item) { if (notifyWindow != none) { notifyWindow.notify(self, DE_DoubleClick); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ qa9K'I» Lz$ _ -rPasswordy-žrK' €@f9@g9@@Pc9B[ 'PBA‚rBk š:A,_ `r_ $<rPasswordz _ bÆReconnect&%‚‚rBy š:A,y-žÆ(b"OverrideClassBotpack.CHSpectator'ÆReconnect& @8 "ip"i9 "name"CI/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenServerFullDialog * $VERSION 1.01 (23-12-2006 14:15) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player has entered an invalid password. * **************************************************************************************************/ class NexgenServerFullDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var UWindowSmallButton spectatorButton; // Spectator button component. var UMenuLabelControl slotLabel; // Slot label component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string slotMessage; // Message describing the amount of slots. var localized string passwordText; // Label to display before the password field. var localized string reconnectText; // Text to display on the reconnect button. var localized string spectatorText; // Text to display on the spectator button. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const reconnectCommand = "Reconnect"; // Console command for reconnecting. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE reconnectButton != none && spectatorButton != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); slotLabel = addLabel(cy); spectatorButton = addButton(spectatorText, 64.0); reconnectButton = addButton(reconnectText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM playerSlots Number of slots available for regular players. * $PARAM vipSlots Number of slots available for VIPs. * $PARAM adminSlots Number of slots available for administrators. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string playerSlots, optional string vipSlots, optional string adminSlots, optional string str4) { slotLabel.setText(class'NexgenUtil'.static.format(slotMessage, playerSlots, vipSlots, adminSlots)); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { getplayerowner().consoleCommand(reconnectCommand); close(); } // Spectator button. if (control == spectatorButton && eventType == DE_Click && !spectatorButton.bDisabled) { getplayerowner().updateURL(SSTR_OverrideClass, spectatorClass, true); getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ j9 "title"@:xQExÎr-w/*/=Qxvp‚–,wQ*¥QQxv- Âh&J'BnJ'h €«k&v9n~1¤C;n-É2..n-UB'8{Am €ñV 60er9~:$"Ëe~l1I'~&$=~tT~H'€B%VT~F'€B&V~,V~,V~,V~E',@ Bk9[:d ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2 t9 yiBêÓnÅŽ> £Y×7XÃŽ> £YŽ> £Y¤á±˜Š M± Z– T]"!Failed to login: server is full.m:xThe server you have tried to enter has no more player slots available. You can try again in a few minutes or reconnect immediately as a spectator. Note that the server may appear to have some unused slots available. These are reserved slots for VIPs or administrators and are not accessible for regular players.L]MKThis server has %1 regular players slots, %2 VIP slots and %3 admin slots.Q] Password:\] ReconnectX] Spectatorqs9C'JÚ zt A'mpC'\n@‚{m –d ,Z~m\n@€mZmm’Z}\n’šd %z,<zd~M@zÔ–~%y@@øy€@~@@~¥d V y|„z@ ™d ,%Zd x–Z,ZV¥ZK e&w9D'FÂ>+..D'-U&Ü(Î<%Ü(Î B'«y9@d @l´/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenScrollPanelContainer * $VERSION 1.01 (11-3-2008 21:32) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page container scroll version. * **************************************************************************************************/ class NexgenScrollPanelContainer extends NexgenPanelContainer; var UWindowPageWindow clientArea; // Scrollable window. var UWindowVScrollBar scrollBar; // Vertical scrollbar control. var NexgenPanel panels[32]; // Panels displayed on this panel. var int numPanels; // Number of panels displayed. var float clientAreaDesiredHeight; // Desired height of the client area. var float nextPanelOffset; // Next vertical offset on the client area. const panelDistance = 4.0; // Distance between panels. const borderDistance = 6.0; // Disatnce between panels and the border of the client area. const defaultPanelHeight = 288.0; // Default height of panels displayed on the client area. /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this panel. * $OVERRIDE * **************************************************************************************************/ function created() { super(NexgenPanel).created(); clientArea = UWindowPageWindow(createWindow(class'UWindowPageWindow', 0, 0, winWidth - 16, winHeight - 6, ownerWindow)); scrollBar = UWindowVScrollbar(createWindow(class'UWindowVScrollbar', winWidth - 16, 0, 12, winHeight - 6)); scrollBar.bAlwaysOnTop = true; clientAreaDesiredHeight = 2 * borderDistance; nextPanelOffset = borderDistance; } /*************************************************************************************************** * * $DESCRIPTION Prepares the window for the paint call. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function beforePaint(Canvas c, float x, float y) { local float clientWidth, clientHeight; local bool bNeedScrollBar; clientWidth = winWidth - 12; clientHeight = clientAreaDesiredHeight; if (clientHeight <= winHeight) { clientHeight = winHeight; } else { bNeedScrollBar = true; } clientArea.setSize(clientWidth, clientHeight); if (bNeedScrollBar) { scrollBar.setRange(0, clientHeight, scrollBar.winHeight, 10); } else { scrollBar.setRange(0, 0, 0, 0); scrollBar.pos = 0; } clientArea.winTop = -scrollBar.pos; super.beforePaint(c, x, y); } /*************************************************************************************************** * * $DESCRIPTION Retrieves the desired window dimensions. * $PARAM w The desired width. * $PARAM h The desired height. * $OVERRIDE * **************************************************************************************************/ function getDesiredDimensions(out float w, out float h){ super(UWindowWindow).getDesiredDimensions(w, h); } /*************************************************************************************************** * * $DESCRIPTION Renders the GUI component. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y) { // Ignore. } /*************************************************************************************************** * * $DESCRIPTION Retrieves the panel with the specified name. * $PARAM panelName Name of the panel that is to be returned. * $REQUIRE panelName != "" * $RETURN The panel that was requested or none if the panel wasn't found. * $OVERRIDE * **************************************************************************************************/ function NexgenPanel getPanel(string panelName) { local NexgenPanel panel; local int index; // Search for panel. while (panel == none && index < numPanels) { if (panels[index].panelIdentifier ~= panelName) { panel = panels[index]; } else if (panels[index].isA('NexgenPanelContainer')) { panel = NexgenPanelContainer(panels[index]).getPanel(panelName); } index++; } // Return result. return panel; } /*************************************************************************************************** * * $DESCRIPTION Adds a new NexgenPanel to the container or one of its subcontainers. To specify * a specific parent use the parent parameter to indicate the path, e.g. * "plugin,settings". If an invalid path is specified the panel won't be created. * $PARAM title Text to display in the tab header. * $PARAM panelClass Type of NexgenPanel to add/create. * $PARAM identifier Identifier to assign to the new panel. * $PARAM parent Path where to the parent of the new panel. * $REQUIRE panelClass != none * $RETURN The panel that was created and added to the container, or none if an invalid path * to the parent container was specified. * $OVERRIDE * **************************************************************************************************/ function NexgenPanel addPanel(string title, class panelClass, optional string identifier, optional string parent) { local NexgenPanel newPanel; local string parentPanel; local string subPanels; local NexgenPanelContainer container; local bool bFound; local int index; local float desiredPanelHeight; // Add to subpanel? if (parent != "") { // Get local parent name. class'NexgenUtil'.static.split(parent, parentPanel, subPanels); // Locate local parent. while (!bFound && index < numPanels) { if (panels[index].isA('NexgenPanelContainer') && NexgenPanelContainer(panels[index]).panelIdentifier ~= parentPanel ) { bFound = true; newPanel = NexgenPanelContainer(panels[index]).addPanel(title, panelClass, identifier, subPanels); } else { index++; } } } else { // Nope, add it to the scroll panel. if (numPanels < arrayCount(panels)) { // Determine panel height. desiredPanelHeight = panelClass.default.panelHeight; if (desiredPanelHeight <= 0) { desiredPanelHeight = defaultPanelHeight; } // Create control. newPanel = NexgenPanel(clientArea.createWindow(panelClass, borderDistance, nextPanelOffset, clientArea.winWidth - 2 * borderDistance, desiredPanelHeight)); panels[numPanels] = newPanel; if (identifier != "") { newPanel.panelIdentifier = identifier; } newPanel.client = self.client; newPanel.setContent(); // Update client area metrics. clientAreaDesiredHeight += desiredPanelHeight; if (numPanels > 0) { clientAreaDesiredHeight += panelDistance; } nextPanelOffset += desiredPanelHeight + panelDistance; numPanels++; } } // Return created panel. return newPanel; } /*************************************************************************************************** * * $DESCRIPTION Selects the panel with the specified name. * $PARAM panelName The name of the panel that is to be selected. * $RETURN True if the panel was selected, false if it wasn't found. * $OVERRIDE * **************************************************************************************************/ function bool selectPanel(string panelName) { local int index; local bool bFound; while (!bFound && index < numPanels) { // Check panel. if (panels[index].panelIdentifier ~= panelName) { // Panel found, select it. bFound = true; } else if (panels[index].isA('NexgenPanelContainer')) { // No, but page is a NexgenPanelContainer, so it may include the panel. bFound = NexgenPanelContainer(panels[index]).selectPanel(panelName); } // Panel found? if (bFound) { // Panel found, select it. scrollBar.pos = panels[index].winTop; } else { // No, continue with next page. index++; } } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Notifies the sub panels on this panel that the server configuration has been * updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { local int index; for (index = 0; index < numPanels; index++) { panels[index].configChanged(configType); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { local int index; for (index = 0; index < numPanels; index++) { panels[index].gameInfoChanged(infoType); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * $OVERRIDE * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { local int index; for (index = 0; index < numPanels; index++) { panels[index].playerEvent(playerNum, eventType, args); } } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { local int index; for (index = 0; index < numPanels; index++) { panels[index].notifyEvent(type, arguments); } } l9 "team"e/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenResidentConfigDialog * $VERSION 1.00 (16-12-2007 15:32) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog displayed if a resident Nexgen configuration is detected. * **************************************************************************************************/ class NexgenResidentConfigDialog extends NexgenPopupDialog; var localized string caption; // Caption to display on the dialog. var localized string message; // Message to dispay on the dialog. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ B|9F:b ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2}9ÉGç§BêÓnÅ– T]&%Local Nexgen configuration detected!mRPA Nexgen configuration has been detected on your client. This may prevent your client from initializing correctly. If you experience problems delete Nexgen.ini or the Nexgen configuration stored UnrealTournament.ini. You can find these files in the system folder of your Unreal Tournament installation.\n \nSorry for the inconvenience.`"NE:p]ð[#ecw›*dpq.› Ud›e›*p Q d Uæ/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPUserAccounts * $VERSION 1.02 (2-11-2007 21:11) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen user account control panel page. * **************************************************************************************************/ class NexgenRCPUserAccounts extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowCheckbox rightEnableInp[16]; var NexgenPlayerACListBox playerList; var NexgenSimpleListBox accountList; var UWindowComboControl accountTypeList; var NexgenEditControl accountNameInp; var NexgenEditControl accountTitleInp; var UWindowSmallButton addButton; var UWindowSmallButton updateButton; var UWindowSmallButton deleteButton; const invalidAccountType = -1; // Invalid or no account type selected. const customAccountType = 0; // Custom account type selected. const defaultAccountType = 1; // Default account type selected. /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; local string rightDef; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); divideRegionH(2, defaultComponentDist); // User info panel. p = addContentPanel(); p.splitRegionH(64); p.splitRegionV(96); p.splitRegionH(16, , , true); p.divideRegionH(3); p.divideRegionH(3); p.divideRegionV(2, defaultComponentDist); p.splitRegionV(192); p.addLabel(client.lng.userNameTxt, true); p.addLabel(client.lng.accountTypeTxt, true); p.addLabel(client.lng.userTitleTxt, true); accountNameInp = p.addEditBox(); accountTypeList = p.addListCombo(); accountTitleInp = p.addEditBox(); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionV(3, defaultComponentDist); p.skipRegion(); for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { rightEnableInp[index] = p.addCheckBox(TA_Left, client.lng.format(client.lng.rightNotDefinedTxt, string(index + 1))); } else { rightEnableInp[index] = p.addCheckBox(TA_Left, mid(rightDef, instr(rightDef, client.sConf.separator) + 1)); } } updateButton = p.addButton(client.lng.updateTxt); addButton = p.addButton(client.lng.addTxt); deleteButton = p.addButton(client.lng.deleteTxt); // Account / player lists. splitRegionH(16); splitRegionH(16); addLabel(client.lng.onlineTxt, true, TA_Center); playerList = NexgenPlayerACListBox(addListBox(class'NexgenPlayerACListBox')); addLabel(client.lng.offlineTxt, true, TA_Center); accountList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Configure components. accountNameInp.setMaxLength(32); accountTitleInp.setMaxLength(24); accountTypeList.setEditable(false); playerList.register(self); accountList.register(self); updateButton.register(self); deleteButton.register(self); accountTypeList.register(self); loadUserAccounts(); loadAccountTypes(); accountSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the account types. * **************************************************************************************************/ function loadAccountTypes() { local int index; accountTypeList.clear(); accountTypeList.addItem(client.lng.customAccountTxt, string(-1)); while(index < arrayCount(client.sConf.atTypeName) && client.sConf.atTypeName[index] != "") { accountTypeList.addItem(client.sConf.atTypeName[index], string(index)); index++; } } /*************************************************************************************************** * * $DESCRIPTION Load the user account list. * **************************************************************************************************/ function loadUserAccounts() { local int index; local NexgenPlayerACListItem playerItem; local NexgenSimpleListItem accountItem; // Clear account list. accountList.items.clear(); accountList.selectedItem = none; // Load each user account. while (index < arrayCount(client.sConf.paPlayerID) && client.sConf.paPlayerID[index] != "") { playerItem = NexgenPlayerACListItem(playerList.getPlayerByID(client.sConf.paPlayerID[index])); // Client is online? if (playerItem == none) { // Nope, add to account list. accountItem = NexgenSimpleListItem(accountList.items.append(class'NexgenSimpleListItem')); accountItem.itemID = index; accountItem.displayText = "[" $ client.sConf.getUserAccountTitle(index) $ "] " $ client.sConf.paPlayerName[index]; } // Continue with next user account. index++; } // Load account info for online players. updatePlayerList(); } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { local NexgenPlayerACListItem playerItem; local NexgenSimpleListItem accountItem; local bool bFound; local int accountNum; // Player has joined the game? if (eventType == client.PE_PlayerJoined) { // Add player. playerItem = NexgenPlayerACListItem(addPlayerToList(playerList, playerNum, args)); // Check if player has an account. accountNum = client.sConf.getUserAccountIndex(playerItem.pClientID); if (accountNum >= 0) { // Player has an account. // Update attributes. playerItem.bHasAccount = true; playerItem.accountNum = accountNum; // Move account item to player list. accountItem = accountList.getItemByID(accountNum); if (accountList.selectedItem == accountItem) { playerItem.bSelected = true; playerList.selectedItem = playerItem; accountList.selectedItem = none; } accountItem.remove(); } } // Player has left the game? if (eventType == client.PE_PlayerLeft) { // Check if player has an account. playerItem = NexgenPlayerACListItem(playerList.getPlayer(playerNum)); if (playerItem != none && playerItem.bHasAccount) { accountItem = NexgenSimpleListItem(accountList.items.append(class'NexgenSimpleListItem')); accountItem.itemID = playerItem.accountNum; accountItem.displayText = "[" $ client.sConf.getUserAccountTitle(playerItem.accountNum) $ "] " $ client.sConf.paPlayerName[playerItem.accountNum]; if (playerList.selectedItem == playerItem) { accountItem.bSelected = true; accountList.selectedItem = accountItem; playerList.selectedItem = none; } } // Delete from player list. playerList.removePlayer(playerNum); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local string clientID; local string accountName; local int accountType; local string rights; local string title; super.notify(control, eventType); setRPCI(); // Online player selected. if (control == playerList && eventType == DE_Click) { if (accountList.selectedItem != none) { accountList.selectedItem.bSelected = false; accountList.selectedItem = none; } accountSelected(); } // Offline player selected. if (control == accountList && eventType == DE_Click) { if (playerList.selectedItem != none) { playerList.selectedItem.bSelected = false; playerList.selectedItem = none; } accountSelected(); } // Account type selected. if (control == accountTypeList && eventType == DE_Change) { accountTypeSelected(); } // Delete button clicked. if (control == deleteButton && eventType == DE_Click && !deleteButton.bDisabled && rpci != none) { rpci.deleteAccount(getSelectedAccountNum()); } // Update button clicked. if (control == updateButton && eventType == DE_Click && !updateButton.bDisabled && rpci != none) { accountName = class'NexgenUtil'.static.trim(accountNameInp.getValue()); accountType = accountTypeList.getSelectedIndex() - 1; if (accountType < 0) { rights = getCurrentRights(); title = class'NexgenUtil'.static.trim(accountTitleInp.getValue()); } else { rights = ""; title = ""; } rpci.updateAccount(getSelectedAccountNum(), accountName, accountType, rights, title); } // Add button clicked. if (control == addButton && eventType == DE_Click && !addButton.bDisabled && rpci != none) { clientID = NexgenPlayerACListItem(playerList.selectedItem).pClientID; accountName = class'NexgenUtil'.static.trim(accountNameInp.getValue()); accountType = accountTypeList.getSelectedIndex() - 1; if (accountType < 0) { rights = getCurrentRights(); title = class'NexgenUtil'.static.trim(accountTitleInp.getValue()); } else { rights = ""; title = ""; } rpci.addAccount(clientID, accountName, accountType, rights, title); } } /*************************************************************************************************** * * $DESCRIPTION Returns a string containing the currently selected rights. * $RETURN A string containing the rights currently selected. * **************************************************************************************************/ function string getCurrentRights() { local string rights; local string rightDef; local int index; // Check for each right if it is selected. for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightEnableInp[index].bChecked && rightDef != "") { if (rights == "") { rights = left(rightDef, instr(rightDef, separator)); } else { rights = rights $ separator $ left(rightDef, instr(rightDef, separator)); } } } // Return result. return rights; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the account number of the account currently selected from the online or * offline account lists. * $RETURN The account number of the currently selected account, or -1 if no account was * selected. * **************************************************************************************************/ function int getSelectedAccountNum() { local int accountNum; // Get account number of selected online / offline account. if (playerList.selectedItem != none) { // Online account selected. accountNum = NexgenPlayerACListItem(playerList.selectedItem).accountNum; } else if (accountList.selectedItem != none) { // Offline account selected. accountNum = NexgenSimpleListItem(accountList.selectedItem).itemID; } else { // No account selected. accountNum = -1; } // Return account number. return accountNum; } /*************************************************************************************************** * * $DESCRIPTION Checks if the currently selected item has an user account. * $RETURN True if the currently selected item has an user account, false if not. * **************************************************************************************************/ function bool selectionHasAccount() { local bool bHasAccount; // Check if selected item has an account. if (playerList.selectedItem != none) { // Online account selected. bHasAccount = NexgenPlayerACListItem(playerList.selectedItem).bHasAccount; } else if (accountList.selectedItem != none) { // Offline account selected. bHasAccount = true; } else { // No account selected. bHasAccount = false; } // Return result. return bHasAccount; } /*************************************************************************************************** * * $DESCRIPTION Called when an user account was selected from the list. * **************************************************************************************************/ function accountSelected() { local bool bItemSelected; local bool bHasAccount; local int index; local int accountNum; // Get account number of selected online / offline account. accountNum = getSelectedAccountNum(); bHasAccount = selectionHasAccount(); // Account selected? bItemSelected = accountNum >= 0; accountNameInp.setDisabled(!bItemSelected); updateButton.bDisabled = !bItemSelected || !bHasAccount || !canEditAccount(accountNum); deleteButton.bDisabled = !bItemSelected || !bHasAccount || !canEditAccount(accountNum); addButton.bDisabled = !bItemSelected || bHasAccount; // $TODO Implement UWindowComboControl class that can be disabled. if (bHasAccount && bItemSelected) { accountNameInp.setValue(client.sConf.paPlayerName[accountNum]); accountTypeList.setSelectedIndex(client.sConf.paAccountType[accountNum] + 1); } else if (!bHasAccount && bItemSelected) { accountNameInp.setValue(NexgenPlayerACListItem(playerList.selectedItem).pName); accountTypeList.setSelectedIndex(defaultAccountType); } else { accountNameInp.setValue(""); accountTypeList.setSelectedIndex(invalidAccountType); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client can delete the specified account. * $PARAM accountNum The number of the account for which has to be checked if it can be * deleted by this client. * $REQUIRE 0 <= accountNum && accountNum < arrayCount(client.sConf.paPlayerID) && * paPlayerID[accountNum] != none * $RETURN True if account can be deleted by this client, false if not. * **************************************************************************************************/ function bool canEditAccount(int accountNum) { local string rights; // Get rights of target account. if (client.sConf.paAccountType[accountNum] < 0) { rights = client.sConf.paCustomRights[accountNum]; } else { rights = client.sConf.atRights[client.sConf.paAccountType[accountNum]]; } // Return result. return client.hasRight(client.R_ServerAdmin) || !hasRight(rights, client.R_ServerAdmin); } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified right is included in the given right string. * $PARAM rights The rights specifier string. * $PARAM rightID String identifier of the client right. * $REQUIRE rightID != "" * $RETURN True if the right is included, false if not. * **************************************************************************************************/ function bool hasRight(string rights, string rightID) { return instr(rights $ separator, rightID $ separator) >= 0; } /*************************************************************************************************** * * $DESCRIPTION Called when an account type was selected from the list. * **************************************************************************************************/ function accountTypeSelected() { local int accountTypeNum; local int accountNum; local int index; local string rightDef; local string rightStr; local bool bItemSelected; local bool bHasAccount; local string title; local bool bClientIsServerAdmin; bClientIsServerAdmin = client.hasRight(client.R_ServerAdmin); // Get currently selected account type. accountTypeNum = accountTypeList.getSelectedIndex(); // Get account number of selected online / offline account. accountNum = getSelectedAccountNum(); bItemSelected = accountNum >= 0; bHasAccount = selectionHasAccount(); // Retieve title. if (accountTypeNum == invalidAccountType) { title = ""; } else if (accountTypeNum == customAccountType) { if (bHasAccount) { title = client.sConf.paCustomTitle[accountNum]; } else { title = client.sConf.getAccountTypeTitle(0); } } else { title = client.sConf.getAccountTypeTitle(accountTypeNum - 1); } // Update GUI. if (accountTypeNum == invalidAccountType) { addButton.bDisabled = true; updateButton.bDisabled = true; } else if (bHasAccount) { addButton.bDisabled = true; updateButton.bDisabled = !canEditAccount(accountNum) || !bClientIsServerAdmin && !canUseAccountType(accountTypeNum); } else { addButton.bDisabled = !bClientIsServerAdmin && !canUseAccountType(accountTypeNum); updateButton.bDisabled = true; } // Update title. accountTitleInp.setValue(title); accountTitleInp.setDisabled(accountTypeNum != customAccountType); // Retrieve rights. if (accountTypeNum == invalidAccountType) { rightStr = ""; } else if (accountTypeNum == customAccountType) { if (bHasAccount) { rightStr = client.sConf.paCustomRights[accountNum]; } else { rightStr = client.sConf.atRights[0]; } } else { rightStr = client.sConf.atRights[accountTypeNum - 1]; } // Update rights. for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { rightEnableInp[index].bDisabled = true; } else { rightEnableInp[index].bDisabled = accountTypeNum != customAccountType || !bClientIsServerAdmin && !client.hasRight(left(rightDef, instr(rightDef, separator))); rightEnableInp[index].bChecked = hasRight(rightStr, left(rightDef, instr(rightDef, separator))); } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client can grant the specified account type. * $PARAM accountType The account type that is to be checked. * $REQUIRE 0 <= accountType && accountType <= arrayCount(client.atTypeName) * $RETURN True if the client can grant the specified account type, false if not. * **************************************************************************************************/ function bool canUseAccountType(int accountType) { return (accountType == customAccountType) || (client.hasRights(client.sConf.atRights[accountType - 1])); } /*************************************************************************************************** * * $DESCRIPTION Updates the player list. Iterates over all players in the player list and resets * their account attributes. * **************************************************************************************************/ function updatePlayerList() { local NexgenPlayerACListItem item; local int index; // For each player... for (item = NexgenPlayerACListItem(playerList.items); item != none; item = NexgenPlayerACListItem(item.next)) { // Check account... index = client.sConf.getUserAccountIndex(item.pClientID); // Set account info... if (index >= 0) { item.bHasAccount = true; item.accountNum = index; item.pTitle = client.sConf.getUserAccountTitle(index); } else { item.bHasAccount = false; item.accountNum = 0; item.pTitle = client.sConf.getAccountTypeTitle(0); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_AccountTypes) { loadUserAccounts(); loadAccountTypes(); accountSelected(); } if (configType == client.sConf.CT_UserAccounts) { loadUserAccounts(); accountSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ B:~&w/¨Ê³}.>/=wpluginsettings›r}*}.>/=@" A$ Apluginsettingsserver,serversettings} "~& Pu9C€¯SPCEQ‚rC š:E,fE v@& C:xYêÇVFtkx•¢Yx(DNamex' I:s }ļº„|;ppget  f.NexgenConfigExt bInstalledT'I|;ppget  f.NexgenConfigSys bInstalledT'GNexgenResidentConfigDialog }€H:{&KÙU Y}.®Uw}*9|}X{&}}.}Þ* e|&@:qDe@l1|&@&$=@1z&@%$ s€J:w&UÀæYs.®Uws*9šsdw&ss.sÞ* ]:v&lðwÀÌ`•¢a/!Y-F.¼•¢-ð.¼•¢-ð(Ktk d:v&Ê•¢a/!Y.¼•¢-ð-F L:t&aª±`e.®^we*Bšedt&eee.eÞ €O:K:q™e.® Bø €«n&€@k&o&wŒ¬:o&sW& €«H "OverrideClass"Q: "Reconnect"R: "Botpack.CHSpectator"eS:i:)F†eil1r&i&$=i1q&i%$=iLism&,@Hn&,@ l¾/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPServerSettings3 * $VERSION 1.02 (15-12-2007 15:40) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen about control panel page. * **************************************************************************************************/ class NexgenRCPServerSettings3 extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox ignoredWeaponList; var NexgenSimpleListBox hudReplacementList; var UWindowSmallButton weapSaveButton; var UWindowSmallButton weapRemButton; var UWindowSmallButton hudReplSaveButton; var UWindowSmallButton hudReplRemButton; var UWindowCheckbox ignorePrimaryFireInp; var UWindowCheckbox ignoreAltFireInp; var NexgenEditControl weaponClassInp; var NexgenEditControl originalHUDClassInp; var NexgenEditControl replacementHUDClassInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); // Ignored weapons. splitRegionH(88, defaultComponentDist); p = addContentPanel(); p.splitRegionV(256, defaultComponentDist); ignoredWeaponList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.divideRegionH(4); p.addLabel(client.lng.ignoredWeaponsTxt, true, TA_Center); p.splitRegionV(96); p.divideRegionV(2, 2 * defaultComponentDist); p.splitRegionV(192); p.addLabel(client.lng.weaponClassTxt); weaponClassInp = p.addEditBox(); ignorePrimaryFireInp = p.addCheckBox(TA_Left, client.lng.ignorePrimaryFireTxt); ignoreAltFireInp = p.addCheckBox(TA_Left, client.lng.ignoreAltFireTxt); p.divideRegionV(2, defaultComponentDist); p.skipRegion(); weapSaveButton = p.addButton(client.lng.saveTxt); weapRemButton = p.addButton(client.lng.removeTxt); // HUD replacement classes. splitRegionH(88, defaultComponentDist); p = addContentPanel(); p.splitRegionV(256, defaultComponentDist); hudReplacementList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.divideRegionH(4); p.addLabel(client.lng.hudReplaceClassesTxt, true, TA_Center); p.splitRegionV(128); p.splitRegionV(128); p.splitRegionV(192); p.addLabel(client.lng.originalHUDClassTxt); originalHUDClassInp = p.addEditBox(); p.addLabel(client.lng.replacementHUDClassTxt); replacementHUDClassInp = p.addEditBox(); p.divideRegionV(2, defaultComponentDist); p.skipRegion(); hudReplSaveButton = p.addButton(client.lng.saveTxt); hudReplRemButton = p.addButton(client.lng.removeTxt); // Configure components. weaponClassInp.setMaxLength(64); originalHUDClassInp.setMaxLength(64); replacementHUDClassInp.setMaxLength(64); loadIgnoredWeaponList(); loadHUDReplacementList(); } /*************************************************************************************************** * * $DESCRIPTION Loads the list of weapons ignored by the spawn protector. * **************************************************************************************************/ function loadIgnoredWeaponList() { local int index; local NexgenSimpleListItem item; local string weaponClass; local string remaining; // Clear list. ignoredWeaponList.selectedItem = none; ignoredWeaponList.items.clear(); // Add weapon classes. while (index < arrayCount(client.sConf.spawnProtectExcludeWeapons) && client.sConf.spawnProtectExcludeWeapons[index] != "") { class'NexgenUtil'.static.split(client.sConf.spawnProtectExcludeWeapons[index], weaponClass, remaining); item = NexgenSimpleListItem(ignoredWeaponList.items.append(class'NexgenSimpleListItem')); item.displayText = weaponClass; item.itemID = index; index++; } // Add 'add new item' option if list isn't full. if (index < arrayCount(client.sConf.spawnProtectExcludeWeapons)) { item = NexgenSimpleListItem(ignoredWeaponList.items.insert(class'NexgenSimpleListItem')); item.displayText = client.lng.addNewItemTxt; item.itemID = -1; } // Signal item select event. weaponSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the HUD class replacement list. * **************************************************************************************************/ function loadHUDReplacementList() { local int index; local NexgenSimpleListItem item; // Clear list. hudReplacementList.selectedItem = none; hudReplacementList.items.clear(); // Add weapon classes. while (index < arrayCount(client.sConf.replacementClass) && client.sConf.replacementClass[index] != "") { item = NexgenSimpleListItem(hudReplacementList.items.append(class'NexgenSimpleListItem')); item.displayText = client.sConf.replacementClass[index]; item.itemID = index; index++; } // Add 'add new item' option if list isn't full. if (index < arrayCount(client.sConf.replacementClass)) { item = NexgenSimpleListItem(hudReplacementList.items.insert(class'NexgenSimpleListItem')); item.displayText = client.lng.addNewItemTxt; item.itemID = -1; } // Signal item select event. hudReplacementSelected(); } /*************************************************************************************************** * * $DESCRIPTION Called when an item from the ignored weapon list was selected. * **************************************************************************************************/ function weaponSelected() { local NexgenSimpleListItem item; local string weaponClass; local string excludedModes; // Get selected item. item = NexgenSimpleListItem(ignoredWeaponList.selectedItem); // Update GUI. weapSaveButton.bDisabled = (item == none); weapRemButton.bDisabled = (item == none) || (item.itemID < 0); ignorePrimaryFireInp.bDisabled = (item == none); ignoreAltFireInp.bDisabled = (item == none); weaponClassInp.setDisabled((item == none)); if (item == none) { ignorePrimaryFireInp.bChecked = false; ignoreAltFireInp.bChecked = false; weaponClassInp.setValue(""); } else if (item.itemID < 0) { ignorePrimaryFireInp.bChecked = false; ignoreAltFireInp.bChecked = false; weaponClassInp.setValue(""); } else { class'NexgenUtil'.static.split(client.sConf.spawnProtectExcludeWeapons[item.itemID], weaponClass, excludedModes); ignorePrimaryFireInp.bChecked = (instr(excludedModes, client.sConf.IW_Fire) >= 0); ignoreAltFireInp.bChecked = (instr(excludedModes, client.sConf.IW_AltFire) >= 0); weaponClassInp.setValue(weaponClass); } } /*************************************************************************************************** * * $DESCRIPTION Called when an item from the hud replacement list was selected. * **************************************************************************************************/ function hudReplacementSelected() { local NexgenSimpleListItem item; local string str; // Get selected item. item = NexgenSimpleListItem(hudReplacementList.selectedItem); // Update GUI. hudReplSaveButton.bDisabled = (item == none); hudReplRemButton.bDisabled = (item == none) || (item.itemID < 0); originalHUDClassInp.setDisabled((item == none)); replacementHUDClassInp.setDisabled((item == none)); if (item == none) { originalHUDClassInp.setValue(""); replacementHUDClassInp.setValue(""); } else if (item.itemID < 0) { originalHUDClassInp.setValue(""); replacementHUDClassInp.setValue(""); } else { str = client.sConf.replacementClass[item.itemID]; originalHUDClassInp.setValue(left(str, instr(str, "="))); replacementHUDClassInp.setValue(mid(str, instr(str, "=") + 1)); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Weapon selected? if (control == ignoredWeaponList && eventType == DE_Click) { weaponSelected(); } // HUD replacement selected? if (control == hudReplacementList && eventType == DE_Click) { hudReplacementSelected(); } // Remove weapon selected? if (control == weapRemButton && eventType == DE_Click && !weapRemButton.bDisabled && setRPCI()) { rpci.delIgnoredWeapon(NexgenSimpleListItem(ignoredWeaponList.selectedItem).itemID); } // Save weapon selected? if (control == weapSaveButton && eventType == DE_Click && !weapSaveButton.bDisabled && setRPCI()) { rpci.saveIgnoredWeapon(NexgenSimpleListItem(ignoredWeaponList.selectedItem).itemID, class'NexgenUtil'.static.trim(weaponClassInp.getValue()), ignorePrimaryFireInp.bChecked, ignoreAltFireInp.bChecked); } // Remove HUD replacement selected? if (control == hudReplRemButton && eventType == DE_Click && !hudReplRemButton.bDisabled && setRPCI()) { rpci.delHUDReplacementClass(NexgenSimpleListItem(hudReplacementList.selectedItem).itemID); } // Save HUD replacement selected? if (control == hudReplSaveButton && eventType == DE_Click && !hudReplSaveButton.bDisabled && setRPCI()) { rpci.saveHUDReplacementClass(NexgenSimpleListItem(hudReplacementList.selectedItem).itemID, class'NexgenUtil'.static.trim(originalHUDClassInp.getValue()), class'NexgenUtil'.static.trim(replacementHUDClassInp.getValue())); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_ExclWeaponList) { loadIgnoredWeaponList(); } else if (configType == client.sConf.CT_HUDReplacementList) { loadHUDReplacementList(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ qT:j&E5 4L) p&j&g&f& €@€ñX:€@PV:qVtÚPqpP‚rqH š:p,ÆReconnect&Ø‚‚rqs š:p,s-žÆ(b"OverrideClassBotpack.CHSpectator'ÆReconnect& €¶_:d&xÛP½# xd&s&-V' €r:^:QÎv»-‚„-J'-m  G åa:\:}MÇ wÓ*Ó $  ômA^*WU^~.I d3«^mA­^ÈA[]^&¯_&?& çn,è-X&A­mp.I S¹rp*p c:ÚA®[?n®]?&p¡n,A­w.I AæF.I ÑA®[?n]B.I  @ÿHHHÿQQQÿbbbÿnnnÿzzzÿ|~~ÿ‡‡‡ÿ‰‰‰ÿ‘‘‘ÿ”––ÿŸŸÿŸŸÿ¥¥¥ÿ£¥«ÿ©©©ÿ««©ÿ¬¬¬ÿ­­­ÿ±±±ÿµµµÿ¹¹¹ÿ»»¹ÿºººÿ¼¼¼ÿ¾¾¾ÿÂÂÂÿÊÊÊÿÏÏÏÿÕÕÓÿØØØÿÙÙÙÿÛÛÙÿÛÛÛÿÝÝÝÿßßßÿáááÿãããÿåååÿéééÿéëëÿîîîÿïïïÿòòòÿùùùÿúúúÿþûûÿþþûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/Ó%b:*'.",")"("+*ŒŒŒ"£PîN¢Ü Ž2@ 00#000000' !)00000000+" !'00000-'00." %,00000 000&$+000000000(")000000(00+"!'000000, 0/#&,000000$&$+000000&-000* o 4.0d: 4.0e: 4.0f: 64.0g: 16.0h: 48.0i: 12.0j: 16.0k: "say"ël:A:-•6ëh€Bd€AA¯¯ºh€@b¬¯¾d@o.K Abhdo Y&A¯¯¯Ah€@€@n.K Abhdn V&d€Ah¯¯¯A€@@B«?,€@A®®€@@B€@b¬¯¾d@{.“K “Abhd{Öh{,ú{O 'h@Bd@AA€@b¬¯¾d@m.¤K ¤Abhdm U&mû$ s«/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPServerSettings2 * $VERSION 1.03 (19-1-2008 19:20) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen extra server settings control panel page. * **************************************************************************************************/ class NexgenRCPServerSettings2 extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; var UWindowCheckbox autoUpdateBansInp; var UWindowCheckbox autoDelExpiredBansInp; var UWindowCheckbox announceTeamKillsInp; var UWindowCheckbox enableNexgenMessageHUDInp; var UWindowCheckbox logEventsInp; var UWindowCheckbox logMessagesInp; var UWindowCheckbox logChatMessagesInp; var UWindowCheckbox LogPrivateMessagesInp; var UWindowCheckbox defaultAllowTeamSwitchInp; var UWindowCheckbox defaultAllowTeamBalanceInp; var UWindowCheckbox defaultAllowNameChangeInp; var UWindowEditControl gameWaitTimeInp; var UWindowEditControl gameStartDelayInp; var UWindowEditControl autoReconnectTimeInp; var UWindowEditControl maxIdleTimeInp; var UWindowEditControl maxIdleTimeCPInp; var UWindowEditControl spawnProtectTimeInp; var UWindowEditControl teamKillDmgProtectInp; var UWindowEditControl teamKillPushProtectInp; var UWindowEditControl autoDisableMatchTimeInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionH(20, defaultComponentDist, , true); p = addContentPanel(); splitRegionV(196, , , true); skipRegion(); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); p.divideRegionV(2, 2 * defaultComponentDist); p.divideRegionH(11); p.splitRegionV(64, , , true); autoUpdateBansInp = p.addCheckBox(TA_Left, client.lng.autoUpdateBansTxt, true); autoDelExpiredBansInp = p.addCheckBox(TA_Left, client.lng.autoDelExpiredBansTxt, true); announceTeamKillsInp = p.addCheckBox(TA_Left, client.lng.announceTeamKillsTxt, true); enableNexgenMessageHUDInp = p.addCheckBox(TA_Left, client.lng.enableNexgenMessageHUDTxt, true); logEventsInp = p.addCheckBox(TA_Left, client.lng.logEventsTxt, true); logMessagesInp = p.addCheckBox(TA_Left, client.lng.logMessagesTxt, true); logChatMessagesInp = p.addCheckBox(TA_Left, client.lng.logChatMessagesTxt, true); LogPrivateMessagesInp = p.addCheckBox(TA_Left, client.lng.LogPrivateMessagesTxt, true); defaultAllowTeamSwitchInp = p.addCheckBox(TA_Left, client.lng.defaultAllowTeamSwitchTxt, true); defaultAllowTeamBalanceInp = p.addCheckBox(TA_Left, client.lng.defaultAllowTeamBalanceTxt, true); defaultAllowNameChangeInp = p.addCheckBox(TA_Left, client.lng.defaultAllowNameChangeTxt, true); p.divideRegionH(11); p.divideRegionH(11); p.addLabel(client.lng.gameWaitTimeTxt, true); p.addLabel(client.lng.gameStartDelayTxt, true); p.addLabel(client.lng.autoReconnectTimeTxt, true); p.addLabel(client.lng.maxIdleTimeTxt, true); p.addLabel(client.lng.maxIdleTimeCPTxt, true); p.addLabel(client.lng.spawnProtectTimeTxt, true); p.addLabel(client.lng.teamKillDmgProtectTxt, true); p.addLabel(client.lng.teamKillPushProtectTxt, true); p.addLabel(client.lng.autoDisableMatchTimeTxt, true); p.skipRegion(); p.skipRegion(); gameWaitTimeInp = p.addEditBox(); gameStartDelayInp = p.addEditBox(); autoReconnectTimeInp = p.addEditBox(); maxIdleTimeInp = p.addEditBox(); maxIdleTimeCPInp = p.addEditBox(); spawnProtectTimeInp = p.addEditBox(); teamKillDmgProtectInp = p.addEditBox(); teamKillPushProtectInp = p.addEditBox(); autoDisableMatchTimeInp = p.addEditBox(); p.skipRegion(); p.skipRegion(); p.skipRegion(); // Configure components. gameWaitTimeInp.setMaxLength(2); gameStartDelayInp.setMaxLength(2); autoReconnectTimeInp.setMaxLength(2); maxIdleTimeInp.setMaxLength(3); maxIdleTimeCPInp.setMaxLength(3); spawnProtectTimeInp.setMaxLength(2); teamKillDmgProtectInp.setMaxLength(2); teamKillPushProtectInp.setMaxLength(2); autoDisableMatchTimeInp.setMaxLength(2); gameWaitTimeInp.setNumericOnly(true); gameStartDelayInp.setNumericOnly(true); autoReconnectTimeInp.setNumericOnly(true); maxIdleTimeInp.setNumericOnly(true); maxIdleTimeCPInp.setNumericOnly(true); spawnProtectTimeInp.setNumericOnly(true); teamKillDmgProtectInp.setNumericOnly(true); teamKillPushProtectInp.setNumericOnly(true); autoDisableMatchTimeInp.setNumericOnly(true); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current server settings. * **************************************************************************************************/ function setValues() { autoUpdateBansInp.bChecked = client.sConf.autoUpdateBans; autoDelExpiredBansInp.bChecked = client.sConf.removeExpiredBans; announceTeamKillsInp.bChecked = client.sConf.broadcastTeamKillAttempts; enableNexgenMessageHUDInp.bChecked = client.sConf.useNexgenMessageHUD; logEventsInp.bChecked = client.sConf.logEvents; logMessagesInp.bChecked = client.sConf.logSystemMessages; logChatMessagesInp.bChecked = client.sConf.logChatMessages; LogPrivateMessagesInp.bChecked = client.sConf.logPrivateMessages; defaultAllowTeamSwitchInp.bChecked = client.sConf.allowTeamSwitch; defaultAllowTeamBalanceInp.bChecked = client.sConf.allowTeamBalance; defaultAllowNameChangeInp.bChecked = client.sConf.allowNameChange; gameWaitTimeInp.setValue(string(client.sConf.waitTime)); gameStartDelayInp.setValue(string(client.sConf.startTime)); autoReconnectTimeInp.setValue(string(client.sConf.autoReconnectTime)); maxIdleTimeInp.setValue(string(client.sConf.maxIdleTime)); maxIdleTimeCPInp.setValue(string(client.sConf.maxIdleTimeCP)); spawnProtectTimeInp.setValue(string(client.sConf.spawnProtectionTime)); teamKillDmgProtectInp.setValue(string(client.sConf.teamKillDamageProtectionTime)); teamKillPushProtectInp.setValue(string(client.sConf.teamKillPushProtectionTime)); autoDisableMatchTimeInp.setValue(string(client.sConf.autoDisableMatchTime)); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: setValues(); break; case saveButton: saveSettings(); break; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_ExtraServerSettings) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Saves the server settings. * **************************************************************************************************/ function saveSettings() { // Make sure the RPC interface is available. if (!setRPCI()) return; // Save settings. rpci.setServerSettingsExt1(autoUpdateBansInp.bChecked, autoDelExpiredBansInp.bChecked, announceTeamKillsInp.bChecked, enableNexgenMessageHUDInp.bChecked, logEventsInp.bChecked, logMessagesInp.bChecked, logChatMessagesInp.bChecked, LogPrivateMessagesInp.bChecked, defaultAllowTeamSwitchInp.bChecked, defaultAllowTeamBalanceInp.bChecked, defaultAllowNameChangeInp.bChecked); rpci.setServerSettingsExt2(int(gameWaitTimeInp.getValue()), int(gameStartDelayInp.getValue()), int(autoReconnectTimeInp.getValue()), int(maxIdleTimeInp.getValue()), int(maxIdleTimeCPInp.getValue()), int(spawnProtectTimeInp.getValue()), int(teamKillDmgProtectInp.getValue()), int(teamKillPushProtectInp.getValue()), int(autoDisableMatchTimeInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ u:DD±ŠôÏrO*,–N, {N¥Nt{&f–{N“{&F{F¥{3N“N&6X{FD6W{FC6U{FB6R{FAòODCBA _•/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPServerSettings * $VERSION 1.03 (7-10-2007 13:21) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen server settings control panel page. * **************************************************************************************************/ class NexgenRCPServerSettings extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; var UWindowEditControl serverNameInp; var UWindowEditControl shortServerNameInp; var UWindowEditControl MOTDInp[4]; var UWindowEditControl adminNameInp; var UWindowEditControl adminEmailInp; var UWindowEditControl serverPasswordInp; var UWindowEditControl adminPasswordInp; var UWindowEditControl playerSlotsInp; var UWindowEditControl vipSlotsInp; var UWindowEditControl adminSlotsInp; var UWindowEditControl specSlotsInp; var UWindowCheckbox doUplinkInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionH(20, defaultComponentDist, , true); p = addContentPanel(); splitRegionV(196, , , true); skipRegion(); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); p.splitRegionH(55, , true); p.splitRegionV(100); p.splitRegionV(30, defaultComponentDist, true); p.divideRegionH(6); p.divideRegionH(6); p.splitRegionV(100); p.splitRegionV(100); p.addLabel(client.lng.serverNameTxt, true); p.addLabel(client.lng.shortServerNameTxt, true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 1), true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 2), true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 3), true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 4), true); serverNameInp = p.addEditBox(); shortServerNameInp = p.addEditBox(); MOTDInp[0] = p.addEditBox(); MOTDInp[1] = p.addEditBox(); MOTDInp[2] = p.addEditBox(); MOTDInp[3] = p.addEditBox(); p.divideRegionH(5); p.divideRegionH(5); p.divideRegionH(5); p.divideRegionH(5); p.addLabel(client.lng.playerSlotsTxt, true); p.addLabel(client.lng.vipSlotsTxt, true); p.addLabel(client.lng.adminSlotsTxt, true); p.addLabel(client.lng.specSlotsTxt, true); p.addLabel(client.lng.advertiseTxt, true); playerSlotsInp = p.addEditBox( , 48, AL_Left); vipSlotsInp = p.addEditBox( , 48, AL_Left); adminSlotsInp = p.addEditBox( , 48, AL_Left); specSlotsInp = p.addEditBox( , 48, AL_Left); doUplinkInp = p.addCheckBox(TA_Right); p.addLabel(client.lng.adminNameTxt, true); p.addLabel(client.lng.adminEmailTxt, true); p.addLabel(client.lng.serverPasswordTxt, true); p.addLabel(client.lng.adminPasswordTxt, true); p.skipRegion(); adminNameInp = p.addEditBox(); adminEmailInp = p.addEditBox(); serverPasswordInp = p.addEditBox(); adminPasswordInp = p.addEditBox(); // Configure components. serverNameInp.setMaxLength(128); shortServerNameInp.setMaxLength(32); MOTDInp[0].setMaxLength(192); MOTDInp[1].setMaxLength(192); MOTDInp[2].setMaxLength(192); MOTDInp[3].setMaxLength(192); adminNameInp.setMaxLength(64); adminEmailInp.setMaxLength(64); serverPasswordInp.setMaxLength(30); adminPasswordInp.setMaxLength(30); playerSlotsInp.setNumericOnly(true); vipSlotsInp.setNumericOnly(true); adminSlotsInp.setNumericOnly(true); specSlotsInp.setNumericOnly(true); playerSlotsInp.setMaxLength(3); vipSlotsInp.setMaxLength(3); adminSlotsInp.setMaxLength(3); specSlotsInp.setMaxLength(3); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current server settings. * **************************************************************************************************/ function setValues() { serverNameInp.setValue(client.sConf.serverName); shortServerNameInp.setValue(client.sConf.shortName); adminNameInp.setValue(client.sConf.adminName); adminEmailInp.setValue(client.sConf.adminEmail); serverPasswordInp.setValue(client.sConf.decode(client.sConf.globalServerPassword)); adminPasswordInp.setValue(client.sConf.decode(client.sConf.globalAdminPassword)); MOTDInp[0].setValue(client.sConf.MOTDLine[0]); MOTDInp[1].setValue(client.sConf.MOTDLine[1]); MOTDInp[2].setValue(client.sConf.MOTDLine[2]); MOTDInp[3].setValue(client.sConf.MOTDLine[3]); playerSlotsInp.setValue(string(client.sConf.playerSlots)); vipSlotsInp.setValue(string(client.sConf.vipSlots)); adminSlotsInp.setValue(string(client.sConf.adminSlots)); specSlotsInp.setValue(string(client.sConf.spectatorSlots)); doUplinkInp.bChecked = client.sConf.enableUplink; } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_GlobalServerSettings) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: setValues(); break; case saveButton: saveSettings(); break; } } } /*************************************************************************************************** * * $DESCRIPTION Saves the server settings. * **************************************************************************************************/ function saveSettings() { local string serverName; local string shortServerName; local string MOTDLine[4]; local string adminName; local string adminEmail; local string serverPassword; local string adminPassword; local int playerSlots; local int vipSlots; local int adminSlots; local int specSlots; local bool bEnableUplink; // Make sure the RPC interface is available. if (!setRPCI()) return; // Collect data from the GUI. serverName = serverNameInp.getValue(); shortServerName = shortServerNameInp.getValue(); MOTDLine[0] = MOTDInp[0].getValue(); MOTDLine[1] = MOTDInp[1].getValue(); MOTDLine[2] = MOTDInp[2].getValue(); MOTDLine[3] = MOTDInp[3].getValue(); adminName = adminNameInp.getValue(); adminEmail = adminEmailInp.getValue(); serverPassword = serverPasswordInp.getValue(); adminPassword = adminPasswordInp.getValue(); playerSlots = int(playerSlotsInp.getValue()); vipSlots = int(vipSlotsInp.getValue()); adminSlots = int(adminSlotsInp.getValue()); specSlots = int(specSlotsInp.getValue()); bEnableUplink = doUplinkInp.bChecked; // Save settings. rpci.setServerSettings(serverName, shortServerName, MOTDLine[0], MOTDLine[1], MOTDLine[2], MOTDLine[3], adminName, adminEmail, serverPassword, adminPassword, playerSlots, vipSlots, adminSlots, specSlots, bEnableUplink); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ | 24.0t:p¼`¹?)-l )-EA< EItk óm:Wq\È<óWK&J&ÑWº¾i }:T7¨ë¶|2w/*/=7TQMz‚–l,wlQ*¥lQ7TQM2 Â~:{WŽcÖÔ³¯•ÊQÍÌL>Q•Ê/a0 CORQwO*R10ÔwO*{%Í–{NOF6X{F6W{F6U{F6R{F¥{eN% €Ôõs:Gm~»õGJH‚rGo š:J,.¿Á ¿&•‚rGn š:J,{øg:Æ$ ¹‚rG{ š:J,I J?/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPServerInfo * $VERSION 1.01 (10-11-2007 19:10) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen server info control panel page. * **************************************************************************************************/ class NexgenRCPServerInfo extends NexgenPanel; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local GameReplicationInfo GRI; local TournamentGameReplicationInfo TGRI; local NexgenContentPanel p; GRI = client.player.gameReplicationInfo; TGRI = TournamentGameReplicationInfo(GRI); // Create layout & add components. createWindowRootRegion(); splitRegionH(32, defaultComponentDist); // Title. p = addContentPanel(); p.addLabel(GRI.serverName, true, TA_Center); // Contact info. splitRegionH(128, defaultComponentDist); p = addContentPanel(); p.splitRegionV(128); p.divideRegionH(7); p.divideRegionH(7); p.addLabel(client.lng.administratorTxt, true); p.addLabel(client.lng.contactAddrTxt, true); p.addLabel(client.lng.msgOfTheDayTxt, true); p.skipRegion(); p.skipRegion(); p.skipRegion(); p.addLabel(client.lng.serverIDTxt, true); p.addLabel(fixStr(GRI.adminName)); p.addLabel(fixStr(GRI.adminEmail)); p.addLabel(fixStr(GRI.MOTDLine1)); p.addLabel(fixStr(GRI.MOTDLine2)); p.addLabel(fixStr(GRI.MOTDLine3)); p.addLabel(fixStr(GRI.MOTDLine4)); p.addLabel(class'NexgenUtil'.static.formatGUID(client.serverID)); // Server game stats. if (TGRI != none) { splitRegionV(160, defaultComponentDist); // Server stats. p = addContentPanel(); p.splitRegionH(20, , true); p.addLabel(client.lng.statisticsTxt, true, TA_Center); p.splitRegionV(100); p.divideRegionH(4); p.divideRegionH(4); p.addLabel(client.lng.totalGamesTxt, true); p.addLabel(client.lng.totalFragsTxt, true); p.addLabel(client.lng.totalDeathsTxt, true); p.addLabel(client.lng.totalFlagsTxt, true); p.addLabel(TGRI.totalGames); p.addLabel(TGRI.totalFrags); p.addLabel(TGRI.totalDeaths); p.addLabel(TGRI.totalFlags); // Top players. p = addContentPanel(); p.splitRegionH(20, , true); p.addLabel(client.lng.bestPlayersTxt, true, TA_Center); p.splitRegionV(160, , , true); p.divideRegionH(4); p.splitRegionV(48); p.addLabel(client.lng.playerNameTxt, true); p.addLabel(fixStr(TGRI.bestPlayers[0])); p.addLabel(fixStr(TGRI.bestPlayers[1])); p.addLabel(fixStr(TGRI.bestPlayers[2])); p.divideRegionH(4); p.divideRegionH(4); p.addLabel(client.lng.FPHTxt, true); p.addLabel(TGRI.bestFPHs[0]); p.addLabel(TGRI.bestFPHs[1]); p.addLabel(TGRI.bestFPHs[2]); p.addLabel(client.lng.recordSetTxt, true); p.addLabel(fixStr(TGRI.bestRecordDate[0])); p.addLabel(fixStr(TGRI.bestRecordDate[1])); p.addLabel(fixStr(TGRI.bestRecordDate[2])); } } /*************************************************************************************************** * * $DESCRIPTION Makes sure a non empty string is returned. * **************************************************************************************************/ static function string fixStr(coerce string str) { if (class'NexgenUtil'.static.trim(str) == "") { return "-"; } else { return str; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ v:€Œw:eIŠQe{ O{e{ ƨsaye {9 "country"óoq<óoH&@&Ñoº¾i C;qk{Æ°q$–:q, ƒ›::q`%P-[] V-['ƒš]^:q`$2qX^‹qq$–:q,ü‚—:qU%™'P:qU:qU%Hq'P‹q• þB;y:L~î q: 16.0@²/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPPrivateMsg * $VERSION 1.03 (21-10-2007 14:38) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen private message system control panel page. * **************************************************************************************************/ class NexgenRCPPrivateMsg extends NexgenPanel; var NexgenClientCore rpci; var UWindowDynamicTextArea history; var NexgenSimplePlayerListBox playerList; var NexgenSimplePlayerListBox blockedPlayerList; var UWindowSmallButton blockToggleButton; var UWindowSmallButton sendNormalButton; var UWindowSmallButton sendWindowedButton; var UWindowEditControl msgInp; var UWindowCheckbox blockAllInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); // Player / blocked player list. p = addContentPanel(); p.divideRegionH(2, defaultComponentDist); p.splitRegionH(16); p.splitRegionH(16); p.addLabel(client.lng.playerListTxt, true, TA_Center); playerList = NexgenSimplePlayerListBox(p.addListBox(class'NexgenSimplePlayerListBox')); p.addLabel(client.lng.blockedListTxt, true, TA_Center); p.splitRegionH(32, defaultComponentDist, , true); blockedPlayerList = NexgenSimplePlayerListBox(p.addListBox(class'NexgenSimplePlayerListBox')); p.divideRegionH(2); blockToggleButton = p.addButton(client.lng.blockToggleTxt); blockAllInp = p.addCheckBox(TA_Left, client.lng.blockAllPMsTxt); // 'Say' panel. splitRegionH(64, defaultComponentDist); p = addContentPanel(); p.divideRegionH(3, defaultComponentDist); p.addLabel(client.lng.messageTxt, true, TA_Center); msgInp = p.addEditbox(); p.splitRegionV(256); p.divideRegionV(2, defaultComponentDist); p.skipRegion(); sendNormalButton = p.addButton(client.lng.sendNormalPMTxt); sendWindowedButton = p.addButton(client.lng.sendWindowedPMTxt); // History panel. p = addContentPanel(); p.splitRegionH(16); p.addLabel(client.lng.historyTxt, true, TA_Center); history = p.addDynamicTextArea(); // Configure components. playerList.register(self); blockedPlayerList.register(self); blockToggleButton.register(self); blockAllInp.register(self); sendNormalButton.register(self); sendWindowedButton.register(self); sendWindowedButton.bDisabled = !client.hasRight(client.R_Moderate); msgInp.register(self); msgInp.setMaxLength(255); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { // Make sure the RPC interface is available. setRPCI(); // Player has joined the game? if (eventType == client.PE_PlayerJoined) { if (rpci != none && rpci.isBlocked(class'NexgenUtil'.static.getProperty(args, client.PA_ClientID))) { addPlayerToList(blockedPlayerList, playerNum, args); } else { addPlayerToList(playerList, playerNum, args); } } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); blockedPlayerList.removePlayer(playerNum); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); updatePlayerInfo(blockedPlayerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local NexgenPlayerList selected; setRPCI(); // Block all clicked. if (control == blockAllInp && eventType == DE_Click && rpci != none) { rpci.bBlockAll = blockAllInp.bChecked; } // Send normal clicked. if (control == sendNormalButton && eventType == DE_Click) { sendMessage(false); } // Enter button pressed on message edit box. if (control == msgInp && eventType == DE_EnterPressed) { sendMessage(false); } // Send windowed clicked. if (control == sendWindowedButton && eventType == DE_Click && !sendWindowedButton.bDisabled) { sendMessage(true); } // Block / Unblock clicked. if (control == blockToggleButton && eventType == DE_Click && rpci != none) { if (playerList.selectedItem != none) { selected = NexgenPlayerList(playerList.selectedItem); rpci.blockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.blockMsg, selected.pName)); playerList.moveSelectedPlayerTo(blockedPlayerList); } else if (blockedPlayerList.selectedItem != none) { selected = NexgenPlayerList(blockedPlayerList.selectedItem); rpci.unblockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.unblockMsg, selected.pName)); blockedPlayerList.moveSelectedPlayerTo(playerList); } } // Unblocked player selected. if (control == playerList && eventType == DE_Click) { if (blockedPlayerList.selectedItem != none) { blockedPlayerList.selectedItem.bSelected = false; blockedPlayerList.selectedItem = none; } } // Blocked player selected. if (control == blockedPlayerList && eventType == DE_Click) { if (playerList.selectedItem != none) { playerList.selectedItem.bSelected = false; playerList.selectedItem = none; } } // Double click on unblocked player -> block that player. if (control == playerList && eventType == DE_DoubleClick && playerList.selectedItem != none && rpci != none) { selected = NexgenPlayerList(playerList.selectedItem); rpci.blockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.blockMsg, selected.pName)); playerList.moveSelectedPlayerTo(blockedPlayerList); } // Double click on blocked player -> unblock that player. if (control == blockedPlayerList && eventType == DE_DoubleClick && blockedPlayerList.selectedItem != none && rpci != none) { selected = NexgenPlayerList(blockedPlayerList.selectedItem); rpci.unblockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.unblockMsg, selected.pName)); blockedPlayerList.moveSelectedPlayerTo(playerList); } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to the to message history box. * $PARAM msg The message to add. * **************************************************************************************************/ function addHistoryMsg(string msg) { local string timeStamp; timeStamp = "[" $ right("0" $ client.level.hour, 2) $ ":" $ right("0" $ client.level.minute, 2) $ "]"; history.addText(timeStamp @ msg); } /*************************************************************************************************** * * $DESCRIPTION Send the currently enterted message to the selected player. * $PARAM bWindowed Whether the message to send should popup in a window. * **************************************************************************************************/ function sendMessage(optional bool bWindowed) { local NexgenPlayerList selectedPlayer; // Make sure the RPC interface is available. if (rpci == none) return; // Get selected player. if (playerList.selectedItem != none) { selectedPlayer = NexgenPlayerList(playerList.selectedItem); } else if (blockedPlayerList.selectedItem != none) { selectedPlayer = NexgenPlayerList(blockedPlayerList.selectedItem); } // Send message. if (selectedPlayer != none && msgInp.getValue() != "" && !client.isMuted() && !( // Spectators muted during matches? client.sConf.matchModeActivated && client.sConf.muteSpectatorsDuringMatch && client.gInf.gameState == client.gInf.GS_Playing && client.bSpectator && !selectedPlayer.isSpectator() && !client.hasRight(client.R_MatchAdmin) && !client.hasRight(client.R_Moderate) ) ) { addHistoryMsg(client.lng.format(client.lng.sendMsgTxt, selectedPlayer.pName, msgInp.getValue())); rpci.sendPM(selectedPlayer.pNum, msgInp.getValue(), bWindowed); msgInp.setValue(""); } // $TODO Play sound on error. } /*************************************************************************************************** * * $DESCRIPTION Called when a new private message was received. Adds the received message to the * chat history. * $PARAM msg The message that was received. * $PARAM pri Player replication info actor of the player that has send the message. * $REQUIRE pri != none * **************************************************************************************************/ function receiveMessage(string msg, PlayerReplicationInfo pri) { addHistoryMsg(client.lng.format(client.lng.receivedMsgTxt, pri.playerName, msg)); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ kº/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPModerate * $VERSION 1.00 (17-11-2007 13:29) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen moderator control panel page. * **************************************************************************************************/ class NexgenRCPModerate extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenPlayerListBox playerList; var UWindowSmallButton muteToggleButton; var UWindowSmallButton setNameButton; var UWindowSmallButton kickButton; var UWindowSmallButton banButton; var UWindowSmallButton showMsgButton; var UWindowEditControl playerNameInp; var UWindowEditControl banReasonInp; var NexgenEditControl numMatchesInp; var NexgenEditControl numDaysInp; var UWindowEditControl messageInp; var UWindowCheckbox banForeverInp; var UWindowCheckbox banMatchesInp; var UWindowCheckbox banDaysInp; var UWindowCheckbox muteAllInp; var UWindowCheckbox allowNameChangeInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); playerList = NexgenPlayerListBox(addListBox(class'NexgenPlayerListBox')); // Player controller. splitRegionH(56, defaultComponentDist); p = addContentPanel(); p.divideRegionH(2); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); muteToggleButton = p.addButton(client.lng.muteToggleTxt); p.skipRegion(); setNameButton = p.addButton(client.lng.setPlayerNameTxt); playerNameInp = p.addEditBox(); // Ban controller. splitRegionH(112, defaultComponentDist); p = addContentPanel(); p.divideRegionH(5); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.addLabel(client.lng.banReasonTxt); banReasonInp = p.addEditBox(); kickButton = p.addButton(client.lng.kickPlayerTxt); p.skipRegion(); banButton = p.addButton(client.lng.banPlayerTxt); p.splitRegionV(96, defaultComponentDist); p.skipRegion(); p.splitRegionV(96, defaultComponentDist); p.skipRegion(); p.splitRegionV(96, defaultComponentDist); banForeverInp = p.addCheckBox(TA_Left, client.lng.banForeverTxt); p.skipRegion(); banMatchesInp = p.addCheckBox(TA_Left, client.lng.banMatchesTxt); numMatchesInp = p.addEditBox(); banDaysInp = p.addCheckBox(TA_Left, client.lng.banDaysTxt); numDaysInp = p.addEditBox(); // Game controller. splitRegionH(70); p = addContentPanel(); p.divideRegionH(3); muteAllInp = p.addCheckBox(TA_Left, client.lng.muteAllTxt); allowNameChangeInp = p.addCheckBox(TA_Left, client.lng.allowNameChangeTxt); p.splitRegionV(96, defaultComponentDist); showMsgButton = p.addButton(client.lng.showAdminMessageTxt); messageInp = p.addEditBox(); // Configure components. playerNameInp.setMaxLength(32); banReasonInp.setMaxLength(250); numMatchesInp.setMaxLength(4); numMatchesInp.setNumericOnly(true); numDaysInp.setMaxLength(4); numDaysInp.setNumericOnly(true); messageInp.setMaxLength(250); playerList.register(self); muteToggleButton.register(self); setNameButton.register(self); kickButton.register(self); banButton.register(self); showMsgButton.register(self); banForeverInp.register(self); banMatchesInp.register(self); banDaysInp.register(self); muteAllInp.register(self); allowNameChangeInp.register(self); banMatchesInp.bChecked = true; numMatchesInp.setValue("3"); numDaysInp.setValue("7"); playerSelected(); banPeriodSelected(); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current game info settings. * **************************************************************************************************/ function setValues() { muteAllInp.bChecked = client.gInf.bMuteAll; allowNameChangeInp.bChecked = !client.gInf.bNoNameChange; } /*************************************************************************************************** * * $DESCRIPTION Called when a player was selected from the list. * **************************************************************************************************/ function playerSelected() { local NexgenPlayerList item; item = NexgenPlayerList(playerList.selectedItem); muteToggleButton.bDisabled = (item == none); setNameButton.bDisabled = (item == none); kickButton.bDisabled = (item == none); banButton.bDisabled = (item == none || !client.hasRight(client.R_BanOperator)); if (item == none) { playerNameInp.setValue(""); } else { playerNameInp.setValue(item.pName); } } /*************************************************************************************************** * * $DESCRIPTION Called when a ban period was selected from the list. * **************************************************************************************************/ function banPeriodSelected() { numMatchesInp.setDisabled(!banMatchesInp.bChecked); numDaysInp.setDisabled(!banDaysInp.bChecked); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Bans the currently selected player. * **************************************************************************************************/ function banPlayer() { local byte banPeriodType; local int banPeriodArgs; if (banMatchesInp.bChecked) { banPeriodType = client.sConf.BP_Matches; banPeriodArgs = int(class'NexgenUtil'.static.trim(numMatchesInp.getValue())); } else if (banDaysInp.bChecked) { banPeriodType = client.sConf.BP_UntilDate; banPeriodArgs = int(class'NexgenUtil'.static.trim(numDaysInp.getValue())); } else { banPeriodType = client.sConf.BP_Forever; } rpci.banPlayer(NexgenPlayerList(playerList.selectedItem).pNum, banPeriodType, banPeriodArgs, class'NexgenUtil'.static.trim(banReasonInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); setRPCI(); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled && rpci != none) { switch (control) { case muteToggleButton: rpci.togglePlayerMute(NexgenPlayerList(playerList.selectedItem).pNum); break; case setNameButton: rpci.setPlayerName(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(playerNameInp.getValue())); break; case kickButton: rpci.kickPlayer(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(banReasonInp.getValue())); break; case banButton: banPlayer(); break; case showMsgButton: rpci.showAdminMessage(class'NexgenUtil'.static.trim(messageInp.getValue())); break; } } // Player selected? if (control == playerList && eventType == DE_Click) { playerSelected(); } // Ban period selected? if (control == banForeverInp && eventType == DE_Click) { banForeverInp.bChecked = true; banMatchesInp.bChecked = false; banDaysInp.bChecked = false; banPeriodSelected(); } else if (control == banMatchesInp && eventType == DE_Click) { banForeverInp.bChecked = false; banMatchesInp.bChecked = true; banDaysInp.bChecked = false; banPeriodSelected(); } else if (control == banDaysInp && eventType == DE_Click) { banForeverInp.bChecked = false; banMatchesInp.bChecked = false; banDaysInp.bChecked = true; banPeriodSelected(); } // Toggle mute all clicked? if (control == muteAllInp && eventType == DE_Click && rpci != none) { rpci.toggleGlobalMute(); } // Toggle allow name change clicked? if (control == allowNameChangeInp && eventType == DE_Click && rpci != none) { rpci.toggleGlobalNameChange(); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); playerSelected(); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); playerSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { if (infoType == client.IT_GlobalRights) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ âE;gO`‹«-Fg-úF-FâgKQRHG©fg-ýVGVgHKQRHDV!QR ^;aH^!­Šv™'Pr/= Has‚–c,wcQ*¥cQ Ha5ˆ:aUr ÂJ×/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPMatchSet * $VERSION 1.03 (27-11-2007 18:05) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen match setup control panel page. * **************************************************************************************************/ class NexgenRCPMatchSet extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowSmallButton saveButton; var UWindowSmallButton startStopButton; var UWindowSmallButton resetButton; var NexgenPlayerListBox playerList; var UWindowEditControl tagInp[4]; var UWindowEditControl numGamesInp; var UWindowEditControl currGameInp; var UWindowEditControl passwordInp; var UWindowCheckbox specNeedPWInp; var UWindowCheckbox muteSpecsInp; var UWindowCheckbox enableBootControlInp; var UWindowCheckbox autoLockInp; var UWindowCheckbox autoPauseInp; var UWindowCheckbox autoSeperateInp; var UWindowSmallButton separateButton; var UWindowSmallButton sendPasswordButton; const numTeams = 4; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenPlayerList playerItem; local NexgenContentPanel p; local int region; local int index; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist, , true); p = addContentPanel(); // Match settings. p.splitRegionH(144, defaultComponentDist); p.splitRegionH(16); region = p.currRegion; p.skipRegion(); p.addLabel(client.lng.matchSettingsTxt, true, TA_Center); p.divideRegionH(7); p.divideRegionV(2); p.splitRegionV(96); specNeedPWInp = p.addCheckBox(, client.lng.matchSpecNoPassTxt); muteSpecsInp = p.addCheckBox(, client.lng.matchMuteSpecsTxt); enableBootControlInp = p.addCheckBox(, client.lng.matchBootControlTxt); autoLockInp = p.addCheckBox(, client.lng.matchAutoLockTeamsTxt); autoPauseInp = p.addCheckBox(, client.lng.matchAutoPauseTxt); //p.addCheckBox(, "Automatically take a screenshot at the end of each game"); // Should be a client option! p.splitRegionV(96); p.splitRegionV(96); p.addLabel(client.lng.passwordTxt); passwordInp = p.addEditBox(); p.addLabel(client.lng.matchNumOfGamesTxt); numGamesInp = p.addEditBox(, 48, AL_Left); p.addLabel(client.lng.matchCurrGameNumTxt); currGameInp = p.addEditBox(, 48, AL_Left); // Separate by tag settings. p.selectRegion(region); p.selectRegion(p.splitRegionH(72, defaultComponentDist)); p.splitRegionH(1); region = p.currRegion; p.skipRegion(); p.addComponent(class'NexgenDummyComponent'); p.splitRegionH(16); p.addLabel(client.lng.matchSeparateByTagTxt, true, TA_Center); p.splitRegionH(16, defaultComponentDist, , true); p.divideRegionH(2); p.splitRegionV(20); p.divideRegionV(numTeams); p.divideRegionV(numTeams); autoSeperateInp = p.addCheckBox(TA_Right); p.splitRegionV(192); for (index = 0; index < numTeams; index++) { p.addLabel(client.lng.getTeamName(index), , TA_Center); } for (index = 0; index < numTeams; index++) { tagInp[index] = p.addEditBox(, 64, AL_Center); tagInp[index].setMaxLength(16); } p.addLabel(client.lng.matchAutoTagSeparateTxt); p.splitRegionV(80, , , true); p.skipRegion(); separateButton = p.addButton(client.lng.matchDoSeparateTxt); // Match setup control buttons. p.selectRegion(region); p.selectRegion(p.splitRegionH(1)); p.addComponent(class'NexgenDummyComponent'); p.splitRegionH(16, , , true); p.skipRegion(); p.divideRegionV(3, defaultComponentDist); saveButton = p.addButton(client.lng.saveTxt); startStopButton = p.addButton(); resetButton = p.addButton(client.lng.resetTxt); // Send password to. splitRegionH(16, defaultComponentDist, , true); playerList = NexgenPlayerListBox(addListBox(class'NexgenSimplePlayerListBox')); sendPasswordButton = addButton(client.lng.sendPasswordTxt); // Configure components. numGamesInp.setNumericOnly(true); numGamesInp.setMaxLength(2); currGameInp.setNumericOnly(true); currGameInp.setMaxLength(2); passwordInp.setMaxLength(32); playerItem = playerList.addPlayer(); playerItem.pNum = -1; playerItem.pName = client.lng.allPlayersTxt; playerItem.pTeam = 4; separateButton.bDisabled = !client.player.gameReplicationInfo.bTeamGame; saveButton.register(self); startStopButton.register(self); resetButton.register(self); separateButton.register(self); playerList.register(self); sendPasswordButton.register(self); numGamesInp.register(self); currGameInp.register(self); passwordInp.register(self); specNeedPWInp.register(self); muteSpecsInp.register(self); enableBootControlInp.register(self); autoLockInp.register(self); autoPauseInp.register(self); autoSeperateInp.register(self); for (index = 0; index < numTeams; index++) { tagInp[index].register(self); } loadMatchSettings(); playerSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the match settings. * **************************************************************************************************/ function loadMatchSettings() { local int index; numGamesInp.setValue(string(client.sConf.matchesToPlay)); currGameInp.setValue(string(client.sConf.currentMatch)); passwordInp.setValue(client.sConf.decode(client.sConf.serverPassword)); specNeedPWInp.bChecked = client.sConf.spectatorsNeedPassword; muteSpecsInp.bChecked = client.sConf.muteSpectatorsDuringMatch; enableBootControlInp.bChecked = client.sConf.enableMatchBootControl; autoLockInp.bChecked = client.sConf.matchAutoLockTeams; autoPauseInp.bChecked = client.sConf.matchAutoPause; autoSeperateInp.bChecked = client.sConf.matchAutoSeparate; for (index = 0; index < numTeams; index++) { tagInp[index].setValue(client.sConf.tagsToSeparate[index]); } if (client.sConf.matchModeActivated) { startStopButton.setText(client.lng.stopMatchTxt); } else { startStopButton.setText(client.lng.startMatchTxt); } startStopButton.bDisabled = false; } /*************************************************************************************************** * * $DESCRIPTION Called when a player was selected from the list. * **************************************************************************************************/ function playerSelected() { sendPasswordButton.bDisabled = playerList.selectedItem == none; } /*************************************************************************************************** * * $DESCRIPTION Separates the players by the currently entered tags. * **************************************************************************************************/ function separatePlayers() { local string teamTags[4]; local int index; // Make sure the RPC interface is available. if (!setRPCI()) return; // Get tags. for (index = 0; index < numTeams; index++) { teamTags[index] = class'NexgenUtil'.static.trim(tagInp[index].getValue()); } // Separate players. rpci.separatePlayers(teamTags); } /*************************************************************************************************** * * $DESCRIPTION Sends the password to the currently selected player. * **************************************************************************************************/ function sendPassword() { // Make sure the RPC interface is available. if (!setRPCI()) return; // Send password. rpci.sendPassword(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(passwordInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Sends the current match settings to the server. * **************************************************************************************************/ function saveSettings() { local string teamTags[4]; local int index; // Make sure the RPC interface is available. if (!setRPCI()) return; // Get tags. for (index = 0; index < numTeams; index++) { teamTags[index] = class'NexgenUtil'.static.trim(tagInp[index].getValue()); } // Update settings. rpci.updateMatchSettings(int(numGamesInp.getValue()), int(currGameInp.getValue()), passwordInp.getValue(), specNeedPWInp.bChecked, muteSpecsInp.bChecked, enableBootControlInp.bChecked, autoLockInp.bChecked, autoPauseInp.bChecked, autoSeperateInp.bChecked, teamTags[0], teamTags[1], teamTags[2], teamTags[3]); } /*************************************************************************************************** * * $DESCRIPTION Starts / stops the match. * **************************************************************************************************/ function toggleMatchMode() { // Make sure the RPC interface is available. if (!setRPCI()) return; // Start / stop match. rpci.toggleMatchMode(); } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); playerSelected(); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: loadMatchSettings(); break; case saveButton: saveSettings(); break; case separateButton: separatePlayers(); break; case sendPasswordButton: sendPassword(); break; case startStopButton: toggleMatchMode(); break; } } // Player selected? if (control == playerList && eventType == DE_Click) { playerSelected(); } // Check if some settings were changed. if (eventType == DE_Change && !client.sConf.matchModeActivated && control != none && (control.isA('UWindowEditControl') || control.isA('UWindowCheckbox'))) { startStopButton.bDisabled = true; } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_MatchSettings) { loadMatchSettings(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ ØG;O>² L-LþO![4-LØONOJDNOO* Z¯/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPMatchControl * $VERSION 1.02 (27-10-2007 13:55) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen match control panel page. * **************************************************************************************************/ class NexgenRCPMatchControl extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenPlayerListBox playerList; var UWindowSmallButton teamButtons[4]; var UWindowSmallButton pauseButton; var UWindowSmallButton endButton; var UWindowSmallButton restartButton; var UWindowSmallButton sendToURLButton; var UWindowSmallButton reconnectAsPlayerButton; var UWindowSmallButton reconnectAsSpecButton; var UWindowSmallButton disableTeamSwitchButton; var UWindowEditControl urlInp; var UWindowCheckbox allowTeamSwitchInp; var UWindowCheckbox allowTeamBalanceInp; var UWindowCheckbox lockTeamsInp; var color teamColor[4]; var color defaultButtonColor; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); playerList = NexgenPlayerListBox(addListBox(class'NexgenPlayerListBox')); splitRegionH(184, defaultComponentDist); // Player controls. p = addContentPanel(); p.splitRegionV(128, defaultComponentDist); p.divideRegionH(8); p.divideRegionH(8); for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index] = p.addButton(client.lng.format(client.lng.switchToTeamTxt, client.lng.getTeamName(index))); } sendToURLButton = p.addButton(client.lng.sendToURLTxt); reconnectAsPlayerButton = p.addButton(client.lng.reconnectAsPlayerTxt); reconnectAsSpecButton = p.addButton(client.lng.reconnectAsSpecTxt); disableTeamSwitchButton =p.addButton(client.lng.disableTeamSwitchTxt); p.skipRegion(); p.skipRegion(); p.skipRegion(); p.skipRegion(); urlInp = p.addEditBox(); // Global match controls. splitRegionV(140, defaultComponentDist); p = addContentPanel(); p.divideRegionH(3); pauseButton = p.addButton(client.lng.pauseGameTxt); endButton = p.addButton(client.lng.endGameTxt); restartButton = p.addButton(client.lng.restartGameTxt); p = addContentPanel(); p.divideRegionH(3); allowTeamSwitchInp = p.addCheckBox(TA_Left, client.lng.allowTeamSwitchTxt); allowTeamBalanceInp = p.addCheckBox(TA_Left, client.lng.allowTeamBalanceTxt); lockTeamsInp = p.addCheckBox(TA_Left, client.lng.lockTeamsTxt); // Configure components. urlInp.setMaxLength(128); for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index].register(self); } pauseButton.register(self); endButton.register(self); restartButton.register(self); playerList.register(self); disableTeamSwitchButton.register(self); reconnectAsPlayerButton.register(self); reconnectAsSpecButton.register(self); sendToURLButton.register(self); allowTeamSwitchInp.register(self); allowTeamBalanceInp.register(self); lockTeamsInp.register(self); allowTeamSwitchInp.bDisabled = !client.player.gameReplicationInfo.bTeamGame; allowTeamBalanceInp.bDisabled = !client.player.gameReplicationInfo.bTeamGame; playerSelected(); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current game info settings. * **************************************************************************************************/ function setValues() { allowTeamSwitchInp.bChecked = !client.gInf.bNoTeamSwitch; allowTeamBalanceInp.bChecked = !client.gInf.bNoTeamBalance; lockTeamsInp.bChecked = client.gInf.bTeamsLocked; } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { if (infoType == client.IT_GlobalRights) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Called when a player was selected from the list. * **************************************************************************************************/ function playerSelected() { local NexgenPlayerList player; local int index; local bool bTeamGame; // Get selected player. player = NexgenPlayerList(playerList.selectedItem); // Determine which buttons can be used. bTeamGame = client.player.gameReplicationInfo.bTeamGame; if (player == none) { for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index].bDisabled = true; teamButtons[index].setTextColor(defaultButtonColor); } sendToURLButton.bDisabled = true; reconnectAsPlayerButton.bDisabled = true; reconnectAsSpecButton.bDisabled = true; disableTeamSwitchButton.bDisabled = true; } else { for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index].bDisabled = !bTeamGame || player.isSpectator() || index == player.pTeam || index >= client.gInf.maxTeams; if (teamButtons[index].bDisabled) { teamButtons[index].setTextColor(defaultButtonColor); } else { teamButtons[index].setTextColor(teamColor[index]); } } sendToURLButton.bDisabled = false; reconnectAsPlayerButton.bDisabled = !player.isSpectator(); reconnectAsSpecButton.bDisabled = player.isSpectator(); disableTeamSwitchButton.bDisabled = player.isSpectator() || !bTeamGame; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); playerSelected(); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); playerSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); setRPCI(); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled && rpci != none) { switch (control) { case pauseButton: rpci.pauseGame(); break; case endButton: rpci.endGame(); break; case restartButton: rpci.restartGame(); break; case teamButtons[0]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 0); break; case teamButtons[1]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 1); break; case teamButtons[2]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 2); break; case teamButtons[3]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 3); break; case disableTeamSwitchButton: rpci.toggleTeamSwitch(NexgenPlayerList(playerList.selectedItem).pNum); break; case reconnectAsPlayerButton: rpci.reconnectPlayer(NexgenPlayerList(playerList.selectedItem).pNum, false); break; case reconnectAsSpecButton: rpci.reconnectPlayer(NexgenPlayerList(playerList.selectedItem).pNum, true); break; case sendToURLButton: rpci.sendPlayerToURL(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(urlInp.getValue())); break; } } // Checkbox pressed? if (control != none && eventType == DE_Click && control.isA('UWindowCheckbox') && !UWindowCheckbox(control).bDisabled && rpci != none) { switch (control) { case allowTeamSwitchInp: rpci.toggleGlobalTeamSwitch(); break; case allowTeamBalanceInp: rpci.toggleGlobalTeamBalance(); break; case lockTeamsInp: rpci.toggleLockedTeams(); break; } } // Player selected? if (control == playerList && eventType == DE_Click) { playerSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ òOPg'›5òPrO*W3O {P ~}/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPHome * $VERSION 1.04 (27-11-2007 23:11) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen client start/home control panel page. * **************************************************************************************************/ class NexgenRCPHome extends NexgenPanel; var UWindowSmallButton teamBalanceButton; var UWindowSmallButton teamButton[4]; var UWindowSmallButton playSpecButton; var UWindowSmallButton reconnectButton; var UWindowSmallButton disconnectButton; var UWindowSmallButton exitButton; var UWindowSmallButton mapVoteButton; var UWindowSmallButton startButton; var UWindowSmallButton loginButton; var UMenuLabelControl serverTitleLabel; var color teamColor[4]; var color rightGranted; var color rightDenied; var color rightNotDefined; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local NexgenContentPanel rightPanel; local UMenuLabelControl l; local int index; local string rightDef; // Create layout & add components. createWindowRootRegion(); splitRegionV(96, defaultComponentDist, , true); splitRegionH(32, defaultComponentDist); divideRegionH(14); // Title. p = addContentPanel(); serverTitleLabel = p.addLabel(client.sConf.serverName, true, TA_Center); // Rights overview. p = addContentPanel(); p.splitRegionH(32, defaultComponentDist); p.divideRegionH(2); p.divideRegionV(2, defaultComponentDist); p.addLabel(client.lng.format(client.lng.welcomeTxt, client.playerName, client.title), true); p.addLabel(client.lng.rightsOverviewTxt); p.divideRegionH(arraycount(client.sConf.rightsDef) / 2, defaultComponentDist); p.divideRegionH(arraycount(client.sConf.rightsDef) / 2, defaultComponentDist); for (index = 0; index < arraycount(client.sConf.rightsDef); index++) { rightPanel = p.addContentPanel(PBT_Transparent); l = rightPanel.addLabel("", true, TA_Center); rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { l.setText(client.lng.format(client.lng.rightNotDefinedTxt, string(index + 1))); l.setTextColor(rightNotDefined); } else { l.setText(mid(rightDef, instr(rightDef, client.sConf.separator) + 1)); if (client.hasRight(left(rightDef, instr(rightDef, client.sConf.separator)))) { l.setTextColor(rightGranted); rightPanel.panelBGType = PBT_Default; } else { l.setTextColor(rightDenied); } } } // Sidebar buttons. teamBalanceButton = addButton(client.lng.teamBalanceTxt); teamButton[0] = addButton(client.lng.redTeamTxt); teamButton[1] = addButton(client.lng.blueTeamTxt); teamButton[2] = addButton(client.lng.greenTeamTxt); teamButton[3] = addButton(client.lng.goldTeamTxt); skipRegion(); playSpecButton = addButton(); reconnectButton = addButton(client.lng.reconnectTxt); disconnectButton = addButton(client.lng.disconnectTxt); exitButton = addButton(client.lng.exitTxt); skipRegion(); mapVoteButton = addButton(client.lng.mapVoteTxt); startButton = addButton(client.lng.startTxt); loginButton = addButton(client.lng.loginTxt); // Configure components. if (client.bSpectator) { playSpecButton.setText(client.lng.playTxt); } else { playSpecButton.setText(client.lng.spectateTxt); } setupTeamButtons(); if (client.gInf.gameState > client.gInf.GS_Ready) { startButton.bDisabled = true; } } /*************************************************************************************************** * * $DESCRIPTION Sets the properties of the team control buttons. * **************************************************************************************************/ function setupTeamButtons() { local TournamentGameReplicationInfo gri; local int index; // Check which buttons should be disabled. if (client.bSpectator || !client.player.gameReplicationInfo.bTeamGame) { if (!client.player.gameReplicationInfo.bTeamGame) { teamBalanceButton.bDisabled = true; } for (index = 0; index < arrayCount(teamButton); index++) { teamButton[index].bDisabled = true; } } else { for (index = 0; index < arrayCount(teamButton); index++) { teamButton[index].bDisabled = index >= client.gInf.maxTeams; } } // Set button colors. for (index = 0; index < arrayCount(teamButton); index++) { if (!teamButton[index].bDisabled) { teamButton[index].setTextColor(teamColor[index]); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case teamBalanceButton: client.player.consoleCommand("mutate nsc balanceteams"); break; case teamButton[0]: client.player.consoleCommand("mutate nsc setteam 0"); break; case teamButton[1]: client.player.consoleCommand("mutate nsc setteam 1"); break; case teamButton[2]: client.player.consoleCommand("mutate nsc setteam 2"); break; case teamButton[3]: client.player.consoleCommand("mutate nsc setteam 3"); break; case playSpecButton: if (client.bSpectator) { client.player.consoleCommand("mutate nsc play"); } else { client.player.consoleCommand("mutate nsc spectate"); } UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case reconnectButton: client.player.consoleCommand("reconnect"); UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case disconnectButton: client.player.consoleCommand("disconnect"); UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case exitButton: client.player.consoleCommand("exit"); break; case mapVoteButton: client.player.consoleCommand("mutate nsc openvote"); UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case startButton: client.player.consoleCommand("mutate nsc start"); break; case loginButton: client.showPopup("NexgenAdminLoginDialog"); break; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_GlobalServerSettings) { serverTitleLabel.setText(client.sConf.serverName); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ d 5I;K&¬RAš}o,T .±Ãpp D#.o ±HT *-L' J;L;S:ê -LKT  €±K;MUJ åMddMhhMYYM``MXXMooMssMT T M-L-L : 12.0M;P;t^½ š:s, €q‚/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPGameInfo * $VERSION 1.04 (6-11-2007 11:49) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen game information overview control panel page. * **************************************************************************************************/ class NexgenRCPGameInfo extends NexgenPanel; var NexgenPlayerListBox playerList; var NexgenSimpleListBox mutatorList; var UMenuLabelControl timeLimitLabel; var UMenuLabelControl fragLimitLabel; var UMenuLabelControl teamScoreLimitLabel; var UMenuLabelControl gameSpeedLabel; var UWindowCheckbox enableTeamSwitchInp; var UWindowCheckbox enableTeamBalanceInp; var UWindowCheckbox teamsLockedInp; var UWindowCheckbox allowNameChangeInp; var UMenuLabelControl fileLabel; var UMenuLabelControl titleLabel; var UMenuLabelControl authorLabel; var UMenuLabelControl playersLabel; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); divideRegionV(2, defaultComponentDist); splitRegionH(160, defaultComponentDist); splitRegionH(96, defaultComponentDist); splitRegionH(16); splitRegionH(16); splitRegionH(16); splitRegionH(16); // Game info. addLabel(client.player.gameReplicationInfo.gameName, true, TA_Center); p = addContentPanel(); p.splitRegionV(200/3, , true); p.divideRegionH(8); p.divideRegionH(8); p.addLabel(client.lng.timeLimitTxt, true); p.addLabel(client.lng.scoreLimitTxt, true); p.addLabel(client.lng.teamScoreLimitTxt, true); p.addLabel(client.lng.gameSpeedTxt, true); p.addLabel(client.lng.teamSwitchEnabledTxt, true); p.addLabel(client.lng.teamBalanceEnabledTxt, true); p.addLabel(client.lng.teamsLockedTxt, true); p.addLabel(client.lng.nameChangeAllowedTxt, true); timeLimitLabel = p.addLabel(); fragLimitLabel = p.addLabel(); teamScoreLimitLabel = p.addLabel(); gameSpeedLabel = p.addLabel(); enableTeamSwitchInp = p.addCheckBox(TA_right); enableTeamBalanceInp = p.addCheckBox(TA_right); teamsLockedInp = p.addCheckBox(TA_right); allowNameChangeInp = p.addCheckBox(TA_right); // Mutators. addLabel(client.lng.mutatorsTxt, true, TA_Center); mutatorList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Level info. addLabel(client.lng.levelTxt, true, TA_Center); p = addContentPanel(); p.splitRegionV(72, defaultComponentDist); if (client.player.level.screenshot == none) { p.addComponent(class'NexgenDummyComponent', 64, 64, AL_Center, AL_Center); } else { p.addImageBox(client.player.level.screenshot, true, 64, 64); } p.splitRegionV(48); p.divideRegionH(4); p.divideRegionH(4); p.addLabel(client.lng.fileTxt, true); p.addLabel(client.lng.titleTxt, true); p.addLabel(client.lng.authorTxt, true); p.addLabel(client.lng.idealPlayerCountTxt, true); fileLabel = p.addLabel(); titleLabel = p.addLabel(); authorLabel = p.addLabel(); playersLabel = p.addLabel(); // Player info. addLabel(client.lng.playerListTxt, true, TA_Center); playerList = NexgenPlayerListBox(addListBox(class'NexgenPlayerListBox')); // Configure components. enableTeamSwitchInp.bDisabled = true; enableTeamBalanceInp.bDisabled = true; teamsLockedInp.bDisabled = true; allowNameChangeInp.bDisabled = true; setGameInfo(); setLevelInfo(); loadMutatorList(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of the game info labels. * **************************************************************************************************/ function setGameInfo() { local GameReplicationInfo GRI; local TournamentGameReplicationInfo TGRI; GRI = client.player.gameReplicationInfo; TGRI = TournamentGameReplicationInfo(GRI); if (TGRI != none) { timeLimitLabel.setText(string(TGRI.timeLimit)); fragLimitLabel.setText(string(TGRI.fragLimit)); teamScoreLimitLabel.setText(string(TGRI.goalTeamScore)); } gameSpeedLabel.setText(int(100.0 * client.gInf.gameSpeed) $ "%"); enableTeamSwitchInp.bChecked = !client.gInf.bNoTeamSwitch; enableTeamBalanceInp.bChecked = !client.gInf.bNoTeamBalance; teamsLockedInp.bChecked = client.gInf.bTeamsLocked; allowNameChangeInp.bChecked = !client.gInf.bNoNameChange; } /*************************************************************************************************** * * $DESCRIPTION Sets the values of the level info labels. * **************************************************************************************************/ function setLevelInfo() { local string levelFile; levelFile = string(client); levelFile = left(levelFile, instr(levelFile, ".")) $ ".unr"; fileLabel.setText(levelFile); titleLabel.setText(client.player.level.summary.title); authorLabel.setText(client.player.level.summary.author); playersLabel.setText(client.player.level.summary.idealPlayerCount); } /*************************************************************************************************** * * $DESCRIPTION Loads the list of active mutators. * **************************************************************************************************/ function loadMutatorList() { local NexgenSimpleListItem item; local string remaining; local string mutatorIndex; local string mutatorInfo; local string mutatorClass; local string mutatorName; // For each mutator index.. remaining = client.sConf.activeMutatorIndices; while (remaining != "") { // Get index. class'NexgenUtil'.static.split(remaining, mutatorIndex, remaining); // Get mutator info. mutatorInfo = client.sConf.mutatorInfo[int(mutatorIndex)]; class'NexgenUtil'.static.split(mutatorInfo, mutatorClass, mutatorName); // Add mutator to the list. item = NexgenSimpleListItem(mutatorList.items.append(class'NexgenSimpleListItem')); item.displayText = mutatorName; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { if (infoType == client.IT_GlobalRights) { setGameInfo(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ N; 4.0tZ/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPClientConfig * $VERSION 1.06 (8-3-2008 22:13) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen client settings control panel page. * **************************************************************************************************/ class NexgenRCPClientConfig extends NexgenPanel; var UWindowCheckbox enableNexgenHUDInp; var UWindowCheckbox useMsgFlashEffectInp; var UWindowCheckbox showPlayerLocationInp; var UWindowCheckbox playPMSoundInp; var UWindowCheckbox autoSSNormalGameInp; var UWindowCheckbox autoSSMatchInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; // Create layout & add components. setAcceptsFocus(); createWindowRootRegion(); splitRegionV(196, defaultComponentDist); // Keybindings. addSubPanel(class'NexgenCPKeyBind'); // User Interface settings. splitRegionH(96, defaultComponentDist); p = addContentPanel(); p.splitRegionH(16); p.addLabel(client.lng.UISettingsTxt, true, TA_Center); p.divideRegionH(4); enableNexgenHUDInp = p.addCheckBox(TA_Left, client.lng.enableMsgHUDTxt); useMsgFlashEffectInp = p.addCheckBox(TA_Left, client.lng.msgFlashEffectTxt); showPlayerLocationInp = p.addCheckBox(TA_Left, client.lng.showPlayerLocationTxt); playPMSoundInp = p.addCheckBox(TA_Left, client.lng.pmSoundTxt); // Other stuff. splitRegionH(64, defaultComponentDist); p = addContentPanel(); p.splitRegionH(16); p.addLabel(client.lng.miscSettingsTxt, true, TA_Center); p.divideRegionH(2); autoSSNormalGameInp = p.addCheckBox(TA_Left, client.lng.autoSSNormalGameTxt); autoSSMatchInp = p.addCheckBox(TA_Left, client.lng.autoSSMatchTxt); // Configure components. enableNexgenHUDInp.register(self); useMsgFlashEffectInp.register(self); showPlayerLocationInp.register(self); playPMSoundInp.register(self); autoSSNormalGameInp.register(self); autoSSMatchInp.register(self); enableNexgenHUDInp.bChecked = client.gc.get(client.SSTR_UseNexgenHUD, "true") ~= "true"; useMsgFlashEffectInp.bChecked = client.gc.get(client.SSTR_FlashMessages, "true") ~= "true"; showPlayerLocationInp.bChecked = client.gc.get(client.SSTR_ShowPlayerLocation, "true") ~= "true"; playPMSoundInp.bChecked = client.gc.get(client.SSTR_PlayPMSound, "true") ~= "true"; autoSSNormalGameInp.bChecked = client.gc.get(client.SSTR_AutoSSNormalGame, "false") ~= "true"; autoSSMatchInp.bChecked = client.gc.get(client.SSTR_AutoSSMatch, "true") ~= "true"; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Toggle Nexgen HUD on/off. if (control == enableNexgenHUDInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_UseNexgenHUD, string(enableNexgenHUDInp.bChecked)); client.gc.saveConfig(); // Change HUD on the fly! if (client.sConf.HUDReplacementClass != none) { if (enableNexgenHUDInp.bChecked) { client.setNexgenMessageHUD(true); } else { client.setNexgenMessageHUD(false); } } } // Toggle message flash effect on/off. if (control == useMsgFlashEffectInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_FlashMessages, string(useMsgFlashEffectInp.bChecked)); client.gc.saveConfig(); // Apply setting. client.nscHUD.bFlashMessages = useMsgFlashEffectInp.bChecked; } // Toggle show player location in teamsay messages on/off. if (control == showPlayerLocationInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_ShowPlayerLocation, string(showPlayerLocationInp.bChecked)); client.gc.saveConfig(); // Apply setting. client.nscHUD.bShowPlayerLocation = showPlayerLocationInp.bChecked; } // Toggle private message sound on/off. if (control == playPMSoundInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_PlayPMSound, string(playPMSoundInp.bChecked)); client.gc.saveConfig(); } // Toggle auto screenshot for normal games on/off. if (control == autoSSNormalGameInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_AutoSSNormalGame, string(autoSSNormalGameInp.bChecked)); client.gc.saveConfig(); } // Toggle auto screenshot for matches on/off. if (control == autoSSMatchInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_AutoSSMatch, string(autoSSMatchInp.bChecked)); client.gc.saveConfig(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ R; 4.0õ5n%aî84s. n%5. i%5ÿÿÿÿ6& T; 2.0\;\D±‘ôÏr\*,–Q, JQ¥QtJ&f–JQ“J&PJP¥J3Q“Q&6mJP\6lJP[6kJPZ6jJPYò\\[ZY €}ö/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPBootControl * $VERSION 1.05 (15-12-2007 15:38) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen boot control panel page. * **************************************************************************************************/ class NexgenRCPBootControl extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowComboControl gameTypeList; var NexgenSimpleListBox inclMutatorList; var NexgenSimpleListBox exclMutatorList; var UWindowSmallButton rebootButton; var UWindowSmallButton saveButton; var UWindowSmallButton resetButton; var NexgenEditControl mapPrefixInp; var NexgenEditControl extraOptionsInp; var NexgenEditControl commandsInp; var UWindowCheckbox enableBootControlInp; var UWindowCheckbox restartOnLastGameInp; var UWindowDynamicTextArea previewBox; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int region; // Create layout & add components. createWindowRootRegion(); splitRegionH(20, defaultComponentDist); divideRegionV(2); splitRegionH(20, defaultComponentDist, , true); splitRegionV(20, defaultComponentDist); splitRegionV(20, defaultComponentDist); p = addContentPanel(); splitRegionV(196, , , true); enableBootControlInp = addCheckBox(TA_Right); addLabel(client.lng.enableBootCtrlTxt, true); restartOnLastGameInp = addCheckBox(TA_Right); addLabel(client.lng.restartOnLastGameTxt, true); rebootButton = addButton(client.lng.rebootTxt, 96, AL_Left); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); // Mutator list. p.splitRegionV(65, 2 * defaultComponentDist, true); region = p.currRegion; p.skipRegion(); p.divideRegionH(2); p.splitRegionH(16); p.splitRegionH(16); p.addLabel(client.lng.inclMutatorsTxt, true); inclMutatorList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.addLabel(client.lng.exclMutatorsTxt, true); exclMutatorList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); // Server boot command line preview. p.selectRegion(region); p.selectRegion(p.splitRegionH(128, defaultComponentDist)); region = p.currRegion; p.skipRegion(); p.splitRegionH(16); p.addLabel(client.lng.bootCmdLineTxt, True); previewBox = p.addDynamicTextArea(); // Server boot options. p.selectRegion(region); p.selectRegion(p.divideRegionH(4, defaultComponentDist)); p.splitRegionV(64); p.splitRegionV(64); p.splitRegionH(16); p.splitRegionH(16); p.addLabel(client.lng.gameTypeTxt, true); gameTypeList = p.addListCombo(); p.addLabel(client.lng.mapPrefixTxt, true); mapPrefixInp = p.addEditBox(); p.addLabel(client.lng.extraCmdLineOptTxt, true); extraOptionsInp = p.addEditBox(); p.addLabel(client.lng.preSwitchCommandsTxt, true); commandsInp = p.addEditBox(); // Configure components. mapPrefixInp.setMaxLength(8); extraOptionsInp.setMaxLength(255); commandsInp.setMaxLength(255); resetButton.register(self); inclMutatorList.register(self); exclMutatorList.register(self); gameTypeList.register(self); mapPrefixInp.register(self); extraOptionsInp.register(self); rebootButton.register(self); loadGameTypeList(); loadMutatorList(); loadBootControlSettings(); previewBox.bTopCentric = true; inclMutatorList.bCanDrag = true; } /*************************************************************************************************** * * $DESCRIPTION Loads the game type list. * **************************************************************************************************/ function loadGameTypeList() { local int index; local string gameClass; local string mapPrefix; local string gameName; while (index < arrayCount(client.sConf.gameTypeInfo) && client.sConf.gameTypeInfo[index] != "") { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[index], gameClass, mapPrefix); class'NexgenUtil'.static.split(mapPrefix, mapPrefix, gameName); gameTypeList.addItem(gameName, string(index)); index++; } } /*************************************************************************************************** * * $DESCRIPTION Loads the mutator list. * **************************************************************************************************/ function loadMutatorList() { local int index; local NexgenSimpleListItem item; local string mutatorClass; local string mutatorName; while (index < arrayCount(client.sConf.mutatorInfo) && client.sConf.mutatorInfo[index] != "") { class'NexgenUtil'.static.split(client.sConf.mutatorInfo[index], mutatorClass, mutatorName); item = NexgenSimpleListItem(exclMutatorList.items.append(class'NexgenSimpleListItem')); item.displayText = mutatorName; item.itemID = index; index++; } exclMutatorList.items.sort(); } /*************************************************************************************************** * * $DESCRIPTION Loads the boot control settings. * **************************************************************************************************/ function loadBootControlSettings() { local NexgenSimpleListItem oldItem; local NexgenSimpleListItem newItem; local string remaining; local string mutatorIndex; local int index; // Select game type. index = client.sConf.getGameIndex(client.sConf.bootGameType); gameTypeList.setSelectedIndex(index); // Move all mutators to excluded list. for (oldItem = NexgenSimpleListItem(inclMutatorList.items); oldItem != none; oldItem = NexgenSimpleListItem(oldItem.next)) { if (oldItem.itemID >= 0) { newItem = NexgenSimpleListItem(exclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = oldItem.displayText; newItem.itemID = oldItem.itemID ; } } inclMutatorList.items.clear(); inclMutatorList.selectedItem = none; // Load included mutator list. if (exclMutatorList.selectedItem != none) { exclMutatorList.selectedItem.bSelected = false; exclMutatorList.selectedItem = none; } remaining = client.sConf.bootMutatorIndices; while (remaining != "") { class'NexgenUtil'.static.split(remaining, mutatorIndex, remaining); index = int(mutatorIndex); oldItem = exclMutatorList.getItemByID(index); newItem = NexgenSimpleListItem(inclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = oldItem.displayText; newItem.itemID = oldItem.itemID; oldItem.remove(); } exclMutatorList.items.sort(); // Load other settings. mapPrefixInp.setValue(client.sConf.bootMapPrefix); extraOptionsInp.setValue(client.sConf.bootOptions); commandsInp.setValue(client.sConf.bootCommands); enableBootControlInp.bChecked = client.sConf.enableBootControl; restartOnLastGameInp.bChecked = client.sConf.restartOnLastGame; // Update preview. updatePreview(); } /*************************************************************************************************** * * $DESCRIPTION Updates the server boot command line preview. * **************************************************************************************************/ function updatePreview() { local string bootCmd; local string gameType; local string mutators; local string mutator; local string remaining; local int index; local NexgenSimpleListItem item; // Get game type. index = gameTypeList.getSelectedIndex(); if (index >= 0) { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[index], gameType, remaining); } // Get mutators. for (item = NexgenSimpleListItem(inclMutatorList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.itemID >= 0) { class'NexgenUtil'.static.split(client.sConf.mutatorInfo[item.itemID], mutator, remaining); if (mutators == "") { mutators = mutator; } else { mutators = mutators $ separator $ " " $ mutator; } } } // Create boot command string. bootCmd = class'NexgenUtil'.static.trim(mapPrefixInp.getValue()) $ "-*.unr"; if (gameType != "") bootCmd = bootCmd $ " ?game=" $ gameType; if (mutators != "") bootCmd = bootCmd $ " ?mutator=" $ mutators; bootCmd = bootCmd $ " " $ class'NexgenUtil'.static.trim(extraOptionsInp.getValue()); // Set preview box contents. previewBox.clear(); previewBox.addText(bootCmd); } /*************************************************************************************************** * * $DESCRIPTION Automatically selects the map prefix for the selected game type. * **************************************************************************************************/ function updateMapPrefix() { local int index; local string gameClass; local string mapPrefix; local string remaining; // Get map prefix. index = gameTypeList.getSelectedIndex(); if (index >= 0) { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[index], gameClass, remaining); class'NexgenUtil'.static.split(remaining, mapPrefix, remaining); } else { mapPrefix = ""; } // Set map prefix. mapPrefixInp.setValue(mapPrefix); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Sends the new boot control settings to the server. * $REQUIRE rpci != none * **************************************************************************************************/ function updateBootControl() { local string mutators; local NexgenSimpleListItem item; // Get mutators. for (item = NexgenSimpleListItem(inclMutatorList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.itemID >= 0) { if (mutators == "") { mutators = string(item.itemID); } else { mutators = mutators $ separator $ " " $ item.itemID; } } } // Send new settings to the server. rpci.updateBootControl(enableBootControlInp.bChecked, restartOnLastGameInp.bChecked, gameTypeList.getSelectedIndex(), mutators, class'NexgenUtil'.static.trim(mapPrefixInp.getValue()), class'NexgenUtil'.static.trim(extraOptionsInp.getValue()), class'NexgenUtil'.static.trim(commandsInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local NexgenSimpleListItem newItem; super.notify(control, eventType); setRPCI(); // Reset button pressed? if (control == resetButton && eventType == DE_Click) { loadBootControlSettings(); } // Save button pressed? if (control == saveButton && eventType == DE_Click && rpci != none) { updateBootControl(); } // Reboot button pressed? if (control == rebootButton && eventType == DE_Click && rpci != none) { rpci.rebootServer(); } // Mutator selected? if (control == inclMutatorList && eventType == DE_Click) { if (exclMutatorList.selectedItem != none) { exclMutatorList.selectedItem.bSelected = false; exclMutatorList.selectedItem = none; } } else if (control == exclMutatorList && eventType == DE_Click) { if (inclMutatorList.selectedItem != none) { inclMutatorList.selectedItem.bSelected = false; inclMutatorList.selectedItem = none; } } // Mutator double clicked? if (control == inclMutatorList && eventType == DE_DoubleClick && inclMutatorList.selectedItem != none) { newItem = NexgenSimpleListItem(exclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = NexgenSimpleListItem(inclMutatorList.selectedItem).displayText; newItem.itemID = NexgenSimpleListItem(inclMutatorList.selectedItem).itemID; if (exclMutatorList.selectedItem != none) { exclMutatorList.selectedItem.bSelected = false; } exclMutatorList.selectedItem = newItem; newItem.bSelected = true; inclMutatorList.selectedItem.remove(); inclMutatorList.selectedItem = none; exclMutatorList.sort(); updatePreview(); } else if (control == exclMutatorList && eventType == DE_DoubleClick && exclMutatorList.selectedItem != none) { newItem = NexgenSimpleListItem(inclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = NexgenSimpleListItem(exclMutatorList.selectedItem).displayText; newItem.itemID = NexgenSimpleListItem(exclMutatorList.selectedItem).itemID; if (inclMutatorList.selectedItem != none) { inclMutatorList.selectedItem.bSelected = false; } inclMutatorList.selectedItem = newItem; newItem.bSelected = true; exclMutatorList.selectedItem.remove(); exclMutatorList.selectedItem = none; updatePreview(); } // Game type selected? if (control == gameTypeList && eventType == DE_Change) { updateMapPrefix(); //updatePreview(); } // Map prefix changed? if (control == mapPrefixInp && eventType == DE_Change) { updatePreview(); } // Extra options changed? if (control == extraOptionsInp && eventType == DE_Change) { updatePreview(); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_BootControl) { loadBootControlSettings(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ V; 0.70g¡/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPBanControl * $VERSION 1.01 (2-11-2007 21:12) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen ban control panel page. * **************************************************************************************************/ class NexgenRCPBanControl extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox banList; var NexgenSimpleListBox ipList; var NexgenSimpleListBox idList; var UWindowSmallButton addBanButton; var UWindowSmallButton updateBanButton; var UWindowSmallButton deleteBanButton; var UWindowEditControl playerNameInp; var UWindowEditControl banReasonInp; var UWindowCheckbox banPeriodInp[3]; var NexgenEditControl matchCountInp; var NexgenEditControl dateInp; var UWindowSmallButton addIPButton; var UWindowSmallButton delIPButton; var UWindowSmallButton addIDButton; var UWindowSmallButton delIDButton; var UWindowEditControl ipAddressInp; var UWindowEditControl clientIDInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int region; local int index; // Create layout & add components. createWindowRootRegion(); splitRegionV(160, defaultComponentDist); splitRegionH(72, defaultComponentDist, , true); // Ban entry editor. p = addContentPanel(); // Player name & ban reason. region = p.splitRegionH(40, defaultComponentDist) + 1; p.splitRegionV(96); p.skipRegion(); p.divideRegionH(2); p.divideRegionH(2); p.addLabel(client.lng.playerNameTxt, true); p.addLabel(client.lng.banReasonTxt, true); playerNameInp = p.addEditBox(); banReasonInp = p.addEditBox(); // Ban period. p.selectRegion(region); p.selectRegion(p.splitRegionH(64, defaultComponentDist)); region = p.currRegion + 1; p.splitRegionH(1); p.skipRegion(); p.addComponent(class'NexgenDummyComponent'); p.splitRegionV(96); p.divideRegionH(3); p.splitRegionV(96, defaultComponentDist); p.addLabel(client.lng.banPeriodTxt, true); p.skipRegion(); p.skipRegion(); p.divideRegionH(3); p.divideRegionH(3); banPeriodInp[0] = p.addCheckBox(TA_Left, client.lng.banForeverTxt); banPeriodInp[1] = p.addCheckBox(TA_Left, client.lng.banMatchesTxt); banPeriodInp[2] = p.addCheckBox(TA_Left, client.lng.banUntilDateTxt); p.skipRegion(); matchCountInp = p.addEditBox(); dateInp = p.addEditBox(); // Banned IP's and ID's. p.selectRegion(region); p.selectRegion(p.splitRegionH(1, defaultComponentDist)); p.addComponent(class'NexgenDummyComponent'); p.divideRegionH(2, defaultComponentDist); p.splitRegionV(224, defaultComponentDist, , true); p.splitRegionV(224, defaultComponentDist, , true); p.splitRegionH(56); ipList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.splitRegionH(56); idList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.divideRegionH(3); p.skipRegion(); p.divideRegionH(3); p.skipRegion(); p.addLabel(client.lng.ipAddressesTxt, true); p.divideRegionV(2, defaultComponentDist); ipAddressInp = p.addEditBox(); p.addLabel(client.lng.clientIDsTxt, true); p.divideRegionV(2, defaultComponentDist); clientIDInp = p.addEditBox(); addIPButton = p.addButton(client.lng.addTxt); delIPButton = p.addButton(client.lng.removeTxt); addIDButton = p.addButton(client.lng.addTxt); delIDButton = p.addButton(client.lng.removeTxt); // Ban list. banList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Ban list editor. p = addContentPanel(); p.divideRegionH(3); addBanButton = p.addButton(client.lng.addBanTxt); updateBanButton = p.addButton(client.lng.updateBanTxt); deleteBanButton = p.addButton(client.lng.delBanTxt); // Configure components. ipAddressInp.setMaxLength(15); clientIDInp.setMaxLength(32); playerNameInp.setMaxLength(32); banReasonInp.setMaxLength(255); matchCountInp.setMaxLength(3); matchCountInp.setNumericOnly(true); dateInp.setMaxLength(24); banList.register(self); ipList.register(self); idList.register(self); addIPButton.register(self); delIPButton.register(self); addIDButton.register(self); delIDButton.register(self); addBanButton.register(self); updateBanButton.register(self); deleteBanButton.register(self); for (index = 0; index < arrayCount(banPeriodInp); index++) { banPeriodInp[index].register(self); } loadBanList(); banPeriodInp[0].bChecked = true; banPeriodTypeSelected(); delIPButton.bDisabled = true; delIDButton.bDisabled = true; dateInp.setValue(client.lng.dateFormatStr); } /*************************************************************************************************** * * $DESCRIPTION Load the banlist. * **************************************************************************************************/ function loadBanList() { local int index; local NexgenSimpleListItem item; local int numBans; // Clear list. banList.items.clear(); banList.selectedItem = none; // Add bans. while(index < arrayCount(client.sConf.bannedName) && client.sConf.bannedName[index] != "") { item = NexgenSimpleListItem(banList.items.append(class'NexgenSimpleListItem')); item.displayText = client.sConf.bannedName[index]; item.itemID = index; index++; } // Configure components. numBans = index; addBanButton.bDisabled = numBans >= arrayCount(client.sConf.bannedName); banSelected(); } /*************************************************************************************************** * * $DESCRIPTION Called when a ban entry has been selected. Loads the info for the selected ban * entry & configures the components. * **************************************************************************************************/ function banSelected() { // Item selected? if (banList.selectedItem == none) { // No. updateBanButton.bDisabled = true; deleteBanButton.bDisabled = true; } else { // Yes. updateBanButton.bDisabled = false; deleteBanButton.bDisabled = false; loadBanInfo(NexgenSimpleListItem(banList.selectedItem).itemID); } } /*************************************************************************************************** * * $DESCRIPTION Loads the information for the specified ban entry. * $PARAM entryNum The entry number in the ban list. * $REQUIRE 0 <= entryNum && entryNum < arrayCount(client.sConf.bannedName) && * client.sConf.bannedName[entryNum] != "" * **************************************************************************************************/ function loadBanInfo(int entryNum) { local string remaining; local string part; local NexgenSimpleListItem item; local byte banPeriodType; local string banArgs; local int index; // Player name & ban reason. playerNameInp.setValue(client.sConf.bannedName[entryNum]); banReasonInp.setValue(client.sConf.banReason[entryNum]); // Ban period. client.sConf.getBanPeriodType(client.sConf.banPeriod[entryNum], banPeriodType, banArgs); for (index = 0; index < arrayCount(banPeriodInp); index++) { banPeriodInp[index].bChecked = index == banPeriodType; } if (banPeriodType == client.sConf.BP_Matches) { matchCountInp.setDisabled(false); matchCountInp.setValue(banArgs); dateInp.setDisabled(true); dateInp.setValue(""); } else if (banPeriodType == client.sConf.BP_UntilDate) { matchCountInp.setDisabled(true); matchCountInp.setValue(""); dateInp.setDisabled(false); dateInp.setValue(client.lng.getLocalizedDateStr(banArgs)); } else { matchCountInp.setDisabled(true); matchCountInp.setValue(""); dateInp.setDisabled(true); dateInp.setValue(""); } // Load ip addresses. ipList.items.clear(); ipList.selectedItem = none; remaining = client.sConf.bannedIPs[entryNum]; while (remaining != "") { // Split head element from tail. index = instr(remaining, separator); if (index < 0) { part = remaining; remaining = ""; } else { part = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Add element to list. item = NexgenSimpleListItem(ipList.items.append(class'NexgenSimpleListItem')); item.displayText = part; } addIPButton.bDisabled = ipList.items.countShown() >= client.sConf.maxBanIPAddresses; delIPButton.bDisabled = true; // Load client id's. idList.items.clear(); idList.selectedItem = none; remaining = client.sConf.bannedIDs[entryNum]; while (remaining != "") { // Split head element from tail. index = instr(remaining, separator); if (index < 0) { part = remaining; remaining = ""; } else { part = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Add element to list. item = NexgenSimpleListItem(idList.items.append(class'NexgenSimpleListItem')); item.displayText = part; } addIDButton.bDisabled = idList.items.countShown() >= client.sConf.maxBanClientIDs; delIDButton.bDisabled = true; } /*************************************************************************************************** * * $DESCRIPTION Called when a ban period type was selected. * **************************************************************************************************/ function banPeriodTypeSelected() { matchCountInp.setDisabled(!banPeriodInp[client.sConf.BP_Matches].bChecked); dateInp.setDisabled(!banPeriodInp[client.sConf.BP_UntilDate].bChecked); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_BanList) { loadBanList(); } } /*************************************************************************************************** * * $DESCRIPTION Assembles the current ban period string. * $RETURN The ban period string for the currently selected settings. * **************************************************************************************************/ function string getCurrentBanPeriod() { if (banPeriodInp[client.sConf.BP_Matches].bChecked) { return "M" $ matchCountInp.getValue(); } else if (banPeriodInp[client.sConf.BP_UntilDate].bChecked) { return "U" $ client.lng.getDelocalizedDateStr(dateInp.getValue()); } else { return ""; } } /*************************************************************************************************** * * $DESCRIPTION Returns the ip addresses entered in the ip list. * $RETURN A string containing all ip addresses entered in the ip list. * **************************************************************************************************/ function string getIPList() { local NexgenSimpleListItem item; local string list; // Assemble list. for (item = NexgenSimpleListItem(ipList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.displayText != "") { if (list == "") { list = item.displayText; } else { list = list $ separator $ item.displayText; } } } // Return the list. return list; } /*************************************************************************************************** * * $DESCRIPTION Returns the client id's entered in the id list. * $RETURN A string containing all client id's entered in the id list. * **************************************************************************************************/ function string getIDList() { local NexgenSimpleListItem item; local string list; // Assemble list. for (item = NexgenSimpleListItem(idList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.displayText != "") { if (list == "") { list = item.displayText; } else { list = list $ separator $ item.displayText; } } } // Return the list. return list; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local int index; local int selectedIndex; local string value; local NexgenSimpleListItem item; local string playerName; super.notify(control, eventType); setRPCI(); // Add ban entry button clicked? if (control == addBanButton && eventType == DE_Click && !addBanButton.bDisabled) { playerName = class'NexgenUtil'.static.trim(playerNameInp.getValue()); if (playerName != "") { rpci.addBan(playerName, getIPList(), getIDList(), class'NexgenUtil'.static.trim(banReasonInp.getValue()), getCurrentBanPeriod()); } } // Update ban entry button clicked? if (control == updateBanButton && eventType == DE_Click && !updateBanButton.bDisabled) { playerName = class'NexgenUtil'.static.trim(playerNameInp.getValue()); if (playerName != "") { rpci.updateBan(NexgenSimpleListItem(banList.selectedItem).itemID, playerName, getIPList(), getIDList(), class'NexgenUtil'.static.trim(banReasonInp.getValue()), getCurrentBanPeriod()); } } // Delete ban entry button clicked? if (control == deleteBanButton && eventType == DE_Click && !deleteBanButton.bDisabled) { rpci.deleteBan(NexgenSimpleListItem(banList.selectedItem).itemID); } // Ban entry selected? if (control == banList && eventType == DE_Click) { banSelected(); } // IP address selected? if (control == ipList && eventType == DE_Click) { delIPButton.bDisabled = ipList.selectedItem == none; if (ipList.selectedItem != none) { ipAddressInp.setValue(NexgenSimpleListItem(ipList.selectedItem).displayText); } } // Client ID selected? if (control == idList && eventType == DE_Click) { delIDButton.bDisabled = idList.selectedItem == none; if (idList.selectedItem != none) { clientIDInp.setValue(NexgenSimpleListItem(idList.selectedItem).displayText); } } // Add IP address pressed? if (control == addIPButton && eventType == DE_Click && !addIPButton.bDisabled) { value = class'NexgenUtil'.static.trim(ipAddressInp.getValue()); if (class'NexgenUtil'.static.isValidIPAddress(value)) { item = NexgenSimpleListItem(ipList.items.append(class'NexgenSimpleListItem')); item.displayText = value; addIPButton.bDisabled = ipList.items.countShown() >= client.sConf.maxBanIPAddresses; } } // Del IP address pressed? if (control == delIPButton && eventType == DE_Click && !delIPButton.bDisabled) { ipList.selectedItem.remove(); ipList.selectedItem = none; delIPButton.bDisabled = true; addIPButton.bDisabled = ipList.items.countShown() >= client.sConf.maxBanIPAddresses; } // Del client ID pressed? if (control == addIDButton && eventType == DE_Click && !addIDButton.bDisabled) { value = class'NexgenUtil'.static.trim(clientIDInp.getValue()); if (class'NexgenUtil'.static.isValidClientID(value)) { item = NexgenSimpleListItem(idList.items.append(class'NexgenSimpleListItem')); item.displayText = value; addIDButton.bDisabled = idList.items.countShown() >= client.sConf.maxBanClientIDs; } } // Del client ID pressed? if (control == delIDButton && eventType == DE_Click && !delIDButton.bDisabled) { idList.selectedItem.remove(); idList.selectedItem = none; delIDButton.bDisabled = true; addIDButton.bDisabled = idList.items.countShown() >= client.sConf.maxBanClientIDs; } // Ban period type selected? if (eventType == DE_Click && control.isA('UWindowCheckbox')) { // Find selected type. selectedIndex = -1; while (selectedIndex < 0 && index < arrayCount(banPeriodInp)) { if (control == banPeriodInp[index]) { selectedIndex = index; } else { index++; } } // Has a period been selected? if (selectedIndex >= 0) { // Yes, update components. for (index = 0; index < arrayCount(banPeriodInp); index++) { banPeriodInp[index].bChecked = index == selectedIndex; } banPeriodTypeSelected(); } } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ e;uWŽjÖÔ³¯•ÊiÍÌL>i•Ê/a0 C\RQw\*R10Ôw\*u%Í–uQ\F6muP6luP6kuP6juP¥ueQ% Mxe5we%YUYx}xV%@YP%o–P,WpWK#:P@&¥P9W  f;\28§®%—wXXw^tœšt V/= 2\™‚–_,w_Q*¥_Q 2\[¬:\`=,ÿ ÂY€@{: 192A €[€Ô}Å/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPAccountTypes * $VERSION 1.01 (20-10-2007 10:50) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen account type manager control panel page. * **************************************************************************************************/ class NexgenRCPAccountTypes extends NexgenPanel; var int numAccountTypes; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox accountTypeList; var UWindowSmallButton addAccountTypeButton; var UWindowSmallButton deleteAccountTypeButton; var UWindowSmallButton moveUpButton; var UWindowSmallButton moveDownButton; var UWindowEditControl accountNameInp; var UWindowEditControl accountTitleInp; var UWindowEditControl accountPasswordInp; var UWindowCheckbox rightEnableInp[16]; var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; local string rightDef; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); splitRegionH(96, defaultComponentDist, , true); // Account info panel. p = addContentPanel(); p.splitRegionH(64); p.splitRegionV(96); p.splitRegionH(16, , , true); p.divideRegionH(3); p.divideRegionH(3); p.divideRegionV(2, defaultComponentDist); p.splitRegionV(182); p.addLabel(client.lng.accountNameTxt, true); p.addLabel(client.lng.accountTitleTxt, true); p.addLabel(client.lng.passwordTxt, true); accountNameInp = p.addEditBox(); accountTitleInp = p.addEditBox(); accountPasswordInp = p.addEditBox(); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionV(2, defaultComponentDist); p.skipRegion(); for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { rightEnableInp[index] = p.addCheckBox(TA_Left, client.lng.format(client.lng.rightNotDefinedTxt, string(index + 1))); rightEnableInp[index].bDisabled = true; } else { rightEnableInp[index] = p.addCheckBox(TA_Left, mid(rightDef, instr(rightDef, client.sConf.separator) + 1)); } } saveButton = p.addButton(client.lng.saveTxt); resetButton = p.addButton(client.lng.resetTxt); // Account type list. accountTypeList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Account type controls. p = addContentPanel(); p.divideRegionH(4); addAccountTypeButton = p.addButton(client.lng.addAccountTypeTxt); deleteAccountTypeButton = p.addButton(client.lng.delAccountTypeTxt); moveUpButton = p.addButton(client.lng.moveUpTxt); moveDownButton = p.addButton(client.lng.moveDownTxt); // Configure components. accountNameInp.setMaxLength(24); accountTitleInp.setMaxLength(24); accountPasswordInp.setMaxLength(32); accountTypeList.register(self); addAccountTypeButton.register(self); deleteAccountTypeButton.register(self); moveUpButton.register(self); moveDownButton.register(self); resetButton.register(self); saveButton.register(self); loadAccountTypes(); accountTypeSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the account types. * **************************************************************************************************/ function loadAccountTypes() { local int index; local NexgenSimpleListItem item; accountTypeList.items.clear(); accountTypeList.selectedItem = none; while(index < arrayCount(client.sConf.atTypeName) && client.sConf.atTypeName[index] != "") { item = NexgenSimpleListItem(accountTypeList.items.append(class'NexgenSimpleListItem')); item.displayText = client.sConf.atTypeName[index]; item.itemID = index; index++; } numAccountTypes = index; } /*************************************************************************************************** * * $DESCRIPTION Loads the settings for the specified account type. * $PARAM accountTypeNum The ID number of the account type to load. * $REQUIRE 0 <= accountTypeNum && accountTypeNum <= arrayCount(client.sConf.atTypeName) * **************************************************************************************************/ function loadAccountTypeInfo(int accountTypeNum) { local int index; local string accountRights; local string rightDef; local string rightID; // Cancel on error. In theory this should not happen. if (client.sConf.atTypeName[accountTypeNum] == "") { return; } // Load general info. accountNameInp.setValue(client.sConf.atTypeName[accountTypeNum]); accountTitleInp.setValue(client.sConf.atTitle[accountTypeNum]); accountPasswordInp.setValue(client.sConf.decode(client.sConf.atPassword[accountTypeNum])); // Load right assignment. accountRights = client.sConf.atRights[accountTypeNum]; for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { // Right isn't defined. rightEnableInp[index].bChecked = false; } else { rightID = left(rightDef, instr(rightDef, separator)); rightEnableInp[index].bChecked = hasRight(accountRights, rightID); } } } /*************************************************************************************************** * * $DESCRIPTION Called when an account type was selected from the list. * **************************************************************************************************/ function accountTypeSelected() { local NexgenSimpleListItem selected; // Check which buttons should be enabled / disabled. if (accountTypeList.selectedItem == none) { // No item selected. deleteAccountTypeButton.bDisabled = true; moveUpButton.bDisabled = true; moveDownButton.bDisabled = true; resetButton.bDisabled = true; saveButton.bDisabled = true; } else { // Other account type selected. selected = NexgenSimpleListItem(accountTypeList.selectedItem); deleteAccountTypeButton.bDisabled = selected.itemID < 1; moveUpButton.bDisabled = selected.itemID < 2; moveDownButton.bDisabled = selected.itemID < 1 || selected.itemID + 1 == numAccountTypes; resetButton.bDisabled = false; saveButton.bDisabled = false; } // Load account info. if (accountTypeList.selectedItem != none) { loadAccountTypeInfo(NexgenSimpleListItem(accountTypeList.selectedItem).itemID); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified right is included in the given right string. * $PARAM rights The rights specifier string. * $PARAM rightID String identifier of the client right. * $REQUIRE rightID != "" * $RETURN True if the right is included, false if not. * **************************************************************************************************/ function bool hasRight(string rights, string rightID) { return instr(rights $ separator, rightID $ separator) >= 0; } /*************************************************************************************************** * * $DESCRIPTION Returns a string containing the currently selected rights. * $RETURN A string containing the rights currently selected. * **************************************************************************************************/ function string getCurrentRights() { local string rights; local string rightDef; local int index; // Check for each right if it is selected. for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightEnableInp[index].bChecked && rightDef != "") { if (rights == "") { rights = left(rightDef, instr(rightDef, separator)); } else { rights = rights $ separator $ left(rightDef, instr(rightDef, separator)); } } } // Return result. return rights; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); setRPCI(); // Account type selected. if (control == accountTypeList && eventType == DE_Click) { accountTypeSelected(); } // Reset account type info. if (control == resetButton && !resetButton.bDisabled && eventType == DE_Click) { loadAccountTypeInfo(NexgenSimpleListItem(accountTypeList.selectedItem).itemID); } // Save account type info. if (control == saveButton && !saveButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.updateAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID, accountNameInp.getValue(), getCurrentRights(), accountTitleInp.getValue(), accountPasswordInp.getValue()); } // Add account type. if (control == addAccountTypeButton && !addAccountTypeButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.addAccountType(accountNameInp.getValue(), getCurrentRights(), accountTitleInp.getValue(), accountPasswordInp.getValue()); } // Delete account type. if (control == deleteAccountTypeButton && !deleteAccountTypeButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.deleteAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID); } // Move account type up. if (control == moveUpButton && !moveUpButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.moveAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID, false); } // Move account type down. if (control == moveDownButton && !moveDownButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.moveAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID, true); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_AccountTypes) { loadAccountTypes(); accountTypeSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ b;€Œþi;d;L~õ ;f%;Ρ{_‚-Z –A ,U‚wA Q*|A QAf%-Z'\¥A w-ZA Qy* U /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPAbout * $VERSION 1.00 (10-3-2007 21:20) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen about control panel page. * **************************************************************************************************/ class NexgenRCPAbout extends NexgenPanel; #exec TEXTURE IMPORT NAME=logo FILE=Resources\logo.pcx GROUP="GFX" FLAGS=1 MIPS=Off /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { // Create layout & add components. createWindowRootRegion(); divideRegionV(2); addImageBox(Texture'logo'); divideRegionH(13); addLabel("Nexgen Server Controller", true, TA_Center); addLabel("version" @ left(class'NexgenUtil'.default.version, 4) @ "build" @ class'NexgenUtil'.default.internalVersion, , TA_Center); addLabel("Copyright © 2006-2008 Zeropoint productions", , TA_Center); addLabel("d.scheerens@gmail.com", , TA_Center); skipRegion(); addLabel("Development", true, TA_Center); addLabel("Daan \"Defrost\" Scheerens", , TA_Center); skipRegion(); addLabel("Credits and thanks to", true, TA_Center); addLabel("Mickaël \"ATHoS\" DEHEZ", , TA_Center); addLabel("Matthew \"MSuLL\" Sullivan", , TA_Center); addLabel("David \"The_Dave\" Schwartzstein", , TA_Center); addLabel("Zohar \"SuB\" Zada", , TA_Center); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ Z; 2âv;UO`’«-^U-úF-^âUcJK`_©fU-ýL_LUHcJK`DL!JK HN/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPrivateMsgDialog * $VERSION 1.00 (25-12-2006 17:06) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player is banned from the server. * **************************************************************************************************/ class NexgenPrivateMsgDialog extends NexgenPopupDialog; var UMenuLabelControl msgLabel[5]; // Message content label components. var UMenuLabelControl senderLabel; // Message sender label component. var UWindowSmallButton replyButton; // Reply button component. var localized string caption; // Caption to display on the dialog. var localized string senderText; // Message sender label text. var localized string messageText; // Received message label text. var localized string replyButtonText; // Received message label text. const firstLineWrapLen = 60; // Wrap lenght at the first message line. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); senderLabel = addPropertyLabel(cy, senderText, 64.0); msgLabel[0] = addPropertyLabel(cy, messageText, 64.0); msgLabel[1] = addLabel(cy); msgLabel[2] = addLabel(cy); msgLabel[3] = addLabel(cy); msgLabel[4] = addLabel(cy); replyButton = addButton(replyButtonText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM message The message that was received. * $PARAM sender Name of the player that has send the message. * $PARAM str3 Not used. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string message, optional string sender, optional string str3, optional string str4) { local int lineNum; local int index; local string remaining; local string lineStr; local int wrapLen; local int wrapPos; local string lineEntry; // Set sender label. senderLabel.setText(sender); // Set message labels. remaining = message $ newlineToken; while (remaining != "" && lineNum < arrayCount(msgLabel)) { // Split at new line tokens. index = instr(remaining, newlineToken); lineStr = left(remaining, index); remaining = mid(remaining, index + len(newlineToken)); // Split at wrap length. do { // Get wrap position. if (lineNum == 0) { wrapLen = firstLineWrapLen; } else { wrapLen = wrapLength; } wrapPos = getWrapPosition(lineStr, wrapLen); // Split line. if (wrapPos < 0) { lineEntry = lineStr; lineStr = ""; } else { lineEntry = left(lineStr, wrapPos); lineStr = mid(lineStr, wrapPos); } msgLabel[lineNum++].setText(class'NexgenUtil'.static.trim(lineEntry)); } until (lineStr == "" || lineNum >= arrayCount(msgLabel)); } // Clean empty lines. for (index = lineNum; index < arrayCount(msgLabel); index++) { msgLabel[index].setText(""); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Reply button. if (control == replyButton && eventType == DE_Click) { client.showPanel(class'NexgenRCPPrivateMsg'.default.panelIdentifier); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ Bj;A:Q ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2 k;³¦·;BêÓnÅ,£¤Šv3Týá¤á±˜¤á±˜Š M± Z¤á±˜– T]*)You have received a new private message.O]From:N] Message:M]Reply`"Oy2/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPopupFrame * $VERSION 1.03 (8-3-2008 16:12) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen GUI popup window class. An instance of this class defines the frame for a * popup window. * **************************************************************************************************/ class NexgenPopupFrame extends UMenuFramedWindow; var float windowWidth; // Width of the popup window frame (in pixels). var float windowHeight; // Height of the popup window frame (in pixels). var NexgenClient client; // Nexgen client instance. var GeneralConfig gc; // General client configuration. var ServerConfig sc; // Server specific client configuration. var string serverID; // Identification code of the server where has been connected to. /*************************************************************************************************** * * $DESCRIPTION Makes sure the popup frame will be properly setup. * $OVERRIDE * **************************************************************************************************/ function created() { super.created(); windowTitle = "Nexgen Server Controller v" $ left(class'NexgenUtil'.default.version, 4); bLeaveOnScreen = true; bMoving = true; } /*************************************************************************************************** * * $DESCRIPTION Changes the contents of the popup window. * $PARAM popupClass The dialog class used for the contents of the popup frame. * $PARAM str1 Dialog specific content data. * $PARAM str2 Dialog specific content data. * $PARAM str3 Dialog specific content data. * $PARAM str4 Dialog specific content data. * $REQUIRE the specified popup class exists and is a subclass of NexgenPopupDialog * **************************************************************************************************/ function showPopup(string popupClass, optional string str1, optional string str2, optional string str3, optional string str4) { local NexgenPopupDialog dialog; local Class dialogClass; // Get the object class. if (instr(popupClass, ".") < 0) { popupClass = class'NexgenUtil'.default.packageName $ "." $ popupClass; } dialogClass = class(DynamicLoadObject(popupClass, class'Class')); // Set popup contents. dialog = NexgenPopupDialog(createWindow(dialogClass, 4, 16, winWidth - 4, winHeight - 16)); dialog.gc = gc; dialog.sc = sc; dialog.serverID = serverID; dialog.client = client; dialog.setContent(str1, str2, str3, str4); clientArea = dialog; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ W‹/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPopupDialog * $VERSION 1.06 (21-10-2007 15:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Parent class for all popup dialogs. * **************************************************************************************************/ class NexgenPopupDialog extends UWindowDialogClientWindow; var NexgenClient client; // Nexgen client instance. var GeneralConfig gc; // General client configuration. var ServerConfig sc; // Server specific client configuration. var string serverID; // Identification code of the server. var bool hasCloseButton; // Automatically add a close button to the dialog? var UWindowSmallButton closeButton; // Default close button. var int wrapLength; // Insert a new line after this many characters. // NOTE: This value is only an estimation for word wrapping. const minWrapRetain = 0.60; // Minimum size of the text before wrapping inside words // will occur. const wrapChars = " -,."; // Preferred characters where wrapping should occur. var bool autoCloseControlPanel; // Automatically close the control panel once the dialog is // shown? // Component positioning. All values are measured in pixels. var float nextButtonPos; // Next horizontal position of a button that is to be added. var float borderSize; // Distance between objects on the dialog and its borders. var float labelHeight; // Height of label objects. var float editControlHeight; // Height of edit control objects. var float editControlLabelVOffset; // Vertical offset of labels relative to their edit control. var float buttonPanelBorderSize; // Distance between the dialog borders and the button panel. var float buttonPanelHeight; // The height of the button panel. var float buttonHeight; // Height of buttons on this dialog. var float buttonWidth; // Default width of a button on this dialog. var float buttonSpace; // Space between two buttons. const newLineToken = "\\n"; // Token used to detect new lines in texts. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE hasCloseButton ? closeButton != none : true * $OVERRIDE * **************************************************************************************************/ function created() { local UMenuLabelControl label; local UWindowRootWindow rootWin; local UWindowWindow win; super.created(); nextButtonPos = winWidth - buttonSpace - buttonPanelBorderSize; // Automatically add close button? if (hasCloseButton) { closeButton = addButton("Close"); } // Automatically close control panel? if (autoCloseControlPanel) { // Yes, iterate over each window. rootWin = WindowConsole(getPlayerOwner().player.console).root; if (rootWin != none) { win = rootWin.firstChildWindow; while (win != none) { // Window is a control panel? if (win.isA('NexgenMainFrame')) { // Yes, close it. win.close(); } // Continue with next window. win = win.nextSiblingWindow; } } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * This function will only check if the close button has been clicked. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); if (control == closeButton && eventType == DE_Click) { close(); } } /*************************************************************************************************** * * $DESCRIPTION Adds a new button to the dialog. The button will be added to the button panel of * the dialog and will be automatically positioned. * $PARAM text Text to display on the button. * $PARAM width Width of the button in pixels. * $RETURN The button that has been added to the button panel of this dialog. * $ENSURE result != none * **************************************************************************************************/ function UWindowSmallButton addButton(string text, optional int width) { local float cx, cy, cw, ch; local UWindowSmallButton newButton; if (width > 0.0) { cw = width; } else { cw = buttonWidth; } ch = buttonHeight; cx = nextButtonPos - cw; cy = winHeight - buttonPanelHeight - buttonPanelBorderSize + (buttonPanelHeight - ch) / 2.0 - 3; newButton = UWindowSmallButton(createControl(class'UWindowSmallButton', cx, cy, cw, ch)); newButton.setText(text); nextButtonPos -= cw + buttonSpace; return newButton; } /*************************************************************************************************** * * $DESCRIPTION Adds a new label component to the dialog. * $PARAM yPos Vertical position on the dialog where the text will be added. * $REQUIRE yPos >= 0 * $RETURN The label that has been added to the dialog. * $ENSURE result != none * **************************************************************************************************/ function UMenuLabelControl addLabel(out float yPos) { local float cx, cy, cw, ch; // Initialze position & dimensions. cx = borderSize; cy = yPos; cw = winWidth - 2.0 * borderSize; ch = labelHeight; yPos += ch; // Create label. return UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); } /*************************************************************************************************** * * $DESCRIPTION Adds a new label component to the dialog with a property description label in * front of the label. * $PARAM yPos Vertical position on the dialog where the text will be added. * $PARAM text Property name / description. * $PARAM labelWidth Width of the property name label (in pixels). * $REQUIRE yPos >= 0 && labelWidth > 0 * $RETURN The label that has been added to the dialog. * $ENSURE result != none * **************************************************************************************************/ function UMenuLabelControl addPropertyLabel(out float yPos, string text, float labelWidth) { local float cx, cy, cw, ch; local UMenuLabelControl label; cx = borderSize; cy = yPos; cw = labelWidth; ch = labelHeight; label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(text); label.setFont(F_Bold); cx += labelWidth; cw = winWidth - 2.0 * borderSize - labelWidth; yPos += ch; return UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); } /*************************************************************************************************** * * $DESCRIPTION Adds a text to the dialog. This function detects new line tokens and writes the * after the token on a new line. Note that it doesn't support word wrapping. The * fontType and align properties will be maintained for all lines written. * $PARAM text The text to write to the dialog. * $PARAM yPos Vertical position on the dialog where the text will be added. * $PARAM fontType Type of font used to display the text. * $PARAM align Horizontal alignment of the text to add. * $PARAM wrapLen Maximum characters on a line. For word wrapping. * $REQUIRE yPos >= 0 && (fontType == F_Normal || fontType == F_Bold) && * (align == TA_Left || align == TA_Center || align == TA_Right) * **************************************************************************************************/ function addText(string text, out float yPos, int fontType, TextAlign align, optional int wrapLen) { local float cx, cy, cw, ch; local UMenuLabelControl label; local string textStr; local string lineStr; local int newLinePos; local string wrapStr; local int wrapPos; // Set proper wrapping length. if (wrapLen <= 0) { wrapLen = wrapLength; } // Initialze position & dimensions. cx = borderSize; cy = yPos; cw = winWidth - 2.0 * borderSize; ch = labelHeight; // Create a label for each line in the text string. textStr = text; while (textStr != "") { // Get text for the current line. newLinePos = instr(textStr, newLineToken); if (newLinePos < 0) { lineStr = textStr; textStr = ""; } else { lineStr = left(textStr, newLinePos); textStr = mid(textStr, newLinePos + len(newLineToken)); } // Word wrapping for the current line. while (lineStr != "") { wrapPos = getWrapPosition(lineStr, wrapLen); // Wrap current line. if (wrapPos < 0) { wrapStr = lineStr; lineStr = ""; } else { wrapStr = left(lineStr, wrapPos + 1); lineStr = mid(lineStr, wrapPos + 1); } // Create label. label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(wrapStr); label.setFont(fontType); label.align = align; // Update vertical offset. cy += ch; } } // Update vertical offset. yPos = cy; } /*************************************************************************************************** * * $DESCRIPTION Gets the first position in the given string where a new line should be inserted * for word wrapping. * $PARAM text The string for which the first wrapping position is to be determined. * $PARAM maxChars Maximum characters allowed on a line. * $REQUIRE maxChars > 0 * $RETURN The first position in the specified string where word wrapping should occur, or * -1 if no wrapping is necessary. * $ENSURE result >= 0 && result < len(text) && result < maxChars || result == -1 * **************************************************************************************************/ function int getWrapPosition(string text, int maxChars) { local int index; // Find wrap position. if (len(text) < maxChars) { // No wrapping necessary. index = -1; } else { // Start at the back. index = maxChars - 1; while ((instr(wrapChars, mid(text, index, 1)) < 0) && (index >= minWrapRetain * maxChars)) { index--; } // Wrap at least after 1 character. index = max(1, index); } return index; } /*************************************************************************************************** * * $DESCRIPTION Adds an empty line in this dialog. No control object is created, only the value of * yPos will be increased. * $PARAM yPos Vertical position of the new line on this dialog. * $ENSURE new.yPos >= old.yPos * **************************************************************************************************/ function addNewLine(out float yPos) { yPos += labelHeight; } /*************************************************************************************************** * * $DESCRIPTION Adds a new edit control object to this dialog. * $PARAM yPos Vertical position of the edit control on this dialog. * $PARAM labelText Label to display before the edit control. * $PARAM labelWidth Width of the label to display * $REQUIRE yPos >= 0 && (labelText != "" ? labelWidth > 0 : true) * $RETURN The edit control object created for this dialog. * $ENSURE result != none * **************************************************************************************************/ function UWindowEditControl addEditControl(out float yPos, optional string labelText, optional float labelWidth) { local float cx, cy, cw, ch; local UMenuLabelControl label; local UWindowEditControl editControl; // Set control position & dimension. ch = editControlHeight; cx = borderSize; cy = yPos; if (labelText != "") { // Add a label before the edit control. cw = labelWidth; cy += editControlLabelVOffset; label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(labelText); label.setFont(F_Bold); cx += labelWidth; cy = yPos; cw = winWidth - 2.0 * borderSize - labelWidth; } else { cw = winWidth - 2.0 * borderSize; } yPos = cy + ch; // Create & setup the edit control. editControl = UWindowEditControl(createControl(class'UWindowEditControl', cx, cy, cw, ch)); editControl.editBoxWidth = cw; editControl.setMaxLength(250); // Return the control. return editControl; } /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ local float cx, cy, cw, ch; super.paint(c, x, y); // Draw the button panel. cx = buttonPanelBorderSize; cy = winHeight - buttonPanelHeight - buttonPanelBorderSize; cw = winWidth - buttonPanelBorderSize * 2; ch = buttonPanelHeight; drawUpBevel(c, cx, cy, cw, ch, getLookAndFeelTexture()); } /*************************************************************************************************** * * $DESCRIPTION Closes the dialog. * $PARAM bByParent The close call was issued by the parent of the dialog. * $OVERRIDE * **************************************************************************************************/ function close(optional bool bByParent) { local UWindowRootWindow rootWin; local UWindowWindow win; local bool bWindowVisible; // Check if there is another visible window. rootWin = WindowConsole(getPlayerOwner().player.console).root; if (rootWin != none) { win = rootWin.firstChildWindow; while (!bWindowVisible && win != none) { // Current window visible? bWindowVisible = win != parentWindow && win.windowIsVisible() && win.bLeaveOnscreen; // Continue with next window. win = win.nextSiblingWindow; } } // Close the window. if (!bWindowVisible) { WindowConsole(getPlayerOwner().player.console).closeUWindow(); } super.close(bByParent); } /*************************************************************************************************** * * $DESCRIPTION Parses a boolean string value. * $PARAM str The string representation of a boolean value. * $RETURN True if the string equals "true" (case insensitive), false otherwise. * **************************************************************************************************/ function bool parseBool(string str) { return str ~= "true"; } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM str1 Dialog specific content data. * $PARAM str2 Dialog specific content data. * $PARAM str3 Dialog specific content data. * $PARAM str4 Dialog specific content data. * $ABSTRACT * **************************************************************************************************/ function setContent(optional string str1, optional string str2, optional string str3, optional string str4); /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ `; 4D¡/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlugin * $VERSION 1.12 (14-3-2008 0:23) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen controller plugin. This is the base class used for nexgen plugins. The * plugin is being setup from here. An instance of this class also receives events * emitted by the Nexgen Server Controller. * **************************************************************************************************/ class NexgenPlugin extends Info abstract; var NexgenController control; // The Nexgen Server Controller instance. var String pluginName; // Name of the plugin. var String pluginAuthor; // Who developed the plugin. var String pluginVersion; // Plugin version description. /*************************************************************************************************** * * $DESCRIPTION Starts up the plugin. First the Nexgen controller is located. Once it has been * found initialize() is called. If the initialization was successfull the plugin * will be registered at the server controller. If any error occurs during the load * process the plugin will be destroyed. * **************************************************************************************************/ function preBeginPlay() { // Locate Nexgen Controller. foreach allActors(class'NexgenController', control) { if (control != none) { break; } } // Quit on error. if (control == none) { warn("CRITICAL EXCEPTION, Nexgen controller not detected."); destroy(); return; } // Don't load on special mode. if (control.bSpecialMode) { destroy(); return; } // Initialize plugin. control.nscLog(control.lng.format(control.lng.loadingPluginMsg, pluginName, pluginVersion)); if (initialize()) { // Register plugin. if (!control.registerPlugin(self)) { // Failed to register, destroy instance. control.nscLog(control.lng.format(control.lng.regFailedMsg, string(self))); destroy(); } } else { // Initialization failed, destroy instance. control.nscLog(control.lng.format(control.lng.initFailedMsg, string(self))); destroy(); } // Make sure the checksums are up to date. control.sConf.updateChecksum(); control.sConf.staticChecksum = control.sConf.calcStaticChecksum(); } /*************************************************************************************************** * * $DESCRIPTION Initializes the plugin. Note that if this function returns false the plugin will * be destroyed and is not to be used anywhere. * $RETURN True if the initialization succeeded, false if it failed. * **************************************************************************************************/ function bool initialize() { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called when a new client has been created. Use this function to setup the new * client with your own extensions (in order to support the plugin). * $PARAM client The client that was just created. * $REQUIRE client != none * **************************************************************************************************/ function clientCreated(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a client is attempting to login. This allows to plugin to accept or * reject the login request. If the function returns false the login request will be * rejected (player will be disconnected). Please make sure the reason parameter * is set in that case, as it will be written to the log. * $PARAM client Client that is requesting to login to the server. * $PARAM rejectType Reject type identification code. * $PARAM reason Message describing why the login is rejected. * $REQUIRE client != none * $RETURN True if the login request is accepted, false if it should be rejected. * $ENSURE result == false ? new.reason != "" : true * **************************************************************************************************/ function bool checkLogin(NexgenClient client, out name rejectType, out string reason) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called whenever the login request of a player has been rejected and allows the * plugin to modify the behaviour. * $PARAM client The client that was denied access to the server. * $PARAM rejectType Reject type identification code. * $PARAM reason Reason why the player was rejected from the server. * $PARAM popupWindowClass Class name of the popup window that is to be shown at the client. * $PARAM popupArgs Optional arguments for the popup window. Note you may have to * explicitly reset them if you change the popupWindowClass. * $REQUIRE client != none * **************************************************************************************************/ function modifyLoginReject(NexgenClient client, out name rejectType, out string reason, out string popupWindowClass, out string popupArgs[4]) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called whenever a player has joined the game (after its login has been accepted). * $PARAM client The player that has joined the game. * $REQUIRE client != none * **************************************************************************************************/ function playerJoined(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called whenever a client has finished its initialisation process. During this * process things such as the remote control window are created. So only after the * client is fully initialized all functions can be safely called. * $PARAM client The client that has finished initializing. * $REQUIRE client != none * **************************************************************************************************/ function clientInitialized(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a player (re)spawns and allows us to modify the player. * $PARAM client The client of the player that was respawned. * $REQUIRE client != none * **************************************************************************************************/ function playerRespawned(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called if a player has left the server. * $PARAM client The player that has left the game. * $REQUIRE client != none * **************************************************************************************************/ function playerLeft(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has switched to another team. * $PARAM client The client that has changed team. * $REQUIRE client != none * **************************************************************************************************/ function playerTeamChanged(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has changed his or her name. * $PARAM client The client that has changed name. * $REQUIRE client != none * **************************************************************************************************/ function playerNameChanged(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the game has started. * **************************************************************************************************/ function gameStarted() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the game has ended. * **************************************************************************************************/ function gameEnded() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called to check if the specified message should be send to the given receiver. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM msg The message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $PARAM type Type of the message that is to be send. * $RETURN True if the message should be send, false if it should be suppressed. * **************************************************************************************************/ function bool mutatorBroadcastMessage(Actor sender, Pawn receiver, out coerce string msg, optional bool bBeep, out optional name type) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called to check if the specified team message should be send to the given * receiver. * is called if a message is send to player. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM pri Player replication info of the sending player. * $PARAM s The message that is to be send. * $PARAM type Type of the message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $RETURN True if the message should be send, false if it should be suppressed. * **************************************************************************************************/ function bool mutatorTeamMessage(Actor sender, Pawn receiver, PlayerReplicationInfo pri, coerce string s, name type, optional bool bBeep) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called to check if the given localized message should be send to the specified * receiver. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM message The class of the localized message that is to be send. * $PARAM switch Optional message switch argument. * $PARAM relatedPRI_1 PlayerReplicationInfo of a player that is related to the message. * $PARAM relatedPRI_2 PlayerReplicationInfo of a player that is related to the message. * $PARAM optionalObject Optional object used to construct the message string. * $REQUIRE message != none * $RETURN True if the message should be send, false if it should be suppressed. * **************************************************************************************************/ function bool mutatorBroadcastLocalizedMessage(Actor sender, Pawn receiver, out class message, out optional int switch, out optional PlayerReplicationInfo relatedPRI_1, out optional PlayerReplicationInfo relatedPRI_2, out optional Object optionalObject) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called when a player has send a mutate call to the server. * $PARAM mutateString Mutator specific string (indicates the action to perform). * $PARAM sender Player that has send the message. * **************************************************************************************************/ function mutate(string mutateString, PlayerPawn sender) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a player attempts to login to the server. Allows mutators to modify * some of the login parameters. * $PARAM spawnClass The PlayerPawn class to use for the player. * $PARAM portal Name of the portal where the player wishes to spawn. * $PARAM option Login option parameters. * **************************************************************************************************/ function modifyLogin(out class spawnClass, out string portal, out string options) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a player (re)spawns and allows us to modify the player. * $PARAM other The pawn/player that has (re)spawned. * $REQUIRE other != none * **************************************************************************************************/ function modifyPlayer(Pawn other) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a pawn takes damage. * $PARAM actualDamage The amount of damage sustained by the pawn. * $PARAM victim Pawn that has become victim of the damage. * $PARAM instigatedBy The pawn that has instigated the damage to the victim. * $PARAM hitLocation Location where the damage was dealt. * $PARAM momentum Momentum of the damage that has been dealt. * $PARAM damageType Type of damage dealt to the victim. * $REQUIRE victim != none * **************************************************************************************************/ function mutatorTakeDamage(out int actualDamage, Pawn victim, Pawn instigatedBy, out vector hitLocation, out vector momentum, name damageType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the server wants to check if a players death should be prevented. * $PARAM victim The pawn that was killed. * $PARAM killer The pawn that has killed the victim. * $PARAM damageType Type of damage dealt to the victim. * $PARAM hitLocation Location where the damage was dealt. * $RETURN True if the players death should be prevented, false if not. * **************************************************************************************************/ function bool preventDeath(Pawn victim, Pawn killer, name damageType, vector hitLocation) { // To implement in subclass. return false; } /*************************************************************************************************** * * $DESCRIPTION Called when a player was killed by another player. * $PARAM killer The pawn that killed the other pawn. Might be none. * $PARAM victim Pawn that was the victim. * **************************************************************************************************/ function scoreKill(Pawn killer, Pawn victim) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this plugin that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * **************************************************************************************************/ function configChanged(byte configType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { // To implement in subclass. } n; 15h”/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerListBox * $VERSION 1.03 (20-10-2007 14:45) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Player listbox GUI component. * **************************************************************************************************/ class NexgenPlayerListBox extends UWindowListBox; #exec TEXTURE IMPORT NAME=noCountry FILE=Resources\NoCountry.pcx GROUP="GFX" FLAGS=2 MIPS=Off var color baseColor; // Default color used to render textures in their original color. var color selectColor; // Background color of selected items. var color teamColor[6]; // Team colors used for drawing the items text. var bool bShowCountryFlag; // Indicates if little country flags should be displayed. /*************************************************************************************************** * * $DESCRIPTION Renders the specified listbox item. * $PARAM c The canvas object on which the rendering will be performed. * $PARAM item Item to render. * $PARAM x Horizontal offset on the canvas. * $PARAM y Vertical offset on the canvas. * $PARAM w Width of the item that is to be rendered. * $PARAM h Height of the item that is to be rendered. * $REQUIRE c != none && item != none * $OVERRIDE * **************************************************************************************************/ function drawItem(Canvas c, UWindowList item, float x, float y, float w, float h) { local int offsetX; local texture flagTex; local color backgroundColor; // Draw background. backgroundColor = getBackgroundColor(NexgenPlayerList(item)); if (backgroundColor != baseColor) { c.drawColor = backgroundColor; drawStretchedTexture(c, x, y, w, h - 1, Texture'WhiteTexture'); } // Draw country flag. offsetX = 2; if (bShowCountryFlag) { c.drawColor = baseColor; flagTex = NexgenPlayerList(item).getFlagTex(); if (flagTex == none) { flagTex = texture'noCountry'; } drawClippedTexture(c, x + offsetX, y + 1, flagTex); offsetX += 18; } // Draw text. c.drawColor = getDisplayColor(NexgenPlayerList(item)); c.font = getDisplayFont(NexgenPlayerList(item)); clipText(c, x + offsetX, y, getDisplayText(NexgenPlayerList(item))); } /*************************************************************************************************** * * $DESCRIPTION Called when an item was double clicked on. * $PARAM item The item which was double clicked. * $REQUIRE item != none * $OVERRIDE * **************************************************************************************************/ function doubleClickItem(UWindowListBoxItem item) { if (notifyWindow != none) { notifyWindow.notify(self, DE_DoubleClick); } } /*************************************************************************************************** * * $DESCRIPTION Returns the font in which the text should be displayed for a list item. * $PARAM item The item for which its display font has to be determined. * $REQUIRE item != none * $RETURN The font in which the text should be displayed for the specified item. * **************************************************************************************************/ function font getDisplayFont(NexgenPlayerList item) { return root.fonts[F_Bold]; } /*************************************************************************************************** * * $DESCRIPTION Returns the text displayed for a list item. * $PARAM item The item for which its display text has to be determined. * $REQUIRE item != none * $RETURN The text that should be displayed for the specified item in the listbox. * **************************************************************************************************/ function string getDisplayText(NexgenPlayerList item) { return "[" $ item.pTitle $ "] " $ item.pName; } /*************************************************************************************************** * * $DESCRIPTION Returns the color of the background in which the text should be displayed for a * list item. * $PARAM item The item for which its background color has to be determined. * $REQUIRE item != none * $RETURN The background color of the the specified item. * **************************************************************************************************/ function color getBackgroundColor(NexgenPlayerList item) { if (item.bSelected) { return selectColor; } else { return baseColor; } } /*************************************************************************************************** * * $DESCRIPTION Returns the color in which the text should be displayed for a list item. * $PARAM item The item for which its display color has to be determined. * $REQUIRE item != none * $RETURN The color in which the text should be displayed for the specified item. * **************************************************************************************************/ function color getDisplayColor(NexgenPlayerList item) { return teamColor[item.pTeam]; } /*************************************************************************************************** * * $DESCRIPTION Adds a new player to the list. * $RETURN The player item that was added to the list. * $ENSURE result != none * **************************************************************************************************/ function NexgenPlayerList addPlayer() { return NexgenPlayerList(items.append(listClass)); } /*************************************************************************************************** * * $DESCRIPTION Removes the player with the specified player code from the list. * $PARAM playerNum The player to remove. * $REQUIRE playerNum >= 0 * $ENSURE getPlayer(playerNum) == none * **************************************************************************************************/ function removePlayer(int playerNum) { local NexgenPlayerList item; // Search for player. for (item = NexgenPlayerList(items); item != none; item = NexgenPlayerList(item.next)) { if (item.pNum == playerNum) { item.remove(); } } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the player item with the specified player number. * $PARAM playerNum Indicates the player to return. * $REQUIRE playerNum >= 0 * $RETURN The player item that has the specified player number, or none if there is no * player item with the specified player number. * **************************************************************************************************/ function NexgenPlayerList getPlayer(int playerNum) { local NexgenPlayerList item; // Search for player. for (item = NexgenPlayerList(items); item != none; item = NexgenPlayerList(item.next)) { if (item.pNum == playerNum) { return item; } } // Player not found, return none. return none; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the player item with the specified player number. * $PARAM playerNum Indicates the player to return. * $REQUIRE playerNum >= 0 * $RETURN The player item that has the specified player number, or none if there is no * player item with the specified player number. * **************************************************************************************************/ function NexgenPlayerList getPlayerByID(string clientID) { local NexgenPlayerList item; // Search for player. for (item = NexgenPlayerList(items); item != none; item = NexgenPlayerList(item.next)) { if (item.pClientID ~= clientID) { return item; } } // Player not found, return none. return none; } /*************************************************************************************************** * * $DESCRIPTION Moves the selected item to the specified player listbox. * $PARAM target The player listbox where the currently selected item should be moved to. * $REQUIRE target != none * $ENSURE selectedItem == none * **************************************************************************************************/ function moveSelectedPlayerTo(NexgenPlayerListBox target) { local NexgenPlayerList item; if (selectedItem != none) { item = target.addPlayer(); NexgenPlayerList(selectedItem).copyTo(item); selectedItem.remove(); selectedItem = none; target.setSelectedItem(item); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ c?/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerList * $VERSION 1.04 (24-10-2007 20:25) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Player list item description class. Stores the basic player attributes, such as * name, team and clientID. * **************************************************************************************************/ class NexgenPlayerList extends UWindowListBoxItem; var int pNum; // Player num. var string pName; // Name used by the player. var string pTitle; // Title of the players account. var string pIPAddress; // IP Address. var string pClientID; // Client identification code. var string pCountry; // Country code based on the IP Address. var byte pTeam; // Team number. var texture flagTex; // Flag texture for the country. var bool bFlagTexSet; // Whether or not the flag texture has been set. const specTeam = 5; // Team used to indicate spectators. /*************************************************************************************************** * * $DESCRIPTION Sets the flag texture based on the current country code of the player. If the * country code is invalid the flag texture will be set to none. * $ENSURE bFlagTexSet * **************************************************************************************************/ function setFlagTex() { if (len(pCountry) == 2) { flagTex = texture(dynamicLoadObject(class'NexgenUtil'.default.countryFlagsPkg $ "." $ pCountry, class'Texture')); } else { flagTex = none; } bFlagTexSet = true; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the flag texture associated with the country code of the player. Note * that if the country code was changed setFlagTex() has to be called first in order * to update the texture. * $RETURN A texture of the country flag of the player, might be none if an invalid country * code is used. * **************************************************************************************************/ function texture getFlagTex() { if (!bFlagTexSet) { setFlagTex(); } return flagTex; } /*************************************************************************************************** * * $DESCRIPTION Copies the attributes of this player list item to the specified player list item. * $PARAM target The player list item where the attributes should be copied to. * $REQUIRE target != none * **************************************************************************************************/ function copyTo(NexgenPlayerList target) { target.pNum = self.pNum; target.pName = self.pName; target.pTitle = self.pTitle; target.pIPAddress = self.pIPAddress; target.pClientID = self.pClientID; target.pCountry = self.pCountry; target.pTeam = self.pTeam; target.flagTex = self.flagTex; target.bFlagTexSet = self.bFlagTexSet; } /*************************************************************************************************** * * $DESCRIPTION Checks whether this player is a spectator. * $RETURN True if this player is a spectator, false if not. * **************************************************************************************************/ function bool isSpectator() { return pTeam == specTeam; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ su/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerData * $VERSION 1.00 (24-11-2007 15:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Saved player data support class. An instance of this class can be used to store * attributes of a leaving player, which can later be retrieved once the player * reconnects. * **************************************************************************************************/ class NexgenPlayerData extends info; struct AttributeEntry { // Attribute data structure. var string name; // Name of the attribute. var string value; // Value of the attribute. }; var private AttributeEntry attributes[64]; // Attributes for this player. var string clientID; // ClientID of the player. var NexgenPlayerData next; // Next player data instance. /*************************************************************************************************** * * $DESCRIPTION Removes all attributes stored in this NexgenPlayerData instance. * **************************************************************************************************/ function clearData() { local int index; // Clear entries. while (index < arrayCount(attributes)) { attributes[index].name = ""; attributes[index].value = ""; index++; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function string get(string name, optional string defaultValue) { local int index; local bool bFound; // Locate index of attribute. while (!bFound && index < arrayCount(attributes)) { if (attributes[index].name ~= name) { bFound = true; } else { index++; } } // Return attribute value, or the default value if the attribute wasn't found. if (bFound) { return attributes[index].value; } else { return defaultValue; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the boolean value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function bool getBool(string name, optional bool defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return value ~= string(true); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the byte value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function byte getByte(string name, optional byte defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return byte(value); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the integer value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function int getInt(string name, optional int defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return int(value); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the floating point value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function float getFloat(string name, optional float defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return float(value); } } /*************************************************************************************************** * * $DESCRIPTION Updates the specified attribute with the given value. * $PARAM name Name of the attribute that is to be stored. * $PARAM value (New) Value of the attribute to store. * $REQUIRE name != "" * $RETURN True if the attribute was updated/save, false if not. * $ENSURE result == true ? new.get(name) == value : true * **************************************************************************************************/ function bool set(string name, coerce string value) { local int index; local bool bFound; // Check if attribute already exists. while (!bFound && index < arrayCount(attributes)) { if (attributes[index].name ~= name) { bFound = true; } else { index++; } } // It doesn't, search for an empty entry. if (!bFound) { index = 0; while (!bFound && index < arrayCount(attributes)) { if (attributes[index].name == "") { bFound = true; } else { index++; } } } // Save attribute. if (bFound) { attributes[index].name = name; attributes[index].value = value; } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ q /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerACListItem * $VERSION 1.01 (21-10-2007 11:24) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Online user acccount list item description class. * **************************************************************************************************/ class NexgenPlayerACListItem extends NexgenPlayerList; var bool bHasAccount; var byte accountNum; A€[íw;g>¹ L-dþI![4-dígfIJDfIg* ò\hg'¢5òhr\*W3\ {h g%/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerACListBox * $VERSION 1.00 (20-10-2007 15:47) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Online player account listbox GUI component. * **************************************************************************************************/ class NexgenPlayerACListBox extends NexgenPlayerListBox; var color accountColor; var color selectedAccountColor; /*************************************************************************************************** * * $DESCRIPTION Returns the font in which the text should be displayed for a list item. * $PARAM item The item for which its display font has to be determined. * $REQUIRE item != none * $RETURN The font in which the text should be displayed for the specified item. * $OVERRIDE * **************************************************************************************************/ function font getDisplayFont(NexgenPlayerList item) { if (NexgenPlayerACListItem(item).bHasAccount) { return root.fonts[F_Bold]; } else { return root.fonts[F_Normal]; } } /*************************************************************************************************** * * $DESCRIPTION Returns the color of the background in which the text should be displayed for a * list item. * $PARAM item The item for which its background color has to be determined. * $REQUIRE item != none * $RETURN The background color of the the specified item. * $OVERRIDE * **************************************************************************************************/ function color getBackgroundColor(NexgenPlayerList item) { if (item.bSelected) { if (NexgenPlayerACListItem(item).bHasAccount) { return selectedAccountColor; } else { return selectColor; } } else { return baseColor; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ h; 60{M/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPasswordDialog * $VERSION 1.01 (23-12-2006 14:15) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player has entered an invalid password. * **************************************************************************************************/ class NexgenPasswordDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var UWindowSmallButton spectatorButton; // Spectator button component. var UWindowEditControl passwordInput; // Password input field component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string passwordText; // Label to display before the password field. var localized string reconnectText; // Text to display on the reconnect button. var localized string spectatorText; // Text to display on the spectator button. const SSTR_ServerPassword = "Password"; // Server password setting string. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const reconnectCommand = "Reconnect"; // Console command for reconnecting. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE reconnectButton != none && spectatorButton != none && passwordInput != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); passwordInput = addEditControl(cy, passwordText, 64.0); spectatorButton = addButton(spectatorText, 64.0); reconnectButton = addButton(reconnectText, 64.0); // Set component properties. passwordInput.setMaxLength(24); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM allowSpecs Indicates if reconnecting as spectator should be allowed. * $PARAM str2 Not used. * $PARAM str3 Not used. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string allowSpecs, optional string str2, optional string str3, optional string str4) { // sc.get(serverID, NexgenClient.SSTR_ServerPassword); // Bah, doesn't work! passwordInput.setValue(sc.get(serverID, SSTR_ServerPassword)); spectatorButton.bDisabled = !parseBool(allowSpecs); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { // Update password. sc.visitServer(serverID); sc.set(serverID, SSTR_ServerPassword, passwordInput.getValue()); sc.saveConfig(); // Reconnect. getplayerowner().consoleCommand(reconnectCommand); close(); } // Spectator button. if (control == spectatorButton && eventType == DE_Click && !spectatorButton.bDisabled) { getplayerowner().updateURL(SSTR_OverrideClass, spectatorClass, true); getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ Bz;h9J ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2{;ÑÖ’ÙBêÓnÅÙÊM¶ÙÊM¶“tµðÙÊM¶Ž> £Y×7XÃŽ> £YŽ> £Y“tµðÙÊM¶×7XÓtµð– T]$#Failed to login: invalid password.]ÆDThis server is password protected. You have either entered an invalid password or no password at all. Please enter the password for this server and click reconnect to login with the new password.Q] Password:\] ReconnectX] Spectatory; 4y“/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPanelContainer * $VERSION 1.03 (11-3-2008 21:31) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page container. * **************************************************************************************************/ class NexgenPanelContainer extends NexgenPanel; var UWindowPageControl pages; // Page control for panels added on this container. /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this panel. * $OVERRIDE * **************************************************************************************************/ function created() { super.created(); pages = UWindowPageControl(createWindow(class'UWindowPageControl', 0, 0, winWidth - 2, winHeight - 3)); } /*************************************************************************************************** * * $DESCRIPTION Retrieves the panel with the specified name. * $PARAM panelName Name of the panel that is to be returned. * $REQUIRE panelName != "" * $RETURN The panel that was requested or none if the panel wasn't found. * **************************************************************************************************/ function NexgenPanel getPanel(string panelName) { local UWindowPageControlPage pageControl; local NexgenPanel panel; local bool bFound; // Search for panel. pageControl = pages.firstPage(); while (!bFound && pageControl != none) { panel = NexgenPanel(pageControl.page); // Is this the one we are looking for? if (panel != none) { if (panel.panelIdentifier ~= panelName) { // Yeah, stop looking and return result. bFound = true; } else if (panel.isA('NexgenPanelContainer')) { // No, but it is a panel container so it might contain the one we're looking for. panel = NexgenPanelContainer(panel).getPanel(panelName); bFound = panel != none; } } // Continue with next page if not found yet. if (!bFound) { pageControl = pageControl.nextPage(); } } // Return result. if (bFound) { return panel; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Adds a new NexgenPanel to the container or one of its subcontainers. To specify * a specific parent use the parent parameter to indicate the path, e.g. * "plugin,settings". If an invalid path is specified the panel won't be created. * $PARAM title Text to display in the tab header. * $PARAM panelClass Type of NexgenPanel to add/create. * $PARAM identifier Identifier to assign to the new panel. * $PARAM parent Path where to the parent of the new panel. * $REQUIRE panelClass != none * $RETURN The panel that was created and added to the container, or none if an invalid path * to the parent container was specified. * **************************************************************************************************/ function NexgenPanel addPanel(string title, class panelClass, optional string identifier, optional string parent) { local UWindowPageControlPage pageControl; local NexgenPanel newPanel; local string parentPanel; local string subPanels; local bool bFound; local NexgenPanelContainer container; // Add to subpanel? if (parent != "") { // Get local parent name. if (instr(parent, separator) > 0) { parentPanel = left(parent, instr(parent, separator)); subPanels = mid(parent, instr(parent, separator) + 1); } else { parentPanel = parent; } // Locate local parent. pageControl = pages.firstPage(); while (!bFound && pageControl != none) { container = NexgenPanelContainer(pageControl.page); if (container != none && container.panelIdentifier ~= parentPanel) { bFound = true; } else { pageControl = pageControl.nextPage(); } } // Delegate action. if (bFound) { // NOTE: Recursion can be avoided here, but the performance gain is insignificant. newPanel = container.addPanel(title, panelClass, identifier, subPanels); } } else { // Create panel. pageControl = pages.addPage(title, panelClass); if (pageControl != none) { newPanel = NexgenPanel(pageControl.page); if (identifier != "") { newPanel.panelIdentifier = identifier; } newPanel.client = self.client; newPanel.setContent(); } } // Return created panel. return newPanel; } /*************************************************************************************************** * * $DESCRIPTION Selects the panel with the specified name. * $PARAM panelName The name of the panel that is to be selected. * $RETURN True if the panel was selected, false if it wasn't found. * **************************************************************************************************/ function bool selectPanel(string panelName) { local UWindowPageControlPage pageControl; local NexgenPanel panel; local bool bFound; pageControl = pages.firstPage(); while (!bFound && pageControl != none) { panel = NexgenPanel(pageControl.page); // Check panel. if (panel.panelIdentifier ~= panelName) { // Panel found, select it. bFound = true; } else if (panel.isA('NexgenPanelContainer')) { // No, but page is a NexgenPanelContainer, so it may include the panel. bFound = NexgenPanelContainer(panel).selectPanel(panelName); } // Panel found? if (bFound) { // Panel found, select it. pages.gotoTab(pageControl, true); } else { // No, continue with next page. pageControl = pageControl.nextPage(); } } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Notifies the sub panels on this panel that the server configuration has been * updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).configChanged(configType); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).gameInfoChanged(infoType); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * $OVERRIDE * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).playerEvent(playerNum, eventType, args); } } } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).notifyEvent(type, arguments); } } } ct/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPanel * $VERSION 1.08 (11-3-2008 21:30) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page. * **************************************************************************************************/ class NexgenPanel extends NexgenContentPanel; var string panelIdentifier; // Unique panel identifier. var NexgenClient client; // Nexgen client instance. var float panelHeight; // Desired panel height. /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this panel. This function is automatically called after * the panel has been successfully created and all variables have been set. This * applies in particular for the client variable which is often required to setup the * contents of the panel. * **************************************************************************************************/ function setContent() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * **************************************************************************************************/ function configChanged(byte configType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * **************************************************************************************************/ function gameInfoChanged(byte infoType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, name eventType, optional string args) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Adds a new player to the specified player listbox. * $PARAM list The player listbox where the new player should be added to. * $PARAM playerNum The player code of the new player to add. * $PARAM args Player joined event arguments. * $REQUIRE list != none && playerNum >= 0 * $RETURN The player item that was added to the list. * $ENSURE result != none * **************************************************************************************************/ function NexgenPlayerList addPlayerToList(NexgenPlayerListBox list, int playerNum, string args) { local NexgenPlayerList playerItem; playerItem = list.addPlayer(); playerItem.pNum = playerNum; playerItem.pName = class'NexgenUtil'.static.getProperty(args, client.PA_Name); playerItem.pTitle = class'NexgenUtil'.static.getProperty(args, client.PA_Title); playerItem.pIPAddress = class'NexgenUtil'.static.getProperty(args, client.PA_IPAddress); playerItem.pClientID = class'NexgenUtil'.static.getProperty(args, client.PA_ClientID); playerItem.pCountry = class'NexgenUtil'.static.getProperty(args, client.PA_Country); playerItem.pTeam = byte(class'NexgenUtil'.static.getProperty(args, client.PA_Team)); return playerItem; } /*************************************************************************************************** * * $DESCRIPTION Updates the attributes of the specified player in the given player listbox. * $PARAM list The player listbox where the player info should be updated. * $PARAM playerNum The player code of the player to update. * $PARAM args Player attribute change event arguments. * $REQUIRE list != none && playerNum >= 0 * $RETURN The player item that was updated in the list. Might be none if the list didn't * contain the player with the specified player code. * **************************************************************************************************/ function NexgenPlayerList updatePlayerInfo(NexgenPlayerListBox list, int playerNum, string args) { local NexgenPlayerList playerItem; local string value; playerItem = list.getPlayer(playerNum); if (playerItem != none) { // Name attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Name); if (value != "") { playerItem.pName = value; } // Title attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Title); if (value != "") { playerItem.pTitle = value; } // Country attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Country); if (value != "") { playerItem.pCountry = value; playerItem.setFlagTex(); } // Team attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Team); if (value != "") { playerItem.pTeam = byte(value); } } return playerItem; } /*************************************************************************************************** * * $DESCRIPTION Adds a new component container panel to the current region. * $PARAM bgType Panel border/background style. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenPanel addSubPanel(class panelClass, optional EPanelBackType bgType) { local NexgenPanel panel; panel = NexgenPanel(addComponent(panelClass)); panel.panelBGType = bgType; panel.parentCP = self; panel.client = self.client; panel.setContent(); return panel; } K<R%dò|¬?‚-T –A,5rAQ*-T'<¥A¤-Th aR%h  h th KS%AQh h  w0/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenNoPlayRightDialog * $VERSION 1.00 (23-12-2006 19:18) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player hasn't got the right to play on the server. * **************************************************************************************************/ class NexgenNoPlayRightDialog extends NexgenPopupDialog; var UWindowSmallButton spectatorButton; // Spectator button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string spectatorText; // Text to display on the spectator button. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. const reconnectCommand = "Reconnect"; // Console command for reconnecting. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE spectatorButton != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); spectatorButton = addButton(spectatorText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the spectator button has been clicked and deals with it accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); // Spectator button. if (control == spectatorButton && eventType == DE_Click) { getplayerowner().updateURL(SSTR_OverrideClass, spectatorClass, true); getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ B@ £YŽ> £Y– T].-Failed to login: you have no playing rights.]ÆDThis server has playing rights disabled by default. You can't play on this server unless an administrator has explicitly allowed you to play. Meanwhile you can still be a spectator on the server.X] Spectator|; 20L[/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenMainPanelBar * $VERSION 1.02 (4-3-2007 23:06) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel root toolbar. * **************************************************************************************************/ class NexgenMainPanelBar extends UWindowDialogClientWindow; var UWindowSmallButton closeButton; // Button to close the control panel. var UWindowSmallButton sendButton; // Button to send the message entered in the msgInp EditBox. var UWindowEditControl msgInp; // Input field for the chat message. var localized string closeButtonText; // Text displayed on the close button. var localized string sendButtonText; // Text displayed on the send button. var localized string msgLabelText; // Label text displayed in front of the input field. const borderDist = 4.0; // Minimum distance between a component and vertical edges. const separatorDist = 4.0; // Extra distance between components for separation. const componentDist = 4.0; // Horizontal distance between components (in pixels). const buttonWidth = 64.0; // Width of button components. const buttonHeight = 16.0; // Height of button components. const labelWidth = 48.0; // Width of label components. const labelHeight = 12.0; // Height of label components. const editCtrlHeight = 16.0; // Height of edit control components. const sayCommand = "say"; // Chat message console command. /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this GUI component. * $OVERRIDE * **************************************************************************************************/ function created() { local float cx, cy, cw, ch; local UMenuLabelControl label; super.created(); // Add close button. cw = buttonWidth; ch = buttonHeight; cx = winWidth - cw - borderDist; cy = (winHeight - ch) / 2.0; closeButton = UWindowSmallButton(createControl(class'UWindowSmallButton', cx, cy, cw, ch)); closeButton.setText(closeButtonText); // Add send button. cx = cx - cw - componentDist - separatorDist; sendButton = UWindowSmallButton(createControl(class'UWindowSmallButton', cx, cy, cw, ch)); sendButton.setText(sendButtonText); // Add message input field. ch = editCtrlHeight; cw = cx - borderDist - labelWidth - 2 * componentDist; cx = borderDist + labelWidth + componentDist; cy = (winHeight - ch) / 2.0; msgInp = UWindowEditControl(createControl(class'UWindowEditControl', cx, cy, cw, ch)); msgInp.editBoxWidth = cw; msgInp.setMaxLength(250); msgInp.setHistory(true); // Add 'say' label. cw = labelWidth; ch = labelHeight; cx = borderDist; cy = (winHeight - ch) / 2.0; label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(msgLabelText); label.align = TA_Center; } /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ super.paint(c, x, y); drawUpBevel(c, 0, 0, winWidth, winHeight, getLookAndFeelTexture()); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); if (control == closeButton && eventType == DE_Click) { UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); } if (control == sendButton && eventType == DE_Click) { msgInp.editBox.keyDown(getPlayerOwner().EInputKey.IK_Enter, 0.0, 0.0); } if (control == msgInp && eventType == DE_EnterPressed) { sendMessage(); } } /*************************************************************************************************** * * $DESCRIPTION Sends the message in the msgInp EditBox to the server and clears the value of the * editBox. No message will be send if the message is an empty string. * $ENSURE msgInp.getValue() == "" * **************************************************************************************************/ function sendMessage() { local string msg; // Get chat message. msg = msgInp.getValue(); // Send message & reset edit box value. if (msg != "") { msgInp.setValue(""); getPlayerOwner().consoleCommand(sayCommand @ msg); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ B< 0.5H<qD±ôÏrc*,–_, l_¥_tl&f–l_“l&jlj¥l3_“_&6Bljq6Aljp6@ljo6ljnòcqpon D< 64_,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenMainPanel * $VERSION 1.01 (3-3-2007 17:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel root. * **************************************************************************************************/ class NexgenMainPanel extends NexgenPanelContainer; const barHeight = 22; /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this panel. * $OVERRIDE * **************************************************************************************************/ function created() { super(NexgenPanel).created(); pages = UWindowPageControl(createWindow(class'UWindowPageControl', 0, 0, winWidth, winHeight - barHeight - 3)); createWindow(class'NexgenMainPanelBar', 0, winHeight - barHeight - 3, winWidth, barHeight); } /*************************************************************************************************** * * $DESCRIPTION Notifies the window that a new level is going to be loaded. * $OVERRIDE * **************************************************************************************************/ function notifyBeforeLevelChange() { super.notifyBeforeLevelChange(); close(); } /*************************************************************************************************** * * $DESCRIPTION Closes the dialog. * $PARAM bByParent The close call was issued by the parent of the dialog. * $OVERRIDE * **************************************************************************************************/ function close(optional bool bByParent) { local UWindowRootWindow rootWin; local UWindowWindow win; local bool bWindowVisible; // Check if there is another visible window. rootWin = WindowConsole(getPlayerOwner().player.console).root; if (rootWin != none) { win = rootWin.firstChildWindow; while (!bWindowVisible && win != none) { // Current window visible? bWindowVisible = win != parentWindow && win.windowIsVisible() && win.bLeaveOnscreen; // Continue with next window. win = win.nextSiblingWindow; } } // Close the window. if (!bWindowVisible) { WindowConsole(getPlayerOwner().player.console).closeUWindow(); } super.close(bByParent); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ M<hWŽiÖÔ³¯•Ê~ÍÌL>~•Ê/a0 CcRQwc*R10Ôwc*h%Í–h_cF6Bhj6Ahj6@hj6hj¥he_% €ÔM/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenMainFrame * $VERSION 1.00 (28-1-2007 18:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen GUI main window class. Defines the frame for the Nexgen main window. * **************************************************************************************************/ class NexgenMainFrame extends UMenuFramedWindow; var float windowWidth; // Width of the main window frame (in pixels). var float windowHeight; // Height of the main window frame (in pixels). var NexgenMainPanel mainPanel; // Control panel root. /*************************************************************************************************** * * $DESCRIPTION Makes sure the main frame is properly setup. * $ENSURE mainPanel != none * $OVERRIDE * **************************************************************************************************/ function created() { super.created(); windowTitle = "Nexgen Server Controller v" $ left(class'NexgenUtil'.default.version, 4); clientArea = createWindow(class'NexgenMainPanel', 4, 16, winWidth - 4, winHeight - 16); mainPanel = NexgenMainPanel(clientArea); bLeaveOnScreen = true; bMoving = true; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ X<B%Ýñ™ B% ÂI<€ŒþO<L¸ L-yþE![4-yí|{EJD{E|* VÁ/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenLang * $VERSION 1.29 (8-3-2008 22:14) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Localization support class. Provides the language dependant functionality of the * Nexgen server controller. * **************************************************************************************************/ class NexgenLang extends Info; var localized string dateFormatStr; // Format of date strings. var localized string startingControllerMsg; // Message logged if the server controller is starting. var localized string noDedicatedServerMsg; // Indicates the server isn't dedicated. var localized string compatibilityModeMsg; // Compatibility mode for a specific mutator is enabled. var localized string autoInstallMsg; // Automatically installing Nexgen at first run. var localized string invalidConfigMsg; // Configuration was invalid and has been repaired. var localized string attrServerIDMsg; // Server ID code attribute & value message. var localized string nexgenBootMsg; // Message logged when the Nexgen boot is activated. var localized string nexgenBootFailMsg; // The message to log when the Nexgen boot failed. var localized string execCommandMsg; // Text to write to the log when a command is executed. var localized string bootLevelSwitchMsg; // Message logged when switching to another map. var localized string nexgenActiveMsg; // Logged when the Nexgen controller is operational. var localized string loadingPluginMsg; // Text written to the log if a plugin is loaded. var localized string initFailedMsg; // Plugin failed to initialize. var localized string regFailedMsg; // Failed to register plugin at the controller. var localized string noHUDReplacementClassMsg; // Unable to find a HUD replacement class. var localized string loginRequestMsg; // Login request message written to the log. var localized string attrClientIPMsg; // IP address used by the client. var localized string attrClientIDMsg; // Client ID of the client that wants to login. var localized string attrPasswordMsg; // The password entered by the client. var localized string duplicateIDMsg; // Another player already is using the same ID. var localized string bannedMsg; // Player is banned or kicked from the server. var localized string invalidPassMsg; // Invalid password entered. var localized string serverCapacityMsg; // Servers max capacity has been reached. var localized string noPlayRightMsg; // Client has no playing rights. var localized string loginAcceptedMsg; // Login request has been accepted. var localized string loginRejectedMsg; // Login request has been rejected. var localized string playerJoinMsg; // When a new player joins the game (and is accepted). var localized string playerLeaveMsg; // When a player leaves the game. var localized string playerNameChangeMsg; // When a player has changed his/her name. var localized string playerLeaveLogMsg; // Written to log when a player leaves. var localized string teamSwitchFailMsg; // When a team switch has been rejected. var localized string teamsLockedMsg; // Message indicating that the teams are locked. var localized string nameChangeFailMsg; // Shown when a player failed to change his/her name. var localized string nameChangeDisabled; // Name change failed because name change is disabled. var localized string invalidCommandMsg; // Player tried to execute an invalid command. var localized string commandFailedMsg; // Message displayed when a command failed. var localized string internalErrorMsg; // An internal error has occurred. var localized string teamSwitchFailedMsg; // Failed to execute a team switch command. var localized string specTeamSwitchMsg; // A spectator tried to teamswitch. var localized string teamSwitchDisabledMsg; // Team switching is disabled. var localized string playerTeamSwitchDisabledMsg; // Player is not allowed to switch team. var localized string invalidTeamMsg; // Tried to switch to a nonexistent team. var localized string sameTeamMsg; // Can't switch to the same team. var localized string noSpecsAllowedMsg; // The server has no spectator slots. var localized string noMoreSpecSlotsMsg; // All spectator slots are taken. var localized string noPlayingRightsMsg; // Client has no playing rights. var localized string noMorePlayerSlotsMsg; // No more player slots available. var localized string teamBalanceFailedMsg; // Team balance command failed to execute. var localized string notATeamGameMsg; // The current game is not a team game. var localized string teamBalanceDisabledMsg; // The team balance command is disabled. var localized string teamsAlreadyBalancedMsg; // Teams are already balanced. var localized string balanceMsg; // Successfully executed team balance message. var localized string launchMsg; // Message displayed when the game has been launched. var localized string teamName[4]; // Team names. var localized string forcedEndMsg; // var localized string rootAdminTitle; // var localized string specTitle; // var localized string deathPreventedMsg; // var localized string mutedReminderMsg; // var localized string accountTypeNameStr; // var localized string teamKillAttemptMsg; // var localized string controllerSystemLogTag; // var localized string eventLogTag; // var localized string messageLogTag; // var localized string chatMessageLogTag; // var localized string teamSayMessageLogTag; // var localized string privateMessageLogTag; // var localized string allowedToPlayRightDesc; // var localized string vipSlotAccessRightDesc; // var localized string adminSlotAccessRightDesc; // var localized string needsNoPWRightDesc; // var localized string canBeIdleRightDesc; // var localized string matchAdminRightDesc; // var localized string matchSetRightDesc; // var localized string moderatorRightDesc; // var localized string banOpRightDesc; // var localized string accountMngrRightDesc; // var localized string serverAdminRightDesc; // var localized string launchGameMsg; // var localized string adminLaunchGameMsg; // var localized string welcomeMsg; // // Control panel actions. var localized string settingsSavedMsg; // var localized string receivedPMMsg; // var localized string receivedPWMsg; // var localized string adminTeamSwitchMsg; // var localized string adminPauseGameMsg; // var localized string adminResumeGameMsg; // var localized string adminRestartGameMsg; // var localized string adminStopGameMsg; // var localized string adminPlayerTeamSwitchDisableMsg; // var localized string adminPlayerTeamSwitchEnableMsg; // var localized string adminReconnectAsPlayerMsg; // var localized string adminReconnectAsSpecMsg; // var localized string adminSendToURLMsg; // var localized string adminDisableTeamSwitchMsg; // var localized string adminEnableTeamSwitchMsg; // var localized string adminDisableTeamBalanceMsg; // var localized string adminEnableTeamBalanceMsg; // var localized string adminLockTeamsMsg; // var localized string adminUnlockTeamsMsg; // var localized string adminAddBanMsg; // var localized string adminDeleteBanMsg; // var localized string adminEnableMatchModeMsg; // var localized string adminDisableMatchModeMsg; // var localized string adminMutePlayerMsg; // var localized string adminUnmutePlayerMsg; // var localized string adminSetNameMsg; // var localized string adminKickPlayerMsg; // var localized string adminBanPlayerMsg; // var localized string adminMuteAllMsg; // var localized string adminUnmuteAllMsg; // var localized string adminEnableNameChangeMsg; // var localized string adminDisableNameChangeMsg; // var localized string adminRebootServerMsg; // var localized string invalidPasswordMsg; // var localized string adminLoginMsg; // // HUD. var localized string autoReconnectAlert; // var localized string reconnectingAlert; // var localized string rebootAlert; // var localized string idleAlert; // var localized string waitingState; // var localized string readyState; // var localized string startingState; // var localized string onlineState; // var localized string offlineState; // var localized string offlineStateRCN; // var localized string endedState; // var localized string pausedState; // var localized string loginState; // var localized string idleState; // var localized string mutedState; // var localized string protectedState; // var localized string deadState; // var localized string matchState; // // GUI. var localized string clientTabTxt; // var localized string homeTabTxt; // var localized string settingsTabTxt; // var localized string privateMessageTabTxt; // var localized string gameTabTxt; // var localized string playersTabTxt; // var localized string moderatorTabTxt; // var localized string matchControlTabTxt; // var localized string matchSetupTabTxt; // var localized string serverTabTxt; // var localized string infoTabTxt; // var localized string banControlTabTxt; // var localized string accountsTabTxt; // var localized string accountTypesTabTxt; // var localized string bootTabTxt; // var localized string pluginsTabTxt; // var localized string aboutTabTxt; // var localized string welcomeTxt; // var localized string rightsOverviewTxt; // var localized string rightNotDefinedTxt; // var localized string teamBalanceTxt; // var localized string redTeamTxt; // var localized string blueTeamTxt; // var localized string greenTeamTxt; // var localized string goldTeamTxt; // var localized string playTxt; // var localized string spectateTxt; // var localized string reconnectTxt; // var localized string disconnectTxt; // var localized string exitTxt; // var localized string mapVoteTxt; // var localized string startTxt; // var localized string loginTxt; // var localized string serverNameTxt; // var localized string shortServerNameTxt; // var localized string MOTDLineTxt; // var localized string adminNameTxt; // var localized string adminEmailTxt; // var localized string serverPasswordTxt; // var localized string adminPasswordTxt; // var localized string playerSlotsTxt; // var localized string vipSlotsTxt; // var localized string adminSlotsTxt; // var localized string specSlotsTxt; // var localized string advertiseTxt; // var localized string resetTxt; // var localized string saveTxt; // var localized string keyBindsTxt; // var localized string balanceBindTxt; // var localized string switchRedBindTxt; // var localized string switchBlueBindTxt; // var localized string switchGreenBindTxt; // var localized string switchGoldBindTxt; // var localized string suicideBindTxt; // var localized string openMapVoteBindTxt; // var localized string openCPBindTxt; // var localized string pauseGameBindTxt; // var localized string UISettingsTxt; // var localized string enableMsgHUDTxt; // var localized string msgFlashEffectTxt; // var localized string showPlayerLocationTxt; // var localized string pmSoundTxt; // var localized string miscSettingsTxt; // var localized string autoSSNormalGameTxt; // var localized string autoSSMatchTxt; // var localized string playerListTxt; // var localized string blockedListTxt; // var localized string blockToggleTxt; // var localized string blockAllPMsTxt; // var localized string messageTxt; // var localized string sendNormalPMTxt; // var localized string sendWindowedPMTxt; // var localized string historyTxt; // var localized string blockMsg; // var localized string unblockMsg; // var localized string sendMsgTxt; // var localized string receivedMsgTxt; // var localized string accountNameTxt; // var localized string accountTitleTxt; // var localized string passwordTxt; // var localized string addAccountTypeTxt; // var localized string delAccountTypeTxt; // var localized string moveUpTxt; // var localized string moveDownTxt; // var localized string userNameTxt; // var localized string accountTypeTxt; // var localized string userTitleTxt; // var localized string onlineTxt; // var localized string offlineTxt; // var localized string updateTxt; // var localized string addTxt; // var localized string deleteTxt; // var localized string customAccountTxt; // var localized string switchToTeamTxt; // var localized string sendToURLTxt; // var localized string reconnectAsPlayerTxt; // var localized string reconnectAsSpecTxt; // var localized string disableTeamSwitchTxt; // var localized string pauseGameTxt; // var localized string endGameTxt; // var localized string restartGameTxt; // var localized string allowTeamSwitchTxt; // var localized string allowTeamBalanceTxt; // var localized string lockTeamsTxt; // var localized string playerNameTxt; // var localized string banReasonTxt; // var localized string banPeriodTxt; // var localized string banForeverTxt; // var localized string banMatchesTxt; // var localized string banDaysTxt; // var localized string banUntilDateTxt; // var localized string ipAddressesTxt; // var localized string clientIDsTxt; // var localized string addBanTxt; // var localized string updateBanTxt; // var localized string delBanTxt; // var localized string removeTxt; // var localized string enableBootCtrlTxt; // var localized string restartOnLastGameTxt; // var localized string inclMutatorsTxt; // var localized string exclMutatorsTxt; // var localized string bootCmdLineTxt; // var localized string gameTypeTxt; // var localized string mapPrefixTxt; // var localized string extraCmdLineOptTxt; // var localized string preSwitchCommandsTxt; // var localized string rebootTxt; // var localized string administratorTxt; // var localized string contactAddrTxt; // var localized string msgOfTheDayTxt; // var localized string serverIDTxt; // var localized string statisticsTxt; // var localized string totalGamesTxt; // var localized string totalFragsTxt; // var localized string totalDeathsTxt; // var localized string totalFlagsTxt; // var localized string bestPlayersTxt; // var localized string FPHTxt; // var localized string recordSetTxt; // var localized string timeLimitTxt; // var localized string scoreLimitTxt; // var localized string teamScoreLimitTxt; // var localized string gameSpeedTxt; // var localized string teamSwitchEnabledTxt; // var localized string teamBalanceEnabledTxt; // var localized string teamsLockedTxt; // var localized string nameChangeAllowedTxt; // var localized string mutatorsTxt; // var localized string levelTxt; // var localized string fileTxt; // var localized string titleTxt; // var localized string authorTxt; // var localized string idealPlayerCountTxt; // var localized string matchSettingsTxt; // var localized string matchNumOfGamesTxt; // var localized string matchCurrGameNumTxt; // var localized string matchSpecNoPassTxt; // var localized string matchMuteSpecsTxt; // var localized string matchBootControlTxt; // var localized string matchAutoLockTeamsTxt; // var localized string matchAutoPauseTxt; // var localized string matchSeparateByTagTxt; // var localized string matchAutoTagSeparateTxt; // var localized string matchDoSeparateTxt; // var localized string startMatchTxt; // var localized string stopMatchTxt; // var localized string sendPasswordTxt; // var localized string allPlayersTxt; // var localized string muteToggleTxt; // var localized string setPlayerNameTxt; // var localized string kickPlayerTxt; // var localized string banPlayerTxt; // var localized string muteAllTxt; // var localized string allowNameChangeTxt; // var localized string showAdminMessageTxt; // var localized string autoUpdateBansTxt; // var localized string autoDelExpiredBansTxt; // var localized string announceTeamKillsTxt; // var localized string enableNexgenMessageHUDTxt; // var localized string logEventsTxt; // var localized string logMessagesTxt; // var localized string logChatMessagesTxt; // var localized string LogPrivateMessagesTxt; // var localized string defaultAllowTeamSwitchTxt; // var localized string defaultAllowTeamBalanceTxt; // var localized string defaultAllowNameChangeTxt; // var localized string gameWaitTimeTxt; // var localized string gameStartDelayTxt; // var localized string autoReconnectTimeTxt; // var localized string maxIdleTimeTxt; // var localized string maxIdleTimeCPTxt; // var localized string spawnProtectTimeTxt; // var localized string teamKillDmgProtectTxt; // var localized string teamKillPushProtectTxt; // var localized string autoDisableMatchTimeTxt; // var localized string ignoredWeaponsTxt; // var localized string weaponClassTxt; // var localized string ignorePrimaryFireTxt; // var localized string ignoreAltFireTxt; // var localized string hudReplaceClassesTxt; // var localized string originalHUDClassTxt; // var localized string replacementHUDClassTxt; // var localized string addNewItemTxt; // /*************************************************************************************************** * * $DESCRIPTION Returns the name of the team specified by the given number. * $PARAM team Number of the team whose name is to be returned. * $RETURN The name of the team, or ? if an invalid team is specified. * **************************************************************************************************/ static function string getTeamName(int team) { if (0 <= team && team < arrayCount(default.teamName)) { return default.teamName[team]; } else { return "?"; } } /*************************************************************************************************** * * $DESCRIPTION Formats the given string by inserting the specified strings into the proper * positions. The positions are indicated by the "%n" tags, where n is number of the * string to insert. * $PARAM source The string that is to be formatted. * $PARAM str1 String number 1 to insert. * $PARAM str2 String number 2 to insert. * $PARAM str3 String number 3 to insert. * $PARAM str4 String number 4 to insert. * $RETURN The formatted string. * **************************************************************************************************/ static function string format(string source, optional coerce string str1, optional coerce string str2, optional coerce string str3, optional coerce string str4) { return class'NexgenUtil'.static.format(source, str1, str2, str3, str4); } /*************************************************************************************************** * * $DESCRIPTION Converts a ban period string into a human readable format. * $PARAM banPeriodStr The string describing the ban period. * $RETURN A string describing (in words) how long the ban lasts. * **************************************************************************************************/ function string getBanPeriodDescription(string banPeriodStr) { local string description; if (banPeriodStr == "") { description = "forever"; } else if (left(banPeriodStr, 1) ~= "M") { description = "for" @ mid(banPeriodStr, 1) @ "matches"; } else if (left(banPeriodStr, 1) ~= "U") { description = "until" @ getLocalizedDateStr(mid(banPeriodStr, 1)); } else { description = "forever"; } return description; } /*************************************************************************************************** * * $DESCRIPTION Returns a compact date description string for the specified date. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $REQUIRE (1 <= month && moth <= 12) && (1 <= day && day <= 31) && * (0 <= hour && hour <= 23) && (0 <= minute && minute <= 59) * $RETURN The date description string for the given date. * **************************************************************************************************/ static function string getCompactDateStr(int year, int month, int day, int hour, int minute) { return class'NexgenUtil'.static.lfill(day, 2, "0") $ "/" $ class'NexgenUtil'.static.lfill(month, 2, "0") $ "/" $ class'NexgenUtil'.static.lfill(year, 4, "0") $ " " $ class'NexgenUtil'.static.lfill(hour, 2, "0") $ ":" $ class'NexgenUtil'.static.lfill(minute, 2, "0"); } /*************************************************************************************************** * * $DESCRIPTION Parses the given date string. * $PARAM dateStr The date string to parse. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $REQUIRE 'dateStr follows dateFormatStr' * $RETURN True if the specified date string was valid, false if not. When false is returned * the outcome (the date) should be ignored. * **************************************************************************************************/ static function bool parseDate(string dateStr, out int year, out int month, out int day, out int hour, out int minute) { local bool bValid; local string remaining; local int index; bValid = true; remaining = class'NexgenUtil'.static.trim(dateStr); // Parse day. index = instr(remaining, "/"); if (index >= 0) { day = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } // Parse month. if (bValid) { index = instr(remaining, "/"); if (index >= 0) { month = int(left(remaining, index)); remaining = class'NexgenUtil'.static.trim(mid(remaining, index + 1)); } else { bValid = false; } } // Parse year. if (bValid) { index = instr(remaining, " "); if (index >= 0) { year = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse hour. if (bValid) { index = instr(remaining, ":"); if (index >= 0) { hour = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse minute. if (bValid) { minute = int(remaining); } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Converts the given date string into a localized format. * $PARAM dateStr The date string to convert. * $RETURN The converted date into a localized format. * **************************************************************************************************/ function string getLocalizedDateStr(string dateStr) { local bool bValid; local int year, month, day, hour, minute; bValid = class'NexgenUtil'.static.readDate(dateStr, year, month, day, hour, minute); if (bValid) { return getCompactDateStr(year, month, day, hour, minute); } else { return dateStr; } } /*************************************************************************************************** * * $DESCRIPTION Converts the given localized date string into a standard format. * $PARAM dateStr The date string to convert. * $RETURN The converted date into a standard format. * **************************************************************************************************/ function string getDelocalizedDateStr(string dateStr) { local bool bValid; local int year, month, day, hour, minute; bValid = parseDate(dateStr, year, month, day, hour, minute); if (bValid) { return class'NexgenUtil'.static.serializeDate(year, month, day, hour, minute); } else { return dateStr; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ c /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenJustBannedDialog * $VERSION 1.00 (17-11-2007 18:34) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player just got kicked / banned from the server. * **************************************************************************************************/ class NexgenJustBannedDialog extends NexgenBannedDialog; /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ JR<C ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2S<ã/ÃJ¦&¿ù– T].-You have been kicked/banned from the server.]ÔRAn administrator has just kicked or banned you from the server. This means you are probably not welcome for the time being. Please go play somewhere else, there are enough of other servers and games out there.Óc}g'¡5Ó}rc*W3c {} N< "\\n"€U< 0_<o$DÑ>˜b"o$p$-@% ÂW< 1Y< 2€^<FD±—ôÏrm*,–f, gf¥ftg&f–gf“g&ZgZ¥g3f“f&6YgZF6XgZE6WgZD6VgZCòmFEDC Z< 3c<cWŽpÖÔ³¯•ÊTÍÌL>T•Ê/a0 CmRQwm*R10Ôwm*c%Í–cfmF6YcZ6XcZ6WcZ6VcZ¥cef% m<ry¼´”ˆ-š:r&b"OverrideClass'kkš:r,(b"OverrideClassBotpack.CHSpectator'Reconnect €€Ôa<€Œþf<b¿ L-Oþ|![4-OíRQ|JDQ|R* èmSg'¨5èSrm*W3m {S €L/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenImageControl * $VERSION 1.00 (10-03-2007 22:54) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Image GUI component. * **************************************************************************************************/ class NexgenImageControl extends UWindowDialogControl; var Texture image; // Image to display. var bool bStretch; // Whether to stretch the image if the dimensions do not match. /*************************************************************************************************** * * $DESCRIPTION Renders the GUI component. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y) { local float xPos; local float yPos; if (image != none) { if (bStretch) { drawStretchedTexture(c, 0, 0, winWidth, winHeight, image); } else { xPos = int((winWidth - image.uSize) / 2.0); yPos = int((winHeight - image.vSize) / 2.0); drawClippedTexture(c, xPos, yPos, image); } } } k,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenIDUsedDialog * $VERSION 1.01 (20-1-2008 12:58) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the players client ID is already in use. * **************************************************************************************************/ class NexgenIDUsedDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string reconnectText; // Text to display on the reconnect button. const reconnectCommand = "Reconnect"; // Console command for reconnecting. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); reconnectButton = addButton(reconnectText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { // Reconnect. getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ E=wE¤—‘ƒw/*/g/ï/Yg-ã'gu{w/= Mw Âd< 5n< 6o< 7p< 8u<]D°†ôÏrq*,–n, On¥ntO&f–On“O&IOI¥O3n“n&6pOI]6oOI\6mOI[6lOIZòq]\[Z Bl £Y– T],+Failed to login: client ID already in use.m—UYour client ID appears to be already in use by another player. This is probably caused because you just left the server and the server wasn't able to receive your logout notification. This can also be caused by someone that has copied your Unreal Tournament installation or by someone that has acquired your Nexgen key. If you continue to experience this problem contact the administrator of this server.\]Retry`"Oq< 9x<QW_ÖÔ³¯•ÊkÍÌL>k•Ê/a0 CqRQwq*R10Ôwq*Q%Í–QnqF6pQI6oQI6mQI6lQI¥Qen% €Ôv<€Œþz<w° L-fþp![4-fíihpJDhpi* òqjg'™5òjrq*W3q {j y< 'sswaiting'~< 'ssready'< 'ssstaring'€D=rW'~z„‚°r{q{$rq¥ÿ*$wr¥ÿ*ÿÿÿÿ|& B{<|8@ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2C=Ÿ©=ËBêÓnÅŽ> £Y– T]! You are kicked from the server.]ywThe server has disconnected your client because you were idle for too long. You can reconnect as soon as you get back.\] Reconnect`"OK=oNñ'#Hs’oX#otW#¥ots l=V# Q=T#G“)w^GT#h#j#{#|#^g^ï^Yg-ã'gu Â@= 'ssended'F= 'sspaused'G= 'ssmatch'H= 'ssnormal'I= 'cslogin'Y=IG¹©U\ %S–\ ,\ zm [ b<Iw[ *2‚š[ Q\  –m , m I[ -m(n“m &+‚-m™n\ zâ™WnI’n&I%-m'(wnInI’n&I’n&Iw¦n¥m [ [ @8¥\  p; 0.50J= 'csidle'}€@K# "0123456789ABCDEF"L= 1.25W=yZ`ÇÇrg*I‡wgÕ*ggÕćwg*pga/!_g&ggÅBy +o'z +p'Põ¬¯gÕºy@^õ¬¯gÕ¾z@ggÕØ +P^yz/.+g/&/=@ %Å–@ ,»w@ Q*@ Qx¥@ ‰ ucžSTu1-u ClientKeye u1-u ClientIDzTT he  v eTu1<u ClientKeyTu1<u ClientIDe u1bÚÚ{ v eTe T he  v eTu1<u ClientKeyTu1<u ClientIDe u1bruo '-ulu PasswordQ{r #^H#u Passwordr  I{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxTDM * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended TDM HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxTDM extends ChallengeTeamHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } äS=W; ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6 T=9^>乄—C»P£‰Ž”‰Ž”C»P£Š¸ùçElŠ¸ùçElŠ¸ùçElC»P£P3–Jl€O{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxDOM * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended DOM HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxDOM extends ChallengeDominationHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } X=sX=6ƒArg*Is Es't Et'cõ¬¯gÕºs@dõ¬¯gÕ¾t@_gÕØ Ecdst^.E_^b^a1^`o ^_l^& B>I$t€ig.²×ÝCg-ùgU grgÕ*gK * m=|O›PNo &F–o G#<–o |l|lo ¥o l ÍV=\<~ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6 Z=“àLÐÍUO4¤C»P£‰Ž”‰Ž”C»P£Š¸ùçElŠ¸ùçElŠ¸ùçElC»P£P3–JM= 'csprotected'q( "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"\= "5-5-5-5-5-5-4"]= "mm/dd/yyyy"^= "NSC"_= ","`= "="a= "\\"b=d=h*Þh%–h}5-5-5-5-5-5-4M 5-5-5-5-5-5-4h&ê‚x0M  xM 9gJM n%ç–ngN pN ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789§}ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789&¥nnüN pN M ¥hN   N €@B{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxDM * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended DM HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxDM extends ChallengeHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } ¹e=o:} ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6 f=6&^ê¹²‘¿!C»P£‰Ž”‰Ž”C»P£Š¸ùçElŠ¸ùçElŠ¸ùçElC»P£P3–JH{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxCTF * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended CTF HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxCTF extends ChallengeCTFHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } P= 4.0h= 2.0c=B#pJ8 –m B#Ž-{j~m vP–j%-{'L pL m ‹L ppL €m j"m m ’j}v L   k€E€|=~P€ÒNn &F–n A#<—n ~k~kn ¥n k {€@[= 'csmuted'àg=E<| ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6 p=?²úÌàõ˜÷C»P£‰Ž”‰Ž”C»P£Š¸ùçElŠ¸ùçElŠ¸ùçElC»P£P3–J~z/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxAS * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended AS HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxAS extends AssaultHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } Ðq=r<{ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6 r=¸H£”І²ç÷C»P£‰Ž”‰Ž”C»P£Š¸ùçElŠ¸ùçElŠ¸ùçElC»P£P3–Jj=z"ráp^pz"%1y"^p^%2x"^p^%3v"^p^%4t"^  o= 'csdead't= 'csnormal'ñu=_”:!¢.ÑC-o|1$-FlashMessagestruetrue-X|1$-ShowPlayerLoctruetrue Ûv=l g©3$‹wÎ*Î gl {l c¯]sh‚‚-P —: U%+„±c?,—:'{%AD®¯< Uc?&5—A%Wl  # v$SA%N%NeWl  u$%N%N‰ô—:'{%A:'{Wl  # t$SA,Nd‰‰‚™y%˜y,AyWl  # s$SA,Nd y€^€@o/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUD * $VERSION 1.18 (9-3-2008 12:08) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen HUD extension class. * **************************************************************************************************/ class NexgenHUD extends Mutator; #exec OBJ LOAD FILE=..\Textures\LadrStatic.utx PACKAGE=Botpack.LadrStatic #exec TEXTURE IMPORT NAME=base FILE=Resources\base.pcx GROUP="GFX" FLAGS=2 MIPS=OFF #exec TEXTURE IMPORT NAME=grad32 FILE=Resources\grad32.pcx GROUP="GFX" FLAGS=2 MIPS=OFF #exec TEXTURE IMPORT NAME=grad64 FILE=Resources\grad64.pcx GROUP="GFX" FLAGS=2 MIPS=OFF #exec TEXTURE IMPORT NAME=playerIcon FILE=Resources\PlayerIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=playerIcon2 FILE=Resources\PlayerIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=serverIcon FILE=Resources\ServerIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=serverIcon2 FILE=Resources\ServerIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=offlineIcon FILE=Resources\OfflineIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=offlineIcon2 FILE=Resources\OfflineIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=waitIcon FILE=Resources\WaitIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=waitIcon2 FILE=Resources\WaitIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=specIcon FILE=Resources\SpecIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=specIcon2 FILE=Resources\SpecIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=shieldIcon FILE=Resources\ShieldIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=shieldIcon2 FILE=Resources\ShieldIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=idleIcon FILE=Resources\IdleIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=idleIcon2 FILE=Resources\IdleIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=mutedIcon FILE=Resources\MutedIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=mutedIcon2 FILE=Resources\MutedIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=matchIcon FILE=Resources\MatchIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=matchIcon2 FILE=Resources\MatchIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF var NexgenClient client; // Client owning this HUD. var float lastSetupTime; // Last time setup() was called. var Font baseFont; // Base font to use for rendering text. var Color blankColor; // White color (#FFFFFF). var Color baseHUDColor; // Base color of the HUD (background color). var Color baseColors[6]; // Base colors available for the HUD. var Color colors[11]; // List of colors for text. var float baseFontHeight; // Height of the base font. struct MessageInfo { // Structure for storing message information. var string text[5]; // Message text list. var int col[5]; // Message text colors. var float timeStamp; // Time at which the message was received. }; struct PanelInfo { // Panel info container structure. var string text; // Text displayed on the panel. var Color textCol; // Color of the text to display. var Texture icon; // Icon displayed in front of the text. var Texture solidIcon; // Solid version of the icon. var bool bBlink; // Indicates if the text should 'blink'. }; var MessageInfo chatMessages[3]; // List of chat messages. var MessageInfo messages[5]; // List of all other messages. var int chatMsgCount; // Number of chat messages stored in the list. var int msgCount; // Number of other messages stored. var Texture faceImg; // Face texture to display in the chat message box. var float msgBoxWidth; // Width of the message box. var float msgBoxLineHeight; // Height of a line in the message box. var float msgBoxHeight; // Total height of the message box. var float minPanelWidth; // Minimum size of the panel. var bool bFlashMessages; // Whether new messages should flash. var float lastResX; // Horizontal resolution at last setup call. var float lastResY; // Vertical resolution at last setup call. var byte lastTeam; // Team number at last setup call. var float lastLevelTimeSeconds; // Last value of level.time seconds. var float timeSeconds; // Gamespeed independent timeSeconds, that keeps counting // even when the game is paused. var bool bShowPlayerLocation; // Show player location name in teamsay messages? const iconSize = 24.0; // Size of the status icons. const chatMessageLifeTime = 16.0; // The amount of time a chat message is diplayed (in sec). const messageLifeTime = 12.0; // Amount of time any other message is displayed (in sec). const messageBlinkTime = 4.0; // How long a message is highlighted (in sec). const messageBlinkRate = 4.0; // Message highlight blink rate (in Hz). const panelBlinkRate = 2.0; // Panel text highlight blink rate (in Hz). const blinkColorDamp = 0.70; // Dampening factor for blinking text. const connectionAlertDelay = 2; // Time to wait before showing the bad connection alert. const secPerMinute = 60; // Number of seconds per minute -- duh! const matchInfoSwitchTime = 4; // Number of seconds to wait before switching between time // remaining and match num. // Alert window settings. const hDefBarSize = 20; // Animated horizontal bar size. const alertAnimTime = 0.5; // Animation cycle time. const alertAnimDist = 64; // Distance the animated bar moves away from the window. const alertBorderSize = 32; // Distance between borders and text of the window. const newLineToken = "\\n"; // Token used to break the text in multiple lines. // Colors. const C_RED = 0; const C_BLUE = 1; const C_GREEN = 2; const C_YELLOW = 3; const C_WHITE = 4; const C_BLACK = 5; const C_PINK = 6; const C_CYAN = 7; const C_METAL = 8; const C_ORANGE = 9; // Server states. const SS_Offline = 'ssoffline'; // Connection with server is failing. const SS_Waiting = 'sswaiting'; // Server is waiting for players. const SS_Ready = 'ssready'; // Game is ready to start. const SS_Starting = 'ssstaring'; // Game is counting down to start. const SS_Ended = 'ssended'; // Game is ended. const SS_Paused = 'sspaused'; // Game is paused. const SS_Match = 'ssmatch'; // A match is in progress. const SS_Normal = 'ssnormal'; // A normal game is in progress. // Client states. const CS_Login = 'cslogin'; // Client is logging in. const CS_Idle = 'csidle'; // Client is idle or camping. const CS_Protected = 'csprotected'; // Client is (damage) protected. const CS_Muted = 'csmuted'; // Client is muted. const CS_Dead = 'csdead'; // Client is dead. const CS_Normal = 'csnormal'; // Client is in normal state. /*************************************************************************************************** * * $DESCRIPTION Initializes the extended HUD. * $REQUIRE owner != none && owner.isA('NexgenClient') * $ENSURE client != none * $OVERRIDE * **************************************************************************************************/ function postBeginPlay() { client = NexgenClient(owner); registerHUDMutator(); bFlashMessages = client.gc.get(client.SSTR_FlashMessages, "true") ~= "true"; bShowPlayerLocation = client.gc.get(client.SSTR_ShowPlayerLocation, "true") ~= "true"; } /*************************************************************************************************** * * $DESCRIPTION Renders the extended Nexgen HUD. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ function postRender(Canvas c) { local float badConnectionTime; local int timeRemaining; // Let other HUD mutators do their job first. if (nextHUDMutator != none) { nextHUDMutator.postRender(c); } // Render alerts. setup(c); badConnectionTime = client.timeSeconds - client.badConnectionSince; if (client.bBadConnectionDetected && client.sConf.autoReconnectTime > 0 && (badConnectionTime > connectionAlertDelay || client.gInf.rebootCountDown > 0)) { // Connection lost alert. timeRemaining = client.sConf.autoReconnectTime - badConnectionTime + 1; if (timeRemaining > 0) { renderAlert(c, client.lng.format(client.lng.autoReconnectAlert, timeRemaining), colors[C_RED], colors[C_RED]); } else { renderAlert(c, client.lng.reconnectingAlert, colors[C_RED], colors[C_RED]); } } else if (client.gInf.rebootCountDown > 0) { // Reboot warning. timeRemaining = client.gInf.rebootCountDown; renderAlert(c, client.lng.format(client.lng.rebootAlert, timeRemaining), colors[C_WHITE], baseHUDColor); } else if (client.idleTimeRemaining >= 0 && client.idleTimeRemaining <= client.idleTimeWarning) { // Idle warning. timeRemaining = client.idleTimeRemaining; renderAlert(c, client.lng.format(client.lng.idleAlert, timeRemaining), colors[C_WHITE], baseHUDColor); } } /*************************************************************************************************** * * $DESCRIPTION desc_here * $PARAM msg * $PARAM textColor * $PARAM baseColor * **************************************************************************************************/ simulated function renderAlert(Canvas c, string msg, color textColor, color baseColor) { local int windowWidth; local int windowHeight; local int cx, cy; local float animIndex; local int dist; local int hBarSize; local float textWidth; local int maxTextWidth; local float textHeight; local int lineCount; local string remaining; local int index; local string text; // Initialize. c.font = ChallengeHUD(c.viewport.actor.myHUD).myFonts.getStaticHugeFont(c.clipX); remaining = msg; lineCount = 0; while (remaining != "") { index = instr(remaining, newLineToken); if (index > 0) { text = left(remaining, index); remaining = mid(remaining, index + len(newLineToken)); } else { text = remaining; remaining = ""; } c.strLen(text, textWidth, textHeight); maxTextWidth = max(maxTextWidth, textWidth); lineCount++; } windowWidth = maxTextWidth + 2 * alertBorderSize; windowHeight = lineCount * textHeight + 2 * alertBorderSize; cx = (c.clipX - windowWidth) / 2; cy = (c.clipY - windowHeight) / 2; // Render frame background. c.style = ERenderStyle.STY_Translucent; c.drawColor = baseColor * 0.4; c.setPos(cx, cy); c.drawTile(Texture'grad64', windowWidth, windowHeight, 0.0, 0.0, 64.0, 64.0); // Render borders. c.drawColor = baseColor * 0.8; c.setPos(cx - 3.0, cy - 1.0); c.drawTile(Texture'base', 2.0, windowHeight + 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 1.0 + windowWidth, cy - 1.0); c.drawTile(Texture'base', 2.0, windowHeight + 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0, cy - 2.0); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0, cy + 1.0 + windowHeight); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hDefBarSize, cy - 2.0); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hDefBarSize, cy + 1.0 + windowHeight); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); // High detail animation effect. if (level.bHighDetailMode) { animIndex = (client.timeSeconds % alertAnimTime) / alertAnimTime; c.drawColor = baseColor * (1.0 - animIndex); dist = sin(animIndex * pi * 0.5) * alertAnimDist; hBarSize = hDefBarSize + hDefBarSize * ((windowWidth + alertAnimDist) / windowWidth) * animIndex; c.setPos(cx - 3.0 - dist, cy - 1.0 - dist); c.drawTile(Texture'base', 2.0, windowHeight + 2.0 + 2 * dist, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 1.0 + windowWidth + dist, cy - 1.0 - dist); c.drawTile(Texture'base', 2.0, windowHeight + 2.0 + 2 * dist, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0 - dist, cy - 2.0 - dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0 - dist, cy + 1.0 + windowHeight + dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hBarSize + dist, cy - 2.0 - dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hBarSize + dist, cy + 1.0 + windowHeight + dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); } // Render text. remaining = msg; lineCount = 0; c.drawColor = textColor; while (remaining != "") { index = instr(remaining, newLineToken); if (index > 0) { text = left(remaining, index); remaining = mid(remaining, index + len(newLineToken)); } else { text = remaining; remaining = ""; } c.setPos(cx + alertBorderSize, cy + alertBorderSize + lineCount * textHeight); c.drawText(text, false); lineCount++; } } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the message HUD. * $PARAM msg The message that is to be displayed. * $PARAM msgType Message type identifier. * $PARAM pri1 Replication info of the first player involved. * $PARAM pri2 Replication info of the second player involved. * **************************************************************************************************/ simulated function message(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int playerColor; local int messageColor; local bool bIsSpecSayMsg; local PlayerReplicationInfo specPRI; local GameReplicationInfo gri; local int index; local string locationName; // Check if the message was send by a spectator using say. if (msgType == 'Event' && instr(msg, ":") >= 0) { // Get shortcut to the game replication info. gri = client.player.gameReplicationInfo; // Find a player. while (index < arrayCount(gri.PRIArray) && gri.PRIArray[index] != none) { if (gri.PRIArray[index].bIsSpectator && left(msg, len(gri.PRIArray[index].playerName) + 1) ~= (gri.PRIArray[index].playerName $ ":")) { if (bIsSpecSayMsg) { if (len(gri.PRIArray[index].playerName) > len(pri1.playerName)) { pri1 = gri.PRIArray[index]; } } else { bIsSpecSayMsg = true; pri1 = gri.PRIArray[index]; } } index++; } } // Check message type. if (bIsSpecSayMsg) { // Chat message. Special case: player is spec and using say (not teamsay). if (pri1.talkTexture != none) { faceImg = pri1.talkTexture; } addChatMsg(C_METAL, pri1.playerName $ ": ", C_METAL, mid(msg, len(pri1.playerName) + 1)); } else if (pri1 != none && msg != "" && (msgType == 'Say' || msgType == 'TeamSay')) { // Chat message. playerColor = getPlayerColor(pri1); if (pri1.talkTexture != none) { faceImg = pri1.talkTexture; } if (msgType == 'TeamSay') { if (pri1.bIsSpectator && !pri1.bWaitingPlayer) { messageColor = C_WHITE; } else { messageColor = playerColor; } if (pri1.playerLocation != none) { locationName = pri1.playerLocation.locationName; } else if (pri1.playerZone != none) { locationName = pri1.playerZone.zoneName; } } else { if (pri1.bIsSpectator && !pri1.bWaitingPlayer) { messageColor = C_METAL; } else { messageColor = C_ORANGE; } } if (locationName != "" && bShowPlayerLocation) { addChatMsg(playerColor, pri1.playerName, C_CYAN, " (" $ locationName $ "): ", messageColor, msg); } else { addChatMsg(playerColor, pri1.playerName $ ": ", messageColor, msg); } } else if (msg != "") { // Other message. addColorizedMessage(msg, pri1, pri2); } } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the area just below the chatbox. Before the message is added an * attempt will be made to highlight player names. This is done by checking if the * messages contain the names of the given player replication info objects. * $PARAM msg Message to add. * $PARAM pri1 Replication info of the first player involved. * $PARAM pri2 Replication info of the second player involved. * **************************************************************************************************/ simulated function addColorizedMessage(string msg, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local string firstPlayerName; local string secondPlayerName; local int firstIndex; local int secondIndex; local int firstPlayerColor; local int secondPlayerColor; local string msgPart1; local string msgPart2; local string msgPart3; local int msgColor; local string tempPlayerName; local int tempIndex; local int tempPlayerColor; // Get message color. msgColor = class'NexgenUtil'.static.getMessageColor(msg); if (msgColor < 0) { msgColor = C_ORANGE; } msg = class'NexgenUtil'.static.removeMessageColorTag(msg); // Get player name indices. getPlayerNameIndices(msg, pri1, pri2, firstIndex, secondIndex); // Get player names & colors. if (pri1 != none) { firstPlayerName = pri1.playerName; firstPlayerColor = getPlayerColor(pri1); } if (pri2 != none) { secondPlayerName = pri2.playerName; secondPlayerColor = getPlayerColor(pri2); } // Swap first and second player if necessary. if (secondIndex >= 0 && (secondIndex < firstIndex || firstIndex < 0)) { tempPlayerName = secondPlayerName; tempIndex = secondIndex; tempPlayerColor = secondPlayerColor; secondPlayerName = firstPlayerName; secondIndex = firstIndex; secondPlayerColor = firstPlayerColor; firstPlayerName = tempPlayerName; firstIndex = tempIndex; firstPlayerColor = tempPlayerColor; } // Split message. if (firstIndex >= 0 && secondIndex >= 0) { msgPart1 = left(msg, firstIndex); msgPart2 = mid(msg, firstIndex + len(firstPlayerName), secondIndex - firstIndex - len(firstPlayerName)); msgPart3 = mid(msg, secondIndex + len(secondPlayerName)); } else if (firstIndex >= 0) { msgPart1 = left(msg, firstIndex); msgPart2 = mid(msg, firstIndex + len(firstPlayerName)); secondPlayerName = ""; } else { firstPlayerName = ""; secondPlayerName = ""; msgPart1 = msg; } // Add message. addMsg(msgColor, msgPart1, firstPlayerColor, firstPlayerName, msgColor, msgPart2, secondPlayerColor, secondPlayerName, msgColor, msgPart3); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the indices of player names in the given message. To speed up * the locating process you can pass the player replication info actors of the * players that are most likely to be included in the message. * $PARAM msg The message which may contain player names. * $PARAM pri1 Replication info of the first player involved. * $PARAM pri2 Replication info of the second player involved. * $PARAM index1 The location in the string where the first player name occurs. * $PARAM index2 The location in the string where the second player name occurs. * $ENSURE (index1 >= 0 ? pri1 != none : true) && (index2 >= 0 ? pri2 != none : true) * **************************************************************************************************/ simulated function getPlayerNameIndices(string msg, out PlayerReplicationInfo pri1, out PlayerReplicationInfo pri2, out int index1, out int index2) { local PlayerReplicationInfo tmpPRI; local GameReplicationInfo gri; local int index; local int nameIndex; local int tmpIndex; // Get shortcut to the game replication info. gri = client.player.gameReplicationInfo; // Initially no indices have been found. index1 = -1; index2 = -1; // Check if the first PRI is actually in the message. This appears not to be the case for some // messages (for example with the Stranglove weapon mod). if (pri1 != none && instr(msg, pri1.playerName) < 0) { pri1 = none; } // Swap player replication info's if needed. if (pri1 == none && pri2 != none) { pri1 = pri2; pri2 = none; } else if (pri1 != none && pri2 != none && len(pri2.playerName) > len(pri1.playerName)) { // Ensure the longest playername is located first. tmpPRI = pri1; pri1 = pri2; pri2 = tmpPRI; } // Get the position of the first player name in the message. if (pri1 == none) { // No PRI found, try to find one. index = 0; while (index < arrayCount(gri.PRIArray) && gri.PRIArray[index] != none) { // Get current player replication info. tmpPRI = gri.PRIArray[index]; // Get position of the players name in the message. nameIndex = instr(msg, tmpPRI.playerName); // Select PRI? if (nameIndex >= 0 && (pri1 == none || len(tmpPRI.playerName) > len(pri1.playerName))) { // Yes, no name has been found so far or a longer player name has been found. pri1 = tmpPRI; index1 = nameIndex; } // Continue with next player name. index++; } } else { // Already got PRI, just find the index of the name. index1 = instr(msg, pri1.playerName); } // Get the position of the second player name in the message. if (pri1 != none && pri2 == none) { // No PRI found, try to find one. index = 0; while (index < arrayCount(gri.PRIArray) && gri.PRIArray[index] != none) { // Get current player replication info. tmpPRI = gri.PRIArray[index]; // Get position of the players name in the message. nameIndex = instr(msg, tmpPRI.playerName); // Check for overlap. if (index1 >=0 && nameIndex >= 0 && index1 <= nameIndex && nameIndex < index1 + len(pri1.playerName)) { // Overlap detected, check if name occurs after the first player name. nameIndex = instr(mid(msg, index1 + len(pri1.playerName)), tmpPRI.playerName); if (nameIndex >= 0) { nameIndex += index1 + len(pri1.playerName); } } // Select PRI? if (nameIndex >= 0 && (pri2 == none || len(tmpPRI.playerName) > len(pri2.playerName))) { // Yes, no name has been found so far or a longer player name has been found. pri2 = tmpPRI; index2 = nameIndex; } // Continue with next player name. index++; } } else if (pri2 != none) { // Already got PRI, just find the index of the name. nameIndex = instr(msg, pri2.playerName); // Check for overlap. if (index1 >= 0 && nameIndex >= 0 && index1 <= nameIndex && nameIndex < index1 + len(pri1.playerName)) { // Overlap detected, check if name occurs after the first player name. nameIndex = instr(mid(msg, index1 + len(pri1.playerName)), pri2.playerName); if (nameIndex >= 0) { nameIndex += index1 + len(pri1.playerName); } } // Set index. index2 = nameIndex; } } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the chatbox. The message is split in several parts, so each can * be displayed in a specified color. * $PARAM col1 Color of the first part of the message. * $PARAM text1 First part of the message. * $PARAM col2 Color of the second part of the message. * $PARAM text2 Second part of the message. * $PARAM col3 Color of the third part of the message. * $PARAM text3 Third part of the message. * $PARAM col4 Color of the fourth part of the message. * $PARAM text4 Fourth part of the message. * $PARAM col5 Color of the fifth part of the message. * $PARAM text5 Fifth part of the message. * **************************************************************************************************/ simulated function addChatMsg(int col1, string text1, optional int col2, optional string text2, optional int col3, optional string text3, optional int col4, optional string text4, optional int col5, optional string text5) { local int index; // Find position in messages list. if (chatMsgCount < arrayCount(chatMessages)) { index = chatMsgCount; chatMsgCount++; } else { // List is full, shift messages. for (index = 1; index < chatMsgCount; index++) { chatMessages[index - 1] = chatMessages[index]; } index = chatMsgCount - 1; } // Store message. chatMessages[index].text[0] = text1; chatMessages[index].text[1] = text2; chatMessages[index].text[2] = text3; chatMessages[index].text[3] = text4; chatMessages[index].text[4] = text5; chatMessages[index].col[0] = col1; chatMessages[index].col[1] = col2; chatMessages[index].col[2] = col3; chatMessages[index].col[3] = col4; chatMessages[index].col[4] = col5; chatMessages[index].timeStamp = timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the area below the chatbox. The message is split in several * parts, so each can be displayed in a specified color. * $PARAM col1 Color of the first part of the message. * $PARAM text1 First part of the message. * $PARAM col2 Color of the second part of the message. * $PARAM text2 Second part of the message. * $PARAM col3 Color of the third part of the message. * $PARAM text3 Third part of the message. * $PARAM col4 Color of the fourth part of the message. * $PARAM text4 Fourth part of the message. * $PARAM col5 Color of the fifth part of the message. * $PARAM text5 Fifth part of the message. * **************************************************************************************************/ simulated function addMsg(int col1, string text1, optional int col2, optional string text2, optional int col3, optional string text3, optional int col4, optional string text4, optional int col5, optional string text5) { local int index; // Find position in messages list. if (msgCount < arrayCount(messages)) { index = msgCount; msgCount++; } else { // List is full, shift messages. for (index = 1; index < msgCount; index++) { messages[index - 1] = messages[index]; } index = msgCount - 1; } // Store message. messages[index].text[0] = text1; messages[index].text[1] = text2; messages[index].text[2] = text3; messages[index].text[3] = text4; messages[index].text[4] = text5; messages[index].col[0] = col1; messages[index].col[1] = col2; messages[index].col[2] = col3; messages[index].col[3] = col4; messages[index].col[4] = col5; messages[index].timeStamp = timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Initializes/updates the variables used in the rendering procedure. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * $ENSURE c.font != none * **************************************************************************************************/ simulated function setup(Canvas c) { local int index; local bool bUpdateBase; // Make sure the font ain't none. if (baseFont == none) baseFont = Font'SmallFont'; c.font = baseFont; // Prevent redundant setups. if (lastSetupTime == level.timeSeconds) { return; } // Timer control. timeSeconds += (level.timeSeconds - lastLevelTimeSeconds) / level.timeDilation; lastLevelTimeSeconds = level.timeSeconds; // Check if the base variables need to be updated. bUpdateBase = lastResX != c.clipX || lastResY != c.clipY || lastTeam != client.player.playerReplicationInfo.team; // Update HUD base variables. if (bUpdateBase) { // General variables. baseFont = ChallengeHUD(c.viewport.actor.myHUD).myFonts.getStaticSmallestFont(c.clipX); c.font = baseFont; c.strLen("Online [00:00]", minPanelWidth, baseFontHeight); if (client.player.playerReplicationInfo.bIsSpectator && !client.player.playerReplicationInfo.bWaitingPlayer) { index = 4; } else if (0 <= client.player.playerReplicationInfo.team && client.player.playerReplicationInfo.team <= 3) { index = client.player.playerReplicationInfo.team; } else { index = 5; } baseHUDColor = baseColors[index]; lastResX = c.clipX; lastResY = c.clipY; lastTeam = client.player.playerReplicationInfo.team; // Message box info. msgBoxWidth = int(c.clipX * 0.75); msgBoxLineHeight = int(baseFontHeight + 4.0); msgBoxHeight = msgBoxLineHeight * arrayCount(chatMessages); } // Remove expired messages. if (chatMsgCount > 0 && timeSeconds - chatMessages[0].timeStamp > chatMessageLifeTime) { for (index = 1; index < chatMsgCount; index++) { chatMessages[index - 1] = chatMessages[index]; } chatMsgCount--; if (chatMsgCount == 0) { faceImg = none; } } if (msgCount > 0 && timeSeconds - messages[0].timeStamp > messageLifeTime) { for (index = 1; index < msgCount; index++) { messages[index - 1] = messages[index]; } msgCount--; } lastSetupTime = level.timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Renders the chat message box. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * **************************************************************************************************/ simulated function renderMessageBox(Canvas c) { local float panelWidth; local float panelHeight; local PanelInfo serverState; local PanelInfo clientState; local int index; local float cx; local float cy; // Initialize. setup(c); panelHeight = int((msgBoxHeight - 3.0) / 2.0); serverState = getServerState(); clientState = getClientState(); panelWidth = fMax(getPanelWidth(serverState, c, panelHeight), getPanelWidth(clientState, c, panelHeight)); // Background. c.style = ERenderStyle.STY_Translucent; c.drawColor = baseHUDColor * 0.4; c.setPos(1.0, 1.0); c.drawTile(Texture'grad64', msgBoxWidth - 2.0, msgBoxHeight - 2.0, 0.0, 0.0, 64.0, 64.0); // Borders. c.drawColor = baseHUDColor * 0.8; c.setPos(0.0, 0.0); c.drawTile(Texture'base', msgBoxWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(0.0, msgBoxHeight - 1.0); c.drawTile(Texture'base', msgBoxWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 1.0 - panelWidth, panelHeight + 1.0); c.drawTile(Texture'base', panelWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(0.0, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 1.0, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxHeight - 1.0, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 2.0 - panelWidth, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); // Panels. renderPanel(serverState, c, panelHeight, msgBoxWidth - panelWidth - 1.0, 1.0); renderPanel(clientState, c, panelHeight, msgBoxWidth - panelWidth - 1.0, panelHeight + 2.0); // Face image. if (faceImg != none) { c.style = ERenderStyle.STY_Normal; c.drawColor = blankColor; c.setPos(1.0, 1.0); c.drawTile(faceImg, msgBoxHeight - 2.0, msgBoxHeight - 2.0, 0.0, 0.0, faceImg.uSize, faceImg.vSize); c.style = ERenderStyle.STY_Translucent; c.drawColor = baseHUDColor * 0.25; c.setPos(1.0, 1.0); c.drawTile(Texture'LadrStatic.Static_a00', msgBoxHeight - 2.0, msgBoxHeight - 2.0, 0.0, 0.0, Texture'LadrStatic.Static_a00'.uSize, Texture'LadrStatic.Static_a00'.vSize); } // Typing prompt. if (client.player.player.console.bTyping) { renderTypingPromt(c, "(>" $ client.player.player.console.typedStr $ "_"); } // Chat messages. cx = msgBoxHeight + 2.0; cy = (msgBoxLineHeight - baseFontHeight) / 2.0; for (index = 0; index < chatMsgCount; index++) { renderMessage(c, cx, cy, chatMessages[index]); cy += msgBoxLineHeight; } // Other messages. cx = 1.0; cy = msgBoxHeight + 2.0; if (client.player.player.console.bTyping) { cy += msgBoxLineHeight; } for (index = 0; index < msgCount; index++) { renderMessage(c, cx, cy, messages[index]); cy += baseFontHeight; } } /*************************************************************************************************** * * $DESCRIPTION Renders the typing promt for the chat message box. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * **************************************************************************************************/ simulated function renderTypingPromt(Canvas c, string msg) { local float msgOffset; // Background. c.style = ERenderStyle.STY_Translucent; c.drawColor = baseHUDColor * 0.4; c.setPos(1.0, msgBoxHeight); c.drawTile(Texture'grad32', msgBoxWidth - 2.0, msgBoxLineHeight - 1.0, 0.0, 0.0, 32.0, 32.0); // Borders. c.drawColor = baseHUDColor * 0.8; c.setPos(0.0, msgBoxHeight + msgBoxLineHeight - 1.0); c.drawTile(Texture'base', msgBoxWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(0.0, msgBoxHeight); c.drawTile(Texture'base', 1.0, msgBoxLineHeight - 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 1.0, msgBoxHeight); c.drawTile(Texture'base', 1.0, msgBoxLineHeight - 1.0, 0.0, 0.0, 1.0, 1.0); // Message. msgOffset = (msgBoxLineHeight - baseFontHeight) / 2.0; c.font = baseFont; c.style = ERenderStyle.STY_Normal; c.drawColor = colors[C_WHITE]; c.setPos(msgOffset, msgOffset + msgBoxHeight); c.drawText(msg, false); } /*************************************************************************************************** * * $DESCRIPTION Renders the specified message. * $PARAM c Canvas object that provides the drawing capabilities. * $PARAM x Horizontal offset. * $PARAM y Vertical offset. * $PARAM msg The message that is to be rendered. * $REQUIRE c != none && msg != none * **************************************************************************************************/ simulated function renderMessage(Canvas c, float x, float y, MessageInfo msg) { local float cx; local int msgIndex; local float msgWidth; local float msgHeight; local float lifeTime; local float blinkFactor; local float blinkIntensity; // Check if the message should blink. lifeTime = (timeSeconds - msg.timeStamp); if (bFlashMessages && lifeTime < messageBlinkTime) { blinkFactor = (1.0 + cos(lifeTime * 2 * pi * messageBlinkRate)) / 2.0; blinkIntensity = (1.0 - blinkFactor) * 255.0; } // Render message. cx = x; c.font = baseFont; c.style = ERenderStyle.STY_Normal; for (msgIndex = 0; msgIndex < 5; msgIndex++) { c.setPos(cx, y); c.drawColor = colors[msg.col[msgIndex]]; if (bFlashMessages && lifeTime < messageBlinkTime) { c.drawColor = c.drawColor * blinkColorDamp; c.drawColor.r = int(float(c.drawColor.r) * blinkFactor + blinkIntensity); c.drawColor.g = int(float(c.drawColor.g) * blinkFactor + blinkIntensity); c.drawColor.b = int(float(c.drawColor.b) * blinkFactor + blinkIntensity); } c.drawText(msg.text[msgIndex], false); c.strLen(msg.text[msgIndex], msgWidth, msgHeight); cx += msgWidth; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the current state of the server. The result will be stored in a * PanelInfo struct so it can be immediately rendered. * $RETURN The current server state. * $ENSURE result != none * **************************************************************************************************/ simulated function PanelInfo getServerState() { local PanelInfo pInf; local string stateInfo; local int remainingTime; local int minutes, seconds; local name stateType; local byte bBlink; local int index; // Check current state. if (client.player.bBadConnectionAlert) { // Server is offline. stateType = SS_Offline; if (client.sConf.autoReconnectTime > 0) { stateInfo = string(max(0, 1 + client.sConf.autoReconnectTime - (client.timeSeconds - client.badConnectionSince))); pInf.text = client.lng.format(client.lng.offlineStateRCN, stateInfo); } else { pInf.text = client.lng.offlineState; } pInf.textCol = colors[C_RED]; pInf.icon = Texture'offlineIcon'; pInf.solidIcon = Texture'offlineIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Waiting) { // Waiting for players. stateType = SS_Waiting; pInf.text = client.lng.format(client.lng.waitingState, client.gInf.countDown); pInf.textCol = colors[C_RED]; pInf.icon = Texture'waitIcon'; pInf.solidIcon = Texture'waitIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Ready) { // Ready to start the game. stateType = SS_Ready; pInf.text = client.lng.readyState; pInf.textCol = colors[C_ORANGE]; pInf.bBlink = true; pInf.icon = Texture'waitIcon'; pInf.solidIcon = Texture'waitIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Starting) { // Game is starting. stateType = SS_Starting; pInf.text = client.lng.format(client.lng.startingState, client.gInf.countDown); pInf.textCol = colors[C_YELLOW]; pInf.bBlink = true; pInf.icon = Texture'waitIcon'; pInf.solidIcon = Texture'waitIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Ended) { // Game has ended. stateType = SS_Ended; pInf.text = client.lng.endedState; pInf.textCol = colors[C_YELLOW]; pInf.icon = Texture'serverIcon'; pInf.solidIcon = Texture'serverIcon2'; } else if (level.pauser != "") { // Game has been paused. stateType = SS_Paused; pInf.text = client.lng.pausedState; pInf.textCol = colors[C_METAL]; pInf.bBlink = true; pInf.icon = Texture'offlineIcon'; pInf.solidIcon = Texture'offlineIcon2'; } else if (client.sConf.matchModeActivated) { // Match in progress. stateType = SS_Match; remainingTime = client.player.gameReplicationInfo.remainingTime ; if (remainingTime > 0 && (remainingTime / matchInfoSwitchTime) % 2 == 1) { minutes = remainingTime / secPerMinute; seconds = remainingTime % secPerMinute; stateInfo = right("0" $ minutes, 2) $ ":" $ right("0" $ seconds, 2); } else { stateInfo = client.sConf.currentMatch $ "/" $ client.sConf.matchesToPlay; } pInf.text = client.lng.format(client.lng.matchState, stateInfo); pInf.textCol = colors[C_YELLOW]; pInf.icon = Texture'matchIcon'; pInf.solidIcon = Texture'matchIcon2'; } else { // Game in progress. stateType = SS_Normal; remainingTime = client.player.gameReplicationInfo.remainingTime ; if (remainingTime > 0) { minutes = remainingTime / secPerMinute; seconds = remainingTime % secPerMinute; stateInfo = right("0" $ minutes, 2) $ ":" $ right("0" $ seconds, 2); } else { stateInfo = right("0" $ level.hour, 2) $ ":" $ right("0" $ level.minute, 2); } pInf.text = client.lng.format(client.lng.onlineState, stateInfo); pInf.textCol = colors[C_GREEN]; pInf.icon = Texture'serverIcon'; pInf.solidIcon = Texture'serverIcon2'; } // Allow plugins to modify the state panel. bBlink = byte(pInf.bBlink); while (index < arrayCount(client.clientCtrl) && client.clientCtrl[index] != none) { if (client.clientCtrl[index].bCanModifyHUDStatePanel) { client.clientCtrl[index].modifyServerState(stateType, pInf.text, pInf.textCol, pInf.icon, pInf.solidIcon, bBlink); } index++; } pInf.bBlink = bool(bBlink); // Return state panel. return pInf; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the current state of the client. The result will be stored in a * PanelInfo struct so it can be immediately rendered. * $RETURN The current client state. * $ENSURE result != none * **************************************************************************************************/ simulated function PanelInfo getClientState() { local PanelInfo pInf; local string stateInfo; local byte protectTime; local name stateType; local byte bBlink; local int index; pInf.icon = Texture'playerIcon'; pInf.solidIcon = Texture'playerIcon2'; // Check current state. if (!client.loginComplete) { // Logging in to the server. stateType = CS_Login; pInf.text = client.lng.loginState; pInf.textCol = colors[C_WHITE]; } else if (client.idleTimeRemaining >= 0) { // Idle / camping. stateType = CS_Idle; stateInfo = string(client.idleTimeRemaining); pInf.text = client.lng.format(client.lng.idleState, stateInfo); pInf.textCol = colors[C_CYAN]; pInf.icon = Texture'idleIcon'; pInf.solidIcon = Texture'idleIcon2'; pInf.bBlink = true; } else if (client.spawnProtectionTime > 0 || client.tkDmgProtectionTime > 0 || client.tkPushProtectionTime > 0) { // Client is damage protected. stateType = CS_Protected; protectTime = max(max(client.spawnProtectionTime, client.tkDmgProtectionTime), client.tkPushProtectionTime); pInf.text = client.lng.format(client.lng.protectedState, protectTime); if (client.tkDmgProtectionTime > 0) { pInf.textCol = colors[C_ORANGE]; } else { pInf.textCol = colors[C_YELLOW]; } pInf.icon = Texture'shieldIcon'; pInf.solidIcon = Texture'shieldIcon2'; } else if (client.bMuted || client.gInf.bMuteAll) { // Client is muted. stateType = CS_Muted; pInf.text = client.lng.mutedState; pInf.textCol = colors[C_RED]; pInf.icon = Texture'mutedIcon'; pInf.solidIcon = Texture'mutedIcon2'; } else if (client.player.health <= 0) { // Player is dead. stateType = CS_Dead; pInf.text = client.lng.deadState; pInf.textCol = colors[C_RED]; } else { // Normal state. stateType = CS_Normal; pInf.text = client.title; if (client.bSpectator) { pInf.textCol = colors[C_CYAN]; pInf.icon = Texture'specIcon'; pInf.solidIcon = Texture'specIcon2'; } else { pInf.textCol = colors[C_GREEN]; } } // Allow plugins to modify the state panel. bBlink = byte(pInf.bBlink); while (index < arrayCount(client.clientCtrl) && client.clientCtrl[index] != none) { if (client.clientCtrl[index].bCanModifyHUDStatePanel) { client.clientCtrl[index].modifyServerState(stateType, pInf.text, pInf.textCol, pInf.icon, pInf.solidIcon, bBlink); } index++; } pInf.bBlink = bool(bBlink); // Return state panel. return pInf; } /*************************************************************************************************** * * $DESCRIPTION Returns the space necessary for the given panel if it is to be rendered. * $PARAM pInf Panel contents. * $PARAM c Canvas object that provides the drawing capabilities. * $PARAM panelHeight Vertical space available for the panel (in pixels). * $REQUIRE pInf != none && c != none && panelHeight > 0 * $RETURN * $ENSURE result > 0 * **************************************************************************************************/ simulated function float getPanelWidth(PanelInfo pInf, canvas c, float panelHeight) { local float separatorWidth; local float width; local float temp; // Get text width. separatorWidth = int(baseFontHeight * 0.4); c.font = baseFont; c.strLen(pInf.text, width, temp); width = fMax(minPanelWidth, width); // And add icon and separator width. if (pInf.icon == none) { width += 2.0 * separatorWidth; } else { if (iconSize > panelHeight) { width += panelHeight + 3.0 * separatorWidth; } else { width += iconSize + 3.0 * separatorWidth; } } return width; } /*************************************************************************************************** * * $DESCRIPTION Renders a panel with the specified contents at the given location. * $PARAM pInf Panel contents. * $PARAM c Canvas object that provides the drawing capabilities. * $PARAM panelHeight Vertical space available for the panel (in pixels). * $PARAM x Horizontal offset. * $PARAM y Vertical offset. * $REQUIRE pInf != none && c != none && panelHeight > 0 * **************************************************************************************************/ simulated function renderPanel(PanelInfo pInf, canvas c, float panelHeight, float x, float y) { local float separatorWidth; local float cx; local float cy; local float iconHeight; local float lifeTime; local float blinkFactor; local float blinkIntensity; if (pInf.bBlink) { blinkFactor = (1.0 + cos(timeSeconds * 2 * pi * panelBlinkRate)) / 2.0; blinkIntensity = (1.0 - blinkFactor) * 255.0; } separatorWidth = int(baseFontHeight * 0.4); // Draw icon. cx = x + separatorWidth; if (pInf.icon != none) { if (iconSize > panelHeight) { iconHeight = panelHeight; cy = y; } else { iconHeight = iconSize; cy = y + int((panelHeight - iconSize) / 2.0); } c.style = ERenderStyle.STY_Translucent; c.drawColor = blankColor; c.setPos(cx, cy); c.drawTile(pInf.icon, iconHeight, iconHeight, 0.0, 0.0, iconSize, iconSize); if (pInf.solidIcon != none) { c.style = ERenderStyle.STY_Normal; c.setPos(cx, cy); c.drawTile(pInf.solidIcon, iconHeight, iconHeight, 0.0, 0.0, iconSize, iconSize); } cx += separatorWidth + iconHeight; } // Draw the text. cy = y + (panelHeight - baseFontHeight) / 2.0; c.style = ERenderStyle.STY_Normal; c.drawColor = pInf.textCol; if (pInf.bBlink) { c.drawColor = c.drawColor * blinkColorDamp; c.drawColor.r = int(float(c.drawColor.r) * blinkFactor + blinkIntensity); c.drawColor.g = int(float(c.drawColor.g) * blinkFactor + blinkIntensity); c.drawColor.b = int(float(c.drawColor.b) * blinkFactor + blinkIntensity); } c.setPos(cx, cy); c.font = baseFont; c.drawText(pInf.text, false); } /*************************************************************************************************** * * $DESCRIPTION Returns the color for the specified player. * $PARAM pri Replication info of the player whose color is to be returned. * $RETURN The color of the player (index in the base color palette). * $ENSURE 0 <= result && result < arrayCount(colors) * **************************************************************************************************/ simulated function int getPlayerColor(PlayerReplicationInfo pri) { local int colorNum; if (pri.bIsSpectator && !pri.bWaitingPlayer) { colorNum = C_METAL; } else if (0 <= pri.team && pri.team <= 3) { colorNum = pri.team; } else { colorNum = C_WHITE; } return colorNum; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ s=s"‰Ž¨Ds"=‚–P}DzDP& ¥P DDPP“}D&‹‚™P%zDP& ¦P_D€D’P&D  A>x=D=r=B•¢a/!Y.¼•¢öD(yb<Øwy*Á‚‚y-E˜%yQ–yQ,¥yQd¥Byy@X–“PddOdd,(t ‘BGfiM%9–M/„–Mdt —Md’t &Ý–Mdt b Pd¥Md¦b dNb Mfi,b Od¦Md¥b dNMb fiG¥M8' P€@X'/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenGameInfo * $VERSION 1.04 (15-12-2007 17:17) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended game info container class. Stores all Nexgen related game extension data. * **************************************************************************************************/ class NexgenGameInfo extends ReplicationInfo; var int updateCount; // How many times the settings have been updated during the current // game. Used to detect setting changes clientside. var bool bTeamsLocked; // Teams have been locked. Nobody may switch or join (as player). var bool bNoTeamSwitch; // Whether team switching has been disabled. var bool bNoTeamBalance; // Whether team balancing has been disabled. var byte gameState; // Current state of the game. var int countDown; // Countdown timer for the gamestate. var byte matchAdminCount; // Number of match admins logged in. var byte maxTeams; // Number of teams available in the game. var bool bMuteAll; // Whether all players are muted. var float gameSpeed; // Game speed multiplier. var bool bNoNameChange; // Whether players can't change their name during the game. var byte rebootCountDown; // Seconds remaining before the server will be rebooted. // Game states. const GS_Waiting = 0; // Waiting for players. const GS_Ready = 1; // Ready for launch. const GS_Starting = 2; // Game is starting. const GS_Playing = 3; // Game is in progress. const GS_Ended = 4; // The game has ended. //const GS_Loading = 5; // Loading a new game. // Game info change events const IT_GlobalRights = 0; // Global game rights. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) bTeamsLocked, bNoTeamSwitch, bNoTeamBalance, gameState, countDown, maxTeams, bMuteAll, gameSpeed, bNoNameChange, rebootCountDown, updateCount; } b/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenEditControl * $VERSION 1.00 (20-10-2007 23:13) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen extended edit control GUI component class. * **************************************************************************************************/ class NexgenEditControl extends UWindowEditControl; /*************************************************************************************************** * * $DESCRIPTION Creates the setup for this GUI component. * $OVERRIDE * **************************************************************************************************/ function created() { super(UWindowDialogControl).created(); editBox = UWindowEditBox(createWindow(class'NexgenEditBox', 0, 0, winWidth, winHeight)); editBox.notifyOwner = self; editBox.bSelectOnFocus = true; editBoxWidth = winWidth / 2; setEditTextColor(lookAndFeel.editBoxTextColor); } /*************************************************************************************************** * * $DESCRIPTION Disables or enables the component. In the disabled state an user can't change the * value entered in the editbox. * $PARAM bDisabled Indicates whether or not the component should be disabled. * **************************************************************************************************/ function setDisabled(bool bDisabled) { NexgenEditBox(editBox).bDisabled = bDisabled; editBox.bCanEdit = !bDisabled; } {=r"U¬ªm_r"1–}_}_pq"_ e‚™j}—}_j_ê_j_  ôC>kâ:(„rÑ*Ña/!^a b.Ñ K>{"Vè}fE{"-D']‚-D {E #EuE-Du-D b 4€_€@}!/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenEditBox * $VERSION 1.01 (21-10-2007 11:02) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen extended editbox GUI component class. * **************************************************************************************************/ class NexgenEditBox extends UWindowEditBox; var bool bDisabled; // Whether the component is enabled or not. /*************************************************************************************************** * * $DESCRIPTION Renders the GUI component. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y) { local float w, h; local float textY; if (bDisabled) { c.drawColor.r = 192; c.drawColor.g = 192; c.drawColor.b = 192; drawStretchedTexture(c, 0, 0, winWidth, winHeight, Texture'UWindow.WhiteTexture'); } super.paint(c, x, y); } /*************************************************************************************************** * * $DESCRIPTION Changes the value entered in the edit box. This is a fixed version of the * UWindow.UWindowEditBox setValue function, which didn't updated the caretOffset * when bAllSelected was set to true. * $PARAM newValue The new value of the edit box. * $PARAM newValue2 New alternate value. * $OVERRIDE * **************************************************************************************************/ function setValue(string newValue, optional string newValue2) { value = newValue; value2 = newValue2; if (bAllSelected || caretOffset > len(value)) { caretOffset = len(value); } notify(DE_Change); } @>o"tÌimgo"1–}g@gpgj" e‚™h@—}ghg€ghg  w=-WáÅ+¶-æ.¹-ÍÛêÈr-÷OE^ % {OK~O\n¾—K%{€OKOO’K}\nÑ{OO-aÐ{PPRúRDP¥^ g^’R,, \D®«?^ P?,, \‘D¯-÷?^,]‘D¯-ý?\,-á$-­b(TÍÌÌ>-·?\?]-(aÒ L)?^?\€B€B-­b(TÍÌL?- ·¯?\@@¯?]€?-.aÒ ]@®?\@€?€?-(·®®?\€??^¯?]€?-.aÒ ]@®?\@€?€?- ·¯?\@@¯?]@-&aÒ ] A€?€?€?-(·¯?\@@®®?]€??\-&aÒ ] A€?€?€?--·¯®®?\@@?^?,¯?]@-&aÒ ] A€?€?€?-5·¯®®?\@@?^?,®®?]€??\-&aÒ ] A€?€?€?Ö•-ëC¬­]??-­b(T¯€?CbD«»««CÛI@??,@nD®?,«?,‘’^,@^C-0·¯¯?\@@?b¯¯?]€??b-:aÒ ]@®®?\@?,b€?€?-8·®®®?\€??^?b¯¯?]€??b-:aÒ ]@®®?\@?,b€?€?-0·¯¯?\@@?b¯¯?]@?b-'aÒ ]?n€?€?€?-8·¯¯?\@@?b®®®?]€??\?b-'aÒ ]?n€?€?€?-@·®¯®®?\@@?^?n?b¯¯?]@?b-'aÒ ]?n€?€?€?-H·®¯®®?\@@?^?n?b®®®?]€??\?b-'aÒ ]?n€?€?€?OE^ %-­T"´{OK~O\nS—K%{€OKOO’K}\nf{OO-)·?’\, ®?’], «?^ P- aÑ{(¥^ ü q€Q>F<¶dÝH‚-S –q,@>|6e qUF-S'E¥qŸ-Sq%Ÿ‚-S –q,@•z6e qU-S'œ¥qZÔ-S6e qUF6SqUl"-S P>m"ýV{&™~pp,h ,pp,m",% g€@i= 30.0G>h"nðž! zh"R~z H„–R%{ë€zRNSC(zzRR~z ˆ–R%Az'A€zRzpzR R%GÿÿÿÿA ÿÿÿÿ‚–R}z –D, mzR&3zm 0‚™G% -`A R‡zm"d–G%G’R&-`'„„{G\A R-`(–G%GRñ™A %¥De"pzG“A G\""GÿÿÿÿA ÿÿÿÿGm¥RÎ'  a€U>q JÝ*wq  Dq~–q %N-Ezt}ez i$h % h-c'z h=q Ø–q  €O%h q  Ih q  €O h Y>f"vù>a -f"5zag"<La z€q/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenDummyComponent * $VERSION 1.00 (7-3-2007 20:49) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dummy GUI component. This component only marks the occupied area, but doesn't * have any means of interaction with the user. Should only be used for testing. * **************************************************************************************************/ class NexgenDummyComponent extends UWindowWindow; /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ super.paint(c, x, y); drawUpBevel(c, 0, 0, winWidth, winHeight, getLookAndFeelTexture()); } G¡/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenCPKeyBind * $VERSION 1.04 (19-1-2008 17:51) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen keybind settings panel. * **************************************************************************************************/ class NexgenCPKeyBind extends NexgenPanel; var UMenuRaisedButton bindButton[9]; // Keybind buttons. var string bindCommand[9]; // Console commands for the key binds. var int selectedBind; // Currently selected key. var bool bPolling; // Waiting for a new key assignment. const bindSeparator = "|"; // Token used to seperate commands in an action string. const getKeyNameCommand = "keyname"; // Console command to retrieve a key name. const getKeyBindCommand = "keybinding"; // Console command to retrieve the action bound to a key. const setKeyBindCommand = "set input"; // Console command to change a key binding. /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local int index; // Create layout & add components. setAcceptsFocus(); createPanelRootRegion(); splitRegionH(16); addLabel(client.lng.keyBindsTxt, true, TA_Center); divideRegionV(2); divideRegionH(arrayCount(bindButton)); divideRegionH(arrayCount(bindButton)); addLabel(client.lng.balanceBindTxt); addLabel(client.lng.switchRedBindTxt); addLabel(client.lng.switchBlueBindTxt); addLabel(client.lng.switchGreenBindTxt); addLabel(client.lng.switchGoldBindTxt); addLabel(client.lng.suicideBindTxt); addLabel(client.lng.openMapVoteBindTxt); addLabel(client.lng.openCPBindTxt); addLabel(client.lng.pauseGameBindTxt); for (index = 0; index < arrayCount(bindButton); index++) { bindButton[index] = addRaisedButton(); bindButton[index].align = TA_Center; bindButton[index].bAcceptsFocus = false; bindButton[index].bIgnoreLDoubleClick = true; bindButton[index].bIgnoreMDoubleClick = true; bindButton[index].bIgnoreRDoubleClick = true; bindButton[index].index = index; bindButton[index].register(self); } loadKeyBinds(); } /*************************************************************************************************** * * $DESCRIPTION Loads the keybind settings and displays them on the config panel. * **************************************************************************************************/ function loadKeyBinds() { local int keyNum; local string keyName; local string keyAction; local int index; // Iterate over all keys. for (keyNum = 0; keyNum < 255; keyNum++) { keyName = client.player.consoleCommand(getKeyNameCommand @ keyNum); if (keyName != "") { // Get action assigned to key. keyAction = client.player.consoleCommand(getKeyBindCommand @ keyName); // Check action string with the keybind commands. for (index = 0; index < arrayCount(bindButton); index++) { if (containsCommand(keyAction, bindCommand[index])) { bindButton[index].text = keyName; } } } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether a keybind action contains one of the specified commands. Commands * are separated by the 'separator' token (comma). * $PARAM action Keybind action string. * $PARAM commands List of commands to check for. * $REQUIRE commands != "" * $RETURN True if the action string contains one of the specified commands. * **************************************************************************************************/ function bool containsCommand(string action, string commands) { local string cmd; local string remaining; local int index; local bool bFound; action = caps(action); // For each command in the command string (separated by commas). remaining = caps(commands); while (remaining != "" && !bFound) { // Get next command. index = instr(remaining, separator); if (index < 0) { cmd = remaining; remaining = ""; } else { cmd = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Compare command. bFound = instr(action, cmd) >= 0; } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local int index; super.notify(control, eventType); if (eventType == DE_Click) { // Keybind button clicked? if (control != none && control.isA('UMenuRaisedButton')) { index = UMenuRaisedButton(control).index; if (bPolling && selectedBind == index) { // Clicked on same button, cancel polling. bindButton[selectedBind].bDisabled = false; bPolling = false; } else if (bPolling) { // New key bind button selected. bindButton[selectedBind].bDisabled = false; bindButton[index].bDisabled = true; selectedBind = index; } else { // No key bind button selected yet. bindButton[index].bDisabled = true; selectedBind = index; bPolling = true; } } else if (bPolling) { // Clicked elsewhere, but still polling, cancel action. bindButton[selectedBind].bDisabled = false; bPolling = false; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of a key press event. * $PARAM key The number of the key that was pressed. * $PARAM x Unknown, x location of mouse cursor? * $PARAM y Unknown, x location of mouse cursor? * $OVERRIDE * **************************************************************************************************/ function keyDown(int key, float x, float y) { local string keyName; keyName = client.player.consoleCommand(getKeyNameCommand @ key); // Assign new key binding? if (bPolling && keyName != "") { // Remove old binding. removeKeybind(bindButton[selectedBind].text, bindCommand[selectedBind]); // Add new binding. addKeybind(keyName, bindCommand[selectedBind]); // Update buttons. bindButton[selectedBind].bDisabled = false; bindButton[selectedBind].text = keyName; bPolling = false; } } /*************************************************************************************************** * * $DESCRIPTION Removes the specified commands from the action bound to the given key. * $PARAM keyName Name of the key for which the bindings should be updated. * $PARAM commands List of commands to remove from the action string. * $REQUIRE keyName != "" * **************************************************************************************************/ function removeKeybind(String keyName, string commands) { local string actionStr; local string remaining; local string cmd; local int index; // Get action string. actionStr = client.player.consoleCommand(getKeyBindCommand @ keyName); // Update action string. remaining = caps(commands); while (remaining != "") { // Get next command. index = instr(remaining, separator); if (index < 0) { cmd = remaining; remaining = ""; } else { cmd = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Remove command from action string. index = instr(caps(actionStr), cmd); if (index >= 0) { actionStr = left(actionStr, index) $ mid(actionStr, index + len(cmd)); if (mid(actionStr, index, len(bindSeparator)) == bindSeparator) { // Remove | token after command. actionStr = left(actionStr, index) $ mid(actionStr, index + len(bindSeparator)); } } } // Store action string. client.player.consoleCommand(setKeyBindCommand @ keyName @ actionStr); } /*************************************************************************************************** * * $DESCRIPTION Adds the specified command from to the action bound to the given key. * $PARAM keyName Name of the key for which the bindings should be updated. * $PARAM command Commands to add to the action string. * $REQUIRE keyName != "" * **************************************************************************************************/ function addKeybind(String keyName, string command) { local string actionStr; local string cmd; // Get action string. actionStr = client.player.consoleCommand(getKeyBindCommand @ keyName); // Update action string. if (instr(command, separator) >= 0) { cmd = left(command, instr(command, separator)); } else { cmd = command; } if (class'NexgenUtil'.static.trim(actionStr) == "") { // No action bound yet. actionStr = cmd; } else { // Some actions already bound. actionStr = actionStr $ bindSeparator $ cmd; } // Store action string. client.player.consoleCommand(setKeyBindCommand @ keyName @ actionStr); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ W>C4¼Csåãš:Ë:$e ICuw×Ý*×Ýde!uãwê*¼-lêdC!uãêde!u Â_€]>\­qt\  _ /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenCorePlugin * $VERSION 1.05 (9-3-2008 11:11) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Base Nexgen controller plugin. * **************************************************************************************************/ class NexgenCorePlugin extends NexgenPlugin; var Actor ipToCountry; // IpToCountry actor. // Controller settings. const timerFreq = 2.0; // Timer tick frequency. // Extra player attributes. const PA_Muted = "muted"; // Whether the player is muted. const PA_NoTeamSwitch = "noTeamSwitch"; // Whether the player is not allowed to switch to // another team. /*************************************************************************************************** * * $DESCRIPTION Initializes the plugin. Note that if this function returns false the plugin will * be destroyed and is not to be used anywhere. * $RETURN True if the initialization succeeded, false if it failed. * $OVERRIDE * **************************************************************************************************/ function bool initialize() { local Actor a; // Set info panel classes. control.sConf.serverInfoPanelClass = class'NexgenRCPServerInfo'; control.sConf.gameInfoPanelClass = class'NexgenRCPGameInfo'; // Get IP-to-country actor. foreach allActors(class'Actor', a, 'IpToCountry') { ipToCountry = a; break; } // Set timer. setTimer(1.0 / timerFreq, true); return true; } /*************************************************************************************************** * * $DESCRIPTION Plugin main loop. * $OVERRIDE * **************************************************************************************************/ function timer() { // Update client country codes. if (ipToCountry != none) { updateCountryCodes(); } } /*************************************************************************************************** * * $DESCRIPTION Updates the country codes for players that don't have a country code set. * **************************************************************************************************/ function updateCountryCodes() { local NexgenClient client; local bool bCancelled; local string ipInfo; // Search for clients with no country codes. client = control.clientList; while (client != none && !bCancelled) { // Country not set? if (client.country == "") { // Get ip info. ipInfo = ipToCountry.getItemName(client.ipAddress); // Parse ip info string. if (left(ipInfo, 1) == "!") { // Status code returned. switch (caps(ipInfo)) { //case "!ADDED TO QUEUE": break //case "!WAITING IN QUEUE": break //case "!RESOLVING NOW": break //case "!AOL - TRYING TO CLARIFY": break case "!BAD INPUT": client.country = "none"; break; //case "!QUEUE FULL": break case "!DISABLED": bCancelled = true; break; } } else if (ipInfo != "") { // Ip information received. if (right(ipInfo, 4) ~= "none") { client.country = "none"; } else { client.country = right(ipInfo, 2); control.announcePlayerAttrChange(client, client.PA_Country, client.country); } } } // Continue with next client. client = client.nextClient; } // Disable IpToCountry support in case of errors. if (bCancelled) { ipToCountry = none; } } /*************************************************************************************************** * * $DESCRIPTION Called when a new client has been created. Use this function to setup the new * client with your own extensions (in order to support the plugin). * $PARAM client The client that was just created. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function clientCreated(NexgenClient client) { client.addController(class'NexgenClientCore'); } /*************************************************************************************************** * * $DESCRIPTION Called whenever a player has joined the game (after its login has been accepted). * $PARAM newClient The player that has joined the game. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerJoined(NexgenClient newClient) { local NexgenClient client; local string args; local string playerName; // Signal player join events, step 1: new client to all others. args = getPlayerJoinEventArgs(newClient); for (client = control.clientList; client != none; client = client.nextClient) { // Cannot send to itself yet, client has to be initialized first. This can be safely done // in step 2 (see clientInitialized). if (client != newClient) { client.playerEvent(newClient.playerNum, client.PE_PlayerJoined, args); } } // Restore saved player data. playerName = class'NexgenUtil'.static.trim(newClient.pDat.get(newClient.PA_Name)); if (control.gInf.bNoNameChange && playerName != "" && playerName != newClient.playerName) { newClient.changeName(playerName); } newClient.bMuted = newClient.pDat.getBool(PA_Muted); newClient.bNoTeamSwitch = newClient.pDat.getBool(PA_NoTeamSwitch); } /*************************************************************************************************** * * $DESCRIPTION Called whenever a client has finished its initialisation process. During this * process things such as the remote control window are created. So only after the * client is fully initialized all functions can be safely called. * $PARAM client The client that has finished initializing. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function clientInitialized(NexgenClient newClient) { local NexgenClient client; local string args; // Signal player join events, step 2: all others to new client. for (client = control.clientList; client != none; client = client.nextClient) { args = getPlayerJoinEventArgs(client); newClient.playerEvent(client.playerNum, client.PE_PlayerJoined, args); } } /*************************************************************************************************** * * $DESCRIPTION Generates a string containing the relevant arguments for a player join event. * $PARAM client The player that has joned the game. * $REQUIRE client != none * $RETURN A string representation of the arguments. * $ENSURE result != "" * **************************************************************************************************/ function string getPlayerJoinEventArgs(NexgenClient client) { local string args; local int teamNum; teamNum = client.team; if (client.bSpectator) { teamNum = 5; } else if (teamNum < 0 || teamNum > 3) { teamNum = 4; } class'NexgenUtil'.static.addProperty(args, client.PA_ClientID, client.playerID); class'NexgenUtil'.static.addProperty(args, client.PA_IPAddress, client.ipAddress); class'NexgenUtil'.static.addProperty(args, client.PA_Name, client.playerName); class'NexgenUtil'.static.addProperty(args, client.PA_Title, client.title); class'NexgenUtil'.static.addProperty(args, client.PA_Team, teamNum); class'NexgenUtil'.static.addProperty(args, client.PA_Country, client.country); return args; } /*************************************************************************************************** * * $DESCRIPTION Called if a player has left the server. * $PARAM oldClient The player that has left the game. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerLeft(NexgenClient oldClient) { local NexgenClient client; // Signal player left events. for (client = control.clientList; client != none; client = client.nextClient) { client.playerEvent(oldClient.playerNum, client.PE_PlayerLeft); } // Store saved player data. oldClient.pDat.set(oldClient.PA_Name, oldClient.playerName); oldClient.pDat.set(PA_Muted, oldClient.bMuted); oldClient.pDat.set(PA_NoTeamSwitch, oldClient.bNoTeamSwitch); } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has switched to another team. * $PARAM client The client that has changed team. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerTeamChanged(NexgenClient client) { control.announcePlayerAttrChange(client, client.PA_Team, client.team); } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has changed his or her name. * $PARAM client The client that has changed name. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerNameChanged(NexgenClient client) { control.announcePlayerAttrChange(client, client.PA_Name, client.playerName); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ ^>a"w‡¾>_ -a"5z_b"<J_ X€N>o^A~*g]p[",p\,BzooppH=]eoppppo,H=]  Zæ /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenController * $VERSION 1.44 (11-3-2008 21:16) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION The Nexgen Server Controller. * **************************************************************************************************/ class NexgenController extends Mutator config(Nexgen); var config bool bUseExternalConfig; // Use an external configuration file rather than using the // servers system config file. //var config string language; // Language to use. var bool bSpecialMode; // Indicates if the server is running in special mode. // During this mode nexgen is executing a special server // process and the current game isn't open for players. var NexgenConfig sConf; // Server configuration. var NexgenClient clientList; // First client of the linked client list. var NexgenCommandHandler cmdHandler; // Handles the execution of nexgen commands. var NexgenGameInfo gInf; // Extended game info. var NexgenLang lng; // Language instance to support localization. var NexgenPlugin plugins[16]; // Controller plugins. var NexgenTeamBalancer teamBalancer; // Team balancer. var NexgenPlayerData playerDataList; // First player data object of the linked player data list. var class loginHandler; // Client login handler. var int nextPlayerNum; // Next client ID num. var int joinOverrideCodes[8]; // Override codes for players trying to enter a locked game. var float overrideCodeLeaseTimes[8]; // Time at which an override code was leased. const maxOverrideCodeLeaseTime = 15.0; // Maximum time an override code is valid. const joinOverrideCodeOption = "NXOC"; // Player login option name of the login override code. var float lastPlayerLeftTime; // Time at which the last player has left the server. var float lastTimeDilation; // Last known time dilation value. var bool bUTPureEnabled; // Whether UTPure has been found on the server. // Timer control. var float virtualTimerCounter; // Just like Acotr.TimerCounter for our virtual timer. var float timeSeconds; // Just like level.timeSeconds, but independent of the gamespeed. // Controller settings. const logTag = 'NSC'; // Console log tag. const timerFreq = 10.0; // Frequency of the main timer in Hz. const serverPauserName = "server"; // Name to use for level.pauser when the server pauses the // game instead of a player. // Nexgen commands. const CMD_Prefix = "NSC"; const CMD_SwitchTeam = "SETTEAM"; const CMD_BalanceTeams = "BALANCETEAMS"; const CMD_Play = "PLAY"; const CMD_Spectate = "SPECTATE"; const CMD_StartGame = "START"; const CMD_Exit = "EXIT"; const CMD_Disconnect = "DISCONNECT"; const CMD_Open = "OPENRCP"; const CMD_OpenVote = "OPENVOTE"; const CMD_Pause = "PAUSE"; // Console commands. const rebootCommand = "debug gpf"; // Server console command for rebooting the server. // Damage types. const suicideDamageType = 'Suicided'; const fallDamageType = 'Fell'; const burnDamageType = 'Burned'; const corrodeDamageType = 'CorrodedMessage'; // Log types. const LT_System = 0; // Nexgen system generated log message. const LT_Event = 1; // Nexgen broadcasted message. const LT_Message = 2; // Mutator broadcasted message. const LT_Say = 3; // Normal chat message. const LT_TeamSay = 4; // Team say chat message. const LT_PrivateMsg = 5; // Private chat message. // Reject types. const RT_DuplicateID = 'duplicateid'; const RT_Banned = 'banned'; const RT_InvalidPassword = 'invalidpass'; const RT_ServerFull = 'serverfull'; const RT_NoPlayRight = 'noplayright'; // Misc constants. const secondsPerMinute = 60; // The number of seconds in a minute. /*************************************************************************************************** * * $DESCRIPTION Starts the Nexgen server controller. * $OVERRIDE * **************************************************************************************************/ function preBeginPlay() { local int index; // Load language localization support. lng = spawn(class'NexgenLang'); nscLog(lng.startingControllerMsg); // Check server mode. if (level.netMode != NM_DedicatedServer) { nscLog(lng.noDedicatedServerMsg); destroy(); return; } // Check for compatibility issues with other mutators. doCompatibilityCheck(); // Load settings. if (bUseExternalConfig) { sConf = spawn(class'NexgenConfigExt', self); } else { sConf = spawn(class'NexgenConfigSys', self); } if (!sConf.bInstalled) { nscLog(lng.autoInstallMsg); sConf.install(); } // Check configuration. if (!sConf.checkConfig()) { nscLog(lng.invalidConfigMsg); } // Check if nexgen boot should be executed. if (isFirstGame() && (sConf.enableBootControl || sConf.enableMatchBootControl && sConf.matchModeActivated)) { nscLog(lng.nexgenBootMsg); // Execute Nexgen boot. bSpecialMode = doNexgenBoot(); // Check if Nexgen boot has been initialized. if (bSpecialMode) { // Yes, stop the initialization process. return; } else { // No, continue in normal mode. nscLog(lng.nexgenBootFailMsg); } } // Post initialize (replication) info. sConf.postInitialize(); nscLog(lng.format(lng.attrServerIDMsg, class'NexgenUtil'.static.formatGUID(sConf.serverID))); // Apply configuration. applyConfig(); // Setup current game. initGameInfo(); // Setup for Nexgen controlled game state. DeathMatchPlus(level.game).bNetReady = false; if (bUTPureEnabled) { // Disable UTPure warmup, which doesn't work with NSC. DeathMatchPlus(level.game).countDown = 1; } // Replace HUD class. if (sConf.useNexgenMessageHUD && !bUTPureEnabled) { setReplacementHUDClass(); } // Load command handler. cmdHandler = spawn(class'NexgenCommandHandler', self); // Load team balancer. if (level.game.isA('TeamGamePlus')) { teamBalancer = spawn(class'NexgenTeamBalancer', self); } // Register controller. nextMutator = level.game.baseMutator; level.game.baseMutator = self; level.game.registerMessageMutator(self); level.game.registerDamageMutator(self); // Set join override codes. for (index = 0; index < arrayCount(joinOverrideCodes); index++) { joinOverrideCodes[index] = -1; } // Load core plugin. spawn(class'NexgenCorePlugin', self); // Let mutator class initialize. super.preBeginPlay(); // Make sure the checksums are up to date. sConf.updateChecksum(); sConf.staticChecksum = sConf.calcStaticChecksum(); // Get time dilation value. lastTimeDilation = level.timeDilation; // Set client login handler class. loginHandler = class'NexgenClientLoginHandler'; // Start running the main loop (via a timer). setTimer(1.0 / timerFreq * level.timeDilation, true); nscLog(lng.nexgenActiveMsg); } /*************************************************************************************************** * * $DESCRIPTION Checks whether this is the first game played since the server was started. * $RETURN True if this is the the first game, false if not. * **************************************************************************************************/ function bool isFirstGame() { return getURLMap() == ""; } /*************************************************************************************************** * * $DESCRIPTION Starts the Nexgen boot sequence. * $RETURN True if the boot sequence was successfully executed, false if not. * **************************************************************************************************/ function bool doNexgenBoot() { local class gameTypeClass; local string randomMap; local string bootURL; local string remaining; local string command; local string result; // Check boot method. if (sConf.restartOnLastGame || sConf.enableMatchBootControl && sConf.matchModeActivated) { // Last game + map. // Check last server url. if (sConf.lastServerURL == "") { return false; // Invalid URL. } // Perform the map switch. level.serverTravel(sConf.lastServerURL, false); } else { // Custom game + random map. // Check if game class exists. gameTypeClass = class(dynamicLoadObject(sConf.bootGameType, class'Class')); if (gameTypeClass == none) { return false; // Game class doesn't exist. } // Select a random map. randomMap = selectRandomBootMap(); if (randomMap == "") { return false; // No map selected. } // Execute pre switch commands. remaining = sConf.bootCommands; while (remaining != "") { class'NexgenUtil'.static.split(remaining, command, remaining); nscLog(lng.format(lng.execCommandMsg, command)); result = consoleCommand(command); if (result != "") { nscLog("> " $ result); } } // Assemble boot command line string. bootURL = randomMap $ "?game=" $ sConf.bootGameType $ "?mutator=" $ sConf.bootMutators $ sConf.bootOptions; // Perform the map switch. nscLog(lng.format(lng.bootLevelSwitchMsg, randomMap)); level.serverTravel(bootURL, false); } // Boot sequence successfully executed. return true; } /*************************************************************************************************** * * $DESCRIPTION Selects a random map matching the boot map prefix. * $RETURN The filename of the randomly selected map. * **************************************************************************************************/ function string selectRandomBootMap() { local string mapName; local string firstMap; local string shortMapName; local bool bFirst; local int mapCount; local int index; local int randomMapIndex; local string randomMap; // Count number of maps with the specified prefix. mapName = getMapName("", "", 0); firstMap = mapName; bFirst = true; while(mapName != "" && (bFirst || mapName != firstMap)) { bFirst = false; // Valid map? if (left(mapName, len(sConf.bootMapPrefix) + 1) ~= (sConf.bootMapPrefix $ "-") && instr(caps(mapName), "TUTORIAL") < 0) { mapCount++; } mapName = getMapName("", mapName, 1); } // Cancel if there are no matching maps. if (mapCount == 0) { return ""; } // Select random map. randomMapIndex = rand(mapCount); mapName = getMapName("", "", 0); firstMap = mapName; bFirst = true; while(mapName != "" && (bFirst || mapName != firstMap) && randomMap == "") { bFirst = false; // Valid map? if (left(mapName, len(sConf.bootMapPrefix) + 1) ~= (sConf.bootMapPrefix $ "-") && instr(caps(mapName), "TUTORIAL") < 0) { if (index == randomMapIndex) { randomMap = mapName; } index++; } mapName = getMapName("", mapName, 1); } // Return result. return randomMap; } /*************************************************************************************************** * * $DESCRIPTION Applies the current configuration to the server. * $REQUIRE sConf != none * **************************************************************************************************/ function applyConfig() { // Apply global server settings. consoleCommand("set Engine.GameInfo GamePassword" @ sConf.decode(sConf.globalServerPassword)); consoleCommand("set Engine.GameInfo AdminPassword" @ sConf.decode(sConf.globalAdminPassword)); level.game.gameReplicationInfo.serverName = sConf.serverName; level.game.gameReplicationInfo.shortName = sConf.shortName; level.game.gameReplicationInfo.adminName = sConf.adminName; level.game.gameReplicationInfo.adminEmail = sConf.adminEmail; level.game.maxPlayers = sConf.playerSlots + sConf.vipSlots + sConf.adminSlots; level.game.maxSpectators = sConf.spectatorSlots; if (sConf.enableUplink) { consoleCommand("set IpServer.UdpServerUplink DoUplink True"); } else { consoleCommand("set IpServer.UdpServerUplink DoUplink False"); } level.game.gameReplicationInfo.MOTDLine1 = sConf.MOTDLine[0]; level.game.gameReplicationInfo.MOTDLine2 = sConf.MOTDLine[1]; level.game.gameReplicationInfo.MOTDLine3 = sConf.MOTDLine[2]; level.game.gameReplicationInfo.MOTDLine4 = sConf.MOTDLine[3]; } /*************************************************************************************************** * * $DESCRIPTION Initializes the extended game (replication) info. * $REQUIRE sConf != none * $ENSURE gInf != none * **************************************************************************************************/ function initGameInfo() { // Setup current game. gInf = spawn(class'NexgenGameInfo', self); gInf.gameState = gInf.GS_Waiting; gInf.countDown = sConf.waitTime; gInf.gameSpeed = level.game.gameSpeed; if (level.game.isA('TeamGamePlus')) { gInf.maxTeams = TeamGamePlus(level.game).maxTeams; } gInf.bNoTeamSwitch = !sConf.allowTeamSwitch; gInf.bNoTeamBalance = !sConf.allowTeamBalance; gInf.bNoNameChange = !sConf.allowNameChange; gInf.updateCount = 1; } /*************************************************************************************************** * * $DESCRIPTION Attemps to replace the default HUD class with a Nexgen HUD class. * $RETURN True if the default HUD has been successfully replaced. * $ENSURE result == true ? classIsChildOf(new.level.game.HUDType, old.level.game.HUDType) : true * **************************************************************************************************/ function bool setReplacementHUDClass() { local string hudClassName; local string hudReplaceClassName; local class replacementClass; local bool bFound; local int index; local int tokenIndex; // Find a suitable replacement class for the current HUD class. hudClassName = string(level.game.HUDType); while (!bFound && index < arrayCount(sConf.replacementClass) && sConf.replacementClass[index] != "") { tokenIndex = instr(sConf.replacementClass[index], "="); if (tokenIndex > 0 && left(sConf.replacementClass[index], tokenIndex) ~= hudClassName) { // Get replacement class. hudReplaceClassName = mid(sConf.replacementClass[index], tokenIndex + 1); replacementClass = class(dynamicLoadObject(hudReplaceClassName, class'Class')); // Valid replacement class? if (replacementClass != none && classIsChildOf(replacementClass, level.game.HUDType)) { bFound = true; } else { index++; } } else { index++; } } // Apply new HUD class if found. if (bFound) { //level.game.HUDType = replacementClass; sConf.HUDReplacementClass = replacementClass; } else { nscLog(lng.format(lng.noHUDReplacementClassMsg, hudClassName)); } // Return result. return bFound; } /*************************************************************************************************** * * $DESCRIPTION Registers a new controller plugin. Since there a limit to the amount of plugins * this action may fail. * $PARAM plugin The plugin to register. * $REQUIRE plugin != none * $RETURN True if the plugin was succesfully added to the server controller, false if the * plugin limit has been reached. * **************************************************************************************************/ function bool registerPlugin(NexgenPlugin plugin) { local bool bFound; local int index; // Locate empty slot. while (!bFound && index < arrayCount(plugins)) { if (plugins[index] == none) { bFound = true; plugins[index] = plugin; // Store plugin in this free slot. } else { index++; } } // Return result. return bFound; } /*************************************************************************************************** * * $DESCRIPTION Handles the connection of a new client. * $PARAM client The playerpawn instance of the new client. * $REQUIRE client != none * **************************************************************************************************/ function newClient(PlayerPawn client) { local NexgenClient clientHandler; local int index; // Create new client handler. clientHandler = spawn(class'NexgenClient', client); // Set attributes. clientHandler.serverID = sConf.serverID; clientHandler.control = self; clientHandler.sConf = sConf; clientHandler.gInf = gInf; clientHandler.lng = lng; clientHandler.playerNum = nextPlayerNum++; clientHandler.loginHandler = loginHandler; clientHandler.loginHandlerChecksum = class'NexgenUtil'.static.stringHash(string(loginHandler)); // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].clientCreated(clientHandler); index++; } } /*************************************************************************************************** * * $DESCRIPTION Called when a player attempts to login to the server. Allows mutators to modify * some of the login parameters. * $PARAM spawnClass The PlayerPawn class to use for the player. * $PARAM portal Name of the portal where the player wishes to spawn. * $PARAM option Login option parameters. * **************************************************************************************************/ function modifyLogin(out class spawnClass, out string portal, out string options) { local int overrideCode; local int index; // Only continue if the login wasn't denied. if (spawnClass == none) { return; } // Check if the player isn't allowed to join the game as player. overrideCode = level.game.getIntOption(options, joinOverrideCodeOption, -1); if (gInf.bTeamsLocked && !isValidJoinOverrideCode(overrideCode)) { spawnClass = class'Botpack.CHSpectator'; } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].modifyLogin(spawnClass, portal, options); index++; } // Allow other mutators to do their job. if (nextMutator != none) { nextMutator.modifyLogin(spawnClass, portal, options); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given login override code is correct. * **************************************************************************************************/ function bool isValidJoinOverrideCode(int overrideCode) { local bool bValid; local bool bInvalid; local int index; // Check override code. while (!bValid && !bInvalid && index < arrayCount(joinOverrideCodes)) { if (joinOverrideCodes[index] == overrideCode) { // Check if lease has expired. if (timeSeconds - overrideCodeLeaseTimes[index] <= maxOverrideCodeLeaseTime) { bValid = true; } else { bInvalid = true; } // Clear override code. joinOverrideCodes[index] = -1; overrideCodeLeaseTimes[index] = 0; } else { index++; } } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Gives the specified client a login override code, so he or she can join a locked * game as player. * **************************************************************************************************/ function giveJoinOverrideCode(NexgenClient client) { local int index; local bool bFound; // Find a free slot. while (!bFound && index < arrayCount(joinOverrideCodes)) { // Check if current slot is free. if (joinOverrideCodes[index] < 0 || timeSeconds - overrideCodeLeaseTimes[index] > maxOverrideCodeLeaseTime) { // Slot is free, use this one. bFound = true; } else { // Nope, maybe next one. index++; } } // Create override code & send to client. if (bFound) { joinOverrideCodes[index] = rand(maxInt); overrideCodeLeaseTimes[index] = timeSeconds; client.updateLoginOption(joinOverrideCodeOption, joinOverrideCodes[index]); } } /*************************************************************************************************** * * $DESCRIPTION Checks if the login request of the specified client should be accepted. If the * request is rejected, this function will automatically kill the client. * $PARAM client The client whose login request is to be checked. * $REQUIRE client != none * **************************************************************************************************/ function checkLogin(NexgenClient client) { local string password; local bool bRejected; local string reason; local string banReason; local string banPeriod; local bool allowSpecReconnect; local int index; local int k; local string cs; local name rejectType; local string popupWindowClass; local string popupArgs[4]; // Get login options. password = class'NexgenUtil'.static.getProperty(client.loginOptions, client.SSTR_ServerPassword); // Log the login request. nscLog(lng.format(lng.loginRequestMsg, client.playerName)); nscLog(lng.format(lng.attrClientIPMsg, client.ipAddress)); nscLog(lng.format(lng.attrClientIDMsg, class'NexgenUtil'.static.formatGUID(client.playerID))); if (password != "") { nscLog(lng.format(lng.attrPasswordMsg, password)); } // Check for duplicate ID's. if (!hasUniqueKey(client)) { bRejected = true; rejectType = RT_DuplicateID; reason = lng.duplicateIDMsg; popupWindowClass = "NexgenIDUsedDialog"; } // Check for bans. if ((!bRejected) && isBanned(client, banReason, banPeriod)) { bRejected = true; rejectType = RT_Banned; reason = lng.bannedMsg; popupWindowClass = "NexgenBannedDialog"; popupArgs[0] = banReason; popupArgs[1] = banPeriod; } // Check password. if ((!bRejected) && (sConf.matchModeActivated)&& (sConf.serverPassword != "") && (password != sConf.decode(sConf.serverPassword)) && (!client.hasRight(client.R_NeedsNoPW)) && (!client.bSpectator || sConf.spectatorsNeedPassword)) { bRejected = true; rejectType = RT_InvalidPassword; reason = lng.invalidPassMsg; allowSpecReconnect = !client.bSpectator && !sConf.spectatorsNeedPassword; popupWindowClass = "NexgenPasswordDialog"; popupArgs[0] = string(allowSpecReconnect); } // Check slots. if ((!bRejected) && !canGetSlot(client)) { bRejected = true; rejectType = RT_ServerFull; reason = lng.serverCapacityMsg; popupWindowClass = "NexgenServerFullDialog"; popupArgs[0] = string(sConf.playerSlots); popupArgs[1] = string(sConf.vipSlots); popupArgs[2] = string(sConf.adminSlots); } // Check play rights. if ((!bRejected) && (!client.bSpectator) && (!client.hasRight(client.R_MayPlay))) { bRejected = true; rejectType = RT_NoPlayRight; reason = lng.noPlayRightMsg; popupWindowClass = "NexgenNoPlayRightDialog"; } // Get player data object for this client. setPlayerData(client); // Check with plugins. while (!bRejected && index < arrayCount(plugins) && plugins[index] != none) { bRejected = !plugins[index].checkLogin(client, rejectType, reason); index++; } // Accept or reject player. if (bRejected) { // Allow plugins to modify the rejection of this player. index = 0; while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].modifyLoginReject(client, rejectType, reason, popupWindowClass, popupArgs); index++; } // Reject the player. if (popupWindowClass != "") { client.showPopup(popupWindowClass, popupArgs[0], popupArgs[1], popupArgs[2], popupArgs[3]); } disconnectClient(client); nscLog(lng.format(lng.loginRejectedMsg, reason)); } else { // Send encryption paramters. Only players with an account receive the right paramters. if (client.bHasAccount) { sConf.getEncryptionParams(k, cs); } client.setEncryptionParams(k, cs); // Signal events. nscLog(lng.loginAcceptedMsg); playerJoined(client); } } /*************************************************************************************************** * * $DESCRIPTION Checks if the player ID of the specified client is unique. * $PARAM client The client whose player ID is to be checked. * $REQUIRE client != none * $RETURN True none of the other clients has the same player ID, false otherwise. * **************************************************************************************************/ function bool hasUniqueKey(NexgenClient client) { local bool bUnique; local NexgenClient currClient; // Compare each ID of the other clients with the ID of the specified client. bUnique = true; currClient = clientList; while (bUnique && currClient != none) { // Same ID? if (currClient != client && currClient.playerID ~= client.playerID) { bUnique = false; } // Compare ID with the next client. currClient = currClient.nextClient; } // Return result. return bUnique; } /*************************************************************************************************** * * $DESCRIPTION Checks if the specified client is banned on this server. * $PARAM client The client for which the ban is to be checked. * $PARAM banReason Description of why the client was banned. * $PARAM banPeriod Textual description indicating how long the player is banned. * $REQUIRE client != none * $RETURN True if the client is banned, false if not. * $ENSURE result == true ? new.banPeriod != "" : true * **************************************************************************************************/ function bool isBanned(NexgenClient client, out string banReason, out string banPeriod) { local int banIndex; local bool bBanned; // Get ban entry. banIndex = sConf.getBanIndex(client.playerName, client.ipAddress, client.playerID); // Check if player is banned and the ban hasn't expired. if (banIndex >= 0) { if (sConf.autoUpdateBans) { if (sConf.updateBan(banIndex, client.ipAddress, client.playerID)) { // Ban entry was changed, update dynamic config data checksum & notify clients. signalConfigUpdate(sConf.CT_BanList); } } banReason = sConf.banReason[banIndex]; banPeriod = lng.getBanPeriodDescription(sConf.banPeriod[banIndex]); bBanned = !sConf.isExpiredBan(banIndex); } // Return result. return bBanned; } /*************************************************************************************************** * * $DESCRIPTION Checks if the specified client can get a player slot on the server, i.e. the * server isn't full for this client. * $PARAM client The client for which is to be checked if a slot available. * $REQUIRE client != none * $RETURN True if there is an empty slot available that the specified client may occupy, * false if not. * **************************************************************************************************/ function bool canGetSlot(NexgenClient client) { local NexgenClient currClient; local int playerCount; // Count used slots. currClient = clientList; while (currClient != none) { // Count all players, except the specified client and spectators. if (currClient != client && !currClient.bSpectator) { playerCount++; } // Next player. currClient = currClient.nextClient; } // Get slot rights for the specified client & check limits. if (client.bSpectator) { return true; } else if (client.hasRight(client.R_AdminAccess)) { return playerCount < sConf.playerSlots + sConf.vipSlots + sConf.adminSlots; } else if (client.hasRight(client.R_VIPAccess)) { return playerCount < sConf.playerSlots + sConf.vipSlots; } else { return playerCount < sConf.playerSlots; } } /*************************************************************************************************** * * $DESCRIPTION Sets the player data object for the specified client. * $PARAM client The client for which the player data object has to be set. * $REQUIRE client != none * $ENSURE client.pDat != none && client.pDat.clientID ~= client.playerID * **************************************************************************************************/ function setPlayerData(NexgenClient client) { local NexgenPlayerData pDat; local bool bFound; // Search for saved player data object for this client. pDat = playerDataList; while (!bFound && pDat != none) { if (pDat.clientID ~= client.playerID) { bFound = true; } else { pDat = pDat.next; } } // Create player data object if necessary. if (!bFound) { // Create & initialize player data object. pDat = spawn(class'NexgenPlayerData', self); pDat.clientID = client.playerID; // Add player data object to the list pDat.next = playerDataList; playerDataList = pDat; } // Set player data object for the client. client.pDat = pDat; } /*************************************************************************************************** * * $DESCRIPTION Disconnects the specified client from this server. * $PARAM client The client that is to be disconnected from the server. * $REQUIRE client != none * **************************************************************************************************/ function disconnectClient(NexgenClient client) { // Remove client from the client list. removeClientHandler(client); // Close connection. client.player.destroy(); // Is this safe? At least thats how Epic does it. client.destroy(); } /*************************************************************************************************** * * $DESCRIPTION Removes the specified client from the client list. * $PARAM client The client that is to be removed. * $REQUIRE client != none * **************************************************************************************************/ function removeClientHandler(NexgenClient client) { local NexgenClient currClient; local bool bDone; // Remove the client from the linked client list. if (clientList == client) { // First element in the list. clientList = client.nextClient; } else { // Somewhere else in the list. currClient = clientList; while (!bDone && currClient != none) { if (currClient.nextClient == client) { bDone = true; currClient.nextClient = client.nextClient; } else { currClient = currClient.nextClient; } } } } /*************************************************************************************************** * * $DESCRIPTION Called when a player (re)spawns and allows us to modify the player. * $PARAM other The pawn/player that has (re)spawned. * $REQUIRE other != none * **************************************************************************************************/ function modifyPlayer(Pawn other) { local NexgenClient client; local int index; // Get client. client = getClient(other); // Signal event. if (client != none) { // Signal event on client. client.respawned(); // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerRespawned(client); index++; } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].modifyPlayer(other); index++; } // Let other mutators do their job. if (nextMutator != none) { nextMutator.modifyPlayer(other); } } /*************************************************************************************************** * * $DESCRIPTION Accepts the login request for the specified client. * $PARAM client The client whose login request has been accepted. * $REQUIRE client != none && !client.loginComplete * **************************************************************************************************/ function playerJoined(NexgenClient client) { local int index; local bool bFound; // Add client to the client list. client.nextClient = clientList; clientList = client; // Set player team if auto separate is enabled. if (!client.bSpectator && level.game.isA('TeamGamePlus') && sConf.matchModeActivated && sConf.matchAutoSeparate) { while (!bFound && index < arrayCount(sConf.tagsToSeparate) && index < TeamGamePlus(level.game).maxTeams) { // Check if player has a tag that separates him/her. if (instr(caps(client.playerName), caps(sConf.tagsToSeparate[index])) >= 0) { bFound = true; if (client.player.playerReplicationInfo.team != index) { client.setTeam(index); } } else { index++; } } } // Update client attributes. client.team = client.player.playerReplicationInfo.team; client.lastSwitchTime = timeSeconds; client.loginComplete = true; if (gInf.gameState == gInf.GS_Playing && !client.bSpectator) { client.spawnProtectionTimeX = sConf.spawnProtectionTime; } // Update game info. if (client.hasRight(client.R_MatchAdmin)) { gInf.matchAdminCount++; } // Notify plugins. index = 0; while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerJoined(client); index++; } // Show game is ready to launch message. if (gInf.gameState == gInf.GS_Ready) { if (sConf.matchModeActivated && gInf.matchAdminCount > 0 && !client.hasRight(client.R_MatchAdmin)) { client.showMsg(lng.adminLaunchGameMsg); } else { client.showMsg(lng.launchGameMsg); } } // Notify other players. broadcastMsg(lng.playerJoinMsg, client.playerName, client.title, , , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Handles the events where a client has been initialized. * $PARAM client The client that has just been initialized. * $REQUIRE client != none && client.loginComplete * **************************************************************************************************/ function clientInitialized(NexgenClient client) { local int index; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].clientInitialized(client); index++; } } /*************************************************************************************************** * * $DESCRIPTION Handles the leaving of the specified client. * $PARAM client The client that has left the server. * $REQUIRE client.owner == none * **************************************************************************************************/ function playerLeft(NexgenClient client) { local int index; // Remove client from the client list. removeClientHandler(client); // Clear player data (data should be added by the plugins). client.pDat.clearData(); // Update game info. if (client.hasRight(client.R_MatchAdmin)) { gInf.matchAdminCount--; } lastPlayerLeftTime = timeSeconds; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerLeft(client); index++; } // Notify other players. broadcastMsg(lng.playerLeaveMsg, client.playerName, client.title); // Log event. nscLog(lng.format(lng.playerLeaveLogMsg, client.playerName)); // Pause the game? if (sConf.matchModeActivated && sConf.matchAutoPause && clientList != none && gInf.gameState == gInf.GS_Playing && gInf.matchAdminCount > 0 && !client.bSpectator) { level.pauser = serverPauserName; } // Unpause the game? if (level.pauser != "" && gInf.matchAdminCount == 0) { //level.pauser = ""; } // No longer need the client handler. client.destroy(); } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has changed his/her name during the game. * $PARAM client The client that has changed his/her name. * $REQUIRE client.playerName != client.player.playerReplicationInfo.playerName * **************************************************************************************************/ function playerNameChanged(NexgenClient client) { local bool bAllowChange; local string reason; local string oldName; local int index; // Check if the name change should be allowed. if (timeSeconds - client.nameChangeOverrideTime <= client.maxOverrideTime) { client.nameChangeOverrideTime = 0; // Clear admin override flag. bAllowChange = true; } else if (gInf.bNoNameChange) { reason = lng.nameChangeDisabled; } else { bAllowChange = true; } // Update name if allowed. if (bAllowChange) { // Update player name. oldName = client.playerName; client.playerName = client.player.playerReplicationInfo.playerName; // Notify other players. broadcastMsg(lng.playerNameChangeMsg, oldName, client.playerName, , , client.player.playerReplicationInfo); // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerNameChanged(client); index++; } } else { // Reset original name. client.changeName(client.playerName); sendMsg(client, lng.nameChangeFailMsg, reason); } } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has switched to another team. * $PARAM client The client that has changed team. * $REQUIRE client.team != client.player.playerReplicationInfo.team * **************************************************************************************************/ function playerTeamChanged(NexgenClient client) { local bool bAllowSwitch; local string reason; local int index; // Check if the teamswitch should be allowed. if (timeSeconds - client.teamSwitchOverrideTime <= client.maxOverrideTime) { client.teamSwitchOverrideTime = 0; // Clear admin override flag. bAllowSwitch = true; } else if (gInf.bNoTeamSwitch) { reason = lng.teamSwitchDisabledMsg; } else if (gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else if (client.bNoTeamSwitch) { reason = lng.playerTeamSwitchDisabledMsg; } else { bAllowSwitch = true; } // Update team if allowed. if (bAllowSwitch) { client.team = client.player.playerReplicationInfo.team; client.lastSwitchTime = timeSeconds; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerTeamChanged(client); index++; } } else { // Switch back to original team. client.setTeam(client.team); sendMsg(client, lng.teamSwitchFailMsg, reason); } } /*************************************************************************************************** * * $DESCRIPTION Called when a pawn takes damage. * $PARAM actualDamage The amount of damage sustained by the pawn. * $PARAM victim Pawn that has become victim of the damage. * $PARAM instigatedBy The pawn that has instigated the damage to the victim. * $PARAM hitLocation Location where the damage was dealt. * $PARAM momentum Momentum of the damage that has been dealt. * $PARAM damageType Type of damage dealt to the victim. * $REQUIRE victim != none * $OVERRIDE * **************************************************************************************************/ function mutatorTakeDamage(out int actualDamage, Pawn victim, Pawn instigatedBy, out vector hitLocation, out vector momentum, name damageType) { local NexgenClient client; local byte bPreventDamage; local byte bResetPlayer; local int index; // Get client. client = getClient(victim); // Check if damage should be prevented. if (client != none && damageType != suicideDamageType) { checkPreventDamage(client, instigatedBy, damageType, actualDamage, bPreventDamage, bResetPlayer); // Prevent the damage. if (bool(bPreventDamage)) { actualDamage = 0; if (bool(bResetPlayer)) { level.game.restartPlayer(client.player); client.showMsg(lng.deathPreventedMsg); } } // Team kill? if (victim != instigatedBy && level.game.gameReplicationInfo.bTeamGame && instigatedBy != none && (instigatedBy.isA('PlayerPawn') || instigatedBy.isA('Bot')) && victim.playerReplicationInfo.team == instigatedBy.playerReplicationInfo.team) { // Yes, prevent damage & protect victim. client.tkPushProtectionTimeX = sConf.teamKillPushProtectionTime; if (!bool(bPreventDamage)) { // Damage hasn't been prevented yet. actualDamage = 0; client.tkDmgProtectionTimeX = sConf.teamKillDamageProtectionTime; // Notify players if desired. if (sConf.broadcastTeamKillAttempts) { broadcastMsg(lng.teamKillAttemptMsg, instigatedBy.playerReplicationInfo.playerName, victim.playerReplicationInfo.playerName, , , instigatedBy.playerReplicationInfo); } } } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutatorTakeDamage(actualDamage, victim, instigatedBy, hitLocation, momentum, damageType); index++; } // Let other mutators do their job. if (nextDamageMutator != none) { nextDamageMutator.mutatorTakeDamage(actualDamage, victim, instigatedBy, hitLocation, momentum, damageType); } } /*************************************************************************************************** * * $DESCRIPTION Called when the server wants to check if a players death should be prevented. * $PARAM victim The pawn that was killed. * $PARAM killer The pawn that has killed the victim. * $PARAM damageType Type of damage dealt to the victim. * $PARAM hitLocation Location where the damage was dealt. * $RETURN True if the players death should be prevented, false if not. * $OVERRIDE * **************************************************************************************************/ function bool preventDeath(Pawn victim, Pawn killer, name damageType, vector hitLocation) { local NexgenClient client; local byte bPreventDamage; local byte bResetPlayer; local int index; // Get client. client = getClient(victim); // Check if damage should be prevented. if (client != none && damageType != suicideDamageType) { checkPreventDamage(client, killer, damageType, 99999, bPreventDamage, bResetPlayer); // Prevent the damage. if (bool(bPreventDamage)) { client.player.health = 100; if (bool(bResetPlayer)) { level.game.restartPlayer(client.player); client.showMsg(lng.deathPreventedMsg); } return true; } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { if (plugins[index].preventDeath(victim, killer, damageType, hitLocation)) return true; index++; } // Let other mutators do their job. if (nextMutator == none) { return false; } else { return nextMutator.preventDeath(victim, killer, damageType, hitLocation); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified damage to the client should be prevented. * $PARAM client The client for which the damage prevention check should be executed. * $PARAM instigator The pawn that has instigated the damage to the victim. * $PARAM damageType Type of damage the player has sustained. * $PARAM damage The amount of damage sustained by the client. * $PARAM bPreventDamage Whether the damage should be prevented or not. * $PARAM bResetPlayer Indicates if the player should restart if the damage is prevented. * $REQUIRE client != none * **************************************************************************************************/ function checkPreventDamage(NexgenClient client, Pawn instigator, name damageType, int damage, out byte bPreventDamage, out byte bResetPlayer) { // Check if player has switched to another team. if (client.team != client.player.playerReplicationInfo.team) { // Yes, don't prevent the damage. bPreventDamage = byte(false); bResetPlayer = byte(false); return; } // Spawn protection. if (client.spawnProtectionTimeX > 0) { bPreventDamage = byte(true); bResetPlayer = byte(client.player.playerReplicationInfo.hasFlag == none && (damageType == fallDamageType && client.player.health <= damage || damageType == burnDamageType || damageType == corrodeDamageType)); } // Team kill damage & push protection. if (!bool(bPreventDamage)) { bPreventDamage = byte(client.tkDmgProtectionTimeX > 0 || client.tkPushProtectionTimeX > 0 && (damageType == fallDamageType || damageType == burnDamageType || damageType == corrodeDamageType || instigator == none)); bResetPlayer = byte(client.tkPushProtectionTimeX > 0 && client.player.playerReplicationInfo.hasFlag == none && (damageType == fallDamageType && client.player.health <= damage || damageType == burnDamageType || damageType == corrodeDamageType)); } } /*************************************************************************************************** * * $DESCRIPTION Attempts to balance the current teams. * $RETURN True if the teams have been balanced, false if they are already balanced. * **************************************************************************************************/ function bool balanceTeams() { if (teamBalancer == none) { return false; } else { return teamBalancer.balanceTeams(); } } /*************************************************************************************************** * * $DESCRIPTION Starts the current game. * $REQUIRE gInf.gameState == gInf.GS_Ready || gInf.gameState == gInf.GS_Starting * $ENSURE gInf.gameState == gInf.GS_Playing; * **************************************************************************************************/ function startGame() { // Start the game immediately? if (gInf.gameState == gInf.GS_Ready && sConf.startTime <= 0 || gInf.gameState == gInf.GS_Starting && gInf.countDown <= 0) { // Yes. gInf.gameState = gInf.GS_Playing; DeathMatchPlus(level.game).bRequireReady = false; DeathMatchPlus(level.game).startMatch(); gameStarted(); } else if (gInf.gameState == gInf.GS_Ready && sConf.startTime > 0) { // No, delayed start. gInf.gameState = gInf.GS_Starting; gInf.countDown = sConf.startTime; } } /*************************************************************************************************** * * $DESCRIPTION Logs the given message. * $PARAM msg Message that should be written to the log. * **************************************************************************************************/ function nscLog(string msg, optional byte logMsgType) { if (logMsgType == LT_Event) { if (sConf.logEvents) log(lng.eventLogTag @ msg, logTag); } else if (logMsgType == LT_Message) { if (sConf.logSystemMessages) log(lng.messageLogTag @ msg, logTag); } else if (logMsgType == LT_Say) { if (sConf.logChatMessages) log(lng.chatMessageLogTag @ msg, logTag); } else if (logMsgType == LT_TeamSay) { if (sConf.logChatMessages) log(lng.teamSayMessageLogTag @ msg, logTag); } else if (logMsgType == LT_PrivateMsg) { if (sConf.logChatMessages) log(lng.privateMessageLogTag @ msg, logTag); } else { log(lng.controllerSystemLogTag @ msg, logTag); } } /*************************************************************************************************** * * $DESCRIPTION Broadcasts the given message to all connected clients. * $PARAM msg Message that should send to all the clients. * $PARAM str1 Message specific content. * $PARAM str2 Message specific content. * $PARAM str3 Message specific content. * $PARAM str4 Message specific content. * $PARAM pri Replication info of the player related to this message. * **************************************************************************************************/ function broadcastMsg(string msg, optional string str1, optional string str2, optional string str3, optional string str4, optional PlayerReplicationInfo pri) { local NexgenClient client; // Format message. msg = class'NexgenUtil'.static.format(msg, str1, str2, str3, str4); // Log message. nscLog(class'NexgenUtil'.static.removeMessageColorTag(msg), LT_Event); // Send message to all clients. for (client = clientList; client != none; client = client.nextClient) { client.showMsg(msg, pri); } } /*************************************************************************************************** * * $DESCRIPTION Broadcasts the given message to all connected clients. * $PARAM client The client to which the message should be send. * $PARAM msg Message that should send to all the clients. * $PARAM str1 Message specific content. * $PARAM str2 Message specific content. * $PARAM str3 Message specific content. * $PARAM str4 Message specific content. * $PARAM pri Replication info of the player related to this message. * **************************************************************************************************/ function sendMsg(NexgenClient client, string msg, optional string str1, optional string str2, optional string str3, optional string str4, optional PlayerReplicationInfo pri) { msg = class'NexgenUtil'.static.format(msg, str1, str2, str3, str4); client.showMsg(msg, pri); } /*************************************************************************************************** * * $DESCRIPTION Locates the NexgenClient instance for the given actor. * $PARAM a The actor for which the client handler instance is to be found. * $REQUIRE a != none * $RETURN The client handler for the given actor. * $ENSURE (!a.isA('PlayerPawn') ? result == none : true) && * (result != none ? result.owner == a : true) * **************************************************************************************************/ function NexgenClient getClient(Actor a) { local NexgenClient client; local bool bFound; // Search for NexgenClient owning this actor. client = clientList; while (!bFound && client != none) { if (client.owner == a) { bFound = true; } else { client = client.nextClient; } } // Return result. if (bFound) { return client; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Locates the NexgenClient instance for the given player code. * $PARAM playerNum The player code of the client handler instance that is to be found. * $REQUIRE playerNum >= 0 * $RETURN The client handler for the given player code. * $ENSURE (result != none ? result.playerNum == playerNum : true) * * **************************************************************************************************/ function NexgenClient getClientByNum(int playerNum) { local NexgenClient client; local bool bFound; // Search for NexgenClient with the specified player code. client = clientList; while (!bFound && client != none) { if (client.playerNum == playerNum) { bFound = true; } else { client = client.nextClient; } } // Return result. if (bFound) { return client; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Locates the NexgenClient instance for the given player client ID. * $PARAM clientID The client ID of the client handler instance that is to be found. * $REQUIRE clientID != "" * $RETURN The client handler for the given player code. * $ENSURE (result != none ? result.playerID ~= clientID : true) * * **************************************************************************************************/ function NexgenClient getClientByID(string clientID) { local NexgenClient client; local bool bFound; // Search for NexgenClient with the specified player code. client = clientList; while (!bFound && client != none) { if (client.playerID ~= clientID) { bFound = true; } else { client = client.nextClient; } } // Return result. if (bFound) { return client; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Handles a potential command message. * $PARAM sender PlayerPawn that has send the message in question. * $PARAM msg Message send by the player, which could be a command. * $REQUIRE sender != none * $RETURN True if the specified message is a command, false if not. * **************************************************************************************************/ function bool handleMsgCommand(PlayerPawn sender, string msg) { local string cmd; local bool bIsCommand; cmd = class'NexgenUtil'.static.trim(msg); bIsCommand = true; switch (cmd) { // Team commands. case "!r": case "!red": mutate(CMD_Prefix @ CMD_SwitchTeam @ 0, sender); break; case "!b": case "!blue": mutate(CMD_Prefix @ CMD_SwitchTeam @ 1, sender); break; case "!g": case "!green": mutate(CMD_Prefix @ CMD_SwitchTeam @ 2, sender); break; case "!y": case "!yellow": case "!gold": mutate(CMD_Prefix @ CMD_SwitchTeam @ 3, sender); break; case "!t": case "!team": case "!teams": mutate(CMD_Prefix @ CMD_BalanceTeams, sender); break; // Game commands. case "!p": case "!play": mutate(CMD_Prefix @ CMD_Play, sender); break; case "!s": case "!spec": mutate(CMD_Prefix @ CMD_Spectate, sender); break; case "!l": case "!start": mutate(CMD_Prefix @ CMD_StartGame, sender); break; case "!quit": case "!exit": mutate(CMD_Prefix @ CMD_Exit, sender); break; case "!leave": case "!bye": mutate(CMD_Prefix @ CMD_Disconnect, sender); break; // GUI commands. case "!o": case "!open": mutate(CMD_Prefix @ CMD_Open, sender); break; case "!v": case "!vote": mutate(CMD_Prefix @ CMD_OpenVote, sender); break; // Not a command. default: bIsCommand = false; } return bIsCommand; } /*************************************************************************************************** * * $DESCRIPTION Called when the game executes its next 'game' tick. This function provides the * support for the gamespeed independent timing support. * $OVERRIDE * **************************************************************************************************/ function tick(float deltaTime) { local NexgenClient client; if (level.pauser == "") { timeSeconds += deltaTime / level.timeDilation; } for (client = clientList; client != none; client = client.nextClient) { if (client.player.player == none) { playerLeft(client); } } } /*************************************************************************************************** * * $DESCRIPTION Executes one iteration of the server controller main loop. Various events are * detected and handled here, including: * - Players that change their name. * - Players that have switched to another team. * - When the game has ended. * - Game speed changes. * This function is also responsible for making our virtual 1 Hz timer tick. * $OVERRIDE * **************************************************************************************************/ function timer() { local NexgenClient client; // For each client... for (client = clientList; client != none; client = client.nextClient) { // Name changed? if (client.playerName != client.player.playerReplicationInfo.playerName) { playerNameChanged(client); } // Team changed? if (client.team != client.player.playerReplicationInfo.team) { playerTeamChanged(client); } } // Game ended? if (level.game.bGameEnded && gInf.gameState != gInf.GS_Ended) { gInf.gameState = gInf.GS_Ended; gameEnded(); } // Check speed changed? if (level.timeDilation != lastTimeDilation) { lastTimeDilation = level.timeDilation; gameSpeedChanged(); } // Simulated 1 Hz timer. virtualTimerCounter += timerRate; if (virtualTimerCounter > level.timeDilation) { virtualTimerCounter = 0; virtualTimer(); } } /*************************************************************************************************** * * $DESCRIPTION This is our home cooked timer, which ticks at a frequency of 1 Hz and is * independent of the game speed. * **************************************************************************************************/ function virtualTimer() { // Update countdown. if (gInf.countDown >= 0) { gInf.countDown--; } // Check if the game if ready to start if (gInf.countDown == 0 && gInf.gameState == gInf.GS_Waiting) { gInf.gameState = gInf.GS_Ready; showGameReadyToLaunchMessages(); } if (gInf.countDown == 0 && gInf.gameState == gInf.GS_Starting) { startGame(); } // Update mutator list. if (!sConf.bActiveMutatorIndicesSet) { sConf.setActiveMutatorList(); } // Automatically disable an inactive match? if (sConf.matchModeActivated && sConf.autoDisableMatchTime > 0 && clientList == none && timeSeconds - lastPlayerLeftTime > secondsPerMinute * sConf.autoDisableMatchTime) { // Yes. sConf.matchModeActivated = false; sConf.saveConfig(); signalConfigUpdate(sConf.CT_MatchSettings); if (sConf.matchAutoLockTeams) { gInf.bTeamsLocked = false; } } // Reboot server? if (gInf.rebootCountDown > 0) { gInf.rebootCountDown--; if (gInf.rebootCountDown == 0) { consoleCommand(rebootCommand); } } } /*************************************************************************************************** * * $DESCRIPTION Sends a message to each client that the game is ready to be started. * **************************************************************************************************/ function showGameReadyToLaunchMessages() { local NexgenClient client; for (client = clientList; client != none; client = client.nextClient) { if (sConf.matchModeActivated && gInf.matchAdminCount > 0 && !client.hasRight(client.R_MatchAdmin)) { client.showMsg(lng.adminLaunchGameMsg); } else { client.showMsg(lng.launchGameMsg); } } } /*************************************************************************************************** * * $DESCRIPTION Called when the game has started. * **************************************************************************************************/ function gameStarted() { local int index; // Lock teams? if (sConf.matchModeActivated && sConf.matchAutoLockTeams && !gInf.bTeamsLocked) { gInf.bTeamsLocked = true; signalGameInfoUpdate(gInf.IT_GlobalRights); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].gameStarted(); index++; } } /*************************************************************************************************** * * $DESCRIPTION Called when the game has ended. * $REQUIRE level.game.bGameEnded * **************************************************************************************************/ function gameEnded() { local int index; // Update match settings. if (sConf.matchModeActivated) { sConf.currentMatch++; } // Unlock teams? if (sConf.matchModeActivated && sConf.matchAutoLockTeams && gInf.bTeamsLocked) { gInf.bTeamsLocked = false; signalGameInfoUpdate(gInf.IT_GlobalRights); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].gameEnded(); index++; } // Save configuration. sConf.saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Called when the game speed has changed. * **************************************************************************************************/ function gameSpeedChanged() { local int index; // Update timer rate. timerRate = 1.0 / timerFreq * level.timeDilation; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { //plugins[index].gameSpeedChanged(); index++; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the message mutator chain so commands can be detected. This function * is called if a message is send to player. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM pri Player replication info of the sending player. * $PARAM s The message that is to be send. * $PARAM type Type of the message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $RETURN True if the message should be send, false if it should be suppressed. * $OVERRIDE * **************************************************************************************************/ function bool mutatorTeamMessage(Actor sender, Pawn receiver, PlayerReplicationInfo pri, coerce string s, name type, optional bool bBeep) { local bool bIsCommand; local NexgenClient client; local byte logType; local int index; // Check for commands. if (sender != none && sender.isA('PlayerPawn') && sender == receiver && (type == 'Say' || type == 'TeamSay')) { bIsCommand = handleMsgCommand(PlayerPawn(sender), s); } // Check if player is muted. client = getClient(sender); if (client != none && client.isMuted()) { // Yeah he/she is, block the message. if (sender == receiver) { if (bIsCommand) { return true; } else { client.showMsg(lng.mutedReminderMsg); } } return false; } // Log message. if (sender == none && receiver != none && receiver.nextPawn == none) { nscLog(s, LT_Message); } else if (sender != none && sender == receiver && receiver.playerReplicationInfo != none) { if (type == 'Say') { logType = LT_Say; } else if (type == 'TeamSay') { logType = LT_TeamSay; } else { logType = LT_Message; } nscLog(receiver.playerReplicationInfo.playerName $ ": " $ s, logType); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutatorTeamMessage(sender, receiver, pri, s, type, bBeep); index++; } // Allow other message mutators to do their job. if (nextMessageMutator != none) { return nextMessageMutator.mutatorTeamMessage(sender, receiver, pri, s, type, bBeep); } else { return true; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the message mutator chain so commands can be detected. This function * is called if a message is send to player. Spectators that use say (not teamsay) * seem to be calling this function instead of mutatorTeamMessage. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM msg The message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $PARAM type Type of the message that is to be send. * $RETURN True if the message should be send, false if it should be suppressed. * $OVERRIDE * **************************************************************************************************/ function bool mutatorBroadcastMessage(Actor sender, Pawn receiver, out coerce string msg, optional bool bBeep, out optional name type) { local PlayerReplicationInfo senderPRI; local bool bIsCommand; local NexgenClient client; local bool bIsSpecMessage; local int index; // Suppress default player join / leave messages. if (sender == level.game && right(msg, len(level.game.leftMessage)) ~= level.game.leftMessage || sender == level.game && right(msg, len(level.game.enteredMessage)) ~= level.game.enteredMessage) { return false; } // Get sender player replication info. if (sender != none && sender.isA('Pawn')) { senderPRI = Pawn(sender).playerReplicationInfo; } // Check if we're dealing with a spectator chat message. bIsSpecMessage = senderPRI != none && sender.isA('Spectator') && left(msg, len(senderPRI.playerName) + 1) ~= (senderPRI.playerName $ ":"); // Check for commands. if (bIsSpecMessage && sender == receiver) { bIsCommand = handleMsgCommand(PlayerPawn(sender), mid(msg, len(senderPRI.playerName) + 1)); } // Check if spectator is muted. if (bIsSpecMessage) { client = getClient(sender); if (client != none && client.isMuted() || sConf.matchModeActivated && sConf.muteSpectatorsDuringMatch && gInf.gameState == gInf.GS_Playing && !client.hasRight(client.R_MatchAdmin) && !client.hasRight(client.R_Moderate)) { // Spectator is muted, block the message. if (sender == receiver) { if (bIsCommand) { return true; } else { client.showMsg(lng.mutedReminderMsg); } } return false; } } // Write message to the log. if (bIsSpecMessage && sender == receiver) { nscLog(msg, LT_Say); } else if (!bIsSpecMessage && receiver != none && (receiver.nextPawn == none || receiver.nextPawn.isA('NexgenControllerPawn'))) { if (senderPRI == none) { nscLog(msg, LT_Message); } else { nscLog(senderPRI.playerName $ ": " $ msg, LT_Message); } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutatorBroadcastMessage(sender, receiver, msg, bBeep, type); index++; } // Allow other message mutators to do their job. if (nextMessageMutator != none) { return nextMessageMutator.mutatorBroadcastMessage(sender, receiver, msg, bBeep, type); } else { return true; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the message mutator chain so messages can be logged. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM message The class of the localized message that is to be send. * $PARAM switch Optional message switch argument. * $PARAM relatedPRI_1 PlayerReplicationInfo of a player that is related to the message. * $PARAM relatedPRI_2 PlayerReplicationInfo of a player that is related to the message. * $PARAM optionalObject Optional object used to construct the message string. * $REQUIRE message != none * $RETURN True if the message should be send, false if it should be suppressed. * $OVERRIDE * **************************************************************************************************/ function bool mutatorBroadcastLocalizedMessage(Actor sender, Pawn receiver, out class message, out optional int switch, out optional PlayerReplicationInfo relatedPRI_1, out optional PlayerReplicationInfo relatedPRI_2, out optional Object optionalObject) { local PlayerReplicationInfo senderPRI; local string msg; local int index; // Get sender player replication info. if (sender != none && sender.isA('Pawn')) { senderPRI = Pawn(sender).playerReplicationInfo; } // Prevent duplicate messages in the log. if (receiver != none && (receiver.nextPawn == none || receiver.nextPawn.isA('NexgenControllerPawn'))) { // Construct message. msg = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); // Log the message. if (senderPRI == none) { nscLog(msg, LT_Message); } else { nscLog(senderPRI.playerName $ ": " $ msg, LT_Message); } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutatorBroadcastLocalizedMessage(sender, receiver, message, switch, relatedPRI_1, relatedPRI_2, optionalObject); index++; } // Allow other message mutators to do their job. if (nextMessageMutator != none) { return nextMessageMutator.mutatorBroadcastLocalizedMessage(sender, receiver, message, switch, relatedPRI_1, relatedPRI_2, optionalObject); } else { return true; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the mutator chain to detect Nexgen actions issued by the clients. If * a Nexgen command is detected it will be parsed and send to the execCommand() * function. * $PARAM mutateString Mutator specific string (indicates the action to perform). * $PARAM sender Player that has send the message. * $OVERRIDE * **************************************************************************************************/ function mutate(string mutateString, PlayerPawn sender) { local bool bIsNexgenCommand; local bool bIsLegacyCommand; local NexgenClient client; local string cmd; local string args[10]; local int index; // Get client handler for the sender. client = getClient(sender); // Parse command. bIsNexgenCommand = class'NexgenUtil'.static.parseCommandStr(mutateString, cmd, args); // Execute command. if (client != none) { if (bIsNexgenCommand) { cmdHandler.execCommand(client, cmd, args); } else if (mutateString ~= "asc#get#window" || mutateString ~= "hz0090") { // ASC/HUT legacy commands. cmdHandler.execCommand(client, CMD_Open, args); bIsLegacyCommand = true; } } // Let plugins and other mutators handle the string if this isn't a valid Nexgen command. if (!bIsNexgenCommand && !bIsLegacyCommand) { // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutate(mutateString, sender); index++; } // Allow other mutators to do their job. if (nextMutator != none) { nextMutator.mutate(mutateString, sender); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the clients that a certain part of the server configuration has changed. * $PARAM configType Type of settings that have been changed. * $ENSURE new.sConf.updateCount = old.sConf.updateCount + 1 * **************************************************************************************************/ function signalConfigUpdate(byte configType) { local NexgenClient client; local int index; // Set update counter. sConf.updateCount++; // Update checksum. sConf.updateChecksum(); // Notify clients. for (client = clientList; client != none; client = client.nextClient) { client.configChanged(configType, sConf.updateCount, sConf.dynamicChecksum); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].configChanged(configType); index++; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the clients that a certain part of the server configuration has changed. * $PARAM configType Type of settings that have been changed. * $ENSURE new.sConf.updateCount = old.sConf.updateCount + 1 * **************************************************************************************************/ function signalGameInfoUpdate(byte infoType) { local NexgenClient client; // Set update counter. gInf.updateCount++; // Notify clients. for (client = clientList; client != none; client = client.nextClient) { client.gameInfoChanged(infoType, gInf.updateCount); } } /*************************************************************************************************** * * $DESCRIPTION Notifies all clients that a specified attribute of a player has changed. * $PARAM client The client of which an attribute has changed. * $REQUIRE client != none * **************************************************************************************************/ function announcePlayerAttrChange(NexgenClient client, string attributeName, coerce string value) { local NexgenClient c; local string args; // Get event arguments. class'NexgenUtil'.static.addProperty(args, attributeName, value); // Signal attribute change events. for (c = clientList; c != none; c = c.nextClient) { c.playerEvent(client.playerNum, client.PE_AttributeChanged, args); } } /*************************************************************************************************** * * $DESCRIPTION Forces the current game to end. * **************************************************************************************************/ function forceEndGame() { Local Actor a; local pawn aPawn; // Set end game comments. level.game.gameReplicationInfo.gameEndedComments = lng.forcedEndMsg; // Notify clients. for (aPawn = level.pawnList; aPawn != none; aPawn=aPawn.nextPawn) { if (aPawn.bIsPlayer) { aPawn.gotoState('GameEnded'); aPawn.clientGameEnded(); } } // Set game info end flags. level.game.bGameEnded = true; level.game.gameReplicationInfo.bStopCountDown = true; if (level.game.isA('DeathMatchPlus')) { DeathMatchPlus(level.game).endTime = level.timeSeconds + 3.0; } // Signal triggers. foreach level.game.allActors(class'Actor', a, 'EndGame') { a.trigger(level.game, none); } } /*************************************************************************************************** * * $DESCRIPTION Called when a player was killed by another player. * $PARAM killer The pawn that killed the other pawn. Might be none. * $PARAM victim Pawn that was the victim. * $OVERRIDE * **************************************************************************************************/ function scoreKill(Pawn killer, Pawn victim) { local int index; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].scoreKill(killer, victim); index++; } // Let other mutators do their job. if (nextMutator != none) { nextMutator.scoreKill(killer, victim); } } /*************************************************************************************************** * * $DESCRIPTION Check for other mutators that might create compatibility issues with Nexgen. * **************************************************************************************************/ function doCompatibilityCheck() { local string serverActors; // Get server actor list. serverActors = caps(consoleCommand("get Engine.GameEngine ServerActors")); // Check for UTPure. bUTPureEnabled = instr(serverActors, ".UTPURESA\"") > 0; if (bUTPureEnabled) { nscLog(lng.format(lng.compatibilityModeMsg, "UTPure")); } } /*************************************************************************************************** * * $DESCRIPTION Signals a general event. The event is broadcasted to all clients and called on * all plugins. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function signalEvent(string type, optional string arguments) { local NexgenClient client; local int index; // Notify clients. for (client = clientList; client != none; client = client.nextClient) { client.notifyEvent(type, arguments); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].notifyEvent(type, arguments); index++; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ b>^"C‰ñl¿-l(ž-^"0r\ *\ êtr[ *[ a X [ Ç\ Ç›w[ *-l'ê[ ½½w\ *ê\  c>\"xq>X -\"5zX]"<IX M> 10[>Y"N_/cfpY",T~f,9‚-[ ™T%½‚™“T}\%zf“T}\}\\]pp]€f“T}\,ff’T},T~f,6]p]€fT|€]’}\}=p\=-['6]ff’T},T~f, [-[]’}\}=aV"  V€A?{ l`ûg,ca CZ‚|1-UseNexgenHUDtruetruew X *C'Za¬€?€@'{ %³–{ ,©w{ Q*{ Q\¥{ w^’J1-RunCount0&1<RunCountS^1b$˜^, 4 r%\ h>W"Q[V BV -W"6zV-X"@|VT' T€@M×/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenContentPanel * $VERSION 1.08 (18-11-2007 21:51) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page with layout managing support. This basic * UWindowPageWindow provides functions to automatically setup the layout of the * window, so it is not necessary to position all components manually. * **************************************************************************************************/ class NexgenContentPanel extends UWindowPageWindow; struct LayoutRegion { // Structure for containing the definition of a region. var float x; // Horizontal offset. var float y; // Vertical offset. var float w; // Width of the region. var float h; // Height of the region. }; var LayoutRegion regions[64]; // Active regions for this panel. var int regionCount; // Number of regions created. var int currRegion; // Current selected region. var enum EPanelBackType { // Background type of the panel. PBT_Default, // Default background. PBT_Beveled, // Beveld background. PBT_Transparent // No background. } panelBGType; var NexgenContentPanel parentCP; // Parent NexgenContentPanel (may be none). const borderSize = 4.0; // Distance between the root region and the window borders. const minRegionSize = 4.0; // Minimum size of a region (in pixels). const AL_Center = 0; // Component is aligned in the center. const AL_Left = 1; // Component is aligned to the left border of the region. const AL_Top = 1; // Component is aligned to the upper border of the region. const AL_Right = 2; // Component is aligned to the right border of the region. const AL_Bottom = 2; // Component is aligned to the lower border of the region. const defaultComponentDist = 4.0; // Default distance between components. const defaultButtonHeight = 16.0; // Default height for UWindowSmallButton components. const defaultLabelHeight = 12.0; // Default height for UMenuLabelControl components. const defaultEditBoxHeight = 16.0; // Default height for UWindowEditControl components. const defaultCheckBoxHeight = 13.0; // Default height for UWindowCheckbox components. const defaultRaisedButtonHeight = 18.0; // Default height for UMenuRaisedButton components. const defaultlistComboHeight = 16.0; // Default height for UWindowComboControl components. const separator = ","; // Token used to seperate elements in a list. /*************************************************************************************************** * * $DESCRIPTION Adds a new region to the content panel. * $PARAM x Horizontal offset. * $PARAM y Vertical offset. * $PARAM w Width of the region. * $PARAM h Height of the region. * $REQUIRE w >= 0 && h >= 0 * $ENSURE result >= 0 ? new.regionCount = old.regionCount + 1 && * regions[result].x = x && * regions[result].y = y && * regions[result].w = w && * regions[result].h = h * : true * **************************************************************************************************/ function int addRegion(float x, float y, float w, float h) { local int index; // Check if there is room for another region. if (regionCount < arrayCount(regions)) { // There is, add the region. regions[regionCount].x = x; regions[regionCount].y = y; regions[regionCount].w = w; regions[regionCount].h = h; return regionCount++; } else { // There isn't, return failure. return -1; } } /*************************************************************************************************** * * $DESCRIPTION Adds the root region to the content panel. This region occupies the whole surface * of this content panel. * $REQUIRE regionCount == 0 * $ENSURE new.regionCount = old.regionCount + 1 * **************************************************************************************************/ function createWindowRootRegion() { currRegion = addRegion(borderSize + 2.0, borderSize + 2.0, winWidth - 2.0 * borderSize - 8.0, winHeight - 2.0 * borderSize - 10.0); } /*************************************************************************************************** * * $DESCRIPTION Adds the root region to the content panel. This region occupies the whole surface * of this content panel. Use this function instead of createRootRegion() if the * panel is added as a component on another panel. * $REQUIRE regionCount == 0 * $ENSURE new.regionCount = old.regionCount + 1 * **************************************************************************************************/ function createPanelRootRegion() { currRegion = addRegion(borderSize + 2.0, borderSize + 2.0, winWidth - 2.0 * borderSize - 4.0, winHeight - 2.0 * borderSize - 4.0); } /*************************************************************************************************** * * $DESCRIPTION Selects the region that is to be used. * $PARAM region Index of the region which should be selected. * $REQUIRE 0 <= region && region < regionCount * $ENSURE currRegion == region * **************************************************************************************************/ function selectRegion(int region) { currRegion = region; } /*************************************************************************************************** * * $DESCRIPTION Skips the current region and selects the next one. * $REQUIRE 0 <= currRegion && currRegion < regionCount * **************************************************************************************************/ function skipRegion() { currRegion++; } /*************************************************************************************************** * * $DESCRIPTION Splits the current region in two horizontal sub regions. Once this function has * been called it will automatically select the next region. * $PARAM height Determines the location of the point where the current region is to be * split. The exact semantics of this parameter depends on the bPercent and * bBottom parameter. * $PARAM dist Distance between the two sub regions (in pixels). * $PARAM bPercent Indicates whether height is an absolute or relative value. * $PARAM bBottom Whether the height parameter counts for the lower or upper sub region. * $REQUIRE (bPercent ? 0 < height && height < 100 : height > 0) && dist >= 0 && * 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1. * **************************************************************************************************/ function int splitRegionH(float height, optional int dist, optional bool bPercent, optional bool bBottom) { local float splitPoint; local float region1height; local float region2height; local float region1top; local float region2top; local int index; // Determine splitpoint. if (bPercent) { if (bBottom) { splitPoint = int(regions[currRegion].h * (100.0 - height) / 100.0); } else { splitPoint = int(regions[currRegion].h * height / 100.0); } } else { if (bBottom) { splitPoint = regions[currRegion].h - height; } else { splitPoint = height; } } // Determine region metrics. region1height = fMax(splitPoint - int(dist / 2.0), minRegionSize); region2height = fMax(regions[currRegion].h - region1height - dist, minRegionSize); region1top = regions[currRegion].y; region2top = region1top + region1height + dist; // Add regions. index = addRegion(regions[currRegion].x, region1top, regions[currRegion].w, region1height); addRegion(regions[currRegion].x, region2top, regions[currRegion].w, region2height); currRegion++; // Automatically select next region. // Return index of first created region. return index; } /*************************************************************************************************** * * $DESCRIPTION Splits the current region in two vertical sub regions. Once this function has been * called it will automatically select the next region. * $PARAM width Determines the location of the point where the current region is to be * split. The exact semantics of this parameter depends on the bPercent and * bRight parameter. * $PARAM dist Distance between the two sub regions (in pixels). * $PARAM bPercent Indicates whether width is an absolute or relative value. * $PARAM bRight Whether the width parameter counts for the left or right sub region. * $REQUIRE (bPercent ? 0 < width && width < 100 : width > 0) && dist >= 0 && * 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1. * **************************************************************************************************/ function int splitRegionV(float width, optional int dist, optional bool bPercent, optional bool bRight) { local float splitPoint; local float region1width; local float region2width; local float region1left; local float region2left; local int index; // Determine splitpoint. if (bPercent) { if (bRight) { splitPoint = int(regions[currRegion].w * (100.0 - width) / 100.0); } else { splitPoint = int(regions[currRegion].w * width / 100.0); } } else { if (bRight) { splitPoint = regions[currRegion].w - width; } else { splitPoint = width; } } // Determine region metrics. region1width = fMax(splitPoint - int(dist / 2.0), minRegionSize); region2width = fMax(regions[currRegion].w - region1width - dist, minRegionSize); region1left = regions[currRegion].x; region2left = region1left + region1width + dist; // Add regions. index = addRegion(region1left, regions[currRegion].y, region1width, regions[currRegion].h); addRegion(region2left, regions[currRegion].y, region2width, regions[currRegion].h); currRegion++; // Automatically select next region. // Return index of first created region. return index; } /*************************************************************************************************** * * $DESCRIPTION Divides the current region into a specified amount of equal sized parts. The * current region will be sliced using horizontal cuts. Automatically selects the * next region. * $PARAM amount Number of sub regions to create. * $PARAM dist Distance between each created sub region (in pixels). * $REQUIRE amount >= 0 && dist >= 0 && 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1, etc. * **************************************************************************************************/ function int divideRegionH(int amount, optional int dist) { local float totalRegionHeight; local float regionHeight; local float leftOver; local float carryOver; local float currHeight; local float currTop; local int index; local int result; local int count; // Determine region metrics. totalRegionHeight = regions[currRegion].h - (amount - 1) * dist; regionHeight = int(totalRegionHeight / amount); leftOver = (totalRegionHeight - regionHeight * amount) / amount; currTop = regions[currRegion].y; // Create regions. for (count = 0; count < amount; count++) { // Finish current region metrics. currHeight = regionHeight; carryOver += leftOver; if (carryOver >= 1) { carryOver = carryOver - 1.0; currHeight = currHeight + 1.0; } // Add the region. index = addRegion(regions[currRegion].x, currTop, regions[currRegion].w, currHeight); if (count == 0) { result = index; } // Update y-offset. currTop = currTop + currHeight + dist; } currRegion++; // Automatically select next region. // Return index of first created region. return result; } /*************************************************************************************************** * * $DESCRIPTION Divides the current region into a specified amount of equal sized parts. The * current region will be sliced using horizontal cuts. Automatically selects the * next region. * $PARAM amount Number of sub regions to create. * $PARAM dist Distance between each created sub region (in pixels). * $REQUIRE amount >= 0 && dist >= 0 && 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1, etc. * **************************************************************************************************/ function int divideRegionV(int amount, optional int dist) { local float totalRegionWidth; local float regionWidth; local float leftOver; local float carryOver; local float currWidth; local float currLeft; local int index; local int result; local int count; // Determine region metrics. totalRegionWidth = regions[currRegion].w - (amount - 1) * dist; regionWidth = int(totalRegionWidth / amount); leftOver = (totalRegionWidth - regionWidth * amount) / amount; currLeft = regions[currRegion].x; // Create regions. for (count = 0; count < amount; count++) { // Finish current region metrics. currWidth = regionWidth; carryOver += leftOver; if (carryOver >= 1) { carryOver = carryOver - 1.0; currWidth = currWidth + 1.0; } // Add the region. index = addRegion(currLeft, regions[currRegion].y, currWidth, regions[currRegion].h); if (count == 0) { result = index; } // Update y-offset. currLeft = currLeft + currWidth + dist; } currRegion++; // Automatically select next region. // Return index of first created region. return result; } /*************************************************************************************************** * * $DESCRIPTION Creates a new component and places it on the current region. Also automatically * selects the next region. * $PARAM wndClass Component type to create. * $PARAM width Preferred width of the component. Use 0 to use all space available. * $PARAM height Preferred height of the component. Use 0 to use all space available. * $PARAM hAlign Horizontal alignment of the component on the region. * $PARAM vAlign Vertical alignment of the component on the region. * $REQUIRE wndClass != none && width >= 0 && height >= 0 && * (hAlign == AL_Left || hAlign == AL_Center || hAlign == AL_Right) && * (vAlign == AL_Top || vAlign == AL_Center || vAlign == AL_Bottom) && * 0 <= currRegion && currRegion < regionCount * $RETURN The component that has been created and added to the window. * $ENSURE result != none * **************************************************************************************************/ function UWindowWindow addComponent(class wndClass, optional float width, optional float height, optional byte hAlign, optional byte vAlign) { local float x; local float y; local float w; local float h; // Determine horizontal metrics. if (width <= 0.0) { x = regions[currRegion].x; w = regions[currRegion].w; } else { w = width; if (hAlign == AL_Left) { x = regions[currRegion].x; } else if (hAlign == AL_Right) { x = regions[currRegion].x + regions[currRegion].w - width; } else { x = int(regions[currRegion].x + (regions[currRegion].w - width) / 2.0); } } // Determine vertical metrics. if (height <= 0.0) { y = regions[currRegion].y; h = regions[currRegion].h; } else { h = height; if (vAlign == AL_Top) { y = regions[currRegion].y; } else if (vAlign == AL_Bottom) { y = regions[currRegion].y + regions[currRegion].h - height; } else { y = int(regions[currRegion].y + (regions[currRegion].h - height) / 2.0); } } // Select next region. currRegion++; // Create component. return createWindow(wndClass, x, y, w, h); } /*************************************************************************************************** * * $DESCRIPTION Adds a new button component to the current region. * $PARAM text Text to display on the button. * $PARAM width Width of the button (in pixels). Use 0 to use all space available. * $PARAM hAlign Horizontal alignment of the button on the current region. * $REQUIRE width >= 0 && (hAlign == AL_Left || hAlign == AL_Center || hAlign == AL_Right) && * 0 <= currRegion && currRegion < regionCount * $RETURN The button that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowSmallButton addButton(optional string text, optional float width, optional byte hAlign) { local UWindowSmallButton button; button = UWindowSmallButton(addComponent(class'UWindowSmallButton', width, defaultButtonHeight, hAlign, AL_Center)); button.setText(text); button.register(self); return button; } /*************************************************************************************************** * * $DESCRIPTION Adds a new label component to the current region. * $PARAM text Text displayed on the label component. * $PARAM bBold Whether or not the text is displayed in a bold font. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The label that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UMenuLabelControl addLabel(optional coerce string text, optional bool bBold, optional TextAlign align) { local UMenuLabelControl label; label = UMenuLabelControl(addComponent(class'UMenuLabelControl', , defaultLabelHeight, , AL_Center)); label.setText(text); if (bBold) { label.setFont(F_Bold); } label.align = align; return label; } /*************************************************************************************************** * * $DESCRIPTION Adds a new edit box component to the current region. * $PARAM text Value of the edit box. * $PARAM width Width of the button (in pixels). Use 0 to use all space available. * $PARAM hAlign Horizontal alignment of the edit box on the current region. * $REQUIRE width >= 0 && (hAlign == AL_Left || hAlign == AL_Center || hAlign == AL_Right) && * 0 <= currRegion && currRegion < regionCount * $RETURN The edit box that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenEditControl addEditBox(optional string text, optional float width, optional byte hAlign) { local NexgenEditControl editBox; local float editBoxWidth; if (width > 0) { editBoxWidth = width; } else { editBoxWidth = regions[currRegion].w; } editBox = NexgenEditControl(addComponent(class'NexgenEditControl', width, defaultEditBoxHeight, hAlign, AL_Center)); editBox.editBoxWidth = editBoxWidth; editBox.setValue(text); return editBox; } /*************************************************************************************************** * * $DESCRIPTION Adds a new check box component to the current region. * $PARAM align Alignment of the check box. * $PARAM text The text to display on the check box. * $PARAM bBold Whether or not the text is displayed in a bold font. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The check box that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowCheckbox addCheckBox(optional TextAlign align, optional string text, optional bool bBold) { local UWindowCheckbox checkBox; checkBox = UWindowCheckbox(addComponent(class'UWindowCheckbox', , defaultCheckBoxHeight, , AL_Center)); checkBox.setText(text); checkBox.align = align; if (bBold) { checkBox.setFont(F_Bold); } return checkBox; } /*************************************************************************************************** * * $DESCRIPTION Adds a new raised button component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UMenuRaisedButton addRaisedButton() { local UMenuRaisedButton raisedButton; raisedButton = UMenuRaisedButton(addComponent(class'UMenuRaisedButton', , defaultRaisedButtonHeight, , AL_Center)); return raisedButton; } /*************************************************************************************************** * * $DESCRIPTION Adds a new image box component to the current region. * $PARAM image Texture to display on the component. * $PARAM bStretch Whether or not the image should be streched. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised button that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenImageControl addImageBox(optional Texture image, optional bool bStretch, optional float width, optional float height) { local NexgenImageControl imageBox; imageBox = NexgenImageControl(addComponent(class'NexgenImageControl', width, height, AL_Center, AL_Center)); imageBox.image = image; imageBox.bStretch = bStretch; return imageBox; } /*************************************************************************************************** * * $DESCRIPTION Adds a new component container panel to the current region. * $PARAM bgType Panel border/background style. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenContentPanel addContentPanel(optional EPanelBackType bgType) { local NexgenContentPanel contentPanel; contentPanel = NexgenContentPanel(addComponent(class'NexgenContentPanel')); contentPanel.panelBGType = bgType; contentPanel.createPanelRootRegion(); contentPanel.parentCP = self; return contentPanel; } /*************************************************************************************************** * * $DESCRIPTION Adds a new combo control component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The combo control that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowComboControl addListCombo() { local UWindowComboControl listCombo; listCombo = UWindowComboControl(addComponent(class'UWindowComboControl', , defaultlistComboHeight, , AL_Center)); listCombo.editBoxWidth = listCombo.winWidth; listCombo.setEditable(false); return listCombo; } /*************************************************************************************************** * * $DESCRIPTION Adds a new dynamic text area control component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The dynamic text area control that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowDynamicTextArea addDynamicTextArea() { local UMenuMapListFrameCW frame; local UWindowDynamicTextArea textArea; frame = UMenuMapListFrameCW(addComponent(class'UMenuMapListFrameCW')); textArea = UWindowDynamicTextArea(CreateControl(class'UWindowDynamicTextArea', 0, 0, 100, 100)); textArea.setTextColor(lookAndFeel.editBoxTextColor); textArea.bTopCentric = false; frame.frame.setFrame(textArea); return textArea; } /*************************************************************************************************** * * $DESCRIPTION Adds a new dynamic text area control component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The dynamic text area control that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowListBox addListBox(class listBoxClass) { local UMenuMapListFrameCW frame; local UWindowListBox listBox; frame = UMenuMapListFrameCW(addComponent(class'UMenuMapListFrameCW')); listBox = UWindowListBox(CreateControl(listBoxClass, 0, 0, 100, 100)); frame.frame.setFrame(listBox); return listBox; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Delegate events to parent. if (parentCP != none) { parentCP.notify(control, eventType); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of a key press event. * $PARAM key The number of the key that was pressed. * $PARAM x Unknown, x location of mouse cursor? * $PARAM y Unknown, x location of mouse cursor? * $OVERRIDE * **************************************************************************************************/ function keyDown(int key, float x, float y) { // Delegate events to parent. if (parentCP != none) { parentCP.keyDown(key, x, y); } } /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ switch (panelBGType) { case PBT_Beveled: drawUpBevel(c, 0, 0, winWidth, winHeight, getLookAndFeelTexture()); break; case PBT_Transparent: // Do nothing. break; default: super.paint(c, x, y); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ } €@M /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfigSys * $VERSION 1.00 (27-1-2007 14:40) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen configuration container/replication class for configurations stored in the * servers configuration file. * **************************************************************************************************/ class NexgenConfigSys extends NexgenConfig config(system); j>Q"-=i mH‚-U –} ,@>|6e } UQ"-U'E¥} e-U6S} UkR" `>N"vŽ‰6þEN"<—EtB¢EtB¥B Œ—E`Bz¢E`Bz‰—£z, z&¥B<ð—E“`Bzrr’“E`Bzrí—£z, z&¥Bü¡rE  U~ }&><–~ ,@6e ~ U6S~ U¥~  D /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfigExt * $VERSION 1.00 (27-1-2007 14:40) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen configuration container/replication class for external stored configuration * file (Nexgen.ini) * **************************************************************************************************/ class NexgenConfigExt extends NexgenConfig config(Nexgen); [€@MÑ/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfigChecker * $VERSION 1.04 (19-1-2008 19:09) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Configuration checking class. This class contains code for checking the * configuration of the server controller. It will automatically try to repair any * errors or inconsitensties. The code has been place into a separate class to save * NexgenConfig from becoming too large (and therefore unreadable). * **************************************************************************************************/ class NexgenConfigChecker extends info; /*************************************************************************************************** * * $DESCRIPTION Validates the given Nexgen configuration. Any invalid settings will be * automatically adjusted to a proper setting. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True, if the configuration was valid, false otherwise. * $ENSURE !old.checkConfig(c) ? new.checkConfig(c) : true * **************************************************************************************************/ function bool checkConfig(NexgenConfig c) { local bool bInvalid; bInvalid = checkEncryption(c) || checkGlobalServerSettings(c) || checkExtraServerSettings(c) || checkBootControlSettings(c) || checkAccountSystemSettings(c) || checkBanList(c) || checkMatchSettings(c); // Save repaired configuration if needed. if (bInvalid) { c.saveConfig(); } // Return result. return !bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the server encryption parameters. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkEncryption(NexgenConfig c) { local bool bInvalid; local int index; bInvalid = c.configEncryptionKey == 0 || len(c.configCodeScheme) != 32; // Fix things if necessary. if (bInvalid) { c.resetEncryptionConfig(); c.globalServerPassword = c.encode(consoleCommand("get Engine.GameInfo GamePassword")); c.globalAdminPassword = c.encode(consoleCommand("get Engine.GameInfo AdminPassword")); c.serverPassword = ""; for (index = 0; index < arrayCount(c.atPassword); index++) { c.atPassword[index] = ""; } } // Return result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the global server settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkGlobalServerSettings(NexgenConfig c) { local bool bInvalid; // Check server info settings. if (c.serverName == "") { bInvalid = true; c.serverName = "[NEXGEN] Another UT Server"; } bInvalid = bInvalid || fixStrLen(c.serverName, 128); bInvalid = bInvalid || fixStrLen(c.shortName, 32); bInvalid = bInvalid || fixStrLen(c.adminName, 64); bInvalid = bInvalid || fixStrLen(c.adminEmail, 64); bInvalid = bInvalid || fixStrLen(c.MOTDLine[0], 192); bInvalid = bInvalid || fixStrLen(c.MOTDLine[1], 192); bInvalid = bInvalid || fixStrLen(c.MOTDLine[2], 192); bInvalid = bInvalid || fixStrLen(c.MOTDLine[3], 192); // Check slot settings. bInvalid = bInvalid || fixByteRange(c.playerSlots, 0, 32); bInvalid = bInvalid || fixByteRange(c.vipSlots, 0, 16); bInvalid = bInvalid || fixByteRange(c.adminSlots, 0, 16); bInvalid = bInvalid || fixByteRange(c.spectatorSlots, 0, 16); if (c.playerSlots + c.vipSlots + c.adminSlots <= 0) { bInvalid = true; c.playerSlots = 16; } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the extra server settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkExtraServerSettings(NexgenConfig c) { local bool bInvalid; local int index; bInvalid = bInvalid || fixByteRange(c.waitTime, 0, 60); bInvalid = bInvalid || fixByteRange(c.startTime, 0, 30); bInvalid = bInvalid || fixByteRange(c.autoReconnectTime, 0, 60); bInvalid = bInvalid || fixIntRange(c.maxIdleTime, 0, 9999); bInvalid = bInvalid || fixIntRange(c.maxIdleTimeCP, 0, 9999); bInvalid = bInvalid || fixByteRange(c.spawnProtectionTime, 0, 60); bInvalid = bInvalid || fixByteRange(c.teamKillDamageProtectionTime, 0, 30); bInvalid = bInvalid || fixByteRange(c.teamKillPushProtectionTime, 0, 60); bInvalid = bInvalid || fixByteRange(c.autoDisableMatchTime, 0, 120); for (index = 0; index < arrayCount(c.spawnProtectExcludeWeapons); index++) { bInvalid = bInvalid || fixStrLen(c.spawnProtectExcludeWeapons[index], 64); } for (index = 0; index < arrayCount(c.replacementClass); index++) { bInvalid = bInvalid || fixStrLen(c.replacementClass[index], 128); } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the boot control settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkBootControlSettings(NexgenConfig c) { local bool bInvalid; if (len(c.bootGameType) > 64) { bInvalid = true; c.bootGameType = ""; } bInvalid = bInvalid || fixStrLen(c.bootMapPrefix, 8); bInvalid = bInvalid || fixStrLen(c.bootOptions, 255); bInvalid = bInvalid || fixStrLen(c.bootCommands, 255); // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the account system settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkAccountSystemSettings(NexgenConfig c) { local bool bInvalid; local int index; // Check account types. while(index < arrayCount(c.atTypeName) && c.atTypeName[index] != "") { bInvalid = bInvalid || fixStrLen(c.atTypeName[index], 24); bInvalid = bInvalid || fixStrLen(c.atRights[index], 255); bInvalid = bInvalid || fixStrLen(c.atTitle[index], 24); if (len(c.atPassword[index]) > 128) { c.atPassword[index] = ""; bInvalid = true; } index++; } // Check user accounts. index = 0; while(index < arrayCount(c.paPlayerID) && c.paPlayerID[index] != "") { bInvalid = bInvalid || fixStrLen(c.paPlayerID[index], 32); bInvalid = bInvalid || fixStrLen(c.paPlayerName[index], 32); bInvalid = bInvalid || fixStrLen(c.paCustomRights[index], 255); bInvalid = bInvalid || fixStrLen(c.paCustomTitle[index], 24); if (c.paAccountType[index] < 0 && class'NexgenUtil'.static.trim(c.paCustomTitle[index]) == "") { bInvalid = true; c.paCustomTitle[index] = "Player*"; } index++; } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the banlist. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkBanList(NexgenConfig c) { local bool bInvalid; local int index; while(index < arrayCount(c.bannedName) && c.bannedName[index] != "") { bInvalid = bInvalid || fixStrLen(c.bannedName[index], 32); bInvalid = bInvalid || fixStrLen(c.bannedIPs[index], 255); bInvalid = bInvalid || fixStrLen(c.bannedIDs[index], 255); bInvalid = bInvalid || fixStrLen(c.banReason[index], 255); bInvalid = bInvalid || fixStrLen(c.banPeriod[index], 32); index++; } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the match settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkMatchSettings(NexgenConfig c) { local bool bInvalid; local int index; bInvalid = bInvalid || fixByteRange(c.matchesToPlay, 1, 100); bInvalid = bInvalid || fixByteRange(c.currentMatch, 1, c.matchesToPlay); if (len(c.serverPassword) > 128) { c.serverPassword = ""; bInvalid = true; } for (index = 0; index < arrayCount(c.tagsToSeparate); index++) { bInvalid = bInvalid || fixStrLen(c.tagsToSeparate[index], 16); } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Fixes the length of a string. This function makes sure the length of a given * string doesn't exceed the specified maximum length. * $PARAM str The string of which the length has to be checked. * $PARAM maxLen The maximum length of the string. * $REQUIRE maxLen >= 0 * $RETURN True if the length of the string was changed, false otherwise. * $ENSURE len(new.str) <= maxLen * **************************************************************************************************/ function bool fixStrLen(out string str, int maxLen) { if (len(str) > maxLen) { str = left(str, maxLen); return true; } else { return false; } } /*************************************************************************************************** * * $DESCRIPTION Fixes the value of a given integer variable. Calling this function will ensure * that the value of the variable will be in the specified domain. * $PARAM intVar The integer variable whose value is to be checked. * $PARAM lowerBound Lower bound on the range of the variable. * $PARAM upperBound Upperbound bound on the range of the variable. * $RETURN True if value of the integer variable was changed, false otherwise. * $ENSURE lowerBound <= intVar && intVar <= upperBound * **************************************************************************************************/ function bool fixIntRange(out int intVar, int lowerBound, int upperBound) { if (intVar < lowerBound) { intVar = lowerBound; return true; } else if (intVar > upperBound) { intVar = upperBound; return true; } else { return false; } } /*************************************************************************************************** * * $DESCRIPTION Fixes the value of a given byte variable. Calling this function will ensure * that the value of the variable will be in the specified domain. * $PARAM byteVar The byte variable whose value is to be checked. * $PARAM lowerBound Lower bound on the range of the variable. * $PARAM upperBound Upperbound bound on the range of the variable. * $RETURN True if value of the byte variable was changed, false otherwise. * $ENSURE lowerBound <= byteVar && byteVar <= upperBound * **************************************************************************************************/ function bool fixByteRange(out byte byteVar, byte lowerBound, byte upperBound) { if (byteVar < lowerBound) { byteVar = lowerBound; return true; } else if (byteVar > upperBound) { byteVar = upperBound; return true; } else { return false; } } @â/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfig * $VERSION 1.27 (23-2-2008 17:02) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen configuration container/replication class. This class contains the settings * for the server controller and necessary code to gather a lot of required data. * WARNING: Any changes made to one of the variables during the game may prevent new * clients from initializing. MAKE SURE THE CHECKSUM IS ALWAYS UP TO DATE!!! * **************************************************************************************************/ class NexgenConfig extends ReplicationInfo; var bool bInitialized; // NexgenConfig instance has been initialized. var NexgenController control; // Server controller. const separator = ","; // Character used to seperate elements in a list. // Replication / data transfer control. var int updateCount; // How many times the settings have been updated // during the current game. Used to detect setting // changes clientside. var int dynamicChecksum; // Checksum for the dynamic replicated variables. var int staticChecksum; // Checksum for the static replicated variables. var int dynamicChecksumSalt; // Dynamic data checksum salt. const CT_GlobalServerSettings = 0; // Global server settings config type. const CT_AccountTypes = 1; // Account types config type. const CT_UserAccounts = 2; // User account list config type. const CT_BanList = 3; // The player ban list config type. const CT_BootControl = 4; // Boot control settings config type. const CT_MatchSettings = 5; // Match settings config type. const CT_ExtraServerSettings = 6; // Extra server settings config type. const CT_ExclWeaponList = 7; // Excluded weapon list. const CT_HUDReplacementList = 8; // HUD class replacement list. // Special settings. var config bool bInstalled; // Whether or not Nexgen has been installed. var private config string serverKey; // Unique server key. // Data encryption support. var int encryptionKey; // Key used to encrypt data. var string codeScheme; // Code scheme used to encrypt data. var config int configEncryptionKey; // You should keep this private. var config string configCodeScheme; // You should keep this private. // Global server settings. var config string serverName; // Name of the server. var config string shortName; // Short server name. var config string adminName; // Name of the admin that controls the server. var config string adminEmail; // Email of the admin that controls the server. var config string globalServerPassword; // Global password needed to enter the server. var config string globalAdminPassword; // Global server administrator password. var config bool enableUplink; // Enable uplink to master server. var config byte playerSlots; // Maximum number of players. var config byte vipSlots; // Extra slots for VIPs if the server if full. var config byte adminSlots; // Extra slots for admins if the server if full. var config byte spectatorSlots; // Number of spectators allowed. var config string MOTDLine[4]; // Message of the day lines. // Extended server settings. var config bool autoUpdateBans; // Automatically update bans? var config bool removeExpiredBans; // Automatically remove expired bans at the // beginning of each game? var config byte waitTime; // Time to wait before the game can be started. var config byte startTime; // Time to wait before the game starts. var config byte autoReconnectTime; // Time to wait before automatically reconnecting. var config int maxIdleTime; // Maximum time a player can be idle. var config int maxIdleTimeCP; // Maximum idle time when the control panel is open. var config byte spawnProtectionTime; // Amount of time spawn protection stays activated. var config byte teamKillDamageProtectionTime; // Amount of time team kill damage protection // stays activated. var config byte teamKillPushProtectionTime; // Amount of time team kill push protection stays // activated. var config bool broadcastTeamKillAttempts; // Notify players of team kill attempts. var config bool logEvents; // Write events to the log? var config bool logSystemMessages; // Write system messages to the log? var config bool logChatMessages; // Write chat messages to the log? var config bool logPrivateMessages; // Write private messages to the log? var config bool allowTeamSwitch; // Whether team switching is allowed by default. var config bool allowTeamBalance; // Whether team balancing is allowed by default. var config bool allowNameChange; // Whether name changing is allowed by default. var config byte autoDisableMatchTime; // Automatically disable match mode if a game is // inactive for more then this amount of minutes. var config string spawnProtectExcludeWeapons[16]; // Weapons and fire modes excluded from cancelling // spawn protection. // Boot control. var config bool enableBootControl; // Whether the nexgen boot control is enabled. var config bool restartOnLastGame; // Whether to restart on the last game played. var config string bootGameType; // Game type to use when booting. var config string bootMapPrefix; // Map prefix to used to select the map to boot. var config string bootMutators; // Mutators to load for the nexgen server boot. var string bootMutatorIndices; // Indices in the mutatorInfo list of the mutators // to load for the nexgen server boot. var config string bootOptions; // Additional boot command line options. var config string bootCommands; // Pre map switch console commands. var config string lastServerURL; // Server commandline URL of last game. // Match setup. var config bool matchModeActivated; // Whether a match is in progress. var config byte matchesToPlay; // Number of games to play for the current match. var config byte currentMatch; // Number of games to play for the current match. var config string serverPassword; // Password (Nexgen) needed to enter the server. var config bool spectatorsNeedPassword; // Do spectators need to enter the password? var config bool muteSpectatorsDuringMatch; // Should spectators be muted during the match? var config bool enableMatchBootControl; // Enable boot control for matches. var config bool matchAutoLockTeams; // Automatically lock teams in match mode. var config bool matchAutoPause; // Automatically pause game when a player leaves. var config bool matchAutoSeparate; // Automatically separate players by tag. var config string tagsToSeparate[4]; // Name tags used to separate players in teams. // Extended HUD. var config bool useNexgenMessageHUD; // Enable the Nexgen message HUD replacement. var config string replacementClass[16]; // HUD class replacement instructions. var class HUDReplacementClass; // Nexgen replacement for the HUD class. // Account system. var config string atTypeName[10]; // Account type names. var config string atRights[10]; // Rights for the account types. var config string atTitle[10]; // Titles for the account types. var config string atPassword[10]; // Password for the account types. var config string paPlayerID[32]; // Player ID list. var config string paPlayerName[32]; // Names of the player accounts. var config int paAccountType[32]; // Associated account type. var config string paCustomRights[32]; // Custom rights if no account type is used. var config string paCustomTitle[32]; // Custom title if no account type is used. var string rightsDef[16]; // Rights definitions. // Ban system. var config string bannedName[32]; // Name of banned player. var config string bannedIPs[32]; // IP address(es) of the banned player. var config string bannedIDs[32]; // Client ID(s) of the banned player. var config string banReason[32]; // Reason why the player was banned. var config string banPeriod[32]; // Ban period; how long the player is banned. const maxBanIPAddresses = 8; // Maximum number of banned IP's per ban entry. const maxBanClientIDs = 6; // Maximum number of banned ID's per ban entry. const BP_Forever = 0; // Banned forever. const BP_Matches = 1; // Banned for 'x' matches. const BP_UntilDate = 2; // Banned until some 'date'. // Misc. var string serverID; // Public (unique) server identification code. // Should be the last value replicated, so the // client can check when the replication is // complete. Edit: this is now taken care of by // the checksum variable. var class serverInfoPanelClass; // Server info panel class. var class gameInfoPanelClass; // Server info panel class. var string gameTypeInfo[16]; // Game type description strings. var string mutatorInfo[32]; // Mutator description strings. var byte activeGameType; // Index in the gameTypeInfo list of the currently // active game type. var string activeMutatorIndices; // Indices in the mutatorInfo list of the mutators // currently loaded. var bool bActiveMutatorIndicesSet; // Whether the activeMutatorIndices has been set. const IW_Fire = "P"; // Ignore weapon primary fire tag. const IW_AltFire = "S"; // Ignore weapon alt fire tag. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Static. Doesn't change during the game. staticChecksum, serverID, rightsDef, HUDReplacementClass, gameTypeInfo, mutatorInfo, activeGameType, serverInfoPanelClass, gameInfoPanelClass, // Dynamic. May change during the game. dynamicChecksum, serverName, shortName, adminName, adminEmail, globalServerPassword, globalAdminPassword, serverPassword, playerSlots, vipSlots, adminSlots, spectatorSlots, spectatorsNeedPassword, enableUplink, MOTDLine, autoReconnectTime, maxIdleTime, atTypeName, atRights, atTitle, atPassword, paPlayerID, paPlayerName, paAccountType, paCustomRights, paCustomTitle, bannedName, bannedIPs, bannedIDs, banReason, banPeriod, enableBootControl, restartOnLastGame, bootGameType, bootMapPrefix, bootMutatorIndices, bootOptions, bootCommands, spawnProtectionTime, activeMutatorIndices, matchModeActivated, matchesToPlay, currentMatch, muteSpectatorsDuringMatch, enableMatchBootControl, matchAutoLockTeams, matchAutoPause, matchAutoSeparate, tagsToSeparate, teamKillDamageProtectionTime, teamKillPushProtectionTime, broadcastTeamKillAttempts, logEvents, logSystemMessages, logChatMessages, logPrivateMessages, autoUpdateBans, removeExpiredBans, useNexgenMessageHUD, waitTime, startTime, allowTeamSwitch, allowTeamBalance, allowNameChange, autoDisableMatchTime, replacementClass, spawnProtectExcludeWeapons, maxIdleTimeCP, dynamicChecksumSalt, updateCount; } /*************************************************************************************************** * * $DESCRIPTION Calculates a checksum of the replicated dynamic variables. * $RETURN The checksum of the replicated variables. * **************************************************************************************************/ simulated function int calcDynamicChecksum() { local int checksum; local int index; checksum += stringHash(serverName); checksum += stringHash(shortName); checksum += stringHash(adminName); checksum += stringHash(adminEmail); checksum += stringHash(globalServerPassword); checksum += stringHash(serverPassword); checksum += playerSlots; checksum += vipSlots; checksum += adminSlots; checksum += boolHash(spectatorsNeedPassword); checksum += boolHash(enableUplink); for (index = 0; index < arrayCount(MOTDLine); index++) { checksum += stringHash(MOTDLine[index]); } checksum += autoReconnectTime; checksum += maxIdleTime; for (index = 0; index < arrayCount(atTypeName); index++) { checksum += stringHash(atTypeName[index]); } for (index = 0; index < arrayCount(atRights); index++) { checksum += stringHash(atRights[index]); } for (index = 0; index < arrayCount(atTitle); index++) { checksum += stringHash(atTitle[index]); } for (index = 0; index < arrayCount(atPassword); index++) { checksum += stringHash(atPassword[index]); } for (index = 0; index < arrayCount(paPlayerID); index++) { checksum += stringHash(paPlayerID[index]); } for (index = 0; index < arrayCount(paPlayerName); index++) { checksum += stringHash(paPlayerName[index]); } for (index = 0; index < arrayCount(paAccountType); index++) { checksum += paAccountType[index]; } for (index = 0; index < arrayCount(paCustomRights); index++) { checksum += stringHash(paCustomRights[index]); } for (index = 0; index < arrayCount(paCustomTitle); index++) { checksum += stringHash(paCustomTitle[index]); } for (index = 0; index < arrayCount(bannedName); index++) { checksum += stringHash(bannedName[index]); } for (index = 0; index < arrayCount(bannedIPs); index++) { checksum += stringHash(bannedIPs[index]); } for (index = 0; index < arrayCount(bannedIDs); index++) { checksum += stringHash(bannedIDs[index]); } for (index = 0; index < arrayCount(banReason); index++) { checksum += stringHash(banReason[index]); } for (index = 0; index < arrayCount(banPeriod); index++) { checksum += stringHash(banPeriod[index]); } checksum += updateCount; checksum += boolHash(enableBootControl); checksum += boolHash(restartOnLastGame); checksum += stringHash(bootGameType); checksum += stringHash(bootMapPrefix); checksum += stringHash(bootMutatorIndices); checksum += stringHash(bootOptions); checksum += stringHash(bootCommands); checksum += spawnProtectionTime; checksum += stringHash(activeMutatorIndices); checksum += boolHash(matchModeActivated); checksum += matchesToPlay; checksum += currentMatch; checksum += boolHash(muteSpectatorsDuringMatch); checksum += boolHash(enableMatchBootControl); checksum += boolHash(matchAutoLockTeams); checksum += boolHash(matchAutoPause); checksum += boolHash(matchAutoSeparate); for (index = 0; index < arrayCount(tagsToSeparate); index++) { checksum += stringHash(tagsToSeparate[index]); } checksum += teamKillDamageProtectionTime; checksum += teamKillPushProtectionTime; checksum += boolHash(broadcastTeamKillAttempts); checksum += boolHash(logEvents); checksum += boolHash(logSystemMessages); checksum += boolHash(logChatMessages); checksum += boolHash(logPrivateMessages); checksum += boolHash(autoUpdateBans); checksum += boolHash(removeExpiredBans); checksum += boolHash(useNexgenMessageHUD); checksum += waitTime; checksum += startTime; checksum += boolHash(allowTeamSwitch); checksum += boolHash(allowTeamBalance); checksum += boolHash(allowNameChange); checksum += autoDisableMatchTime; for (index = 0; index < arrayCount(replacementClass); index++) { checksum += stringHash(replacementClass[index]); } for (index = 0; index < arrayCount(spawnProtectExcludeWeapons); index++) { checksum += stringHash(spawnProtectExcludeWeapons[index]); } checksum += dynamicChecksumSalt; checksum += maxIdleTimeCP; return checksum; } /*************************************************************************************************** * * $DESCRIPTION Calculates a checksum of the replicated static variables. * $RETURN The checksum of the replicated variables. * **************************************************************************************************/ simulated function int calcStaticChecksum() { local int checksum; local int index; for (index = 0; index < arrayCount(rightsDef); index++) { checksum += stringHash(rightsDef[index]); } checksum += stringHash(HUDReplacementClass); for (index = 0; index < arrayCount(gameTypeInfo); index++) { checksum += stringHash(gameTypeInfo[index]); } for (index = 0; index < arrayCount(mutatorInfo); index++) { checksum += stringHash(mutatorInfo[index]); } checksum += stringHash(serverID); checksum += stringHash(serverInfoPanelClass); checksum += stringHash(gameInfoPanelClass); checksum += activeGameType; return checksum; } /*************************************************************************************************** * * $DESCRIPTION Gives the integer hash value of a string. * $PARAM str The string that is to be hashed. * $RETURN The hash value of the given string. * **************************************************************************************************/ static function int stringHash(coerce string str) { local int hash; if (str != "") { hash = 29789 * asc(left(str, 1)) + 953 * asc(mid(str, len(str) / 2, 1)) + 31 * asc(right(str, 1)) + len(str); } return hash; } // This version uses too much CPU processing power. /* static function int stringHash(coerce string str) { local int hash; local int index; for (index = 0; index < len(str); index++) { hash = 31 * hash + asc(mid(str, index, 1)); } return hash; } */ /*************************************************************************************************** * * $DESCRIPTION Gives the integer hash value of a boolean. * $PARAM value The boolean value. * $RETURN The integer hash value of the given boolean value. * $ENSURE result == value ? -1 : 0 * **************************************************************************************************/ static function int boolHash(bool value) { if (value) { return 1; } else { return 0; } } /*************************************************************************************************** * * $DESCRIPTION Updates the checksum for the current replication info. * $ENSURE checksum == calcChecksum() * **************************************************************************************************/ function updateChecksum() { dynamicChecksumSalt = rand(maxInt); dynamicChecksum = calcDynamicChecksum(); } /*************************************************************************************************** * * $DESCRIPTION Loads the server configuration. * $OVERRIDE * **************************************************************************************************/ function preBeginPlay() { // Check owner. if (owner == none || !owner.isA('NexgenController')) { destroy(); return; } control = NexgenController(owner); // Finish initialization. bInitialized = true; } /*************************************************************************************************** * * $DESCRIPTION Finalizes the initialization process. Calling this function makes sure that any * uninitialized variables are set. * $REQUIRE bInitialized && bInstalled * **************************************************************************************************/ function postInitialize() { local string mutatorClass; local string remaining; local int index; local string url; local string activeGameTypeClass; local string gameTypeClass; local bool bFound; serverID = class'MD5Hash'.static.MD5String(serverKey); updateCount = 1; setEncryptionParams(configEncryptionKey, configCodeScheme); // Add default right definitions. addRightDefiniton("A", control.lng.allowedToPlayRightDesc); addRightDefiniton("B", control.lng.vipSlotAccessRightDesc); addRightDefiniton("C", control.lng.adminSlotAccessRightDesc); addRightDefiniton("D", control.lng.needsNoPWRightDesc); addRightDefiniton("E", control.lng.canBeIdleRightDesc); addRightDefiniton("F", control.lng.matchAdminRightDesc); addRightDefiniton("G", control.lng.moderatorRightDesc); addRightDefiniton("K", control.lng.matchSetRightDesc); addRightDefiniton("H", control.lng.banOpRightDesc); addRightDefiniton("I", control.lng.accountMngrRightDesc); addRightDefiniton("J", control.lng.serverAdminRightDesc); // Load game & mutator lists. loadGameTypeList(); loadMutatorList(); // Load mutator index list. remaining = bootMutators; while (remaining != "") { class'NexgenUtil'.static.split(remaining, mutatorClass, remaining); index = getMutatorIndex(mutatorClass); if (index >= 0) { if (bootMutatorIndices == "") { bootMutatorIndices = string(index); } else { bootMutatorIndices = bootMutatorIndices $ separator $ index; } } } // Get current server command line. url = level.getLocalURL(); url = mid(url, instr(url, "/") + 1); lastServerURL = url; // Get currently active game type. activeGameTypeClass = string(level.game.class); index = 0; while (!bFound && index < arrayCount(gameTypeInfo) && gameTypeInfo[index] != "") { // Get game type class. class'NexgenUtil'.static.split(gameTypeInfo[index], gameTypeClass, remaining); // Check if the classes match. if (activeGameTypeClass ~= gameTypeClass) { bFound = true; activeGameType = index; } else { index++; } } // Update match settings. if (matchModeActivated) { if (currentMatch > matchesToPlay) { currentMatch = 0; matchModeActivated = false; } } // Remove expired bans if desired. if (removeExpiredBans) { cleanExpiredBans(); } // Update ban periods. updateBanPeriods(); // Save data. saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Loads a list of all game types available on this server. * **************************************************************************************************/ function loadGameTypeList() { local string game; local string description; local int index; local string gameInfoStr; local class gameClass; local string mapName; // For each game type... getNextIntDesc("TournamentGameInfo", 0, game, description); while(game != "" && index < arrayCount(gameTypeInfo)) { // Get game type info. gameClass = class(dynamicLoadObject(game, class'Class')); if (class'NexgenUtil'.static.trim(description) == "") { description = gameClass.default.gameName; } gameInfoStr = game $ separator $ gameClass.default.mapPrefix $ separator $ description; // Store game type info. gameTypeInfo[index] = gameInfoStr; // Continue with next game type. getNextIntDesc("TournamentGameInfo", ++index, game, description); } } /*************************************************************************************************** * * $DESCRIPTION Loads a list of all mutators available on this server. * **************************************************************************************************/ function loadMutatorList() { local string mutator; local string description; local int index; local string mutatorInfoStr; // For each mutator... getNextIntDesc("Mutator", 0, mutator, description); while (mutator != "" && index < arrayCount(mutatorInfo)) { // Get mutator info. if (instr(description, ",") >= 0) { description = left(description, instr(description, ",")); } mutatorInfoStr = mutator $ separator $ description; // Store mutator info. mutatorInfo[index] = mutatorInfoStr; // Continue with next mutator. getNextIntDesc("Mutator", ++index, mutator, description); } } /*************************************************************************************************** * * $DESCRIPTION Generates new encryption parameters for the server. * **************************************************************************************************/ function resetEncryptionConfig() { local byte cb; local bool bValidChar; configEncryptionKey = (rand(0x10000) << 16) | rand(0x10000); configCodeScheme = ""; while (len(configCodeScheme) < 32) { do { cb = rand(93) + 33; bValidChar = (cb != 34) && (instr(configCodeScheme, chr(cb)) < 0); } until (bValidChar); configCodeScheme = configCodeScheme $ chr(cb); } setEncryptionParams(configEncryptionKey, configCodeScheme); } /*************************************************************************************************** * * $DESCRIPTION Performs a default installation of the Nexgen Server Controller. Calling this * function will automatically configure the server for first use. * $REQUIRE bInitialized && !bInstalled * **************************************************************************************************/ function install() { local string packageName; packageName = class'NexgenUtil'.default.packageName; // Create unique key for this server. serverKey = class'NexgenUtil'.static.makeKey(); // Set encryption parameters for this server. resetEncryptionConfig(); // Add default account types. addAccountType("", "A", "Player"); // No account. addAccountType("VIP", "A,B"); addAccountType("Level 3 Admin", "A,B,C,D,F", "L3 Admin"); addAccountType("Level 4 Admin", "A,B,C,D,F,G", "L4 Admin"); addAccountType("Level 5 Admin", "A,B,C,D,E,F,G,H,K", "L5 Admin"); addAccountType("Level 6 Admin", "A,B,C,D,E,F,G,H,I,K", "L6 Admin"); addAccountType("Level 7 Admin", "A,B,C,D,E,F,G,H,I,J,K", "L7 Admin"); // Set default settings. serverName = level.game.gameReplicationInfo.serverName; shortName = level.game.gameReplicationInfo.shortName; adminName = level.game.gameReplicationInfo.adminName; adminEmail = level.game.gameReplicationInfo.adminEmail; globalServerPassword = encode(consoleCommand("get Engine.GameInfo GamePassword")); globalAdminPassword = encode(consoleCommand("get Engine.GameInfo AdminPassword")); serverPassword = ""; enableUplink = consoleCommand("get IpServer.UdpServerUplink DoUplink") ~= "True"; spectatorsNeedPassword = false; playerSlots = level.game.maxPlayers; vipSlots = 2; adminSlots = 2; spectatorSlots = level.game.maxSpectators; autoUpdateBans = tru