Update refactor.uc

Update refactor.uc

Changed {@Eval} to {@Evaluate} (where possible) for speed and to resolve quotes within arg

# refactor.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 4/17/2014 10:42:45 AM
# Comment: This rewrites code in more proper PB form

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

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: This rewrites code in more proper PB form
Selected: False
Highlight: False
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
FilterTally: False
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
InputFile:
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Comment: Defines comments as whitespace and colon as separator
Find:
Replace: {@Define::
            Token: [\x27\_].* ~~ Properties: ucWhiteSpace
            Token: : ~~ Properties: ucStatementSep
         }{@Define: Var: Args As String}

Criteria: 2
Comment: 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] | {'[#!@$%&]+'} | () }")
            Args = SetSyntaxParams(Args, Args)
         }{@Define::
            {@Eval: "PassOnce ~~ Syntax: {name} "+Args+" ::= {name}("+Args+")"}
            {@Eval: "SkipOver ~~ Syntax: {name} ({etc})"}
         }{Self}

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

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

# End Search

# refactor.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.95 on 3/24/2014 5:11:45 PM
# Comment: This rewrites code in more proper PB form

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

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: This rewrites code in more proper PB form
Selected: False
Highlight: False
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
FilterTally: False
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
InputFile:
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Comment: Defines comments as whitespace and colon as separator
Find:
Replace: {@Define::
            Token: [\x27\_].* ~~ Properties: ucWhiteSpace
            Token: : ~~ Properties: ucStatementSep
         }{@Define: Var: Args As String}

Criteria: 2
Comment: 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: {@Eval:
            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: 3
Comment: Adds closing quote for quoted text with missing closing quote
BackColor: DarkKhaki
Find: {QuotedText:"\q[^\q\n]*"}{nl}
Replace: {QuotedText}"{nl}

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

# End Search


Update pb-to-cpp.uc

Update pb-to-cpp.uc

Converts these keywords: Global, Local, Register

# convert.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.0 on 12/11/2013 12:34:34 PM
# Comment: Converts PB source code to C++; modified by Daniel Corbier

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

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Converts PB source code to C++; modified by Daniel Corbier
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
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find: ' {comment~}
Replace: // {comment}

Criteria: 2
BackColor: Green
Find: {nl}%{equate} = {value%}
Replace: {nl}const int {equate} = {value};

Criteria: 3
BackColor: Green
Find: %{equate}
Replace: {equate}

Criteria: 4
BackColor: SandyBrown
Find: {"&h"}
Replace: 0x

Criteria: 5
BackColor: Green
Find: If {cond%} Then {statement%}{nl}
Replace: if ({cond}) { {statement} }{nl}

Criteria: 6
Find: If {cond%} Then {nl}
         {code%+}
      End If
Replace: if ({cond}) {
            {code}
         }

Criteria: 7
BackColor: DarkKhaki
Find: Function {name} ([{args%}]) As {type%}{nl}
         {code%+}
      End Function
Replace: {type} {name}({args}) {
            {code}
         }

Criteria: 8
Find: Sub {name} ([{args%}]){nl}
         {code%+}
      End Sub
Replace: void {name}({args}) {
            {code}
         }

Criteria: 9
Find: For {x} = {start} To {stop} [Step {inc=1}]{nl}
         {code%+}
      Next
Replace: for ({x}={start}; {x}{@Eval: IIF(sgn({inc})>0, '<', '>')}={stop}; x += {inc}) {
            {code}
         }

Criteria: 10
Find: While {cond%} {nl}
         {code%+}
      Wend
Replace: while ({cond}) {
            {code}
         }

Criteria: 11
BackColor: Orange
Find: Dim {var} As {type%}
Replace: {type} {var};

Criteria: 12
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

Criteria: 13
Selected: True
BackColor: Gold
PassOnce: False
Find: { Local | Global | Register }
Replace: Dim

Criteria: 14
BackColor: DeepSkyBlue
Find: Function = {value}
Replace: return {value};

Criteria: 15
BackColor: Lime
Find: Long
Replace: long

Criteria: 16
BackColor: Red
Find: Single
Replace: float

Criteria: 17
BackColor: SlateBlue
Find: ByRef {arg} As {type%:1}
Replace: {type}& {arg}

Criteria: 18
BackColor: Pink
Find: ByVal {arg} As {type%:1}
Replace: {type} {arg}

# End Search

# convert.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.0 on 12/10/2013 10:22:31 AM
# Comment: Converts PB source code to C++; modified by Daniel Corbier

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

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Converts PB source code to C++; modified by Daniel Corbier
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
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Find: ' {comment~}
Replace: // {comment}

Criteria: 2
BackColor: Green
Find: {nl}%{equate} = {value%}
Replace: {nl}const int {equate} = {value};

Criteria: 3
BackColor: Green
Find: %{equate}
Replace: {equate}

Criteria: 4
BackColor: SandyBrown
Find: {"&h"}
Replace: 0x

Criteria: 5
BackColor: Green
Find: If {cond%} Then {statement%}{nl}
Replace: if ({cond}) { {statement} }{nl}

Criteria: 6
Find: If {cond%} Then {nl}
         {code%+}
      End If
Replace: if ({cond}) {
            {code}
         }

Criteria: 7
BackColor: DarkKhaki
Find: Function {name} ([{args%}]) As {type%}{nl}
         {code%+}
      End Function
Replace: {type} {name}({args}) {
            {code}
         }

Criteria: 8
Find: Sub {name} ([{args%}]){nl}
         {code%+}
      End Sub
Replace: void {name}({args}) {
            {code}
         }

Criteria: 9
Find: For {x} = {start} To {stop} [Step {inc=1}]{nl}
         {code%+}
      Next
Replace: for ({x}={start}; {x}{@Eval: IIF(sgn({inc})>0, '<', '>')}={stop}; x += {inc}) {
            {code}
         }

Criteria: 10
Selected: True
Find: While {cond%} {nl}
         {code%+}
      Wend
Replace: while ({cond}) {
            {code}
         }

Criteria: 11
BackColor: Orange
Find: Dim {var} As {type%}
Replace: {type} {var};

Criteria: 12
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

Criteria: 13
BackColor: DeepSkyBlue
Find: Function = {value}
Replace: return {value};

Criteria: 14
BackColor: Lime
Find: Long
Replace: long

Criteria: 15
BackColor: Red
Find: Single
Replace: float

Criteria: 16
BackColor: SlateBlue
Find: ByRef {arg} As {type%:1}
Replace: {type}& {arg}

Criteria: 17
BackColor: Pink
Find: ByVal {arg} As {type%:1}
Replace: {type} {arg}

# End Search


Fix Issue Id for X2WeaponUpgradeTemplate.UpgradeCats

class X2WeaponUpgradeTemplate extends X2ItemTemplate;

var array<WeaponAttachment>    UpgradeAttachments;

var localized string TinySummary;

var int	AimBonus;			// hit chance modifier
var int CritBonus;			// crit chance modifier
var int ClipSizeBonus;		// extra ammo
var int FreeFireChance;		// chance out of 100 that the action will be free
var int NumFreeReloads;		// setting to 0 or less will mean no limit
var WeaponDamageValue BonusDamage;			// amount of bonus damage on shots that allow for it
var WeaponDamageValue CHBonusDamage;		// Issue #237 - secondary amount of bonus damage, more general purpose than the miss-damage-only BonusDamage
var array<name> UpgradeCats;		// Issue #260
var int FreeKillChance;		// chance out of 100 that a damaging shot will become an automatic kill

var array<name> MutuallyExclusiveUpgrades; // upgrades which cannot be equipped at the same time as any of the others

var array<name> BonusAbilities;         //  abilities granted to the unit when this upgrade is on an item in its inventory

var delegate<CanApplyUpgradeToWeaponDelegate>			CanApplyUpgradeToWeaponFn;
var delegate<AddCritChanceModifierDelegate>				AddCritChanceModifierFn;
var delegate<AddHitChanceModifierDelegate>				AddHitChanceModifierFn;
var delegate<AdjustClipSizeDelegate>					AdjustClipSizeFn;
var delegate<FreeFireCostDelegate>						FreeFireCostFn;
var delegate<FreeReloadCostDelegate>					FreeReloadCostFn;
var delegate<FreeKillDelegate>							FreeKillFn;
var delegate<FriendlyRenameAbilityDelegate>             FriendlyRenameFn;
var delegate<GetBonusAmountDelegate>					GetBonusAmountFn;
var delegate<AddCHDamageModifierDelegate>				AddCHDamageModifierFn; // Issue #237

delegate bool CanApplyUpgradeToWeaponDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Item Weapon, int SlotIndex);
delegate bool AddCritChanceModifierDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, out int CritChanceMod);
delegate bool AddHitChanceModifierDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, const GameRulesCache_VisibilityInfo VisInfo, out int HitChanceMod);
delegate bool AdjustClipSizeDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Item Weapon, const int CurrentClipSize, out int AdjustedClipSize);
delegate bool FreeFireCostDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Ability FireAbility);
delegate bool FreeReloadCostDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Ability ReloadAbility, XComGameState_Unit UnitState);
delegate bool FreeKillDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Unit TargetUnit);
delegate string FriendlyRenameAbilityDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Ability AbilityState);
delegate int GetBonusAmountDelegate(X2WeaponUpgradeTemplate UpgradeTemplate);
delegate bool AddCHDamageModifierDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, out int StatMod, name StatType); // Issue #237

function AddUpgradeAttachment(
	Name AttachSocket,
	Name UIArmoryCameraPointTag,
	string MeshName,
	string ProjectileName,
	name MatchWeaponTemplate,
	optional bool AttachToPawn,
	optional string IconName,
	optional string InventoryIconName,
	optional string InventoryCategoryIcon,
	optional delegate<X2TacticalGameRulesetDataStructures.CheckUpgradeStatus> ValidateAttachmentFn)
{
	local WeaponAttachment Attach;

	Attach.AttachSocket = AttachSocket;
	Attach.UIArmoryCameraPointTag = UIArmoryCameraPointTag;
	Attach.AttachMeshName = MeshName;
	Attach.AttachProjectileName = ProjectileName;
	Attach.ApplyToWeaponTemplate = MatchWeaponTemplate;
	Attach.AttachToPawn = AttachToPawn;
	Attach.AttachIconName = IconName;
	Attach.InventoryIconName = InventoryIconName;
	Attach.InventoryCategoryIcon = InventoryCategoryIcon;
	Attach.ValidateAttachmentFn = ValidateAttachmentFn;
	UpgradeAttachments.AddItem(Attach);
}

function bool CanApplyUpgradeToWeapon(XComGameState_Item Weapon, optional int SlotIndex = 0)
{
	if (CanApplyUpgradeToWeaponFn != none)
		return CanApplyUpgradeToWeaponFn(self, Weapon, SlotIndex);
	return true;
}

/**
 * Gets the socket name that is used to identify location of upgrade on weapon, used by UIArmory_WeaponUpgrade
 *
 * IMPORTANT: Assumes one socket per weapon - sbatista
 */
function name GetAttachmentSocketName(XComGameState_Item Weapon)
{
	local WeaponAttachment Attach;

	foreach UpgradeAttachments(Attach)
	{
		if(Attach.ApplyToWeaponTemplate == Weapon.GetMyTemplateName())
			return Attach.AttachSocket;
	}

	return '';
}

/**
* Gets the image name of the attachment for the specified weapon, used by UIArmory_WeaponUpgrade
*
* IMPORTANT: Assumes one valid attachment per weapon - bsteiner
*/
function string GetAttachmentInventoryImage(XComGameState_Item Weapon)
{
	local WeaponAttachment Attach;

	foreach UpgradeAttachments(Attach)
	{
		if( Attach.ApplyToWeaponTemplate == Weapon.GetMyTemplateName() )
			return Attach.InventoryIconName;
	}

	return "";
}

/**
* Gets the category image names of the attachment for the specified weapon, used by UIArmory_WeaponUpgrade
*
*/
function array<string> GetAttachmentInventoryCategoryImages(XComGameState_Item Weapon)
{
	local WeaponAttachment Attach;
	local array<string> Images;

	foreach UpgradeAttachments(Attach)
	{
		if( Attach.InventoryCategoryIcon != "" &&
			Attach.ApplyToWeaponTemplate == Weapon.GetMyTemplateName() &&
			Images.Find(Attach.InventoryCategoryIcon) == INDEX_NONE )
			Images.AddItem(Attach.InventoryCategoryIcon);
	}

	return Images;
}

