Update strings.uc

Update strings.uc

Changes \ to \\ in string literals

# strings.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 4/16/2014 4:45:41 PM
# Comment: Converts string-related code from PowerBASIC to C++

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: Converts string-related code from PowerBASIC to C++
Selected: False
Highlight: True
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:
Replace: {@Token:: : ~~ Properties: ucStatementSep }

Criteria: 2
Comment:
Pass: 1

Criteria: 3
Find: {@Start}
Replace: #include <algorithm>
         #include <string>
         #include <sstream>
         #include "pbstrings.h"
         using namespace std;{nl}{nl}

Criteria: 4
SkipOver: True
Find: ' {Comment:".*"}
Replace: [Skip over]

Criteria: 5
Find: String
Replace: string

Criteria: 6
Find: WString
Replace: wstring

Criteria: 7
BackColor: DodgerBlue
PassOnce: False
Find: InStr([{n=1},] {MainStr}, {MatchStr})
Replace: PB_INSTR({n}, {MainStr}, {MatchStr})

Criteria: 8
BackColor: Tomato
PassOnce: False
Find: InStr([{n=1},] {MainStr}, Any {MatchStr})
Replace: PB_INSTR_ANY({n}, {MainStr}, {MatchStr})

Criteria: 9
BackColor: Red
PassOnce: False
Find: Left$({Str}, {n})
Replace: PB_LEFT({Str}, {n})

Criteria: 10
BackColor: Brown
PassOnce: False
Find: Right$({Str}, {n})
Replace: PB_RIGHT({Str}, {n})

Criteria: 11
BackColor: Orange
PassOnce: False
Find: Mid$({Str}, {Start} [, {Length=FULL_STRING}])
Replace: PB_MID({Str}, {Start}, {Length})

Criteria: 12
BackColor: DeepSkyBlue
PassOnce: False
Find: {Sep: {nl} | : | Then | Else }
      Mid$({Str}, {Start} [, {Length=FULL_STRING}]) = {Txt%}
Replace: {Sep} PB_MID_REPLACE({Str}, {Txt}, {Start}, {Length})

Criteria: 13
BackColor: RoyalBlue
PassOnce: False
Find: Val({Str})
Replace: stold({Str})

Criteria: 14
Find: Space$({count})
Replace: string({count}, _char( ))

Criteria: 15
Find: String$({count}, {char})
Replace: string({count}, {char})

Criteria: 16
BackColor: Violet
Find: String$({count}, {q}{char}{q})
Replace: string({count}, _char({char}))

Criteria: 17
Find: {function: UCase | LCase | Hex | Oct | Str}$({arg})
Replace: PB_{@Eval: UCase("{function}", "{'.*'}")}({arg})

Criteria: 18
Find: Trim$({Str} [, ANY {chars=" "}])
Replace: PB_TRIM_ANY({Str}, {chars})

Criteria: 19
BackColor: Gold
Find: LTrim$({Str} [, ANY {chars=" "}])
Replace: PB_LTRIM_ANY({Str}, {chars})

Criteria: 20
BackColor: Silver
Find: RTrim$({Str} [, ANY {chars=" "}])
Replace: PB_RTRIM_ANY({Str}, {chars})

Criteria: 21
Find: Extract$([{start=1},] {MainStr}, {MatchStr})
Replace: PB_EXTRACT({start}, {MainStr}, {MatchStr})

Criteria: 22
Find: Extract$([{start=1},] {MainStr}, Any {MatchStr})
Replace: PB_EXTRACT_ANY({start}, {MainStr}, {MatchStr})

Criteria: 23
Find: Remain$([{start=1},] {MainStr}, {MatchStr})
Replace: PB_REMAIN({start}, {MainStr}, {MatchStr})

Criteria: 24
Find: Remain$([{start=1},] {MainStr}, Any {MatchStr})
Replace: PB_REMAIN_ANY({start}, {MainStr}, {MatchStr})

Criteria: 25
Find: Remove$({Str}, {MatchStr})
Replace: PB_REMOVE({Str}, {MatchStr})

Criteria: 26
Find: Remove$({Str}, Any {MatchStr})
Replace: PB_REMOVE_ANY({Str}, {MatchStr})

Criteria: 27
Find: Replace {MatchStr} With {NewStr} In {MainStr}
Replace: PB_REPLACE({MainStr}, {MatchStr}, {NewStr})

Criteria: 28
Find: Replace Any {MatchStr} With {NewStr} In {MainStr}
Replace: PB_REPLACE_ANY({MainStr}, {MatchStr}, {NewStr})

Criteria: 29
Find: Repeat$({count}, {Str})
Replace: PB_REPEAT({Str}, {count})

Criteria: 30
Find: Asc({Str}[, {pos=1}])
Replace: PB_ASC({Str}, {pos})

Criteria: 31
Find: ${equate: NUL|BEL|BS|TAB|LF|VT|FF|CR|CRLF|EOF|ESC|SPC|DQ|SQ|QCQ }
Replace: PB_{@Eval: UCase("{equate}", "{'.*'}")}

Criteria: 32
Find: Peek$
Replace: PB_PEEK

Criteria: 33
Find: Poke$ {address}, {stringexpr}
Replace: PB_POKE({address}, {stringexpr})

Criteria: 34
Comment: Doubles backslash to avoid escape
Pass: 2

