Allow mods to add ruleset observers

Allow mods to add ruleset observers

//---------------------------------------------------------------------------------------
//  FILE:    X2GameRuleset.uc
//  AUTHOR:  Ryan McFall  --  10/9/2013
//  PURPOSE: The rule set is the interface between the game state and the systems that want
//			 to change it. These systems include: player UI, AI, cheat manager, kismet,
//			 and multi-player. The rule set is responsible for enforcing game rules like
//			 interrupts and processing game state based events.
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------
class X2GameRuleset extends Actor
	dependson(XGTacticalGameCore, XComGameState, XComGameState_BaseObject)
	native(Core);

cpptext
{
	/* Latent Function Declarations */
	DECLARE_FUNCTION(execPollLatentWaitingForPlayerSync);
}

struct native AvailableTarget
{
	var StateObjectReference PrimaryTarget;             //  unit the user should be targeting via UI.
	var array<StateObjectReference> AdditionalTargets;  //  units that will be affected by the ability but are not directly targeted.
														//  all targets live here for free aim abilities.
	structcpptext
	{
		FAvailableTarget()
		{
			appMemzero(this, sizeof(FAvailableTarget));
		}

		FAvailableTarget(EEventParm)
		{
			appMemzero(this, sizeof(FAvailableTarget));
		}
	}
};

struct native AvailableAction
{
	var StateObjectReference AbilityObjectRef;
	var array<AvailableTarget> AvailableTargets;
	var int AvailableTargetCurrIndex;				//  points to which of the target we are targeting at right now.
	var bool bFreeAim;                          //  if this is true, targets will change based on cursor location.
	var name AvailableCode;     //  if this is anything except 'AA_Success', the ability can't be used.
	var EAbilityIconBehavior eAbilityIconBehaviorHUD;   //  store off whether this ability should be shown in the HUD.
	var bool bInputTriggered;                   //  true if this ability is 'active' IE - it is triggered by player input.
	var int ShotHUDPriority;                    // this number is used to sort the icon position in the Ability Container in Tactical HUD.

	structcpptext
	{
		FAvailableAction()
		{
			appMemzero(this, sizeof(FAvailableAction));
		}

		FAvailableAction(EEventParm)
		{
			appMemzero(this, sizeof(FAvailableAction));
		}
	}
};

struct native GameRulesCache_Unit
{
	var StateObjectReference UnitObjectRef;
	var array<AvailableAction> AvailableActions;
	var bool bAnyActionsAvailable;
	var int LastUpdateHistoryIndex; // keep track of the last history index this cache was updated so we can update the cache only as needed

	structcpptext
	{
		FGameRulesCache_Unit()
		{
			appMemzero(this, sizeof(FGameRulesCache_Unit));
		}

		FGameRulesCache_Unit(EEventParm)
		{
			appMemzero(this, sizeof(FGameRulesCache_Unit));
		}
	}
};

//******** Event Observers
var /*protected*/ array<class> EventObserverClasses; //Add to this array in default properties of the concrete ruleset class to add observers. Issue #481: unprotected
var protected array<X2GameRulesetEventObserverInterface> EventObservers; //The list of instantiated event observer. One per entry in EventObserverClasses

//******** Cached systems variables
var XComGameStateHistory CachedHistory; //Cache to the singleton XComGameStateHistory, manages game state
var XComGameStateVisualizationMgr VisualizationMgr; //`XCOMVISUALIZATIONMGR - processes history frames to show the player game state changes
var XComGameStateNetworkManager NetworkMgr; //Cached reference to the network manager to support MP
var X2GameRulesetVisibilityManager VisibilityMgr; //Cached reference to the visibility manager, manages visibility relationships between game state objects

//******** Local rules processing variables
var transient private array<XComGameState> BuiltGameStateStack;
var private int ContextBuildDepth; //Keep track of how far we've recursed into building game states
var protected bool bWaitingForNewStates; //This flag is set to true when the rules engine is waiting for a decision on what action will be taken next
var protectedwrite bool bProcessingLoad; //This flag is used to determine whether the game rules are processing a load, and thus should not admit changes to the game state
var protected int CurrentSyncPoint; // Current location where the ruleset is waiting for a network sync; this is automatically updated per LatentWaitingForPlayerSync call.
var private bool bProcessingInterrupt; //This is a flag set within the interrupt processing loop. It is set to true and then cleared if an interrupt is encountered. This is used to
									   //tag the first history frame in an interrupt with the history frame it was interrupting.
var private int HistoryIndexInterrupted; //Used with bProcessingInterrupt

//******** Latent processing variables
var privatewrite bool BuildingLatentGameState; // true while a game state is building on another thread
var private delegate<LatentSubmitGameStateContextCallbackFn> LatentCallbackFunction; // function to be called when the latent game state completes

struct native LatentSubmissionResult
{
	var XComGameState LatentSubmitGameStateResult;
	var delegate<LatentSubmitGameStateContextCallbackFn> LatentCallbackFunction; // function to be called when the latent game state completes
};

var array<LatentSubmissionResult> LatentSubmissionResults;

native function bool IsDoingLatentSubmission();

// Callback delegate prototype for latent context submission
delegate LatentSubmitGameStateContextCallbackFn(XComGameState SubmittedGameState);

/// <summary>
/// Returns a state object reference to the battle data
/// </summary>
simulated function StateObjectReference GetCachedBattleDataRef();

/// <summary>
/// Entry point for the rules engine if the map URL indicates this IS NOT a loaded save game
/// </summary>
simulated function StartNewGame();

/// <summary>
/// Entry point for the rules engine if the map URL indicates this IS a loaded save game
/// </summary>
simulated function LoadGame();

/// <summary>
/// This method builds a local list of state object references for objects that are relatively static, and that we
/// may need to access frequently. Using the cached ObjectID from a game state object reference is much faster than
/// searching for it each time we need to use it.
/// </summary>
simulated function BuildLocalStateObjectCache()
{
	local int Index;
	local X2GameRulesetEventObserverInterface EventObserver;

	CachedHistory = `XCOMHISTORY;
	VisualizationMgr = `XCOMVISUALIZATIONMGR;
	NetworkMgr = `XCOMNETMANAGER;

	for(Index = 0; Index < EventObserverClasses.Length; ++Index)
	{
		EventObserver = new(self)EventObserverClasses[Index];
		EventObserver.Initialize();
		EventObservers.AddItem(EventObserver);
	}
}

/// <summary>
/// Submits a game state latently on a separate hardware thread, and calls the specified callback (if any)
/// when submission is complete
/// </summary>
native function LatentSubmitGameStateContext(XComGameStateContext_Ability NewContext, optional delegate<LatentSubmitGameStateContextCallbackFn> CompletionCallback);

event Tick(float DeltaTime)
{
	local LatentSubmissionResult latentResult;
	local XComGameState LatentSubmitGameStateResult;

	if( ! IsDoingLatentSubmission() )
	{
		// A latent submission has completed and filled out this sentinel object. Now that we're back
		// on the main thread, we can safely clean up after it.

		// do this now in case other gamestates have to be submitted in response
		`XCOMHISTORY.EndLatentUpdate();
		BuildingLatentGameState = false;

		foreach LatentSubmissionResults(latentResult)
		{
			LatentCallbackFunction = latentResult.LatentCallbackFunction;
			LatentSubmitGameStateResult = latentResult.LatentSubmitGameStateResult;

			LatentCallbackFunction( LatentSubmitGameStateResult );
		}

		LatentSubmissionResults.Length = 0;

	}

	super.Tick(DeltaTime);

}

