Update typespecifiers.uc

Update typespecifiers.uc

Added widestring prefix

# typespecifiers.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 4/30/2014 8:52:37 PM
# Comment: Replaces data type specifiers with explicit type names

ExternalKeywords: Exclude, Comment, Selected, ParentChild, FindMode, InputFile, OutputFile, BatchAction, SEND
ExternalKeywords: Highlight, ForeColor, BackColor, FontName, FontSize, FontStyle
ExternalKeywords: FilterEndText, FilterSeparator, FilterSort, FilterSortFunc, FilterStartText, FilterUnique, FilterTally

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Replaces data type specifiers with explicit type names
Selected: False
Highlight: False
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
FilterTally: False
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
InputFile:
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find: {@Note:
         This transform does the following:

         * Adds explicit ByRef to args w/o ByVal or ByRef
         * Changes type specifiers to type names in param
           definitions and removes them elsewhere
         * Adds data type name to params that have neither
           an explicit type name or specifier, based on
           DefType definitions

         The section to the immediate right defines the
         required tables and an array to accomplish this.

         Bug: LCase() and UCase() are re-defined on the right
              due to a problem affecting pre-existing LCase/UCase.
              That is a temp solution.
      }
Replace: {@Define:
            LineContinue: " _"
            Var: DefTypeInitial(255) As String
            Var: Specifier As Table = _
                 {"%", "Integer", "&", "Long", "&&", "Quad", _
                  "?", "Byte", "??", "Word", "???", "DWORD", _
                  "!", "Single", "#", "double", "##", "Extended", _
                  "@", "Currency", "@@", "CurrencyX", _
                  "$", "string", "$$", "wstring"}
            Var: DefType As Table = _
                 {"INT", "Integer", "LNG", "Long", "QUD", "Quad", _
                 "BYT", "Byte", "WRD", "Word", "DWD", "Dword", _
                 "SNG", "Single", "DBL", "Double", "EXT", "Extended", _
                 "CUR", "Currency", "CUX", "CurrencyX", "STR", "String"}
            Var: dType="(INT|LNG|QUD|BYT|WRD|DWD|SNG|DBL|EXT|CUR|CUX|STR)\b"
            Func: LCase(c As String) As String = IIF(Asc(c) > 64 and Asc(c) < 91, Chr(Asc(c)+32), c)
            Func: UCase(c As String) As String = IIF(Asc(c) > 96 and Asc(c) < 123, Chr(Asc(c)-32), c)
            Func: Round(x) = Sgn(x)*Int(Abs(x)+0.5)
         }{@Define:: Token: \x27.* ~~ Properties: ucWhitespace}

Criteria: 2
Comment: Handles DefType declarations; also inserts <Arg> marker for easier parsing
Pass: 1

Criteria: 3
Comment: Stores the DefType statement associated with character initials
BackColor: Purple
Find: {"Def"}{type:"{@Eval: dType}"} {char} {nl}
Replace: {@Evaluate:
            SetVar(DefTypeInitial(Asc(LCase({char}))), ReadStr(DefType, {type}))
            SetVar(DefTypeInitial(Asc(UCase({char}))), ReadStr(DefType, {type}))
         }

Criteria: 4
Comment: Changes a DefType range into multiple DefType statements
BackColor: SlateBlue
PassOnce: False
Find: {"Def"}{type:"{@Eval: dType}"} {from}-{to} {nl}
Replace: {@Eval: Range(Asc("{from}"), Asc("{to}"), "'Def{type} '+Chr(x)+Chr(10)")}

Criteria: 5
Comment: Break DefType statements containing commas into multiple DefStype statements (one per line)
BackColor: Pink
PassOnce: False
Find: {"Def"}{type:"{@Eval: dType}"} {etc}, {more} {nl}
Replace: Def{type} {etc}
         Def{type} {more}

Criteria: 6
Comment: Inserts <arg> marker for easier parsing
BackColor: Red
Find: {nl}{decl: Sub | Function } {etc} ({args})
Replace: {nl}{decl} {etc}(<Arg>{@Evaluate: Replace({args}, ",", ", <Arg>")})

Criteria: 7
SkipOver: True
Find: {nl}Function =
Replace: [Skip over]

Criteria: 8
Comment: Inserts ByRef to params that have neither ByRef nor ByVal
Pass: 2

Criteria: 9
Comment: Inserts ByRef in front of params that do not explicitlly have ByVal or ByRef
BackColor: SandyBrown
Find: <Arg> [{optional: Optional}] {name:1}
Replace: <Arg>{optional} ByRef {name}

Criteria: 10
Comment: Skips params that already have ByVal or ByRef
BackColor: Green
SkipOver: True
Find: <Arg> [Optional] { ByVal | ByRef }
Replace: [Skip over]

Criteria: 11
Comment: Adds explicit data types to arg or variable declarations (functions also)
Pass: 3

Criteria: 12
BackColor: RoyalBlue
Find: <Arg> [Optional] {by: ByVal | ByRef } {name:1}
Replace: <Arg>{by} {name} As {@Evaluate: DefTypeInitial(Asc({name}))}

Criteria: 13
Find: <Arg> [Optional] {by: ByVal | ByRef } {name:1}{spec:"[!?@#$%&]+"}
Replace: <Arg>{by} {name} As {@Evaluate: ReadStr(Specifier, {spec})}

Criteria: 14
SkipOver: True
Find: <Arg> [Optional] {by: ByVal | ByRef } {name:1}[()] As
Replace: [Skip over]

Criteria: 15
Find: Dim {var:1}{comment:" *'.*"}{nl}
Replace: Dim {var} As {@Evaluate: DefTypeInitial(Asc({var}))}{comment}{nl}

Criteria: 16
BackColor: Tomato
PassOnce: False
Find: {decl: Dim|Global|Local|Static|Register}{etc}{spec:"[!?@#$%&]+"}
Replace: {decl} {etc} As {@Evaluate: ReadStr(Specifier, {spec})}

Criteria: 17
Comment: Adds the appropriate type for functions with no explicit type or specifier
BackColor: CornflowerBlue
Find: {nl}Function {name:1}([{args%}])
Replace: {nl}Function {name}({args}) As {@Evaluate: DefTypeInitial(Asc({name}))}

Criteria: 18
Comment: Changes function type specifier to data type name
Find: {nl}Function {name:1}{spec:"[!?@#$%&]+"}([{args%}])
Replace: {nl}Function {name}({args}) As {@Evaluate: ReadStr(Specifier, {spec})}

Criteria: 19
Comment: Skips functions that already have an explicit type
BackColor: Gold
Find: {nl}Function {name}([{args%}]) As {type}
Replace: {nl}Function {name}({args}) As {type}

Criteria: 20
Find: As {type:1}([{arraysize}])
Replace: ({arraysize}) As {type}

Criteria: 21
Comment: Cleans up; removes <Arg> marker; as well as type specifiers
Pass: 4

Criteria: 22
Comment: Removes <Arg> that was inserted in earlier pass
BackColor: SandyBrown
Find: <Arg>
Replace: {Nothing}

Criteria: 23
Comment: Removes statement specifiers
BackColor: Violet
Find: {variable:"[a-z][a-z0-9_]*"}{spec:"[!?@#$%&]+"}
Replace: {variable}

Criteria: 24
SkipOver: True
Find: {@Eval: "{'"+Retain(FileText("PBKeywords.txt"), "{keyword:'.*'}", Delim("\b|"))+"\b'}"}
Replace: [Skip over]

