Able to prevent replacing default inventory items (and also support for Dual Enforcer)

class TestCentralWeaponReplacement extends UTMutator
	config(TestCentralWeaponReplacement);

const DialogDefaultColor = "<color:R=1,G=1,B=1,A=1>";

//**********************************************************************************
// Structs
//**********************************************************************************

struct ReplacementOptionsInfo
{
	/** Whether to replace/remove the class name */
	var bool bReplaceWeapon;
	/** Whether to check for subclasses  */
	var bool bSubclasses;
	/** Whether to add the class to the weapon lockers */
	var bool bAddToLocker;
	/** Whether to add the class to the default inventory (on spawn) */
	var bool bAddToDefault;

	/** Whether to prevent replacing default inventory items (to be used with bSubclasses=true) */
	var bool bNoDefaultInventory;

	structdefaultproperties
	{
		bReplaceWeapon=true
		bSubclasses=false
		bAddToLocker=false
		bAddToDefault=false
		bNoDefaultInventory=false
	}
};

struct ReplacementInfoEx
{
	/** class name of the weapon we want to get rid of */
	var name OldClassName;
	/** fully qualified path of the class to replace it with */
	var string NewClassPath;

	/** the options for this item */
	var ReplacementOptionsInfo Options;

	/** the mutator/object which has registered the replacement
	(might be none if set by default configuration) */
	var transient Object Registrar;
};

struct ErrorMessageInfo
{
	var Object Conflict;
	var Object Registrar;
	var name ClassName;
	var string NewClassPath;
};

struct HashedMutatorInfo
{
	var string Hash;
	var UTUIDataProvider_Mutator Info;
};

struct HashedWeaponInfo
{
	var string Hash;
	var name ClassName;
	var UTUIDataProvider_Weapon Info;
};

struct HashedAmmoInfo
{
	var string Hash;
	var name ClassName;
	var string Text;
};

//**********************************************************************************
// Variables
//**********************************************************************************

// Config
// ------------

var config array<ReplacementInfoEx> DefaultWeaponsToReplace;
var config array<ReplacementInfoEx> DefaultAmmoToReplace;

// Workflow
// ------------

var array<ReplacementInfoEx> WeaponsToReplace;
var array<ReplacementInfoEx> AmmoToReplace;

var array<HashedMutatorInfo> HashedMutators;
var array<HashedWeaponInfo> HashedWeapons;
var array<HashedAmmoInfo> HashedAmmos;

var array<int> EnforcerIndizes;

// UI
// ------------

var array<ErrorMessageInfo> ErrorMessages;

var() string DialogNameColor;
var() string DialogGoodColor;
var() string DialogBadColor;

// Localization
// ------------

var localized string MessageErrors;
var localized string MessageClassReplacedBy;
var localized string MessageClassReplacedDefault;
var localized string MessageClassSubstBy;
var localized string MessageClassSubstDefault;
var localized string MessageClassRemovedBy;
var localized string MessageClassRemovedDefault;

//**********************************************************************************
// Inherited functions
//**********************************************************************************

/* Don't call Actor PreBeginPlay() for Mutator
*/
event PreBeginPlay()
{
	local int i;
	super.PreBeginPlay();

	if (!IsPendingKill())
	{
		WeaponsToReplace.Length = 0;
		AmmoToReplace.Length = 0;

		for (i=0; i<DefaultWeaponsToReplace.Length; i++)
			RegisterWeaponReplacement(none, DefaultWeaponsToReplace[i].OldClassName, DefaultWeaponsToReplace[i].NewClassPath, false, DefaultWeaponsToReplace[i].Options);

		for (i=0; i<DefaultAmmoToReplace.Length; i++)
			RegisterWeaponReplacement(none, DefaultAmmoToReplace[i].OldClassName, DefaultAmmoToReplace[i].NewClassPath, true, DefaultAmmoToReplace[i].Options);
	}
}

/**
 * This function can be used to parse the command line parameters when a server
 * starts up
 */

function InitMutator(string Options, out string ErrorMessage)
{
	local int i, j;
	local UTGame G;
	local class<UTWeapon> WeaponClass;

	super.InitMutator(Options, ErrorMessage);

	// Make sure the game does not hold a null reference
	G = UTGame(WorldInfo.Game);
	if(G != none)
	{
		for (j=0; j<WeaponsToReplace.Length; j++)
		{
			for (i=0; i<G.DefaultInventory.length; i++)
			{
				if (G.DefaultInventory[i] == None) continue;
				if (!WeaponsToReplace[j].Options.bSubclasses && G.DefaultInventory[i].Name != WeaponsToReplace[j].OldClassName) continue;

				// IsA doesn't work for abstract classes, we need to use the workaround/hackfix
				if (WeaponsToReplace[j].Options.bSubclasses && !IsSubClass(G.DefaultInventory[i], WeaponsToReplace[j].OldClassName)) continue;

				if (WeaponsToReplace[j].Options.bReplaceWeapon && !WeaponsToReplace[j].Options.bNoDefaultInventory)
				{
					if (!ClassPathValid(WeaponsToReplace[j].NewClassPath))
					{
						// replace with nothing
						G.DefaultInventory.Remove(i, 1);
						i--;
						continue;
					}
					else
					{
						WeaponClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
						if (WeaponsToReplace[j].Options.bReplaceWeapon)
						{
							G.DefaultInventory[i] = WeaponClass;
						}
					}
				}
			}

			if (WeaponsToReplace[j].Options.bAddToDefault && ClassPathValid(WeaponsToReplace[j].NewClassPath))
			{
				WeaponClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
				if (WeaponClass != none) G.DefaultInventory.AddItem(WeaponClass);
			}

			if (WeaponsToReplace[j].Options.bAddToLocker)
			{
				WeaponClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
				AddToLocker(WeaponClass);
			}
		}

		if (G.TranslocatorClass != None)
		{
			j = WeaponsToReplace.Find('OldClassName', G.TranslocatorClass.Name);
			if (j != INDEX_NONE)
			{
				if (WeaponsToReplace[j].NewClassPath == "" || !WeaponsToReplace[j].Options.bReplaceWeapon)
				{
					// replace with nothing
					G.TranslocatorClass = None;
				}
				else
				{
					G.TranslocatorClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
				}
			}
		}

		EnforcerIndizes.Length = 0;
		for (i=0; i<G.DefaultInventory.length; i++)
		{
			if (IsSubClass(G.DefaultInventory[i], 'UTWeap_Enforcer'))
			{
				EnforcerIndizes.AddItem(i);
			}
		}
	}
}

function bool CheckReplacement(Actor Other)
{
	local UTWeaponPickupFactory WeaponPickup;
	local UTWeaponLocker Locker;
	local UTAmmoPickupFactory AmmoPickup, NewAmmo;
	local int i, Index;
	local class<UTAmmoPickupFactory> NewAmmoClass;

	WeaponPickup = UTWeaponPickupFactory(Other);
	if (WeaponPickup != None)
	{
		if (WeaponPickup.WeaponPickupClass != None)
		{
			if (ShouldBeReplaced(index, WeaponPickup.WeaponPickupClass, false) && WeaponsToReplace[index].Options.bReplaceWeapon)
			{
				if (WeaponsToReplace[index].NewClassPath == "")
				{
					// replace with nothing
					return false;
				}
				WeaponPickup.WeaponPickupClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[index].NewClassPath, class'Class'));
				WeaponPickup.InitializePickup();
			}
		}
	}
	else
	{
		if (UTWeaponLocker(Other) != None)
		{
			Locker = UTWeaponLocker(Other);
			for (i = 0; i < Locker.Weapons.length; i++)
			{
				if (Locker.Weapons[i].WeaponClass != none && ShouldBeReplaced(index, Locker.Weapons[i].WeaponClass, false) && WeaponsToReplace[index].Options.bReplaceWeapon)
				{
					if (WeaponsToReplace[index].NewClassPath == "")
					{
						// replace with nothing
						Locker.ReplaceWeapon(i, None);
					}
					else
					{
						Locker.ReplaceWeapon(i, class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[index].NewClassPath, class'Class')));
					}
				}
			}
		}
		else if (UTAmmoPickupFactory(Other) != none)
		{
			AmmoPickup = UTAmmoPickupFactory(Other);
			if (AmmoPickup.Class != none)
			{
				if (ShouldBeReplaced(index, AmmoPickup.Class, true) && AmmoToReplace[index].Options.bReplaceWeapon)
				{
					if (AmmoToReplace[index].NewClassPath == "")
					{
						// replace with nothing
						return false;
					}
					NewAmmoClass = class<UTAmmoPickupFactory>(DynamicLoadObject(AmmoToReplace[index].NewClassPath, class'Class'));
					if (NewAmmoClass == None)
					{
						// replace with nothing
						return false;
					}
					else if (NewAmmoClass.default.bStatic || NewAmmoClass.default.bNoDelete)
					{
						// transform the current ammo into the desired class
						AmmoPickup.TransformAmmoType(NewAmmoClass);
						return true;
					}
					else
					{
						// spawn the new ammo, link it to the old, then disable the old one
						NewAmmo = AmmoPickup.Spawn(NewAmmoClass);
						NewAmmo.OriginalFactory = AmmoPickup;
						AmmoPickup.ReplacementFactory = NewAmmo;
						return false;
					}
				}
			}
		}

		// remove initial anim for Enforcers (may only work on server/listen player)
		else if (EnforcerIndizes.Length > 1 && UTWeap_Enforcer(Other) != none)
		{
			UTWeap_Enforcer(Other).bLoaded = true;
			UTWeap_Enforcer(Other).EquipTime = UTWeap_Enforcer(Other).ReloadTime;
		}
	}

	return true;
}

/* called by GameInfo.RestartPlayer()
	change the players jumpz, etc. here
*/
function ModifyPlayer(Pawn Other)
{
	local int i, index;
	local UTWeap_Enforcer Enf;
	super.ModifyPlayer(Other);

	// special case for Akimbo (Enforcer only)
	if (EnforcerIndizes.Length > 1 && UTGame(WorldInfo.Game) != none)
	{
		index = EnforcerIndizes[0];
		Enf = UTWeap_Enforcer(Other.FindInventoryType(UTGame(WorldInfo.Game).DefaultInventory[0]));
		if (Enf != none)
		{
			for (i=1; i<EnforcerIndizes.Length; i++)
			{
				index = EnforcerIndizes[i];
				if (index < UTGame(WorldInfo.Game).DefaultInventory.Length)
				{
					if (!Enf.DenyPickupQuery(UTGame(WorldInfo.Game).DefaultInventory[index], none))
					{
						Other.CreateInventory(UTGame(WorldInfo.Game).DefaultInventory[index], false);
					}
				}
			}
		}
	}
}

// called when gameplay actually starts
function MatchStarting()
{
	local int i;
	local string str;

	super.MatchStarting();

	if (ErrorMessages.Length > 0 && WorldInfo.NetMode != NM_DedicatedServer)
	{
		// init datastore for weapons and mutators
		LoadMutators();
		LoadWeapons();

		for (i=0; i<ErrorMessages.Length; i++)
		{
			if (str != "" || i > 0)
				str $= Chr(10);
			str $= GenerateErrorInfo(ErrorMessages[i]);
		}

		// clear out data
		HashedMutators.Length = 0;
		HashedWeapons.Length = 0;
		HashedAmmos.Length = 0;

		str = Repl(Repl(MessageErrors, "  ", Chr(10)), "`errors", str);
		ShowMessageBox(str, GetHumanReadableName());
	}
}

// Returns the human readable string representation of an object.
simulated function String GetHumanReadableName()
{
	return GetMutatorName(self);
}

//**********************************************************************************
// Interface functions
//**********************************************************************************

