498 lines
20 KiB
C++

/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ScriptEngine.h ///////////////////////////////////////////////////////////////////////////
// Script evaluation engine.
// Author: John Ahlquist, Nov. 2001
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef __SCRIPTENGINE_H_
#define __SCRIPTENGINE_H_
#include "Common/GameType.h"
#include "Common/GameMemory.h"
#include "Common/STLTypedefs.h"
#include "Common/Science.h"
#include "Common/Snapshot.h"
#include "Common/SubsystemInterface.h"
#include "GameLogic/Scripts.h"
class DataChunkInput;
struct DataChunkInfo;
class DataChunkOutput;
class Team;
class Object;
class ThingTemplate;
class Player;
class PolygonTrigger;
class ObjectTypes;
#ifdef _INTERNAL
#define SPECIAL_SCRIPT_PROFILING
#endif
// Slightly odd place to put breeze info, but the breeze info is
// set by script, so it's as good a place as any. john a.
struct BreezeInfo
{
Real m_direction; ///< Direction of the breeze in radians. 0 == +x direction.
Coord2D m_directionVec; ///< sin/cos of direction, for efficiency.
Real m_intensity; ///< How far to sway back & forth in radians. 0 = none.
Real m_lean; ///< How far to lean with the wind in radians. 0 = none.
Real m_randomness; ///< Randomness 0=perfectly uniform, 1 = +- up to 50% randomly.
Short m_breezePeriod; ///< How many frames it takes to sway forward & back.
Short m_breezeVersion; ///< Incremented each time the settings are updated.
};
// This could belong elsewhere, but for now...
struct NamedReveal
{
AsciiString m_revealName;
AsciiString m_waypointName;
Real m_radiusToReveal;
AsciiString m_playerName;
};
struct TScriptListReadInfo
{
Int numLists;
ScriptList *readLists[MAX_PLAYER_COUNT];
};
struct TCounter
{
Int value;
AsciiString name;
Bool isCountdownTimer;
};
struct TFlag
{
Bool value;
AsciiString name;
};
typedef std::list<AsciiString> ListAsciiString;
typedef std::list<AsciiString>::iterator ListAsciiStringIt;
typedef std::pair<AsciiString, UnsignedInt> PairAsciiStringUINT;
typedef std::list<PairAsciiStringUINT> ListAsciiStringUINT;
typedef ListAsciiStringUINT::iterator ListAsciiStringUINTIt;
typedef std::map< const ThingTemplate *, Int, std::less<const ThingTemplate *> > AttackPriorityMap;
typedef std::pair<AsciiString, ObjectID> AsciiStringObjectIDPair;
typedef std::list<AsciiStringObjectIDPair> ListAsciiStringObjectID;
typedef std::list<AsciiStringObjectIDPair>::iterator ListAsciiStringObjectIDIt;
typedef std::pair<AsciiString, Coord3D> AsciiStringCoord3DPair;
typedef std::list<AsciiStringCoord3DPair> ListAsciiStringCoord3D;
typedef ListAsciiStringCoord3D::iterator ListAsciiStringCoord3DIt;
typedef std::map< AsciiString, Int > ObjectTypeCount;
typedef std::vector<Player *> VectorPlayerPtr;
typedef VectorPlayerPtr::iterator VectorPlayerPtrIt;
typedef std::vector<ObjectTypes*> AllObjectTypes;
typedef AllObjectTypes::iterator AllObjectTypesIt;
typedef std::vector<NamedReveal> VecNamedReveal;
typedef VecNamedReveal::iterator VecNamedRevealIt;
class AttackPriorityInfo : public MemoryPoolObject, public Snapshot
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AttackPriorityInfo, "AttackPriorityInfo")
// friend bad for MPOs. (srj)
//friend class ScriptEngine;
public:
AttackPriorityInfo();
//~AttackPriorityInfo();
public:
void setPriority(const ThingTemplate *tThing, Int priority);
Int getPriority(const ThingTemplate *tThing) const;
AsciiString getName(void) const {return m_name;}
#ifdef _DEBUG
void dumpPriorityInfo(void);
#endif
void friend_setName(const AsciiString& n) { m_name = n; }
void friend_setDefaultPriority(Int n) { m_defaultPriority = n; }
void reset(void);
protected:
// sanapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
AsciiString m_name;
Int m_defaultPriority;
AttackPriorityMap *m_priorityMap;
};
class SequentialScript : public MemoryPoolObject, public Snapshot
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(SequentialScript, "SequentialScript")
public:
enum { START_INSTRUCTION = -1 };
Team *m_teamToExecOn;
ObjectID m_objectID;
Script *m_scriptToExecuteSequentially;
Int m_currentInstruction; // Which action within m_scriptToExecuteSequentially am I currently executing?
Int m_timesToLoop; // 0 = do once, >0, loop till 0, <0, loop infinitely
Int m_framesToWait; // 0 = transition to next instruction, >0 = countdown to 0, <0 = advance on idle only
Bool m_dontAdvanceInstruction; // Must be set every frame by the instruction requesting the wait.
// so this parm tells us how many we've been idle so far.
SequentialScript *m_nextScriptInSequence;
SequentialScript();
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
};
EMPTY_DTOR(SequentialScript)
#ifdef NOT_IN_USE
class SequentialScriptStatus : public MemoryPoolObject, public Snapshot
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(SequentialScriptStatus, "SequentialScriptStatus")
public:
ObjectID m_objectID;
AsciiString m_sequentialScriptCompleted;
Bool m_isExecutingSequentially;
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
};
#endif
//-----------------------------------------------------------------------------
// ScriptEngine
//-----------------------------------------------------------------------------
/** Implementation for the Script Engine singleton */
//-----------------------------------------------------------------------------
class ScriptEngine : public SubsystemInterface,
public Snapshot
{
public:
enum {MAX_COUNTERS=256, MAX_FLAGS=256, MAX_ATTACK_PRIORITIES=256};
enum TFade {FADE_NONE, FADE_SUBTRACT, FADE_ADD, FADE_SATURATE, FADE_MULTIPLY};
ScriptEngine();
virtual ~ScriptEngine();
virtual void init( void ); ///< Init
virtual void reset( void ); ///< Reset
virtual void update( void ); ///< Update
void appendSequentialScript(const SequentialScript *scriptToSequence);
void removeSequentialScript(SequentialScript *scriptToRemove);
void notifyOfTeamDestruction(Team *teamDestroyed);
void notifyOfObjectCreationOrDestruction(void);
UnsignedInt getFrameObjectCountChanged(void) {return m_frameObjectCountChanged;}
void setSequentialTimer(Object *obj, Int frameCount);
void setSequentialTimer(Team *team, Int frameCount);
void removeAllSequentialScripts(Object *obj);
void removeAllSequentialScripts(Team *team);
AsciiString getStats(Real *curTime, Real *script1Time, Real *script2Time);
virtual void newMap( ); ///< reset script engine for new map
virtual const ActionTemplate *getActionTemplate( Int ndx); ///< Get the template for a script action.
virtual const ConditionTemplate *getConditionTemplate( Int ndx); ///< Get the template for a script Condition.
virtual void startEndGameTimer(void); ///< Starts the end game timer after a mission is won or lost.
Bool isGameEnding( void ) { return m_endGameTimer >= 0; }
virtual void startQuickEndGameTimer(void); ///< Starts the quick end game timer after a campaign is won or lost.
virtual void startCloseWindowTimer(void); ///< Starts the timer to close windows after a mission is won or lost.
virtual void runScript(const AsciiString& scriptName, Team *pThisTeam=NULL); ///< Runs a script.
virtual void runObjectScript(const AsciiString& scriptName, Object *pThisObject=NULL); ///< Runs a script attached to this object.
virtual Team *getTeamNamed(const AsciiString& teamName); ///< Gets the named team. May be null.
virtual Player *getSkirmishEnemyPlayer(void); ///< Gets the ai's enemy Human player. May be null.
virtual Player *getCurrentPlayer(void); ///< Gets the player that owns the current script. May be null.
virtual Player *getPlayerFromAsciiString(const AsciiString& skirmishPlayerString);
void setObjectsShouldReceiveDifficultyBonus(Bool receive) { m_objectsShouldReceiveDifficultyBonus = receive; }
Bool getObjectsShouldReceiveDifficultyBonus() const { return m_objectsShouldReceiveDifficultyBonus; }
void setChooseVictimAlwaysUsesNormal(Bool receive) { m_ChooseVictimAlwaysUsesNormal = receive; }
Bool getChooseVictimAlwaysUsesNormal() const { return m_ChooseVictimAlwaysUsesNormal; }
Bool hasShownMPLocalDefeatWindow(void);
void markMPLocalDefeatWindowShown(void);
// NOTE NOTE NOTE: do not store of the return value of this call (getObjectTypeList) beyond the life of the
// function it will be used in, as it can be deleted from under you if maintenance is performed on the object.
virtual ObjectTypes *getObjectTypes(const AsciiString& objectTypeList);
virtual void doObjectTypeListMaintenance(const AsciiString& objectTypeList, const AsciiString& objectType, Bool addObject);
/// Return the trigger area with the given name
virtual PolygonTrigger *getQualifiedTriggerAreaByName( AsciiString name );
// For other systems to evaluate Conditions, execute Actions, etc.
///< if pThisTeam is specified, then scripts in here can use <This Team> to mean the team this script is attached to.
virtual Bool evaluateConditions( Script *pScript, Team *pThisTeam = NULL, Player *pPlayer=NULL );
virtual void friend_executeAction( ScriptAction *pActionHead, Team *pThisTeam = NULL); ///< Use this at yer peril.
virtual Object *getUnitNamed(const AsciiString& unitName); ///< Gets the named unit. May be null.
virtual Bool didUnitExist(const AsciiString& unitName);
virtual void addObjectToCache( Object* pNewObject );
virtual void removeObjectFromCache( Object* pDeadObject );
virtual void transferObjectName( const AsciiString& unitName, Object *pNewObject );
virtual void notifyOfObjectDestruction( Object *pDeadObject );
virtual void notifyOfCompletedVideo( const AsciiString& completedVideo ); ///< Notify the script engine that a video has completed
virtual void notifyOfTriggeredSpecialPower( Int playerIndex, const AsciiString& completedPower, ObjectID sourceObj );
virtual void notifyOfMidwaySpecialPower ( Int playerIndex, const AsciiString& completedPower, ObjectID sourceObj );
virtual void notifyOfCompletedSpecialPower( Int playerIndex, const AsciiString& completedPower, ObjectID sourceObj );
virtual void notifyOfCompletedUpgrade ( Int playerIndex, const AsciiString& upgrade, ObjectID sourceObj );
virtual void notifyOfAcquiredScience ( Int playerIndex, ScienceType science );
virtual void signalUIInteract(const AsciiString& hookName); ///< Notify that a UI button was pressed and some flag should go true, for one frame only.
virtual Bool isVideoComplete( const AsciiString& completedVideo, Bool removeFromList ); ///< Determine whether a video has completed
virtual Bool isSpeechComplete( const AsciiString& completedSpeech, Bool removeFromList ); ///< Determine whether a speech has completed
virtual Bool isAudioComplete( const AsciiString& completedAudio, Bool removeFromList ); ///< Determine whether a sound has completed
virtual Bool isSpecialPowerTriggered( Int playerIndex, const AsciiString& completedPower, Bool removeFromList, ObjectID sourceObj );
virtual Bool isSpecialPowerMidway ( Int playerIndex, const AsciiString& completedPower, Bool removeFromList, ObjectID sourceObj );
virtual Bool isSpecialPowerComplete ( Int playerIndex, const AsciiString& completedPower, Bool removeFromList, ObjectID sourceObj );
virtual Bool isUpgradeComplete ( Int playerIndex, const AsciiString& upgrade, Bool removeFromList, ObjectID sourceObj );
virtual Bool isScienceAcquired ( Int playerIndex, ScienceType science, Bool removeFromList );
void setToppleDirection( const AsciiString& objectName, const Coord3D *direction );
virtual void adjustToppleDirection( Object *object, Coord2D *direction);
virtual void adjustToppleDirection( Object *object, Coord3D *direction);
virtual const Script *findScriptByName(const AsciiString& scriptName) {return findScript(scriptName);} ///< Finds a script.
const BreezeInfo& getBreezeInfo() const {return m_breezeInfo;}
Bool isTimeFrozenScript( void ); ///< Ask whether a script has frozen time or not
void doFreezeTime( void );
void doUnfreezeTime( void );
/// The following functions are used to update and query the debug window
Bool isTimeFrozenDebug( void ); ///< Ask whether the debug window has requested a pause.
Bool isTimeFast( void ); ///< Ask whether the debug window has requested a fast forward.
void forceUnfreezeTime( void ); ///< Force that time becomes unfrozen temporarily.
void AppendDebugMessage(const AsciiString& strToAdd, Bool forcePause);
void AdjustDebugVariableData(const AsciiString& variableName, Int value, Bool forcePause);
void clearTeamFlags(void); ///< Hack for dustin.
void clearFlag(const AsciiString &name); ///< Hack for dustin.
TFade getFade(void) {return m_fade;}
Real getFadeValue(void) {return m_curFadeValue;}
AsciiString getCurrentTrackName() const { return m_currentTrackName; }
void setCurrentTrackName(AsciiString a) { m_currentTrackName = a; }
GameDifficulty getGlobalDifficulty( void ) const { return m_gameDifficulty; }
void setGlobalDifficulty( GameDifficulty difficulty );
/// Attack priority stuff.
const AttackPriorityInfo *getDefaultAttackInfo(void);
const AttackPriorityInfo *getAttackInfo(const AsciiString& name);
const TCounter *getCounter(const AsciiString& counterName);
void createNamedMapReveal(const AsciiString& revealName, const AsciiString& waypointName, Real radiusToReveal, const AsciiString& playerName);
void doNamedMapReveal(const AsciiString& revealName);
void undoNamedMapReveal(const AsciiString& revealName);
void removeNamedMapReveal(const AsciiString& revealName);
Int getObjectCount(Int playerIndex, const AsciiString& objectTypeName) const;
void setObjectCount(Int playerIndex, const AsciiString& objectTypeName, Int newCount);
//Kris: Moved to public... so that I can refresh it when building abilities in script dialogs.
void createNamedCache( void );
///Begin VTUNE
void setEnableVTune(Bool value);
Bool getEnableVTune() const;
///End VTUNE
//#if defined(_DEBUG) || defined(_INTERNAL)
void debugVictory( void );
//#endif
protected:
// snapshot methods
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
Int allocateCounter( const AsciiString& name);
Int allocateFlag( const AsciiString& name);
void executeScripts( Script *pScriptHead );
void executeScript( Script *pScript );
Script *findScript(const AsciiString& name);
ScriptGroup *findGroup(const AsciiString& name);
void setSway( ScriptAction *pAction );
void setCounter( ScriptAction *pAction );
void addCounter( ScriptAction *pAction );
void subCounter( ScriptAction *pAction );
void setFade( ScriptAction *pAction );
void setFlag( ScriptAction *pAction );
void pauseTimer( ScriptAction *pAction );
void restartTimer( ScriptAction *pAction );
void setTimer( ScriptAction *pAction, Bool milisecondTimer, Bool random);
void adjustTimer( ScriptAction *pAction, Bool milisecondTimer, Bool add);
void enableScript( ScriptAction *pAction );
void disableScript( ScriptAction *pAction );
void callSubroutine( ScriptAction *pAction );
void checkConditionsForTeamNames(Script *pScript);
Bool evaluateCounter( Condition *pCondition );
Bool evaluateFlag( Condition *pCondition );
Bool evaluateTimer( Condition *pCondition );
Bool evaluateCondition( Condition *pCondition );
void executeActions( ScriptAction *pActionHead );
void setPriorityThing( ScriptAction *pAction );
void setPriorityKind( ScriptAction *pAction );
void setPriorityDefault( ScriptAction *pAction );
// For Object types maintenance.
void removeObjectTypes(ObjectTypes *typesToRemove);
void particleEditorUpdate( void );
void updateFades( void );
AttackPriorityInfo *findAttackInfo(const AsciiString& name, Bool addIfNotFound);
protected:
/// Stuff to execute scripts sequentially
typedef std::vector<SequentialScript*> VecSequentialScriptPtr;
typedef VecSequentialScriptPtr::iterator VecSequentialScriptPtrIt;
VecSequentialScriptPtr m_sequentialScripts;
void evaluateAndProgressAllSequentialScripts( void );
VecSequentialScriptPtrIt cleanupSequentialScript(VecSequentialScriptPtrIt it, Bool cleanDanglers);
Bool hasUnitCompletedSequentialScript( Object *object, const AsciiString& sequentialScriptName );
Bool hasTeamCompletedSequentialScript( Team *team, const AsciiString& sequentialScriptName );
protected:
ActionTemplate m_actionTemplates[ScriptAction::NUM_ITEMS];
ConditionTemplate m_conditionTemplates[Condition::NUM_ITEMS];
TCounter m_counters[MAX_COUNTERS];
Int m_numCounters;
TFlag m_flags[MAX_FLAGS];
Int m_numFlags;
AttackPriorityInfo m_attackPriorityInfo[MAX_ATTACK_PRIORITIES];
Int m_numAttackInfo;
Int m_endGameTimer;
Int m_closeWindowTimer;
Team *m_callingTeam; ///< Team that is calling script, used for THIS_TEAM
Object *m_callingObject; ///< Object that is calling script, used for THIS_OBJECT
Team *m_conditionTeam; ///< Team that is being used to evaluate conditions, used for THIS_TEAM
Object *m_conditionObject; ///< Unit that is being used to evaluate conditions, used for THIS_OBJECT
VecNamedRequests m_namedObjects;
Bool m_firstUpdate;
Player *m_currentPlayer;
Player *m_skirmishHumanPlayer;
AsciiString m_currentTrackName;
TFade m_fade;
Real m_minFade;
Real m_maxFade;
Real m_curFadeValue;
Int m_curFadeFrame;
Int m_fadeFramesIncrease;
Int m_fadeFramesHold;
Int m_fadeFramesDecrease;
UnsignedInt m_frameObjectCountChanged;
ObjectTypeCount m_objectCounts[MAX_PLAYER_COUNT];
/// These are three separate lists rather than one to increase speed efficiency
ListAsciiString m_completedVideo;
ListAsciiStringUINT m_testingSpeech;
ListAsciiStringUINT m_testingAudio;
ListAsciiString m_uiInteractions;
ListAsciiStringObjectID m_triggeredSpecialPowers[MAX_PLAYER_COUNT];
ListAsciiStringObjectID m_midwaySpecialPowers [MAX_PLAYER_COUNT];
ListAsciiStringObjectID m_finishedSpecialPowers [MAX_PLAYER_COUNT];
ListAsciiStringObjectID m_completedUpgrades [MAX_PLAYER_COUNT];
ScienceVec m_acquiredSciences [MAX_PLAYER_COUNT];
ListAsciiStringCoord3D m_toppleDirections;
VecNamedReveal m_namedReveals;
BreezeInfo m_breezeInfo;
GameDifficulty m_gameDifficulty;
Bool m_freezeByScript;
AllObjectTypes m_allObjectTypeLists;
Bool m_objectsShouldReceiveDifficultyBonus;
Bool m_ChooseVictimAlwaysUsesNormal;
Bool m_shownMPLocalDefeatWindow;
#ifdef SPECIAL_SCRIPT_PROFILING
#ifdef DEBUG_LOGGING
double m_numFrames;
double m_totalUpdateTime;
double m_maxUpdateTime;
double m_curUpdateTime;
#endif
#endif
}; // end class ScriptEngine
extern ScriptEngine *TheScriptEngine; ///< singleton definition
#endif // end __SCRIPTENGINE_H_