Criteria: 25
Find: {num:"([0-9]*\.)?[0-9]+(e[-+]?[0-9]+)?"}
      { {single: !} | {ext: #[#]} }

Replace: {num}{@Evaluate:
            IIf(Val({num})==Int(Val({num})), ".0")
         }{single:F}

Criteria: 26
Find: {num:"([0-9]*\.)?[0-9]+(e[-+]?[0-9]+)?"}
      { {quad: &&}|{long: &}|{dword: ???} }
Replace: {@Eval: Round({num})}{long: }{dword:U}{quad:L}

Criteria: 27
Find: {num:"&h[0-9a-f]+"}{ {quad: &&}|{long: &}|{dword: ???} }
Replace: {num}{long: }{dword:U}{quad:L}

Criteria: 28
Selected: True
Find: {text:"\q[^\q]*\q"}$$
Replace: L{text}

Criteria: 29
Comment: Accommodates array passed as arg
Pass: 5

Criteria: 30
Find: As ()
Replace: ()

# End Search

# typespecifiers.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 4/29/2014 9:56:49 AM
# Comment: Replaces data type specifiers with explicit type names

ExternalKeywords: Exclude, Comment, Selected, ParentChild, FindMode, InputFile, OutputFile, BatchAction, SEND
ExternalKeywords: Highlight, ForeColor, BackColor, FontName, FontSize, FontStyle
ExternalKeywords: FilterEndText, FilterSeparator, FilterSort, FilterSortFunc, FilterStartText, FilterUnique, FilterTally

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Replaces data type specifiers with explicit type names
Selected: False
Highlight: False
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
FilterTally: False
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
InputFile:
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find: {@Note:
         This transform does the following:

         * Adds explicit ByRef to args w/o ByVal or ByRef
         * Changes type specifiers to type names in param
           definitions and removes them elsewhere
         * Adds data type name to params that have neither
           an explicit type name or specifier, based on
           DefType definitions

         The section to the immediate right defines the
         required tables and an array to accomplish this.

         Bug: LCase() and UCase() are re-defined on the right
              due to a problem affecting pre-existing LCase/UCase.
              That is a temp solution.
      }
Replace: {@Define:
            LineContinue: " _"
            Var: DefTypeInitial(255) As String
            Var: Specifier As Table = _
                 {"%", "Integer", "&", "Long", "&&", "Quad", _
                  "?", "Byte", "??", "Word", "???", "DWORD", _
                  "!", "Single", "#", "double", "##", "Extended", _
                  "@", "Currency", "@@", "CurrencyX", _
                  "$", "string", "$$", "wstring"}
            Var: DefType As Table = _
                 {"INT", "Integer", "LNG", "Long", "QUD", "Quad", _
                 "BYT", "Byte", "WRD", "Word", "DWD", "Dword", _
                 "SNG", "Single", "DBL", "Double", "EXT", "Extended", _
                 "CUR", "Currency", "CUX", "CurrencyX", "STR", "String"}
            Var: dType="(INT|LNG|QUD|BYT|WRD|DWD|SNG|DBL|EXT|CUR|CUX|STR)\b"
            Func: LCase(c As String) As String = IIF(Asc(c) > 64 and Asc(c) < 91, Chr(Asc(c)+32), c)
            Func: UCase(c As String) As String = IIF(Asc(c) > 96 and Asc(c) < 123, Chr(Asc(c)-32), c)
            Func: Round(x) = Sgn(x)*Int(Abs(x)+0.5)
         }{@Define:: Token: \x27.* ~~ Properties: ucWhitespace}

Criteria: 2
Comment: Handles DefType declarations; also inserts <Arg> marker for easier parsing
Pass: 1

Criteria: 3
Comment: Stores the DefType statement associated with character initials
BackColor: Purple
Find: {"Def"}{type:"{@Eval: dType}"} {char} {nl}
Replace: {@Evaluate:
            SetVar(DefTypeInitial(Asc(LCase({char}))), ReadStr(DefType, {type}))
            SetVar(DefTypeInitial(Asc(UCase({char}))), ReadStr(DefType, {type}))
         }

Criteria: 4
Comment: Changes a DefType range into multiple DefType statements
BackColor: SlateBlue
PassOnce: False
Find: {"Def"}{type:"{@Eval: dType}"} {from}-{to} {nl}
Replace: {@Eval: Range(Asc("{from}"), Asc("{to}"), "'Def{type} '+Chr(x)+Chr(10)")}

Criteria: 5
Comment: Break DefType statements containing commas into multiple DefStype statements (one per line)
BackColor: Pink
PassOnce: False
Find: {"Def"}{type:"{@Eval: dType}"} {etc}, {more} {nl}
Replace: Def{type} {etc}
         Def{type} {more}

Criteria: 6
Comment: Inserts <arg> marker for easier parsing
BackColor: Red
Find: {nl}{decl: Sub | Function } {etc} ({args})
Replace: {nl}{decl} {etc}(<Arg>{@Evaluate: Replace({args}, ",", ", <Arg>")})

Criteria: 7
SkipOver: True
Find: {nl}Function =
Replace: [Skip over]

Criteria: 8
Comment: Inserts ByRef to params that have neither ByRef nor ByVal
Pass: 2

Criteria: 9
Comment: Inserts ByRef in front of params that do not explicitlly have ByVal or ByRef
BackColor: SandyBrown
Find: <Arg> [{optional: Optional}] {name:1}
Replace: <Arg>{optional} ByRef {name}

Criteria: 10
Comment: Skips params that already have ByVal or ByRef
BackColor: Green
SkipOver: True
Find: <Arg> [Optional] { ByVal | ByRef }
Replace: [Skip over]

Criteria: 11
Comment: Adds explicit data types to arg or variable declarations (functions also)
Pass: 3

Criteria: 12
BackColor: RoyalBlue
Find: <Arg> [Optional] {by: ByVal | ByRef } {name:1}
Replace: <Arg>{by} {name} As {@Evaluate: DefTypeInitial(Asc({name}))}

Criteria: 13
Find: <Arg> [Optional] {by: ByVal | ByRef } {name:1}{spec:"[!?@#$%&]+"}
Replace: <Arg>{by} {name} As {@Evaluate: ReadStr(Specifier, {spec})}

Criteria: 14
SkipOver: True
Find: <Arg> [Optional] {by: ByVal | ByRef } {name:1}[()] As
Replace: [Skip over]

Criteria: 15
Find: Dim {var:1}{comment:" *'.*"}{nl}
Replace: Dim {var} As {@Evaluate: DefTypeInitial(Asc({var}))}{comment}{nl}

Criteria: 16
BackColor: Tomato
PassOnce: False
Find: {decl: Dim|Global|Local|Static|Register}{etc}{spec:"[!?@#$%&]+"}
Replace: {decl} {etc} As {@Evaluate: ReadStr(Specifier, {spec})}

Criteria: 17
Comment: Adds the appropriate type for functions with no explicit type or specifier
BackColor: CornflowerBlue
Find: {nl}Function {name:1}([{args%}])
Replace: {nl}Function {name}({args}) As {@Evaluate: DefTypeInitial(Asc({name}))}

Criteria: 18
Comment: Changes function type specifier to data type name
Find: {nl}Function {name:1}{spec:"[!?@#$%&]+"}([{args%}])
Replace: {nl}Function {name}({args}) As {@Evaluate: ReadStr(Specifier, {spec})}

Criteria: 19
Comment: Skips functions that already have an explicit type
BackColor: Gold
Find: {nl}Function {name}([{args%}]) As {type}
Replace: {nl}Function {name}({args}) As {type}

Criteria: 20
Find: As {type:1}([{arraysize}])
Replace: ({arraysize}) As {type}

Criteria: 21
Comment: Cleans up; removes <Arg> marker; as well as type specifiers
Pass: 4

Criteria: 22
Comment: Removes <Arg> that was inserted in earlier pass
BackColor: SandyBrown
Find: <Arg>
Replace: {Nothing}

Criteria: 23
Comment: Removes statement specifiers
BackColor: Violet
Find: {variable:"[a-z][a-z0-9_]*"}{spec:"[!?@#$%&]+"}
Replace: {variable}

Criteria: 24
SkipOver: True
Find: {@Eval: "{'"+Retain(FileText("PBKeywords.txt"), "{keyword:'.*'}", Delim("\b|"))+"\b'}"}
Replace: [Skip over]

Criteria: 25
Selected: True
Find: {num:"([0-9]*\.)?[0-9]+(e[-+]?[0-9]+)?"}
      { {single: !} | {ext: #[#]} }

Replace: {num}{@Evaluate:
            IIf(Val({num})==Int(Val({num})), ".0")
         }{single:F}

Criteria: 26
Find: {num:"([0-9]*\.)?[0-9]+(e[-+]?[0-9]+)?"}
      { {quad: &&}|{long: &}|{dword: ???} }
Replace: {@Eval: Round({num})}{long: }{dword:U}{quad:L}

Criteria: 27
Find: {num:"&h[0-9a-f]+"}{ {quad: &&}|{long: &}|{dword: ???} }
Replace: {num}{long: }{dword:U}{quad:L}

Criteria: 28
Comment: Accommodates array passed as arg
Pass: 5

Criteria: 29
Find: As ()
Replace: ()

# End Search


Fixed simple ammo override by non GC-ed page

class TestCWRUIConfig extends UTUIFrontEnd;

struct DynamicPageInfo
{
	var name Tag;
	var EReplacementType ReplacementType;
	var name DatastoreReference;

	var bool Hidden;
	var name After;

	// Runtime
	var transient TestCWRUITabList CreatedPage;
};

struct LocalizedPageCaptionMap
{
	var localized name Tag;
	var localized string Text;
};

var() transient localized string Title;
var() transient localized string BuildingUIMessage;

var() transient localized string ConfirmNoOptionsMessage;
var() transient localized string ConfirmNoOptionsTitle;
var() transient localized string ConfirmNoOptionsButtonAccept;

var() transient localized string ConfirmHideAmmoMessage;
var() transient localized string ConfirmHideAmmoTitle;
var() transient localized string ConfirmHideAmmoButtonAccept;

var() transient localized string CustomAmmoReplacementMessage;
var() transient localized string CustomAmmoReplacementTitle;

var() transient localized string ShowAmmoCaption;
var() transient localized string HideAmmoCaption;
var() transient localized string TransferAmmoCaption;

var() transient localized array<LocalizedPageCaptionMap> TitlesMapping;
var() transient array<DynamicPageInfo> DynamicPages;

/** Reference to the messagebox scene. */
var transient UTUIScene_MessageBox MessageBoxReference;

var transient TestCWRUI UIData;

var bool bAmmoVisible;
var bool bAmmoInfoShown;

/** Initialize callback */
event Initialized()
{
	local int i;
	local UTUITabControl TempTabControl;

	Super.Initialized();

	TempTabControl = UTUITabControl(FindChild('pnlTabControl', true));

	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].Hidden) continue;

		DynamicPages[i].CreatedPage = CreateReplacementTab(TempTabControl, DynamicPages[i]);
	}

	i = DynamicPages.Find('Tag', 'Weapons');
	if (i != INDEX_NONE && DynamicPages[i].CreatedPage != none)
	{
		DynamicPages[i].CreatedPage.OnExtraSave = OnReplacementsExtraSave_Weapons;
	}
}

/** Scene activated event, sets up the title for the scene. */
event SceneActivated(bool bInitialActivation)
{
	local array<string> MessageBoxOptions;

	Super.SceneActivated(bInitialActivation);

	if (bInitialActivation)
	{
		UIData = class'TestCWRUI'.static.GetData();

		MessageBoxReference = GetMessageBoxScene();
		if (MessageBoxReference != none)
		{
			MessageBoxOptions.Length = 0;
			MessageBoxReference.SetPotentialOptions(MessageBoxOptions);
			MessageBoxReference.Display(BuildingUIMessage, Title);
			LoadTimed();
		}
		else
		{
			InitLoad();
		}
	}
}

function InitLoad()
{
	UIData.LoadAll();
	InitReplacements();

	if (MessageBoxReference != none)
	{
		CloseScene(MessageBoxReference);
	}
}

function InitReplacements()
{
	local int i, AmmoIdx, InsertIdx;
	local array<ReplacementInfoEx> ConfigWeaponReplacements, ConfigAmmoReplacements;
	local array<ReplacementInfoEx> SimpleAmmoReplacements;
	local ReplacementOptionsInfo EmptyOptions;
	local bool bCustomAmmo;

	// update all dynamic pages
	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].CreatedPage != none)
		{
			DynamicPages[i].CreatedPage.LoadReplacements(UIData);
		}
	}

	// check if config ammo replacements differ from current simple ammo replacements
	if (class'TestCentralWeaponReplacement'.static.GetConfigReplacements(RT_Weapon, ConfigWeaponReplacements) &&
		class'TestCentralWeaponReplacement'.static.GetConfigReplacements(RT_Ammo, ConfigAmmoReplacements))
	{
		for (i=0; i<ConfigAmmoReplacements.Length; i++)
		{
			if (ConfigAmmoReplacements[i].Options != EmptyOptions)
			{
				bCustomAmmo = true;
				break;
			}
		}

		if (!bCustomAmmo)
		{
			SimpleAmmoReplacements = GetAmmoBasicReplacementsFor(ConfigWeaponReplacements);

			if (ConfigAmmoReplacements.Length != SimpleAmmoReplacements.Length)
			{
				bCustomAmmo = true;
			}
			else
			{
				for (i=0; i<SimpleAmmoReplacements.Length; i++)
				{
					ConfigAmmoReplacements.RemoveItem(SimpleAmmoReplacements[i]);
				}

				if (ConfigAmmoReplacements.Length > 0)
				{
					bCustomAmmo = true;
				}
			}
		}
	}

	// add ammo tab if custom ammo replacements are found
	AmmoIdx = DynamicPages.Find('Tag', 'Ammo');
	if (bCustomAmmo && AmmoIdx != INDEX_NONE)
	{
		bAmmoVisible = true;
		InsertIdx = DynamicPages.Find('Tag', DynamicPages[AmmoIdx].After);
		if (InsertIdx != INDEX_NONE) InsertIdx += 1;
		DynamicPages[AmmoIdx].CreatedPage = CreateReplacementTab(TabControl, DynamicPages[AmmoIdx], InsertIdx, true);
		if (DynamicPages[AmmoIdx].CreatedPage != none)
		{
			DynamicPages[AmmoIdx].CreatedPage.LoadReplacements(UIData);
		}
	}
}

