Difference between revisions of "Single player ladder"

From Oldunreal-Wiki
Jump to navigation Jump to search
(Created page with "{{Stub|reason=Finish and polish!}}{{Infobox Tutorial |title=Single player ladder |image= |topic=Coding |order= |series= |previous= |next= }} In this tutorial we'll see how to create a single-player ladder. The process is very complicated and involves a lot of (unnecessary) subclassing because of the amount of hardcodedness there. __TOC__ == Planning phase == Your first step should '''ALWAYS''' be planning the ladder. This saves '''A LOT''' of time afterwards. Just so y...")
 
 
Line 4: Line 4:
|topic=Coding
|topic=Coding
|order=
|order=
|series=
|series=Unreal Tournament
|previous=
|previous=
|next=
|next=
Line 2,459: Line 2,459:
The last thing to be done is to create the interface from which the new Ladder will be launched. So we create new scripts called "MyMenuMyClientWindow" (subclass of '''UMenu.UMenuPageWindow'''), "MyMenuMyItem" (subclass of '''UMenu.UMenuModMenuItem''') and "MyMenuMyWindow" (subclass of '''UMenu.UMenuFramedWindow''').
The last thing to be done is to create the interface from which the new Ladder will be launched. So we create new scripts called "MyMenuMyClientWindow" (subclass of '''UMenu.UMenuPageWindow'''), "MyMenuMyItem" (subclass of '''UMenu.UMenuModMenuItem''') and "MyMenuMyWindow" (subclass of '''UMenu.UMenuFramedWindow''').


{{Navbox Tutorials}}
{{Navbox Tutorials|series=Unreal Tournament}}

Latest revision as of 18:30, 26 July 2022

Article stub
The following is an article stub. You can help us by expanding it.
Single player ladder
Topic Coding
Series Unreal Tournament

In this tutorial we'll see how to create a single-player ladder. The process is very complicated and involves a lot of (unnecessary) subclassing because of the amount of hardcodedness there.

Planning phase

Your first step should ALWAYS be planning the ladder. This saves A LOT of time afterwards. Just so you get the idea, here are the properties of every RatedMatchInfo:

var() int NumBots; // Total number of bots. In team games this is the total amount of bots, i.e. for a 4-on-4 game, the setting is 7.
var() int NumAllies; // Total number of allied bots in team games. (Only for team-based modes) For a 4-on-4 game, the setting is usually 3.

var() float ModifiedDifficulty; // Difficulty modifier (0.000000-5.000000)

var() class<RatedTeamInfo> EnemyTeam; // RatedTeamInfo, a.k.a. who's the enemy team. (Only for team-based modes) It uses the class name.

// [8] means "up to 8 bot individual settings"
var() localized string BotNames[8]; // Bot names. Only for non-team-based modes. ("Malcolm", "Aryss", "Ivana", "Nikita", etc.)
var() localized string BotClassifications[8]; // Bot classifications. Only for non-team-based modes. ("Tourney champion", "Aircraft pilot", "Actress", "Prisoner", etc.)
var() int BotTeams[8]; // Bot "team color" settings. Valid values: 255, 0, 1, 2, 3 (neutral - red - blue - green - gold). Only for non-team-based modes.
var() float BotSkills[8]; // Bot "skill" settings.
var() float BotAccuracy[8]; // Bot "accuracy" settings.
var() float CombatStyle[8]; // Bot "combat style" settings. Negative values mean the bot will be more cautious while higher positive values will tend toward aggressiveness.
var() float Camping[8]; // Bot "campiness" settings.
var() string FavoriteWeapon[8]; // Bot "favorite weapon" settings.
var() string BotClasses[8]; // Bot "character class" (Male Commando/Soldier, Female Commando/Soldier...) settings. Rather than the "localized" name, it uses the class's name (i.e. SoldierSkins for Male Soldier, SGirlSkins for Female Soldier...)
var() string BotSkins[8]; // Bot "skin" settings.
var() string BotFaces[8]; // Bot "face" settings.
var() localized string Bio[8]; // Bot "bio"/"description" settings.
var() byte BotJumpy[8]; // Bot loves to jump (True/1) or not (False/0).
var() float StrafingAbility[8]; // Bot "tendency to dodge" settings.

You can see these values in the User.ini file for some examples.

As for the Ladders, these settings arre found under the Ladder subclass. Here are the settings:

var() int Matches; // Amount of ladder matches for verification purposes.
var() bool bTeamGame; // If true, it's a team-based ladder. If false, it's not.
var() localized string Titles[9]; // Up to 9 ranking	Titles ("Untrained"-"Contender"-"Light Weight"-"Heavy Weight"-"Warlord"-"Battle Master"-"Champion")
var() string MapPrefix; // If all maps use the same prefix, this is specified here (e.g. "DOM-", "CTF-", "AS-")

// [32] means "up to 32 matches"
var() string Maps[32]; // Map, without prefix if the prefix was specified in MapPrefix. ("Oblivion", "Stalwart", "Fractal", "Coret", etc.)
var() string MapAuthors[32]; // Map authors.
var() localized string MapTitle[32]; // Map	Titles/names ("ITV Oblivion", "Fractal Reactor"...). The "localized" string means you can output this to an .int file. (And .frt/.est/.itt...)
var() localized string MapDescription[32]; // Map descriptions, usually for lore/storytelling purposes.
var() int RankedGame[32]; // After beating this map, the rank is updated to this title.
var() int GoalTeamScore[32]; // Score limits for team-based games. Doesn't affect AS maps.
var() int FragLimits[32]; // Frag limits for non-team based games.
var() int TimeLimits[32]; // Time limits.
var() string MatchInfo[32]; // Associated RatedMatchInfo. (See above) Uses the class's name.
var() int DemoDisplay[32]; // Specifies if this map is for demo display only.

var() class<RatedTeamInfo>	LadderTeams[32]; // Up to 32 teams (RatedTeamInfo) can fight in this Ladder. Here their class names are specified.

var globalconfig bool HasBeatenGame; // If you ever wondered how people can have Xan as selectable without having played the Ladder, here's your answer.

Finally we have the RatedTeamInfo, the specific team infos.

var() localized string TeamName; // Team name. ("Thunder Crash", "Iron Guard", "Blood Reavers", etc.)
var() texture TeamSymbol; // Team symbol. Must be a texture. If you don't know how to import a texture, you can use the already existing team symbols.
var() localized string TeamBio; // Overall bio/description of the team.

// Up to 8 bots per team.
var() localized string BotNames[8]; // Individual names. ("Malcolm", "Aryss", "Ivana", "Nikita", etc.)
var() localized string BotClassifications[8]; // Individual classifications ("Tourney champion", "Aircraft pilot", "Actress", "Prisoner", etc.)
var() float BotSkills[8]; // Individual "skill" settings.
var() float BotAccuracy[8]; // Individual "accuracy" settings.
var() float CombatStyle[8]; // Individual "combat style" settings.
var() float Camping[8]; // Individual "camping" settings.
var() string FavoriteWeapon[8]; // Individual "favorite weapon" settings.
var() string BotClasses[8]; // Individual "character class" settings.
var() string BotSkins[8]; // Individual "skin" settings.
var() string BotFaces[8]; // Individual "face" settings.
var() localized string BotBio[8]; // Individual "bio"/"description" settings.
var() byte BotJumpy[8]; // Individual "does this bot jump a lot?" settings.

var() class<TournamentPlayer> MaleClass; // Male character class. The class name is used.
var() string MaleSkin; // Male skin class. The class name is used.

var() class<TournamentPlayer> FemaleClass; // Female character class. The class name is used.
var() string FemaleSkin; // Female skin class. The class name is used.

In all fairness, you should start first for the individual gametypes, then for the maps for each ladder, then for the bots and bot teams.

Then you should create a new folder in the root folder of UT (e.g. "C:\UnrealTournament"), for example "MyLadder" (e.g. "C:\UnrealTournament\MyLadder"), and a subfolder inside it called Classes (e.g. "C:\UnrealTournament\MyLadder\Classes").

