diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index 85d384b87a7..95f714cfcc5 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -269,6 +269,7 @@ static PoolSizeRec PoolSizes[] = { "SupplyWarehouseCreate", 48, 16 }, { "SupplyWarehouseDockUpdate", 48, 16 }, { "EnemyNearUpdate", 1024, 32 }, + { "ProximityCaptureUpdate", 32, 32 }, { "TechBuildingBehavior", 32, 32 }, { "ToppleUpdate", 256, 128 }, { "TransitionDamageFX", 384, 128 }, diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index 9fb9bbae71f..b3ee009a069 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -65,7 +65,7 @@ set(GAMEENGINE_SRC Include/Common/List.h # Include/Common/LocalFile.h # Include/Common/LocalFileSystem.h -# Include/Common/MapObject.h +# Include/Common/MapObject.h Include/Common/MapReaderWriterInfo.h Include/Common/MessageStream.h Include/Common/MiniLog.h @@ -77,7 +77,7 @@ set(GAMEENGINE_SRC Include/Common/Money.h Include/Common/MultiplayerSettings.h Include/Common/NameKeyGenerator.h -# Include/Common/ObjectStatusTypes.h +# Include/Common/ObjectStatusTypes.h Include/Common/OSDisplay.h Include/Common/Overridable.h Include/Common/Override.h @@ -90,7 +90,7 @@ set(GAMEENGINE_SRC Include/Common/ProductionPrerequisite.h Include/Common/QuickmatchPreferences.h Include/Common/QuotedPrintable.h -# Include/Common/Radar.h +# Include/Common/Radar.h # Include/Common/RAMFile.h # Include/Common/RandomValue.h Include/Common/Recorder.h @@ -200,7 +200,7 @@ set(GAMEENGINE_SRC Include/GameClient/Line2D.h Include/GameClient/LoadScreen.h Include/GameClient/LookAtXlat.h -# Include/GameClient/MapUtil.h +# Include/GameClient/MapUtil.h Include/GameClient/MessageBox.h Include/GameClient/MetaEvent.h Include/GameClient/Module/AnimatedParticleSysBoneClientUpdate.h @@ -208,7 +208,7 @@ set(GAMEENGINE_SRC Include/GameClient/Module/SwayClientUpdate.h Include/GameClient/Module/DynamicGeometryClientUpdate.h Include/GameClient/Mouse.h -# Include/GameClient/ParabolicEase.h +# Include/GameClient/ParabolicEase.h Include/GameClient/ParticleSys.h Include/GameClient/PlaceEventTranslator.h Include/GameClient/ProcessAnimateWindow.h @@ -220,15 +220,15 @@ set(GAMEENGINE_SRC Include/GameClient/Shell.h Include/GameClient/ShellHooks.h Include/GameClient/ShellMenuScheme.h -# Include/GameClient/Smudge.h -# Include/GameClient/Snow.h +# Include/GameClient/Smudge.h +# Include/GameClient/Snow.h Include/GameClient/Statistics.h -# Include/GameClient/TerrainRoads.h -# Include/GameClient/TerrainVisual.h +# Include/GameClient/TerrainRoads.h +# Include/GameClient/TerrainVisual.h Include/GameClient/TintStatus.h # Include/GameClient/VideoPlayer.h -# Include/GameClient/View.h -# Include/GameClient/Water.h +# Include/GameClient/View.h +# Include/GameClient/Water.h Include/GameClient/WindowLayout.h # Include/GameClient/WindowVideoManager.h Include/GameClient/WindowXlat.h @@ -322,6 +322,7 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/EjectPilotDie.h Include/GameLogic/Module/EMPUpdate.h Include/GameLogic/Module/EnemyNearUpdate.h + Include/GameLogic/Module/ProximityCaptureUpdate.h Include/GameLogic/Module/ExperienceScalarUpgrade.h Include/GameLogic/Module/FireOCLAfterWeaponCooldownUpdate.h Include/GameLogic/Module/FireSpreadUpdate.h @@ -529,58 +530,58 @@ set(GAMEENGINE_SRC Include/GameLogic/WeaponSetFlags.h Include/GameLogic/WeaponSetType.h Include/GameLogic/WeaponStatus.h -# Include/GameNetwork/Connection.h -# Include/GameNetwork/ConnectionManager.h -# Include/GameNetwork/DisconnectManager.h -# Include/GameNetwork/DownloadManager.h -# Include/GameNetwork/FileTransfer.h -# Include/GameNetwork/FirewallHelper.h -# Include/GameNetwork/FrameData.h -# Include/GameNetwork/FrameDataManager.h -# Include/GameNetwork/FrameMetrics.h -# Include/GameNetwork/GameInfo.h -# Include/GameNetwork/GameMessageParser.h -# Include/GameNetwork/GameSpy/BuddyDefs.h -# Include/GameNetwork/GameSpy/BuddyThread.h -# Include/GameNetwork/GameSpy/GameResultsThread.h -# Include/GameNetwork/GameSpy/GSConfig.h -# Include/GameNetwork/GameSpy/LadderDefs.h -# Include/GameNetwork/GameSpy/LobbyUtils.h -# Include/GameNetwork/GameSpy/MainMenuUtils.h -# Include/GameNetwork/GameSpy/PeerDefs.h -# Include/GameNetwork/GameSpy/PeerDefsImplementation.h -# Include/GameNetwork/GameSpy/PeerThread.h -# Include/GameNetwork/GameSpy/PersistentStorageDefs.h -# Include/GameNetwork/GameSpy/PersistentStorageThread.h -# Include/GameNetwork/GameSpy/PingThread.h -# Include/GameNetwork/GameSpy/StagingRoomGameInfo.h -# Include/GameNetwork/GameSpy/ThreadUtils.h -# Include/GameNetwork/GameSpyChat.h -# Include/GameNetwork/GameSpyGameInfo.h -# Include/GameNetwork/GameSpyGP.h -# Include/GameNetwork/GameSpyOverlay.h -# Include/GameNetwork/GameSpyThread.h +# Include/GameNetwork/Connection.h +# Include/GameNetwork/ConnectionManager.h +# Include/GameNetwork/DisconnectManager.h +# Include/GameNetwork/DownloadManager.h +# Include/GameNetwork/FileTransfer.h +# Include/GameNetwork/FirewallHelper.h +# Include/GameNetwork/FrameData.h +# Include/GameNetwork/FrameDataManager.h +# Include/GameNetwork/FrameMetrics.h +# Include/GameNetwork/GameInfo.h +# Include/GameNetwork/GameMessageParser.h +# Include/GameNetwork/GameSpy/BuddyDefs.h +# Include/GameNetwork/GameSpy/BuddyThread.h +# Include/GameNetwork/GameSpy/GameResultsThread.h +# Include/GameNetwork/GameSpy/GSConfig.h +# Include/GameNetwork/GameSpy/LadderDefs.h +# Include/GameNetwork/GameSpy/LobbyUtils.h +# Include/GameNetwork/GameSpy/MainMenuUtils.h +# Include/GameNetwork/GameSpy/PeerDefs.h +# Include/GameNetwork/GameSpy/PeerDefsImplementation.h +# Include/GameNetwork/GameSpy/PeerThread.h +# Include/GameNetwork/GameSpy/PersistentStorageDefs.h +# Include/GameNetwork/GameSpy/PersistentStorageThread.h +# Include/GameNetwork/GameSpy/PingThread.h +# Include/GameNetwork/GameSpy/StagingRoomGameInfo.h +# Include/GameNetwork/GameSpy/ThreadUtils.h +# Include/GameNetwork/GameSpyChat.h +# Include/GameNetwork/GameSpyGameInfo.h +# Include/GameNetwork/GameSpyGP.h +# Include/GameNetwork/GameSpyOverlay.h +# Include/GameNetwork/GameSpyThread.h Include/GameNetwork/GUIUtil.h -# Include/GameNetwork/IPEnumeration.h -# Include/GameNetwork/LANAPI.h -# Include/GameNetwork/LANAPICallbacks.h -# Include/GameNetwork/LANGameInfo.h -# Include/GameNetwork/LANPlayer.h -# Include/GameNetwork/NAT.h -# Include/GameNetwork/NetCommandList.h -# Include/GameNetwork/NetCommandMsg.h -# Include/GameNetwork/NetCommandRef.h -# Include/GameNetwork/NetCommandWrapperList.h -# Include/GameNetwork/NetPacket.h -# Include/GameNetwork/NetworkDefs.h -# Include/GameNetwork/NetworkInterface.h -# Include/GameNetwork/networkutil.h -# Include/GameNetwork/RankPointValue.h -# Include/GameNetwork/Transport.h -# Include/GameNetwork/udp.h -# Include/GameNetwork/User.h -# Include/GameNetwork/WOLBrowser/FEBDispatch.h -# Include/GameNetwork/WOLBrowser/WebBrowser.h +# Include/GameNetwork/IPEnumeration.h +# Include/GameNetwork/LANAPI.h +# Include/GameNetwork/LANAPICallbacks.h +# Include/GameNetwork/LANGameInfo.h +# Include/GameNetwork/LANPlayer.h +# Include/GameNetwork/NAT.h +# Include/GameNetwork/NetCommandList.h +# Include/GameNetwork/NetCommandMsg.h +# Include/GameNetwork/NetCommandRef.h +# Include/GameNetwork/NetCommandWrapperList.h +# Include/GameNetwork/NetPacket.h +# Include/GameNetwork/NetworkDefs.h +# Include/GameNetwork/NetworkInterface.h +# Include/GameNetwork/networkutil.h +# Include/GameNetwork/RankPointValue.h +# Include/GameNetwork/Transport.h +# Include/GameNetwork/udp.h +# Include/GameNetwork/User.h +# Include/GameNetwork/WOLBrowser/FEBDispatch.h +# Include/GameNetwork/WOLBrowser/WebBrowser.h Include/Precompiled/PreRTS.h # Source/Common/Audio/AudioEventRTS.cpp # Source/Common/Audio/AudioRequest.cpp @@ -684,9 +685,9 @@ set(GAMEENGINE_SRC # Source/Common/System/LocalFile.cpp # Source/Common/System/LocalFileSystem.cpp #Source/Common/System/MemoryInit.cpp -# Source/Common/System/ObjectStatusTypes.cpp +# Source/Common/System/ObjectStatusTypes.cpp Source/Common/System/QuotedPrintable.cpp -# Source/Common/System/Radar.cpp +# Source/Common/System/Radar.cpp # Source/Common/System/RAMFile.cpp Source/Common/System/registry.cpp Source/Common/System/SaveGame/GameState.cpp @@ -830,7 +831,7 @@ set(GAMEENGINE_SRC Source/GameClient/Input/Mouse.cpp Source/GameClient/LanguageFilter.cpp Source/GameClient/Line2D.cpp -# Source/GameClient/MapUtil.cpp +# Source/GameClient/MapUtil.cpp Source/GameClient/MessageStream/CommandXlat.cpp Source/GameClient/MessageStream/GUICommandTranslator.cpp Source/GameClient/MessageStream/HintSpy.cpp @@ -840,10 +841,10 @@ set(GAMEENGINE_SRC Source/GameClient/MessageStream/PlaceEventTranslator.cpp Source/GameClient/MessageStream/SelectionXlat.cpp Source/GameClient/MessageStream/WindowXlat.cpp -# Source/GameClient/ParabolicEase.cpp +# Source/GameClient/ParabolicEase.cpp Source/GameClient/RadiusDecal.cpp Source/GameClient/SelectionInfo.cpp -# Source/GameClient/Snow.cpp +# Source/GameClient/Snow.cpp Source/GameClient/Statistics.cpp Source/GameClient/System/Anim2D.cpp Source/GameClient/System/CampaignManager.cpp @@ -852,13 +853,13 @@ set(GAMEENGINE_SRC Source/GameClient/System/Image.cpp Source/GameClient/System/ParticleSys.cpp Source/GameClient/System/RayEffect.cpp -# Source/GameClient/System/Smudge.cpp -# Source/GameClient/Terrain/TerrainRoads.cpp -# Source/GameClient/Terrain/TerrainVisual.cpp +# Source/GameClient/System/Smudge.cpp +# Source/GameClient/Terrain/TerrainRoads.cpp +# Source/GameClient/Terrain/TerrainVisual.cpp # Source/GameClient/VideoPlayer.cpp # Source/GameClient/VideoStream.cpp -# Source/GameClient/View.cpp -# Source/GameClient/Water.cpp +# Source/GameClient/View.cpp +# Source/GameClient/Water.cpp Source/GameLogic/AI/AI.cpp Source/GameLogic/AI/AIDock.cpp Source/GameLogic/AI/AIGroup.cpp @@ -1053,6 +1054,7 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Update/DynamicShroudClearingRangeUpdate.cpp Source/GameLogic/Object/Update/EMPUpdate.cpp Source/GameLogic/Object/Update/EnemyNearUpdate.cpp + Source/GameLogic/Object/Update/ProximityCaptureUpdate.cpp Source/GameLogic/Object/Update/FireOCLAfterWeaponCooldownUpdate.cpp Source/GameLogic/Object/Update/FireSpreadUpdate.cpp Source/GameLogic/Object/Update/FirestormDynamicGeometryInfoUpdate.cpp @@ -1146,53 +1148,53 @@ set(GAMEENGINE_SRC Source/GameLogic/System/GameLogic.cpp Source/GameLogic/System/GameLogicDispatch.cpp Source/GameLogic/System/RankInfo.cpp -# Source/GameNetwork/Connection.cpp -# Source/GameNetwork/ConnectionManager.cpp -# Source/GameNetwork/DisconnectManager.cpp -# Source/GameNetwork/DownloadManager.cpp -# Source/GameNetwork/FileTransfer.cpp -# Source/GameNetwork/FirewallHelper.cpp -# Source/GameNetwork/FrameData.cpp -# Source/GameNetwork/FrameDataManager.cpp -# Source/GameNetwork/FrameMetrics.cpp -# Source/GameNetwork/GameInfo.cpp -# Source/GameNetwork/GameMessageParser.cpp -# #Source/GameNetwork/GameSpyChat.cpp -# #Source/GameNetwork/GameSpyGameInfo.cpp -# #Source/GameNetwork/GameSpyGP.cpp -# Source/GameNetwork/GameSpy/Chat.cpp -# Source/GameNetwork/GameSpy/GSConfig.cpp -# Source/GameNetwork/GameSpy/LadderDefs.cpp -# Source/GameNetwork/GameSpy/LobbyUtils.cpp -# Source/GameNetwork/GameSpy/MainMenuUtils.cpp -# Source/GameNetwork/GameSpy/PeerDefs.cpp -# Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp -# Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp -# Source/GameNetwork/GameSpy/Thread/GameResultsThread.cpp -# Source/GameNetwork/GameSpy/Thread/PeerThread.cpp -# Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp -# Source/GameNetwork/GameSpy/Thread/PingThread.cpp -# Source/GameNetwork/GameSpy/Thread/ThreadUtils.cpp -# Source/GameNetwork/GameSpyOverlay.cpp +# Source/GameNetwork/Connection.cpp +# Source/GameNetwork/ConnectionManager.cpp +# Source/GameNetwork/DisconnectManager.cpp +# Source/GameNetwork/DownloadManager.cpp +# Source/GameNetwork/FileTransfer.cpp +# Source/GameNetwork/FirewallHelper.cpp +# Source/GameNetwork/FrameData.cpp +# Source/GameNetwork/FrameDataManager.cpp +# Source/GameNetwork/FrameMetrics.cpp +# Source/GameNetwork/GameInfo.cpp +# Source/GameNetwork/GameMessageParser.cpp +# #Source/GameNetwork/GameSpyChat.cpp +# #Source/GameNetwork/GameSpyGameInfo.cpp +# #Source/GameNetwork/GameSpyGP.cpp +# Source/GameNetwork/GameSpy/Chat.cpp +# Source/GameNetwork/GameSpy/GSConfig.cpp +# Source/GameNetwork/GameSpy/LadderDefs.cpp +# Source/GameNetwork/GameSpy/LobbyUtils.cpp +# Source/GameNetwork/GameSpy/MainMenuUtils.cpp +# Source/GameNetwork/GameSpy/PeerDefs.cpp +# Source/GameNetwork/GameSpy/StagingRoomGameInfo.cpp +# Source/GameNetwork/GameSpy/Thread/BuddyThread.cpp +# Source/GameNetwork/GameSpy/Thread/GameResultsThread.cpp +# Source/GameNetwork/GameSpy/Thread/PeerThread.cpp +# Source/GameNetwork/GameSpy/Thread/PersistentStorageThread.cpp +# Source/GameNetwork/GameSpy/Thread/PingThread.cpp +# Source/GameNetwork/GameSpy/Thread/ThreadUtils.cpp +# Source/GameNetwork/GameSpyOverlay.cpp Source/GameNetwork/GUIUtil.cpp -# Source/GameNetwork/IPEnumeration.cpp -# Source/GameNetwork/LANAPI.cpp -# Source/GameNetwork/LANAPICallbacks.cpp -# Source/GameNetwork/LANAPIhandlers.cpp -# Source/GameNetwork/LANGameInfo.cpp -# Source/GameNetwork/NAT.cpp -# Source/GameNetwork/NetCommandList.cpp -# Source/GameNetwork/NetCommandMsg.cpp -# Source/GameNetwork/NetCommandRef.cpp -# Source/GameNetwork/NetCommandWrapperList.cpp -# Source/GameNetwork/NetMessageStream.cpp -# Source/GameNetwork/NetPacket.cpp -# Source/GameNetwork/Network.cpp -# Source/GameNetwork/NetworkUtil.cpp -# Source/GameNetwork/Transport.cpp -# Source/GameNetwork/udp.cpp -# Source/GameNetwork/User.cpp -# Source/GameNetwork/WOLBrowser/WebBrowser.cpp +# Source/GameNetwork/IPEnumeration.cpp +# Source/GameNetwork/LANAPI.cpp +# Source/GameNetwork/LANAPICallbacks.cpp +# Source/GameNetwork/LANAPIhandlers.cpp +# Source/GameNetwork/LANGameInfo.cpp +# Source/GameNetwork/NAT.cpp +# Source/GameNetwork/NetCommandList.cpp +# Source/GameNetwork/NetCommandMsg.cpp +# Source/GameNetwork/NetCommandRef.cpp +# Source/GameNetwork/NetCommandWrapperList.cpp +# Source/GameNetwork/NetMessageStream.cpp +# Source/GameNetwork/NetPacket.cpp +# Source/GameNetwork/Network.cpp +# Source/GameNetwork/NetworkUtil.cpp +# Source/GameNetwork/Transport.cpp +# Source/GameNetwork/udp.cpp +# Source/GameNetwork/User.cpp +# Source/GameNetwork/WOLBrowser/WebBrowser.cpp Source/Precompiled/PreRTS.cpp ) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProximityCaptureUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProximityCaptureUpdate.h new file mode 100644 index 00000000000..00ebafd4152 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProximityCaptureUpdate.h @@ -0,0 +1,178 @@ +/* +** Command & Conquer Generals Zero Hour(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: ProximityCaptureUpdate.h ///////////////////////////////////////////////////////////////////////////// +// Author: Matthew D. Campbell, April 2002 +// Desc: Reacts when an enemy is within range +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "GameLogic/Module/UpdateModule.h" +#include "Common/KindOf.h" + +// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// +class Object; +// class AudioEventRTS; +class FXList; + +//------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ +class ProximityCaptureUpdateModuleData : public UpdateModuleData +{ +public: + UnsignedInt m_captureTickDelay; ///< Time delay between evaluation ticks + Real m_captureRate; ///< How much to capture per tick + Real m_uncapRate; ///< How much progress to remove + Real m_uncapRateNeutral; ///< How much progress to remove when neutral; + Real m_recoverRate; ///< How much progress to recover when no units are around; + Real m_captureRadius; + KindOfMaskType m_requiredKindOf; ///< Must be set on target + KindOfMaskType m_forbiddenKindOf; ///< Must be clear on target + Bool m_isCountAirborne; ///< Affect Airborne targets + Bool m_requiresAllKindOfs; ///< requires ALL requiredKindOfs or just one of them + + Real m_unitValueContentionDelta; ///< unit values between players need to differ by this much to trigger capture + Real m_unitValueCountFactor; ///< value factor for the number of units (i.e. equal value for each unit) + //Real m_unitValueBuildCostFactor; ///< value factor for sellValue of the unit + + Bool m_showProgressBar; + + UnsignedShort m_capturePingInterval; ///< number of ticks between capture FX + Bool m_showCapturePingFlash; + Bool m_playDefectorPingSound; + // AudioEventRTS m_capturePingSound; + + const FXList* m_startCaptureFX; + const FXList* m_startUncapFX; + const FXList* m_finishCaptureFX; + const FXList* m_capturePingFX; + const FXList* m_capturePingContestedFX; + + Int m_skillPointsForCapture; ///< grant XP to the player on capture + + ProximityCaptureUpdateModuleData() + { + m_captureTickDelay = LOGICFRAMES_PER_SECOND; + m_captureRate = 1.0 / 30.0f; + m_uncapRate = 1.0 / 30.0f; + m_uncapRateNeutral = -1.0f; + m_recoverRate = -1.0f; + + m_unitValueContentionDelta = 0.01; + m_unitValueCountFactor = 1.0; + //m_unitValueBuildCostFactor = 0.0; + + m_startCaptureFX = NULL; + m_startUncapFX = NULL; + m_finishCaptureFX = NULL; + m_capturePingFX = NULL; + m_capturePingContestedFX = NULL; + m_showCapturePingFlash = true; + m_playDefectorPingSound = true; + } + + static void buildFieldParse(MultiIniFieldParse& p) + { + UpdateModuleData::buildFieldParse(p); + static const FieldParse dataFieldParse[] = + { + { "CaptureTickDelay", INI::parseDurationUnsignedInt, NULL, offsetof( ProximityCaptureUpdateModuleData, m_captureTickDelay) }, + { "CaptureRadius", INI::parseReal, NULL, offsetof( ProximityCaptureUpdateModuleData, m_captureRadius) }, + { "CaptureProgressPerTick", INI::parsePercentToReal, NULL, offsetof( ProximityCaptureUpdateModuleData, m_captureRate) }, + { "CaptureProgressRemovePerTick", INI::parsePercentToReal, NULL, offsetof( ProximityCaptureUpdateModuleData, m_uncapRate) }, + { "CaptureProgressRemovePerTickFromNeutral", INI::parsePercentToReal, NULL, offsetof( ProximityCaptureUpdateModuleData, m_uncapRateNeutral) }, + { "CaptureProgressRecoverPerTick", INI::parsePercentToReal, NULL, offsetof( ProximityCaptureUpdateModuleData, m_recoverRate) }, + { "RequiredKindOf", KindOfMaskType::parseFromINI, NULL, offsetof(ProximityCaptureUpdateModuleData, m_requiredKindOf) }, + { "RequiresAllKindOfs", INI::parseBool, NULL, offsetof(ProximityCaptureUpdateModuleData, m_requiresAllKindOfs) }, + { "ForbiddenKindOf", KindOfMaskType::parseFromINI, NULL, offsetof(ProximityCaptureUpdateModuleData, m_forbiddenKindOf) }, + { "AllowAirborne", INI::parseBool, NULL, offsetof(ProximityCaptureUpdateModuleData, m_isCountAirborne) }, + { "UnitValueContentionDelta", INI::parseReal, NULL, offsetof(ProximityCaptureUpdateModuleData, m_unitValueContentionDelta) }, + { "UnitValueCountFactor", INI::parseReal, NULL, offsetof(ProximityCaptureUpdateModuleData, m_unitValueCountFactor) }, + //{ "UnitValueBuildCostFactor", INI::parseReal, NULL, offsetof(ProximityCaptureUpdateModuleData, m_unitValueBuildCostFactor) }, + { "ShowProgressBar", INI::parseBool, NULL, offsetof(ProximityCaptureUpdateModuleData, m_showProgressBar) }, + { "CapturePingInterval", INI::parseUnsignedShort, NULL, offsetof(ProximityCaptureUpdateModuleData, m_capturePingInterval) }, + { "ShowCaptureFlash", INI::parseBool, NULL, offsetof(ProximityCaptureUpdateModuleData, m_showCapturePingFlash) }, + { "PlayDefectorPingSound", INI::parseBool, NULL, offsetof(ProximityCaptureUpdateModuleData, m_playDefectorPingSound) }, + //{ "CapturePingSound", INI::parseAudioEventRTS, NULL, offsetof(ProximityCaptureUpdateModuleData, m_capturePingSound) }, + { "StartCaptureFX", INI::parseFXList, NULL, offsetof(ProximityCaptureUpdateModuleData, m_startCaptureFX) }, + { "StartRemoveFX", INI::parseFXList, NULL, offsetof(ProximityCaptureUpdateModuleData, m_startUncapFX) }, + { "FinishCaptureFX", INI::parseFXList, NULL, offsetof(ProximityCaptureUpdateModuleData, m_finishCaptureFX) }, + { "CapturePingFX", INI::parseFXList, NULL, offsetof(ProximityCaptureUpdateModuleData, m_capturePingFX) }, + { "CapturePingContestedFX", INI::parseFXList, NULL, offsetof(ProximityCaptureUpdateModuleData, m_capturePingContestedFX) }, + { "SkillPointsForCapture", INI::parseInt, NULL, offsetof(ProximityCaptureUpdateModuleData, m_skillPointsForCapture) }, + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); + } +}; + +//------------------------------------------------------------------------------------------------- +/** EnemyNear update */ +//------------------------------------------------------------------------------------------------- +class ProximityCaptureUpdate : public UpdateModule +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ProximityCaptureUpdate, "ProximityCaptureUpdate" ) + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( ProximityCaptureUpdate, ProximityCaptureUpdateModuleData ) + +public: + + ProximityCaptureUpdate( Thing *thing, const ModuleData* moduleData ); + // virtual destructor prototype provided by memory pool declaration + + virtual UpdateSleepTime update(); + + Bool getProgressBarInfo(Real& progress, Int& type, RGBAColorInt& color, RGBAColorInt& colorBG); + +protected: + + Int checkDominantPlayer( void ); + + void handleCaptureProgress(Int dominantPlayer); + void handleFlashEffects(Int dominantPlayer); + + Real getValueForUnit(const Object* obj) const; + +private: + + Int m_capturingPlayer; + Int m_dominantPlayerPrev; + Bool m_isContested; + Real m_captureProgress; + UnsignedInt m_lastTickFrame; + + UnsignedShort m_capturePingDelay; + + Real m_currentProgressRate; ///< current speed/direction for interpolation + + void startCapture(Int playerId); + void finishCapture(Int playerId); + void startUncap(Int playerId); + void finishUncap(Int playerId); + + Real getCaptureProgressInterp(); + +}; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index 6b38fad466e..e2be6907b99 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -123,6 +123,7 @@ #include "GameLogic/Module/DynamicGeometryInfoUpdate.h" #include "GameLogic/Module/DynamicShroudClearingRangeUpdate.h" #include "GameLogic/Module/EnemyNearUpdate.h" +#include "GameLogic/Module/ProximityCaptureUpdate.h" #include "GameLogic/Module/FireSpreadUpdate.h" #include "GameLogic/Module/FirestormDynamicGeometryInfoUpdate.h" #include "GameLogic/Module/FireWeaponUpdate.h" @@ -428,6 +429,7 @@ void ModuleFactory::init( void ) addModule( HordeUpdate ); addModule( ToppleUpdate ); addModule( EnemyNearUpdate ); + addModule( ProximityCaptureUpdate ); addModule( LifetimeUpdate ); addModule( RadiusDecalUpdate ); addModule( RadiusDecalBehavior ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/FXList.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/FXList.cpp index 041d2b0570f..1f3982336c9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/FXList.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/FXList.cpp @@ -665,10 +665,18 @@ class ParticleSystemFXNugget : public FXNugget { //old way: //newPos.z = TheTerrainLogic->getGrsoundHeight( newPos.x, newPos.y ) + 1;// The plus one prevents scissoring with terrain - //new way: now we allow bridges in the GroundHeight. + //newer way: include water + PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(&newPos); - newPos.z = TheTerrainLogic->getLayerHeight( newPos.x, newPos.y, layer ); + + if (Real waterZ = 0; TheGlobalData->m_heightAboveTerrainIncludesWater && layer == LAYER_GROUND && + TheTerrainLogic->isUnderwater(newPos.x, newPos.y, &waterZ)) { + newPos.z = waterZ; + } + else { + newPos.z = TheTerrainLogic->getLayerHeight(newPos.x, newPos.y, layer); + } } else newPos.z = primary->z + offset.z + m_height.getValue(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 292f15624bf..033b2355b10 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -102,6 +102,7 @@ #include "GameLogic/Module/UpdateModule.h" #include "GameLogic/Module/UpgradeModule.h" #include "GameLogic/Module/EnergyShieldBehavior.h" +#include "GameLogic/Module/ProximityCaptureUpdate.h" #include "GameLogic/Object.h" #include "GameLogic/PartitionManager.h" @@ -1548,7 +1549,6 @@ Bool Object::getProgressBarShowingInfo(bool selected, Real& progress, Int& type, return FALSE; // We put every case of Progress bars here. - // Maybe we should require a KindOf for performance? type = 0; // TODO color = { 255, 255, 255, 255 }; // Default = white @@ -1577,6 +1577,13 @@ Bool Object::getProgressBarShowingInfo(bool selected, Real& progress, Int& type, return true; } } + else if ((*u)->getModuleNameKey() == NAMEKEY("ProximityCaptureUpdate")) { + ProximityCaptureUpdate* pcu = (ProximityCaptureUpdate*)(*u); + if (pcu->getProgressBarInfo(progress, type, color, colorBG)) { + return true; + } + + } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AutoDepositUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AutoDepositUpdate.cpp index 5fe4519b75c..5ea93f121e8 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AutoDepositUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AutoDepositUpdate.cpp @@ -133,7 +133,7 @@ void AutoDepositUpdate::awardInitialCaptureBonus( Player *player ) moneyString.format( TheGameText->fetch( "GUI:AddCash" ), getAutoDepositUpdateModuleData()->m_initialCaptureBonus ); Coord3D pos; pos.set( getObject()->getPosition() ); - pos.z += 10.0f; //add a little z to make it show up above the unit. + pos.z += 10.0f + getAutoDepositUpdateModuleData()->m_textZOffset; //add a little z to make it show up above the unit. Color color = player->getPlayerColor() | GameMakeColor( 0, 0, 0, 230 ); TheInGameUI->addFloatingText( moneyString, &pos, color ); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProximityCaptureUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProximityCaptureUpdate.cpp new file mode 100644 index 00000000000..1f2b596ed73 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProximityCaptureUpdate.cpp @@ -0,0 +1,517 @@ +/* +** Command & Conquer Generals Zero Hour(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: ProximityCaptureUpdate.cpp /////////////////////////////////////////////////////////////////////////// +// Author: Matthew D. Campbell, December 2002 +// Desc: Reacts when an enemy is within range +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine + +#include "Common/PerfTimer.h" +#include "Common/ThingTemplate.h" +#include "Common/Xfer.h" +#include "GameClient/Drawable.h" +#include "GameClient/Eva.h" +#include "Common/Radar.h" +#include "Common/PlayerList.h" +#include "Common/Player.h" +#include "GameLogic/PartitionManager.h" +#include "GameLogic/Module/ProximityCaptureUpdate.h" +#include "GameLogic/Object.h" +#include "GameLogic/GameLogic.h" +#include "Common/GameUtility.h" +#include "Common/GameAudio.h" +#include "Common/AudioEventRTS.h" +#include "GameClient/FXList.h" +#include "Common/MiscAudio.h" +//#include "GameLogic/AI.h" +//#include "GameLogic/Module/AIUpdate.h" +#include "GameLogic/Module/ContainModule.h" +#include + +//------------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +ProximityCaptureUpdate::ProximityCaptureUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData ), + m_capturingPlayer(-1), + m_captureProgress(1.0), + m_isContested(false) +{ + // We always start off as captured by the original owner (including neutral) + m_capturingPlayer = getObject()->getControllingPlayer()->getPlayerIndex(); + + // bias a random amount so everyone doesn't spike at once + UnsignedInt initialDelay = GameLogicRandomValue(0, getProximityCaptureUpdateModuleData()->m_captureTickDelay); + setWakeFrame(getObject(), UPDATE_SLEEP(initialDelay)); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +ProximityCaptureUpdate::~ProximityCaptureUpdate( void ) +{ +} + +//----------------- +// Helper functions +// ---------------- +Bool arePlayersAllied(Int player1, Int player2) { + if (player1 == player2 || player1 < 0 || player1 >= MAX_PLAYER_COUNT || player2 < 0 || player2 >= MAX_PLAYER_COUNT) + return FALSE; + + return ThePlayerList->getNthPlayer(player1)->getRelationship(ThePlayerList->getNthPlayer(player2)->getDefaultTeam()) == ALLIES; +} + + +//------------------------------------------------------------------------------------------------- +struct PlayerResult { + Int playerId = -1; + Real totalValue = 0.0f; + + bool operator<(const PlayerResult& other) const { + return totalValue > other.totalValue; // Descending sort + } +}; +//------------------------------------------------------------------------------------------------- +// Look around us for units +//------------------------------------------------------------------------------------------------- +Int ProximityCaptureUpdate::checkDominantPlayer( void ) +{ + Object* me = getObject(); + const ProximityCaptureUpdateModuleData* data = getProximityCaptureUpdateModuleData(); + + PartitionFilterSameMapStatus filterMapStatus(me); + PartitionFilterAlive filterAlive; + //PartitionFilterRejectByKindOf filterRejectKindof(data->m_forbiddenKindOf); + //Note: we don't filter for accepted KindOfs her because we want to differentiate between ALL and ANY match cases + + PartitionFilter* filters[] = { &filterAlive, &filterMapStatus, NULL }; + + // scan objects in our region + ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(me->getPosition(), + data->m_captureRadius, + FROM_CENTER_2D, + filters); + + MemoryPoolObjectHolder hold(iter); + + // Get total value per player + //constexpr size_t SIZE = MAX_PLAYER_COUNT; + std::array playerTotals{}; + playerTotals.fill(0.0f); + + for (Object* currentObj = iter->first(); currentObj != NULL; currentObj = iter->next()) + { + bool match = FALSE; + if (!data->m_requiresAllKindOfs) + match = currentObj->isAnyKindOf(data->m_requiredKindOf) && !currentObj->isAnyKindOf(data->m_forbiddenKindOf); + else + match = currentObj->isKindOfMulti(data->m_requiredKindOf, data->m_forbiddenKindOf); + + match &= (data->m_isCountAirborne || !currentObj->isAirborneTarget()); + + match &= (currentObj != me); + match &= (!currentObj->isNeutralControlled()); + + if (!match) + continue; + + PlayerIndex pId = currentObj->getControllingPlayer()->getPlayerIndex(); + if (pId > PLAYER_INDEX_INVALID) { + playerTotals[pId] += getValueForUnit(currentObj); + } + } + + // Find player with highest influence + PlayerResult bestResult; + PlayerResult secondBestResult; + //result.totalValue = -std::numeric_limits::infinity(); + + for (int i = 0; i < MAX_PLAYER_COUNT; i++) { + + if (playerTotals[i] > bestResult.totalValue) { + + if (bestResult.playerId != -1) { + secondBestResult.totalValue = bestResult.totalValue; + secondBestResult.playerId = bestResult.playerId; + } + + bestResult.totalValue = playerTotals[i]; + bestResult.playerId = i; + } + else if (playerTotals[i] > secondBestResult.totalValue) { + secondBestResult.totalValue = playerTotals[i]; + secondBestResult.playerId = i; + } + } + + if (bestResult.totalValue <= 0) { + // No units at all. No player dominant, but not contested. + return -1; + } + + if (!arePlayersAllied(bestResult.playerId, secondBestResult.playerId)) { + // DEBUG_LOG((">>> ProximityCaptureUpdate:: Is Contested? - best = %f, second = %f", bestResult.totalValue, secondBestResult.totalValue)); + m_isContested = bestResult.totalValue <= (secondBestResult.totalValue + data->m_unitValueContentionDelta); + } + + if (!m_isContested) { + // Is the current capturing player an ally? + if (arePlayersAllied(bestResult.playerId, m_capturingPlayer)) { + if (playerTotals[m_capturingPlayer] > 0) { // Ally still has units in radius + return m_capturingPlayer; + } + } + return bestResult.playerId; + } + else { + return -1; + } + +} + +//------------------------------------------------------------------------------------------------- + +Real ProximityCaptureUpdate::getValueForUnit(const Object* obj) const +{ + const ProximityCaptureUpdateModuleData* data = getProximityCaptureUpdateModuleData(); + Real value = data->m_unitValueCountFactor; + + //if (data->m_unitValueBuildCostFactor > 0.0f) { + // value += obj->getTemplate()->calcCostToBuild(); + //} + + return value; +} + + +//------------------------------------------------------------------------------------------------- +void ProximityCaptureUpdate::handleCaptureProgress(Int dominantPlayer) +{ + + Real captureProgressPrev = m_captureProgress; + + if (m_isContested) { + // No update while contested + return; + } + + const ProximityCaptureUpdateModuleData* data = getProximityCaptureUpdateModuleData(); + Object* me = getObject(); + PlayerIndex owningPlayer = me->getControllingPlayer()->getPlayerIndex(); + + // If players are allied, we treat it as no dominant player + if (arePlayersAllied(dominantPlayer, m_capturingPlayer)) { + dominantPlayer = -1; + } + + DEBUG_ASSERTCRASH(m_capturingPlayer != -1, ("The capturing player should always have a valid index")); + + // Currently fully captured + if (m_captureProgress >= 1.0f) { + // No capture process + if (dominantPlayer == -1 || dominantPlayer == owningPlayer) + return; + + if (dominantPlayer != m_capturingPlayer) { + startUncap(dominantPlayer); + } + } + + if (dominantPlayer == m_capturingPlayer) + { // Capture is in progress + m_captureProgress += data->m_captureRate; + } + else if (dominantPlayer == -1 && m_capturingPlayer == owningPlayer) + { // Recover capture status + if (data->m_recoverRate >= 0.0f) + m_captureProgress += data->m_recoverRate; + else + m_captureProgress += data->m_captureRate; + } + else + { // Uncap is in progress + if (me->isNeutralControlled() && data->m_uncapRateNeutral > 0.0f) { + m_captureProgress -= data->m_uncapRateNeutral; + } + else { + m_captureProgress -= data->m_uncapRate; + } + } + + if (m_captureProgress <= 0) { + //DEBUG_ASSERTCRASH(dominantPlayer != -1, ("Can't uncap without a dominant player.")); + m_captureProgress = 0; + + // Finished uncapping, reset capturing player + if (dominantPlayer != -1) { + finishUncap(dominantPlayer); + // Change ownership to neutral + m_capturingPlayer = dominantPlayer; + startCapture(m_capturingPlayer); + } + else + { // abort capture, back to the owner (neutral) + m_capturingPlayer = owningPlayer; + } + } + else if (m_captureProgress >= 1) + { + m_captureProgress = 1.0; + if (m_capturingPlayer == owningPlayer) { + // finished recovering, nothing to do here really. + } + else { + // finished capturing, change ownership + finishCapture(m_capturingPlayer); + } + } + + m_currentProgressRate = m_captureProgress - captureProgressPrev; +} +// ------------------------------------------------------------------------------------------------ +// A player starts capturing (i.e. filling up his progress) +void ProximityCaptureUpdate::startCapture(Int playerId) +{ + DEBUG_ASSERTLOG(getObject()->isNeutralControlled(), ("ProximityCaptureUpdate::startCapture: Warning, current owner should be neutral!")); + + FXList::doFXObj(getProximityCaptureUpdateModuleData()->m_startCaptureFX, getObject()); + + m_capturePingDelay = 0; +} +// ------------------------------------------------------------------------------------------------ +// A player has finished capturing (i.e. got ownership) +void ProximityCaptureUpdate::finishCapture(Int playerId) +{ + //TODO: Booby trap support maybe later + // if (getObject()->checkAndDetonateBoobyTrap(getObject())) // We need to store at least one unit of the dominant player ?! + + Object* me = getObject(); + const ProximityCaptureUpdateModuleData* data = getProximityCaptureUpdateModuleData(); + + // Just in case we are capturing a building which is already garrisoned by other + ContainModuleInterface* contain = me->getContain(); + if (contain && contain->isGarrisonable()) + { + contain->removeAllContained(TRUE); + } + + DEBUG_ASSERTLOG(me->isNeutralControlled(), ("ProximityCaptureUpdate::finishCapture: Warning, current owner should be neutral!")); + + FXList::doFXObj(data->m_finishCaptureFX, getObject()); + + Player* newOwner = ThePlayerList->getNthPlayer(playerId); + if (newOwner) { + me->defect(newOwner->getDefaultTeam(), 1); // one frame of flash! + + newOwner->getAcademyStats()->recordBuildingCapture(); + + if (data->m_skillPointsForCapture > 0) + { + newOwner->addSkillPoints(data->m_skillPointsForCapture); + } + + if (newOwner == rts::getObservedOrLocalPlayer()) + TheRadar->tryEvent(RADAR_EVENT_INFORMATION, me->getPosition()); + } +} +// ------------------------------------------------------------------------------------------------ +// A player starts removing the progress of another +void ProximityCaptureUpdate::startUncap(Int playerId) { + Object* me = getObject(); + //Warn the victim so he might have a chance to react! + if (me && me->isLocallyViewed()) + { + TheEva->setShouldPlay(EVA_BuildingBeingStolen); + } + TheRadar->tryInfiltrationEvent(me); + + FXList::doFXObj(getProximityCaptureUpdateModuleData()->m_startUncapFX, getObject()); + + m_capturePingDelay = 0; +} +// ------------------------------------------------------------------------------------------------ +// A player has finished neutralizing +void ProximityCaptureUpdate::finishUncap(Int playerId) { + Object* me = getObject(); + + //Play the "building stolen" EVA event if the local player is the victim! + if (me && me->isLocallyViewed()) + { + TheEva->setShouldPlay(EVA_BuildingStolen); + } + + me->defect(ThePlayerList->getNeutralPlayer()->getDefaultTeam(), 1); // one frame of flash! +} +// ------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------- +UpdateSleepTime ProximityCaptureUpdate::update() +{ + const ProximityCaptureUpdateModuleData* data = getProximityCaptureUpdateModuleData(); + + Int dominantPlayer = checkDominantPlayer(); + + // DEBUG_LOG((">>> ProximityCaptureUpdate::update - dominantPlayer = %d", dominantPlayer)); + + handleCaptureProgress(dominantPlayer); + + handleFlashEffects(dominantPlayer); + + m_lastTickFrame = TheGameLogic->getFrame(); + + //m_dominantPlayerPrev = dominantPlayer; + + return UPDATE_SLEEP(data->m_captureTickDelay); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +Bool ProximityCaptureUpdate::getProgressBarInfo(Real& progress, Int& type, RGBAColorInt& color, RGBAColorInt& colorBG) +{ + const ProximityCaptureUpdateModuleData* data = getProximityCaptureUpdateModuleData(); + if (!data->m_showProgressBar) + return false; + + if (m_captureProgress >= 1.0) + return false; + + progress = getCaptureProgressInterp(); + + if (m_isContested) + colorBG = { 255, 255, 0, 255 }; + else + colorBG = { 0, 0, 0, 255 }; + + RGBColor col; + col.setFromInt(ThePlayerList->getNthPlayer(m_capturingPlayer)->getPlayerColor()); + + color = { (byte)(col.red * 255.0),(byte)(col.green * 255.0), (byte)(col.blue * 255.0), 255 }; + + + return true; +} + + +//------------------------------------------------------------------------------------------------- +void ProximityCaptureUpdate::handleFlashEffects(Int dominantPlayer) +{ + if (m_capturePingDelay-- > 0) + return; + + Object* me = getObject(); + Drawable* draw = me->getDrawable(); + + if (m_isContested) { + FXList::doFXObj(getProximityCaptureUpdateModuleData()->m_capturePingContestedFX, getObject()); + } else if (m_captureProgress < 1.0 && dominantPlayer != -1 && dominantPlayer != me->getControllingPlayer()->getPlayerIndex() && dominantPlayer != ThePlayerList->getNeutralPlayer()->getPlayerIndex()) { + if (draw) { + RGBColor myHouseColor; + myHouseColor.setFromInt(ThePlayerList->getNthPlayer(dominantPlayer)->getPlayerColor()); + + Real saturation = TheGlobalData->m_selectionFlashSaturationFactor; + draw->saturateRGB(myHouseColor, saturation); + draw->flashAsSelected(&myHouseColor); //In MY house color, not his! + + } + if (getProximityCaptureUpdateModuleData()->m_playDefectorPingSound) { + AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound; + defectorTimerSound.setObjectID(me->getID()); + TheAudio->addAudioEvent(&defectorTimerSound); + } + + FXList::doFXObj(getProximityCaptureUpdateModuleData()->m_capturePingFX, getObject()); + + } + + m_capturePingDelay = getProximityCaptureUpdateModuleData()->m_capturePingInterval; +} + +// ------------------------------------------------------------------------------------------------ +Real ProximityCaptureUpdate::getCaptureProgressInterp() +{ + if (m_captureProgress > 1.0) + return 1.0; + if (m_captureProgress < 0.0) + return 0.0; + + if (m_isContested || getProximityCaptureUpdateModuleData()->m_captureTickDelay == 0) + return m_captureProgress; + + UnsignedInt frameDiff = TheGameLogic->getFrame() - m_lastTickFrame; + Real progDiff = INT_TO_REAL(frameDiff) / INT_TO_REAL(getProximityCaptureUpdateModuleData()->m_captureTickDelay); + + Real frameShift = (0.5 / INT_TO_REAL(getProximityCaptureUpdateModuleData()->m_captureTickDelay)) * m_currentProgressRate; // half a tick offset + + return MAX(0.0, MIN(m_captureProgress + m_currentProgressRate * progDiff - frameShift, 1.0)); +} + + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void ProximityCaptureUpdate::crc( Xfer *xfer ) +{ + + // extend base class + UpdateModule::crc( xfer ); + +} + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ +// ------------------------------------------------------------------------------------------------ +void ProximityCaptureUpdate::xfer( Xfer *xfer ) +{ + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion( &version, currentVersion ); + + // extend base class + UpdateModule::xfer( xfer ); + + xfer->xferInt(&m_capturingPlayer); + xfer->xferBool(&m_isContested); + xfer->xferReal(&m_captureProgress); + xfer->xferReal(&m_currentProgressRate); + xfer->xferUnsignedInt(&m_lastTickFrame); + xfer->xferUnsignedShort(&m_capturePingDelay); + +} + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void ProximityCaptureUpdate::loadPostProcess( void ) +{ + + // extend base class + UpdateModule::loadPostProcess(); + +}