/// <summary>
/// The SubmitGameStateXXX methods are the interface by which the UI, AI, and MP subsystems push new game states
/// into the history. SubmitGameStateContext is used by AI and UI to indicate what action they intend to perform
/// when they are allowed input.
///
/// In some cases the resulting history frames will match exactly what the incoming context is requesting. However
/// actions can be interrupted which may result in a sequence of unintended state changes ( such as a unit being
/// killed while moving ). The rules engine is responsible for converting the state change context into a
/// series of new history states/frames.
/// Return true of successful, false otherwise
/// </summary>
simulated event bool SubmitGameStateContext(XComGameStateContext Context, bool bAISpawningCode = false)
{
	local int Index;
	local bool bCanAdd;

	local bool bWasWaitingForNewStates;

	bCanAdd = AddNewStatesAllowed();
	if(bCanAdd)
	{
		if(ContextBuildDepth == 0)
		{
			bWasWaitingForNewStates = bWaitingForNewStates;
			bWaitingForNewStates = false;
			CachedHistory.BeginEventChain();
		}

		if(Context.bNetworkAdded)
		{
			// Use the exiting random seed from the given context
			Context.SetEngineRandSeedFromContext();
		}
		else
		{
			// Any further replay will need the original context's starting rand seed, so store the engine's seed into this context.
			Context.SetContextRandSeedFromEngine();

			//Send off any contexts that are generated via "user-input"
			if(NetworkMgr != none && NetworkMgr.HasConnections())
			{
				if(Context.bSendGameState)
				{
					Context.SetSendGameState(false);
					NetworkMgr.QueueContextForSending(Context, CachedHistory.GetCurrentHistoryIndex());
				}
				else
				{
					`log(`location @ "Did not Queue Context for Sending!! " @ Context.SummaryString() @ `ShowVar(GetStateName(), State) @ `ShowVar(bWasWaitingForNewStates) @ `ShowVar(bWaitingForNewStates) @ `ShowVar(ContextBuildDepth));
					ScriptTrace();
				}
			}
		}

		//Anything that should only be run ONCE per external call to SubmitGameStateContext should go in here.
		if(ContextBuildDepth == 0)
		{
			for(Index = 0; Index < EventObservers.Length; ++Index)
			{
				EventObservers[Index].CacheGameStateInformation();
			}
		}

		//@TODO - rmcfall - find a home for these ( other turn phases? )
		/*
		1. Turn starts 	a. Ex Rapid Regen
		2. Turn ends 	a. Ex EXALTs Regen Pheromones
		3. Mission start 	a. Ex (maybe): MEC Heavys Body Shield, if mission starts with enemies visible
		4. End of mission 	a. XP bonuses from abilities (like medals)
		*/
		ContextBuildDepth++;

		//Process any preemptive events
		/*
		* Ex:
		* 1. A unit state variable needs to change when an ability context has been submitted ala. ReflexObserver
		*/
		for(Index = 0; Index < EventObservers.Length && Context != none; ++Index)
		{
			EventObservers[Index].PreBuildGameStateFromContext(Context);
		}

		// Added this function to be reused for SubmitGameStateContexts function.
		SubmitGameStateContext_Internal(Context, bWasWaitingForNewStates);

		ContextBuildDepth--;

		if (ContextBuildDepth == 0)
		{
			CachedHistory.EndEventChain();
		}
	}
	else if (`REPLAY.bInTutorial)
	{
		return `TUTORIAL.HandleSubmittedGameStateContext(Context);
	}

	assert(ContextBuildDepth >= 0);

	return true;
}

/// <summary>
//  Same as SubmitGameStateContext, but takes several contexts to be submitted sequentially.
/// </summary>
simulated event bool SubmitGameStateContexts(array<XComGameStateContext> Contexts, bool bAISpawningCode = false, bool bGroupVisualizations = false)
{
	local int Index;
	local bool bCanAdd;

	//Used for interruption processing
	local XComGameStateContext Context;

	local bool bWasWaitingForNewStates;

	local int VisualizeAtHistoryIndex;

	bCanAdd = AddNewStatesAllowed();
	if(bCanAdd)
	{
		if(ContextBuildDepth == 0)
		{
			bWasWaitingForNewStates = bWaitingForNewStates;
			bWaitingForNewStates = false;
			CachedHistory.BeginEventChain();
		}

		Context = Contexts[0];
		if(Context.bNetworkAdded)
		{
			// Use the exiting random seed from the given context
			Context.SetEngineRandSeedFromContext();
		}
		else
		{
			// Any further replay will need the original context's starting rand seed, so store the engine's seed into this context.
			Context.SetContextRandSeedFromEngine();

			//Send off any contexts that are generated via "user-input"
			if(NetworkMgr != none && NetworkMgr.HasConnections())
			{
				if(Context.bSendGameState)
				{
					Context.SetSendGameState(false);
					NetworkMgr.QueueContextForSending(Context, CachedHistory.GetCurrentHistoryIndex());
				}
				else
				{
					`log(`location @ "Did not Queue Context for Sending!! " @ Context.SummaryString() @ `ShowVar(GetStateName(), State) @ `ShowVar(bWasWaitingForNewStates) @ `ShowVar(bWaitingForNewStates) @ `ShowVar(ContextBuildDepth));
					ScriptTrace();
				}
			}
		}

		//Anything that should only be run ONCE per external call to SubmitGameStateContext should go in here.
		if(ContextBuildDepth == 0)
		{
			for(Index = 0; Index < EventObservers.Length; ++Index)
			{
				EventObservers[Index].CacheGameStateInformation();
			}
		}

		//@TODO - rmcfall - find a home for these ( other turn phases? )
		/*
		1. Turn starts 	a. Ex Rapid Regen
		2. Turn ends 	a. Ex EXALTs Regen Pheromones
		3. Mission start 	a. Ex (maybe): MEC Heavys Body Shield, if mission starts with enemies visible
		4. End of mission 	a. XP bonuses from abilities (like medals)
		*/
		ContextBuildDepth++;

		//Process any preemptive events
		/*
		* Ex:
		* 1. A unit state variable needs to change when an ability context has been submitted ala. ReflexObserver
		*/
		for(Index = 0; Index < EventObservers.Length && Context != none; ++Index)
		{
			EventObservers[Index].PreBuildGameStateFromContext(Context);
		}

		VisualizeAtHistoryIndex = CachedHistory.GetEventChainStartIndex();

		foreach Contexts(Context)
		{
			if( bGroupVisualizations && VisualizeAtHistoryIndex != INDEX_NONE )
			{
				Context.SetDesiredVisualizationBlockIndex(VisualizeAtHistoryIndex);
			}

			SubmitGameStateContext_Internal(Context, bWasWaitingForNewStates);
		}

		ContextBuildDepth--;

		if (ContextBuildDepth == 0)
		{
			CachedHistory.EndEventChain();
		}
	}
	else if (`REPLAY.bInTutorial)
	{
		return `TUTORIAL.HandleSubmittedGameStateContext(Context);
	}

	assert(ContextBuildDepth >= 0);

	return true;
}

// Guts of the SubmitGameStateContext, pulled out for reuse
function SubmitGameStateContext_Internal(XComGameStateContext Context, bool bWasWaitingForNewStates)
{
	local int InterruptStep, LastInterruptedStep;
	local XComGameState SubmittedGameState;
	local bool bStateInterrupted;

	//Used for interruption processing
	local XComGameState InterruptedGameState;
	local XComGameState LastInterruptedGameState;
	local XComGameStateContext InterruptedContext;

	//locally cached copies of these vars at the start of processing, since this method is recursive
	local bool bProcessingInterrupt_Cached;
	local int HistoryIndexInterrupted_Cached;

	bProcessingInterrupt_Cached = bProcessingInterrupt;
	HistoryIndexInterrupted_Cached = HistoryIndexInterrupted;

	//Process any interrupt events
	/*
	*  Ex:
	*  1. Unit enters tile / uses ability that causes another ability to fire (Overwatch) (Close Combat Specialist)
	*  3. Unit sighted (AI Alert)
	*/
	bStateInterrupted = false;
	InterruptStep = 0;
	LastInterruptedGameState = None;
	do
	{
		InterruptedGameState = Context.ContextBuildInterruptedGameState(InterruptStep, eInterruptionStatus_Interrupt);
		if( InterruptedGameState != none )
		{
			if( LastInterruptedGameState != None )
			{
				//Set internal state of the contexts for the interrupted and resumed game states, linking them.
				class'XComGameStateContext'.static.InterruptionPostProcess(LastInterruptedGameState, InterruptedGameState);
			}

			InterruptedContext = InterruptedGameState.GetContext();
			if (bProcessingInterrupt_Cached)
			{
				InterruptedContext.SetHistoryIndexInterruptedBySelf(HistoryIndexInterrupted_Cached);
			}

			//tentatively add the interrupted state to the history. We'll remove it again if
			//no interruption observers add new game states in response
			SubmitGameStateInternal(InterruptedGameState, true, bWasWaitingForNewStates);

			//if no observers submitted a game state context, then roll back the tentative interruption
			//state. It is unneeded
			if( InterruptedContext.AssociatedState != none &&
			   InterruptedContext.AssociatedState.HistoryIndex == CachedHistory.GetCurrentHistoryIndex() )
			{
				CachedHistory.ObliterateGameStatesFromHistory(1);

				// since we just obliterated the resume context, make sure to clear out the index in the last interrupted state.
				// This was set in InterruptionPostProcess
				if( LastInterruptedGameState != none )
				{
					LastInterruptedGameState.GetContext().SetInterruptionIndex(true, -1);
				}
			}
			else
			{
				bStateInterrupted = true;

				LastInterruptedGameState = InterruptedGameState;
				LastInterruptedStep = InterruptStep;
				Context = InterruptedContext;
			}
		}

		++InterruptStep;

	} until(InterruptedGameState == none);

	if (bProcessingInterrupt_Cached)
	{
		Context.SetHistoryIndexInterruptedBySelf(HistoryIndexInterrupted_Cached);
	}

	// if there was no interruption, submit using a normally built game state
	if( !bStateInterrupted )
	{
		//Get the game state output from the passed-in context
		SubmittedGameState = Context.ContextBuildGameState();
	}
	else
	{
		SubmittedGameState = Context.ContextBuildInterruptedGameState(LastInterruptedStep, eInterruptionStatus_Resume);

		//Set internal state of the contexts for the interrupted and resumed game states, linking them.
		class'XComGameStateContext'.static.InterruptionPostProcess(LastInterruptedGameState, SubmittedGameState);
	}

	//If SubmittedGameState was uninterrupted, or resumed - add it to the history here
	//SubmittedGameState can be none if conditions in the interrupt loop above cause it to fail, or if the context
	//itself determines that there should not be one ( ie. a roll failed for an ability and there were no side-effects )
	if( SubmittedGameState != none )
	{
		SubmitGameStateInternal(SubmittedGameState, false, bWasWaitingForNewStates);
	}
}