Now we get the easy part out of the way: create all the needed Ladders, RatedTeamInfo and RatedMatchInfo. I say "needed" because you can reuse stuff from the default ladder (I did reuse all the teams and some RatedMatchInfos for a custom ladder I've made). Here's how a custom RatedTeamInfo looks like:

class RatedTeamInfo9 expands RatedTeamInfo;

// The Corrupt
#exec TEXTURE IMPORT NAME=TLPhalanx FILE=textures\teamsymbols\TLPhalanx.PCX GROUP="TeamSymbols" MIPS=OFF

defaultproperties {
	TeamName="The Corrupt"
	TeamSymbol=Texture'Botpack.TeamSymbols.TLPhalanx'
	TeamBio="The Corrupt is Liandri's sponsored team entry in the Tournament. It is entirely made up of the best advanced AIs that the corporation's wealth can buy, and features the reigning champion, Xan Kriegor, as its leader."
	BotNames(0)="Vector"
	BotNames(1)="Cathode"
	BotNames(2)="Matrix"
	BotNames(3)="Silicon"
	BotNames(4)="Divisor"
	BotNames(5)="Tensor"
	BotNames(6)="Function"
	BotNames(7)="Enigma"
	BotClassifications(0)="Slave Warrior"
	BotClassifications(1)="Slave Warrior"
	BotClassifications(2)="Slave Warrior"
	BotClassifications(3)="Slave Warrior"
	BotClassifications(4)="Slave Warrior"
	BotClassifications(5)="Slave Warrior"
	BotClassifications(6)="Slave Warrior"
	BotClassifications(7)="Slave Warrior"
	BotClasses(0)="BotPack.TMale2Bot"
	BotClasses(1)="BotPack.TFemale2Bot"
	BotClasses(2)="BotPack.TMale2Bot"
	BotClasses(3)="BotPack.TFemale2Bot"
	BotClasses(4)="BotPack.TFemale2Bot"
	BotClasses(5)="BotPack.TMale2Bot"
	BotClasses(6)="BotPack.TFemale2Bot"
	BotClasses(7)="BotPack.TFemale2Bot"
	BotSkins(0)="hkil"
	BotSkins(1)="fwar"
	BotSkins(2)="hkil"
	BotSkins(3)="fwar"
	BotSkins(4)="fwar"
	BotSkins(5)="hkil"
	BotSkins(6)="fwar"
	BotSkins(7)="fwar"
	BotFaces(0)="Vector"
	BotFaces(1)="Cathode"
	BotFaces(2)="Matrix"
	BotFaces(3)="Lilith"
	BotFaces(4)="Fury"
	BotFaces(5)="Tensor"
	BotFaces(6)="Fury"
	BotFaces(7)="Lilith"
	BotBio(0)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(1)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(2)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(3)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(4)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(5)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(6)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	BotBio(7)="A warrior slave. Member of 'The Corrupt,' a group of reprogrammed warriors who fight for the entertainment of Xan Kriegor."
	Maleclass=class'Botpack.TMale2'
	MaleSkin="SoldierSkins.hkil"
	Femaleclass=class'Botpack.TFemale2'
	FemaleSkin="SGirlSkins.fwar"
}

Most of the stuff here is self-explanatory. The #exec command tells the compiler that you're importing a texture in order to use it in your package. Since I didn't want to create a texture for this team I've reused a default texture. Bear in mind that the game has troubles with double quote parsing, so you can wither use a single quote, or two single quotes. Also remember to encase all sentences in DefaultProperties under double quotes!

Now to both varieties of RatedMatchInfo. Here's the non-team-based one:

class RatedMatchDM10AM extends RatedMatchInfo;

defaultproperties {
	NumBots=2
	ModifiedDifficulty=1.000000

	BotNames(0)="Vanessa"
	BotNames(1)="Xoleras"
	BotClassifications(0)="Psychotic"
	BotClassifications(1)="Executioner"
	BotTeams(0)=255
	BotTeams(1)=1
	CombatStyle(0)=1.000000
	CombatStyle(1)=0.500000
	FavoriteWeapon(0)="Botpack.UT_FlakCannon"
	FavoriteWeapon(1)="Botpack.PulseGun"
	BotClasses(0)="Botpack.TFemale1Bot"
	BotClasses(1)="Botpack.TMale2Bot"
	BotSkins(0)="FCommandoSkins.cmdo"
	BotSkins(1)="SoldierSkins.sldr"
	BotFaces(0)="FCommandoSkins.Gromida"
	BotFaces(1)="SoldierSkins.Harlin"
	Bio(0)="Vanessa was committed at a psychiatric institution at the age of twelve for murdering her parents and brother. This one is a real nut case. She likes to blow her opponents open with a well aimed rocket."
	Bio(1)="A former Greek mob executioner on 'extended leave' from his 'family business', Xoleras has a reputation of completely and utterly dominating his opponents. A master of thin ledges and with vision of an eagle, he rarely misses an opportunity to blank an unskilled opponent."
}

And here's a team-based one:

class RatedMatchCTF10AM extends RatedMatchInfo;

defaultproperties {
	NumBots=7
	NumAllies=3
	ModifiedDifficulty=1.500000

	EnemyTeam=class'Botpack.RatedTeamInfo2'
}

If you want to improve the readability of your code, you can group the entries. As long as you respect the syntax, you should be fine:

class RatedMatchDM10AM extends RatedMatchInfo;

defaultproperties {
	NumBots=2
	ModifiedDifficulty=1.000000

	BotNames(0)="Vanessa"
	BotClassifications(0)="Psychotic"
	BotTeams(0)=255
	CombatStyle(0)=1.000000
	FavoriteWeapon(0)="Botpack.UT_FlakCannon"
	BotClasses(0)="Botpack.TFemale1Bot"
	BotSkins(0)="FCommandoSkins.cmdo"
	BotFaces(0)="FCommandoSkins.Gromida"
	Bio(0)="Vanessa was committed at a psychiatric institution at the age of twelve for murdering her parents and brother. This one is a real nut case. She likes to blow her opponents open with a well aimed rocket."

	BotNames(1)="Xoleras"
	BotClassifications(1)="Executioner"
	BotTeams(1)=1
	CombatStyle(1)=0.500000
	FavoriteWeapon(1)="Botpack.PulseGun"
	BotClasses(1)="Botpack.TMale2Bot"
	BotSkins(1)="SoldierSkins.sldr"
	BotFaces(1)="SoldierSkins.Harlin"
	Bio(1)="A former Greek mob executioner on 'extended leave' from his 'family business', Xoleras has a reputation of completely and utterly dominating his opponents. A master of thin ledges and with vision of an eagle, he rarely misses an opportunity to blank an unskilled opponent."
}

Now here's the complicated part: the Ladders. You need to extend Botpack.Ladder and UTMenu.UTLadder. But before that, here's an example of Ladder:

class MyLadderDM extends LadderDM config(MyLadder);

defaultproperties {
	Matches=16
	MapPrefix="DM-"

	Maps(0)="Tutorial.unr"
	MapAuthors(0)="Cliff Bleszinski"
	MapTitle(0)="DM Tutorial"
	MapDescription(0)="Learn the basic rules of Deathmatch in this special training environment. Test your skills against an untrained enemy before entering the tournament proper."
	RankedGame(0)=1
	FragLimits(0)=3
	MatchInfo(0)="Botpack.RatedMatchDMTUT"

	Maps(1)="Oblivion.unr"
	MapAuthors(1)="Juan Pancho Eekels"
	MapTitle(1)="Oblivion"
	MapDescription(1)="The ITV Oblivion is one of Liandri's armored transport ships. It transports new contestants via hyperspace jump from the Initiation Chambers to their first events on Earth. Little do most fighters know, however, that the ship itself is a battle arena."
	RankedGame(1)=1
	FragLimits(1)=10
	MatchInfo(1)="MyLadder.MyRatedMatchDM1"

//(...)

	Maps(14)="StalwartXL.unr"
	MapAuthors(14)="Alan Willard"
	MapTitle(14)="Stalwart Rematch"
	MapDescription(14)="It didn't take long for Jerl Liandri to realize that there was a hidden, but large, room behind closed garage doors. Amazed, he named his discovery as part of the battle arena and added some more powerful weapons, including Jerl's own personal favorite, the 'hellraising' Warhead Launcher."
	RankedGame(14)=3
	FragLimits(14)=20
	MatchInfo(14)="MyLadder.MyRatedMatchDM14"

	Maps(15)="Turbine.unr"
	MapAuthors(15)="Cliff Bleszinski"
	MapTitle(15)="Turbine"
	MapDescription(15)="A decaying water-treatment facility that has been purchased for use in the Tourney, the Turbine Facility offers an extremely tight and fast arena for combatants which ensures that there is no running, and no hiding from certain death."
	RankedGame(15)=3
	FragLimits(15)=20
	MatchInfo(15)="MyLadder.MyRatedMatchDM15"
}

Here, the novelty is the first line, it contains an addition: config(MyLadder) specifies that the data of the Ladder will be saved to the file MyLadder.ini under System (e.g. "C:\UnrealTournament\System\MyLadder.ini"). This is fundamental, otherwise, the progress of your ladder won't be saved!.

Subclassing galore!

Now onto the tedious part: unlike everything else in the game, the Ladder system in UT wasn't designed to be extensible. There's a LOT of duplicate coding to be done. In addition to the already mentioned, you also need to subclass window and game components. We'll also create a new window from which our Ladder will be launched. More specifically, you need to subclass:

  • Botpack.Ladder (C:\UnrealTournament\Botpack\Classes\Ladder.uc)
  • UTMenu.EnemyBrowser (C:\UnrealTournament\UTMenu\Classes\EnemyBrowser.uc)
  • UTMenu.KillGameQueryClient (C:\UnrealTournament\UTMenu\Classes\KillGameQueryClient.uc)
  • UTMenu.KillGameQueryWindow (C:\UnrealTournament\UTMenu\Classes\KillGameQueryWindow.uc)
  • UTMenu.ManagerWindow (C:\UnrealTournament\UTMenu\Classes\ManagerWindow.uc)
  • UTMenu.NewCharacterWindow (C:\UnrealTournament\UTMenu\Classes\NewCharacterWindow.uc)
  • UTMenu.NewGameInterimObject (C:\UnrealTournament\UTMenu\Classes\NewGameInterimObject.uc)
  • UTMenu.ObjectiveBrowser (C:\UnrealTournament\UTMenu\Classes\ObjectiveBrowser.uc)
  • UTMenu.SlotWindow (C:\UnrealTournament\UTMenu\Classes\SlotWindow.uc)
  • UTMenu.TeamBrowser (C:\UnrealTournament\UTMenu\Classes\TeamBrowser.uc)
  • UTMenu.UTLadder (C:\UnrealTournament\UTMenu\Classes\UTLadder.uc)
  • UTMenu.UTLadderAS (C:\UnrealTournament\UTMenu\Classes\UTLadderAS.uc)
  • UTMenu.UTLadderChal (C:\UnrealTournament\UTMenu\Classes\UTLadderchal.uc)
  • UTMenu.UTLadderCTF (C:\UnrealTournament\UTMenu\Classes\UTLadderCTF.uc)
  • UTMenu.UTLadderDM (C:\UnrealTournament\UTMenu\Classes\UTLadderDM.uc)
  • UTMenu.UTLadderDOM (C:\UnrealTournament\UTMenu\Classes\UTLadderDOM.uc)[/list]

We'll start first with Botpack.Ladder and the UTMenu.UTLadder* classes. UTLadder is an Abstract class, it cannot be instantiated but it's used to build other classes. These classes, instead, have one or more Abstract Method(s) which each class must implement. UTLadder's subclasses are UTLadderAS, UTLadderChal, UTLadderCTF, UTLadderDM and UTLadderDOM. So we create a new class called "MyLadderLadder" with the following code:

class MyLadderLadder extends Ladder Config(MyLadder);

defaultproperties {
	Titles(0)="Untrained"
	Titles(1)="Contender"
	Titles(2)="Light Weight"
	Titles(3)="Heavy Weight"
	Titles(4)="Warlord"
	Titles(5)="Battle Master"
	Titles(6)="Champion"
	LadderTeams(0)=Class'Botpack.RatedTeamInfo1'
	LadderTeams(1)=Class'Botpack.RatedTeamInfo2'
	LadderTeams(2)=Class'Botpack.RatedTeamInfo3'
	LadderTeams(3)=Class'Botpack.RatedTeamInfo4'
	LadderTeams(4)=Class'Botpack.RatedTeamInfo5'
	LadderTeams(5)=Class'Botpack.RatedTeamInfo6'
	LadderTeams(6)=Class'Botpack.RatedTeamInfoS'
	LadderTeams(7)=Class'Botpack.RatedTeamInfoDemo1'
	LadderTeams(8)=Class'Botpack.RatedTeamInfoDemo2'
	LadderTeams(9)=Class'MyLadder.RatedTeamInfo9'
	NumTeams=10
}

Titles and LadderTeams, of course, can be modified as seen on the first steps. We subclassed this in order to bypass a game's limitation: references can be confused and the Ladder can be reset to the original Ladder, with unpredictable results. So, in order to bypass this limitation, we'll replace every generic class'Ladder' with class'MyLadderLadder'. This doesn't apply to the individual Ladders, so these can be subclassed without most problems.

Next we subclass UTMenu.KillGameQueryClient and UTMenu.KillGameQueryWindow as "MyKillGameQueryClient" and "MyKillGameQueryWindow". These are used to ask the player if they want to delete a saved session progress and free one of five slots. They contain calls to the classes "MySlotWindow" (subclass of UTMenu.SlotWindow) and "MyManagerWindow" (UTMenu.MyManagerWindow), which we're subclassing later.

class MyKillGameQueryClient extends KillGameQueryClient;

var SlotWindow SlotWindow;

function YesPressed () {
	if (SlotWindow != None) {
		SlotWindow.Saves[SlotIndex] = "";
		SlotWindow.SaveConfig();
		SlotWindow.SlotButton[SlotIndex].Text = class'MySlotWindow'.Default.EmptyText;
		class'MyManagerWindow'.Default.DOMDoorOpen[SlotIndex] = 0;
		class'MyManagerWindow'.Default.CTFDoorOpen[SlotIndex] = 0;
		class'MyManagerWindow'.Default.ASDoorOpen[SlotIndex] = 0;
		class'MyManagerWindow'.Default.ChalDoorOpen[SlotIndex] = 0;
		class'MyManagerWindow'.Default.TrophyDoorOpen[SlotIndex] = 0;
		class'MyManagerWindow'.StaticSaveConfig();
	}
	Close();
}

defaultproperties {
	QueryText="Are you sure you want to delete this session?"
	YesText="Yes."
	NoText="No."
}
class MyKillGameQueryWindow extends KillGameQueryWindow;

function BeginPlay () {
	ClientClass = Class'MyKillGameQueryClient';
}

defaultproperties {
	WindowTitle="Delete saved game?"
}

Now we're subclassing the Browsers. These are going to be called "MyTeamBrowser" (subclass of UTMenu.TeamBrowser) and "MyEnemyBrowser" (subclass of UTMenu.EnemyBrowser), which, as their name suggests, are the pre-match "Team Roster" and "Enemy Roster" screens.

class MyEnemyBrowser extends EnemyBrowser Config(MyLadder);
class MyTeamBrowser extends TeamBrowser;

function NextPressed() {
	local EnemyBrowser EB;

	HideWindow();
	EB = EnemyBrowser(Root.CreateWindow(class'MyEnemyBrowser', 100, 100, 200, 200, Root, True));
	EB.LadderWindow = LadderWindow;
	EB.TeamWindow = Self;
	EB.Ladder = Ladder;
	EB.Match = Match;
	EB.GameType = GameType;
	EB.Initialize();
}

Next we subclass UTMenu.NewGameInterimObject as "MyNewGameInterimObject", which is the character creation screen:

class MyNewGameInterimObject expands NewGameInterimObject;

var string GameWindowType;

function PostBeginPlay() {
	local LadderInventory LadderObj;
	local int EmptySlot, j;
	
	EmptySlot = -1;
	for (j=0; j<5; j++) {
		if (class'MySlotWindow'.Default.Saves[j] == "") {
			EmptySlot = j;
			break;
		}
	}

	if (EmptySlot < 0) {
		// Create "You must first free a slot..." dialog.
		TournamentConsole(PlayerPawn(Owner).Player.Console).Root.CreateWindow(class'FreeSlotsWindow', 100, 100, 200, 200);
		return;
	}

	// Create new game dialog.
	TournamentConsole(PlayerPawn(Owner).Player.Console).bNoDrawWorld = True;
	TournamentConsole(PlayerPawn(Owner).Player.Console).bLocked = True;
	UMenuRootWindow(TournamentConsole(PlayerPawn(Owner).Player.Console).Root).MenuBar.HideWindow();

	// Make them a ladder object.
	LadderObj = LadderInventory(PlayerPawn(Owner).FindInventoryType(class'LadderInventory'));

	if (LadderObj == None) {
		// Make them a ladder object.
		LadderObj = Spawn(class'LadderInventory');
		Log("Created a new LadderInventory.");
		LadderObj.GiveTo(PlayerPawn(Owner));
	}

	LadderObj.Reset();
	LadderObj.Slot = EmptySlot; // Find a free slot.
	class'MyManagerWindow'.Default.DOMDoorOpen[EmptySlot] = 0;
	class'MyManagerWindow'.Default.CTFDoorOpen[EmptySlot] = 0;
	class'MyManagerWindow'.Default.ASDoorOpen[EmptySlot] = 0;
	class'MyManagerWindow'.Default.ChalDoorOpen[EmptySlot] = 0;
	class'MyManagerWindow'.Static.StaticSaveConfig();
	Log("Assigned player a LadderInventory.");

	// Clear all slots.
	Owner.PlaySound(sound'LadderSounds.ladvance', SLOT_None, 0.1);
	Owner.PlaySound(sound'LadderSounds.ladvance', SLOT_Misc, 0.1);
	Owner.PlaySound(sound'LadderSounds.ladvance', SLOT_Pain, 0.1);
	Owner.PlaySound(sound'LadderSounds.ladvance', SLOT_Interact, 0.1);
	Owner.PlaySound(sound'LadderSounds.ladvance', SLOT_Talk, 0.1);
	Owner.PlaySound(sound'LadderSounds.ladvance', SLOT_Interface, 0.1);

	// Go to the character creation screen.
	TournamentConsole(PlayerPawn(Owner).Player.Console).Root.CreateWindow(Class<UWindowWindow>(DynamicLoadObject(GameWindowType, Class'Class')), 100, 100, 200, 200, TournamentConsole(PlayerPawn(Owner).Player.Console).Root, True);
}

defaultproperties {
	GameWindowType="MyLadder.MyNewCharacterWindow"
}

Now we subclass UTMenu.SlotWindow (the saved game slots window) as "MySlotWindow":

class MySlotWindow extends SlotWindow config(MyLadder);

function KillGame(int i) {
	local KillGameQueryWindow KGQWindow;

	if (SlotButton''.Text != EmptyText) {
		// Are you sure?
		KGQWindow = KillGameQueryWindow(Root.CreateWindow(class'MyKillGameQueryWindow', 100, 100, 100, 100));
		MiKillGameQueryClient(KGQWindow.ClientArea).SlotWindow = Self;
		MiKillGameQueryClient(KGQWindow.ClientArea).SlotIndex = i;
		ShowModal(KGQWindow);
	}
}

function RestoreGame(int i)
{
	local LadderInventory LadderObj;
	local string Temp, Name, PlayerSkin;
	local Class<TournamentPlayer> PlayerClass;
	local int Pos, Team, j, Face;

	if (Saves'' == "") {
		return;
	}

	// Check ladder object.
	LadderObj = LadderInventory(GetPlayerOwner().FindInventoryType(class'LadderInventory'));

	if (LadderObj == None) {
		// Make them a ladder object.
		LadderObj = GetPlayerOwner().Spawn(class'LadderInventory');
		LadderObj.GiveTo(GetPlayerOwner());
	}

	// Fill the ladder object.

	// Slot...
	LadderObj.Slot = i;

	// Difficulty...
	LadderObj.TournamentDifficulty = int(Left(Saves'', 1));
	LadderObj.SkillText = class'MyNewCharacterWindow'.Default.SkillText[LadderObj.TournamentDifficulty];

	// Team
	Temp = Right(Saves'', Len(Saves'') - 2);
	Pos = InStr(Temp, "\\");
	Team = int(Left(Temp, Pos));
	LadderObj.Team = class'MyLadderLadder'.Default.LadderTeams[Team];

	// DMRank
	Temp = Right(Saves'', Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.DMRank = int(Left(Temp, Pos));

	// DMPosition
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.DMPosition = int(Left(Temp, Pos));

	// DOMRank
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.DOMRank = int(Left(Temp, Pos));

	// DOMPosition
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.DOMPosition = int(Left(Temp, Pos));

	// CTFRank
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.CTFRank = int(Left(Temp, Pos));

	// CTFPosition
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.CTFPosition = int(Left(Temp, Pos));

	// ASRank
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.ASRank = int(Left(Temp, Pos));

	// ASPosition
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.ASPosition = int(Left(Temp, Pos));

	// ChalRank
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.ChalRank = int(Left(Temp, Pos));

	// ChalPosition
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.ChalPosition = int(Left(Temp, Pos));

	// Sex
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	LadderObj.Sex = Left(Temp, Pos);

	// Face
	Temp = Right(Temp, Len(Temp) - Pos - 1);
	Pos = InStr(Temp, "\\");
	Face = int(Left(Temp, Pos));
	LadderObj.Face = Face;

	// Name
	Temp = Right(Temp, Len(Temp) - 2);
	Name = Temp;
	GetPlayerOwner().ChangeName(Name);
	GetPlayerOwner().UpdateURL("Name", Name, True);

	if (LadderObj.Sex ~= "M") {
		PlayerClass = LadderObj.Team.Default.MaleClass;
		PlayerSkin = LadderObj.Team.Default.MaleSkin;
	} else {
		PlayerClass = LadderObj.Team.Default.FemaleClass;
		PlayerSkin = LadderObj.Team.Default.FemaleSkin;
	}

	IterateFaces(PlayerSkin, GetPlayerOwner().GetItemName(string(PlayerClass.Default.Mesh)));
	GetPlayerOwner().UpdateURL("Class", string(PlayerClass), True);
	GetPlayerOwner().UpdateURL("Skin", PlayerSkin, True);
	GetPlayerOwner().UpdateURL("Face", Faces[Face], True);
	GetPlayerOwner().UpdateURL("Voice", PlayerClass.Default.VoiceType, True);
	GetPlayerOwner().UpdateURL("Team", "255", True);

	// Goto Manager
	HideWindow();
	Root.CreateWindow(class'MyManagerWindow', 100, 100, 200, 200, Root, True);
}

defaultproperties {
	BGName1(0)="UTMenu.Save11"
	BGName1(1)="UTMenu.Save12"
	BGName1(2)="UTMenu.Save13"
	BGName1(3)="UTMenu.Save14"
	BGName2(0)="UTMenu.Save21"
	BGName2(1)="UTMenu.Save22"
	BGName2(2)="UTMenu.Save23"
	BGName2(3)="UTMenu.Save24"
	BGName3(0)="UTMenu.Save31"
	BGName3(1)="UTMenu.Save32"
	BGName3(2)="UTMenu.Save33"
	BGName3(3)="UTMenu.Save34"
	EmptyText="UNUSED"
	AvgRankStr="Average Rank:"
	CompletedStr="Matches Won:"
}

With all in place, now we subclass UTLadder, as "MyLadder".

class MyLadder extends UTLadder;

function EvaluateMatch(optional bool bTrophyVictory) {
	local string SaveString;
	local int Team, i;

	if (LadderObj != None) {
		SaveString = string(LadderObj.TournamentDifficulty);
		for (i=0; i<class'MyLadderLadder'.Default.NumTeams; i++) {
			if (class'MyLadderLadder'.Default.LadderTeams[i] == LadderObj.Team) {
				Team = i;
			}
		}
		SaveString = SaveString$"\\"$Team;
		SaveString = SaveString$"\\"$LadderObj.DMRank;
		SaveString = SaveString$"\\"$LadderObj.DMPosition;
		SaveString = SaveString$"\\"$LadderObj.DOMRank;
		SaveString = SaveString$"\\"$LadderObj.DOMPosition;
		SaveString = SaveString$"\\"$LadderObj.CTFRank;
		SaveString = SaveString$"\\"$LadderObj.CTFPosition;
		SaveString = SaveString$"\\"$LadderObj.ASRank;
		SaveString = SaveString$"\\"$LadderObj.ASPosition;
		SaveString = SaveString$"\\"$LadderObj.ChalRank;
		SaveString = SaveString$"\\"$LadderObj.ChalPosition;
		SaveString = SaveString$"\\"$LadderObj.Sex;
		SaveString = SaveString$"\\"$LadderObj.Face;
		SaveString = SaveString$"\\"$GetPlayerOwner().PlayerReplicationInfo.PlayerName;
		class'MySlotWindow'.Default.Saves[LadderObj.Slot] = SaveString;
		class'MySlotWindow'.Static.StaticSaveConfig();

		if (LadderObj.PendingPosition > 7) {
			SelectedMatch = LadderObj.PendingPosition;
			BaseMatch = LadderObj.PendingPosition - 7;
			ArrowPos = LadderObj.PendingPosition - 1;
			PendingPos = LadderObj.PendingPosition;
		}

		LadderObj.PendingPosition = 0;

		if (bTrophyVictory) {
			bTrophyTravelPending = True;
		}

		SelectedMatch = LadderPos;
		SetMapShot(LadderPos);
		FillInfoArea(LadderPos);
	}
}

function EscClose()
{
	BackPressed();
}

function BackPressed()
{
	Root.CreateWindow(class'MyManagerWindow', 100, 100, 200, 200, Root, True);
	Close();
}

function ShowWindow()
{
	LadderObj.CurrentLadder = Ladder;
	Super.ShowWindow();
}

Now we subclass UTMenu.ObjectiveBrowser, for Assault-based ladders, as "MyObjectiveBrowser".

class MyObjectiveBrowser extends ObjectiveBrowser;

function NextPressed ()
{
	local TeamBrowser TB;

	HideWindow();
	TB = TeamBrowser(Root.CreateWindow(Class'MyTeamBrowser',100.0,100.0,200.0,200.0,Root,True));
	TB.LadderWindow = LadderWindow;
	TB.ObjectiveWindow = self;
	TB.LadderWindow = LadderWindow;
	TB.Ladder = Ladder;
	TB.Match = Match;
	TB.GameType = GameType;
	TB.Initialize();
}

function Initialize()
{
	local class<Bot> InitialMate;
	local int i;
	local int W, H;
	local float XWidth, YHeight, XMod, YMod, XPos, YPos, YOffset;
	local color TextColor;
	local AssaultInfo AI;

	GetPlayerOwner().ViewRotation.Pitch = 0;
	GetPlayerOwner().ViewRotation.Roll = 0;

	/*
	* Setup window parameters.
	*/

	bLeaveOnScreen = True;
	bAlwaysOnTop = True;
	class'UTLadderStub'.Static.GetStubClass().Static.SetupWinParams(Self, Root, W, H);

	XMod = 4*W;
	YMod = 3*H;

	/*
	* Load the background.
	*/

	BG1[0] = Texture(DynamicLoadObject(BGName1[0], Class'Texture'));
	BG1[1] = Texture(DynamicLoadObject(BGName1[1], Class'Texture'));
	BG1[2] = Texture(DynamicLoadObject(BGName1[2], Class'Texture'));
	BG1[3] = Texture(DynamicLoadObject(BGName1[3], Class'Texture'));
	BG2[0] = Texture(DynamicLoadObject(BGName2[0], Class'Texture'));
	BG2[1] = Texture(DynamicLoadObject(BGName2[1], Class'Texture'));
	BG2[2] = Texture(DynamicLoadObject(BGName2[2], Class'Texture'));
	BG2[3] = Texture(DynamicLoadObject(BGName2[3], Class'Texture'));
	BG3[0] = Texture(DynamicLoadObject(BGName3[0], Class'Texture'));
	BG3[1] = Texture(DynamicLoadObject(BGName3[1], Class'Texture'));
	BG3[2] = Texture(DynamicLoadObject(BGName3[2], Class'Texture'));
	BG3[3] = Texture(DynamicLoadObject(BGName3[3], Class'Texture'));

	/*
	* Create components.
	*/

	// Title
	XPos = 74.0/1024 * XMod;
	YPos = 69.0/768 * YMod;
	XWidth = 352.0/1024 * XMod;
	YHeight = 41.0/768 * YMod;
	Title1 = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	Title1.Text = BrowserName;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	Title1.NotifyWindow = Self;
	Title1.SetTextColor(TextColor);
	Title1.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);
	Title1.bStretched = True;
	Title1.bDisabled = True;

	if (!Ladder.Default.bTeamGame) {
		Title1.bDisabled = True;
	}

	// Names
	TextColor.R = 0;
	TextColor.G = 128;
	TextColor.B = 255;
	XPos = 168.0/1024 * XMod;
	YPos = 255.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	YOffset = 48.0/768 * YMod;
	NumNames = class'MyLadderAS'.Static.GetObjectiveCount(Match, AI);

	for (i=0; i<NumNames; i++) {
		Names[i] = LadderButton(CreateWindow(class'LadderButton', XPos, YPos + i*YOffset, XWidth, YHeight));
		Names[i].MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
		Names[i].NotifyWindow = Self;
		Names[i].SetTextColor(TextColor);
		Names[i].bStretched = True;
		Names[i].bDontSetLabel = True;
		Names[i].LabelWidth = 178.0/1024 * XMod;
		Names[i].LabelHeight = 49.0/768 * YMod;
		Names[i].OverSound = sound'LadderSounds.lcursorMove';
		Names[i].DownSound = sound'SpeechWindowClick';
		Names[i].Text = ObjectiveString@i+1;
	}
	Names[0].bBottom = True;
	Names[NumNames-1].bTop = True;

	// Back Button
	XPos = 192.0/1024 * XMod;
	YPos = 701.0/768 * YMod;
	XWidth = 64.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	BackButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	BackButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.LeftUp", Class'Texture'));
	BackButton.UpTexture = Texture(DynamicLoadObject("UTMenu.LeftUp", Class'Texture'));
	BackButton.DownTexture = Texture(DynamicLoadObject("UTMenu.LeftDown", Class'Texture'));
	BackButton.OverTexture = Texture(DynamicLoadObject("UTMenu.LeftOver", Class'Texture'));
	BackButton.NotifyWindow = Self;
	BackButton.Text = "";
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	BackButton.SetTextColor(TextColor);
	BackButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	BackButton.bStretched = True;
	BackButton.OverSound = sound'LadderSounds.lcursorMove';
	BackButton.DownSound = sound'LadderSounds.ladvance';

	// Next Button
	XPos = 256.0/1024 * XMod;
	YPos = 701.0/768 * YMod;
	XWidth = 64.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	NextButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	NextButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.RightUp", Class'Texture'));
	NextButton.UpTexture = Texture(DynamicLoadObject("UTMenu.RightUp", Class'Texture'));
	NextButton.DownTexture = Texture(DynamicLoadObject("UTMenu.RightDown", Class'Texture'));
	NextButton.OverTexture = Texture(DynamicLoadObject("UTMenu.RightOver", Class'Texture'));
	NextButton.NotifyWindow = Self;
	NextButton.Text = "";
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	NextButton.SetTextColor(TextColor);
	NextButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	NextButton.bStretched = True;
	NextButton.OverSound = sound'LadderSounds.lcursorMove';
	NextButton.DownSound = sound'LadderSounds.ladvance';

	// Obj Desc
	XPos = 529.0/1024 * XMod;
	YPos = 586.0/768 * YMod;
	XWidth = 385.0/1024 * XMod;
	YHeight = 113.0/768 * YMod;
	ObjDescArea = UTFadeTextArea(CreateWindow(Class<UWindowWindow>(DynamicLoadObject("UTMenu.UTFadeTextArea", Class'Class')), XPos, YPos, XWidth, YHeight));
	ObjDescArea.TextColor.R = 255;
	ObjDescArea.TextColor.G = 255;
	ObjDescArea.TextColor.B = 0;
	ObjDescArea.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	ObjDescArea.bAlwaysOnTop = True;
	ObjDescArea.bAutoScrolling = True;

	// DescScrollup
	XPos = 923.0/1024 * XMod;
	YPos = 590.0/768 * YMod;
	XWidth = 32.0/1024 * XMod;
	YHeight = 16.0/768 * YMod;
	DescScrollup = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	DescScrollup.NotifyWindow = Self;
	DescScrollup.Text = "";
	DescScrollup.bStretched = True;
	DescScrollup.UpTexture = Texture(DynamicLoadObject("UTMenu.AroUup", Class'Texture'));
	DescScrollup.OverTexture = Texture(DynamicLoadObject("UTMenu.AroUovr", Class'Texture'));
	DescScrollup.DownTexture = Texture(DynamicLoadObject("UTMenu.AroUdwn", Class'Texture'));
	DescScrollup.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	DescScrollup.bAlwaysOnTop = True;

	// DescScrolldown
	XPos = 923.0/1024 * XMod;
	YPos = 683.0/768 * YMod;
	XWidth = 32.0/1024 * XMod;
	YHeight = 16.0/768 * YMod;
	DescScrolldown = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	DescScrolldown.NotifyWindow = Self;
	DescScrolldown.Text = "";
	DescScrolldown.bStretched = True;
	DescScrolldown.UpTexture = Texture(DynamicLoadObject("UTMenu.AroDup", Class'Texture'));
	DescScrolldown.OverTexture = Texture(DynamicLoadObject("UTMenu.AroDovr", Class'Texture'));
	DescScrolldown.DownTexture = Texture(DynamicLoadObject("UTMenu.AroDdwn", Class'Texture'));
	DescScrolldown.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	DescScrolldown.bAlwaysOnTop = True;

	// StaticArea
	XPos = 608.0/1024 * XMod;
	YPos = 90.0/768 * YMod;
	XWidth = 320.0/1024 * XMod;
	YHeight = 319.0/768 * YMod;
	MapStatic = StaticArea(CreateWindow(class'StaticArea', XPos, YPos, XWidth, YHeight));
	MapStatic.VStaticScale = 300.0;

	Initialized = True;
	Root.Console.bBlackout = True;

	SelectedO = 0;
	SetMapShot(class'MyLadderAS'.Static.GetObjectiveShot(Match, 0, AI));
	AddObjDesc();
	StaticScale = 1.0;
}

function NameSelected(int i)
{
	local AssaultInfo AI;

	SelectedO = i;
	SetMapShot(class'MyLadderAS'.Static.GetObjectiveShot(Match, i, AI));
	AddObjDesc();
}

function AddObjDesc()
{
	local AssaultInfo AI;

	ObjDescArea.Clear();
	ObjDescArea.AddText(OrdersTransmissionText);
	ObjDescArea.AddText("---");
	ObjDescArea.AddText(class'MyLadderAS'.Static.GetObjectiveString(Match, SelectedO, AI));
}

Then we subclass each of the UTLadder: UTMenu.UTLadderAS ("MyLadderASMenu") - UTMenu.UTLadderChal ("MyLadderChalMenu") - UTMenu.UTLadderCTF ("MyLadderCTFMenu") - UTMenu.UTLadderDM ("MyLadderTDMMenu") - UTMenu.UTLadderDOM ("MyLadderDOMMenu"). These are different between each other since they have differing functionality: DM is a non-team-based ladder, DOM is a regular team-based ladder, CTF has a short name function and AS features the previously mentioned (My)ObjectiveBrowser. Also, all of them extend MyLadder and NOT UTLadder, since they're subclasses of this abstract class.

class MyLadderASMenu extends MyLadder;

function Created()
{
	Super.Created();

	if (LadderObj.ASPosition == -1) {
		LadderObj.ASPosition = 1;
		SelectedMatch = 0;
	} else {
		SelectedMatch = LadderObj.ASPosition;
	}
	SetupLadder(LadderObj.ASPosition, LadderObj.ASRank);
}

function FillInfoArea(int i)
{
	MapInfoArea.Clear();
	MapInfoArea.AddText(MapText$" "$Ladder.Static.GetMapTitle(i));
	MapInfoArea.AddText(Ladder.Static.GetDesc(i));
}

function NextPressed()
{
	local ObjectiveBrowser OB;

	if (PendingPos > ArrowPos) {
		return;
	}

	HideWindow();
	OB = ObjectiveBrowser(Root.CreateWindow(class'MyObjectiveBrowser', 100, 100, 200, 200, Root, True));
	OB.LadderWindow = Self;
	OB.Ladder = Ladder;
	OB.Match = SelectedMatch;
	OB.GameType = GameType;
	OB.Initialize();
}

function EvaluateMatch(optional bool bTrophyVictory)
{
	if (LadderObj.PendingPosition > LadderObj.ASPosition) {
		PendingPos = LadderObj.PendingPosition;
		LadderObj.ASPosition = LadderObj.PendingPosition;
	}

	if (LadderObj.PendingRank > LadderObj.ASRank) {
		LadderObj.ASRank = LadderObj.PendingRank;
		LadderObj.PendingRank = 0;
	}

	LadderPos = LadderObj.ASPosition;
	LadderRank = LadderObj.ASRank;

	if (LadderObj.ASRank == 6) {
		Super.EvaluateMatch(True);
	} else {
		Super.EvaluateMatch();
	}
}

defaultproperties {
	GameType="Botpack.Assault"
	TrophyMap="EOL_Assault.unr"
	LadderName="Assault"
	Ladder=Class'MyLadder.MyLadderAS'
	LadderTrophy=Texture'UTMenu.Skins.TrophyAS'
}
class MyLadderCTFMenu extends MyLadder;

function Created() {
	Super.Created();

	if (LadderObj.CTFPosition == -1) {
		LadderObj.CTFPosition = 1;
		SelectedMatch = 0;
	} else {
		SelectedMatch = LadderObj.CTFPosition;
	}
	SetupLadder(LadderObj.CTFPosition, LadderObj.CTFRank);
}

function FillInfoArea(int i) {
	MapInfoArea.Clear();
	MapInfoArea.AddText(MapText$" "$LadderObj.CurrentLadder.Static.GetMapTitle(i));
	MapInfoArea.AddText(TeamScoreText$" "$LadderObj.CurrentLadder.Static.GetGoalTeamScore(i));
	MapInfoArea.AddText(LadderObj.CurrentLadder.Static.GetDesc(i));
}

function NextPressed() {
	local TeamBrowser TB;
	local string MapName;

	if (PendingPos > ArrowPos) {
		return;
	}

	if (SelectedMatch == 0) {
		MapName = LadderObj.CurrentLadder.Default.MapPrefix$Ladder.Static.GetMap(0);
		CloseUp();
		StartMap(MapName, 0, "Botpack.TrainingCTF");
	} else {
		if (LadderObj.CurrentLadder.Default.DemoDisplay[SelectedMatch] == 1) {
			return;
		}

		HideWindow();
		TB = TeamBrowser(Root.CreateWindow(class'AMTeamBrowser', 100, 100, 200, 200, Root, True));
		TB.LadderWindow = Self;
		TB.Ladder = LadderObj.CurrentLadder;
		TB.Match = SelectedMatch;
		TB.GameType = GameType;
		TB.Initialize();
	}
}

function EvaluateMatch(optional bool bTrophyVictory) {
	if (LadderObj.PendingPosition > LadderObj.CTFPosition) {
		if (class'UTLadderStub'.Static.IsDemo() && LadderObj.PendingPosition > 1) {
			PendingPos = 1;
		} else {
			PendingPos = LadderObj.PendingPosition;
			LadderObj.CTFPosition = LadderObj.PendingPosition;
		}
	}
	if (LadderObj.PendingRank > LadderObj.CTFRank) {
		LadderObj.CTFRank = LadderObj.PendingRank;
		LadderObj.PendingRank = 0;
	}

	LadderPos = LadderObj.CTFPosition;
	LadderRank = LadderObj.CTFRank;

	if (LadderObj.CTFRank == 6) {
		Super.EvaluateMatch(True);
	} else {
		Super.EvaluateMatch();
	}
}

function CheckOpenCondition() {
	Super.CheckOpenCondition();
}

defaultproperties {
	GameType="Botpack.CTFGame"
	TrophyMap="EOL_CTF.unr"
	LadderName="Capture The Flag"
	Ladder=Class'MyLadder.MyLadderCTF'
	LadderTrophy=Texture'UTMenu.Skins.TrophyCTF'
}
class MyLadderDMMenu extends MyLadder;

function Created() {
	Super.Created();

	if (LadderObj.DMPosition == -1) {
		LadderObj.DMPosition = 1;
		SelectedMatch = 0;
	} else {
		SelectedMatch = LadderObj.DMPosition;
	}

	SetupLadder(LadderObj.DMPosition, LadderObj.DMRank);
}

function FillInfoArea(int i) {
	MapInfoArea.Clear();
	MapInfoArea.AddText(MapText$" "$LadderObj.CurrentLadder.Static.GetMapTitle(i));
	MapInfoArea.AddText(FragText$" "$LadderObj.CurrentLadder.Static.GetFragLimit(i));
	MapInfoArea.AddText(LadderObj.CurrentLadder.Static.GetDesc(i));
}

function NextPressed() {
	local EnemyBrowser EB;
	local string MapName;

	if (PendingPos > ArrowPos) {
		return;
	}

	if (SelectedMatch == 0) {
		MapName = LadderObj.CurrentLadder.Default.MapPrefix$Ladder.Static.GetMap(0);
		CloseUp();
		StartMap(MapName, 0, "Botpack.TrainingDM");
	} else {
		HideWindow();
		EB = EnemyBrowser(Root.CreateWindow(class'MyEnemyBrowser', 100, 100, 200, 200, Root, True));
		EB.LadderWindow = Self;
		EB.Ladder = LadderObj.CurrentLadder;
		EB.Match = SelectedMatch;
		EB.GameType = GameType;
		EB.Initialize();
	}
}

function StartMap(string StartMap, int Rung, string GameType) {
	local Class<GameInfo> GameClass;

	GameClass = Class<GameInfo>(DynamicLoadObject(GameType, Class'Class'));
	GameClass.Static.ResetGame();

	StartMap = StartMap$"?Game="$GameType$"?Mutator="$"?Tournament="$Rung$"?Name="
		$GetPlayerOwner().PlayerReplicationInfo.PlayerName$"?Team=255";

	Root.Console.CloseUWindow();
	GetPlayerOwner().ClientTravel(StartMap, TRAVEL_Absolute, True);
}

function EvaluateMatch(optional bool bTrophyVictory)
{
	local int Pos;
	local string MapName;

	if (LadderObj.PendingPosition > LadderObj.DMPosition) {
		PendingPos = LadderObj.PendingPosition;
		LadderObj.DMPosition = LadderObj.PendingPosition;
	}

	if (LadderObj.PendingRank > LadderObj.DMRank) {
		LadderObj.DMRank = LadderObj.PendingRank;
		LadderObj.PendingRank = 0;
	}

	LadderPos = LadderObj.DMPosition;
	LadderRank = LadderObj.DMRank;

	if (LadderObj.DMRank == 6) {
		Super.EvaluateMatch(True);
	} else {
		Super.EvaluateMatch();
	}
}

function CheckOpenCondition() {
	Super.CheckOpenCondition();
}

defaultproperties {
	GameType="Botpack.TeamGamePlus"
	TrophyMap="EOL_DeathMatch.unr"
	LadderName="Deathmatch"
	Ladder=Class'MyLadder.MyLadderTDM'
	LadderTrophy=Texture'UTMenu.Skins.TrophyDM'
}
class MyLadderDOMMenu extends MyLadder;

function Created() {
	Super.Created();

	if (LadderObj.DOMPosition == -1) {
		LadderObj.DOMPosition = 1;
		SelectedMatch = 0;
	} else {
		SelectedMatch = LadderObj.DOMPosition;
	}
	SetupLadder(LadderObj.DOMPosition, LadderObj.DOMRank);
}

function FillInfoArea(int i) {
	MapInfoArea.Clear();
	MapInfoArea.AddText(MapText$" "$LadderObj.CurrentLadder.Static.GetMapTitle(i));
	MapInfoArea.AddText(TeamScoreText$" "$LadderObj.CurrentLadder.Static.GetGoalTeamScore(i));
	MapInfoArea.AddText(LadderObj.CurrentLadder.Static.GetDesc(i));
}

function NextPressed() {
	local TeamBrowser TB;
	local string MapName;

	if (PendingPos > ArrowPos) {
		return;
	}

	if (SelectedMatch == 0) {
		MapName = LadderObj.CurrentLadder.Default.MapPrefix$Ladder.Static.GetMap(0);
		CloseUp();
		StartMap(MapName, 0, "Botpack.TrainingDOM");
	} else {
		if (LadderObj.CurrentLadder.Default.DemoDisplay[SelectedMatch] == 1) {
			return;
		}

		HideWindow();
		TB = TeamBrowser(Root.CreateWindow(class'MyTeamBrowser', 100, 100, 200, 200, Root, True));
		TB.LadderWindow = Self;
		TB.Ladder = LadderObj.CurrentLadder;
		TB.Match = SelectedMatch;
		TB.GameType = GameType;
		TB.Initialize();
	}
}

function EvaluateMatch(optional bool bTrophyVictory)
{
	local int Pos;
	local string MapName;

	if (LadderObj.PendingPosition > LadderObj.DOMPosition) {
		PendingPos = LadderObj.PendingPosition;
		LadderObj.DOMPosition = LadderObj.PendingPosition;
	}

	if (LadderObj.PendingRank > LadderObj.DOMRank) {
		LadderObj.DOMRank = LadderObj.PendingRank;
		LadderObj.PendingRank = 0;
	}

	LadderPos = LadderObj.DOMPosition;
	LadderRank = LadderObj.DOMRank;

	if (LadderObj.DOMRank == 6) {
		Super.EvaluateMatch(True);
	} else {
		Super.EvaluateMatch();
	}
}

function CheckOpenCondition() {
	Super.CheckOpenCondition();
}

defaultproperties {
	GameType="Botpack.Domination"
	TrophyMap="EOL_Domination.unr"
	LadderName="Domination"
	Ladder=Class'MyLadder.MyLadderDOM'
	LadderTrophy=Texture'UTMenu.Skins.TrophyDOM'
}
class MyLadderChalMenu extends MyLadder;

function Created() {
	Super.Created();

	SelectedMatch = LadderObj.ChalPosition;
	SetupLadder(LadderObj.ChalPosition, LadderObj.ChalRank);
}

function FillInfoArea(int i) {
	MapInfoArea.Clear();
	MapInfoArea.AddText(MapText$" "$Ladder.Static.GetMapTitle(i));
	MapInfoArea.AddText(FragText$" "$Ladder.Static.GetFragLimit(i));
	MapInfoArea.AddText(Ladder.Static.GetDesc(i));
}

function NextPressed() {
	local EnemyBrowser EB;

	if (PendingPos > ArrowPos) {
		return;
	}

	HideWindow();
	EB = EnemyBrowser(Root.CreateWindow(class'MyEnemyBrowser', 100, 100, 200, 200, Root, True));
	EB.LadderWindow = Self;
	EB.Ladder = Ladder;
	EB.Match = SelectedMatch;

	if (SelectedMatch == 3) {
		EB.GameType = "Botpack.ChallengeDMP";
	} else {
		EB.GameType = GameType;
	}

	EB.Initialize();
}

function EvaluateMatch(optional bool bTrophyVictory) {
	if (LadderObj.PendingPosition > LadderObj.ChalPosition) {
		PendingPos = LadderObj.PendingPosition;
		LadderObj.ChalPosition = LadderObj.PendingPosition;
	}

	if (LadderObj.PendingRank > LadderObj.ChalRank) {
		LadderObj.ChalRank = LadderObj.PendingRank;
		LadderObj.PendingRank = 0;
	}

	LadderPos = LadderObj.ChalPosition;
	LadderRank = LadderObj.ChalRank;

	if (LadderObj.ChalRank == 6) {
		Super.EvaluateMatch(True);
	} else {
		Super.EvaluateMatch();
	}

	Super.EvaluateMatch();
}

defaultproperties {
	GameType="Botpack.ChallengeDMP"
	TrophyMap="EOL_Challenge.unr"
	LadderName="Final Challenge"
	Ladder=Class'MyLadder.MyLadderChal'
	LadderTrophy=Texture'UTMenu.Skins.TrophyChal'
}

Next we subclass UTMenu.NewCharacterWindow as "MyNewCharacterWindow", which of course, is the aforementioned Character Creation window:

class MyNewCharacterWindow extends NewCharacterWindow config(MyLadder);

function BackPressed () {
	SwitchBack();
	Close();
}

function EscClose () {
	SwitchBack();
	Close();
}

function SwitchBack () {
	UTConsole(GetPlayerOwner().Player.Console).ManagerWindowClass = "UTMenu.ManagerWindow";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDMClass = "UTMenu.UTLadderDM";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDOMClass = "UTMenu.UTLadderDOM";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderCTFClass = "UTMenu.UTLadderCTF";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderASClass = "UTMenu.UTLadderAS";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderChalClass = "UTMenu.UTLadderChal";
	UTConsole(GetPlayerOwner().Player.Console).InterimObjectType = "UTMenu.NewGameInterimObject";
	UTConsole(GetPlayerOwner().Player.Console).SlotWindowType = "UTMenu.SlotWindow";
	Log("Now Playing UT Ladder");
}

function StartLadder () {
	UTConsole(GetPlayerOwner().Player.Console).ManagerWindowClass = "MyLadder.MyManagerWindow";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDMClass = "MyLadder.MyLadderDMMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDOMClass = "MyLadder.MyLadderDOMMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderCTFClass = "MyLadder.MyLadderCTFMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderASClass = "MyLadder.MyLadderASMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderChalClass = "MyLadder.MyLadderChalMenu";
	UTConsole(GetPlayerOwner().Player.Console).InterimObjectType = "MyLadder.MyNewGameInterimObject";
	UTConsole(GetPlayerOwner().Player.Console).SlotWindowType = "MyLadder.MySlotWindow";
	Log("Now Playing My Ladder");
}

function TeamPressed () {
	local string MeshName;

	PreferredTeam++;

	if (PreferredTeam == class'MyLadderLadder'.Default.NumTeams) {
		PreferredTeam = 0;
	}

	TeamButton.Text = Class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.TeamName;
	LadderObj.Team = Class'MyLadderLadder'.Default.LadderTeams[PreferredTeam];
	MaleClass = Class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.MaleClass;
	MaleSkin = Class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.MaleSkin;
	FemaleClass = Class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.FemaleClass;
	FemaleSkin = Class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.FemaleSkin;

	if ((MaleClass == None) && (SexButton.Text ~= MaleText)) {
		TeamPressed();
	}
	if ((FemaleClass == None) && (SexButton.Text ~= FemaleText)) {
		TeamPressed();
	}

	if (SexButton.Text ~= MaleText) {
		IterateFaces(MaleSkin,GetPlayerOwner().GetItemName(string(MaleClass.Default.Mesh)));
		PreferredFace = 0;
		MeshName = MaleClass.Default.SelectionMesh;
		MeshWindow.SetMeshString(MeshName);
		MaleClass.static.SetMultiSkin(MeshWindow.MeshActor, MaleSkin, Faces[PreferredFace], 255);
		GetPlayerOwner().UpdateURL("Class",string(MaleClass),True);
		GetPlayerOwner().UpdateURL("Voice",MaleClass.Default.VoiceType,True);
		GetPlayerOwner().UpdateURL("Skin",MaleSkin,True);
		GetPlayerOwner().UpdateURL("Face",Faces[PreferredFace],True);
		FaceButton.Text = FaceDescs[PreferredFace];
		SexButton.Text = MaleText;
		PreferredSex = 0;
		LadderObj.Sex = "M";
	} else {
		IterateFaces(FemaleSkin,GetPlayerOwner().GetItemName(string(FemaleClass.Default.Mesh)));
		PreferredFace = 0;
		MeshName = FemaleClass.Default.SelectionMesh;
		MeshWindow.SetMeshString(MeshName);
		FemaleClass.static.SetMultiSkin(MeshWindow.MeshActor,FemaleSkin,Faces[PreferredFace],255);
		GetPlayerOwner().UpdateURL("Class",string(FemaleClass),True);
		GetPlayerOwner().UpdateURL("Voice",FemaleClass.Default.VoiceType,True);
		GetPlayerOwner().UpdateURL("Skin",FemaleSkin,True);
		GetPlayerOwner().UpdateURL("Face",Faces[PreferredFace],True);
		FaceButton.Text = FaceDescs[PreferredFace];
		SexButton.Text = FemaleText;
		PreferredSex = 1;
		LadderObj.Sex = "F";
	}

	TeamDescArea.Clear();
	TeamDescArea.AddText(TeamNameString @ LadderObj.Team.static.GetTeamName());
	TeamDescArea.AddText(" ");
	TeamDescArea.AddText(LadderObj.Team.static.GetTeamBio());
	SaveConfig();
}

function Created () {
	local string MeshName, SkinDesc, Temp;
	local int i;
	local int W, H;
	local float XWidth, YHeight, XMod, YMod, XPos, YPos;
	local color TextColor;

	Super.Created();

	// Window parameters.
	bLeaveOnScreen = True;
	bAlwaysOnTop = True;

	class'UTLadderStub'.Static.GetStubClass().Static.SetupWinParams(Self, Root, W, H);

	XMod = 4*W;
	YMod = 3*H;

	// Background.
	BG1[0] = Texture(DynamicLoadObject(BGName1[0], Class'Texture'));
	BG1[1] = Texture(DynamicLoadObject(BGName1[1], Class'Texture'));
	BG1[2] = Texture(DynamicLoadObject(BGName1[2], Class'Texture'));
	BG1[3] = Texture(DynamicLoadObject(BGName1[3], Class'Texture'));
	BG2[0] = Texture(DynamicLoadObject(BGName2[0], Class'Texture'));
	BG2[1] = Texture(DynamicLoadObject(BGName2[1], Class'Texture'));
	BG2[2] = Texture(DynamicLoadObject(BGName2[2], Class'Texture'));
	BG2[3] = Texture(DynamicLoadObject(BGName2[3], Class'Texture'));
	BG3[0] = Texture(DynamicLoadObject(BGName3[0], Class'Texture'));
	BG3[1] = Texture(DynamicLoadObject(BGName3[1], Class'Texture'));
	BG3[2] = Texture(DynamicLoadObject(BGName3[2], Class'Texture'));
	BG3[3] = Texture(DynamicLoadObject(BGName3[3], Class'Texture'));

	// Object state verification.
	LadderObj = LadderInventory(GetPlayerOwner().FindInventoryType(class'LadderInventory'));

	if (LadderObj == None) {
		Log("MyNewCharacterWindow: Player has no LadderInventory!!");
	}

	// Component creation.
	MaleClass = class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.MaleClass;
	MaleSkin = class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.MaleSkin;
	FemaleClass = class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.FemaleClass;
	FemaleSkin = class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.FemaleSkin;
	IterateFaces(MaleSkin, GetPlayerOwner().GetItemName(string(MaleClass.Default.Mesh)));
	LadderObj.Face = PreferredFace;

	// MeshView
	XPos = 608.0/1024 * XMod;
	YPos = 88.0/768 * YMod;
	XWidth = 323.0/1024 * XMod;
	YHeight = 466.0/768 * YMod;
	MeshWindow = UMenuPlayerMeshClient(CreateWindow(class'UMenuPlayerMeshClient', XPos, YPos, XWidth, YHeight));
	MeshWindow.SetMeshString(MaleClass.Default.SelectionMesh);
	MeshWindow.ClearSkins();
	MaleClass.static.SetMultiSkin(MeshWindow.MeshActor, MaleSkin, MaleFace, 255);
	GetPlayerOwner().UpdateURL("Class", "Botpack."$string(MaleClass.Name), True);
	GetPlayerOwner().UpdateURL("Skin", MaleSkin, True);
	GetPlayerOwner().UpdateURL("Face", Faces[PreferredFace], True);
	GetPlayerOwner().UpdateURL("Team", "255", True);
	GetPlayerOwner().UpdateURL("Voice", MaleClass.Default.VoiceType, True);

	// Name Label
	XPos = 164.0/1024 * XMod;
	YPos = 263.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	NameLabel = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	NameLabel.NotifyWindow = Self;
	NameLabel.Text = NameText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	NameLabel.SetTextColor(TextColor);
	NameLabel.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	NameLabel.bStretched = True;
	NameLabel.bDisabled = True;
	NameLabel.bDontSetLabel = True;
	NameLabel.LabelWidth = 178.0/1024 * XMod;
	NameLabel.LabelHeight = 49.0/768 * YMod;

	// Name Button
	XPos = 164.0/1024 * XMod;
	YPos = 295.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	NameButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	NameButton.NotifyWindow = Self;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	NameButton.SetTextColor(TextColor);
	NameButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	NameButton.bStretched = True;
	NameButton.bDisabled = True;
	NameButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.Plate3Plain", Class'Texture'));
	NameButton.bDontSetLabel = True;
	NameButton.LabelWidth = 178.0/1024 * XMod;
	NameButton.LabelHeight = 49.0/768 * YMod;
	NameButton.OverSound = sound'LadderSounds.lcursorMove';
	NameButton.DownSound = sound'SpeechWindowClick';

	// Name Edit
	XPos = 164.0/1024 * XMod;
	YPos = 295.0/768 * YMod;
	XWidth = 178.0/1024 * XMod;
	YHeight = 49.0/768 * YMod;
	NameEdit = NameEditBox(CreateWindow(class'NameEditBox', XPos, YPos, XWidth, YHeight));
	NameEdit.bDelayedNotify = True;
	NameEdit.SetValue(GetPlayerOwner().PlayerReplicationInfo.PlayerName);
	NameEdit.CharacterWindow = Self;
	NameEdit.bCanEdit = True;
	NameEdit.bShowCaret = True;
	NameEdit.bAlwaysOnTop = True;
	NameEdit.bSelectOnFocus = True;
	NameEdit.MaxLength = 20;
	NameEdit.TextColor.R = 255;
	NameEdit.TextColor.G = 255;
	NameEdit.TextColor.B = 0;

	// Sex Label
	XPos = 164.0/1024 * XMod;
	YPos = 338.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	SexLabel = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	SexLabel.NotifyWindow = Self;
	SexLabel.Text = SexText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	SexLabel.SetTextColor(TextColor);
	SexLabel.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	SexLabel.bStretched = True;
	SexLabel.bDisabled = True;
	SexLabel.bDontSetLabel = True;
	SexLabel.LabelWidth = 178.0/1024 * XMod;
	SexLabel.LabelHeight = 49.0/768 * YMod;

	// Sex Button
	XPos = 164.0/1024 * XMod;
	YPos = 370.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	SexButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	SexButton.NotifyWindow = Self;
	SexButton.Text = MaleText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	SexButton.SetTextColor(TextColor);
	SexButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	SexButton.bStretched = True;
	SexButton.UpTexture = Texture(DynamicLoadObject("UTMenu.Plate3Plain", Class'Texture'));
	SexButton.DownTexture = Texture(DynamicLoadObject("UTMenu.PlateYellow2", Class'Texture'));
	SexButton.OverTexture = Texture(DynamicLoadObject("UTMenu.Plate2", Class'Texture'));
	SexButton.bDontSetLabel = True;
	SexButton.LabelWidth = 178.0/1024 * XMod;
	SexButton.LabelHeight = 49.0/768 * YMod;
	SexButton.bIgnoreLDoubleclick = True;
	SexButton.OverSound = sound'LadderSounds.lcursorMove';
	SexButton.DownSound = sound'SpeechWindowClick';

	// Team Label
	XPos = 164.0/1024 * XMod;
	YPos = 413.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	TeamLabel = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	TeamLabel.NotifyWindow = Self;
	TeamLabel.Text = TeamText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	TeamLabel.SetTextColor(TextColor);
	TeamLabel.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	TeamLabel.bStretched = True;
	TeamLabel.bDisabled = True;
	TeamLabel.bDontSetLabel = True;
	TeamLabel.LabelWidth = 178.0/1024 * XMod;
	TeamLabel.LabelHeight = 49.0/768 * YMod;

	// Team Button
	XPos = 164.0/1024 * XMod;
	YPos = 445.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	TeamButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	TeamButton.NotifyWindow = Self;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	TeamButton.SetTextColor(TextColor);
	TeamButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	TeamButton.bStretched = True;
	TeamButton.UpTexture = Texture(DynamicLoadObject("UTMenu.Plate3Plain", Class'Texture'));
	TeamButton.DownTexture = Texture(DynamicLoadObject("UTMenu.PlateYellow2", Class'Texture'));
	TeamButton.OverTexture = Texture(DynamicLoadObject("UTMenu.Plate2", Class'Texture'));
	TeamButton.bDontSetLabel = True;
	TeamButton.LabelWidth = 178.0/1024 * XMod;
	TeamButton.LabelHeight = 49.0/768 * YMod;
	TeamButton.bIgnoreLDoubleclick = True;

	if (PreferredTeam == class'MyLadderLadder'.Default.NumTeams) {
		PreferredTeam = 0;
	}

	TeamButton.Text = class'MyLadderLadder'.Default.LadderTeams[PreferredTeam].Default.TeamName;
	LadderObj.Team = class'MyLadderLadder'.Default.LadderTeams[PreferredTeam];
	TeamButton.OverSound = sound'LadderSounds.lcursorMove';
	TeamButton.DownSound = sound'SpeechWindowClick';

	// Face Label
	XPos = 164.0/1024 * XMod;
	YPos = 488.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	FaceLabel = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	FaceLabel.NotifyWindow = Self;
	FaceLabel.Text = FaceText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	FaceLabel.SetTextColor(TextColor);
	FaceLabel.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	FaceLabel.bStretched = True;
	FaceLabel.bDisabled = True;
	FaceLabel.bDontSetLabel = True;
	FaceLabel.LabelWidth = 178.0/1024 * XMod;
	FaceLabel.LabelHeight = 49.0/768 * YMod;

	// Face Button
	XPos = 164.0/1024 * XMod;
	YPos = 520.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	FaceButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	FaceButton.NotifyWindow = Self;
	FaceButton.Text = FaceDescs[PreferredFace];
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	FaceButton.SetTextColor(TextColor);
	FaceButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	FaceButton.bStretched = True;
	FaceButton.UpTexture = Texture(DynamicLoadObject("UTMenu.Plate3Plain", Class'Texture'));
	FaceButton.DownTexture = Texture(DynamicLoadObject("UTMenu.PlateYellow2", Class'Texture'));
	FaceButton.OverTexture = Texture(DynamicLoadObject("UTMenu.Plate2", Class'Texture'));
	FaceButton.bDontSetLabel = True;
	FaceButton.LabelWidth = 178.0/1024 * XMod;
	FaceButton.LabelHeight = 49.0/768 * YMod;
	FaceButton.bIgnoreLDoubleclick = True;
	FaceButton.OverSound = sound'LadderSounds.lcursorMove';
	FaceButton.DownSound = sound'SpeechWindowClick';

	// Skill Label
	XPos = 164.0/1024 * XMod;
	YPos = 563.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	SkillLabel = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	SkillLabel.NotifyWindow = Self;
	SkillLabel.Text = SkillsText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	SkillLabel.SetTextColor(TextColor);
	SkillLabel.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	SkillLabel.bStretched = True;
	SkillLabel.bDisabled = True;
	SkillLabel.bDontSetLabel = True;
	SkillLabel.LabelWidth = 178.0/1024 * XMod;
	SkillLabel.LabelHeight = 49.0/768 * YMod;

	// Skill Button
	XPos = 164.0/1024 * XMod;
	YPos = 595.0/768 * YMod;
	XWidth = 256.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	SkillButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	SkillButton.NotifyWindow = Self;
	SkillButton.Text = SkillText[1];
	CurrentSkill = 1;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	SkillButton.SetTextColor(TextColor);
	SkillButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	SkillButton.bStretched = True;
	SkillButton.UpTexture = Texture(DynamicLoadObject("UTMenu.Plate3Plain", Class'Texture'));
	SkillButton.DownTexture = Texture(DynamicLoadObject("UTMenu.PlateYellow2", Class'Texture'));
	SkillButton.OverTexture = Texture(DynamicLoadObject("UTMenu.Plate2", Class'Texture'));
	SkillButton.bDontSetLabel = True;
	SkillButton.LabelWidth = 178.0/1024 * XMod;
	SkillButton.LabelHeight = 49.0/768 * YMod;
	SkillButton.bIgnoreLDoubleclick = True;
	CurrentSkill = PreferredSkill;
	SkillButton.Text = SkillText[CurrentSkill];
	LadderObj.TournamentDifficulty = CurrentSkill;
	LadderObj.SkillText = SkillText[LadderObj.TournamentDifficulty];
	SkillButton.OverSound = sound'LadderSounds.lcursorMove';
	SkillButton.DownSound = sound'SpeechWindowClick';

	// Title Button
	XPos = 84.0/1024 * XMod;
	YPos = 69.0/768 * YMod;
	XWidth = 342.0/1024 * XMod;
	YHeight = 41.0/768 * YMod;
	TitleButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	TitleButton.NotifyWindow = Self;
	TitleButton.Text = CCText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	TitleButton.SetTextColor(TextColor);
	TitleButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);
	TitleButton.bStretched = True;

	// Back Button
	XPos = 192.0/1024 * XMod;
	YPos = 701.0/768 * YMod;
	XWidth = 64.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	BackButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	BackButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.LeftUp", Class'Texture'));
	BackButton.UpTexture = Texture(DynamicLoadObject("UTMenu.LeftUp", Class'Texture'));
	BackButton.DownTexture = Texture(DynamicLoadObject("UTMenu.LeftDown", Class'Texture'));
	BackButton.OverTexture = Texture(DynamicLoadObject("UTMenu.LeftOver", Class'Texture'));
	BackButton.NotifyWindow = Self;
	BackButton.Text = "";
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	BackButton.SetTextColor(TextColor);
	BackButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	BackButton.bStretched = True;
	BackButton.OverSound = sound'LadderSounds.lcursorMove';
	BackButton.DownSound = sound'LadderSounds.ladvance';

	// Next Button
	XPos = 256.0/1024 * XMod;
	YPos = 701.0/768 * YMod;
	XWidth = 64.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	NextButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	NextButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.RightUp", Class'Texture'));
	NextButton.UpTexture = Texture(DynamicLoadObject("UTMenu.RightUp", Class'Texture'));
	NextButton.DownTexture = Texture(DynamicLoadObject("UTMenu.RightDown", Class'Texture'));
	NextButton.OverTexture = Texture(DynamicLoadObject("UTMenu.RightOver", Class'Texture'));
	NextButton.NotifyWindow = Self;
	NextButton.Text = "";
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	NextButton.SetTextColor(TextColor);
	NextButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetBigFont(Root);
	NextButton.bStretched = True;
	NextButton.OverSound = sound'LadderSounds.lcursorMove';
	NextButton.DownSound = sound'LadderSounds.ladvance';

	// Team Desc
	XPos = 609.0/1024 * XMod;
	YPos = 388.0/768 * YMod;
	XWidth = 321.0/1024 * XMod;
	YHeight = 113.0/768 * YMod;
	TeamDescArea = UTFadeTextArea(CreateWindow(Class<UWindowWindow>(DynamicLoadObject("UTMenu.UTFadeTextArea", Class'Class')), XPos, YPos, XWidth, YHeight));
	TeamDescArea.TextColor.R = 255;
	TeamDescArea.TextColor.G = 255;
	TeamDescArea.TextColor.B = 0;
	TeamDescArea.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	TeamDescArea.bAlwaysOnTop = True;
	TeamDescArea.bMousePassThrough = True;
	TeamDescArea.bAutoScrolling = True;
	TeamDescArea.Clear();
	TeamDescArea.AddText(TeamNameString@LadderObj.Team.Static.GetTeamName());
	TeamDescArea.AddText(" ");
	TeamDescArea.AddText(LadderObj.Team.Static.GetTeamBio());

	// DescScrollup
	XPos = 715.0/1024 * XMod;
	YPos = 538.0/768 * YMod;
	XWidth = 32.0/1024 * XMod;
	YHeight = 16.0/768 * YMod;
	DescScrollup = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	DescScrollup.NotifyWindow = Self;
	DescScrollup.Text = "";
	DescScrollup.bStretched = True;
	DescScrollup.UpTexture = Texture(DynamicLoadObject("UTMenu.AroUup", Class'Texture'));
	DescScrollup.OverTexture = Texture(DynamicLoadObject("UTMenu.AroUovr", Class'Texture'));
	DescScrollup.DownTexture = Texture(DynamicLoadObject("UTMenu.AroUdwn", Class'Texture'));
	DescScrollup.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	DescScrollup.bAlwaysOnTop = True;

	// DescScrolldown
	XPos = 799.0/1024 * XMod;
	YPos = 538.0/768 * YMod;
	XWidth = 32.0/1024 * XMod;
	YHeight = 16.0/768 * YMod;
	DescScrolldown = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	DescScrolldown.NotifyWindow = Self;
	DescScrolldown.Text = "";
	DescScrolldown.bStretched = True;
	DescScrolldown.UpTexture = Texture(DynamicLoadObject("UTMenu.AroDup", Class'Texture'));
	DescScrolldown.OverTexture = Texture(DynamicLoadObject("UTMenu.AroDovr", Class'Texture'));
	DescScrolldown.DownTexture = Texture(DynamicLoadObject("UTMenu.AroDdwn", Class'Texture'));
	DescScrolldown.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	DescScrolldown.bAlwaysOnTop = True;

	if (PreferredSex == 1) {
		LadderObj.Sex = "F";
		SexButton.Text = MaleText;
	} else {
		LadderObj.Sex = "M";
		SexButton.Text = FemaleText;
	}

	SexPressed();
	Initialized = True;
	Root.Console.bBlackout = True;
}