/** Sets the title for this scene. */
function SetTitle()
{
	local UILabel TitleLabel;

	TitleLabel = GetTitleLabel();
	if ( TitleLabel != None )
	{
		TitleLabel.SetDataStoreBinding(Title);
	}
}

/** Setup the scene's button bar. */
function SetupButtonBar()
{
	local int index;

	if(ButtonBar != None)
	{
		ButtonBar.Clear();
		ButtonBar.AppendButton("<Strings:UTGameUI.ButtonCallouts.Back>", OnButtonBar_Back);
		ButtonBar.AppendButton("<Strings:UTGameUI.ButtonCallouts.Accept>", OnButtonBar_Accept);

		if(TabControl != None && UTTabPage(TabControl.ActivePage) != none)
		{
			if (bAmmoVisible)
			{
				index = DynamicPages.Find('Tag', 'Ammo');
				if (index != INDEX_NONE && TabControl.ActivePage == DynamicPages[index].CreatedPage)
				{
					ButtonBar.AppendButton(HideAmmoCaption, OnButtonBar_HideAmmo);
				}
			}

			index = DynamicPages.Find('Tag', 'Weapons');
			if (index != INDEX_NONE && TabControl.ActivePage == DynamicPages[index].CreatedPage)
			{
				ButtonBar.AppendButton(bAmmoVisible ? HideAmmoCaption : ShowAmmoCaption, OnButtonBar_ShowAmmo);
				if (bAmmoVisible)
				{
					ButtonBar.AppendButton(TransferAmmoCaption, OnButtonBar_TransferAmmo);
				}
			}

			// Let the current tab page append buttons.
			UTTabPage(TabControl.ActivePage).SetupButtonBar(ButtonBar);
		}
	}
}

function bool HandleInputKey( const out InputEventParameters EventParms )
{
	local bool bResult;
	local UTTabPage CurrentTabPage;

	// Let the tab page's get first chance at the input
	CurrentTabPage = UTTabPage(TabControl.ActivePage);
	bResult = CurrentTabPage.HandleInputKey(EventParms);

	// If the tab page didn't handle it, let's handle it ourselves.
	if(bResult==false)
	{
		if(EventParms.EventType==IE_Released)
		{
			if(EventParms.InputKeyName=='XboxTypeS_B' || EventParms.InputKeyName=='Escape')
			{
				OnBack();
				bResult=true;
			}
		}
	}

	return bResult;
}

/** Buttonbar Accept Callback. */
function bool OnButtonBar_Accept(UIScreenObject InButton, int PlayerIndex)
{
	local array<string> MessageBoxOptions;
	local array<string> pages;
	local string pagesstr, str;

	if (HasIgnoredOptions(pages))
	{
		MessageBoxReference = GetMessageBoxScene();
		if(MessageBoxReference != none)
		{
			JoinArray(pages, pagesstr, "\n");
			str = Repl(ConfirmNoOptionsMessage, "  ", "\n");
			str = Repl(str, "`pages", pagesstr);

			MessageBoxOptions.AddItem(ConfirmNoOptionsButtonAccept);
			MessageBoxOptions.AddItem("<Strings:UTGameUI.ButtonCallouts.Cancel>");

			MessageBoxReference.SetPotentialOptions(MessageBoxOptions);
			MessageBoxReference.Display(str, ConfirmNoOptionsTitle, OnAccept_Confirm, 1);
			return true;
		}
	}

	OnAccept();
	CloseScene(Self);

	return true;
}

/** Buttonbar Back Callback. */
function bool OnButtonBar_Back(UIScreenObject InButton, int InPlayerIndex)
{
	OnBack();

	return true;
}

/** Buttonbar ShowAmmo Callback. */
function bool OnButtonBar_HideAmmo(UIScreenObject InButton, int InPlayerIndex)
{
	local array<string> MessageBoxOptions;

	MessageBoxReference = GetMessageBoxScene();
	if(MessageBoxReference != none)
	{
		MessageBoxOptions.AddItem(ConfirmHideAmmoButtonAccept);
		MessageBoxOptions.AddItem("<Strings:UTGameUI.ButtonCallouts.Cancel>");

		MessageBoxReference.SetPotentialOptions(MessageBoxOptions);
		MessageBoxReference.Display(Repl(ConfirmHideAmmoMessage, "  ", "\n"), ConfirmHideAmmoTitle, OnHideAmmo_Confirm, 1);
		return true;
	}

	ToggleAmmoTab(true);
	return true;
}

/** Buttonbar ShowAmmo Callback. */
function bool OnButtonBar_ShowAmmo(UIScreenObject InButton, int InPlayerIndex)
{
	ToggleAmmoTab();

	return true;
}

/** Buttonbar TransferAmmo Callback. */
function bool OnButtonBar_TransferAmmo(UIScreenObject InButton, int InPlayerIndex)
{
	TransferAmmo();

	return true;
}

