*E% @6=}+kOL9E;ab% 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+s9u9>NBQ։> Y M Z> Y M Z}"#}"#Q֎> Y}"#ZQ+s9[qZZZ+s9[q+s9[q[q[q[q[q> Y> Y> Y> Y> Y+s+s+s/T2/T2+s9E6E6E6E6/T2/T2/T2/T2o .'l.'l.'l+s+s/T2o /T2o /T2o +s+s9[qE6E6/T2o /T2o /T2o E6E6E6E6E6> YR@G/T2/T2/T2/T2 M ZQu9 M Z> YR@G> YR@GI&/> Y> Y5I> Y5IQ֎> Y5I5I> Y> YM[qMMMu9> Y{#U> Y> YQ֊ud2ZZ> Y> Y> Y> YX' M Z M Z> Y> Y> Y> Y> Y> YZZ> Y> Y+s> YQ֎> Y{#U> YZZ> Y M ZZZZ> YMM> YQ֎> Y{#UQ3 e~|$@n>a2l / ::$bYQZ=$: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 ZvުQu9Qu9Qu9Qu9Qu9Qu9Qu9Qu9Qu9Qu9Qu9 M Z M ZԝX3 e*\>H5^E Q֧=$:e,,,I1u9u9I1=$:e'n 6u9ttttt'n 6-%nԝX M Z,,,,,Z,,Z,,,,I1 M ZtPZtPZI1=$:eԝXttttEl{#UI1=$:eԝXtԝX{#U{#U{#U,ZZ,,,,,u9ttt{#UI1=$:eԝX,,u9ttt{#UI1=$:eZZZI1ZZZI1,Z,,,u9,u9,ZZZZZZZZZZZZZZ,,,> Y{#U,,> Y{#U,ZZZZ,,> Y, M Z,,,,,, M Z, M Z M Z,,,u9u9u9u9u9u9ZZZZZZZ-%n-%nZZZZZG,,> Y{#U,,> Y{#U,> Y,,,,> Y{#U,> Y,> Y,,u9I1=$:e,,u9'n 6ttt{#Ut{#U,,u9t{#Ut{#UtI1=$:e,,,Zu9Zu9,u9,,> Y{#U,I1,,u9,,,Zu9,,,> Y{#Uu9,,> YI1,,u9,V^S,,ZI1u9,,u9u9,ZZZ,Z,I1,,}"#,,> Y{#U,,,> Y{#U,,ZZ,,,,ZI1ZZZ,,,u9,u9u9,,,> Y,I1I1=$:e,,,,,,> Y,V^S,V^SV^S,V^S,,,,,,,,,,,,,u9,,, M Z,,u9u9,u9u9,u9u9 M Z,u9u9u9u9,,,u9,u9,,,u9I1I1,u9u9,,u9,ZI1=$:e,,,,,,,, M ZI1u9u9ZZZZZ}"#ZZZZ'n 6'n 6'n 6'n 6'n 6'n 6'n 6'n 6 M Zu9u9u9u9u9u9u9u9u9u9u9u9 M Z-%n-%nu93mg ee>_*p l5ezZԌzZl5e_4_4_4c ¦c ¦|.Xڋ|.Xڋ|.X[[[[᱘᱘᱘7XÍ7X Q