simulated event SubmitGameStateContextNative(XComGameStateContext StateChangeContext, bool bAISpawningCode=false)
{
	SubmitGameStateContext( StateChangeContext, bAISpawningCode );
}

simulated event SubmitChallengeGameStateContextNative(XComGameStateContext StateChangeContext)
{
	StateChangeContext.SetEngineRandSeedFromContext();
	SubmitGameStateContextNative(StateChangeContext);
}

/// <summary>
/// Internal helper method that helps encapsulate the logic that has to happen each time a game state is submitted
/// </summary>
simulated function SubmitGameStateInternal(XComGameState NewGameState, bool bInterrupt = false, bool bSendGameState = false)
{
	local int Index;
	local X2EventManager EventManager;

	// assign the parent game state based on the top of the build stack
	if( BuiltGameStateStack.Length > 0 )
	{
		NewGameState.SetParentGameState(BuiltGameStateStack[BuiltGameStateStack.Length - 1]);
	}

	EventManager = `XEVENTMGR;

	++ContextBuildDepth;

	// push this state onto the stack
	`assert(BuiltGameStateStack.Find(NewGameState) == INDEX_NONE);
	BuiltGameStateStack.AddItem(NewGameState);

	// cause any queued up events waiting for the PreStateChange deferral to execute
	EventManager.PreGameStateSubmitted(NewGameState);

	// clear the interrupt processing flag, at this point it has been recorded into the state context, and should be reset for the next iteration
	bProcessingInterrupt = false;

	// commit the state change
	CachedHistory.AddGameStateToHistory(NewGameState);

	if(bInterrupt)
	{
		bProcessingInterrupt = true;
		HistoryIndexInterrupted = CachedHistory.GetNumGameStates() - 1; //Most recent state

		// let state change listeners respond to the post state change event
		for(Index = 0; Index < EventObservers.Length; ++Index)
		{
			EventObservers[Index].InterruptGameState(NewGameState);
		}
	}
	else
	{
		// let state change listeners respond to the post state change event
		for(Index = 0; Index < EventObservers.Length; ++Index)
		{
			EventObservers[Index].PostBuildGameState(NewGameState);
		}
	}

	//Process any result events
	/*
	* Ex:
	* 1. Unit takes damage/gets healed 	a. Ex: Psi reflect, Damage Control
	* 2. Unit changes status 	a. ex: unit is concealed and enemy successfully spots, but unit has ability to mitigate the effects of being spotted
	* 3. Unit dies a. Ex: Secondary Heart
	* 4. Unit scores a kill 	a. Ex: the Ranger ability that resets Melee Charges cooldown
	* 5. Civilian interacted with 	a. Ex: an ability that gives bonus Supplies every time a civilian is rescued
	* 6. Hackable object hacked 	a. Ex: reinforcement timers extended because of a hack
	* 7. Unit bursts 	a. Burst is the replacement stat for crit
	* 8. Get loot from box or body
	*/

	// cause any queued up events waiting for the PostStateChange deferral to execute
	EventManager.OnGameStateSubmitted(NewGameState);

	// pop the build stack
	`assert(BuiltGameStateStack.Find(NewGameState) == BuiltGameStateStack.Length - 1);
	BuiltGameStateStack.Remove(BuiltGameStateStack.Length-1, 1);

	--ContextBuildDepth;
}

/// <summary>
/// The SubmitGameStateXXX methods are the interface by which the UI, AI, and MP subsystems push new game states
/// into the history. SubmitGameStates is used by MP and DEBUG to directly add new game states to the history
///
/// The rules engine is responsible for validating and vetting the incoming game states. Additionally, some turn
/// phases / rules engine state may prohibit the addition of new states this way while they are running. In this
/// situation the calling system is responsible for handling this condition and trying again when it is permitted.
/// </summary>
simulated function bool SubmitGameStates(array<XComGameState> NewStates)
{
	local XComGameState NewState;
	local bool bCanAdd;
	local bool bValid;
	local bool bWasWaitingForNewStates;

	bCanAdd = AddNewStatesAllowed();
	if(bCanAdd)
	{
		if(ContextBuildDepth == 0)
		{
			bWasWaitingForNewStates = bWaitingForNewStates;
			bWaitingForNewStates = false;
			CachedHistory.BeginEventChain();
		}

		bValid = ValidateIncomingGameStates();
		if(bValid)
		{
			foreach NewStates(NewState)
			{
				// commit the state change
				SubmitGameStateInternal(NewState, false, bWasWaitingForNewStates);
			}
			`assert(ContextBuildDepth >= 0);
		}
		else
		{
			`log("Detected cheating or errant behavior!", , 'XCom_GameStates');
		}
	}
	else
	{
		//Tell the history we don't actually want these game states
		foreach NewStates(NewState)
		{
			CachedHistory.CleanupPendingGameState(NewState);
		}
	}

	if(bCanAdd && ContextBuildDepth == 0)
	{
		CachedHistory.EndEventChain();
	}

	return bCanAdd;
}

simulated event bool SubmitGameState( XComGameState NewState)
{
	local array<XComGameState> SubmitArray;

	SubmitArray.AddItem(NewState);
	return SubmitGameStates(SubmitArray);
}

function Object GetEventObserver(int ObserverIndex)
{
	return EventObservers[ObserverIndex];
}

simulated function X2GameRulesetEventObserverInterface GetEventObserverOfType( class DesiredClass )
{
	local int Index;

	for (Index = 0; Index < EventObservers.Length; ++Index)
	{
		if (Object(EventObservers[Index]).IsA( DesiredClass.Name ))
		{
			return EventObservers[Index];
		}
	}

	return none;
}

/// <summary>
/// This event is called from native code when new states are generated natively. States are received by a remote machine
/// come via this event.
/// </summary>
simulated event bool SubmitGameStatesNative(array<XComGameState> NewStates)
{
	return SubmitGameStates(NewStates);
}

/// <summary>
/// Overridden by subclasses - used in submit game state context to gate the addition of new states
/// </summary>
simulated function bool AddNewStatesAllowed()
{
	if (CachedHistory == None) //We can't add new states if there's nothing to add to.
	{
		//At present, nothing triggers this aside from some destructibles looking for initial state in the obstaclecourse. -btopp 2015-07-22
		`log("X2GameRuleset : AddNewStatesAllowed() queried before CachedHistory is available!", , 'XCom_GameStates');
		return false;
	}

	return !bProcessingLoad;
}