/**
 * Callback for the accept confirmation dialog box.
 *
 * @param SelectedOption	Selected item
 * @param PlayerIndex	Index of player that performed the action.
 */
function OnAccept_Confirm(UTUIScene_MessageBox MessageBox, int SelectedOption, int PlayerIndex)
{
	if(SelectedOption == 0)
	{
		OnAccept();
		CloseScene(self);
	}
}

/**
 * Callback for the ignore ammo replacement dialog box.
 *
 * @param SelectedOption	Selected item
 * @param PlayerIndex	Index of player that performed the action.
 */
function OnHideAmmo_Confirm(UTUIScene_MessageBox MessageBox, int SelectedOption, int PlayerIndex)
{
	if(SelectedOption == 0)
	{
		ToggleAmmoTab(true);
	}
}

/** Callback for when the user wants to back out of this screen. */
function OnBack()
{
	CloseScene(self);
}

function OnAccept()
{
	local int i;

	// save all dynamic pages
	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].CreatedPage != none)
		{
			DynamicPages[i].CreatedPage.SaveReplacements();
		}
	}
}

function OnReplacementsExtraSave_Weapons(TestCWRUITabList RefTab)
{
	local array<ReplacementInfoEx> WeaponReplacements, AmmoReplacements;

	if (!bAmmoVisible)
	{
		WeaponReplacements = RefTab.GetReplacements();
		AmmoReplacements = GetAmmoBasicReplacementsFor(WeaponReplacements);

		class'TestCentralWeaponReplacement'.static.SetConfigReplacements(RT_Ammo, AmmoReplacements);
	}
}

function ToggleAmmoTab(optional bool bForceHide)
{
	local int AmmoIdx, InsertIdx;
	local TestCWRUITabList TabPage;
	AmmoIdx = DynamicPages.Find('Tag', 'Ammo');
	if (AmmoIdx == INDEX_NONE)
		return;

	if (bAmmoVisible || bForceHide)
	{
		bAmmoVisible = false;
		if (DynamicPages[AmmoIdx].CreatedPage != none)
		{
			TabControl.RemovePage(DynamicPages[AmmoIdx].CreatedPage, GetBestPlayerIndex());
			DynamicPages[AmmoIdx].CreatedPage = none;
		}
	}
	else
	{
		bAmmoVisible = true;
		InsertIdx = DynamicPages.Find('Tag', DynamicPages[AmmoIdx].After);
		if (InsertIdx != INDEX_NONE) InsertIdx += 1;
		TabPage = CreateReplacementTab(TabControl, DynamicPages[AmmoIdx], InsertIdx, true);
		if (TabPage != none)
		{
			DynamicPages[AmmoIdx].CreatedPage = TabPage;
			TabPage.LoadReplacements(UIData);
		}

		if (!bAmmoInfoShown)
		{
			bAmmoInfoShown = true;
			DisplayMessageBox(Repl(CustomAmmoReplacementMessage, "  ", "\n"), CustomAmmoReplacementTitle);
		}
	}

	SetupButtonBar();
}

function TransferAmmo()
{
	local int WeaponIdx, AmmoIdx;
	local array<ReplacementInfoEx> WeaponReplacements, AmmoReplacements;

	WeaponIdx = DynamicPages.Find('Tag', 'Weapons');
	AmmoIdx = DynamicPages.Find('Tag', 'Ammo');
	if (WeaponIdx == INDEX_NONE || AmmoIdx == INDEX_NONE ||
		DynamicPages[WeaponIdx].CreatedPage == none || DynamicPages[AmmoIdx].CreatedPage == none)
		return;

	WeaponReplacements = DynamicPages[WeaponIdx].CreatedPage.GetReplacements();
	AmmoReplacements = GetAmmoBasicReplacementsFor(WeaponReplacements);

	if (AmmoReplacements.Length > 0)
	{
		DynamicPages[AmmoIdx].CreatedPage.AddReplacements(AmmoReplacements, true);
	}
}

function array<ReplacementInfoEx> GetAmmoBasicReplacementsFor(array<ReplacementInfoEx> WeaponReplacements)
{
	local ReplacementInfoEx Replacement;
	local int i;
	local name AmmoClassFrom;
	local string AmmoClassTo;
	local array<ReplacementInfoEx> Replacements;

	for (i=0; i<WeaponReplacements.Length; i++)
	{
		if (!UIData.GetAmmoInfoForWeapon(WeaponReplacements[i].OldClassName,, AmmoClassFrom))
			continue;

		Replacement.OldClassName = AmmoClassFrom;

		if (UIData.GetAmmoInfoForWeapon(WeaponReplacements[i].NewClassPath, AmmoClassTo))
		{
			Replacement.NewClassPath = AmmoClassTo;
		}
		else
		{
			Replacement.NewClassPath = "";
		}

		Replacements.AddItem(Replacement);
	}

	return Replacements;
}

function bool HasIgnoredOptions(out array<string> OutPages)
{
	local int i;
	local string str;

	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].CreatedPage != none && DynamicPages[i].CreatedPage.IsIgnoringOptions())
		{
			str = GetTabString(DynamicPages[i].Tag);
			OutPages.AddItem(str);
		}
	}

	return OutPages.Length > 0;
}

function LoadTimed(optional float time = 2.0)
{
	local PlayerController PC;

	PC = GetPlayerOwner().Actor;
	if (PC != none)
	{
		PC.SetTimer(2.0, false, 'InitLoad', self);
	}
	else
	{
		InitLoad();
	}
}

function string GetTabString(name Tag)
{
	local int index;
	index = TitlesMapping.Find('Tag', Tag);
	if (index != INDEX_NONE)
	{
		return TitlesMapping[index].Text;
	}

	 Return Localize("Titles", string(Tag), string(class.GetPackageName()));
}

function TestCWRUITabList CreateReplacementTab(UTUITabControl InTabControl, DynamicPageInfo PageInfo, optional int InsertIndex = INDEX_NONE, optional bool bSkipActivate, optional int PlayerIndex=GetBestPlayerIndex())
{
	local TestCWRUITabList TabPage;
	Local name TabName;
	TabName = name("pnlTabRep_"$PageInfo.Tag);
	TabPage = TestCWRUITabList(CreateNamedTabPage(InTabControl, class'TestCWRUITabList', TabName));
	if (TabPage != none)
	{
		TabPage.SetTitle(Caps(GetTabString(PageInfo.Tag)));
		TabPage.SetReplacementInfo(PageInfo.ReplacementType, PageInfo.DatastoreReference);

		InTabControl.InsertPage(TabPage, PlayerIndex, InsertIndex, !bSkipActivate);
	}

	return TabPage;
}

static function UITabPage CreateNamedTabPage(UITabControl OwnerControl, class<UITabPage> TabPageClass, optional name WidgetName, optional UITabPage PagePrefab )
{
	local UITabPage TabPage;
	TabPage = UITabPage(OwnerControl.CreateWidget(OwnerControl, TabPageClass, PagePrefab, WidgetName));
	if (TabPage != none) TabPage.GetTabButton(OwnerControl);
	return TabPage;
}

DefaultProperties
{
	Title="[Central Weapon Replacement]"
	BuildingUIMessage="Building UI. May take some seconds..."

	ConfirmNoOptionsMessage="You have switched to simple mode but there are replacement options set for the following pages:  `pages    By continuing saving the replacements, the options will be cleared to default. Do you want to continue?"
	ConfirmNoOptionsTitle="Clear out options"
	ConfirmNoOptionsButtonAccept="CONTINUE"

	CustomAmmoReplacementMessage="By enabling custom ammo replacement, you have to either transfer all the ammo replacements from the current weapon replacement or setup your own replacements.    No ammo pickups will be replaced automatically by whatever weapon you choose to be replaced."
	CustomAmmoReplacementTitle="Custom ammo replacment"

	ConfirmHideAmmoMessage="You made changes to the ammo replacements. By switching back to simple ammo replacement (based on the selected weapons), you will loose any made changes to the ammo replacements.    Do you want to ignore these changes and switch to simple ammo replacement?"
	ConfirmHideAmmoTitle="Ignore custom replacements?"
	ConfirmHideAmmoButtonAccept="IGNORE"

	ShowAmmoCaption="CUSTOM AMMO"
	HideAmmoCaption="SIMPLE AMMO"
	TransferAmmoCaption="TRANSFER AMMO"

	TitlesMapping[0]=(Tag="Weapons",Text="Weapons")
	TitlesMapping[1]=(Tag="Healths",Text="Health")
	TitlesMapping[2]=(Tag="Armors",Text="Armor")
	TitlesMapping[3]=(Tag="Powerups",Text="Powerups")
	TitlesMapping[4]=(Tag="Deployables",Text="Deployables")
	TitlesMapping[5]=(Tag="Vehicles",Text="Vehicles")
	TitlesMapping[6]=(Tag="Customs",Text="Customs")
	TitlesMapping[7]=(Tag="Ammos",Text="Ammo")

	DynamicPages.Add((Tag="Weapons",ReplacementType=RT_Weapon,DatastoreReference="WeaponSelection"))
	DynamicPages.Add((Tag="Ammo",ReplacementType=RT_Ammo,DatastoreReference="AmmoSelection",Hidden=true,After="Weapons"))
	DynamicPages.Add((Tag="Healths",ReplacementType=RT_Health,DatastoreReference="HealthSelection"))
	DynamicPages.Add((Tag="Armors",ReplacementType=RT_Armor,DatastoreReference="ArmorSelection"))
	DynamicPages.Add((Tag="Powerups",ReplacementType=RT_Powerup,DatastoreReference="PowerupSelection"))
	DynamicPages.Add((Tag="Deployables",ReplacementType=RT_Deployable,DatastoreReference="DeployableSelection"))
	DynamicPages.Add((Tag="Vehicles",ReplacementType=RT_Vehicle,DatastoreReference="VehicleSelection"))
	DynamicPages.Add((Tag="Customs",ReplacementType=RT_Custom,DatastoreReference="CustomSelection",Hidden=true))
}