function RegisterWeaponReplacement(Object Registrar, name OldClassName, string NewClassPath, bool bAmmo, ReplacementOptionsInfo ReplacementOptions)
{
	local int index;

	// ensure empty class path
	if (NewClassPath ~= "None") NewClassPath = "";
	NewClassPath = TrimRight(NewClassPath);

	if (bAmmo)
	{
		if (IsNewItem(AmmoToReplace, index, OldClassName))
		{
			AddWeaponReplacement(true, Registrar, OldClassName, NewClassPath, ReplacementOptions);
		}
		else if (!(AmmoToReplace[index].NewClassPath ~= NewClassPath))
		{
			AddErrorMessage(AmmoToReplace[index].Registrar, Registrar, OldClassName, NewClassPath);
		}
	}
	else if (IsNewItem(WeaponsToReplace, index, OldClassName))
	{
		AddWeaponReplacement(false, Registrar, OldClassName, NewClassPath, ReplacementOptions);
	}
	else if (!(WeaponsToReplace[index].NewClassPath ~= NewClassPath))
	{
		AddErrorMessage(WeaponsToReplace[index].Registrar, Registrar, OldClassName, NewClassPath);
	}
}

static function bool StaticRegisterWeaponReplacement(Object Registrar, coerce name OldClassName, string NewClassPath, bool bAmmo, optional ReplacementOptionsInfo ReplacementOptions, optional bool bSilent, optional out string ErrorMessage)
{
	local WorldInfo WI;
	local TestCentralWeaponReplacement mut;

	if (Registrar == none)
	{
		ErrorMessage = "No Registrar.";
		return false;
	}

	if (Actor(Registrar) != none)
		WI = Actor(Registrar).WorldInfo;
	if (WI == none)
		WI = class'Engine'.static.GetCurrentWorldInfo();

	if (WI == none)
	{
		ErrorMessage = "No WorldInfo found.";
		return false;
	}

	// try to find an existent gneric mutator
	foreach WI.DynamicActors(class'TestCentralWeaponReplacement', mut)
		break;

	// if not mutator was found, create one
	if (mut == none)
	{
		mut = WI.Spawn(class'TestCentralWeaponReplacement', WI.Game);
		if (WI.Game != none)
		{
			mut.NextMutator = WI.Game.BaseMutator;
			WI.Game.BaseMutator = mut;
		}
	}

	// register the weapon replacement
	mut.RegisterWeaponReplacement(Registrar, OldClassName, NewClassPath, bAmmo, ReplacementOptions);
	return true;
}

//**********************************************************************************
// Private functions
//**********************************************************************************

private function AddWeaponReplacement(bool bAmmo, Object Registrar, name OldClassName, string NewClassPath, ReplacementOptionsInfo ReplacementOptions)
{
	local ReplacementInfoEx item;
	item.OldClassName = OldClassName;
	item.NewClassPath = NewClassPath;
	item.Registrar = Registrar;
	item.Options = ReplacementOptions;

	if (bAmmo) AmmoToReplace.AddItem(item);
	else WeaponsToReplace.AddItem(item);
}

private function AddErrorMessage(Object Conflicting, Object Registrar, name OldClassName, string NewClassPath)
{
	local int index;
	index = ErrorMessages.Length;
	ErrorMessages.Add(1);
	ErrorMessages[index].Conflict = Conflicting;
	ErrorMessages[index].Registrar = Registrar;
	ErrorMessages[index].ClassName = OldClassName;
	ErrorMessages[index].NewClassPath = NewClassPath;
}

private function LoadMutators()
{
	local array<UTUIResourceDataProvider> ProviderList;
	local int i;
	local string hash;
	local UTUIDataProvider_Mutator mut;
	local HashedMutatorInfo item;

	class'UTUIDataStore_MenuItems'.static.GetAllResourceDataProviders(class'UTUIDataProvider_Mutator', ProviderList);
	for (i=0; i<ProviderList.length; i++)
	{
		mut = UTUIDataProvider_Mutator(ProviderList[i]);
		if (mut == none || !ClassPathValid(mut.ClassName)) continue;

		// don't add duplicate items
		hash = Locs(mut.ClassName);
		if (HashedMutators.Find('Hash', hash) != INDEX_NONE) continue;

		item.Hash = hash;
		item.Info = mut;

		HashedMutators.AddItem(item);
	}
}

private function LoadWeapons()
{
	local array<UTUIResourceDataProvider> ProviderList;
	local int i, index;
	local string hash;
	local UTUIDataProvider_Weapon weapn;
	local HashedWeaponInfo item;
	local HashedAmmoInfo ammo;

	local string str, s1, s2;

	class'UTUIDataStore_MenuItems'.static.GetAllResourceDataProviders(class'UTUIDataProvider_Weapon', ProviderList);
	for (i=0; i<ProviderList.length; i++)
	{
		weapn = UTUIDataProvider_Weapon(ProviderList[i]);
		if (weapn == none || !ClassPathValid(weapn.ClassName)) continue;

		// don't add duplicate items
		hash = Locs(weapn.ClassName);
		if (HashedWeapons.Find('Hash', hash) != INDEX_NONE) continue;

		item.Hash = hash;
		item.Info = weapn;
		item.ClassName = name(Mid(weapn.ClassName, Instr(weapn.ClassName, ".")+1));

		HashedWeapons.AddItem(item);

		// Ammo
		if (!ClassPathValid(weapn.AmmoClassPath)) continue;
		hash = Locs(weapn.AmmoClassPath);
		if (HashedAmmos.Find('Hash', hash) != INDEX_NONE) continue;

		index = Instr(weapn.AmmoClassPath, ".");
		s1 = Left(weapn.AmmoClassPath, index);
		s2 = Mid(weapn.AmmoClassPath, index+1);

		str = Localize(s2, "PickupMessage", s1);
		FixFriendlyName(str);

		ammo.Hash = hash;
		ammo.Text = str;
		ammo.ClassName = name(s2);

		HashedAmmos.AddItem(ammo);
	}
}

function AddToLocker(class<UTWeapon> WeaponClass)
{
	local UTWeaponLocker Locker;
	local WeaponEntry ent;

	if (WeaponClass == none)
		return;

	foreach DynamicActors(class'UTWeaponLocker', Locker)
	{
		ent.WeaponClass = WeaponClass;
		Locker.MaxDesireability += WeaponClass.Default.AIRating;

		//if (WeaponClass.default.PickupFactoryMesh != none)
		//	ent.PickupMesh = WeaponClass.default.PickupFactoryMesh;

		Locker.Weapons.AddItem(ent);
	}
}

function bool ShouldBeReplaced(out int index, class ClassToCheck, bool bAmmo)
{
	local int i;
	if (bAmmo)
	{
		index = AmmoToReplace.Find('OldClassName', ClassToCheck.Name);
		if (index == INDEX_NONE)
		{
			for (i=0; i<AmmoToReplace.Length; i++)
			{

				if (AmmoToReplace[i].Options.bSubclasses && IsSubClass(ClassToCheck, AmmoToReplace[i].OldClassName))
				{
					index = i;
					return true;
				}
			}
		}
	}
	else
	{
		index = WeaponsToReplace.Find('OldClassName', ClassToCheck.Name);
		if (index == INDEX_NONE)
		{
			for (i=0; i<WeaponsToReplace.Length; i++)
			{
				if (WeaponsToReplace[i].Options.bSubclasses && IsSubClass(ClassToCheck, WeaponsToReplace[i].OldClassName))
				{
					index = i;
					return true;
				}
			}
		}
	}

	return index != INDEX_NONE;
}

//**********************************************************************************
// Helper functions
//**********************************************************************************

function bool IsNewItem(out array<ReplacementInfoEx> items, out int index, name ClassName)
{
	index = items.Find('OldClassName', ClassName);
	return index == INDEX_NONE;
}

private function string GenerateErrorInfo(ErrorMessageInfo err)
{
	local string str;

	if (!ClassPathValid(err.NewClassPath)) // TODO: Check for valid class path and not just for empty string
	{
		str = err.Registrar != none ? MessageClassRemovedBy : MessageClassRemovedDefault;
	}
	else
	{
		str = err.Registrar != none ? MessageClassSubstBy : MessageClassSubstDefault;
		str $= " ";
		str $= err.Conflict != none ? MessageClassReplacedBy : MessageClassReplacedDefault;
	}

	str = Repl(str, "`new", Colorize(GetPickupName(err.NewClassPath, true), DialogNameColor));
	str = Repl(str, "`old", Colorize(GetPickupName(err.ClassName, false), DialogNameColor));
	str = Repl(str, "`already", Colorize(GetObjectFriendlyName(err.Conflict), DialogGoodColor));
	str = Repl(str, "`mutator", Colorize(GetObjectFriendlyName(err.Registrar), DialogBadColor));
	return str;
}

private function string GetPickupName(coerce string Path, bool IsPath)
{
	local int index;
	local string str;
	if (IsPath) index = HashedWeapons.Find('Hash', Locs(Path));
	else index = HashedWeapons.Find('ClassName', name(Path));
	if (index != INDEX_NONE && HashedWeapons[index].Info != none)
	{
		str = HashedWeapons[index].Info.FriendlyName;
	}
	else
	{
		if (IsPath) index = HashedAmmos.Find('Hash', Locs(Path));
		else index = HashedAmmos.Find('ClassName', name(Path));
		if (index != INDEX_NONE)
		{
			str = HashedAmmos[index].Text;
		}
	}

	if (str == "" || Left(str, 1) == "?")
	{
		str = Mid(Path, Instr(Path, ".")+1);
	}

	return str;
}

private function string GetObjectFriendlyName(Object obj, optional string defaultstr)
{
	local int index;
	local string str;

	if (class(Obj) != none)
		str = PathName(Obj);
	else if (Obj != none)
		str = PathName(Obj.Class);

	if (str != "")
	{
		index = HashedMutators.Find('Hash', Locs(str));
		if (index != INDEX_NONE && HashedMutators[index].Info != none)
		{
			str = HashedMutators[index].Info.FriendlyName;
		}
	}

	if (str == "")
	{
		if (Actor(obj) != none)
			str = Actor(obj).GetHumanReadableName();
		if (str == "" && class<Actor>(obj) != none)
			str = class<Actor>(obj).static.GetLocalString();
		else if (obj != none)
			str = string(obj.Name);
	}

	return str != "" ? str : defaultstr;
}

static function bool IsSubClass(class ClassToCheck, name ParentClassName)
{
	local Object Obj;
	// get default object which works for abstract classes
	return GetDefaultObject(ClassToCheck, obj) && obj.IsA(ParentClassName);
}

// NOTE: needed for hackfix
static function bool GetDefaultObject(class<Object> cls, out Object obj)
{
	local string path;

	path = GetDefaultPath(cls);
	obj = FindObject(path, cls);
	return (obj != none);
}

// NOTE: needed for hackfix
static function string GetDefaultPath(class<Object> cls)
{
	return cls.GetPackageName() $".Default__"$ cls.Name;
}

//**********************************************************************************
// Static functions
//**********************************************************************************

static function ShowMessageBox(string message, optional string Title="")
{
	local GameUISceneClient MySceneClient;
	local UIScene OpenedScene;
	MySceneClient = UTGameUISceneClient(class'UIRoot'.static.GetSceneClient());
	if (MySceneClient != none && MySceneClient.OpenScene(class'UTUIScene'.default.MessageBoxScene,, OpenedScene) && UTUIScene_MessageBox(OpenedScene) != none)
	{
		UTUIScene_MessageBox(OpenedScene).Display(message, Title);
	}
}

static function string Colorize(coerce string str, string ColorString)
{
	//<color:/> doesn't work so we need to use another tag with white color
	return "<color:"$ColorString$">"$str$DialogDefaultColor;
}

static function bool ClassPathValid(string classpath)
{
	if (classpath == "" || Len(classpath) > 63 || InStr(classpath, " ") != INDEX_NONE)
		return false;

	return (InStr(classpath, ".") != INDEX_NONE && Right(classpath, 1) != ".");
}

/** Trim the right part of the string */
static simulated function string TrimRight(coerce string sInput, optional coerce string sTrim = " ")
{
	local string sOutput;
	local int l;
	sOutput = sInput;

	l = Len(sTrim);
	while (Right(sOutput, l) == sTrim)
		sOutput = Left(sOutput, Len(sOutput)-1);

	return sOutput;
}