/// <summary>
/// This event is called after a system adds a gamestate to the history, perhaps circumventing the ruleset itself.
/// </summary>
simulated event OnSubmitGameState();

/// <summary>
/// Reponsible for verifying that a set of newly incoming game states obey the rules.
/// </summary>
simulated function bool ValidateIncomingGameStates()
{
	return true;
}

/// <summary>
/// Returns true if the visualizer is busy
/// </summary>
simulated function bool WaitingForVisualizer();

simulated function bool IsSavingAllowed()
{
	return true;
}

/// <summary>
/// Returns true if all players remote and local are at the same point in their game rules engine state system
/// </summary>
native final latent function LatentWaitingForPlayerSync();

/// <summary>
/// Returns cached information about the unit such as what actions are available
/// </summary>
simulated function bool GetGameRulesCache_Unit(StateObjectReference UnitStateRef, out GameRulesCache_Unit OutCacheData);

simulated function DrawDebugLabel(Canvas kCanvas);

simulated native function RegisterEventObserver(X2GameRulesetEventObserverInterface EventObserver);

simulated native function UnregisterEventObserver(X2GameRulesetEventObserverInterface EventObserver);

simulated event string GetStateDebugString();

//---------------------------------------------------------------------------------------
//  FILE:    X2GameRuleset.uc
//  AUTHOR:  Ryan McFall  --  10/9/2013
//  PURPOSE: The rule set is the interface between the game state and the systems that want
//			 to change it. These systems include: player UI, AI, cheat manager, kismet,
//			 and multi-player. The rule set is responsible for enforcing game rules like
//			 interrupts and processing game state based events.
//
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------
class X2GameRuleset extends Actor
	dependson(XGTacticalGameCore, XComGameState, XComGameState_BaseObject)
	native(Core);

cpptext
{
	/* Latent Function Declarations */
	DECLARE_FUNCTION(execPollLatentWaitingForPlayerSync);
}

struct native AvailableTarget
{
	var StateObjectReference PrimaryTarget;             //  unit the user should be targeting via UI.
	var array<StateObjectReference> AdditionalTargets;  //  units that will be affected by the ability but are not directly targeted.
														//  all targets live here for free aim abilities.
	structcpptext
	{
		FAvailableTarget()
		{
			appMemzero(this, sizeof(FAvailableTarget));
		}

		FAvailableTarget(EEventParm)
		{
			appMemzero(this, sizeof(FAvailableTarget));
		}
	}
};

struct native AvailableAction
{
	var StateObjectReference AbilityObjectRef;
	var array<AvailableTarget> AvailableTargets;
	var int AvailableTargetCurrIndex;				//  points to which of the target we are targeting at right now.
	var bool bFreeAim;                          //  if this is true, targets will change based on cursor location.
	var name AvailableCode;     //  if this is anything except 'AA_Success', the ability can't be used.
	var EAbilityIconBehavior eAbilityIconBehaviorHUD;   //  store off whether this ability should be shown in the HUD.
	var bool bInputTriggered;                   //  true if this ability is 'active' IE - it is triggered by player input.
	var int ShotHUDPriority;                    // this number is used to sort the icon position in the Ability Container in Tactical HUD.

	structcpptext
	{
		FAvailableAction()
		{
			appMemzero(this, sizeof(FAvailableAction));
		}

		FAvailableAction(EEventParm)
		{
			appMemzero(this, sizeof(FAvailableAction));
		}
	}
};

struct native GameRulesCache_Unit
{
	var StateObjectReference UnitObjectRef;
	var array<AvailableAction> AvailableActions;
	var bool bAnyActionsAvailable;
	var int LastUpdateHistoryIndex; // keep track of the last history index this cache was updated so we can update the cache only as needed

	structcpptext
	{
		FGameRulesCache_Unit()
		{
			appMemzero(this, sizeof(FGameRulesCache_Unit));
		}

		FGameRulesCache_Unit(EEventParm)
		{
			appMemzero(this, sizeof(FGameRulesCache_Unit));
		}
	}
};

//******** Event Observers
var protected array<class> EventObserverClasses; //Add to this array in default properties of the concrete ruleset class to add observers
var protected array<X2GameRulesetEventObserverInterface> EventObservers; //The list of instantiated event observer. One per entry in EventObserverClasses

//******** Cached systems variables
var XComGameStateHistory CachedHistory; //Cache to the singleton XComGameStateHistory, manages game state
var XComGameStateVisualizationMgr VisualizationMgr; //`XCOMVISUALIZATIONMGR - processes history frames to show the player game state changes
var XComGameStateNetworkManager NetworkMgr; //Cached reference to the network manager to support MP
var X2GameRulesetVisibilityManager VisibilityMgr; //Cached reference to the visibility manager, manages visibility relationships between game state objects

//******** Local rules processing variables
var transient private array<XComGameState> BuiltGameStateStack;
var private int ContextBuildDepth; //Keep track of how far we've recursed into building game states
var protected bool bWaitingForNewStates; //This flag is set to true when the rules engine is waiting for a decision on what action will be taken next
var protectedwrite bool bProcessingLoad; //This flag is used to determine whether the game rules are processing a load, and thus should not admit changes to the game state
var protected int CurrentSyncPoint; // Current location where the ruleset is waiting for a network sync; this is automatically updated per LatentWaitingForPlayerSync call.
var private bool bProcessingInterrupt; //This is a flag set within the interrupt processing loop. It is set to true and then cleared if an interrupt is encountered. This is used to
									   //tag the first history frame in an interrupt with the history frame it was interrupting.
var private int HistoryIndexInterrupted; //Used with bProcessingInterrupt

//******** Latent processing variables
var privatewrite bool BuildingLatentGameState; // true while a game state is building on another thread
var private delegate<LatentSubmitGameStateContextCallbackFn> LatentCallbackFunction; // function to be called when the latent game state completes

struct native LatentSubmissionResult
{
	var XComGameState LatentSubmitGameStateResult;
	var delegate<LatentSubmitGameStateContextCallbackFn> LatentCallbackFunction; // function to be called when the latent game state completes
};

var array<LatentSubmissionResult> LatentSubmissionResults;

native function bool IsDoingLatentSubmission();

// Callback delegate prototype for latent context submission
delegate LatentSubmitGameStateContextCallbackFn(XComGameState SubmittedGameState);

/// <summary>
/// Returns a state object reference to the battle data
/// </summary>
simulated function StateObjectReference GetCachedBattleDataRef();

/// <summary>
/// Entry point for the rules engine if the map URL indicates this IS NOT a loaded save game
/// </summary>
simulated function StartNewGame();

/// <summary>
/// Entry point for the rules engine if the map URL indicates this IS a loaded save game
/// </summary>
simulated function LoadGame();

/// <summary>
/// This method builds a local list of state object references for objects that are relatively static, and that we
/// may need to access frequently. Using the cached ObjectID from a game state object reference is much faster than
/// searching for it each time we need to use it.
/// </summary>
simulated function BuildLocalStateObjectCache()
{
	local int Index;
	local X2GameRulesetEventObserverInterface EventObserver;

	CachedHistory = `XCOMHISTORY;
	VisualizationMgr = `XCOMVISUALIZATIONMGR;
	NetworkMgr = `XCOMNETMANAGER;

	for(Index = 0; Index < EventObserverClasses.Length; ++Index)
	{
		EventObserver = new(self)EventObserverClasses[Index];
		EventObserver.Initialize();
		EventObservers.AddItem(EventObserver);
	}
}

/// <summary>
/// Submits a game state latently on a separate hardware thread, and calls the specified callback (if any)
/// when submission is complete
/// </summary>
native function LatentSubmitGameStateContext(XComGameStateContext_Ability NewContext, optional delegate<LatentSubmitGameStateContextCallbackFn> CompletionCallback);

event Tick(float DeltaTime)
{
	local LatentSubmissionResult latentResult;
	local XComGameState LatentSubmitGameStateResult;

	if( ! IsDoingLatentSubmission() )
	{
		// A latent submission has completed and filled out this sentinel object. Now that we're back
		// on the main thread, we can safely clean up after it.

		// do this now in case other gamestates have to be submitted in response
		`XCOMHISTORY.EndLatentUpdate();
		BuildingLatentGameState = false;

		foreach LatentSubmissionResults(latentResult)
		{
			LatentCallbackFunction = latentResult.LatentCallbackFunction;
			LatentSubmitGameStateResult = latentResult.LatentSubmitGameStateResult;

			LatentCallbackFunction( LatentSubmitGameStateResult );
		}

		LatentSubmissionResults.Length = 0;

	}

	super.Tick(DeltaTime);

}