/**
* Gets the renamed friendly-name for an ability that we alter.
*
*/
function string GetRenamedAbilityFriendlyName(XComGameState_Ability AbilityState)
{
	if (FriendlyRenameFn != none)
		return FriendlyRenameFn(self, AbilityState);

	return "";
}

DefaultProperties
{
	ItemCat="upgrade"
	MaxQuantity=3
}

class X2WeaponUpgradeTemplate extends X2ItemTemplate;

var array<WeaponAttachment>    UpgradeAttachments;

var localized string TinySummary;

var int	AimBonus;			// hit chance modifier
var int CritBonus;			// crit chance modifier
var int ClipSizeBonus;		// extra ammo
var int FreeFireChance;		// chance out of 100 that the action will be free
var int NumFreeReloads;		// setting to 0 or less will mean no limit
var WeaponDamageValue BonusDamage;			// amount of bonus damage on shots that allow for it
var WeaponDamageValue CHBonusDamage;		// Issue #237 - secondary amount of bonus damage, more general purpose than the miss-damage-only BonusDamage
var array<name> UpgradeCats;		// Issue #237
var int FreeKillChance;		// chance out of 100 that a damaging shot will become an automatic kill

var array<name> MutuallyExclusiveUpgrades; // upgrades which cannot be equipped at the same time as any of the others

var array<name> BonusAbilities;         //  abilities granted to the unit when this upgrade is on an item in its inventory

var delegate<CanApplyUpgradeToWeaponDelegate>			CanApplyUpgradeToWeaponFn;
var delegate<AddCritChanceModifierDelegate>				AddCritChanceModifierFn;
var delegate<AddHitChanceModifierDelegate>				AddHitChanceModifierFn;
var delegate<AdjustClipSizeDelegate>					AdjustClipSizeFn;
var delegate<FreeFireCostDelegate>						FreeFireCostFn;
var delegate<FreeReloadCostDelegate>					FreeReloadCostFn;
var delegate<FreeKillDelegate>							FreeKillFn;
var delegate<FriendlyRenameAbilityDelegate>             FriendlyRenameFn;
var delegate<GetBonusAmountDelegate>					GetBonusAmountFn;
var delegate<AddCHDamageModifierDelegate>				AddCHDamageModifierFn; // Issue #237

delegate bool CanApplyUpgradeToWeaponDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Item Weapon, int SlotIndex);
delegate bool AddCritChanceModifierDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, out int CritChanceMod);
delegate bool AddHitChanceModifierDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, const GameRulesCache_VisibilityInfo VisInfo, out int HitChanceMod);
delegate bool AdjustClipSizeDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Item Weapon, const int CurrentClipSize, out int AdjustedClipSize);
delegate bool FreeFireCostDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Ability FireAbility);
delegate bool FreeReloadCostDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Ability ReloadAbility, XComGameState_Unit UnitState);
delegate bool FreeKillDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Unit TargetUnit);
delegate string FriendlyRenameAbilityDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, XComGameState_Ability AbilityState);
delegate int GetBonusAmountDelegate(X2WeaponUpgradeTemplate UpgradeTemplate);
delegate bool AddCHDamageModifierDelegate(X2WeaponUpgradeTemplate UpgradeTemplate, out int StatMod, name StatType); // Issue #237

function AddUpgradeAttachment(
	Name AttachSocket,
	Name UIArmoryCameraPointTag,
	string MeshName,
	string ProjectileName,
	name MatchWeaponTemplate,
	optional bool AttachToPawn,
	optional string IconName,
	optional string InventoryIconName,
	optional string InventoryCategoryIcon,
	optional delegate<X2TacticalGameRulesetDataStructures.CheckUpgradeStatus> ValidateAttachmentFn)
{
	local WeaponAttachment Attach;

	Attach.AttachSocket = AttachSocket;
	Attach.UIArmoryCameraPointTag = UIArmoryCameraPointTag;
	Attach.AttachMeshName = MeshName;
	Attach.AttachProjectileName = ProjectileName;
	Attach.ApplyToWeaponTemplate = MatchWeaponTemplate;
	Attach.AttachToPawn = AttachToPawn;
	Attach.AttachIconName = IconName;
	Attach.InventoryIconName = InventoryIconName;
	Attach.InventoryCategoryIcon = InventoryCategoryIcon;
	Attach.ValidateAttachmentFn = ValidateAttachmentFn;
	UpgradeAttachments.AddItem(Attach);
}

function bool CanApplyUpgradeToWeapon(XComGameState_Item Weapon, optional int SlotIndex = 0)
{
	if (CanApplyUpgradeToWeaponFn != none)
		return CanApplyUpgradeToWeaponFn(self, Weapon, SlotIndex);
	return true;
}

/**
 * Gets the socket name that is used to identify location of upgrade on weapon, used by UIArmory_WeaponUpgrade
 *
 * IMPORTANT: Assumes one socket per weapon - sbatista
 */
function name GetAttachmentSocketName(XComGameState_Item Weapon)
{
	local WeaponAttachment Attach;

	foreach UpgradeAttachments(Attach)
	{
		if(Attach.ApplyToWeaponTemplate == Weapon.GetMyTemplateName())
			return Attach.AttachSocket;
	}

	return '';
}

/**
* Gets the image name of the attachment for the specified weapon, used by UIArmory_WeaponUpgrade
*
* IMPORTANT: Assumes one valid attachment per weapon - bsteiner
*/
function string GetAttachmentInventoryImage(XComGameState_Item Weapon)
{
	local WeaponAttachment Attach;

	foreach UpgradeAttachments(Attach)
	{
		if( Attach.ApplyToWeaponTemplate == Weapon.GetMyTemplateName() )
			return Attach.InventoryIconName;
	}

	return "";
}

/**
* Gets the category image names of the attachment for the specified weapon, used by UIArmory_WeaponUpgrade
*
*/
function array<string> GetAttachmentInventoryCategoryImages(XComGameState_Item Weapon)
{
	local WeaponAttachment Attach;
	local array<string> Images;

	foreach UpgradeAttachments(Attach)
	{
		if( Attach.InventoryCategoryIcon != "" &&
			Attach.ApplyToWeaponTemplate == Weapon.GetMyTemplateName() &&
			Images.Find(Attach.InventoryCategoryIcon) == INDEX_NONE )
			Images.AddItem(Attach.InventoryCategoryIcon);
	}

	return Images;
}

/**
* Gets the renamed friendly-name for an ability that we alter.
*
*/
function string GetRenamedAbilityFriendlyName(XComGameState_Ability AbilityState)
{
	if (FriendlyRenameFn != none)
		return FriendlyRenameFn(self, AbilityState);

	return "";
}

DefaultProperties
{
	ItemCat="upgrade"
	MaxQuantity=3
}


Added Inline Documentation - Issue #936

Added Inline Documentation - Issue #936

//---------------------------------------------------------------------------------------
//  FILE:    X2EventListener_DefaultWillEvents.uc
//  AUTHOR:  David Burchanowski
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------

class XComGameStateContext_WillRoll extends XComGameStateContext
	config(GameCore)
	native(Core);

enum WillEventRoll_StatType
{
	WillEventRollStat_None,
	WillEventRollStat_PercentageHealthLost,
	WillEventRollStat_SquadmateRank,
	WillEventRollStat_MaxWill,
	WillEventRollStat_BondLevel,
	WillEventRollStat_Flat
};

struct native WillEventRollData_PanicWeight
{
	var name PanicAbilityName;
	var float Weight; // 0.0-1.0

	structdefaultproperties
	{
		Weight = 1
	}
};

// This structure provides a simple uniform mechanism for doing rolls to lose will
struct native WillEventRollData
{
	var EMentalState MinimumTiredState;  // this will data will not be applied to units that are less fatigued than this state
	var float WillEventActivationChance;  // if all other conditions are met, this is the chance that this will event should actually modify will/roll on panic
	var name WillEventActivationType;	// if non-none, will events of this type are restricted to only happening once per unit per mission

	// values for determining how much will to lose
	var float WillLossChance;    // chance to lose will.
	var bool FlatWillLossChance; // if true, will do a straight roll against WillLossChance, if false rolls against WillLossChance - eStat_Will

	// calculated will loss is the value of the chosen WillLossStat *  WillLossStatMultiplier.
	// the final will loss is max(<calculated will loss>, MinimumWillLoss)
	var WillEventRoll_StatType WillLossStat;
	var float WillLossStatMultiplier;
	var int MinimumWillLoss;

	var float MaxWillPercentageLostPerMission; // if > 0, will will only be lost up to this percentage of the unit's max will
	var bool CanZeroOutWill; // if false, will always leave at least 1 will remaining

	// When a unit loses will, a roll will be made against PanicChance. If the roll passes,
	// one of the abilities in PanicWeights will be selected.
	var float PanicChance; // 0.0-1.0f, will be further reduced by a unit's will
	var array<WillEventRollData_PanicWeight> PanicWeights;

	structdefaultproperties
	{
		MinimumTiredState = eMentalState_Ready
		WillEventActivationChance = 1.0
	}
};

// text that appears above soldier's head when he loses will from a will event
var localized const string LostWillFlyover;
var localized const string LostWillWorldMessage;
var localized const string DefaultLossSource;
var localized const string PanicTestCaption;
var localized const string PanicResultFlyover;
var localized const string PanicResultWorldMessage;
var localized const string ResistedText;

// Percentage of will to lose per bond level when checking WillEventRollStat_BondLevel
var protected const config array<float>      BondLevelWillLoss;

// the reduction in will loss when this soldier is on a mission with his bondmate
var protected const config float SoldierBondWillReductionMultiplier;

// If a soldier does a "normal" panic ability more than once, reroll using the values in this table
var protected const config array<WillEventRollData_PanicWeight> MultiplePanicAltWeights;

// It's possible to do multiple rolls per context, so keep track of the running total here.
var privatewrite int RunningWillLossTotal;

// the type of will event being conducted
var privatewrite name WillEventType;

// It's possible to get multiple panic results from different rolls, so just use the most recent one
var privatewrite name PanicAbilityName;

// The unit we are performing the roll on.
var privatewrite XComGameState_Unit SourceUnit;
// The target id our source unit is rolling against.
var privatewrite int TargetUnitID;

// Localized friendly name of the roll source. Ex. "Fear of Missing Shots"
var protectedwrite string RollSourceFriendly;
var protectedwrite name RollSource;

// If true, will show flyovers and world messages
var protectedwrite bool ShowMessages;

// If the target unit is affected by any of these Effects, skip the panic roll
var protected const config array<name>      SkipPanicTestWhenAfflictedByEffects;

//////////////////////////////////////////////////////////////////////////////////