function NextPressed () 
	{
	local int i;
	local ManagerWindow ManagerWindow;

	StartLadder();

	if (LadderObj.Sex ~= "F") {
		SexButton.Text = MaleText;
	} else {
		SexButton.Text = FemaleText;
	}

	SexPressed();
	LadderObj.DMRank = 0;
	LadderObj.DMPosition = -1;
	LadderObj.CTFRank = 0;
	LadderObj.CTFPosition = -1;
	LadderObj.DOMRank = 0;
	LadderObj.DOMPosition = -1;
	LadderObj.ASRank = 0;
	LadderObj.ASPosition = -1;
	LadderObj.ChalRank = 0;
	LadderObj.ChalPosition = 0;
	LadderObj = None;
	Close();
	ManagerWindow = ManagerWindow(Root.CreateWindow(Class'MyManagerWindow',100.0,100.0,200.0,200.0,Root,True));
}

All that remains is to subclass UTMenu.ManagerWindow as "MyManagerWindow".

class MyManagerWindow extends ManagerWindow Config(MyLadder);

function StartLadder () {
	UTConsole(GetPlayerOwner().Player.Console).ManagerWindowClass = "MyLadder.MiManagerWindow";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDMClass = "MyLadder.MyLadderDMMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDOMClass = "MyLadder.MyLadderDOMMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderCTFClass = "MyLadder.MyLadderCTFMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderASClass = "MyLadder.MyLadderASMenu";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderChalClass = "MyLadder.MyLadderChalMenu";
	UTConsole(GetPlayerOwner().Player.Console).InterimObjectType = "MyLadder.MiNewGameInterimObject";
	UTConsole(GetPlayerOwner().Player.Console).SlotWindowType = "MyLadder.MiSlotWindow";
	Log("Now Playing My Ladder");
}