/// <summary>
/// The SubmitGameStateXXX methods are the interface by which the UI, AI, and MP subsystems push new game states
/// into the history. SubmitGameStateContext is used by AI and UI to indicate what action they intend to perform
/// when they are allowed input.
///
/// In some cases the resulting history frames will match exactly what the incoming context is requesting. However
/// actions can be interrupted which may result in a sequence of unintended state changes ( such as a unit being
/// killed while moving ). The rules engine is responsible for converting the state change context into a
/// series of new history states/frames.
/// Return true of successful, false otherwise
/// </summary>
simulated event bool SubmitGameStateContext(XComGameStateContext Context, bool bAISpawningCode = false)
{
	local int Index;
	local bool bCanAdd;

	local bool bWasWaitingForNewStates;

	bCanAdd = AddNewStatesAllowed();
	if(bCanAdd)
	{
		if(ContextBuildDepth == 0)
		{
			bWasWaitingForNewStates = bWaitingForNewStates;
			bWaitingForNewStates = false;
			CachedHistory.BeginEventChain();
		}

		if(Context.bNetworkAdded)
		{
			// Use the exiting random seed from the given context
			Context.SetEngineRandSeedFromContext();
		}
		else
		{
			// Any further replay will need the original context's starting rand seed, so store the engine's seed into this context.
			Context.SetContextRandSeedFromEngine();

			//Send off any contexts that are generated via "user-input"
			if(NetworkMgr != none && NetworkMgr.HasConnections())
			{
				if(Context.bSendGameState)
				{
					Context.SetSendGameState(false);
					NetworkMgr.QueueContextForSending(Context, CachedHistory.GetCurrentHistoryIndex());
				}
				else
				{
					`log(`location @ "Did not Queue Context for Sending!! " @ Context.SummaryString() @ `ShowVar(GetStateName(), State) @ `ShowVar(bWasWaitingForNewStates) @ `ShowVar(bWaitingForNewStates) @ `ShowVar(ContextBuildDepth));
					ScriptTrace();
				}
			}
		}

		//Anything that should only be run ONCE per external call to SubmitGameStateContext should go in here.
		if(ContextBuildDepth == 0)
		{
			for(Index = 0; Index < EventObservers.Length; ++Index)
			{
				EventObservers[Index].CacheGameStateInformation();
			}
		}

		//@TODO - rmcfall - find a home for these ( other turn phases? )
		/*
		1. Turn starts 	a. Ex Rapid Regen
		2. Turn ends 	a. Ex EXALTs Regen Pheromones
		3. Mission start 	a. Ex (maybe): MEC Heavys Body Shield, if mission starts with enemies visible
		4. End of mission 	a. XP bonuses from abilities (like medals)
		*/
		ContextBuildDepth++;

		//Process any preemptive events
		/*
		* Ex:
		* 1. A unit state variable needs to change when an ability context has been submitted ala. ReflexObserver
		*/
		for(Index = 0; Index < EventObservers.Length && Context != none; ++Index)
		{
			EventObservers[Index].PreBuildGameStateFromContext(Context);
		}

		// Added this function to be reused for SubmitGameStateContexts function.
		SubmitGameStateContext_Internal(Context, bWasWaitingForNewStates);

		ContextBuildDepth--;

		if (ContextBuildDepth == 0)
		{
			CachedHistory.EndEventChain();
		}
	}
	else if (`REPLAY.bInTutorial)
	{
		return `TUTORIAL.HandleSubmittedGameStateContext(Context);
	}

	assert(ContextBuildDepth >= 0);

	return true;
}

/// <summary>
//  Same as SubmitGameStateContext, but takes several contexts to be submitted sequentially.
/// </summary>
simulated event bool SubmitGameStateContexts(array<XComGameStateContext> Contexts, bool bAISpawningCode = false, bool bGroupVisualizations = false)
{
	local int Index;
	local bool bCanAdd;

	//Used for interruption processing
	local XComGameStateContext Context;

	local bool bWasWaitingForNewStates;

	local int VisualizeAtHistoryIndex;

	bCanAdd = AddNewStatesAllowed();
	if(bCanAdd)
	{
		if(ContextBuildDepth == 0)
		{
			bWasWaitingForNewStates = bWaitingForNewStates;
			bWaitingForNewStates = false;
			CachedHistory.BeginEventChain();
		}

		Context = Contexts[0];
		if(Context.bNetworkAdded)
		{
			// Use the exiting random seed from the given context
			Context.SetEngineRandSeedFromContext();
		}
		else
		{
			// Any further replay will need the original context's starting rand seed, so store the engine's seed into this context.
			Context.SetContextRandSeedFromEngine();

			//Send off any contexts that are generated via "user-input"
			if(NetworkMgr != none && NetworkMgr.HasConnections())
			{
				if(Context.bSendGameState)
				{
					Context.SetSendGameState(false);
					NetworkMgr.QueueContextForSending(Context, CachedHistory.GetCurrentHistoryIndex());
				}
				else
				{
					`log(`location @ "Did not Queue Context for Sending!! " @ Context.SummaryString() @ `ShowVar(GetStateName(), State) @ `ShowVar(bWasWaitingForNewStates) @ `ShowVar(bWaitingForNewStates) @ `ShowVar(ContextBuildDepth));
					ScriptTrace();
				}
			}
		}

		//Anything that should only be run ONCE per external call to SubmitGameStateContext should go in here.
		if(ContextBuildDepth == 0)
		{
			for(Index = 0; Index < EventObservers.Length; ++Index)
			{
				EventObservers[Index].CacheGameStateInformation();
			}
		}

		//@TODO - rmcfall - find a home for these ( other turn phases? )
		/*
		1. Turn starts 	a. Ex Rapid Regen
		2. Turn ends 	a. Ex EXALTs Regen Pheromones
		3. Mission start 	a. Ex (maybe): MEC Heavys Body Shield, if mission starts with enemies visible
		4. End of mission 	a. XP bonuses from abilities (like medals)
		*/
		ContextBuildDepth++;

		//Process any preemptive events
		/*
		* Ex:
		* 1. A unit state variable needs to change when an ability context has been submitted ala. ReflexObserver
		*/
		for(Index = 0; Index < EventObservers.Length && Context != none; ++Index)
		{
			EventObservers[Index].PreBuildGameStateFromContext(Context);
		}

		VisualizeAtHistoryIndex = CachedHistory.GetEventChainStartIndex();

		foreach Contexts(Context)
		{
			if( bGroupVisualizations && VisualizeAtHistoryIndex != INDEX_NONE )
			{
				Context.SetDesiredVisualizationBlockIndex(VisualizeAtHistoryIndex);
			}

			SubmitGameStateContext_Internal(Context, bWasWaitingForNewStates);
		}

		ContextBuildDepth--;

		if (ContextBuildDepth == 0)
		{
			CachedHistory.EndEventChain();
		}
	}
	else if (`REPLAY.bInTutorial)
	{
		return `TUTORIAL.HandleSubmittedGameStateContext(Context);
	}

	assert(ContextBuildDepth >= 0);

	return true;
}