/** Fix names which are uppercase */
static function FixFriendlyName(out string FriendlyName)
{
	local string str;
	local array<String> splitname;
	local int i;

	// check for bad string
	if (FriendlyName != "" && Caps(FriendlyName) == FriendlyName)
	{
		// remove the special char from the end of the string
		str = TrimRight(FriendlyName, "!");

		// parse single words into an array
		ParseStringIntoArray(str, splitname, " ", true);

		// properly format longer words
		for (i=0; i<splitname.Length;i++)
			if (len(splitname[i]) > 2)
				splitname[i] = Left(splitname[i], 1)$Mid(locs(splitname[i]), 1);

		// join split name
		JoinArray(splitname, str, " ");
		FriendlyName = str;
	}
}

private static function string GetMutatorName(Mutator M)
{
	local Object TempObj;
	local UTUIDataProvider_Mutator DP;
	local string Pack;

	Pack = ""$M.class.GetPackageName();

	// Check that the data provider for this mutator exists
	TempObj = new(M.Class.Outer, Pack) Class'Package';
	DP = new(TempObj, string(M.Class)) Class'UTUIDataProvider_Mutator';

	if (DP != none && DP.FriendlyName != "") {
		return DP.FriendlyName;
	} else {
		return ""$M.Class.Name;
	}
}

Defaultproperties
{
	GroupNames[0]="WEAPONMOD"

	DialogNameColor="R=1.0,G=1.0,B=0.5,A=1.0"
	DialogBadColor="R=1.0,G=0.5,B=0.5,A=1"
	DialogGoodColor="R=0.1328125,G=0.69140625,B=0.296875,A=1"

	MessageErrors="Some errors occurred in replacing weapons:    `errors"
	MessageClassReplacedBy="The mutator `already is already replacing `old."
	MessageClassReplacedDefault="`old is already replaced by the default configuration."
	MessageClassSubstBy="`new cannot be added by `mutator."
	MessageClassSubstDefault="`new cannot be added."
	MessageClassRemovedBy="The mutator `already already removed `old."
	MessageClassRemovedDefault="`old is already removed by the default configuration."
}

class TestCentralWeaponReplacement extends UTMutator
	config(TestCentralWeaponReplacement);

const DialogDefaultColor = "<color:R=1,G=1,B=1,A=1>";

//**********************************************************************************
// Structs
//**********************************************************************************

struct ReplacementOptionsInfo
{
	/** Whether to replace/remove the class name */
	var bool bReplaceWeapon;
	/** Whether to check for subclasses  */
	var bool bSubclasses;
	/** Whether to add the class to the weapon lockers */
	var bool bAddToLocker;
	/** Whether to add the class to the default inventory (on spawn) */
	var bool bAddToDefault;

	structdefaultproperties
	{
		bReplaceWeapon=true
		bSubclasses=false
		bAddToLocker=false
		bAddToDefault=false
	}
};

struct ReplacementInfoEx
{
	/** class name of the weapon we want to get rid of */
	var name OldClassName;
	/** fully qualified path of the class to replace it with */
	var string NewClassPath;

	/** the options for this item */
	var ReplacementOptionsInfo Options;

	/** the mutator/object which has registered the replacement
	(might be none if set by default configuration) */
	var transient Object Registrar;
};

struct ErrorMessageInfo
{
	var Object Conflict;
	var Object Registrar;
	var name ClassName;
	var string NewClassPath;
};

struct HashedMutatorInfo
{
	var string Hash;
	var UTUIDataProvider_Mutator Info;
};

struct HashedWeaponInfo
{
	var string Hash;
	var name ClassName;
	var UTUIDataProvider_Weapon Info;
};

struct HashedAmmoInfo
{
	var string Hash;
	var name ClassName;
	var string Text;
};

//**********************************************************************************
// Variables
//**********************************************************************************

// Config
// ------------

var config array<ReplacementInfoEx> DefaultWeaponsToReplace;
var config array<ReplacementInfoEx> DefaultAmmoToReplace;

// Workflow
// ------------

var array<ReplacementInfoEx> WeaponsToReplace;
var array<ReplacementInfoEx> AmmoToReplace;

var array<HashedMutatorInfo> HashedMutators;
var array<HashedWeaponInfo> HashedWeapons;
var array<HashedAmmoInfo> HashedAmmos;

// UI
// ------------

var array<ErrorMessageInfo> ErrorMessages;

var() string DialogNameColor;
var() string DialogGoodColor;
var() string DialogBadColor;

// Localization
// ------------

var localized string MessageErrors;
var localized string MessageClassReplacedBy;
var localized string MessageClassReplacedDefault;
var localized string MessageClassSubstBy;
var localized string MessageClassSubstDefault;
var localized string MessageClassRemovedBy;
var localized string MessageClassRemovedDefault;

//**********************************************************************************
// Inherited functions
//**********************************************************************************

/* Don't call Actor PreBeginPlay() for Mutator
*/
event PreBeginPlay()
{
	local int i;
	super.PreBeginPlay();

	if (!IsPendingKill())
	{
		WeaponsToReplace.Length = 0;
		AmmoToReplace.Length = 0;

		for (i=0; i<DefaultWeaponsToReplace.Length; i++)
			RegisterWeaponReplacement(none, DefaultWeaponsToReplace[i].OldClassName, DefaultWeaponsToReplace[i].NewClassPath, false, DefaultWeaponsToReplace[i].Options);

		for (i=0; i<DefaultAmmoToReplace.Length; i++)
			RegisterWeaponReplacement(none, DefaultAmmoToReplace[i].OldClassName, DefaultAmmoToReplace[i].NewClassPath, true, DefaultAmmoToReplace[i].Options);
	}
}

/**
 * This function can be used to parse the command line parameters when a server
 * starts up
 */

function InitMutator(string Options, out string ErrorMessage)
{
	local int i, j;
	local UTGame G;
	local class<UTWeapon> WeaponClass;

	super.InitMutator(Options, ErrorMessage);

	// Make sure the game does not hold a null reference
	G = UTGame(WorldInfo.Game);
	if(G != none)
	{
		for (j=0; j<WeaponsToReplace.Length; j++)
		{
			WeaponClass = none;
			for (i=0; i<G.DefaultInventory.length; i++)
			{
				if (G.DefaultInventory[i] == None) continue;
				if (!WeaponsToReplace[j].Options.bSubclasses && G.DefaultInventory[i].Name != WeaponsToReplace[j].OldClassName) continue;

				// IsA doesn't work for abstract classes, we need to use the workaround/hackfix
				if (WeaponsToReplace[j].Options.bSubclasses && !IsSubClass(G.DefaultInventory[i], WeaponsToReplace[j].OldClassName)) continue;

				if (!ClassPathValid(WeaponsToReplace[j].NewClassPath))
				{
					// replace with nothing
					G.DefaultInventory.Remove(i, 1);
					i--;
					continue;
				}
				else
				{
					WeaponClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
					if (WeaponsToReplace[j].Options.bReplaceWeapon)
					{
						G.DefaultInventory[i] = WeaponClass;
					}
					else if (WeaponsToReplace[j].Options.bAddToDefault)
					{
						G.DefaultInventory.AddItem(WeaponClass);
					}
				}
			}

			if (WeaponsToReplace[j].Options.bAddToLocker)
			{
				WeaponClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
				AddToLocker(WeaponClass);
			}
		}

		if (G.TranslocatorClass != None)
		{
			j = WeaponsToReplace.Find('OldClassName', G.TranslocatorClass.Name);
			if (j != INDEX_NONE)
			{
				if (WeaponsToReplace[j].NewClassPath == "" || !WeaponsToReplace[j].Options.bReplaceWeapon)
				{
					// replace with nothing
					G.TranslocatorClass = None;
				}
				else
				{
					G.TranslocatorClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[j].NewClassPath, class'Class'));
				}
			}
		}
	}
}

function bool CheckReplacement(Actor Other)
{
	local UTWeaponPickupFactory WeaponPickup;
	local UTWeaponLocker Locker;
	local UTAmmoPickupFactory AmmoPickup, NewAmmo;
	local int i, Index;
	local class<UTAmmoPickupFactory> NewAmmoClass;

	WeaponPickup = UTWeaponPickupFactory(Other);
	if (WeaponPickup != None)
	{
		if (WeaponPickup.WeaponPickupClass != None)
		{
			if (ShouldBeReplaced(index, WeaponPickup.WeaponPickupClass, false) && WeaponsToReplace[index].Options.bReplaceWeapon)
			{
				if (WeaponsToReplace[index].NewClassPath == "")
				{
					// replace with nothing
					return false;
				}
				WeaponPickup.WeaponPickupClass = class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[index].NewClassPath, class'Class'));
				WeaponPickup.InitializePickup();
			}
		}
	}
	else
	{
		if (UTWeaponLocker(Other) != None)
		{
			Locker = UTWeaponLocker(Other);
			for (i = 0; i < Locker.Weapons.length; i++)
			{
				if (Locker.Weapons[i].WeaponClass != none && ShouldBeReplaced(index, Locker.Weapons[i].WeaponClass, false) && WeaponsToReplace[index].Options.bReplaceWeapon)
				{
					if (WeaponsToReplace[index].NewClassPath == "")
					{
						// replace with nothing
						Locker.ReplaceWeapon(i, None);
					}
					else
					{
						Locker.ReplaceWeapon(i, class<UTWeapon>(DynamicLoadObject(WeaponsToReplace[index].NewClassPath, class'Class')));
					}
				}
			}
		}
		else if (UTAmmoPickupFactory(Other) != none)
		{
			AmmoPickup = UTAmmoPickupFactory(Other);
			if (AmmoPickup.Class != none)
			{
				if (ShouldBeReplaced(index, AmmoPickup.Class, true) && AmmoToReplace[index].Options.bReplaceWeapon)
				{
					if (AmmoToReplace[index].NewClassPath == "")
					{
						// replace with nothing
						return false;
					}
					NewAmmoClass = class<UTAmmoPickupFactory>(DynamicLoadObject(AmmoToReplace[index].NewClassPath, class'Class'));
					if (NewAmmoClass == None)
					{
						// replace with nothing
						return false;
					}
					else if (NewAmmoClass.default.bStatic || NewAmmoClass.default.bNoDelete)
					{
						// transform the current ammo into the desired class
						AmmoPickup.TransformAmmoType(NewAmmoClass);
						return true;
					}
					else
					{
						// spawn the new ammo, link it to the old, then disable the old one
						NewAmmo = AmmoPickup.Spawn(NewAmmoClass);
						NewAmmo.OriginalFactory = AmmoPickup;
						AmmoPickup.ReplacementFactory = NewAmmo;
						return false;
					}
				}
			}
		}
	}

	return true;
}

// called when gameplay actually starts
function MatchStarting()
{
	local int i;
	local string str;

	super.MatchStarting();

	if (ErrorMessages.Length > 0 && WorldInfo.NetMode != NM_DedicatedServer)
	{
		// init datastore for weapons and mutators
		LoadMutators();
		LoadWeapons();

		for (i=0; i<ErrorMessages.Length; i++)
		{
			if (str != "" || i > 0)
				str $= Chr(10);
			str $= GenerateErrorInfo(ErrorMessages[i]);
		}

		// clear out data
		HashedMutators.Length = 0;
		HashedWeapons.Length = 0;
		HashedAmmos.Length = 0;

		str = Repl(Repl(MessageErrors, "  ", Chr(10)), "`errors", str);
		ShowMessageBox(str, GetHumanReadableName());
	}
}

// Returns the human readable string representation of an object.
simulated function String GetHumanReadableName()
{
	return GetMutatorName(self);
}

//**********************************************************************************
// Interface functions
//**********************************************************************************

function RegisterWeaponReplacement(Object Registrar, name OldClassName, string NewClassPath, bool bAmmo, ReplacementOptionsInfo ReplacementOptions)
{
	local int index;

	// ensure empty class path
	if (NewClassPath ~= "None") NewClassPath = "";
	NewClassPath = TrimRight(NewClassPath);

	if (bAmmo)
	{
		if (IsNewItem(AmmoToReplace, index, OldClassName))
		{
			AddWeaponReplacement(true, Registrar, OldClassName, NewClassPath, ReplacementOptions);
		}
		else if (!(AmmoToReplace[index].NewClassPath ~= NewClassPath))
		{
			AddErrorMessage(AmmoToReplace[index].Registrar, Registrar, OldClassName, NewClassPath);
		}
	}
	else if (IsNewItem(WeaponsToReplace, index, OldClassName))
	{
		AddWeaponReplacement(false, Registrar, OldClassName, NewClassPath, ReplacementOptions);
	}
	else if (!(WeaponsToReplace[index].NewClassPath ~= NewClassPath))
	{
		AddErrorMessage(WeaponsToReplace[index].Registrar, Registrar, OldClassName, NewClassPath);
	}
}