static event bool ShouldPerformWillRoll(const out WillEventRollData RollInfo, XComGameState_Unit AffectedUnit)
{
	if( AffectedUnit == none )
	{
		return false;
	}

	// skip this test on any dead, or evacuated units
	if( AffectedUnit.IsDead() ||
	   AffectedUnit.IsInStasis() ||
	   AffectedUnit.bRemovedFromPlay )
	{
		return false;
	}

	// raw random chance to skip this activation
	if( `SYNC_FRAND_STATIC() >= RollInfo.WillEventActivationChance )
	{
		return false;
	}

	return true;
}

static function bool ShouldPerformPanicRoll(const out WillEventRollData RollInfo, XComGameState_Unit AffectedUnit)
{
	local name TestEffect;

	if( RollInfo.PanicChance <= 0.0 )
	{
		return false;
	}

	// restrict rolling on will while panicked
	if( !AffectedUnit.IsAbleToAct() )
	{
		return false;
	}

	// restrict by the units current fatigue state
	if( AffectedUnit.GetMentalState() > RollInfo.MinimumTiredState )
	{
		return false;
	}

	// restrict panic checks to once per turn
	if( AffectedUnit.PanicTestsPerformedThisTurn > 0 )
	{
		return false;
	}

	// restrict if the unit has already rolled on this event type this mission
	if( RollInfo.WillEventActivationType != '' && AffectedUnit.WillEventsActivatedThisMission.Find(RollInfo.WillEventActivationType) != INDEX_NONE )
	{
		return false;
	}

	// Avoid panic tests on units affected by certain status effects
	foreach default.SkipPanicTestWhenAfflictedByEffects(TestEffect)
	{
		if( AffectedUnit.AffectedByEffectNames.Find(TestEffect) != INDEX_NONE )
		{
			return false;
		}
	}

	// skip the roll for panic for any condition that would fail a panic ability
	if( class'X2Condition_Panic'.static.StaticCallMeetsCondition(AffectedUnit) != 'AA_Success' )
	{
		return false;
	}

	return true;
}

// "SquadmateUnit" needs to be supplied when doing rolls against squadmate rank
event DoWillRoll(WillEventRollData RollInfo, optional XComGameState_Unit SquadmateUnit)
{
	if( SquadmateUnit == None )
	{
		SquadmateUnit = SourceUnit;
	}

	TargetUnitID = SquadmateUnit.ObjectID;

	WillEventType = RollInfo.WillEventActivationType;

	CalculateWillRoll(RollInfo, SquadmateUnit, SourceUnit, RollSource, RunningWillLossTotal, PanicAbilityName);

	if( ShouldPerformPanicRoll(RollInfo, SquadmateUnit) )
	{
		// also make a panic roll
		DoPanicRoll(SquadmateUnit, RunningWillLossTotal, RollInfo.PanicChance, RollInfo.PanicWeights, false, PanicAbilityName);
	}
	else
	{
		// never show messages if there was no panic roll
		ShowMessages = false;
	}
}

static function PerformWillRollOnUnitForNewGameState(WillEventRollData RollInfo, XComGameState_Unit InSourceUnit, name InRollSource, XComGameState NewGameState)
{
	local int RunningWillLoss;
	local name PanicAbility;
	local XComGameState_Unit NewSourceUnitState;

	CalculateWillRoll(RollInfo, None, InSourceUnit, InRollSource, RunningWillLoss, PanicAbility);

	NewSourceUnitState = XComGameState_Unit(NewGameState.ModifyStateObject(class'XComGameState_Unit', InSourceUnit.ObjectId));
	NewSourceUnitState.ModifyCurrentStat(eStat_Will, -RunningWillLoss);
	++NewSourceUnitState.PanicTestsPerformedThisTurn;

	if( RollInfo.WillEventActivationType != '' && NewSourceUnitState.WillEventsActivatedThisMission.Find(RollInfo.WillEventActivationType) == INDEX_NONE )
	{
		NewSourceUnitState.WillEventsActivatedThisMission.AddItem(RollInfo.WillEventActivationType);
	}
}

static function CalculateWillRoll(WillEventRollData RollInfo, XComGameState_Unit SquadmateUnit, XComGameState_Unit InSourceUnit, name InRollSource, out int InRunningWillLossTotal, out name InPanicAbilityName)
{
	local XComGameStateHistory History;
	local XComGameState_Unit StartOfMissionUnit;
	local bool ShouldLoseWill;
	local float CalculatedWillLoss;
	local int WillLossOverage;
	local float WillLossRemainder;
	local int CurrentWill; // current will, including the running loss total
	local int MaximumWillLossPerMission;
	local int MinimumAllowedWill;
	local SoldierBond BondData;
	local bool bBondmateOnMission;
	local StateObjectReference BondmateRef;
	local XComGameState_BattleData BattleDataState;
	local X2SitRepEffect_ModifyWillPenalties SitRepEffect;
	local float SitRepWillLossScalar;
	// Start Issue #44
	local float WillStartOfMission;
	local UnitValue WillStartOfMissionUnitVal;
	// End Issue #44

	if( InSourceUnit.IsDead()
		|| InSourceUnit.IsIncapacitated()
		|| InSourceUnit.IsMindControlled())
	{
		return;
	}

	if( InSourceUnit.UsesWillSystem())
	{
		if(RollInfo.FlatWillLossChance)
		{
			ShouldLoseWill = class'Engine'.static.SyncFRand("DoWillRoll") < RollInfo.WillLossChance;
		}
		else
		{
			ShouldLoseWill = class'Engine'.static.SyncFRand("DoWillRoll") < RollInfo.WillLossChance - (InSourceUnit.GetCurrentStat(eStat_Will) * 0.01f);
		}
	}

	if(ShouldLoseWill)
	{
		bBondmateOnMission = InSourceUnit.HasSoldierBond(BondmateRef, BondData) && `XCOMHQ.IsUnitInSquad(BondmateRef);

		// determine how much will we want to lose
		switch(RollInfo.WillLossStat)
		{
		case WillEventRollStat_None:
			CalculatedWillLoss = 0;
			break;
		case WillEventRollStat_PercentageHealthLost:
			CalculatedWillLoss = InSourceUnit.GetCurrentStat(eStat_HP) / InSourceUnit.GetMaxStat(eStat_HP);
			break;
		case WillEventRollStat_SquadmateRank:
			assert(SquadmateUnit != none);
			CalculatedWillLoss = SquadmateUnit.GetSoldierRank();
			break;
		case WillEventRollStat_MaxWill:
			CalculatedWillLoss = InSourceUnit.GetMaxStat(eStat_Will);
			break;
		case WillEventRollStat_BondLevel:
			if( bBondmateOnMission )
			{
				if(BondData.BondLevel < 0 || BondData.BondLevel > default.BondLevelWillLoss.Length)
				{
					`Redscreen("DoWillRoll(): " $ InSourceUnit.GetFullName() $ "has an unexpected bond level " $ BondData.BondLevel);
					BondData.BondLevel = 0;
				}
				CalculatedWillLoss = InSourceUnit.GetMaxStat(eStat_Will) * default.BondLevelWillLoss[BondData.BondLevel];
			}
			break;
		// Start Issue #936
		/// HL-Docs: feature:AdditionalWillRollStats; issue:936; tags:tactical
		/// Unused element `WillEventRollStat_Flat` of `enum WillEventRoll_StatType` added to the switch block in `CalculateWillRoll`.
		/// Sets `CalculatedWillLoss` to `1.0f` so that it can be adusted by `RollInfo.WillLossStatMultiplier` and `SitRepWillLossScalar`.
		case WillEventRollStat_Flat:
			CalculatedWillLoss = 1.0f;
			break;
		// End Issue #936
		default:
			`assert(false);
		}

		// Gather sitrep granted abilities
		BattleDataState = XComGameState_BattleData(`XCOMHISTORY.GetSingleGameStateObjectForClass(class'XComGameState_BattleData'));
		SitRepWillLossScalar = 1.0f;
		if (BattleDataState != none)
		{
			foreach class'X2SitreptemplateManager'.static.IterateEffects(class'X2SitRepEffect_ModifyWillPenalties', SitRepEffect, BattleDataState.ActiveSitReps)
			{
				if ((SitRepEffect.WillEventNames.Length == 0) || (SitRepEffect.WillEventNames.Find(InRollSource) != INDEX_NONE))
				{
					SitRepWillLossScalar *= SitRepEffect.ModifyScalar;
				}
			}
		}

		CalculatedWillLoss = (CalculatedWillLoss * RollInfo.WillLossStatMultiplier * SitRepWillLossScalar);

		if( bBondmateOnMission )
		{
			CalculatedWillLoss *= default.SoldierBondWillReductionMultiplier;
		}

		// if we have a remainder, roll on it for an extra integer point
		WillLossRemainder = CalculatedWillLoss % 1.0f;
		if(class'Engine'.static.SyncFRand("DoWillRoll") < WillLossRemainder)
		{
			CalculatedWillLoss += 1;
		}

		// make sure we are taking at least the minimum amount
		CalculatedWillLoss = max(int(CalculatedWillLoss), RollInfo.MinimumWillLoss);

		// since the history hasn't been updated yet, add in the will loss from previous rolls
		CurrentWill = InSourceUnit.GetCurrentStat(eStat_Will) - InRunningWillLossTotal;

		// and determine if we have any lower floor on how much will we are allowed to lose in the mission.
		// by default, we can go all the way to zero
		MinimumAllowedWill = 0;
		if(!RollInfo.CanZeroOutWill)
		{
			// don't allow dropping below 1
			MinimumAllowedWill = 1;
		}

		if(RollInfo.MaxWillPercentageLostPerMission > 0)
		{
			// Start Issue #44
			// If we want to use the changed behavior (no reset of Start-of-mission Will on Tactical-To-Tactical Transfer),
			// check if we have the UnitValue recorded -- otherwise it might break when we add the Highlander in the middle of a mission
			if (class'CHHelpers'.default.MULTIPART_MISSION_WILL_LOSS_CAP && InSourceUnit.GetUnitValue('CH_StartMissionWill', WillStartOfMissionUnitVal))
			{
				WillStartOfMission = WillStartOfMissionUnitVal.fValue;
			}
			else
			{
				// get the unit's will at the start of the mission
				History = `XCOMHISTORY;
				StartOfMissionUnit = XComGameState_Unit(History.GetGameStateForObjectID(InSourceUnit.ObjectID,, History.FindStartStateIndex()));
				WillStartOfMission = StartOfMissionUnit.GetCurrentStat(eStat_Will);
			}
			MaximumWillLossPerMission = RollInfo.MaxWillPercentageLostPerMission * InSourceUnit.GetMaxStat(eStat_Will);
			MinimumAllowedWill = max(MinimumAllowedWill, WillStartOfMission - MaximumWillLossPerMission);
			// End Issue #44
		}

		// now that we know our lower floor, make sure we won't drop below the minimum
		WillLossOverage = CurrentWill - (CalculatedWillLoss + MinimumAllowedWill);
		CalculatedWillLoss -= Max(0, -WillLossOverage);

		// add in the loss for this roll to the running total
		InRunningWillLossTotal += CalculatedWillLoss;
	}
}

static function DoPanicRoll(XComGameState_Unit InSourceUnit, int InRunningWillLossTotal, float PanicChance, array<WillEventRollData_PanicWeight> PanicWeights, bool Reroll, out name InPanicAbilityName)
{
	local int CurrentWill;
	local float PanicRoll;
	local WillEventRollData_PanicWeight PanicWeight;
	local float TotalPanicWeight;

	CurrentWill = InSourceUnit.GetCurrentStat(eStat_Will) - InRunningWillLossTotal;

	PanicRoll = `SYNC_FRAND_STATIC("DoWillRoll");
	if((PanicRoll < (PanicChance - (CurrentWill * 0.01)))
		|| CurrentWill <= 0
		|| (`CHEATMGR != none && `CHEATMGR.bAlwaysPanic))
	{
		// do a weighted pick from the available choices. Add up the total weight of all options,
		// do a random roll from [0..TotalWeight], and then start adding up the weights again.
		// when the total goes over the random number, we have our choice.
		foreach PanicWeights(PanicWeight)
		{
			TotalPanicWeight += PanicWeight.Weight;
		}

		PanicRoll = `SYNC_FRAND_STATIC("DoWillRoll") * TotalPanicWeight;

		TotalPanicWeight = 0.0f;
		foreach PanicWeights(PanicWeight)
		{
			TotalPanicWeight += PanicWeight.Weight;
			if(TotalPanicWeight >= PanicRoll)
			{
				InPanicAbilityName = PanicWeight.PanicAbilityName;

				// To favor the new panic behaviors, if we roll a normal panic and this unit has already
				// done a normal panic on this mission, reroll (but only once)
				if(!Reroll
					&& InPanicAbilityName == 'Panicked'
					&& UnitHasAlreadyUsedNormalPanicThisMission(InSourceUnit))
				{
					DoPanicRoll(InSourceUnit, InRunningWillLossTotal, PanicChance, default.MultiplePanicAltWeights, true, InPanicAbilityName);
				}

				break;
			}
		}
	}
}

// Does what it says, nativized since it can require a lot of iteration late into missions.
static native function bool UnitHasAlreadyUsedNormalPanicThisMission(XComGameState_Unit InSourceUnit);