// Guts of the SubmitGameStateContext, pulled out for reuse
function SubmitGameStateContext_Internal(XComGameStateContext Context, bool bWasWaitingForNewStates)
{
	local int InterruptStep, LastInterruptedStep;
	local XComGameState SubmittedGameState;
	local bool bStateInterrupted;

	//Used for interruption processing
	local XComGameState InterruptedGameState;
	local XComGameState LastInterruptedGameState;
	local XComGameStateContext InterruptedContext;

	//locally cached copies of these vars at the start of processing, since this method is recursive
	local bool bProcessingInterrupt_Cached;
	local int HistoryIndexInterrupted_Cached;

	bProcessingInterrupt_Cached = bProcessingInterrupt;
	HistoryIndexInterrupted_Cached = HistoryIndexInterrupted;

	//Process any interrupt events
	/*
	*  Ex:
	*  1. Unit enters tile / uses ability that causes another ability to fire (Overwatch) (Close Combat Specialist)
	*  3. Unit sighted (AI Alert)
	*/
	bStateInterrupted = false;
	InterruptStep = 0;
	LastInterruptedGameState = None;
	do
	{
		InterruptedGameState = Context.ContextBuildInterruptedGameState(InterruptStep, eInterruptionStatus_Interrupt);
		if( InterruptedGameState != none )
		{
			if( LastInterruptedGameState != None )
			{
				//Set internal state of the contexts for the interrupted and resumed game states, linking them.
				class'XComGameStateContext'.static.InterruptionPostProcess(LastInterruptedGameState, InterruptedGameState);
			}

			InterruptedContext = InterruptedGameState.GetContext();
			if (bProcessingInterrupt_Cached)
			{
				InterruptedContext.SetHistoryIndexInterruptedBySelf(HistoryIndexInterrupted_Cached);
			}

			//tentatively add the interrupted state to the history. We'll remove it again if
			//no interruption observers add new game states in response
			SubmitGameStateInternal(InterruptedGameState, true, bWasWaitingForNewStates);

			//if no observers submitted a game state context, then roll back the tentative interruption
			//state. It is unneeded
			if( InterruptedContext.AssociatedState != none &&
			   InterruptedContext.AssociatedState.HistoryIndex == CachedHistory.GetCurrentHistoryIndex() )
			{
				CachedHistory.ObliterateGameStatesFromHistory(1);

				// since we just obliterated the resume context, make sure to clear out the index in the last interrupted state.
				// This was set in InterruptionPostProcess
				if( LastInterruptedGameState != none )
				{
					LastInterruptedGameState.GetContext().SetInterruptionIndex(true, -1);
				}
			}
			else
			{
				bStateInterrupted = true;

				LastInterruptedGameState = InterruptedGameState;
				LastInterruptedStep = InterruptStep;
				Context = InterruptedContext;
			}
		}

		++InterruptStep;

	} until(InterruptedGameState == none);

	if (bProcessingInterrupt_Cached)
	{
		Context.SetHistoryIndexInterruptedBySelf(HistoryIndexInterrupted_Cached);
	}

	// if there was no interruption, submit using a normally built game state
	if( !bStateInterrupted )
	{
		//Get the game state output from the passed-in context
		SubmittedGameState = Context.ContextBuildGameState();
	}
	else
	{
		SubmittedGameState = Context.ContextBuildInterruptedGameState(LastInterruptedStep, eInterruptionStatus_Resume);

		//Set internal state of the contexts for the interrupted and resumed game states, linking them.
		class'XComGameStateContext'.static.InterruptionPostProcess(LastInterruptedGameState, SubmittedGameState);
	}

	//If SubmittedGameState was uninterrupted, or resumed - add it to the history here
	//SubmittedGameState can be none if conditions in the interrupt loop above cause it to fail, or if the context
	//itself determines that there should not be one ( ie. a roll failed for an ability and there were no side-effects )
	if( SubmittedGameState != none )
	{
		SubmitGameStateInternal(SubmittedGameState, false, bWasWaitingForNewStates);
	}
}

simulated event SubmitGameStateContextNative(XComGameStateContext StateChangeContext, bool bAISpawningCode=false)
{
	SubmitGameStateContext( StateChangeContext, bAISpawningCode );
}

simulated event SubmitChallengeGameStateContextNative(XComGameStateContext StateChangeContext)
{
	StateChangeContext.SetEngineRandSeedFromContext();
	SubmitGameStateContextNative(StateChangeContext);
}

/// <summary>
/// Internal helper method that helps encapsulate the logic that has to happen each time a game state is submitted
/// </summary>
simulated function SubmitGameStateInternal(XComGameState NewGameState, bool bInterrupt = false, bool bSendGameState = false)
{
	local int Index;
	local X2EventManager EventManager;

	// assign the parent game state based on the top of the build stack
	if( BuiltGameStateStack.Length > 0 )
	{
		NewGameState.SetParentGameState(BuiltGameStateStack[BuiltGameStateStack.Length - 1]);
	}

	EventManager = `XEVENTMGR;

	++ContextBuildDepth;

	// push this state onto the stack
	`assert(BuiltGameStateStack.Find(NewGameState) == INDEX_NONE);
	BuiltGameStateStack.AddItem(NewGameState);

	// cause any queued up events waiting for the PreStateChange deferral to execute
	EventManager.PreGameStateSubmitted(NewGameState);

	// clear the interrupt processing flag, at this point it has been recorded into the state context, and should be reset for the next iteration
	bProcessingInterrupt = false;

	// commit the state change
	CachedHistory.AddGameStateToHistory(NewGameState);

	if(bInterrupt)
	{
		bProcessingInterrupt = true;
		HistoryIndexInterrupted = CachedHistory.GetNumGameStates() - 1; //Most recent state

		// let state change listeners respond to the post state change event
		for(Index = 0; Index < EventObservers.Length; ++Index)
		{
			EventObservers[Index].InterruptGameState(NewGameState);
		}
	}
	else
	{
		// let state change listeners respond to the post state change event
		for(Index = 0; Index < EventObservers.Length; ++Index)
		{
			EventObservers[Index].PostBuildGameState(NewGameState);
		}
	}

	//Process any result events
	/*
	* Ex:
	* 1. Unit takes damage/gets healed 	a. Ex: Psi reflect, Damage Control
	* 2. Unit changes status 	a. ex: unit is concealed and enemy successfully spots, but unit has ability to mitigate the effects of being spotted
	* 3. Unit dies a. Ex: Secondary Heart
	* 4. Unit scores a kill 	a. Ex: the Ranger ability that resets Melee Charges cooldown
	* 5. Civilian interacted with 	a. Ex: an ability that gives bonus Supplies every time a civilian is rescued
	* 6. Hackable object hacked 	a. Ex: reinforcement timers extended because of a hack
	* 7. Unit bursts 	a. Burst is the replacement stat for crit
	* 8. Get loot from box or body
	*/

	// cause any queued up events waiting for the PostStateChange deferral to execute
	EventManager.OnGameStateSubmitted(NewGameState);

	// pop the build stack
	`assert(BuiltGameStateStack.Find(NewGameState) == BuiltGameStateStack.Length - 1);
	BuiltGameStateStack.Remove(BuiltGameStateStack.Length-1, 1);

	--ContextBuildDepth;
}

/// <summary>
/// The SubmitGameStateXXX methods are the interface by which the UI, AI, and MP subsystems push new game states
/// into the history. SubmitGameStates is used by MP and DEBUG to directly add new game states to the history
///
/// The rules engine is responsible for validating and vetting the incoming game states. Additionally, some turn
/// phases / rules engine state may prohibit the addition of new states this way while they are running. In this
/// situation the calling system is responsible for handling this condition and trying again when it is permitted.
/// </summary>
simulated function bool SubmitGameStates(array<XComGameState> NewStates)
{
	local XComGameState NewState;
	local bool bCanAdd;
	local bool bValid;
	local bool bWasWaitingForNewStates;

	bCanAdd = AddNewStatesAllowed();
	if(bCanAdd)
	{
		if(ContextBuildDepth == 0)
		{
			bWasWaitingForNewStates = bWaitingForNewStates;
			bWaitingForNewStates = false;
			CachedHistory.BeginEventChain();
		}

		bValid = ValidateIncomingGameStates();
		if(bValid)
		{
			foreach NewStates(NewState)
			{
				// commit the state change
				SubmitGameStateInternal(NewState, false, bWasWaitingForNewStates);
			}
			`assert(ContextBuildDepth >= 0);
		}
		else
		{
			`log("Detected cheating or errant behavior!", , 'XCom_GameStates');
		}
	}
	else
	{
		//Tell the history we don't actually want these game states
		foreach NewStates(NewState)
		{
			CachedHistory.CleanupPendingGameState(NewState);
		}
	}

	if(bCanAdd && ContextBuildDepth == 0)
	{
		CachedHistory.EndEventChain();
	}

	return bCanAdd;
}

simulated event bool SubmitGameState( XComGameState NewState)
{
	local array<XComGameState> SubmitArray;

	SubmitArray.AddItem(NewState);
	return SubmitGameStates(SubmitArray);
}

function Object GetEventObserver(int ObserverIndex)
{
	return EventObservers[ObserverIndex];
}

simulated function X2GameRulesetEventObserverInterface GetEventObserverOfType( class DesiredClass )
{
	local int Index;

	for (Index = 0; Index < EventObservers.Length; ++Index)
	{
		if (Object(EventObservers[Index]).IsA( DesiredClass.Name ))
		{
			return EventObservers[Index];
		}
	}

	return none;
}

/// <summary>
/// This event is called from native code when new states are generated natively. States are received by a remote machine
/// come via this event.
/// </summary>
simulated event bool SubmitGameStatesNative(array<XComGameState> NewStates)
{
	return SubmitGameStates(NewStates);
}

/// <summary>
/// Overridden by subclasses - used in submit game state context to gate the addition of new states
/// </summary>
simulated function bool AddNewStatesAllowed()
{
	if (CachedHistory == None) //We can't add new states if there's nothing to add to.
	{
		//At present, nothing triggers this aside from some destructibles looking for initial state in the obstaclecourse. -btopp 2015-07-22
		`log("X2GameRuleset : AddNewStatesAllowed() queried before CachedHistory is available!", , 'XCom_GameStates');
		return false;
	}

	return !bProcessingLoad;
}