static function bool StaticRegisterWeaponReplacement(Object Registrar, coerce name OldClassName, string NewClassPath, bool bAmmo, optional ReplacementOptionsInfo ReplacementOptions, optional bool bSilent, optional out string ErrorMessage)
{
	local WorldInfo WI;
	local TestCentralWeaponReplacement mut;

	if (Registrar == none)
	{
		ErrorMessage = "No Registrar.";
		return false;
	}

	if (Actor(Registrar) != none)
		WI = Actor(Registrar).WorldInfo;
	if (WI == none)
		WI = class'Engine'.static.GetCurrentWorldInfo();

	if (WI == none)
	{
		ErrorMessage = "No WorldInfo found.";
		return false;
	}

	// try to find an existent gneric mutator
	foreach WI.DynamicActors(class'TestCentralWeaponReplacement', mut)
		break;

	// if not mutator was found, create one
	if (mut == none)
	{
		mut = WI.Spawn(class'TestCentralWeaponReplacement', WI.Game);
		if (WI.Game != none)
		{
			mut.NextMutator = WI.Game.BaseMutator;
			WI.Game.BaseMutator = mut;
		}
	}

	// register the weapon replacement
	mut.RegisterWeaponReplacement(Registrar, OldClassName, NewClassPath, bAmmo, ReplacementOptions);
	return true;
}

//**********************************************************************************
// Private functions
//**********************************************************************************

private function AddWeaponReplacement(bool bAmmo, Object Registrar, name OldClassName, string NewClassPath, ReplacementOptionsInfo ReplacementOptions)
{
	local ReplacementInfoEx item;
	item.OldClassName = OldClassName;
	item.NewClassPath = NewClassPath;
	item.Registrar = Registrar;
	item.Options = ReplacementOptions;

	if (bAmmo) AmmoToReplace.AddItem(item);
	else WeaponsToReplace.AddItem(item);
}

private function AddErrorMessage(Object Conflicting, Object Registrar, name OldClassName, string NewClassPath)
{
	local int index;
	index = ErrorMessages.Length;
	ErrorMessages.Add(1);
	ErrorMessages[index].Conflict = Conflicting;
	ErrorMessages[index].Registrar = Registrar;
	ErrorMessages[index].ClassName = OldClassName;
	ErrorMessages[index].NewClassPath = NewClassPath;
}

private function LoadMutators()
{
	local array<UTUIResourceDataProvider> ProviderList;
	local int i;
	local string hash;
	local UTUIDataProvider_Mutator mut;
	local HashedMutatorInfo item;

	class'UTUIDataStore_MenuItems'.static.GetAllResourceDataProviders(class'UTUIDataProvider_Mutator', ProviderList);
	for (i=0; i<ProviderList.length; i++)
	{
		mut = UTUIDataProvider_Mutator(ProviderList[i]);
		if (mut == none || !ClassPathValid(mut.ClassName)) continue;

		// don't add duplicate items
		hash = Locs(mut.ClassName);
		if (HashedMutators.Find('Hash', hash) != INDEX_NONE) continue;

		item.Hash = hash;
		item.Info = mut;

		HashedMutators.AddItem(item);
	}
}

private function LoadWeapons()
{
	local array<UTUIResourceDataProvider> ProviderList;
	local int i, index;
	local string hash;
	local UTUIDataProvider_Weapon weapn;
	local HashedWeaponInfo item;
	local HashedAmmoInfo ammo;

	local string str, s1, s2;

	class'UTUIDataStore_MenuItems'.static.GetAllResourceDataProviders(class'UTUIDataProvider_Weapon', ProviderList);
	for (i=0; i<ProviderList.length; i++)
	{
		weapn = UTUIDataProvider_Weapon(ProviderList[i]);
		if (weapn == none || !ClassPathValid(weapn.ClassName)) continue;

		// don't add duplicate items
		hash = Locs(weapn.ClassName);
		if (HashedWeapons.Find('Hash', hash) != INDEX_NONE) continue;

		item.Hash = hash;
		item.Info = weapn;
		item.ClassName = name(Mid(weapn.ClassName, Instr(weapn.ClassName, ".")+1));

		HashedWeapons.AddItem(item);

		// Ammo
		if (!ClassPathValid(weapn.AmmoClassPath)) continue;
		hash = Locs(weapn.AmmoClassPath);
		if (HashedAmmos.Find('Hash', hash) != INDEX_NONE) continue;

		index = Instr(weapn.AmmoClassPath, ".");
		s1 = Left(weapn.AmmoClassPath, index);
		s2 = Mid(weapn.AmmoClassPath, index+1);

		str = Localize(s2, "PickupMessage", s1);
		FixFriendlyName(str);

		ammo.Hash = hash;
		ammo.Text = str;
		ammo.ClassName = name(s2);

		HashedAmmos.AddItem(ammo);
	}
}

function AddToLocker(class<UTWeapon> WeaponClass)
{
	local UTWeaponLocker Locker;
	local WeaponEntry ent;

	if (WeaponClass == none)
		return;

	foreach DynamicActors(class'UTWeaponLocker', Locker)
	{
		ent.WeaponClass = WeaponClass;
		Locker.MaxDesireability += WeaponClass.Default.AIRating;

		//if (WeaponClass.default.PickupFactoryMesh != none)
		//	ent.PickupMesh = WeaponClass.default.PickupFactoryMesh;

		Locker.Weapons.AddItem(ent);
	}
}

function bool ShouldBeReplaced(out int index, class ClassToCheck, bool bAmmo)
{
	local int i;
	if (bAmmo)
	{
		index = AmmoToReplace.Find('OldClassName', ClassToCheck.Name);
		if (index == INDEX_NONE)
		{
			for (i=0; i<AmmoToReplace.Length; i++)
			{

				if (AmmoToReplace[i].Options.bSubclasses && IsSubClass(ClassToCheck, AmmoToReplace[i].OldClassName))
				{
					index = i;
					return true;
				}
			}
		}
	}
	else
	{
		index = WeaponsToReplace.Find('OldClassName', ClassToCheck.Name);
		if (index == INDEX_NONE)
		{
			for (i=0; i<WeaponsToReplace.Length; i++)
			{
				if (WeaponsToReplace[i].Options.bSubclasses && IsSubClass(ClassToCheck, WeaponsToReplace[i].OldClassName))
				{
					index = i;
					return true;
				}
			}
		}
	}

	return index != INDEX_NONE;
}

//**********************************************************************************
// Helper functions
//**********************************************************************************

function bool IsNewItem(out array<ReplacementInfoEx> items, out int index, name ClassName)
{
	index = items.Find('OldClassName', ClassName);
	return index == INDEX_NONE;
}

private function string GenerateErrorInfo(ErrorMessageInfo err)
{
	local string str;

	if (!ClassPathValid(err.NewClassPath)) // TODO: Check for valid class path and not just for empty string
	{
		str = err.Registrar != none ? MessageClassRemovedBy : MessageClassRemovedDefault;
	}
	else
	{
		str = err.Registrar != none ? MessageClassSubstBy : MessageClassSubstDefault;
		str $= " ";
		str $= err.Conflict != none ? MessageClassReplacedBy : MessageClassReplacedDefault;
	}

	str = Repl(str, "`new", Colorize(GetPickupName(err.NewClassPath, true), DialogNameColor));
	str = Repl(str, "`old", Colorize(GetPickupName(err.ClassName, false), DialogNameColor));
	str = Repl(str, "`already", Colorize(GetObjectFriendlyName(err.Conflict), DialogGoodColor));
	str = Repl(str, "`mutator", Colorize(GetObjectFriendlyName(err.Registrar), DialogBadColor));
	return str;
}

private function string GetPickupName(coerce string Path, bool IsPath)
{
	local int index;
	local string str;
	if (IsPath) index = HashedWeapons.Find('Hash', Locs(Path));
	else index = HashedWeapons.Find('ClassName', name(Path));
	if (index != INDEX_NONE && HashedWeapons[index].Info != none)
	{
		str = HashedWeapons[index].Info.FriendlyName;
	}
	else
	{
		if (IsPath) index = HashedAmmos.Find('Hash', Locs(Path));
		else index = HashedAmmos.Find('ClassName', name(Path));
		if (index != INDEX_NONE)
		{
			str = HashedAmmos[index].Text;
		}
	}

	if (str == "" || Left(str, 1) == "?")
	{
		str = Mid(Path, Instr(Path, ".")+1);
	}

	return str;
}

private function string GetObjectFriendlyName(Object obj, optional string defaultstr)
{
	local int index;
	local string str;

	if (class(Obj) != none)
		str = PathName(Obj);
	else if (Obj != none)
		str = PathName(Obj.Class);

	if (str != "")
	{
		index = HashedMutators.Find('Hash', Locs(str));
		if (index != INDEX_NONE && HashedMutators[index].Info != none)
		{
			str = HashedMutators[index].Info.FriendlyName;
		}
	}

	if (str == "")
	{
		if (Actor(obj) != none)
			str = Actor(obj).GetHumanReadableName();
		if (str == "" && class<Actor>(obj) != none)
			str = class<Actor>(obj).static.GetLocalString();
		else if (obj != none)
			str = string(obj.Name);
	}

	return str != "" ? str : defaultstr;
}

static function bool IsSubClass(class ClassToCheck, name ParentClassName)
{
	local Object Obj;
	// get default object which works for abstract classes
	return GetDefaultObject(ClassToCheck, obj) && obj.IsA(ParentClassName);
}

// NOTE: needed for hackfix
static function bool GetDefaultObject(class<Object> cls, out Object obj)
{
	local string path;

	path = GetDefaultPath(cls);
	obj = FindObject(path, cls);
	return (obj != none);
}

// NOTE: needed for hackfix
static function string GetDefaultPath(class<Object> cls)
{
	return cls.GetPackageName() $".Default__"$ cls.Name;
}

//**********************************************************************************
// Static functions
//**********************************************************************************

static function ShowMessageBox(string message, optional string Title="")
{
	local GameUISceneClient MySceneClient;
	local UIScene OpenedScene;
	MySceneClient = UTGameUISceneClient(class'UIRoot'.static.GetSceneClient());
	if (MySceneClient != none && MySceneClient.OpenScene(class'UTUIScene'.default.MessageBoxScene,, OpenedScene) && UTUIScene_MessageBox(OpenedScene) != none)
	{
		UTUIScene_MessageBox(OpenedScene).Display(message, Title);
	}
}

static function string Colorize(coerce string str, string ColorString)
{
	//<color:/> doesn't work so we need to use another tag with white color
	return "<color:"$ColorString$">"$str$DialogDefaultColor;
}

static function bool ClassPathValid(string classpath)
{
	if (classpath == "" || Len(classpath) > 63 || InStr(classpath, " ") != INDEX_NONE)
		return false;

	return (InStr(classpath, ".") != INDEX_NONE && Right(classpath, 1) != ".");
}

/** Trim the right part of the string */
static simulated function string TrimRight(coerce string sInput, optional coerce string sTrim = " ")
{
	local string sOutput;
	local int l;
	sOutput = sInput;

	l = Len(sTrim);
	while (Right(sOutput, l) == sTrim)
		sOutput = Left(sOutput, Len(sOutput)-1);

	return sOutput;
}

/** Fix names which are uppercase */
static function FixFriendlyName(out string FriendlyName)
{
	local string str;
	local array<String> splitname;
	local int i;

	// check for bad string
	if (FriendlyName != "" && Caps(FriendlyName) == FriendlyName)
	{
		// remove the special char from the end of the string
		str = TrimRight(FriendlyName, "!");

		// parse single words into an array
		ParseStringIntoArray(str, splitname, " ", true);

		// properly format longer words
		for (i=0; i<splitname.Length;i++)
			if (len(splitname[i]) > 2)
				splitname[i] = Left(splitname[i], 1)$Mid(locs(splitname[i]), 1);

		// join split name
		JoinArray(splitname, str, " ");
		FriendlyName = str;
	}
}