function bool Validate(optional EInterruptionStatus InInterruptionStatus)
{
	return true;
}

function XComGameState ContextBuildGameState()
{
	local XComGameState NewGameState;
	local XComGameState_Unit UnitState;
	local X2EventManager Manager;
	local Object SelfObj;

	// Add the units to the new game state
	NewGameState = `XCOMHISTORY.CreateNewGameState(true, self);

	UnitState = XComGameState_Unit(NewGameState.ModifyStateObject(SourceUnit.Class, SourceUnit.ObjectId));
	UnitState.ModifyCurrentStat(eStat_Will, -RunningWillLossTotal);

	if( WillEventType != '' && UnitState.WillEventsActivatedThisMission.Find(WillEventType) == INDEX_NONE )
	{
		UnitState.WillEventsActivatedThisMission.AddItem(WillEventType);
	}

	SelfObj = self;
	Manager = `XEVENTMGR;

	// trigger an event to do our panic behavior if one was selected. We need to wait for this context
	// to complete before we can do the panic, and an event will serve that purpose just fine
	if(PanicAbilityName != '')
	{
		Manager.RegisterForEvent(SelfObj, 'DoPanicAbility', OnDoPanicAbility, ELD_OnStateSubmitted);
		Manager.TriggerEvent('DoPanicAbility', UnitState, self, NewGameState);
	}

	NewGameState.GetContext().SetAssociatedPlayTiming(SPT_AfterSequential);

	// Start Issue #13
	Manager.TriggerEvent('WillRollContext', UnitState, self, NewGameState);
	// End Issue #13

	return NewGameState;
}