/// <summary>
/// This event is called after a system adds a gamestate to the history, perhaps circumventing the ruleset itself.
/// </summary>
simulated event OnSubmitGameState();

/// <summary>
/// Reponsible for verifying that a set of newly incoming game states obey the rules.
/// </summary>
simulated function bool ValidateIncomingGameStates()
{
	return true;
}

/// <summary>
/// Returns true if the visualizer is busy
/// </summary>
simulated function bool WaitingForVisualizer();

simulated function bool IsSavingAllowed()
{
	return true;
}

/// <summary>
/// Returns true if all players remote and local are at the same point in their game rules engine state system
/// </summary>
native final latent function LatentWaitingForPlayerSync();

/// <summary>
/// Returns cached information about the unit such as what actions are available
/// </summary>
simulated function bool GetGameRulesCache_Unit(StateObjectReference UnitStateRef, out GameRulesCache_Unit OutCacheData);

simulated function DrawDebugLabel(Canvas kCanvas);

simulated native function RegisterEventObserver(X2GameRulesetEventObserverInterface EventObserver);

simulated native function UnregisterEventObserver(X2GameRulesetEventObserverInterface EventObserver);

simulated event string GetStateDebugString();


Update math.uc

Update math.uc

Added Max

# math.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.5 on 3/7/2014 4:49:52 PM
# Comment: Converts math-related code from PowerBASIC to C++

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: False
Exclude: False
Comment: Converts math-related code from PowerBASIC to C++
Selected: False
Highlight: True
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
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
Comment: ^ (power)
Pass: 1

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

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

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

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

Criteria: 6
Enabled: True
Comment: Bitwise AND and OR
BackColor: YellowGreen
Find: ({expr})
Replace: ({@Eval: Replace(Replace('{expr}', "And", "&"), "Or", "|")})

Criteria: 7
Comment: Everything else
Pass: 3

Criteria: 8
Enabled: True
Find: {@Start}
      {@Note:
         ToDo:  \ exp10 frac sgn shift
               eqv imp bin hex oct Not
               and fix LCase for next version
      }
Replace: #include <math.h>
         #include <stdlib.h>
         {@Note: rand & srand are in stdlib.h}

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

Criteria: 10
Enabled: False
Find: {func: Abs|Sin|Cos|Tan|Exp|Exp2|Log|Log2|Log10|Ceil}
Replace: {@Evaluate: LCase({func})}

Criteria: 11
Enabled: True
Find: Mod
Replace: %

Criteria: 12
Enabled: True
BackColor: Yellow
Find: Atn
Replace: atan

Criteria: 13
Enabled: True
BackColor: Lime
Find: Sqr
Replace: sqrt

Criteria: 14
Enabled: True
BackColor: Brown
Find: Fix
Replace: trunc

Criteria: 15
Enabled: True
BackColor: Pink
Find: Cint
Replace: lround

Criteria: 16
Enabled: True
BackColor: Red
Find: Incr {var:1}
Replace: {var}++

Criteria: 17
Enabled: True
BackColor: RoyalBlue
Find: Decr {var:1}
Replace: {var}--

Criteria: 18
Enabled: True
Comment: Logical (short-circuit) AND
Find: And
Replace: &&

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

Criteria: 20
Enabled: True
BackColor: Khaki
Find: Xor
Replace: ^

Criteria: 21
Enabled: True
BackColor: Green
Find: Not
Replace: ~

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

Criteria: 23
Enabled: True
BackColor: Silver
Find: IsTrue {x%}  [{stop-: And | Or | Then | : }]
Replace: (({x}) <> 0)

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

Criteria: 25
Enabled: True
BackColor: Violet
Find: <>
Replace: !=

Criteria: 26
Enabled: True
Find: Randomize {number}
Replace: srand({number})

Criteria: 27
Enabled: True
Find: Rnd[()]
Replace: (rand() / RAND_MAX)

Criteria: 28
Enabled: True
Find: Rnd({a}, {b})
Replace: (rand() % ({b}) + ({a}))

Criteria: 29
Enabled: True
Find: Shift Right {ivar}, {countexpr}
Replace: {ivar} = ({ivar} >> {countexpr})

Criteria: 30
Enabled: True
Find: Shift Left {ivar}, {countexpr}
Replace: {ivar} = ({ivar} << {countexpr})

Criteria: 31
Enabled: True
Find: Sgn({number})
Replace: PB_SGN({number})

Criteria: 32
Enabled: True
Selected: True
Find: Max({args})
Replace: PB_MAX({args})

# End Search