D 2u9U[ڊ 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]redxbluexgreenxyellowo ]%$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 ZxW M Z M Z z$ff?y">c] Nexgen105x]CountryFlags2] ' W9U;f  EyEy&. EyE EyE o" b9X9e  yJ~ EyE EyE EyE EyExxxxxxxxxxo  EyE ]$PAs ~=I2y / ::$ZYQZ3 r;O;N ^y&.^^^^^^^^^^^^^^^^^^ M Z H"%[~>T1i ::$::$@t[q,,Q M Z M ZQQQQ M Z M ZQQQ,,QQQQQQQQQ,,Q M ZQQQ M ZQQQQQQ,,QQQQQQQQQ,,QQu9,,> YQZ,,QQQQQQQQQQQQ,Qu9,,QQQQQQQQQQQQQQ,,QZQZQ,QZQQu9,,> YQQu9,,> Y,,QZQZQ,QZQQu9,,> YQQu9,,> Y,,tt> Y> Y> Yt,,,,QQQ M Z M ZQu9,,> YQQu9,,,> YQQQ,Q,Q,QQQQQ,,Q,,> YQQu9,,,> Y M Z,,Q,,QQu9,,> Y,,Q,,,QQu9,,,> YQQu9,,,> Y,,QQQQQZQZQZQQZQQ,QQQQQu9,,> YQQu9,,> Y,,QQQQQQQQQQQQQQQQQ,Qu9,M,,M,,,M,,u9,u9,,QQQ,tQ,,t,,,Q,,Q}"#,Q,> Y{#U,,,,QQ, M Z,QQQQQ M Z, M Z,QQQQQQQQQ,Qu9,, M ZQQQQQQQQQQ,, M ZQQQQQQQQQQQQu9,,> Y,,QQQQQQQQQu9,,> YQQQQQQQQ,,QQu9Q,,QQu9,Q,> Y,,> YQQQ,Qu9,,QZQZQ,QZQQu9,,> YQQu9,,> Y,,QZQZQ,QZQQu9,,> YQQu9,,> Y,,QZQZQ,QZQQu9,,> YQQu9,,> Y M ZQ,,,QQu9,,,> YQ,,,,,Q,,QQu9,,,> YQQu9,,,> YQ,,,,,QQu9,,,> YQQu9,,,> YQ,,'n 6}"#,,QQu9,,Qu9,> Y,,QQu9,,> Y,,QZQZQQQu9,,> Y,,QZQZ,QQu9,,> YQQu9,,> Y,, M ZQQ M ZQQQQ,,> YQQQQQQQQ,,QQ M ZQQ M ZQQ,,> YQ M ZQQQQQQQQ,,QQQQ,,> YQQQQQQQQQQQQQQQQQQQQ,,QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ,,QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ,Q,,Qu9QQ,,Q,,,,Q M ZQQ M ZQQQQQ,,QQQQQu9Qu9Q M ZQQ M ZQQQQQ,M,,> Y,+s9⥊v3Tv3T,{#U,,u9,u9{#U,,,QQQQZQZ,,,,,,,,,t,,> Y,,Q,,Q,,QQQQQQQQQQQQQQQQQQQ'n 6'n 6'n 6'n 6'n 6'n 6'n 6'n 6QQ,Qu9,+s9,u9,+s9,u9,+s9,u9,+s9,u9,+s9,u9,,+s9,u9,,,,+s9,u9,,,+s9,u9,,,+s9,u9,+s9,u9,,+s9,u9,,,,+s9,u9,,,+s9,u9,,,+s9,u9,+s9,u9,+s9,+s9,+s9,+s9,u9,+s9,u93 v= ClientCoreB~;2u 36Z6Z6Z6Z6Z6Z&; 롊 M Z,^ M Z,^ M Z,^^ M Z,^&; ^^ M Z,^ M Z,^ M Z,^ M Z,^ M Z,^ M Z, =K9x [[tg9 CU K/U K/U K/Ԍ Q @@\qr CIq;~9L &; 롚&; ^y&.&; ^^^^^^^^^^K4^^^^o ~xx^xx M *r*ȖM*MMMȖMddd[]$PAs1 X @B(m@|JG<@.F  9>NB> YR@G/T2o .'l.'l.'l> YR@G/T2 @]mainh @@F D9};U,G2>NB6Z7`?{?{?{6Z?{7`?{?{?{6Z?{7`?{?{?{6Z?{7`?{?{?{6Z?{7`?{6Z6Z>NB7`?{7`?{>NB?{>NB7`?{6Z6Z>NB6Z7`?{6Z6Z>NB?{ T @tJm;qc Bn~> YR@G/T2o .'l.'l.'l> YR@G/T2᱘᱘tt᱘᱘᱘᱘᱘7XÎ> YR@G/T2o .'l.'l.'l [`"PM$AS $AY$AX$@@W$@V$AU$A]$@BT$@z=x2z UCP=$:e{#U{#U{#U{#U{#Uxxxxxxxxxxxxxxxxxxxxxx,,u9,,,u9,u9,,,,,,,u9,u9,,,Z,u9,> Y,u9,,,,,[q,[q,> Y,,,,,u9,u9,u9,Z,Z,u9,u9,Z,Z,Z,u9,Z,Z,u9,u9,Z,Z,Z,u9,u9,,> Y'n 6,,,u9,u9,> Y'n 6,u9,u9,,,[q,[qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxZ!Z!xxxxZ!Z!,> YR@GI&/,> YR@GI&/,> YR@GI&/xxx,> Y{#Ux> Y!flUxxx,> Y{#U,> Y{#U,> Y{#U,> Y{#U,> Y{#Uxx,> Y{#Ux,> 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{#UFz{#U{#U1{#U{#U{#U{#Uxx> Y!flUxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxx=$:e,,,,,Z,,u9,u9,u9,Z,Z,u9,u9,,,,,u9,u9,M,,M,3 K*z*zzzzzX*ZZXZZXZZXZXX2X2XX 2X ~xr E,~3io6ih4wa *a ih o;l2s I1U[QQQu9Qu9QQQu9Qu9QQu9Qu9QQQ3 Hq I @rc [KOj@os!j @@gb>E8h  ::$[qU[3 ~|$?u  @@/%M6*'.",")"("+*"b' A@Y @@f6uf5&x;p9K b&; ^.S.So o  q*p*Ȗd[Ss.Ghw6y6:;Q ah[ ;-G)cClose-C)^.w^*q^wq*qa/!_q&qq f6^[ @@o@yabwX  @@_ @I@G@e @J0l@ J B@bkK p o` @N@bq@UDIDa$Gz#y#x#o#DcDp#G  l;_8P 2 E60 M ZBnBnBnBnBnŊ M Zo c$Cd$HC@ u X @@j]DB~OIB^$Kg#f#d#[#BcB^#K  M@}I@S$LS#P#O#L#@c@N#L  K@eke6kl4rkc :l,& F @DFm+S$e@ &Nen$zK$J$H$}#eceB$ez  s;J>O  V^SU[ڊ M Z M Z M Z M Z3 ~#^ @@^ @J @@]CK @@ZQM&nH0BI  @@n/{Y@d+l+iJ.SG]D`kZ @@w @@V5RR@|Tr@[ j W{qd DS @u @@|MO,kh Jo[@G @v  @@o@Rs @@w \Q\@xw A0JY]@CsWO @@y &mCLYypE n. BzxBA @V  @`rKD@bS @^N @o%zi32, u n @_#w @@ab wQ @K%_o @[pc,f q @F@f @C$~VUL/EPxr E ms^d[@_ @g$lx R @I &Sf Zg@Z @PJzOV  @@X N @Xt  f @zjXFH A oq |MqSMDCUr@M0@Cj}d@@\%@P0O@H@y&u_@@mX}@kH@J@{HR`@@;Z3\ Zv3T6Z,u9,u9{#U6`k6`k6`k6`kt,,,,Z,Z,^,,,,,u9,u9^tt^tt,,zZt׋|.Xڍ7X6`k6`kt^,u9,u9^6`k6`k6`kt^,u9,u9^6`k6`k6`ky&.6`k6`k6`ky&.6`k6`k6`kt^,u9,u9^6`k6`k6`kt^,u9,u9^6`k,t׊ M Z,,6`k6`k,,t,u9,u9,u9,u9,u9,u9,u9,u96`k6`k7XË|.Xڍ7XÍ7XÍ7X,,tt @= privatemsgd|DrY&biI@D@Qz@c Z O@}k@fV@Mm5 {KQ  @o%QCK%e v @O@@H"y Lu@]D~ iC_V[@g$c@|s V@@~Ca @@C$@ e @@H HLTC_#N kCT U @Nc?O=a vުxW T{ df d|u2t NaY@qf  @E @F @Jz c@w@Te @; `~ \ @R RUYLKdb@\e37H  _f@@^yg N  @bc2@lo  @PW)X)v @Y  @S\W ]|]XI S rw^B~@b@kK K Go  L[+tp  yNtW/@Y@`@mM#@JEAt~@Uhb @Kgr0@Nb @@@s@xM@yIv ~c @@`W@ G  fQW@K j.MZD AdBBJMW@E @E6!M @oS Z wOc @B(@k|:v K Gޔ.'l m @g@h@nTRLyY U _ EF[ ^ XL N@^ k Z z @I@f  X l z @m V@TZH @X @q U @|2nPg D  @H  @x}F M @L  @Hh @^ O  @@C @@ET  @G{ @p @N: }|zDrm@iP)@TW@V@d @Ql @_'r@I,t/@@r@av x |+ZQy } ~  Y@ L@J LB Rw[L WK M [@N @@YL?@@b mZ \ ] S_ PN@@aLbS@eBi @[ "vq MWs v z { | NUw e t bEe\AINmW]+@jx{csZ@@ @L\u^_eH@cB@P \&Ka#@q%@j'@`@g@{pv^C0@sRs@@yDUFAEFk d@G@cIJl @S@{K@} @DV i|'@[X0v@bu`b0@eb T ` [pEa fG)@gy ft v @}@@)Xit(iU JQP ?@@@X+J{Ct@c k  @W@Q @hY  @db@Gg3H8 Cag uBbE3@kXr@SC4\ ed@eH8L8A sP I@zW'A@uyCL X@YVJQ@d,@HG[@U I`@PY@@\@P%@RC%@aMaA;!aba, b  zD#@dM gL b@G@jH@O^ @Em>L/m QseVsU[ M Z3 |>V0k qtPZU[,ttQZQZQQZ,,QQu9,,> Yu9,,QZu9QZu9QQu9,,> Yu9,,u9,Qu9QZu9,,Q,,Qu9Q,u9,,,u9QZu9,u9QZu9}"#u9,> Y{#Uu9u9u9,u9u9Qu9Qu9QQQQQ,,Q,,Q,Q։=$:e,,u9,u9Q3 }>R=j )ud2xW,M,,M, M Zvު,M,,M,,Mvު M Zvު,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 CU K/xxx L>E>n o nV#fE#@tdvb@z^w#@Ox@J#ALJK@GyGu@dK`@E%A%Bpjaw$`]h`]r$ax$=A%`&a^a,@h`  pLXRHO @h EODQS %6bO,,?M%6bOO ,O ,&6bO&6bOO ,R,@S O RMS%6OOS RE6HO6OOBRB,?O MSBI%,@E6HOIB,@S %$B%MSB6OOS O B  @P@@M]w^&X@rTJ@(mlkk(Vw(Pr{npKL@  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~vD@@~r~t)AU ` @E9B=h GU[,,,> Y{#U,> Y{#U,Q,,,}"#Q,,,,,,ԝX3 aHv@h @Jg2MNOWS@U+@~@D(QW@F8B@J|pg z t7kyql7r@h@u,@f mwaS[k[hR ^m@S7HCwK|}@{@Z(@w@y+ vRw@ @o@A q@o+\@@B  @q@b@|li@|e@I)e@]k@xI6pqsSRq5vFg0@}CB@A@ }}y@DL] sp@Z nu NOY Q@u4@K^ S VW@YU[\\S@Xa^ ^[gCpkK |)@GF ij]konqrstuvTpyz{|a @}W+uAVBGtIIEm  Fi@h s@Uh {3Q*E xp3VZ@_la iki @c el @d@_]@@@@pqu@c3@u[@Wh*@yw{@j@F@@@@A@G@Jx@x @CJk @{KMOQP*@\ ~@SwL*@A*@X h@X]4@Y+@@QbY1g@O1@ei@|hi)@kc+owI@\s@HvGwy.n0@]@a @r@n@_@@@h+@G@i+a o@U@Q NOPt@T@U@oWe @XZ[[6kl@dC)gc _e(@lB7pX@r r@f@@u@a(@@J7@zN@|}LS/@~X(BM/IJ D@GJ/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. ?,?, SH Y0q@iPf+@I1Q BWVTy'` @i'@g'\b'_]'cF@js X*a*hyE{ @my@uL@@v@@L5z}@J @FI'~>z9[-c #ApK>NB6Z6Z6Z6Z6Z6Z>NB6Z M Z6Z>NB>NB6Z6Z6ZApK6Z6Z6Z>NB NEBU6s@I~r&l@l6OMx@q6QUK&h6/%i(*'." ," )" (" +*"5*" " @ =U}q>r˲ o8`tmxuejeD4 F,-Pnled;.:Oz2#&XĔHIW[Z'0Ԟ3M+Y9K~^Yb/)^]JwA $_\%A _QE i" {SGp*VRyC 욚1@C(0?<70g505|LA7hvT=fޫ!6BѦߤh06kascN7  u66/%i(*'." ," )" (" +*5*"K  $@q>r˲o8`tmxuejeD4F,-Pnled;.:Oz2#&XĔHIX[Z'0Ԟ2M9~^b^]w_[_Qi{SqRzC욚1>C0><70g505|LA7hvT=fޫ"6CѦߤh06l`rdN7 [B@_`acm8Q'ijm@G'@o9H&d&dcW:_oyt_;@z|W&@~@@R&@W@{ABC@e%`E1l %`&`,`,`E%\%,@U%,xjU&, VU,,p $U,,νU,,|U,, *ƇGU,,F0U,,FU,,ؘiU, , DU, ,[U, ,\U, ,"kU, , qU,,CyU,,!ID&,b%D,, @@D, ,QZ^&D%,ǶD,,]/D, , SDD,,D,,D, ,!D,, 7D,, D,,ZED, ,D,, D,,ogD, ,L*M,,B9M,, qM, ,"amM,, 8M&,D꾤M,, KM,,`KM, ,pM, ,~(M%, 'M,,0M,,M, ,9M, , M,,|M,,eVN%,D")N,, *CN,,#N,,9N, ,Y[eN,,  N, ,}N&,]N,,O~oN,, ,N,,CN, ,NN,,~SN, , 5:N,,*N, ,ӆ%`&`,`,`  VF@G@i@QV%ZR] %w%wP%wZ=] \,w&Z=:=] \,,w,Z=:=] \,,w,Z=:=] \,,] w,  O@NFT@r%@RUW@JYZ[@x$L^@_bV<Jl$I@fPjkl@lnop@Hs@t`<@FK$E@{j@A@gCDE@z#CH@Ig#S#U$@g|@QZG$@VWX@OZ[\@Pw`@a_cunp@hIlVmo@A=qsD@"@mn@p@\ jG#q"A#j"e"@d@mT"@l"@G@["@D?@BE?p8f 2J&BnŤ᱘᱘᱘ 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>{>@RS"@O@u>u!@v!w!@y!@z!@{!@|!@}!@q>~!@k>n A"&,3m@"@Z@C"@D"@E"@T@e zG"@I"@J"@K"@L"@BM"@g>o P"$BE3 R"@f>@@TL@d>@X"@a>\@U"@]@]"@Z>Z"@IX>L6r \`"iYI1Q,,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,QZ,,,,V^S,,V^S,Q,ԝX,,,,Q,,,QQ3 s]Nexgen core controllerr] Zeropointq]1.05 build 1086b"@V>T>U0t 8c"Y>n6Z,> Y M Z,> Y,> Y,> Y,> Ysss֨)ssssss,> Y,> Ys,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9sssssss _]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# tU#@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$bN$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$@ceeel$ef$  p$@@%@m$@`q$@s$@t$@u$@r$w$@v$@y$@z$@{$@|$@}$@~$@]@@N^@MD%OLN%p%pC%ND%:p^:p&^,:p,^,:p,^,Np,  $@F%@G%@H%@C YMtU K/> Y7XÍ7XÓttt᱘᱘ K]CloseJ]SendI]Say:mdI%@L%@M%@N%@] O%@S%[u;Q @Q%@UT%CEY,i&7i,@YpY%iMJ6bQ ,W%6bQ ,,?W,8P,8WP,xWUQ YPXi%i,XpX:iJiUQ X,MT%6HQ ,i%Ci,@i6OQ $i  U%@W%@X%@Y%@g;_-R [%Lh6Z M Z M Z @]about@Z%@]%@^%@_%@`%@a%@b%@c;L.S Wd%=ќ6Z,,t׍7X yJ EyE7Xt yJ EyEttt7Xtדttt7Xt yJ EyE7Xt yJ EyE7Xt yJ EyE,|.X yJ7XÍ7XÍ7XÍ7XÍ7X yJ7X EyE7X EyE7X EyE EyE7XÍ7X yJ yJ EyE,t,t,t,,,,|.Xڋ|.X yJK4 yJ,, yJK4 EyE, EyE,u9,u9,u9,,u9,u9|.X,,u9,u9,u9,u9,u9,u9ttt yJ7XÍ7XÍ7XÍ7XÍ7XÍ7X @] accounttypes];I F$Ah%6bI %&6bI %%6HI #Eg&6HI ,6HI ܺ,6HI vT2  a;@[;@/T zg%#6Z7XÊ M Ztt׊ M Zt7XÊ M Ztt yJ EyE M Zt7Xt yJ EyE7X yJ yJt yJ EyE7X yJ yJt yJ EyE7XÊ M Zt M Z yJK4 EyE7X yJK4,7X yJy&. yJ7XÍ7X yJK4,7XÊ M Zt M Z yJK4 EyE7X yJK4,7X yJy&. yJ7XÍ7X yJK4,)|.X yJ EyE EyE EyE EyE EyE yJ EyE EyE EyE EyE EyE,|.X[[,|.X,u9[[,,t[[,|.X[[,|.Xړt,t,,,|.X,[[[[[[[[,[[[[[[[[,u9[[[[[[[[ yJK4 yJ, yJK4 EyE7X yJK4,7X yJK4 yJ, yJK4 EyE7X yJK4,7X yJ7XÍ7XÍ7XÍ7X yJ EyE yJK4 yJ,, yJK4 EyE, EyE7X,,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9tttt[[[[[[ yJ yJ yJ7XÍ7XÍ7XÍ7XÍ7XÍ7XÍ7XË|.Xڋ|.Xڍ7XÍ7X[[,u9 @= bancontrolY;~/U uh%6Z,t yJ yJy&. yJ yJ yJy&. yJ yJ yJK4 EyE yJ EyE EyE yJ EyE yJ yJy&. yJ EyE yJy&. yJ yJ yJ yJK4 EyE yJ EyE EyE yJ EyE yJ yJy&. yJ EyE yJy&. yJ yJ EyE EyE EyE EyE EyEt׋|.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[[zZzZ,,_4 yJ EyE EyE yJK4 EyE EyE EyE EyE EyE yJK4 yJ yJ yJy&. yJ, M Z yJ yJK4 EyE EyE EyE EyE EyE yJK4[[,[[,[[,|.X,|.X,,, M Z, yJK4 EyE EyE yJK4,, M Z, M Z_4,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9[[[[[[7X yJ yJ_4[[[[7XzZ yJ @] bootcontrolX;c%@j%@k%@l%@i%Xdm%@p%@i@S@S;I0V kt%>6Z,M,|.X,M,|.X,,,M,|.X,M,CP|.X,M,|.X,M,CP|.X,M,|.X,M,M,|.X,M,M,|.X,M,u9,u9,u9,u9,u9,u9,u9,u9|.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 yJK4 EyE᱘᱘,> Yv?᱘,> Yv?᱘,> Yv?,> Y᱘;(᱘;(᱘;(᱘,Z|.X,Z|.X,Z|.X,Z|.X,Z,> Y'n 6,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,> Y,> Y,u9,u9,u9,u9,u9|.Xڋ|.Xڋ|.Xڋ|.X @] gameinfoH;H1X Bw%i6Z,᱘,)7X,> Y,> Y,> Y,> Y,> Y,,> Y,> YM,> YM,> YM,> Y,> YM,> Y,,,> Y'n 6,> Y'n 67XÍ7XÍ7X,Z7XÍ7X,,u9,u9,,,u9,,,,᱘,u9,u9᱘᱘,,,᱘Ť᱘,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,7X,u97X,u9,Z,Z7X M*MMdM^*`]*`\*@@@@] startpageF;j1Y Ux%=d6Z)7Xtttt&; ^t&; ^t&; ^t&; ^t&; ^t&; ^t&; ^t&; ^ M Zt)|.Xttt,t,,&; ,&; ,> Y'n 67XÍ7XÍ7XÍ7XÍ7XÍ7XÍ7X^^,Z7XÍ7XÍ7XÍ7XÍ7X^7X^7X^,|.X,Z|.X,Z|.X,Z,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9t7XÍ7XÍ7XÍ7X&; 롍7XÍ7XÍ7XÍ7XË|.Xڋ|.Xڋ|.Xڋ|.X,> Y'n 6|.X,> Y'n 6 M*MMdM@] matchcontrolu%@D;X2Z Jz%݆6Z,,tר)7X,))7X,,&; ,t׊ M Zttדttt|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xt&; ^ M Zt M Ztt׍7X&; 롓t,t,t,,|.X,|.X,|.X,|.X,|.X,|.X,t,,7X,u97X,u97X,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9œt,u9,u9,u9,u9,u9ttttt&; ^^,u9^7X,> Y'n 67XÍ7XÍ7XÍ7X&; 롍7XÓttt|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xړt @= matchsetupy%@{%@|%@}%@A;}2[ `%,:F6Z,,,&; ,)7Xt&; ^t&; ^ M Ztt&; ^ M Ztt׊ M Zt|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xtt׋|.X, M Z[[|.X, M Z[[,t&; ^ M Zt,t[[|.X[[|.X&; 롍7XÍ7XÍ7XÍ7X,,tt^|.X,Z|.X,Z,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9œtt[[[[[[[[t&; 롍7XÍ7XÍ7XÍ7XÍ7XË|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.X[[[[ @] moderate~%@A&@B&@C&@D&@E&@F&@@&x:a3] iI&E6Z M Z,> Yŝ'n 6,u9,u9,u9,u9ŝ'n 6ŝ'n 6ŝ'n 6ŝ'n 6ŝ'n 6ŝ'n 6Ŋ M Z,,u9,u9,u9,u9,u9ŵ;(ŵ;(ŵ;(ŵ;(,u9,u9ŵ;(ŵ;(ŵ;(,u9ŵ;(ŵ;(ŵ;(,u9ŵ;(ŵ;(ŵ;( @= serverinfoJ&p:}3^ pL&h6Ztttttttttttttt|.Xtר)7X,t,t,t,t,t,t,,t,,t,t,t,t,t,t,t,t,|.X,,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9œtttttttttttttttttt @Mserversettingsn:k4_ @M&j3a6Zt׋|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xڋ|.Xtדttttttttt,t,)7XË|.X,|.X,|.X,|.X,|.X,|.X,|.X,|.X,|.X,|.X,|.X,t,t,t,t,t,t,t,t,t,,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9œtttttttttttttttttt @]serversettings2G&@N&@O&@P&@Q&@}@S&@@U&@X&V&@T&@Z&@[&@\&@_&n]&@`&@a&@U:N5` qc&6Z,,7Xt yJ EyE7Xt yJ EyE M Z[[|.Xڋ|.Xڍ7Xt yJ EyE7Xt yJ EyE M Z[[ M Z[[,t yJ7XÍ7X EyE[[[[[[[[ EyE[[[[, EyE[[[[ yJ7XÍ7X EyE|.Xڋ|.X[[|.Xڋ|.X[[ EyE|.Xڋ|.X[[ M Z, EyE|.X,|.X,[[ yJ yJK4,, yJK4 EyE, EyE, yJK4 EyE,u9 EyE yJ yJK4,, M Z, yJK4 EyE EyE, yJK4 EyE,u9 EyE,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9,u9[[[[[[ @]abouts&`:Y:F]S&( Z:@f&@e&sBk,ppp[sY] sh b&@g&@h&l&~z#l&-{!m P:@m&@M:N:@p&@q&@@ei&@FG: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[[_4bb.S yJbb.S yJ yJ EyE,|.X yJ yJy&. yJbby&.b7Xt׍7XÊ M Z[[_4 M Z[[t׍7Xb.S M Z[[_4 M Z[[t,,.S.S.S yJ yJ.Sb yJ EyE,b.S yJK4 EyE.S EyE,.S,.Sb EyE yJbb, yJK4 yJ,,b, yJK4 EyE EyE,,_4_4,u9,,_4,,u9,u9,u9,,u9,u9,,u9,u9,u9,u9,u9[[[[_4b yJ7XÍ7Xý_4 @] useraccountsN@D:@z&@u&@}}&@&@x9@A'@q9@E'@@'@F'@H'@d9e9@@L'@l@N'@O'@R'M'@Y9S'@@                !$ (+++,%!%!1!2!3! 5# 6%=&>&:2:0""!''(.2:32+97-444>><>>>F.L/D8 A:B<X- ^/ Q6 Q5 V1 W6V: Y7Y8Z8 Y? U?a/c4 c<f?h0r!I@SB^A OI&GC4JE2QD'SG%PL;PM?XP&UP:aBeAeB dCbHaMkBiG jOkK hQmP jQqG tK xJ rRyU}TlX1o[6vW y^*p]9zaxk0SYbQXdhaEqqMaehy|z^[[\bm ah`qq u { | dldhg"r/p>}3v?tC 9 4S^A]CHl|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/210315630542952986=:5<<3==:Z$|! M=;ECK ]@4@>9J>:fjzn*d j><|(3z@x3WBD:EA=GD?DEEDBKIFBMGDHGKJHBMIFOLGKKMOQMOQPQB^RQGQPMURMXUORRQUPPWUPZVQYYS^[R^ZT_\WZZX\YY^^ZB@cEClIG`NGlIHlIDyNLpNIzcUPa^Yg_[e_\zEFba[f`Yea\kd\``bgcegfdjf`jhckignhammfeczoopnlxslcrmlqrlvqjuvlztlvvrxwzzzu{||72<<979;9;:;72596994=988@=D6C1B;U5MGVQb`~|{~~goprCBZSebzz':6<F!D&O{u|t|  18!.)5  ++..,-~f}~¼ƾm'@t'bn'@u'@v'@w'@r'@@z'@}'@~'@'@F@{'@x'@\n@      #%&)*!!/##/%%4((7))9)):**<--=22?..@33D22G33H88M99S::R::T<;U<;V==Q==U<=X>>X@?[AAPAAY@@Z@@\BA]BA^CC\BB^DD\DD_FF]FF_LL\CC`DC`DD`FEbFF`FFbFFdHGeHGfIIbHHeHHfJIdJIgJJeJJfLLfJIhJJiKJjLKjLLiLLjOOkMLlNMnOOlNNnPOoPOpQQkQPnRRmRRnVVlQPqQPrRQrSRpRRrSRtTSuTSvTTrWVpTTtUTwVUuVVtWWvVTxWVxXWtXVzYW|ZZsYXv]\s^^uYYxZY{ZZzYX|ZX}[Y[[|[Z\[|_^z\\|\\~^^``uccygg{\Z][\\]\^\^^_^^]_^_b`^`_``aabba`a`ccedddeeffb`cacbdbeceeffedfdgegfhgihkknmhhkjjjkkllllgfhfigihjjkikjkolkljljoomknlompnpntt~~qpqqssrpusttuuvvwwxwyxtyzzxx|z~~~~}{@(@@@     )'!*#-#,&/&2&3,5,4* 4,!4,#=2%@3'D4%C7*A:.D:-G>.M7)I>/X?0MC3SC-TE3TH4[F1fN6qT=uZ?_TAcUBdXEj[Ayb@bFfFmGmJlJnLnQqNwKtKuNrLuNxSqPvRwRvT{R{T|Q}TsMtKwNvL{N}T}T}PNPVSagTVXYXURUZ\\]_QSY[TVX]YY_eacacibeacfkhlmhmmkmoimpo_‘bŒ`ǔeÜnŘmȕcϜjϞmpsřrƞqʜrўlПqsvä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.11122443366678::6;999;;*CA?LA5NEx[6}\8AABDCBEEECCLHFEHHHIJLNLKMMMOPRQGDXOCTNR\QGZTOQQPPPTUUUWWYXV__XSYYYZZ\\[[\\\]]a_`c`]\m_Rw_Gfa^pg^{gSaaabbfeeeffjhgejhgnhcihhhinoljllljkpkmrqqqvtsvvvuvywx}yxuzyzyy~}yxz|}}h>jNhEkJrFzWzz`yG~LRs]TbipŸpʥ}ϳؽӻA(@J8@J(@^|N(As7@w@@T(S@EF@p@[(\(S@^(_(`(E7b(@c(@p@m@g(@h(@@@        "&! $ ) * &##, 3# 1& 2$6%9$9(:-?,=)'$ +&!0*$>4$421:60897998@'F( L* B-D+L,D1 E1 O8 E2G2L3U7 P8 Y0]: P0R5R:Q8W9T9];]9^>F6!J3 O;#K:*N>.@80C>8Y?#]?)`> j>c;e?g?"]A_A^G@@26b|YOOOMMRϺTd2,e25^RNQONLLKGGH4$8/26mLGILLIE &.*120JEEGIF &$,=E &F @        '3 $2!**!",?/$,)&'9.9 +0<0)&%*G'-R-3\66@07\-4f19i29v5D3@?L>S?M;;CyK=JLCVK]@P"EZ$YL S_26b|YOOOMMRϺTd2, e25^RNQONLLKGGH4$<8/26mLGILLIE &.!9*120JEEGIF &- (!$,=E &F   (@aB)@D)@E)@c@  """"#####%%%&&&'''((()))***+++,,,---...000111222333444776777787888999::9:::;=<===Pd?BBBCCBCCCCDCDDCDEDFFFGGFHHGHHHIJHJJIJLKKLLNPOPPOPPPQQQRRRSSSUUSUUUVVVWWWYYYZZXZ[[[[Z\\\]_^^^^^_^___aaabbbcccbdcddceedeeeeefffeiihiiijjijjjkkjkkklllmmlmmmmmnnnnoooylkppov~opppqqqrrrssstttuutuuuvvuwwu{{{{{||||}}}~~}~~~Fcmw1/{R~Hxw¾_ F)@J)@/%t*'."@,"@)"@("@+*"1 @@¿}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuussssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd@@MK)@N)@jO)@Q)@R)@F6N6@U)@D@~@Z)@\)@Ij @_)@b)U5@a)@c)@d)@C5@v)[ g)@h)~4@QRm)@n)q4p4Es)@@Q @h4w)@@*jz){)@X4})@~)@)@h@V*@F*@C*D*E*R4G*@H*@@N4fK*xM*@O*@P@B@S*@| @U*|3Z*W*{*@R*@`*H\*gq+)wa *a g\*]*^* ]*^*[*fqsVq 4$fiT <$T fa*b* d*b*m3@g*w k*i*@j*m*@l*@n*@`3@u*@_\3QU  p*@nt*&y S3@n@O3@tz*&dB,B3@p *y @A+~2foE+@G+f2@o@J+K+M+N+O+P+R+E@S+T+V+@2@Z PZ+` cz0_+a+b+e+@q @Y@E@gbT0j+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.>VwJ*|J@_-`'VVop-`y J"FE[BVWG FEwV*y .V{[y @[y y y  u@aw+@P@{+}+@O/~+]t@x x+@Mv Wr-|  wv *G.v i|G@@-| 'Ga/!G-| .>G M@-| W | v 'v v o-|  D/@@B/~.KVw ' T,@GD,K,2ius 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-_# Cf,@h-g,@h,@i,@R sV@m,n,xk,@[@p,q,t,w,@ly,@yz,@{,@,o@x+O-},"{r #rqp-o F uF Ha/!G!|.>F H@q-o'D.>F H"},oqpF *u, ` oe*` ?%` CD.} 4o@l} ?,@` uHD{qD@qDDB` u%B@l` @uD d D-@-n-A-o[.' DN-E-o>F-c-|,n:sd <]-m wd *Frd |,-m'Zd d @ o-md q* zD,~,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 wvc 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 *Fi _ e,-h'Zi i @ o-hi q* L-U,V-o,QI,AV%?VuVHQo,k,V g-^-l,d>* x?, C B8C C @-w'} xC -wk#\C k Ak\k} kl,m,n, Hd-:#X} . ?,?,k. ?,@A?,k-'B@@l@ zA$,J a- , Nexgen Server Controller'$versionU q,buildS E# $Copyright 2006-2008 Zeropoint productions $d.scheerens@gmail.com $Development'$Daan "Defrost" Scheerens $Credits and thanks to'$Mickal "ATHoS" DEHEZ $Matthew "MSuLL" Sullivan $David "The_Dave" Schwartzstein $Zohar "SuB" Zada $ Z-6%b-*'.",")"("+*iy5*" l@VdfwȽpnbZpÿnKNbnZXPXXSSXZXSKNKIDDDKXSSSSKICAA""A"A"AAAQY\[EEE"RRRRUQWW[W\))****BBBDDV[[enunuwww|~|~ddofpppp}}~}~~~}}pwpppfoîx~xwwwuGFFFFFFDFDDDDBBBBBBB***)*)DLKKKKKGKGGGGGEDDFSKJHe­ׅܹ{؎uy󬀣zyeyTXczzUcUQQQGEUQGGEB\\EGD"QURUVBW[[[eBFLV[gxKѾߎþЮߧڧ~ߎuwuwZeneeewLVVVRVTLLRLGgLgvxvWg[UKD[NJ5.fzÎǫ|unSYyyxUcUQQGQEQQGGEA\\EG"BRUTUWAW[eggDELR[euDìwЬо~~йѐuufw|Teee[[uLVVTRTLLLRLFWRexg|vxU\WRKCbJ.++wأɰǨ⺩ynXUyczT\SQQGQAUGGGEA\\EGQTUUWWBU\eguBDLR[guGì~ЏߥϏڅϹ{~ufuu|Leee[[eKVTTLTLLLLLFWL[vgxxvuQx\VLI*N.+ 嬰ΫǯۯyXXXyYnzzYITQQKE"AGKGEAA\\\IARUUTD"D[geT))LRVefDg|оw~Ў؏o~o徧Ⴥ~{~{wKnefdKFZZWVVDFTLLKKLKKB*KK[|gKTxxugKK\[TKC(H+#װɴۨnNDSNybyySYKDID"GECAI"DKE\[)DBAKFA"VRKLKBBFFNK.),JNZd{zw~{~}}ϊ}ϊ~}~~ஹᮏڎp~~~~DFZVKVVDKPFKL.BFKDDKD*DKD)TLK|eTuXFTYKIKC+! ⯫պPNIHrynbXX[SKDC"EDDDA)DCIICL"CC*,"JHKLKNDY)DD.# '44}d}oϊ}}ooޫ}~ndn{{~XVJKNLPLDHINKKK*.DD.)FIFFDFLn[ZTKFXVLKID., Ư믗PNP,bbbXXNSNIHC)DCCCCD)IHIC.! DJKN.Z).F( #334P3'M^s_qdP_}omϤZ~tmZtLoorKPFJKJKMDFJFJHK,.DD).FD5D5DZXLNLSJLKPIH).( ӞƴկėOJM.N_PPPMNIHC,))H)))!C!D.  ,HFJ.P(,D+ &'&3mts&''''3#4Mh}sP_}PoiPP}mtttmPpNZmZ9K.JHJHF.HHFHDJ!...)(F..*.DNLNKKJHKNHFD(- ŵʖ쳳ŚҴėՙM9H59PNNNHMHHC(().)(!)(.,$HHJ,P!,.+ &&&0trPi&0'0''31'1kPsOis}sOdPOt_ttstsZ_osNOPJPPZ9H,HH5HH-.5...H!,,,).,,,,,NJIJJF.NHJH,(( ִĖ՝֞řėiaššęk_55+5PNMJ5HJH.!!(),!(!!C(  .(((N!+,$  %&&OJPO�''11114li}_is9ssssiMOPMimtPiOmmmPqPOmMMMJJPN.,,(-5.5)!(...((!(,,,+(,HJF.(.5JD,(, 읖ֱ웙⛞ů띙_kikՠ떕kP]M55H]P9HNPJ5)! !!,!)(!,!  (.J5.!!((    qtt;'0# #&#&&ii}ssMoiiPP9JMmP}imttPP_mmssNJPMPPJM..5H+.(,5H!!.!+.,(!,,!((!HF5,.,NHHD.,., 얞䞳ĕ䝝֞ijį_엛_ii֙ęՕO]NJMH5PJHONNH$  !(C()!!!J    HD.JJ+,$  &'s4'1#&0'#'1'4_08hPOPi_PPO9o_mssoJqmmsqMMNOPdH5.5HH(....5H!(.,-H.,+,!,..JI,JNHJH.(K,,$ ힳ쳳䞖❛k՛ճPs톙ik_k뙝ԝ]OMJOH7MMqNM;&  !(,((!!!D$ ...JM+(( %&0r}0'#0'0'111<81'0#8iMPsPPOP9}PqsqsJqmmqimMNMNPm55D5.J(-....J!,,,,.,+)+!.(.JJ,NHJHHD(J.,( ųijl䝛kӗʝPqq흗k_햙ĝkkOOOMN9JM_M713&  !.("!!H(    +..HJ(+)$  %&0tts#&'01''11hJOP0'0'00';iMO_PPOO9sPsoqqMdmqmmMJMNONmH.5.5F,....J!.,,,,+,+,(!,).JF.NMHHH.,J5,+ ų䳳kձaț_iqq񝗗ęi_9P]M]JMNJ6444&  (,((!!.) $,.H9(((( % 0siN9&&''1'4'4lii_OM<1'0'#0&0&'5OPPOO5d_qosmqmJ_smmiMJJNMNPH.5.55!(....,!!,-,+)+)(!(+.FM(PHHHH,,H5.+  ҙ쳳͖͝_䁈PsONqq_kkęP^_^OMMMM_r^44;   !,((,,( ..5!(!! #&0PP<'0&'111#114sssO410&&0#&&#&<9OOOJ9PitiOmoPimMPmP_ZJ9NONJ_+(...,((..!+.!(((((!+)!(.J,H,HHH.5J.J5+$ ųŴֱ끖sq_m_qaikaki__MOMM_kk6;1&   !!!!C,) !,(5+!(  &0&mP}q8''1 &&000'_s'10&000&##%0isPJJ9JiPsPmttiPPmmsJJPP_JMHMJNPH(!-,.5..(,,,.,((((,,)(!..NJHHHHMNNMHM33  lij񗁈sqsq񗖈sN_tkkkiO_]qkqa44  !)(!,,,  (J5(!($  &0&tPJMNq301'0'''1;s4000&0&&4osPMMJ9___sostMmm_qoOmPPJNNJMPN.M!....H!+..,-.!((+.(+()-(XHIJJJHiP__]MM3   ų䳳䳖qsqis웗ti_웕k^q^_q]kq7%0   (+(,),  H(-((!  #&0&HPt0''0'1141^Oi'0'&0000&&OioPJM99dPioqqMd_m_mPPPPJJPHPON.J!.....)-,,,,(((.)((.(+NHJJMMJbŝ>1 ű󛖳lkOOPOP__񗗁i흆iaiakkklkkak>;>^i_MP_k_1  !),)  5(-  &&MPs0'0&'1'1#14h_O1000%0&0#OM95555-_$8simmtMPiPPsNPPPH55J5D9(+!!(!((!!!!!!!((.,.5HJMk__^N;7&  ŝlkikMPOPPPi񗖁is헖a_aiakkklkk?;;:a_MM]__4 !)((+--  q-&0''1114lsOJJP;000&0%'OO5555..i9#&Ji_mJNiPP_JJONH5HJ5.J(.!(((-!(!!!!((!..HJJMqk__^^]60  ӳ󳳚li__9hPPOP_s`>>:?]M^qiOM5 !!, -!.-   & O}M90&&&0'1141isO#00000%&%5OM55.55.O#%##'MP_OMP_MNPJJ5HJ55.J.(++$.!(!(!!!(!!(+!5,9HPkkk__kƝ?1 Ş󴞝liMOMsOPPPsʗՁk^OO^_kkla``?`>;??]k__aN5.J   !)()+!5+ &&OPt4&'&&'1'1#16is}}<##0&0#&&&#JMM555..9'####-PiPOJNNNMN5J5D.5M(,+)+.((,+((!!!(!((!!+(!,HJHPkb__]7&  llkOMOMP___˖▖P_]MM]ikkk````?^?`>;;kq_PM.NJ+  (!(! $!--  %%qZOJMt}&0&0'#%&1_s}4&0&&%8J9O.55.5-##&9POMMJMMF5J5.5.J,,,+,5(((().((!((!!++!,!,HJJkqq_]M6&  󞝝l䖁____O_iks՗՗얕O^M9MMai?;:;`P]NH,J.5    !!!!5-   &&m9PoiNi&&&'%&010#1^ssi&&%#&%&MMOO55..9&JJJJJJHJ5.5.J5,,,,.,(,)+(.(((()(,+!!,!.JMMqka_];;?1 ų읖i_OOOik՝욙lks__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# ##%&&isiss_00&0000&%&&#P_PPOmiP9#&   $59J+(-9...(.((.PNHHJNP..FJHNN,HHJH..JNHH,(!(O]Jk_MMa5& 鳝lҖř̘j^j;640&M.,)(. !    PPPMH.     +55H9J%#%0_siP<'1&0000&%&&'_PiPOoi_&#% 59HHN(J5..5+(((.Pm5JNNNPHHJJNJXJHNJDHKJNJ.(HJJPM_kJa53$̝͖qⳝj`443&.,,((!)$ !   MPOMJ.   +.J9J.'POOi'00&000&%&&%9_PiOPms8#&# $HHH5J-....H+!+(.PZ.NNNNNZ.NKMJX.MJJ.HJNHJIJHMP]kaaJ+3 ՞`k볠?;40& .,()!,,  !+   5MOPJH     +++-,+  '_O9_O0&&&%&%%#%&&PPiPOPiM%%%  #-HH5HM-...5+5+!9NP,NNNNMZ+KMKPN5HJI,NNJIJJJHJ__PM5+ŝ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&iis4441144110%'smidiO%&&& #.,(55J5.+($  +!.NNZ5,KNJH.,NJNJH).)5DHHHD(.HM7MOM.( ųš̓j>;6:ŝę>64&&& # !.)  ,!!C,    ,MJ5.   JOPOP'&&0&&}^'11&412'11'0_Pismi_'&%#  .9MN,.9.HH5  !(NPSmN.JJFPbH.HDJN(NNHD.).S.,5Okq_]5+-ŝšŠj`?;664^⛛ij]0&& & H)+)!+  ,.D  5__H,-4(-9.4$$'JOONP### %&&0&4O84440%44111%9qiqi8&%&#%# #JJJJP.5HH5 (P_NNXZ,PNNNb(HJHF_NHJHH(SM,5M,___PJ. ҝl鞗֝Ğ֝j`j>>>6644;ŗq0 #   .,)+(.  +..$ $-5H,Mi_JHN_.OPHP55M59Odim9-8''##&0&8_44644064441%tims%&& %  -JJJJP(H5.#   ONNNPJHNNMNX(HHHNPNJJMJ,PN.JM7___].+𱖈ih❚қ͖`?`?>;>6:64:kPtqq9#  ++)(),   .C+..HIH,,,.HN__JMHJ.HMJ_5HMJJ-...._mimmi;309_9OM<4444410%&%&8si3%#% % -JOJMJ(H5    #HNNX.HNN_JX!HINJNNNNNM5bNMMJ-P___.(յҵŴhӚkl??``?>>>;6666:kaNmbPb  (,(.$ !.),)HHH,H5HHHN__NMHM.MMHP55MJ.5JMMJPimmisOs}s&64444&0000#htsO%%&% 5.JJ9MJ,($   5NP..H_KHN(.JJJHJPb_NJb_MJ55__aM,+ ҴŵʵsՈ՚s֝``jj???>6:868OPqJ_NMM.    ((( !((HJ...,.IJIHJNP_NM._.MNJP5HPJ,JJMJJ5osi}Oi&6464400000&mi'#&#&#%-JJ55H.JM-    +ODMNJ.JC.H5555JO___J55NMaJ5+!+ҵŵŵ䁁Ӗiӗ_՝j??>:`6'3__tHPMNJ    .)!!(  !(!IHHDH,)HJJJJKM___DJP.MNMMH-.+9JJMMONJiosPtsil;0&1000&8sO]%##%& &&$JJOH5.HH-    5.JJPN)-.DJHX,MPkėqHOPa^M7M5+ ȵŵݖklsՈ̘j`?`>40&3ZP9NNJN.%   .(!!+ .,HD5HDMHHIJJN,9J_MJNP.HPMM(-9.HJJMMN55559is}ssi;4000&_sssiO0#%& &#& HJOJ(JHJ.   N.NHPHI5)HH.HN.NaՙMabaO]]M+( ӞҞҳ◖՗k՝jj>:1&& 4PbMHPMJM+  (!!!!($(!+HDHDHHMJDJJKN,NJi_JJP.H_JM.J955JJMMO.NOPPOOik81's};## %%%% MJPJ9JJ5#   HJHHOHHJN,..C5N.Pk]_MMMN].($ ų֛⚈ț?:40% #5PiJJPMJJ  !!!!((++HD5DHHIPJ,,X.HMJq_M.b.J_HP.M5JJ.Omi.9OOPOih__sOi&##&# %&#+NM(-MHH$  JJ,MDHJHM!,..DH.O_MJ7H^J-+$+ ҳ⚁֝llk֝`;:61&&5PM5HN5H+   !!-!.D.DHHDP.,.,HIJNk_NHH9HOMP5J9JJ-..-JOPP_iOOJOiiMMMsisi_s8% ##&%%-55,5(JJ$   ,5HM,(5DH,,!),.,MMkM7J.57M]53++& Ş֝ųŞ]i՞j>6110& %#JPiHJHHJ   (!!!($,!HDHJX-HJJH,JNq_N5N9JPMP5M9J.9JMNJOOP_s^issssiiii0#%#%#%%#H.MJM.+-    5J.DJ(5)..D,(,.H__͝PJ5..MJ9754733 ҳ̳֞;<ݚ靖`>:4&&&%%%#MNs5MJF+  !!((!!.(!DMKNHHJIJJJHPq5JNJJ_JOHMM55MMMOOMOdssOlsssPss8## %&%&-.MHMHH   !J.,H,M(,,(H!,.MMՠM7H5]5_JJM;];7: ҝj>::_kśkk440%% %##$MmJJM9H# 5!!!!!!,(!(!(.MIJJNN,J^NJNNMJ_PP.95HJMNONPPJPOissssssiP_P' %$+5MMHH-    -,,5,.D(( .!)5J]PkkJ593M_J7J]^a>̓j>:6;llhkq<& %##&NOHMHH5  (+.!!!!!+-).(HHHHMJJJN.HP_qNJPJMiPP9MMJ.O_OPPMMi__ssssoiO_9#%%% O59JJH.    (,5).,. .H.J_ab9M55]7559]a`=/ j>;:6;hs_OMlP4#%+5MHJ5H$+.((,.!DHHIJNXXPXHNPikPNPNPqN_JPMOJPsmqiJ_s_kOssqsmMdPM9##-PM.OMJ#    (.).((  +(.JPk͝O7J75--.5=6/ `::::8^ihMO^m9PiO&-MJ.J5,-!!($-$+!!,(DHHJN.H,H.JNPqqbbJPMi_rM_PPPMOMJPiissl_P_siP_Ossis_isismPOMOO9' N_J5((    +!,(( (.9_͙k^MM5+((+35]aj=/잠j`;6:6:64Ph9M;MsPsP&#&#&%#MHMJJ.J.,+.+.!,(!!(!(!!+!!,HHIN.MPNNNNPbqrPPbNkPiJOMNMM_ONOPPiisl_iPkOii_OPssidisi_iPiMJMJMO_OPM__.M.   ++(!J((   -.MOPM,+!!(5JM^k=ɱŞ霓jj?;6>>;14_sPisM59O5% #  J5-.,!NJJJNJ5q5D.H(ND.5..(,H.+5!!,.H.(JJHHJ.JPP_JPOJiMPPtl_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__NsPiqPhiMOPOP^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_NXNNPqiqrbmtss__M___OPOO55---9+...-95.-+ !.5+,5)5)  3MkʕaMrJM..,,HHMN_ka^6 ՞Şh;>??>;:::810&##8JOO59MO#   #..-+(9(-,JJPH.H5JOD,,J..J,JN5H5MHN((,JDHJJNbqbb_brrrȱȼҼ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_PbPINbbbybƪɼӴӼisPO9_OMJ.9J9+(..MJ55NOPN_-$.J,(,D5(  ++]_kkaJJH.,JHHMO_j=/ ɵɴҵҳl?;>>>:410&%#&#OJ9OJJMM# '5J999..-J-J5OJHNOJJMJqJJNNMJJ__bNbrkSXby˫ƻʺɼӱsi_9POJ9J9.55M+PH5JJNNbPP. 5J,,.(H+  (+5M_k̙OMMJ5.HHMNk?62  ޵ұ?>?;>:610&###%%&9OJ^J9O^+O59999M..M-,OJOJPJNJNH__MPNPNPrXbryƻȪsmiOJP_O.95.J9M+MOJJmbPPNi. HM.,.,,   !!-]kř__PMJ5JMMj=/  伵Şl?;>>;:40&%#% 9OJ^JJO--99O5-5M.M-.5.5_HMJNONJMNsNPbbrPXqrκdzk^?:2022216>`jݱs_PN5955.5J9J9,.!-JmPSNN+ ..J,5,,  !.]ka_PHJM_k=  l>?j:810&%#% 4OJ_99_9.9J95-95.M-,55MMPJMNMJP_PXsbbƣƇƝ`@2=/// 6⵴tiPON_9.-5MJ9.+J5JO.bPNNP555J.5H,. !!5]_MP]Oabkikj?/  Ȟj?:61'0%%##4iJOJMiO559MMP5595-,JmNPMJJrmb_rۯ˰Ơ==222222////////// 2似_OiO9.-9JM5.9HJ5NJHbb_HMMNJ,.H.   -_iqP_k_k^:%  Ӽұj>640&%%%&#8Oi9Js999JM5P99^..MZOqP_Nqmmsư˰ꢟ@@@=@=@@@@=222/2/22/2/  6ӽȵPOOm_P.559M,,.HJ5JJPJJ.H,ONMJHJ,(   !,9MkʖkaqkP_P_akk`:% ۶ɳ̢硜ȝҪ^611&&%%19MO_JOOO99OHM99J^.J_PPsqPmtꢟ@=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_aqakŢ=  ɪšjŝh00&&%&#.OMMOO9O5.OO95O..MM9_PmsbȻ͢@=2 >ұsk1&&&&##8OOMMJOOMOO5OM99_-.M_isbst롑2/ 2@=2 %26:?h<;6:21& ^m_OMM9.59.5.5++9(,_PPPNHHH,((H,    4>`]O_ab_@:/ ȯҠjjj```?^??^>`ȴs1&%0#'iOOO9JMO_9OP9_9JJiOO_iqtԑ2%; / ;lȵqOP_99-.59.5,-P.JMJXiPJ5H..,,..)  (- &:``_kbkaۖꡑ=0  kҜ`>????66>>><>^j?>;:6'6Ossh#%MiOOO_O9iOOOJJhMOM9iMO_iqʽ̑2  4ռʴ_Mq_O.5,55,,+OHH9HNOHNJH,J..,.5("! ()!( &:jj`kkȖaa;6 ɪȴj>::::?^j?>;:44144:ssssh<}<_iOO_MMi__MMMi9_MOO_sȰ՘= l%ֵŝimOON9.5-5.,M!MMHP_HNMHHHHJKH5!(!!( .,++   4?`?qkk^]& j?::;>>^>:>8641%%&#%8}OMhPiOi_O_MMMJ9_OO_OiOiim͑/ aί յӳқibi__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_riiJO9H9(._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ʖkkOOJ5&Tkl;6000400&0#%#%#%0%&&0is9MOhiP_hiPOihPOsii_sst=  ˺ƺůư;ƺƯŚŞiOOk___kNMM95-+P..9H9M(,+!,PJHNN.,!+(,,+(((  --..Oakq_J53 ZҞ_^4'#%#%# #4Jssslsisiʵ= ˺ĨįƯǰ ʯěk_lksi_qq_PJqJM]JMNMP,_PNJM),.7,+(,+5 -9.+5]OMO^___OM59-+ wlisii8##&%##OOss2 4ɴqũưůėiillh_tsPbMJP__iH_rPXJ.,HOH..((+!   -J5,HM9MM9__^__^J9.+#uν󲝨ĞOiss__O&# #-9Mis2 6˯bůęikkhh__qPkibOb_biJPXqbHH5].!!(((!-    +95,JJ5.5JOJP_^__M953e񯝛jŞkq___PiiPO4& &O99Ps= 6͵kqģņ_llklslkkhiqqsPiqqaNrqbrbbNiN.(,.)!((!!(!(    $H.(9H(+,,.JOJ^_k^_^OM5-&e񯛗Ş_OMPPPPO9ss;###OOJ9O}= :ʯkqqģįĩěl_kkllll_sPqbqrrrqrrNMS_JH+,+(+!+!((!!    -9J.MOJJ5D5H5XO9_^;^kM]M73 e񯛕jԙiO9JOMJ.miiPO' 8PMJ9M}i@ 0ɵƛPk_qk^illlllik_rrq_N__NINHH,!+,!(!!   995MOJNH,JJ.H9J_5:;;>aia_M;8& [jj^iOPO595-JPPPiiO-#MMOO9MPP_isӳƛqii_kr_kh_aklllliklir_PrXPPNM((.5+.  7J5__JMM5H5.,HHJ5406:;>lk^^]80 W`;6&-+_iPPPJPPPP9o<%8OMJ9MOsss= ʪa_iaqk^lklklhlqkiưybrrb_CNNNM(!(+!.,,(,$&;bMaqPa_bMONJJ59+ 10;>>>kkh> W񳛗^``:4&-O_P9NPOH.PPPM'OOOO95isssist Ũ_i_kkhlkkklkl_ƴrNJXPM,,,5-((((5,7O_kis_i_M5% #&00:>;;lkl=  V|h:>`j>61$5M9--.PMMMPO.MO99MOsssissqȱ@ ȯś__a__k^lkhlkhlkqİưǻǰbSPqqJHDJ,(!((((,535_kqk?61 & &11>:?>^kȕj=/  Vq?>?`^j?;40% 4PsP9MMMM5O_OOP9Mssiʼ2 ųkikk__kalkaʯƳƺۯPNPP5,.)!!!+((.PNMP__ęšj?600&&041>>>;`ȕ?6/ V퉆_O446:?``j?>:4'& ##8.9MMJ.J99MJ9OsssPs}֟=0śqkklalllaůƺ˰۴= krXbN,.)(!((!(!--H]MP_`>4&&1&146>;>?klꡑ=0  V_M013446;::410&%  ##4M.5J95559O_iiis@// >մėkkkkkkklhŰŜ@@2 ƩqPJDH,,-!(!!-..JJMO_`:1&&011::?>>aʕl=  Vq400&003&4110&%  # $9M59555..2  յ͙kkalkllkŴ颡@22 6bXMJHH,,-(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:;??askkah6&&%&46:>klkkkia^>> [qPNMHMJ+    +.-5.5-99-5iiPPmmil  /ěĚĞ2 P_M,.-(+((!+-HHJHMa?j瓓`;41%&&014^aakkkkah>> {kPNMJ.HJ...(  -.-(+(.4+9MOPiimqss^///ijĠ̝/6ɩq_P9,()+(.-()JM5_iq;??jjj^?61&0004;9skklkka^? [ʖqPMMIH.5.....,  3+((+(+((5-9iii_ss; 22/ Šĝġ̡̙̠̠Š;ƨNJ5,,+.-(((+MqOO__];;`?`?`>:41&33MiMkkʕkȖʖl`j%V_PJJ5.JH......,(!$   -P__((((+(+5mPiqt4 /==/ /ġ̡̡̡̡̡̞ > 6ƇmPMHJ55.((.99PiJMPiP::>;;>>;643;_bis^ҁkՖ͖=  Vՙ_OJ5.H5D.,...,,.,,$   J5H5O9-((++.PO_sst׼ /=@2 =̡̡ ;k ۰r_PN5..5.9..5_POJOP;4:::;>8?kj^>/  KQRTYDHN     -JJ.5JH_NNNPNJHMobNHJNMPJH+,.HMPlʳ2@`:222ƲPNMOMMO..99JO9O__kO____^P_klh``j```??>la]6&  GKQLFDK,   +-5JJMP_PHHmZPPPHJJNJJH+.5JMPrƝ;Ư 2@ 2 @ 6@2 l⼺sq_PMJOM595JO_MOikO_iqh^^>`???^````^?;:h^;1  GEB"FD$    #$-J5ib_PNP.5H.HJH,.5JNsql6ͼ6 =@=2 2@@Dz_P_POJ_59OM9M_O_^^?j=::>`?>>>jj`?`j??:6>?`?;;:?`>??`j```j^??:6;_<4  QRUUT**    ##JJPP_NPNNt5_qbiP.NN_t=/κ/=@2 @2 /@/ =k⼺m_ZOmMPM_kk>?``;>>`????j?j``k`>?>848aM1  QQRURQ"     MNHNNPPPbb,PP_bPOP_q2ۺtkijŝ /@=/  / @2@/㴼mm_PPaa?```;?``>j`j`?j``?>?>641_;3 QQRRRD     KMJ(NNNNPqHP_bmMHP_s2ɺ2=/ /=22/  碢=2 /@Ƕsslkk횝klj`?j>j`??`j`??j`?;;>>840M<1  KQQQQ"H    $JJD.HJNXXHHJNZPHO_PbȠ2 κqqij 222  `=// 6=ۺȗO_l훚j`````???j`???jj>;;>:;:40<5'  KQQQB.   +J.HJNHN,,JJJS5JJNPqɟ/ ɨiijĴ 22/  =/ `/aӯmiq񛕖`??j??``>`?^;;>;>>>;64<;&  G""QD+   $,HJJKJ,.JNJNP5XJMSmbӟ2k⨳ͳj /// ==2/  2כll`?jj```jj^?`j`?:?;>??`>>;^M0 GKQGAEFDC,),(  .,5IJJN,.MKJNZ5PJNNbqӟ/?ίƯ /22/    2 /=@2  ר񝝳󝝖`jj``jj`?>`?<6>;?k՞i<&%  GQGQGCDD.,),()()   .,DHJJJS.IJJNPHNJNPbr26ȺƳʴ /22/    /@꟔@22 =Ӳ잛𞝝j```j`?`?^?`h]̝k<0% GGQGQE)C)),()()(( H,5DHNK...JKMX.NJNPbr2ʺƯį/ /2@=/  2@@=// /ӯ♝񚛝j?``j```llŝl]0& GGGQGA)!)((((!)!!(C!.,$ (,D.H((,H,HJ,HHN.NNXr/ƯƯ2/2=@/  /222 @ ׼ӛ𛝝j```kllis֞l<4 EGGGEEDD.H(,,.,!!((!C.C,(!!.D5.D(5D5D.).HJNHNJbt2Ǩ=/=@2 2/ //  /^Ӽl쳞𳖖𛝝񝗗_^4  (.H55.H.!,..C.H,D.H.H,HIJNHJNbr2۰ƺ@@/    񡔟@4Ӽ֝䝝󖝝󗚝󛛈_Ŝ^6%  ,5..55N.MMJMMJ5HHD.N(HHJNIHNbr=kܺƯ򑔟@2   /  2222@@/ ӵݞ֝󳖛񳖛񞝚kh6% EDED)),)(!!!)),,D(),..CJ.HHMMPHNP__PMbrr=6򟡢=/  = /    /2/ ȼձ➞Ş쳳엗ss鞚`8 AAAA"   (!(((,C.!,,,D.(,.HJNNNbq˝@=/ /// l  ӵ֞֝l՚kklkslshOO_O̜h:%  AAAA"   +5,,(.5.,,,(D+DJH,JNXr@2/  2 /// : ױՖ֚lh:6::;:6&4&%%&1046?j>^6% AAAA   (H(.5H5H.!.HHHMHMJNbr̠@2 2/ 22/ %  ȵ՝❝Ӛh:::::641%%%&141;?袜``6% DAAA     !(!((((+,,5H5JNbj@a`@@=/ 22 /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 ų֛֝Ֆkh66666610%%#&146;jj:0 EAEA)  !((!!("(!,HC+DJSb6224/2@@22/ 22=@2/  ֝՝֗lk:6666611&&%%%11:>`韑j;/ QQQQC    !(!DHNby6/@   2 2@@2/  ŞŞ֞֝h::46:4400%%%&06?ꢜj?6%  "v\\\A!!!)!HNXr2/=k  22=@2/ ` Ҟ֛֞h:::::6410&%&%04>j`4/ "vv\QC    !!!(,.Prr2/= / 6 2@=2/  ՞ȝl>>:>>:>66414%114>``6% "UQQDY !!!-..+-.H]Jbk62kj  22=@2/  촛k>>:;;;;6441'%%16:`顜`2% "\UIJH5!.H5H.,+5!.!(..(++.($,-+(+!++5HH_r:/@/  2 2@@2/ ^ Ş֚֝llՖkklihOO< == 6/ / 22=@2/   Śśⳝݖ䝱_鞓^1 AvvcNCMH5NNPNNPN(NNNJJ!,(!(!,,.,H(!!!!!((.!,Nbra /@@ =//22/ 2 2@=2/  ̳ŝֳݝ䱝i֡<1& EIznSNIHKJKNNP,NPNNN+H()(((.!!!!,!!()5(,NZr =@/򜔑@2/ 62=@2/ /  Şś䝯ձ쳳kҡ͞<1/ AvvUQ[XSNNMNPPKNN!KJKMI!.()().!!!!!.((()5!,Nb 2=2 ꟔=/ 2 2@=2/ j  4ⳳҞijӛųŝk>1& AEACAvYXSNKHJHKNNI.HKNJ)H!! (!!((!(.H95.Pr  /=2;>@@=/ 22=@2/   ^ųk䝞❞>`jա?2% AASXSNHIJKH.).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/ 62  `矑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:`jlh;&  ELR[vFYJ  ## ## OJMHHH.JOM_..Pbt     2 ɯȺ=/ ŝ񛗈;?`ꢘ`>6100&16:?`?`>h]4#  EFEEEDD)      5HHHM5JMJ5HJ5)JNPmt6 / // @/ Ư?/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!HHJMPM5imt602@@2 =/ ƺ2 jƵŝk<610&&&%%49-$  GxFndJ# '39M5NZbZ_d.PH9MJ5+5JJMJ!5JMMMN9NOOP++_stݯ/2=@2 /@@2/ ˯ͺĝikĝ͞_P^M8'#95-#  GGGKILIZb599H.PPDmHHdmbHMOMJ.,,((.J!.- 5N-5O55JM.M9Jmq2@@2   /=2  ȯ۴ 6˰Ġġ^?^įkqM_P55M.55$# "RLQXHX..!,JJK.HJN-5OJJOH.JKHMHqH.& &&JPP_PbMsPObMHO__ql%@@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.MPmstj2  > ΰƴܻ2Ưĝkakk<4100116:?jj`kk񝛝qO993#  )KD[xZVXNNJ,JMKM(,J.-$  %& #%3JMPPPmqPPN.9OJO_m&2  ˰̩6ĝęa_k_a6644404::>`j``?>;j񝗖iiP93#  *gRG)FeXNNM.HNMNNJ ## & &%% PMbmmmMN9JMPMPm/6/  / ǯƯƴΰ Ɨįękiai^66::6:64:>?`jjj`??>???`뙛񕛕i<4   *|xxuDnZPNJJNMJ- # #& &00# #5JmimqtPOH9MOMOshj  ? ˯ʩkaƕʠ̡̡??``jki]7&%  *x|xKdbXPNH5$ #&&& &# # mMmmqiNP9JMOsOs`?>:1  ƯƯ]㯣ʖi__:446666;4::>;>;>>>;:``^`?j^`kO;&  B||eF,(!,J# # %& &&# #%&MMoiqosOP9JO_i^s  6 ƺưƣ⣗_Ŗka_O;114464646:6::6:::::>```>jjj`h;' B||KZZXN.O$  %##& &39_mPMmqm_PO5OM_PiOi^ 2: ưįǺܗPNk̝_O_]N756446464166464444::>j`?^`jkĖ?6 Bx|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% BC{mXPNMN' # ##-OMmmqmtMPmqo_t_siOO9_iisOl >>0ʰP__N_bk̡̡?;`^jj`kŘ^6 DeKdmPNMP5 # #-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 ʯǺŴijkrXPk_PNMPbMƙēa>;40664406?j`>;79M9i_]_Ę`6 F|wwwZKPPMH-J9J9J599-.-..-P_5#&%&&&%&&&00&0&00'%001sOPPOsissl? 6˺ưʰƺŴŴĴ?%ƩtXNPr_NO_q˕Ւ?;14&77464:?j`>:666M_____aį`6& FfwfZPVNNJJ.9JJ9559H-.-.-.9& %%&&0#&&&0&1&0&00'%'>;666616;^_aiaĝl^6/ IVLLKVNMJH95...-5555.-..+#%&&&&&#&&0000&0&0014sM_iOsMsi   `Ͱưƺ˴2>ȯqXN_MXH__N_b͖ĕk50&360404776014Ř`6 {VPbmqPMmPPPOJ'# #%#####'OOiiiisissO_m& /˻ƵŴƵҵ/ ƯbPNO.!+MMH]a_MMH$&0336 4666&366;:667^9kqakkah;4 ڃ¶t_Ptt9%## #####&<_POOOiiis_Ȱ]  ?ȴ@/ƴbrb5J.!(M^O]ak]M.N_,3444&06640041:666414__a^kkaa_^^;1  Љtq_mOP###%%%%%####&'OiPssPi6'iȼ  ۵ʼ2 2ۼȴb_J(.M,!(^M_k̜]HMNN_.5314033146474101040HMaʝ?1 ߏPP_o8#%&####%4iOssssOO1'08sssa /ʺ韑2 ǰtrqNq,!.M5H5ak렙j>757PNONq]446444447640& 317MM9˕qk_kJ^O]]7& ߬tOmts0&##%%##%#&OsssPss8&00&&;isq4 2˵ԟ@2ȭPPqNM(!.MM5Pka͙`?`>6&.bNNN__b_M6764467643&&&JM]9_aiPP5M9977%  ڷt_Pi_t<&####%%%%##&08isssM_sss<%0000&hiOsqi˻ۙ 1顔=/ ?ȰqiPP_.M!5MaJaƖkk``;>>4&H___bM__]6667>761&&H5HM-_OMP],55754;0 ­ioqssst4%%#%%&%&%%-OOPssssOMsss8&#00&0%0kOttiqiPttƼa  2蟔@/ 6ۺӼƯtb_PbPNM.M.59a͖j`?;:44&&J_iq7;;>??;4& 3N.H5.MOJJH-5..437  î׮m}i_0&0&&00004ilssiOsOi_O1&&0&&&%0kmit_smqsqiskPiMO_NqƩƫ˺^  2Ԣ@2/ %ɶƲrmPqHNN9H-_OiՙŘ̒`?;:400% 3M͕k_^^?j`?60&.J,(+95MH,(-53$6 ׹_t<10&0046:hsssiOs}0#%%&&%00%&1%&Oliiqmt_s_q5mPOP_ik :̟@2/ 2ӰZNm_iPH9MMšj?;:0 %014:ařk˕j^77JH,5H.+J-,!37  ~mPPm41144:^sissh&0%&%&1&%&&&0%0itPimssm_sPi_5PJMPqHbt0 2`ҡ̟@=2  hȼqmiN_Zi_MP++Pa?>?6& &6:>j͙ʖj;NJHH.55,(,.(!+36 描iŝh66:^s<&&0&%%&1%0&%&&&&<OPss_s_Pi_OHJJ9_OMq_ibb1 /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ΰȼțӵss_PPOOP_JbPqM99k_Ġ՘ki40 &:?`jj`akʖkM,MH!(+(,+!3 иsҞԳh'&&%&%%8Oqmid_d&%#9_JPPPMPJJ,9(5.55+O(M5,.M.5,MHHHNXyǻ˰ȺӵiPMP_PMPP_H..-._Ma;;?`?j`kk_].$ %3?^``j```>;^]Naka_PMJ,J.!(+(++!!4 ڦtűҞ^40%%#&%% #%9O_PsP_P4#%#8ZMOPMJPJJ..5+5.+9.55((.,.((95,HHbbNrbbɰƼɺƴȼ˱ƼӼȵtlsssOJNOO_PJPNN.8H]4114:;::7]OM_M-(% 06??^?>:7:66MMMJ_]OH5,.H,!(++-,(4ᥤmԳҞh:60&%%&%%%%# &MO_O_PPP8##'J9OJJMJM.-+(9(5-!.-(!,!,!+,,(..MNJrNyyűsimskPJPPM_MMPNM.(M&&0&&11003H9J](($(0 &067??;:6434647H5H9H5+!(,5!(.-5-(!3®揮sҞŚj:0&&%&%%%#&OP_OPOON955H9H5O!-(++!9-(((-(+!MJ(.bHJbbrbrsimPss_qOJMMMOPMPN-  %%$(+(](+.5611667?;6410040&5,,((,+.((+,55795,!3 sqiiiss䞝֚?:40&%0%%%& #9oiPiosmM##&%## 55JJ_HOH55(5+(]),!(.+(++(,+MC.NXbrNbbrrttstsslslii_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,,NPXbJbbqbrPrrmqtiiq_sqsissks_ssiOsiii_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_isimsksissholii_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حsh800%&%#999M5Oiimi8&#%%& #-NJMMM#.+9(-(!!+!$!(+!!(.,5HHJHJNHJO_MMNOO_MPMJ9MOMOOO_POOkPPhsiisOiksiOslsi_iqi_OPiJOMMM5'-OO+$5    4_ŕa;0311;::;kNMM5,..HMNa_7,!3᮸sllsh'0&0%%#%#1M9O99MOisq<0%&&0%%%% &JMP5M+ -(.-(((!(!,(.+.+H..95HJOJJJMMOMMM59MM9M_PiMOOiOPhiii9siOsitsOP_iJO9M4M9.(-$  &^M7&&03>;_]_]HPM.,HJ]aH+!4Ϯ׸sssss՛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?kO9M+,7 +.JMMMM]bM59Ma_Ė_MH5M537 p~oiOOssաĞœjj?>60+5(9+9(    $,,9).(,!,(!!! (-(+.+.($!(--9+5$,M5OOP_Pi_POOk_ilh9OM<9OOOOMM9-   '_iPP_O5   %&0:^qaM_M9,+,7,-5-OMMM;aMJ7aę_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`jj??k_OOM55&%&1114&  5(,H(.$    '$4-'  9;<88 #'#####  3333&$&$$ 3$$-+$(++599.--+(5!!(!+5H5NHO_a^;4 յ󝝚l񛗵ųͳ󝗙j??ahM_kP94016:>?>60 -,)+JHH    # &&'3  8888'  &#  433'  + $$-+.$!+--M95.--+7(!(!(.JJJ,HONPPP_aP;& 욚l𛝪󵳴ų󚗗󝗗`>>ak_M^99744:?jjj?:0&,-,.,+,   # &  4'411       # $(+++-M955-5.5$(,((,(,,.5JMJM5-3  ųĞlʳ󞝝󝛗k^^ak_M_]M:6>`j?:&35(,H+(.      %&%&%    +$+($-5MOMMM997,,(,.,)(,...M,,.(ŝҞ񚗛󝝨흕얕__aOO]::`j>4HJ(H,,,(             +-+(--7_aaaa_9_+(.,,,(,C.H.,,+$ 읗ȝ񝝝lki?7O.(H.!.  $$(+++5]kĕaP],5,,)((,,,C!-(! ҝšĞ󝗝ų񝖯񝖗kkii^_]6:^j?O].(HH((   +($+5MaaO.H.,(!)()(.!!(Şŝʳų񛙖kikq_O^466?ja_M.+9,(!  ($$++5;a^M.,,()(,()()$+ʳijʳ񛙖l_Oa<67;>`硘a_J..5,(( $$($+-MaO5MJHH!()+()!,,(ҞŞŞų󵝝񛙖ka_]k466:>`ԙk]J.7).( #+$(+9_]JJ..C5!)))(,,($šŞĝқŝ񙗖k_^O_O;;:>jkM95,.((    (+-]a_9HH5,,D((!((),(! ҳŝĞ횗kaOJM,M!   #(++-+5]_kĕa5.H..,(,DHHC(,((ŞŞĞĝŝŗ󝗝񙁗kki_M]_OM`^kMMH+,,     $(--..M^_^OM-^(5.,,)+,,,.!,($ ŞŞŝ󝝙ksakkkk_]PMMMk^akO55(@/읞ﯙkkaqP_qbiaPi̙kM5,! =2ŞĝĞkk흗q_MqNMqJOP_iiPJ+-(   3+-+-....J-J9HH5MHHH.)-)/ŞŝŞ읗iai얆iPMM_JJiHJMMPiqPaMJJ+!!q_;3   % 4^ǻܻq;/񗚖ikiPOOMJJ_N55HJOPNMJH55!- q_]M0   %% 6]kǰǻǽ_72ҝĞkkaiitPNO.HN_....HP..J.,55!bPM7&   %% 4]qq]4/ͳĞҝijlia_aaqPMOJHHJ-.H..H,,J.,..5!NMM4&   %& %8]^iyrrPJ42떕kka_kqO_NMMHHN5D5.D5.,...,D9)! JJ]-$    &%0 &8]_it{rr{rrtbNP7&/ŞijĞki_kOMJMHH5_...D.J,,.,,,J.)!JJH4$    %&&0 07P^bqstbbbbZmbbbXPX_M5 /ҴųŠĞękkaikqqkPMJJHHHJN,D...H,,,,,)O)( ..-.            &30   49^O_ibqqqrtrtyttrttPXPNPXPNSkNNNJH-$/k_^PaO^__NJ.H.HHNH.,.DJD+),,CH5,C-+++          # &-'&$&37MOMOP_rq_Z_bmtrqtmb_ZrNMJIHPSMJIPHJH-+ 2ȴġĠka_OOaO9JaNMH5H.DM,,..,H((,.(,.5!!((!($        %%  #& $'3&&$ $34999MOMPPMP_PPbNMNMZPZ_P_JH.H..,H..DM.,+$/弸ġššĠkaaP61>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;789ES "K &R)G,T1M3Y +d-`5d;s!-A)6F%9X3=I2>Q$aE\B^KdHhLpTjTlYr[uJkMpSmRsStUyUyZuYv[zZ|\|anhvezr|q~e}`^]_]niyu~ebedklmmabeejjjjkluuyurtt{|}||glqu}i-j,]Is su,w,y,z,j,4s{, e- 288.0f- 6.0}  4.0i j-KK Kd,f,g,h, IK$R <wR *R 4Ki,R R @U k-O ;S=:W &:-UO&O !uQ{:W ,x-QN&O !uQ:W ,-k G&O !uQ:W ,-k F&O !uQ5:W ,2-k E&O !uQP&O !u m-[ռ::;&:t%3:;,F%;$.-(.ow88:;&:t%;$F:t r-l-D{"rC*( CD p-`-l)' zlD+ A @C,B, 'DD BD  BDA 'D ,D ,D $,,D  6CD D"'D C"'D Z'V DY D_ DD  ,,D  ,,D $,,Dx%x,|x Zgz|xL D@$ & {Sx&xL -'xL D.$|~| ,&xJ D [K D O `.8 DD ,QD B"U D @"T D ~!S D }!V ,Y ,_ , `QU T S K J AE t-SB& B-z-n74' E.VZQFVQ:V}@(u@( VA?%}@'u@rV*RC!HVj C!L C!T;}}@Vk?%SV^?%<C!H C!L C!T rc,*u@V^?%$rV*RC!HVj 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 -(~dn~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|*-dw|||-||K-d.i-], ~-C.~y q %q ,z q  Zq L - {z zn n z ~z ,n ppn ,z ~z ,q n  n @O.xm!O@ nxw@ * m!|Z@ wmgi;g@ ,d;i@@ @ 4~'w,ww%*w%mxwme'w)r*(Mmxwme [,D.l> HB.l]$Hlk:rl` :k,ErlK K - :k,R. `$0rlJ 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.:meW. ?,?, J%?,?,A Q.v zԡdnq wd* Y!|ZdEYv ya;yv %;a@dd4~wq E"- wE*(Ea/!HEa/!N3:q :Ed^<g ;yv %dk<f -x Q&Eq  E`b,wb%*b%$zv q E^_Ybw*$zv q E^_Y  22U.v^3GkvKv?vK-z'p-pSl-~Sjv-KSm-z']-zvQ:vv{kZW,wW%*W% ^vWvl=vQ]vn'S @R.@S.@V.C_xSGkCFC?CF-{'xp-x[h'x-{'J-{\CCCv'\C CGR,wR%*R% _CRCYC]Cj'[ X.aBڍW]}QFHWkD,wD%*D% aD\w'z"u'-?-Q  w<*:;,:H%-Eserver{:H%a p.Z.X,\/kECl,wl%*l% \X,l T.\.VbOV@<<V>V-Ea/!Y-?-T >-Y o,$o.4~Vo{%-Y'1:VoV l=o;ozVQ:VV{kV-l ':;,V-EVA<EVVFHo%^o,wo%*o% bVo:;&-?:H%VVFV4TV4Sx'VVz V Y.^.|J~kn|nwk*kpn[,w[%*[% lk[+[,w[%*[% J|[nw* J| g.[.h a.hW){&r<h<h@T <-W wT *rT @h-W'T @h@T T @1 zs 0A  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 VBVB*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.f9rB*g -'f -'}g -(f -(C. B$ h.i C M # i  ^^ # i  K -ri  wVDm%m,mD-m:Vm=:V &qC(q  DpC'p :V ,qC'q pC(p#  XDqC'q pC'p SVS*Qi  Y{Qm~Q,m%F QQF QmQQm},W. S B W5F Iy-S} ,\-'FVF*Qi  ^1{Qm~Q,m%F QQF QmQQm},W. F B W5F }-F} ,]-' J z.H RF#o,J <YwJ *BwJ H J -EZJ J @ pH -E'*H H CZ:H:S:o*H H BZ: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.||"1O. SwO*|{O5\zo o O5|o ppo ,O5O. Oo  O@.m u"i E 0Lm m Xm qE %-u 'sE m Xm q $I,E KT,\E w-Q kE -Q Px.}.}34S. FwS*|{S5\zp p S5|p ppp ,S5S. Sp  S@F/OI'e-P'<z-P w*cwO|qOq-P(@-P H|.yB8HyRry@  :R,@ -l M  {ls ;Yl|} ^  zryg  :R,g -l M  {ls Xs=. B$l|} ^  zryf  :R,f -s #V=. B$ryB :R,fryS :R,\-rS*wS*d" . S5ryF :R,]-rF*wF*`" . F5ryy :R,y-r d  WrM. S B M5ry-S} ,ry\ :R,\-SeS*\-'y-S} ,ry} :R,}-r `  XrM. F B M5r}-F} ,+ry] :R,]-FeF*]-'}-F} ,:R,ya/!dq q % J,ryJDq JJYq %J%J,JD-Jq Jy n.C,`1zkC,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/Sj7VO '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(-LS-E-sfNexgenPasswordDialog%_T-L-SRS-S'z!\oj(fNexgenServerFullDialog%_RH&_RS,_Ro9-SS-ESSA-S'z!^oL(fNexgenNoPlayRightDialog@S-S L,wL%*-SL%jSzoLD-SL%L,wL%*L%hSzof_LL{fS)Gf%_&_,_,_TSA(oS-crMNSxMNJ(bS H/Q/A,KcP]-E ],S]Z %k]u pA-E'Z]-E]Z ]u kA,DNXOCS]Z  W5@,unw!w,vwwiv&[vwv&BvBv(vw&%6TvB~+&6TvB{+,6TvBv+,6TvBs+,6TvBp+%6[vB@,&6[vB}+,6[vBw+,6[vBt+,6[vBq+6w vB@ R/AET3F!:A:FAF'DB:A:GAG'D( q-\/?E6 GA/RCoR<wR*T{RR_RRQ:R^RRR@ -:;,;$S8hhkemeeo Y/+LAL-G -H h,hZ +bkhu pA-G'j-H'hZ hu h-G T/Bb;/BBABA'@>BBBB'@( _/M!%+,(}M}MM}'*( z_4*3AA,$,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%FxF%:;%;$nF%:;,[-bx-?:M% r<*#kW?,<:M-?(b $-M-~(:{%{:{%debug gpf VU/z yz ,"{z  y -#z  y|+P #PPwtwSz z  c/zLH rz* {iNNXOCj-~L{z },w}%*}%LzyN}jw*LzyN V/rnr<wr*-?:H%rrFr4Tr4Srr@  X^/@X/| || , "{|  A -#|  Ay+uQ. K B Q5uQ$| | KA 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/LT9 Z/CwN^-?-M-~-~']$C,wC%*C%wC^ ]/akV(u ~ ht pua. Rwa*a$%w. K B w5a5w$a$a. abRVR*ewK*K-(K*v fF{v #vsvuJsaK iuw. R B w5a5w$a$aeKAw  ed  N K  O J- -I K- -L F i/z+MQCaa z+alwata a'a a_ W+a_Aag VA x,wx%*x% aax a/S,"-?v~-?-M-~-~(]$,w%*%S~b Ued/ZkKM? AKZ,wZ%*Z" b/HFtXtCVX% -#X yVoA . RwA *A $% 6#A $ AWoz@ @ 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+Mw-U B q/p jf#"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_%ZtCwZ% -#Z yo+\ #\[\[w  [ }/ft`k, {kf:-|-|!kf:,-|-|!kfh,-|-|!kf[,}kfJ,kfJ-|'kk%Wk, {kfX-|-|!kfX, -|-|!kfj, -|-|!kfI,-|-|!kfu,MkfO%(z kfu-|'kfuPlayer*k-| g/`olUpw`*`a/!H r`x] !S ] !A-M_.`H in`wi*iQr`x-M'i4(r`* wx*rx*H $w`* r`xwx*f] !Sg$] !Ag$g$ppx: H gd,wd%*d%%o`xJH ] -KdQw*%o`xJH ] -KS' K/k{srN*N kNDB @||-ivkyk*:z:-iN.kskkNkaOnline [00:00]uq-$-l,QI%:&:,l:Ql,dly&vkykzL?Dk@?|?Dq@`|?,nN%@6w %{Al&UlNl&{l{l"NnN%P*w%@6w %B@Al&lwl&BlBlwB dp/v/0[(C?r_*_.; Aw_*A' u/C MH*;L. RwL*L$%\zC C SL$C pppC , SL$L. L_MJ-K-tCC  w  d  K  Y{/x/O9YV2-s R,{Rpt~Rp=(t%#|RptYqRpt&rq wr* ar-s'%R/RR-sX rzx(Y-s Hw/lhM0CHlm:rlI :m,kkrlC :m, w_*MrlH :m, w_*_DrlR :m,wK*K-(K*``rlK :m,`wR*R-(R*rlR :m, wR*C. K B C5. R5C$. R$@wK*K-(KCC-'ReR*KAFrlK :m, wK*C. R B C5. K5C$. K$wR*R-(RCC-'KeK*Frlt :m&_rlw :m&FArld :m&F |/P5+a ;$F:Y Da/!YJ=.-p-B -N-D -x-H P& E0Rs/set Engine.GameInfo GamePasswordO{ set Engine.GameInfo AdminPasswordOV Vv w x :H:S:o:R-t set IpServer.UdpServerUplink DoUplink True9set IpServer.UdpServerUplink DoUplink False%\&\,\,\ B0n w0}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$3b(d>3??34a L)L@`@BB3b(dL?33&a ]L???3`?3&a ]L???3%L?a O?3&a ]a ???3?3-a ]?`@??3L??3-a ]?`@??3`??3-a ]?`@??3L@a ?3-a ]?`@??U`3OLa ??Ub3OLa ?O@wP*3$3q3??3HaP`@`@?P?P3$3b(d>3??3Ha `@`@? ? Y-x3pp(>_X`@i|q@i%iNY3Xii{i|iX?i`@&-i|i%riwY3XiiBiqi- TL0c-T-TEcY $$<-T-TEct$$-T-TEcU$$<-T-TbcG%'-T-TbcF%'-T-TEcE$$<-T-TEcf $$-T-TEcg $$<-T-TEcM$$xj%j,-T-T!jce,@jj%j,-T-T!jcp,j-T M @C@^0D0T=*Cb%qC-M '{C-M  {Cq-M (|C}e&pe-~CTUTORIAL%dCbC&!d%gdCb%qC-M '{C-M  {Cq z_-M (|C}e&pe-~CTUTORIAL%eg_CeCbC&_ 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$Tb(d>T?`T4a P6L@|?BBTb(dL?T`|?T&a ]L???T`T-a ]?|???TL?`T-a ]?|???h|q@TNT$T,NThh`T am+( HF0~Z!dH~|r~B  :|,1-<UseNexgenHUDTB -1bw X *B -C'C(or~G  :|,1.<FlashMessagesTG -1bc-oG - r~K  :|,1.<ShowPlayerLocTK -1bc-XK -{r~D  :|,1,< PlayPMSoundTD -1br~J  :|,11<AutoSSNormalGameTJ -1bbr~L  :|,1,< AutoSSMatchTL -1b FO0xH^ 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 iG0jY;g@6w bx-o g@b ?g?,I@@@i?b Cvl+jNj$v%9v,jvj+jv6[bN-o g@jb(j333?6j=D<6jb i6j=D<6jb i6j=D<6jb ijav6Tb(jav6TbIi+vIv R0VQ? $-iV[%}Vb, -iVeV{ V.Rget Engine.GameInfo GamePasswordVV V/Rget Engine.GameInfo AdminPasswordVZ]%], ]VJ]-i n/d0ztE? at -nQa Ha a wa ta ja ea-nab-n S0@w0Lh)%^ keybindingL[~q,%|q~q,f|qz ^ ^ |^ pp^ ||set inputL^  ]0k+]<(0.k+; Aj' QW0@Mzf2)aA$,, C,B,AAAA'$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 BB$$21J'BB2  @B2 ,2 ,2 ` '2 a '2 b '2 c 'R2P2O2N2 L'$f.&8 &o-'p-'q-'s-'Gi| _Z0MG{nMQ .MwQ *[SQ ZSQ XSQ W+pSDB'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_*(kTzk(lO T{l #lglD)gegQ{ep> e`pppppk?game=h?mutator=aN B)k l`(' \0R iKR VR pR ~R ..unrR R P/O/N/ _0J|a c{a #aHaGJH A #Gh+EJ. \ B J5E co0`0h zb# m0j0fjF c6a/!YeJa&_ffF-c'A-~eJjA-NeJ`&-c'O-c-uAD;-uA<J\&f fOeJ]&-c-u r/rlrr:|}}Trr:|}}(wr*ra/!fV.r-Z wV*ra/!A0|}}V&pV:-Z  rrD-R_.r}}V&-Z BnrwB*BQ-?-v:;,BBFBBGrrDq-R'B4(-Z  rrD}$EE-Z  wD*1rD*Da/!w"rV*}$EppV: }$E,wE%*E% lrD}-NPEEw* lrD}-NP' x_a0z7U-}!bdfzAY}!mf az}!jcfzA N0k0z$-E!G: U%mS%D?&: U]s6i) " h$m6i) 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,?,?&oH,<pD?H?,<mppp0So,:p0Sp,kmppR v/R T6i) " X$m6o),N6w) B96@) [9E!HkH%oH,<pD?H?,<mppp0So,:p0Sp,mppp0S,:p0S,6i) " j$m6o),N6w) A)6@) z(F@-6`)] ,w] @Q*] @Q-q] @Q8wE6i)6o)6w)6@)F] ,-6`);F) q0to7ttAyJu&{t-EA RtyJi&A-~yJj-x'-xtyt$-x )@|\h0g+He:g+%G v0~p0`kla R)R::$Q)a fs-Q+a A"a P"-IN)PtK)ih8-I $-Y -?F)-EeU-E iE)U1J) }wRP.-( -G.&/-V  -GODa tja/!YCa G~t`%`,`Z `a `"Xg|hA ua? A'( }0 60u0AqKa A<Owa *8a -E@a a @:AR%}J&@:ARA-E}J}&-~'-~AyA$-~ Ki0t0~Q36w7 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 v8D%\!y6i7 Y$6o7%N\!x6i7z-E6o7,N6w7 I86@7 Q86o7,NJ@-6`7xZ ,wZ @Q*nZ @Q-qZ @Q8w\6i76o76w76@7JZ -6`7;J7 7@|G1utG)u-EgJN'[A-pgJlu-KgJmA-~gJj-f%Ca/!Y$f. f,gJG'f:ugJ(J@'J Rf-K'-Ku l=f-K x0{0Og;eYkeybindingOFf+1{Fb~F,{b%kFFkFbFFb},b~Yk.b%YpYbYb}k.zYb}||YpYbYb}|>set inputOY X1Iy0[*e1`+gIkeynameS`+-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 > & {SW &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's0jVA?Dq>iNia6ijQ[+QuQr6wj*Q@AAeQe@@AQA@@AQ @1 'serverfull'A1 'invalidpass'D1 'banned'|0C1B{S-E$-q-@-'W%W,W}-'WxW%W,W}-W:'JWW%QW,GW}-W}fWc+W WF1 'duplicateid'HB1X HX mwX * :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 5M1iv5xJ['}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^+2FD:^+ %o V VK1 4L1 3P1 2N1k&A.JA A "BDBMAPVOTE VOTEMENU"@Q1 1R1 0S1 'CorrodedMessage'U1 'Burned'c1GM*(6Mj  J G,z fz g G%G%G,"{GpG|G, Gppp f= gb $ V1 'Fell'W1 'Suicided'[1 "debug gpf"_1AzmP.]A @C,a.&8 &8C,PP  C,P ,P ,r%r,rMPR : U! RrrY P wZ P v\ P t] P sPPPPSP  C,PP ,MP mNP nOP oPP ,^ P$ p_ P$ qPP$ rS,r%r,rMrlMNOa] Z \ Y ^ _ P^ --_ --FI \1 "PAUSE"a1 "OPENVOTE"Z1Ir7~^ -'-p_ -'-NP-'-~ \]1\+H:\+%I ^1`FC`.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 :TS ,&S pS &pS K,&pb $ _`1_7-a!bda_^_a!ma a_Fa!jca_^F HB2H/HHQ-:Q,wH*Ha/!XQ .H-s hQ h}-(-s(-sh}-(Q }-'hQ Q }-'hQ -s'---sh}-(-s( Q0w1mXB( f0] n:w] *] a/!fL.] w^ *1r^ *^ a/!wJXHQONMrL*J$ppL: J$Bv,wv%*v%)n] ^ XQONMvw*)n] ^ XQONM' d1i1C?rm*m.; Awm*A' Hh1]"|H]Lw]* :L,]a/!v.]- wm*] Mmj NmS Omq %Mm#c.ad% 5&Mm#c.ad& p,Mm$c.ad, ,Mm$c.ad, ] m"N.ad Z m#g.ad( O\ m#g.ad' Y m@O.ad S  Vw]* :L,]a/!d.]- wm*] ^ mQV 9_ mRV SPmTV zr]a :L,F l1 "EXIT"m1 "START"o1 "SPECTATE"~0`Uj>U-6`` ?@?,I@@@P? CU?Dq>E X+Uw6w`*AG~GxP~AxP?DGA@c$cqcE xc+a6w`~~AAw6@`*c$cE xc+a6@`~~AAE U~xPGq@c$c6o`-6``cb(c333?6c=D<6c P6c=D<6c P6c=D<6c PcE xcNca6i`( q1 "PLAY"x1Y\*{M  J Y,z ] Y%Y%Y,"{YeY_Y, ` p ],-Y+` p` P-Z+` p` SYe` b $ r1 "BALANCETEAMS"s1 "SETTEAM"t1 "NSC"u1 "server"v1 10.0z1 'NSC'|1y1f^$?  J :f% :f, K :fK ,&K eK &eK K,&eb $ }1DS!"  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 2MA @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 $vv%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 dp h r p s$s --b[ds pq M N P JIA w v v%?v,vI vFF VC2Q2UcuUU~U+{~ -YD~~,dD%V~~V~D~~D},-Y~UV%-Y E21l FIHM  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* F2HNp  k %Zk ,k H k I  k \ NH f1P2E]D L2 0G2O7X  \@O.pd P   J2j @!  i %Zi ,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 aHFI!jcpHE R2Y2[S %,CkeynameS{CXkeybindingC` %` , 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 1S2W2h1C?r\*\.; Aw\*A' GV2L+2o4'%:L+ ,F z[2J$.aA 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 O5}^,  zE ?}E ?,?&%W =f,&W =:=f,,,W =:=f,,,W =:=f,,i}E ,&i%H ~^E i,&&H %l=H H ~^E i,&H %5l=:lH ,ri}E ,&L=:l:D?i?,W l=:l:L:D?i?,W Mp:lMLliM t2m )+"  G -m -m ] $-m E$   )E~$    O@i2jRi}^,  zj%f =f,&f =:=f,,,f =:=f,,,f =:=f,,s%s}js}jeg g $$s}j&e=js&g =$e=js&g =js&&e=:e:D?s?,f :g OppO^:e,,&^:e,&sO } "|"LH2j22$F o2F+rT}F+fG+^ zt0-PA @C,k.&8 &`B,00 ,0  B,0  B,y 0 s 0z 0 t h 0B,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 0B00 ,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 k2ITD-'-m E-'-x m2qFq.ky -rq*z -rq*{ -rq*| -rq*Hrq*h  h  qh w2D+xHfD+^E+ n2NrBt Ct-j CZ- p2r2C?rt*t.; Awt*A' q2HP7Tt-H $\J t  Z-H $\J j  H $tJP.kdH\ B  {2H+j"  G owo*oa/!H.oo.o s@.o yH+%oo6  Hs2YDHYRwY* :R,Ya/!v.Y- wt*Y y t"a.kd z t@b.kd h   S{ t@d.kd B  d| P Nt$j Q  rYk :R,FrYh  :R,h -'t-(Z-(NwrYt :R,h -(t-'Z-(NrYZ :R,h -(t-(Z-'NrYD :R, wt*tmBrYE :R, wt*tp F3|x-c/a0 |rFrV|qF%YzccSFqcppc,SF10-b'X n1z B3z -z -T,x%:z :z ,T:z T,T _v2^79'*-_!bdk^l__!mk a^F_!jck^lF g1i ?HIcnI-H ni bFwc*k-HDvcbF|i asc#get#window|i hz0090DvcOPENRCPF-a'G-H -a#`,w`%*`%?i I`Gw*?i I A3C+PD  G  H u SC+kru* :o&NpMS&p||:o,[ZVxy 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@+Sqj~ .>@+~ qA+~ a ~ ~ ~  D3@G3~*d"  G y  S~*Iry * y &GNexgenJustBannedDialog*-y aRG% y    d uF1RIFF> WAVEfmt "VDdata #y[ 9 MJ \4][Y "*/.)!ɖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖ)ۋͻS@݌& z'./1.(xJ] TO .@p ]@l.]U^  (  ^1LLISTBINFOICRD 1997-04-25IENG J@KER !ISFTSound Forge 4.0@nR3|*~-b d ,{d y]d y #]k]v|k|*-b'}d -bd  M3}*bdp  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*ceny* Uz*]wn* Ndnamen{nh Ndtitle{nY Nd country{nonK Ndteam]{nsIn H3K3` C?rK*K.; AwK*A' _J3|7z`!bvwK*1K'K Na iddf|a d_|a `!m_ a|f a| `!jc_|a cf|a  N3x*aB 7"  G t Sx*Irt* t-Jt-Jt-JRL% t  5RI% t   A4"  F -?-?b:-M/:;,3-~-?-~-?] $ $-?EN%   EM%    b HL3yPryK :z, wK*K-}K-urye :z,I(ryL :z,I(ryc  :z,c -I'Eryf :z, wK*w_*O._K`OXs + TOh_ ]fEEwf*O.fKuOXs + UOhf ]_ry_ :z,wf*f-(f*ryf :z,w_*_-(_*ry_ :z, w_* wK*O._K`OXs + TOh_ ]fryf :z, wf* wK*O.fKuOXs + UOhf ]_ ~ 2.0Y3w*r-a b , {b Adb A #didv|iw*-a'}b -ab  P3v*sN"vnpppp[p0S,:p0S,]j1nv* IQ3 "muted"T3r*I$ rK* =w_*n._jjwf*n.fwn*{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@zX.AB,##'$C,##  C# ,# ,# J '# K '# L '#### M '#J#J#J#J#J#J## }lwX*  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,QZ~Z,Q @]3rJoa*"z r-(r  @@b3d3z2d v#j Xs <|ws *s '2dj fs s @*Z,wZ%*Z% 2dZ| V3 "noTeamSwitch"f3f*]'aPw <_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'TT i-YT o @gn3C>w~*m zV*+.AA, '* 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 J4Xi82#z:X[:X:0:X[ s3IW  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 ~hFXzU &!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 wwH * :w,H a/!v.H -H ssI u@  @lz3z@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*8kel.> E.KT*BBl iEE L,w3N*a/N* d  @ 5.0B4j_jL"  F j&jSS&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.0G4FO7"  F fzCxOZqxCF%I SFk.I ;Ak ]x5I<5wI*k.I ;Ak ]xII@ I@O4F4NOkC  Fa/!Y I<iwI*RI-EM%-G(R-G M,$M.H~IM{%-G'E:IMI l=MOMII@W mL4H4o*hm.> { .K BB{ f{ -(m i{ {  Z4Rh%8":RO%:Ru6i=:RO } P4K4pwdT} .> A $} } } l(}  l3og-kT&mwm*m-mq!SmjmmC-'-'"a/!l.@@/a0 o!Sho{*10 ~ T4J*MN!"  J -I -J*-L -K*%x x, -#x  yPa hPheL*fTaa TI{a  #a Na BJNF%B B,  -#B  A~P* zaa~Fappa,~HN M*O O*b $ 4i S4I*baQ~ .> ~ qI*~ a~ a ~  Y,3gbr^gCG<wC*owCgC'7g_ C!brCC@%y -g]-gnameG-x {y{ygg Yyg-Jg] Qmutedg-Kg]QnoTeamSwitch Oc 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|1nHL,wL%*L%EsuLlw*Esu A `4gY?  Hz g -m :h, z:h^-m'h?-m :h^g:hY|):h^}):hK~):hw)b $IQ% g   \4A FMqoA , {A ^rA wYXe:Y&A wpMS%JX&A  o4y) uWhA .> PA $A z)A y)`-{)A f&A  _4x)mjx)j, oj&, j^jYj^jKjwj^j&^jYj&Yj^j&^jKj&Kjwj&wj  { r4^4HtWN{ , {{ ^Dk{ m{ -Z'K{ -Z b4NV&X  H :N, #z:N^ [:N^ m:Nb $IO% [   zG ,5AA, ', 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'_=QQX-S-`QVq q AS&Q(q q :_Q%by$  h=Q   "GNexgenAccountUpdatedDialog ab $ 4z$ a4INw- -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 awu * :a,u a/!v.u -u sRI S@  f4TU+"  F -~-~] $-~EW%   )EU%    l4R:+"  F -N-N] $-NEY%   )EX%    Gd4u)2 '%:u) ,I Mg4j4"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)$MCM  r)M @C y4o)ktro)wUV::U&-@JV%kc:U, $ZVWY[\\-@WWYY~[d[J\0\\k-@(-@ w4q)OT s)Q  Sq)q  F rQ * zT Q openTV^% Q T Q  m4 "ClientKey"JD5l)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(AB,88  C,S.8 8 8 ,8  N!'$8  B8$,D?,@8  @C8 O!g8k 8$ P!n 8$ Q!8 $,,88 [D8 YB,88  C,U.8 8 8 ,8  R!'$8  C8  C8  @C8 S!b88 T!^88 $,,8E8 [F8 Yg,@b,@^,@WY M5_sgc_Y-J {c #cHcH H||HP-J'd-J d,z_Y_YP_Ypp_Y,Pc_^n-I {c #cGcG Gd|GQ-I'kb-I b,z_^_^Q_^pp_^,Q-J -Ib'( x4C Wf`S*SVC ,"{C  e -#C  eli)D . S B D 5lD $C C (XC ,D . S a D 5 hD $b t4 "ClientID"W4yf 4yget Engine.GameEngine ServerActors-G~y.UTPURESA"%-GO)UTPure E5j)N 7Q Sj)I  F rQ* Q-KQ-KQ-KRb% Q Q5Ra% Q Q Kz4F YBU*UVF ,"{F  pG . U B G 5F  pG $F F (:F ,G . U a G 5 hG $c {4 "Password"@5 "OverrideClass"4bbBb. S-rb*D-rb*b$%k -rb*n -rb*gCrb*rb*k -(n -(g 1b$%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 *$- [ % [ ,Ca/!Y$[ .S Q[  S l=[ nm% S  R[  S  B5@cg@. UE-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%|HWU4rWS :U,bXrWU :U,crWD :U,D-x#^=. S$irW :U,-x^\. S$ g k -n -rWF :U,F-x#Z=. U$zrWE :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%    T5SbAU  F/:;, gEc%    A5 "UseNexgenHUD"xX5R5ta:Ga s-xGtGa-x i5P jAvaU  F/:;, -P {-P  -P -P -P El%   _Ek%    EG@k8^)@}fw!N,tNNit&[tNt&{t{t(tN&%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])Pf]-E L,SzLZ-E'LZpp]),_)ZL-E Q5 "FlashMessages"| -1Z5 0[5 1z\5U,4A @C, ,,UU BU  BUA '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!AA 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!(IG ?D6\!(6p!(I@#JF 6U!(~6G!(~JS:z&F 6U!(:z,F 6U!(6G!(JF ?D6U!(6G!(J@![)G F }~ k?i?CXtDr**:%:%'MVk-d I,{I e $#I eKS a|MK-d'S  S -e~S  P%:%.~S  S%:%hIb-e ]5r AooVo# W!Sr , "{r  :o)r  :Sr r ; d5Y)Zz{us-z j , izj :-z'j :Y)j hZ)j [\)pj  a5}{lAV*9}, "{} X|..Z#K} X/r|*.  B $}5ppp[ h=}] } j}(f k5w P|lw  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 AdminVv 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 iSrJn-'Zn*Jeq{!mn..Z UQ]wn*n-UJ.  B J$:nNJ5ppp[ hnN] :nN j]rZnJ-'JZ*Z aQ{!jcZQC e5 "PlayPMSound"s5|Jlx  Iz B r r, 3r"zr:*rz ~ T-C -D R, zRX-C'QJ|RX|-D'QRl-C-D ] v|w]*]"GNexgenAccountUpdatedDialog]aRX|RjBROrr%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@"`HXYxrXZ :Y,rw*-(*BrX :Y,wZ*Z-(Z*BrXo :Y&ETrXs  :Y,s - w|*| G=UIrXa :Y,a- w|*U S } oC&} %| ~r  e  | r |!I=UU} | r ^rXB :Y,B- w|*L..ZXU S } oC&&} %| ~r  e  6| r |JLU} | r  v5T|HfMutator%TK{T w, [~K,%KK~K,FppT,KwAFMutatorwTK @n5 "RunCount"z5~I{n  I :~% :~, #z:~Xz K o o, 3o"zo:*oz D _$v:~Xw_*_"GNexgenAccountUpdatedDialog_a:~j K:~Oo2o%:~IW):~uDt:~I:~ub $ r5 "A"t5 "B"A6j y$aTournamentGameInfo%j l {j  r,tj z l l tSppppj ,t,l rySTournamentGameInforj l % o5x5~C+g %g ,d g  Zg 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 Xwb*b"GNexgenAccountUpdatedDialogban:O n, n&, nXnjnO%nInunXn&Xnjn&jnOn&OnIn&Inun&unb $ y5 "D"w5}5U`/:wZ*^:..ZN~sw*^. $~^^ ^{5 "E"|5@6[{3k;wZ*-_..Z-UbZw*-_'b-_(-_ _C6YUVHw v erj &x[bPAAD&PBAC&PCAB&PDAA&PEA~%PFA}%PGA{%PKA|%PHAy%PIAu%PJAs%y|e a${e  #e Ye WrY!W% zffSW!fppf,SWf }f f ~f /&Cf VVW%-T W,{Wy #WyUe |VU-T'f=WW.-?.:v:Tv$-?(=-XHFb 5zB6R U-W [-zR %SC-za--z -W GR s --z -W GR B--z-W B-W -zS# R  jo&pR  O&-W -zS" ..Zhop&S o p D6kSB(r*a/!^a A.-P(' Q6X0RzfV R B6mG=Im O%km Ikm O hJkJ ~5 "F"kH6 "G"G6V):A~pV),pU),% b,l5w_&3G(Zwwnamew @3f3333f333ff3fffff3f3f3f3333f3333333333f333333f3f33f3ff3f3f33333f3333333f3333333f333ff3fffff3f3f33ff3f3f3fffff3fffffffffff3fffffff3fffffff3fffff3f3333f333ff3fffff3f̙̙3̙f̙̙̙3f3f̙3333f3̙33ff3fff̙ff3f̙3f̙3f̙3f3333f333ff3fffff3f3f3fK6EECx-mJEoCd U-P)d %-j[yE` E%-j` d  u`  i$`  i=E&1EB-'a-'-jB-'a-Gd -m@EB--m@Ea-'e  ` e CE% Ec fE%D-jc d  Icc % hc E& hT%vT,yT ZzyTX -'lTX -E%0-m#y~y,TX -c y~y,T /%t*'." ," )" (" +*" 2@þ}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxssssssssssssssssssssssssssssssssnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnniiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiidddddddddddddddddddddddddddddddd T6T)0P-T)&%  J6 "H"]W6t iLbZ{t ]]tt &t }t ,&,t &}t ]  uK]6V6|KH|%5|,K|Z|KVX |%}|,K|y|O|%|, K|A|KwKVvKVtK:fK O6^@6N?^%0&V^& h h6Zn0vZ>  J :Z, #z:Z:C-j8z:Z&::Z&, -j :Z, x:Z-jB:Z&B:Z&k, "{kXxkOxkOB-l'kOBkOx-l'kWx:x:B:B:WWxhxhBhBhWWx[x[B[B[WWxJxJBJBJWb $<-l $ Y6sfGPs..Zws*n DsXn%s-U'sN=nsY h=ns-U(sN$sY i$s..s ^6X6Vw7Vv w x { Z:H:S:o0-s0-t %,\:UG%<, :%q, hC%, [x%, J%, X%E, j%t, OL%, I{%, u%, ^%H, Y%}, ^O%, K%, wj 0-I 0-L hefN O :Ec0-?:T:v0-v0-Y 0-M0-Q 0-T %S,{%:f :g 0-x 0-U0-Q0-k 0-]0-u 0-X0-V :Y :t0-B 0-D 0-H :M%,pl%,ezF e6 "S"G\6a2eT]0:a &{AB[:a ,{B R6 "I"_6b6~WC?r|*|.; Aw|*A' p6R $[9Qu6p!(?R &Gv?Du?R Duv?R ?R E6\!(F%BFR P vBDB?&BB?P P ?@[E6U!(P 6G!(F%~@EEP ?GF|!~ 6/%H)*'." ," )" (" +* 5*"yU6 C@  YZ5 +Ozek;;u̿6/r`ԀC]*0rb|}~j ئ h䵢ҭǁ$#߶xc3lӟG=< 5^%sɽhUa{tr?HM&F !Wm n&fo{->8)-Lghcn_('VŰJSm9 [%miy{wx\! R4vamqpdPEFFKToBLIf[26cXQ1":Lq.7L@, D_G NA.  o6 "P"`6 "J"6/%H)*'." ," )" (" +*5*"e {G@YZ5+Ozek;;u̿6/r`ԀC]*0rb|}~jئ h䵢ҭǁ߶xc3lӟG=< 5^%sɽhUa{tr?HM&F !Wmn&fo{->8)-Lghcn_('VŰJSm9[%miy{wx\!R4vamqpdPEFFKToBLIf[26cXQ1":Lq.7L@,D_GNA. n6l EPf  J :l ,  :l &#z:l : RM, "{MXMO:l MO%HHMO:l MOMO&MfM:l $M, "{M:"M&, M:MhM[MJM:M&:MhM&hM[M&[MJM&JM^b $-M) $ f6 "K"f 0.60j6 " -,."ki6 0~6HDK=H<;wH*cDHqc%H-Eu }u i$u  h=c${Hzu Hzu  ZHHtitleu HH@ r6 2t6W  2Qk6G!(?W &Nr?Dk?W Kkr?W ?W 6U!(@%B@W U r~K~?&~~?U U ?J[6\!(6p!(U @%IJU ?N@|!I Y s6 1x6 0v6{ )k-w(B-SY ?D6p!(B{BhY ?D6p!({B-SY 6p!({Y {}Y ?D?i@@i6p!(}?i@j6\!(Pj}?iO[j6U!(}6G!([P6U!(i6G!(!O \ z6sO k-k(B-Z\ ?D6G!(BsBh\ ?D6G!(sB-Z\ 6G!(s\ sz\ ?D?g@@X6G!(z?g@h6U!(Vhz?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(nar p|[ |T@?,R.K npr TR d(ar hR C7auZ![@@@@@@@@@@ }6 2nG7AdZ![@@@@@@A@@ A D7N7tZx@H  J"{, &: v{p:pHztp:&R&Sp& p: tpha(p[ b(pJ Rc(b $ I7]([KwoX ,@6\X (](6UX (^(6pX (_(6GX (`(X u 6vmxlzv|@lLyvL.K xz|L K7 ","xM7 16.0A7 "Reconnect"Q7 18.0T7Y(R+;(-[(-} KY( | 1"- PlayPMSoundtruetrue  n  'S.v /=w v@wS*SkOp*-Z( 1GNexgenPrivateMsgDialogOp R4  5  q%pO\(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\ng%uXXuXgXXg}\n{uhMu@h%ruu2ruh&uuh&f.K hektf rf fU(fT(et[Se \7 12.0X7 "Botpack.CHSpectator"]7 16.0j7 4.0[7 ","^7 'pj'c7 6b7vz$"  J KzvvV]W%W%oL%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 -Pbvcb`]ZXVset Engine.GameInfo GamePasswordUset Engine.GameInfo AdminPasswordTWLTR $-Pset IpServer.UdpServerUplink DoUplink TrueXset IpServer.UdpServerUplink DoUplink False $ 4i X'k7x /=*"  W$ >client /=+"  V$ w% client /=+"  M t% client /=+"  U$ v client /=("  R$ >gamew  t* /=;"  U  t game/  G /=)"  P$ % game  F /=)"  O$ x% game  K /=)"  M$ z% game /=*"  I$ >serverw  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&@,}spPzPp&l~ -,.sP&%?P??pP-P&PP o7 2r7S(uuSQ-m w, G|wI S(-m'wI Nw Px7 0i7R(=C/R(y u7 2n7n`Yg3No@)ll\ n{yZ i\ y(e.K l\ Z oe yef&li\ nZ @liZ @ln\ op.K l\ Z opZ p,p g7 'ac'v7u`XcKu a-t x, WzxI -t'xI u^x jlz7 1w7Q(K? LC-k j, 9|jI Q(-k'@j-k y7 10P( ","|7 ","}7 1p7{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(rK?|K(true 6/%I(*'." ," )" (" +*,*)5*"Jr l@/i~xaGi]ʾ~-,ǿyG¹p'}n[b{|WOqӌc9Kx`)eg8.tyk,vѐd"Ls{k,HU`Q2`ri*VfSZJ+%C@Wa]'XmT34;: MYVKl=<$F5 IPI&v#A6R?GI)zu71Dғ?0 (/,Nwuh :BEE>0 ))^w{uhX! &&)Vorj\OH!&&&GX__VL/,)(&/ILI/)& G8M8@N8@O8@@K86/%I(*'." ," )" (" +**('5*"O 9q@ʾǿy¹p}n[b{|Wqӌc9Kx`eg8.tyk,vѐd"Ls{k,U`Q2`ri*fSZJ+%C@Wa]'mT34;: MYVl=<$F5 IPIv#A6R?GI)zu71Dғ?0 (/,hwuh :BEE>0 ))^w{uhX! &&OVorj\OH!&&&)GX__VL/,)(&/ILI/)& 6/%F(*'." ," )" (" +*?3%5*" u@ 0 )$"&.UV/! 1ڞ(oƏu9涍az}νőmp ӮwgԤ񻖉Хᓒ¾דt|k̼Ѣjqr S]fDzsOM@'Q{ѯvnYQ*=eaf\M?5dPL=8 ,CIRC7 %AD3# 6/%F(*'." ," )" (" +*;0"5*" y@ڞƏu趍az}νƑmpӮugգ򺖉Х䑔בtzk̺УkprS]fDzsOM@Q{ЯumYQ=eaf]M=5=iapRK:S=xtxBF2[bcyuyyaH6;W[aYkk]bcNFHWBdOYdKNHW=hBNRKFN>dOK=BIRBAB D "Reconnect"eT8t:!Wetl1H(t&$=t1G(t%$DE(,@ a8\`)JB:\a/!H wP*PM.\\ PU8I;N RPISPrID :S,Reconnect& P8X8@Y8o8Z8w\ 6/%C(*'." ," )" (" +*((65*" @+O]<" =Ԙ O8(-&0Io>yljHrfJ.*(D2KRE95r-0vLJQLF\700`ܬeWZcbZSG?0A!_wbgqutqgZJ.zn O؇sm}Ȕ~m[6;Жaxk^\iӠ̤iUA0% M:CESdtҸ}bF1MCIhBWii6pYCBFZq»q9pV D)5EZrrS9Nf(1EXqqR6,`'@rShûJ5;QJ\tֶ}Bln(6BSgב~dP6'T-6P{tdQ?e=D.6R\grqgL?n OU05@FfWRJW6# 3D/19d?91Yp!`|jfp3'Ujj^D$  6/%C(*'." ," )" (" +*%%25*"I @O]ԘO8(-&0IyljHrfJ.*(DKRE95r-0vLJQLF\700ܬeWZcbZSG?0A_wbgqutqgZJ.z؇sm}Ȕ~m[6;Жaxk^\iӠ̤iUA0M:CESdtҸ}bF1CIhBWii6pYCBFZq»q9pVD)5EZrrS9f(1EXqqR6'@rShûJ5;QJ\tֶ}Bln(6BSgב~dP6T-6P{tdQ?eD.6R\grqgL?nU05@FfWRJW6D/19d?91Yp|jfpjj^ 6/%q'*'." ," )" (" +*(!5*" 5@ ap\ !xM.! EXLUb:#'-:+&"U}v,#(/:FJ/) XbU%(/FMNNPt{'WJ('JOV^-M5~RQ_F GW]lF%)$xO&:nswV&.H˛IciAK[:1XŲIdhjBƭ;[MH룙Sgfɬ:wV?D>qszVyC8 ^mz^rᘒ¨4T_Z[vɭ7eMMYU ऋ= 9o10Gu }ڿ63k`6*2ʻ@{6L|<>H1  i8YG8 0~Y.%Ypp f.Y^BY F.B^@A?,?,FI)aF_ `Fr_FfbF|'}'~''F eTN :"KeN l1D(N &$=N 1@(N %$=N W`N {'BTz',@W,  Pk2::$Pa  P`8UD PUVrUT :V,&T W S.f; A{T wS*S vT c8d8e8f8@g8s': OpNexgen Server Controller vU q,-'-' o':pNexgen Server Controller vU q, 9@A?,?,=.9-'-' U?XqT>E {|bwY*~XY%Y*rY* wg*Ygg*wY* wg*#}g}YrYYggrrY*L%L, wLE *rLE a~Xra%0rY*#}r}YYr{aL{~XYbwY* rg*L%_L, wLE *rLE a~Xr{% a% {aa{}Ya~X{}Yra%a{}YUa%0rg*#}r}ggr|aL<<wg*a~Xg1{% a% {aa{}Ya~X{}Yg1a%a{}Y|a dl8eRD:"eDl1y'D&$=D1r'D%$=DRTDm'@BWTDl'@B ~8h8u qn8Y< M#zYR k'7R YW i' @D eq wD *;-feD d?D?D @c?D?D @edcD  t8@@q86/%q'*'." ," )" (" +*' 5*"7 Д@ap\xM.! EXLUb:#'-:+&U}v,#(/:FJ/XbU%(/FMNNPt{'WJ('JOV^-M5~RQ_F GW]lF%)$xO&:nswV&.H˛IciAK[:1XŲIdhjBƭ;[MH룙Sgfɬ:wV?D>qszVyC8 ^mz^rᘒ¨4T_Z[vɭ7eMMYUऋ= 9o10Gu}ڿ63k`6*2ʻ@{6L|<>H1 J9e' 6Ie'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[\Pr[Z :\,Reconnect& u8G9}87m x8@96/%V'*'." ," )" (" +* 5*ƥ" @I LTH 5j6z2J 3M.}KN."Om/'Qq-7Pt&~py|Y(4nXgaZxs?rokdc[`A0l]wvhWS)+u{_e)$U ERB,=iV 8D!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^_Pr^] :_,Reconnect& I98H] X'H92QL  gk q}o-g6k $6k $6k $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># Pcjrc` :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 *9C $T'C C . C * 6/%V'*'." ," )" (" +* 5*ƥ"No <@ILTHjzM}NmQqPt~py|YnXgaZxsrokdc[`l]wvhWSu{_eUERiVb\f^FG P^?Y. k-6P$6P$6P$PjiQ'R'?& 6P=,6P=,6P=, 6P$6P$6P$P%(Pj?,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[ 'PBArBk :A,_ `r_ $<rPasswordz _ bReconnect&%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"@:xQExr-w/*/=Qxvp,wQ*QQxv- h&J'BnJ'h k&v9n~1C;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 yiBnŎ> Y7XÎ> 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@mZmmZ}\nd %z,<zd~M@z~%y@@y@~@@~d V y|z@ d ,%Zd xZ,ZVZK 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}9GBn 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. Ude*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/}.>/=wpluginsettingsr}*}.>/=@" A$ Apluginsettingsserver,serversettings} "~& Pu9CSPCEQrC :E,fE v@& C:xYVFtkxYx(DNamex' I:s }|;ppget  f.NexgenConfigExt bInstalledT'I|;ppget  f.NexgenConfigSys bInstalledT'GNexgenResidentConfigDialog }H:{&KU Y}.Uw}*9|}X{&}}.}* e|&@:qDe@l1|&@&$=@1z&@%$ sJ:w&UYs.Uws*9sdw&ss.s* ]:v&lw`a/!Y-F.-.-(Ktk d:v&a/!Y.--F L:t&a`e.^we*Bedt&eee.e O:K:qe. B n&@k&o&w:o&sW& H "OverrideClass"Q: "Reconnect"R: "Botpack.CHSpectator"eS:i:)Feil1r&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:qVtPqpPrqH :p,Reconnect&rqs :p,s-(b"OverrideClassBotpack.CHSpectator'Reconnect& _:d&xP# xd&s&-V' r:^:Qv--J'-m  G a:\:}M w* $  mA^*WU^~.I d3^mA^A[]^&_&?& n,-X&Amp.I Srp*p c:A[?n]?&pn,Aw.I AF.I A[?n]B.I  @HHHQQQbbbnnnzzz|~~/%b:*'.",")"("+*"PN 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:-6hBdAAh@bd@o.K Abhdo Y&AAh@@n.K Abhdn V&dAhA@@B?,@A@@B@bd@{.K Abhd{h{,{O 'h@Bd@AA@bd@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:DDrO*,N, {NNt{&f{N{&F{F{3NN&6X{FD6W{FC6U{FB6R{FAODCBA _/*************************************************************************************************** * * 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&Wi }:T7|2w/*/=7TQMzl,wlQ*lQ7TQM2 ~:{WcQL>Q/a0 CORQwO*R10wO*{%{NOF6X{F6W{F6U{F6R{F{eN% s:Gm~GJHrGo :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:eIQe{ O{e{ saye {9 "country"oq<oH&@&oi C;qk{ưq$:q, ::q`%P-[] V-[']^:q`$2qX^qq$:q,:qU%'P:qU:qU%Hq'Pq 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-FgKQRHGfg-VGVgHKQRHDV!QR ^;aH^!v'Pr/= Hasc,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-LO![4-LONOJDNOO* 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'5PrO*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.05n%a84s. n%5. i%56& T; 2.0\;\Dr\*,Q, JQQtJ&fJQJ&PJPJ3QQ&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;uWjiL>i/a0 C\RQw\*R10w\*u%uQ\F6muP6luP6kuP6juPueQ% Mxe5we%YUYx}xV%@YP%oP,WpWK#:P@&P9W  f;\28%wXXw^tt 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 ,UwA 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("Mickal \"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; 2v;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;;Bn,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-dI![4-dgfIJDfIg* \hg'5hr\*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{;֒BnMMtM> Y7XÎ> Y> YtM7XÓ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<qDrc*,_, l__tl&fl_l&jljl3__&6Bljq6Aljp6@ljo6ljncqpon 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<hWi~L>~/a0 CcRQwc*R10wc*h%h_cF6Bhj6Ahj6@hj6hjhe_% 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-yE![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^<FDrm*,f, gfftg&fgfg&ZgZg3ff&6YgZF6XgZE6WgZD6VgZCmFEDC Z< 3c<cWpTL>T/a0 CmRQwm*R10wm*c%cfmF6YcZ6XcZ6WcZ6VcZcef% m<ry-:r&b"OverrideClass'kk:r,(b"OverrideClassBotpack.CHSpectator'Reconnect a<f<b L-O|![4-ORQ|JDQ|R* mSg'5Srm*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=wEw/*/g//Yg-'gu{w/= Mw d< 5n< 6o< 7p< 8u<]Drq*,n, OnntO&fOnO&IOIO3nn&6pOI]6oOI\6mOI[6lOIZq]\[Z Bl Y T],+Failed to login: client ID already in use.mUYour 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_kL>k/a0 CqRQwq*R10wq*Q%QnqF6pQI6oQI6mQI6lQIQen% v<z<w L-fp![4-fihpJDhpi* qjg'5jrq*W3q {j y< 'sswaiting'~< 'ssready'< 'ssstaring'D=rW'~zr{q{$rq*$wr*|& B{<|8@ 2C==BnŎ> 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'#HsoX#otW#ots l=V# Q=T#G)w^GT#h#j#{#|#^g^^Yg-'gu @= 'ssended'F= 'sspaused'G= 'ssmatch'H= 'ssnormal'I= 'cslogin'Y=IGU\ %S\ ,\ zm [ b<Iw[ *2[ Q\  m , m I[ -m(nm &+-mn\ zWnIn&I%-m'(wnInIn&In&Iwnm [ [ @8\  p; 0.50J= 'csidle'}@K# "0123456789ABCDEF"L= 1.25W=yZ`Çrg*Iwg*ggwg*pga/!_g&ggBy +o'z +p'Pgy@^gz@gg +P^yz/.+g/&/=@ %@ ,w@ Q*@ Qx@  ucSTu1-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^>CPCPElElElCPP3JlO{/*************************************************************************************************** * * 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=6Arg*Is Es't Et'cgs@dgt@_g Ecdst^.E_^b^a1^`o ^_l^& B>I$tig.Cg-gU grg*gK * m=|OPNo &Fo G#<o |l|lo o l V=\<~ 6 Z=LUO4CPCPElElElCPP3JM= '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&nnN 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&^!CPCPElElElCPP3JH{/*************************************************************************************************** * * 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 vPj%-{'L pL m L ppL m j"m m j}v L   kE|=~PNn &Fn A#<n ~k~kn n k {@[= 'csmuted'g=E<| 6 p=?CPCPElElElCPP3J~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=HCPCPElElElCPP3Jj=z"rp^pz"%1y"^p^%2x"^p^%3v"^p^%4t"^  o= 'csdead't= 'csnormal'u=_:!.C-o|1$-FlashMessagestruetrue-X|1$-ShowPlayerLoctruetrue v=l g3$w* gl {l c]sh-P : U%+c?,:'{%AD< Uc?&5A%Wl  # v$SA%N%NeWl  u$%N%N:'{%A:'{Wl  # t$SA,Ndy%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_DDP&D  A>x=D=r=Ba/!Y.D(yb<wy*y-E%yQyQ,yQdByy@XPddOdd,(t BGfiM%9M/Mdt Mdt &Mdt b PdMdb dNb Mfi,b OdMdb dNMb fiGM8' 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"Um_r"1}_}_pq"_ ej}}_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"timgo"1}g@gpgj" eh@}ghgghg  w=-W+-.-r-OE^ % {OK~O\nK%{OKOOK}\n{OO-a{PPRRDP^ g^R,, \D?^ P?,, \D-?^,]D-?\,-$-b(T>-?\?]-(a L)?^?\BB-b(TL?- ?\@@?]?-.a ]@?\@??-(?\??^?]?-.a ]@?\@??- ?\@@?]@-&a ]A???-(?\@@?]??\-&a ]A???--?\@@?^?,?]@-&a ]A???-5?\@@?^?,?]??\-&a ]A???-C]??-b(T?CbDCI@??,@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\nSK%{OKOOK}\nf{OO-)?\, ?], ?^ P- a{(^  qQ>F<dH-S q,@>|6e qUF-S'Eq-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 HR%{zRNSC(zzRR~z R%Az'AzRzpzR R%GA R}z D, mzR&3zm 0G% -`A Rzm"dG%GR&-`'{G\A R-`(G%GRA %De"pzGA G\""GA GmR'  aU>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 zq/*************************************************************************************************** * * 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>C4Cs::$e ICuw*de!uw*-ldC!ude!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_ XN>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>^"Cl-l(-^"0r\ *\ tr[ *[ a X [ \ w[ *-l'[ w\ *\  c>\"xq>X -\"5zX]"<IX M> 10[>Y"N_/cfpY",T~f,9-[ T%T}\%zfT}\}\\]pp]fT}\,ffT},T~f,6]p]fT|]}\}=p\=-['6]ffT},T~f, [-[]}\}=aV"  VA?{ 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"v6EN"<EtBEtBB E`BzE`Bzz, z&B<E`BzrrE`Bzrz, z&BrE  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 = true; waitTime = 10; startTime = 5; autoReconnectTime = 10; maxIdleTime = 40; maxIdleTimeCP = 120; spawnProtectionTime = 10; teamKillDamageProtectionTime = 3; teamKillPushProtectionTime = 8; broadcastTeamKillAttempts = true; MOTDLine[0] = level.game.gameReplicationInfo.MOTDLine1; MOTDLine[1] = level.game.gameReplicationInfo.MOTDLine2; MOTDLine[2] = level.game.gameReplicationInfo.MOTDLine3; MOTDLine[3] = level.game.gameReplicationInfo.MOTDLine4; spawnProtectExcludeWeapons[0]="Botpack.Translocator,PS"; spawnProtectExcludeWeapons[1]="Botpack.SniperRifle,S"; enableBootControl = false; lastServerURL = ""; restartOnLastGame = true; matchModeActivated = false; matchesToPlay = 5; currentMatch = 1; serverPassword = ""; spectatorsNeedPassword = true; muteSpectatorsDuringMatch = true; enableMatchBootControl = true; matchAutoLockTeams = true; matchAutoPause = false; matchAutoSeparate = false; allowTeamSwitch = true; allowTeamBalance = true; allowNameChange = true; autoDisableMatchTime = 5; // Message HUD. useNexgenMessageHUD = true; replacementClass[0] = "BotPack.ChallengeHUD=" $ packageName $ ".NexgenHUDxDM"; replacementClass[1] = "BotPack.ChallengeTeamHUD=" $ packageName $ ".NexgenHUDxTDM"; replacementClass[2] = "BotPack.ChallengeCTFHUD=" $ packageName $ ".NexgenHUDxCTF"; replacementClass[3] = "BotPack.AssaultHUD=" $ packageName $ ".NexgenHUDxAS"; replacementClass[4] = "BotPack.ChallengeDominationHUD=" $ packageName $ ".NexgenHUDxDOM"; // Server controller has been successfully installed. bInstalled = true; saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Adds the specified account type to the server controller. * $PARAM typeName Name of the account type to add. * $PARAM rights The rights for this account type. * $PARAM title Title for players that have this account name. * $REQUIRE typeName != "" * **************************************************************************************************/ function addAccountType(string typeName, string rights, optional string title) { local int index; local bool bFound; // Find an empty account type slot. while (!bFound && index < arrayCount(atTypeName)) { if (atTypeName[index] == "") { // This slot is empty. bFound = true; atTypeName[index] = typeName; atRights[index] = rights; atTitle[index] = title; } else { // This one is used, continue search. index++; } } } /*************************************************************************************************** * * $DESCRIPTION Defines a new type of client right. Note that defining a right may fail if there * isn't a free slot available for the definition. * $PARAM rightID String identifier of the client right. * $PARAM description Description of the right. * $REQUIRE bInitialized && rightID != "" && description != "" * $RETURN True, if the right definition was added, false not. * **************************************************************************************************/ function bool addRightDefiniton(string rightID, string description) { local bool bFound; local int index; // Find empty slot. while (!bFound && index < arrayCount(rightsDef)) { if (rightsDef[index] == "") { // Empty slot found. bFound = true; rightsDef[index] = rightID $ separator $ description; } else { // Slot is in use, continue with the next one. index++; } } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Validates the current configuration. Any invalid settings will be automatically * adjusted to a proper setting. * $REQUIRE bInitialized * $RETURN True, if the configuration was valid, false otherwise. * $ENSURE !old.checkConfig() ? new.checkConfig() : true * **************************************************************************************************/ function bool checkConfig() { local bool bConfigOk; local NexgenConfigChecker cCheck; cCheck = spawn(class'NexgenConfigChecker'); bConfigOk = cCheck.checkConfig(self); cCheck.destroy(); return bConfigOk; } /*************************************************************************************************** * * $DESCRIPTION Returns the index in the ban list for the given player info. * $PARAM playerName Name of the player for which the entry in the ban list is to be found. * $PARAM playerIP IP address of the player. * $PARAM playerID ID code of the player. * $RETURN The index in the ban list for the specified player if banned, -1 if the player is * not banned on the server. * $ENSURE 0 <= result && result <= arrayCount(bannedName) || result == -1 * **************************************************************************************************/ function int getBanIndex(string playerName, string playerIP, string playerID) { local int index; local bool bFound; local bool bNameMatch; local bool bIPMatch; local bool bIDMatch; // Lookup player in the ban list. while (!bFound && index < arrayCount(bannedName) && bannedName[index] != "") { bNameMatch = bannedName[index] ~= playerName; bIPMatch = instr(bannedIPs[index], playerIP) >= 0; bIDMatch = instr(bannedIDs[index], playerID) >= 0; // Match? if (bNameMatch || bIPMatch || bIDMatch) { // Oh yeah. bFound = true; } else { // Nope, maybe next. index++; } } // Return index in the ban list. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Updates the specified ban entry. If a new IP or ID for the specified entry is * detected it will be added. * $PARAM index Location in the banlist. * $PARAM playerIP IP address of the player. * $PARAM playerID ID code of the player. * $REQUIRE 0 <= index && index <= arrayCount(bannedName) && bannedName[index] != "" * $RETURN True if the specified ban entry was updated, false if no changes were made. * $ENSURE instr(bannedIPs[index], playerIP) >= 0 && instr(bannedIDs[index], playerID) >= 0 * **************************************************************************************************/ function bool updateBan(int index, string playerIP, string playerID) { local bool bIPMatch; local bool bIDMatch; local string remaining; local string currIP; local string currID; local int ipCount; local int idCount; // Compare & count IP address. remaining = bannedIPs[index]; while (!bIPMatch && remaining != "") { class'NexgenUtil'.static.split(remaining, currIP, remaining); currIP = class'NexgenUtil'.static.trim(currIP); if (currIP ~= playerIP) { bIPMatch = true; } else { ipCount++; } } // Add IP address if not already in the list and the list isn't full. if (!bIPMatch && ipCount < maxBanIPAddresses) { if (bannedIPs[index] == "") { bannedIPs[index] = playerIP; } else { bannedIPs[index] = bannedIPs[index] $ separator $ playerIP; } } // Compare & count client ID's. remaining = bannedIDs[index]; while (!bIDMatch && remaining != "") { class'NexgenUtil'.static.split(remaining, currID, remaining); currID = class'NexgenUtil'.static.trim(currID); if (currID ~= playerID) { bIDMatch = true; } else { idCount++; } } // Add client ID if not already in the list and the list isn't full. if (!bIDMatch && idCount < maxBanClientIDs) { if (bannedIDs[index] == "") { bannedIDs[index] = playerID; } else { bannedIDs[index] = bannedIDs[index] $ separator $ playerID; } } // Save changes. if (!bIPMatch || !bIDMatch) { saveConfig(); return true; } else { return false; } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified ban entry has expired. * $PARAM index Location in the banlist. * $REQUIRE 0 <= index && index <= arrayCount(bannedName) && bannedName[index] != "" * $RETURN True if the specified ban entry has expired, false if not. * **************************************************************************************************/ function bool isExpiredBan(int index) { local bool bExpired; local byte banPeriodType; local string banPeriodArgs; local int year, month, day, hour, minute; // Get period type. getBanPeriodType(banPeriod[index], banPeriodType, banPeriodArgs); // Check for expiration. if (banPeriodType == BP_Matches) { // Banned for some matches. bExpired = (int(banPeriodArgs) <= 0); } else if (banPeriodType == BP_UntilDate) { // Banned until some date. class'NexgenUtil'.static.readDate(banPeriodArgs, year, month, day, hour, minute); bExpired = level.year > year || level.year == year && (level.month > month || level.month == month && (level.day > day || level.day == day && (level.hour > hour || level.hour == hour && level.minute >= minute))); } else { // Banned forever, never expires. bExpired = false; } // Return result. return bExpired; } /*************************************************************************************************** * * $DESCRIPTION Removes all expired bans from the banlist. * $RETURN True if one ore more bans were removed from the banlist. * **************************************************************************************************/ function bool cleanExpiredBans() { local int currBan; local bool bBanDeleted; // Check each ban entry. while (currBan < arrayCount(bannedName) && bannedName[currBan] != "") { if (isExpiredBan(currBan)) { removeBan(currBan); bBanDeleted = true; } else { currBan++; } } // Return result. return bBanDeleted; } /*************************************************************************************************** * * $DESCRIPTION Removes the specified entry from the banlist. * $PARAM entryNum Location in the banlist. * $REQUIRE 0 <= entryNum && entryNum <= arrayCount(bannedName) && bannedName[entryNum] != "" * $ENSURE new.bannedName[entryNum] != old.bannedName[entryNum] * **************************************************************************************************/ function removeBan(int entryNum) { local int index; for (index = entryNum; index < arrayCount(bannedName); index++) { // Last entry? if (index + 1 == arrayCount(bannedName)) { // Yes, clear fields. bannedName[index] = ""; bannedIPs[index] = ""; bannedIDs[index] = ""; banReason[index] = ""; banPeriod[index] = ""; } else { // No, copy fields from next entry. bannedName[index] = bannedName[index + 1]; bannedIPs[index] = bannedIPs[index + 1]; bannedIDs[index] = bannedIDs[index + 1]; banReason[index] = banReason[index + 1]; banPeriod[index] = banPeriod[index + 1]; } } } /*************************************************************************************************** * * $DESCRIPTION Updates the ban period strings. Note this function should only be called once * during the game, preferrably at the beginning of the game as it might cause a * checksum mismatch for the dynamic config data. * **************************************************************************************************/ function updateBanPeriods() { local int currBan; local byte banPeriodType; local string banPeriodArgs; // Check each ban entry. while (currBan < arrayCount(bannedName) && bannedName[currBan] != "") { getBanPeriodType(banPeriod[currBan], banPeriodType, banPeriodArgs); if (banPeriodType == BP_Matches) { banPeriod[currBan] = "M" $ max(0, int(banPeriodArgs) - 1); } currBan++; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the title for the specified user account. * $PARAM accountNum Index of the user account which is to be used. * $REQUIRE 0 <= accountNum && accountNum <= arrayCount(paPlayerID) * $RETURN The title assigned to the specified account. * $ENSURE result != "" * **************************************************************************************************/ simulated function string getUserAccountTitle(byte accountNum) { if (paAccountType[accountNum] < 0) { return paCustomTitle[accountNum]; } else { return getAccountTypeTitle(paAccountType[accountNum]); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the title for the specified account type. * $PARAM accountType Index of the account type which is to be used. * $REQUIRE 0 <= accountType && accountType <= arrayCount(atTypeName) * $RETURN The title assigned to the specified account type. * $ENSURE result != "" * **************************************************************************************************/ simulated function string getAccountTypeTitle(byte accountType) { if (atTitle[accountType] == "") { return atTypeName[accountType]; } else { return atTitle[accountType]; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the account index for the specified clientID. * $PARAM clientID The client identification code of the player whose account index is to * be returned. * $REQUIRE clientID != "" * $RETURN The index in the user account list for the specified player. In case there is no * account for the player, -1 will be returned. * $ENSURE result == -1 || 0 <= result && result <= arrayCount(paPlayerID) && * result >= 0 ? paPlayerID[result] ~= clientID : true * **************************************************************************************************/ simulated function int getUserAccountIndex(string clientID) { local int index; local bool bFound; // Locate account. while (!bFound && index < arrayCount(paPlayerID) && paPlayerID[index] != "") { // Check entry... if (paPlayerID[index] ~= clientID) { bFound = true; } else { index++; } } // Return result. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Adds a new user account to the server. Note that if there already is an existing * account for the specified clientID, that account will be overwritten. * $PARAM clientID Identification code of the player for which an account has to be * added. * $PARAM accountName Name of the player for which the account is to be made. * $PARAM accountType Type of account to use. Use -1 to indicate a custom account. * $PARAM customRights Rights string for a custom account type. * $PARAM customTitle User title for a custom account type. * $REQUIRE clientID != "" && accountName != "" && * -1 <= accountType && accountType <= arrayCount(atTypeName) && * (accountType == -1 ? customTitle != "" : true) * $RETURN The position in the user accountlist where the account was stored. If all user * account slots are occupied -1 will be returned and the account isn't saved. * $ENSURE -1 <= result && result <= arrayCount(paPlayerID) && * (result >= 0 ? paPlayerID[result] == clientID && * paPlayerName[result] == accountName && * paAccountType[result] == accountType && * paCustomRights[result] == customRights && * paCustomTitle[result] == customTitle * : true) * **************************************************************************************************/ function int addUserAccount(string clientID, string accountName, int accountType, optional string customRights, optional string customTitle) { local int index; local bool bFound; // Check if player already has an account. index = getUserAccountIndex(clientID); if (index >= 0) { // Yeah, update existing account. bFound = true; } else { // Nope find an empty slot. index = 0; while (!bFound && index < arrayCount(paPlayerID)) { if (paPlayerID[index] == "") { // Empty slot found, use it! bFound = true; } else { // Slot is used, check next one. index++; } } } // Store account. if (bFound) { paPlayerID[index] = clientID; paPlayerName[index] = accountName; paAccountType[index] = accountType; paCustomRights[index] = customRights; paCustomTitle[index] = customTitle; } // Return index of created account. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Creates a rights string containing all available rights. * $RETURN A rights string with all rights that are defined on this server. * $ENSURE result != "" * **************************************************************************************************/ function string getAllRights() { local int index; local string rights; while (index < arrayCount(rightsDef) && rightsDef[index] != "") { if (index > 0) { rights = rights $ separator $ left(rightsDef[index], instr(rightsDef[index], separator)); } else { rights = left(rightsDef[index], instr(rightsDef[index], separator)); } index++; } return rights; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the ban period type and its argument from the specified ban period * string. * $PARAM banPeriodStr The ban period description string. * $PARAM banPeriodType Ban period type number. * $PARAM args Optional arguments of the ban period type. * $ENSURE banPeriodType == BP_Forever || * banPeriodType == BP_Matches || * banPeriodType == BP_UntilDate * **************************************************************************************************/ static function getBanPeriodType(string banPeriodStr, out byte banPeriodType, out string args) { if (banPeriodStr == "") { banPeriodType = BP_Forever; } else if (left(banPeriodStr, 1) ~= "M") { banPeriodType = BP_Matches; args = mid(banPeriodStr, 1); } else if (left(banPeriodStr, 1) ~= "U") { banPeriodType = BP_UntilDate; args = mid(banPeriodStr, 1); } else { banPeriodType = BP_Forever; } } /*************************************************************************************************** * * $DESCRIPTION Locates the position of the specified mutator in the mutator list. * $PARAM mutatorClass The class of the mutator that is to be located. * $RETURN The index of the specified mutator in the mutator list, or -1 if the mutator isn't * found. * **************************************************************************************************/ simulated function int getMutatorIndex(string mutatorClass) { local int index; local bool bFound; local string currClass; local string remaining; // Attempt to locate mutator. while (!bFound && index < arrayCount(mutatorInfo) && mutatorInfo[index] != "") { remaining = mutatorInfo[index]; class'NexgenUtil'.static.split(remaining, currClass, remaining); if (currClass ~= mutatorClass) { bFound = true; } else { index++; } } // Return result. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Locates the position of the specified game in the game list. * $PARAM gameClass The class of the game that is to be located. * $RETURN The index of the specified gane in the game list, or -1 if the game isn't found. * **************************************************************************************************/ simulated function int getGameIndex(string gameClass) { local int index; local bool bFound; local string currClass; local string remaining; // Attempt to locate mutator. while (!bFound && index < arrayCount(gameTypeInfo) && gameTypeInfo[index] != "") { remaining = gameTypeInfo[index]; class'NexgenUtil'.static.split(remaining, currClass, remaining); if (currClass ~= gameClass) { bFound = true; } else { index++; } } // Return result. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Sets the list of active mutators. * $ENSURE bActiveMutatorIndicesSet * **************************************************************************************************/ function setActiveMutatorList() { local Mutator m; local int index; // Reset list. activeMutatorIndices = ""; // Get name for each mutator instance. foreach allActors(class'Mutator', m) { index = getMutatorIndex(string(m.class)); if (index >= 0) { if (activeMutatorIndices == "") { activeMutatorIndices = string(index); } else { activeMutatorIndices = activeMutatorIndices $ separator $ string(index); } } } bActiveMutatorIndicesSet = true; // Update checksum. updateChecksum(); } /*************************************************************************************************** * * $DESCRIPTION Sets the current encryption parameters. * **************************************************************************************************/ simulated function setEncryptionParams(int k, string cs) { default.encryptionKey = k; default.codeScheme = cs; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the current encryption parameters. * **************************************************************************************************/ function int getEncryptionParams(out int k, out string cs) { k = default.encryptionKey; cs = default.codeScheme; } /*************************************************************************************************** * * $DESCRIPTION Encodes the given string. * $PARAM str The string that is to be encoded. * $RETURN The encoded string. * $ENSURE decode(result) == str * **************************************************************************************************/ simulated function string encode(string str) { local string output; local int index; local byte ck[4]; local byte cb; local byte nb; if (len(default.codeScheme) != 32 || str == "") return ""; ck[0] = default.encryptionKey & 0xFF; ck[1] = (default.encryptionKey >>> 8) & 0xFF; ck[2] = (default.encryptionKey >>> 16) & 0xFF; ck[3] = (default.encryptionKey >>> 24) & 0xFF; for (index = 0; index <= len(str); index++) { if (index == len(str)) { cb = nb; nb = 0; } else if (index == len(str) - 1) { cb = asc(mid(str, index, 1)); nb = rand(256); } else { cb = asc(mid(str, index, 1)); nb = asc(mid(str, index + 1, 1)); } cb = cb ^ ck[index % 4] ^ nb; output = output $ mid(default.codeScheme, (cb >>> 4) & 0x0F, 1) $ mid(default.codeScheme, cb & 0x0F, 1); } return output; } /*************************************************************************************************** * * $DESCRIPTION Decodes the given string. * $PARAM str The string that is to be decoded. * $RETURN The decoded string. * **************************************************************************************************/ simulated function string decode(string str) { local string output; local int index; local int ci; local byte ck[4]; local byte cb; local byte lb; if (len(default.codeScheme) != 32 || str == "" || len(str) % 2 == 1) return ""; ck[0] = default.encryptionKey & 0xFF; ck[1] = (default.encryptionKey >>> 8) & 0xFF; ck[2] = (default.encryptionKey >>> 16) & 0xFF; ck[3] = (default.encryptionKey >>> 24) & 0xFF; for (index = len(str) / 2 - 1; index >= 0; index--) { ci = instr(default.codeScheme, mid(str, index * 2 + 1, 1)); if (ci < 0) return ""; else cb = ci; ci = instr(default.codeScheme, mid(str, index * 2, 1)); if (ci < 0) return ""; else cb = cb | (ci << 4); if (index == len(str) / 2 - 1) { lb = cb ^ ck[index % 4]; } else { cb = cb ^ lb ^ ck[index % 4]; output = chr(cb) $ output; lb = cb; } } return output; } i>!t:h!nm  t>\{Pnh-[y\YXWVS`-[ \YXWVSf\ o>x!`=v! &, *,'hx!,*, 2,, :,, B,, J,, R,, Z,, b, , j, , r, , z, ,   b@I?cX;kh-b $Zca`_^]`-bva`_^]fc r>ZhWA=?Z??%$?Z?,?%?Z?,d?%  H>CK<_O!u~C:%y_D, wDy*UDy-N|C}Dy&pDy:3-N0}Dy}WWDyU-N'WDyD>-NwW*PW@,pW: ,C}W&wW* {CO!S O!ARBW?wW*PWO!AW-W-j,jRwW*XWwW*XW/'W-W-j,/j, {X-X@RW,pp (X): jC@RpW: jC{CpCWs! v>r!W(D-d'E &Jr!-d E ,c~J.eE , c%-d(c% E ,-d(c%CJcJJc&CJJ-d-d{C }C,c%b-dc}CYCc&-d0Y Y9c-d-dJCE -d  }dN/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenCommandHandler * $VERSION 1.07 (19-1-2008 18:00) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Handles the nexgen commands issued via the mutate command in the console. The * command handling has been placed into this class because it gets rather lengthy. * This saves the NexgenController class from becoming one huge unreadable monster. * **************************************************************************************************/ class NexgenCommandHandler extends info; var NexgenController control; // Server controller. var NexgenLang lng; // Language instance to support localization. const openMapVoteCommand = "BDBMAPVOTE VOTEMENU"; // Mutate command to open the mapvote window. /*************************************************************************************************** * * $DESCRIPTION Initializes the command handler. * $REQUIRE owner != none && owner.isA('NexgenController') * $ENSURE control != none && lng != none * **************************************************************************************************/ function preBeginPlay() { control = NexgenController(owner); lng = control.lng; } /*************************************************************************************************** * * $DESCRIPTION Executes the given command. * $PARAM client The client that has issued the command. * $PARAM cmd Name of the command to execute. * $PARAM args Arguments for the command to execute. * $REQUIRE sender != none * **************************************************************************************************/ function execCommand(NexgenClient client, string cmd, string args[10]) { local bool bInvalidCommand; local bool bSuccess; local string failMessage; local string reason; // Determine action. failMessage = lng.commandFailedMsg; reason = lng.internalErrorMsg; switch (caps(cmd)) { case control.CMD_SwitchTeam: bSuccess = execSwitchTeam(client, int(args[0]), reason); failMessage = lng.teamSwitchFailedMsg; break; case control.CMD_BalanceTeams: bSuccess = execBalanceTeams(client, reason); failMessage = lng.teamBalanceFailedMsg; break; case control.CMD_Play: bSuccess = execJoinAsPlayer(client, reason); break; case control.CMD_Spectate: bSuccess = execJoinAsSpec(client, reason); break; case control.CMD_StartGame: bSuccess = execStartGame(client, reason); break; case control.CMD_Pause: bSuccess = execPauseGame(client, reason); break; case control.CMD_Exit: bSuccess = true; client.clientCommand(client.exitCommand); break; case control.CMD_Disconnect: bSuccess = true; client.clientCommand(client.disconnectCommand); break; case control.CMD_Open: bSuccess = true; client.showPanel(); break; case control.CMD_OpenVote: bSuccess = true; level.game.baseMutator.mutate(openMapVoteCommand, client.player); break; default: bInvalidCommand = true; } // Check outcome. if (bInvalidCommand) { client.showMsg(lng.invalidCommandMsg); } else if (!bSuccess) { client.showMsg(lng.format(failMessage, reason)); } } /*************************************************************************************************** * * $DESCRIPTION Executes a team switch command. * $PARAM client The client that has issued the command. * $PARAM newTeam The team where the player wants to switch to. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execSwitchTeam(NexgenClient client, int newTeam, out string reason) { local bool bAllowed; // Check if the player is allowed to switch. if (client.bSpectator) { reason = lng.specTeamSwitchMsg; } else if (control.gInf.bNoTeamSwitch) { reason = lng.teamSwitchDisabledMsg; /* } else if ('player has switched too much') { reason = lng.switchLimitReachedMsg; */ } else if (client.bNoTeamSwitch) { reason = lng.playerTeamSwitchDisabledMsg; } else if (control.gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else if ((newTeam < 0) || (level.game.isA('TeamGamePlus') && newTeam >= TeamGamePlus(level.game).maxTeams) || (newTeam > 3)) { reason = lng.invalidTeamMsg; } else if (newTeam == client.player.playerReplicationInfo.team) { reason = lng.format(lng.sameTeamMsg, lng.getTeamName(newTeam)); } else { // All seems to be ok. bAllowed = true; } // Switch team if allowed. if (bAllowed) { client.setTeam(newTeam); } return bAllowed; } /*************************************************************************************************** * * $DESCRIPTION Executes a reconnect as spectator command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execJoinAsSpec(NexgenClient client, out string reason) { local bool bAllowed; local int numSpecs; local NexgenClient currClient; // Count spectators. currClient = control.clientList; while (currClient != none) { if (currClient.bSpectator) { numSpecs++; } currClient = currClient.nextClient; } // Check if the player is allowed to join as a spectator. if (control.sConf.spectatorSlots == 0) { reason = lng.noSpecsAllowedMsg; } else if (numSpecs >= control.sConf.spectatorSlots && !client.bSpectator) { reason = lng.noMoreSpecSlotsMsg; } else { bAllowed = true; } // Reconnect as spectator if allowed. if (bAllowed) { client.reconnect(client.RCN_ReconnectAsSpec); } return bAllowed; } /*************************************************************************************************** * * $DESCRIPTION Executes a reconnect as player command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execJoinAsPlayer(NexgenClient client, out string reason) { local bool bAllowed; // Check if the player is allowed to join as a player. if (!client.hasRight(client.R_MayPlay)) { reason = lng.noPlayingRightsMsg; } else if (client.bSpectator && !control.canGetSlot(client)) { reason = lng.noMorePlayerSlotsMsg; } else if (control.gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else { bAllowed = true; } // Reconnect as player if allowed. if (bAllowed) { client.reconnect(client.RCN_ReconnectAsPlayer); } return bAllowed; } /*************************************************************************************************** * * $DESCRIPTION Executes a balance team command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execBalanceTeams(NexgenClient client, out string reason) { local bool bAllowed; local bool bSuccess; // Check if the player is allowed to balance the teams. if (!level.game.isA('TeamGamePlus')) { reason = lng.notATeamGameMsg; } else if (client.hasRight(client.R_MatchAdmin)) { bAllowed = true; } else if (control.gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else if (control.gInf.bNoTeamBalance) { reason = lng.teamBalanceDisabledMsg; } else { bAllowed = true; } // Balance teams if allowed. if (bAllowed) { bSuccess = control.balanceTeams(); if (bSuccess) { control.broadcastMsg(lng.balanceMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { reason = lng.teamsAlreadyBalancedMsg; } } return bAllowed && bSuccess; } /*************************************************************************************************** * * $DESCRIPTION Executes a start game command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execStartGame(NexgenClient client, out string reason) { local bool bAllowed; if (control.gInf.gameState != control.gInf.GS_Ready) { // bAllowed = false; } else if (control.sConf.matchModeActivated && control.gInf.matchAdminCount > 0 && !client.hasRight(client.R_MatchAdmin)) { // bAllowed = false; } else { bAllowed = true; } if (bAllowed) { control.startGame(); control.broadcastMsg(lng.launchMsg, client.playerName, , , , client.player.playerReplicationInfo); } return true; } /*************************************************************************************************** * * $DESCRIPTION Executes a pause game command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execPauseGame(NexgenClient client, out string reason) { NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)).pauseGame(); return true; } I"/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClientLoginHandler * $VERSION 1.00 (10-3-2008 17:59) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Default login handler for the Nexgen Server Controller. * **************************************************************************************************/ class NexgenClientLoginHandler extends Object abstract; /*************************************************************************************************** * * $DESCRIPTION Retrieves the login parameters for the specified client. * $PARAM clientID Unique client identifier GUID. * $PARAM loginOptions Extra login parameters. * **************************************************************************************************/ static function getLoginParameters(NexgenClient client, out string clientID, out string loginOptions) { local string clientKey; local string password; // Load client key. clientKey = client.gc.get(client.SSTR_ClientKey); clientID = client.gc.get(client.SSTR_ClientID); if (clientKey == "") { // Client has no key. Create a new one. clientKey = class'NexgenUtil'.static.makeKey(); clientID = class'MD5Hash'.static.MD5String(clientKey); client.gc.set(client.SSTR_ClientKey, clientKey); client.gc.set(client.SSTR_ClientID, clientID); client.gc.saveConfig(); } else if (class'MD5Hash'.static.MD5String(clientKey) != clientID) { // KEY/ID mismatch. Reset key. clientKey = class'NexgenUtil'.static.makeKey(); clientID = class'MD5Hash'.static.MD5String(clientKey); client.gc.set(client.SSTR_ClientKey, clientKey); client.gc.set(client.SSTR_ClientID, clientID); client.gc.saveConfig(); } // Load login info. password = client.sc.get(client.serverID, client.SSTR_ServerPassword); // Set login options. if (password != "") class'NexgenUtil'.static.addProperty(loginOptions, client.SSTR_ServerPassword, password); } R /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClientCore * $VERSION 1.26 (9-3-2008 11:12) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Core client controller class that takes care of the basic Nexgen functionality. * **************************************************************************************************/ class NexgenClientCore extends NexgenClientController; var string blockedPlayers[32]; // List containing all clientIDs of blocked players. var bool bBlockAll; // Whether the client doesn't want to receive PMs at all. const openURLCommand = "open"; // Console command for opening an URL. const restartURL = "?restart"; // Server URL for restarting the server. const separator = ","; // Character used to seperate elements in a list. const rebootDelay = 10; // Delay in seconds before the server will be rebooted. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Replicate to client... receivePM, receivePassword; reliable if (role == ROLE_SimulatedProxy) // Replicate to server... setServerSettings, sendPM, addAccountType, updateAccountType ,deleteAccountType, moveAccountType, deleteAccount, updateAccount, addAccount, pauseGame, endGame, restartGame, setPlayerTeam, toggleTeamSwitch, reconnectPlayer, sendPlayerToURL, toggleGlobalTeamSwitch, toggleGlobalTeamBalance, toggleLockedTeams, adminLogin, deleteBan, addBan, updateBan, updateBootControl, separatePlayers, sendPassword, updateMatchSettings, toggleMatchMode, togglePlayerMute, setPlayerName, kickPlayer, showAdminMessage, toggleGlobalMute, toggleGlobalNameChange, banPlayer, setServerSettingsExt1, setServerSettingsExt2, rebootServer, delIgnoredWeapon, saveIgnoredWeapon, delHUDReplacementClass, saveHUDReplacementClass; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified client is blocked by this player. Blocked players * will not be able to send private messages to this client. * $PARAM clientID The identification string of the player which is to be checked. * $REQUIRE clientID != "" * $RETURN True if the specified client is blocked, false if not. * **************************************************************************************************/ simulated function bool isBlocked(string clientID) { local int index; local bool bBlocked; while (!bBlocked && index < arrayCount(blockedPlayers)) { if (blockedPlayers[index] ~= clientID) { bBlocked = true; } else { index++; } } return bBlocked; } /*************************************************************************************************** * * $DESCRIPTION Blocks the specified player. * $PARAM clientID The identification string of the player which is to be blocked. * $REQUIRE clientID != "" * **************************************************************************************************/ simulated function blockPlayer(string clientID) { local int index; local bool bFound; // Cancel if player is blocked already. if (isBlocked(clientID)) { return; } // Find a free slot. while (!bFound && index < arrayCount(blockedPlayers)) { if (blockedPlayers[index] == "") { bFound = true; blockedPlayers[index] = clientID; } else { index++; } } } /*************************************************************************************************** * * $DESCRIPTION Unblocks the specified player. * $PARAM clientID The identification string of the player which is to be unblocked. * $REQUIRE clientID != "" * **************************************************************************************************/ simulated function unblockPlayer(string clientID) { local int index; local bool bFound; // Find a free slot. while (!bFound && index < arrayCount(blockedPlayers)) { if (blockedPlayers[index] ~= clientID) { bFound = true; blockedPlayers[index] = ""; } else { index++; } } } /*************************************************************************************************** * * $DESCRIPTION Modifies the setup of the Nexgen remote control panel. * $OVERRIDE * **************************************************************************************************/ simulated function setupControlPanel() { // Client. client.mainWindow.mainPanel.addPanel(client.lng.clientTabTxt, class'NexgenPanelContainer', "client"); client.mainWindow.mainPanel.addPanel(client.lng.homeTabTxt, class'NexgenRCPHome', , "client"); client.mainWindow.mainPanel.addPanel(client.lng.settingsTabTxt, class'NexgenRCPClientConfig', , "client"); client.mainWindow.mainPanel.addPanel(client.lng.privateMessageTabTxt, class'NexgenRCPPrivateMsg', , "client"); // Match. client.mainWindow.mainPanel.addPanel(client.lng.gameTabTxt, class'NexgenPanelContainer', "game"); if (client.sConf.gameInfoPanelClass != none) { client.mainWindow.mainPanel.addPanel(client.lng.infoTabTxt, client.sConf.gameInfoPanelClass, , "game"); } if (client.hasRight(client.R_Moderate)) { client.mainWindow.mainPanel.addPanel(client.lng.moderatorTabTxt, class'NexgenRCPModerate', , "game"); } if (client.hasRight(client.R_MatchAdmin)) { client.mainWindow.mainPanel.addPanel(client.lng.matchControlTabTxt, class'NexgenRCPMatchControl', , "game"); } if (client.hasRight(client.R_MatchSet)) { client.mainWindow.mainPanel.addPanel(client.lng.matchSetupTabTxt, class'NexgenRCPMatchSet', , "game"); } // Server. client.mainWindow.mainPanel.addPanel(client.lng.serverTabTxt, class'NexgenPanelContainer', "server"); if (client.sConf.serverInfoPanelClass != none) { client.mainWindow.mainPanel.addPanel(client.lng.infoTabTxt, client.sConf.serverInfoPanelClass, , "server"); } if (client.hasRight(client.R_BanOperator)) { client.mainWindow.mainPanel.addPanel(client.lng.banControlTabTxt, class'NexgenRCPBanControl', , "server"); } if (client.hasRight(client.R_AccountManager)) { client.mainWindow.mainPanel.addPanel(client.lng.accountsTabTxt, class'NexgenRCPUserAccounts', , "server"); } if (client.hasRight(client.R_ServerAdmin)) { client.mainWindow.mainPanel.addPanel(client.lng.accountTypesTabTxt, class'NexgenRCPAccountTypes', , "server"); client.mainWindow.mainPanel.addPanel(client.lng.settingsTabTxt, class'NexgenPanelContainer', "serversettings", "server"); client.mainWindow.mainPanel.addPanel("1", class'NexgenRCPServerSettings', , "server,serversettings"); client.mainWindow.mainPanel.addPanel("2", class'NexgenRCPServerSettings2', , "server,serversettings"); client.mainWindow.mainPanel.addPanel("3", class'NexgenRCPServerSettings3', , "server,serversettings"); client.mainWindow.mainPanel.addPanel(client.lng.bootTabTxt, class'NexgenRCPBootControl', , "server,serversettings"); } // About. client.mainWindow.mainPanel.addPanel(client.lng.aboutTabTxt, class'NexgenRCPAbout'); } /*************************************************************************************************** * * $DESCRIPTION Updates the global server settings & notifies the other clients. * $PARAM serverName Name of the server. * $PARAM shortServerName Short name of the server. * $PARAM MOTD1 Message of the day line 1. * $PARAM MOTD2 Message of the day line 2. * $PARAM MOTD3 Message of the day line 3. * $PARAM MOTD4 Message of the day line 4. * $PARAM adminName Name of the server administrator. * $PARAM adminEmail E-Mail address of the server administrator. * $PARAM serverPassword Password for entering the server. * $PARAM adminPassword Password needed to login as server administrator. * $PARAM playerSlots Number of players allowed. * $PARAM vipSlots Amount of extra slots available reserved for VIPs. * $PARAM adminSlots Amount of extra slots available reserved for admins. * $PARAM specSlots How many spectators are allowed during the game. * $PARAM bEnableUplink Indicates if the server should connect to the master server. * **************************************************************************************************/ function setServerSettings(string serverName, string shortServerName, string MOTD1, string MOTD2, string MOTD3, string MOTD4, string adminName, string adminEmail, string serverPassword, string adminPassword, int playerSlots, int vipSlots, int adminSlots, int specSlots, bool bEnableUplink) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Check values. if (serverName == "") serverName = control.sConf.serverName; if (playerSlots < 0) playerSlots = 0; if (vipSlots < 0) vipSlots = 0; if (adminSlots < 0) adminSlots = 0; if (specSlots < 0) specSlots = 0; if (playerSlots > 32) playerSlots = 32; if (vipSlots > 16) vipSlots = 16; if (adminSlots > 16) adminSlots = 16; if (specSlots > 16) specSlots = 16; if (playerSlots + vipSlots + adminSlots <= 0) playerSlots = 16; // Save settings. control.sConf.serverName = serverName; control.sConf.shortName = shortServerName; control.sConf.MOTDLine[0] = MOTD1; control.sConf.MOTDLine[1] = MOTD2; control.sConf.MOTDLine[2] = MOTD3; control.sConf.MOTDLine[3] = MOTD4; control.sConf.adminName = adminName; control.sConf.adminEmail = adminEmail; control.sConf.globalServerPassword = control.sConf.encode(serverPassword); control.sConf.globalAdminPassword = control.sConf.encode(adminPassword); control.sConf.playerSlots = playerSlots; control.sConf.vipSlots = vipSlots; control.sConf.adminSlots = adminSlots; control.sConf.spectatorSlots = specSlots; control.sConf.enableUplink = bEnableUplink; control.sConf.saveConfig(); // Apply settings. level.game.gameReplicationInfo.serverName = serverName; level.game.gameReplicationInfo.shortName = shortServerName; level.game.gameReplicationInfo.MOTDLine1 = MOTD1; level.game.gameReplicationInfo.MOTDLine2 = MOTD2; level.game.gameReplicationInfo.MOTDLine3 = MOTD3; level.game.gameReplicationInfo.MOTDLine4 = MOTD4; level.game.gameReplicationInfo.adminName = adminName; level.game.gameReplicationInfo.adminEmail = adminEmail; consoleCommand("set Engine.GameInfo GamePassword" @ serverPassword); consoleCommand("set Engine.GameInfo AdminPassword" @ adminPassword); level.game.maxPlayers = playerSlots + vipSlots + adminSlots; level.game.maxSpectators = specSlots; if (bEnableUplink) { consoleCommand("set IpServer.UdpServerUplink DoUplink True"); } else { consoleCommand("set IpServer.UdpServerUplink DoUplink False"); } // Notify clients. control.signalConfigUpdate(control.sConf.CT_GlobalServerSettings); client.showMsg(control.lng.settingsSavedMsg); } /*************************************************************************************************** * * $DESCRIPTION Send a private message to the player with the specified player code. Note that the * message may not be send if the client isn't allowed to, e.g. he/she is muted. * $PARAM playerNum Player code of the player that should receive the message. * $PARAM msg The message to send. * $PARAM bWindowed Whether the message to send should popup in a window. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function sendPM(int playerNum, string msg, optional bool bWindowed) { local NexgenClient target; local NexgenClientCore targetCtrl; local string senderTitle; // Check for abuse. if (!client.hasRight(client.R_Moderate) && (client.isMuted() || bWindowed)) { return; } // Locate target player. target = control.getClientByNum(playerNum); // Check if player is allowed to send message when in match mode. Not allowed if: // - match mode is activated and spectators are muted during the match... if (control.sConf.matchModeActivated && control.sConf.muteSpectatorsDuringMatch && // - AND the match is in progress... control.gInf.gameState == control.gInf.GS_Playing && // - AND the player is a spectator and the target player isn't.. client.bSpectator && !target.bSpectator && // - AND the player isn't a moderator or match admin. !client.hasRight(client.R_MatchAdmin) && !client.hasRight(client.R_Moderate)) { return; } // Get sender title. if (client.bHasAccount) { senderTitle = client.title; } // Send private message. if (target != none) { targetCtrl = NexgenClientCore(target.getController(ctrlID)); targetCtrl.receivePM(client.playerID, client.player.playerReplicationInfo, msg, bWindowed, client.hasRight(client.R_Moderate), senderTitle); control.nscLog(client.playerName $ " -> " $ target.playerName $ ": " $ msg, control.LT_PrivateMsg); } } /*************************************************************************************************** * * $DESCRIPTION Send a private message to the player with the specified player code. Note that the * message may not be send if the client isn't allowed to, e.g. he/she is muted. * $PARAM senderID Client ID of the player that has send the message. * $PARAM pri The player replication info actor of the sending player. * $PARAM msg The message that was received. * $PARAM bWindowed Whether the received message should popup in a window. * $PARAM bForced Indicates if the message should be forced (i.e. can't be blocked). * $PARAM senderTitle Title of the player that has send the message. * $REQUIRE senderID != "" && pri != none * **************************************************************************************************/ simulated function receivePM(string senderID, PlayerReplicationInfo pri, string msg, optional bool bWindowed, optional bool bForced, optional string senderTitle) { local NexgenRCPPrivateMsg pmPanel; // Check if message is blocked. if (!bForced && (bBlockAll || isBlocked(senderID))) { return; } // Play PM receive sound. if (client.gc.get(client.SSTR_PlayPMSound, "true") ~= "true") { //client.player.playSound(sound'UnrealShare.TransA3', SLOT_Misc); client.player.clientPlaySound(sound'UnrealShare.TransA3', , true); } // Display message. pmPanel = NexgenRCPPrivateMsg(client.mainWindow.mainPanel.getPanel(class'NexgenRCPPrivateMsg'.default.panelIdentifier)); if (pmPanel != none) { pmPanel.receiveMessage(msg, pri); } if (bWindowed) { client.showPopup("NexgenPrivateMsgDialog", msg, pri.playerName); } else { client.showMsg(client.lng.format(client.lng.receivedPMMsg, pri.playerName, msg, senderTitle), pri); } } /*************************************************************************************************** * * $DESCRIPTION Adds the specified account type to the list. * $PARAM atTypeName Name of the account type to add. * $PARAM atRights Rights assigned to the account type. * $PARAM atTitle Display title of the account type. * $PARAM atPassword Password for the account type. * **************************************************************************************************/ function addAccountType(string atTypeName, string atRights, string atTitle, string atPassword) { local int index; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || control.sConf.atTypeName[arrayCount(control.sConf.atTypeName) - 1] != "") { return; } // Locate a free entry. while (control.sConf.atTypeName[index] != "") { index++; } // Store the account type. if (atTypeName == "") { control.sConf.atTypeName[index] = control.lng.format(control.lng.accountTypeNameStr, index + 1); } else { control.sConf.atTypeName[index] = class'NexgenUtil'.static.trim(atTypeName); } control.sConf.atRights[index] = atRights; control.sConf.atTitle[index] = class'NexgenUtil'.static.trim(atTitle); control.sConf.atPassword[index] = control.sConf.encode(atPassword); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); } /*************************************************************************************************** * * $DESCRIPTION Updates the account type info for the specified account type. * $PARAM accountTypeNum The index of the account type that is to be updated. * $PARAM atTypeName Name of the account type to update. * $PARAM atRights Rights assigned to the account type. * $PARAM atTitle Display title of the account type. * $PARAM atPassword Password for the account type. * **************************************************************************************************/ function updateAccountType(byte accountTypeNum, string atTypeName, string atRights, string atTitle, string atPassword) { // Preliminary checks. if (!client.hasRight(client.R_AccountManager)) { return; } // Store the account type. if (atTypeName != "") { control.sConf.atTypeName[accountTypeNum] = class'NexgenUtil'.static.trim(atTypeName); } control.sConf.atRights[accountTypeNum] = atRights; control.sConf.atTitle[accountTypeNum] = class'NexgenUtil'.static.trim(atTitle); control.sConf.atPassword[accountTypeNum] = control.sConf.encode(atPassword); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); updatePlayerTitles(); } /*************************************************************************************************** * * $DESCRIPTION Updates the titles of each player. Iterates over the player list and checks * whether the title of a player has changed. In case it has changed all players * will be notified. * **************************************************************************************************/ function updatePlayerTitles() { local NexgenClient c; local string newTitle; local int accountNum; // For each client... for (c = control.clientList; c != none; c = c.nextClient) { // Get new title... accountNum = control.sConf.getUserAccountIndex(c.playerID); if (accountNum < 0) { if (c.bSpectator) { newTitle = control.lng.specTitle; } else { newTitle = control.sConf.getAccountTypeTitle(0); } } else { newTitle = control.sConf.getUserAccountTitle(accountNum); } // Compare with old title & update if necessary. if (c.title != newTitle) { c.title = newTitle; control.announcePlayerAttrChange(c, c.PA_Title, newTitle); } } } /*************************************************************************************************** * * $DESCRIPTION Deletes the specified account type from the list. * $PARAM accountTypeNum The index of the account type that is to be deleted. * **************************************************************************************************/ function deleteAccountType(byte accountTypeNum) { local bool userAccountsChanged; local int index; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || accountTypeNum >= arrayCount(control.sConf.atTypeName) || accountTypeNum < 1 || control.sConf.atTypeName[accountTypeNum] == "") { return; } // Update user account entries. while (index < arrayCount(control.sConf.paPlayerID) && control.sConf.paPlayerID[index] != "") { // Update account info? if (control.sConf.paAccountType[index] == accountTypeNum) { // User has the deleted account type, give him/her the default account. control.sConf.paAccountType[index] = 0; } else if (control.sConf.paAccountType[index] >= accountTypeNum) { // User has an account type num above the deleted account, update account type num. control.sConf.paAccountType[index] = control.sConf.paAccountType[index] - 1; } // Continue with next account. index++; } // Update account type entries. index = accountTypeNum; while (index < arrayCount(control.sConf.atTypeName) && control.sConf.atTypeName[index] != "") { // Last entry? if (index + 1 == arrayCount(control.sConf.atTypeName)) { // Yes, clear all fields. control.sConf.atTypeName[index] = ""; control.sConf.atRights[index] = ""; control.sConf.atTitle[index] = ""; control.sConf.atPassword[index] = ""; } else { // No, copy from next entry. control.sConf.atTypeName[index] = control.sConf.atTypeName[index + 1]; control.sConf.atRights[index] = control.sConf.atRights[index + 1]; control.sConf.atTitle[index] = control.sConf.atTitle[index + 1]; control.sConf.atPassword[index] = control.sConf.atPassword[index + 1]; } // Continue with next account type. index++; } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); if (userAccountsChanged) { control.signalConfigUpdate(control.sConf.CT_UserAccounts); } } /*************************************************************************************************** * * $DESCRIPTION Moves the specified account type one position in the list. * $PARAM accountTypeNum The index of the account type that is to be moved. * $PARAM bDown Whether the item is to be moved up or down. * **************************************************************************************************/ function moveAccountType(byte accountTypeNum, bool bDown) { local int index; local bool userAccountsChanged; local int oldPosition; local int newPosition; local string temp; // Preliminary checks. Fails if: // - The client doesn't have the rights to manage to accounts. if (!client.hasRight(client.R_ServerAdmin) || // - accountTypeNum higher then the size of the list accountTypeNum >= arrayCount(control.sConf.atTypeName) || // - There is no account type stored at the specified index. control.sConf.atTypeName[accountTypeNum] == "" || // - The account type is moved down, but it already is the last used entry. bDown && (control.sConf.atTypeName[accountTypeNum + 1] == "" || accountTypeNum + 1 >= arrayCount(control.sConf.atTypeName)) || // - The account type is moved up, but it is either the first or second entry. !bDown && accountTypeNum < 2) { return; } // Determine positions. oldPosition = accountTypeNum; if (bDown) { newPosition = accountTypeNum + 1; } else { newPosition = accountTypeNum - 1; } // Update user account entries. while (index < arrayCount(control.sConf.paPlayerID) && control.sConf.paPlayerID[index] != "") { // Check if account type number should be changed. if (control.sConf.paAccountType[index] == oldPosition) { control.sConf.paAccountType[index] = newPosition; userAccountsChanged = true; } else if (control.sConf.paAccountType[index] == newPosition) { control.sConf.paAccountType[index] = oldPosition; userAccountsChanged = true; } // Continue with next account. index++; } // Update account type entries. temp = control.sConf.atTypeName[oldPosition]; control.sConf.atTypeName[oldPosition] = control.sConf.atTypeName[newPosition]; control.sConf.atTypeName[newPosition] = temp; temp = control.sConf.atRights[oldPosition]; control.sConf.atRights[oldPosition] = control.sConf.atRights[newPosition]; control.sConf.atRights[newPosition] = temp; temp = control.sConf.atTitle[oldPosition]; control.sConf.atTitle[oldPosition] = control.sConf.atTitle[newPosition]; control.sConf.atTitle[newPosition] = temp; temp = control.sConf.atPassword[oldPosition]; control.sConf.atPassword[oldPosition] = control.sConf.atPassword[newPosition]; control.sConf.atPassword[newPosition] = temp; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); if (userAccountsChanged) { control.signalConfigUpdate(control.sConf.CT_UserAccounts); } } /*************************************************************************************************** * * $DESCRIPTION Deletes the specified account. * $PARAM accountNum The index number of the account to delete. * **************************************************************************************************/ function deleteAccount(byte accountNum) { local int index; local NexgenClient c; // Preliminary checks. if (!client.hasRight(client.R_AccountManager) || accountNum < 0 || accountNum >= arrayCount(control.sConf.paPlayerID) || control.sConf.paPlayerID[accountNum] == "") { return; } // Kill the players client if he/she is online. c = control.getClientByID(control.sConf.paPlayerID[accountNum]); if (c != none) { c.showPopup("NexgenAccountUpdatedDialog"); c.player.destroy(); } // Update user account entries. for (index = accountNum; index < arrayCount(control.sConf.paPlayerID); index++) { // Last entry? if (index + 1 == arrayCount(control.sConf.paPlayerID)) { // Yes, clear all fields. control.sConf.paPlayerID[index] = ""; control.sConf.paPlayerName[index] = ""; control.sConf.paAccountType[index] = 0; control.sConf.paCustomRights[index] = ""; control.sConf.paCustomTitle[index] = ""; } else { control.sConf.paPlayerID[index] = control.sConf.paPlayerID[index + 1]; control.sConf.paPlayerName[index] = control.sConf.paPlayerName[index + 1]; control.sConf.paAccountType[index] = control.sConf.paAccountType[index + 1]; control.sConf.paCustomRights[index] = control.sConf.paCustomRights[index + 1]; control.sConf.paCustomTitle[index] = control.sConf.paCustomTitle[index + 1]; } } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); } /*************************************************************************************************** * * $DESCRIPTION Updates the specified account. * $PARAM accountNum The index number of the account to update. * $PARAM accountName Name of the account user. * $PARAM accountType The type of account assigned to the user. * $PARAM customRights Rights string for a custom account type. * $PARAM customTitle Custom account title. * **************************************************************************************************/ function updateAccount(byte accountNum, string accountName, int accountType, string customRights, string customTitle) { local NexgenClient c; // Preliminary checks. Fails if: // - The client doesn't have the rights to manage to accounts. if (!client.hasRight(client.R_AccountManager) || // - An invalid account number is specified. accountNum < 0 || accountNum >= arrayCount(control.sConf.paPlayerID) || // - The account doesn't exist. control.sConf.paPlayerID[accountNum] == "" || // - No account name was specified. class'NexgenUtil'.static.trim(accountName) == "" || // - An invalid account type number is specified. accountType < -1 || accountType >= arrayCount(control.sConf.atTypeName) || // - The account type isn't used. accountType != -1 && control.sConf.atTypeName[accountType] == "" || // - A custom account is used, but no custom title was set. accountType == -1 && class'NexgenUtil'.static.trim(customTitle) == "") { return; } // Kill the players client if he/she is online. c = control.getClientByID(control.sConf.paPlayerID[accountNum]); if (c != none) { c.showPopup("NexgenAccountUpdatedDialog"); c.player.destroy(); } // Update account. control.sConf.paPlayerName[accountNum] = class'NexgenUtil'.static.trim(accountName); control.sConf.paAccountType[accountNum] = accountType; if (accountType < 0) { control.sConf.paCustomRights[accountNum] = customRights; control.sConf.paCustomTitle[accountNum] = customTitle; } else { control.sConf.paCustomRights[accountNum] = ""; control.sConf.paCustomTitle[accountNum] = ""; } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); } /*************************************************************************************************** * * $DESCRIPTION Creates a new user account. * $PARAM accountNum The index number of the account to update. * $PARAM accountName Name of the account user. * $PARAM accountType The type of account assigned to the user. * $PARAM customRights Rights string for a custom account type. * $PARAM customTitle Custom account title. * **************************************************************************************************/ function addAccount(string clientID, string accountName, int accountType, string customRights, string customTitle) { local NexgenClient c; local int index; local bool bFound; local bool bAlreadyHasAccount; // Preliminary checks. Fails if: // - The client doesn't have the rights to manage to accounts. if (!client.hasRight(client.R_AccountManager) || // - No account name was specified. class'NexgenUtil'.static.trim(accountName) == "" || // - An invalid account type number is specified. accountType < -1 || accountType >= arrayCount(control.sConf.atTypeName) || // - The account type isn't used. accountType != -1 && control.sConf.atTypeName[accountType] == "" || // - A custom account is used, but no custom title was set. accountType == -1 && class'NexgenUtil'.static.trim(customTitle) == "") { return; } // Get free user account slot. while (!bFound && !bAlreadyHasAccount && index < arrayCount(control.sConf.paPlayerID)) { if (control.sConf.paPlayerID[index] == "") { bFound = true; } else if (control.sConf.paPlayerID[index] ~= clientID) { bAlreadyHasAccount = true; } else { index++; } } // Cancel on error. if (!bFound || bAlreadyHasAccount) { return; } // Kill the players client if he/she is online. c = control.getClientByID(clientID); if (c != none) { c.showPopup("NexgenAccountUpdatedDialog"); c.player.destroy(); } // Add account. control.sConf.paPlayerID[index] = clientID; control.sConf.paPlayerName[index] = accountName; control.sConf.paAccountType[index] = accountType; if (accountType < 0) { control.sConf.paCustomRights[index] = customRights; control.sConf.paCustomTitle[index] = customTitle; } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); } /*************************************************************************************************** * * $DESCRIPTION Pauses the game. If the game is already paused when this function is called the * game will be resumed. * **************************************************************************************************/ function pauseGame() { local bool bGameIsPaused; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || control.gInf.gameState != control.gInf.GS_Playing) { return; } // Check if game is currently paused. bGameIsPaused = level.pauser != ""; // Pause / resume game. if (bGameIsPaused) { level.pauser = ""; } else { level.pauser = client.playerName; } bGameIsPaused = !bGameIsPaused; // Announce event. if (bGameIsPaused) { control.broadcastMsg(control.lng.adminPauseGameMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminResumeGameMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Ends the current game. * **************************************************************************************************/ function endGame() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || control.gInf.gameState != control.gInf.GS_Playing) { return; } // End the game. level.pauser = ""; // Make sure the game doesn't remain paused. control.forceEndGame(); // Announce event. control.broadcastMsg(control.lng.adminStopGameMsg, client.playerName, , , , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Restarts the current game. * **************************************************************************************************/ function restartGame() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Restart. level.serverTravel(restartURL, false); // Announce event. control.broadcastMsg(control.lng.adminRestartGameMsg, client.playerName, , , , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Changes the team of the specified player. * $PARAM playerNum The player that is to be switched to another team. * $PARAM newTeam The number of the team where to player should be switched to. * **************************************************************************************************/ function setPlayerTeam(int playerNum, int newTeam) { local NexgenClient target; // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. Fails if: // - The player doesn't have match admin rights. if (!client.hasRight(client.R_MatchAdmin) || // - An invalid player number was specified. target == none || // - The game isn't a team game. !level.game.gameReplicationInfo.bTeamGame || // - An invalid team number was specified. newTeam < 0 || newTeam >= 4 || // - A non existing team was specified. level.game.isA('TeamGamePlus') && newTeam >= TeamGamePlus(level.game).maxTeams || // - The target player is already on the specified team. target.team == newTeam) { return; } // Switch player. target.setTeam(newTeam); // Announce event. control.broadcastMsg(control.lng.adminTeamSwitchMsg, client.playerName, target.playerName, control.lng.getTeamName(newTeam), , target.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Enables or disables team switching for the specified player. * $PARAM playerNum The player code of the player whose team switch right is to be changed. * **************************************************************************************************/ function toggleTeamSwitch(int playerNum) { local NexgenClient target; // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || target == none) { return; } // Toggle team switch on or off. target.bNoTeamSwitch = !target.bNoTeamSwitch; // Announce event. if (target.bNoTeamSwitch) { control.broadcastMsg(control.lng.adminPlayerTeamSwitchDisableMsg, client.playerName, target.playerName, , , target.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminPlayerTeamSwitchEnableMsg, client.playerName, target.playerName, , , target.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Reconnects the specified player. * $PARAM playerNum The player code of the player that is to be reconnected. * $PARAM bSpec Whether the player should be reconnected as spectator. * **************************************************************************************************/ function reconnectPlayer(int playerNum, bool bSpec) { local NexgenClient target; // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || target == none || target.bSpectator == bSpec) { return; } // Reconnect player. if (bSpec) { target.reconnect(target.RCN_ReconnectAsSpec); } else { control.giveJoinOverrideCode(target); target.reconnect(target.RCN_ReconnectAsPlayer); } // Announce event. if (bSpec) { control.broadcastMsg(control.lng.adminReconnectAsSpecMsg, client.playerName, target.playerName, , , target.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminReconnectAsPlayerMsg, client.playerName, target.playerName, , , target.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Sends the specified player to the target URL. * $PARAM playerNum The player which is to be send to the specified URL. * $PARAM url The target url string. * **************************************************************************************************/ function sendPlayerToURL(int playerNum, string url) { local NexgenClient target; local string fURL; // Format parameter. fURL = class'NexgenUtil'.static.trim(url); // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || target == none || fURL == "") { return; } // Send to url. target.clientCommand(openURLCommand @ fURL); // Announce event. control.broadcastMsg(control.lng.adminSendToURLMsg, client.playerName, target.playerName, fURL, , target.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team switching for all players in the current game. * **************************************************************************************************/ function toggleGlobalTeamSwitch() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bNoTeamSwitch = !control.gInf.bNoTeamSwitch; // Announce event. control.signalGameInfoUpdate(client.IT_GlobalRights); if (control.gInf.bNoTeamSwitch) { control.broadcastMsg(control.lng.adminDisableTeamSwitchMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminEnableTeamSwitchMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team balancing for all players in the current game. * **************************************************************************************************/ function toggleGlobalTeamBalance() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bNoTeamBalance = !control.gInf.bNoTeamBalance; // Announce event. control.signalGameInfoUpdate(client.IT_GlobalRights); if (control.gInf.bNoTeamBalance) { control.broadcastMsg(control.lng.adminDisableTeamBalanceMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminEnableTeamBalanceMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Locks / unlocks the teams for the current game. * **************************************************************************************************/ function toggleLockedTeams() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bTeamsLocked = !control.gInf.bTeamsLocked; // Announce event. control.signalGameInfoUpdate(client.IT_GlobalRights); if (control.gInf.bTeamsLocked) { control.broadcastMsg(control.lng.adminLockTeamsMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminUnlockTeamsMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Attempts to give the player an account. The account given to the player depends on * the password that was specified. It can either be the server admin password * (Engine.GameInfo.AdminPassword) or one of the nexgen account type passwords. * $PARAM password The administrator password. * **************************************************************************************************/ function adminLogin(string password) { local bool bValidPassword; local bool bRootAdmin; local int index; local byte accountType; // Check password. if (password == "") { // You wish it was that easy! } if (password == control.sConf.decode(control.sConf.globalAdminPassword)) { // Server root admin password. bValidPassword = true; bRootAdmin = true; } else { // Check with account types. while (!bValidPassword && index < arrayCount(control.sConf.atTypeName) && control.sConf.atTypeName[index] != "") { if (control.sConf.atPassword[index] != "" && control.sConf.decode(control.sConf.atPassword[index]) == password) { // Account type password match. bValidPassword = true; accountType = index; } else { // Passwords do not match, continue with next account type. index++; } } } // Valid password entered? if (bValidPassword) { // Yes, add / update user account. if (bRootAdmin) { index = control.sConf.addUserAccount(client.playerID, client.playerName, -1, control.sConf.getAllRights(), control.lng.rootAdminTitle); } else { index = control.sConf.addUserAccount(client.playerID, client.playerName, accountType); } // Account successfully created / updated? if (index >= 0) { // Yes. // Announce event. control.broadcastMsg(control.lng.adminLoginMsg, client.playerName, control.sConf.getUserAccountTitle(index), , , client.player.playerReplicationInfo); // Disconnect client. client.showPopup("NexgenAccountUpdatedDialog"); client.player.destroy(); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); } } else { // No, notify client. client.showMsg(control.lng.invalidPasswordMsg); } } /*************************************************************************************************** * * $DESCRIPTION Deletes the specified entry from the banlist. * $PARAM entryNum Entry number of the ban to delete. * **************************************************************************************************/ function deleteBan(byte entryNum) { local string deletedBan; local int index; // Preliminary checks. if (!client.hasRight(client.R_BanOperator) || entryNum >= arrayCount(control.sConf.bannedName) || control.sConf.bannedName[entryNum] == "") { return; } // Remove ban. deletedBan = control.sConf.bannedName[entryNum]; control.sConf.removeBan(entryNum); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); // Announce event. control.broadcastMsg(control.lng.adminDeleteBanMsg, client.playerName, deletedBan, , , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Adds specified ban to the banlist. * $PARAM playerName Name of the player that is banned. * $PARAM ipList List of banned ip addresses. * $PARAM idList List of banned client id's. * $PARAM banReason The reason why this player was banned. * $PARAM banPeriod Describes the period for which the player is banned. * **************************************************************************************************/ function addBan(string playerName, string ipList, string idList, string banReason, string banPeriod) { local byte entryNum; local bool bFound; // Preliminary checks. if (!client.hasRight(client.R_BanOperator) || class'NexgenUtil'.static.trim(playerName) == "") { return; } // Find a free slot. while (!bFound && entryNum < arrayCount(control.sConf.bannedName)) { if (control.sConf.bannedName[entryNum] == "") { bFound = true; } else { entryNum++; } } // Cancel on error. if (!bFound) { return; } // Store ban. control.sConf.bannedName[entryNum] = playerName; control.sConf.bannedIPs[entryNum] = ipList; control.sConf.bannedIDs[entryNum] = idList; control.sConf.banReason[entryNum] = banReason; control.sConf.banPeriod[entryNum] = banPeriod; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); // Announce event. control.broadcastMsg(control.lng.adminAddBanMsg, client.playerName, playerName, , , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Updates the specified ban entry. * $PARAM entryNum The entry in the banlist that is to be updated. * $PARAM playerName Name of the player that is banned. * $PARAM ipList List of banned ip addresses. * $PARAM idList List of banned client id's. * $PARAM banReason The reason why this player was banned. * $PARAM banPeriod Describes the period for which the player is banned. * **************************************************************************************************/ function updateBan(byte entryNum, string playerName, string ipList, string idList, string banReason, string banPeriod) { // Preliminary checks. if (!client.hasRight(client.R_BanOperator) || class'NexgenUtil'.static.trim(playerName) == "" || entryNum >= arrayCount(control.sConf.bannedName) || control.sConf.bannedName[entryNum] == "") { return; } // Store ban. control.sConf.bannedName[entryNum] = playerName; control.sConf.bannedIPs[entryNum] = ipList; control.sConf.bannedIDs[entryNum] = idList; control.sConf.banReason[entryNum] = banReason; control.sConf.banPeriod[entryNum] = banPeriod; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); // Announce event. /* control.broadcastMsg(control.lng.adminUpdateBanMsg, client.playerName, playerName, , , client.player.playerReplicationInfo); */ } /*************************************************************************************************** * * $DESCRIPTION Updates the boot control settings. * $PARAM bEnable Whether nexgen boot control should be used. * $PARAM bLoadLastMap Indicates if the game before the crash/reboot is to be loaded. * $PARAM gameType The class of the game type used to boot the server with. * $PARAM mutators List of mutator classes that are loaded when the server gets booted. * $PARAM mapPrefix Map prefix of the levels to load. * $PARAM extraOptions Extra command line options for the nexgen server boot. * $PARAM bootCommands Console commands to execute before the server is booted. * **************************************************************************************************/ function updateBootControl(bool bEnable, bool bLoadLastMap, int gameType, string mutators, string mapPrefix, string extraOptions, string bootCommands) { local string gameClass; local string mutatorClass; local string remaining; local string indexStr; local string temp; local int index; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Store new settings. control.sConf.enableBootControl = bEnable; control.sConf.restartOnLastGame = bLoadLastMap; if (0 <= gameType && gameType < arrayCount(client.sConf.gameTypeInfo)) { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[gameType], gameClass, remaining); control.sConf.bootGameType = gameClass; } else { control.sConf.bootGameType = ""; } control.sConf.bootMapPrefix = mapPrefix; control.sConf.bootMutatorIndices = mutators; control.sConf.bootMutators = ""; remaining = mutators; while (remaining != "") { class'NexgenUtil'.static.split(remaining, indexStr, remaining); index = int(indexStr); if (0 <= index && index < arrayCount(client.sConf.mutatorInfo)) { class'NexgenUtil'.static.split(client.sConf.mutatorInfo[index], mutatorClass, temp); if (control.sConf.bootMutators == "") { control.sConf.bootMutators = mutatorClass; } else { control.sConf.bootMutators = control.sConf.bootMutators $ separator $ mutatorClass; } } } control.sConf.bootOptions = extraOptions; control.sConf.bootCommands = bootCommands; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BootControl); client.showMsg(control.lng.settingsSavedMsg); } /*************************************************************************************************** * * $DESCRIPTION Separates players by the specified tags. * $PARAM teamTags The tags used for each team. * **************************************************************************************************/ function separatePlayers(string teamTags[4]) { local NexgenClient c; local int index; local bool bFound; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || !level.game.isA('TeamGamePlus')) { return; } // Switch players. for (c = control.clientList; c != none; c = c.nextClient) { if (!c.bSpectator) { index = 0; bFound = false; while (!bFound && index < arrayCount(control.sConf.tagsToSeparate) && index < TeamGamePlus(level.game).maxTeams) { // Check if player has a tag that separates him/her. if (instr(caps(c.playerName), caps(control.sConf.tagsToSeparate[index])) >= 0) { bFound = true; if (c.player.playerReplicationInfo.team != index) { c.setTeam(index); } } else { index++; } } } } } /*************************************************************************************************** * * $DESCRIPTION Sends the match password to the specified player. * $PARAM targetPlayer Player code of the player that is to receive the password. In case * -1 is passed the password will be send to all players. * $PARAM password The match password that is to be send. * **************************************************************************************************/ function sendPassword(int targetPlayer, optional string password) { local string pwToSend; local NexgenClient target; local NexgenClientCore targetCtrl; local string pwReceivedMsg; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Get password. if (password == "") { pwToSend = control.sConf.decode(control.sConf.serverPassword); } else { pwToSend = password; } // Send password. if (targetPlayer >= 0) { target = control.getClientByNum(targetPlayer); targetCtrl = NexgenClientCore(target.getController(ctrlID)); targetCtrl.receivePassword(pwToSend); } else { for (target = control.clientList; target != none; target = target.nextClient) { targetCtrl = NexgenClientCore(target.getController(ctrlID)); targetCtrl.receivePassword(pwToSend); } } } /*************************************************************************************************** * * $DESCRIPTION Send the specified match password to the client. * $PARAM password The password to send. * **************************************************************************************************/ simulated function receivePassword(string password) { client.sc.visitServer(client.serverID); client.sc.set(client.serverID, client.SSTR_ServerPassword, password); client.sc.saveConfig(); client.showMsg(client.lng.format(client.lng.receivedPWMsg, password)); } /*************************************************************************************************** * * $DESCRIPTION Updates the match settings * $PARAM matchesToPlay Number of matches to play. * $PARAM currentMatch Current match number. * $PARAM serverPassword The password needed to enter the match. * $PARAM spectatorsNeedPassword Whether spectators need to enter the password. * $PARAM muteSpectatorsDuringMatch Are spectators allowed to chat during the match? * $PARAM enableMatchBootControl Use Nexgen boot control during the match. * $PARAM matchAutoLockTeams Whether the teams are automatically locked when the * match begins. * $PARAM matchAutoPause Automatically pause the game when a player leaves? * $PARAM matchAutoSeparate Automatically separate players in teams? * $PARAM teamTags The tags used to separate players in teams. * **************************************************************************************************/ function updateMatchSettings(int matchesToPlay, int currentMatch, string serverPassword, bool spectatorsNeedPassword, bool muteSpectatorsDuringMatch, bool enableMatchBootControl, bool matchAutoLockTeams, bool matchAutoPause, bool matchAutoSeparate, string teamTag0, string teamTag1, string teamTag2, string teamTag3) { local int index; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Validate parameters. matchesToPlay = max(1, matchesToPlay); currentMatch = clamp(currentMatch, 1, matchesToPlay); // Save settings. control.sConf.matchesToPlay = matchesToPlay; control.sConf.currentMatch = currentMatch; control.sConf.serverPassword = control.sConf.encode(serverPassword); control.sConf.spectatorsNeedPassword = spectatorsNeedPassword; control.sConf.muteSpectatorsDuringMatch = muteSpectatorsDuringMatch; control.sConf.enableMatchBootControl = enableMatchBootControl; control.sConf.matchAutoLockTeams = matchAutoLockTeams; control.sConf.matchAutoPause = matchAutoPause; control.sConf.matchAutoSeparate = matchAutoSeparate; /* lame... looks like another replication issue for (index = 0; index < arrayCount(teamTags); index++) { control.sConf.tagsToSeparate[index] = teamTags[index]; } */ control.sConf.tagsToSeparate[0] = teamTag0; control.sConf.tagsToSeparate[1] = teamTag1; control.sConf.tagsToSeparate[2] = teamTag2; control.sConf.tagsToSeparate[3] = teamTag3; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_MatchSettings); client.showMsg(control.lng.settingsSavedMsg); } /*************************************************************************************************** * * $DESCRIPTION Turns the match mode on or off. * **************************************************************************************************/ function toggleMatchMode() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Save settings. control.sConf.matchModeActivated = !control.sConf.matchModeActivated; control.sConf.saveConfig(); // (Un)lock teams? if (control.sConf.matchAutoLockTeams && control.gInf.gameState == control.gInf.GS_Playing && control.gInf.bTeamsLocked != control.sConf.matchModeActivated) { control.gInf.bTeamsLocked = control.sConf.matchModeActivated; control.signalGameInfoUpdate(client.IT_GlobalRights); } // Notify clients. control.signalConfigUpdate(control.sConf.CT_MatchSettings); if (control.sConf.matchModeActivated) { control.broadcastMsg(control.lng.adminEnableMatchModeMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminDisableMatchModeMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Mutes / unmutes the specified player. * $PARAM playerNum The player code of the player that is to be (un)muted. * **************************************************************************************************/ function togglePlayerMute(int playerNum) { local NexgenClient target; // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Mute target player. target.bMuted = !target.bMuted; // Announce event. if (target.bMuted) { control.broadcastMsg(control.lng.adminMutePlayerMsg, client.playerName, target.playerName , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminUnmutePlayerMsg, client.playerName, target.playerName , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Changes the name of the specified player. * $PARAM playerNum The player code of the player whose name is to be changed. * $PARAM newName New name for the player. * **************************************************************************************************/ function setPlayerName(int playerNum, string newName) { local NexgenClient target; local string oldName; newName = class'NexgenUtil'.static.trim(newName); // Preliminary checks. if (!client.hasRight(client.R_Moderate) || newName == "") { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Change name. oldName = target.playerName; target.changeName(newName); // Announce event. control.broadcastMsg(control.lng.adminSetNameMsg, client.playerName, oldName, newName, , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Kicks the specified player from the server. * $PARAM playerNum The player code of the player the player that is to be kicked. * $PARAM reason Description of why the player was kicked. * **************************************************************************************************/ function kickPlayer(int playerNum, string reason) { local NexgenClient target; // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Kick player. target.showPopup("NexgenJustBannedDialog", reason, "-"); target.player.destroy(); // Announce event. control.broadcastMsg(control.lng.adminKickPlayerMsg, client.playerName, target.playerName, , , client.player.playerReplicationInfo); } /*************************************************************************************************** * * $DESCRIPTION Bans the specified player from the server. * $PARAM playerNum The player code of the player the player that is to be banned. * $PARAM banPeriodType The type of period for which the player is banned. 1 means x * matches and 2 means x days, where x is specified by the * banPeriodArgs argument. Any other value means the player is banned * forever. * $PARAM banPeriodArgs Optional argument for the ban period type. * $PARAM reason Description of why the player was banned. * **************************************************************************************************/ function banPlayer(int playerNum, byte banPeriodType, int banPeriodArgs, string reason) { local NexgenClient target; local string banPeriod; local string banPeriodDesc; local int year, month, day, hour, minute; local byte entryNum; local bool bFound; // Preliminary checks. if (!client.hasRight(client.R_Moderate) || !client.hasRight(client.R_BanOperator)) { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Get ban period. if (banPeriodType == control.sConf.BP_Matches) { banPeriod = "M" $ max(1, banPeriodArgs); } else if (banPeriodType == control.sConf.BP_UntilDate) { year = level.year; month = level.month; day = level.day; hour = level.hour; minute = level.minute; class'NexgenUtil'.static.computeDate(max(1, banPeriodArgs), year, month, day); banPeriod = "U" $ class'NexgenUtil'.static.serializeDate(year, month, day, hour, minute); } banPeriodDesc = control.lng.getBanPeriodDescription(banPeriod); // Kick player from the server. target.showPopup("NexgenJustBannedDialog", reason, banPeriodDesc); target.player.destroy(); // Announce event. control.broadcastMsg(control.lng.adminBanPlayerMsg, client.playerName, target.playerName, , , client.player.playerReplicationInfo); // Find a free slot in the ban list. while (!bFound && entryNum < arrayCount(control.sConf.bannedName)) { if (control.sConf.bannedName[entryNum] == "") { bFound = true; } else { entryNum++; } } // Cancel on error. if (!bFound) { return; } // Store ban. control.sConf.bannedName[entryNum] = target.playerName; control.sConf.bannedIPs[entryNum] = target.ipAddress; control.sConf.bannedIDs[entryNum] = target.playerID; control.sConf.banReason[entryNum] = reason; control.sConf.banPeriod[entryNum] = banPeriod; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); } /*************************************************************************************************** * * $DESCRIPTION Shows a special administrator message. * $PARAM msg The message to display. * **************************************************************************************************/ function showAdminMessage(string msg) { local Pawn p; // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Show message. for (p = level.pawnList; p != none; p = p.nextPawn) { if(p.isA('PlayerPawn')) { PlayerPawn(p).clearProgressMessages(); PlayerPawn(p).setProgressTime(6); PlayerPawn(p).setProgressMessage(msg, 0); } } } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team switching for all players in the current game. * **************************************************************************************************/ function toggleGlobalMute() { // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Update setting. control.gInf.bMuteAll = !control.gInf.bMuteAll; // Announce event. control.signalGameInfoUpdate(client.IT_GlobalRights); if (control.gInf.bMuteAll) { control.broadcastMsg(control.lng.adminMuteAllMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminUnmuteAllMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team switching for all players in the current game. * **************************************************************************************************/ function toggleGlobalNameChange() { // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Update setting. control.gInf.bNoNameChange = !control.gInf.bNoNameChange; // Announce event. control.signalGameInfoUpdate(client.IT_GlobalRights); if (control.gInf.bNoNameChange) { control.broadcastMsg(control.lng.adminDisableNameChangeMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { control.broadcastMsg(control.lng.adminEnableNameChangeMsg, client.playerName, , , , client.player.playerReplicationInfo); } } /*************************************************************************************************** * * $DESCRIPTION Updates the first part of the extra server settings. * $PARAM autoUpdateBans Automatically update ban entries? * $PARAM removeExpiredBans Automatically remove expired ban entries? * $PARAM broadcastTeamKillAttempts Whether or not to show team kill messages. * $PARAM useNexgenMessageHUD Enable the Nexgen message HUD on this server? * $PARAM logEvents Write Nexgen events messages to the server log? * $PARAM logSystemMessages Write system messages to the server log? * $PARAM logChatMessages Write chat messages to the server log? * $PARAM logPrivateMessages Write private messages to the server log? * $PARAM allowTeamSwitch Whether team switching is allowed by default. * $PARAM allowTeamBalance Whether team balancing is allowed by default. * $PARAM allowNameChange Whether name changing is allowed by default. * **************************************************************************************************/ function setServerSettingsExt1(bool autoUpdateBans, bool removeExpiredBans, bool broadcastTeamKillAttempts, bool useNexgenMessageHUD, bool logEvents, bool logSystemMessages, bool logChatMessages, bool logPrivateMessages, bool allowTeamSwitch, bool allowTeamBalance, bool allowNameChange) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Save settings. control.sConf.autoUpdateBans = autoUpdateBans; control.sConf.removeExpiredBans = removeExpiredBans; control.sConf.broadcastTeamKillAttempts = broadcastTeamKillAttempts; control.sConf.useNexgenMessageHUD = useNexgenMessageHUD; control.sConf.logEvents = logEvents; control.sConf.logSystemMessages = logSystemMessages; control.sConf.logChatMessages = logChatMessages; control.sConf.logPrivateMessages = logPrivateMessages; control.sConf.allowTeamSwitch = allowTeamSwitch; control.sConf.allowTeamBalance = allowTeamBalance; control.sConf.allowNameChange = allowNameChange; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExtraServerSettings); } /*************************************************************************************************** * * $DESCRIPTION Updates the second part of the extra server settings. * $PARAM waitTime Time to wait before the game can be started. * $PARAM startTime Game starting countdown time. * $PARAM autoReconnectTime Time to wait before automatically reconnecting. * $PARAM maxIdleTime Maximum time a player can be idle on the server. * $PARAM spawnProtectionTime How long the player remains protected after spawning. * $PARAM teamKillDamageProtectionTime How long the player remains damage protected after a tk. * $PARAM teamKillPushProtectionTime How long the player remains push protected after a tk. * $PARAM autoDisableMatchTime Time to wait before disabling match mode. * **************************************************************************************************/ function setServerSettingsExt2(byte waitTime, byte startTime, byte autoReconnectTime, int maxIdleTime, int maxIdleTimeCP, byte spawnProtectionTime, byte teamKillDamageProtectionTime, byte teamKillPushProtectionTime, byte autoDisableMatchTime) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Check values. waitTime = clamp(waitTime, 0, 60); startTime = clamp(startTime, 0, 30); autoReconnectTime = clamp(autoReconnectTime, 0, 60); maxIdleTime = clamp(maxIdleTime, 0, 9999); maxIdleTimeCP = clamp(maxIdleTimeCP, 0, 9999); spawnProtectionTime = clamp(spawnProtectionTime, 0, 60); teamKillDamageProtectionTime = clamp(teamKillDamageProtectionTime, 0, 30); teamKillPushProtectionTime = clamp(teamKillPushProtectionTime, 0, 60); autoDisableMatchTime = clamp(autoDisableMatchTime, 0, 120); // Save settings. control.sConf.waitTime = waitTime; control.sConf.startTime = startTime; control.sConf.autoReconnectTime = autoReconnectTime; control.sConf.maxIdleTime = maxIdleTime; control.sConf.maxIdleTimeCP = maxIdleTimeCP; control.sConf.spawnProtectionTime = spawnProtectionTime; control.sConf.teamKillDamageProtectionTime = teamKillDamageProtectionTime; control.sConf.teamKillPushProtectionTime = teamKillPushProtectionTime; control.sConf.autoDisableMatchTime = autoDisableMatchTime; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExtraServerSettings); client.showMsg(control.lng.settingsSavedMsg); } /*************************************************************************************************** * * $DESCRIPTION Reboots the server. * **************************************************************************************************/ function rebootServer() { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Announce event. control.broadcastMsg(control.lng.adminRebootServerMsg, client.playerName, , , , client.player.playerReplicationInfo); // Reboot the server. control.gInf.rebootCountDown = rebootDelay; //consoleCommand(rebootCommand); } /*************************************************************************************************** * * $DESCRIPTION Deletes the weapon in the spawn protect ignored weapon list at the specified index. * $PARAM index Index of the weapon that is to be deleted. * **************************************************************************************************/ function delIgnoredWeapon(byte index) { local int currIndex; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index < 0 || index >= arrayCount(control.sConf.spawnProtectExcludeWeapons)) { return; } // Delete weapon. for (currIndex = index; currIndex < arrayCount(control.sConf.spawnProtectExcludeWeapons) - 1; currIndex++) { control.sConf.spawnProtectExcludeWeapons[currIndex] = control.sConf.spawnProtectExcludeWeapons[currIndex + 1]; } control.sConf.spawnProtectExcludeWeapons[arrayCount(control.sConf.spawnProtectExcludeWeapons) - 1] = ""; // Save settings. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExclWeaponList); } /*************************************************************************************************** * * $DESCRIPTION Saves the spawn protect ignore settings for the specified weapon. * $PARAM index Position in the list where the settings are to be stored. A * value below zero indicates that a free slot is to be used. * $PARAM weaponClass Class of the weapon type that is to be added / saved. * $PARAM ignorePrimaryFire Whether primary fire is ignored by the spawn protector. * $PARAM ignoreAltFire Whether alternate fire is ignored by the spawn protector. * **************************************************************************************************/ function saveIgnoredWeapon(int index, string weaponClass, bool ignorePrimaryFire, bool ignoreAltFire) { local string weaponConfigStr; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index >= arrayCount(control.sConf.spawnProtectExcludeWeapons) || class'NexgenUtil'.static.trim(weaponClass) == "") { return; } // Find index if weapon is to be added. if (index < 0) { index = 0; while (index < arrayCount(control.sConf.spawnProtectExcludeWeapons) && control.sConf.spawnProtectExcludeWeapons[index] != "") { index++; } // Valid index? if (index >= arrayCount(control.sConf.spawnProtectExcludeWeapons)) { // Nope, cancel action. return; } } // Save settings. weaponConfigStr = class'NexgenUtil'.static.trim(weaponClass) $ separator; if (ignorePrimaryFire) weaponConfigStr = weaponConfigStr $ control.sConf.IW_Fire; if (ignoreAltFire) weaponConfigStr = weaponConfigStr $ control.sConf.IW_AltFire; control.sConf.spawnProtectExcludeWeapons[index] = weaponConfigStr; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExclWeaponList); } /*************************************************************************************************** * * $DESCRIPTION Deletes the hud replacement class in the list at the specified index. * $PARAM index Index of the hud replacement class that is to be deleted. * **************************************************************************************************/ function delHUDReplacementClass(byte index) { local int currIndex; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index < 0 || index >= arrayCount(control.sConf.replacementClass)) { return; } // Delete weapon. for (currIndex = index; currIndex < arrayCount(control.sConf.replacementClass) - 1; currIndex++) { control.sConf.replacementClass[currIndex] = control.sConf.replacementClass[currIndex + 1]; } control.sConf.replacementClass[arrayCount(control.sConf.replacementClass) - 1] = ""; // Save settings. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_HUDReplacementList); } /*************************************************************************************************** * * $DESCRIPTION Saves the HUD replacement class settings at the specified position in the list. * $PARAM index Position in the list where the settings are to be stored. A * value below zero indicates that a free slot is to be used. * $PARAM originalClass Original HUD class that is to be replaced. * $PARAM replacementClass Replacement HUD class. * **************************************************************************************************/ function saveHUDReplacementClass(int index, string originalClass, string replacementClass) { local string replacementConfigStr; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index >= arrayCount(control.sConf.replacementClass) || class'NexgenUtil'.static.trim(originalClass) == "" || class'NexgenUtil'.static.trim(replacementClass) == "") { return; } // Find index if weapon is to be added. if (index < 0) { index = 0; while (index < arrayCount(control.sConf.replacementClass) && control.sConf.replacementClass[index] != "") { index++; } // Valid index? if (index >= arrayCount(control.sConf.replacementClass)) { // Nope, cancel action. return; } } // Save settings. control.sConf.replacementClass[index] = class'NexgenUtil'.static.trim(originalClass) $ "=" $ class'NexgenUtil'.static.trim(replacementClass); control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_HUDReplacementList); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ Or/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClientController * $VERSION 1.05 (11-3-2008 21:25) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Client controller class. Multiple client controller can be hooked onto a * NexgenClient instance in order to have client side control. This class is used to * support plugins for the Nexgen server controller system. * **************************************************************************************************/ class NexgenClientController extends Info abstract; var string ctrlID; // Identifier for this controller. var NexgenClient client; // The client to which this controller is linked. var NexgenController control; // Nexgen controller. var bool bCanModifyHUDStatePanel; // Whether it can modify the client and server state panels // in the Nexgen HUD. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Replicate to client... client; } /*************************************************************************************************** * * $DESCRIPTION Initializes the client controller. This function is automatically called after * the critical variables have been set, such as the client variable. * $PARAM creator The Actor that has added the controller to the client. * **************************************************************************************************/ function initialize(optional Actor creator) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the NexgenClient has received its initial replication info is has * been initialized. At this point it's safe to use all functions of the client. * **************************************************************************************************/ simulated function clientInitialized() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Modifies the setup of the Nexgen remote control panel. * **************************************************************************************************/ simulated function setupControlPanel() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * **************************************************************************************************/ simulated 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. * **************************************************************************************************/ simulated 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 * **************************************************************************************************/ simulated function playerEvent(int playerNum, name eventType, optional string args) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Modifies the client state panel on the Nexgen HUD. * $PARAM stateType State type identifier. * $PARAM text Text to display on the state panel. * $PARAM textColor Color of the text to display. * $PARAM icon State icon. The icon is displayed in front of the text. * $PARAM solidIcon Solid version of the icon (masked, no transparency). * $PARAM bBlink Whether the text on the panel should blink. * **************************************************************************************************/ simulated function modifyClientState(out name stateType, out string text, out Color textColor, out Texture icon, out Texture solidIcon, out byte bBlink) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Modifies the client state panel on the Nexgen HUD. * $PARAM stateType State type identifier. * $PARAM text Text to display on the state panel. * $PARAM textColor Color of the text to display. * $PARAM icon State icon. The icon is displayed in front of the text. * $PARAM solidIcon Solid version of the icon (masked, no transparency). * $PARAM bBlink Whether the text on the panel should blink. * **************************************************************************************************/ simulated function modifyServerState(out name stateType, out string text, out Color textColor, out Texture icon, out Texture solidIcon, out byte bBlink) { // 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. * **************************************************************************************************/ simulated function notifyEvent(string type, optional string arguments) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ g/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClient * $VERSION 1.44 (11-3-2008 21:16) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Client information class. An instance of this class keeps track of the information * of a specific player. It will also provide a the necessary means to allow * communication between the client and server, via replication. * **************************************************************************************************/ class NexgenClient extends Info; #exec AUDIO IMPORT NAME=timer_tick FILE=Resources\TimerTick.wav // General info. var NexgenClient nextClient; // Next client (linked list). var NexgenController control; // The Nexgen server controller instance. var NexgenConfig sConf; // Server configuration. var NexgenGameInfo gInf; // Extended game info. var GeneralConfig gc; // General client configuration. var ServerConfig sc; // Server specific client configuration. var PlayerPawn player; // The PlayerPawn object for this client. var NexgenLang lng; // Language instance to support localization. var NexgenPlayerData pDat; // Player data actor for this client. var class loginHandler; // Client login handler. var int loginHandlerChecksum; // Login handler class checksum. // Login support. var string serverID; // ID of the server. var bool bNetLogin; // Client is ready to login (waiting for server). var bool bNetWait; // Client is waiting for initial replication. var bool loginComplete; // Login procedure has been completed? var string ipAddress; // IP address of the client. var string playerID; // Player identification code. var int playerNum; // Client number. var bool bHasAccount; // Indicates if this client has an account on the // server. var string loginOptions; // Extra login parameters. const loginTimerFreq = 5.0; // Rate of the main timer when loggin in. const waitTimeOut = 20.0; // Login timeout (in seconds). // Player info. var string playerName; // Name of the player in the current game. var int team; // Current team. var string rights; // Rights owned by the client. var string title; // Title of the client. var string country; // Country based on IP address. var bool bSpectator; // The client is a spectator. var bool bFirePressed; // Player has the fire button pressed. // Dynamic control info. var float badConnectionSince; // Bad connection has been detected at this time. var bool bBadConnectionDetected; // Whether the bad connection alert has been detected. var vector oldLocation; // Last known 'camp/idle' location. var bool bMuted; // Indicates if this client has been muted. var bool bNoTeamSwitch; // Whether team switching for this player is disabled. var float teamSwitchOverrideTime; // Time at which the team switch override flag was set. var float nameChangeOverrideTime; // Time at which the name change override flag was set. var float lastRespawnTime; // Last time at which the player has respawned. var float lastSwitchTime; // Last time the player has switched to another team. var float spawnProtectionTimeX; // Spawn protection time remaining (server only). var byte spawnProtectionTime; // Spawn protection time remaining (replicated). var float tkDmgProtectionTimeX; // Team kill damage protection time remaining (server only). var byte tkDmgProtectionTime; // Team kill damage protection time remaining (replicated). var float tkPushProtectionTimeX; // Team kill push protection time remaining (server only). var byte tkPushProtectionTime; // Team kill push protection time remaining (replicated). var bool bScreenShotTaken; // Whether a screenshot has been taken. var bool bReconnecting; // Client is reconnecting to the server. var HUD originalHUD; // Original HUD used by the player. var HUD newHUD; // The NexgenHUD. var float gameEndTime; // Time at which the game has ended (local). var bool bEncryptionParamsSet; // Set to true when the client has received the // encryption parameters. var float idleTime; // Number of seconds the player has been idle. var float idleTimeCP; // Same but when the control panel is open. var int idleTimeRemaining; // Time remaining before the player will be kicked. var bool bUsingNexgenMessageHUD; // Whether the extended Nexgen message HUD is used. // Game speed independent timer support. var float timeSeconds; // Time elpased since the creation of this client. // Controller extensions. var NexgenClientController clientCtrl[16]; // Client controller extensions. // GUI data. var NexgenHUD nscHUD; // Nexgen HUD extension mutator. var WindowConsole consoleWindow; // Shortcut to the console of the player. var NexgenPopupFrame popupWindow; // The popup window. var NexgenMainFrame mainWindow; // The main window. // Config change events. var byte configUpdate[9]; // Config update check per type. var int nextChecksum; // Next checksum to wait for. var int nextUpdateCount; // Next update count to wait for. // Game info change events var int gameInfoUpdate[2]; // Game info update check per type. // $TODO Move constants to NexgenGameInfo class. const IT_GlobalRights = 0; // Global game rights. // Client side settings. const SSTR_ClientKey = "ClientKey"; // Private client key setting. const SSTR_ClientID = "ClientID"; // Public client ID setting. const SSTR_ServerPassword = "Password"; // Server join password setting. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const SSTR_UseNexgenHUD = "UseNexgenHUD"; // Whether or not to use the Nexgen HUD. const SSTR_FlashMessages = "FlashMessages"; // Flash new messages on the Nexgen HUD. const SSTR_ShowPlayerLocation = "ShowPlayerLoc"; // Show player location on teamsay messages. const SSTR_PlayPMSound = "PlayPMSound"; // Play a sound when a PM is received. const SSTR_AutoSSNormalGame = "AutoSSNormalGame"; // Automatically take scrshot at end of normal games. const SSTR_AutoSSMatch = "AutoSSMatch"; // Automatically take scrshot at end of matches. const SSTR_RunCount = "RunCount"; // Number of times Nexgen has been used. // Client rights. const R_MayPlay = "A"; // Allowed to play on the server. const R_VIPAccess = "B"; // Has access to VIP slots. const R_AdminAccess = "C"; // Has access to admin slots. const R_NeedsNoPW = "D"; // Doesn't need a password to enter the server. const R_CanBeIdle = "E"; // Whether the client can be idle on the server. const R_MatchAdmin = "F"; // Controls the current game. const R_Moderate = "G"; // Player can moderate the game. const R_BanOperator = "H"; // Player can ban and unban players. const R_AccountManager = "I"; // Is allowed to change the player accounts. const R_ServerAdmin = "J"; // Can edit the global server settings. const R_MatchSet = "K"; // Can setup clan matches. // Reconnect options. const RCN_ReconnectOnly = 0; // Just reconnect, nothing more. const RCN_ReconnectAsPlayer = 1; // Set OverrideClass to "" and reconnect. const RCN_ReconnectAsSpec = 2; // Set OverrideClass to 'spec' and reconnect. // General constants. const reconnectCommand = "Reconnect"; // Console command for reconnecting. const disconnectCommand = "Disconnect"; // Console command for disconnecting. const exitCommand = "Exit"; // Console command for quitting UT. const startCommand = "Mutate NSC START"; // Console command for starting the game. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. const separator = ","; // Character used to seperate elements in a list. // Player events. const PE_PlayerJoined = 'pj'; // A new player has joined the server. const PE_PlayerLeft = 'pl'; // Somebody left the server. const PE_AttributeChanged = 'ac'; // An attribute of the player has changed. // Player attributes. const PA_ClientID = "id"; // The client identification code. const PA_IPAddress = "ip"; // IP address of the client. const PA_Name = "name"; // Nickname of the player const PA_Title = "title"; // The players title/role on the server. const PA_Team = "team"; // Team to which the player belongs. const PA_Country = "country"; // Country where the player is from. // Settings. const maxIdleRadius = 192; // Radius around oldLocation that counts as idle. const idleCountDelay = 4; // Delay before player is marked as idle. const idleTimeWarning = 15; // Idle time remaining when alerting the player. const maxOverrideTime = 0.50; // Max elapsed time since override flag was set (in sec). const cancelSpawnProtectDelay = 1.25; // Delay in seconds before checking the spawn // protection cancelling conditions. const normalModeTimerFreq = 4.0; // Frequency of timer when the client is fully // initialized (in Hz). const autoScreenShotDelay = 2.0; // Seconds to wait before automatically taking a // a screenshot at the end of the game. const autoSSMinGameTime = 30.0; // Number of seconds the player has to be at least // in the game if a screenshot is to be taken. // This prevents players entering an ended game to // take a screenshot. const welcomeMsgCount = 10; // How many times should the welcome message be displayed? // Debug settings. /* $BEGIN_DEBUG * / const bDebugEnabled = true; const DEBUG_TAG = 'NX_DEBUG'; // $END_DEBUG */ /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Replicate to client... // Variables. clientCtrl, serverID, loginHandler, loginHandlerChecksum, sConf, gInf, loginComplete, ipAddress, playerName, rights, title, bSpectator, playerNum, bHasAccount, country, bMuted, spawnProtectionTime, tkDmgProtectionTime, tkPushProtectionTime, // Functions. showPopup, showPanel, showMsg, reconnect, clientCommand, configChanged, gameInfoChanged, playerEvent, updateLoginOption, setEncryptionParams, notifyEvent; reliable if (role < ROLE_Authority) // Replicate to server... // Variables. // Functions. login, clientInitialized; } /*************************************************************************************************** * * $DESCRIPTION Initializes the client handler. If called it will initiate the login procedure. * $OVERRIDE * **************************************************************************************************/ simulated function preBeginPlay() { /* $BEGIN_DEBUG * / debug(self $ ".preBeginPlay()"); // $END_DEBUG */ // Execute client side actions. if (role == ROLE_SimulatedProxy) { // Load client configuration. gc = spawn(class'GeneralConfig'); sc = spawn(class'ServerConfig'); lng = spawn(class'NexgenLang'); // Start waiting for replication. bNetLogin = true; bNetWait = true; idleTimeRemaining = -1; } // Execute server side actions. if (role == ROLE_Authority) { // Set player. player = PlayerPawn(owner); } // Start timer (set for the login procedure). setTimer(1.0 / loginTimerFreq, true); } /*************************************************************************************************** * * $DESCRIPTION Main loop for the client handler. Handles the following: * - Login triggering (waiting for replication). * - Server config update checks. * $OVERRIDE * **************************************************************************************************/ simulated function timer() { // Client side code. if (role == ROLE_SimulatedProxy) { // Received login info? if (bNetLogin && bNetOwner && serverID != "" && owner != none && PlayerPawn(owner).playerReplicationInfo != none && loginHandler != none && loginHandlerChecksum == class'NexgenUtil'.static.stringHash(string(loginHandler))) { bNetLogin = false; /* $BEGIN_DEBUG * / debug("Login information received for " $ self); // $END_DEBUG */ clientLogin(); } // Check for login timeout. if (bNetLogin && timeSeconds >= waitTimeOut) { setTimer(0.0, false); /* $BEGIN_DEBUG * / debug("Login timeout for " $ self $ ", destroying..."); // $END_DEBUG */ destroy(); // This object is not relevant for the client. } /* $BEGIN_DEBUG * / if (bNetWait && sConf != none) { debug(" STATIC CHECKSUM [0x" $ class'MD5Hash'.static.decToHex(sConf.staticChecksum, 4) $ "] - [0x" $ class'MD5Hash'.static.decToHex(sConf.calcStaticChecksum(), 4) $ "]"); debug("DYNAMIC CHECKSUM [0x" $ class'MD5Hash'.static.decToHex(sConf.dynamicChecksum, 4) $ "] - [0x" $ class'MD5Hash'.static.decToHex(sConf.calcDynamicChecksum(), 4) $ "]"); } // $END_DEBUG */ // Client has received initial replication info? if (!bNetLogin && bNetWait && bNetOwner && sConf != none && sConf.updateCount > 0 && gInf != none && gInf.updateCount > 0 && player != none && rights != "" && bEncryptionParamsSet && sConf.staticChecksum == sConf.calcStaticChecksum() && sConf.dynamicChecksum == sConf.calcDynamicChecksum()) { bNetWait = false; setTimer(0.0, false); /* $BEGIN_DEBUG * / debug("NetWait done for " $ self); // $END_DEBUG */ clientInitialize(); } // Server config update check. if (loginComplete) { checkConfigUpdate(); } // Check if game has ended. if (!bNetWait && gameEndTime <= 0 && gInf.gameState == gInf.GS_Ended) { gameEndTime = timeSeconds; } // Automatically take a screenshot. //if (!bNetWait && gInf.gameState == gInf.GS_Ended && !bScreenShotTaken && player.bShowScores) { if (gameEndTime > 0 && timeSeconds - gameEndTime >= autoScreenShotDelay && !bScreenShotTaken && player.bShowScores) { if (timeSeconds > autoSSMinGameTime && (gc.get(SSTR_AutoSSNormalGame, "false") ~= "true" || gc.get(SSTR_AutoSSMatch, "true") ~= "true" && sConf.matchModeActivated)) { player.sShot(); } bScreenShotTaken = true; } } // Server side code. if (role == ROLE_Authority) { // Check for timeout. if (!loginComplete && timeSeconds >= waitTimeOut) { setTimer(0.0, false); control.nscLog("Login timeout for" @ player.playerReplicationInfo.playerName); control.disconnectClient(self); } } } /*************************************************************************************************** * * $DESCRIPTION Time critical event detection loop. Detects the following events: * - If the fire button is pressed. * $OVERRIDE * **************************************************************************************************/ simulated function tick(float deltaTime) { local int lastIdleTimeRemaining; // Gamespeed independent timer support. if (level.pauser == "") { timeSeconds += deltaTime / level.timeDilation; } // Client side actions. if (role == ROLE_SimulatedProxy && player != none && !bNetWait) { // Check fire button state. if (player.bFire != 0 && !bFirePressed) { // Fire button press. bFirePressed = true; firePressed(); } else if (player.bFire == 0 && bFirePressed) { // Fire button release. bFirePressed = false; } // Check for bad connection. if (player.bBadConnectionAlert && !bBadConnectionDetected) { badConnectionSince = timeSeconds; bBadConnectionDetected = true; } else if (bBadConnectionDetected && !player.bBadConnectionAlert) { bBadConnectionDetected = false; } else if (bBadConnectionDetected && sConf.autoReconnectTime > 0 && !bReconnecting && timeSeconds - badConnectionSince > sConf.autoReconnectTime) { bReconnecting = true; reconnect(); } // Idle detection. if (gInf.gameState == gInf.GS_Playing) { if (vSize((oldLocation - player.location) * vect(1, 1, 0)) > maxIdleRadius) { oldLocation = player.location; idleTime = -idleCountDelay; idleTimeCP = -idleCountDelay; idleTimeRemaining = -1; } else if (!bSpectator && !hasRight(R_CanBeIdle) && level.pauser == "") { lastIdleTimeRemaining = idleTimeRemaining; // Control panel opened? if (mainWindow.bWindowVisible) { // Yes, increase open control panel idle time. idleTimeCP += deltaTime / level.timeDilation; if (idleTimeCP < 0 || sConf.maxIdleTimeCP == 0) { idleTimeRemaining = -1; } else { idleTimeRemaining = max(0, 1 + sConf.maxIdleTimeCP - idleTimeCP); } } else { // No, increase normal idle time. idleTime += deltaTime / level.timeDilation; if (idleTime < 0 || sConf.maxIdleTime == 0) { idleTimeRemaining = -1; } else { idleTimeRemaining = max(0, 1 + sConf.maxIdleTime - idleTime); } } // Play warning sound? if (idleTimeRemaining >= 0 && idleTimeRemaining <= idleTimeWarning && idleTimeRemaining != lastIdleTimeRemaining) { player.clientPlaySound(sound'timer_tick', , true); } // Maximum idle time reached? if (idleTimeRemaining == 0) { showPopup("NexgenIdleKickedDialog"); clientCommand(disconnectCommand); } } } else if (idleTimeRemaining >= 0) { idleTime = -idleCountDelay; idleTimeCP = -idleCountDelay; idleTimeRemaining = -1; } } // Server side actions. if (role == ROLE_Authority) { // Spawn protection. if (spawnProtectionTimeX > 0) { // Disable spawn protection? if ((control.timeSeconds - lastRespawnTime >= cancelSpawnProtectDelay) && (player.playerReplicationInfo.hasFlag != none || !ignoreWeaponFire()) || (player.health <= 0) || (gInf.gameState == gInf.GS_Ended)) { // Yes. spawnProtectionTimeX = 0; spawnProtectionTime = 0; } else { // No, update timer. spawnProtectionTimeX -= deltaTime / level.timeDilation; if (spawnProtectionTimeX <= 0) { spawnProtectionTime = 0; } else { spawnProtectionTime = byte(spawnProtectionTimeX) + 1; } } } // Team kill damage protection. if (tkDmgProtectionTimeX > 0) { tkDmgProtectionTimeX -= deltaTime / level.timeDilation; if (tkDmgProtectionTimeX <= 0) { tkDmgProtectionTime = 0; } else { tkDmgProtectionTime = byte(tkDmgProtectionTimeX) + 1; } } // Team kill push protection. if (tkPushProtectionTimeX > 0) { tkPushProtectionTimeX -= deltaTime / level.timeDilation; if (tkPushProtectionTimeX <= 0) { tkPushProtectionTime = 0; } else { tkPushProtectionTime = byte(tkPushProtectionTimeX) + 1; } } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether primary or alt fire for the current weapon carried by the player * should be ignored when checking if it should cancel spawn protection. * $RETURN True if the weapon should be ignored, false if not. * **************************************************************************************************/ function bool ignoreWeaponFire() { local bool bIgnore; local bool bFound; local bool bIgnoreFire; local bool bIgnoreAltFire; local int index; local string weaponClass; local string currWeaponClass; local string excludedModes; // Trivial case. if (player.weapon == none || player.bFire == 0 && player.bAltFire == 0) { return true; } // Non trivial case, locate weapon class in configuration file. weaponClass = string(player.weapon.class); while(!bFound && index < arrayCount(sConf.spawnProtectExcludeWeapons) && sConf.spawnProtectExcludeWeapons[index] != "") { // Get currently selected weapon in exclude list. class'NexgenUtil'.static.split(sConf.spawnProtectExcludeWeapons[index], currWeaponClass, excludedModes); // Class match? if (weaponClass ~= currWeaponClass) { // Yes. bFound = true; excludedModes = caps(class'NexgenUtil'.static.trim(excludedModes)); bIgnore = (instr(excludedModes, sConf.IW_Fire) >= 0 && player.bFire > 0) || (instr(excludedModes, sConf.IW_AltFire) >= 0 && player.bAltFire > 0); } else { // No, continue searching... index++; } } // Return result. return bIgnore; } /*************************************************************************************************** * * $DESCRIPTION Called when the fire button is pressed. * **************************************************************************************************/ simulated function firePressed() { // Check if the game should be started. if (gInf != none && gInf.gameState == gInf.GS_Ready) { clientCommand(startCommand); } } /*************************************************************************************************** * * $DESCRIPTION Initiates the client side login procedure. Retrieves the necessary client info * and sends it to the server for a login request. * $REQUIRE owner != none && serverID != "" * $ENSURE player != none && playerID != none * **************************************************************************************************/ simulated function clientLogin() { // Set player. player = PlayerPawn(owner); // Retrieve login information. loginHandler.static.getLoginParameters(self, playerID, loginOptions); // Initialize the popup window, client might receive a message if login fails. initializePopupWindow(); // Try to login to the server. login(playerID, loginOptions); // Check if a Nexgen configuration is resident on the client. checkResidentConfig(); } /*************************************************************************************************** * * $DESCRIPTION Requests the login of this client. * $ENSURE playerName != "" && ipAddress != "" && playerID != "" * **************************************************************************************************/ function login(string clientID, string args) { // Stop timer (it is no longer used server side). setTimer(0.0, false); // Store client information. playerName = player.playerReplicationInfo.playerName; ipAddress = player.getPlayerNetworkAddress(); ipAddress = left(ipAddress, instr(ipAddress, ":")); bSpectator = player.isA('Spectator'); playerID = clientID; loginOptions = args; // Set account information. setAccountInfo(); // Let the server controller check the login request. control.checkLogin(self); } /*************************************************************************************************** * * $DESCRIPTION Initializes the clientside of this object. This can only be done once the initial * replication data has been received (detected in the timer function). Calling this * function will setup things like the HUD and user interface objects. * $REQUIRE player != none && sConf != none && gInf != none * $ENSURE nscHUD != none * **************************************************************************************************/ simulated function clientInitialize() { local int index; local int runCount; // Setup GUI. nscHUD = spawn(class'NexgenHUD', self); if (gc.get(SSTR_UseNexgenHUD, "true") ~= "true" && sConf.HUDReplacementClass != none) { setNexgenMessageHUD(true); } initializeControlPanel(); // Set timer frequency. setTimer(1.0 / normalModeTimerFreq, true); // Let the client controllers know we're initialized. for (index = 0; index < arrayCount(clientCtrl); index++) { if (clientCtrl[index] != none) { clientCtrl[index].clientInitialized(); } } // Update run count. runCount = int(gc.get(SSTR_RunCount, "0")) + 1; gc.set(SSTR_RunCount, string(runCount)); gc.saveConfig(); // Show 'welcome to Nexgen' message? if (runCount <= welcomeMsgCount) { showMsg(lng.welcomeMsg); } // Initialisation complete, notify server. clientInitialized(); } /*************************************************************************************************** * * $DESCRIPTION Enables or disables the Nexgen message HUD. * $PARAM bEnable Whether the nexgen message HUD should be enabled. * $REQUIRE sConf.HUDReplacementClass != none * **************************************************************************************************/ simulated function setNexgenMessageHUD(bool bEnable) { bUsingNexgenMessageHUD = false; if (bEnable) { // Store original HUD if not already set. if (originalHUD == none) { originalHUD = player.myHUD; } // Create new HUD if not already done. if (newHUD == none) { newHUD = spawn(sConf.HUDReplacementClass, player); newHUD.HUDMutator = originalHUD.HUDMutator; } // Enable the new HUD. if (newHUD != none) { bUsingNexgenMessageHUD = true; player.myHUD = newHUD; } } else if (originalHUD != none) { // Reset HUD to original HUD. player.myHUD = originalHUD; } } /*************************************************************************************************** * * $DESCRIPTION Called when the client has finished its initialisation process. This function is * called on the client in the clientInitialize() function and is replicated to the * server. * **************************************************************************************************/ function clientInitialized() { control.clientInitialized(self); } /*************************************************************************************************** * * $DESCRIPTION Displays the specified message on the screen of this client. * $PARAM msg The message to display. * $PARAM pri Player replication info related to this message. * **************************************************************************************************/ simulated function showMsg(string msg, optional PlayerReplicationInfo pri) { local string cleanMsg; if (role == ROLE_SimulatedProxy) { // Strip color tag. cleanMsg = class'NexgenUtil'.static.removeMessageColorTag(msg); // Write message to console. if (player.player.console != none) { player.player.console.message(pri, cleanMsg, 'Event'); } // Write message to HUD. if (player.myHUD != none) { if (bUsingNexgenMessageHUD) { player.myHUD.message(pri, msg, 'Event'); } else { player.myHUD.message(pri, cleanMsg, 'Event'); } } } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the account information of this client. * $REQUIRE playerID != none * $ENSURE rights != "" && title != none * **************************************************************************************************/ function setAccountInfo() { local int accountNum; // Player has an account? accountNum = sConf.getUserAccountIndex(playerID); if (accountNum < 0) { // No, use default account type. if (bSpectator) { title = control.lng.specTitle; } else { title = sConf.getAccountTypeTitle(0); } rights = sConf.atRights[0]; } else { // Yes, load account info. bHasAccount = true; title = sConf.getUserAccountTitle(accountNum); if (sConf.paAccountType[accountNum] < 0) { rights = sConf.paCustomRights[accountNum]; } else { rights = sConf.atRights[sConf.paAccountType[accountNum]]; } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client has the specified right. * $PARAM rightID String identifier of the client right. * $REQUIRE rightID != "" * $RETURN True if the client has the specfied right, false if not. * **************************************************************************************************/ simulated function bool hasRight(string rightID) { return instr(separator $ rights $ separator, separator $ rightID $ separator) >= 0; } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client has the specified rights. * $PARAM rightsStr String containing the rights to check. * $RETURN True if the client has the specfied rights, false if not. * **************************************************************************************************/ simulated function bool hasRights(string rightsStr) { local string remaining; local string rightID; local bool bHasRights; remaining = rightsStr; // Check rights. bHasRights = true; while (bHasRights && remaining != "") { class'NexgenUtil'.static.split(remaining, rightID, remaining); bHasRights = hasRight(rightID); } // Return result. return bHasRights; } /*************************************************************************************************** * * $DESCRIPTION Initializes the console window so other windows can be created. * $ENSURE consoleWindow != none * **************************************************************************************************/ simulated function initializeConsoleWindow() { consoleWindow = WindowConsole(player.player.console); if (consoleWindow.bShowConsole) { consoleWindow.hideConsole(); } if (consoleWindow.root == none) { consoleWindow.createRootWindow(none); } } /*************************************************************************************************** * * $DESCRIPTION Creates the popup window. * $ENSURE popupWindow != none * **************************************************************************************************/ simulated function initializePopupWindow() { local float wWidth; local float wHeight; local float wTop; local float wLeft; local UWindowWindow window; // Setup console window. if (consoleWindow == none) { initializeConsoleWindow(); } // Create popup window. wWidth = class'NexgenPopupFrame'.default.windowWidth; wHeight = class'NexgenPopupFrame'.default.windowHeight; wLeft = fMax(0.0, (consoleWindow.root.winWidth - wWidth) / 2.0); wTop = fMax(0.0, (consoleWindow.root.winHeight - wHeight) / 2.0); window = consoleWindow.root.createWindow(class'NexgenPopupFrame', wLeft, wTop, wWidth, wHeight); popupWindow = NexgenPopupFrame(window); popupWindow.client = self; popupWindow.gc = gc; popupWindow.sc = sc; popupWindow.serverID = serverID; //sConf.serverID; popupWindow.close(); } /*************************************************************************************************** * * $DESCRIPTION Creates the remote control panel window. * $ENSURE mainWindow != none * **************************************************************************************************/ simulated function initializeControlPanel() { local float wWidth; local float wHeight; local float wTop; local float wLeft; local UWindowWindow window; local int index; // Setup console window. if (consoleWindow == none) { initializeConsoleWindow(); } // Make sure the control panel of the last game is closed. if (consoleWindow.root != none) { window = consoleWindow.root.firstChildWindow; while (window != none) { // Window is a control panel? if (window.isA('NexgenMainFrame')) { // Yes, close it. window.close(); } // Continue with next window. window = window.nextSiblingWindow; } } // Create main window. wWidth = class'NexgenMainFrame'.default.windowWidth; wHeight = class'NexgenMainFrame'.default.windowHeight; wLeft = fMax(0.0, (consoleWindow.root.winWidth - wWidth) / 2.0); wTop = fMax(0.0, (consoleWindow.root.winHeight - wHeight) / 2.0); window = consoleWindow.root.createWindow(class'NexgenMainFrame', wLeft, wTop, wWidth, wHeight); mainWindow = NexgenMainFrame(window); mainWindow.close(); mainWindow.mainPanel.client = self; // Let the client controllers add stuff to the control panel. for (index = 0; index < arrayCount(clientCtrl); index++) { if (clientCtrl[index] != none) { clientCtrl[index].setupControlPanel(); } } } /*************************************************************************************************** * * $DESCRIPTION Shows a popup dialog at the client. * $PARAM popupClass Class name of the popup dialog to show. * $PARAM str1 Dialog specific argument. * $PARAM str2 Dialog specific argument. * $PARAM str3 Dialog specific argument. * $PARAM str4 Dialog specific argument. * $REQUIRE windowsInitialized * **************************************************************************************************/ simulated function showPopup(string popupClass, optional string str1, optional string str2, optional string str3, optional string str4) { popupWindow.showPopup(popupClass, str1, str2, str3, str4); popupWindow.focusWindow(); popupWindow.bringToFront(); popupWindow.showWindow(); consoleWindow.bQuickKeyEnable = true; consoleWindow.launchUWindow(); } /*************************************************************************************************** * * $DESCRIPTION Opens the Nexgen control panel (main window). * $PARAM panel The panel that is to be displayed. * **************************************************************************************************/ simulated function showPanel(optional string panel) { if (mainWindow != none) { mainWindow.focusWindow(); mainWindow.bringToFront(); mainWindow.showWindow(); consoleWindow.bQuickKeyEnable = true; consoleWindow.launchUWindow(); if (panel != "") { mainWindow.mainPanel.selectPanel(panel); } } } /*************************************************************************************************** * * $DESCRIPTION Reconnects this client. * $PARAM option Determines the reconnect option. * $REQUIRE option == RCN_ReconnectOnly || * option == RCN_ReconnectAsPlayer || * option == RCN_ReconnectAsSpec * **************************************************************************************************/ simulated function reconnect(optional byte option) { if (option == RCN_ReconnectAsPlayer) { player.updateURL(SSTR_OverrideClass, "", true); } else if (option == RCN_ReconnectAsSpec) { player.updateURL(SSTR_OverrideClass, spectatorClass, true); } player.consoleCommand(reconnectCommand); } /*************************************************************************************************** * * $DESCRIPTION Update a login option for the client. * $PARAM optionName Name of the option that is to be updated. * $PARAM value The new value of the option. * $PARAM bSaveDefault Whether this value should be saved as default for this option. * $REQUIRE optionName != "" * **************************************************************************************************/ simulated function updateLoginOption(string optionName, coerce string value, optional bool bSaveDefault) { player.updateURL(optionName, value, bSaveDefault); } /*************************************************************************************************** * * $DESCRIPTION Executes the given command on the client. * $PARAM cmd Console command to execute. * **************************************************************************************************/ simulated function clientCommand(string cmd) { player.consoleCommand(cmd); } /*************************************************************************************************** * * $DESCRIPTION Creates a new client controller and hooks it onto this client. * $PARAM controllerClass Type of controller to create. * $PARAM creator The Actor that wants to add the controller. * $REQUIRE controllerClass != none * $RETURN The newly created controller or none if all client controller slots are used. * **************************************************************************************************/ function NexgenClientController addController(class controllerClass, optional Actor creator) { local int index; local bool bFound; local NexgenClientController controller; // Locate empty controller slot. while (!bFound && index < arrayCount(clientCtrl)) { if (clientCtrl[index] == none) { bFound = true; } else { index++; } } // Create controller. if (bFound) { controller = spawn(controllerClass, player); controller.client = self; controller.control = control; controller.initialize(creator); clientCtrl[index] = controller; } // Return the new controller. return controller; } /*************************************************************************************************** * * $DESCRIPTION Fetches the client controller with the specified id. * $PARAM controllerID ID of the controller to return. * $RETURN The controller with the specified id, or none if the controller couln't be found. * $ENSURE result != none ? result.ctrlID == controllerID : true * **************************************************************************************************/ simulated function NexgenClientController getController(string controllerID) { local int index; local bool bFound; // Locate controller. while (!bFound && index < arrayCount(clientCtrl)) { if (clientCtrl[index] != none && clientCtrl[index].ctrlID ~= controllerID) { bFound = true; } else { index++; } } // Return controller if found. if (bFound) { return clientCtrl[index]; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client that the server configuration has been updated. The new * settings might not yet have been replicated, so the client has to wait for the * replication to complete. In that case the function will be automatically called * again once the replication has completed, see checkConfigUpdate(). * $PARAM configType Type of settings that have been changed. * $PARAM updateNum Config update number for the new settings. * $PARAM checksum Checksum of the new settings. * $REQUIRE 0 <= configType && configType < arrayCount(configUpdate) && updateNum >= 0 * **************************************************************************************************/ simulated function configChanged(byte configType, int updateNum, int checksum) { local int index; // Set update parameters. if (updateNum > nextUpdateCount) { nextUpdateCount = updateNum; nextChecksum = checksum; } // Check if replication of the new settings has completed. if (checksum == sConf.calcDynamicChecksum()) { // It has, notify GUI. mainWindow.mainPanel.configChanged(configType); // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].configChanged(configType); } } else { // No it hasn't, wait for replication to complete. configUpdate[configType] = 0xFF; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client that the extended game information has been updated. The new * settings might not yet have been replicated, so the client has to wait for the * replication to complete. In that case the function will be automatically called * again once the replication has completed, see checkConfigUpdate(). * $PARAM infoType Type of information that has been changed. * $PARAM updateNum Game info update number for the new settings. * $REQUIRE 0 <= configType && configType < arrayCount(gameInfoUpdate) && updateNum >= 0 * **************************************************************************************************/ simulated function gameInfoChanged(byte infoType, int updateNum) { local int index; // Check if replication of the new settings has completed. if (gInf.updateCount >= updateNum) { // It has, notify GUI. mainWindow.mainPanel.gameInfoChanged(infoType); // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].gameInfoChanged(infoType); } } else { // No it hasn't, wait for replication to complete. gameInfoUpdate[infoType] = updateNum; } } /*************************************************************************************************** * * $DESCRIPTION Checks for each config type if new settings have been received. If new settings * have been received configChanged() is called. * **************************************************************************************************/ simulated function checkConfigUpdate() { local byte type; local int currentChecksum; local bool currentChecksumSet; // Server configuration. for (type = 0; type < arrayCount(configUpdate); type++) { if (configUpdate[type] != 0) { // Get current checksum. if (!currentChecksumSet) { currentChecksum = sConf.calcDynamicChecksum(); currentChecksumSet = true; } // Check if replication has completed. if (currentChecksum == nextChecksum) { configUpdate[type] = 0; configChanged(type, nextUpdateCount, nextChecksum); } } } // Extended game info. for (type = 0; type < arrayCount(gameInfoUpdate); type++) { if (gameInfoUpdate[type] > 0 && gInf.updateCount >= gameInfoUpdate[type]) { gameInfoUpdate[type] = 0; gameInfoChanged(type, gInf.updateCount); } } } /*************************************************************************************************** * * $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 * **************************************************************************************************/ simulated function playerEvent(int playerNum, name eventType, optional string args) { local int index; /* $BEGIN_DEBUG * / debug(self $ ".playerEvent(" $ playerNum $ ", " $ eventType $ ", " $ args $ ")"); // $END_DEBUG */ // Notify GUI. if (mainWindow != none) { mainWindow.mainPanel.playerEvent(playerNum, eventType, args); } // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].playerEvent(playerNum, eventType, args); } } /*************************************************************************************************** * * $DESCRIPTION Called when the client has respawned. * **************************************************************************************************/ function respawned() { // Actions that require the login to be completed. if (loginComplete) { if (!bSpectator) { spawnProtectionTimeX = sConf.spawnProtectionTime; } } lastRespawnTime = control.timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client is muted. * $RETURN True if the player has been muted, false if not. * **************************************************************************************************/ simulated function bool isMuted() { return (bMuted || gInf.bMuteAll) && !hasRight(R_Moderate); } /*************************************************************************************************** * * $DESCRIPTION Sets the current encryption parameters. * $REQUIRE sConf != none * $ENSURE bEncryptionParamsSet * **************************************************************************************************/ simulated function setEncryptionParams(int k, string cs) { sConf.setEncryptionParams(k, cs); bEncryptionParamsSet = true; } /*************************************************************************************************** * * $DESCRIPTION Changes the team for this player. * $PARAM newTeam The index of the target team. * **************************************************************************************************/ function setTeam(byte newTeam) { local bool bPlayersBalanceTeams; // We should use the changeTeam function or else we might break compatibility with other game // types. However if bPlayersBalanceTeams is set to true it might prevent the player from being // switched to the desired team. Therefore it is temporarily disabled when we make the switch. // Set bPlayersBalanceTeams to false. if (level.game.isA('TeamGamePlus')) { bPlayersBalanceTeams = TeamGamePlus(level.game).bPlayersBalanceTeams; TeamGamePlus(level.game).bPlayersBalanceTeams = false; } // Set override flag. teamSwitchOverrideTime = control.timeSeconds; // Switch to target team. player.changeTeam(newTeam); // Restore bPlayersBalanceTeams value. if (level.game.isA('TeamGamePlus')) { TeamGamePlus(level.game).bPlayersBalanceTeams = bPlayersBalanceTeams; } } /*************************************************************************************************** * * $DESCRIPTION Checks whether there is a Nexgen configuration resident on the client. If this is * the case the client might not be able to initialize correctly because of checksum * mismatches. Therefore the client will be notified if problems may occur. * **************************************************************************************************/ simulated function checkResidentConfig() { if (player.consoleCommand("get " $ class'NexgenUtil'.default.packageName $ ".NexgenConfigExt bInstalled") ~= string(true) || player.consoleCommand("get " $ class'NexgenUtil'.default.packageName $ ".NexgenConfigSys bInstalled") ~= string(true)) { showPopup("NexgenResidentConfigDialog"); } } /*************************************************************************************************** * * $DESCRIPTION Changes the player name of this client. Automatically sets the name change * override flag, so Nexgen won't reset it if name changing isn't allowed by the * administrators. * $PARAM newName The new name of the player. * $REQUIRE newName != "" * **************************************************************************************************/ function changeName(string newName) { nameChangeOverrideTime = control.timeSeconds; playerName = newName; level.game.changeName(player, newName, false); updateLoginOption("Name", newName, true); } /*************************************************************************************************** * * $DESCRIPTION Adds a new plugin configuration panel to the control panel. * $PARAM panelClass The panel class of the plugin configuration panel. * $REQUIRE panelClass != none * **************************************************************************************************/ simulated function addPluginConfigPanel(class panelClass) { local NexgenPanelContainer container; container = NexgenPanelContainer(mainWindow.mainPanel.getPanel("pluginsettings")); if (container == none) { container = NexgenPanelContainer(mainWindow.mainPanel.addPanel(lng.pluginsTabTxt, class'NexgenScrollPanelContainer', "pluginsettings", "server,serversettings")); } container.addPanel("", panelClass); } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of an event. The event is passed to the Nexgen control panel * and the client controllers attached to this client. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ simulated function notifyEvent(string type, optional string arguments) { local int index; // Notify GUI. if (mainWindow != none) { mainWindow.mainPanel.notifyEvent(type, arguments); } // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].notifyEvent(type, arguments); } } /*************************************************************************************************** * * $DESCRIPTION Debug logging function. * $PARAM msg The debug message that should be written to the log. * **************************************************************************************************/ /* $BEGIN_DEBUG * / simulated function debug(coerce string msg) { local string timeStamp; local string timeSecondsStr; local string part; timeSecondsStr = string(int(level.timeSeconds * 100.0 + 0.5) / 100.0); timeStamp = left(timeSecondsStr, instr(timeSecondsStr, ".")) $ "." $ class'NexgenUtil'.static.rfill(mid(timeSecondsStr, instr(timeSecondsStr, ".") + 1), 2, "0", 2); log("[" $ timeStamp $ "]" @ msg, DEBUG_TAG); } // $END_DEBUG */ /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ C?t!y?xca(XXX~X:-Ea/!Aqt!iS"Jtj y>IX9J-{}I, -{ W, D IW&0D  D 9 AD  D F-{(W-{  F?F%`H._cqiXyqis {p,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenBannedDialog * $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 NexgenBannedDialog extends NexgenPopupDialog; var UMenuLabelControl reasonLabel; // Ban reason label component. var UMenuLabelControl periodLabel; // Ban period label component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string reasonText; // Ban reason label text. var localized string periodText; // Ban period label text. var localized string noReasonText; // Text to display if no reason is given. /*************************************************************************************************** * * $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); reasonLabel = addPropertyLabel(cy, reasonText, 48.0); periodLabel = addPropertyLabel(cy, periodText, 48.0); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM reason Reason why the player was banned. * $PARAM period The period for which the player is banned. * $PARAM str3 Not used. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string reason, optional string period, optional string str3, optional string str4) { if (reason == "") { reasonLabel.setText(noReasonText); } else { reasonLabel.setText(reason); } periodLabel.setText(period); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ `5E]ECw'*:';'&Mutate NSC START _>k5S::$1a o a  a -o '-m 'yq::$.a?@' B?k!\YOtppppppppUSk!,0_USj!,0_USd!,0_USb!,0_USa!,0  K?l!ype-}'r l!I~r/hI%o!JrIrrI&p-}(-}I~r/I%n!JrIr rI&-}(--}I~r %I%m!JrIrrI&--}(-}I~r:|I%p!JrIrrI&-}(-}q!Jr-}  @P?e!v-`pppppppp USg!,0/ USf!,0/ USe!,0  USh!,0: USi!,0  V @y@H?`!ZuT-e's `!G~s_hG%_!JsGssG&p-e(-eG~s_G%Y!JsGs sG&-e(--eG~s_%G%X!JsGssG&--e(-eG~s_|G%V!JsGssG&-e(-eJs-e  R?U \R[zU V foreverQ|U &MV forU &matches|U &UV untilXU &V foreverV  @T?Z!X+ Z![!\!]!^!  @S)LRT.(%L L,LZ&,?  w>ipHbp ti-p%p, i Iiqi[\~rw[*u[HB[w\*`\bB\Rr%r~ ~%x`nrVb`ur~bHux~nHV~% r%ci~Bi~}ur~}uyir}`( ~%ci~Bi~}u`(u`ciupcHupBb`py eC5/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenAdminLoginDialog * $VERSION 1.00 (27-10-2007 18:41) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player has entered an invalid password. * **************************************************************************************************/ class NexgenAdminLoginDialog extends NexgenPopupDialog; var UWindowSmallButton loginButton; // 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 loginText; // Text to display on the login button. /*************************************************************************************************** * * $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); loginButton = addButton(loginText, 64.0); // Set component properties. passwordInput.setMaxLength(32); } /*************************************************************************************************** * * $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) { local string password; local NexgenClientCore rpci; super.notify(control, eventType); // Login button. if (control == loginButton && eventType == DE_Click) { close(); password = class'NexgenUtil'.static.trim(passwordInput.getValue()); rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); if (password != "" && rpci != none) { rpci.adminLogin(password); } } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ BW?b8e 2 X?BnŊ M Zt,ttדt T]Administrator login.]qPlease enter your administrator password below and click login. If no account passwords have been set you can login as root administrator by entering the server admin password.Q] Password:S]Login`"OK/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenActor * $VERSION 1.01 (24-11-2007 22:08) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION The Nexgen ServerActor. Execution of the Nexgen server controller starts in this * class. New client connections are also detected in this class. * **************************************************************************************************/ class NexgenActor extends SpawnNotify; var NexgenController serverController; // Active Nexgen Server Controller. /*************************************************************************************************** * * $DESCRIPTION Creates a NexgenServer instance if called on the server. * **************************************************************************************************/ simulated function preBeginPlay() { if(role == ROLE_Authority) { serverController = spawn(class'NexgenController'); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the server controller that a new client has connected to the server. * $PARAM a Newly spawned actor. * $REQUIRE a != none * **************************************************************************************************/ simulated function Actor spawnNotification(Actor a) { if (a.isA('PlayerPawn') && serverController != none) { serverController.newClient(PlayerPawn(a)); } return a; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ O?~#Z^@~~,1@%Q~R\Q~@R~@},  Y?V8d [?ե6mԝXQ3 |fSgSG?C9]::$-o - {l w*w.* w_*g V_-o (F-o  ]Aa(a-o -m - w * j % w'*'P% w* {h -V  g |  f V-m (a(l-l k-m  F?%:';',F]F?%]F@ -L-]At|1-AutoSSNormalGamefalsetrue?|1-AutoSSMatchtruetrue -?{-L'[::$[-l  ]Aa(t2Login timeout fortT Ih,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenAccountUpdatedDialog * $VERSION 1.00 (21-10-2007 15:22) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the account of the player was updated. * **************************************************************************************************/ class NexgenAccountUpdatedDialog 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); 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. * **************************************************************************************************/ Z? }^]Tpppppppp ,- ,,- , ,- ,,- ,,   B^?W8b 2`?䦤BnŎ> Y T] Your account has been updated.m~The server has disconnected your client because your account was updated. For security reasons and in order to apply the changes to your account, you had to be disconnected from the server. You can reconnect immediately.\n \nSorry for the inconvenience.\] Reconnect`"OZ@_?FI`;3|F,F,9F  O// MD5 Hash generator -- Copyright 2004 (c) Petr Jelinek -- Modified for UT99. // Source: UnrealWiki: MD5 -- http://wiki.beyondunreal.com/wiki/MD5 class MD5Hash extends Object; /** MD5 context */ struct MD5_CTX { /** state (ABCD) */ var int state[64]; /** number of bits, modulo 2^64 (lsb first) */ var int count[64]; /** input buffer */ var byte buffer[64]; }; /** return the MD5 of the input string */ static function string MD5String (string str) { local MD5_CTX context; local byte digest[64]; local string Hex; local int i; MD5Init (context); MD5Update (context, str, Len(str)); MD5Final (digest, context); for (i = 0; i < 16; i++) Hex = Hex $ DecToHex(digest[i], 1); return Hex; } /** initialize the MD5 context */ static final function MD5Init(out MD5_CTX context) { context.count[0] = 0; context.count[1] = 0; context.state[0] = 0x67452301; context.state[1] = 0xefcdab89; context.state[2] = 0x98badcfe; context.state[3] = 0x10325476; } static final function MD5Transform(out int Buf[64], byte block[64]) { local int A,B,C,D; local int x[64]; A = Buf[0]; B = Buf[1]; C = Buf[2]; D = Buf[3]; Decode (x, block, 64); /* Round 1 */ FF (a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */ FF (b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */ FF (b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */ FF (a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */ FF (d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], 9, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */ HH (a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */ II (d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ II (b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ II (d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ II (b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */ II (a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ II (c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */ Buf[0] += A; Buf[1] += B; Buf[2] += C; Buf[3] += D; } /** update MD5 context */ static final function MD5Update(out MD5_CTX Context, string Data, int inputLen) { local int i, index, partlen; local byte tmpbuf[64]; index = ((context.count[0] >>> 3) & 0x3F); if ((context.count[0] += (inputLen << 3)) < (inputLen << 3)) context.count[1]++; context.count[1] += (inputLen >>> 29); partLen = 64 - index; if (inputLen >= partLen) { MD5Move(Data, 0, context.buffer, index, partLen); MD5Transform (context.state, context.buffer); for (i = partLen; i + 63 < inputLen; i += 64) { MD5Move(Data, i, tmpbuf, 0, 64); MD5Transform (context.state, tmpbuf); } index = 0; } else i = 0; MD5Move(Data, i, context.buffer, index, inputLen-i); } /** finalize the MD5 context */ static final function MD5Final (out byte digest[64], out MD5_CTX context) { local byte bits[64]; local int i, index, padLen; local string strbits; local string PADDING; PADDING = chr(0x80); for (i = 1; i < 64; i++) PADDING = PADDING$chr(0); Encode (bits, context.count, 8); index = ((context.count[0] >>> 3) & 0x3f); if (index < 56) padLen = (56 - index); else padLen = (120 - index); MD5Update (context, PADDING, padLen); strbits = ""; for (i=0;i<8;i++) strbits = strbits$Chr(bits[i]); MD5Update (context, strbits, 8); Encode (digest, context.state, 16); for (i = 0; i < 64; i++) { context.buffer[i] = 0; } } static final function Encode (out byte output[64], int input[64], int len) { local int i, j; i = 0; for (j = 0; j < len; j += 4) { output[j] = (input[i] & 0xff); output[j+1] = ((input[i] >> 8) & 0xff); output[j+2] = ((input[i] >> 16) & 0xff); output[j+3] = ((input[i] >> 24) & 0xff); i++; } } static final function Decode(out int output[64], byte input[64], int len) { local int i, j; i = 0; for (j = 0; j < len; j += 4) { output[i] = ((input[j]) | (int(input[j+1]) << 8) | (int(input[j+2]) << 16) | (int(input[j+3]) << 24)); i++; } } static final function MD5Move(string src, int srcindex, out byte buffer[64], int bufindex, int len) { local int i,j; j = bufindex; for (i = srcindex; i < srcindex+len; i++) { buffer[j] = Asc(Mid(src, i, 1)); j++; if (j == 64) break; } } static final function int ROTATE_LEFT (int x, int n) { return (((x) << (n)) | ((x) >>> (32-(n)))); } static final function int F (int x, int y, int z) { return (((x) & (y)) | ((~x) & (z))); } static final function int G (int x, int y, int z) { return ((x & z) | (y & (~z))); } static final function int H (int x, int y, int z) { return (x ^ y ^ z); } static final function int I (int x, int y, int z) { return (y ^ (x | (~z))); } static final function FF(out int a, int b, int c, int d, int x, int s, int ac) { a += F(b, c, d) + x + ac; a = ROTATE_LEFT (a, s); a += b; } static final function GG(out int a, int b, int c, int d, int x, int s, int ac) { a += G(b, c, d) + x + ac; a = rotate_left (a, s) +b; } static final function HH(out int a, int b, int c, int d, int x, int s, int ac) { a += H(b, c, d) + x + ac; a = rotate_left (a, s) +b; } static final function II(out int a, int b, int c, int d, int x, int s, int ac) { a += I(b, c, d) + x + ac; a = rotate_left (a, s) +b; } static final function string DecToHex(int dec, int size) { const hex = "0123456789ABCDEF"; local string s; local int i; for (i = 0; i < size*2; i++) { s = mid(hex, dec & 0xf, 1) $ s; dec = dec >>> 4; } return s; } @b?Qtb>6|Q,JQ,,<  ]?e?fdJd%Bd}fM,Mfd&dM  eM\?z `\Fh1z]z ::$ w* -m :% -r-r'E:%-r-r(- -P s]-P 'p-P --P (pp-P : U% -O]s< U-O'y:';',a(B #???,B X ?,U ?,y-E Ezgy/-U z U ?% F%yy%D?& FU &X z X ?% G%y&y%D?& GX fy% y, yg n C3 'y%GNexgenIdleKickedDialogDisconnecty%X ?,U ?,yf::$A?%tkI?(w* C%:';',Ac $Az A?%c $c =:CA&k?%kz k?%b $b =:Ck&f^?%^z U^?%A$fA=:C^& N#N#3N#f"W$f"^$f"C$f"b$f"`$f"df"f"f"df"@!f"vf"Hf"K!f"uf"_$f"P#f" f"C^$J!f"~#f"c$f"fN#Pf"I^$[f"ef"W `$Wf"N#o`$`^$Vf"x#f"q^$uf"X$f"bf"}N#yf"oR$f"rX$z^$l#W$gf"]$f"M"f"Rf"^f"uf"Yf"oC$Cf"df"^_$W#^$W f"Yf"w_$[#f"tf"Xf"\$f"x!f"{f"Xf"eC$pc$tf"H `$F_$ic$d"f"af"\f"l_$j^$W ^$rf"V^$af"a"^$}"f"%^$x!C$wf"v^$E^$if"s^$a"^$Wf"R$W$\c$hC$Of"rf"]f"[^$G!`$~c$Hc$I^$EC$:c$aC$R$O$^$zf"w!^$SC$gC$qC$kC$b$Q_$y^$i#f"d `$Rc$Xb$b`$VC$L^$c`$bC$\b$FC$]^$S^$NC$:`$"C$}c$J^$]f"xW$if"V^$`C$u&b$`C$AW$l^$_#W$sW$q_$k#f"I W$wW$kC$e`$`^$}"f"Af"pW$f"X3M"^DN#]e"N#Ef"E\$f"b$,3M"q#b$aW$Nb$.^$P"W$\b$hb$O#\W"`$If"G!C$CC$qb$d^$Gb$eW$~f"r `$}W$@b$|^$Z_$GW$P_$MC$`b$m"^$Yf"s C$kX$Hc$N^$X^$H_$R#f"W^$\`$a^$s ^$E^$Vf"[C$`$IC$`gW$aC$d^$Y^$B$C$b$mC$x^$K!`$b_$}C$q^$Zf"jX$cEW$u`$E#^$AC$}^$cW$xC$sg`$PC$t\$OC$X f"pb$PC$r^$]C$``$T f"e C$^C$a`$c#C$`\$s`$~"W$j#`$h#_$k#^$l"^$o`$DC$:`$d`$c`$h"`$H`$G`$MC$ C$``$k"C$:N#V$N#A3K"uC$qf"#C$gf"cC$lC$nC$of"N#C$mW$]W$E"W$Z"W$KC$gW$`$MW$cW$ WC$H``$dW$o3G!BC$EC$?C$zb$ZC$JC$RW$o`$V_$m_$K`$eb$Tb$C$_`$Lf"sb$@b$~b$Sb$Rb$QW$S`$cc$Wf"K"C$Lg B"Jl GLy!S^`\ap VAD mW[mDx Ri k_tJ[y!Xxgf qvx! ge ~ty crN SUt h[i ouu d Cy!q u Q x JP P Z gf Z u EH A EL M ER Y EN e EO q ]} O K  ]X ] f o s s L ON u] Ml ) S{ ] J _|W ! ] S R` M Xn DF ] T `a  ] o R| ]J Q# X &g ad 3 SE 8 ] T >F a f` Do w +I M GQZ tk _z tmH c ]V z ojF H# T ma qo O ~ d K i X c e I r ^~ qL 7 P Z  Rf s t p A B N  M[ Z i V b v aC  OR m` cn < ]| P J g VW g Ue +ws AA m O q \ y!i G Q w T D PQ Z"` P o CX { s H s ZU y!c @ q Y } g NJ o X {h e Lq fo 2N Rk roy P H YT +P c |P p CT } 2P J ToV " P e y! r T P O  W \ r y!i P w fD 7 d S Zo` }o  O ~ j mK FY 9 P g |p# t o@ B AN P \ u" i s v \ C P P X" \ _ i pov RE P S m_ KP m Sy z OI P W Z P d P p  } } ^" K rX CU h pu mC cQ j_ {n & _ | sZ I |U F Ac Oq h @ r C M P Z ) U g Zt I$ C ) m P U ] P j Z w P D I Q CI^ Zm {P | = I I i P V l!c Rn# q m!~  RL I Z p f P s N P M g XY g Zg s}# u h A EY# M  RZ MC" h 9 B u CKB ^ {P K ^ h k  ]w P E Q R c _ ml ) T z UG | U P b CM n k { n H h BT h b \ Bo h jq o P  |L e Z J E!g CIu G | J B V P c u^" p u } & y!J _ X & pe T$ s 7 B  T P L t Y  Rf g B"t P B vE!O m] fp l fEy P H Tp T c a An s| TL G[ U i ]" v p P C  ~ P U^ TEl }{ [ J pp W n Sd |!s Y A pEN p ] zi Zp x Qz!E {S P b  P o o{ zJ ZEX rp g ] t FA rEP y! _ J#l \z IJ a XX ]p A~ |y!M M [ @h Jw c P E n [ R |U _ ] P l wU y E Z$ S > j` Jn K~ LM C\ K^j B x p G" E T R _ Eo v } kJ 5P Y Re ^ u S A K c N & e[ K mi o mw o cE M P S z f ` z {n Q$ | v! H 7U ` x!r y @ u!M P [ y!g zu xC t!R P ` Z l z x P E sR G a \ n s Y { E e H A T i Y a pn I } P J oV Me e {q g W F" M P Z c f g Ys mA JO Ci ] Rj b x  y!E .Z S . y!_ Jm G P | ML$ I  IV pd F s A @  P M .Z Z Ag CVu > { C P P A] P k .g x q"E M# S +_ ` F# m uP z h  G 3 U T P a x n h {{ i I  ]V Z d Mq h k@ A N 5 } [ 5 | h P u Z A d M v Z g g uB s I! @ I# M q Z eJ g ~ t A A * y!N ^ y! \  M i ov n Z D G Q aZ ^ \ j c v A C G g P G \ n i A v t vC ZA Q cS^ C l R y t wF y!T e Zb Gp t x~ P L  D Y Z f \# r o  V ZL Z [ E I g Gt y B kN g ] N j nv X" E E } R Z _ Mb l ` y cF p N U Ua < G o B k{ w G f RW s g Q@ g PN  | \ Rj  z x r!F NT j|# b s!n a| RJ XX o Z" f g {r  y @ P N @[ kj wy P H G U Xb o u q o Y ~ U K v P X y!e J$ s s P @ : P M : AZ z h ? P u ? A B I O z g \ d F j d Ew d DE z h S z i a j _ o z j | e P J C V d @c d q d ~ WM K\ Dj Nx n H Ky!U Kt c g p  d } K P J > x W w c |] p O P } I y!I BW Ie Hu FE Y y!S b P a Dn wR} BK w y!Z  G h  G u Z B G N G [ z h < Du  GC d c Q y! ] jj r x P E  ^ R P"^ ^ m a$ y QP F P R G_ ] U  m [$ z Y$ F > P S K ^ ` m l u y IF v YT jh m cL z cD G z ZT T [ b 3 In i y! | P I zV A d d Aq j T  @ L d BY & f g }t |C g ^R g p` d Cn P |  B H wU Je ux" u PZ B v N N DZ [ ug H v kC | v" R e_ d n u { c H x U P b X" n y!{ VI PX W g y! t GA I P X] U"k Ry M G  K T n P a g On  D | BI 6 _ X Ae n Y t _ W A _ O N @[ }j H y!y x J G |S g Rb wp v } N l[ U j c J w kC x R i_ U n g{ j L 3 P Y BW e j r ) P  g SK BaY Z" h 1 G u  P B H" O Z \ B i ` v w P C w u O ] \ v V i JRv v G" D y!Q R_ A" m A" y f E a P R }! ^ F z k $ mw k! E l R j!^ i!l P z  ` G P T h!a g!o f!} e!K  ` Y d!f n t t yA +|O c!] {k my AG t zU V!c t { q U!~ T!L i  Z S!g  G u  J  B ` C N P Z Q g G" t < P @ E mM x [ [ x \ i ^ w x ] C x ^ Q x ` _ x a m Q { P H P T vD!a P o U$ | KI J W  Rd P r r ~ P K P W Ke c mP p YP |  U H z q  U  S b z p o |\ } m J z o W z n e c s  t @  u N  v \  w j P x P E z m Q :_ z mn  S| [ J   W  C  e P r h ~ cK z l Y z k g vF! u j c B &O c^ Q l x b y u G c S ` w L o } {L L!Z W h \  t M!A aO b] O!k P!y Q!G / P U ! B a m n n { P H R!T v Ob p s   N  L F Y  G f  B s &M @ y N C[ [ zh  f v  hC ^ S | ` P l B x P E P Q  g ]  [i S x h oD P R p ^ n!k 2 d y mF i T P a o!m p!{ q!I 9 h W 9 i d 9 k q 9 l ~ h lK X P Y Ae ] G s t  V" L g c Y k zf g Z t p b @ o U M cZ Ah Y v X C y P P \ S h  P u | Z A  g M g \ Y  y!f  _t % X C g [P % | ^ % y!k  ~ y y!F  Q T g ` y!m P { I" G P S ) { _ ) @ l Ty Bs H D" U Qb 5 y!s RA sR G V a BS  n k { { H BV U p b w o y | C" I > Y U w b u o P { ~ G C" T n v ` H P m _ { y _ D" F D g# S  P ` / i#l X z  HG n Q U Ea P p | | ] I FU Id P r O~ C M P Z Z f Hs Y Q X" ^ pk P } u" I P V y!b P p Z } _J _" X {" e h q  s }  GJ Y X C e v G r u"  R" L BY m v n C Z P :Z \ g P h P t  u @ o"M g"[ t i o G v r OB u{" P E ] Z j yv  P F c R P _ & P l ~ x mE e mS ; c a c m i _ z K# G GS ]g MS# u b B d }N ]# \ " e i 3 a v J B [ P N V P Z V b# g V l t Mt"@ V P N MO# Z i Rg E O#u s R C b P e ] z R i A5v P k L w c D dQ cP _ y!l j{# z 7 c" G S P T G$ ` Nm M$ { XH  x V d c m c p k CS j {V  Q P ^ P k P P x O UD s R  c" _ n k A" x P E P Q P ] r j P w O C B O _ \  Ui l w ` P D n Q 5 X ^ t j F w e D |  Q _ ^ _ k ^ x ~ E M Q ^ _ ~ k P w ~ D [$ P h \ ^ i T v N$ C F O ~ [ K$ g ~ s m  wT L XY J g |^s w vA o# w |_ D o# Q mb ^ YC k Kg x cC" E Dd Q <Q ^ z k Rt x WR D WS Q :k^ :^l :r z b F QR R QS _ C  l (Q y m F H S &k` L n &^{ L I &r V L b Q o k| ^J r X Q d kq ^ r M BjY E b C Q P V a# ] kj ^x  F ^ S DI` r i V# u C` B ZN ^ ] O i jv W D { Q P ^ G# j lw CJE P Y ~A e _ r P ~ Q H m U Q b r o u | P I _ U J |"b J Ep c ~ mK v Y to"f ICt { w | D y! Q x ^ uy" j u\" v l y! C +v P _ Y ] _ H j mP v / { B hc O hm\ / k# j x v Q C P P h |] g Y ^f g _ t _ A g eN g j\ g ^j g kx n F g T# S i"` "{ n "t { P H c" U g"b g p ^  P K i W Ld d r z~ k M ^ Z _ g w ^ s z d N _Z jh iw Y ^ F m R P _ { l s x I ^ D J P > m] b" l > i y `" F c _ S u" ` r P m s A z \G XU Oc a q q ~ qK y! Y ^ f y!r \" @ AM _ [ Z h a u ^ B m N s [ h ih h Ev h mD h nR u" ` z l J" y _ F K S A` Y" n L{ u" I ^ V w c b p P } ~ J P W K d B q C} ezL  G [ Hg BQ" v IC Z R  C ^  h k By!w  x E  K R  p ^ _ k u x R D Z Q  p ^ v @ j v A x v B F v P T v C a v o % Q ~  Y J p P W * X d * | p ( c } g c J i W W ( c d 1 X q c B" ~ w K K 1 y!Y o g i ^ t i Y A X ^ M ) p#Z S ph w iv B C D 3 p#P M ^ ^ < V k x Y y x Z G x gU 9 n c 9 b p 7 _ } x _ J c PX 2 cf 2 b t 2 h A I N 2 e [ ` ~ h 2 d t 2 b A " t N H [ d c h Gu d c C z FP E^ z Em g { x PH  g V i c b!p a!~ `!L C Z  ^ g _!s }  A } HN ^!\ ]!j \!x [!F Z!T Y!b X!p  w ~ } IK } JY } K g } L t } MA  } O } \\ } Nj px qH rW vf y t r @  ^ M c Y c f Qs q C H P t" ]  x j t" x n W E  { R Z`  r n ^ { n Y G d T W! a Yn ^ | e H h U b b B n c z c G c T ^ a ^ n g{ B J vC!V vB!d br d A O M vRZ O i P v P B l N cZ b h | u z B EO B ] Fj W x c E b R ` _  l ~ y CF k T  t a w m q z L G ET  t c mp P   t K U X r e s r T  S L z Y C f  p s { @   M D Z Ef Ft  n B  g O  \ j i  b v  cB  _ P h \ 2 ` i p l u d P B " Y N " f [ $ c h 9 f u x c  A 9 g N > v [ T Q h T h u M k B > RN > S\ M l k : l x E c E M m R w R _ w Q m G U { B l H 3 WU 3 T c N j p w P } w O K S q Y w N f b ^ t w M @ c UN 1 | \ i S i k y v 1 Z B ( P O i U [ g P h s S u ! e B p | N ! O Z ! r g  N t ! AA s U O o W \  |i v G  w v F D v E R  X ` v D l v ~ z ^ G @ S A ` v m v ~{ } I v }V v |d v hr F @ Y L  @Y  F h j u L B BEN B` \  P i Nv  B F BT S F _ f k mx c F { S Y ` B m Z z } G ] T r a f n e { J G o T Y ` j m Z z n G X" T g a H n Gz J H j U k b h Fo F } _ J jW ke p s ^ @ Y M X Z O g @ t L A y!N D \ A h ^ t J @ L M q Z w g L t k @ ^ M B Y | f d s A @ OM T\ @ k Px  F ~ S v s ` Ol v J { OH Y V Pc r K q X" ~ d z K ^ X K g d > ^ q > l ~ > k K > M# X \ e ^ r W ~ O g K ] w X ] L" d @ q h~ RM Y [ v h c" t Z A  g N X" [ l h u" u M A  mO Z" ^ g k >:Gx m XM o [  c h _ u & o B 7 c O & g \ e ci & _ w c D Q Q "m_ "cm j"{ ` I 0| V CM c <mq <c  3M L FmZ Fc h o"u h C c" P Rm] jk y!y RG L U Hc b Hmo Rc } M J ]X ]g P v LB  FP [q ^  P j  \w r"E  XS mr a . P n mg z < H! G _ fS _ Ka H g o H r | h I s"V P d >c Nq CH  mL | Z c g I t Q @ tM M h [ P h | u tz"B P P Q ] P j s v c C j  P S  ] c j P w n C RP l ^ |Uk ~ @ g M }UY f n K{ s J aV `e y!s 2J A 2L M 2RZ cd$ h W t KW @ \W L \O Y \Af ~  t }  ApANR\~IjCKxc F ASZ aCZ nC\ {c Hz UC] bC^o&r}C_ Km  Xl  ek  rU#bMa[Q i_ vEiCq lV yjF^# TV `#aT# oq |RoH_ wXDR Rfn _o b lT  yS  Fo aSs ao Q mo _ zbGaUQ c_ pe# }q JV WjdcO rcY T# Lq Y. O e_ rXR MTn ZK b gK atK Q BK _ Ob\ajQ x_ Ef Rq _V ljy! V G-U TT# `q mH y_ FXSR apn n' b {' aH' Q V' _ cbpa~Q L_ YH f&q s&V @&jMH [H hT  u&T# B(q OQU [(_ h(Xu)R CZn PL  ] b j aw Q E _ R2b_2am2Q {2_ HQT UWz! b:q o:V |:jIWU WWT d:T# q<q ~cM J<_ W<Xd=R rrn b LaYr gQ t_ ABlNBk\Di jD` ws Djy# QKf^z lp yK_ FjL SY[ `@$ mm[ z|` G@$ SE$ `|h m_ zF$ G_ TU$ aH$ nT {W HBf `U` B uU  B` l OW\W k] xT" ES" R` _M  kL  xb Ed Re _h lG  ys FP S` `b ld ye Fh Ss `P m` ^ z`  Gz T} a~ m q yk  Fm  Sn  `t  ms  zr  Gq  Tp  ao  nu  {v  Hl  Uw  bx  oG  |E  Iq  V`  c_  p^  }]  J` W[  dZ  qY  ~X  KW  XV  eU  rT  R  LQ  YP  fO  sN  @L  MK  ZJ  gI  tH  AG  NF  [E  hD  uC  BB  OA  \@  i v~ C} P| ]{ jz wy Dx Qw ^v ku xt Eq Rp _n lm yk Fj Si `h mg zf Ge Td ac nb {a H` U_ b^ o\ |[ IZ VY cW pV }U JT WS dR qQ ~P KO XN eM rL K LJ YI fH sG @F ME ZD gC tB AA N@ [ hy  ub Bz  Od \e it vV CW PX ]Y jh wP D` Qb ^{  kd xh Ee Rd _b l` ye Fh SP `h me zd Gb T` af nb{W I}  Ve c~  ph }  J@  WA  dB  qC  ~D  Kh XE  en YrF  KH  XI  eJ  rK  N  LzYO  mP  zQ  GR  TS  aS$ nT  {o XHU$ `f mP zNFV  TP$ aU$ nf {M$ HW  US bU$ of|H JX  VIr LcU$ of |t b IY  kP xU$ Ef RD$ _Z  lA$ y[  F~ Ss `\  mt z]  GA$ Tt a{t nsY {^  HsX U_  bsW osV |st IN V`  ca  pb  }jz# Jc  WmU djt qd  ~ x K y We  dYU qRh ~f  KM X|nej Sg  _m lO yN Fj  Sk  `R mEX zl  GD} TD^aD_ om  |n  Ij Vo  bp  om |rdIq  ]r  jt  wu  DO Qv  ^N kR xEV Ew  REW _x  ly  yz  F{  Sj `m l|  y}  F~  S  `@  mA  zB  GO TN aR nEX {EY Hj TD  `E  mF  zG  Gm TZdaH  uI  BJ  OK  \O iM  vN CR P V ]N  j W wO  DP  Q O ^R  kb.x V fS  sU  @V  MW  ZX  gY  tZ  A W N O [H,h! W T! O aa3n[  a\  n]  {^  H. V Updb_  v`  Ca  Pb  ]. W jc  we3D[ w~ Dd  QM ^e  kf  xg  Eh  Rf# _S#li  zj  Gk  Tl  am  nn  {o  Hs Ud# a c" nE M zE t" GOGTp  [q  hr  us  BH yOTdHt  \u  iv  vw  CMM Px  ]^j_zV U#Hy  VCKcz  n{  {|  H}  UR HbEZ# j~  w@  DA  QB  ^C  kD  xE  ES iRFl{a gT a-tU y#UHNF  \H  iI  vJ  CLPfd^L  rM  P  LV  YV `fW  FW FSX dYY z}Y  wZ qDZ  u[  B\  O]  \[ viW _^  k_  x`  Ea  Rb  _c  ld  yO F] HSW [O g^ Kt_ ]e  \f  ih  vi  Cj  Pn  ]o  jJ I wJ J C&MP&[ ^J K jx wp  Dq  Qr  ^Y kH xs  Et  Ru  _` Ll@ x&F+ES pT }&B@Jv  JU W&~7d A[tX it\ uABtL Pt ]tT jL wc Cw  P}]c ka m#xCze} sW }T Lx  Ymfy  uz  Bu^ Obp \u ivAvM DN P{  ]O jT wnADcp" RX _\ k  x@ EA R\Y _\H lQ xZo E RDC_T b[xoX L XB eC rT 3\ LJO XJW e3 r3T C\ LC X7b e7@ rCT ~D K0n" XE eP rQ ~R KG X+c e+d rJC~ Ac Nd [H hI uJ BK OT \S iQ uV BW OX \Y i vL CnPICdT g\ tGC@ CT PFC]M `z mN zB G W T O ak n W { J H W T Xa [" p \ } c J \ W J d F q K ~ H K O X W e Y r H  e L d Y c f \ s e @ S M d Y c fECrO u i BP OB] \Q i{UvR KS XT eU rBU zULW a i nY {BX HyUUCCj[ m] z_ Gb TBY axUnc CBZ Pe ]f jg wB[ DBCQB`Th bi ouKA| R Gj Tk a F" nl {m Hn U ~ b s o u |s T Ii T Vb v cb u p_ G}b Z" LX s YW { fX G" sM g @M n MW | ZM b gW } tE c AD \ ND { [D Kh: B v= c B7 c O6 \ \6 m# i6 Xv2 P E. MR3 c `/ \ m3 ` z/ KG' j UW ~ bW  o ] | XI \ X m# e j r k  Y L X YW @ f j s S@ d$ N { [ D" h k u Y B X O J[ H j m w K D o Q y! ^ v k I x q E ~ R p _ Pl o {W A HW B U n b j o n |g I [^ l gx MtH j Bw \ O; D \w ]iw ^ww MEG c" SG P `7 b m, l z! } G O T W a y!n O | _ I j V  c D p C }>wOJ B Y A f ts @ B  O>"X\ } t y!A ~ O  \ } i | v n C { P y!]W D k r x>MAEIs FIq SIK _ j  Y LG>2pY jIIaW\>Hru lg _ u_>7vB c x c E P R X _o*>Qul g a r n y!{IbI j gI\t~ s R B _~ u lIayI^Ww W uI_Br M`i V nY c {i W Hi X Ui Y bi Xoc y!}[ r K] RX] O f] W s[ g @V P MV c ZV c gW Y tW H Ac V MS lZc W hP juc X Cc Y Pc X ]I Lj> Z" xx+"BEI LGB pUB ^cIo q> LQB X_n L mn g y> nFn o TntaO LUD,Mqcn _ TK L aB m nr+wQ{J,2GLU W SU O _M,HIlU Ru_*qCP,7Mb`)oB g XSten pYU,QLgB o sdY@H:VYz[ fon ^UyScLCv]Jyv Cu Ot YB L cY Lo{}Ux[yMm L FD8RIl Jzd M jISwIn TZ[td ApOIw" y! \d RjivtS L G y! Tq ^a y! od ES|w LOd 3]q pP9&S^~ L qd ~f} L c y! pmL}IlIHd N g y! u9X %Bd Jg L qGd 2<}H9:fy y! _zsl9n_^Ig I p K | | I_qVamGq t\OC M Rb _ y!^JwlIhcWSA L Tzg R`T?r | q M ~g VHK@xS y!KIj Yg ftyg CI m L vR{C y! ~ L K_q X L Iq T# Vg yhcg JK L UGg 29aq kZg zNh L vg |NB L PuS] L pg }N} L K L XI~dHg Tb`vIkku L `Ir mq ^zjfHq jnq L |KcH L kCu_wsERV L hI?t L QC^^L\|sbNX L fs!=rzh Poon Lm h yR{ LBM n`O L o H | h |rI ^ { seIH ILQ wPo h k} MU| SnQ L  L L kYY h FCr N_u LT sjqb  [S j b L o J" | h _@H st_H oVg C{| } L y h JF L P h Mv\ L R Oq_ Hh p P Pe@ RS e sw`x Gh 29X IJQ C{Fo  L u!swB! E y! L F!T{S!zt p N!CxM~! P K!Ht ] W! L t! q A!sHQN! L _!CY}l!sQHi! L q!Iz~!stI\! L e! | q!h~~!t]=|! L y! | F! L S!zv f`! L F!v G[S!tbWn!epE!v i}u!# L r!v |`~!% L ^!h&k! W Q!tjb^!lt @!* L t"_v 7yA"Czjz"toGd") Lk"\v H3y" Xl"1 L z"kW G"H ^"tqbg"5 L I"C~\ V"3 Lr"ttg@" Lg"ggu" O\"; W k"; O x"[*gcE"zw [h"IC"CVzW"JQ"Kd"Lx"w BVG"B |]"Mk"Hw j "Ni"tvp q"Gw 2Ma"& L n"O{"PC"QK"tk4S"tSG"T R c"Ro"Sw"T"UW"MPf"V v"WC"XT" Tf"> L u"zx T B"YV#Zd#x Iru#\x H3g# C Z#x F@f#[f#\v#ZWI#_x 7A`#Hza#Im [#nw{#x Jr#h L |#Hx p H#] x#^E#_S#CUA d#` e#\rr#ad#by#c I#dU#e d#f o#f X{#^WI#D{`#k [#l h#f \s#sbA#z Uc#zz tk# L _#f C l#cay#f OZ#z Fhi#f L Q#z F4]#z NlQ#IE}#z[#z OWc#z @[z#{U#|]#z 2e#_z 7}W# \T#[b# X a#Hz cn#}Q$~Y$z Ja$ L k$Gz 29w$zQp$A$^I$][$\n$rF~$ L D$peQ$OLv$meB$ L g$R]t$[ Q$ L [$g L g$I2t$r,Q$ j }$z ZJ$IQd$ IUB$ FvW$x,M$ NKy$ JD$ L N$ PZZ$j`t$n ~ T$H C a$x~d$CBHb$_ 7Aj$?Rk$Pr }$ L o$\ H3|$ Lo$Sc}$ g `$dGm$ L t$^O1A$ r P% L\%~Pj%bAz%zvy {%cat%vJU% L _%_v7Wk%aqB%Es% L x%Hvp E%e u%rP% P O%vsw[% LR%u`%vI\n%dXJ%ryb%vkL[% r g% g s%z o@% L o%AL|% L H% J_s(CrR( AWD(Zq[( {}L(PqI(Qz(+y P(_ 7r\(RN(Jmb(SO(eXh(`,^E@(g ^ E(TR(H P f(|^v(v w T(U`(IXq(V I(W S(yV]( ~fs(w L Y(X f(G` p(Y P) U{Z)| L U)Z b) [kl) L W)Uv d) BcZ)kO})X0L) P |) L I) GaU)[ v) L @)\ M) 3W)b,_CJ)lCM) L P) Ed \)tK@)0+K)] v) L @)mM) L z) L G)|LT) L `) @Jl)nO v) L E) feQ)VIv)J )G 2_I)^ h) Jr) L |)$H)vUG)K \*_ f*wUp*E\ E*` a*BN" k*BO" v* L C*aP*D~X*SV* ^* L ]*Tj*Ur* gz* L a*gn*Bo U*Va*e_i* w*WW*L$_*bC*BnK*Bly*aje*cO* LW*Aje* P O* L [*Z_h*[kG*Bar*l S* L]*b k*dv*a H*zS*BTgM*et*` G*_ R* L]*{mk*f X*_e*`m*gu*B1PN*^ ^*hi*] E*\ P*l Z*i d*ao*zlw*X'xkc*bN+cV+d^+ef+k n+fy+BMDA+[E+u]M+ L j+gw+B=%+Zd+B`zl+m f+`iq+ L Z+ Lg+Yu+KX}+y U+l ^+l h+Xr+Bqqz+xk+W|+V D+w N+n [+r f+g r+B&S+M R+[Q\+L z+p# G+Br*T+~U~+L S+R_+1V m+1W z+1X G+1Y T+A`+Un+@UC+AUX+dm+e:S+`NR+PZ`+Nz+\ H+U U+[wb+BU@+CUU+DUj,GN,e:xM,k3E,PVx,/p# N,/R[,/Ai,/Nw,/\ E,/U R,:]_,+:G|,CqA C,2W D,2O Q,e:s^,[uQ,qRo,>P A,cqRM,0X _,0Y l,>c x,EUE, PZ,Jt j,Cdv,eC:SH, h4[,PCZZ/>c t/[7A/Gl ^/JH k/JY x/FUE/3dZ0e3:Yl0h4JE0P3ZO1[Hi1Ij G1[2T1qxq1CCi1:nl1NZ1hp1dL1e:Y^1XA w1PJD1[\N1U_ k1[Kx16h4QV1 h4@g1}5g1ZL\1iYj1GUC1^IX1Ma1Nr1dH1hZ1e:Dv1h4C5z1qS}2nL P2cW \2cX i2cY v2PFB2o H2p S2th4NI`2qn2Qq|2h&6B)m3pL V3k&b~Oc3vLr3w" @3e:bI3Bd | k3qqg3e&bFLX3bqd3uX r3uY 3h4xL3r D4}h4pQ4Bb bA4&]`c4 h4aC4wdd7PXH7YV`7sv7Lu7&KZC7e}:G]7Ld7&UZr7lfL7&a_r7L Q7&q.^7L L7tQ Y7&w.f7 L T7tNa7tdw7thI7et:oe7 h4xT7qt=L9 L I9 LU9 Ac9Y q9PtM}9A J9x6W9L M9Q?Y9&}5X9&^}M9KCJ9vKM9J @# X9J A# b9J B# l9J ] v9J U A9J m L9J S  W9J C# b9J D# m9J :ly9 h4e9fDEd: h4ki;Ca T<pI_<J qEh<7tm<fWua<tV<J jd< h4U?N<sc<J IXq<sI<qDY<k^]<fL{<Cb Z<vh4Le< h4wq>fOQh?HCy?z h4V|?fPRAx h4fbAfgDHBw h4I~LBL#UCKV]CS/sCL bCUKoCCc zCt'ECL lCv h4}xCCd uDt h4ZDCe ~E aDHECf LETDEVEL [Eh h4IgECg pGg h4s{GTWunIeocI2ZRIL lIt yILCItRId h4I`IsiKTLwK;|VK[ h4` RKChrKTOQzKh4SNKKBQ }^Lh4D3[LBh4c_LuBNIh4PJNv ZQ&h4tcQh4n?WRh4~uER.h4| CSLSTPNSTgD^Sbh4r%bSCi TTh4FN]TBJ icTCjLT>h4ETTh4ntYUdYGVh4B1`VBI wbWCk YWJ h4W[bWCl yWpDECWCm HW9h4j,QWpWu{X tpX+h4X~X-VX sCXpLQXCn pXpOQyXpPJXh4bZXh4n |\JC cj\pgDM\Co Q\cL ]\Cpi\D3q\Cqd\Crl\. L t\ZDE@\CsE\ZWuM\yVB\! L X\#td\#sr\ZL@\C_\ L g\ZOQs\Q  D\ZPQ\ZgDa\ L e\ch4Wq\3h4v,H\E{~\Czy\CtA\CuI\CvQ\rDEY\BA A ^\Cw_]rWug]8t\]8sj]rLx]CxW]rOQi]Ch4w,z]rPq]rgDA]CzE]C|W]C~g]BL y]W~E]B@ [C]NN^]Gsl]C@_]CBo]CD@]CFP]CHa]GVq]w G]CJR]L a]V#n]x G]ZdR]ucXv]Th4T{N] Db^YL f^Zh4Z{s^XCM_IhP_OTx_~ DL_CLP_ r#-d_ s#Q_ t#g_ u# z_ l F_ v# P_ w# Z_ hDe_cL i_fh4M{v_} BC`ph4S{E`y Xaz ba pHlamL taL AaPTNajL baCNoa| Darh4I{Ca{ DLb iPbCPybCRHbC_XYbCgdqb|L UbsL bbCh4{ob [jhDsEi{L xih4c'Eih4mhi UkUikI@iVcIiZliL tiL @ih4H"Mi tkUiCWk@iL kj<Bxj=zjL wj{ Dj nhOjL wjJRDjvGVjL ]jh4|jjh4Sfj4}ykL vk\+Ckh4knkwGYlL`l ^mnlh4f [lCfAvxGgv| nv NwwvL nvlF{vQKAvL Lvh4YYvL rzh4X z-nWz vREz}JWzh4O azL pzsh4Y}zh4LV| t7b{aYL z `UFL [XahL I hSUC| hL d WXpL HL Uth4Zbuh4T"|ׄh4^ P[h4Zrnh4sH؏yH{ XdCFIgL ph4{,}ERxٖk}Jۖ \|GݖyNCߖL QvZ]L wfDL R ZN^\^lL J6VL LRBXCpBZL \h4N5iBe Twh4VK #aaӗd yB՗CI {֗` A Dh4s,Q }fDBb fjL P IO\h4[kL F tTR` @ fL s S` L RL _`Pls|