function SwitchBack () {
	UTConsole(GetPlayerOwner().Player.Console).ManagerWindowClass = "UTMenu.ManagerWindow";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDMClass = "UTMenu.UTLadderDM";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderDOMClass = "UTMenu.UTLadderDOM";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderCTFClass = "UTMenu.UTLadderCTF";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderASClass = "UTMenu.UTLadderAS";
	UTConsole(GetPlayerOwner().Player.Console).UTLadderChalClass = "UTMenu.UTLadderChal";
	UTConsole(GetPlayerOwner().Player.Console).InterimObjectType = "UTMenu.NewGameInterimObject";
	UTConsole(GetPlayerOwner().Player.Console).SlotWindowType = "UTMenu.SlotWindow";
	Log("Now Playing UT Ladder");
}

function Created() {
	local float Xs, Ys;
	local int i;
	local int W, H;
	local float XWidth, YHeight, XMod, YMod, XPos, YPos;
	local color TextColor;

	Super.Created();

	// Window properties.
	bLeaveOnScreen = True;
	bAlwaysOnTop = True;
	class'UTLadderStub'.Static.GetStubClass().Static.SetupWinParams(Self, Root, W, H);

	XMod = 4*W;
	YMod = 3*H;

	// Background.
	BG1[0] = Texture(DynamicLoadObject(BGName1[0], Class'Texture'));
	BG1[1] = Texture(DynamicLoadObject(BGName1[1], Class'Texture'));
	BG1[2] = Texture(DynamicLoadObject(BGName1[2], Class'Texture'));
	BG1[3] = Texture(DynamicLoadObject(BGName1[3], Class'Texture'));
	BG2[0] = Texture(DynamicLoadObject(BGName2[0], Class'Texture'));
	BG2[1] = Texture(DynamicLoadObject(BGName2[1], Class'Texture'));
	BG2[2] = Texture(DynamicLoadObject(BGName2[2], Class'Texture'));
	BG2[3] = Texture(DynamicLoadObject(BGName2[3], Class'Texture'));
	BG3[0] = Texture(DynamicLoadObject(BGName3[0], Class'Texture'));
	BG3[1] = Texture(DynamicLoadObject(BGName3[1], Class'Texture'));
	BG3[2] = Texture(DynamicLoadObject(BGName3[2], Class'Texture'));
	BG3[3] = Texture(DynamicLoadObject(BGName3[3], Class'Texture'));

	// Window components.

// Check ladder object.
	LadderObj = LadderInventory(GetPlayerOwner().FindInventoryType(class'LadderInventory'));

	if (LadderObj == None) {
		Log("MyManagerWindow: Player has no LadderInventory!!");
	}

	LadderObj.LastMatchType = 0;

	// TDM ladder.
	XPos = 95.0/1024 * XMod;
	YPos = 102.0/768 * YMod;
	XWidth = 307.0/1024 * XMod;
	YHeight = 44.0/768 * YMod;
	DMLadderButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	DMLadderButton.NotifyWindow = Self;
	DMLadderButton.bStretched = True;
	DMLadderButton.Text = DMText;
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	DMLadderButton.SetTextColor(TextColor);
	DMLadderButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	// DOM ladder.
	XPos = 95.0/1024 * XMod;
	YPos = 231.0/768 * YMod;
	XWidth = 307.0/1024 * XMod;
	YHeight = 44.0/768 * YMod;
	DOMLadderButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	DOMLadderButton.NotifyWindow = Self;
	DOMLadderButton.bStretched = True;
	DOMLadderButton.Text = DOMText;
	TextColor.R = 255;
	TextColor.G = 0;
	TextColor.B = 0;
	DOMLadderButton.SetTextColor(TextColor);
	DOMLadderButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	// DOM Door.
	if (DOMDoorOpen[LadderObj.Slot] == 0) {
		XPos = 83.0/1024 * XMod;
		YPos = 222.0/768 * YMod;
		XWidth = 332.0/1024 * XMod;
		YHeight = 63.0/768 * YMod;
		DOMDoor = DoorArea(CreateWindow(class'DoorArea', XPos, YPos, XWidth, YHeight));
		DOMDoor.bClosed = True;
	}

	// CTF ladder.
	XPos = 95.0/1024 * XMod;
	YPos = 363.0/768 * YMod;
	XWidth = 307.0/1024 * XMod;
	YHeight = 44.0/768 * YMod;
	CTFLadderButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	CTFLadderButton.NotifyWindow = Self;
	CTFLadderButton.bStretched = True;

	if (LadderObj.CTFRank == 6) {
		CTFLadderButton.Text = " "$CTFText;
	} else {
		CTFLadderButton.Text = CTFText;
	}

	TextColor.R = 255;
	TextColor.G = 0;
	TextColor.B = 0;
	CTFLadderButton.SetTextColor(TextColor);
	CTFLadderButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	// CTF door.
	if (CTFDoorOpen[LadderObj.Slot] == 0) {
		XPos = 83.0/1024 * XMod;
		YPos = 356.0/768 * YMod;
		XWidth = 332.0/1024 * XMod;
		YHeight = 63.0/768 * YMod;
		CTFDoor = DoorArea(CreateWindow(class'DoorArea', XPos, YPos, XWidth, YHeight));
		CTFDoor.bClosed = True;
	}

	// AS Ladder.
	XPos = 95.0/1024 * XMod;
	YPos = 497.0/768 * YMod;
	XWidth = 307.0/1024 * XMod;
	YHeight = 44.0/768 * YMod;
	ASLadderButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	ASLadderButton.NotifyWindow = Self;
	ASLadderButton.bStretched = True;
	ASLadderButton.Text = ASText;
	TextColor.R = 255;
	TextColor.G = 0;
	TextColor.B = 0;
	ASLadderButton.SetTextColor(TextColor);
	ASLadderButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	// AS Door.
	if (ASDoorOpen[LadderObj.Slot] == 0) {
		XPos = 83.0/1024 * XMod;
		YPos = 488.0/768 * YMod;
		XWidth = 332.0/1024 * XMod;
		YHeight = 63.0/768 * YMod;
		ASDoor = DoorArea(CreateWindow(class'DoorArea', XPos, YPos, XWidth, YHeight));
		ASDoor.bClosed = True;
	}

	// Challenge ladder.
	XPos = 95.0/1024 * XMod;
	YPos = 627.0/768 * YMod;
	XWidth = 307.0/1024 * XMod;
	YHeight = 44.0/768 * YMod;
	ChallengeLadderButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	ChallengeLadderButton.NotifyWindow = Self;
	ChallengeLadderButton.bStretched = True;
	ChallengeLadderButton.Text = ChallengeText;
	TextColor.R = 255;
	TextColor.G = 0;
	TextColor.B = 0;
	ChallengeLadderButton.SetTextColor(TextColor);
	ChallengeLadderButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	// Challenge door.
	if (ChalDoorOpen[LadderObj.Slot] == 0) {
		XPos = 83.0/1024 * XMod;
		YPos = 618.0/768 * YMod;
		XWidth = 332.0/1024 * XMod;
		YHeight = 63.0/768 * YMod;
		ChalDoor = DoorArea(CreateWindow(class'DoorArea', XPos, YPos, XWidth, YHeight));
		ChalDoor.bClosed = True;
	}

	// Sala de Trofeos.
	XPos = 656.0/1024 * XMod;
	YPos = 63.0/768 * YMod;
	XWidth = 222.0/1024 * XMod;
	YHeight = 50.0/768 * YMod;
	TrophyButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	TrophyButton.NotifyWindow = Self;
	TrophyButton.bStretched = True;
	TrophyButton.Text = TrophyText;
	TextColor.R = 255;
	TextColor.G = 0;
	TextColor.B = 0;
	TrophyButton.SetTextColor(TextColor);
	TrophyButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	// Trophy door.
	if (TrophyDoorOpen[LadderObj.Slot] == 0) {
		XPos = 649.0/1024 * XMod;
		YPos = 57.0/768 * YMod;
		XWidth = 236.0/1024 * XMod;
		YHeight = 62.0/768 * YMod;
		TrophyDoor = DoorArea(CreateWindow(class'DoorArea', XPos, YPos, XWidth, YHeight));
		TrophyDoor.bClosed = True;
	}

	// Back button.
	XPos = 192.0/1024 * XMod;
	YPos = 701.0/768 * YMod;
	XWidth = 64.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	BackButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	BackButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.LeftUp", Class'Texture'));
	BackButton.UpTexture = Texture(DynamicLoadObject("UTMenu.LeftUp", Class'Texture'));
	BackButton.DownTexture = Texture(DynamicLoadObject("UTMenu.LeftDown", Class'Texture'));
	BackButton.OverTexture = Texture(DynamicLoadObject("UTMenu.LeftOver", Class'Texture'));
	BackButton.NotifyWindow = Self;
	BackButton.Text = "";
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	BackButton.SetTextColor(TextColor);
	BackButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);
	BackButton.bStretched = True;
	BackButton.OverSound = sound'LadderSounds.lcursorMove';
	BackButton.DownSound = sound'LadderSounds.ladvance';

	// Next button.
	XPos = 256.0/1024 * XMod;
	YPos = 701.0/768 * YMod;
	XWidth = 64.0/1024 * XMod;
	YHeight = 64.0/768 * YMod;
	NextButton = NotifyButton(CreateWindow(class'NotifyButton', XPos, YPos, XWidth, YHeight));
	NextButton.DisabledTexture = Texture(DynamicLoadObject("UTMenu.RightUp", Class'Texture'));
	NextButton.UpTexture = Texture(DynamicLoadObject("UTMenu.RightUp", Class'Texture'));
	NextButton.DownTexture = Texture(DynamicLoadObject("UTMenu.RightDown", Class'Texture'));
	NextButton.OverTexture = Texture(DynamicLoadObject("UTMenu.RightOver", Class'Texture'));
	NextButton.NotifyWindow = Self;
	NextButton.Text = "";
	TextColor.R = 255;
	TextColor.G = 255;
	TextColor.B = 0;
	NextButton.SetTextColor(TextColor);
	NextButton.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);
	NextButton.bStretched = True;
	NextButton.OverSound = sound'LadderSounds.lcursorMove';
	NextButton.DownSound = sound'LadderSounds.ladvance';

	// Info
	XPos = 617.0/1024 * XMod;
	YPos = 233.0/768 * YMod;
	XWidth = 303.0/1024 * XMod;
	YHeight = 440.0/768 * YMod;
	InfoArea = UTFadeTextArea(CreateWindow(Class<UWindowWindow>(DynamicLoadObject("UTMenu.UTFadeTextArea", Class'Class')), XPos, YPos, XWidth, YHeight));
	InfoArea.TextColor.R = 255;
	InfoArea.TextColor.G = 255;
	InfoArea.TextColor.B = 0;
	InfoArea.MyFont = class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
	InfoArea.FadeFactor = 8;
	InfoArea.Clear();

	if (Root.WinWidth < 512) {
		return;
	}

	if (LadderObj.DMPosition == -1) {
		InfoArea.AddText(MatchesString@"0");
	} else {
		if (LadderObj.DMRank == 6) {
			InfoArea.AddText(MatchesString@LadderObj.DMPosition);
		} else {
			InfoArea.AddText(MatchesString@(LadderObj.DMPosition-1));
		}
	}

	if (LadderObj.DMPosition >= 4) {
		InfoArea.AddText(" ");
		InfoArea.AddText(RankString[1]@class'MyLadderLadder'.Static.GetRank(LadderObj.DOMRank));
		
		if (LadderObj.DOMPosition == -1) {
			InfoArea.AddText(MatchesString@"0");
		} else {
			if (LadderObj.DOMRank == 6) {
				InfoArea.AddText(MatchesString@LadderObj.DOMPosition);
			} else {
				InfoArea.AddText(MatchesString@(LadderObj.DOMPosition-1));
			}
		}
	}

	if (LadderObj.DOMPosition >= 4) {
		InfoArea.AddText(" ");
		InfoArea.AddText(RankString[2]@class'MyLadderLadder'.Static.GetRank(LadderObj.CTFRank));
		
		if (LadderObj.CTFPosition == -1) {
			InfoArea.AddText(MatchesString@"0");
		} else {
			if (LadderObj.CTFRank == 6) {
				InfoArea.AddText(MatchesString@LadderObj.CTFPosition);
			} else {
				InfoArea.AddText(MatchesString@(LadderObj.CTFPosition-1));
			}
		}
	}

	if (LadderObj.CTFPosition >= 4) {
		InfoArea.AddText(" ");
		InfoArea.AddText(RankString[3]@class'MyLadderLadder'.Static.GetRank(LadderObj.ASRank));
		if (LadderObj.ASPosition == -1) {
			InfoArea.AddText(MatchesString@"0");
		} else {
			if (LadderObj.ASRank == 6) {
				InfoArea.AddText(MatchesString@LadderObj.ASPosition);
			} else {
				InfoArea.AddText(MatchesString@(LadderObj.ASPosition-1));
			}
		}
	}

	if ((LadderObj.DMRank == 6) && (LadderObj.DOMRank == 6) && (LadderObj.CTFRank == 6) && (LadderObj.ASRank == 6)) {
		InfoArea.AddText(" ");
		InfoArea.AddText(ChallengeString);
		InfoArea.AddText(ChalPosString@class'MyLadderLadder'.Static.GetRank(LadderObj.ChalRank));
	}
	Root.Console.bBlackOut = True;
}