# math.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.5 on 3/4/2014 5:27:46 PM
# Comment: Converts math-related code from PowerBASIC to C++

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: False
Exclude: False
Comment: Converts math-related code from PowerBASIC to C++
Selected: False
Highlight: True
ForeColor: ControlText
BackColor: Aqua
FontName:
FontSize:
FontStyle:
CaseSensitive: False
QuoteSensitive: True
CodeBlockSensitive: True
FilterEndText:
FilterSeparator: {#10}
FilterSort: False
FilterSortFunc:
FilterStartText:
FilterUnique: False
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
Comment: ^ (power)
Pass: 1

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

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

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

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

Criteria: 6
Enabled: True
Comment: Bitwise AND and OR
BackColor: YellowGreen
Find: ({expr})
Replace: ({@Eval: Replace(Replace('{expr}', "And", "&"), "Or", "|")})

Criteria: 7
Comment: Everything else
Pass: 3

Criteria: 8
Enabled: True
Find: {@Start}
      {@Note:
         ToDo:  \ exp10 frac sgn shift
               eqv imp bin hex oct Not
               and fix LCase for next version
      }
Replace: #include <math.h>
         #include <stdlib.h>
         {@Note: rand & srand are in stdlib.h}

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

Criteria: 10
Enabled: False
Find: {func: Abs|Sin|Cos|Tan|Exp|Exp2|Log|Log2|Log10|Ceil}
Replace: {@Evaluate: LCase({func})}

Criteria: 11
Enabled: True
Find: Mod
Replace: %

Criteria: 12
Enabled: True
BackColor: Yellow
Find: Atn
Replace: atan

Criteria: 13
Enabled: True
BackColor: Lime
Find: Sqr
Replace: sqrt

Criteria: 14
Enabled: True
BackColor: Brown
Find: Fix
Replace: trunc

Criteria: 15
Enabled: True
BackColor: Pink
Find: Cint
Replace: lround

Criteria: 16
Enabled: True
BackColor: Red
Find: Incr {var:1}
Replace: {var}++

Criteria: 17
Enabled: True
BackColor: RoyalBlue
Find: Decr {var:1}
Replace: {var}--

Criteria: 18
Enabled: True
Comment: Logical (short-circuit) AND
Find: And
Replace: &&

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

Criteria: 20
Enabled: True
BackColor: Khaki
Find: Xor
Replace: ^

Criteria: 21
Enabled: True
BackColor: Green
Find: Not
Replace: ~

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

Criteria: 23
Enabled: True
BackColor: Silver
Find: IsTrue {x%}  [{stop-: And | Or | Then | : }]
Replace: (({x}) <> 0)

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

Criteria: 25
Enabled: True
BackColor: Violet
Find: <>
Replace: !=

Criteria: 26
Enabled: True
Find: Randomize {number}
Replace: srand({number})

Criteria: 27
Enabled: True
Find: Rnd[()]
Replace: (rand() / RAND_MAX)

Criteria: 28
Enabled: True
Find: Rnd({a}, {b})
Replace: (rand() % ({b}) + ({a}))

Criteria: 29
Enabled: True
Find: Shift Right {ivar}, {countexpr}
Replace: {ivar} = ({ivar} >> {countexpr})

Criteria: 30
Enabled: True
Find: Shift Left {ivar}, {countexpr}
Replace: {ivar} = ({ivar} << {countexpr})

Criteria: 31
Enabled: True
Find: Sgn({number})
Replace: PB_SGN({number})

# End Search


Update pb-to-cpp.uc

Update pb-to-cpp.uc

Added #include "pbMisc.h"

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.5 on 2/28/2014 8:25:14 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: 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
Selected: True
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

         #include "pbOS.h"
         #include "pbMisc.h"
         {@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: Functions, Subs, =
Pass: 2

Criteria: 7
Highlight: True
Find: =
Replace: ==

Criteria: 8
SkipOver: True
Find: !=
Replace: [Skip over]

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

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

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

Criteria: 12
Highlight: True
PassOnce: False
Find: Sub {name:1} [Alias {alias}]([{args}])[{exp: Export}] [{comment:" *'.*"}] {nl}
         {code+}
      End Sub
Replace: {exp:extern "C" __declspec(dllexport)} void {exp:__stdcall} {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

Criteria: 13
Highlight: True
BackColor: DarkKhaki
PassOnce: False
Find: Function {name:1} [Alias {alias}]([{args}])[{exp: Export}] As {type}[{comment:" *'.*"}]{nl}
         {code+}
      End Function
Replace: {exp:extern "C" __declspec(dllexport)} {type} {exp:__stdcall} {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

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

Criteria: 15
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: 16
Highlight: True
Find: Declare Sub {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])]
Replace: {lib: extern __declspec(dllimport)} void {name}({args})

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

Criteria: 18
Comment: Misc
Pass: 3

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

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

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

Criteria: 22
Highlight: True
BackColor: Green
Find: %{equate:"[a-z]+"}
Replace: {equate}

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

Criteria: 24
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: 25
Highlight: True
PassOnce: False
Find: If {cond} Then {nl}
         {code+}
      End If
Replace: if ({cond}) {
            {code}
         }

Criteria: 26
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: 27
Highlight: True
PassOnce: False
Find: While {cond} {nl}
         {code+}
      Wend
Replace: while ({cond}) {
            {code}
         }

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

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

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

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

Criteria: 32
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: 33
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: 34
Highlight: True
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

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

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

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

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

Criteria: 39
Find: Iterate
Replace: continue

Criteria: 40
Find: Exit
Replace: break

Criteria: 41
Highlight: True
Find: Exit Sub
Replace: return

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

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

Criteria: 44
Find: Double
Replace: double

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

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

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

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

Criteria: 49
Find: AsciiZ
Replace: LPCSTR

Criteria: 50
Highlight: True
Find: Quad
Replace: __int64

Criteria: 51
SkipOver: True
Find: long double
Replace: [Skip over]

Criteria: 52
Highlight: True
Find: { Extended | Ext }
Replace: long double

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

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

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

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

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

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

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

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

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

Criteria: 62
Find: Choose[&]({index}, {choice})
Replace: IIf({index}, {choice}, 0)

Criteria: 63
Find: Choose$({index}, {choice})
Replace: IIf({index}, {choice}, "")

Criteria: 64
PassOnce: False
Find: Choose[{type: $ | & }]({index}, {choice1}, {more})
Replace: IIf({index}, {choice1}, Choose{type}({index}, {more}))

Criteria: 65
Find: StdOut {text} { {sameline: ;} | {NewLine: } }
Replace: cout << {text} {NewLine: << endl;}

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

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

Criteria: 68
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: 69
SkipOver: True
Find: ({cond} ? {this} :
Replace: [Skip over]

Criteria: 70
Highlight: True
Find: :
Replace: ;

Criteria: 71
SkipOver: True
Find: ::
Replace: [Skip over]

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

Criteria: 73
PassOnce: False
Find: IIf[$]({cond}, {this}, {that})
Replace: ({cond} ? {this} : {that})

Criteria: 74
Find: _char([{char= }])
Replace: '{char}'

# End Search

# pb-to-cpp.uc - uCalc Transformation file
# This file was saved with uCalc Transform 2.5 on 2/26/2014 5:07:01 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
Selected: True
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

         #include "pbOs.h"
         {@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: Functions, Subs, =
Pass: 2

Criteria: 7
Highlight: True
Find: =
Replace: ==

Criteria: 8
SkipOver: True
Find: !=
Replace: [Skip over]

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

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

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

Criteria: 12
Highlight: True
PassOnce: False
Find: Sub {name:1} [Alias {alias}]([{args}])[{exp: Export}] [{comment:" *'.*"}] {nl}
         {code+}
      End Sub
Replace: {exp:extern "C" __declspec(dllexport)} void {exp:__stdcall} {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

Criteria: 13
Highlight: True
BackColor: DarkKhaki
PassOnce: False
Find: Function {name:1} [Alias {alias}]([{args}])[{exp: Export}] As {type}[{comment:" *'.*"}]{nl}
         {code+}
      End Function
Replace: {exp:extern "C" __declspec(dllexport)} {type} {exp:__stdcall} {name}({args}) { {comment}
            {code}
         !!ReleaseDynamicArrays!!}

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

Criteria: 15
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: 16
Highlight: True
Find: Declare Sub {name:1} [Lib {lib}] [Alias {alias}]
      [([{args}])]
Replace: {lib: extern __declspec(dllimport)} void {name}({args})

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

Criteria: 18
Comment: Misc
Pass: 3

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

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

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

Criteria: 22
Highlight: True
BackColor: Green
Find: %{equate:"[a-z]+"}
Replace: {equate}

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

Criteria: 24
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: 25
Highlight: True
PassOnce: False
Find: If {cond} Then {nl}
         {code+}
      End If
Replace: if ({cond}) {
            {code}
         }

Criteria: 26
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: 27
Highlight: True
PassOnce: False
Find: While {cond} {nl}
         {code+}
      Wend
Replace: while ({cond}) {
            {code}
         }

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

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

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

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

Criteria: 32
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: 33
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: 34
Highlight: True
BackColor: SandyBrown
PassOnce: False
Find: Dim {var1}, {more}
Replace: Dim {var1}
         Dim {more}

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

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

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

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

Criteria: 39
Find: Iterate
Replace: continue

Criteria: 40
Find: Exit
Replace: break

Criteria: 41
Highlight: True
Find: Exit Sub
Replace: return

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

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

Criteria: 44
Find: Double
Replace: double

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

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

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

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

Criteria: 49
Find: AsciiZ
Replace: LPCSTR

Criteria: 50
Highlight: True
Find: Quad
Replace: __int64

Criteria: 51
SkipOver: True
Find: long double
Replace: [Skip over]

Criteria: 52
Highlight: True
Find: { Extended | Ext }
Replace: long double

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

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

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

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

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

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

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

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

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

Criteria: 62
Find: Choose[&]({index}, {choice})
Replace: IIf({index}, {choice}, 0)

Criteria: 63
Find: Choose$({index}, {choice})
Replace: IIf({index}, {choice}, "")

Criteria: 64
PassOnce: False
Find: Choose[{type: $ | & }]({index}, {choice1}, {more})
Replace: IIf({index}, {choice1}, Choose{type}({index}, {more}))

Criteria: 65
Find: StdOut {text} { {sameline: ;} | {NewLine: } }
Replace: cout << {text} {NewLine: << endl;}

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

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

Criteria: 68
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: 69
SkipOver: True
Find: ({cond} ? {this} :
Replace: [Skip over]

Criteria: 70
Highlight: True
Find: :
Replace: ;

Criteria: 71
SkipOver: True
Find: ::
Replace: [Skip over]

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

Criteria: 73
PassOnce: False
Find: IIf[$]({cond}, {this}, {that})
Replace: ({cond} ? {this} : {that})

Criteria: 74
Find: _char([{char= }])
Replace: '{char}'

# End Search