From 36557eb8fdcf6dd09cdd8fbb5822a7b499890e00 Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 24 Aug 2025 15:46:19 +0200 Subject: [PATCH 1/3] Basic Functionality Working --- Core/GameEngine/CMakeLists.txt | 2 + .../System/GameMemoryInitPools_Generals.inl | 1 + .../System/GameMemoryInitPools_GeneralsMD.inl | 1 + GeneralsMD/Code/GameEngine/CMakeLists.txt | 50 +- .../Include/GameLogic/Module/ContainModule.h | 5 + .../GameLogic/Module/MultiAddOnContain.h | 162 ++++ .../Include/GameLogic/Module/OpenContain.h | 4 + .../Code/GameEngine/Source/Common/INI/INI.cpp | 2 +- .../Source/Common/Thing/ModuleFactory.cpp | 2 + .../Object/Behavior/GrantStealthBehavior.cpp | 8 + .../GameLogic/Object/Contain/HelixContain.cpp | 2 +- .../Object/Contain/MultiAddOnContain.cpp | 761 ++++++++++++++++++ .../Source/GameLogic/Object/Object.cpp | 15 + .../GameLogic/Object/Update/AIUpdate.cpp | 9 + .../Update/AIUpdate/ChinookAIUpdate.cpp | 65 +- .../Update/AIUpdate/TransportAIUpdate.cpp | 203 +++-- .../Source/GameLogic/Object/WeaponSet.cpp | 33 +- .../Module/W3DOverlordAircraftDraw.h | 2 + .../GameClient/Module/W3DOverlordTankDraw.h | 2 + .../GameClient/Module/W3DOverlordTruckDraw.h | 2 + .../Drawable/Draw/W3DDependencyModelDraw.cpp | 16 +- .../Drawable/Draw/W3DOverlordAircraftDraw.cpp | 62 +- .../Drawable/Draw/W3DOverlordTankDraw.cpp | 52 +- .../Drawable/Draw/W3DOverlordTruckDraw.cpp | 54 +- 24 files changed, 1345 insertions(+), 170 deletions(-) create mode 100644 GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h create mode 100644 GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index 494d544f73d..9f495b545f0 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -339,6 +339,7 @@ set(GAMEENGINE_SRC # Include/GameLogic/Module/HeightDieUpdate.h # Include/GameLogic/Module/HelicopterSlowDeathUpdate.h # Include/GameLogic/Module/HelixContain.h +# Include/GameLogic/Module/MultiAddOnContain.h # Include/GameLogic/Module/HighlanderBody.h # Include/GameLogic/Module/HijackerUpdate.h # Include/GameLogic/Module/HiveStructureBody.h @@ -905,6 +906,7 @@ set(GAMEENGINE_SRC # Source/GameLogic/Object/Contain/GarrisonContain.cpp # Source/GameLogic/Object/Contain/HealContain.cpp # Source/GameLogic/Object/Contain/HelixContain.cpp +# Source/GameLogic/Object/Contain/MultiAddOnContain.cpp # Source/GameLogic/Object/Contain/InternetHackContain.cpp # Source/GameLogic/Object/Contain/MobNexusContain.cpp # Source/GameLogic/Object/Contain/OpenContain.cpp diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl index b049809df14..31b24b6551b 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl @@ -196,6 +196,7 @@ static PoolSizeRec PoolSizes[] = { "OverchargeBehavior", 32, 32 }, { "OverlordContain", 32, 32 }, { "HelixContain", 32, 32 }, + { "MultiAddOnContain", 64, 32 }, { "ParachuteContain", 128, 32 }, { "PhysicsBehavior", 600, 32 }, { "PoisonedBehavior", 512, 64 }, diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index c00ec752b39..49c4f6fe416 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -200,6 +200,7 @@ static PoolSizeRec PoolSizes[] = { "OverchargeBehavior", 32, 32 }, { "OverlordContain", 32, 32 }, { "HelixContain", 32, 32 }, + { "MultiAddOnContain", 64, 32 }, { "ParachuteContain", 128, 32 }, { "PhysicsBehavior", 600, 32 }, { "PoisonedBehavior", 512, 64 }, diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index 36e3f897c9e..6ef78dbac79 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -21,13 +21,13 @@ set(GAMEENGINE_SRC Include/Common/CDManager.h Include/Common/ClientUpdateModule.h Include/Common/CommandLine.h -# Include/Common/crc.h -# Include/Common/CRCDebug.h +# Include/Common/crc.h +# Include/Common/CRCDebug.h Include/Common/CriticalSection.h Include/Common/CustomMatchPreferences.h Include/Common/DamageFX.h Include/Common/DataChunk.h -# Include/Common/Debug.h +# Include/Common/Debug.h Include/Common/Dict.h Include/Common/Directory.h Include/Common/DisabledTypes.h @@ -45,7 +45,7 @@ set(GAMEENGINE_SRC # Include/Common/GameDefines.h Include/Common/GameEngine.h Include/Common/GameLOD.h -# Include/Common/GameMemory.h +# Include/Common/GameMemory.h # Include/Common/GameMusic.h # Include/Common/GameSounds.h Include/Common/GameSpyMiscPreferences.h @@ -92,7 +92,7 @@ set(GAMEENGINE_SRC Include/Common/QuotedPrintable.h Include/Common/Radar.h # Include/Common/RAMFile.h -# Include/Common/RandomValue.h +# Include/Common/RandomValue.h Include/Common/Recorder.h # Include/Common/ReplaySimulation.h Include/Common/Registry.h @@ -142,7 +142,7 @@ set(GAMEENGINE_SRC Include/GameClient/CDCheck.h Include/GameClient/ChallengeGenerals.h Include/GameClient/ClientInstance.h -# Include/GameClient/ClientRandomValue.h +# Include/GameClient/ClientRandomValue.h Include/GameClient/Color.h Include/GameClient/CommandXlat.h Include/GameClient/ControlBar.h @@ -224,12 +224,12 @@ set(GAMEENGINE_SRC Include/GameClient/Statistics.h Include/GameClient/TerrainRoads.h Include/GameClient/TerrainVisual.h - Include/GameClient/TintStatus.h -# Include/GameClient/VideoPlayer.h + Include/GameClient/TintStatus.h +# Include/GameClient/VideoPlayer.h Include/GameClient/View.h Include/GameClient/Water.h Include/GameClient/WindowLayout.h -# Include/GameClient/WindowVideoManager.h +# Include/GameClient/WindowVideoManager.h Include/GameClient/WindowXlat.h Include/GameClient/WinInstanceData.h Include/GameLogic/AI.h @@ -253,7 +253,7 @@ set(GAMEENGINE_SRC Include/GameLogic/GhostObject.h Include/GameLogic/Locomotor.h Include/GameLogic/LocomotorSet.h -# Include/GameLogic/LogicRandomValue.h +# Include/GameLogic/LogicRandomValue.h Include/GameLogic/Module/ActiveBody.h Include/GameLogic/Module/ActiveShroudUpgrade.h Include/GameLogic/Module/AIUpdate.h @@ -344,6 +344,7 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/HeightDieUpdate.h Include/GameLogic/Module/HelicopterSlowDeathUpdate.h Include/GameLogic/Module/HelixContain.h + Include/GameLogic/Module/MultiAddOnContain.h Include/GameLogic/Module/HighlanderBody.h Include/GameLogic/Module/HijackerUpdate.h Include/GameLogic/Module/HiveStructureBody.h @@ -578,8 +579,8 @@ set(GAMEENGINE_SRC Source/Common/Bezier/BezierSegment.cpp Source/Common/BitFlags.cpp Source/Common/CommandLine.cpp -# Source/Common/crc.cpp -# Source/Common/CRCDebug.cpp +# Source/Common/crc.cpp +# Source/Common/CRCDebug.cpp Source/Common/DamageFX.cpp Source/Common/Dict.cpp Source/Common/DiscreteCircle.cpp @@ -611,7 +612,7 @@ set(GAMEENGINE_SRC Source/Common/INI/INITerrainBridge.cpp Source/Common/INI/INITerrainRoad.cpp Source/Common/INI/INIUpgrade.cpp -# Source/Common/INI/INIVideo.cpp +# Source/Common/INI/INIVideo.cpp Source/Common/INI/INIWater.cpp Source/Common/INI/INIWeapon.cpp Source/Common/INI/INIWebpageURL.cpp @@ -622,7 +623,7 @@ set(GAMEENGINE_SRC Source/Common/NameKeyGenerator.cpp Source/Common/PartitionSolver.cpp Source/Common/PerfTimer.cpp -# Source/Common/RandomValue.cpp +# Source/Common/RandomValue.cpp Source/Common/Recorder.cpp # Source/Common/ReplaySimulation.cpp Source/Common/RTS/AcademyStats.cpp @@ -651,7 +652,7 @@ set(GAMEENGINE_SRC Source/Common/System/CDManager.cpp Source/Common/System/CriticalSection.cpp Source/Common/System/DataChunk.cpp -# Source/Common/System/Debug.cpp +# Source/Common/System/Debug.cpp Source/Common/System/Directory.cpp Source/Common/System/DisabledTypes.cpp Source/Common/System/encrypt.cpp @@ -806,7 +807,7 @@ set(GAMEENGINE_SRC Source/GameClient/GUI/Shell/Shell.cpp Source/GameClient/GUI/Shell/ShellMenuScheme.cpp Source/GameClient/GUI/WindowLayout.cpp -# Source/GameClient/GUI/WindowVideoManager.cpp +# Source/GameClient/GUI/WindowVideoManager.cpp Source/GameClient/GUI/WinInstanceData.cpp Source/GameClient/InGameUI.cpp Source/GameClient/Input/Keyboard.cpp @@ -838,8 +839,8 @@ set(GAMEENGINE_SRC 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/VideoPlayer.cpp +# Source/GameClient/VideoStream.cpp Source/GameClient/View.cpp Source/GameClient/Water.cpp Source/GameLogic/AI/AI.cpp @@ -924,6 +925,7 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Contain/GarrisonContain.cpp Source/GameLogic/Object/Contain/HealContain.cpp Source/GameLogic/Object/Contain/HelixContain.cpp + Source/GameLogic/Object/Contain/MultiAddOnContain.cpp Source/GameLogic/Object/Contain/InternetHackContain.cpp Source/GameLogic/Object/Contain/MobNexusContain.cpp Source/GameLogic/Object/Contain/OpenContain.cpp @@ -1167,16 +1169,16 @@ set(GAMEENGINE_SRC if(RTS_GAMEMEMORY_ENABLE) # Uses the original Game Memory implementation. list(APPEND GAMEENGINE_SRC -# Source/Common/System/GameMemory.cpp -# Source/Common/System/GameMemoryInitDMA_GeneralsMD.inl -# Source/Common/System/GameMemoryInitPools_GeneralsMD.inl -# Source/Common/System/MemoryInit.cpp +# Source/Common/System/GameMemory.cpp +# Source/Common/System/GameMemoryInitDMA_GeneralsMD.inl +# Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +# Source/Common/System/MemoryInit.cpp ) else() # Uses the null implementation when disabled. list(APPEND GAMEENGINE_SRC -# Source/Common/System/GameMemoryNull.cpp -# Include/Common/GameMemoryNull.h +# Source/Common/System/GameMemoryNull.cpp +# Include/Common/GameMemoryNull.h ) endif() diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h index 3b172314756..4c2e73235c8 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h @@ -208,6 +208,11 @@ class ContainModuleInterface return true; } + + // AW: New additions for MultiAddonContain only + virtual short getRiderSlot(ObjectID riderID) const = 0; // get the slot occupied by the object. + virtual short getPortableSlot(ObjectID portableID) const = 0; // get the slot occupied by the object. + virtual const ContainedItemsList* getAddOnList() const = 0; }; //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h new file mode 100644 index 00000000000..12f91e651fa --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h @@ -0,0 +1,162 @@ +/* +** 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: MultiAddOnContain.h //////////////////////////////////////////////////////////////////////// +// Author: Graham Smallwood, September, 2002 +// Desc: Contain module that acts as transport normally, but when full it redirects queries to the first passenger +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef __MULTIADDON_CONTAIN_H_ +#define __MULTIADDON_CONTAIN_H_ + +// USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// +#include "GameLogic/Module/TransportContain.h" +#include "GameLogic/GameLogic.h" + + +typedef std::vector TemplateNameList; +typedef std::vector::const_iterator TemplateNameIterator; + +typedef std::vector VecObjectID; +typedef VecObjectID::iterator VecObjectIDIt; + +typedef std::vector VecObjectPtr; +typedef VecObjectPtr::iterator VecObjectPtrIt; + +typedef std::map< NameKeyType, AsciiString> AddOnEntryMap; + +//------------------------------------------------------------------------------------------------- +class MultiAddOnContainModuleData : public TransportContainModuleData +{ +public: + + MultiAddOnContainModuleData(); + + TemplateNameList m_payloadTemplateNameData; + Bool m_drawPips; // TODO: Move this to generic transportcontain + + AddOnEntryMap m_addOnEntries; + + AsciiString m_addOnBoneName; + + + static void buildFieldParse(MultiIniFieldParse& p); + static void parseAddOnEntry(INI* ini, void* instance, void* store, const void* /*userData*/); +}; + +//------------------------------------------------------------------------------------------------- +class MultiAddOnContain : public TransportContain +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(MultiAddOnContain, "MultiAddOnContain") + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(MultiAddOnContain, MultiAddOnContainModuleData) + + virtual void onBodyDamageStateChange( const DamageInfo* damageInfo, + BodyDamageType oldState, + BodyDamageType newState); ///< state change callback +public: + + MultiAddOnContain(Thing* thing, const ModuleData* moduleData); + // virtual destructor prototype provided by memory pool declaration + + virtual OpenContain* asOpenContain() { return this; } ///< treat as open container + virtual Bool isHealContain() const { return false; } ///< true when container only contains units while healing (not a transport!) + virtual Bool isTunnelContain() const { return FALSE; } + virtual Bool isImmuneToClearBuildingAttacks() const { return true; } + virtual Bool isSpecialOverlordStyleContainer() const { return TRUE; } + + virtual void onDie(const DamageInfo* damageInfo); ///< the die callback + virtual void onDelete(void); ///< Last possible moment cleanup + virtual void onCapture(Player* oldOwner, Player* newOwner); + virtual void onObjectCreated(); + virtual void onContaining(Object* obj, Bool wasSelected); + virtual void onRemoving(Object* obj); + //virtual UpdateSleepTime update(); ///< called once per frame + + virtual void onSelling(); + + //virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; + //virtual void addToContain(Object* obj); ///< add 'obj' to contain list + //virtual void addToContainList(Object* obj); ///< The part of AddToContain that inheritors can override (Can't do whole thing because of all the private stuff involved) + //virtual void removeFromContain(Object* obj, Bool exposeStealthUnits = FALSE); ///< remove 'obj' from contain list + //virtual void removeAllContained( Bool exposeStealthUnits = FALSE ); ///< remove all objects on contain list + virtual Bool isEnclosingContainerFor(const Object* obj) const; ///< Does this type of Contain Visibly enclose its contents? + virtual Bool isPassengerAllowedToFire(ObjectID id = INVALID_ID) const; ///< Hey, can I shoot out of this container? + + // Friend for our Draw module only. + //virtual const Object* friend_getRider() const; ///< Damn. The draw order dependency bug for riders means that our draw module needs to cheat to get around it. + + ///< if my object gets selected, then my visible passengers should, too + ///< this gets called from + virtual void clientVisibleContainedFlashAsSelected(); + + virtual void redeployOccupants(); + + virtual Bool getContainerPipsToShow(Int& numTotal, Int& numFull) + { + if (getMultiAddOnContainModuleData()->m_drawPips == FALSE) + { + return FALSE; + } + + return ContainModuleInterface::getContainerPipsToShow(numTotal, numFull); + } + + virtual void createPayload(); + + virtual short getRiderSlot(ObjectID riderID) const; + virtual short getPortableSlot(ObjectID riderID) const; + virtual const ContainedItemsList* getAddOnList() const { return &m_addOnList; } + +private: + void parseAddOnEntry(INI* ini, void* instance, void* store, const void* /*userData*/); + + //VecObjectPtr& getPortableStructures(void) const; + + struct AddOnSlotData + { + ObjectID occupantID; + ObjectID portableID; + UnsignedShort slot; + }; + + std::vector m_addOnSlots; + ContainedItemsList m_addOnList; + //bool m_addOnList_valid; + + //std::vector m_addOnPoints; + //Bool m_noAddOnPointsInArt; + + bool createAddOnForRider(Object* obj); + bool removeAddOnForRider(Object* obj); + + void redeployAddOns(); + void putObjAtSlot(Object* obj, short slot); + +}; + +#endif + diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h index 9ea2adb8b06..c037171162d 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h @@ -255,6 +255,10 @@ class OpenContain : public UpdateModule, // exists primarily for TransportContain to override virtual void killRidersWhoAreNotFreeToExit() { } + virtual short getRiderSlot(ObjectID riderID) const { return -1; } + virtual short getPortableSlot(ObjectID portableID) const { return -1; } + virtual const ContainedItemsList* getAddOnList() const { return NULL; } + void pruneDeadWanters(); ContainedItemsList m_containList; ///< the list of contained objects diff --git a/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp b/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp index 7d80d6daae5..e11ba37c2df 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/INI/INI.cpp @@ -1947,7 +1947,7 @@ void INI::parseDeathTypeFlags(INI* ini, void* /*instance*/, void* store, const v if (TheGlobalData) { flags &= ~TheGlobalData->m_defaultExcludedDeathTypes; - DEBUG_LOG(("INI::parseDeathTypeFlags - flags = %X\n", flags)); + // DEBUG_LOG(("INI::parseDeathTypeFlags - flags = %X\n", flags)); } else { DEBUG_LOG(("INI::parseDeathTypeFlags - TheGlobalData is NULL\n")); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index 681b17138da..3db7e5df038 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -70,6 +70,7 @@ #include "GameLogic/Module/TunnelContain.h" #include "GameLogic/Module/OverlordContain.h" #include "GameLogic/Module/HelixContain.h" +#include "GameLogic/Module/MultiAddOnContain.h" #include "GameLogic/Module/ParachuteContain.h" #ifdef ALLOW_SURRENDER #include "GameLogic/Module/POWTruckBehavior.h" @@ -361,6 +362,7 @@ void ModuleFactory::init( void ) addModule( TunnelContain ); addModule( OverlordContain ); addModule( HelixContain ); + addModule( MultiAddOnContain ); addModule( ParachuteContain ); #ifdef ALLOW_SURRENDER addModule( POWTruckBehavior ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp index 1e023454b7c..967aa8f9ecf 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp @@ -83,6 +83,14 @@ static void checkForGrantStealth( Object *testObj, void *userData ) { listToAddTo->push_back(rider); } + + // same for multi addons + const std::list* addOnList = testObj->getContain()->getAddOnList(); + if (addOnList) { + for (Object* addOn : *addOnList) { + listToAddTo->push_back(addOn); + } + } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp index 5d5057d661b..8536f9c7680 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp @@ -418,7 +418,7 @@ void HelixContain::onRemoving( Object *obj ) TransportContain::onRemoving(obj); // give the object back a regular weapon - obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED ); + //obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED ); obj->clearDisabled( DISABLED_HELD ); } // end onRemoving diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp new file mode 100644 index 00000000000..1943304e1bf --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp @@ -0,0 +1,761 @@ +/* +** 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: MultiAddOnContain.cpp //////////////////////////////////////////////////////////////////////// +// Author: Mark Lorenzen, April, 2003 +// +// Desc: +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine +#include "Common/Player.h" +#include "Common/Xfer.h" +#include "Common/ThingTemplate.h" +#include "Common/ThingFactory.h" +#include "GameClient/ControlBar.h" +#include "GameClient/Drawable.h" +#include "GameLogic/Module/BodyModule.h" +#include "GameLogic/Module/MultiAddOnContain.h" +#include "GameLogic/Object.h" +#include "GameLogic/ObjectCreationList.h" +#include "GameLogic/PartitionManager.h" +#include "GameLogic/GameLogic.h" +#include "GameLogic/Weapon.h" + + + + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +MultiAddOnContainModuleData::MultiAddOnContainModuleData() +{ + // m_initialPayload.count = 0; + m_drawPips = TRUE; + m_addOnEntries.clear(); + m_addOnBoneName = "FIREPOINT"; +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContainModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + TransportContainModuleData::buildFieldParse(p); + + static const FieldParse dataFieldParse[] = + { + { "PayloadTemplateName", INI::parseAsciiStringVectorAppend, NULL, offsetof(MultiAddOnContainModuleData, m_payloadTemplateNameData) }, + { "ShouldDrawPips", INI::parseBool, NULL, offsetof(MultiAddOnContainModuleData, m_drawPips) }, + { "AddOnBoneName", INI::parseAsciiString, NULL, offsetof(MultiAddOnContainModuleData, m_addOnBoneName) }, + { "AddOnEntry", parseAddOnEntry, NULL, 0 }, + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContainModuleData::parseAddOnEntry( INI* ini, void *instance, void *store, const void* /*userData*/ ) +{ + DEBUG_LOG((">>> MultiAddOnContainModuleData::parseAddOnEntry 0")); + MultiAddOnContainModuleData* self = (MultiAddOnContainModuleData*)instance; + const char* riderName = ini->getNextToken(); + const char* addOnName = ini->getNextToken(); + DEBUG_LOG((">>> MultiAddOnContainModuleData::parseAddOnEntry 1")); + self->m_addOnEntries.insert_or_assign(NAMEKEY(riderName), AsciiString(addOnName)); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +MultiAddOnContain::MultiAddOnContain(Thing* thing, const ModuleData* moduleData) : + TransportContain(thing, moduleData) +{ + DEBUG_LOG((">>> MultiAddOnContain::INIT 0")); + + m_payloadCreated = FALSE; + + m_addOnList.clear(); + + const MultiAddOnContainModuleData* data = getMultiAddOnContainModuleData(); + + // why slotCapacity and not containMax? + // What about units requiring multiple slots? + // Should we base it off GarrisonContain instead? + for (UnsignedInt i = 0; i < data->m_slotCapacity; i++) { + AddOnSlotData addOn; + addOn.occupantID = INVALID_ID; + addOn.portableID = INVALID_ID; + addOn.slot = i; + m_addOnSlots.push_back(addOn); + } + + DEBUG_LOG((">>> MultiAddOnContain::INIT 1, m_addOnSlots.size() = %d", m_addOnSlots.size())); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +MultiAddOnContain::~MultiAddOnContain(void) +{ + +} + + +void MultiAddOnContain::onObjectCreated(void) +{ + MultiAddOnContain::createPayload(); +} + + + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +//UpdateSleepTime MultiAddOnContain::update() +//{ +// //DEBUG_LOG((">>> MultiAddOnContain::update 0, m_addOnList.size() = %d", m_addOnList.size())); +// // TODO +// //for (ContainedItemList::iterator it = m_addOnList.begin(); it != m_addOnList.end(); ++it) { +// for (Object* obj : m_addOnList) { +// if (obj) { +// obj->setPosition(getObject()->getPosition()); +// obj->setOrientation(getObject()->getOrientation()); //Why orientation and not Transform? +// } +// } +// +// //DEBUG_LOG((">>> MultiAddOnContain::update 1, m_addOnList.size() = %d", m_addOnList.size())); +// return TransportContain::update(); // extend base +//} + + +//------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::redeployOccupants(void) +{ + TransportContain::redeployOccupants(); + + redeployAddOns(); +} + + +//------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::createPayload() +{ + MultiAddOnContainModuleData* self = (MultiAddOnContainModuleData*)getMultiAddOnContainModuleData(); + + + // Any number of different passengers can be loaded here at init time + Object* object = getObject(); + ContainModuleInterface* contain = object->getContain(); + if (contain) + { + contain->enableLoadSounds(FALSE); + + TemplateNameList list = self->m_payloadTemplateNameData; + TemplateNameIterator iter = list.begin(); + while (iter != list.end()) + { + const ThingTemplate* temp = TheThingFactory->findTemplate(*iter); + if (temp) + { + Object* payload = TheThingFactory->newObject(temp, object->getTeam()); + + if (contain->isValidContainerFor(payload, true)) + { + contain->addToContain(payload); + } + else + { + DEBUG_CRASH(("MultiAddOnContain::createPayload: %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str())); + } + + } + + ++iter; + } + + contain->enableLoadSounds(TRUE); + + } // endif contain + + m_payloadCreated = TRUE; + +} + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +//VecObjectPtr& MultiAddOnContain::getPortableStructures(void) const +//{ +// DEBUG_LOG((">>> MultiAddOnContainModuleData::getPortableStructures 0")); +// VecObjectPtr portableStructures; +// int i = 0; +// for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); it++) { +// DEBUG_LOG((">>> MultiAddOnContainModuleData::getPortableStructures[%d]: occupantID = %d, portableID = %d, slot = %d", +// i, (*it).occupantID, (*it).portableID, (*it).slot)); +// i++; +// Object* obj = TheGameLogic->findObjectByID((*it).portableID); +// if (obj) +// portableStructures.push_back(obj); +// } +// DEBUG_LOG((">>> MultiAddOnContainModuleData::getPortableStructures 1")); +// return portableStructures; +//} + + +// ------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::onBodyDamageStateChange(const DamageInfo* damageInfo, + BodyDamageType oldState, + BodyDamageType newState) ///< state change callback +{ + // Need to apply state change to the portable structure + if (newState != BODY_RUBBLE) + { + for (Object* obj : m_addOnList) { + if (obj) { + obj->getBodyModule()->setDamageState(newState); + } + } + } + +} + +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::onDie(const DamageInfo* damageInfo) +{ + for (Object* obj : m_addOnList) { + if (obj) + obj->kill(); + } + + TransportContain::onDie(damageInfo);//extend base class +} + +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::onDelete(void) +{ + for (Object* obj : m_addOnList) { + if (obj) + TheGameLogic->destroyObject(obj); + } + + TransportContain::onDelete(); +} + +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::onCapture(Player* oldOwner, Player* newOwner) +{ + // Need to setteam() the portable structure, that's all; + + for (Object* obj : m_addOnList) { + if (obj) + obj->setTeam(newOwner->getDefaultTeam()); + } +} + +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::onSelling() +{ + // An OpenContain tells everyone to leave. + orderAllPassengersToExit(CMD_FROM_AI, TRUE); + + /*for (Object* obj : m_addOnList) { + if (obj) + TheGameLogic->destroyObject(obj); + }*/ +} + +//------------------------------------------------------------------------------------------------- +//void MultiAddOnContain::addToContainList(Object* obj) +//{ +// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE)) +// { +// // This is probably redundant and should happen in addToContain instead +// //m_portableStructureIDs.push_back(obj->getID()); +// //obj->friend_setContainedBy(getObject());//fool portable into thinking my object is his container +// } +// else { +// TransportContain::addToContainList(obj); +// } +//} + +//------------------------------------------------------------------------------------------------- +//void MultiAddOnContain::addToContain(Object* obj) +//{ +// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE)) +// { +// const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); +// +// ////Find the slot we belong to +// //for (slot = 0; slot < d->m_slotCapacity; slot++) { +// // if (m_addOnSlots[i].occupantID == obj->getID()) { +// // if (m_addOnSlots[i].portableID != INVALID_ID) { +// // DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - portable for slot %d is already occupied?!", slot)); +// // } +// // break; +// // }E +// //} +// //m_portableStructureIDs.push_back(obj->getID()); +// +// // TODO +// +// obj->friend_setContainedBy(getObject());//fool portable into thinking my object is his container +// +// } +// else { +// TransportContain::addToContain(obj); +// +// } +//} + +//------------------------------------------------------------------------------------------------- +//void MultiAddOnContain::removeFromContain(Object* obj, Bool exposeStealthUnits) +//{ +// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE)) +// { +// for (VecObjectIDIt it = m_portableStructureIDs.begin(); it != m_portableStructureIDs.end(); ) { +// if (obj->getID() == (*it)) +// m_portableStructureIDs.erase(it); +// } +// } +// else +// { +// // Get corresponding Structure and remove it +// TransportContain::removeFromContain(obj, exposeStealthUnits); +// } +//} + + +//------------------------------------------------------------------------------------------------- +//Bool MultiAddOnContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const +//{ +// // TODO: Check list of allowed passengers +// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) && INVALID_ID == m_portableStructureID) +// return TRUE; +// +// return TransportContain::isValidContainerFor(obj, checkCapacity); +//} + + +//------------------------------------------------------------------------------------------------- +//const Object* MultiAddOnContain::friend_getRider() const +//{ +// // The draw order dependency bug for riders means that our draw module needs to cheat to get around it. +// +// if (m_portableStructureID != INVALID_ID) +// { +// const Object* portableAsRider = TheGameLogic->findObjectByID(m_portableStructureID); +// return portableAsRider; +// } +// +// return NULL; +//} + +//------------------------------------------------------------------------------------------------- +Bool MultiAddOnContain::isEnclosingContainerFor(const Object* obj) const +{ + + //for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); it++) { + // if ((*it).portableID == obj->getID()) { + // const Object* portableAsRider = TheGameLogic->findObjectByID((*it).portableID); + // if (portableAsRider == obj) + // return FALSE; + // } + //} + + for (Object* addOn : m_addOnList) { + if (addOn == obj) { + //DEBUG_LOG((">>> MultiAddOnContain::isEnclosingContainerFor -> object found -> FALSE")); + return FALSE; + } + } + + return TransportContain::isEnclosingContainerFor(obj); +} + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +// if my object gets selected, then my visible passengers should, too +// this gets called from +void MultiAddOnContain::clientVisibleContainedFlashAsSelected() +{ + for (Object* obj : m_addOnList) { + if (obj) { + Drawable* draw = obj->getDrawable(); + if (draw) + { + draw->flashAsSelected(); + } + } + } +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +Bool MultiAddOnContain::isPassengerAllowedToFire(ObjectID id) const +{ + if (getObject() && getObject()->getContainedBy()) // nested containment voids firing, always + return FALSE; + + //if (id == INVALID_ID && m_addOnList.size() > 0) { + // return TRUE; // This handles cases where we just check if we can attack at all. + //} + + // Wait, the portable structures aren't actually contained, so we skip this + //for (std::vector::iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); ) { + // if (*it->portableID == id && id != INVALID_ID) { + // return TRUE; // Portable structures can always fire + // } + //} + + return TransportContain::isPassengerAllowedToFire(id); //extend for everything else +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::onContaining(Object* obj, Bool wasSelected) +{ + DEBUG_LOG((">>> MultiAddOnContainModuleData::onContaining 0")); + // extend base class + TransportContain::onContaining(obj, wasSelected); + + // Spawn the Turret. + // This should be the only place where we do this! No turret should ever enter the actual container + createAddOnForRider(obj); + + DEBUG_LOG((">>> MultiAddOnContainModuleData::onContaining 1")); +} // end onContaining + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::onRemoving(Object* obj) +{ + // extend base class + TransportContain::onRemoving(obj); + + // Remove the corresponding turret + removeAddOnForRider(obj); + +} // end onRemoving + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +bool MultiAddOnContain::createAddOnForRider(Object* obj) +{ + const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); + + DEBUG_LOG((">>> MultiAddOnContainModuleData::createAddOnForRider 0")); + + // Get next free slot. + UnsignedInt slot = -1; + //for (std::vector::iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); ) { + //} + for (UnsignedInt i = 0; i < d->m_slotCapacity; i++) { + + if (m_addOnSlots[i].occupantID == INVALID_ID) { + // sanity checks + if (m_addOnSlots[i].portableID != INVALID_ID) + DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - WARNING: - Portable in slot %d is not empty", i)); + + if (m_addOnSlots[i].slot != i) + DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - WARNING: - Slot number is invalid?!", i)); + + if (slot == -1) + slot = i; // we choose this slot, but still continue the loop for sanity checks. + } + + if (m_addOnSlots[i].occupantID == obj->getID()) { + DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - WARNING: - Object %d already exists in slot %d?!", obj->getID(), i)); + return false; + } + } + if (slot == -1) { + DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - WARNING: - no free slot found!")); + return false; + } + + // create the actual object + Object* addOnObj = NULL; + AddOnEntryMap::const_iterator it = d->m_addOnEntries.find(NAMEKEY(obj->getTemplate()->getName())); + if (it != d->m_addOnEntries.end()) + { + AsciiString tmplName = (*it).second; + if (tmplName.isNotEmpty()) { + const ThingTemplate* thing = TheThingFactory->findTemplate(tmplName); + if (thing) + { + addOnObj = TheThingFactory->newObject(thing, getObject()->getTeam()); + } + } + } + + if (addOnObj == NULL) { + DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - WARNING: - failed to create AddOn object!")); + return false; + } + + // TODO: figure out if we need to align to a bone + addOnObj->friend_setContainedBy(getObject());//fool portable into thinking my object is his container + addOnObj->setPosition(getObject()->getPosition()); + addOnObj->setTransformMatrix(getObject()->getTransformMatrix()); + + // Need to hide if they are hidden. + if (getObject()->getDrawable() && addOnObj->getDrawable() && getObject()->getDrawable()->isDrawableEffectivelyHidden()) + addOnObj->getDrawable()->setDrawableHidden(TRUE); + + // Register rider and addOn + m_addOnSlots[slot].occupantID = obj->getID(); + m_addOnSlots[slot].portableID = addOnObj->getID(); + + m_addOnList.push_back(addOnObj); + + redeployAddOns(); + + DEBUG_LOG((">>> MultiAddOnContainModuleData::createAddOnForRider 1")); + + return true; +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +bool MultiAddOnContain::removeAddOnForRider(Object* obj) +{ + const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); + + // Search slots for our object + UnsignedInt slot = -1; + + for (UnsignedInt i = 0; i < d->m_slotCapacity; i++) { + if (m_addOnSlots[i].occupantID == obj->getID()) { + // sanity checks + if (m_addOnSlots[i].portableID == INVALID_ID) + DEBUG_LOG(("MultiAddOnContain::removeAddOnForRider - WARNING: - Portable in slot %d is empty?!", i)); + + if (m_addOnSlots[i].slot != i) + DEBUG_LOG(("MultiAddOnContain::removeAddOnForRider - WARNING: - Slot number is invalid?!", i)); + + if (slot == -1) { + slot = i; + } + else { + DEBUG_LOG(("MultiAddOnContain::removeAddOnForRider - WARNING: - Occupant in multiple slots?", i)); + } + } + } + if (slot == -1) { + DEBUG_LOG(("MultiAddOnContain::removeAddOnForRider - WARNING: - rider was not found!")); + return false; + } + + Object* addOnObj = TheGameLogic->findObjectByID(m_addOnSlots[slot].portableID); + if (!addOnObj) { + DEBUG_LOG(("MultiAddOnContain::removeAddOnForRider - WARNING: - rider object was not found!")); + return false; + } + + ContainedItemsList::iterator it = std::find(m_addOnList.begin(), m_addOnList.end(), addOnObj); + if (it != m_addOnList.end()) + { + m_addOnList.erase(it); + } + + m_addOnSlots[slot].occupantID = INVALID_ID; + m_addOnSlots[slot].portableID = INVALID_ID; + + TheGameLogic->destroyObject(addOnObj); + +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::redeployAddOns(void) +{ + for (const AddOnSlotData& addOn : m_addOnSlots) { + Object* obj = TheGameLogic->findObjectByID(addOn.portableID); + if (obj) { + putObjAtSlot(obj, addOn.slot); + } + } +} + +//------------------------------------------------------------------------------------------------- +/** Place the object at the 3D position of the next fire point to use */ +//------------------------------------------------------------------------------------------------- +void MultiAddOnContain::putObjAtSlot(Object* obj, short slot) +{ + const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); + + // Load and cache addOn positions + //UnsignedShort count = 0; + //if (m_addOnPoints.size() == 0 && m_noAddOnPointsInArt == false) + //{ + // Matrix3D matrix; + // for (int i = 0; i < m_addOnSlots.size(); i++) { + // AsciiString boneName; + // boneName.format("%s%02d", d->m_addOnBoneName.str(), i + 1); + // Bool found = getObject()->getSingleLogicalBonePositionOnTurret(TURRET_MAIN, boneName.str(), NULL, &matrix); + // if (found) + // count++; + // m_addOnPoints.push_back(matrix); + // } + + // // + // // if there is still no firepoints in the art, we'll set a flag so that we don't + // // ever go through the art stuff again + // // + // if (count == 0) + // m_noAddOnPointsInArt = TRUE; + //} // end if + + // + // if there are no fire points in the art we just put the object at the center + // of the object + // + //if (m_noAddOnPointsInArt == TRUE) + //{ + // obj->setOrientation(getObject()->getOrientation()); + // obj->setPosition(getObject()->getPosition()); + // return; + + //} // end if + + // get the position + Matrix3D matrix; + Bool found = FALSE; + AsciiString boneName; + boneName.format("%s%02d", d->m_addOnBoneName.str(), slot + 1); + if (d->m_passengersInTurret) + { + found = getObject()->getSingleLogicalBonePositionOnTurret(TURRET_MAIN, boneName.str(), NULL, &matrix); + } + else + { + found = getObject()->getSingleLogicalBonePosition(boneName.str(), NULL, &matrix); + } + + if (!found) { + obj->setOrientation(getObject()->getOrientation()); + obj->setPosition(getObject()->getPosition()); + return; + } + + obj->setTransformMatrix(&matrix); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +short MultiAddOnContain::getRiderSlot(ObjectID riderID) const { + for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); it++) { + if ((*it).occupantID == riderID) { + return (*it).slot; + } + } + return -1; +} +// ------------------------------------------------------------------------------------------------ +short MultiAddOnContain::getPortableSlot(ObjectID portableID) const { + for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); it++) { + if ((*it).portableID == portableID) { + return (*it).slot; + } + } + return -1; +} +// ------------------------------------------------------------------------------------------------ + +//const ContainedItemsList* MultiAddOnContain::getAddOnList() const { +// if (m_addOnList_valid) { +// return &m_addOnList; +// } +// +// m_addOnList.clear(); +// for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); ) { +// Object* obj = TheGameLogic->findObjectByID((*it).portableID); +// if (obj) +// portableStructures.push_back(obj); +// } +// return &portableStructures; +//} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::crc(Xfer* xfer) +{ + + // extend base class + TransportContain::crc(xfer); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ + // ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::xfer(Xfer* xfer) +{ + + // version + XferVersion currentVersion = 2; + XferVersion version = currentVersion; + xfer->xferVersion(&version, currentVersion); + + + // List of AddOns + // save/load each item + for (std::vector::iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); ++it) + { + AddOnSlotData entry = (*it); + xfer->xferObjectID(&entry.occupantID); + xfer->xferObjectID(&entry.portableID); + // xfer->xferUnsignedShort(&entry->slot); We don't need this + + if (xfer->getXferMode() == XFER_LOAD) + { + Object* obj = TheGameLogic->findObjectByID(entry.portableID); + m_addOnList.push_back(obj); + } + } + + // extend base class + TransportContain::xfer(xfer); + + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::loadPostProcess(void) +{ + + // extend base class + TransportContain::loadPostProcess(); + +} // end loadPostProcess diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index a4b8fffaa47..075905525c7 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -2228,8 +2228,16 @@ void Object::setDisabledUntil( DisabledType type, UnsignedInt frame ) { rider->setDisabledUntil(type, frame); } + + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* addOn : *addOnList) { + addOn->setDisabledUntil(type, frame); + } + } } + if ( isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) ) { SpawnBehaviorInterface *sbi = this->getSpawnBehaviorInterface(); @@ -2368,6 +2376,13 @@ Bool Object::clearDisabled( DisabledType type ) { rider->clearDisabled(type); } + + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* addOn : *addOnList) { + addOn->clearDisabled(type); + } + } } if ( isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp index a801f840aed..7612e42ff84 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cpp @@ -3162,6 +3162,15 @@ void AIUpdateInterface::privateIdle(CommandSourceType cmdSource) ai->aiIdle(cmdSource); } } + + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + AIUpdateInterface* ai = obj ? obj->getAI() : NULL; + if (ai) + ai->aiIdle(cmdSource); + } + } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index 5e95e707e10..9ba391fbc15 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -1410,6 +1410,15 @@ void ChinookAIUpdate::privateIdle(CommandSourceType cmdSource) if (riderAI) riderAI->aiIdle(cmdSource); } + + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + AIUpdateInterface* ai = obj ? obj->getAI() : NULL; + if (ai) + ai->aiIdle(cmdSource); + } + } } SupplyTruckAIUpdate::privateIdle( cmdSource ); @@ -1502,6 +1511,25 @@ void ChinookAIUpdate::private___TellPortableStructureToAttackWithMe( Object *vic riderAI->aiAttackObject(victim, maxShotsToFire, cmdSource); } } + + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + if (obj + && obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) + && !obj->isDisabledByType(DISABLED_HACKED) + && !obj->isDisabledByType(DISABLED_EMP) + && !obj->isDisabledByType(DISABLED_SUBDUED) + && !obj->isDisabledByType(DISABLED_PARALYZED)) + { + AIUpdateInterface* riderAI = obj->getAIUpdateInterface(); + if (riderAI) + { + riderAI->aiAttackObject(victim, maxShotsToFire, cmdSource); + } + } + } + } } } @@ -1577,7 +1605,24 @@ void ChinookAIUpdate::privateForceAttackObject( Object *victim, Int maxShotsToFi riderAI->aiForceAttackObject(victim, maxShotsToFire, cmdSource); } } - + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + if (obj + && obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) + && !obj->isDisabledByType(DISABLED_HACKED) + && !obj->isDisabledByType(DISABLED_EMP) + && !obj->isDisabledByType(DISABLED_SUBDUED) + && !obj->isDisabledByType(DISABLED_PARALYZED)) + { + AIUpdateInterface* riderAI = obj->getAIUpdateInterface(); + if (riderAI) + { + riderAI->aiForceAttackObject(victim, maxShotsToFire, cmdSource); + } + } + } + } } } @@ -1654,6 +1699,24 @@ void ChinookAIUpdate::privateAttackPosition( const Coord3D *pos, Int maxShotsToF riderAI->aiAttackPosition(pos, maxShotsToFire, cmdSource); } } + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + if (obj + && obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) + && !obj->isDisabledByType(DISABLED_HACKED) + && !obj->isDisabledByType(DISABLED_EMP) + && !obj->isDisabledByType(DISABLED_SUBDUED) + && !obj->isDisabledByType(DISABLED_PARALYZED)) + { + AIUpdateInterface* riderAI = obj->getAIUpdateInterface(); + if (riderAI) + { + riderAI->aiAttackPosition(pos, maxShotsToFire, cmdSource); + } + } + } + } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/TransportAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/TransportAIUpdate.cpp index b55a17bdb45..31054609baa 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/TransportAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/TransportAIUpdate.cpp @@ -61,37 +61,58 @@ TransportAIUpdate::~TransportAIUpdate( void ) */ void TransportAIUpdate::privateAttackObject( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource ) { + DEBUG_LOG((">>> TransportAIUpdate::privateAttackObject 0")); ContainModuleInterface* contain = getObject()->getContain(); - if( contain != NULL && contain->isPassengerAllowedToFire() ) + if( contain != NULL) { // As an extension of the normal attack, I may want to tell my passengers to attack // too, but only if this is a direct command. (As opposed to a passive aquire) if( cmdSource == CMD_FROM_PLAYER || cmdSource == CMD_FROM_SCRIPT ) { - const ContainedItemsList *passengerList = contain->getContainedItemsList(); - ContainedItemsList::const_iterator passengerIterator; - passengerIterator = passengerList->begin(); - - while( passengerIterator != passengerList->end() ) - { - Object *passenger = *passengerIterator; - //Advance to the next iterator - passengerIterator++; - - // If I am an overlord with a gattling upgrade, I do not tell it to fire if it is disabled - if ( passenger->isKindOf( KINDOF_PORTABLE_STRUCTURE ) ) - { - if( passenger->isDisabledByType( DISABLED_HACKED ) - || passenger->isDisabledByType( DISABLED_EMP ) - || passenger->isDisabledByType( DISABLED_SUBDUED ) - || passenger->isDisabledByType( DISABLED_PARALYZED) ) - continue; - } + if (contain->isPassengerAllowedToFire()) { + const ContainedItemsList *passengerList = contain->getContainedItemsList(); + ContainedItemsList::const_iterator passengerIterator; + passengerIterator = passengerList->begin(); - AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface(); - if( passengerAI ) + while (passengerIterator != passengerList->end()) { - passengerAI->aiAttackObject( victim, maxShotsToFire, cmdSource ); + Object* passenger = *passengerIterator; + //Advance to the next iterator + passengerIterator++; + + // If I am an overlord with a gattling upgrade, I do not tell it to fire if it is disabled + if (passenger->isKindOf(KINDOF_PORTABLE_STRUCTURE)) + { + if (passenger->isDisabledByType(DISABLED_HACKED) + || passenger->isDisabledByType(DISABLED_EMP) + || passenger->isDisabledByType(DISABLED_SUBDUED) + || passenger->isDisabledByType(DISABLED_PARALYZED)) + continue; + } + + AIUpdateInterface* passengerAI = passenger->getAIUpdateInterface(); + if (passengerAI) + { + passengerAI->aiAttackObject(victim, maxShotsToFire, cmdSource); + } + } + } + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + if (obj + && obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) + && !obj->isDisabledByType(DISABLED_HACKED) + && !obj->isDisabledByType(DISABLED_EMP) + && !obj->isDisabledByType(DISABLED_SUBDUED) + && !obj->isDisabledByType(DISABLED_PARALYZED)) + { + AIUpdateInterface* riderAI = obj->getAIUpdateInterface(); + if (riderAI) + { + riderAI->aiAttackObject(victim, maxShotsToFire, cmdSource); + } + } } } } @@ -106,37 +127,60 @@ void TransportAIUpdate::privateAttackObject( Object *victim, Int maxShotsToFire, */ void TransportAIUpdate::privateForceAttackObject( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource ) { + DEBUG_LOG((">>> TransportAIUpdate::privateForceAttackObject 0")); ContainModuleInterface* contain = getObject()->getContain(); - if( contain != NULL && contain->isPassengerAllowedToFire() ) + if( contain) { // As an extension of the normal attack, I may want to tell my passengers to attack // too, but only if this is a direct command. (As opposed to a passive aquire) if( cmdSource == CMD_FROM_PLAYER || cmdSource == CMD_FROM_SCRIPT ) { - const ContainedItemsList *passengerList = contain->getContainedItemsList(); - ContainedItemsList::const_iterator passengerIterator; - passengerIterator = passengerList->begin(); - - while( passengerIterator != passengerList->end() ) - { - Object *passenger = *passengerIterator; - //Advance to the next iterator - passengerIterator++; - - // If I am an overlord with a gattling upgrade, I do not tell it to fire if it is disabled - if ( passenger->isKindOf( KINDOF_PORTABLE_STRUCTURE ) ) - { - if( passenger->isDisabledByType( DISABLED_HACKED ) - || passenger->isDisabledByType( DISABLED_EMP ) - || passenger->isDisabledByType( DISABLED_SUBDUED ) - || passenger->isDisabledByType( DISABLED_PARALYZED) ) - continue; - } + if (contain->isPassengerAllowedToFire()) { + const ContainedItemsList* passengerList = contain->getContainedItemsList(); + ContainedItemsList::const_iterator passengerIterator; + passengerIterator = passengerList->begin(); - AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface(); - if( passengerAI ) + while (passengerIterator != passengerList->end()) { - passengerAI->aiForceAttackObject( victim, maxShotsToFire, cmdSource ); + Object* passenger = *passengerIterator; + //Advance to the next iterator + passengerIterator++; + + // If I am an overlord with a gattling upgrade, I do not tell it to fire if it is disabled + if (passenger->isKindOf(KINDOF_PORTABLE_STRUCTURE)) + { + if (passenger->isDisabledByType(DISABLED_HACKED) + || passenger->isDisabledByType(DISABLED_EMP) + || passenger->isDisabledByType(DISABLED_SUBDUED) + || passenger->isDisabledByType(DISABLED_PARALYZED)) + continue; + } + + AIUpdateInterface* passengerAI = passenger->getAIUpdateInterface(); + if (passengerAI) + { + passengerAI->aiForceAttackObject(victim, maxShotsToFire, cmdSource); + } + } + } + DEBUG_LOG((">>> TransportAIUpdate::privateForceAttackObject 1")); + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + if (obj + && obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) + && !obj->isDisabledByType(DISABLED_HACKED) + && !obj->isDisabledByType(DISABLED_EMP) + && !obj->isDisabledByType(DISABLED_SUBDUED) + && !obj->isDisabledByType(DISABLED_PARALYZED)) + { + AIUpdateInterface* riderAI = obj->getAIUpdateInterface(); + if (riderAI) + { + DEBUG_LOG((">>> TransportAIUpdate::privateForceAttackObject -> pass to rider")); + riderAI->aiForceAttackObject(victim, maxShotsToFire, cmdSource); + } + } } } } @@ -151,37 +195,60 @@ void TransportAIUpdate::privateForceAttackObject( Object *victim, Int maxShotsTo */ void TransportAIUpdate::privateAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource ) { + DEBUG_LOG((">>> TransportAIUpdate::privateAttackPosition 0")); ContainModuleInterface* contain = getObject()->getContain(); - if( contain != NULL && contain->isPassengerAllowedToFire() ) + if( contain != NULL) { // As an extension of the normal attack, I may want to tell my passengers to attack // too, but only if this is a direct command. (As opposed to a passive aquire) if( cmdSource == CMD_FROM_PLAYER || cmdSource == CMD_FROM_SCRIPT ) { - const ContainedItemsList *passengerList = contain->getContainedItemsList(); - ContainedItemsList::const_iterator passengerIterator; - passengerIterator = passengerList->begin(); - - while( passengerIterator != passengerList->end() ) - { - Object *passenger = *passengerIterator; - //Advance to the next iterator - passengerIterator++; - - // If I am an overlord with a gattling upgrade, I do not tell it ti fire if it is disabled - if ( passenger->isKindOf( KINDOF_PORTABLE_STRUCTURE ) ) + if (contain->isPassengerAllowedToFire()) { + const ContainedItemsList* passengerList = contain->getContainedItemsList(); + ContainedItemsList::const_iterator passengerIterator; + passengerIterator = passengerList->begin(); + + while (passengerIterator != passengerList->end()) { - if( passenger->isDisabledByType( DISABLED_HACKED ) - || passenger->isDisabledByType( DISABLED_EMP) - || passenger->isDisabledByType( DISABLED_SUBDUED ) - || passenger->isDisabledByType( DISABLED_PARALYZED) ) - continue; + Object* passenger = *passengerIterator; + //Advance to the next iterator + passengerIterator++; + + // If I am an overlord with a gattling upgrade, I do not tell it ti fire if it is disabled + if (passenger->isKindOf(KINDOF_PORTABLE_STRUCTURE)) + { + if (passenger->isDisabledByType(DISABLED_HACKED) + || passenger->isDisabledByType(DISABLED_EMP) + || passenger->isDisabledByType(DISABLED_SUBDUED) + || passenger->isDisabledByType(DISABLED_PARALYZED)) + continue; + } + + AIUpdateInterface* passengerAI = passenger->getAIUpdateInterface(); + if (passengerAI) + { + passengerAI->aiAttackPosition(pos, maxShotsToFire, cmdSource); + } } + } - AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface(); - if( passengerAI ) - { - passengerAI->aiAttackPosition( pos, maxShotsToFire, cmdSource ); + const std::list* addOnList = contain->getAddOnList(); + if (addOnList) { + for (Object* obj : *addOnList) { + if (obj + && obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) + && !obj->isDisabledByType(DISABLED_HACKED) + && !obj->isDisabledByType(DISABLED_EMP) + && !obj->isDisabledByType(DISABLED_SUBDUED) + && !obj->isDisabledByType(DISABLED_PARALYZED)) + { + AIUpdateInterface* riderAI = obj->getAIUpdateInterface(); + if (riderAI) + { + DEBUG_LOG((">>> TransportAIUpdate::privateAttackPosition - order rider!\n")); + riderAI->aiAttackPosition(pos, maxShotsToFire, cmdSource); + } + } } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp index 50ba0214fc4..e4b615d1fcf 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp @@ -781,20 +781,39 @@ CanAttackResult WeaponSet::getAbleToUseWeaponAgainstTarget( AbleToAttackType att // Do a check to see if we have an occupied container (garrisoned building, transport that allows passengers to fire). ContainModuleInterface *contain = source->getContain(); - if (contain && contain->isPassengerAllowedToFire()) + if (contain) { - // Loop through each member and if just one of them can attack the specific target, then - // we are good to go! - const ContainedItemsList* items = contain->getContainedItemsList(); + if (contain->isPassengerAllowedToFire()) { + // Loop through each member and if just one of them can attack the specific target, then + // we are good to go! + const ContainedItemsList* items = contain->getContainedItemsList(); + if (items) + { + for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it) + { + Object* garrisonedMember = *it; + if (garrisonedMember->isAbleToAttack()) + { + CanAttackResult result = garrisonedMember->getAbleToUseWeaponAgainstTarget(attackType, victim, pos, commandSource); + if (result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING) + { + return result; + } + } + } + } + } + // Check MultiAddons + const ContainedItemsList* items = contain->getAddOnList(); if (items) { for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it) { Object* garrisonedMember = *it; - if( garrisonedMember->isAbleToAttack() ) + if (garrisonedMember->isAbleToAttack()) { - CanAttackResult result = garrisonedMember->getAbleToUseWeaponAgainstTarget( attackType, victim, pos, commandSource ); - if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING ) + CanAttackResult result = garrisonedMember->getAbleToUseWeaponAgainstTarget(attackType, victim, pos, commandSource); + if (result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING) { return result; } diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordAircraftDraw.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordAircraftDraw.h index 662955c00ff..12c421e3bde 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordAircraftDraw.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordAircraftDraw.h @@ -47,6 +47,8 @@ class W3DOverlordAircraftDrawModuleData : public W3DModelDrawModuleData { public: + Bool m_hasMultiAddOns; ///< compatibility for MultiAddOnContain and similar modules; + W3DOverlordAircraftDrawModuleData(); ~W3DOverlordAircraftDrawModuleData(); static void buildFieldParse(MultiIniFieldParse& p); diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordTankDraw.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordTankDraw.h index 296319a80c4..e0af86d66f0 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordTankDraw.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DOverlordTankDraw.h @@ -47,6 +47,8 @@ class W3DOverlordTankDrawModuleData : public W3DTankDrawModuleData Real m_treadPivotSpeedFraction; ///getCurrentWorldspaceClientBonePositions( md->m_attachToDrawableBoneInContainer.str(), theirBoneMtx ) ) + + AsciiString boneName; + // if we are in a MultiAddOnContain, we add the slot number to the bone name + short slot = me->getContainedBy()->getContain()->getPortableSlot(me->getID()); + DEBUG_LOG((">>> W3DDependencyModelDraw::adjustTransformMtx - riderSlot = %d", slot)); + if (slot != -1) + boneName.format("%s%02d", md->m_attachToDrawableBoneInContainer.str(), slot+1); + + if ((slot != -1) && theirDrawable->getCurrentWorldspaceClientBonePositions(boneName.str(), theirBoneMtx)) + { + mtx = theirBoneMtx; + } + else if( theirDrawable->getCurrentWorldspaceClientBonePositions( md->m_attachToDrawableBoneInContainer.str(), theirBoneMtx ) ) { mtx = theirBoneMtx; } else { mtx = *theirDrawable->getTransformMatrix();//TransformMatrix(); - DEBUG_LOG(("m_attachToDrawableBoneInContainer %s not found",getW3DDependencyModelDrawModuleData()->m_attachToDrawableBoneInContainer.str())); + DEBUG_LOG(("m_attachToDrawableBoneInContainer %s not found", md->m_attachToDrawableBoneInContainer.str())); } } } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp index 39c1cbe3250..e87cf7795e7 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp @@ -61,6 +61,7 @@ void W3DOverlordAircraftDrawModuleData::buildFieldParse(MultiIniFieldParse& p) static const FieldParse dataFieldParse[] = { + {"HasMultiAddOns", INI::parseBool, NULL, offsetof(W3DOverlordAircraftDrawModuleData, m_hasMultiAddOns)}, { 0, 0, 0, 0 } }; p.add(dataFieldParse); @@ -86,25 +87,27 @@ void W3DOverlordAircraftDraw::doDrawModule(const Matrix3D* transformMtx) // Our big thing is that we get our specific passenger (the turret thing) and then wake it up and make it draw // It depends on us because our renderObject is only made correct in the act of drawing. - Object *me = getDrawable()->getObject(); - if( me - && me->getContain() - && me->getContain()->friend_getRider() - && me->getContain()->friend_getRider()->getDrawable() - ) - { - Drawable *riderDraw = me->getContain()->friend_getRider()->getDrawable(); - if ( riderDraw ) - { - TintEnvelope *env = getDrawable()->getColorTintEnvelope(); - if ( env ) - riderDraw->setColorTintEnvelope( *env ); - - riderDraw->notifyDrawableDependencyCleared(); - riderDraw->draw( NULL );// What the hell? This param isn't used for anything - } - DEBUG_ASSERTCRASH( riderDraw, ("OverlordAircraftDraw finds no rider's drawable") ); - + Object* me = getDrawable()->getObject(); + if (me && me->getContain()) { + if (getW3DOverlordAircraftDrawModuleData()->m_hasMultiAddOns) { + const ContainedItemsList* addOns = me->getContain()->getAddOnList(); + DEBUG_LOG((">>> W3DOverlordAircraftDraw::doDrawModule - addOns.size = %d", addOns->size())); + for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { + Drawable* riderDraw = (*it)->getDrawable(); + riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope()); + riderDraw->notifyDrawableDependencyCleared(); + riderDraw->draw(NULL); + } + } + else if (me->getContain()->friend_getRider() + && me->getContain()->friend_getRider()->getDrawable()) + { + Drawable* riderDraw = me->getContain()->friend_getRider()->getDrawable(); + riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope()); + + riderDraw->notifyDrawableDependencyCleared(); + riderDraw->draw(NULL);// What the hell? This param isn't used for anything + } } } @@ -114,14 +117,19 @@ void W3DOverlordAircraftDraw::setHidden(Bool h) W3DModelDraw::setHidden(h); // We need to hide our rider, since he won't realize he's being contained in a contained container - Object *me = getDrawable()->getObject(); - if( me - && me->getContain() - && me->getContain()->friend_getRider() - && me->getContain()->friend_getRider()->getDrawable() - ) - { - me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h); + Object* me = getDrawable()->getObject(); + if (me && me->getContain()) { + if (getW3DOverlordAircraftDrawModuleData()->m_hasMultiAddOns) { + const ContainedItemsList* addOns = me->getContain()->getAddOnList(); + for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { + (*it)->getDrawable()->setDrawableHidden(h); + } + } + else if (me->getContain()->friend_getRider() + && me->getContain()->friend_getRider()->getDrawable()) + { + me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h); + } } } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTankDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTankDraw.cpp index 039cd926f12..f867bc61651 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTankDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTankDraw.cpp @@ -52,6 +52,7 @@ void W3DOverlordTankDrawModuleData::buildFieldParse(MultiIniFieldParse& p) static const FieldParse dataFieldParse[] = { + {"HasMultiAddOns", INI::parseBool, NULL, offsetof(W3DOverlordTankDrawModuleData, m_hasMultiAddOns)}, { 0, 0, 0, 0 } }; p.add(dataFieldParse); @@ -78,17 +79,25 @@ void W3DOverlordTankDraw::doDrawModule(const Matrix3D* transformMtx) // Our big thing is that we get our specific passenger (the turret thing) and then wake it up and make it draw // It depends on us because our renderObject is only made correct in the act of drawing. Object *me = getDrawable()->getObject(); - if( me - && me->getContain() - && me->getContain()->friend_getRider() - && me->getContain()->friend_getRider()->getDrawable() - ) - { - Drawable *riderDraw = me->getContain()->friend_getRider()->getDrawable(); - riderDraw->setColorTintEnvelope( *getDrawable()->getColorTintEnvelope() ); - - riderDraw->notifyDrawableDependencyCleared(); - riderDraw->draw( NULL );// What the hell? This param isn't used for anything + if (me && me->getContain()) { + if (getW3DOverlordTankDrawModuleData()->m_hasMultiAddOns) { + const ContainedItemsList* addOns = me->getContain()->getAddOnList(); + for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { + Drawable* riderDraw = (*it)->getDrawable(); + riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope()); + riderDraw->notifyDrawableDependencyCleared(); + riderDraw->draw(NULL); + } + } + else if (me->getContain()->friend_getRider() + && me->getContain()->friend_getRider()->getDrawable()) + { + Drawable* riderDraw = me->getContain()->friend_getRider()->getDrawable(); + riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope()); + + riderDraw->notifyDrawableDependencyCleared(); + riderDraw->draw(NULL);// What the hell? This param isn't used for anything + } } } @@ -98,14 +107,19 @@ void W3DOverlordTankDraw::setHidden(Bool h) W3DTankDraw::setHidden(h); // We need to hide our rider, since he won't realize he's being contained in a contained container - Object *me = getDrawable()->getObject(); - if( me - && me->getContain() - && me->getContain()->friend_getRider() - && me->getContain()->friend_getRider()->getDrawable() - ) - { - me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h); + Object* me = getDrawable()->getObject(); + if (me && me->getContain()) { + if (getW3DOverlordTankDrawModuleData()->m_hasMultiAddOns) { + const ContainedItemsList* addOns = me->getContain()->getAddOnList(); + for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { + (*it)->getDrawable()->setDrawableHidden(h); + } + } + else if (me->getContain()->friend_getRider() + && me->getContain()->friend_getRider()->getDrawable()) + { + me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h); + } } } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTruckDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTruckDraw.cpp index b8c26fe2842..9a7561a50ad 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTruckDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordTruckDraw.cpp @@ -52,6 +52,7 @@ void W3DOverlordTruckDrawModuleData::buildFieldParse(MultiIniFieldParse& p) static const FieldParse dataFieldParse[] = { + {"HasMultiAddOns", INI::parseBool, NULL, offsetof(W3DOverlordTruckDrawModuleData, m_hasMultiAddOns)}, { 0, 0, 0, 0 } }; p.add(dataFieldParse); @@ -77,18 +78,26 @@ void W3DOverlordTruckDraw::doDrawModule(const Matrix3D* transformMtx) // Our big thing is that we get our specific passenger (the turret thing) and then wake it up and make it draw // It depends on us because our renderObject is only made correct in the act of drawing. - Object *me = getDrawable()->getObject(); - if( me - && me->getContain() - && me->getContain()->friend_getRider() - && me->getContain()->friend_getRider()->getDrawable() - ) - { - Drawable *riderDraw = me->getContain()->friend_getRider()->getDrawable(); - riderDraw->setColorTintEnvelope( *getDrawable()->getColorTintEnvelope() ); - - riderDraw->notifyDrawableDependencyCleared(); - riderDraw->draw( NULL );// What the hell? This param isn't used for anything + Object* me = getDrawable()->getObject(); + if (me && me->getContain()) { + if (getW3DOverlordTruckDrawModuleData()->m_hasMultiAddOns) { + const ContainedItemsList* addOns = me->getContain()->getAddOnList(); + for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { + Drawable* riderDraw = (*it)->getDrawable(); + riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope()); + riderDraw->notifyDrawableDependencyCleared(); + riderDraw->draw(NULL); + } + } + else if (me->getContain()->friend_getRider() + && me->getContain()->friend_getRider()->getDrawable()) + { + Drawable* riderDraw = me->getContain()->friend_getRider()->getDrawable(); + riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope()); + + riderDraw->notifyDrawableDependencyCleared(); + riderDraw->draw(NULL);// What the hell? This param isn't used for anything + } } } @@ -98,14 +107,19 @@ void W3DOverlordTruckDraw::setHidden(Bool h) W3DTruckDraw::setHidden(h); // We need to hide our rider, since he won't realize he's being contained in a contained container - Object *me = getDrawable()->getObject(); - if( me - && me->getContain() - && me->getContain()->friend_getRider() - && me->getContain()->friend_getRider()->getDrawable() - ) - { - me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h); + Object* me = getDrawable()->getObject(); + if (me && me->getContain()) { + if (getW3DOverlordTruckDrawModuleData()->m_hasMultiAddOns) { + const ContainedItemsList* addOns = me->getContain()->getAddOnList(); + for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { + (*it)->getDrawable()->setDrawableHidden(h); + } + } + else if (me->getContain()->friend_getRider() + && me->getContain()->friend_getRider()->getDrawable()) + { + me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h); + } } } From 6fb4ce6e3c5b7ca548cc7b3ae9da2b6c87384bc6 Mon Sep 17 00:00:00 2001 From: andreasw Date: Fri, 29 Aug 2025 10:26:28 +0200 Subject: [PATCH 2/3] add valid container check --- .../GameLogic/Module/MultiAddOnContain.h | 2 +- .../Object/Contain/MultiAddOnContain.cpp | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h index 12f91e651fa..55ae85d8947 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h @@ -98,7 +98,7 @@ class MultiAddOnContain : public TransportContain virtual void onSelling(); - //virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; + virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; //virtual void addToContain(Object* obj); ///< add 'obj' to contain list //virtual void addToContainList(Object* obj); ///< The part of AddToContain that inheritors can override (Can't do whole thing because of all the private stuff involved) //virtual void removeFromContain(Object* obj, Bool exposeStealthUnits = FALSE); ///< remove 'obj' from contain list diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp index 1943304e1bf..161305ecd03 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp @@ -350,14 +350,16 @@ void MultiAddOnContain::onSelling() //------------------------------------------------------------------------------------------------- -//Bool MultiAddOnContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const -//{ -// // TODO: Check list of allowed passengers -// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) && INVALID_ID == m_portableStructureID) -// return TRUE; -// -// return TransportContain::isValidContainerFor(obj, checkCapacity); -//} +Bool MultiAddOnContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const +{ + const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); + + // TODO: Check list of allowed passengers + if (d->m_addOnEntries.find(NAMEKEY(obj->getTemplate()->getName())) == d->m_addOnEntries.end()) + return FALSE; + + return TransportContain::isValidContainerFor(obj, checkCapacity); +} //------------------------------------------------------------------------------------------------- From 93b6facb2059b033568960922a2dd963e76ed032 Mon Sep 17 00:00:00 2001 From: andreasw Date: Mon, 1 Sep 2025 18:48:53 +0200 Subject: [PATCH 3/3] added param to show/hide subobjects --- .../GameLogic/Module/MultiAddOnContain.h | 8 +- .../Object/Contain/MultiAddOnContain.cpp | 196 +++++------------- .../Drawable/Draw/W3DOverlordAircraftDraw.cpp | 1 - 3 files changed, 63 insertions(+), 142 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h index 55ae85d8947..7dc2e8b3027 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h @@ -58,9 +58,11 @@ class MultiAddOnContainModuleData : public TransportContainModuleData TemplateNameList m_payloadTemplateNameData; Bool m_drawPips; // TODO: Move this to generic transportcontain - AddOnEntryMap m_addOnEntries; + AddOnEntryMap m_addOnEntries; ///< allowed passengers and corresponding turrets - AsciiString m_addOnBoneName; + AsciiString m_addOnBoneName; ///< positions to place the turrets + AsciiString m_emptySlotSubObjName; ///< sub objects to hide/show when a slot is unmanned + AsciiString m_occupiedSlotSubObjName; ///< sub objects to hide/show when a slot is occupied static void buildFieldParse(MultiIniFieldParse& p); @@ -156,6 +158,8 @@ class MultiAddOnContain : public TransportContain void redeployAddOns(); void putObjAtSlot(Object* obj, short slot); + void updateSubObjForSlot(short slot, bool isNowOccupied); + }; #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp index 161305ecd03..7cd5bd04d49 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp @@ -71,6 +71,8 @@ void MultiAddOnContainModuleData::buildFieldParse(MultiIniFieldParse& p) { "PayloadTemplateName", INI::parseAsciiStringVectorAppend, NULL, offsetof(MultiAddOnContainModuleData, m_payloadTemplateNameData) }, { "ShouldDrawPips", INI::parseBool, NULL, offsetof(MultiAddOnContainModuleData, m_drawPips) }, { "AddOnBoneName", INI::parseAsciiString, NULL, offsetof(MultiAddOnContainModuleData, m_addOnBoneName) }, + { "EmptySlotSubObjectName", INI::parseAsciiString, NULL, offsetof(MultiAddOnContainModuleData, m_emptySlotSubObjName) }, + { "OccupiedSlotSubObjectName", INI::parseAsciiString, NULL, offsetof(MultiAddOnContainModuleData, m_occupiedSlotSubObjName) }, { "AddOnEntry", parseAddOnEntry, NULL, 0 }, { 0, 0, 0, 0 } }; @@ -93,8 +95,6 @@ void MultiAddOnContainModuleData::parseAddOnEntry( INI* ini, void *instance, voi MultiAddOnContain::MultiAddOnContain(Thing* thing, const ModuleData* moduleData) : TransportContain(thing, moduleData) { - DEBUG_LOG((">>> MultiAddOnContain::INIT 0")); - m_payloadCreated = FALSE; m_addOnList.clear(); @@ -110,9 +110,10 @@ MultiAddOnContain::MultiAddOnContain(Thing* thing, const ModuleData* moduleData) addOn.portableID = INVALID_ID; addOn.slot = i; m_addOnSlots.push_back(addOn); - } - DEBUG_LOG((">>> MultiAddOnContain::INIT 1, m_addOnSlots.size() = %d", m_addOnSlots.size())); + // Draw is NULL at this point, so lets leave it to the art code to hide the objects initially + //updateSubObjForSlot(i, FALSE); + } } //------------------------------------------------------------------------------------------------- @@ -288,73 +289,11 @@ void MultiAddOnContain::onSelling() }*/ } -//------------------------------------------------------------------------------------------------- -//void MultiAddOnContain::addToContainList(Object* obj) -//{ -// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE)) -// { -// // This is probably redundant and should happen in addToContain instead -// //m_portableStructureIDs.push_back(obj->getID()); -// //obj->friend_setContainedBy(getObject());//fool portable into thinking my object is his container -// } -// else { -// TransportContain::addToContainList(obj); -// } -//} - -//------------------------------------------------------------------------------------------------- -//void MultiAddOnContain::addToContain(Object* obj) -//{ -// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE)) -// { -// const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); -// -// ////Find the slot we belong to -// //for (slot = 0; slot < d->m_slotCapacity; slot++) { -// // if (m_addOnSlots[i].occupantID == obj->getID()) { -// // if (m_addOnSlots[i].portableID != INVALID_ID) { -// // DEBUG_LOG(("MultiAddOnContain::createAddOnForRider - portable for slot %d is already occupied?!", slot)); -// // } -// // break; -// // }E -// //} -// //m_portableStructureIDs.push_back(obj->getID()); -// -// // TODO -// -// obj->friend_setContainedBy(getObject());//fool portable into thinking my object is his container -// -// } -// else { -// TransportContain::addToContain(obj); -// -// } -//} - -//------------------------------------------------------------------------------------------------- -//void MultiAddOnContain::removeFromContain(Object* obj, Bool exposeStealthUnits) -//{ -// if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE)) -// { -// for (VecObjectIDIt it = m_portableStructureIDs.begin(); it != m_portableStructureIDs.end(); ) { -// if (obj->getID() == (*it)) -// m_portableStructureIDs.erase(it); -// } -// } -// else -// { -// // Get corresponding Structure and remove it -// TransportContain::removeFromContain(obj, exposeStealthUnits); -// } -//} - - //------------------------------------------------------------------------------------------------- Bool MultiAddOnContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const { const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); - // TODO: Check list of allowed passengers if (d->m_addOnEntries.find(NAMEKEY(obj->getTemplate()->getName())) == d->m_addOnEntries.end()) return FALSE; @@ -362,32 +301,10 @@ Bool MultiAddOnContain::isValidContainerFor(const Object* obj, Bool checkCapacit } -//------------------------------------------------------------------------------------------------- -//const Object* MultiAddOnContain::friend_getRider() const -//{ -// // The draw order dependency bug for riders means that our draw module needs to cheat to get around it. -// -// if (m_portableStructureID != INVALID_ID) -// { -// const Object* portableAsRider = TheGameLogic->findObjectByID(m_portableStructureID); -// return portableAsRider; -// } -// -// return NULL; -//} - //------------------------------------------------------------------------------------------------- Bool MultiAddOnContain::isEnclosingContainerFor(const Object* obj) const { - //for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); it++) { - // if ((*it).portableID == obj->getID()) { - // const Object* portableAsRider = TheGameLogic->findObjectByID((*it).portableID); - // if (portableAsRider == obj) - // return FALSE; - // } - //} - for (Object* addOn : m_addOnList) { if (addOn == obj) { //DEBUG_LOG((">>> MultiAddOnContain::isEnclosingContainerFor -> object found -> FALSE")); @@ -441,7 +358,6 @@ Bool MultiAddOnContain::isPassengerAllowedToFire(ObjectID id) const //------------------------------------------------------------------------------------------------- void MultiAddOnContain::onContaining(Object* obj, Bool wasSelected) { - DEBUG_LOG((">>> MultiAddOnContainModuleData::onContaining 0")); // extend base class TransportContain::onContaining(obj, wasSelected); @@ -449,7 +365,6 @@ void MultiAddOnContain::onContaining(Object* obj, Bool wasSelected) // This should be the only place where we do this! No turret should ever enter the actual container createAddOnForRider(obj); - DEBUG_LOG((">>> MultiAddOnContainModuleData::onContaining 1")); } // end onContaining //------------------------------------------------------------------------------------------------- @@ -471,7 +386,7 @@ bool MultiAddOnContain::createAddOnForRider(Object* obj) { const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); - DEBUG_LOG((">>> MultiAddOnContainModuleData::createAddOnForRider 0")); + //DEBUG_LOG((">>> MultiAddOnContainModuleData::createAddOnForRider 0")); // Get next free slot. UnsignedInt slot = -1; @@ -536,9 +451,11 @@ bool MultiAddOnContain::createAddOnForRider(Object* obj) m_addOnList.push_back(addOnObj); + updateSubObjForSlot(slot, true); + redeployAddOns(); - DEBUG_LOG((">>> MultiAddOnContainModuleData::createAddOnForRider 1")); + //DEBUG_LOG((">>> MultiAddOnContainModuleData::createAddOnForRider 1")); return true; } @@ -591,6 +508,8 @@ bool MultiAddOnContain::removeAddOnForRider(Object* obj) TheGameLogic->destroyObject(addOnObj); + updateSubObjForSlot(slot, false); + } // ------------------------------------------------------------------------------------------------ @@ -612,40 +531,6 @@ void MultiAddOnContain::putObjAtSlot(Object* obj, short slot) { const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); - // Load and cache addOn positions - //UnsignedShort count = 0; - //if (m_addOnPoints.size() == 0 && m_noAddOnPointsInArt == false) - //{ - // Matrix3D matrix; - // for (int i = 0; i < m_addOnSlots.size(); i++) { - // AsciiString boneName; - // boneName.format("%s%02d", d->m_addOnBoneName.str(), i + 1); - // Bool found = getObject()->getSingleLogicalBonePositionOnTurret(TURRET_MAIN, boneName.str(), NULL, &matrix); - // if (found) - // count++; - // m_addOnPoints.push_back(matrix); - // } - - // // - // // if there is still no firepoints in the art, we'll set a flag so that we don't - // // ever go through the art stuff again - // // - // if (count == 0) - // m_noAddOnPointsInArt = TRUE; - //} // end if - - // - // if there are no fire points in the art we just put the object at the center - // of the object - // - //if (m_noAddOnPointsInArt == TRUE) - //{ - // obj->setOrientation(getObject()->getOrientation()); - // obj->setPosition(getObject()->getPosition()); - // return; - - //} // end if - // get the position Matrix3D matrix; Bool found = FALSE; @@ -689,20 +574,53 @@ short MultiAddOnContain::getPortableSlot(ObjectID portableID) const { return -1; } // ------------------------------------------------------------------------------------------------ +void MultiAddOnContain::updateSubObjForSlot(short slot, bool isNowOccupied) { + + const MultiAddOnContainModuleData* d = getMultiAddOnContainModuleData(); + + Object* obj = getObject(); + Drawable* draw = obj->getDrawable(); + if (draw) + { + AsciiString hideSubObjName; + AsciiString showSubObjName; + bool updateSubObjects = false; + + // Get SubObj names to hide/show + if (d->m_emptySlotSubObjName.isNotEmpty() && isNowOccupied) { + hideSubObjName.format("%s%02d", d->m_emptySlotSubObjName.str(), slot + 1); + } else if (d->m_occupiedSlotSubObjName.isNotEmpty() && !isNowOccupied) { + hideSubObjName.format("%s%02d", d->m_occupiedSlotSubObjName.str(), slot + 1); + } + + if (d->m_emptySlotSubObjName.isNotEmpty() && !isNowOccupied) { + showSubObjName.format("%s%02d", d->m_emptySlotSubObjName.str(), slot + 1); + } + else if (d->m_occupiedSlotSubObjName.isNotEmpty() && isNowOccupied) { + showSubObjName.format("%s%02d", d->m_occupiedSlotSubObjName.str(), slot + 1); + } + + // Hide / Show Objects + if (hideSubObjName.isNotEmpty()) { + draw->showSubObject(hideSubObjName, false); + updateSubObjects = true; + } + + if (showSubObjName.isNotEmpty()) { + draw->showSubObject(showSubObjName, true); + updateSubObjects = true; + } + + if (updateSubObjects) + { + draw->updateSubObjects(); + } + } + else { + DEBUG_LOG((">>> MultiAddOnContain::updateSubObjForSlot: DRAW IS NULL")); + } +} -//const ContainedItemsList* MultiAddOnContain::getAddOnList() const { -// if (m_addOnList_valid) { -// return &m_addOnList; -// } -// -// m_addOnList.clear(); -// for (std::vector::const_iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); ) { -// Object* obj = TheGameLogic->findObjectByID((*it).portableID); -// if (obj) -// portableStructures.push_back(obj); -// } -// return &portableStructures; -//} // ------------------------------------------------------------------------------------------------ /** CRC */ diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp index e87cf7795e7..26f8ad3e3cd 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DOverlordAircraftDraw.cpp @@ -91,7 +91,6 @@ void W3DOverlordAircraftDraw::doDrawModule(const Matrix3D* transformMtx) if (me && me->getContain()) { if (getW3DOverlordAircraftDrawModuleData()->m_hasMultiAddOns) { const ContainedItemsList* addOns = me->getContain()->getAddOnList(); - DEBUG_LOG((">>> W3DOverlordAircraftDraw::doDrawModule - addOns.size = %d", addOns->size())); for (ContainedItemsList::const_iterator it = addOns->begin(); it != addOns->end(); it++) { Drawable* riderDraw = (*it)->getDrawable(); riderDraw->setColorTintEnvelope(*getDrawable()->getColorTintEnvelope());