class TestCWRUIConfig extends UTUIFrontEnd;

struct DynamicPageInfo
{
	var name Tag;
	var EReplacementType ReplacementType;
	var name DatastoreReference;

	var bool Hidden;
	var name After;

	// Runtime
	var transient TestCWRUITabList CreatedPage;
};

struct LocalizedPageCaptionMap
{
	var localized name Tag;
	var localized string Text;
};

var() transient localized string Title;
var() transient localized string BuildingUIMessage;

var() transient localized string ConfirmNoOptionsMessage;
var() transient localized string ConfirmNoOptionsTitle;
var() transient localized string ConfirmNoOptionsButtonAccept;

var() transient localized string ConfirmHideAmmoMessage;
var() transient localized string ConfirmHideAmmoTitle;
var() transient localized string ConfirmHideAmmoButtonAccept;

var() transient localized string CustomAmmoReplacementMessage;
var() transient localized string CustomAmmoReplacementTitle;

var() transient localized string ShowAmmoCaption;
var() transient localized string HideAmmoCaption;
var() transient localized string TransferAmmoCaption;

var() transient localized array<LocalizedPageCaptionMap> TitlesMapping;
var() transient array<DynamicPageInfo> DynamicPages;

/** Reference to the messagebox scene. */
var transient UTUIScene_MessageBox MessageBoxReference;

var transient TestCWRUI UIData;

var bool bAmmoVisible;
var bool bAmmoInfoShown;

/** Initialize callback */
event Initialized()
{
	local int i;
	local UTUITabControl TempTabControl;

	Super.Initialized();

	TempTabControl = UTUITabControl(FindChild('pnlTabControl', true));

	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].Hidden) continue;

		DynamicPages[i].CreatedPage = CreateReplacementTab(TempTabControl, DynamicPages[i]);
	}

	i = DynamicPages.Find('Tag', 'Weapons');
	if (i != INDEX_NONE && DynamicPages[i].CreatedPage != none)
	{
		DynamicPages[i].CreatedPage.OnExtraSave = OnReplacementsExtraSave_Weapons;
	}
}

/** Scene activated event, sets up the title for the scene. */
event SceneActivated(bool bInitialActivation)
{
	local array<string> MessageBoxOptions;

	Super.SceneActivated(bInitialActivation);

	if (bInitialActivation)
	{
		UIData = class'TestCWRUI'.static.GetData();

		MessageBoxReference = GetMessageBoxScene();
		if (MessageBoxReference != none)
		{
			MessageBoxOptions.Length = 0;
			MessageBoxReference.SetPotentialOptions(MessageBoxOptions);
			MessageBoxReference.Display(BuildingUIMessage, Title);
			LoadTimed();
		}
		else
		{
			InitLoad();
		}
	}
}

function InitLoad()
{
	UIData.LoadAll();
	InitReplacements();

	if (MessageBoxReference != none)
	{
		CloseScene(MessageBoxReference);
	}
}

function InitReplacements()
{
	local int i, AmmoIdx, InsertIdx;
	local array<ReplacementInfoEx> ConfigWeaponReplacements, ConfigAmmoReplacements;
	local array<ReplacementInfoEx> SimpleAmmoReplacements;
	local ReplacementOptionsInfo EmptyOptions;
	local bool bCustomAmmo;

	// update all dynamic pages
	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].CreatedPage != none)
		{
			DynamicPages[i].CreatedPage.LoadReplacements(UIData);
		}
	}

	// check if config ammo replacements differ from current simple ammo replacements
	if (class'TestCentralWeaponReplacement'.static.GetConfigReplacements(RT_Weapon, ConfigWeaponReplacements) &&
		class'TestCentralWeaponReplacement'.static.GetConfigReplacements(RT_Ammo, ConfigAmmoReplacements))
	{
		for (i=0; i<ConfigAmmoReplacements.Length; i++)
		{
			if (ConfigAmmoReplacements[i].Options != EmptyOptions)
			{
				bCustomAmmo = true;
				break;
			}
		}

		if (!bCustomAmmo)
		{
			SimpleAmmoReplacements = GetAmmoBasicReplacementsFor(ConfigWeaponReplacements);

			if (ConfigAmmoReplacements.Length != SimpleAmmoReplacements.Length)
			{
				bCustomAmmo = true;
			}
			else
			{
				for (i=0; i<SimpleAmmoReplacements.Length; i++)
				{
					ConfigAmmoReplacements.RemoveItem(SimpleAmmoReplacements[i]);
				}

				if (ConfigAmmoReplacements.Length > 0)
				{
					bCustomAmmo = true;
				}
			}
		}
	}

	// add ammo tab if custom ammo replacements are found
	AmmoIdx = DynamicPages.Find('Tag', 'Ammo');
	if (bCustomAmmo && AmmoIdx != INDEX_NONE)
	{
		bAmmoVisible = true;
		InsertIdx = DynamicPages.Find('Tag', DynamicPages[AmmoIdx].After);
		if (InsertIdx != INDEX_NONE) InsertIdx += 1;
		DynamicPages[AmmoIdx].CreatedPage = CreateReplacementTab(TabControl, DynamicPages[AmmoIdx], InsertIdx, true);
		if (DynamicPages[AmmoIdx].CreatedPage != none)
		{
			DynamicPages[AmmoIdx].CreatedPage.LoadReplacements(UIData);
		}
	}
}

/** Sets the title for this scene. */
function SetTitle()
{
	local UILabel TitleLabel;

	TitleLabel = GetTitleLabel();
	if ( TitleLabel != None )
	{
		TitleLabel.SetDataStoreBinding(Title);
	}
}

/** Setup the scene's button bar. */
function SetupButtonBar()
{
	local int index;

	if(ButtonBar != None)
	{
		ButtonBar.Clear();
		ButtonBar.AppendButton("<Strings:UTGameUI.ButtonCallouts.Back>", OnButtonBar_Back);
		ButtonBar.AppendButton("<Strings:UTGameUI.ButtonCallouts.Accept>", OnButtonBar_Accept);

		if(TabControl != None && UTTabPage(TabControl.ActivePage) != none)
		{
			if (bAmmoVisible)
			{
				index = DynamicPages.Find('Tag', 'Ammo');
				if (index != INDEX_NONE && TabControl.ActivePage == DynamicPages[index].CreatedPage)
				{
					ButtonBar.AppendButton(HideAmmoCaption, OnButtonBar_HideAmmo);
				}
			}

			index = DynamicPages.Find('Tag', 'Weapons');
			if (index != INDEX_NONE && TabControl.ActivePage == DynamicPages[index].CreatedPage)
			{
				ButtonBar.AppendButton(bAmmoVisible ? HideAmmoCaption : ShowAmmoCaption, OnButtonBar_ShowAmmo);
				if (bAmmoVisible)
				{
					ButtonBar.AppendButton(TransferAmmoCaption, OnButtonBar_TransferAmmo);
				}
			}

			// Let the current tab page append buttons.
			UTTabPage(TabControl.ActivePage).SetupButtonBar(ButtonBar);
		}
	}
}

function bool HandleInputKey( const out InputEventParameters EventParms )
{
	local bool bResult;
	local UTTabPage CurrentTabPage;

	// Let the tab page's get first chance at the input
	CurrentTabPage = UTTabPage(TabControl.ActivePage);
	bResult = CurrentTabPage.HandleInputKey(EventParms);

	// If the tab page didn't handle it, let's handle it ourselves.
	if(bResult==false)
	{
		if(EventParms.EventType==IE_Released)
		{
			if(EventParms.InputKeyName=='XboxTypeS_B' || EventParms.InputKeyName=='Escape')
			{
				OnBack();
				bResult=true;
			}
		}
	}

	return bResult;
}

