diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index 635563d957e..69d218f593b 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -245,9 +245,9 @@ set(GAMEENGINE_SRC # Include/GameLogic/AITNGuard.h # Include/GameLogic/Armor.h # Include/GameLogic/ArmorSet.h -# Include/GameLogic/CaveSystem.h -# Include/GameLogic/CrateSystem.h -# Include/GameLogic/Damage.h + Include/GameLogic/CaveSystem.h + Include/GameLogic/CrateSystem.h + Include/GameLogic/Damage.h # Include/GameLogic/ExperienceTracker.h # Include/GameLogic/FiringTracker.h # Include/GameLogic/FPUControl.h @@ -486,7 +486,7 @@ set(GAMEENGINE_SRC # Include/GameLogic/PartitionManager.h # Include/GameLogic/PolygonTrigger.h # Include/GameLogic/Powers.h -# Include/GameLogic/RankInfo.h + Include/GameLogic/RankInfo.h # Include/GameLogic/ScriptActions.h # Include/GameLogic/ScriptConditions.h # Include/GameLogic/ScriptEngine.h @@ -1090,12 +1090,12 @@ set(GAMEENGINE_SRC # Source/GameLogic/ScriptEngine/ScriptEngine.cpp # Source/GameLogic/ScriptEngine/Scripts.cpp # Source/GameLogic/ScriptEngine/VictoryConditions.cpp -# Source/GameLogic/System/CaveSystem.cpp -# Source/GameLogic/System/CrateSystem.cpp -# Source/GameLogic/System/Damage.cpp + Source/GameLogic/System/CaveSystem.cpp + Source/GameLogic/System/CrateSystem.cpp + Source/GameLogic/System/Damage.cpp # Source/GameLogic/System/GameLogic.cpp -# Source/GameLogic/System/GameLogicDispatch.cpp -# Source/GameLogic/System/RankInfo.cpp + Source/GameLogic/System/GameLogicDispatch.cpp + Source/GameLogic/System/RankInfo.cpp Source/GameNetwork/Connection.cpp Source/GameNetwork/ConnectionManager.cpp Source/GameNetwork/DisconnectManager.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/CaveSystem.h b/Core/GameEngine/Include/GameLogic/CaveSystem.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/CaveSystem.h rename to Core/GameEngine/Include/GameLogic/CaveSystem.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/CrateSystem.h b/Core/GameEngine/Include/GameLogic/CrateSystem.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/CrateSystem.h rename to Core/GameEngine/Include/GameLogic/CrateSystem.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Damage.h b/Core/GameEngine/Include/GameLogic/Damage.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/Damage.h rename to Core/GameEngine/Include/GameLogic/Damage.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/RankInfo.h b/Core/GameEngine/Include/GameLogic/RankInfo.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/RankInfo.h rename to Core/GameEngine/Include/GameLogic/RankInfo.h diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/CaveSystem.cpp b/Core/GameEngine/Source/GameLogic/System/CaveSystem.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/System/CaveSystem.cpp rename to Core/GameEngine/Source/GameLogic/System/CaveSystem.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/CrateSystem.cpp b/Core/GameEngine/Source/GameLogic/System/CrateSystem.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/System/CrateSystem.cpp rename to Core/GameEngine/Source/GameLogic/System/CrateSystem.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/Damage.cpp b/Core/GameEngine/Source/GameLogic/System/Damage.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/System/Damage.cpp rename to Core/GameEngine/Source/GameLogic/System/Damage.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp similarity index 99% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp rename to Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index ccb3fd83c01..c002ff8f239 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -756,25 +756,28 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData ) //--------------------------------------------------------------------------------------------- case GameMessage::MSG_DO_SPECIAL_POWER_AT_LOCATION: { + const Bool hasAngle = msg->getArgumentCount() >= 6; + Int argumentIndex = 0; + // first argument is the special power ID - UnsignedInt specialPowerID = msg->getArgument( 0 )->integer; + UnsignedInt specialPowerID = msg->getArgument( argumentIndex++ )->integer; // Location argument 2 is destination - Coord3D targetCoord = msg->getArgument(1)->location; + Coord3D targetCoord = msg->getArgument( argumentIndex++ )->location; // Angle argument 3 is the orientation of the special power (if applicable) - Real angle = msg->getArgument(2)->real; + Real angle = hasAngle ? msg->getArgument( argumentIndex++ )->real : INVALID_ANGLE; // Object in way -- if applicable (some specials care, others don't) - ObjectID objectID = msg->getArgument( 3 )->objectID; + ObjectID objectID = msg->getArgument( argumentIndex++ )->objectID; Object *objectInWay = findObjectByID( objectID ); // Command button options -- special power may care about variance options - UnsignedInt options = msg->getArgument( 4 )->integer; + UnsignedInt options = msg->getArgument( argumentIndex++ )->integer; // check for possible specific source, ignoring selection. - ObjectID sourceID = msg->getArgument(5)->objectID; - Object* source = findObjectByID(sourceID); + ObjectID sourceID = msg->getArgument( argumentIndex++ )->objectID; + Object* source = findObjectByID( sourceID ); if (source != nullptr) { #if !RETAIL_COMPATIBLE_CRC @@ -1027,6 +1030,7 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData ) } break; } + //--------------------------------------------------------------------------------------------- case GameMessage::MSG_DEBUG_HURT_OBJECT: { @@ -1055,9 +1059,6 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData ) } #endif - - - #ifdef ALLOW_SURRENDER //--------------------------------------------------------------------------------------------- case GameMessage::MSG_DO_SURRENDER: diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/RankInfo.cpp b/Core/GameEngine/Source/GameLogic/System/RankInfo.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/System/RankInfo.cpp rename to Core/GameEngine/Source/GameLogic/System/RankInfo.cpp diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt index f28e5c0552b..37a1e2a5d7e 100644 --- a/Generals/Code/GameEngine/CMakeLists.txt +++ b/Generals/Code/GameEngine/CMakeLists.txt @@ -228,9 +228,9 @@ set(GAMEENGINE_SRC Include/GameLogic/AITNGuard.h Include/GameLogic/Armor.h Include/GameLogic/ArmorSet.h - Include/GameLogic/CaveSystem.h - Include/GameLogic/CrateSystem.h - Include/GameLogic/Damage.h +# Include/GameLogic/CaveSystem.h +# Include/GameLogic/CrateSystem.h +# Include/GameLogic/Damage.h Include/GameLogic/ExperienceTracker.h Include/GameLogic/FiringTracker.h Include/GameLogic/FPUControl.h @@ -439,7 +439,7 @@ set(GAMEENGINE_SRC Include/GameLogic/PartitionManager.h Include/GameLogic/PolygonTrigger.h Include/GameLogic/Powers.h - Include/GameLogic/RankInfo.h +# Include/GameLogic/RankInfo.h Include/GameLogic/ScriptActions.h Include/GameLogic/ScriptConditions.h Include/GameLogic/ScriptEngine.h @@ -997,12 +997,12 @@ set(GAMEENGINE_SRC Source/GameLogic/ScriptEngine/ScriptEngine.cpp Source/GameLogic/ScriptEngine/Scripts.cpp Source/GameLogic/ScriptEngine/VictoryConditions.cpp - Source/GameLogic/System/CaveSystem.cpp - Source/GameLogic/System/CrateSystem.cpp - Source/GameLogic/System/Damage.cpp +# Source/GameLogic/System/CaveSystem.cpp +# Source/GameLogic/System/CrateSystem.cpp +# Source/GameLogic/System/Damage.cpp Source/GameLogic/System/GameLogic.cpp - Source/GameLogic/System/GameLogicDispatch.cpp - Source/GameLogic/System/RankInfo.cpp +# Source/GameLogic/System/GameLogicDispatch.cpp +# Source/GameLogic/System/RankInfo.cpp # Source/GameNetwork/Connection.cpp # Source/GameNetwork/ConnectionManager.cpp # Source/GameNetwork/DisconnectManager.cpp diff --git a/Generals/Code/GameEngine/Include/Common/MessageStream.h b/Generals/Code/GameEngine/Include/Common/MessageStream.h index 2b04f40be2e..a8ddb0f368d 100644 --- a/Generals/Code/GameEngine/Include/Common/MessageStream.h +++ b/Generals/Code/GameEngine/Include/Common/MessageStream.h @@ -572,6 +572,7 @@ class GameMessage : public MemoryPoolObject MSG_CREATE_FORMATION, ///< Creates a formation. MSG_LOGIC_CRC, ///< CRC from the logic passed around in a network game :) MSG_SET_MINE_CLEARING_DETAIL, ///< CRC from the logic passed around in a network game :) + MSG_ENABLE_RETALIATION_MODE, ///< Turn retaliation mode on or off. MSG_BEGIN_DEBUG_NETWORK_MESSAGES = 1900, ///< network messages that exist only in debug/internal builds. all grouped separately. diff --git a/Generals/Code/GameEngine/Include/Common/Player.h b/Generals/Code/GameEngine/Include/Common/Player.h index 1288b30dc8b..43c00857936 100644 --- a/Generals/Code/GameEngine/Include/Common/Player.h +++ b/Generals/Code/GameEngine/Include/Common/Player.h @@ -651,6 +651,11 @@ class Player : public Snapshot void setCashBounty(Real percentage) { m_cashBountyPercent = percentage; } void doBountyForKill(const Object* killer, const Object* victim); + //Set via logical message. Options menu sets the client value in global data. Player::update() + //detects a change, and posts a message. When the message gets processed, this value gets set. + Bool isLogicalRetaliationModeEnabled() const { return m_logicalRetaliationModeEnabled; } + void setLogicalRetaliationModeEnabled( Bool set ) { m_logicalRetaliationModeEnabled = set; } + private: /** give the science. doesn't check prereqs, nor charge to purchase points... @@ -805,4 +810,5 @@ class Player : public Snapshot Squad *m_currentSelection; ///< This player's currently selected group Bool m_isPlayerDead; + Bool m_logicalRetaliationModeEnabled; }; diff --git a/Generals/Code/GameEngine/Include/GameLogic/CaveSystem.h b/Generals/Code/GameEngine/Include/GameLogic/CaveSystem.h deleted file mode 100644 index 73ec70eb18d..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/CaveSystem.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -** 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: CaveSystem.h ///////////////////////////////////////////////////////////////////////////////// -// Author: Graham Smallwood July 2002 -// Desc: System responsible for keeping track of the connectedness of all cave systems on the map -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -class Object; -class TunnelTracker; // The player owns one such object for his Tunnels, so instead of duplicating -// so much code, this SubSystem will manage all of the Cave systems. - -#include "Common/Snapshot.h" -#include "Common/SubsystemInterface.h" - -/** - System responsible for Crates as code objects - ini, new/delete etc -*/ -class CaveSystem : public SubsystemInterface, - public Snapshot -{ -public: - CaveSystem(); - virtual ~CaveSystem() override; - - virtual void init() override; - virtual void reset() override; - virtual void update() override; - - Bool canSwitchIndexToIndex( Int oldIndex, Int newIndex ); // If either Index has guys in it, no, you can't - void registerNewCave( Int theIndex ); // All Caves are born with a default index, which could be new - void unregisterCave( Int theIndex ); // - TunnelTracker *getTunnelTrackerForCaveIndex( Int theIndex ); - -protected: - - // snapshot methods - virtual void crc( Xfer *xfer ) override { } - virtual void xfer( Xfer *xfer ) override; - virtual void loadPostProcess() override { } - -private: - std::vector m_tunnelTrackerVector;// A vector of pointers where the indexes are known by - // others, so it can have NULLs in it to keep position. I've been advised against a map, so don't be a jerk - // and use spot 20 first. - -}; - -extern CaveSystem *TheCaveSystem; diff --git a/Generals/Code/GameEngine/Include/GameLogic/CrateSystem.h b/Generals/Code/GameEngine/Include/GameLogic/CrateSystem.h deleted file mode 100644 index 90ad674f2c8..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/CrateSystem.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -** 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: CrateSystem.h ///////////////////////////////////////////////////////////////////////////////// -// Author: Graham Smallwood Feb 2002 -// Desc: System responsible for Crates as code objects - ini, new/delete etc -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "Common/INI.h" -#include "Common/Overridable.h" -#include "Common/Override.h" - -enum ScienceType CPP_11(: Int); - -struct crateCreationEntry -{ - AsciiString crateName; - Real crateChance; -}; - -typedef std::list< crateCreationEntry > crateCreationEntryList; -typedef std::list< crateCreationEntry >::iterator crateCreationEntryIterator; -typedef std::list< crateCreationEntry >::const_iterator crateCreationEntryConstIterator; - -/** - A CrateTemplate is a ini defined set of conditions plus a ThingTemplate that is the Object - containing the correct CrateCollide module. -*/ -class CrateTemplate : public Overridable -{ - MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( CrateTemplate, "CrateTemplate" ) - -public: - CrateTemplate(); - // virtual destructor declared by memory pool - - void setName( AsciiString name ) { m_name = name; } - AsciiString getName(){ return m_name; } - - const FieldParse *getFieldParse() const { return TheCrateTemplateFieldParseTable; } - static const FieldParse TheCrateTemplateFieldParseTable[]; ///< the parse table for INI definition - - static void parseCrateCreationEntry( INI* ini, void *instance, void *store, const void* /*userData*/ ); - - AsciiString m_name; ///< name for this CrateTemplate - - Real m_creationChance; ///< Condition for random percentage chance of creating - VeterancyLevel m_veterancyLevel; ///< Condition specifing level of killed unit - KindOfMaskType m_killedByTypeKindof; ///< Must be killed by something with all these bits set - ScienceType m_killerScience; ///< Must be killed by something posessing this science - crateCreationEntryList m_possibleCrates; ///< CreationChance is for this CrateData to succeed, this list controls one-of-n crates created on success - Bool m_isOwnedByMaker; ///< Design needs crates to be owned sometimes. - -private: - -}; - -typedef OVERRIDE CrateTemplateOverride; - - -/** - System responsible for Crates as code objects - ini, new/delete etc -*/ -class CrateSystem : public SubsystemInterface -{ -public: - CrateSystem(); - virtual ~CrateSystem() override; - - virtual void init() override; - virtual void reset() override; - virtual void update() override {} - - const CrateTemplate *findCrateTemplate(AsciiString name) const; - CrateTemplate *friend_findCrateTemplate(AsciiString name); - - CrateTemplate *newCrateTemplate( AsciiString name ); - CrateTemplate *newCrateTemplateOverride( CrateTemplate *crateToOverride ); - - - - static void parseCrateTemplateDefinition(INI* ini); - -private: - std::vector m_crateTemplateVector; - -}; - -extern CrateSystem *TheCrateSystem; diff --git a/Generals/Code/GameEngine/Include/GameLogic/Damage.h b/Generals/Code/GameEngine/Include/GameLogic/Damage.h deleted file mode 100644 index 7f5ed8dbb17..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/Damage.h +++ /dev/null @@ -1,373 +0,0 @@ -/* -** 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: Damage.h ///////////////////////////////////////////////////////////////////////////////// -// Author: Colin Day, November 2001 -// Desc: Damage description -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// -#include "Common/BitFlags.h" -#include "Common/GameType.h" -#include "Common/ObjectStatusTypes.h" // Precompiled header anyway, no detangling possibility -#include "Common/Snapshot.h" - - -// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// -class Object; -class INI; -class ThingTemplate; - -//------------------------------------------------------------------------------------------------- -/** Damage types, keep this in sync with DamageTypeFlags::s_bitNameList[] */ -//------------------------------------------------------------------------------------------------- -enum DamageType CPP_11(: Int) -{ - DAMAGE_EXPLOSION = 0, - DAMAGE_CRUSH = 1, - DAMAGE_ARMOR_PIERCING = 2, - DAMAGE_SMALL_ARMS = 3, - DAMAGE_GATTLING = 4, - DAMAGE_RADIATION = 5, - DAMAGE_FLAME = 6, - DAMAGE_LASER = 7, - DAMAGE_SNIPER = 8, - DAMAGE_POISON = 9, - DAMAGE_HEALING = 10, - DAMAGE_UNRESISTABLE = 11, // this is for scripting to cause 'armorproof' damage - DAMAGE_WATER = 12, - DAMAGE_DEPLOY = 13, // for transports to deploy units and order them to all attack. - DAMAGE_SURRENDER = 14, // if something "dies" to surrender damage, they surrender.... duh! - DAMAGE_HACK = 15, - DAMAGE_KILLPILOT = 16, // special snipe attack that kills the pilot and renders a vehicle unmanned. - DAMAGE_PENALTY = 17, // from game penalty (you won't receive radar warnings BTW) - DAMAGE_FALLING = 18, - DAMAGE_MELEE = 19, // Blades, clubs... - DAMAGE_DISARM = 20, // "special" damage type used for disarming mines, bombs, etc (NOT for "disarming" an opponent!) - DAMAGE_HAZARD_CLEANUP = 21, // special damage type for cleaning up hazards like radiation or bio-poison. - DAMAGE_PARTICLE_BEAM = 22, // Incinerates virtually everything (insanely powerful orbital beam) - DAMAGE_TOPPLING = 23, // damage from getting toppled. - DAMAGE_INFANTRY_MISSILE = 24, - DAMAGE_AURORA_BOMB = 25, - DAMAGE_LAND_MINE = 26, - DAMAGE_JET_MISSILES = 27, - DAMAGE_STEALTHJET_MISSILES = 28, - DAMAGE_MOLOTOV_COCKTAIL = 29, - DAMAGE_COMANCHE_VULCAN = 30, -#if RTS_GENERALS - DAMAGE_FLESHY_SNIPER = 31, // like DAMAGE_SNIPER, but (generally) does no damage to vehicles. -#endif - DAMAGE_SUBDUAL_MISSILE /*= 31*/, ///< Damage that does not kill you, but produces some special effect based on your Body Module. Separate HP from normal damage. - DAMAGE_SUBDUAL_VEHICLE /*= 32*/, - DAMAGE_SUBDUAL_BUILDING /*= 33*/, - DAMAGE_SUBDUAL_UNRESISTABLE /*= 34*/, - DAMAGE_MICROWAVE /*= 35*/, ///< Radiation that only affects infantry - DAMAGE_KILL_GARRISONED /*= 36*/, ///< Kills Passengers up to the number specified in Damage - DAMAGE_STATUS /*= 37*/, ///< Damage that gives a status condition, not that does hitpoint damage - - // Please note: There is a string array DamageTypeFlags::s_bitNameList[] - - DAMAGE_NUM_TYPES -}; - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- - -typedef BitFlags DamageTypeFlags; - -inline Bool getDamageTypeFlag(DamageTypeFlags flags, DamageType dt) -{ - return flags.test(dt); -} - -inline DamageTypeFlags setDamageTypeFlag(DamageTypeFlags flags, DamageType dt) -{ - flags.set(dt, TRUE); - return flags; -} - -inline DamageTypeFlags clearDamageTypeFlag(DamageTypeFlags flags, DamageType dt) -{ - flags.set(dt, FALSE); - return flags; -} - -// Instead of checking against a single damage type, gather the question here so we can have more than one -inline Bool IsSubdualDamage( DamageType type ) -{ - switch( type ) - { - case DAMAGE_SUBDUAL_MISSILE: - case DAMAGE_SUBDUAL_VEHICLE: - case DAMAGE_SUBDUAL_BUILDING: - case DAMAGE_SUBDUAL_UNRESISTABLE: - return TRUE; - } - - return FALSE; -} - -/// Does this type of damage go to internalChangeHealth? -inline Bool IsHealthDamagingDamage( DamageType type ) -{ - // The need for this function brought to you by "Have the guy with no game experience write the weapon code" Foundation. - // Health Damage should be one type of WeaponEffect. Thinking "Weapons can only do damage" is why AoE is boring. - switch( type ) - { - case DAMAGE_STATUS: - case DAMAGE_SUBDUAL_MISSILE: - case DAMAGE_SUBDUAL_VEHICLE: - case DAMAGE_SUBDUAL_BUILDING: - case DAMAGE_SUBDUAL_UNRESISTABLE: - case DAMAGE_KILLPILOT: - case DAMAGE_KILL_GARRISONED: - return FALSE; - } - - return TRUE; -} - -inline void SET_ALL_DAMAGE_TYPE_BITS(DamageTypeFlags& m) -{ - m.clear(); - m.flip(); -} - -extern DamageTypeFlags DAMAGE_TYPE_FLAGS_NONE; -extern DamageTypeFlags DAMAGE_TYPE_FLAGS_ALL; - - -//------------------------------------------------------------------------------------------------- -/** Death types, keep this in sync with TheDeathNames[] */ -//------------------------------------------------------------------------------------------------- -enum DeathType CPP_11(: Int) -{ - // note that these DELIBERATELY have (slightly) different names from the damage names, - // since there isn't necessarily a one-to-one correspondence. e.g., DEATH_BURNED - // can come from DAMAGE_FLAME but also from DAMAGE_PARTICLE_BEAM. - DEATH_NORMAL = 0, - DEATH_NONE = 1, ///< this is a "special case" that can't normally cause death - DEATH_CRUSHED = 2, - DEATH_BURNED = 3, - DEATH_EXPLODED = 4, - DEATH_POISONED = 5, - DEATH_TOPPLED = 6, - DEATH_FLOODED = 7, - DEATH_SUICIDED = 8, - DEATH_LASERED = 9, - DEATH_DETONATED = 10, /**< this is the "death" that occurs when a missile/warhead/etc detonates normally, - as opposed to being shot down, etc */ - DEATH_SPLATTED = 11, /**< the death that results from DAMAGE_FALLING */ - DEATH_POISONED_BETA = 12, - - // these are the "extra" types for yet-to-be-defined stuff. Don't bother renaming or adding - // or removing these; they are reserved for modders :-) - DEATH_EXTRA_2 = 13, - DEATH_EXTRA_3 = 14, - DEATH_EXTRA_4 = 15, - DEATH_EXTRA_5 = 16, - DEATH_EXTRA_6 = 17, - DEATH_EXTRA_7 = 18, - DEATH_EXTRA_8 = 19, - DEATH_POISONED_GAMMA = 20, - - DEATH_NUM_TYPES -}; - -#ifdef DEFINE_DEATH_NAMES -static const char *const TheDeathNames[] = -{ - "NORMAL", - "NONE", - "CRUSHED", - "BURNED", - "EXPLODED", - "POISONED", - "TOPPLED", - "FLOODED", - "SUICIDED", - "LASERED", - "DETONATED", - "SPLATTED", - "POISONED_BETA", - "EXTRA_2", - "EXTRA_3", - "EXTRA_4", - "EXTRA_5", - "EXTRA_6", - "EXTRA_7", - "EXTRA_8", - "POISONED_GAMMA", - - nullptr -}; -static_assert(ARRAY_SIZE(TheDeathNames) == DEATH_NUM_TYPES + 1, "Incorrect array size"); -#endif // end DEFINE_DEATH_NAMES - - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- - -typedef UnsignedInt DeathTypeFlags; - -const DeathTypeFlags DEATH_TYPE_FLAGS_ALL = 0xffffffff; -const DeathTypeFlags DEATH_TYPE_FLAGS_NONE = 0x00000000; - -inline Bool getDeathTypeFlag(DeathTypeFlags flags, DeathType dt) -{ - return (flags & (1UL << (dt - 1))) != 0; -} - -inline DeathTypeFlags setDeathTypeFlag(DeathTypeFlags flags, DeathType dt) -{ - return (flags | (1UL << (dt - 1))); -} - -inline DeathTypeFlags clearDeathTypeFlag(DeathTypeFlags flags, DeathType dt) -{ - return (flags & ~(1UL << (dt - 1))); -} - -//------------------------------------------------------------------------------------------------- -/** Damage info inputs */ -//------------------------------------------------------------------------------------------------- -class DamageInfoInput : public Snapshot -{ - -public: - - DamageInfoInput() - { - m_sourceID = INVALID_ID; - m_sourceTemplate = nullptr; - m_sourcePlayerMask = 0; - m_damageType = DAMAGE_EXPLOSION; - m_damageStatusType = OBJECT_STATUS_NONE; - m_damageFXOverride = DAMAGE_UNRESISTABLE; - m_deathType = DEATH_NORMAL; - m_amount = 0; - m_kill = FALSE; - - m_shockWaveVector.zero(); - m_shockWaveAmount = 0.0f; - m_shockWaveRadius = 0.0f; - m_shockWaveTaperOff = 0.0f; - } - - ObjectID m_sourceID; ///< source of the damage - const ThingTemplate *m_sourceTemplate; ///< source of the damage (the template). - PlayerMaskType m_sourcePlayerMask; ///< Player mask of m_sourceID. - DamageType m_damageType; ///< type of damage - ObjectStatusTypes m_damageStatusType; ///< If status damage, what type - DamageType m_damageFXOverride; ///< If not marked as the default of Unresistable, the damage type to use in doDamageFX instead of the real damage type - DeathType m_deathType; ///< if this kills us, death type to be used - Real m_amount; ///< # value of how much damage to inflict - Bool m_kill; ///< will always cause object to die regardless of damage. - - // These are used for damage causing shockwave, forcing units affected to be pushed around - Coord3D m_shockWaveVector; ///< This represents the incoming damage vector - Real m_shockWaveAmount; ///< This represents the amount of shockwave created by the damage. 0 = no shockwave, 1.0 = shockwave equal to damage. - Real m_shockWaveRadius; ///< This represents the effect radius of the shockwave. - Real m_shockWaveTaperOff; ///< This represents the taper off effect of the shockwave at the tip of the radius. 0.0 means shockwave is 0% at the radius edge. - - -protected: - - // snapshot methods - virtual void crc( Xfer *xfer ) override { } - virtual void xfer( Xfer *xfer ) override; - virtual void loadPostProcess() override { } - -}; - -const Real HUGE_DAMAGE_AMOUNT = 999999.0f; - -//------------------------------------------------------------------------------------------------- -/** Damage into outputs */ -//------------------------------------------------------------------------------------------------- -class DamageInfoOutput : public Snapshot -{ - -public: - - DamageInfoOutput() - { - m_actualDamageDealt = 0; - m_actualDamageClipped = 0; - m_noEffect = false; - } - - /** - m_actualDamageDealt is the damage we tried to apply to object (after multipliers and such). - m_actualDamageClipped is the value of m_actualDamageDealt, but clipped to the max health remaining of the obj. - example: - a mammoth tank fires a round at a small tank, attempting 100 damage. - the small tank has a damage multiplier of 50%, meaning that only 50 damage is applied. - furthermore, the small tank has only 30 health remaining. - so: m_actualDamageDealt = 50, m_actualDamageClipped = 30. - - this distinction is useful, since visual fx really wants to do the fx for "50 damage", - even though it was more than necessary to kill this object; game logic, on the other hand, - may want to know the "clipped" damage for ai purposes. - */ - Real m_actualDamageDealt; - Real m_actualDamageClipped; ///< (see comment for m_actualDamageDealt) - Bool m_noEffect; ///< if true, no damage was done at all (generally due to being InactiveBody) - -protected: - - // snapshot methods - virtual void crc( Xfer *xfer ) override { } - virtual void xfer( Xfer *xfer ) override; - virtual void loadPostProcess() override { } - -}; - -//------------------------------------------------------------------------------------------------- -/** DamageInfo is a descriptor of damage we're trying to inflict. The structure - * is divided up into two parts, inputs and outputs. - * - * INPUTS: You must provide valid values for these fields in order for damage - * calculation to correctly take place - * OUTPUT: Upon returning from damage issuing functions, the output fields - * will be filled with the results of the damage occurrence - */ -//------------------------------------------------------------------------------------------------- -class DamageInfo : public Snapshot -{ - -public: - - DamageInfoInput in; ///< inputs for the damage info - DamageInfoOutput out; ///< results for the damage occurrence - -protected: - - virtual void crc( Xfer *xfer ) override { } - virtual void xfer( Xfer *xfer ) override; - virtual void loadPostProcess() override { } - -}; diff --git a/Generals/Code/GameEngine/Include/GameLogic/RankInfo.h b/Generals/Code/GameEngine/Include/GameLogic/RankInfo.h deleted file mode 100644 index f05b28aec5f..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/RankInfo.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -** 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: RankInfo.h //////////////////////////////////////////////////////////////////////////////// -// Author: Steven Johnson, Sep 2002 -// Desc: RankInfo descriptions -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// -#include "Common/Science.h" -#include "Common/UnicodeString.h" -#include "Common/STLTypedefs.h" - -class Player; - -//------------------------------------------------------------------------------------------------- -class RankInfo : public Overridable -{ - MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( RankInfo, "RankInfo" ); -public: - UnicodeString m_rankName; - Int m_skillPointsNeeded; - Int m_sciencePurchasePointsGranted; - ScienceVec m_sciencesGranted; -}; -//EMPTY_DTOR(RankInfo) - -//------------------------------------------------------------------------------------------------- -class RankInfoStore : public SubsystemInterface -{ -public: - virtual ~RankInfoStore() override; - -public: - virtual void init() override; - virtual void reset() override; - virtual void update() override { } - - Int getRankLevelCount() const; - - // note that level is 1...n, NOT 0...n-1 - const RankInfo* getRankInfo(Int level) const; - - static void friend_parseRankDefinition(INI* ini); - -private: - - typedef std::vector RankInfoVec; - RankInfoVec m_rankInfos; -}; - -extern RankInfoStore* TheRankInfoStore; diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index b4b5d63db75..7bc48908c7a 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -487,6 +487,9 @@ void Player::init(const PlayerTemplate* pt) deleteInstance(tof); } + //Always off at the beginning of a game! Only GameLogic::update has + //the power to turn it on. Don't want to cause desyncs! + m_logicalRetaliationModeEnabled = FALSE; } //============================================================================= diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/CaveSystem.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/CaveSystem.cpp deleted file mode 100644 index 058a98cb938..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/System/CaveSystem.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* -** 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: CaveSystem.cpp ///////////////////////////////////////////////////////////////////////////////// -// Author: Graham Smallwood July 2002 -// Desc: System responsible for keeping track of all cave systems on the map -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/GameState.h" -#include "Common/TunnelTracker.h" -#include "Common/Xfer.h" -#include "GameLogic/CaveSystem.h" - -CaveSystem *TheCaveSystem = nullptr; - -CaveSystem::CaveSystem() -{ -} - -CaveSystem::~CaveSystem() -{ -} - -void CaveSystem::init() -{ -} - -void CaveSystem::reset() -{ - for( std::vector::iterator iter = m_tunnelTrackerVector.begin(); iter != m_tunnelTrackerVector.end(); iter++ ) - { - TunnelTracker *currentTracker = *iter; // could be null, since we don't slide back to fill deleted entries so offsets don't shift - deleteInstance(currentTracker); - } - m_tunnelTrackerVector.clear(); -} - -void CaveSystem::update() -{ -} - -Bool CaveSystem::canSwitchIndexToIndex( Int oldIndex, Int newIndex ) -{ - // When I grant permission, you need to do it. ie call Unregister and then re-register with the new number - TunnelTracker *oldTracker = nullptr; - TunnelTracker *newTracker = nullptr; - if( m_tunnelTrackerVector.size() > oldIndex ) - { - oldTracker = m_tunnelTrackerVector[oldIndex]; - if( oldTracker && oldTracker->getContainCount() > 0 ) - return FALSE;// You can't switch a connection if one of the two is non empty - } - if( m_tunnelTrackerVector.size() > newIndex ) - { - newTracker = m_tunnelTrackerVector[newIndex]; - if( newTracker && newTracker->getContainCount() > 0 ) - return FALSE;// You can't switch a connection if one of the two is non empty - } - - // Both are either empty or non-existent, so go ahead. - // (Remember non-exist is only a valid case because you are going to do the switch now.) - - return TRUE; -} - -void CaveSystem::registerNewCave( Int theIndex ) -{ - Bool needToCreate = FALSE; - if( theIndex >= m_tunnelTrackerVector.size() ) - { - // You are new and off the edge, so I will fill NULLs up to you and then make a newTracker at that spot - while( theIndex >= m_tunnelTrackerVector.size() ) - m_tunnelTrackerVector.push_back( nullptr ); - - needToCreate = TRUE; - } - else - { - // else you either exist or have existed, so I will either let things be or re-create that slot - if( m_tunnelTrackerVector[theIndex] == nullptr ) - needToCreate = TRUE; - } - - if( needToCreate )// if true, we new theIndex is the index of a nullptr to be filled - m_tunnelTrackerVector[theIndex] = newInstance(TunnelTracker); -} - -void CaveSystem::unregisterCave( Int theIndex ) -{ - // Doesn't need to do a thing. ContainModule logic knows how to say goodbye, and a TunnelTracker - // knows how to exist while having no entry points. - theIndex; -} - -TunnelTracker *CaveSystem::getTunnelTrackerForCaveIndex( Int theIndex ) -{ - TunnelTracker *theTracker = nullptr; - if( theIndex < m_tunnelTrackerVector.size() ) - { - theTracker = m_tunnelTrackerVector[theIndex]; - } - - DEBUG_ASSERTCRASH( theTracker != nullptr, ("No one should be interested in a sub-cave that doesn't exist.") ); - - return theTracker; -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer Method - * Version Info - * 1: Initial version */ -// ------------------------------------------------------------------------------------------------ -void CaveSystem::xfer( Xfer *xfer ) -{ - - // version - XferVersion currentVersion = 1; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // tunnel tracker size and data - UnsignedShort count = m_tunnelTrackerVector.size(); - xfer->xferUnsignedShort( &count ); - TunnelTracker *tracker; - if( xfer->getXferMode() == XFER_SAVE ) - { - std::vector< TunnelTracker* >::iterator it; - - for( it = m_tunnelTrackerVector.begin(); it != m_tunnelTrackerVector.end(); ++it ) - { - - // xfer data - tracker = *it; - xfer->xferSnapshot( tracker ); - - } - - } - else - { - - // the list must be empty now - if( m_tunnelTrackerVector.empty() == FALSE ) - { - - DEBUG_CRASH(( "CaveSystem::xfer - m_tunnelTrackerVector should be empty but is not" )); - throw SC_INVALID_DATA; - - } - - // read each item - for( UnsignedShort i = 0; i < count; ++i ) - { - - // allocate new tracker - tracker = newInstance( TunnelTracker ); - - // read data - xfer->xferSnapshot( tracker ); - - // put in vector - m_tunnelTrackerVector.push_back( tracker ); - - } - - } - -} - - diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/CrateSystem.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/CrateSystem.cpp deleted file mode 100644 index fe31dcdc64a..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/System/CrateSystem.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* -** 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: CrateSystem.cpp ///////////////////////////////////////////////////////////////////////////////// -// Author: Graham Smallwood Feb 2002 -// Desc: System responsible for Crates as code objects - ini, new/delete etc -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[] - -#include "GameLogic/CrateSystem.h" -#include "Common/BitFlagsIO.h" - -CrateSystem *TheCrateSystem = nullptr; - -CrateSystem::CrateSystem() -{ - m_crateTemplateVector.clear(); -} - -CrateSystem::~CrateSystem() -{ - Int count = m_crateTemplateVector.size(); - for( Int templateIndex = 0; templateIndex < count; templateIndex ++ ) - { - CrateTemplate *currentTemplate = m_crateTemplateVector[templateIndex]; - deleteInstance(currentTemplate); - } - m_crateTemplateVector.clear(); -} - -void CrateSystem::init() -{ - reset(); -} - -void CrateSystem::reset() -{ - // clean up overrides - std::vector::iterator it; - for( it = m_crateTemplateVector.begin(); it != m_crateTemplateVector.end(); ) - { - CrateTemplate *currentTemplate = *it; - if( currentTemplate ) - { - Overridable *tempCrateTemplate = currentTemplate->deleteOverrides(); - if (!tempCrateTemplate) - { - // base dude was an override - kill it from the vector - it = m_crateTemplateVector.erase(it); - } - else - { - ++it; - } - } - else - { - it = m_crateTemplateVector.erase(it); - } - } -} - -void CrateSystem::parseCrateTemplateDefinition(INI* ini) -{ - AsciiString name; - - // read the crateTemplate name - const char* c = ini->getNextToken(); - name.set(c); - - CrateTemplate *crateTemplate = TheCrateSystem->friend_findCrateTemplate(name); - if (crateTemplate == nullptr) { - crateTemplate = TheCrateSystem->newCrateTemplate(name); - - if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) { - crateTemplate->markAsOverride(); - } - } else if( ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES ) { - DEBUG_CRASH(( "[LINE: %d in '%s'] Duplicate crate %s found!", ini->getLineNum(), ini->getFilename().str(), name.str() )); - } else { - crateTemplate = TheCrateSystem->newCrateTemplateOverride(crateTemplate); - } - - // parse the ini weapon definition - ini->initFromINI(crateTemplate, crateTemplate->getFieldParse()); -} - -CrateTemplate *CrateSystem::newCrateTemplate( AsciiString name ) -{ - // sanity - if(name.isEmpty()) - return nullptr; - - // allocate a new weapon - CrateTemplate *ct = newInstance(CrateTemplate); - - // if the default template is present, get it and copy over any data to the new template - const CrateTemplate *defaultCT = findCrateTemplate("DefaultCrate"); - if(defaultCT) - { - *ct = *defaultCT; - } - - ct->setName( name ); - m_crateTemplateVector.push_back(ct); - - return ct; -} - -CrateTemplate *CrateSystem::newCrateTemplateOverride( CrateTemplate *crateToOverride ) -{ - if (!crateToOverride) { - return nullptr; - } - - CrateTemplate *newOverride = newInstance(CrateTemplate); - *newOverride = *crateToOverride; - - newOverride->markAsOverride(); - - crateToOverride->setNextOverride(newOverride); - return newOverride; -} - -const CrateTemplate *CrateSystem::findCrateTemplate(AsciiString name) const -{ - // search weapon list for name - for (size_t i = 0; i < m_crateTemplateVector.size(); i++) - if(m_crateTemplateVector[i]->getName() == name) { - CrateTemplateOverride overridable(m_crateTemplateVector[i]); - return overridable; - } - - - return nullptr; -} - -CrateTemplate *CrateSystem::friend_findCrateTemplate(AsciiString name) -{ - // search weapon list for name - for (size_t i = 0; i < m_crateTemplateVector.size(); i++) - if(m_crateTemplateVector[i]->getName() == name) { - CrateTemplateOverride overridable(m_crateTemplateVector[i]); - return const_cast((const CrateTemplate *)overridable); - } - return nullptr; -} - - - -//-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- -const FieldParse CrateTemplate::TheCrateTemplateFieldParseTable[] = -{ - { "CreationChance", INI::parseReal, nullptr, offsetof( CrateTemplate, m_creationChance ) }, - { "VeterancyLevel", INI::parseIndexList, TheVeterancyNames, offsetof( CrateTemplate, m_veterancyLevel ) }, - { "KilledByType", KindOfMaskType::parseFromINI, nullptr, offsetof( CrateTemplate, m_killedByTypeKindof) }, - { "CrateObject", CrateTemplate::parseCrateCreationEntry, nullptr, 0 }, - { "KillerScience", INI::parseScience, nullptr, offsetof( CrateTemplate, m_killerScience) }, - { "OwnedByMaker", INI::parseBool, nullptr, offsetof( CrateTemplate, m_isOwnedByMaker) }, - { nullptr, nullptr, nullptr, 0 }, -}; - -CrateTemplate::CrateTemplate() -{ - m_creationChance = 0; - CLEAR_KINDOFMASK(m_killedByTypeKindof); - m_veterancyLevel = LEVEL_INVALID; - m_killerScience = SCIENCE_INVALID; - m_possibleCrates.clear(); - m_isOwnedByMaker = FALSE; -} - -CrateTemplate::~CrateTemplate() -{ - m_possibleCrates.clear(); -} - -void CrateTemplate::parseCrateCreationEntry( INI* ini, void *instance, void *, const void* ) -{ - CrateTemplate *self = (CrateTemplate *)instance; - - const char *token = ini->getNextToken(); - AsciiString crateName = token; - - token = ini->getNextToken(); - Real crateValue; - if (sscanf( token, "%f", &crateValue ) != 1) - throw INI_INVALID_DATA; - - crateCreationEntry newEntry; - newEntry.crateName = crateName; - newEntry.crateChance = crateValue; - - self->m_possibleCrates.push_back( newEntry ); -} - diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/Damage.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/Damage.cpp deleted file mode 100644 index c77f9f8bb6b..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/System/Damage.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* -** 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: Damage.cpp /////////////////////////////////////////////////////////////////////////////// -// Author: Colin Day, September 2002 -// Desc: Basic structures for the damage process -/////////////////////////////////////////////////////////////////////////////////////////////////// - -// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// -#include "PreRTS.h" -#include "Common/Xfer.h" -#include "GameLogic/Damage.h" -#include "Common/BitFlagsIO.h" -#include "Common/ThingFactory.h" -#include "Common/ThingTemplate.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -template<> -const char* const DamageTypeFlags::s_bitNameList[] = -{ - "EXPLOSION", - "CRUSH", - "ARMOR_PIERCING", - "SMALL_ARMS", - "GATTLING", - "RADIATION", - "FLAME", - "LASER", - "SNIPER", - "POISON", - "HEALING", - "UNRESISTABLE", - "WATER", - "DEPLOY", - "SURRENDER", - "HACK", - "KILL_PILOT", - "PENALTY", - "FALLING", - "MELEE", - "DISARM", - "HAZARD_CLEANUP", - "PARTICLE_BEAM", - "TOPPLING", - "INFANTRY_MISSILE", - "AURORA_BOMB", - "LAND_MINE", - "JET_MISSILES", - "STEALTHJET_MISSILES", - "MOLOTOV_COCKTAIL", - "COMANCHE_VULCAN", -#if RTS_GENERALS - "FLESHY_SNIPER", -#endif - "SUBDUAL_MISSILE", - "SUBDUAL_VEHICLE", - "SUBDUAL_BUILDING", - "SUBDUAL_UNRESISTABLE", - "MICROWAVE", - "KILL_GARRISONED", - "STATUS", - - nullptr -}; -static_assert(ARRAY_SIZE(DamageTypeFlags::s_bitNameList) == DamageTypeFlags::NumBits + 1, "Incorrect array size"); - -DamageTypeFlags DAMAGE_TYPE_FLAGS_NONE; // inits to all zeroes -DamageTypeFlags DAMAGE_TYPE_FLAGS_ALL(DamageTypeFlags::kInitSetAll); - -// ------------------------------------------------------------------------------------------------ -/** Xfer method - * Version Info: - * 1: Initial version */ -// ------------------------------------------------------------------------------------------------ -void DamageInfo::xfer( Xfer *xfer ) -{ - - // version - XferVersion currentVersion = 1; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // xfer input - xfer->xferSnapshot( &in ); - - // xfer output - xfer->xferSnapshot( &out ); - -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer method - * Version Info: - * 1: Initial version - * 2: Damage FX override (Added for Zero Hour) - * 3: Shock wave and damage status type (Added for Zero Hour) -*/ -// ------------------------------------------------------------------------------------------------ -void DamageInfoInput::xfer( Xfer *xfer ) -{ - - // version -#if RTS_GENERALS && RETAIL_COMPATIBLE_XFER_SAVE - XferVersion currentVersion = 1; -#else - XferVersion currentVersion = 3; -#endif - - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // source id - xfer->xferObjectID( &m_sourceID ); - - // source player mask - xfer->xferUser( &m_sourcePlayerMask, sizeof( PlayerMaskType ) ); - - // damage type - xfer->xferUser( &m_damageType, sizeof( DamageType ) ); - - // damage FX Override - if( version >= 2 ) - xfer->xferUser( &m_damageFXOverride, sizeof( DamageType ) ); - - // death type - xfer->xferUser( &m_deathType, sizeof( DeathType ) ); - - // amount - xfer->xferReal( &m_amount ); - - // kill no matter what (old versions default to FALSE). - if( currentVersion >= 2 ) - { - xfer->xferBool( &m_kill ); - } - - if( version >= 3 ) - { - xfer->xferUser( &m_damageStatusType, sizeof(ObjectStatusTypes) );//It's an enum - - xfer->xferCoord3D(&m_shockWaveVector); - xfer->xferReal( &m_shockWaveAmount ); - xfer->xferReal( &m_shockWaveRadius ); - xfer->xferReal( &m_shockWaveTaperOff ); - - AsciiString thingString = m_sourceTemplate ? m_sourceTemplate->getName() : AsciiString::TheEmptyString; - xfer->xferAsciiString( &thingString ); - if( xfer->getXferMode() == XFER_LOAD ) - { - m_sourceTemplate = TheThingFactory->findTemplate( thingString ); - } - } - -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer method - * Version Info: - * 1: Initial version */ -// ------------------------------------------------------------------------------------------------ -void DamageInfoOutput::xfer( Xfer *xfer ) -{ - - // version - XferVersion currentVersion = 1; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // actual damage - xfer->xferReal( &m_actualDamageDealt ); - - // damage clipped - xfer->xferReal( &m_actualDamageClipped ); - - // no effect - xfer->xferBool( &m_noEffect ); - -} - diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp deleted file mode 100644 index 78d051ba60b..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ /dev/null @@ -1,2123 +0,0 @@ -/* -** 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: GameLogicDispatch.cpp //////////////////////////////////////////////////////////////////// -// Author: Mike Booth, Colin Day -// Description: Message logic to drive the game play -/////////////////////////////////////////////////////////////////////////////////////////////////// - -// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/CRCDebug.h" -#include "Common/FramePacer.h" -#include "Common/GameAudio.h" -#include "Common/GameEngine.h" -#include "Common/GlobalData.h" -#include "Common/NameKeyGenerator.h" -#include "Common/ThingFactory.h" -#include "Common/Player.h" -#include "Common/PlayerList.h" -#include "Common/PlayerTemplate.h" -#include "Common/MessageStream.h" -#include "Common/MultiplayerSettings.h" -#include "Common/Recorder.h" -#include "Common/BuildAssistant.h" -#include "Common/SpecialPower.h" -#include "Common/ThingTemplate.h" -#include "Common/Upgrade.h" -#include "Common/StatsCollector.h" -#include "Common/Radar.h" - -#include "GameLogic/AIPathfind.h" -#include "GameLogic/GameLogic.h" -#include "GameLogic/Locomotor.h" -#include "GameLogic/Object.h" -#include "GameLogic/ObjectCreationList.h" -#include "GameLogic/ObjectIter.h" -//#include "GameLogic/PartitionManager.h" -#include "GameLogic/AI.h" -#include "GameLogic/Module/AIUpdate.h" -#include "GameLogic/Module/BodyModule.h" -#include "GameLogic/Module/OpenContain.h" -#include "GameLogic/Module/ProductionUpdate.h" -#include "GameLogic/Module/SpecialPowerModule.h" -#include "GameLogic/ScriptActions.h" -#include "GameLogic/ScriptEngine.h" -#include "GameLogic/VictoryConditions.h" -#include "GameLogic/Weapon.h" - -#include "GameClient/CommandXlat.h" -#include "GameClient/ControlBar.h" -#include "GameClient/Drawable.h" -#include "GameClient/Eva.h" -#include "GameClient/GameText.h" -#include "GameClient/GameWindowManager.h" -#include "GameClient/GUICallbacks.h" -#include "GameClient/InGameUI.h" -#include "GameClient/KeyDefs.h" -#include "GameClient/Mouse.h" -#include "GameClient/ParticleSys.h" -#include "GameClient/Shell.h" -#include "GameClient/Module/BeaconClientUpdate.h" -#include "GameClient/LookAtXlat.h" - -#include "GameNetwork/NetworkInterface.h" - - - - -#define MAX_PATH_SUBJECTS 64 -static Bool theBuildPlan = false; -static Object *thePlanSubject[ MAX_PATH_SUBJECTS ]; -static int thePlanSubjectCount = 0; -//static WindowLayout *background = nullptr; - -// ------------------------------------------------------------------------------------------------ -/** Issue the movement command to the object */ -// ------------------------------------------------------------------------------------------------ -static void doMoveTo( Object *obj, const Coord3D *pos ) -{ - AIUpdateInterface *ai = obj->getAIUpdateInterface(); - DEBUG_ASSERTCRASH(ai, ("Attempted doMoveTo() on an Object with no AI")); - if (ai) - { - if (theBuildPlan) - { - int i; - - // if this object isn't in the buildPlan set, add it - for( i=0; iqueueWaypoint( pos ); - } - else - { - ai->clearWaypointQueue(); - obj->leaveGroup(); - obj->releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks. - ai->aiMoveToPosition( pos, CMD_FROM_PLAYER ); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ -static void doSetRallyPoint( Object *obj, const Coord3D& pos ) -{ - Bool isLocalPlayer = obj->isLocallyControlled(); - - // - // we must be able to find a path from the object to the point they have chosen, cause setting - // a rally point at a invalid location would suck. To be super nice, we have to make sure - // that every type of object that can be created from the thing setting the rally point - // can actually find a path from the thing to the point - // - - // to see the never-finished code to check all locomotor sets, see past revs of GUICommandTranslator.cpp -MDC - - // - // for now, just use the basic human locomotor ... and enable the above code when Steven - // tells me how to get the locomotor sets based on a thing template (CBD) - // - NameKeyType key = NAMEKEY( "BasicHumanLocomotor" ); - LocomotorSet locomotorSet; - locomotorSet.addLocomotor( TheLocomotorStore->findLocomotorTemplate( key ) ); - if( TheAI->pathfinder()->clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), &pos ) == FALSE ) - { - - // user feedback - if( isLocalPlayer ) - { - - // display error message to user - TheInGameUI->message( TheGameText->fetch( "GUI:RallyPointNoPath" ) ); - - // play the no can do sound - static AudioEventRTS rallyNotSet("UnableToSetRallyPoint"); - rallyNotSet.setPosition(&pos); - rallyNotSet.setPlayerIndex(obj->getControllingPlayer()->getPlayerIndex()); - TheAudio->addAudioEvent(&rallyNotSet); - - } - - return; - - } - - // feedback to the player - if( isLocalPlayer ) - { - - // print a message to the user - UnicodeString info; - info.format( TheGameText->fetch( "GUI:RallyPointSet" ), - obj->getTemplate()->getDisplayName().str() ); - TheInGameUI->message( info ); - - // play a sound for setting the rally point - static AudioEventRTS rallyPointSet("RallyPointSet"); - rallyPointSet.setPosition(&pos); - rallyPointSet.setPlayerIndex(obj->getControllingPlayer()->getPlayerIndex()); - TheAudio->addAudioEvent(&rallyPointSet); - - // mark the UI as dirty so that we re-evaluate the selection and show the rally point - Drawable *draw = obj->getDrawable(); - if( draw && draw->isSelected() ) - TheControlBar->markUIDirty(); - - } - - // if this object has a ProductionExitUpdate interface, we are setting a rally point - ExitInterface *exitInterface = obj->getObjectExitInterface(); - if( exitInterface ) - { - // set the rally point - exitInterface->setRallyPoint( &pos ); - - } - -} - -static Object * getSingleObjectFromSelection(const AIGroup *currentlySelectedGroup) -{ - if( currentlySelectedGroup && !currentlySelectedGroup->isEmpty() ) - { - const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs(); - DEBUG_ASSERTCRASH(selectedObjects.size() == 1, ("Trying to get single object from multiple selection!")); - VecObjectID::const_iterator it = selectedObjects.begin(); - return TheGameLogic->findObjectByID(*it); - } - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ -void GameLogic::closeWindows() -{ - HideDiplomacy(); - ResetDiplomacy(); - HideInGameChat(); - ResetInGameChat(); - TheControlBar->hidePurchaseScience(); - TheControlBar->hideSpecialPowerShortcut(); - HideQuitMenu(); - - // hide the options menu - NameKeyType buttonID = TheNameKeyGenerator->nameToKey( "OptionsMenu.wnd:ButtonBack" ); - GameWindow *button = TheWindowManager->winGetWindowFromId( nullptr, buttonID ); - GameWindow *window = TheWindowManager->winGetWindowFromId( nullptr, TheNameKeyGenerator->nameToKey("OptionsMenu.wnd:OptionsMenuParent") ); - if(window) - TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, - (WindowMsgData)button, buttonID ); -} - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ -void GameLogic::clearGameData( Bool showScoreScreen ) -{ - if( !isInGame() ) - { - DEBUG_CRASH(("We tried to clear the game data when we weren't in a game")); - return; - } - - setClearingGameData( TRUE ); - -// m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); -// DEBUG_ASSERTCRASH(m_background,("We Couldn't Load Menus/BlankWindow.wnd")); -// m_background->hide(FALSE); -// m_background->bringForward(); - // reset the game engine to accept data for a new game - if(TheStatsCollector) - TheStatsCollector->writeFileEnd(); - - TheScriptActions->closeWindows(FALSE); // Close victory or defeat windows. - - Bool shellGame = FALSE; - if ((!isInShellGame() || !isInGame()) && showScoreScreen && !TheGlobalData->m_headless) - { - shellGame = TRUE; - TheShell->push("Menus/ScoreScreen.wnd"); - TheShell->showShell(FALSE); // by passing in false, we don't want to run the Init on the shell screen we just pushed on - - void FixupScoreScreenMovieWindow(); - FixupScoreScreenMovieWindow(); - } - - TheGameEngine->reset(); - setGameMode(GAME_NONE); -// m_background->bringForward(); -// if(shellGame) - - - if (TheGlobalData->m_initialFile.isEmpty() == FALSE || m_quitToDesktopAfterMatch) - { - TheGameEngine->setQuitting(TRUE); - m_quitToDesktopAfterMatch = FALSE; - } - - HideControlBar(); - closeWindows(); - - TheMouse->setVisibility(TRUE); - - if(m_background) - { - m_background->destroyWindows(); - deleteInstance(m_background); - m_background = nullptr; - } - - setClearingGameData( FALSE ); - -} - -// ------------------------------------------------------------------------------------------------ -/** Prepare for a new game */ -// ------------------------------------------------------------------------------------------------ -void GameLogic::prepareNewGame( GameMode gameMode, GameDifficulty diff, Int rankPoints ) -{ - //Kris: Commented this out, but leaving it around incase it bites us later. I cleaned up the - // nomenclature. Look for setLoadingMap() and setLoadingSave() - //setGameLoading(TRUE); - - TheScriptEngine->setGlobalDifficulty(diff); - - if(!m_background) - { - m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); - DEBUG_ASSERTCRASH(m_background,("We Couldn't Load Menus/BlankWindow.wnd")); - m_background->hide(FALSE); - m_background->bringForward(); - } - m_background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); - setGameMode( gameMode ); - if (!TheGlobalData->m_pendingFile.isEmpty()) - { - TheWritableGlobalData->m_mapName = TheGlobalData->m_pendingFile; - TheWritableGlobalData->m_pendingFile.clear(); - } - - m_rankPointsToAddAtGameStart = rankPoints; - DEBUG_LOG(("GameLogic::prepareNewGame() - m_rankPointsToAddAtGameStart = %d", m_rankPointsToAddAtGameStart)); - - // If we're about to start a game, hide the shell. - if(!isInShellGame()) - TheShell->hideShell(); - - m_startNewGame = FALSE; - -} - -//------------------------------------------------------------------------------------------------- -/** This message handles dispatches object command messages to the - * appropriate objects. - * @todo Rename this to "CommandProcessor", or similar. */ -//------------------------------------------------------------------------------------------------- -void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData ) -{ -#ifdef RTS_DEBUG - DEBUG_ASSERTCRASH(msg != nullptr && msg != (GameMessage*)0xdeadbeef, ("bad msg")); -#endif - - Player *msgPlayer = ThePlayerList->getNthPlayer( msg->getPlayerIndex() ); - if (msgPlayer == nullptr) - { - DEBUG_CRASH(("logicMessageDispatcher: Processing message from unknown player (player index '%d')", msg->getPlayerIndex())); - return; - } - - AIGroupPtr currentlySelectedGroup = nullptr; - - if (isInGame()) - { - if (msg->getType() >= GameMessage::MSG_BEGIN_NETWORK_MESSAGES && msg->getType() <= GameMessage::MSG_END_NETWORK_MESSAGES) - { - if (msg->getType() != GameMessage::MSG_LOGIC_CRC && msg->getType() != GameMessage::MSG_SET_REPLAY_CAMERA) - { - currentlySelectedGroup = TheAI->createGroup(); // can't do this outside a game - it'll cause sync errors galore. - CRCGEN_LOG(( "Creating AIGroup %d in GameLogic::logicMessageDispatcher()", currentlySelectedGroup?currentlySelectedGroup->getID():0 )); -#if RETAIL_COMPATIBLE_AIGROUP - msgPlayer->getCurrentSelectionAsAIGroup(currentlySelectedGroup); -#else - msgPlayer->getCurrentSelectionAsAIGroup(currentlySelectedGroup.Peek()); -#endif - - // We can't issue commands to groups that contain units that don't belong to the issuing player, so pretend like - // there's nothing selected. Also, if currentlySelectedGroup is empty, go ahead and delete it, so that we can skip - // any processing on it. - if (currentlySelectedGroup->isEmpty()) - { -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(currentlySelectedGroup); -#endif - currentlySelectedGroup = nullptr; - } - - // If there are any units that the player doesn't own, then remove them from the "currentlySelectedGroup" - if (currentlySelectedGroup) - if (currentlySelectedGroup->removeAnyObjectsNotOwnedByPlayer(msgPlayer)) - currentlySelectedGroup = nullptr; - - if(TheStatsCollector) - TheStatsCollector->collectMsgStats(msg); - } - } - } - -#ifdef DEBUG_LOGGING - AsciiString commandName; - - commandName = msg->getCommandAsString(); - if (msg->getType() < GameMessage::MSG_BEGIN_NETWORK_MESSAGES || msg->getType() > GameMessage::MSG_END_NETWORK_MESSAGES) - { - commandName.concat(" (NON-LOGIC-MESSAGE!!!)"); - } - else if (msg->getType() == GameMessage::MSG_BEGIN_NETWORK_MESSAGES) - { - commandName = " (CRC message!)"; - } -#if 0 - if (commandName.isNotEmpty() /*&& msg->getType() != GameMessage::MSG_FRAME_TICK*/) - { - DEBUG_LOG(("Frame %d: GameLogic::logicMessageDispatcher() saw a %s from player %d (%ls)", getFrame(), commandName.str(), - msgPlayer->getPlayerIndex(), msgPlayer->getPlayerDisplayName().str())); - } -#endif -#endif // DEBUG_LOGGING - - // process the message - GameMessage::Type msgType = msg->getType(); - switch( msgType ) - { - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_NEW_GAME: - { -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @fix stephanmeesters 11/03/2026 - // Make sure we're ready to start a new game. This prevents an issue where an infinite disconnect screen - // can be force-triggered in an online match by using cheats. - if ( isInGame() || isClearingGameData() || isLoadingMap() ) - { - DEBUG_CRASH( ("Called MSG_NEW_GAME while game is not ready (inGame=%d, clearingData=%d, loadingMap=%d)", - isInGame(), isClearingGameData(), isLoadingMap()) ); - break; - } -#endif - - //DEBUG_ASSERTCRASH(msg->getArgumentCount() == 1 || msg->getArgumentCount() == 2, ("%d arguments to MSG_NEW_GAME", msg->getArgumentCount())); - GameMode gameMode = (GameMode)msg->getArgument( 0 )->integer; - Int rankPoints = 0; - GameDifficulty diff = DIFFICULTY_NORMAL; - if ( msg->getArgumentCount() >= 2 ) - diff = (GameDifficulty)msg->getArgument( 1 )->integer; - if ( msg->getArgumentCount() >= 3 ) - rankPoints = msg->getArgument( 2 )->integer; - - if ( msg->getArgumentCount() >= 4 ) - { - Int maxFPS = msg->getArgument( 3 )->integer; - if (maxFPS < 1 || maxFPS > 1000) - maxFPS = TheGlobalData->m_framesPerSecondLimit; - DEBUG_LOG(("Setting max FPS limit to %d FPS", maxFPS)); - TheFramePacer->setFramesPerSecondLimit(maxFPS); - TheWritableGlobalData->m_useFpsLimit = true; - } - - // prepare for new game - prepareNewGame( gameMode, diff, rankPoints ); - - // start new game - startNewGame( FALSE ); - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_CLEAR_GAME_DATA: - { - -#if defined(RTS_DEBUG) - if (TheDisplay && TheGlobalData->m_dumpAssetUsage) - TheDisplay->dumpAssetUsage(TheGlobalData->m_mapName.str()); -#endif - - if (currentlySelectedGroup) - { -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(currentlySelectedGroup); -#else - currentlySelectedGroup->removeAll(); -#endif - } - currentlySelectedGroup = nullptr; - clearGameData(); - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_META_BEGIN_PATH_BUILD: - { - DEBUG_LOG(("META: begin path build")); - DEBUG_ASSERTCRASH(!theBuildPlan, ("mismatched theBuildPlan")); - - if (theBuildPlan == false) - { - theBuildPlan = true; - thePlanSubjectCount = 0; - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_META_END_PATH_BUILD: - { - DEBUG_LOG(("META: end path build")); - DEBUG_ASSERTCRASH(theBuildPlan, ("mismatched theBuildPlan")); - - // tell everyone who participated in the plan to move - for( int i=0; igetAIUpdateInterface(); - if (ai) - ai->executeWaypointQueue(); - } - - theBuildPlan = false; - thePlanSubjectCount = 0; - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_SET_RALLY_POINT: - { - Object *obj = findObjectByID( msg->getArgument( 0 )->objectID ); - Coord3D dest = msg->getArgument( 1 )->location; - - if (obj) - { -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @fix stephanmeesters 11/03/2026 Validate the owner of the source object - if ( obj->getControllingPlayer() != msgPlayer ) - { - DEBUG_CRASH( ("MSG_SET_RALLY_POINT: Player '%ls' attempted to set the rally point of object '%s' owned by player '%ls'.", - msgPlayer->getPlayerDisplayName().str(), - obj->getTemplate()->getName().str(), - obj->getControllingPlayer()->getPlayerDisplayName().str()) ); - break; - } -#endif - - doSetRallyPoint( obj, dest ); - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_WEAPON: - { - WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer; - Int maxShotsToFire = msg->getArgument( 1 )->integer; - - // lock it just till the weapon is empty or the attack is "done" - if( currentlySelectedGroup && currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_TEMPORARILY )) - { - currentlySelectedGroup->groupAttackPosition( nullptr, maxShotsToFire, CMD_FROM_PLAYER ); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_COMBATDROP_AT_OBJECT: - { - Object *targetObject = findObjectByID( msg->getArgument( 0 )->objectID ); - - // issue command for either single object or for selected group - if( currentlySelectedGroup && targetObject ) - currentlySelectedGroup->groupCombatDrop( targetObject, - *targetObject->getPosition(), - CMD_FROM_PLAYER ); - -/* - if( sourceObject && targetObject ) - { - AIUpdateInterface* sourceAI = sourceObject->getAIUpdateInterface(); - if (sourceAI) - { - sourceAI->aiCombatDrop( targetObject, *targetObject->getPosition(), CMD_FROM_PLAYER ); - } - } -*/ - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_COMBATDROP_AT_LOCATION: - { - Coord3D targetLoc = msg->getArgument( 0 )->location; - - if( currentlySelectedGroup ) - currentlySelectedGroup->groupCombatDrop( nullptr, targetLoc, CMD_FROM_PLAYER ); - -/* - if( sourceObject ) - { - AIUpdateInterface* sourceAI = sourceObject->getAIUpdateInterface(); - if (sourceAI) - { - sourceAI->aiCombatDrop( nullptr, targetLoc, CMD_FROM_PLAYER ); - } - } -*/ - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_WEAPON_AT_OBJECT: - { - // Lock the weapon choice to the right weapon, then give an attack command - - WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer; - Object *targetObject = findObjectByID( msg->getArgument( 1 )->objectID ); - Int maxShotsToFire = msg->getArgument( 2 )->integer; - - // sanity - if( targetObject == nullptr ) - break; - - - // issue command for either single object or for selected group - if( currentlySelectedGroup ) - { - // lock it just till the weapon is empty or the attack is "done" - if (currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_TEMPORARILY )) - currentlySelectedGroup->groupAttackObject( targetObject, maxShotsToFire, CMD_FROM_PLAYER ); - } - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_SWITCH_WEAPONS: - { - // use the selected group - WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer; - // lock until un-switched, or switched to something else. - if( currentlySelectedGroup ) - currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_PERMANENTLY ); - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_SET_MINE_CLEARING_DETAIL: - { - if( currentlySelectedGroup ) - { - currentlySelectedGroup->setMineClearingDetail(true); - } - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_WEAPON_AT_LOCATION: - { - WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer; - Coord3D targetLoc = msg->getArgument( 1 )->location; - Int maxShotsToFire = msg->getArgument( 2 )->integer; - - // issue command for either single object or for selected group - if( currentlySelectedGroup ) - { - // lock it just till the weapon is empty or the attack is "done" - if (currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_TEMPORARILY )) - currentlySelectedGroup->groupAttackPosition( &targetLoc, maxShotsToFire, CMD_FROM_PLAYER ); - - - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_SPECIAL_POWER: - { - - // first argument is the special power ID - UnsignedInt specialPowerID = msg->getArgument( 0 )->integer; - - // Command button options -- special power may care about variance options - UnsignedInt options = msg->getArgument( 1 )->integer; - - // check for possible specific source, ignoring selection. - ObjectID sourceID = msg->getArgument(2)->objectID; - Object* source = findObjectByID(sourceID); - if (source != nullptr) - { -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @fix stephanmeesters 01/03/2026 Validate the origin of the source object - if ( source->getControllingPlayer() != msgPlayer ) - { - DEBUG_CRASH( ("MSG_DO_SPECIAL_POWER: Player '%ls' attempted to control the object '%s' owned by player '%ls'.", - msgPlayer->getPlayerDisplayName().str(), - source->getTemplate()->getName().str(), - source->getControllingPlayer()->getPlayerDisplayName().str()) ); - break; - } -#endif - - AIGroupPtr theGroup = TheAI->createGroup(); - theGroup->add(source); - theGroup->groupDoSpecialPower( specialPowerID, options ); -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(theGroup); -#else - theGroup->removeAll(); -#endif - } - else - { - //Use the selected group! - if( currentlySelectedGroup ) - { - currentlySelectedGroup->groupDoSpecialPower( specialPowerID, options ); - } - } - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_SPECIAL_POWER_AT_LOCATION: - { - // first argument is the special power ID - UnsignedInt specialPowerID = msg->getArgument( 0 )->integer; - - // Location argument 2 is destination - Coord3D targetCoord = msg->getArgument(1)->location; - - // Object in way -- if applicable (some specials care, others don't) - ObjectID objectID = msg->getArgument( 2 )->objectID; - Object *objectInWay = findObjectByID( objectID ); - - // Command button options -- special power may care about variance options - UnsignedInt options = msg->getArgument( 3 )->integer; - - // check for possible specific source, ignoring selection. - ObjectID sourceID = msg->getArgument(4)->objectID; - Object* source = findObjectByID(sourceID); - if (source != nullptr) - { -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @fix stephanmeesters 01/03/2026 Validate the origin of the source object - if ( source->getControllingPlayer() != msgPlayer ) - { - DEBUG_CRASH( ("MSG_DO_SPECIAL_POWER_AT_LOCATION: Player '%ls' attempted to control the object '%s' owned by player '%ls'.", - msgPlayer->getPlayerDisplayName().str(), - source->getTemplate()->getName().str(), - source->getControllingPlayer()->getPlayerDisplayName().str()) ); - break; - } -#endif - - AIGroupPtr theGroup = TheAI->createGroup(); - theGroup->add(source); - theGroup->groupDoSpecialPowerAtLocation( specialPowerID, &targetCoord, INVALID_ANGLE, objectInWay, options ); -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(theGroup); -#else - theGroup->removeAll(); -#endif - } - else - { - //Use the selected group! - if( currentlySelectedGroup ) - { - currentlySelectedGroup->groupDoSpecialPowerAtLocation( specialPowerID, &targetCoord, INVALID_ANGLE, objectInWay, options ); - } - } - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_SPECIAL_POWER_AT_OBJECT: - { - // first argument is the special power ID - UnsignedInt specialPowerID = msg->getArgument( 0 )->integer; - - // argument 2 is target object - ObjectID targetID = msg->getArgument(1)->objectID; - Object *target = findObjectByID( targetID ); - if( !target ) - { - break; - } - - // Command button options -- special power may care about variance options - UnsignedInt options = msg->getArgument( 2 )->integer; - - // check for possible specific source, ignoring selection. - ObjectID sourceID = msg->getArgument(3)->objectID; - Object* source = findObjectByID(sourceID); - if (source != nullptr) - { -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @fix stephanmeesters 01/03/2026 Validate the origin of the source object - if ( source->getControllingPlayer() != msgPlayer ) - { - DEBUG_CRASH( ("MSG_DO_SPECIAL_POWER_AT_OBJECT: Player '%ls' attempted to control the object '%s' owned by player '%ls'.", - msgPlayer->getPlayerDisplayName().str(), - source->getTemplate()->getName().str(), - source->getControllingPlayer()->getPlayerDisplayName().str()) ); - break; - } -#endif - - AIGroupPtr theGroup = TheAI->createGroup(); - theGroup->add(source); - theGroup->groupDoSpecialPowerAtObject( specialPowerID, target, options ); -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(theGroup); -#else - theGroup->removeAll(); -#endif - } - else - { - if( currentlySelectedGroup ) - { - currentlySelectedGroup->groupDoSpecialPowerAtObject( specialPowerID, target, options ); - } - } - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_ATTACKMOVETO: - { - Coord3D dest = msg->getArgument( 0 )->location; - - if (currentlySelectedGroup) - { - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupAttackMoveToPosition( &dest, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER ); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_FORCEMOVETO: - { - Coord3D dest = msg->getArgument( 0 )->location; - - if (currentlySelectedGroup) - { - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupMoveToPosition( &dest, false, CMD_FROM_PLAYER ); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - // MSG_DO_SALVAGE is intentionally set up to mimic the moveto. - case GameMessage::MSG_DO_SALVAGE: - case GameMessage::MSG_DO_MOVETO: - { - Coord3D dest = msg->getArgument( 0 )->location; - - if( currentlySelectedGroup ) - { - //DEBUG_LOG(("GameLogicDispatch - got a MSG_DO_MOVETO command")); - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupMoveToPosition( &dest, false, CMD_FROM_PLAYER ); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_ADD_WAYPOINT: - { - Coord3D dest = msg->getArgument( 0 )->location; - - if( currentlySelectedGroup ) - { - //DEBUG_LOG(("GameLogicDispatch - got a MSG_DO_MOVETO command")); - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupMoveToPosition( &dest, true, CMD_FROM_PLAYER ); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_GUARD_POSITION: - { - Coord3D loc = msg->getArgument( 0 )->location; - GuardMode gm = (GuardMode)msg->getArgument( 1 )->integer; - if (currentlySelectedGroup) - { - currentlySelectedGroup->groupGuardPosition(&loc, gm, CMD_FROM_PLAYER); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_GUARD_OBJECT: - { - Object* obj = findObjectByID( msg->getArgument( 0 )->objectID ); - if (!obj) - break; - - GuardMode gm = (GuardMode)msg->getArgument( 1 )->integer; - if (currentlySelectedGroup) - { - currentlySelectedGroup->groupGuardObject(obj, gm, CMD_FROM_PLAYER); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_STOP: - { - if (currentlySelectedGroup) - { - currentlySelectedGroup->groupIdle(CMD_FROM_PLAYER); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_SCATTER: - { - if (currentlySelectedGroup) - { - currentlySelectedGroup->groupScatter(CMD_FROM_PLAYER); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_CREATE_FORMATION: - { - if (currentlySelectedGroup) - { - currentlySelectedGroup->groupCreateFormation(CMD_FROM_PLAYER); - } - - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_CLEAR_INGAME_POPUP_MESSAGE: - { - - if( TheInGameUI ) - { - TheInGameUI->clearPopupMessageData(); - } - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_CHEER: - { - //All selected units play cheer animation. - if( currentlySelectedGroup ) - { - currentlySelectedGroup->groupCheer( CMD_FROM_PLAYER ); - } - break; - } - -#if defined(RTS_DEBUG) - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DEBUG_KILL_SELECTION: - { - //All selected units die - if( currentlySelectedGroup ) - { - const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs(); - for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it) - { - Object *obj = findObjectByID(*it); - if (obj) - { - obj->kill(); - } - } - } - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DEBUG_HURT_OBJECT: - { - Object* objToHurt = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID ); - if (objToHurt) - { - DamageInfo damageInfo; - damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE; - damageInfo.in.m_deathType = DEATH_NORMAL; - damageInfo.in.m_sourceID = INVALID_ID; - damageInfo.in.m_amount = objToHurt->getBodyModule()->getMaxHealth() / 10.0f; - objToHurt->attemptDamage( &damageInfo ); - } - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DEBUG_KILL_OBJECT: - { - Object* objToHurt = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID ); - if (objToHurt) - { - objToHurt->kill(); - } - break; - } -#endif - -#ifdef ALLOW_SURRENDER - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_SURRENDER: - { - //All selected units surrender - if( currentlySelectedGroup ) - { - Object* objWeSurrenderedTo = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID ); - Bool surrender = msg->getArgument( 1 )->boolean; - currentlySelectedGroup->groupSurrender( objWeSurrenderedTo, surrender, CMD_FROM_PLAYER ); - } - break; - } -#endif - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_ENTER: - { - Object *enter = findObjectByID( msg->getArgument( 1 )->objectID ); - - // sanity - if( enter == nullptr ) - break; - - if( currentlySelectedGroup ) - { - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupEnter( enter, CMD_FROM_PLAYER ); - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_EXIT: - { - Object *objectWantingToExit = findObjectByID( msg->getArgument( 0 )->objectID ); -#if RETAIL_COMPATIBLE_AIGROUP - Object *objectContainingExiter = getSingleObjectFromSelection(currentlySelectedGroup); -#else - Object *objectContainingExiter = getSingleObjectFromSelection(currentlySelectedGroup.Peek()); -#endif - - // sanity - if( objectWantingToExit == nullptr ) - break; - - if( objectContainingExiter == nullptr ) - break; - - // sanity, the player must actually control this object - if( objectWantingToExit->getControllingPlayer() != msgPlayer ) - break; - - objectWantingToExit->releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks. - - // exit whatever objectWantingToExit is INSIDE of - AIUpdateInterface *ai = objectWantingToExit->getAIUpdateInterface(); - if( ai ) - ai->aiExit( objectContainingExiter, CMD_FROM_PLAYER ); - // Just like Enter, Exit needs to know the thing to exit. This can no longer be assumed because of the Tunnel system. - // If you do not specify the thing to Exit, it will Exit the thing it thinks it is in. For a tunnel network, - // that will be the specific Tunnel it entered. (Scripts can talk directly to the guy to say Get Out Regardless) - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_EVACUATE: - { - // issue command for either single object or for selected group -// AIGroup *group = TheAI->findGroup( *selectedGroupID ); - if( currentlySelectedGroup ) - { - //Coord3D pos; - //Bool hasArgs = FALSE; - //hasArgs = (msg->getArgumentCount() > 0); - - //if (hasArgs) - // pos = msg->getArgument(0)->location; - - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - - // evacuate message is for the selected group - //if (hasArgs) - // currentlySelectedGroup->groupMoveToAndEvacuate( &pos, CMD_FROM_PLAYER ); - //else - currentlySelectedGroup->groupEvacuate( CMD_FROM_PLAYER ); - -// no, this is bad, don't do here, do when POSTING message -// pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), GameMessage::MSG_EVACUATE ); - - } - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_EXECUTE_RAILED_TRANSPORT: - { - - // issue command to currently selected objects - if( currentlySelectedGroup ) - currentlySelectedGroup->groupExecuteRailedTransport( CMD_FROM_PLAYER ); - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_INTERNET_HACK: - { -// ObjectID sourceID = msg->getArgument( 0 )->objectID; - if( currentlySelectedGroup ) - { - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupHackInternet( CMD_FROM_PLAYER ); - } - break; - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_GET_REPAIRED: - { - Object *repairDepot = findObjectByID( msg->getArgument( 0 )->objectID ); - - // sanity - if( repairDepot == nullptr ) - break; - - // tell the currently selected group to go get repaired - if( currentlySelectedGroup ) - currentlySelectedGroup->groupGetRepaired( repairDepot, CMD_FROM_PLAYER ); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_DOCK: - { - Object *dockBuilding = findObjectByID( msg->getArgument( 0 )->objectID ); - - // sanity - if( dockBuilding == nullptr ) - break; - - // tell the currently selected group to go get repaired - if( currentlySelectedGroup ) - currentlySelectedGroup->groupDock( dockBuilding, CMD_FROM_PLAYER ); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_GET_HEALED: - { - Object *healDest = findObjectByID( msg->getArgument( 0 )->objectID ); - - // sanity - if( healDest == nullptr ) - break; - - // tell the currently selected group to enter the building for healing - if( currentlySelectedGroup ) - currentlySelectedGroup->groupGetHealed( healDest, CMD_FROM_PLAYER ); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_REPAIR: - { - Object *repairTarget = findObjectByID( msg->getArgument( 0 )->objectID ); - - // sanity - if( repairTarget == nullptr ) - break; - - // - // tell the currently selected group of objects to go repair the target object, note - // that only one of them will actually go ahead and do the repair - // - if( currentlySelectedGroup ) - currentlySelectedGroup->groupRepair( repairTarget, CMD_FROM_PLAYER ); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_RESUME_CONSTRUCTION: - { - Object *constructTarget = findObjectByID( msg->getArgument( 0 )->objectID ); - - // sanity - if( constructTarget == nullptr ) - break; - - // - // tell the currently selected group of objects to resume construction on - // the target object, note that only one of them will go off and resume construction - // on the target - // - if( currentlySelectedGroup ) - currentlySelectedGroup->groupResumeConstruction( constructTarget, CMD_FROM_PLAYER ); - -// no, this is bad, don't do here, do when POSTING message -// pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), msg->getType() ); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION: - { - const Coord3D *loc = &msg->getArgument( 0 )->location; - SpecialPowerType spType = (SpecialPowerType)msg->getArgument( 1 )->integer; - - ObjectID sourceID = msg->getArgument(2)->objectID; - Object* source = findObjectByID(sourceID); - if (source != nullptr) - { -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @fix stephanmeesters 01/03/2026 Validate the origin of the source object - if ( source->getControllingPlayer() != msgPlayer ) - { - DEBUG_CRASH( ("MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION: Player '%ls' attempted to control the object '%s' owned by player '%ls'.", - msgPlayer->getPlayerDisplayName().str(), - source->getTemplate()->getName().str(), - source->getControllingPlayer()->getPlayerDisplayName().str()) ); - break; - } -#endif - - AIGroupPtr theGroup = TheAI->createGroup(); - theGroup->add(source); - theGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER ); -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(theGroup); -#else - theGroup->removeAll(); -#endif - } - else - { - if( currentlySelectedGroup ) - { - currentlySelectedGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER ); - } - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_ATTACK_OBJECT: - { - Object *enemy = findObjectByID( msg->getArgument( 0 )->objectID ); - - // Check enemy, as it is possible that he died this frame. - if (enemy) - { - if (currentlySelectedGroup) - { - - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupAttackObject( enemy, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER ); - - } - - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_FORCE_ATTACK_OBJECT: - { - Object *enemy = findObjectByID( msg->getArgument( 0 )->objectID ); - - // Check enemy, as it is possible that he died this frame. - if (enemy) - { - if (currentlySelectedGroup) - { - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks. - currentlySelectedGroup->groupForceAttackObject( enemy, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER ); - } - - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DO_FORCE_ATTACK_GROUND: - { - const Coord3D *pos = &msg->getArgument( 0 )->location; - - if (currentlySelectedGroup) - { - - ///////////////////////////////////////////////////////////////////// - //Lorenzen sez: unclear, yet how to solve this for all cases - //Kris: This code was added to allow the toxin tractor to force attack - // while contaminating. When this change was made, it was causing - // rangers and scud launchers to reset to primary weapon mode whenever - // force attacking while not idle. I fixed this by enforcing the - // temporary and permanent modes that are already set when attempting - // the new lock. In this case, the temp lock attempt will fail whenever - // a permanent lock is in effect, thus fixing the ranger and scud and - // allowing the tox tractor to work as well. - Bool forceAttackRequiresPrimaryWeapon = !currentlySelectedGroup->isIdle(); - if ( forceAttackRequiresPrimaryWeapon ) - { - currentlySelectedGroup->setWeaponLockForGroup( PRIMARY_WEAPON, LOCKED_TEMPORARILY ); - currentlySelectedGroup->groupAttackPosition( pos, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER ); - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); - } - else - /////////////////////////////////////////////////////////////////// - { - currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); - currentlySelectedGroup->groupAttackPosition( pos, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER ); - } - - - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_QUEUE_UPGRADE: - { - const UpgradeTemplate *upgradeT = TheUpgradeCenter->findUpgradeByKey( (NameKeyType)(msg->getArgument( 1 )->integer) ); - if (!upgradeT) // sanity - break; - - if (currentlySelectedGroup) - currentlySelectedGroup->queueUpgrade( upgradeT ); - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_CANCEL_UPGRADE: - { -#if RETAIL_COMPATIBLE_AIGROUP - Object *producer = getSingleObjectFromSelection(currentlySelectedGroup); -#else - Object *producer = getSingleObjectFromSelection(currentlySelectedGroup.Peek()); -#endif - const UpgradeTemplate *upgradeT = TheUpgradeCenter->findUpgradeByKey( (NameKeyType)(msg->getArgument( 0 )->integer) ); - - // sanity - if( producer == nullptr || upgradeT == nullptr ) - break; - - // the player must actually control the producer object - if( producer->getControllingPlayer() != msgPlayer ) - break; - - // producer must have a production update - ProductionUpdateInterface *pu = producer->getProductionUpdateInterface(); - if( pu == nullptr ) - break; - - // cancel the upgrade - pu->cancelUpgrade( upgradeT ); - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_QUEUE_UNIT_CREATE: - { -#if RETAIL_COMPATIBLE_AIGROUP - Object *producer = getSingleObjectFromSelection(currentlySelectedGroup); -#else - Object *producer = getSingleObjectFromSelection(currentlySelectedGroup.Peek()); -#endif - const ThingTemplate *whatToCreate; - ProductionID productionID; - - // get data from the message - whatToCreate = TheThingFactory->findByTemplateID( msg->getArgument( 0 )->integer ); - productionID = (ProductionID)msg->getArgument( 1 )->integer; - - // sanity - if ( producer == nullptr || whatToCreate == nullptr ) - break; - - // get the production interface for the producer - ProductionUpdateInterface *pu = producer->getProductionUpdateInterface(); - if( pu == nullptr ) - { - - DEBUG_CRASH( ("MSG_QUEUE_UNIT_CREATE: Producer '%s' doesn't have a unit production interface", - producer->getTemplate()->getName().str()) ); - break; - - } - - // queue the build - pu->queueCreateUnit( whatToCreate, productionID ); - - break; - - } - - //------------------------------------------------------------------------------------------------- - case GameMessage::MSG_CANCEL_UNIT_CREATE: - { -#if RETAIL_COMPATIBLE_AIGROUP - Object *producer = getSingleObjectFromSelection(currentlySelectedGroup); -#else - Object *producer = getSingleObjectFromSelection(currentlySelectedGroup.Peek()); -#endif - ProductionID productionID = (ProductionID)msg->getArgument( 0 )->integer; - - // sanity - if( producer == nullptr ) - break; - - // sanity, the player must control the producer - if( producer->getControllingPlayer() != msgPlayer ) - break; - - // get the unit production interface - ProductionUpdateInterface *pu = producer->getProductionUpdateInterface(); - if( pu == nullptr ) - break; - - // cancel the production - pu->cancelUnitCreate( productionID ); - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DOZER_CONSTRUCT: - case GameMessage::MSG_DOZER_CONSTRUCT_LINE: - { - const ThingTemplate *place; - Coord3D loc; - Real angle; - - // get player, what to place, and location -#if RETAIL_COMPATIBLE_AIGROUP - Object *constructorObject = getSingleObjectFromSelection(currentlySelectedGroup); -#else - Object *constructorObject = getSingleObjectFromSelection(currentlySelectedGroup.Peek()); -#endif - place = TheThingFactory->findByTemplateID( msg->getArgument( 0 )->integer ); - loc = msg->getArgument( 1 )->location; - angle = msg->getArgument( 2 )->real; - - if( place == nullptr || constructorObject == nullptr ) - break; //These are not crashes, as the object may have died before this message came in - - if( msg->getType() == GameMessage::MSG_DOZER_CONSTRUCT ) - { - - TheBuildAssistant->buildObjectNow( constructorObject, place, &loc, angle, - constructorObject->getControllingPlayer() ); - - } - else - { - Coord3D locEnd; - - // get the end of the line location in the world - locEnd = msg->getArgument( 3 )->location; - - // place the line of structures, the end location being present will make it happen - TheBuildAssistant->buildObjectLineNow( constructorObject, place, &loc, &locEnd, angle, - constructorObject->getControllingPlayer() ); - - } - - // place the sound for putting a building down - - static AudioEventRTS placeBuilding("PlaceBuilding"); - placeBuilding.setObjectID(constructorObject->getID()); - TheAudio->addAudioEvent( &placeBuilding ); - - -// no, this is bad, don't do here, do when POSTING message -// pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), msg->getType() ); - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DOZER_CANCEL_CONSTRUCT: - { - - // get the building to cancel construction on -#if RETAIL_COMPATIBLE_AIGROUP - Object *building = getSingleObjectFromSelection(currentlySelectedGroup); -#else - Object *building = getSingleObjectFromSelection(currentlySelectedGroup.Peek()); -#endif - if( building == nullptr ) - break; - - // the player sending this message must actually control this building - if( building->getControllingPlayer() != msgPlayer ) - break; - - // Check to make sure it is actually under construction - if( !building->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ) - break; - - // OK, refund the money to the player, unless it is a rebuilding Hole. - if( !building->testStatus(OBJECT_STATUS_RECONSTRUCTING)) - { - Money *money = msgPlayer->getMoney(); - UnsignedInt amount = building->getTemplate()->calcCostToBuild( msgPlayer ); - money->deposit( amount, TRUE, FALSE ); - } - - // - // Destroy the building ... killing the - // building will automatically cause the dozer also cancel its own building - // behavior and it will go on its merry way doing other assigned tasks - // - building->kill(); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_SELL: - { - - // use the selected group - if( currentlySelectedGroup ) - currentlySelectedGroup->groupSell( CMD_FROM_PLAYER ); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_TOGGLE_OVERCHARGE: - { - - // use the selected group - if( currentlySelectedGroup ) - currentlySelectedGroup->groupToggleOvercharge( CMD_FROM_PLAYER ); - - break; - - } - -#ifdef ALLOW_SURRENDER - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_PICK_UP_PRISONER: - { - Object *prisoner = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID ); - - if( prisoner ) - { - - // use selected group - if( currentlySelectedGroup ) - currentlySelectedGroup->groupPickUpPrisoner( prisoner, CMD_FROM_PLAYER ); - - } - - break; - - } -#endif - -#ifdef ALLOW_SURRENDER - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_RETURN_TO_PRISON: - { - - // use selected group - if( currentlySelectedGroup ) - currentlySelectedGroup->groupReturnToPrison( nullptr, CMD_FROM_PLAYER ); - - break; - - } -#endif - - //--------------------------------------------------------------------------------------------- - // No sound does exactly the same logical processing as the usual message. Just double them up. - case GameMessage::MSG_CREATE_SELECTED_GROUP_NO_SOUND: - case GameMessage::MSG_CREATE_SELECTED_GROUP: - { - Bool createNewGroup = msg->getArgument( 0 )->boolean; - Bool firstObject = TRUE; - - for (Int i = 1; i < msg->getArgumentCount(); ++i) { - Object *obj = findObjectByID( msg->getArgument( i )->objectID ); - if (!obj) { - continue; - } - - selectObject(obj, createNewGroup && firstObject, msgPlayer->getPlayerMask()); - firstObject = FALSE; - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_REMOVE_FROM_SELECTED_GROUP: - { - for (Int i = 0; i < msg->getArgumentCount(); ++i) { - ObjectID objID = msg->getArgument(i)->objectID; - Object *objToRemove = findObjectByID(objID); - if (!objToRemove) { - continue; - } - - deselectObject(objToRemove, msgPlayer->getPlayerMask()); - } - - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_DESTROY_SELECTED_GROUP: - { - msgPlayer->setCurrentlySelectedAIGroup(nullptr); - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_SELECTED_GROUP_COMMAND: - { - - break; - - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_PLACE_BEACON: - { - if (msgPlayer->getPlayerTemplate() == nullptr) - break; - Coord3D pos = msg->getArgument( 0 )->location; - Region3D r; - TheTerrainLogic->getExtent(&r); - if (!r.isInRegionNoZ(&pos)) - pos = TheTerrainLogic->findClosestEdgePoint(&pos); - const ThingTemplate *thing = TheThingFactory->findTemplate( msgPlayer->getPlayerTemplate()->getBeaconTemplate() ); - if (thing && !TheVictoryConditions->hasSinglePlayerBeenDefeated(msgPlayer)) - { - // how many does this player have active? - Int count; - msgPlayer->countObjectsByThingTemplate( 1, &thing, false, &count ); - DEBUG_LOG(("Player already has %d beacons active", count)); - if (count >= TheMultiplayerSettings->getMaxBeaconsPerPlayer()) - { - if (msgPlayer == ThePlayerList->getLocalPlayer()) - { - // tell the user - TheInGameUI->message( TheGameText->fetch("GUI:TooManyBeacons") ); - - // play a sound - static AudioEventRTS aSound("BeaconPlacementFailed"); - aSound.setPosition(&pos); - aSound.setPlayerIndex(msgPlayer->getPlayerIndex()); - TheAudio->addAudioEvent(&aSound); - } - - break; - } - Object *object = TheThingFactory->newObject( thing, msgPlayer->getDefaultTeam() ); - object->setPosition( &pos ); - object->setProducer(nullptr); - - if (msgPlayer->getRelationship( ThePlayerList->getLocalPlayer()->getDefaultTeam() ) == ALLIES || ThePlayerList->getLocalPlayer()->isPlayerObserver()) - { - // tell the user - UnicodeString s; - s.format(TheGameText->fetch("GUI:BeaconPlaced"), msgPlayer->getPlayerDisplayName().str()); - TheInGameUI->message( s ); - - // play a sound - static AudioEventRTS aSound("BeaconPlaced"); - aSound.setPlayerIndex(msgPlayer->getPlayerIndex()); - aSound.setPosition(&pos); - TheAudio->addAudioEvent(&aSound); - - // beacons are a rare event; play a nifty radar event thingy - TheRadar->createEvent( object->getPosition(), RADAR_EVENT_INFORMATION ); - - if (ThePlayerList->getLocalPlayer()->getRelationship(msgPlayer->getDefaultTeam()) == ALLIES) - TheEva->setShouldPlay(EVA_BeaconDetected); - - TheControlBar->markUIDirty(); // check if we should grey out the button - } - else - { - - Int updateCount = 0; - static NameKeyType nameKeyClientUpdate = NAMEKEY("BeaconClientUpdate"); - ClientUpdateModule ** clientModules = object->getDrawable()->getClientUpdateModules(); - if (clientModules) - { - while (*clientModules) - { - if ((*clientModules)->getModuleNameKey() == nameKeyClientUpdate) - { - (*(BeaconClientUpdate **)clientModules)->hideBeacon(); - ++updateCount; - } - - ++clientModules; - } - } - DEBUG_ASSERTCRASH(updateCount == 1, ("Saw %d update modules for the beacon!", updateCount)); - - } - } - else - { - // tell the user - TheInGameUI->message( TheGameText->fetch("GUI:BeaconPlacementFailed") ); - - // play a sound - static AudioEventRTS aSound("BeaconPlacementFailed"); - aSound.setPosition(&pos); - aSound.setPlayerIndex(msgPlayer->getPlayerIndex()); - TheAudio->addAudioEvent(&aSound); - } - break; - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_REMOVE_BEACON: - { - AIGroupPtr allSelectedObjects = TheAI->createGroup(); -#if RETAIL_COMPATIBLE_AIGROUP - msgPlayer->getCurrentSelectionAsAIGroup(allSelectedObjects); // need to act on all objects, so we can hide teammates' beacons. -#else - msgPlayer->getCurrentSelectionAsAIGroup(allSelectedObjects.Peek()); // need to act on all objects, so we can hide teammates' beacons. -#endif - if( allSelectedObjects ) - { - const VecObjectID& selectedObjects = allSelectedObjects->getAllIDs(); - for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it) - { - Object *beacon = findObjectByID(*it); - if (beacon) - { - // TheSuperHackers @bugfix Prevent runtime crashing when a beacon is no longer associated with an initialized player. - const PlayerTemplate *playerTemplate = beacon->getControllingPlayer()->getPlayerTemplate(); - if (!playerTemplate) - continue; - - const ThingTemplate *thing = TheThingFactory->findTemplate( playerTemplate->getBeaconTemplate() ); - if (thing && thing->isEquivalentTo(beacon->getTemplate())) - { - if (beacon->getControllingPlayer() == msgPlayer) - { - destroyObject(beacon); // the owner is telling it to go away. such is life. - - TheControlBar->markUIDirty(); // check if we should un-grey out the button - } - else if (msgPlayer == ThePlayerList->getLocalPlayer()) - { - Drawable *beaconDrawable = beacon->getDrawable(); - if (beaconDrawable) - { - - static NameKeyType nameKeyClientUpdate = NAMEKEY("BeaconClientUpdate"); - ClientUpdateModule ** clientModules = beaconDrawable->getClientUpdateModules(); - if (clientModules) - { - while (*clientModules) - { - if ((*clientModules)->getModuleNameKey() == nameKeyClientUpdate) - (*(BeaconClientUpdate **)clientModules)->hideBeacon(); - - ++clientModules; - } - } - } - } - } - } - } -#if RETAIL_COMPATIBLE_AIGROUP - if (allSelectedObjects->isEmpty()) - { - TheAI->destroyGroup(allSelectedObjects); - allSelectedObjects = nullptr; - } -#endif - } - break; - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_SET_BEACON_TEXT: - { - if( currentlySelectedGroup ) - { - const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs(); - for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it) - { - Object *beacon = findObjectByID(*it); - if (beacon) - { - Drawable *beaconDrawable = beacon->getDrawable(); - if (beaconDrawable) - { - UnicodeString s; - for( int i=0; igetArgumentCount(); i++ ) - { - s.concat( msg->getArgument(i)->wChar ); - } - - if (s.isEmpty()) - beaconDrawable->clearCaptionText(); - else - beaconDrawable->setCaptionText(s); - } - } - } - } - break; - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_SELF_DESTRUCT: - { - if (msg->getArgument(0)->boolean) - { - // transfer control to any living ally - Int i=0; - for (; igetPlayerCount(); ++i) - { - if (i != msgPlayer->getPlayerIndex()) - { - Player *otherPlayer = ThePlayerList->getNthPlayer(i); - if (msgPlayer->getRelationship(otherPlayer->getDefaultTeam()) == ALLIES && - otherPlayer->getRelationship(msgPlayer->getDefaultTeam()) == ALLIES) - { - if (TheVictoryConditions->hasSinglePlayerBeenDefeated(otherPlayer)) - continue; - - // a living ally! hooray! - otherPlayer->transferAssetsFromThat(msgPlayer); - msgPlayer->killPlayer(); // just to be safe (and to kill beacons etc that don't transfer) - break; - } - } - } - if (i == ThePlayerList->getPlayerCount()) - { - // didn't find any allies. die, loner! - msgPlayer->killPlayer(); - } - } - else - { - msgPlayer->killPlayer(); - } - // There is no reason to do any notification here, it now takes place in the victory conditions. - // bonehead. - break; - } - - // -------------------------------------------------------------------------------------------- - case GameMessage::MSG_SET_REPLAY_CAMERA: - { - if (TheRecorder->isPlaybackMode() && TheGlobalData->m_useCameraInReplay && TheControlBar->getObserverLookAtPlayer() == msgPlayer) - { - if (TheTacticalView->isCameraMovementFinished()) - { - const Coord3D pos = msg->getArgument( 0 )->location; - const Real angle = msg->getArgument( 1 )->real; - const Real pitch = msg->getArgument( 2 )->real; - const Real zoom = msg->getArgument( 3 )->real; - const Mouse::MouseCursor mouseCursor = static_cast(msg->getArgument( 4 )->integer); - const ICoord2D mousePos = msg->getArgument( 5 )->pixel; - - // TheSuperHackers @info Definitely call in user mode to ensure the camera operates with auto-zoom - // over terrain elevations, because the Replay Camera does not store the absolute camera location, - // but key parameters relative to the terrain height at the camera pivot. - TheTacticalView->userSetPosition(pos); - TheTacticalView->userSetAngle(angle); - TheTacticalView->userSetPitch(pitch); - TheTacticalView->userSetZoom(zoom); - - // TheSuperHackers @fix Make sure there is no scrolling ever. - const Coord2D scroll = {0, 0}; - TheTacticalView->userScrollBy(&scroll); - - if (msg->getArgumentCount() >= 8) - { - // TheSuperHackers @feature Override all the settings above with real camera position and view direction. - // This ensures that the camera looks EXACTLY like it was at the time of recording, no matter how the - // View is configured or tweaked. Note that the above settings are still required to set regardless, because - // when the replay camera is exited, then the pivot position and angles will be needed to build the camera - // where it was left off. - const Coord3D camPos = msg->getArgument( 6 )->location; - const Coord3D camDir = msg->getArgument( 7 )->location; - - TheTacticalView->setUserControlled(false); - TheTacticalView->set3DCameraLookAt(camPos, camDir, 0.0f); - } - - // TheSuperHackers @fix Lock the new location to avoid user input from changing the camera in this frame. - TheTacticalView->lockUserControlUntilFrame( getFrame() + 1 ); - - if (!TheLookAtTranslator->hasMouseMovedRecently()) - { - TheMouse->setCursor( mouseCursor ); - TheMouse->setPosition( mousePos.x, mousePos.y ); - TheLookAtTranslator->setCurrentPos( mousePos ); - } - } - } - break; - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_CREATE_TEAM0: - case GameMessage::MSG_CREATE_TEAM1: - case GameMessage::MSG_CREATE_TEAM2: - case GameMessage::MSG_CREATE_TEAM3: - case GameMessage::MSG_CREATE_TEAM4: - case GameMessage::MSG_CREATE_TEAM5: - case GameMessage::MSG_CREATE_TEAM6: - case GameMessage::MSG_CREATE_TEAM7: - case GameMessage::MSG_CREATE_TEAM8: - case GameMessage::MSG_CREATE_TEAM9: - { - msgPlayer->processCreateTeamGameMessage(msg->getType() - GameMessage::MSG_CREATE_TEAM0, msg); - break; - } - - case GameMessage::MSG_SELECT_TEAM0: - case GameMessage::MSG_SELECT_TEAM1: - case GameMessage::MSG_SELECT_TEAM2: - case GameMessage::MSG_SELECT_TEAM3: - case GameMessage::MSG_SELECT_TEAM4: - case GameMessage::MSG_SELECT_TEAM5: - case GameMessage::MSG_SELECT_TEAM6: - case GameMessage::MSG_SELECT_TEAM7: - case GameMessage::MSG_SELECT_TEAM8: - case GameMessage::MSG_SELECT_TEAM9: - { - msgPlayer->processSelectTeamGameMessage(msg->getType() - GameMessage::MSG_SELECT_TEAM0); - break; - } - - case GameMessage::MSG_ADD_TEAM0: - case GameMessage::MSG_ADD_TEAM1: - case GameMessage::MSG_ADD_TEAM2: - case GameMessage::MSG_ADD_TEAM3: - case GameMessage::MSG_ADD_TEAM4: - case GameMessage::MSG_ADD_TEAM5: - case GameMessage::MSG_ADD_TEAM6: - case GameMessage::MSG_ADD_TEAM7: - case GameMessage::MSG_ADD_TEAM8: - case GameMessage::MSG_ADD_TEAM9: - { - msgPlayer->processAddTeamGameMessage(msg->getType() - GameMessage::MSG_ADD_TEAM0); - break; - } - - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_LOGIC_CRC: - { - if (TheNetwork) - { - Int slotIndex = -1; - for (Int i=0; igetPlayerType() == PLAYER_HUMAN && TheNetwork->getPlayerName(i) == msgPlayer->getPlayerDisplayName()) - { - slotIndex = i; - break; - } - } - - if (slotIndex < 0 || !TheNetwork->isPlayerConnected(slotIndex)) - break; - - if (msgPlayer->isLocalPlayer()) - { -#if defined(RTS_DEBUG) - // don't even put this in release, cause someone might hack it. - if (!TheDebugIgnoreSyncErrors) - { -#endif - m_shouldValidateCRCs = TRUE; -#if defined(RTS_DEBUG) - } -#endif - } - - UnsignedInt newCRC = msg->getArgument(0)->integer; - //DEBUG_LOG(("Received CRC of %8.8X from %ls on frame %d", newCRC, - //msgPlayer->getPlayerDisplayName().str(), m_frame)); - m_cachedCRCs[msgPlayer->getPlayerIndex()] = newCRC; - } - else if (TheRecorder && TheRecorder->isPlaybackMode()) - { - UnsignedInt newCRC = msg->getArgument(0)->integer; - //DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d", - //newCRC, msgPlayer->getPlayerIndex(), getCRC(), msg->getArgumentCount())); - - TheRecorder->handleCRCMessage(newCRC, msgPlayer->getPlayerIndex(), (msg->getArgument(1)->boolean)); - } - break; - - } - - //--------------------------------------------------------------------------------------------- - case GameMessage::MSG_PURCHASE_SCIENCE: - { - ScienceType science = (ScienceType)msg->getArgument( 0 )->integer; - - // sanity - if( science == SCIENCE_INVALID ) - break; - - msgPlayer->attemptToPurchaseScience(science); - - break; - - } - - } - -#if RETAIL_COMPATIBLE_AIGROUP - // TheSuperHackers @bugfix xezon 28/06/2025 This hack avoids crashing when players are selected during Replay playback. - // It can read data from an already deleted AIGroup and return this function when its member size is 0, signifying that - // it is indeed deleted. - if (currentlySelectedGroup && currentlySelectedGroup->getCount() == 0) - return; -#endif - - /**/ /// @todo: multiplayer semantics - if (currentlySelectedGroup && TheRecorder->isPlaybackMode() && TheGlobalData->m_useCameraInReplay && TheControlBar->getObserverLookAtPlayer() == msgPlayer /*&& !TheRecorder->isMultiplayer()*/) - { - const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs(); - TheInGameUI->deselectAllDrawables(); - for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it) - { - const Object *obj = findObjectByID(*it); - if (obj) - { - Drawable *draw = obj->getDrawable(); - if (draw) - TheInGameUI->selectDrawable(draw); - } - } - } - /**/ - - if( currentlySelectedGroup != nullptr ) - { -#if RETAIL_COMPATIBLE_AIGROUP - TheAI->destroyGroup(currentlySelectedGroup); -#else - currentlySelectedGroup->removeAll(); -#endif - } - -} diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/RankInfo.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/RankInfo.cpp deleted file mode 100644 index 18265789d1f..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/System/RankInfo.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* -** 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: RankInfo.cpp ///////////////////////////////////////////////////////// -// Created: Steven Johnson, Sep 2002 -// Desc: -//----------------------------------------------------------------------------- - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/INI.h" -#include "Common/Player.h" -#include "GameLogic/RankInfo.h" - -RankInfoStore* TheRankInfoStore = nullptr; - - -//----------------------------------------------------------------------------- -RankInfo::~RankInfo() -{ -} - - -//----------------------------------------------------------------------------- -RankInfoStore::~RankInfoStore() -{ - Int level; - for (level =0; level < getRankLevelCount(); level++) - { - RankInfo* ri = m_rankInfos[level]; - deleteInstance(ri); - } - m_rankInfos.clear(); -} - - -//----------------------------------------------------------------------------- -void RankInfoStore::init() -{ - DEBUG_ASSERTCRASH(m_rankInfos.empty(), ("Hmm")); - m_rankInfos.clear(); -} - -//----------------------------------------------------------------------------- -void RankInfoStore::reset() -{ - // nope. - //m_rankInfos.clear(); - - for (RankInfoVec::iterator it = m_rankInfos.begin(); it != m_rankInfos.end(); /*++it*/) - { - RankInfo* ri = *it; - if (ri) - { - Overridable* temp = ri->deleteOverrides(); - if (!temp) - { - DEBUG_CRASH(("hmm, should not be possible for RankInfo")); - it = m_rankInfos.erase(it); - } - else - { - ++it; - } - } - } -} - -//----------------------------------------------------------------------------- -Int RankInfoStore::getRankLevelCount() const -{ - return m_rankInfos.size(); -} - -//----------------------------------------------------------------------------- -// note that level is 1...n, NOT 0...n-1 -const RankInfo* RankInfoStore::getRankInfo(Int level) const -{ - if (level >= 1 && level <= getRankLevelCount()) - { - const RankInfo* ri = m_rankInfos[level-1]; - if (ri) - { - return (const RankInfo*)ri->getFinalOverride(); - } - } - return nullptr; -} - -//----------------------------------------------------------------------------- -void RankInfoStore::friend_parseRankDefinition( INI* ini ) -{ - if (TheRankInfoStore) - { - Int rank = INI::scanInt(ini->getNextToken()); - - static const FieldParse myFieldParse[] = - { - { "RankName", INI::parseAndTranslateLabel, nullptr, offsetof( RankInfo, m_rankName ) }, - { "SkillPointsNeeded", INI::parseInt, nullptr, offsetof( RankInfo, m_skillPointsNeeded ) }, - { "SciencesGranted", INI::parseScienceVector, nullptr, offsetof( RankInfo, m_sciencesGranted ) }, - { "SciencePurchasePointsGranted", INI::parseUnsignedInt, nullptr, offsetof( RankInfo, m_sciencePurchasePointsGranted ) }, - { nullptr, nullptr, nullptr, 0 } - }; - - if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) - { - // we aren't allowed to add ranks in overrides, only to override existing ones. - if (rank < 1 || rank > TheRankInfoStore->m_rankInfos.size()) - { - DEBUG_CRASH(("Rank not found in map.ini")); - throw INI_INVALID_DATA; - } - - RankInfo* info = TheRankInfoStore->m_rankInfos[rank-1]; - if (!info) - { - DEBUG_CRASH(("Rank not found in map.ini")); - throw INI_INVALID_DATA; - } - - RankInfo* newInfo = newInstance(RankInfo); - - // copy data from final override to 'newInfo' as a set of initial default values - info = (RankInfo*)(info->friend_getFinalOverride()); - - *newInfo = *info; - info->setNextOverride(newInfo); - newInfo->markAsOverride(); // must do AFTER the copy - - ini->initFromINI(newInfo, myFieldParse); - //TheRankInfoStore->m_rankInfos.push_back(newInfo); // NO, BAD, WRONG -- don't add in this case. - - } - else - { - if (rank != TheRankInfoStore->m_rankInfos.size() + 1) - { - DEBUG_CRASH(("Ranks must increase monotonically")); - throw INI_INVALID_DATA; - } - RankInfo* info = newInstance(RankInfo); - ini->initFromINI(info, myFieldParse); - TheRankInfoStore->m_rankInfos.push_back(info); - } - } -} - -//----------------------------------------------------------------------------- -void INI::parseRankDefinition( INI* ini ) -{ - RankInfoStore::friend_parseRankDefinition(ini); -} - diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index 32edb03fdd2..ea7ef771ff7 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -238,9 +238,9 @@ set(GAMEENGINE_SRC Include/GameLogic/AITNGuard.h Include/GameLogic/Armor.h Include/GameLogic/ArmorSet.h - Include/GameLogic/CaveSystem.h - Include/GameLogic/CrateSystem.h - Include/GameLogic/Damage.h +# Include/GameLogic/CaveSystem.h +# Include/GameLogic/CrateSystem.h +# Include/GameLogic/Damage.h Include/GameLogic/ExperienceTracker.h Include/GameLogic/FiringTracker.h Include/GameLogic/FPUControl.h @@ -479,7 +479,7 @@ set(GAMEENGINE_SRC Include/GameLogic/PartitionManager.h Include/GameLogic/PolygonTrigger.h Include/GameLogic/Powers.h - Include/GameLogic/RankInfo.h +# Include/GameLogic/RankInfo.h Include/GameLogic/ScriptActions.h Include/GameLogic/ScriptConditions.h Include/GameLogic/ScriptEngine.h @@ -1075,12 +1075,12 @@ set(GAMEENGINE_SRC Source/GameLogic/ScriptEngine/ScriptEngine.cpp Source/GameLogic/ScriptEngine/Scripts.cpp Source/GameLogic/ScriptEngine/VictoryConditions.cpp - Source/GameLogic/System/CaveSystem.cpp - Source/GameLogic/System/CrateSystem.cpp - Source/GameLogic/System/Damage.cpp +# Source/GameLogic/System/CaveSystem.cpp +# Source/GameLogic/System/CrateSystem.cpp +# Source/GameLogic/System/Damage.cpp Source/GameLogic/System/GameLogic.cpp - Source/GameLogic/System/GameLogicDispatch.cpp - Source/GameLogic/System/RankInfo.cpp +# Source/GameLogic/System/GameLogicDispatch.cpp +# Source/GameLogic/System/RankInfo.cpp # Source/GameNetwork/Connection.cpp # Source/GameNetwork/ConnectionManager.cpp # Source/GameNetwork/DisconnectManager.cpp diff --git a/scripts/cpp/unify_move_files.py b/scripts/cpp/unify_move_files.py index 87e840d698d..b293dd29157 100644 --- a/scripts/cpp/unify_move_files.py +++ b/scripts/cpp/unify_move_files.py @@ -474,7 +474,7 @@ def main(): #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8wrapper.h", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8wrapper.h") #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8caps.cpp", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8caps.cpp") #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp") - + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameClient/Display.cpp", Game.CORE, "GameEngine/Source/GameClient/Display.cpp") #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameClient/Display.h", Game.CORE, "GameEngine/Include/GameClient/Display.h") #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameClient/System/Anim2D.cpp", Game.CORE, "GameEngine/Source/GameClient/System/Anim2D.cpp") @@ -527,6 +527,16 @@ def main(): #unify_file(Game.ZEROHOUR, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DTextEntry.cpp", Game.CORE, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DTextEntry.cpp") #unify_file(Game.ZEROHOUR, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DVerticalSlider.cpp", Game.CORE, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DVerticalSlider.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/CaveSystem.h", Game.CORE, "GameEngine/Include/GameLogic/CaveSystem.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/CrateSystem.h", Game.CORE, "GameEngine/Include/GameLogic/CrateSystem.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/Damage.h", Game.CORE, "GameEngine/Include/GameLogic/Damage.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/RankInfo.h", Game.CORE, "GameEngine/Include/GameLogic/RankInfo.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/CaveSystem.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/CaveSystem.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/CrateSystem.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/CrateSystem.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/Damage.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/Damage.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/RankInfo.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/RankInfo.cpp") + return