private static function string GetMutatorName(Mutator M)
{
	local Object TempObj;
	local UTUIDataProvider_Mutator DP;
	local string Pack;

	Pack = ""$M.class.GetPackageName();

	// Check that the data provider for this mutator exists
	TempObj = new(M.Class.Outer, Pack) Class'Package';
	DP = new(TempObj, string(M.Class)) Class'UTUIDataProvider_Mutator';

	if (DP != none && DP.FriendlyName != "") {
		return DP.FriendlyName;
	} else {
		return ""$M.Class.Name;
	}
}

Defaultproperties
{
	GroupNames[0]="WEAPONMOD"

	DialogNameColor="R=1.0,G=1.0,B=0.5,A=1.0"
	DialogBadColor="R=1.0,G=0.5,B=0.5,A=1"
	DialogGoodColor="R=0.1328125,G=0.69140625,B=0.296875,A=1"

	MessageErrors="Some errors occurred in replacing weapons:    `errors"
	MessageClassReplacedBy="The mutator `already is already replacing `old."
	MessageClassReplacedDefault="`old is already replaced by the default configuration."
	MessageClassSubstBy="`new cannot be added by `mutator."
	MessageClassSubstDefault="`new cannot be added."
	MessageClassRemovedBy="The mutator `already already removed `old."
	MessageClassRemovedDefault="`old is already removed by the default configuration."
}


Update refactor.uc

Update refactor.uc

Handles functions spanning multiple lines with the use of _ (underscore)

# refactor.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/29/2014 6:14:24 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
BackColor: Khaki
Find:
Replace: {@Define: Var: Args As String}

Criteria: 2
Comment: Defines comments as whitespace and adds missing quotes
Pass: 1

Criteria: 3
Find: {@Start}
Replace: {@Define::
            Token: _.*\n  ~~ Properties: ucWhiteSpace
            Token: \x27.* ~~ Properties: ucWhiteSpace
            Token: :      ~~ Properties: ucStatementSep
         }{nl}

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

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

Criteria: 6
Find: Opt
Replace: Optional

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

Criteria: 8
Comment: Misc
Pass: 3

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

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

Criteria: 11
PassOnce: False
Find: {dim: Local|Global|Static}
      [{etc},] {item:1}[{array1: ()}], {last:1}[{array2: ()}] As {type}
Replace: {dim} {etc: {etc},} {item}{array1} As {type}
         {dim} {last}{array2} As {type}

Criteria: 12
Find: Call {function} [To {var:1}]
Replace: {var: {var} = }{function}

# End Search

# refactor.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/28/2014 3:24:55 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
BackColor: Khaki
Find:
Replace: {@Define: Var: Args As String}

Criteria: 2
Comment: Defines comments as whitespace and adds missing quotes
Pass: 1

Criteria: 3
Selected: True
Find: {@Start}
Replace: {@Define::
            Token: _.*\n  ~~ Properties: ucWhiteSpace
            Token: \x27.* ~~ Properties: ucWhiteSpace
            Token: :      ~~ Properties: ucStatementSep
         }{nl}

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

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

Criteria: 6
Find: Opt
Replace: Optional

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

Criteria: 8
Comment: Misc
Pass: 3

Criteria: 9
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: 10
Comment: Adds () As Long to PBMain if missing
Find: Function PBMain [()] [As Long]
Replace: Function PBMain() As Long

Criteria: 11
PassOnce: False
Find: {dim: Local|Global|Static}
      [{etc},] {item:1}[{array1: ()}], {last:1}[{array2: ()}] As {type}
Replace: {dim} {etc: {etc},} {item}{array1} As {type}
         {dim} {last}{array2} As {type}

Criteria: 12
Find: Call {function} [To {var:1}]
Replace: {var: {var} = }{function}

# End Search


Update implicit-dim.uc

Update implicit-dim.uc

Added code to make casing (upper case vs lower case) of words match that of original definitions

# implicit-dim.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/9/2014 10:15:18 AM
# Comment: Declares variables (with Dim) that were not explicitely declared before

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: Declares variables (with Dim) that were not explicitely declared before
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: ImplicitDim.Bas
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find:
Replace: {@Define::
            Token: \x27.* ~~ Properties: ucWhitespace
            Token: _[^\n]*\n ~~ Properties: ucWhitespace
         }
         {@Define:
            Var: Globals As Table
            Var: ImplicitDim As Table
            Var: ExplicitDim As Table
            Var: CurrentRoutine As String
            Var: ExpIndex As Long
            Var: ImpIndex As Long
            Var: GlobalIndex As Long
         }

Criteria: 2
Comment: Teporarily adds Dim keyword in front of args for further parsing
Pass: 1

Criteria: 3
Find: {nl}{routine: Sub | Function } {etc}({args})
Replace: {nl}{routine} {etc}(Dim {args})

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

Criteria: 5
Comment: Inserts explicitly Dimmed variable names in global or local tables
Pass: 2

Criteria: 6
Find: Global{" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(Globals, "{variable}")}

Criteria: 7
BackColor: Silver
Find: {nl}{ Macro | Type | Union | % | $[$] | Declare {func:1} }  {name:1}
Replace: {Self}{@Eval: Insert(Globals, "{name}")}

Criteria: 8
BackColor: Lime
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Define:
            Var: {RoutineName}ExplicitDim As Table
            Var: {RoutineName}ImplicitDim As Table
         }{@Eval: SetVar(CurrentRoutine, "{RoutineName}"); Insert(Globals,"{RoutineName}")}

Criteria: 9
Find: {nl}End { Sub | Function }
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "")}

Criteria: 10
BackColor: Pink
Find: {declare: Dim [[Optional] { ByVal | ByRef }] | Local | Static | Register | fstream }
      {" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(~Eval(CurrentRoutine)ExplicitDim, "{variable}")}

Criteria: 11
Comment: Temporarily inserts declaration for each individual variable a lines with a list of multiple vars, for easier parsing
BackColor: SlateBlue
PassOnce: False
Find: {declare: Global|Dim|Local|Static|Register} {variable},
Replace: {declare} {variable} ::: {declare}

Criteria: 12
Comment: Places non-Dimmed variable names in separate local tables
Pass: 3

Criteria: 13
Selected: True
Find: {variable:"[a-z][a-z0-9_]*[#!?%&]*"}
      [{IsFunc:" *\("}]
Replace: {@Eval:
            ExpIndex    = Index(~Eval(CurrentRoutine)ExplicitDim, "{variable}")
            ImpIndex    = Index(~Eval(CurrentRoutine)ImplicitDim, "{variable}")
            GlobalIndex = Index(Globals, "{variable}")
            IIf(ExpIndex+ImpIndex+GlobalIndex == 0 And "{IsFunc}" == "",
                Insert(~Eval(CurrentRoutine)ImplicitDim, "{variable}"))

            text = "{variable}"
            IIf(GlobalIndex, text = ReadKey(Globals, GlobalIndex))
            IIf(ExpIndex, text = ReadKey(~Eval(CurrentRoutine)ExplicitDim, ExpIndex))
            IIf(ImpIndex, text = ReadKey(~Eval(CurrentRoutine)ImplicitDim, ImpIndex))
            text + "{IsFunc}"
         }

Criteria: 14
SkipOver: True
Find: {"[a-z0-9_]+"}  {UDT:"\.[a-z0-9_\@\.]+"}
Replace: [Skip over]

Criteria: 15
SkipOver: True
Find: {"&h[0-9a-f]+"}
Replace: [Skip over]

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

Criteria: 17
BackColor: Brown
Find: {"\n"}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "{RoutineName}")}

Criteria: 18
Find: {"\n"}End { Sub | Function }
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "")}

Criteria: 19
Comment: Inserts local Dim statements for variables that were not dimmed
Pass: 4

Criteria: 20
BackColor: Purple
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"} {etc}
Replace: {Self}
         {@Eval:
            Range(1, Count({RoutineName}ImplicitDim),
            {Q}"   Dim "+ReadKey({RoutineName}ImplicitDim, x)+" ' Implicit"+Chr(10){Q})
         }

Criteria: 21
Comment: Clean up (temp declaration statements removed)
Pass: 5

Criteria: 22
BackColor: Violet
Find: ::: {declare:1}
Replace: ,

Criteria: 23
BackColor: CornflowerBlue
Find: (Dim {args})
Replace: ({@Eval: Replace("{args}", "::: Dim", ",")})

Criteria: 24
Find: Dim {nl}
Replace: {Nothing}

# End Search

# implicit-dim.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 5/1/2014 7:29:08 PM
# Comment: Declares variables (with Dim) that were not explicitely declared before

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: Declares variables (with Dim) that were not explicitely declared before
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: ImplicitDim.Bas
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find:
Replace: {@Define::
            Token: \x27.* ~~ Properties: ucWhitespace
            Token: _[^\n]*\n ~~ Properties: ucWhitespace
         }
         {@Define:
            Var: Globals As Table
            Var: ImplicitDim As Table
            Var: ExplicitDim As Table
            Var: CurrentRoutine As String
         }

Criteria: 2
Comment: Teporarily adds Dim keyword in front of args for further parsing
Pass: 1

Criteria: 3
Find: {nl}{routine: Sub | Function } {etc}({args})
Replace: {nl}{routine} {etc}(Dim {args})

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

Criteria: 5
Comment: Inserts explicitly Dimmed variable names in global or local tables
Pass: 2

Criteria: 6
Find: Global{" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(Globals, "{variable}")}

Criteria: 7
Selected: True
BackColor: Silver
Find: {nl}{ Macro | Type | Union | % | $[$] | Declare {func:1} }  {name:1}
Replace: {Self}{@Eval: Insert(Globals, "{name}")}

Criteria: 8
BackColor: Lime
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Define:
            Var: {RoutineName}ExplicitDim As Table
            Var: {RoutineName}ImplicitDim As Table
         }{@Eval: SetVar(CurrentRoutine, "{RoutineName}"); Insert(Globals,"{RoutineName}")}

Criteria: 9
Find: {nl}End { Sub | Function }
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "")}

Criteria: 10
BackColor: Pink
Find: {declare: Dim [[Optional] { ByVal | ByRef }] | Local | Static | Register | fstream }
      {" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(~Eval(CurrentRoutine)ExplicitDim, "{variable}")}

Criteria: 11
Comment: Temporarily inserts declaration for each individual variable a lines with a list of multiple vars, for easier parsing
BackColor: SlateBlue
PassOnce: False
Find: {declare: Global|Dim|Local|Static|Register} {variable},
Replace: {declare} {variable} ::: {declare}

Criteria: 12
Comment: Places non-Dimmed variable names in separate local tables
Pass: 3

Criteria: 13
Find: {variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval:
            IIf(Handle(Globals, "{variable}")==0 And Handle(~Eval(CurrentRoutine)ExplicitDim, "{variable}")==0,
                Insert(~Eval(CurrentRoutine)ImplicitDim, "{variable}"); "")
         }

Criteria: 14
SkipOver: True
Find: {"[a-z0-9_]+"} { {UDT:"\.[a-z0-9_\@\.]+"} | {Func:" *\("} }
Replace: [Skip over]

Criteria: 15
SkipOver: True
Find: {"&h[0-9a-f]+"}
Replace: [Skip over]

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

Criteria: 17
BackColor: Brown
Find: {"\n"}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "{RoutineName}")}

Criteria: 18
Find: {"\n"}End { Sub | Function }
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "")}

Criteria: 19
Comment: Inserts local Dim statements for variables that were not dimmed
Pass: 4

Criteria: 20
BackColor: Purple
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"} {etc}
Replace: {Self}
         {@Eval:
            text = ""
            uc_For(x, 1, Count({RoutineName}ImplicitDim), 1,
               text += "   Dim "
               text += ReadKey({RoutineName}ImplicitDim, x)
               text += " ' Implicit"+Chr(10)
            );
            text
         }

Criteria: 21
Comment: Clean up (temp declaration statements removed)
Pass: 5