function EventListenerReturn OnDoPanicAbility(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
	local XComGameStateHistory History;
	local GameRulesCache_Unit UnitCache;
	local XComGameState_Unit UnitState;
	local XComGameState_Ability AbilityState;
	local int i;

	History = `XCOMHISTORY;

	UnitState = XComGameState_Unit(EventData);
	if(EventSource != self)
	{
		return ELR_NoInterrupt;
	}

	// find the specified panic ability and activate it
	if (`TACTICALRULES.GetGameRulesCache_Unit(UnitState.GetReference(), UnitCache))
	{
		for (i = 0; i < UnitCache.AvailableActions.Length; ++i)
		{
			AbilityState = XComGameState_Ability(History.GetGameStateForObjectID(UnitCache.AvailableActions[i].AbilityObjectRef.ObjectID));
			if (AbilityState.GetMyTemplateName() == PanicAbilityName)
			{
				if (UnitCache.AvailableActions[i].AvailableCode == 'AA_Success' && UnitCache.AvailableActions[i].AvailableTargets.Length > 0)
				{
					class'XComGameStateContext_Ability'.static.ActivateAbility(UnitCache.AvailableActions[i], 0, , , , , , , SPT_AfterSequential);
				}
				break;
			}
		}
	}

	`XEVENTMGR.UnRegisterFromEvent(EventSource, 'DoPanicAbility');
	return ELR_NoInterrupt;
}

protected function ContextBuildVisualization()
{
	local VisualizationActionMetadata ActionMetadata, EmptyMetadata;
	local X2Action_PlaySoundAndFlyover FlyoverAction;
	//local X2Action_PlayMessageBanner WorldMessageAction;
	local XComGameState_Unit UnitState;
	local XGParamTag Tag;
	local X2Action_RevealArea RevealAreaAction;
	local XComWorldData WorldData;
	local TTile TileLocation;
	local Vector WorldLocation;
	local X2Action_CameraLookAt LookAtAction;
	local X2Action_StartStopSound SoundAction;
	local X2Action_UpdateUI UIUpdateAction;
	local X2AbilityTemplate AbilityTemplate;
	local X2Action_Delay DelayAction;
	local X2Action_CentralBanner BannerAction;

	// add a flyover track for every unit in the game state. This timing of this is too late to look good
	// but since Ryan is tearing it all out there is no reason to augment the visualization system to allow
	// it to look pretty
	if(ShowMessages)
	{
		WorldData = `XWORLD;

		Tag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
		Tag.StrValue0 = RollSourceFriendly;
		Tag.IntValue0 = RunningWillLossTotal;

		if( PanicAbilityName == '' )
		{
			Tag.StrValue2 = ResistedText;
		}
		else
		{
			AbilityTemplate = class'XComGameState_Ability'.static.GetMyTemplateManager().FindAbilityTemplate(PanicAbilityName);
			Tag.StrValue2 = AbilityTemplate.LocFriendlyName;
		}

		// clear unnecessary HUD components
		UIUpdateAction = X2Action_UpdateUI(class'X2Action_UpdateUI'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
		UIUpdateAction.UpdateType = EUIUT_SetHUDVisibility;
		UIUpdateAction.DesiredHUDVisibility.bMessageBanner = true;

		foreach AssociatedState.IterateByClassType(class'XComGameState_Unit', UnitState)
		{
			UnitState.GetKeystoneVisibilityLocation(TileLocation);
			WorldLocation = WorldData.GetPositionFromTileCoordinates(TileLocation);

			Tag.StrValue1 = UnitState.GetFullName();

			ActionMetadata = EmptyMetadata;
			ActionMetadata.StateObject_OldState = UnitState.GetPreviousVersion();
			ActionMetadata.StateObject_NewState = UnitState;

			Tag.StrValue0 = RollSourceFriendly;

			// new visualization flow:
			// for each affected unit:
			// 1a) Clear FOW around unit
			RevealAreaAction = X2Action_RevealArea(class'X2Action_RevealArea'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			RevealAreaAction.ScanningRadius = class'XComWorldData'.const.WORLD_StepSize * 5.0f;
			RevealAreaAction.TargetLocation = WorldLocation;
			RevealAreaAction.bDestroyViewer = false;
			RevealAreaAction.AssociatedObjectID = UnitState.ObjectID;

			// 1b) Focus camera on unit (wait until centered)
			LookAtAction = X2Action_CameraLookAt(class'X2Action_CameraLookAt'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			LookAtAction.LookAtLocation = WorldLocation;
			LookAtAction.BlockUntilActorOnScreen = true;
			LookAtAction.LookAtDuration = 10.0f;		// longer than we need - camera will be removed by tag below
			LookAtAction.TargetZoomAfterArrival = -0.7f;
			LookAtAction.CameraTag = 'WillTestCamera';
			LookAtAction.bRemoveTaggedCamera = false;

			// 2a) Raise Special Event overlay "Breaking Point: <RollSource>"
			BannerAction = X2Action_CentralBanner(class'X2Action_CentralBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			BannerAction.BannerText = `XEXPAND.ExpandString(PanicTestCaption);
			BannerAction.BannerState = eUIState_Normal;

			// 2b) Play will loss update on UnitFlag will bar
			UIUpdateAction = X2Action_UpdateUI(class'X2Action_UpdateUI'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			UIUpdateAction.SpecificID = UnitState.ObjectID;
			UIUpdateAction.UpdateType = EUIUT_UnitFlag_Health;

			// 2c) Trigger Panic Event buildup audio FX
			SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			SoundAction.Sound = new class'SoundCue';
			SoundAction.Sound.AkEventOverride = AkEvent'XPACK_SoundTacticalUI.Panic_Check_Start';
			SoundAction.bIsPositional = false;
			SoundAction.vWorldPosition = WorldLocation;
			SoundAction.WaitForCompletion = true;

			// 3a) Wait 0.5s - or until SoundAction is complete
			DelayAction = X2Action_Delay(class'X2Action_Delay'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			DelayAction.Duration = 2.5;
			DelayAction.bIgnoreZipMode = true;

			// 4a) Update Special Event overlay to show panic results
			BannerAction = X2Action_CentralBanner(class'X2Action_CentralBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			BannerAction.BannerText = `XEXPAND.ExpandString(PanicResultFlyover);
			if( PanicAbilityName == '' )
			{
				BannerAction.BannerState = eUIState_Good;
			}
			else
			{
				BannerAction.BannerState = eUIState_Bad;
			}

			// 4b) Trigger Result flyover (ex. "Resisted", "Panicked")
			// Update 4/20 - only trigger if the result was "resisted"
			if( PanicAbilityName == '' )
			{
				FlyoverAction = X2Action_PlaySoundAndFlyover(class'X2Action_PlaySoundAndFlyover'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
				FlyoverAction.SetSoundAndFlyOverParameters(none, `XEXPAND.ExpandString(PanicResultFlyover), '', eColor_Good);
			}

			// 4c) Trigger result audio FX
			SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			SoundAction.Sound = new class'SoundCue';
			if( PanicAbilityName == '' )
			{
				// good result sound
				SoundAction.Sound.AkEventOverride = AkEvent'SoundTacticalUI.TacticalUI_UnitFlagPositive';
			}
			else
			{
				// bad result sound
				SoundAction.Sound.AkEventOverride = AkEvent'SoundTacticalUI.TacticalUI_UnitFlagWarning';
			}
			SoundAction.bIsPositional = false;
			SoundAction.vWorldPosition = WorldLocation;
			SoundAction.WaitForCompletion = false;

			// 5a) complete panic behavior visualization (if applicable)
			// - for now, will just rely on the panic processing to complete the behavior visualization

			// 5a) Wait 1.5s
			DelayAction = X2Action_Delay(class'X2Action_Delay'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			DelayAction.bIgnoreZipMode = true;
			if( PanicAbilityName == '' )
			{
				// if the panic was resisted, there will not be a panic visualization following this action,
				// so we need to give the user more time to enjoy the "resisted" flyover
				DelayAction.Duration = 3.0;
			}
			else
			{
				DelayAction.Duration = 1.5;
			}

			// 6a) Lower Special Event overlay
			BannerAction = X2Action_CentralBanner(class'X2Action_CentralBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			BannerAction.BannerText = "";

			// 6b) restore FOW
			RevealAreaAction = X2Action_RevealArea(class'X2Action_RevealArea'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			RevealAreaAction.bDestroyViewer = true;
			RevealAreaAction.AssociatedObjectID = UnitState.ObjectID;

			// 6c) release camera
			LookAtAction = X2Action_CameraLookAt(class'X2Action_CameraLookAt'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			LookAtAction.CameraTag = 'WillTestCamera';
			LookAtAction.bRemoveTaggedCamera = true;

			// 6d) Play world message explaining what just happened
			// Update 4/20 - no banner needed when resisting; the panic state will handle the banner when the will check is failed
			//WorldMessageAction = X2Action_PlayMessageBanner(class'X2Action_PlayMessageBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			//WorldMessageAction.AddMessageBanner(class'UIEventNoticesTactical'.default.PanicCheckTitle,
			//							   /*class'UIUtilities_Image'.const.UnitStatus_Panicked*/,
			//								UnitState.GetName(eNameType_RankFull),
			//							   `XEXPAND.ExpandString(PanicResultWorldMessage),
			//							   (PanicAbilityName == '') ? eUIState_Good : eUIState_Bad);
		}

		// restore HUD components to previous vis state
		UIUpdateAction = X2Action_UpdateUI(class'X2Action_UpdateUI'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
		UIUpdateAction.UpdateType = EUIUT_RestoreHUDVisibility;
	}
}

static event XComGameStateContext_WillRoll CreateWillRollContext(XComGameState_Unit InSourceUnit, name InRollSource, optional string InRollSourceFriendly, optional bool InShowMessages = true)
{
	local XComGameStateContext_WillRoll RollContext;

	`assert(InSourceUnit != none);
	RollContext = XComGameStateContext_WillRoll(CreateXComGameStateContext());
	RollContext.SourceUnit = InSourceUnit;
	RollContext.RollSource = InRollSource;
	RollContext.RollSourceFriendly = InRollSourceFriendly == "" ? default.DefaultLossSource : InRollSourceFriendly;
	RollContext.ShowMessages = InShowMessages;
	return RollContext;
}

event Submit()
{
	local X2TacticalGameRuleset Rules;

	if(SourceUnit.IsDead() || SourceUnit.IsIncapacitated() || SourceUnit.IsMindControlled())
	{
		return;
	}

	if(RunningWillLossTotal > 0 || PanicAbilityName != '')
	{
		Rules = `TACTICALRULES;
		if(Rules == none)
		{
			`Redscreen("Attempting to submit a will roll outside of tactical.");
			return;
		}

		if(!Rules.SubmitGameStateContext(self))
		{
			`Redscreen("Unable to submit will roll context.");
		}
	}
}

function string SummaryString()
{
	return "Will Roll: " $ SourceUnit.GetFullName() $ " lost " $ RunningWillLossTotal $ " Will";
}

//---------------------------------------------------------------------------------------
//  FILE:    X2EventListener_DefaultWillEvents.uc
//  AUTHOR:  David Burchanowski
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------

class XComGameStateContext_WillRoll extends XComGameStateContext
	config(GameCore)
	native(Core);

enum WillEventRoll_StatType
{
	WillEventRollStat_None,
	WillEventRollStat_PercentageHealthLost,
	WillEventRollStat_SquadmateRank,
	WillEventRollStat_MaxWill,
	WillEventRollStat_BondLevel,
	WillEventRollStat_Flat
};

struct native WillEventRollData_PanicWeight
{
	var name PanicAbilityName;
	var float Weight; // 0.0-1.0

	structdefaultproperties
	{
		Weight = 1
	}
};

// This structure provides a simple uniform mechanism for doing rolls to lose will
struct native WillEventRollData
{
	var EMentalState MinimumTiredState;  // this will data will not be applied to units that are less fatigued than this state
	var float WillEventActivationChance;  // if all other conditions are met, this is the chance that this will event should actually modify will/roll on panic
	var name WillEventActivationType;	// if non-none, will events of this type are restricted to only happening once per unit per mission

	// values for determining how much will to lose
	var float WillLossChance;    // chance to lose will.
	var bool FlatWillLossChance; // if true, will do a straight roll against WillLossChance, if false rolls against WillLossChance - eStat_Will

	// calculated will loss is the value of the chosen WillLossStat *  WillLossStatMultiplier.
	// the final will loss is max(<calculated will loss>, MinimumWillLoss)
	var WillEventRoll_StatType WillLossStat;
	var float WillLossStatMultiplier;
	var int MinimumWillLoss;

	var float MaxWillPercentageLostPerMission; // if > 0, will will only be lost up to this percentage of the unit's max will
	var bool CanZeroOutWill; // if false, will always leave at least 1 will remaining

	// When a unit loses will, a roll will be made against PanicChance. If the roll passes,
	// one of the abilities in PanicWeights will be selected.
	var float PanicChance; // 0.0-1.0f, will be further reduced by a unit's will
	var array<WillEventRollData_PanicWeight> PanicWeights;

	structdefaultproperties
	{
		MinimumTiredState = eMentalState_Ready
		WillEventActivationChance = 1.0
	}
};

// text that appears above soldier's head when he loses will from a will event
var localized const string LostWillFlyover;
var localized const string LostWillWorldMessage;
var localized const string DefaultLossSource;
var localized const string PanicTestCaption;
var localized const string PanicResultFlyover;
var localized const string PanicResultWorldMessage;
var localized const string ResistedText;

// Percentage of will to lose per bond level when checking WillEventRollStat_BondLevel
var protected const config array<float>      BondLevelWillLoss;

// the reduction in will loss when this soldier is on a mission with his bondmate
var protected const config float SoldierBondWillReductionMultiplier;

// If a soldier does a "normal" panic ability more than once, reroll using the values in this table
var protected const config array<WillEventRollData_PanicWeight> MultiplePanicAltWeights;

// It's possible to do multiple rolls per context, so keep track of the running total here.
var privatewrite int RunningWillLossTotal;

// the type of will event being conducted
var privatewrite name WillEventType;

// It's possible to get multiple panic results from different rolls, so just use the most recent one
var privatewrite name PanicAbilityName;

// The unit we are performing the roll on.
var privatewrite XComGameState_Unit SourceUnit;
// The target id our source unit is rolling against.
var privatewrite int TargetUnitID;

// Localized friendly name of the roll source. Ex. "Fear of Missing Shots"
var protectedwrite string RollSourceFriendly;
var protectedwrite name RollSource;

// If true, will show flyovers and world messages
var protectedwrite bool ShowMessages;

// If the target unit is affected by any of these Effects, skip the panic roll
var protected const config array<name>      SkipPanicTestWhenAfflictedByEffects;

//////////////////////////////////////////////////////////////////////////////////

static event bool ShouldPerformWillRoll(const out WillEventRollData RollInfo, XComGameState_Unit AffectedUnit)
{
	if( AffectedUnit == none )
	{
		return false;
	}

	// skip this test on any dead, or evacuated units
	if( AffectedUnit.IsDead() ||
	   AffectedUnit.IsInStasis() ||
	   AffectedUnit.bRemovedFromPlay )
	{
		return false;
	}

	// raw random chance to skip this activation
	if( `SYNC_FRAND_STATIC() >= RollInfo.WillEventActivationChance )
	{
		return false;
	}

	return true;
}

static function bool ShouldPerformPanicRoll(const out WillEventRollData RollInfo, XComGameState_Unit AffectedUnit)
{
	local name TestEffect;

	if( RollInfo.PanicChance <= 0.0 )
	{
		return false;
	}

	// restrict rolling on will while panicked
	if( !AffectedUnit.IsAbleToAct() )
	{
		return false;
	}

	// restrict by the units current fatigue state
	if( AffectedUnit.GetMentalState() > RollInfo.MinimumTiredState )
	{
		return false;
	}

	// restrict panic checks to once per turn
	if( AffectedUnit.PanicTestsPerformedThisTurn > 0 )
	{
		return false;
	}

	// restrict if the unit has already rolled on this event type this mission
	if( RollInfo.WillEventActivationType != '' && AffectedUnit.WillEventsActivatedThisMission.Find(RollInfo.WillEventActivationType) != INDEX_NONE )
	{
		return false;
	}

	// Avoid panic tests on units affected by certain status effects
	foreach default.SkipPanicTestWhenAfflictedByEffects(TestEffect)
	{
		if( AffectedUnit.AffectedByEffectNames.Find(TestEffect) != INDEX_NONE )
		{
			return false;
		}
	}

	// skip the roll for panic for any condition that would fail a panic ability
	if( class'X2Condition_Panic'.static.StaticCallMeetsCondition(AffectedUnit) != 'AA_Success' )
	{
		return false;
	}

	return true;
}

// "SquadmateUnit" needs to be supplied when doing rolls against squadmate rank
event DoWillRoll(WillEventRollData RollInfo, optional XComGameState_Unit SquadmateUnit)
{
	if( SquadmateUnit == None )
	{
		SquadmateUnit = SourceUnit;
	}

	TargetUnitID = SquadmateUnit.ObjectID;

	WillEventType = RollInfo.WillEventActivationType;

	CalculateWillRoll(RollInfo, SquadmateUnit, SourceUnit, RollSource, RunningWillLossTotal, PanicAbilityName);

	if( ShouldPerformPanicRoll(RollInfo, SquadmateUnit) )
	{
		// also make a panic roll
		DoPanicRoll(SquadmateUnit, RunningWillLossTotal, RollInfo.PanicChance, RollInfo.PanicWeights, false, PanicAbilityName);
	}
	else
	{
		// never show messages if there was no panic roll
		ShowMessages = false;
	}
}

static function PerformWillRollOnUnitForNewGameState(WillEventRollData RollInfo, XComGameState_Unit InSourceUnit, name InRollSource, XComGameState NewGameState)
{
	local int RunningWillLoss;
	local name PanicAbility;
	local XComGameState_Unit NewSourceUnitState;

	CalculateWillRoll(RollInfo, None, InSourceUnit, InRollSource, RunningWillLoss, PanicAbility);

	NewSourceUnitState = XComGameState_Unit(NewGameState.ModifyStateObject(class'XComGameState_Unit', InSourceUnit.ObjectId));
	NewSourceUnitState.ModifyCurrentStat(eStat_Will, -RunningWillLoss);
	++NewSourceUnitState.PanicTestsPerformedThisTurn;

	if( RollInfo.WillEventActivationType != '' && NewSourceUnitState.WillEventsActivatedThisMission.Find(RollInfo.WillEventActivationType) == INDEX_NONE )
	{
		NewSourceUnitState.WillEventsActivatedThisMission.AddItem(RollInfo.WillEventActivationType);
	}
}

static function CalculateWillRoll(WillEventRollData RollInfo, XComGameState_Unit SquadmateUnit, XComGameState_Unit InSourceUnit, name InRollSource, out int InRunningWillLossTotal, out name InPanicAbilityName)
{
	local XComGameStateHistory History;
	local XComGameState_Unit StartOfMissionUnit;
	local bool ShouldLoseWill;
	local float CalculatedWillLoss;
	local int WillLossOverage;
	local float WillLossRemainder;
	local int CurrentWill; // current will, including the running loss total
	local int MaximumWillLossPerMission;
	local int MinimumAllowedWill;
	local SoldierBond BondData;
	local bool bBondmateOnMission;
	local StateObjectReference BondmateRef;
	local XComGameState_BattleData BattleDataState;
	local X2SitRepEffect_ModifyWillPenalties SitRepEffect;
	local float SitRepWillLossScalar;
	// Start Issue #44
	local float WillStartOfMission;
	local UnitValue WillStartOfMissionUnitVal;
	// End Issue #44

	if( InSourceUnit.IsDead()
		|| InSourceUnit.IsIncapacitated()
		|| InSourceUnit.IsMindControlled())
	{
		return;
	}

	if( InSourceUnit.UsesWillSystem())
	{
		if(RollInfo.FlatWillLossChance)
		{
			ShouldLoseWill = class'Engine'.static.SyncFRand("DoWillRoll") < RollInfo.WillLossChance;
		}
		else
		{
			ShouldLoseWill = class'Engine'.static.SyncFRand("DoWillRoll") < RollInfo.WillLossChance - (InSourceUnit.GetCurrentStat(eStat_Will) * 0.01f);
		}
	}

	if(ShouldLoseWill)
	{
		bBondmateOnMission = InSourceUnit.HasSoldierBond(BondmateRef, BondData) && `XCOMHQ.IsUnitInSquad(BondmateRef);

		// determine how much will we want to lose
		switch(RollInfo.WillLossStat)
		{
		case WillEventRollStat_None:
			CalculatedWillLoss = 0;
			break;
		case WillEventRollStat_PercentageHealthLost:
			CalculatedWillLoss = InSourceUnit.GetCurrentStat(eStat_HP) / InSourceUnit.GetMaxStat(eStat_HP);
			break;
		case WillEventRollStat_SquadmateRank:
			assert(SquadmateUnit != none);
			CalculatedWillLoss = SquadmateUnit.GetSoldierRank();
			break;
		case WillEventRollStat_MaxWill:
			CalculatedWillLoss = InSourceUnit.GetMaxStat(eStat_Will);
			break;
		case WillEventRollStat_BondLevel:
			if( bBondmateOnMission )
			{
				if(BondData.BondLevel < 0 || BondData.BondLevel > default.BondLevelWillLoss.Length)
				{
					`Redscreen("DoWillRoll(): " $ InSourceUnit.GetFullName() $ "has an unexpected bond level " $ BondData.BondLevel);
					BondData.BondLevel = 0;
				}
				CalculatedWillLoss = InSourceUnit.GetMaxStat(eStat_Will) * default.BondLevelWillLoss[BondData.BondLevel];
			}
			break;
		// Start Issue #936
		case WillEventRollStat_Flat:
			CalculatedWillLoss = 1.0f;
			break;
		// End Issue #936
		default:
			`assert(false);
		}

		// Gather sitrep granted abilities
		BattleDataState = XComGameState_BattleData(`XCOMHISTORY.GetSingleGameStateObjectForClass(class'XComGameState_BattleData'));
		SitRepWillLossScalar = 1.0f;
		if (BattleDataState != none)
		{
			foreach class'X2SitreptemplateManager'.static.IterateEffects(class'X2SitRepEffect_ModifyWillPenalties', SitRepEffect, BattleDataState.ActiveSitReps)
			{
				if ((SitRepEffect.WillEventNames.Length == 0) || (SitRepEffect.WillEventNames.Find(InRollSource) != INDEX_NONE))
				{
					SitRepWillLossScalar *= SitRepEffect.ModifyScalar;
				}
			}
		}

		CalculatedWillLoss = (CalculatedWillLoss * RollInfo.WillLossStatMultiplier * SitRepWillLossScalar);

		if( bBondmateOnMission )
		{
			CalculatedWillLoss *= default.SoldierBondWillReductionMultiplier;
		}

		// if we have a remainder, roll on it for an extra integer point
		WillLossRemainder = CalculatedWillLoss % 1.0f;
		if(class'Engine'.static.SyncFRand("DoWillRoll") < WillLossRemainder)
		{
			CalculatedWillLoss += 1;
		}

		// make sure we are taking at least the minimum amount
		CalculatedWillLoss = max(int(CalculatedWillLoss), RollInfo.MinimumWillLoss);

		// since the history hasn't been updated yet, add in the will loss from previous rolls
		CurrentWill = InSourceUnit.GetCurrentStat(eStat_Will) - InRunningWillLossTotal;

		// and determine if we have any lower floor on how much will we are allowed to lose in the mission.
		// by default, we can go all the way to zero
		MinimumAllowedWill = 0;
		if(!RollInfo.CanZeroOutWill)
		{
			// don't allow dropping below 1
			MinimumAllowedWill = 1;
		}

		if(RollInfo.MaxWillPercentageLostPerMission > 0)
		{
			// Start Issue #44
			// If we want to use the changed behavior (no reset of Start-of-mission Will on Tactical-To-Tactical Transfer),
			// check if we have the UnitValue recorded -- otherwise it might break when we add the Highlander in the middle of a mission
			if (class'CHHelpers'.default.MULTIPART_MISSION_WILL_LOSS_CAP && InSourceUnit.GetUnitValue('CH_StartMissionWill', WillStartOfMissionUnitVal))
			{
				WillStartOfMission = WillStartOfMissionUnitVal.fValue;
			}
			else
			{
				// get the unit's will at the start of the mission
				History = `XCOMHISTORY;
				StartOfMissionUnit = XComGameState_Unit(History.GetGameStateForObjectID(InSourceUnit.ObjectID,, History.FindStartStateIndex()));
				WillStartOfMission = StartOfMissionUnit.GetCurrentStat(eStat_Will);
			}
			MaximumWillLossPerMission = RollInfo.MaxWillPercentageLostPerMission * InSourceUnit.GetMaxStat(eStat_Will);
			MinimumAllowedWill = max(MinimumAllowedWill, WillStartOfMission - MaximumWillLossPerMission);
			// End Issue #44
		}

		// now that we know our lower floor, make sure we won't drop below the minimum
		WillLossOverage = CurrentWill - (CalculatedWillLoss + MinimumAllowedWill);
		CalculatedWillLoss -= Max(0, -WillLossOverage);

		// add in the loss for this roll to the running total
		InRunningWillLossTotal += CalculatedWillLoss;
	}
}

static function DoPanicRoll(XComGameState_Unit InSourceUnit, int InRunningWillLossTotal, float PanicChance, array<WillEventRollData_PanicWeight> PanicWeights, bool Reroll, out name InPanicAbilityName)
{
	local int CurrentWill;
	local float PanicRoll;
	local WillEventRollData_PanicWeight PanicWeight;
	local float TotalPanicWeight;

	CurrentWill = InSourceUnit.GetCurrentStat(eStat_Will) - InRunningWillLossTotal;

	PanicRoll = `SYNC_FRAND_STATIC("DoWillRoll");
	if((PanicRoll < (PanicChance - (CurrentWill * 0.01)))
		|| CurrentWill <= 0
		|| (`CHEATMGR != none && `CHEATMGR.bAlwaysPanic))
	{
		// do a weighted pick from the available choices. Add up the total weight of all options,
		// do a random roll from [0..TotalWeight], and then start adding up the weights again.
		// when the total goes over the random number, we have our choice.
		foreach PanicWeights(PanicWeight)
		{
			TotalPanicWeight += PanicWeight.Weight;
		}

		PanicRoll = `SYNC_FRAND_STATIC("DoWillRoll") * TotalPanicWeight;

		TotalPanicWeight = 0.0f;
		foreach PanicWeights(PanicWeight)
		{
			TotalPanicWeight += PanicWeight.Weight;
			if(TotalPanicWeight >= PanicRoll)
			{
				InPanicAbilityName = PanicWeight.PanicAbilityName;

				// To favor the new panic behaviors, if we roll a normal panic and this unit has already
				// done a normal panic on this mission, reroll (but only once)
				if(!Reroll
					&& InPanicAbilityName == 'Panicked'
					&& UnitHasAlreadyUsedNormalPanicThisMission(InSourceUnit))
				{
					DoPanicRoll(InSourceUnit, InRunningWillLossTotal, PanicChance, default.MultiplePanicAltWeights, true, InPanicAbilityName);
				}

				break;
			}
		}
	}
}

// Does what it says, nativized since it can require a lot of iteration late into missions.
static native function bool UnitHasAlreadyUsedNormalPanicThisMission(XComGameState_Unit InSourceUnit);

function bool Validate(optional EInterruptionStatus InInterruptionStatus)
{
	return true;
}

function XComGameState ContextBuildGameState()
{
	local XComGameState NewGameState;
	local XComGameState_Unit UnitState;
	local X2EventManager Manager;
	local Object SelfObj;

	// Add the units to the new game state
	NewGameState = `XCOMHISTORY.CreateNewGameState(true, self);

	UnitState = XComGameState_Unit(NewGameState.ModifyStateObject(SourceUnit.Class, SourceUnit.ObjectId));
	UnitState.ModifyCurrentStat(eStat_Will, -RunningWillLossTotal);

	if( WillEventType != '' && UnitState.WillEventsActivatedThisMission.Find(WillEventType) == INDEX_NONE )
	{
		UnitState.WillEventsActivatedThisMission.AddItem(WillEventType);
	}

	SelfObj = self;
	Manager = `XEVENTMGR;

	// trigger an event to do our panic behavior if one was selected. We need to wait for this context
	// to complete before we can do the panic, and an event will serve that purpose just fine
	if(PanicAbilityName != '')
	{
		Manager.RegisterForEvent(SelfObj, 'DoPanicAbility', OnDoPanicAbility, ELD_OnStateSubmitted);
		Manager.TriggerEvent('DoPanicAbility', UnitState, self, NewGameState);
	}

	NewGameState.GetContext().SetAssociatedPlayTiming(SPT_AfterSequential);

	// Start Issue #13
	Manager.TriggerEvent('WillRollContext', UnitState, self, NewGameState);
	// End Issue #13

	return NewGameState;
}

function EventListenerReturn OnDoPanicAbility(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
	local XComGameStateHistory History;
	local GameRulesCache_Unit UnitCache;
	local XComGameState_Unit UnitState;
	local XComGameState_Ability AbilityState;
	local int i;

	History = `XCOMHISTORY;

	UnitState = XComGameState_Unit(EventData);
	if(EventSource != self)
	{
		return ELR_NoInterrupt;
	}

	// find the specified panic ability and activate it
	if (`TACTICALRULES.GetGameRulesCache_Unit(UnitState.GetReference(), UnitCache))
	{
		for (i = 0; i < UnitCache.AvailableActions.Length; ++i)
		{
			AbilityState = XComGameState_Ability(History.GetGameStateForObjectID(UnitCache.AvailableActions[i].AbilityObjectRef.ObjectID));
			if (AbilityState.GetMyTemplateName() == PanicAbilityName)
			{
				if (UnitCache.AvailableActions[i].AvailableCode == 'AA_Success' && UnitCache.AvailableActions[i].AvailableTargets.Length > 0)
				{
					class'XComGameStateContext_Ability'.static.ActivateAbility(UnitCache.AvailableActions[i], 0, , , , , , , SPT_AfterSequential);
				}
				break;
			}
		}
	}

	`XEVENTMGR.UnRegisterFromEvent(EventSource, 'DoPanicAbility');
	return ELR_NoInterrupt;
}

protected function ContextBuildVisualization()
{
	local VisualizationActionMetadata ActionMetadata, EmptyMetadata;
	local X2Action_PlaySoundAndFlyover FlyoverAction;
	//local X2Action_PlayMessageBanner WorldMessageAction;
	local XComGameState_Unit UnitState;
	local XGParamTag Tag;
	local X2Action_RevealArea RevealAreaAction;
	local XComWorldData WorldData;
	local TTile TileLocation;
	local Vector WorldLocation;
	local X2Action_CameraLookAt LookAtAction;
	local X2Action_StartStopSound SoundAction;
	local X2Action_UpdateUI UIUpdateAction;
	local X2AbilityTemplate AbilityTemplate;
	local X2Action_Delay DelayAction;
	local X2Action_CentralBanner BannerAction;

	// add a flyover track for every unit in the game state. This timing of this is too late to look good
	// but since Ryan is tearing it all out there is no reason to augment the visualization system to allow
	// it to look pretty
	if(ShowMessages)
	{
		WorldData = `XWORLD;

		Tag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
		Tag.StrValue0 = RollSourceFriendly;
		Tag.IntValue0 = RunningWillLossTotal;

		if( PanicAbilityName == '' )
		{
			Tag.StrValue2 = ResistedText;
		}
		else
		{
			AbilityTemplate = class'XComGameState_Ability'.static.GetMyTemplateManager().FindAbilityTemplate(PanicAbilityName);
			Tag.StrValue2 = AbilityTemplate.LocFriendlyName;
		}

		// clear unnecessary HUD components
		UIUpdateAction = X2Action_UpdateUI(class'X2Action_UpdateUI'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
		UIUpdateAction.UpdateType = EUIUT_SetHUDVisibility;
		UIUpdateAction.DesiredHUDVisibility.bMessageBanner = true;

		foreach AssociatedState.IterateByClassType(class'XComGameState_Unit', UnitState)
		{
			UnitState.GetKeystoneVisibilityLocation(TileLocation);
			WorldLocation = WorldData.GetPositionFromTileCoordinates(TileLocation);

			Tag.StrValue1 = UnitState.GetFullName();

			ActionMetadata = EmptyMetadata;
			ActionMetadata.StateObject_OldState = UnitState.GetPreviousVersion();
			ActionMetadata.StateObject_NewState = UnitState;

			Tag.StrValue0 = RollSourceFriendly;

			// new visualization flow:
			// for each affected unit:
			// 1a) Clear FOW around unit
			RevealAreaAction = X2Action_RevealArea(class'X2Action_RevealArea'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			RevealAreaAction.ScanningRadius = class'XComWorldData'.const.WORLD_StepSize * 5.0f;
			RevealAreaAction.TargetLocation = WorldLocation;
			RevealAreaAction.bDestroyViewer = false;
			RevealAreaAction.AssociatedObjectID = UnitState.ObjectID;

			// 1b) Focus camera on unit (wait until centered)
			LookAtAction = X2Action_CameraLookAt(class'X2Action_CameraLookAt'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			LookAtAction.LookAtLocation = WorldLocation;
			LookAtAction.BlockUntilActorOnScreen = true;
			LookAtAction.LookAtDuration = 10.0f;		// longer than we need - camera will be removed by tag below
			LookAtAction.TargetZoomAfterArrival = -0.7f;
			LookAtAction.CameraTag = 'WillTestCamera';
			LookAtAction.bRemoveTaggedCamera = false;

			// 2a) Raise Special Event overlay "Breaking Point: <RollSource>"
			BannerAction = X2Action_CentralBanner(class'X2Action_CentralBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			BannerAction.BannerText = `XEXPAND.ExpandString(PanicTestCaption);
			BannerAction.BannerState = eUIState_Normal;

			// 2b) Play will loss update on UnitFlag will bar
			UIUpdateAction = X2Action_UpdateUI(class'X2Action_UpdateUI'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			UIUpdateAction.SpecificID = UnitState.ObjectID;
			UIUpdateAction.UpdateType = EUIUT_UnitFlag_Health;

			// 2c) Trigger Panic Event buildup audio FX
			SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			SoundAction.Sound = new class'SoundCue';
			SoundAction.Sound.AkEventOverride = AkEvent'XPACK_SoundTacticalUI.Panic_Check_Start';
			SoundAction.bIsPositional = false;
			SoundAction.vWorldPosition = WorldLocation;
			SoundAction.WaitForCompletion = true;

			// 3a) Wait 0.5s - or until SoundAction is complete
			DelayAction = X2Action_Delay(class'X2Action_Delay'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			DelayAction.Duration = 2.5;
			DelayAction.bIgnoreZipMode = true;

			// 4a) Update Special Event overlay to show panic results
			BannerAction = X2Action_CentralBanner(class'X2Action_CentralBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			BannerAction.BannerText = `XEXPAND.ExpandString(PanicResultFlyover);
			if( PanicAbilityName == '' )
			{
				BannerAction.BannerState = eUIState_Good;
			}
			else
			{
				BannerAction.BannerState = eUIState_Bad;
			}

			// 4b) Trigger Result flyover (ex. "Resisted", "Panicked")
			// Update 4/20 - only trigger if the result was "resisted"
			if( PanicAbilityName == '' )
			{
				FlyoverAction = X2Action_PlaySoundAndFlyover(class'X2Action_PlaySoundAndFlyover'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
				FlyoverAction.SetSoundAndFlyOverParameters(none, `XEXPAND.ExpandString(PanicResultFlyover), '', eColor_Good);
			}

			// 4c) Trigger result audio FX
			SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			SoundAction.Sound = new class'SoundCue';
			if( PanicAbilityName == '' )
			{
				// good result sound
				SoundAction.Sound.AkEventOverride = AkEvent'SoundTacticalUI.TacticalUI_UnitFlagPositive';
			}
			else
			{
				// bad result sound
				SoundAction.Sound.AkEventOverride = AkEvent'SoundTacticalUI.TacticalUI_UnitFlagWarning';
			}
			SoundAction.bIsPositional = false;
			SoundAction.vWorldPosition = WorldLocation;
			SoundAction.WaitForCompletion = false;

			// 5a) complete panic behavior visualization (if applicable)
			// - for now, will just rely on the panic processing to complete the behavior visualization

			// 5a) Wait 1.5s
			DelayAction = X2Action_Delay(class'X2Action_Delay'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			DelayAction.bIgnoreZipMode = true;
			if( PanicAbilityName == '' )
			{
				// if the panic was resisted, there will not be a panic visualization following this action,
				// so we need to give the user more time to enjoy the "resisted" flyover
				DelayAction.Duration = 3.0;
			}
			else
			{
				DelayAction.Duration = 1.5;
			}

			// 6a) Lower Special Event overlay
			BannerAction = X2Action_CentralBanner(class'X2Action_CentralBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			BannerAction.BannerText = "";

			// 6b) restore FOW
			RevealAreaAction = X2Action_RevealArea(class'X2Action_RevealArea'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			RevealAreaAction.bDestroyViewer = true;
			RevealAreaAction.AssociatedObjectID = UnitState.ObjectID;

			// 6c) release camera
			LookAtAction = X2Action_CameraLookAt(class'X2Action_CameraLookAt'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			LookAtAction.CameraTag = 'WillTestCamera';
			LookAtAction.bRemoveTaggedCamera = true;

			// 6d) Play world message explaining what just happened
			// Update 4/20 - no banner needed when resisting; the panic state will handle the banner when the will check is failed
			//WorldMessageAction = X2Action_PlayMessageBanner(class'X2Action_PlayMessageBanner'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
			//WorldMessageAction.AddMessageBanner(class'UIEventNoticesTactical'.default.PanicCheckTitle,
			//							   /*class'UIUtilities_Image'.const.UnitStatus_Panicked*/,
			//								UnitState.GetName(eNameType_RankFull),
			//							   `XEXPAND.ExpandString(PanicResultWorldMessage),
			//							   (PanicAbilityName == '') ? eUIState_Good : eUIState_Bad);
		}

		// restore HUD components to previous vis state
		UIUpdateAction = X2Action_UpdateUI(class'X2Action_UpdateUI'.static.AddToVisualizationTree(ActionMetadata, self, false, ActionMetadata.LastActionAdded));
		UIUpdateAction.UpdateType = EUIUT_RestoreHUDVisibility;
	}
}

static event XComGameStateContext_WillRoll CreateWillRollContext(XComGameState_Unit InSourceUnit, name InRollSource, optional string InRollSourceFriendly, optional bool InShowMessages = true)
{
	local XComGameStateContext_WillRoll RollContext;

	`assert(InSourceUnit != none);
	RollContext = XComGameStateContext_WillRoll(CreateXComGameStateContext());
	RollContext.SourceUnit = InSourceUnit;
	RollContext.RollSource = InRollSource;
	RollContext.RollSourceFriendly = InRollSourceFriendly == "" ? default.DefaultLossSource : InRollSourceFriendly;
	RollContext.ShowMessages = InShowMessages;
	return RollContext;
}

event Submit()
{
	local X2TacticalGameRuleset Rules;

	if(SourceUnit.IsDead() || SourceUnit.IsIncapacitated() || SourceUnit.IsMindControlled())
	{
		return;
	}

	if(RunningWillLossTotal > 0 || PanicAbilityName != '')
	{
		Rules = `TACTICALRULES;
		if(Rules == none)
		{
			`Redscreen("Attempting to submit a will roll outside of tactical.");
			return;
		}

		if(!Rules.SubmitGameStateContext(self))
		{
			`Redscreen("Unable to submit will roll context.");
		}
	}
}

function string SummaryString()
{
	return "Will Roll: " $ SourceUnit.GetFullName() $ " lost " $ RunningWillLossTotal $ " Will";
}


Update pb-to-cpp.uc

Update pb-to-cpp.uc

Declare Sub & Declare Function
AsciiZ data type

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.0 on 1/10/2014 2:52:37 PM
# Comment: Converts PB source code to C++; modified by Daniel Corbier

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

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Converts PB source code to C++; modified by Daniel Corbier
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
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
Highlight: True
PassOnce: False
Find: {@Start}
Replace: // This file ({@Eval: Extract(ShortName(InputFile), ".")}.cpp) was converted from {@Eval: Extract(ShortName(InputFile), ".")}.Bas
         // with {@Eval: AppName} on {@Eval: TimeStamp()} using the Open Source
         // PowerBASIC to C++ converter found at https://github.com/uCalc/powerbasic-to-cpp

         {@Define:
            Var: Array As String
            Var: ArrayNames As Table
            Var: CleanUp As String
            Var: BitType As String
         }

Criteria: 2
Comment: Handles long line break (underscore _)
Pass: 1

Criteria: 3
Highlight: True
PassOnce: False
Find: {nl}{line} _ [{comment:".*"}] {nl}

Replace: {comment: ' {comment}{nl}}{nl}{line}{sp}

Criteria: 4
Highlight: True
SkipOver: True
Find: {"[\(\)]"} {@Note: These are ( and ); must be skipped}
Replace: [Skip over]

Criteria: 5
Highlight: True
SkipOver: True
Find: ' [{comment:".*"}]
Replace: [Skip over]

Criteria: 6
Comment: Processes some operators
Pass: 2

Criteria: 7
Comment: Bitwise AND and OR
Highlight: True
Find: ({expr%})
Replace: ({@Eval: Replace(Replace(Replace('{expr}', "And", "&"), "Or", "|"), "=", "==")})

Criteria: 8
Comment: Logical (short-circuit) AND
Highlight: True
Find: And
Replace: &&

Criteria: 9
Comment: Logical (short-circuit) OR
Highlight: True
Find: Or
Replace: ||

Criteria: 10
Highlight: True
Find: Xor
Replace: ^

Criteria: 11
Highlight: True
Find: Not
Replace: ~

Criteria: 12
Highlight: True
Find: IsFalse
Replace: !

Criteria: 13
Highlight: True
Find: <>
Replace: !=

Criteria: 14
Highlight: True
Find: =
Replace: ==

Criteria: 15
Highlight: True
SkipOver: True
Find: { {nl}[%]|Then|Else|For|: } {var:" *[a-z\@\.]+"}[({index})] =
Replace: [Skip over]

Criteria: 16
Highlight: True
SkipOver: True
Find: { { #If | #ElseIf } Not | ' [{comment:".*"}] }
Replace: [Skip over]

Criteria: 17
Highlight: True
SkipOver: True
Find: Macro {name}[({params})] =
Replace: [Skip over]

Criteria: 18
Highlight: True
PassOnce: False
Find: Sub {name}([{args}]) [{comment:" *'.*"}] {nl}
         {code+}
      End Sub
Replace: void {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

Criteria: 19
Highlight: True
BackColor: DarkKhaki
PassOnce: False
Find: Function {name}([{args}]) As {type}[{comment:" *'.*"}]{nl}
         {code+}
      End Function
Replace: {type} {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

Criteria: 20
Highlight: True
PassOnce: False
Find: Function PBMain [()] [As Long]
Replace: Function main() As Long

Criteria: 21
Highlight: True
PassOnce: False
Find: Declare Function {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])] As {type}
Replace: {lib: extern __declspec(dllimport)} {type} {name}({args})

Criteria: 22
Highlight: True
Find: Declare Sub {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])]
Replace: {lib: extern __declspec(dllimport)} void {name}({args})

Criteria: 23
Highlight: True
Find: Static
Replace: static dim

Criteria: 24
Comment:
Pass: 3

Criteria: 25
Highlight: True
SkipOver: True
Find: // [{comment:".*"}]
Replace: [Skip over]

Criteria: 26
Highlight: True
Find: ' [{comment:".*"}]
Replace: // {comment}

Criteria: 27
Highlight: True
BackColor: Green
PassOnce: False
Find: {nl}%{equate} = {value}
Replace: {nl}const int {equate} = {value};

Criteria: 28
Highlight: True
BackColor: Green
Find: %{equate}
Replace: {equate}

Criteria: 29
Highlight: True
BackColor: SandyBrown
Find: {"&h"}
Replace: 0x

Criteria: 30
Highlight: True
BackColor: Green
PassOnce: False
Find: If {cond} Then {statement} [Else {else}]{nl}
Replace: if ({cond}) {{statement}{nl}}{else: else {{else}{nl}}}{nl}

Criteria: 31
Highlight: True
PassOnce: False
Find: If {cond} Then {nl}
         {code+}
      End If
Replace: if ({cond}) {
            {code}
         }

Criteria: 32
Highlight: True
PassOnce: False
Find: For {x} = {start} To {stop} [Step {inc=1}]{nl}
         {code+}
      Next
Replace: for ({x}={start}; {x}{@Eval: IIF(sgn({inc})>0, '<', '>')}={stop}; {x} += {inc}) {
            {code}
         }

Criteria: 33
Highlight: True
PassOnce: False
Find: While {cond} {nl}
         {code+}
      Wend
Replace: while ({cond}) {
            {code}
         }

Criteria: 34
Highlight: True
PassOnce: False
Find: Do { {while: While} | {until: Until} } {cond} {nl}
         {code+}
      Loop
Replace: while ({while: {cond}}{until:!({cond})}) {
            {code}
         }

Criteria: 35
Highlight: True
PassOnce: False
Find: Do{nl}
         {code+}
      Loop { {while: While} | {until: Until} } {cond}
Replace: do {
            {code}
         } while ({while: {cond}}{until:!({cond})})

Criteria: 36
Highlight: True
Find: !!ReleaseDynamicArrays!!
Replace: {@Eval:
            CleanUp = ""
            uc_For(x, 1, Count(ArrayNames), 1,
               Array = ReadKey(ArrayNames, x)
               CleanUp=CleanUp+"delete[] "+Array+";{nl}"
               Delete(ArrayNames, Array)
            )
            CleanUp
         }

Criteria: 37
Highlight: True
BackColor: Orange
PassOnce: False
Find: { Dim | {member: {nl}}} {var:1} As {type} [{ptr: Ptr}]
Replace: {member: {nl}}{type} {ptr:*}{var};

Criteria: 38
Highlight: True
PassOnce: False
Find: {nl} {bitfield:1} As Bit * {size} [{in: In {type}}]
Replace: {nl}{@Eval:
            IIf("{type}" <> "", SetVar(BitType, "{type}"))
            BitType
         } {bitfield} : {size};

Criteria: 39
Highlight: True
PassOnce: False
Find: Dim {array}({subscript}) As {type}
Replace: {type} *{array} = new {type} [{subscript}+1];{@Eval:
            Insert(ArrayNames, "{array}")
         }{@Define:: Syntax: {array}({index}) ::= {array}[{index}]}

Criteria: 40
Highlight: True
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

Criteria: 41
Highlight: True
BackColor: Gold
PassOnce: False
Find: { Local | Global | Register }
Replace: Dim

Criteria: 42
Highlight: True
Find: @
Replace: *

Criteria: 43
Highlight: True
BackColor: Silver
Find: VarPtr({var})
Replace: &{var}

Criteria: 44
Highlight: True
BackColor: DeepSkyBlue
PassOnce: False
Find: Function = {value}
Replace: return {value};

Criteria: 45
Highlight: True
Find: Exit Sub
Replace: return;

Criteria: 46
Highlight: True
BackColor: Lime
Find: Long
Replace: int

Criteria: 47
Highlight: True
BackColor: Red
Find: Single
Replace: float

Criteria: 48
Highlight: True
Find: { Double | Extended | Ext }
Replace: double{@Note: May instead use "long double" for Extended}

Criteria: 49
Highlight: True
Find: Byte
Replace: unsigned char

Criteria: 50
Highlight: True
Find: Integer
Replace: short

Criteria: 51
Highlight: True
Find: Word
Replace: unsigned short

Criteria: 52
Highlight: True
Find: Dword
Replace: unsigned int

Criteria: 53
Highlight: True
Find: Quad
Replace: long long

Criteria: 54
Selected: True
Find: AsciiZ
Replace: LPCSTR

Criteria: 55
Highlight: True
BackColor: SlateBlue
PassOnce: False
Find: ByRef {arg} As {type:1}
Replace: {type}& {arg}

Criteria: 56
Highlight: True
BackColor: Pink
PassOnce: False
Find: ByVal {arg} As {type:1} [{ptr: Ptr}]
Replace: {type} {ptr:*}{arg}

Criteria: 57
Highlight: True
PassOnce: False
Find: Type {name:1}
         {members+}
      End Type
Replace: struct {name} {
            {members}
         }

Criteria: 58
Highlight: True
Find: Macro {name} [{params: ({paramlist})}] = {replacement}
Replace: #define {name}{params} {replacement}

Criteria: 59
Highlight: True
Find: #If
Replace: #if

Criteria: 60
Highlight: True
Find: #Else
Replace: #else

Criteria: 61
Highlight: True
Find: #ElseIf
Replace: #elif

Criteria: 62
Highlight: True
Find: #EndIf
Replace: #endif

Criteria: 63
Highlight: True
PassOnce: False
Find: [{NOT: Not }] %Def({const})
Replace: {NOT:!}defined {const}

Criteria: 64
Comment: Adds semi-colons to statements
Pass: 4

Criteria: 65
Highlight: True
SkipOver: True
Find: { "{" | "}" | ; | //[{".*"}] | #{".*"} } {nl} [{"[ \n]+"}]
Replace: [Skip over]

Criteria: 66
Comment: Skips over so that colons in bit fields are not affected
Highlight: True
SkipOver: True
Find: struct {name:1} "{" {members+} "}"
Replace: [Skip over]

Criteria: 67
Highlight: True
Find: :
Replace: ;

Criteria: 68
Highlight: True
PassOnce: False
Find: {nl}
Replace: ;{nl}

# End Search

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.0 on 1/8/2014 10:14:52 AM
# Comment: Converts PB source code to C++; modified by Daniel Corbier

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

FindMode: Replace

# Definitions

# Search Criteria

Criteria: 0
Enabled: True
Exclude: False
Comment: Converts PB source code to C++; modified by Daniel Corbier
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
Min: 0
Max: -1
MinSoft: 0
MaxSoft: -1
BatchAction: Transform
OutputFile:
SEND:
StartAfter: 0
StopAfter: -1
SkipOver: False
ParentChild: 0
Pass: 0
PassOnce: True
Precedence: 0
RightToLeft: False

Criteria: 1
PassOnce: False
Find: {@Start}
Replace: // This file ({@Eval: Extract(ShortName(InputFile), ".")}.cpp) was converted from {@Eval: Extract(ShortName(InputFile), ".")}.Bas
         // with {@Eval: AppName} on {@Eval: TimeStamp()} using the Open Source
         // PowerBASIC to C++ converter found at https://github.com/uCalc/powerbasic-to-cpp

         {@Define:
            Var: Array As String
            Var: ArrayNames As Table
            Var: CleanUp As String
            Var: BitType As String
         }

Criteria: 2
Comment: Handles long line break (underscore _)
Pass: 1

Criteria: 3
PassOnce: False
Find: {nl}{line} _ [{comment:".*"}] {nl}

Replace: {comment: ' {comment}{nl}}{nl}{line}{sp}

Criteria: 4
SkipOver: True
Find: {"[\(\)]"} {@Note: These are ( and ); must be skipped}
Replace: [Skip over]

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

Criteria: 6
Comment: Processes some operators
Pass: 2

Criteria: 7
Comment: Bitwise AND and OR
Find: ({expr%})
Replace: ({@Eval: Replace(Replace(Replace('{expr}', "And", "&"), "Or", "|"), "=", "==")})

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

Criteria: 9
Comment: Logical (short-circuit) OR
Find: Or
Replace: ||

Criteria: 10
Find: Xor
Replace: ^

Criteria: 11
Find: Not
Replace: ~

Criteria: 12
Find: IsFalse
Replace: !

Criteria: 13
Find: <>
Replace: !=

Criteria: 14
Find: =
Replace: ==

Criteria: 15
SkipOver: True
Find: { {nl}[%]|Then|Else|For|: } {var:" *[a-z\@\.]+"}[({index})] =
Replace: [Skip over]

Criteria: 16
SkipOver: True
Find: { { #If | #ElseIf } Not | ' [{comment:".*"}] }
Replace: [Skip over]

Criteria: 17
Selected: True
SkipOver: True
Find: Macro {name}[({params})] =
Replace: [Skip over]

Criteria: 18
PassOnce: False
Find: Sub {name}([{args}]) [{comment:" *'.*"}] {nl}
         {code+}
      End Sub
Replace: void {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

Criteria: 19
BackColor: DarkKhaki
PassOnce: False
Find: Function {name}([{args}]) As {type}[{comment:" *'.*"}]{nl}
         {code+}
      End Function
Replace: {type} {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

Criteria: 20
PassOnce: False
Find: Function PBMain [()] [As Long]
Replace: Function main() As Long

Criteria: 21
Find: Static
Replace: static dim

Criteria: 22
Comment:
Pass: 3

Criteria: 23
SkipOver: True
Find: // [{comment:".*"}]
Replace: [Skip over]

Criteria: 24
Find: ' [{comment:".*"}]
Replace: // {comment}

Criteria: 25
BackColor: Green
PassOnce: False
Find: {nl}%{equate} = {value}
Replace: {nl}const int {equate} = {value};

Criteria: 26
BackColor: Green
Find: %{equate}
Replace: {equate}

Criteria: 27
BackColor: SandyBrown
Find: {"&h"}
Replace: 0x

Criteria: 28
BackColor: Green
PassOnce: False
Find: If {cond} Then {statement} [Else {else}]{nl}
Replace: if ({cond}) {{statement}{nl}}{else: else {{else}{nl}}}{nl}

Criteria: 29
PassOnce: False
Find: If {cond} Then {nl}
         {code+}
      End If
Replace: if ({cond}) {
            {code}
         }

Criteria: 30
PassOnce: False
Find: For {x} = {start} To {stop} [Step {inc=1}]{nl}
         {code+}
      Next
Replace: for ({x}={start}; {x}{@Eval: IIF(sgn({inc})>0, '<', '>')}={stop}; {x} += {inc}) {
            {code}
         }

Criteria: 31
PassOnce: False
Find: While {cond} {nl}
         {code+}
      Wend
Replace: while ({cond}) {
            {code}
         }

Criteria: 32
PassOnce: False
Find: Do { {while: While} | {until: Until} } {cond} {nl}
         {code+}
      Loop
Replace: while ({while: {cond}}{until:!({cond})}) {
            {code}
         }

Criteria: 33
PassOnce: False
Find: Do{nl}
         {code+}
      Loop { {while: While} | {until: Until} } {cond}
Replace: do {
            {code}
         } while ({while: {cond}}{until:!({cond})})

Criteria: 34
Find: !!ReleaseDynamicArrays!!
Replace: {@Eval:
            CleanUp = ""
            uc_For(x, 1, Count(ArrayNames), 1,
               Array = ReadKey(ArrayNames, x)
               CleanUp=CleanUp+"delete[] "+Array+";{nl}"
               Delete(ArrayNames, Array)
            )
            CleanUp
         }

Criteria: 35
BackColor: Orange
PassOnce: False
Find: { Dim | {member: {nl}}} {var:1} As {type} [{ptr: Ptr}]
Replace: {member: {nl}}{type} {ptr:*}{var};

Criteria: 36
PassOnce: False
Find: {nl} {bitfield:1} As Bit * {size} [{in: In {type}}]
Replace: {nl}{@Eval:
            IIf("{type}" <> "", SetVar(BitType, "{type}"))
            BitType
         } {bitfield} : {size};

Criteria: 37
PassOnce: False
Find: Dim {array}({subscript}) As {type}
Replace: {type} *{array} = new {type} [{subscript}+1];{@Eval:
            Insert(ArrayNames, "{array}")
         }{@Define:: Syntax: {array}({index}) ::= {array}[{index}]}

Criteria: 38
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

Criteria: 39
BackColor: Gold
PassOnce: False
Find: { Local | Global | Register }
Replace: Dim

Criteria: 40
Find: @
Replace: *

Criteria: 41
BackColor: Silver
Find: VarPtr({var})
Replace: &{var}

Criteria: 42
BackColor: DeepSkyBlue
PassOnce: False
Find: Function = {value}
Replace: return {value};

Criteria: 43
Find: Exit Sub
Replace: return;

Criteria: 44
BackColor: Lime
Find: Long
Replace: int

Criteria: 45
BackColor: Red
Find: Single
Replace: float

Criteria: 46
Find: { Double | Extended | Ext }
Replace: double{@Note: May instead use "long double" for Extended}

Criteria: 47
Find: Byte
Replace: unsigned char

Criteria: 48
Find: Integer
Replace: short

Criteria: 49
Find: Word
Replace: unsigned short

Criteria: 50
Find: Dword
Replace: unsigned int

Criteria: 51
Find: Quad
Replace: long long

Criteria: 52
BackColor: SlateBlue
PassOnce: False
Find: ByRef {arg} As {type:1}
Replace: {type}& {arg}

Criteria: 53
BackColor: Pink
PassOnce: False
Find: ByVal {arg} As {type:1} [{ptr: Ptr}]
Replace: {type} {ptr:*}{arg}

Criteria: 54
PassOnce: False
Find: Type {name:1}
         {members+}
      End Type
Replace: struct {name} {
            {members}
         }

Criteria: 55
Find: Macro {name} [{params: ({paramlist})}] = {replacement}
Replace: #define {name}{params} {replacement}

Criteria: 56
Find: #If
Replace: #if

Criteria: 57
Find: #Else
Replace: #else

Criteria: 58
Find: #ElseIf
Replace: #elif

Criteria: 59
Find: #EndIf
Replace: #endif

Criteria: 60
PassOnce: False
Find: [{NOT: Not }] %Def({const})
Replace: {NOT:!}defined {const}

Criteria: 61
Comment: Adds semi-colons to statements
Pass: 4

Criteria: 62
SkipOver: True
Find: { "{" | "}" | ; | //[{".*"}] | #{".*"} } {nl} [{"[ \n]+"}]
Replace: [Skip over]

Criteria: 63
Comment: Skips over so that colons in bit fields are not affected
SkipOver: True
Find: struct {name:1} "{" {members+} "}"
Replace: [Skip over]

Criteria: 64
Find: :
Replace: ;

Criteria: 65
PassOnce: False
Find: {nl}
Replace: ;{nl}

# End Search