/** Buttonbar Accept Callback. */
function bool OnButtonBar_Accept(UIScreenObject InButton, int PlayerIndex)
{
	local array<string> MessageBoxOptions;
	local array<string> pages;
	local string pagesstr, str;

	if (HasIgnoredOptions(pages))
	{
		MessageBoxReference = GetMessageBoxScene();
		if(MessageBoxReference != none)
		{
			JoinArray(pages, pagesstr, "\n");
			str = Repl(ConfirmNoOptionsMessage, "  ", "\n");
			str = Repl(str, "`pages", pagesstr);

			MessageBoxOptions.AddItem(ConfirmNoOptionsButtonAccept);
			MessageBoxOptions.AddItem("<Strings:UTGameUI.ButtonCallouts.Cancel>");

			MessageBoxReference.SetPotentialOptions(MessageBoxOptions);
			MessageBoxReference.Display(str, ConfirmNoOptionsTitle, OnAccept_Confirm, 1);
			return true;
		}
	}

	OnAccept();
	CloseScene(Self);

	return true;
}

/** Buttonbar Back Callback. */
function bool OnButtonBar_Back(UIScreenObject InButton, int InPlayerIndex)
{
	OnBack();

	return true;
}

/** Buttonbar ShowAmmo Callback. */
function bool OnButtonBar_HideAmmo(UIScreenObject InButton, int InPlayerIndex)
{
	local array<string> MessageBoxOptions;

	MessageBoxReference = GetMessageBoxScene();
	if(MessageBoxReference != none)
	{
		MessageBoxOptions.AddItem(ConfirmHideAmmoButtonAccept);
		MessageBoxOptions.AddItem("<Strings:UTGameUI.ButtonCallouts.Cancel>");

		MessageBoxReference.SetPotentialOptions(MessageBoxOptions);
		MessageBoxReference.Display(Repl(ConfirmHideAmmoMessage, "  ", "\n"), ConfirmHideAmmoTitle, OnHideAmmo_Confirm, 1);
		return true;
	}

	ToggleAmmoTab(true);
	return true;
}

/** Buttonbar ShowAmmo Callback. */
function bool OnButtonBar_ShowAmmo(UIScreenObject InButton, int InPlayerIndex)
{
	ToggleAmmoTab();

	return true;
}

/** Buttonbar TransferAmmo Callback. */
function bool OnButtonBar_TransferAmmo(UIScreenObject InButton, int InPlayerIndex)
{
	TransferAmmo();

	return true;
}

/**
 * Callback for the accept confirmation dialog box.
 *
 * @param SelectedOption	Selected item
 * @param PlayerIndex	Index of player that performed the action.
 */
function OnAccept_Confirm(UTUIScene_MessageBox MessageBox, int SelectedOption, int PlayerIndex)
{
	if(SelectedOption == 0)
	{
		OnAccept();
		CloseScene(self);
	}
}

/**
 * Callback for the ignore ammo replacement dialog box.
 *
 * @param SelectedOption	Selected item
 * @param PlayerIndex	Index of player that performed the action.
 */
function OnHideAmmo_Confirm(UTUIScene_MessageBox MessageBox, int SelectedOption, int PlayerIndex)
{
	if(SelectedOption == 0)
	{
		ToggleAmmoTab(true);
	}
}

/** Callback for when the user wants to back out of this screen. */
function OnBack()
{
	CloseScene(self);
}

function OnAccept()
{
	local int i;

	// save all dynamic pages
	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].CreatedPage != none)
		{
			DynamicPages[i].CreatedPage.SaveReplacements();
		}
	}
}

function OnReplacementsExtraSave_Weapons(TestCWRUITabList RefTab)
{
	local array<ReplacementInfoEx> WeaponReplacements, AmmoReplacements;

	if (!bAmmoVisible)
	{
		WeaponReplacements = RefTab.GetReplacements();
		AmmoReplacements = GetAmmoBasicReplacementsFor(WeaponReplacements);

		class'TestCentralWeaponReplacement'.static.SetConfigReplacements(RT_Ammo, AmmoReplacements);
	}
}

function ToggleAmmoTab(optional bool bForceHide)
{
	local int AmmoIdx, InsertIdx;
	local TestCWRUITabList TabPage;
	AmmoIdx = DynamicPages.Find('Tag', 'Ammo');
	if (AmmoIdx == INDEX_NONE)
		return;

	if (bAmmoVisible || bForceHide)
	{
		bAmmoVisible = false;
		if (DynamicPages[AmmoIdx].CreatedPage != none)
		{
			TabControl.RemovePage(DynamicPages[AmmoIdx].CreatedPage, GetBestPlayerIndex());
		}
	}
	else
	{
		bAmmoVisible = true;
		InsertIdx = DynamicPages.Find('Tag', DynamicPages[AmmoIdx].After);
		if (InsertIdx != INDEX_NONE) InsertIdx += 1;
		TabPage = CreateReplacementTab(TabControl, DynamicPages[AmmoIdx], InsertIdx, true);
		if (TabPage != none)
		{
			DynamicPages[AmmoIdx].CreatedPage = TabPage;
			TabPage.LoadReplacements(UIData);
		}

		if (!bAmmoInfoShown)
		{
			bAmmoInfoShown = true;
			DisplayMessageBox(Repl(CustomAmmoReplacementMessage, "  ", "\n"), CustomAmmoReplacementTitle);
		}
	}

	SetupButtonBar();
}

function TransferAmmo()
{
	local int WeaponIdx, AmmoIdx;
	local array<ReplacementInfoEx> WeaponReplacements, AmmoReplacements;

	WeaponIdx = DynamicPages.Find('Tag', 'Weapons');
	AmmoIdx = DynamicPages.Find('Tag', 'Ammo');
	if (WeaponIdx == INDEX_NONE || AmmoIdx == INDEX_NONE ||
		DynamicPages[WeaponIdx].CreatedPage == none || DynamicPages[AmmoIdx].CreatedPage == none)
		return;

	WeaponReplacements = DynamicPages[WeaponIdx].CreatedPage.GetReplacements();
	AmmoReplacements = GetAmmoBasicReplacementsFor(WeaponReplacements);

	if (AmmoReplacements.Length > 0)
	{
		DynamicPages[AmmoIdx].CreatedPage.AddReplacements(AmmoReplacements, true);
	}
}

function array<ReplacementInfoEx> GetAmmoBasicReplacementsFor(array<ReplacementInfoEx> WeaponReplacements)
{
	local ReplacementInfoEx Replacement;
	local int i;
	local name AmmoClassFrom;
	local string AmmoClassTo;
	local array<ReplacementInfoEx> Replacements;

	for (i=0; i<WeaponReplacements.Length; i++)
	{
		if (!UIData.GetAmmoInfoForWeapon(WeaponReplacements[i].OldClassName,, AmmoClassFrom))
			continue;

		Replacement.OldClassName = AmmoClassFrom;

		if (UIData.GetAmmoInfoForWeapon(WeaponReplacements[i].NewClassPath, AmmoClassTo))
		{
			Replacement.NewClassPath = AmmoClassTo;
		}
		else
		{
			Replacement.NewClassPath = "";
		}

		Replacements.AddItem(Replacement);
	}

	return Replacements;
}

function bool HasIgnoredOptions(out array<string> OutPages)
{
	local int i;
	local string str;

	for (i=0; i<DynamicPages.Length; i++)
	{
		if (DynamicPages[i].CreatedPage != none && DynamicPages[i].CreatedPage.IsIgnoringOptions())
		{
			str = GetTabString(DynamicPages[i].Tag);
			OutPages.AddItem(str);
		}
	}

	return OutPages.Length > 0;
}

function LoadTimed(optional float time = 2.0)
{
	local PlayerController PC;

	PC = GetPlayerOwner().Actor;
	if (PC != none)
	{
		PC.SetTimer(2.0, false, 'InitLoad', self);
	}
	else
	{
		InitLoad();
	}
}

function string GetTabString(name Tag)
{
	local int index;
	index = TitlesMapping.Find('Tag', Tag);
	if (index != INDEX_NONE)
	{
		return TitlesMapping[index].Text;
	}

	 Return Localize("Titles", string(Tag), string(class.GetPackageName()));
}

function TestCWRUITabList CreateReplacementTab(UTUITabControl InTabControl, DynamicPageInfo PageInfo, optional int InsertIndex = INDEX_NONE, optional bool bSkipActivate, optional int PlayerIndex=GetBestPlayerIndex())
{
	local TestCWRUITabList TabPage;
	Local name TabName;
	TabName = name("pnlTabRep_"$PageInfo.Tag);
	TabPage = TestCWRUITabList(CreateNamedTabPage(InTabControl, class'TestCWRUITabList', TabName));
	if (TabPage != none)
	{
		TabPage.SetTitle(Caps(GetTabString(PageInfo.Tag)));
		TabPage.SetReplacementInfo(PageInfo.ReplacementType, PageInfo.DatastoreReference);

		InTabControl.InsertPage(TabPage, PlayerIndex, InsertIndex, !bSkipActivate);
	}

	return TabPage;
}

