/* ** 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 . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Team.h /////////////////////////////////////////////////////////////// // Team interface definition // Author: Michael S. Booth, Steven Johnson, March 2001 #pragma once #ifndef _TEAM_H_ #define _TEAM_H_ #include "Common/GameType.h" #include "Common/Snapshot.h" #include "Common/Thing.h" #include "GameLogic/Object.h" // ------------------------------------------------------------------------------------------------ typedef UnsignedInt TeamID; #define TEAM_ID_INVALID 0 // ------------------------------------------------------------------------------------------------ typedef UnsignedInt TeamPrototypeID; #define TEAM_PROTOTYPE_ID_INVALID 0 // ------------------------------------------------------------------------------------------------ typedef std::hash_map< TeamID, Relationship, std::hash, std::equal_to > TeamRelationMapType; class TeamRelationMap : public MemoryPoolObject, public Snapshot { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( TeamRelationMap, "TeamRelationMapPool" ) public: TeamRelationMap( void ); // virtual destructor provided by memory pool object /** @todo I'm jsut wrappign this up in a nice snapshot object, we really should isolate * m_map from public access and make access methods for our operations */ TeamRelationMapType m_map; protected: virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); }; // ------------------------------------------------------------------------------------------------ typedef void (*ObjectIterateFunc)( Object *obj, void *userData ); ///< callback type for iterating objects // ------------------------------------------------------------------------ /** How are teams represented in mapfiles? -- All Players always have at least one top-level team, with the name "team" (WB will enforce this in mapfiles.) -- We'll have a TeamDict list that is parallel to the PlayerDict list -- All Teams (toplevel and subteams) are in the list -- Each TeamDict contains: -- Team Name (an internal ascii str) -- Owning Team (or Player), by Name -- Misc Flags -- Teams we are allies with, by name (only for toplevel teams) -- Teams we are enemies with, by name (only for toplevel teams) -- Note that Players and Teams share the same namespace, so you cannot have a team with the same name as a player (or vice versa). -- MapObject Dicts will be revised to have an "owning team" rather than "owning player" entry -- BuildLists must also have a way to assign units to Teams, as appropriate */ // ------------------------------------------------------------------------ class AIGroup; class Dict; class Player; class PolygonTrigger; class Script; class SidesList; class TeamFactory; class TeamPrototype; class Team; class ThingTemplate; class Waypoint; class PlayerRelationMap; enum AttitudeType; typedef struct { Int minUnits; Int maxUnits; AsciiString unitThingName; } TCreateUnitsInfo; enum { MAX_GENERIC_SCRIPTS = 16 }; // ------------------------------------------------------------------------ /// This is the info for creating reinforcement and AI teams. class TeamTemplateInfo : public Snapshot { public: TeamTemplateInfo(Dict *d); enum {MAX_UNIT_TYPES = 7}; typedef enum {NORMAL=0, IGNORE_DISTRACTIONS=1, DEAL_AGGRESSIVELY=2} TBehavior; TCreateUnitsInfo m_unitsInfo[MAX_UNIT_TYPES]; ///< Quantity of units to create or build. Int m_numUnitsInfo; ///< Number of entries in m_unitsInfo Coord3D m_homeLocation; ///< Spawn location for team. Bool m_hasHomeLocation; ///< True is m_homeLocation is valid. AsciiString m_scriptOnCreate; ///< Script executed when team is created. AsciiString m_scriptOnIdle; ///< Script executed when team is idle. Int m_initialIdleFrames; ///< Number of frames to continue recruiting after the minimum team size is achieved. AsciiString m_scriptOnEnemySighted; ///< Script executed when enemy is sighted. AsciiString m_scriptOnAllClear; ///< Script executed when enemy is sighted. AsciiString m_scriptOnUnitDestroyed; ///< Script executed each time a unit on this team dies. AsciiString m_scriptOnDestroyed; ///< Script executed m_destroyedThreshold of member units are destroyed. Real m_destroyedThreshold; ///< OnDestroyed threshold - 1.0 = 100% = all destroyed, .5 = 50% = half of the units destroyed, 0 = useless. Bool m_isAIRecruitable; ///< True if other ai teams can recruit. Bool m_isBaseDefense; ///< True if is base defense team. Bool m_isPerimeterDefense; ///< True if is a perimeter base defense team. Bool m_automaticallyReinforce; ///< True is team automatically tries to reinforce. Bool m_transportsReturn; ///< True if transports return to base after unloading. Bool m_avoidThreats; ///< True if the team avoids threats. Bool m_attackCommonTarget; ///< True if the team attacks the same target unit. Int m_maxInstances; ///< Maximum number of instances of a team that is not singleton. mutable Int m_productionPriority; ///< Production priority. Int m_productionPrioritySuccessIncrease; ///< Production priority increase on success. Int m_productionPriorityFailureDecrease; ///< Production priority decrease on failure. AttitudeType m_initialTeamAttitude; ///< The initial team attitude AsciiString m_transportUnitType; ///< Unit used to transport the team. AsciiString m_startReinforceWaypoint; ///< Waypoint where the reinforcement team starts. Bool m_teamStartsFull; ///< If true, team loads into member transports. Bool m_transportsExit; ///< True if the transports leave after deploying team. VeterancyLevel m_veterancy; ///< Veterancy level; // Production scripts stuff AsciiString m_productionCondition; ///< Script that contains the production conditions. Bool m_executeActions; ///< If this is true, then when the production condition becomes true, we also execute the actions. AsciiString m_teamGenericScripts[MAX_GENERIC_SCRIPTS]; protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); }; // ------------------------------------------------------------------------ class Team : public MemoryPoolObject, public Snapshot { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(Team, "TeamPool" ) private: TeamPrototype *m_proto; ///< the prototype used to create this Team TeamID m_id; ///< unique team id // lists we own /// @todo srj -- convert to non-DLINK list, after it is once again possible to test the change MAKE_DLINK_HEAD(Object, TeamMemberList) ///< the members of this team // lists we are members of /// @todo srj -- convert to non-DLINK list, after it is once again possible to test the change MAKE_DLINK(Team, TeamInstanceList) ///< the instances of our prototype AsciiString m_state; ///< Name of the current AI state. Bool m_enteredOrExited; ///< True if a team member entered or exited a trigger area this frame. Bool m_active; ///< True if a team is complete. False while members are being added. Bool m_created; ///< True when first activated. // Enemy sighted & All Clear: Bool m_checkEnemySighted;///< True if we have an on enemy sighted or all clear script. Bool m_seeEnemy; ///< True if we see an enemy. Bool m_prevSeeEnemy; ///< Last value. // Idle flag. Bool m_wasIdle; ///< True if idle last frame. // On %Destroyed Int m_destroyThreshold; Int m_curUnits; // Following waypoint paths as a team. const Waypoint *m_currentWaypoint; // Should check/Execute generic script Bool m_shouldAttemptGenericScript[MAX_GENERIC_SCRIPTS]; // Recruitablity. Bool m_isRecruitablitySet; ///< If false, recruitability is team proto value. If true, m_isRecruitable. Bool m_isRecruitable; // Attack target. ObjectID m_commonAttackTarget; TeamRelationMap *m_teamRelations; ///< override allies & enemies PlayerRelationMap *m_playerRelations; ///< override allies & enemies std::list< ObjectID > m_xferMemberIDList; ///< list for post processing and restoring object pointers after a load protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); public: Team( TeamPrototype *proto, TeamID id ); // ~Team(); /// return the prototype used to create this team const TeamPrototype *getPrototype( void ) { return m_proto; } void setID( TeamID id ) { m_id = id; } TeamID getID() const { return m_id; } /** Set the attack priority name for a team. */ void setAttackPriorityName(AsciiString name); /** return the player currently controlling this team (backtracking up if necessary) */ Player *getControllingPlayer() const; /** set the team's owner. (NULL is not allowed) */ void setControllingPlayer(Player *newController); /** Get the team's state */ const AsciiString& getState(void) const {return m_state;} /** fill pAIGroup (which must be NONNULL) with the members of this team as a Group. */ void getTeamAsAIGroup(AIGroup *pAIGroup); /** Get the team's state */ inline const AsciiString& getName() const; /** Get the count of live things that are either alive or are buildings. */ Int getTargetableCount() const; /** Set the team's AI state. */ void setState(const AsciiString& state) {m_state = state;} /** Set the team's AI recruitablity. */ void setRecruitable(Bool recruitable) {m_isRecruitablitySet = true; m_isRecruitable = recruitable;} /** Set the team's target object. */ void setTeamTargetObject(const Object *target) ; /** Set the team's target object. */ Object *getTeamTargetObject(void); /** Set the team as active. A team is considered created when set active. */ void setActive(void) {if (!m_active) { m_created = true;m_active = true;}} /** Is this team active? */ Bool isActive(void) {return m_active;} /** Is this team just createc? (stays true one logic frame.) */ Bool isCreated(void) {return m_created;} /** Note that a team member entered or exited a trigger area. */ void setEnteredExited(void) {m_enteredOrExited = true;} /** Did a team member enter or exit a trigger area. */ Bool didEnterOrExit(void) {return m_enteredOrExited;} /** Clear the flag that a team member entered or exited a trigger area. Also checks and executes any onCreate scripts, and clears the created flag. */ void updateState(void); void notifyTeamOfObjectDeath( void ); Bool didAllEnter(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const; ///< All members entered the area Bool didPartialEnter(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const; ///< One member entered the area Bool didAllExit(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const; ///< All members exited the area Bool didPartialExit(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const; ///< One member exited the area Bool allInside(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const; ///< All members are inside the area Bool someInsideSomeOutside(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const;///< One or more in, one or more out. Bool noneInside(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const; ///< No members are in the area. Object * tryToRecruit(const ThingTemplate *, const Coord3D *teamHome, Real maxDist); ///< Try to recruit the closest unit of this thing type. Return true if successful. /** return our relationship with the other team. this is done as follows: -- if there's a TeamRelationship between this team and that team, return it. -- otherwise, we use the relationship between our players. */ Relationship getRelationship(const Team *that) const; /** set a special relationship between this team and that team, that overrides the one between our Players. (note that this doesn't imply anything about the relation of that to this.) */ void setOverrideTeamRelationship( TeamID teamID, Relationship r ); /** remove the special relation (if any) between this and that. remove all special relations for 'this' if that==null. return true if anything removed. */ Bool removeOverrideTeamRelationship( TeamID teamID ); /** set a special relationship between this team and that Player, that overrides the one between our Players. (note that this doesn't imply anything about the relation of that to this.) Note that override-team relationships take precedence over override-player relationships. */ void setOverridePlayerRelationship( Int playerIndex, Relationship r ); /** remove the special relation (if any) between this and that. remove all special relations for 'this' if that==null. return true if anything removed. */ Bool removeOverridePlayerRelationship( Int playerIndex ); /** a convenience routine to count the number of owned objects that match a set of ThingTemplates. You input the count and an array of ThingTemplate*, and provide an array of Int of the same size. It fills in the array to the correct counts. This is handy because we must traverse the team's list-of-objects only once. */ void countObjectsByThingTemplate(Int numTmplates, const ThingTemplate* const* things, Bool ignoreDead, Int *counts, Bool ignoreUnderConstruction = TRUE ) const; /** returns the number of buildings on this team, by checking each things' template kindof against KINDOF_STRUCTURE */ Int countBuildings(void); /** simply returns the number of objects on this team with a specific KindOfMaskType */ Int countObjects(KindOfMaskType setMask, KindOfMaskType clearMask); /** This Team will heal all its members */ void healAllObjects(); /** * Iterate all members of the team */ void iterateObjects( ObjectIterateFunc func, void *userData ); /** a convenience routine to quickly check if any buildings are owned. */ Bool hasAnyBuildings(void) const; /** a convenience routine to quickly check if any buildings with a specific KindOfType flag are owned. */ Bool hasAnyBuildings(KindOfMaskType kindOf) const; /** a convenience routine to quickly check if any units are owned. */ Bool hasAnyUnits(void) const; /** a convenience routine to quickly check if any objects are owned. */ Bool hasAnyObjects(void) const; /** a convenience routine to quickly check if all the units are idle. */ Bool isIdle(void) const; /** a convenience routine to quickly check if any objects are in a trigger area. */ Bool unitsEntered(PolygonTrigger *pTrigger) const; /** a convenience routine to quickly check if any buildfacilities are owned. */ Bool hasAnyBuildFacility(void) const; /** Move team to destination. */ void moveTeamTo(Coord3D destination); /** a convenience routine to quickly destroy a team. */ Bool damageTeamMembers(Real amount); /** a convenience routine to destroy this team. The team goes through all shutdown stuff. Note that this doesn't actually call deleteInstance, the team will actually be deleted on the next update, if it is a deletable team. IgnoreDead lets you not delete people who are dying anyway. Needed for scripts. */ void deleteTeam(Bool ignoreDead = FALSE); /** a convenience routine used to estimate the team's position by just returning the position of the first member of the team */ const Coord3D* getEstimateTeamPosition(void); /** a convenience routine to move a team's units to another team. */ void transferUnitsTo(Team *newTeam); /** a function to kill all members of a team */ void killTeam(void); /** a function to make all containers on a team to dump contents */ void evacuateTeam(void); /** the current waypoint for a team following a waypoint path. */ const Waypoint *getCurrentWaypoint(void) {return m_currentWaypoint;} void setCurrentWaypoint(const Waypoint *way) {m_currentWaypoint = way;} /** Update the generic scripts, allow them to see if they should run, etc. */ void updateGenericScripts(void); }; // ------------------------------------------------------------------------ /** Note that TeamPrototype is used to hold information that is invariant between multiple instances of a given Team (e.g., alliance info). However, a TeamPrototype doesn't contain any build-list style info; that is handled by the BuildList stuff. */ class TeamPrototype : public MemoryPoolObject, public Snapshot { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TeamPrototype, "TeamPrototypePool" ) public: TeamPrototype( TeamFactory *tf, const AsciiString& name, Player *ownerPlayer, Bool isSingleton, Dict *d, TeamPrototypeID id ); // virtual destructor prototype provided by memory pool object inline TeamPrototypeID getID() const { return m_id; } inline const AsciiString& getName() const { return m_name; } inline Bool getIsSingleton() const { return (m_flags & TEAM_SINGLETON) != 0; } inline const TeamTemplateInfo *getTemplateInfo(void) const {return &m_teamTemplate;} /** return the team's owner (backtracking up if necessary) */ Player *getControllingPlayer() const; /** * Return a team with matching team ID if present */ Team *findTeamByID( TeamID teamID ); /** set the team's owner. (NULL is not allowed) */ void setControllingPlayer(Player *newController); /** Evaluate team's production condition. */ Bool evaluateProductionCondition(void); /** count for all the team instances belonging to this prototype. */ void countObjectsByThingTemplate(Int numTmplates, const ThingTemplate* const* things, Bool ignoreDead, Int *counts, Bool ignoreUnderConstruction = TRUE ) const; /** count the buildings owned by this Team template */ Int countBuildings(void); /** simply returns the number of objects on this team with a specific KindOfMaskType */ Int countObjects(KindOfMaskType setMask, KindOfMaskType clearMask); /** This TeamProtoType will heal all objects in all its instances */ void healAllObjects(); /** * Iterate all members of the team */ void iterateObjects( ObjectIterateFunc func, void *userData ); /// count the number of teams that have been instanced by this prototype Int countTeamInstances( void ); /** Checks & clears the flags that a team member entered or exited a trigger area, or was created. */ void updateState(void); /** a convenience routine to quickly check if any buildings are owned. */ Bool hasAnyBuildings(void) const; /** a convenience routine to quickly check if any buildings with a specific KindOfType flag are owned. */ Bool hasAnyBuildings(KindOfMaskType kindOf) const; /** a convenience routine to quickly check if any units are owned. */ Bool hasAnyUnits(void) const; /** a convenience routine to quickly check if any objects are owned. */ Bool hasAnyObjects(void) const; /** a convenience routine to quickly check if any buildfacilities are owned. */ Bool hasAnyBuildFacility(void) const; /** a convenience routine to quickly destroy a team. */ void damageTeamMembers(Real amount); /** Move team to destination. */ void moveTeamTo(Coord3D destination); // this is intended for use ONLY by class Player. void friend_setOwningPlayer(Player* p) { m_owningPlayer = p; } void teamAboutToBeDeleted(Team* team); Script *getGenericScript(Int scriptToRetrieve); // Make a team more likely to be selected by the ai for building due to success. void increaseAIPriorityForSuccess(void) const; // Make a team less likely to be selected by the ai for building due to failure. void decreaseAIPriorityForFailure(void) const; void setAttackPriorityName(const AsciiString &name) { m_attackPriorityName = name;} AsciiString getAttackPriorityName(void) const { return m_attackPriorityName;} protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); private: enum TeamPrototypeFlags { /** if set, this prototype should only produce one team. if there's already a team by this name, newly produced team members are added to it, rather than put into a new team of the same name. */ TEAM_SINGLETON = 0x01 }; TeamFactory *m_factory; ///< the factory that created us Player *m_owningPlayer; ///< the Player that currently controls the team-proto (null if NOT a top-level team) TeamPrototypeID m_id; ///< unique prototype ID AsciiString m_name; ///< name of the team(s) produced Int m_flags; ///< misc team flags Bool m_productionConditionAlwaysFalse; ///< Flag set to true if we don't have a production condition. Script *m_productionConditionScript; ///< Script to evaluate for production condition. Bool m_retrievedGenericScripts; Script *m_genericScriptsToRun[MAX_GENERIC_SCRIPTS]; TeamTemplateInfo m_teamTemplate; ///< Team template info. AsciiString m_attackPriorityName; // lists we own /// @todo srj -- convert to non-DLINK list, after it is once again possible to test the change MAKE_DLINK_HEAD(Team, TeamInstanceList) ///< the instances of this prototype }; // ------------------------------------------------------------------------ class TeamFactory : public SubsystemInterface, public Snapshot { public: TeamFactory(); ~TeamFactory(); // subsystem methods virtual void init( void ); virtual void reset( void ); virtual void update( void ); void clear(); void initFromSides(SidesList *sides); /// create a new TeamPrototype and if singleton, a team. void initTeam(const AsciiString& name, const AsciiString& owner, Bool isSingleton, Dict *d); /// return the TeamPrototype with the given name. if none exists, return null. TeamPrototype *findTeamPrototype(const AsciiString& name); /// return TeamPrototype with matching ID. if none exists NULL is returned TeamPrototype *findTeamPrototypeByID( TeamPrototypeID id ); /// search all prototypes for the team with the matching id, if none found NULL is returned Team *findTeamByID( TeamID teamID ); // note that there is no way to directly destroy a specific TeamPrototype (or a Team); the only // way to do this is to call TeamFactory::reset(), which destroys all of them. // ------ routines for dealing with Teams /// create a team. there must be a TeamPrototype with the given name, or an exception is thrown. Team *createTeam(const AsciiString& name); /// create a team given an explicity team prototype rather than a prototype name Team *createTeamOnPrototype( TeamPrototype *prototype ); /// create a team. there must be a TeamPrototype with the given name, or an exception is thrown. Team *createInactiveTeam(const AsciiString& name); /// return the team with the given name, or null if none exists. if multiple instances exist, return one arbitrarily. Team *findTeam(const AsciiString& name); void addTeamPrototypeToList(TeamPrototype* team); void removeTeamPrototypeFromList(TeamPrototype* team); void teamAboutToBeDeleted(Team* team); protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); private: typedef std::map< NameKeyType, TeamPrototype*, std::less > TeamPrototypeMap; TeamPrototypeMap m_prototypes; TeamPrototypeID m_uniqueTeamPrototypeID; ///< used to assign unique ids to each team prototype TeamID m_uniqueTeamID; ///< used to assign unique team ids to each team instance }; extern TeamFactory *TheTeamFactory; // inline function ------------------------------------------------------------------------ const AsciiString& Team::getName(void) const { return m_proto->getName(); } // ------------------------------------------------------------------------ #endif // _TEAM_H_