function BeforePaint (Canvas C, float X, float Y)
{
	local LadderInventory LadderObj;
	local float Xs;
	local float Ys;
	local int i;
	local int W;
	local int H;
	local float XWidth;
	local float YHeight;
	local float XMod;
	local float YMod;
	local float XPos;
	local float YPos;

	Super.BeforePaint(C,X,Y);
	Class'UTLadderStub'.Static.GetStubClass().Static.SetupWinParams(self,Root,W,H);
	XMod = 4.0 * W;
	YMod = 3.0 * H;
	XPos = 95.0 / 1024 * XMod;
	YPos = 102.0 / 768 * YMod;
	XWidth = 307.0 / 1024 * XMod;
	YHeight = 44.0 / 768 * YMod;
	DMLadderButton.WinLeft = XPos;
	DMLadderButton.WinTop = YPos;
	DMLadderButton.SetSize(XWidth,YHeight);
	DMLadderButton.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);
	XPos = 95.0 / 1024 * XMod;
	YPos = 231.0 / 768 * YMod;
	XWidth = 307.0 / 1024 * XMod;
	YHeight = 44.0 / 768 * YMod;
	DOMLadderButton.WinLeft = XPos;
	DOMLadderButton.WinTop = YPos;
	DOMLadderButton.SetSize(XWidth,YHeight);
	DOMLadderButton.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	if (DOMDoor != None) {
		XPos = 83.0 / 1024 * XMod;
		YPos = 222.0 / 768 * YMod;
		XWidth = 332.0 / 1024 * XMod;
		YHeight = 63.0 / 768 * YMod;
		DOMDoor.WinLeft = XPos;
		DOMDoor.WinTop = YPos;
		DOMDoor.SetSize(XWidth,YHeight);
	}

	XPos = 95.0 / 1024 * XMod;
	YPos = 363.0 / 768 * YMod;
	XWidth = 307.0 / 1024 * XMod;
	YHeight = 44.0 / 768 * YMod;
	CTFLadderButton.WinLeft = XPos;
	CTFLadderButton.WinTop = YPos;
	CTFLadderButton.SetSize(XWidth,YHeight);
	CTFLadderButton.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	if (CTFDoor != None) {
		XPos = 83.0 / 1024 * XMod;
		YPos = 356.0 / 768 * YMod;
		XWidth = 332.0 / 1024 * XMod;
		YHeight = 63.0 / 768 * YMod;
		CTFDoor.WinLeft = XPos;
		CTFDoor.WinTop = YPos;
		CTFDoor.SetSize(XWidth,YHeight);
	}

	XPos = 95.0 / 1024 * XMod;
	YPos = 497.0 / 768 * YMod;
	XWidth = 307.0 / 1024 * XMod;
	YHeight = 44.0 / 768 * YMod;
	ASLadderButton.WinLeft = XPos;
	ASLadderButton.WinTop = YPos;
	ASLadderButton.SetSize(XWidth,YHeight);
	ASLadderButton.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	if (ASDoor != None) {
		XPos = 83.0 / 1024 * XMod;
		YPos = 488.0 / 768 * YMod;
		XWidth = 332.0 / 1024 * XMod;
		YHeight = 63.0 / 768 * YMod;
		ASDoor.WinLeft = XPos;
		ASDoor.WinTop = YPos;
		ASDoor.SetSize(XWidth,YHeight);
	}

	XPos = 95.0 / 1024 * XMod;
	YPos = 627.0 / 768 * YMod;
	XWidth = 307.0 / 1024 * XMod;
	YHeight = 44.0 / 768 * YMod;
	ChallengeLadderButton.WinLeft = XPos;
	ChallengeLadderButton.WinTop = YPos;
	ChallengeLadderButton.SetSize(XWidth,YHeight);
	ChallengeLadderButton.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	if ( ChalDoor != None ) {
		XPos = 83.0 / 1024 * XMod;
		YPos = 618.0 / 768 * YMod;
		XWidth = 332.0 / 1024 * XMod;
		YHeight = 63.0 / 768 * YMod;
		ChalDoor.WinLeft = XPos;
		ChalDoor.WinTop = YPos;
		ChalDoor.SetSize(XWidth,YHeight);
	}

	XPos = 656.0 / 1024 * XMod;
	YPos = 63.0 / 768 * YMod;
	XWidth = 222.0 / 1024 * XMod;
	YHeight = 50.0 / 768 * YMod;
	TrophyButton.WinLeft = XPos;
	TrophyButton.WinTop = YPos;
	TrophyButton.SetSize(XWidth,YHeight);
	TrophyButton.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetHugeFont(Root);

	if ( TrophyDoor != None )
	{
		XPos = 650.0 / 1024 * XMod;
		YPos = 58.0 / 768 * YMod;
		XWidth = 236.0 / 1024 * XMod;
		YHeight = 62.0 / 768 * YMod;
		TrophyDoor.WinLeft = XPos;
		TrophyDoor.WinTop = YPos;
		TrophyDoor.SetSize(XWidth,YHeight);
	}

	XPos = 192.0 / 1024 * XMod;
	YPos = 701.0 / 768 * YMod;
	XWidth = 64.0 / 1024 * XMod;
	YHeight = 64.0 / 768 * YMod;
	BackButton.SetSize(XWidth,YHeight);
	BackButton.WinLeft = XPos;
	BackButton.WinTop = YPos;
	XPos = 256.0 / 1024 * XMod;
	YPos = 701.0 / 768 * YMod;
	XWidth = 64.0 / 1024 * XMod;
	YHeight = 64.0 / 768 * YMod;
	NextButton.SetSize(XWidth,YHeight);
	NextButton.WinLeft = XPos;
	NextButton.WinTop = YPos;
	XPos = 617.0 / 1024 * XMod;
	YPos = 233.0 / 768 * YMod;
	XWidth = 303.0 / 1024 * XMod;
	YHeight = 440.0 / 768 * YMod;
	InfoArea.SetSize(XWidth,YHeight);
	InfoArea.WinLeft = XPos;
	InfoArea.WinTop = YPos;
	InfoArea.MyFont = Class'UTLadderStub'.Static.GetStubClass().Static.GetSmallFont(Root);
}