static function UITabPage CreateNamedTabPage(UITabControl OwnerControl, class<UITabPage> TabPageClass, optional name WidgetName, optional UITabPage PagePrefab )
{
	local UITabPage TabPage;
	TabPage = UITabPage(OwnerControl.CreateWidget(OwnerControl, TabPageClass, PagePrefab, WidgetName));
	if (TabPage != none) TabPage.GetTabButton(OwnerControl);
	return TabPage;
}

DefaultProperties
{
	Title="[Central Weapon Replacement]"
	BuildingUIMessage="Building UI. May take some seconds..."

	ConfirmNoOptionsMessage="You have switched to simple mode but there are replacement options set for the following pages:  `pages    By continuing saving the replacements, the options will be cleared to default. Do you want to continue?"
	ConfirmNoOptionsTitle="Clear out options"
	ConfirmNoOptionsButtonAccept="CONTINUE"

	CustomAmmoReplacementMessage="By enabling custom ammo replacement, you have to either transfer all the ammo replacements from the current weapon replacement or setup your own replacements.    No ammo pickups will be replaced automatically by whatever weapon you choose to be replaced."
	CustomAmmoReplacementTitle="Custom ammo replacment"

	ConfirmHideAmmoMessage="You made changes to the ammo replacements. By switching back to simple ammo replacement (based on the selected weapons), you will loose any made changes to the ammo replacements.    Do you want to ignore these changes and switch to simple ammo replacement?"
	ConfirmHideAmmoTitle="Ignore custom replacements?"
	ConfirmHideAmmoButtonAccept="IGNORE"

	ShowAmmoCaption="CUSTOM AMMO"
	HideAmmoCaption="SIMPLE AMMO"
	TransferAmmoCaption="TRANSFER AMMO"

	TitlesMapping[0]=(Tag="Weapons",Text="Weapons")
	TitlesMapping[1]=(Tag="Healths",Text="Health")
	TitlesMapping[2]=(Tag="Armors",Text="Armor")
	TitlesMapping[3]=(Tag="Powerups",Text="Powerups")
	TitlesMapping[4]=(Tag="Deployables",Text="Deployables")
	TitlesMapping[5]=(Tag="Vehicles",Text="Vehicles")
	TitlesMapping[6]=(Tag="Customs",Text="Customs")
	TitlesMapping[7]=(Tag="Ammos",Text="Ammo")

	DynamicPages.Add((Tag="Weapons",ReplacementType=RT_Weapon,DatastoreReference="WeaponSelection"))
	DynamicPages.Add((Tag="Ammo",ReplacementType=RT_Ammo,DatastoreReference="AmmoSelection",Hidden=true,After="Weapons"))
	DynamicPages.Add((Tag="Healths",ReplacementType=RT_Health,DatastoreReference="HealthSelection"))
	DynamicPages.Add((Tag="Armors",ReplacementType=RT_Armor,DatastoreReference="ArmorSelection"))
	DynamicPages.Add((Tag="Powerups",ReplacementType=RT_Powerup,DatastoreReference="PowerupSelection"))
	DynamicPages.Add((Tag="Deployables",ReplacementType=RT_Deployable,DatastoreReference="DeployableSelection"))
	DynamicPages.Add((Tag="Vehicles",ReplacementType=RT_Vehicle,DatastoreReference="VehicleSelection"))
	DynamicPages.Add((Tag="Customs",ReplacementType=RT_Custom,DatastoreReference="CustomSelection",Hidden=true))
}


Dep checker typo fixes and clarifications

/// HL-Docs: feature:ModDependencyCheck; issue:524; tags:compatibility
/// Allows mods to declare required and incompatible mods. The Highlander
/// will show popups for missing requirements and detected incompatibilities.
///
/// ## Terminology
///
/// We'll need to distinguish a few identifiers in mod project setups to explain this feature,
/// so here's a short rundown.
///
/// * **`DLCName`** is the actual ID of the mod. It's always the base name of the `.XComMod` file,
/// so `YetAnotherF1.XComMod` has a DLCName of `YetAnotherF1`. This is also the name that mod launchers
/// specify in the `ActiveMods` list, and the way the game knows how to load that mod. Every mod has exactly
/// one DLCName. Mods with the same DLCName can only be enabled or disabled together.
/// The game stores the `DLCName` in the save file, and shows a warning upon attempting to load a save file
/// when it had recorded a mod that's no longer enabled. DLCNames are case-insensitive.
/// * **`DLCIdentifier`** is an identifier corresponding to a `X2DownloadableContentInfo` classes.
/// Every mod has zero, one, or several `X2DownloadableContentInfo` classes, and the DLCIdentifier may be empty for
/// any of them. This DLCIdentifier is used for some customization aspects (like part icons or slider names) and
/// narrative content options (for the official DLCs). It's also used in CHL's Run Order (still need docs, sorry)
/// since Run Order is all about `X2DownloadableContentInfo` classes. DLCIdentifiers are generally case-sensitive.
///
/// In the default ModBuddy mod project, the mod has exactly one `X2DownloadableContentInfo` subclass
/// with a DLCIdentifier identical to the DLCName. However, config-only mods have no `X2DownloadableContentInfo`
/// at all, and some mods may have one but misconfigured the class so that the `DLCIdentifier` is empty.
///
/// ## CHModDependency
///
/// Now for the actual feature: Let's call the information about a mod provided to
/// the dependency checker *dependency info*, or *DepInfo*. A mod declares dependency
/// info through configuration entries for a class of type `CHModDependency` in `XComGame.ini`.
/// Let's look at an example upfront (taken from [Musashi's RPG Overhaul](https://steamcommunity.com/sharedfiles/filedetails/?id=1280477867)):
///
/// ```ini
/// [XCOM2RPGOverhaul CHModDependency]
/// DisplayName="Musashis RPG Overhaul"
///
/// +IncompatibleMods="NewPromotionScreenbyDefault"
/// +IncompatibleMods="DetailedSoldierListWOTC"
/// +IncompatibleMods="ABetterBarracksTLE"
/// +IncompatibleMods="ABetterBarracks"
/// +IncompatibleMods="ViewLockedSkillsWotc"
/// +IncompatibleMods="AddMintToMyChocolate"
/// +IncompatibleMods="RevisedWeaponUpgrades"
///
/// +RequiredMods="WOTC_LW2SecondaryWeapons"
/// +RequiredMods="PrimarySecondaries"
/// +RequiredMods="BetterSecondWaveSupport"
///
/// +IgnoreRequiredMods="NewPromotionScreenbyDefault"
/// +IgnoreRequiredMods="ViewLockedSkillsWotc"
/// +IgnoreRequiredMods="DetailedSoldierListWOTC"
/// ```
///
/// `CHModDependency` is the class that contains our DepInfo. That class is
/// `perobjectconfig`, so we can provide unique configuration for differently
/// named instances of the class. In this case, since the DLCName of RPGO is
/// `XCOM2RPGOverhaul`, we use that in the header.
///
/// `DisplayName` is a human-readable name of the mod. This is used for the actual
/// popup that says for example "<DisplayName> detected INCOMPATIBLE mods".
///
/// `IncompatibleMods` is a list of DLCNames that should not be enabled together
/// with this mod. For every mod with an enabled incompatible mod, there will be a popup
/// that lists all enabled incompatible mods.
///
/// `RequiredMods` is a list of DLCNames that should be enabled together with
/// this mod. For every mod with a missing required mod, there will be a popup
/// that lists all missing requirements.
///
/// `IgnoreRequiredMods` is a list of DLCNames that should be considered enabled if
/// this mod is enabled. For example, RPGO integrates `NewPromotionScreenbyDefault`,
/// so another mod should not consider `NewPromotionScreenbyDefault` missing if RPGO
/// is enabled.
///
/// There also is a `IgnoreIncompatibleMods` list of DLCNames that I am unfortunately
/// not qualified to present a use case for. What `+IgnoreIncompatibleMods="SomeDLCName"`
/// does is silence any warning that a third mod would throw if it listed
/// `+IncompatibleMods="SomeDLCName"`. If you think this is useful in any way, please tell
/// us about it in [HL issue #967](https://github.com/X2CommunityCore/X2WOTCCommunityHighlander/issues/967).
///
/// This is what the popup for missing requirements looks like:
/// ![Screenshot of RPGO's missing required mods](https://i.imgur.com/D1G6ZF7.png)
/// Since the missing requirements aren't enabled, we don't have any info about their
/// DisplayName.
///
/// ## How the dependency checker retrieves dependency info
///
/// The dependency checker looks at the config objects from
///
/// * All currently enabled DLCNames
/// * All non-empty DLCIdentifiers from found `X2DownloadableContentInfo` classes
///
/// while ignoring duplicates. Note that only DLCNames are supported for `IncompatibleMods`,
/// `RequiredMods` etc., you cannot specify a requirement on or incompatibility with a given DLCIdentifier!
///
///
/// This means that mods can easily provide dependency info for other mods --
/// the config object will be ignored if the corresponding DLCName isn't
/// enabled. This also allows mods to add friendly names for other mods so
/// that even if the mod isn't installed or doesn't set a `DisplayName`, the
/// popup doesn't only show an internal ID.
///
/// For example, `AddMintToMyChocolate` doesn't participate in the CHL depencency
/// checker at all, but this DLCName is kind of confusing and a reported incompatibility
/// would leave users unable to figure out what the actual conflict is.
///
/// RPGO could add this to its `XComGame.ini` so that the name becomes clearer:
///
/// ```ini
/// [AddMintToMyChocolate CHModDependency]
/// DisplayName="Classless XCOM: MINT"
/// ```
///
/// and the popup would display that incompatibility as "Classless XCOM: MINT (AddMintToMyChocolate)".
/// The same system can also be used to provide a DisplayName for requirements that may be missing
/// and as such can't inform the dependency checker about their DisplayName.
///
/// If the user presses "Do not show me this again", the Highlander will store the object
/// name (DLCName or DLCIdentifier, depending on how the depencency checker found it in
/// the first place) of the mod that provides this data in a user config file
/// (`%USERPROFILE%/Documents/My Games/XCOM 2 War of the Chosen/XComGame/Config/X2WOTCCommunityHighlander_NullConfig.ini`)
/// and won't show this popup again.