Criteria: 35
Find: {q}{text$}{q}
Replace: {q}{@Evaluate: Replace({text}, "\", "\\")}{q}

# End Search

# strings.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 3/28/2014 2:21:16 PM
# Comment: Converts string-related code from PowerBASIC to C++

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: Converts string-related code from PowerBASIC to C++
Selected: False
Highlight: True
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:
Replace: {@Token:: : ~~ Properties: ucStatementSep }

Criteria: 2
Find: {@Start}
Replace: #include <algorithm>
         #include <string>
         #include <sstream>
         #include "pbstrings.h"
         using namespace std;{nl}{nl}

Criteria: 3
SkipOver: True
Find: ' {Comment:".*"}
Replace: [Skip over]

Criteria: 4
Find: String
Replace: string

Criteria: 5
Find: WString
Replace: wstring

Criteria: 6
BackColor: DodgerBlue
PassOnce: False
Find: InStr([{n=1},] {MainStr}, {MatchStr})
Replace: PB_INSTR({n}, {MainStr}, {MatchStr})

Criteria: 7
BackColor: Tomato
PassOnce: False
Find: InStr([{n=1},] {MainStr}, Any {MatchStr})
Replace: PB_INSTR_ANY({n}, {MainStr}, {MatchStr})

Criteria: 8
BackColor: Red
PassOnce: False
Find: Left$({Str}, {n})
Replace: PB_LEFT({Str}, {n})

Criteria: 9
BackColor: Brown
PassOnce: False
Find: Right$({Str}, {n})
Replace: PB_RIGHT({Str}, {n})

Criteria: 10
BackColor: Orange
PassOnce: False
Find: Mid$({Str}, {Start} [, {Length=FULL_STRING}])
Replace: PB_MID({Str}, {Start}, {Length})

Criteria: 11
BackColor: DeepSkyBlue
PassOnce: False
Find: {Sep: {nl} | : | Then | Else }
      Mid$({Str}, {Start} [, {Length=FULL_STRING}]) = {Txt%}
Replace: {Sep} PB_MID_REPLACE({Str}, {Txt}, {Start}, {Length})

Criteria: 12
BackColor: RoyalBlue
PassOnce: False
Find: Val({Str})
Replace: stold({Str})

Criteria: 13
Find: Space$({count})
Replace: string({count}, _char( ))

Criteria: 14
Find: String$({count}, {char})
Replace: string({count}, {char})

Criteria: 15
BackColor: Violet
Find: String$({count}, {q}{char}{q})
Replace: string({count}, _char({char}))

Criteria: 16
Find: {function: UCase | LCase | Hex | Oct | Str}$({arg})
Replace: PB_{@Eval: UCase("{function}", "{'.*'}")}({arg})

Criteria: 17
Find: Trim$({Str} [, ANY {chars=" "}])
Replace: PB_TRIM_ANY({Str}, {chars})

Criteria: 18
BackColor: Gold
Find: LTrim$({Str} [, ANY {chars=" "}])
Replace: PB_LTRIM_ANY({Str}, {chars})

Criteria: 19
BackColor: Silver
Find: RTrim$({Str} [, ANY {chars=" "}])
Replace: PB_RTRIM_ANY({Str}, {chars})

Criteria: 20
Find: Extract$([{start=1},] {MainStr}, {MatchStr})
Replace: PB_EXTRACT({start}, {MainStr}, {MatchStr})

Criteria: 21
Find: Extract$([{start=1},] {MainStr}, Any {MatchStr})
Replace: PB_EXTRACT_ANY({start}, {MainStr}, {MatchStr})

Criteria: 22
Find: Remain$([{start=1},] {MainStr}, {MatchStr})
Replace: PB_REMAIN({start}, {MainStr}, {MatchStr})

Criteria: 23
Find: Remain$([{start=1},] {MainStr}, Any {MatchStr})
Replace: PB_REMAIN_ANY({start}, {MainStr}, {MatchStr})

Criteria: 24
Find: Remove$({Str}, {MatchStr})
Replace: PB_REMOVE({Str}, {MatchStr})

Criteria: 25
Find: Remove$({Str}, Any {MatchStr})
Replace: PB_REMOVE_ANY({Str}, {MatchStr})

Criteria: 26
Find: Replace {MatchStr} With {NewStr} In {MainStr}
Replace: PB_REPLACE({MainStr}, {MatchStr}, {NewStr})

Criteria: 27
Find: Replace Any {MatchStr} With {NewStr} In {MainStr}
Replace: PB_REPLACE_ANY({MainStr}, {MatchStr}, {NewStr})

Criteria: 28
Find: Repeat$({count}, {Str})
Replace: PB_REPEAT({Str}, {count})

Criteria: 29
Find: Asc({Str}[, {pos=1}])
Replace: PB_ASC({Str}, {pos})

Criteria: 30
Find: ${equate: NUL|BEL|BS|TAB|LF|VT|FF|CR|CRLF|EOF|ESC|SPC|DQ|SQ|QCQ }
Replace: PB_{@Eval: UCase("{equate}", "{'.*'}")}

Criteria: 31
Find: Peek$
Replace: PB_PEEK

Criteria: 32
Selected: True
Find: Poke$ {address}, {stringexpr}
Replace: PB_POKE({address}, {stringexpr})

# End Search


Allow to equip Psi PCS on units with PsiOffense (#602)

class UIInventory_Implants extends UIInventory;

var localized string m_strNoImplants;

var localized string m_strInstallImplantTitle;
var localized string m_strInstallImplantText;
var localized string m_strReplaceImplantTitle;
var localized string m_strReplaceImplantText;

var array<XComGameState_Item> Implants;

simulated function InitScreen(XComPlayerController InitController, UIMovie InitMovie, optional name InitName)
{
	super.InitScreen(InitController, InitMovie, InitName);

	List.OnItemClicked = OnItemSelected;

	if(`ISCONTROLLERACTIVE)
		List.OnItemDoubleClicked = OnItemSelected;

	ItemCard.SetPosition(1200, 0);
	SetInventoryLayout();
	PopulateData();
}

//bsg-crobinson (3.17.17) Update the navhelp on init
simulated function OnInit()
{
	super.OnInit();
	UpdateNavHelp();
}
//bsg-crobinson (3.17.17) end

simulated function PopulateData()
{
	local XComGameState_Item Implant;
	local UIInventory_ListItem ListItem;

	super.PopulateData();

	Implants = XComHQ.GetAllCombatSimsInInventory();
	Implants.Sort(SortImplants);
	Implants.Sort(SortImplantsStatType);
	Implants.Sort(SortItemsTier);

	foreach Implants(Implant)
	{
		UIInventory_ListItem(List.CreateItem(class'UIInventory_ListItem')).InitInventoryListItem(Implant.GetMyTemplate(), Implant.Quantity, Implant.GetReference());
	}

	if(List.ItemCount > 0)
	{
		ListItem = UIInventory_ListItem(List.GetItem(0));
		PopulateItemCard(ListItem.ItemTemplate, ListItem.ItemRef);
	}
	else
	{
		Spawn(class'UIText', ListContainer)
			.InitText('', class'UIUtilities_Text'.static.GetColoredText(m_strNoImplants, eUIState_Header, 24), true)
			.SetPosition(List.x + 20, List.y - 40);
	}

	List.SetSelectedIndex(0);
}

simulated function int SortImplants(XComGameState_Item A, XComGameState_Item B)
{
	local StatBoost StatBoostA, StatBoostB;

	StatBoostA = class'UIUtilities_Strategy'.static.GetStatBoost(A);
	StatBoostB = class'UIUtilities_Strategy'.static.GetStatBoost(B);

	if(StatBoostA.Boost < StatBoostB.Boost) return -1;
	else if(StatBoostA.Boost > StatBoostB.Boost) return 1;
	return 0;
}

simulated function int SortImplantsStatType(XComGameState_Item A, XComGameState_Item B)
{
	local StatBoost StatBoostA, StatBoostB;

	StatBoostA = class'UIUtilities_Strategy'.static.GetStatBoost(A);
	StatBoostB = class'UIUtilities_Strategy'.static.GetStatBoost(B);

	if (StatBoostA.StatType > StatBoostB.StatType) return -1;
	else if (StatBoostA.StatType < StatBoostB.StatType) return 1;
	else return 0;
}

function int SortItemsTier(XComGameState_Item A, XComGameState_Item B)
{
	local X2ItemTemplate ItemTemplateA, ItemTemplateB;

	ItemTemplateA = A.GetMyTemplate();
	ItemTemplateB = B.GetMyTemplate();

	if (ItemTemplateA.Tier > ItemTemplateB.Tier)
	{
		return 1;
	}
	else if (ItemTemplateA.Tier < ItemTemplateB.Tier)
	{
		return -1;
	}
	else
	{
		return 0;
	}
}

simulated function bool CanEquipImplant(StateObjectReference ImplantRef)
{
	local XComGameState_Unit Unit;
	local XComGameState_Item Implant, ImplantToRemove;
	local array<XComGameState_Item> EquippedImplants;

	Implant = XComGameState_Item(History.GetGameStateForObjectID(ImplantRef.ObjectID));
	Unit = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit();

	EquippedImplants = Unit.GetAllItemsInSlot(eInvSlot_CombatSim);

	if (EquippedImplants.Length > 0)
	{
		ImplantToRemove = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(EquippedImplants[0].ObjectID));

		if(class'UIUtilities_Strategy'.static.GetStatBoost(Implant).StatType ==
			class'UIUtilities_Strategy'.static.GetStatBoost(ImplantToRemove).StatType  &&
			class'UIUtilities_Strategy'.static.GetStatBoost(Implant).Boost <=
			class'UIUtilities_Strategy'.static.GetStatBoost(ImplantToRemove).Boost)
			return false;
	}

	return class'UIUtilities_Strategy'.static.GetStatBoost(Implant).StatType != eStat_PsiOffense || Unit.GetCurrentStat(eStat_PsiOffense) > 0; // # Issue 602
}

simulated function SelectedItemChanged(UIList ContainerList, int ItemIndex)
{
	local int SlotIndex;
	local XComGameState_Unit Unit;
	local UISoldierHeader SoldierHeader;
	local array<XComGameState_Item> EquippedImplants;
	local XComGameState_Item ImplantToAdd, ImplantToRemove;
	local string Will, Aim, Health, Mobility, Tech, Psi;

	super.SelectedItemChanged(ContainerList, ItemIndex);

	Unit = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit();
	SoldierHeader = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).Header;
	SlotIndex = 0;
	EquippedImplants = Unit.GetAllItemsInSlot(eInvSlot_CombatSim);

	ImplantToAdd = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(Implants[List.SelectedIndex].ObjectID));
	if(SlotIndex < EquippedImplants.Length)
		ImplantToRemove = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(EquippedImplants[SlotIndex].ObjectID));

	Will = string( int(Unit.GetCurrentStat( eStat_Will )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Will);
	Aim = string( int(Unit.GetCurrentStat( eStat_Offense )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Offense);
	Health = string( int(Unit.GetCurrentStat( eStat_HP )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_HP);
	Mobility = string( int(Unit.GetCurrentStat( eStat_Mobility )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Mobility);
	Tech = string( int(Unit.GetCurrentStat( eStat_Hacking )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Hacking);

	if(Unit.IsPsiOperative())
		Psi = string( int(Unit.GetCurrentStat( eStat_PsiOffense )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_PsiOffense);

	SoldierHeader.SetSoldierStats(Will, Aim, Health, Mobility, Tech, Psi);
}

simulated function string GetStatBoostString(XComGameState_Item ImplantToAdd, XComGameState_Item ImplantToRemove, ECharStatType StatType)
{
	local int Index, TotalBoost, BoostValue;
	local bool bHasStatBoostBonus;

	if (XComHQ != none)
	{
		bHasStatBoostBonus = XComHQ.SoldierUnlockTemplates.Find('IntegratedWarfareUnlock') != INDEX_NONE;
	}

	if(ImplantToAdd != none)
	{
		Index = ImplantToAdd.StatBoosts.Find('StatType', StatType);
		if (Index != INDEX_NONE)
		{
			BoostValue = ImplantToAdd.StatBoosts[Index].Boost;
			if (bHasStatBoostBonus)
			{
				if (X2EquipmentTemplate(ImplantToAdd.GetMyTemplate()).bUseBoostIncrement)
					BoostValue += class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostIncrement;
				else
					BoostValue += Round(BoostValue * class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostValue);
			}

			if ((StatType == eStat_HP) && `SecondWaveEnabled('BetaStrike'))
			{
				BoostValue *= class'X2StrategyGameRulesetDataStructures'.default.SecondWaveBetaStrikeHealthMod;
			}

			TotalBoost += BoostValue;
		}

	}

	if(ImplantToRemove != none)
	{
		Index = ImplantToRemove.StatBoosts.Find('StatType', StatType);
		if (Index != INDEX_NONE)
		{
			BoostValue = ImplantToRemove.StatBoosts[Index].Boost;
			if (bHasStatBoostBonus)
			{
				if (X2EquipmentTemplate(ImplantToRemove.GetMyTemplate()).bUseBoostIncrement)
					BoostValue += class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostIncrement;
				else
					BoostValue += Round(BoostValue * class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostValue);
			}

			if ((StatType == eStat_HP) && `SecondWaveEnabled('BetaStrike'))
			{
				BoostValue *= class'X2StrategyGameRulesetDataStructures'.default.SecondWaveBetaStrikeHealthMod;
			}

			TotalBoost -= BoostValue;
		}
	}

	if(TotalBoost != 0)
		return class'UIUtilities_Text'.static.GetColoredText((TotalBoost > 0 ? "+" : "") $ string(TotalBoost), TotalBoost > 0 ? eUIState_Good : eUIState_Bad);
	else
		return "";
}

simulated function OnItemSelected(UIList ContainerList, int ItemIndex)
{
	local int SlotIndex;
	local XComGameState_Unit Unit;
	local array<XComGameState_Item> EquippedImplants;
	local StateObjectReference ImplantRef;

	ImplantRef = UIInventory_ListItem(ContainerList.GetItem(ItemIndex)).ItemRef;

	if (CanEquipImplant(ImplantRef))
	{
		Unit = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit();
		SlotIndex = 0;

		EquippedImplants = Unit.GetAllItemsInSlot(eInvSlot_CombatSim);

		if (XComHQ.bReusePCS)
		{
			// Skip the popups if the continent bonus for reusing upgrades is active
			if (SlotIndex < EquippedImplants.Length)
				ConfirmImplantRemovalCallback('eUIAction_Accept');
			else
				ConfirmImplantInstallCallback('eUIAction_Accept');
		}
		else
		{
			// Unequip previous implant
			if (SlotIndex < EquippedImplants.Length)
				ConfirmImplantRemoval(EquippedImplants[SlotIndex].GetMyTemplate(), UIInventory_ListItem(List.GetSelectedItem()).ItemTemplate);
			else
				ConfirmImplantInstall(UIInventory_ListItem(List.GetSelectedItem()).ItemTemplate);
		}
	}
	else
		Movie.Pres.PlayUISound(eSUISound_MenuClose);
}

simulated function ConfirmImplantInstall(X2ItemTemplate ImplantTemplate)
{
	local XGParamTag LocTag;
	local TDialogueBoxData DialogData;

	DialogData.eType = eDialog_Alert;
	DialogData.strTitle = m_strInstallImplantTitle;
	DialogData.strAccept = class'UIUtilities_Text'.default.m_strGenericYes;
	DialogData.strCancel = class'UIUtilities_Text'.default.m_strGenericNO;
	DialogData.fnCallback = ConfirmImplantInstallCallback;

	LocTag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
	LocTag.StrValue0 = ImplantTemplate.GetItemFriendlyName();
	DialogData.strText = `XEXPAND.ExpandString(m_strInstallImplantText);
	Movie.Pres.UIRaiseDialog(DialogData);
}

simulated function ConfirmImplantRemoval(X2ItemTemplate ImplantToRemove, X2ItemTemplate ImplantToInstall)
{
	local XGParamTag LocTag;
	local TDialogueBoxData DialogData;

	DialogData.eType = eDialog_Alert;
	DialogData.strTitle = m_strReplaceImplantTitle;
	DialogData.strAccept = class'UIUtilities_Text'.default.m_strGenericYes;
	DialogData.strCancel = class'UIUtilities_Text'.default.m_strGenericNO;
	DialogData.fnCallback = ConfirmImplantRemovalCallback;

	LocTag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
	LocTag.StrValue0 = ImplantToRemove.GetItemFriendlyName();
	LocTag.StrValue1 = ImplantToInstall.GetItemFriendlyName();
	DialogData.strText = `XEXPAND.ExpandString(m_strReplaceImplantText);
	Movie.Pres.UIRaiseDialog(DialogData);
}

simulated function ConfirmImplantInstallCallback(Name eAction)
{
	if(eAction == 'eUIAction_Accept')
	{
		InstallImplant();
		CloseScreen();
	}
}

simulated function ConfirmImplantRemovalCallback(Name eAction)
{
	if(eAction == 'eUIAction_Accept')
	{
		RemoveImplant();
		InstallImplant();
		CloseScreen();
	}
}

simulated function RemoveImplant()
{
	local int SlotIndex;
	local XComGameState UpdatedState;
	local StateObjectReference UnitRef;
	local XComGameState_Unit UpdatedUnit;
	local array<XComGameState_Item> EquippedImplants;

	UpdatedState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Remove Personal Combat Sim");

	UnitRef = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit().GetReference();
	UpdatedUnit = XComGameState_Unit(UpdatedState.ModifyStateObject(class'XComGameState_Unit', UnitRef.ObjectID));
	EquippedImplants = UpdatedUnit.GetAllItemsInSlot(eInvSlot_CombatSim);

	SlotIndex = 0;

	if(UpdatedUnit.RemoveItemFromInventory(EquippedImplants[SlotIndex], UpdatedState))
	{
		if (XComHQ.bReusePCS) // Breakthrough research is letting us reuse PCS, so put it back into the inventory
		{
			XComHQ = XComGameState_HeadquartersXCom(UpdatedState.ModifyStateObject(class'XComGameState_HeadquartersXCom', XComHQ.ObjectID));
			XComHQ.PutItemInInventory(UpdatedState, EquippedImplants[SlotIndex]);
		}
		else
		{
			UpdatedState.RemoveStateObject(EquippedImplants[SlotIndex].ObjectID); // Combat sims cannot be reused
		}

		`GAMERULES.SubmitGameState(UpdatedState);
	}
	else
		`XCOMHISTORY.CleanupPendingGameState(UpdatedState);
}

simulated function InstallImplant()
{
	local XComGameState UpdatedState;
	local StateObjectReference UnitRef;
	local XComGameState_Unit UpdatedUnit;
	local XComGameState_Item UpdatedImplant;
	local XComGameState_HeadquartersXCom UpdatedHQ;

	UpdatedState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Install Personal Combat Sim");

	UnitRef = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit().GetReference();
	UpdatedHQ = XComGameState_HeadquartersXCom(UpdatedState.ModifyStateObject(class'XComGameState_HeadquartersXCom', XComHQ.ObjectID));
	UpdatedUnit = XComGameState_Unit(UpdatedState.ModifyStateObject(class'XComGameState_Unit', UnitRef.ObjectID));

	UpdatedHQ.GetItemFromInventory(UpdatedState, Implants[List.SelectedIndex].GetReference(), UpdatedImplant);

	UpdatedUnit.AddItemToInventory(UpdatedImplant, eInvSlot_CombatSim, UpdatedState);

	`XEVENTMGR.TriggerEvent('PCSApplied', UpdatedUnit, UpdatedImplant, UpdatedState);
	`XSTRATEGYSOUNDMGR.PlaySoundEvent("Strategy_UI_PCS_Equip");

	`GAMERULES.SubmitGameState(UpdatedState);
}

//bsg-crobinson (3.17.17) Adding in buttons
simulated function UpdateNavHelp()
{
	local UINavigationHelp NavHelp;

	if(!bIsFocused)
	{
		return;
	}

	NavHelp = `HQPRES.m_kAvengerHUD.NavHelp;
	NavHelp.Show();
	NavHelp.ClearButtonHelp();
	NavHelp.AddBackButton(CloseScreen);
	NavHelp.AddSelectNavHelp();
}
//bsg-crobinson (3.17.17) end

simulated function bool OnUnrealCommand(int cmd, int arg)
{
	// Only pay attention to presses or repeats; ignoring other input types
	// NOTE: Ensure repeats only occur with arrow keys
	if ( !CheckInputIsReleaseOrDirectionRepeat(cmd, arg) )
		return false;

	switch( cmd )
	{
		// OnAccept
`if(`notdefined(FINAL_RELEASE))
		case class'UIUtilities_Input'.const.FXS_KEY_TAB:
`endif
		case class'UIUtilities_Input'.const.FXS_BUTTON_B:
		case class'UIUtilities_Input'.const.FXS_KEY_ESCAPE:
		case class'UIUtilities_Input'.const.FXS_R_MOUSE_DOWN:
			CloseScreen();
			return true;
		case class'UIUtilities_Input'.const.FXS_BUTTON_START:
			`HQPRES.UIPauseMenu( ,true );
			return true;
	}

	return super.OnUnrealCommand(cmd, arg);
}

simulated function CloseScreen()
{
	super.CloseScreen();
	Movie.Pres.PlayUISound(eSUISound_MenuClose);
}

defaultproperties
{
	bHideOnLoseFocus = false;
	InputState = eInputState_Consume; // don't cascade input down into the armory
	DisplayTag = "UIBlueprint_Promotion";
	CameraTag = "UIBlueprint_Promotion";
}

class UIInventory_Implants extends UIInventory;

var localized string m_strNoImplants;

var localized string m_strInstallImplantTitle;
var localized string m_strInstallImplantText;
var localized string m_strReplaceImplantTitle;
var localized string m_strReplaceImplantText;

var array<XComGameState_Item> Implants;

simulated function InitScreen(XComPlayerController InitController, UIMovie InitMovie, optional name InitName)
{
	super.InitScreen(InitController, InitMovie, InitName);

	List.OnItemClicked = OnItemSelected;

	if(`ISCONTROLLERACTIVE)
		List.OnItemDoubleClicked = OnItemSelected;

	ItemCard.SetPosition(1200, 0);
	SetInventoryLayout();
	PopulateData();
}

//bsg-crobinson (3.17.17) Update the navhelp on init
simulated function OnInit()
{
	super.OnInit();
	UpdateNavHelp();
}
//bsg-crobinson (3.17.17) end

simulated function PopulateData()
{
	local XComGameState_Item Implant;
	local UIInventory_ListItem ListItem;

	super.PopulateData();

	Implants = XComHQ.GetAllCombatSimsInInventory();
	Implants.Sort(SortImplants);
	Implants.Sort(SortImplantsStatType);
	Implants.Sort(SortItemsTier);

	foreach Implants(Implant)
	{
		UIInventory_ListItem(List.CreateItem(class'UIInventory_ListItem')).InitInventoryListItem(Implant.GetMyTemplate(), Implant.Quantity, Implant.GetReference());
	}

	if(List.ItemCount > 0)
	{
		ListItem = UIInventory_ListItem(List.GetItem(0));
		PopulateItemCard(ListItem.ItemTemplate, ListItem.ItemRef);
	}
	else
	{
		Spawn(class'UIText', ListContainer)
			.InitText('', class'UIUtilities_Text'.static.GetColoredText(m_strNoImplants, eUIState_Header, 24), true)
			.SetPosition(List.x + 20, List.y - 40);
	}

	List.SetSelectedIndex(0);
}

simulated function int SortImplants(XComGameState_Item A, XComGameState_Item B)
{
	local StatBoost StatBoostA, StatBoostB;

	StatBoostA = class'UIUtilities_Strategy'.static.GetStatBoost(A);
	StatBoostB = class'UIUtilities_Strategy'.static.GetStatBoost(B);

	if(StatBoostA.Boost < StatBoostB.Boost) return -1;
	else if(StatBoostA.Boost > StatBoostB.Boost) return 1;
	return 0;
}

simulated function int SortImplantsStatType(XComGameState_Item A, XComGameState_Item B)
{
	local StatBoost StatBoostA, StatBoostB;

	StatBoostA = class'UIUtilities_Strategy'.static.GetStatBoost(A);
	StatBoostB = class'UIUtilities_Strategy'.static.GetStatBoost(B);

	if (StatBoostA.StatType > StatBoostB.StatType) return -1;
	else if (StatBoostA.StatType < StatBoostB.StatType) return 1;
	else return 0;
}

function int SortItemsTier(XComGameState_Item A, XComGameState_Item B)
{
	local X2ItemTemplate ItemTemplateA, ItemTemplateB;

	ItemTemplateA = A.GetMyTemplate();
	ItemTemplateB = B.GetMyTemplate();

	if (ItemTemplateA.Tier > ItemTemplateB.Tier)
	{
		return 1;
	}
	else if (ItemTemplateA.Tier < ItemTemplateB.Tier)
	{
		return -1;
	}
	else
	{
		return 0;
	}
}

simulated function bool CanEquipImplant(StateObjectReference ImplantRef)
{
	local XComGameState_Unit Unit;
	local XComGameState_Item Implant, ImplantToRemove;
	local array<XComGameState_Item> EquippedImplants;

	Implant = XComGameState_Item(History.GetGameStateForObjectID(ImplantRef.ObjectID));
	Unit = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit();

	EquippedImplants = Unit.GetAllItemsInSlot(eInvSlot_CombatSim);

	if (EquippedImplants.Length > 0)
	{
		ImplantToRemove = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(EquippedImplants[0].ObjectID));

		if(class'UIUtilities_Strategy'.static.GetStatBoost(Implant).StatType ==
			class'UIUtilities_Strategy'.static.GetStatBoost(ImplantToRemove).StatType  &&
			class'UIUtilities_Strategy'.static.GetStatBoost(Implant).Boost <=
			class'UIUtilities_Strategy'.static.GetStatBoost(ImplantToRemove).Boost)
			return false;
	}

	return class'UIUtilities_Strategy'.static.GetStatBoost(Implant).StatType != eStat_PsiOffense || Unit.IsPsiOperative();
}

simulated function SelectedItemChanged(UIList ContainerList, int ItemIndex)
{
	local int SlotIndex;
	local XComGameState_Unit Unit;
	local UISoldierHeader SoldierHeader;
	local array<XComGameState_Item> EquippedImplants;
	local XComGameState_Item ImplantToAdd, ImplantToRemove;
	local string Will, Aim, Health, Mobility, Tech, Psi;

	super.SelectedItemChanged(ContainerList, ItemIndex);

	Unit = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit();
	SoldierHeader = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).Header;
	SlotIndex = 0;
	EquippedImplants = Unit.GetAllItemsInSlot(eInvSlot_CombatSim);

	ImplantToAdd = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(Implants[List.SelectedIndex].ObjectID));
	if(SlotIndex < EquippedImplants.Length)
		ImplantToRemove = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(EquippedImplants[SlotIndex].ObjectID));

	Will = string( int(Unit.GetCurrentStat( eStat_Will )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Will);
	Aim = string( int(Unit.GetCurrentStat( eStat_Offense )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Offense);
	Health = string( int(Unit.GetCurrentStat( eStat_HP )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_HP);
	Mobility = string( int(Unit.GetCurrentStat( eStat_Mobility )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Mobility);
	Tech = string( int(Unit.GetCurrentStat( eStat_Hacking )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_Hacking);

	if(Unit.IsPsiOperative())
		Psi = string( int(Unit.GetCurrentStat( eStat_PsiOffense )) ) $ GetStatBoostString(ImplantToAdd, ImplantToRemove, eStat_PsiOffense);

	SoldierHeader.SetSoldierStats(Will, Aim, Health, Mobility, Tech, Psi);
}

simulated function string GetStatBoostString(XComGameState_Item ImplantToAdd, XComGameState_Item ImplantToRemove, ECharStatType StatType)
{
	local int Index, TotalBoost, BoostValue;
	local bool bHasStatBoostBonus;

	if (XComHQ != none)
	{
		bHasStatBoostBonus = XComHQ.SoldierUnlockTemplates.Find('IntegratedWarfareUnlock') != INDEX_NONE;
	}

	if(ImplantToAdd != none)
	{
		Index = ImplantToAdd.StatBoosts.Find('StatType', StatType);
		if (Index != INDEX_NONE)
		{
			BoostValue = ImplantToAdd.StatBoosts[Index].Boost;
			if (bHasStatBoostBonus)
			{
				if (X2EquipmentTemplate(ImplantToAdd.GetMyTemplate()).bUseBoostIncrement)
					BoostValue += class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostIncrement;
				else
					BoostValue += Round(BoostValue * class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostValue);
			}

			if ((StatType == eStat_HP) && `SecondWaveEnabled('BetaStrike'))
			{
				BoostValue *= class'X2StrategyGameRulesetDataStructures'.default.SecondWaveBetaStrikeHealthMod;
			}

			TotalBoost += BoostValue;
		}

	}

	if(ImplantToRemove != none)
	{
		Index = ImplantToRemove.StatBoosts.Find('StatType', StatType);
		if (Index != INDEX_NONE)
		{
			BoostValue = ImplantToRemove.StatBoosts[Index].Boost;
			if (bHasStatBoostBonus)
			{
				if (X2EquipmentTemplate(ImplantToRemove.GetMyTemplate()).bUseBoostIncrement)
					BoostValue += class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostIncrement;
				else
					BoostValue += Round(BoostValue * class'X2SoldierIntegratedWarfareUnlockTemplate'.default.StatBoostValue);
			}

			if ((StatType == eStat_HP) && `SecondWaveEnabled('BetaStrike'))
			{
				BoostValue *= class'X2StrategyGameRulesetDataStructures'.default.SecondWaveBetaStrikeHealthMod;
			}

			TotalBoost -= BoostValue;
		}
	}

	if(TotalBoost != 0)
		return class'UIUtilities_Text'.static.GetColoredText((TotalBoost > 0 ? "+" : "") $ string(TotalBoost), TotalBoost > 0 ? eUIState_Good : eUIState_Bad);
	else
		return "";
}

simulated function OnItemSelected(UIList ContainerList, int ItemIndex)
{
	local int SlotIndex;
	local XComGameState_Unit Unit;
	local array<XComGameState_Item> EquippedImplants;
	local StateObjectReference ImplantRef;

	ImplantRef = UIInventory_ListItem(ContainerList.GetItem(ItemIndex)).ItemRef;

	if (CanEquipImplant(ImplantRef))
	{
		Unit = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit();
		SlotIndex = 0;

		EquippedImplants = Unit.GetAllItemsInSlot(eInvSlot_CombatSim);

		if (XComHQ.bReusePCS)
		{
			// Skip the popups if the continent bonus for reusing upgrades is active
			if (SlotIndex < EquippedImplants.Length)
				ConfirmImplantRemovalCallback('eUIAction_Accept');
			else
				ConfirmImplantInstallCallback('eUIAction_Accept');
		}
		else
		{
			// Unequip previous implant
			if (SlotIndex < EquippedImplants.Length)
				ConfirmImplantRemoval(EquippedImplants[SlotIndex].GetMyTemplate(), UIInventory_ListItem(List.GetSelectedItem()).ItemTemplate);
			else
				ConfirmImplantInstall(UIInventory_ListItem(List.GetSelectedItem()).ItemTemplate);
		}
	}
	else
		Movie.Pres.PlayUISound(eSUISound_MenuClose);
}

simulated function ConfirmImplantInstall(X2ItemTemplate ImplantTemplate)
{
	local XGParamTag LocTag;
	local TDialogueBoxData DialogData;

	DialogData.eType = eDialog_Alert;
	DialogData.strTitle = m_strInstallImplantTitle;
	DialogData.strAccept = class'UIUtilities_Text'.default.m_strGenericYes;
	DialogData.strCancel = class'UIUtilities_Text'.default.m_strGenericNO;
	DialogData.fnCallback = ConfirmImplantInstallCallback;

	LocTag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
	LocTag.StrValue0 = ImplantTemplate.GetItemFriendlyName();
	DialogData.strText = `XEXPAND.ExpandString(m_strInstallImplantText);
	Movie.Pres.UIRaiseDialog(DialogData);
}

simulated function ConfirmImplantRemoval(X2ItemTemplate ImplantToRemove, X2ItemTemplate ImplantToInstall)
{
	local XGParamTag LocTag;
	local TDialogueBoxData DialogData;

	DialogData.eType = eDialog_Alert;
	DialogData.strTitle = m_strReplaceImplantTitle;
	DialogData.strAccept = class'UIUtilities_Text'.default.m_strGenericYes;
	DialogData.strCancel = class'UIUtilities_Text'.default.m_strGenericNO;
	DialogData.fnCallback = ConfirmImplantRemovalCallback;

	LocTag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
	LocTag.StrValue0 = ImplantToRemove.GetItemFriendlyName();
	LocTag.StrValue1 = ImplantToInstall.GetItemFriendlyName();
	DialogData.strText = `XEXPAND.ExpandString(m_strReplaceImplantText);
	Movie.Pres.UIRaiseDialog(DialogData);
}

simulated function ConfirmImplantInstallCallback(Name eAction)
{
	if(eAction == 'eUIAction_Accept')
	{
		InstallImplant();
		CloseScreen();
	}
}

simulated function ConfirmImplantRemovalCallback(Name eAction)
{
	if(eAction == 'eUIAction_Accept')
	{
		RemoveImplant();
		InstallImplant();
		CloseScreen();
	}
}

simulated function RemoveImplant()
{
	local int SlotIndex;
	local XComGameState UpdatedState;
	local StateObjectReference UnitRef;
	local XComGameState_Unit UpdatedUnit;
	local array<XComGameState_Item> EquippedImplants;

	UpdatedState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Remove Personal Combat Sim");

	UnitRef = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit().GetReference();
	UpdatedUnit = XComGameState_Unit(UpdatedState.ModifyStateObject(class'XComGameState_Unit', UnitRef.ObjectID));
	EquippedImplants = UpdatedUnit.GetAllItemsInSlot(eInvSlot_CombatSim);

	SlotIndex = 0;

	if(UpdatedUnit.RemoveItemFromInventory(EquippedImplants[SlotIndex], UpdatedState))
	{
		if (XComHQ.bReusePCS) // Breakthrough research is letting us reuse PCS, so put it back into the inventory
		{
			XComHQ = XComGameState_HeadquartersXCom(UpdatedState.ModifyStateObject(class'XComGameState_HeadquartersXCom', XComHQ.ObjectID));
			XComHQ.PutItemInInventory(UpdatedState, EquippedImplants[SlotIndex]);
		}
		else
		{
			UpdatedState.RemoveStateObject(EquippedImplants[SlotIndex].ObjectID); // Combat sims cannot be reused
		}

		`GAMERULES.SubmitGameState(UpdatedState);
	}
	else
		`XCOMHISTORY.CleanupPendingGameState(UpdatedState);
}

simulated function InstallImplant()
{
	local XComGameState UpdatedState;
	local StateObjectReference UnitRef;
	local XComGameState_Unit UpdatedUnit;
	local XComGameState_Item UpdatedImplant;
	local XComGameState_HeadquartersXCom UpdatedHQ;

	UpdatedState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Install Personal Combat Sim");

	UnitRef = UIArmory_MainMenu(Movie.Pres.ScreenStack.GetScreen(class'UIArmory_MainMenu')).GetUnit().GetReference();
	UpdatedHQ = XComGameState_HeadquartersXCom(UpdatedState.ModifyStateObject(class'XComGameState_HeadquartersXCom', XComHQ.ObjectID));
	UpdatedUnit = XComGameState_Unit(UpdatedState.ModifyStateObject(class'XComGameState_Unit', UnitRef.ObjectID));

	UpdatedHQ.GetItemFromInventory(UpdatedState, Implants[List.SelectedIndex].GetReference(), UpdatedImplant);

	UpdatedUnit.AddItemToInventory(UpdatedImplant, eInvSlot_CombatSim, UpdatedState);

	`XEVENTMGR.TriggerEvent('PCSApplied', UpdatedUnit, UpdatedImplant, UpdatedState);
	`XSTRATEGYSOUNDMGR.PlaySoundEvent("Strategy_UI_PCS_Equip");

	`GAMERULES.SubmitGameState(UpdatedState);
}

//bsg-crobinson (3.17.17) Adding in buttons
simulated function UpdateNavHelp()
{
	local UINavigationHelp NavHelp;

	if(!bIsFocused)
	{
		return;
	}

	NavHelp = `HQPRES.m_kAvengerHUD.NavHelp;
	NavHelp.Show();
	NavHelp.ClearButtonHelp();
	NavHelp.AddBackButton(CloseScreen);
	NavHelp.AddSelectNavHelp();
}
//bsg-crobinson (3.17.17) end

simulated function bool OnUnrealCommand(int cmd, int arg)
{
	// Only pay attention to presses or repeats; ignoring other input types
	// NOTE: Ensure repeats only occur with arrow keys
	if ( !CheckInputIsReleaseOrDirectionRepeat(cmd, arg) )
		return false;

	switch( cmd )
	{
		// OnAccept
`if(`notdefined(FINAL_RELEASE))
		case class'UIUtilities_Input'.const.FXS_KEY_TAB:
`endif
		case class'UIUtilities_Input'.const.FXS_BUTTON_B:
		case class'UIUtilities_Input'.const.FXS_KEY_ESCAPE:
		case class'UIUtilities_Input'.const.FXS_R_MOUSE_DOWN:
			CloseScreen();
			return true;
		case class'UIUtilities_Input'.const.FXS_BUTTON_START:
			`HQPRES.UIPauseMenu( ,true );
			return true;
	}

	return super.OnUnrealCommand(cmd, arg);
}

simulated function CloseScreen()
{
	super.CloseScreen();
	Movie.Pres.PlayUISound(eSUISound_MenuClose);
}

defaultproperties
{
	bHideOnLoseFocus = false;
	InputState = eInputState_Consume; // don't cascade input down into the armory
	DisplayTag = "UIBlueprint_Promotion";
	CameraTag = "UIBlueprint_Promotion";
}


Add item states for MergeAmmosAsNeeded() (#608)

SeqAct_SpawnUnitFromAvenger now adds the unit's items to the new game
state before initialising the unit's abilities. This ensures that
MergeAmmosAsNeeded() works properly, since it only checks for ammo items
in the current game state, not in the history.

Fixes #608.

///---------------------------------------------------------------------------------------
//  FILE:    SeqAct_SpawnUnitFromAvenger.uc
//  AUTHOR:  David Burchanowski  --  11/05/2014
//  PURPOSE: Spawns a unit from the avenger reserves
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------

class SeqAct_SpawnUnitFromAvenger extends SequenceAction;

var XComGameState_Unit SpawnedUnit;

event Activated()
{
	SpawnedUnit = SpawnUnitFromAvenger();

	OutputLinks[0].bHasImpulse = SpawnedUnit != none;
	OutputLinks[1].bHasImpulse = SpawnedUnit == none;
}

static function XComGameState_Unit SpawnUnitFromAvenger()
{
	local XComGameStateHistory History;
	local XComGameState_Unit StrategyUnit;

	History = `XCOMHISTORY;

	if(History.FindStartStateIndex() > 1) // this is a strategy game
	{
		// try to get a unit from the strategy game
		StrategyUnit = ChooseStrategyUnit(History);

		// and add it to the board
		return AddStrategyUnitToBoard(StrategyUnit, History);
	}
	else
	{
		// this is a debug game of some kind, so we need to make a new fake unit
		return CreateDebugUnit(History);
	}
}

// Scans the strategy game and chooses a unit to place on the game board
private static function XComGameState_Unit ChooseStrategyUnit(XComGameStateHistory History)
{
	local array<StateObjectReference> UnitsInPlay;
	local XComGameState_Unit UnitInPlay;
	local XComGameState_HeadquartersXCom XComHQ;
	local XComGameState StrategyState;
	local int LastStrategyStateIndex;
	local XComGameState_Unit StrategyUnit;

	LastStrategyStateIndex = History.FindStartStateIndex() - 1;
	if(LastStrategyStateIndex > 0)
	{
		// build a list of all units currently on the board, we will exclude them from consideration. Add non-xcom units as well
		// in case they are mind controlled or otherwise under the control of the enemy team
		foreach History.IterateByClassType(class'XComGameState_Unit', UnitInPlay)
		{
			UnitsInPlay.AddItem(UnitInPlay.GetReference());
		}

		// grab the archived strategy state from the history and the headquarters object
		StrategyState = History.GetGameStateFromHistory(LastStrategyStateIndex, eReturnType_Copy, false);
		foreach StrategyState.IterateByClassType(class'XComGameState_HeadquartersXCom', XComHQ)
		{
			break;
		}

		if(XComHQ == none)
		{
			`Redscreen("SeqAct_SpawnUnitFromAvenger: Could not find an XComGameState_HeadquartersXCom state in the archive!");
		}

		// and find a unit in the strategy state that is not on the board
		foreach StrategyState.IterateByClassType(class'XComGameState_Unit', StrategyUnit)
		{
			// only living soldier units please
			if (StrategyUnit.IsDead() || !StrategyUnit.IsSoldier() 	|| StrategyUnit.IsTraining() || StrategyUnit.IsOnCovertAction())
			{
				continue;
			}

			// only if we have already recruited this soldier
			if(XComHQ != none && XComHQ.Crew.Find('ObjectID', StrategyUnit.ObjectID) == INDEX_NONE)
			{
				continue;
			}

			// only if not already on the board
			if(UnitsInPlay.Find('ObjectID', StrategyUnit.ObjectID) != INDEX_NONE)
			{
				continue;
			}

			return StrategyUnit;
		}
	}

	return none;
}

// chooses a location for the unit to spawn in the spawn zone
private static function bool ChooseSpawnLocation(out Vector SpawnLocation)
{
	local XComParcelManager ParcelManager;
	local XComGroupSpawn SoldierSpawn;
	local array<Vector> FloorPoints;

	// attempt to find a place in the spawn zone for this unit to spawn in
	ParcelManager = `PARCELMGR;
	SoldierSpawn = ParcelManager.SoldierSpawn;

	if(SoldierSpawn == none) // check for test maps, just grab any spawn
	{
		foreach `XComGRI.AllActors(class'XComGroupSpawn', SoldierSpawn)
		{
			break;
		}
	}

	SoldierSpawn.GetValidFloorLocations(FloorPoints);
	if(FloorPoints.Length == 0)
	{
		return false;
	}
	else
	{
		SpawnLocation = FloorPoints[0];
		return true;
	}
}

// Places the given strategy unit on the game board
private static function XComGameState_Unit AddStrategyUnitToBoard(XComGameState_Unit Unit, XComGameStateHistory History)
{
	local X2TacticalGameRuleset Rules;
	local Vector SpawnLocation;
	local XComGameStateContext_TacticalGameRule NewGameStateContext;
	local XComGameState NewGameState;
	local XComGameState_Player PlayerState;
	local StateObjectReference ItemReference;
	local XComGameState_Item ItemState;

	if(Unit == none)
	{
		return none;
	}

	// pick a floor point at random to spawn the unit at
	if(!ChooseSpawnLocation(SpawnLocation))
	{
		return none;
	}

	// create the history frame with the new tactical unit state
	NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded);
	NewGameState = NewGameStateContext.ContextBuildGameState( );

	Unit = XComGameState_Unit(NewGameState.ModifyStateObject(class'XComGameState_Unit', Unit.ObjectID));
	Unit.BeginTacticalPlay(NewGameState);   // this needs to be called explicitly since we're adding an existing state directly into tactical
	Unit.SetVisibilityLocationFromVector(SpawnLocation);
	Unit.bSpawnedFromAvenger = true;

	// assign the new unit to the human team
	foreach History.IterateByClassType(class'XComGameState_Player', PlayerState)
	{
		if(PlayerState.GetTeam() == eTeam_XCom)
		{
			Unit.SetControllingPlayer(PlayerState.GetReference());
			break;
		}
	}

	// add item states. This needs to be done so that the visualizer sync picks up the IDs and
	// creates their visualizers
	foreach Unit.InventoryItems(ItemReference)
	{
		ItemState = XComGameState_Item(NewGameState.ModifyStateObject(class'XComGameState_Item', ItemReference.ObjectID));
		ItemState.BeginTacticalPlay(NewGameState);   // this needs to be called explicitly since we're adding an existing state directly into tactical

		// add any cosmetic items that might exists
		ItemState.CreateCosmeticItemUnit(NewGameState);
	}

	Rules = `TACTICALRULES;

	// submit it
	XComGameStateContext_TacticalGameRule(NewGameState.GetContext()).UnitRef = Unit.GetReference();
	Rules.SubmitGameState(NewGameState);

	// make sure the visualizer has been created so self-applied abilities have a target in the world
	Unit.FindOrCreateVisualizer(NewGameState);

	// add abilities
	// Must happen after unit is submitted, or it gets confused about when the unit is in play or not
	NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Adding Reserve Unit Abilities");

	// Start Issue #608
	//
	// The MergeAmmoAsNeeded() function that is called during unit ability initialization requires
	// the unit's items to be included in the new game state. So add them again.
	foreach Unit.InventoryItems(ItemReference)
	{
		NewGameState.ModifyStateObject(class'XComGameState_Item', ItemReference.ObjectID);
	}
	// End Issue #608

	Rules.InitializeUnitAbilities(NewGameState, Unit);

	// make the unit concealed, if they have Phantom
	// (special-case code, but this is how it works when starting a game normally)
	if (Unit.FindAbility('Phantom').ObjectID > 0)
	{
		Unit.EnterConcealmentNewGameState(NewGameState);
	}

	Rules.SubmitGameState(NewGameState);

	return Unit;
}

// Creates a random unit and places it on the game board
private static function XComGameState_Unit CreateDebugUnit(XComGameStateHistory History)
{
	local X2TacticalGameRuleset Rules;
	local Vector SpawnLocation;
	local XGCharacterGenerator CharacterGenerator;
	local X2CharacterTemplate CharTemplate;
	local XComGameStateContext_TacticalGameRule NewGameStateContext;
	local XComGameState NewGameState;
	local XComGameState_Unit Unit;
	local XComGameState_Player PlayerState;
	local TSoldier Soldier;

	// pick a floor point at random to spawn the unit at
	if(!ChooseSpawnLocation(SpawnLocation))
	{
		return none;
	}

	NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded);
	NewGameState = NewGameStateContext.ContextBuildGameState();

	// generate a debug unit
	CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate('Soldier');
	`assert(CharTemplate != none);
	CharacterGenerator = `XCOMGRI.Spawn(CharTemplate.CharacterGeneratorClass);
	`assert(CharacterGenerator != none);

	Unit = CharTemplate.CreateInstanceFromTemplate(NewGameState);

	// assign the player to him
	foreach History.IterateByClassType(class'XComGameState_Player', PlayerState)
	{
		if(PlayerState.GetTeam() == eTeam_XCom)
		{
			Unit.SetControllingPlayer(PlayerState.GetReference());
			break;
		}
	}

	// give him a loadout
	Unit.ApplyInventoryLoadout(NewGameState);

	// give him abilities
	Rules = `TACTICALRULES;
	Rules.InitializeUnitAbilities(NewGameState, Unit);

	// give him an appearance
	Soldier = CharacterGenerator.CreateTSoldier();
	Unit.SetTAppearance(Soldier.kAppearance);
	Unit.SetCharacterName(Soldier.strFirstName, Soldier.strLastName, Soldier.strNickName);
	Unit.SetCountry(Soldier.nmCountry);

	// put him on the start tile
	Unit.SetVisibilityLocationFromVector(SpawnLocation);

	// submit the new unit
	XComGameStateContext_TacticalGameRule(NewGameState.GetContext()).UnitRef = Unit.GetReference();
	Rules.SubmitGameState(NewGameState);

	// and cleanup the generator object
	CharacterGenerator.Destroy();

	return Unit;
}

defaultproperties
{
	ObjCategory="Unit"
	ObjName="Spawn Unit From Avenger"

	bConvertedForReplaySystem=true
	bCanBeUsedForGameplaySequence=true
	bAutoActivateOutputLinks=false

	OutputLinks(0)=(LinkDesc="Success")
	OutputLinks(1)=(LinkDesc="Avenger Empty")

	VariableLinks(0)=(ExpectedType=class'SeqVar_GameUnit',LinkDesc="Spawned Unit",PropertyName=SpawnedUnit)
}

///---------------------------------------------------------------------------------------
//  FILE:    SeqAct_SpawnUnitFromAvenger.uc
//  AUTHOR:  David Burchanowski  --  11/05/2014
//  PURPOSE: Spawns a unit from the avenger reserves
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------

class SeqAct_SpawnUnitFromAvenger extends SequenceAction;

var XComGameState_Unit SpawnedUnit;

event Activated()
{
	SpawnedUnit = SpawnUnitFromAvenger();

	OutputLinks[0].bHasImpulse = SpawnedUnit != none;
	OutputLinks[1].bHasImpulse = SpawnedUnit == none;
}

static function XComGameState_Unit SpawnUnitFromAvenger()
{
	local XComGameStateHistory History;
	local XComGameState_Unit StrategyUnit;

	History = `XCOMHISTORY;

	if(History.FindStartStateIndex() > 1) // this is a strategy game
	{
		// try to get a unit from the strategy game
		StrategyUnit = ChooseStrategyUnit(History);

		// and add it to the board
		return AddStrategyUnitToBoard(StrategyUnit, History);
	}
	else
	{
		// this is a debug game of some kind, so we need to make a new fake unit
		return CreateDebugUnit(History);
	}
}

// Scans the strategy game and chooses a unit to place on the game board
private static function XComGameState_Unit ChooseStrategyUnit(XComGameStateHistory History)
{
	local array<StateObjectReference> UnitsInPlay;
	local XComGameState_Unit UnitInPlay;
	local XComGameState_HeadquartersXCom XComHQ;
	local XComGameState StrategyState;
	local int LastStrategyStateIndex;
	local XComGameState_Unit StrategyUnit;

	LastStrategyStateIndex = History.FindStartStateIndex() - 1;
	if(LastStrategyStateIndex > 0)
	{
		// build a list of all units currently on the board, we will exclude them from consideration. Add non-xcom units as well
		// in case they are mind controlled or otherwise under the control of the enemy team
		foreach History.IterateByClassType(class'XComGameState_Unit', UnitInPlay)
		{
			UnitsInPlay.AddItem(UnitInPlay.GetReference());
		}

		// grab the archived strategy state from the history and the headquarters object
		StrategyState = History.GetGameStateFromHistory(LastStrategyStateIndex, eReturnType_Copy, false);
		foreach StrategyState.IterateByClassType(class'XComGameState_HeadquartersXCom', XComHQ)
		{
			break;
		}

		if(XComHQ == none)
		{
			`Redscreen("SeqAct_SpawnUnitFromAvenger: Could not find an XComGameState_HeadquartersXCom state in the archive!");
		}

		// and find a unit in the strategy state that is not on the board
		foreach StrategyState.IterateByClassType(class'XComGameState_Unit', StrategyUnit)
		{
			// only living soldier units please
			if (StrategyUnit.IsDead() || !StrategyUnit.IsSoldier() 	|| StrategyUnit.IsTraining() || StrategyUnit.IsOnCovertAction())
			{
				continue;
			}

			// only if we have already recruited this soldier
			if(XComHQ != none && XComHQ.Crew.Find('ObjectID', StrategyUnit.ObjectID) == INDEX_NONE)
			{
				continue;
			}

			// only if not already on the board
			if(UnitsInPlay.Find('ObjectID', StrategyUnit.ObjectID) != INDEX_NONE)
			{
				continue;
			}

			return StrategyUnit;
		}
	}

	return none;
}

// chooses a location for the unit to spawn in the spawn zone
private static function bool ChooseSpawnLocation(out Vector SpawnLocation)
{
	local XComParcelManager ParcelManager;
	local XComGroupSpawn SoldierSpawn;
	local array<Vector> FloorPoints;

	// attempt to find a place in the spawn zone for this unit to spawn in
	ParcelManager = `PARCELMGR;
	SoldierSpawn = ParcelManager.SoldierSpawn;

	if(SoldierSpawn == none) // check for test maps, just grab any spawn
	{
		foreach `XComGRI.AllActors(class'XComGroupSpawn', SoldierSpawn)
		{
			break;
		}
	}

	SoldierSpawn.GetValidFloorLocations(FloorPoints);
	if(FloorPoints.Length == 0)
	{
		return false;
	}
	else
	{
		SpawnLocation = FloorPoints[0];
		return true;
	}
}

// Places the given strategy unit on the game board
private static function XComGameState_Unit AddStrategyUnitToBoard(XComGameState_Unit Unit, XComGameStateHistory History)
{
	local X2TacticalGameRuleset Rules;
	local Vector SpawnLocation;
	local XComGameStateContext_TacticalGameRule NewGameStateContext;
	local XComGameState NewGameState;
	local XComGameState_Player PlayerState;
	local StateObjectReference ItemReference;
	local XComGameState_Item ItemState;

	if(Unit == none)
	{
		return none;
	}

	// pick a floor point at random to spawn the unit at
	if(!ChooseSpawnLocation(SpawnLocation))
	{
		return none;
	}

	// create the history frame with the new tactical unit state
	NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded);
	NewGameState = NewGameStateContext.ContextBuildGameState( );

	Unit = XComGameState_Unit(NewGameState.ModifyStateObject(class'XComGameState_Unit', Unit.ObjectID));
	Unit.BeginTacticalPlay(NewGameState);   // this needs to be called explicitly since we're adding an existing state directly into tactical
	Unit.SetVisibilityLocationFromVector(SpawnLocation);
	Unit.bSpawnedFromAvenger = true;

	// assign the new unit to the human team
	foreach History.IterateByClassType(class'XComGameState_Player', PlayerState)
	{
		if(PlayerState.GetTeam() == eTeam_XCom)
		{
			Unit.SetControllingPlayer(PlayerState.GetReference());
			break;
		}
	}

	// add item states. This needs to be done so that the visualizer sync picks up the IDs and
	// creates their visualizers
	foreach Unit.InventoryItems(ItemReference)
	{
		ItemState = XComGameState_Item(NewGameState.ModifyStateObject(class'XComGameState_Item', ItemReference.ObjectID));
		ItemState.BeginTacticalPlay(NewGameState);   // this needs to be called explicitly since we're adding an existing state directly into tactical

		// add any cosmetic items that might exists
		ItemState.CreateCosmeticItemUnit(NewGameState);
	}

	Rules = `TACTICALRULES;

	// submit it
	XComGameStateContext_TacticalGameRule(NewGameState.GetContext()).UnitRef = Unit.GetReference();
	Rules.SubmitGameState(NewGameState);

	// make sure the visualizer has been created so self-applied abilities have a target in the world
	Unit.FindOrCreateVisualizer(NewGameState);

	// add abilities
	// Must happen after unit is submitted, or it gets confused about when the unit is in play or not
	NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Adding Reserve Unit Abilities");
	Rules.InitializeUnitAbilities(NewGameState, Unit);

	// make the unit concealed, if they have Phantom
	// (special-case code, but this is how it works when starting a game normally)
	if (Unit.FindAbility('Phantom').ObjectID > 0)
	{
		Unit.EnterConcealmentNewGameState(NewGameState);
	}

	Rules.SubmitGameState(NewGameState);

	return Unit;
}

// Creates a random unit and places it on the game board
private static function XComGameState_Unit CreateDebugUnit(XComGameStateHistory History)
{
	local X2TacticalGameRuleset Rules;
	local Vector SpawnLocation;
	local XGCharacterGenerator CharacterGenerator;
	local X2CharacterTemplate CharTemplate;
	local XComGameStateContext_TacticalGameRule NewGameStateContext;
	local XComGameState NewGameState;
	local XComGameState_Unit Unit;
	local XComGameState_Player PlayerState;
	local TSoldier Soldier;

	// pick a floor point at random to spawn the unit at
	if(!ChooseSpawnLocation(SpawnLocation))
	{
		return none;
	}

	NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded);
	NewGameState = NewGameStateContext.ContextBuildGameState();

	// generate a debug unit
	CharTemplate = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager().FindCharacterTemplate('Soldier');
	`assert(CharTemplate != none);
	CharacterGenerator = `XCOMGRI.Spawn(CharTemplate.CharacterGeneratorClass);
	`assert(CharacterGenerator != none);

	Unit = CharTemplate.CreateInstanceFromTemplate(NewGameState);

	// assign the player to him
	foreach History.IterateByClassType(class'XComGameState_Player', PlayerState)
	{
		if(PlayerState.GetTeam() == eTeam_XCom)
		{
			Unit.SetControllingPlayer(PlayerState.GetReference());
			break;
		}
	}

	// give him a loadout
	Unit.ApplyInventoryLoadout(NewGameState);

	// give him abilities
	Rules = `TACTICALRULES;
	Rules.InitializeUnitAbilities(NewGameState, Unit);

	// give him an appearance
	Soldier = CharacterGenerator.CreateTSoldier();
	Unit.SetTAppearance(Soldier.kAppearance);
	Unit.SetCharacterName(Soldier.strFirstName, Soldier.strLastName, Soldier.strNickName);
	Unit.SetCountry(Soldier.nmCountry);

	// put him on the start tile
	Unit.SetVisibilityLocationFromVector(SpawnLocation);

	// submit the new unit
	XComGameStateContext_TacticalGameRule(NewGameState.GetContext()).UnitRef = Unit.GetReference();
	Rules.SubmitGameState(NewGameState);

	// and cleanup the generator object
	CharacterGenerator.Destroy();

	return Unit;
}

defaultproperties
{
	ObjCategory="Unit"
	ObjName="Spawn Unit From Avenger"

	bConvertedForReplaySystem=true
	bCanBeUsedForGameplaySequence=true
	bAutoActivateOutputLinks=false

	OutputLinks(0)=(LinkDesc="Success")
	OutputLinks(1)=(LinkDesc="Avenger Empty")

	VariableLinks(0)=(ExpectedType=class'SeqVar_GameUnit',LinkDesc="Spawned Unit",PropertyName=SpawnedUnit)
}


Version 1.15.0

Version 1.15.0

//---------------------------------------------------------------------------------------
//  FILE:    CHXComGameVersionTemplate.uc
//  AUTHOR:  X2CommunityCore
//  PURPOSE: Version information for the Community Highlander XComGame replacement.
//
//  Issue #1
//
//  This is implemented as a template to allow mods to detect whether the Community Highlander
//  is installed. It's a template to avoid needing to compile against any
//  new sources to get mods to build (which would then crash the game if it tried
//  to access a function //  or variable that wasn't installed anyway). Can be queried by:
//
//  1) Query the StrategyElementTemplateManager for a template named 'CHXComGameVersion'. If you get back
//     a non-none result the XcomGame replacement is installed (or someone is lying and added the template without
//     the actual XComGame replacement...)
//  2) If you need more fine-grained info, such as the particular version, then once you get back a non-none
//     result you can cast it to class 'CHXComGameVersionTemplate' to get the version number through the API below.
//
//  Don't directly look up the template and cast it without checking if you got a non-none result or the game will
//  probably crash when the replacement XComGame isn't present.
//
//  Supports major, minor, and build versions, but build is currently unimplemented.
//---------------------------------------------------------------------------------------
class CHXComGameVersionTemplate extends X2StrategyElementTemplate;

var int MajorVersion;
var int MinorVersion;
var int PatchVersion;

// "Short" version number (minus the patch)
function String GetShortVersionString()
{
    return default.MajorVersion $ "." $ default.MinorVersion;
}

// Version number in string format.
function String GetVersionString()
{
    return default.MajorVersion $ "." $ default.MinorVersion $ "." $ default.PatchVersion;
}

// Version number in comparable numeric format. Number in decimal is MMmmmmPPPP where:O
// "M" is major version
// "m" is minor version
// "P" is patch number
//
// Allows for approx. 2 digits of major, 4 digits of minor versions and 9,999 builds before overflowing.
//
// Optional params take individual components of the version
//
// Note: build number currently disabled and is always 0.
function int GetVersionNumber()
{
    return (default.MajorVersion * 100000000) + (default.MinorVersion * 10000) + (default.PatchVersion);
}

defaultproperties
{
    MajorVersion = 1;
    MinorVersion = 15;
    PatchVersion = 0;
}

//---------------------------------------------------------------------------------------
//  FILE:    CHXComGameVersionTemplate.uc
//  AUTHOR:  X2CommunityCore
//  PURPOSE: Version information for the Community Highlander XComGame replacement.
//
//  Issue #1
//
//  This is implemented as a template to allow mods to detect whether the Community Highlander
//  is installed. It's a template to avoid needing to compile against any
//  new sources to get mods to build (which would then crash the game if it tried
//  to access a function //  or variable that wasn't installed anyway). Can be queried by:
//
//  1) Query the StrategyElementTemplateManager for a template named 'CHXComGameVersion'. If you get back
//     a non-none result the XcomGame replacement is installed (or someone is lying and added the template without
//     the actual XComGame replacement...)
//  2) If you need more fine-grained info, such as the particular version, then once you get back a non-none
//     result you can cast it to class 'CHXComGameVersionTemplate' to get the version number through the API below.
//
//  Don't directly look up the template and cast it without checking if you got a non-none result or the game will
//  probably crash when the replacement XComGame isn't present.
//
//  Supports major, minor, and build versions, but build is currently unimplemented.
//---------------------------------------------------------------------------------------
class CHXComGameVersionTemplate extends X2StrategyElementTemplate;

var int MajorVersion;
var int MinorVersion;
var int PatchVersion;

// "Short" version number (minus the patch)
function String GetShortVersionString()
{
    return default.MajorVersion $ "." $ default.MinorVersion;
}

// Version number in string format.
function String GetVersionString()
{
    return default.MajorVersion $ "." $ default.MinorVersion $ "." $ default.PatchVersion;
}

// Version number in comparable numeric format. Number in decimal is MMmmmmPPPP where:O
// "M" is major version
// "m" is minor version
// "P" is patch number
//
// Allows for approx. 2 digits of major, 4 digits of minor versions and 9,999 builds before overflowing.
//
// Optional params take individual components of the version
//
// Note: build number currently disabled and is always 0.
function int GetVersionNumber()
{
    return (default.MajorVersion * 100000000) + (default.MinorVersion * 10000) + (default.PatchVersion);
}

defaultproperties
{
    MajorVersion = 1;
    MinorVersion = 14;
    PatchVersion = 0;
}


Update refactor.uc

Update refactor.uc

Added support for variable declarations with syntax such as:
Local a, b, c, d As Long

# refactor.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/7/2014 10:50:22 PM
# Comment: This rewrites code in more proper PB form

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: This rewrites code in more proper PB form
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
Comment: Defines comments as whitespace and colon as separator
Find:
Replace: {@Define::
            Token: [\x27\_].* ~~ Properties: ucWhiteSpace
            Token: : ~~ Properties: ucStatementSep
         }{@Define: Var: Args As String}

Criteria: 2
Comment: Uniform syntax for optional args
Pass: 1

Criteria: 3
Find: Opt
Replace: Optional

Criteria: 4
BackColor: Lime
Find: "[", {arg} "]"
Replace: Optional {arg}

Criteria: 5
Comment: Misc
Pass: 2

Criteria: 6
Comment: Adds parenthesis around args in function/sub calls that do not have it
BackColor: Lime
Find: [Declare]{ Function|Sub } {name} [Lib {lib}] [Alias {alias}] ({args})
Replace: {@Evaluate:
            Args = Remove({args}, "{ Optional|ByVal|ByRef|As {type:1} [Ptr] | {'[#!@$%&]+'} | () }")
            Args = SetSyntaxParams(Args, Args)
         }{@Define::
            {@Eval: "PassOnce ~~ Syntax: {name} "+Args+" ::= {name}("+Args+")"}
            {@Eval: "SkipOver ~~ Syntax: {name} ({etc})"}
         }{Self}

Criteria: 7
Comment: Adds closing quote for quoted text with missing closing quote
BackColor: DarkKhaki
Find: {QuotedText:"\q[^\q\n]*"}{nl}
Replace: {QuotedText}"{nl}

Criteria: 8
Comment: Adds () As Long to PBMain if missing
Find: Function PBMain [()] [As Long]
Replace: Function PBMain() As Long

Criteria: 9
Selected: True
PassOnce: False
Find: Local [{etc},] {item:1}, {last:1} As {type}
Replace: Local {etc: {etc},} {item} As {type}
         Local {last} As {type}

# End Search

# refactor.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/6/2014 11:42:21 AM
# Comment: This rewrites code in more proper PB form

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: This rewrites code in more proper PB form
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
Comment: Defines comments as whitespace and colon as separator
Find:
Replace: {@Define::
            Token: [\x27\_].* ~~ Properties: ucWhiteSpace
            Token: : ~~ Properties: ucStatementSep
         }{@Define: Var: Args As String}

Criteria: 2
Comment: Uniform syntax for optional args
Pass: 1

Criteria: 3
Find: Opt
Replace: Optional

Criteria: 4
Selected: True
BackColor: Lime
Find: "[", {arg} "]"
Replace: Optional {arg}

Criteria: 5
Comment: Misc
Pass: 2

Criteria: 6
Comment: Adds parenthesis around args in function/sub calls that do not have it
BackColor: Lime
Find: [Declare]{ Function|Sub } {name} [Lib {lib}] [Alias {alias}] ({args})
Replace: {@Evaluate:
            Args = Remove({args}, "{ Optional|ByVal|ByRef|As {type:1} [Ptr] | {'[#!@$%&]+'} | () }")
            Args = SetSyntaxParams(Args, Args)
         }{@Define::
            {@Eval: "PassOnce ~~ Syntax: {name} "+Args+" ::= {name}("+Args+")"}
            {@Eval: "SkipOver ~~ Syntax: {name} ({etc})"}
         }{Self}

Criteria: 7
Comment: Adds closing quote for quoted text with missing closing quote
BackColor: DarkKhaki
Find: {QuotedText:"\q[^\q\n]*"}{nl}
Replace: {QuotedText}"{nl}

Criteria: 8
Comment: Adds () As Long to PBMain if missing
Find: Function PBMain [()] [As Long]
Replace: Function PBMain() As Long

# End Search