function OpenDoors ()
{
	local bool bOneOpened;

	if ( LadderObj.DMPosition >= 3 ) {
		if ( (DOMDoorOpen[LadderObj.Slot] == 0) && (DOMDoor != None) ) {
			DOMDoorOpen[LadderObj.Slot] = 1;
			DOMDoor.Open();
			SaveConfig();
			bOneOpened = True;
		}
	}

	if ( LadderObj.DOMPosition >= 2 ) {
		if ( (CTFDoorOpen[LadderObj.Slot] == 0) && (CTFDoor != None) ) {
			CTFDoorOpen[LadderObj.Slot] = 1;
			CTFDoor.Open();
			SaveConfig();
			bOneOpened = True;
		}
	}

	if ( LadderObj.CTFPosition >= 3 ) {
		if ( (ASDoorOpen[LadderObj.Slot] == 0) && (ASDoor != None) ) {
			ASDoorOpen[LadderObj.Slot] = 1;
			ASDoor.Open();
			SaveConfig();
			bOneOpened = True;
		}
	}

	if ((LadderObj.DMRank == 6) && (LadderObj.DOMRank == 6) && (LadderObj.CTFRank == 6) && (LadderObj.ASRank == 6)) {
		if ( (ChalDoorOpen[LadderObj.Slot] == 0) && (ChalDoor != None) )
		{
			ChalDoorOpen[LadderObj.Slot] = 1;
			ChalDoor.Open();
			SaveConfig();
			bOneOpened = True;
		}
	}

	if ( (LadderObj.DMRank == 6) || (LadderObj.DOMRank == 6) || (LadderObj.CTFRank == 6) || (LadderObj.ASRank == 6) ) {
		if ( (TrophyDoorOpen[LadderObj.Slot] == 0) && (TrophyDoor != None) )
		{
			TrophyDoorOpen[LadderObj.Slot] = 1;
			TrophyDoor.Open();
			SaveConfig();
			bOneOpened = True;
		}
	}

	if ( bOneOpened ) {
		GetPlayerOwner().PlaySound(Sound'LadderSounds.ldoorsopen1b',SLOT_Interface);
	}
	bOpened = True;
}