class CHModDependency extends Object perobjectconfig Config(Game);

var config array<string> IncompatibleMods;
var config array<string> IgnoreIncompatibleMods;
var config array<string> RequiredMods;
var config array<string> IgnoreRequiredMods;
var config string DisplayName;

final function bool IsInteresting()
{
	return IncompatibleMods.Length > 0 || RequiredMods.Length > 0
		|| IgnoreIncompatibleMods.Length > 0 || IgnoreRequiredMods.Length > 0
		|| DisplayName != "";
}

/// HL-Docs: feature:ModDependencyCheck; issue:524; tags:compatibility
/// Allows mods to declare required and incompatible mods. The Highlander
/// will show popups for missing requirements and detected incompatibilities.
///
/// ## Terminology
///
/// We'll need to distinguish a few identifiers in mod project setups to explain this feature,
/// so here's a short rundown.
///
/// * **`DLCName`** is the actual ID of the mod. It's always the base name of the `.XComMod` file,
/// so `YetAnotherF1.XComMod` has a DLCName of `YetAnotherF1`. This is also the name that mod launchers
/// specify in the `ActiveMods` list, and the way the game knows how to load that mod. Every mod has exactly
/// one DLCName. Mods with the same DLCName can only be enabled or disabled together.
/// The game stores the `DLCName` in the save file, and shows a warning upon attempting to load a save file
/// when it had recorded a mod that's no longer enabled.
/// * **`DLCIdentifier`** is an identifier corresponding to a `X2DownloadableContentInfo` classes.
/// Every mod has zero, one, or several `X2DownloadableContentInfo` classes, and the DLCIdentifier may be empty for
/// any of them. This DLCIdentifier is used for some customization aspects (like part icons or slider names) and
/// narrative content options (for the official DLCs). It's also used in CHL's Run Order (still need docs, sorry)
/// since Run Order is all about `X2DownloadableContentInfo` classes.
///
/// In the default ModBuddy mod project, the mod has exactly one `X2DownloadableContentInfo` subclass
/// with a DLCIdentifier identical to the DLCName. However, config-only mods have no `X2DownloadableContentInfo`
/// at all, and some mods may have one but misconfigured the class so that the `DLCIdentifier` is empty.
///
/// ## CHModDependency
///
/// Now for the actual feature: Let's call the information about a mod provided to
/// the dependency checker *dependency info*, or *DepInfo*. A mod declares dependency
/// info through configuration entries for a class of type `CHModDependency` in `XComGame.ini`.
/// Let's look at an example upfront (taken from [Musashi's RPG Overhaul](https://steamcommunity.com/sharedfiles/filedetails/?id=1280477867)):
///
/// ```ini
/// [XCOM2RPGOverhaul CHModDependency]
/// DisplayName="Musashis RPG Overhaul"
///
/// +IncompatibleMods="NewPromotionScreenbyDefault"
/// +IncompatibleMods="DetailedSoldierListWOTC"
/// +IncompatibleMods="ABetterBarracksTLE"
/// +IncompatibleMods="ABetterBarracks"
/// +IncompatibleMods="ViewLockedSkillsWotc"
/// +IncompatibleMods="AddMintToMyChocolate"
/// +IncompatibleMods="RevisedWeaponUpgrades"
///
/// +RequiredMods="WOTC_LW2SecondaryWeapons"
/// +RequiredMods="PrimarySecondaries"
/// +RequiredMods="BetterSecondWaveSupport"
///
/// +IgnoreRequiredMods="NewPromotionScreenbyDefault"
/// +IgnoreRequiredMods="ViewLockedSkillsWotc"
/// +IgnoreRequiredMods="DetailedSoldierListWOTC"
/// ```
///
/// `CHModDependency` is the class that contains our DepInfo. That class is
/// `perobjectconfig`, so we can provide unique configuration for differently
/// named instances of the class. In this case, since the DLCName of RPGO is
/// `XCOM2RPGOverhaul`, we use that in the header.
///
/// `DisplayName` is a human-readable name of the mod. This is used for the actual
/// popup that says for example "<DisplayName> detected INCOMPATIBLE mods".
///
/// `IncompatibleMods` is a list of DLCNames that should not be enabled together
/// with this mod. For every mod with an enabled incompatible mod, there will be a popup
/// that lists all enabled incompatible mods.
///
/// `RequiredMods` is a list of DLCNames that should be enabled together with
/// this mod. For every mod with a missing required mod, there will be a popup
/// that lists all missing requirements.
///
/// `IgnoreRequiredMods` is a list of DLCNames that should be considered enabled if
/// this mod is enabled. For example, RPGO integrates `NewPromotionScreenbyDefault`,
/// so another mod should not consider `NewPromotionScreenbyDefault` missing if RPGO
/// is enabled.
///
/// There also is a `IgnoreIncompatibleMods` list of DLCNames that I am unfortunately
/// not qualified to present a use case for. What `+IgnoreIncompatibleMods="SomeDLCName"`
/// does is silence any warning that a third mod would throw if it listed
/// `+IncompatibleMods="SomeDLCName"`. If you think this is useful in any way, please tell
/// us about it in [HL issue #967](https://github.com/X2CommunityCore/X2WOTCCommunityHighlander/issues/967).
///
/// This is what the popup for missing requirements looks like:
/// ![Screenshot of RPGO's missing required mods](https://i.imgur.com/D1G6ZF7.png)
/// Since the missing requirements aren't enabled, we don't have any info about their
/// DisplayName.
///
/// ## How the dependency checker retrieves dependency info
///
/// The dependency checker looks at the config objects from
///
/// * All currently enabled DLCNames
/// * All non-empty DLCIdentifiers from found `X2DownloadableContentInfo` classes
///
/// while ignoring duplicates.
/// This means that mods can easily provide dependency info for other mods --
/// the config object will be ignored if the corresponding DLCName isn't
/// enabled. This also allows mods to add friendly names for other mods so
/// that even if the mod isn't installed or doesn't set a `DisplayName`.
///
/// For example, `AddMintToMyChocolate` doesn't participate in the CHL depencency
/// checker at all, but this DLCName is kind of confusing and a reported incompatibility
/// would leave users unable to figure out what the actual conflict is.
///
/// RPGO could add this to its `XComGame.ini` so that the name becomes clearer:
///
/// ```ini
/// [AddMintToMyChocolate CHModDependency]
/// DisplayName="Classless XCOM: MINT"
/// ```
///
/// and the popup would display that incompatibility as "Classless XCOM: MINT (AddMintToMyChocolate)".
/// The same system can also be used to provide a DisplayName for requirements that may be missing
/// and as such can't inform the dependency checker about their DisplayName.
///
/// If the user presses "Do not show me this again", the Highlander will store the object
/// name (DLCName or DLCIdentifier, depending on how the depencency checker found it in
/// the first place) of the mod that provides this data in a user config file
/// (`%USERPROFILE%/Documents/My Games/XCOM 2 War of the Chosen/XComGame/Config/X2WOTCCommunityHighlander_NullConfig.ini`)
/// and won't show this popup again.

class CHModDependency extends Object perobjectconfig Config(Game);

var config array<string> IncompatibleMods;
var config array<string> IgnoreIncompatibleMods;
var config array<string> RequiredMods;
var config array<string> IgnoreRequiredMods;
var config string DisplayName;

final function bool IsInteresting()
{
	return IncompatibleMods.Length > 0 || RequiredMods.Length > 0
		|| IgnoreIncompatibleMods.Length > 0 || IgnoreRequiredMods.Length > 0
		|| DisplayName != "";
}