/* ** 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: GameCommon.h //////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: GameCommon.h // // Created: Steven Johnson, October 2001 // // Desc: This is a catchall header for some basic types and definitions // needed by various bits of the GameLogic/GameClient, but that // we haven't found a good place for yet. Hopefully this file // should go away someday, but for now is a convenient spot. // //----------------------------------------------------------------------------- #pragma once #ifndef _GAMECOMMON_H_ #define _GAMECOMMON_H_ // ---------------------------------------------------------------------------------------------- #include "Lib/BaseType.h" // ---------------------------------------------------------------------------------------------- #if defined(_INTERNAL) || defined(_DEBUG) || defined(_PLAYTEST) #define NO_DUMP_PERF_STATS #else #define NO_DUMP_PERF_STATS #endif // ---------------------------------------------------------------------------------------------- enum { LOGICFRAMES_PER_SECOND = 30, MSEC_PER_SECOND = 1000 }; const Real LOGICFRAMES_PER_MSEC_REAL = (((Real)LOGICFRAMES_PER_SECOND) / ((Real)MSEC_PER_SECOND)); const Real MSEC_PER_LOGICFRAME_REAL = (((Real)MSEC_PER_SECOND) / ((Real)LOGICFRAMES_PER_SECOND)); const Real LOGICFRAMES_PER_SECONDS_REAL = (Real)LOGICFRAMES_PER_SECOND; const Real SECONDS_PER_LOGICFRAME_REAL = 1.0f / LOGICFRAMES_PER_SECONDS_REAL; // ---------------------------------------------------------------------------------------------- // note that this returns a REAL value, not an int... most callers will want to // call ceil() on the result, so that partial frames get converted to full frames! inline Real ConvertDurationFromMsecsToFrames(Real msec) { return (msec * LOGICFRAMES_PER_MSEC_REAL); } // ---------------------------------------------------------------------------------------------- inline Real ConvertVelocityInSecsToFrames(Real distPerMsec) { // this looks wrong, but is the correct conversion factor. return (distPerMsec * SECONDS_PER_LOGICFRAME_REAL); } // ---------------------------------------------------------------------------------------------- inline Real ConvertAccelerationInSecsToFrames(Real distPerSec2) { // this looks wrong, but is the correct conversion factor. const Real SEC_PER_LOGICFRAME_SQR = (SECONDS_PER_LOGICFRAME_REAL * SECONDS_PER_LOGICFRAME_REAL); return (distPerSec2 * SEC_PER_LOGICFRAME_SQR); } // ---------------------------------------------------------------------------------------------- inline Real ConvertAngularVelocityInDegreesPerSecToRadsPerFrame(Real degPerSec) { const Real RADS_PER_DEGREE = PI / 180.0f; return (degPerSec * (SECONDS_PER_LOGICFRAME_REAL * RADS_PER_DEGREE)); } // ---------------------------------------------------------------------------------------------- enum { MAX_PLAYER_COUNT = 16 ///< max number of Players. }; // ---------------------------------------------------------------------------------------------- /** a bitmask that can uniquely represent each player. */ #if MAX_PLAYER_COUNT <= 16 typedef UnsignedShort PlayerMaskType; const PlayerMaskType PLAYERMASK_ALL = 0xffff; const PlayerMaskType PLAYERMASK_NONE = 0x0; #else #error "this is the wrong size" #endif //------------------------------------------------------------------------------------------------- enum GameDifficulty { DIFFICULTY_EASY, DIFFICULTY_NORMAL, DIFFICULTY_HARD, DIFFICULTY_COUNT }; //------------------------------------------------------------------------------------------------- enum PlayerType { PLAYER_HUMAN, ///< player is human-controlled PLAYER_COMPUTER, ///< player is computer-controlled PLAYERTYPE_COUNT }; //------------------------------------------------------------------------------------------------- /// A PartitionCell can be one of three states for Shroud enum CellShroudStatus { CELLSHROUD_CLEAR, CELLSHROUD_FOGGED, CELLSHROUD_SHROUDED, CELLSHROUD_COUNT }; //------------------------------------------------------------------------------------------------- /// Since an object can take up more than a single PartitionCell, this is a status that applies to the whole Object enum ObjectShroudStatus { OBJECTSHROUD_INVALID, ///< indeterminate state, will recompute OBJECTSHROUD_CLEAR, ///< object is not shrouded at all (ie, completely visible) OBJECTSHROUD_PARTIAL_CLEAR, ///< object is partly clear (rest is shroud or fog) OBJECTSHROUD_FOGGED, ///< object is completely fogged OBJECTSHROUD_SHROUDED, ///< object is completely shrouded OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID, ///< indeterminate state, will recompute, BUT previous status is valid, don't reset (used for save/load) OBJECTSHROUD_COUNT }; //------------------------------------------------------------------------------------------------- enum GuardMode { GUARDMODE_NORMAL, GUARDMODE_GUARD_WITHOUT_PURSUIT, // no pursuit out of guard area GUARDMODE_GUARD_FLYING_UNITS_ONLY // ignore nonflyers }; // --------------------------------------------------- enum { NEVER = 0, FOREVER = 0x3fffffff // (we use 0x3fffffff so that we can add offsets and not overflow... // at 30fps we're still pretty safe!) }; //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /// Veterancy level define needed by several files that don't need the full Experience code. // NOTE NOTE NOTE: Keep TheVeterencyNames in sync with these. enum VeterancyLevel { LEVEL_REGULAR = 0, LEVEL_VETERAN, LEVEL_ELITE, LEVEL_HEROIC, LEVEL_COUNT, LEVEL_INVALID, LEVEL_FIRST = LEVEL_REGULAR, LEVEL_LAST = LEVEL_HEROIC }; // TheVeterancyNames is defined in GameCommon.cpp extern const char *TheVeterancyNames[]; //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- enum CommandSourceType { CMD_FROM_PLAYER = 0, CMD_FROM_SCRIPT, CMD_FROM_AI, CMD_FROM_DOZER, // Special rare command when the dozer originates a command to attack a mine. Mines are not ai-attackable, and it seems deceitful for the dozer to generate a player or script command. jba. }; ///< the source of a command //------------------------------------------------------------------------------------------------- enum AbleToAttackType { _ATTACK_FORCED = 0x01, _ATTACK_CONTINUED = 0x02, _ATTACK_TUNNELNETWORK_GUARD = 0x04, /** can we attack if this is a new target? */ ATTACK_NEW_TARGET = (0), /** can we attack if this is a new target, via force-fire? (The only current difference between this and ATTACK_NEW_TARGET is that disguised units are force-attackable even when stealthed.) */ ATTACK_NEW_TARGET_FORCED= (_ATTACK_FORCED), /** can we attack if this is continuation of an existing attack? (The only current difference between this and ATTACK_NEW_TARGET is you are allowed to follow immobile shrouded units into the fog) */ ATTACK_CONTINUED_TARGET = (_ATTACK_CONTINUED), /** can we attack if this is continuation of an existing attack? (The only current difference between this and ATTACK_NEW_TARGET is you are allowed to follow immobile shrouded units into the fog) */ ATTACK_CONTINUED_TARGET_FORCED = (_ATTACK_FORCED | _ATTACK_CONTINUED), /** Special case that bypasses some of the checks for units guarding from within tunnel networks! For example, a unit inside couldn't normally see outside and would fail. */ ATTACK_TUNNEL_NETWORK_GUARD = (_ATTACK_TUNNELNETWORK_GUARD) }; //------------------------------------------------------------------------------------------------- inline Bool isForcedAttack(AbleToAttackType t) { return (((Int)t) & _ATTACK_FORCED) != 0; } //------------------------------------------------------------------------------------------------- inline Bool isContinuedAttack(AbleToAttackType t) { return (((Int)t) & _ATTACK_CONTINUED) != 0; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- typedef UnsignedInt VeterancyLevelFlags; const VeterancyLevelFlags VETERANCY_LEVEL_FLAGS_ALL = 0xffffffff; const VeterancyLevelFlags VETERANCY_LEVEL_FLAGS_NONE = 0x00000000; inline Bool getVeterancyLevelFlag(VeterancyLevelFlags flags, VeterancyLevel dt) { return (flags & (1UL << (dt - 1))) != 0; } inline VeterancyLevelFlags setVeterancyLevelFlag(VeterancyLevelFlags flags, VeterancyLevel dt) { return (flags | (1UL << (dt - 1))); } inline VeterancyLevelFlags clearVeterancyLevelFlag(VeterancyLevelFlags flags, VeterancyLevel dt) { return (flags & ~(1UL << (dt - 1))); } // ---------------------------------------------------------------------------------------------- #define BOGUSPTR(p) ((((unsigned int)(p)) & 1) != 0) // ---------------------------------------------------------------------------------------------- #define MAKE_DLINK_HEAD(OBJCLASS, LISTNAME) \ public: \ inline DLINK_ITERATOR iterate_##LISTNAME() const \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(m_dlinkhead_##LISTNAME.m_head), ("bogus head ptr")); \ return DLINK_ITERATOR(m_dlinkhead_##LISTNAME.m_head, OBJCLASS::dlink_next_##LISTNAME); \ } \ inline OBJCLASS *getFirstItemIn_##LISTNAME() const \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(m_dlinkhead_##LISTNAME.m_head), ("bogus head ptr")); \ return m_dlinkhead_##LISTNAME.m_head; \ } \ inline Bool isInList_##LISTNAME(OBJCLASS* o) const \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(m_dlinkhead_##LISTNAME.m_head), ("bogus head ptr")); \ return o->dlink_isInList_##LISTNAME(&m_dlinkhead_##LISTNAME.m_head); \ } \ inline void prependTo_##LISTNAME(OBJCLASS* o) \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(m_dlinkhead_##LISTNAME.m_head), ("bogus head ptr")); \ if (!isInList_##LISTNAME(o)) \ o->dlink_prependTo_##LISTNAME(&m_dlinkhead_##LISTNAME.m_head); \ } \ inline void removeFrom_##LISTNAME(OBJCLASS* o) \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(m_dlinkhead_##LISTNAME.m_head), ("bogus head ptr")); \ if (isInList_##LISTNAME(o)) \ o->dlink_removeFrom_##LISTNAME(&m_dlinkhead_##LISTNAME.m_head); \ } \ typedef void (*RemoveAllProc_##LISTNAME)(OBJCLASS* o); \ inline void removeAll_##LISTNAME(RemoveAllProc_##LISTNAME p = NULL) \ { \ while (m_dlinkhead_##LISTNAME.m_head) \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(m_dlinkhead_##LISTNAME.m_head), ("bogus head ptr"));\ OBJCLASS *tmp = m_dlinkhead_##LISTNAME.m_head; \ removeFrom_##LISTNAME(tmp); \ if (p) (*p)(tmp); \ } \ } \ inline void reverse_##LISTNAME() \ { \ OBJCLASS* cur = m_dlinkhead_##LISTNAME.m_head; \ OBJCLASS* prev = NULL; \ while (cur) \ { \ OBJCLASS* originalNext = cur->dlink_next_##LISTNAME(); \ cur->dlink_swapLinks_##LISTNAME(); \ prev = cur; \ cur = originalNext; \ } \ m_dlinkhead_##LISTNAME.m_head = prev; \ } \ private: \ /* a trick: init head to zero */ \ struct DLINKHEAD_##LISTNAME \ { \ public: \ OBJCLASS* m_head; \ inline DLINKHEAD_##LISTNAME() : \ m_head(0) { } \ inline ~DLINKHEAD_##LISTNAME() \ { DEBUG_ASSERTCRASH(!m_head,("destroying dlinkhead still in a list " #LISTNAME)); } \ }; \ DLINKHEAD_##LISTNAME m_dlinkhead_##LISTNAME; // ---------------------------------------------------------------------------------------------- #define MAKE_DLINK(OBJCLASS, LISTNAME) \ public: \ OBJCLASS* dlink_prev_##LISTNAME() const { return m_dlink_##LISTNAME.m_prev; } \ OBJCLASS* dlink_next_##LISTNAME() const { return m_dlink_##LISTNAME.m_next; } \ void dlink_swapLinks_##LISTNAME() \ { \ OBJCLASS* originalNext = m_dlink_##LISTNAME.m_next; \ m_dlink_##LISTNAME.m_next = m_dlink_##LISTNAME.m_prev; \ m_dlink_##LISTNAME.m_prev = originalNext; \ } \ Bool dlink_isInList_##LISTNAME(OBJCLASS* const* pListHead) const \ { \ DEBUG_ASSERTCRASH(!BOGUSPTR(*pListHead) && !BOGUSPTR(m_dlink_##LISTNAME.m_next) && !BOGUSPTR(m_dlink_##LISTNAME.m_prev), ("bogus ptrs")); \ return *pListHead == this || m_dlink_##LISTNAME.m_prev || m_dlink_##LISTNAME.m_next; \ } \ void dlink_prependTo_##LISTNAME(OBJCLASS** pListHead) \ { \ DEBUG_ASSERTCRASH(!dlink_isInList_##LISTNAME(pListHead), ("already in list " #LISTNAME)); \ DEBUG_ASSERTCRASH(!BOGUSPTR(*pListHead) && !BOGUSPTR(m_dlink_##LISTNAME.m_next) && !BOGUSPTR(m_dlink_##LISTNAME.m_prev), ("bogus ptrs")); \ m_dlink_##LISTNAME.m_next = *pListHead; \ if (*pListHead) \ (*pListHead)->m_dlink_##LISTNAME.m_prev = this; \ *pListHead = this; \ DEBUG_ASSERTCRASH(!BOGUSPTR(*pListHead) && !BOGUSPTR(m_dlink_##LISTNAME.m_next) && !BOGUSPTR(m_dlink_##LISTNAME.m_prev), ("bogus ptrs")); \ } \ void dlink_removeFrom_##LISTNAME(OBJCLASS** pListHead) \ { \ DEBUG_ASSERTCRASH(dlink_isInList_##LISTNAME(pListHead), ("not in list" #LISTNAME)); \ DEBUG_ASSERTCRASH(!BOGUSPTR(*pListHead) && !BOGUSPTR(m_dlink_##LISTNAME.m_next) && !BOGUSPTR(m_dlink_##LISTNAME.m_prev), ("bogus ptrs")); \ if (m_dlink_##LISTNAME.m_next) \ m_dlink_##LISTNAME.m_next->m_dlink_##LISTNAME.m_prev = m_dlink_##LISTNAME.m_prev; \ if (m_dlink_##LISTNAME.m_prev) \ m_dlink_##LISTNAME.m_prev->m_dlink_##LISTNAME.m_next = m_dlink_##LISTNAME.m_next; \ else \ *pListHead = m_dlink_##LISTNAME.m_next; \ m_dlink_##LISTNAME.m_prev = 0; \ m_dlink_##LISTNAME.m_next = 0; \ DEBUG_ASSERTCRASH(!BOGUSPTR(*pListHead) && !BOGUSPTR(m_dlink_##LISTNAME.m_next) && !BOGUSPTR(m_dlink_##LISTNAME.m_prev), ("bogus ptrs")); \ } \ private: \ /* a trick: init links to zero */ \ struct DLINK_##LISTNAME \ { \ public: \ OBJCLASS* m_prev; \ OBJCLASS* m_next; \ inline DLINK_##LISTNAME() : \ m_prev(0), m_next(0) { } \ inline ~DLINK_##LISTNAME() \ { DEBUG_ASSERTCRASH(!m_prev && !m_next,("destroying dlink still in a list " #LISTNAME)); } \ }; \ DLINK_##LISTNAME m_dlink_##LISTNAME; // ------------------------------------------------------------------------ // this is the weird C++ syntax for "call pointer-to-member-function"... see C++ FAQ LITE for details. #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) // ------------------------------------------------------------------------ template class DLINK_ITERATOR { public: // this is the weird C++ syntax for "pointer-to-member-function" typedef OBJCLASS* (OBJCLASS::*GetNextFunc)() const; private: OBJCLASS* m_cur; GetNextFunc m_getNextFunc; // this is the weird C++ syntax for "pointer-to-member-function" public: DLINK_ITERATOR(OBJCLASS* cur, GetNextFunc getNextFunc) : m_cur(cur), m_getNextFunc(getNextFunc) { } void advance() { if (m_cur) m_cur = callMemberFunction(*m_cur, m_getNextFunc)(); } Bool done() const { return m_cur == NULL; } OBJCLASS* cur() const { return m_cur; } }; // ------------------------------------------------------------------------ enum WhichTurretType { TURRET_INVALID = -1, TURRET_MAIN = 0, TURRET_ALT, MAX_TURRETS }; // ------------------------------------------------------------------------ // this normalizes an angle to the range -PI...PI. extern Real normalizeAngle(Real angle); // ------------------------------------------------------------------------ // this returns the difference between a1 and a2, normalized. inline Real stdAngleDiff(Real a1, Real a2) { return normalizeAngle(a1 - a2); } // ------------------------------------------------------------------------ // NOTE NOTE NOTE: Keep TheRelationShipNames in sync with this enum enum Relationship { ENEMIES = 0, NEUTRAL, ALLIES }; // TheRelationShipNames is defined in Common/GameCommon.cpp extern const char *TheRelationshipNames[]; #endif // _GAMECOMMON_H_