function ShowWindow ()
{
	Super(UWindowWindow).ShowWindow();

	if ((DOMDoorOpen[LadderObj.Slot] == 0) && (DOMDoor != None)) {
		DOMDoor.bClosed = True;
	}

	if ((CTFDoorOpen[LadderObj.Slot] == 0) && (CTFDoor != None)) {
		CTFDoor.bClosed = True;
	}

	if ((ASDoorOpen[LadderObj.Slot] == 0) && (ASDoor != None)) {
		ASDoor.bClosed = True;
	}

	if ((ChalDoorOpen[LadderObj.Slot] == 0) && (ChalDoor != None)) {
		ChalDoor.bClosed = True;
	}

	if ((TrophyDoorOpen[LadderObj.Slot] == 0) && (TrophyDoor != None)) {
		TrophyDoor.bClosed = True;
	}

	OpenTime = 0.0;
	InfoArea.Clear();

	if ( Root.WinWidth < 512 ) {
		return;
	}

	InfoArea.AddText(RankString[0] @ Class'MyLadderLadder'.Static.GetRank(LadderObj.DMRank));
	InfoArea.AddText(MatchesString @ string(LadderObj.DMPosition - 1));

	if (LadderObj.DMPosition >= 3) {
		InfoArea.AddText("");
		InfoArea.AddText(RankString[1] @ Class'MyLadderLadder'.Static.GetRank(LadderObj.DOMRank));
		InfoArea.AddText(MatchesString @ string(LadderObj.DOMPosition - 1));
	}

	if (LadderObj.DOMPosition >= 2) {
		InfoArea.AddText("");
		InfoArea.AddText(RankString[2] @ Class'MyLadderLadder'.Static.GetRank(LadderObj.CTFRank));
		InfoArea.AddText(MatchesString @ string(LadderObj.CTFPosition - 1));
	}

	if (LadderObj.CTFPosition >= 3) {
		InfoArea.AddText("");
		InfoArea.AddText(RankString[3] @ Class'MyLadderLadder'.Static.GetRank(LadderObj.ASRank));
		InfoArea.AddText(MatchesString @ string(LadderObj.ASPosition - 1));
	}

	if ((LadderObj.DMRank == 6) && (LadderObj.DOMRank == 6) && (LadderObj.CTFRank == 6) && (LadderObj.ASRank == 6)) {
		InfoArea.AddText(" ");
		InfoArea.AddText(ChallengeString);
		InfoArea.AddText(ChalPosString @ Class'MyLadderLadder'.Static.GetRank(LadderObj.ChalRank));
	}
}

defaultproperties
{
	LadderTypes(0)="MyLadder.MyLadderDMMenu"
	LadderTypes(1)="MyLadder.MyLadderDOMMenu"
	LadderTypes(2)="MyLadder.MyLadderCTFMenu"
	LadderTypes(3)="MyLadder.MyLadderASMenu"
	LadderTypes(4)="MyLadder.MyLadderChalMenu"
}

Some things to bear in mind:

  • We've mentioned this priorhand, but it deserves to be repeated: all the class'Ladder' were replaced by class'MyLadderLadder'. This is a necessary change, since otherwise, the game will revert to the default ladder with unpredictable consequences.
  • There are some minor things that we can do without much effort such as deciding how many matches must be beaten before the door to the next Ladder is opened, give new names to the Ladders, or configure them so other gametypes can be played on them.

The last thing to be done is to create the interface from which the new Ladder will be launched. So we create new scripts called "MyMenuMyClientWindow" (subclass of UMenu.UMenuPageWindow), "MyMenuMyItem" (subclass of UMenu.UMenuModMenuItem) and "MyMenuMyWindow" (subclass of UMenu.UMenuFramedWindow).


Unreal Tournament tutorials