Criteria: 22
BackColor: Violet
Find: ::: {declare:1}
Replace: ,

Criteria: 23
BackColor: CornflowerBlue
Find: (Dim {args})
Replace: ({@Eval: Replace("{args}", "::: Dim", ",")})

Criteria: 24
Find: Dim {nl}
Replace: {Nothing}

# End Search


Update implicit-dim.uc

Update implicit-dim.uc

A function prototype section was added

# implicit-dim.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/14/2014 9:09:03 AM
# Comment: Declares variables (with Dim) that were not explicitely declared before

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: Declares variables (with Dim) that were not explicitely declared before
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: ImplicitDim.Bas
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:

         * Declares variables that were not previously declared explicitely
         * Comments out user declarations that are from the Windows API
         * Ensures that the casing of variables/functions is consistent
           with their declarations since C++ is case-sensitive

      }

Replace: {@Define::
            Token: \x27.* ~~ Properties: ucWhitespace
            Token: _[^\n]*\n ~~ Properties: ucWhitespace
         }
         {@Define:
            Var: Globals As Table
            Var: ImplicitDim As Table
            Var: ExplicitDim As Table
            Var: CurrentRoutine As String
            Var: Prototype As String
            Var: Prototypes As String
            Var: ExpIndex As Long
            Var: ImpIndex As Long
            Var: GlobalIndex As Long
         }
         {@Exec:
            Split(File("WinAPIKeywords.txt"))
            uc_For(x, 1, StringItemCount, 2, Insert(Globals, StringItem(x)))
         }

Criteria: 2
Comment: Teporarily adds Dim keyword in front of args for further parsing
Pass: 1

Criteria: 3
Selected: True
Find: {nl}{routine: Sub | Function } {etc}([{args}]) [[{exp: Export}] As {type:1}]
Replace: {@Eval:
            Prototype = {Q}{routine} {etc}({args:Dim {args}}){type: {exp} As {type}}{Q}
            Prototypes += "{nl}Declare " + Prototype
            "{nl}" + Prototype
         }

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

Criteria: 5
Comment: Inserts explicitly Dimmed variable names in global or local tables
Pass: 2

Criteria: 6
PassOnce: False
Find: {@Start}
Replace: ' Prototypes
         {@Eval: Prototypes}
         ' End of prototypes{nl}

Criteria: 7
Find: Global{" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Evaluate: Insert(Globals, {variable})}

Criteria: 8
BackColor: Silver
Find: {nl}{ Macro | Type | Union | % | $[$] | Declare {func:1} }  {name:1}
Replace: {Self}{@Evaluate: Insert(Globals, {name})}

Criteria: 9
BackColor: Lime
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Define:
            Var: {RoutineName}ExplicitDim As Table
            Var: {RoutineName}ImplicitDim As Table
         }{@Eval: SetVar(CurrentRoutine, "{RoutineName}"); Insert(Globals,"{RoutineName}")}

Criteria: 10
Find: {nl}End { Sub | Function }
Replace: {Self}{@Evaluate: SetVar(CurrentRoutine, "")}

Criteria: 11
BackColor: Pink
Find: {declare: Dim [[Optional] { ByVal | ByRef }] | Local | Static | Register | fstream }
      {" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(~Eval(CurrentRoutine)ExplicitDim, "{variable}")}

Criteria: 12
Comment: Temporarily inserts declaration for each individual variable a lines with a list of multiple vars, for easier parsing
BackColor: SlateBlue
PassOnce: False
Find: {declare: Global|Dim|Local|Static|Register} {variable},
Replace: {declare} {variable} ::: {declare}

Criteria: 13
Find: {nl}%{equate:1} = {@If: Index(Globals, {equate})}
Replace: {nl} ' <WinAPI> {equate} =

Criteria: 14
Find: {nl}Declare {method: Sub | Function } {name:1}  {@If: Index(Globals, {name})}
Replace: {nl} ' <WinAPI> Declare {method} {name}

Criteria: 15
Comment: Places non-Dimmed variable names in separate local tables
Pass: 3

Criteria: 16
Find: [{eq: %}]{name:"[a-z][a-z0-9_]*[#!?%&]*"} [{IsFunc:" *\("}]
Replace: {eq}{@Eval:
            ExpIndex    = Index(~Eval(CurrentRoutine)ExplicitDim, "{name}")
            ImpIndex    = Index(~Eval(CurrentRoutine)ImplicitDim, "{name}")
            GlobalIndex = Index(Globals, "{name}")
            IIf(ExpIndex+ImpIndex+GlobalIndex == 0 And "{IsFunc}" == "",
                Insert(~Eval(CurrentRoutine)ImplicitDim, "{name}"))

            text = "{name}"
            IIf(GlobalIndex, text = ReadKey(Globals, GlobalIndex))
            IIf(ExpIndex, text = ReadKey(~Eval(CurrentRoutine)ExplicitDim, ExpIndex))
            IIf(ImpIndex, text = ReadKey(~Eval(CurrentRoutine)ImplicitDim, ImpIndex))
            text + "{IsFunc}"
         }

Criteria: 17
SkipOver: True
Find: {"[a-z0-9_]+"}  {UDT:"\.[a-z0-9_\@\.]+"}
Replace: [Skip over]

Criteria: 18
SkipOver: True
Find: {"&h[0-9a-f]+"}
Replace: [Skip over]

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

Criteria: 20
BackColor: Brown
Find: {"\n"}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Evaluate: SetVar(CurrentRoutine, {RoutineName})}

Criteria: 21
Find: {"\n"}End { Sub | Function }
Replace: {Self}{@Evaluate: SetVar(CurrentRoutine, "")}

Criteria: 22
Comment: Inserts local Dim statements for variables that were not dimmed
Pass: 4

Criteria: 23
BackColor: Purple
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"} {etc}
Replace: {Self}
         {@Eval:
            Range(1, Count({RoutineName}ImplicitDim),
            {Q}"   Dim "+ReadKey({RoutineName}ImplicitDim, x)+" ' Implicit"+Chr(10){Q})
         }

Criteria: 24
Comment: Clean up (temp declaration statements removed)
Pass: 5

Criteria: 25
BackColor: Violet
Find: ::: {declare:1}
Replace: ,

Criteria: 26
BackColor: CornflowerBlue
Find: (Dim {args})
Replace: ({@Eval: Replace("{args}", "::: Dim", ",")})

Criteria: 27
Find: Dim {nl}
Replace: {Nothing}

# End Search

# implicit-dim.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/12/2014 10:33:51 AM
# Comment: Declares variables (with Dim) that were not explicitely declared before

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: Declares variables (with Dim) that were not explicitely declared before
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: ImplicitDim.Bas
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find: {@Note:
         In addition to declaring variables that were not previously declared
         explicitely, this transform also comments out user declarations that
         are from the Windows API, since Windows.h already takes care of it,
         and the PB mixed casing of an API equate might not correspond with
         the exact API case-sensitive C++ orthograph.
      }

Replace: {@Define::
            Token: \x27.* ~~ Properties: ucWhitespace
            Token: _[^\n]*\n ~~ Properties: ucWhitespace
         }
         {@Define:
            Var: Globals As Table
            Var: ImplicitDim As Table
            Var: ExplicitDim As Table
            Var: CurrentRoutine As String
            Var: ExpIndex As Long
            Var: ImpIndex As Long
            Var: GlobalIndex As Long
         }
         {@Exec:
            Split(File("WinAPIKeywords.txt"))
            uc_For(x, 1, StringItemCount, 2, Insert(Globals, StringItem(x)))
         }

Criteria: 2
Comment: Teporarily adds Dim keyword in front of args for further parsing
Pass: 1

Criteria: 3
Find: {nl}{routine: Sub | Function } {etc}({args})
Replace: {nl}{routine} {etc}(Dim {args})

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

Criteria: 5
Comment: Inserts explicitly Dimmed variable names in global or local tables
Pass: 2

Criteria: 6
Find: Global{" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(Globals, "{variable}")}

Criteria: 7
BackColor: Silver
Find: {nl}{ Macro | Type | Union | % | $[$] | Declare {func:1} }  {name:1}
Replace: {Self}{@Eval: Insert(Globals, "{name}")}

Criteria: 8
BackColor: Lime
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Define:
            Var: {RoutineName}ExplicitDim As Table
            Var: {RoutineName}ImplicitDim As Table
         }{@Eval: SetVar(CurrentRoutine, "{RoutineName}"); Insert(Globals,"{RoutineName}")}

Criteria: 9
Find: {nl}End { Sub | Function }
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "")}

Criteria: 10
BackColor: Pink
Find: {declare: Dim [[Optional] { ByVal | ByRef }] | Local | Static | Register | fstream }
      {" +"}{variable:"[a-z][a-z0-9_]*[#!?%&]*"}
Replace: {Self}{@Eval: Insert(~Eval(CurrentRoutine)ExplicitDim, "{variable}")}

Criteria: 11
Comment: Temporarily inserts declaration for each individual variable a lines with a list of multiple vars, for easier parsing
BackColor: SlateBlue
PassOnce: False
Find: {declare: Global|Dim|Local|Static|Register} {variable},
Replace: {declare} {variable} ::: {declare}

Criteria: 12
Selected: True
Find: {nl}%{equate:1} = {@If: Index(Globals, {equate})}
Replace: {nl} ' <WinAPI> {equate} =

Criteria: 13
Find: {nl}Declare {method: Sub | Function } {name:1}  {@If: Index(Globals, {name})}
Replace: {nl} ' <WinAPI> Declare {method} {name}

Criteria: 14
Comment: Places non-Dimmed variable names in separate local tables
Pass: 3

Criteria: 15
Find: [{eq: %}]{name:"[a-z][a-z0-9_]*[#!?%&]*"} [{IsFunc:" *\("}]
Replace: {eq}{@Eval:
            ExpIndex    = Index(~Eval(CurrentRoutine)ExplicitDim, "{name}")
            ImpIndex    = Index(~Eval(CurrentRoutine)ImplicitDim, "{name}")
            GlobalIndex = Index(Globals, "{name}")
            IIf(ExpIndex+ImpIndex+GlobalIndex == 0 And "{IsFunc}" == "",
                Insert(~Eval(CurrentRoutine)ImplicitDim, "{name}"))

            text = "{name}"
            IIf(GlobalIndex, text = ReadKey(Globals, GlobalIndex))
            IIf(ExpIndex, text = ReadKey(~Eval(CurrentRoutine)ExplicitDim, ExpIndex))
            IIf(ImpIndex, text = ReadKey(~Eval(CurrentRoutine)ImplicitDim, ImpIndex))
            text + "{IsFunc}"
         }

Criteria: 16
SkipOver: True
Find: {"[a-z0-9_]+"}  {UDT:"\.[a-z0-9_\@\.]+"}
Replace: [Skip over]

Criteria: 17
SkipOver: True
Find: {"&h[0-9a-f]+"}
Replace: [Skip over]

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

Criteria: 19
BackColor: Brown
Find: {"\n"}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"}
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "{RoutineName}")}

Criteria: 20
Find: {"\n"}End { Sub | Function }
Replace: {Self}{@Eval: SetVar(CurrentRoutine, "")}

Criteria: 21
Comment: Inserts local Dim statements for variables that were not dimmed
Pass: 4

Criteria: 22
BackColor: Purple
Find: {nl}{ Sub | Function }{" +"}{RoutineName:"[a-z0-9_]+"} {etc}
Replace: {Self}
         {@Eval:
            Range(1, Count({RoutineName}ImplicitDim),
            {Q}"   Dim "+ReadKey({RoutineName}ImplicitDim, x)+" ' Implicit"+Chr(10){Q})
         }

Criteria: 23
Comment: Clean up (temp declaration statements removed)
Pass: 5

Criteria: 24
BackColor: Violet
Find: ::: {declare:1}
Replace: ,

Criteria: 25
BackColor: CornflowerBlue
Find: (Dim {args})
Replace: ({@Eval: Replace("{args}", "::: Dim", ",")})

Criteria: 26
Find: Dim {nl}
Replace: {Nothing}

# End Search


Update filehandler.uc

Update filehandler.uc

# filehandler.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/16/2014 11:49:59 AM
# Comment: File handler

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: File handler
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: {@Var: FreeFileNum}
         {@Token:: : ~~ Properties: ucStatementSep }

Criteria: 2
Comment:
Pass: 1

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

Criteria: 4
BackColor: DeepSkyBlue
Find: Open {filespec}
      { For {{Input: Input}|{Output: Output}|{Append: Append}|{Binary: Binary}|{Random: Random}}|{Default: } }
      { Access Read | Access Write | Access Read Write | }
      { Lock {{LockRead: Read}|{LockWrite: Write}|{LockBoth: Read Write}|{LockShared: Shared}}|{LockDefault: } }
      As [#]{filenum} [Len = {size=128}]
Replace: fstream file_{filenum} (string({filespec}).c_str(),
            {Input:   ios::in}
            {Output:  ios::out | ios::trunc}
            {Append:  ios::out | ios::app}
            {Binary:  ios::binary | ios::in | ios::out}
            {Random:  ios::binary | ios::in | ios::out}
            {Default: ios::binary | ios::in | ios::out}
         )

Criteria: 5
BackColor: Lime
Find: Close [#]{filenum}
Replace: file_{filenum}.close() ' PB: Close #{filenum}

Criteria: 6
BackColor: RoyalBlue
PassOnce: False
Find: Close [#]{file1}, {more}
Replace: Close {file1}
         Close {more}

Criteria: 7
BackColor: Pink
Find: Seek [#] {filenum}, {position}
Replace: file_{filenum}.seekg({position}-1)
         file_{filenum}.seekp({position}-1)

Criteria: 8
Find: Seek([#] {filenum})
Replace: (int)file_{filenum}.tellg()+1

Criteria: 9
BackColor: SlateBlue
Find: LOF([#] {filenum})
Replace: PB_LOF(file_{filenum})

Criteria: 10
Find: EOF([#] {filenum})
Replace: file_{filenum}.eof()

Criteria: 11
Find: FreeFile
Replace: {@Eval: FreeFileNum += 1; FreeFileNum}

Criteria: 12
BackColor: Green
Find: Get$ [#] {filenum}, {count}, {StringVar}
Replace: {StringVar}.resize({count})
         file_{filenum}.read(&{StringVar}[0], {count})

Criteria: 13
BackColor: SandyBrown
Find: Put$ [#] {filenum}, {text}
Replace: file_{filenum}.write(&{text}[0], {text}.size())

Criteria: 14
Find: IsFile({file})
Replace: PB_ISFILE({file})

Criteria: 15
Find: Name {oldname} As {newname}
Replace: PB_NAME({oldname}, {newname})

Criteria: 16
BackColor: Gold
Find: EXE.Extn[$]
Replace: PB_EXE_EXTN

Criteria: 17
Selected: True
BackColor: Silver
Find: EXE.Full[$]
Replace: PB_EXE_FULL()

Criteria: 18
Find: EXE.Name[$]
Replace: PB_EXE_NAME

Criteria: 19
BackColor: CornflowerBlue
Find: EXE.Namex[$]
Replace: PB_EXE_NAMEX

Criteria: 20
Find: EXE.Path[$]
Replace: PB_EXE_PATH

Criteria: 21
Find: Dir$({mask})
Replace: PB_DIR({mask})

Criteria: 22
Find: Dir$([Next])
Replace: PB_DIR_NEXT()

Criteria: 23
Find: Print #{filenum}
Replace: file_{filenum} << endl

Criteria: 24
Find: Print #{filenum}, {text}
Replace: file_{filenum} << {text} << endl

Criteria: 25
Find: Print #{filenum}, {text};
Replace: file_{filenum} << {text}

Criteria: 26
PassOnce: False
Find: Print #{filenum}, {text}; {MoreText}
Replace: Print #{filenum}, {text} << {MoreText}

Criteria: 27
Comment:
Pass: 2

Criteria: 28
BackColor: Yellow
Find: fstream {file} ({args+})
Replace: {@Evaluate: Replace({@Self}, '{"[ \r\n]+"}', ' ')}

# End Search

# filehandler.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/13/2014 7:53:23 AM
# Comment: File handler

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: File handler
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: {@Var: FreeFileNum}
         {@Token:: : ~~ Properties: ucStatementSep }

Criteria: 2
Comment:
Pass: 1

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

Criteria: 4
Selected: True
BackColor: DeepSkyBlue
Find: Open {filespec}
      { For {{Input: Input}|{Output: Output}|{Append: Append}|{Binary: Binary}|{Random: Random}}|{Default: } }
      { Access Read | Access Write | Access Read Write | }
      { Lock {{LockRead: Read}|{LockWrite: Write}|{LockBoth: Read Write}|{LockShared: Shared}}|{LockDefault: } }
      As [#]{filenum} [Len = {size=128}]
Replace: fstream file_{filenum} (string({filespec}).c_str(),
            {Input:   ios::in}
            {Output:  ios::out | ios::trunc}
            {Append:  ios::out | ios::app}
            {Binary:  ios::binary | ios::in | ios::out}
            {Random:  ios::binary | ios::in | ios::out}
            {Default: ios::binary | ios::in | ios::out}
         )

Criteria: 5
BackColor: Lime
Find: Close [#]{filenum}
Replace: file_{filenum}.close() ' PB: Close #{filenum}

Criteria: 6
BackColor: RoyalBlue
PassOnce: False
Find: Close [#]{file1}, {more}
Replace: Close {file1}
         Close {more}

Criteria: 7
BackColor: Pink
Find: Seek [#] {filenum}, {position}
Replace: file_{filenum}.seekg({position}-1)
         file_{filenum}.seekp({position}-1)

Criteria: 8
Find: Seek([#] {filenum})
Replace: (int)file_{filenum}.tellg()+1

Criteria: 9
BackColor: SlateBlue
Find: LOF([#] {filenum})
Replace: PB_LOF(file_{filenum})

Criteria: 10
Find: EOF([#] {filenum})
Replace: file_{filenum}.eof()

Criteria: 11
Find: FreeFile
Replace: {@Eval: FreeFileNum += 1; FreeFileNum}

Criteria: 12
BackColor: Green
Find: Get$ [#] {filenum}, {count}, {StringVar}
Replace: {StringVar}.resize({count})
         file_{filenum}.read(&{StringVar}[0], {count})

Criteria: 13
BackColor: SandyBrown
Find: Put$ [#] {filenum}, {text}
Replace: file_{filenum}.write(&{text}[0], {text}.size())

Criteria: 14
Find: IsFile({file})
Replace: PB_ISFILE({file})

Criteria: 15
Find: Name {oldname} As {newname}
Replace: PB_NAME({oldname}, {newname})

Criteria: 16
BackColor: Gold
Find: EXE.Extn$
Replace: PB_EXE_EXTN

Criteria: 17
BackColor: Silver
Find: EXE.Full$
Replace: PB_EXE_FULL()

Criteria: 18
Find: EXE.Name$
Replace: PB_EXE_NAME

Criteria: 19
BackColor: CornflowerBlue
Find: EXE.Namex$
Replace: PB_EXE_NAMEX

Criteria: 20
Find: EXE.Path$
Replace: PB_EXE_PATH

Criteria: 21
Find: Dir$({mask})
Replace: PB_DIR({mask})

Criteria: 22
Find: Dir$([Next])
Replace: PB_DIR_NEXT()

Criteria: 23
Find: Print #{filenum}
Replace: file_{filenum} << endl

Criteria: 24
Find: Print #{filenum}, {text}
Replace: file_{filenum} << {text} << endl

Criteria: 25
Find: Print #{filenum}, {text};
Replace: file_{filenum} << {text}

Criteria: 26
PassOnce: False
Find: Print #{filenum}, {text}; {MoreText}
Replace: Print #{filenum}, {text} << {MoreText}

Criteria: 27
Comment:
Pass: 2

Criteria: 28
BackColor: Yellow
Find: fstream {file} ({args+})
Replace: {@Evaluate: Replace({@Self}, '{"[ \r\n]+"}', ' ')}

# End Search


Update math.uc

Update math.uc

Improved syntax for integer conversion

# math.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/13/2014 9:34:53 AM
# Comment: Converts math-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 math-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: {@Var: Type As String}
         {@Note: rand & srand are in <cstdlib>, the others in <cmath>}
         {@Note: ToDo: \ eqv imp bin }

Criteria: 2
Comment: ^ (power)
Pass: 1

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

Criteria: 4
BackColor: Gold
Precedence: 100
Find: {x} ^ {y}  {@Note: precedence property set to 100}
Replace: pow({x}, {y})

Criteria: 5
Comment: Bitwise AND and OR
Pass: 2

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

Criteria: 7
Comment: Bitwise AND and OR
BackColor: YellowGreen
Find: ({expr})
Replace: ({@Evaluate: Replace(Replace({expr}, "And", "&"), "Or", "|")})

Criteria: 8
Comment: Everything else
Pass: 3

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

Criteria: 10
Find: Mod
Replace: %

Criteria: 11
BackColor: Yellow
Find: Atn
Replace: atan

Criteria: 12
BackColor: Lime
Find: Sqr
Replace: sqrt

Criteria: 13
BackColor: Pink
Find: Cint
Replace: Integer

Criteria: 14
Selected: True
Find: Int(
Replace: __int64(

Criteria: 15
BackColor: Red
Find: Incr {var:1}
Replace: {var}++

Criteria: 16
BackColor: RoyalBlue
Find: Decr {var:1}
Replace: {var}--

Criteria: 17
Comment: Logical (short-circuit) AND
Find: And
Replace: &&

Criteria: 18
Comment: Logical (short-circuit) OR
BackColor: Purple
Find: Or
Replace: ||

Criteria: 19
BackColor: Khaki
Find: Xor
Replace: ^

Criteria: 20
BackColor: Green
Find: Not
Replace: ~

Criteria: 21
SkipOver: True
Find: #{metastatement:1} Not
Replace: [Skip over]

Criteria: 22
BackColor: Silver
Find: IsTrue {x%}  [{stop-: And | Or | Then | : }]
Replace: (({x}) != 0)

Criteria: 23
BackColor: SandyBrown
Find: IsFalse {x%} [{stop-: And | Or | Then | : }]
Replace: !({x})

Criteria: 24
BackColor: Violet
Find: <>
Replace: !=

Criteria: 25
Find: Randomize {number%}
Replace: srand({number})

Criteria: 26
Find: Rnd[()]
Replace: (rand() / RAND_MAX)

Criteria: 27
Find: Rnd({a%}, {b%})
Replace: (rand() % ({b}) + ({a}))

Criteria: 28
Find: Shift Right {ivar}, {countexpr%}
Replace: {ivar} = ({ivar} >> {countexpr})

Criteria: 29
Find: Shift Left {ivar}, {countexpr%}
Replace: {ivar} = ({ivar} << {countexpr})

Criteria: 30
Find: {func: Abs|Sin|Cos|Tan|Exp|Exp2|Log|Log2|Log10|Ceil}
Replace: {@Evaluate: LCase("{func}", "{'.*'}")}

Criteria: 31
Find: {func: Exp10 | Fix | Frac | Sgn}
Replace: PB_{@Evaluate: UCase("{func}", "{'.*'}")}

Criteria: 32
PassOnce: False
Find: {func: Min | Max}[{ {int: &} | {str: $} | {dbl: } }]
      ({args})
Replace: PB_{@Evaluate:
            Type = "(double)"
            IIf("{int}" <> "", Type = "(int)")
            IIf("{str}" <> "", Type = "(string)")
            UCase("{func}", "{'.*'}")
         }{int:_INT}{str:_STR}(ARGCOUNT({@Evaluate:
         Tally({args}, ",", Skip("({nest})"))+1}), {@Evaluate:
         Type + Replace({args}, ",", ", "+Type, Skip("({nest})"))})

# End Search

# math.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.96 on 5/6/2014 3:05:35 PM
# Comment: Converts math-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 math-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: {@Var: Type As String}
         {@Note: rand & srand are in <cstdlib>, the others in <cmath>}
         {@Note: ToDo: \ eqv imp bin }

Criteria: 2
Comment: ^ (power)
Pass: 1

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

Criteria: 4
BackColor: Gold
Precedence: 100
Find: {x} ^ {y}  {@Note: precedence property set to 100}
Replace: pow({x}, {y})

Criteria: 5
Comment: Bitwise AND and OR
Pass: 2

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

Criteria: 7
Comment: Bitwise AND and OR
BackColor: YellowGreen
Find: ({expr})
Replace: ({@Evaluate: Replace(Replace({expr}, "And", "&"), "Or", "|")})

Criteria: 8
Comment: Everything else
Pass: 3

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

Criteria: 10
Find: Mod
Replace: %

Criteria: 11
BackColor: Yellow
Find: Atn
Replace: atan

Criteria: 12
BackColor: Lime
Find: Sqr
Replace: sqrt

Criteria: 13
BackColor: Pink
Find: Cint
Replace: (__int16)

Criteria: 14
Find: Int({arg%})
Replace: (__int64)({arg})

Criteria: 15
BackColor: Red
Find: Incr {var:1}
Replace: {var}++

Criteria: 16
BackColor: RoyalBlue
Find: Decr {var:1}
Replace: {var}--

Criteria: 17
Comment: Logical (short-circuit) AND
Find: And
Replace: &&

Criteria: 18
Comment: Logical (short-circuit) OR
BackColor: Purple
Find: Or
Replace: ||

Criteria: 19
BackColor: Khaki
Find: Xor
Replace: ^

Criteria: 20
BackColor: Green
Find: Not
Replace: ~

Criteria: 21
SkipOver: True
Find: #{metastatement:1} Not
Replace: [Skip over]

Criteria: 22
Selected: True
BackColor: Silver
Find: IsTrue {x%}  [{stop-: And | Or | Then | : }]
Replace: (({x}) != 0)

Criteria: 23
BackColor: SandyBrown
Find: IsFalse {x%} [{stop-: And | Or | Then | : }]
Replace: !({x})

Criteria: 24
BackColor: Violet
Find: <>
Replace: !=

Criteria: 25
Find: Randomize {number%}
Replace: srand({number})

Criteria: 26
Find: Rnd[()]
Replace: (rand() / RAND_MAX)

Criteria: 27
Find: Rnd({a%}, {b%})
Replace: (rand() % ({b}) + ({a}))

Criteria: 28
Find: Shift Right {ivar}, {countexpr%}
Replace: {ivar} = ({ivar} >> {countexpr})

Criteria: 29
Find: Shift Left {ivar}, {countexpr%}
Replace: {ivar} = ({ivar} << {countexpr})

Criteria: 30
Find: {func: Abs|Sin|Cos|Tan|Exp|Exp2|Log|Log2|Log10|Ceil}
Replace: {@Evaluate: LCase("{func}", "{'.*'}")}

Criteria: 31
Find: {func: Exp10 | Fix | Frac | Sgn}
Replace: PB_{@Evaluate: UCase("{func}", "{'.*'}")}

Criteria: 32
PassOnce: False
Find: {func: Min | Max}[{ {int: &} | {str: $} | {dbl: } }]
      ({args})
Replace: PB_{@Evaluate:
            Type = "(double)"
            IIf("{int}" <> "", Type = "(int)")
            IIf("{str}" <> "", Type = "(string)")
            UCase("{func}", "{'.*'}")
         }{int:_INT}{str:_STR}(ARGCOUNT({@Evaluate:
         Tally({args}, ",", Skip("({nest})"))+1}), {@Evaluate:
         Type + Replace({args}, ",", ", "+Type, Skip("({nest})"))})

# End Search


- fixed distance display (removed Z component)

- show color indicator for dodge delay and double jump window

class TachometerInteraction extends Interaction config (MutatH0r);

// defined at initialization
var PlayerController PC;
var CRZMutator_Tachometer Owner;

// static
var Font MessageFont;
var Color TextColor, TextColorGreen, TextColorRed, BackgroundColor, VeloColor;

// instance
var config bool Disabled;
var vector LastGroundPos;
var bool isJumping;
var float jumpDistance;

static function TachometerInteraction Create(CRZMutator_Tachometer theOwner, PlayerController controller, optional bool bReturnExisting=true)
{
  local TachometerInteraction newInt;
  local int i;

  if (bReturnExisting)
  {
    for (i=0; i<controller.Interactions.length; i++)
    {
      if (controller.Interactions[i].class == default.class)
        return TachometerInteraction(controller.Interactions[i]);
    }
  }

  newInt = new(LocalPlayer(controller.Player).ViewportClient) default.class;
  LocalPlayer(controller.Player).ViewportClient.InsertInteraction(newInt, 0);
  controller.Interactions.AddItem(newInt);
  newInt.Owner = theOwner;
  return newInt;
}

function Initialized()
{
  super.Initialized();
  PC = GameViewportClient(Outer).GetPlayerOwner(0).Actor;
}

exec function ShowTacho(bool show)
{
  self.Disabled = !show;
  self.SaveConfig();
}

event PostRender(Canvas canvas)
{
  local CRZHud wrapper;

  super.PostRender(canvas);

  if (Disabled)
    return;
  if (Owner == None) // actor destroyed when match is over
    return;
  wrapper = CRZHud(PC.myHUD);
  if (wrapper == None || wrapper.CurrentHudMode == HM_Intro)
    return;

  RenderTickRate(canvas);
}

function RenderTickRate(Canvas canvas)
{
  local CRZPawn p;
  local CRZPlayerInput inp;
  local float x,y;
  local vector v;
  local string text;
  local bool canDoubleJump;

  p = CRZPawn(PC.Pawn);
  if (p == none)
    return;

  inp = CRZPlayerInput(PC.PlayerInput);

  if (!isJumping && p.Physics == PHYS_Walking)
    LastGroundPos = p.Location;
  else if (pc.Pawn.Physics == PHYS_Falling)
    isJumping = true;
  else if (isJumping && p.Physics == PHYS_Walking)
  {
    isJumping = false;
    LastGroundPos.Z = 0;
    v = p.Location;
    v.Z = 0;
    jumpDistance = VSize(v - LastGroundPos);
  }

  v.X = p.Velocity.X;
  v.Y = p.Velocity.Y;
  v.Z = 0;

  // draw box
  x = canvas.ClipX * 0.45;
  y = canvas.ClipY * 0.45;
  canvas.DrawColor = BackgroundColor;
  canvas.SetPos(x, y);
  canvas.DrawRect(95, 79);

  // draw H
  text = "H: " $ string(int(VSize(v) + 0.5));
  canvas.DrawColor = inp.Outer.DoubleClickDir == DCLICK_Done ? TextColorRed : TextColor;
  canvas.Font = MessageFont;
  canvas.SetPos(x + 2, y + 2);
  canvas.DrawText(text, false, 1.0, 1.0);

  // draw V
  // from CRZPawn.DoJump():
  canDoubleJump = p.CanDoubleJump() && p.bAllowFallingMultiJump ? (p.Velocity.Z < p.DoubleJumpThreshold) : (Abs(p.Velocity.Z) < p.DoubleJumpThreshold);
  text = "V: " $ string(int(p.Velocity.Z + 0.5));
  canvas.DrawColor = (p.Physics == PHYS_Walking || p.MultiJumpRemaining == 0) ? TextColor : (canDoubleJump ? TextColorGreen : TextColor /*Red*/);
  canvas.Font = MessageFont;
  canvas.SetPos(x + 2, y + 27);
  canvas.DrawText(text, false, 1.0, 1.0);

  // draw D
  text = "D: " $ string(int(jumpDistance + 0.5));
  canvas.DrawColor = TextColor;
  canvas.Font = MessageFont;
  canvas.SetPos(x + 2, y + 52);
  canvas.DrawText(text, false, 1.0, 1.0);

  // draw velocity vector line
  v = v << PC.Rotation;
  canvas.Draw2DLine(Canvas.ClipX / 2, Canvas.ClipY / 2, Canvas.ClipX / 2 + v.Y/5, Canvas.ClipY / 2 - v.X/5, VeloColor);
}

event NotifyGameSessionEnded()
{
  super.NotifyGameSessionEnded();
  Owner = None;
  PC = None;
}

DefaultProperties
{
  MessageFont=Font'UI_Fonts.Fonts.UI_Fonts_Positec18'
  TextColor=(R=255,G=255,B=255,A=128)
  TextColorGreen=(R=0,G=255,B=0,A=128)
  TextColorRed=(R=255,G=0,B=0,A=128)
  BackgroundColor=(R=0, G=0, B=0, A=64)
  VeloColor=(R=255,G=255,B=0,A=128);
}

class TachometerInteraction extends Interaction config (MutatH0r);

// defined at initialization
var PlayerController PC;
var CRZMutator_Tachometer Owner;

// static
var Font MessageFont;
var Color TextColor, BackgroundColor, VeloColor;

// instance
var config bool Disabled;
var vector LastGroundPos;
var bool isJumping;
var float jumpDistance;

static function TachometerInteraction Create(CRZMutator_Tachometer theOwner, PlayerController controller, optional bool bReturnExisting=true)
{
  local TachometerInteraction newInt;
  local int i;

  if (bReturnExisting)
  {
    for (i=0; i<controller.Interactions.length; i++)
    {
      if (controller.Interactions[i].class == default.class)
        return TachometerInteraction(controller.Interactions[i]);
    }
  }

  newInt = new(LocalPlayer(controller.Player).ViewportClient) default.class;
  LocalPlayer(controller.Player).ViewportClient.InsertInteraction(newInt, 0);
  controller.Interactions.AddItem(newInt);
  newInt.Owner = theOwner;
  return newInt;
}

function Initialized()
{
  super.Initialized();
  PC = GameViewportClient(Outer).GetPlayerOwner(0).Actor;
}

exec function ShowTacho(bool show)
{
  self.Disabled = !show;
  self.SaveConfig();
}

event PostRender(Canvas canvas)
{
  local CRZHud wrapper;

  super.PostRender(canvas);

  if (Disabled)
    return;
  if (Owner == None) // actor destroyed when match is over
    return;
  wrapper = CRZHud(PC.myHUD);
  if (wrapper == None || wrapper.CurrentHudMode == HM_Intro)
    return;

  RenderTickRate(canvas);
}

function RenderTickRate(Canvas canvas)
{
  local float x,y;
  local string text;
  local vector v;

  if (pc.Pawn == none)
    return;

  if (!isJumping && pc.Pawn.Physics == PHYS_Walking)
    LastGroundPos = pc.Pawn.Location;
  else if (pc.Pawn.Physics == PHYS_Falling)
    isJumping = true;
  else if (isJumping && pc.Pawn.Physics == PHYS_Walking)
  {
    isJumping = false;
    jumpDistance = VSize(pc.pawn.location - LastGroundPos);
  }

  v.X = pc.Pawn.Velocity.X;
  v.Y = pc.Pawn.Velocity.Y;
  v.Z = 0;

  text = "S: " $ string(int(VSize(v) + 0.5)) $ "\n"
    $ "D: " $ string(int(jumpDistance + 0.5));

  // draw box
  x = canvas.ClipX * 0.45;
  y = canvas.ClipY * 0.45;
  canvas.DrawColor = BackgroundColor;
  canvas.SetPos(x, y);
  canvas.DrawRect(85, 55);

  // draw number
  canvas.DrawColor = TextColor;
  canvas.Font = MessageFont;
  canvas.SetPos(x + 2, y + 2);
  canvas.DrawText(text, false, 1.0, 1.0);

  v = v << PC.Rotation;
  canvas.Draw2DLine(Canvas.ClipX / 2, Canvas.ClipY / 2, Canvas.ClipX / 2 + v.Y/5, Canvas.ClipY / 2 - v.X/5, VeloColor);
}

event NotifyGameSessionEnded()
{
  super.NotifyGameSessionEnded();
  Owner = None;
  PC = None;
}

DefaultProperties
{
  MessageFont=Font'UI_Fonts.Fonts.UI_Fonts_Positec18'
  TextColor=(R=255,G=255,B=255,A=128)
  BackgroundColor=(R=0, G=0, B=0, A=64)
  VeloColor=(R=255,G=255,B=0,A=128);
}