diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index d91bedf41d..322d1a668f 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -677,5 +677,10 @@ static PoolSizeRec PoolSizes[] = { "SmudgeSet", 32, 32}, { "Smudge", 128, 32}, { "ResetSpecialPowerTimerWhileAliveUpdate", 8, 8 }, + { "DroneCarrierAIUpdate", 8, 8 }, + { "DroneCarrierSlavedUpdate", 8, 8 }, + { "DroneCarrierContain", 8, 8 }, + { "W3DDependencyCarrierDraw", 16, 16 }, + { "CarrierDroneAIUpdate", 16, 16 }, { 0, 0, 0 } }; diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index fa5f444cab..e25aabab8f 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -335,6 +335,7 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/DelayedUpgradeBehavior.h Include/GameLogic/Module/FlammableUpdate.h Include/GameLogic/Module/FlightDeckBehavior.h + Include/GameLogic/Module/DroneCarrierAIUpdate.h Include/GameLogic/Module/FloatUpdate.h Include/GameLogic/Module/FXListDie.h Include/GameLogic/Module/GarrisonContain.h @@ -497,6 +498,9 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/WorkerAIUpdate.h Include/GameLogic/Module/KodiakDeploymentUpdate.h Include/GameLogic/Module/KodiakUpdate.h + Include/GameLogic/Module/DroneCarrierSlavedUpdate.h + Include/GameLogic/Module/DroneCarrierContain.h + Include/GameLogic/Module/CarrierDroneAIUpdate.h Include/GameLogic/Object.h Include/GameLogic/ObjectCreationList.h Include/GameLogic/ObjectIter.h @@ -946,6 +950,7 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Contain/RiderChangeContain.cpp Source/GameLogic/Object/Contain/TransportContain.cpp Source/GameLogic/Object/Contain/TunnelContain.cpp + Source/GameLogic/Object/Contain/DroneCarrierContain.cpp Source/GameLogic/Object/Create/CreateModule.cpp Source/GameLogic/Object/Create/GrantUpgradeCreate.cpp Source/GameLogic/Object/Create/LockWeaponCreate.cpp @@ -1017,6 +1022,8 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Update/AIUpdate/WanderAIUpdate.cpp Source/GameLogic/Object/Update/AIUpdate/TeleporterAIUpdate.cpp Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp + Source/GameLogic/Object/Update/AIUpdate/DroneCarrierAIUpdate.cpp + Source/GameLogic/Object/Update/AIUpdate/CarrierDroneAIUpdate.cpp Source/GameLogic/Object/Update/AnimationSteeringUpdate.cpp Source/GameLogic/Object/Update/AssistedTargetingUpdate.cpp Source/GameLogic/Object/Update/AutoDepositUpdate.cpp @@ -1094,6 +1101,7 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Update/ArmorDamageScalarUpdate.cpp Source/GameLogic/Object/Update/KodiakDeploymentUpdate.cpp Source/GameLogic/Object/Update/KodiakUpdate.cpp + Source/GameLogic/Object/Update/DroneCarrierSlavedUpdate.cpp Source/GameLogic/Object/Upgrade/ActiveShroudUpgrade.cpp Source/GameLogic/Object/Upgrade/ArmorUpgrade.cpp Source/GameLogic/Object/Upgrade/CommandSetUpgrade.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/CarrierDroneAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/CarrierDroneAIUpdate.h new file mode 100644 index 0000000000..513242436f --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/CarrierDroneAIUpdate.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Common/STLTypedefs.h" +#include "Common/GameMemory.h" +#include "GameLogic/AIStateMachine.h" +#include "GameLogic/Module/AIUpdate.h" + + +//------------------------------------------------------------------------------------------------- +class CarrierDroneAIUpdate : public AIUpdateInterface +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(CarrierDroneAIUpdate, "CarrierDroneAIUpdate") + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(CarrierDroneAIUpdate, AIUpdateModuleData) + + //virtual UpdateSleepTime update(); + +public: + + CarrierDroneAIUpdate(Thing* thing, const ModuleData* moduleData); + // virtual destructor prototype provided by memory pool declaration + + //stop contained drones from attacking + virtual void privateAttackPosition(const Coord3D* pos, Int maxShotsToFire, CommandSourceType cmdSource) override; ///< attack given spot +}; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierAIUpdate.h new file mode 100644 index 0000000000..6021d4dc1e --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierAIUpdate.h @@ -0,0 +1,112 @@ +// FILE: DroneCarrierAIUpdate.h ///////////////////////////////////////////////////////////////////// +// Desc: Mobile drone carrier. +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef __DRONE_CARRIER_AI_UPDATE_H +#define __DRONE_CARRIER_AI_UPDATE_H + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "GameLogic/Module/BehaviorModule.h" +#include "GameLogic/Module/DieModule.h" +#include "GameLogic/Module/AIUpdate.h" +#include "GameLogic/Module/SpawnBehavior.h" + + +//------------------------------------------------------------------------------------------------- +class DroneCarrierAIUpdateModuleData : public AIUpdateModuleData +{ +public: + Int m_slots; + UnsignedInt m_respawn_time; + + std::vector m_spawnTemplateNameData; + + DroneCarrierAIUpdateModuleData(); + + static void buildFieldParse(MultiIniFieldParse& p); +}; + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +class DroneCarrierAIUpdate : public AIUpdateInterface, + public SpawnBehaviorInterface, + public DieModuleInterface + //public ExitInterface +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DroneCarrierAIUpdate, "DroneCarrierAIUpdate") + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(DroneCarrierAIUpdate, DroneCarrierAIUpdateModuleData) + +public: + + DroneCarrierAIUpdate(Thing* thing, const ModuleData* moduleData); + // virtual destructor prototype provided by memory pool declaration + + static Int getInterfaceMask() { return UpdateModule::getInterfaceMask() | (MODULEINTERFACE_DIE); } + + // BehaviorModule + virtual DieModuleInterface* getDie() { return this; } + + // UpdateModule + virtual UpdateSleepTime update(); + + // SpawnBehaviorInterface + virtual SpawnBehaviorInterface* getSpawnBehaviorInterface() { return this; } + + virtual Bool maySpawnSelfTaskAI(Real maxSelfTaskersRatio) { return false; }; + virtual void onSpawnDeath(ObjectID deadSpawn, DamageInfo* damageInfo); + virtual Object* getClosestSlave(const Coord3D* pos); + virtual void orderSlavesToAttackTarget(Object* target, Int maxShotsToFire, CommandSourceType cmdSource); + virtual void orderSlavesToAttackPosition(const Coord3D* pos, Int maxShotsToFire, CommandSourceType cmdSource); + virtual CanAttackResult getCanAnySlavesAttackSpecificTarget(AbleToAttackType attackType, const Object* target, CommandSourceType cmdSource); + virtual CanAttackResult getCanAnySlavesUseWeaponAgainstTarget(AbleToAttackType attackType, const Object* victim, const Coord3D* pos, CommandSourceType cmdSource); + virtual Bool canAnySlavesAttack(); + virtual void orderSlavesToGoIdle(CommandSourceType cmdSource); + virtual void orderSlavesDisabledUntil(DisabledType type, UnsignedInt frame); + virtual void orderSlavesToClearDisabled(DisabledType type); + virtual void giveSlavesStealthUpgrade(Bool grantStealth); + virtual Bool areAllSlavesStealthed() const; + virtual void revealSlaves(); + virtual Bool doSlavesHaveFreedom() const { return false; }; + + // DieModule + virtual void onDie(const DamageInfo* damageInfo); + + // AIUpdateInterface + virtual void aiDoCommand(const AICommandParms* parms); + + static bool isDroneCombatReady(Object* drone); + +private: + + Bool is_full(); ///< has this carrier an open spot? + Bool createSpawn(); ///< Actual work of creating a guy + + void deployDrones(); ///< let all drones exit the transport + void retrieveDrones(); ///< order all drones to go back to the transport + + void propagateOrdersToDrones(); + void propagateOrderToSpecificDrone(Object* drone); + + //works for both Object and Coord3D + template + bool targetInRange(const T* target); + + const ThingTemplate* m_spawnTemplate; ///< What it is I spawn + std::vector m_spawnIDs; ///< IDs of currently active drones + std::vector::const_iterator m_templateNameIterator; + UnsignedInt m_rebuild_time; // which frame a drone will be rebuilt. 0 if not active + Bool m_active; ///< Am I currently turned on + Bool m_initial_spawns; ///< Have initial drones be spawned? (first update frame) + ObjectID m_designatedTarget; + AICommandType m_designatedCommand; + Coord3D m_designatedPosition; +}; + +template bool DroneCarrierAIUpdate::targetInRange(const Object* target); +template bool DroneCarrierAIUpdate::targetInRange(const Coord3D* target); + +#endif // __DRONE_CARRIER_AI_UPDATE_H + diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierContain.h new file mode 100644 index 0000000000..0d6d620445 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierContain.h @@ -0,0 +1,64 @@ +// FILE: DroneCarrierContain.h //////////////////////////////////////////////////////////////////////// +// Desc: expanded transport contain to work with drone carrier +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef __DRONE_CARRIER_CONTAIN_H_ +#define __DRONE_CARRIER_CONTAIN_H_ + +// USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// +#include "GameLogic/Module/OpenContain.h" +#include "GameLogic/Module/TransportContain.h" +#include "GameLogic/Module/GarrisonContain.h" + +//------------------------------------------------------------------------------------------------- +class DroneCarrierContainModuleData: public TransportContainModuleData +{ +public: + Real m_launchVelocityBoost; + + DroneCarrierContainModuleData(); + + static void buildFieldParse(MultiIniFieldParse& p); + +}; + +//------------------------------------------------------------------------------------------------- +class DroneCarrierContain: public TransportContain +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DroneCarrierContain, "DroneCarrierContain") + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(DroneCarrierContain, DroneCarrierContainModuleData) + +public: + + DroneCarrierContain(Thing* thing, const ModuleData* moduleData); + // virtual destructor prototype provided by memory pool declaration + + //Only allow slaved units in + virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; + + virtual Bool isEnclosingContainerFor(const Object* obj) const { return true; } //TODO param in module + + virtual Bool isPassengerAllowedToFire(ObjectID id = INVALID_ID) const override; ///< Hey, can I shoot out of this container? + + //support for specific exit bones + virtual void onRemoving(Object* obj) override; + virtual void onContaining(Object* obj, Bool wasSelected) override; ///< object now contains 'obj' + + virtual short getRiderSlot(ObjectID riderID) const override; + virtual short getPortableSlot(ObjectID portableID) const override; + virtual const ContainedItemsList* getAddOnList() const override; + virtual ContainedItemsList* getAddOnList() override; + + // Called from the AI update to reload the contained drones + void updateContainedReloadingStatus(); + +protected: + + // Saves slot assignement and frame when entered + std::vector> m_contained_units; +}; + +#endif // __TransportContain_H_ diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierSlavedUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierSlavedUpdate.h new file mode 100644 index 0000000000..640411cbd1 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DroneCarrierSlavedUpdate.h @@ -0,0 +1,40 @@ + +// FILE: DroneCarrierSlavedUpdate.cpp ///////////////////////////////////////////////////////////////////////// +// Desc: expanded Slaved update to work with drone carrier +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once + +#ifndef _DRONE_CARRIER_SLAVED_UPDATE_H_ +#define _DRONE_CARRIER_SLAVED_UPDATE_H_ +#include "Common/INI.h" +#include "GameLogic/Module/UpdateModule.h" +#include "GameLogic/Module/SlavedUpdate.h" + +//------------------------------------------------------------------------------------------------- +class DroneCarrierSlavedUpdateModuleData : public SlavedUpdateModuleData +{ +public: + Real m_leashRange; + + DroneCarrierSlavedUpdateModuleData(); + static void buildFieldParse(MultiIniFieldParse& p); +}; + +class DroneCarrierSlavedUpdate : public SlavedUpdate +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DroneCarrierSlavedUpdate, "DroneCarrierSlavedUpdate") + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(DroneCarrierSlavedUpdate, DroneCarrierSlavedUpdateModuleData) + +public: + + DroneCarrierSlavedUpdate(Thing* thing, const ModuleData* moduleData); + // virtual destructor prototype provided by memory pool declaration + + virtual UpdateSleepTime update(); ///< Deciding whether or not to make new guys + + virtual SlavedUpdateInterface* getSlavedUpdateInterface() { return this; } + +}; + +#endif diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlavedUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlavedUpdate.h index 9bbc916415..98241464ca 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlavedUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlavedUpdate.h @@ -167,7 +167,7 @@ class SlavedUpdate : public UpdateModule, public SlavedUpdateInterface virtual UpdateSleepTime update(); ///< Deciding whether or not to make new guys -private: +protected: void startSlavedEffects( const Object *slaver ); ///< We have been marked as Slaved, so we can't be selected or move too far or other stuff void stopSlavedEffects(); ///< We are no longer slaved. diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TransportContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TransportContain.h index 2cb73f73ea..c6634e1d53 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TransportContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TransportContain.h @@ -109,10 +109,8 @@ class TransportContain : public OpenContain void letRidersUpgradeWeaponSet( void ); Bool m_payloadCreated; - -private: - Int m_extraSlotsInUse; + UnsignedInt m_frameExitNotBusy; }; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 429a8f1b9f..4582a189b6 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -478,6 +478,7 @@ class Object : public Thing, public Snapshot // Weapons & Damage ------------------------------------------------------------------------------------------------- void reloadAllAmmo(Bool now); Bool isOutOfAmmo() const; + Bool isFullAmmo() const; //added by OFS Bool hasAnyWeapon() const; Bool hasAnyDamageWeapon() const; //Kris: a should be used for real weapons that directly inflict damage... not deploy, hack, etc. Bool hasWeaponToDealDamageType(DamageType typeToDeal) const; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/WeaponSet.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/WeaponSet.h index 772d8a8cea..9a27b85ab7 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/WeaponSet.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/WeaponSet.h @@ -238,6 +238,7 @@ class WeaponSet : public Snapshot void updateWeaponSet(const Object* obj); void reloadAllAmmo(const Object *obj, Bool now); Bool isOutOfAmmo() const; + Bool isFullAmmo() const; // Added for OFS Bool hasAnyWeapon() const { return m_filledWeaponSlotMask != 0; } Bool hasAnyDamageWeapon() const { return m_hasDamageWeapon; } Bool hasWeaponToDealDamageType(DamageType typeToDeal) const { return m_totalDamageTypeMask.test(typeToDeal); } @@ -281,6 +282,7 @@ class WeaponSet : public Snapshot Weapon* getWeaponInWeaponSlot(WeaponSlotType wslot) const; + static ModelConditionFlags getModelConditionForWeaponSlot(WeaponSlotType wslot, WeaponSetConditionType a); }; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index b7d073b308..bbab7d75a5 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -204,6 +204,10 @@ #include "GameLogic/Module/CheckpointUpdate.h" #include "GameLogic/Module/EMPUpdate.h" #include "GameLogic/Module/ResetSpecialPowerTimerWhileAliveUpdate.h" +#include "GameLogic/Module/DroneCarrierAIUpdate.h" +#include "GameLogic/Module/DroneCarrierSlavedUpdate.h" +#include "GameLogic/Module/DroneCarrierContain.h" +#include "GameLogic/Module/CarrierDroneAIUpdate.h" // upgrade includes #include "GameLogic/Module/ActiveShroudUpgrade.h" @@ -395,6 +399,7 @@ void ModuleFactory::init( void ) addModule( JetSlowDeathBehavior ); addModule( RailroadBehavior ); addModule( SpawnBehavior ); + addModule( DroneCarrierContain ); // die modules addModule( DestroyDie ); @@ -503,6 +508,9 @@ void ModuleFactory::init( void ) addModule( PowerPlantUpdate ); addModule( CheckpointUpdate ); addModule( ResetSpecialPowerTimerWhileAliveUpdate ); + addModule( DroneCarrierAIUpdate ); + addModule( DroneCarrierSlavedUpdate ); + addModule( CarrierDroneAIUpdate ); // upgrade modules addModule( CostModifierUpgrade ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/DroneCarrierContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/DroneCarrierContain.cpp new file mode 100644 index 0000000000..73093466c2 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/DroneCarrierContain.cpp @@ -0,0 +1,406 @@ +// FILE: DroneCarrierContain.cpp ////////////////////////////////////////////////////////////////////// +// Desc: Contain module for drone carrier +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine + +#include "Common/Player.h" +#include "Common/ThingTemplate.h" +#include "Common/ThingFactory.h" +#include "Common/Xfer.h" +#include "GameClient/Drawable.h" +#include "GameLogic/AI.h" +#include "GameLogic/AIPathfind.h" +#include "GameLogic/Locomotor.h" +#include "GameLogic/Module/AIUpdate.h" +#include "GameLogic/Module/BodyModule.h" +#include "GameLogic/Module/PhysicsUpdate.h" +#include "GameLogic/Module/StealthUpdate.h" +#include "GameLogic/Module/TransportContain.h" +#include "GameLogic/Module/DroneCarrierContain.h" +#include "GameLogic/Object.h" +#include "GameLogic/Weapon.h" +#include "GameLogic/WeaponSetType.h" + + +DroneCarrierContainModuleData::DroneCarrierContainModuleData() : TransportContainModuleData() +{ + m_launchVelocityBoost = 0.0f; +} + +void DroneCarrierContainModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + TransportContainModuleData::buildFieldParse(p); + + static const FieldParse dataFieldParse[] = + { + { "LaunchVelocityBoost", INI::parseReal, NULL, offsetof(DroneCarrierContainModuleData, m_launchVelocityBoost) }, + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +DroneCarrierContain::DroneCarrierContain(Thing* thing, const ModuleData* moduleData) : + TransportContain(thing, moduleData) +{ + const TransportContainModuleData* data = dynamic_cast(moduleData); + if (data != nullptr) { + m_contained_units.reserve(data->m_slotCapacity); + for (size_t i = 0; i < data->m_slotCapacity; i++) { + m_contained_units.emplace_back(INVALID_ID, 0); + } + } +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +DroneCarrierContain::~DroneCarrierContain(void) +{ +} + +Bool DroneCarrierContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const +{ + // sanity + if (!rider) + return false; + + // no... actually, only OUR OWN units can be transported. + if (rider->getControllingPlayer() != getObject()->getControllingPlayer()) + return false; + + Int transportSlotCount = rider->getTransportSlotCount(); + + // if 0, this object isn't transportable. + if (transportSlotCount == 0) + return false; + + if (checkCapacity) + { + Int containMax = getContainMax(); + Int containCount = getContainCount(); + + /*if (!(m_extraSlotsInUse + containCount + transportSlotCount <= containMax)) { + return false; + }*/ + if (containCount >= containMax) { + return false; + } + } + + // Check if this is actually a slaved unit + for (BehaviorModule** update = rider->getBehaviorModules(); *update; ++update) + { + SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface(); + if (sdu != nullptr) + { + return sdu->getSlaverID() == getObject()->getID(); + } + } + + return false; +} + +Bool DroneCarrierContain::isPassengerAllowedToFire(ObjectID id) const +{ + return false; +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void DroneCarrierContain::onRemoving(Object* rider) +{ + //Intentionally not use TransportContain::onRemoving + OpenContain::onRemoving(rider); + + // object is no longer held inside a transport + rider->clearDisabled(DISABLED_HELD); + + const DroneCarrierContainModuleData* d = getDroneCarrierContainModuleData(); + + // give the object back a regular weapon + rider->clearWeaponBonusCondition(WEAPONBONUSCONDITION_CONTAINED); + rider->clearWeaponSetFlag(WEAPONSET_CONTAINED); + + Drawable* rider_draw = rider->getDrawable(); + if (rider_draw != nullptr) { + rider_draw->enableAmbientSound(true); + } + + // If we have a bone and no exit paths, put each rider at the numbered bone position + if (!d->m_exitBone.isEmpty() && d->m_numberOfExitPaths <= 0) + { + Drawable* draw = getObject()->getDrawable(); + if (draw) + { + Coord3D bonePos; + Matrix3D boneMat; + AsciiString boneName; + + //Int slot = static_cast(getContainCount()); + Int slot = getRiderSlot(rider->getID()); + if (slot >= 0) { + boneName.format("%s%02d", d->m_exitBone.str(), slot + 1); + + Int foundBones = draw->getPristineBonePositions(boneName.str(), 0, &bonePos, &boneMat, 1); + + if (foundBones > 0) { + Coord3D worldPos; + Matrix3D worldMat; + getObject()->convertBonePosToWorldPos(&bonePos, &boneMat, &worldPos, &worldMat); + rider->setPosition(&worldPos); + rider->setTransformMatrix(&worldMat); + } + } + } + } + + if (d->m_orientLikeContainerOnExit) + { + rider->setOrientation(getObject()->getOrientation()); + } + + if (d->m_keepContainerVelocityOnExit) + { + PhysicsBehavior* parent = getObject()->getPhysics(); + PhysicsBehavior* child = rider->getPhysics(); + if (parent && child) + { + Coord3D startingForce = *parent->getVelocity(); + Real mass = child->getMass(); + startingForce.x *= mass; + startingForce.y *= mass; + startingForce.z *= mass; + child->applyMotiveForce(&startingForce); + + Real pitchRate = child->getCenterOfMassOffset() * d->m_exitPitchRate; + child->setPitchRate(pitchRate); + } + } + + if (d->m_launchVelocityBoost > 0.0f) { + PhysicsBehavior* phys = rider->getPhysics(); + if (phys != nullptr) { + Coord3D dir; + rider->getUnitDirectionVector3D(dir); + Real mass = phys->getMass() * d->m_launchVelocityBoost; + dir.x *= mass; + dir.y *= mass; + dir.z *= mass; + phys->applyMotiveForce(&dir); + } + } + + Int transportSlotCount = rider->getTransportSlotCount(); + DEBUG_ASSERTCRASH(transportSlotCount > 0, ("Hmm, this object isnt transportable")); + m_extraSlotsInUse -= transportSlotCount - 1; + DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Hmm, bad slot count")); + + // when we are empty again, clear the model condition for loaded + if (getContainCount() == 0) + { + Drawable* draw = getObject()->getDrawable(); + + if (draw) + draw->clearModelConditionState(MODELCONDITION_LOADED); + + } // end if + + if (getObject()->isAboveTerrain()) + { + // temporarily mark the guy as being allowed to fall + // (overriding his locomotor's stick-to-ground attribute). + // this will be reset (by PhysicsBehavior) when he touches the ground. + PhysicsBehavior* physics = rider->getPhysics(); + if (physics) + physics->setAllowToFall(true); + } + + // AI might need help using this transport in a good way. Make the passengers aggressive. + //There is no computer player check since Aggressive only means something for computer players anyway + if (d->m_goAggressiveOnExit && rider->getAI()) + { + rider->getAI()->setAttitude(ATTITUDE_AGGRESSIVE); + } + if (getObject()->isEffectivelyDead()) { + scatterToNearbyPosition(rider); + } + if (d->m_resetMoodCheckTimeOnExit && rider->getAI()) + { + rider->getAI()->wakeUpAndAttemptToTarget(); + } + + // Remove unit from slot assign vector + for (size_t i = 0; i < m_contained_units.size(); i++) { + if (std::get<0>(m_contained_units[i]) == rider->getID()){ + m_contained_units[i] = { INVALID_ID, 0 }; + } + } + + + + m_frameExitNotBusy = TheGameLogic->getFrame() + d->m_exitDelay; +} + +void DroneCarrierContain::onContaining(Object* obj, Bool wasSelected) +{ + TransportContain::onContaining(obj, wasSelected); + + Drawable* draw = obj->getDrawable(); + if (draw != nullptr) { + draw->enableAmbientSound(false); + } + + // Assing in first free slot + for (size_t i = 0; i < m_contained_units.size(); i++) { + if (std::get<0>(m_contained_units[i]) == INVALID_ID) { + m_contained_units[i] = { obj->getID(), TheGameLogic->getFrame() }; + break; + } + } +} + +short DroneCarrierContain::getRiderSlot(ObjectID riderID) const +{ + for (size_t i = 0; i < m_contained_units.size(); i++) { + if (std::get<0>(m_contained_units[i]) == riderID) { + return i; + } + } + return -1; +} + +short DroneCarrierContain::getPortableSlot(ObjectID portableID) const +{ + return getRiderSlot(portableID); +} + +const ContainedItemsList* DroneCarrierContain::getAddOnList() const +{ + return &m_containList; +} + +ContainedItemsList* DroneCarrierContain::getAddOnList() +{ + return &m_containList; +} + +// Similar to jet ai when parking +void DroneCarrierContain::updateContainedReloadingStatus() +{ + UnsignedInt now = TheGameLogic->getFrame(); + + for (size_t i = 0; i < m_contained_units.size(); i++) { + const auto& [objectID, enteredFrame] = m_contained_units[i]; + if (objectID != INVALID_ID) { + + Object* drone = TheGameLogic->findObjectByID(objectID); + if (drone != nullptr) { + for (Int i = 0; i < WEAPONSLOT_COUNT; ++i) + { + Weapon* w = drone->getWeaponInWeaponSlot((WeaponSlotType)i); + if (w == NULL) + continue; + + Int reloadTime = w->getClipReloadTime(drone); + UnsignedInt reloadDone = enteredFrame + reloadTime; + + if (now >= reloadDone) + w->setClipPercentFull(1.0f, false); + else + w->setClipPercentFull((Real)(reloadTime - (reloadDone - now)) / reloadTime, false); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierContain::crc(Xfer* xfer) +{ + + // extend base class + TransportContain::crc(xfer); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ + // ------------------------------------------------------------------------------------------------ +void DroneCarrierContain::xfer(Xfer* xfer) +{ + // extend base class + TransportContain::xfer(xfer); + + //xfer the contain vector + // xfer the count of the vector + UnsignedShort listCount = m_contained_units.size(); + xfer->xferUnsignedShort(&listCount); + + // xfer vector data + std::tuple entry; + if (xfer->getXferMode() == XFER_SAVE || xfer->getXferMode() == XFER_CRC) + { + // save all tuples + std::vector< std::tuple >::const_iterator it; + for (it = m_contained_units.begin(); it != m_contained_units.end(); ++it) + { + + entry = *it; + xfer->xferObjectID(&std::get<0>(entry)); + xfer->xferUnsignedInt(&std::get<1>(entry)); + + } // end for + + } // end if, save + else if (xfer->getXferMode() == XFER_LOAD) + { + m_contained_units.clear(); + m_contained_units.reserve(listCount); + + // sanity, the list should be empty before we transfer more data into it + if (m_contained_units.size() != 0) + { + + DEBUG_CRASH(("DroneCarrierContain::xfer - object vector should be empty before loading")); + throw XFER_LIST_NOT_EMPTY; + + } // end if + + // read all ids + for (UnsignedShort i = 0; i < listCount; ++i) + { + + xfer->xferObjectID(&std::get<0>(entry)); + xfer->xferUnsignedInt(&std::get<1>(entry)); + m_contained_units.push_back(entry); + + } // end for, i + + } // end else if + else + { + + DEBUG_CRASH(("DroneCarrierContain::xfer - Unknown xfer mode '%d'", xfer->getXferMode())); + throw XFER_MODE_UNKNOWN; + + } // end else + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierContain::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 7da79cec6b..85e884c061 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -1277,6 +1277,12 @@ Bool Object::isOutOfAmmo() const return m_weaponSet.isOutOfAmmo(); } +//============================================================================= +Bool Object::isFullAmmo() const +{ + return m_weaponSet.isFullAmmo(); +} + //============================================================================= Bool Object::hasAnyWeapon() const { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/CarrierDroneAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/CarrierDroneAIUpdate.cpp new file mode 100644 index 0000000000..47c912d774 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/CarrierDroneAIUpdate.cpp @@ -0,0 +1,78 @@ +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine + +#include "Common/CRCDebug.h" +#include "Common/Player.h" +#include "Common/Team.h" +#include "Common/ThingFactory.h" +#include "Common/ThingTemplate.h" +#include "Common/Xfer.h" + +#include "GameClient/Drawable.h" +#include "GameClient/ParticleSys.h" + +#include "GameLogic/AI.h" +#include "GameLogic/AIPathfind.h" +#include "GameLogic/GameLogic.h" +#include "GameLogic/Object.h" +#include "GameLogic/PartitionManager.h" +#include "GameLogic/TerrainLogic.h" +#include "GameLogic/Weapon.h" + +#include "GameLogic/Module/CarrierDroneAIUpdate.h" +#include "GameLogic/Module/JetAIUpdate.h" +#include "GameLogic/Module/ProductionUpdate.h" +#include "GameLogic/Module/ContainModule.h" + +CarrierDroneAIUpdate::CarrierDroneAIUpdate(Thing* thing, const ModuleData* moduleData) : AIUpdateInterface(thing, moduleData) +{ +} + +void CarrierDroneAIUpdate::privateAttackPosition(const Coord3D* pos, Int maxShotsToFire, CommandSourceType cmdSource) +{ + if (getObject() && !getObject()->isContained()) { + AIUpdateInterface::privateAttackPosition(pos, maxShotsToFire, cmdSource); + } +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +CarrierDroneAIUpdate::~CarrierDroneAIUpdate(void) +{ +} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void CarrierDroneAIUpdate::crc(Xfer* xfer) +{ + // extend base class + AIUpdateInterface::crc(xfer); +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ + // ------------------------------------------------------------------------------------------------ +void CarrierDroneAIUpdate::xfer(Xfer* xfer) +{ + + // version + XferVersion currentVersion = 2; + XferVersion version = currentVersion; + xfer->xferVersion(&version, currentVersion); + + // extend base class + AIUpdateInterface::xfer(xfer); + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void CarrierDroneAIUpdate::loadPostProcess(void) +{ + // extend base class + AIUpdateInterface::loadPostProcess(); +} // end loadPostProcess diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DroneCarrierAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DroneCarrierAIUpdate.cpp new file mode 100644 index 0000000000..6bb0ccd8d7 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DroneCarrierAIUpdate.cpp @@ -0,0 +1,776 @@ +// FILE: DroneCarrierAIUpdate.cpp /////////////////////////////////////////////////////////////////// +// Desc: Handles aircraft movement and parking behavior for aircraft carriers. +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine + +#include "Common/CRCDebug.h" +#include "Common/Player.h" +#include "Common/Team.h" +#include "Common/GameState.h" +#include "Common/ThingFactory.h" +#include "Common/ThingTemplate.h" +#include "Common/Xfer.h" + +#include "GameClient/Drawable.h" +#include "GameClient/ParticleSys.h" + +#include "GameLogic/AI.h" +#include "GameLogic/AIPathfind.h" +#include "GameLogic/GameLogic.h" +#include "GameLogic/Object.h" +#include "GameLogic/PartitionManager.h" +#include "GameLogic/TerrainLogic.h" +#include "GameLogic/Weapon.h" + +#include "GameLogic/Module/DroneCarrierAIUpdate.h" +#include "GameLogic/Module/JetAIUpdate.h" +#include "GameLogic/Module/ProductionUpdate.h" +#include "GameLogic/Module/ContainModule.h" +#include "GameLogic/Module/DroneCarrierContain.h" + + +DroneCarrierAIUpdateModuleData::DroneCarrierAIUpdateModuleData() +{ + m_respawn_time = 0; + m_spawnTemplateNameData.clear(); +} +//------------------------------------------------------------------------------------------------- +void DroneCarrierAIUpdateModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + AIUpdateModuleData::buildFieldParse(p); + + static const FieldParse dataFieldParse[] = + { + { "RespawnTime", INI::parseDurationUnsignedInt, NULL, offsetof(DroneCarrierAIUpdateModuleData, m_respawn_time) }, + { "DroneTemplateName",INI::parseAsciiStringVectorAppend,NULL, offsetof(DroneCarrierAIUpdateModuleData, m_spawnTemplateNameData) }, + { "Slots", INI::parseInt, NULL, offsetof(DroneCarrierAIUpdateModuleData, m_slots) }, + + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +DroneCarrierAIUpdate::DroneCarrierAIUpdate(Thing* thing, const ModuleData* moduleData) : AIUpdateInterface(thing, moduleData) +{ + const DroneCarrierAIUpdateModuleData* md = getDroneCarrierAIUpdateModuleData(); + + m_templateNameIterator = md->m_spawnTemplateNameData.begin(); + m_spawnTemplate = TheThingFactory->findTemplate(*m_templateNameIterator); + m_rebuild_time = 0; + m_active = true; + m_initial_spawns = false; + + m_designatedTarget = INVALID_ID; + m_designatedCommand = AICMD_NO_COMMAND; + m_designatedPosition.zero(); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +DroneCarrierAIUpdate::~DroneCarrierAIUpdate(void) +{ +} + +Bool DroneCarrierAIUpdate::createSpawn() +{ + Object* parent = getObject(); + const DroneCarrierAIUpdateModuleData* md = getDroneCarrierAIUpdateModuleData(); + ContainModuleInterface* contain = parent->getContain(); + if (contain == nullptr) { + DEBUG_CRASH(("DroneCarrierAIUpdate requires unit to have a contain module!")); + return false; + } + if (contain->getContainMax() < md->m_slots) { + DEBUG_CRASH(("DroneCarrierAIUpdate requires unit to have a contain module with equal or more slots!")); + return false; + } + + Object* newSpawn = NULL; + + m_spawnTemplate = TheThingFactory->findTemplate(*m_templateNameIterator); + + newSpawn = TheThingFactory->newObject(m_spawnTemplate, parent->getTeam()); // just a little worried about this... + + // Count this unit towards our score. + newSpawn->getControllingPlayer()->onUnitCreated(parent, newSpawn); + + ++m_templateNameIterator; + if (m_templateNameIterator == md->m_spawnTemplateNameData.end()) + { + m_templateNameIterator = md->m_spawnTemplateNameData.begin(); + } + + newSpawn->setProducer(parent); + + // If they have a SlavedUpdate, then I have to tell them who their daddy is from now on. + for (BehaviorModule** update = newSpawn->getBehaviorModules(); *update; ++update) + { + SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface(); + if (sdu != NULL) + { + sdu->onEnslave(parent); + break; + } + } + m_spawnIDs.push_back(newSpawn->getID()); + + contain->addToContain(newSpawn); + return TRUE; +} + +Bool DroneCarrierAIUpdate::is_full() { + const DroneCarrierAIUpdateModuleData* md = getDroneCarrierAIUpdateModuleData(); + return m_spawnIDs.size() >= md->m_slots; +} + +Object* DroneCarrierAIUpdate::getClosestSlave(const Coord3D* pos) +{ + Object* closest = NULL; + Real closestDistance; + + for (const ObjectID & spawn_id : m_spawnIDs) { + Object* obj = TheGameLogic->findObjectByID(spawn_id); + if (obj) + { + Real distance = ThePartitionManager->getDistanceSquared(obj, pos, FROM_CENTER_2D); + + if (!closest || closestDistance > distance) + { + closest = obj; + closestDistance = distance; + } + } + } + return closest; //Could be null! +} + +void DroneCarrierAIUpdate::onSpawnDeath(ObjectID deadSpawn, DamageInfo* damageInfo) +{ + auto it = std::find(m_spawnIDs.begin(), m_spawnIDs.end(), deadSpawn); + + // If the iterator is at the end, we didn't find deadSpawn, so bail out. + // Otherwise, bad crash stuff will happen. + if (it == m_spawnIDs.end()) + return; + + //When one dies, you push (now + delay) as the time a new one should be made + const DroneCarrierAIUpdateModuleData* md = getDroneCarrierAIUpdateModuleData(); + + //trigger rebuilt timer when not active, carrier must have open slot if one died + if (m_rebuild_time == 0) { + m_rebuild_time = TheGameLogic->getFrame() + md->m_respawn_time; + } + + m_spawnIDs.erase(it); +} + +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::orderSlavesToAttackTarget(Object* target, Int maxShotsToFire, CommandSourceType cmdSource) +{ + for (const auto & slave_id: m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + AIUpdateInterface* ai = obj->getAI(); + if (ai) + { + ai->aiForceAttackObject(target, maxShotsToFire, cmdSource); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::orderSlavesToAttackPosition(const Coord3D* pos, Int maxShotsToFire, CommandSourceType cmdSource) +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + AIUpdateInterface* ai = obj->getAI(); + if (ai) + { + ai->aiAttackPosition(pos, maxShotsToFire, cmdSource); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::orderSlavesToGoIdle(CommandSourceType cmdSource) +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + AIUpdateInterface* ai = obj->getAI(); + if (ai) + { + ai->aiIdle(cmdSource); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::orderSlavesDisabledUntil(DisabledType type, UnsignedInt frame) +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + AIUpdateInterface* ai = obj->getAI(); + if (ai) + { + ai->aiIdle(CMD_FROM_AI); + } + obj->setDisabledUntil(type, frame); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::orderSlavesToClearDisabled(DisabledType type) +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + obj->clearDisabled(type); + } + } +} + +// ------------------------------------------------------------------------------------------------ +CanAttackResult DroneCarrierAIUpdate::getCanAnySlavesAttackSpecificTarget(AbleToAttackType attackType, const Object* target, CommandSourceType cmdSource) +{ + Bool invalidShot = FALSE; + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + CanAttackResult result = obj->getAbleToAttackSpecificObject(attackType, target, cmdSource); + + switch (result) + { + case ATTACKRESULT_POSSIBLE: + case ATTACKRESULT_POSSIBLE_AFTER_MOVING: + return result; + + case ATTACKRESULT_NOT_POSSIBLE: + break; + + case ATTACKRESULT_INVALID_SHOT: + invalidShot = TRUE; + break; + + default: + DEBUG_CRASH(("DroneCarrierAIUpdate::getCanAnySlavesAttackSpecificTarget encountered unhandled CanAttackResult of %d. Treating as not possible...", result)); + break; + } + } + } + //Prioritize the reasonings! + if (invalidShot) + { + return ATTACKRESULT_INVALID_SHOT; + } + return ATTACKRESULT_NOT_POSSIBLE; +} + +// ------------------------------------------------------------------------------------------------ +CanAttackResult DroneCarrierAIUpdate::getCanAnySlavesUseWeaponAgainstTarget(AbleToAttackType attackType, const Object* victim, const Coord3D* pos, CommandSourceType cmdSource) +{ + Bool invalidShot = FALSE; + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + CanAttackResult result = obj->getAbleToUseWeaponAgainstTarget(attackType, victim, pos, cmdSource); + + switch (result) + { + case ATTACKRESULT_POSSIBLE: + case ATTACKRESULT_POSSIBLE_AFTER_MOVING: + return result; + + case ATTACKRESULT_NOT_POSSIBLE: + break; + + case ATTACKRESULT_INVALID_SHOT: + invalidShot = TRUE; + break; + + default: + DEBUG_CRASH(("DroneCarrierAIUpdate::getCanAnySlavesUseWeaponAgainstTarget encountered unhandled CanAttackResult of %d. Treating as not possible...", result)); + break; + } + } + } + //Prioritize the reasonings! + if (invalidShot) + { + return ATTACKRESULT_INVALID_SHOT; + } + return ATTACKRESULT_NOT_POSSIBLE; +} + +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::giveSlavesStealthUpgrade(Bool grantStealth) +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + obj->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_CAN_STEALTH), grantStealth); + } + } +} + +Bool DroneCarrierAIUpdate::canAnySlavesAttack() +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj) + { + if (obj->isAbleToAttack()) + { + return true; + } + } + } + return false; +} +// ------------------------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------------------------- +Bool DroneCarrierAIUpdate::areAllSlavesStealthed() const +{ + Object* currentSpawn; + + for (const auto& slave_id : m_spawnIDs) + { + currentSpawn = TheGameLogic->findObjectByID((slave_id)); + if (currentSpawn) + { + const StealthUpdate* stealthUpdate = currentSpawn->getStealth(); + if (!stealthUpdate || !stealthUpdate->allowedToStealth(currentSpawn)) + { + return FALSE; + } + } + } + + return TRUE; //0 or more spawns are ALL stealthed... I suppose if you have NO spawns, then they are considered stealthed ;) +} + +//------------------------------------------------------------------------------------------------- +void DroneCarrierAIUpdate::revealSlaves() +{ + Object* currentSpawn; + + for (const auto& slave_id : m_spawnIDs) + { + currentSpawn = TheGameLogic->findObjectByID((slave_id)); + if (currentSpawn) + { + StealthUpdate* stealthUpdate = currentSpawn->getStealth(); + if (stealthUpdate) + { + stealthUpdate->markAsDetected(); + } + } + } +} + +/*************************************************/ +/* DIE */ +//------------------------------------------------------------------------------------------------- +void DroneCarrierAIUpdate::onDie(const DamageInfo* damageInfo) +{ + // kill all drone slaves + for (const auto& slave_id : m_spawnIDs) + { + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj == NULL || obj->isEffectivelyDead()) + continue; + + //TODO config option for disabled effect? + obj->setDisabled(DISABLED_UNMANNED); + if (obj->getAI()) + obj->getAI()->aiIdle(CMD_FROM_AI); + //obj->kill(); + } +} + +/** +* Check if drone has ammo and is repaired depending on combat state +*/ +bool DroneCarrierAIUpdate::isDroneCombatReady(Object* drone) { + if (drone == nullptr) return false; + if (drone->isContained()) { + // Contained drone must be repaired before getting back to fight + BodyModuleInterface* body = drone->getBodyModule(); + if (body) { + if (body->getHealth() < body->getMaxHealth()) { + DEBUG_LOG(("Drone %d is contained annd not fully healed", drone->getID())); + return false; + } + } + if (drone->isFullAmmo()) { + DEBUG_LOG(("Drone %d is contained and full ammo", drone->getID())); + } + else { + DEBUG_LOG(("Drone %d is contained and but NOT full ammo", drone->getID())); + } + return drone->isFullAmmo(); + } + else { + if (drone->isOutOfAmmo()) { + DEBUG_LOG(("Drone %d is outside and out of ammo", drone->getID())); + } + else { + DEBUG_LOG(("Drone %d is outside and has ammo", drone->getID())); + } + + // if not contained we can fight as long as we have ammo + return !drone->isOutOfAmmo(); + } + return false; +} + +/****************************************/ +void DroneCarrierAIUpdate::deployDrones() +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* carrier = getObject(); + Object* obj = TheGameLogic->findObjectByID(slave_id); + if (obj == NULL || obj->isEffectivelyDead()) + continue; + + if (obj->isContained() && isDroneCombatReady(obj)) { + AIUpdateInterface* ai = obj->getAI(); + if (ai != nullptr) { + ai->aiExit(carrier, CMD_FROM_AI); + } + } + } +} + +void DroneCarrierAIUpdate::retrieveDrones() +{ + //Order all outside drones back inside! + for (const auto& slave_id : m_spawnIDs) + { + Object* member = TheGameLogic->findObjectByID(slave_id); + AIUpdateInterface* ai = member ? member->getAI() : NULL; + if (member && ai) + { + Bool contained = member->isContained(); + if (!contained) + { + if (ai->getAIStateType() != AI_ENTER) { + ai->aiEnter(getObject(), CMD_FROM_AI); + } + } + } + } +} + +void DroneCarrierAIUpdate::propagateOrderToSpecificDrone(Object* drone) +{ + if (drone != nullptr) + { + AIUpdateInterface* ai = drone->getAI(); + if (ai != nullptr) + { + Object* target = TheGameLogic->findObjectByID(m_designatedTarget); + + AICommandType cmd = isDroneCombatReady(drone) ? m_designatedCommand : AICMD_IDLE; + + bool contained = drone->isContained(); + switch (cmd) + { + case AICMD_GUARD_POSITION: + if (!contained) { + ai->aiGuardPosition(&m_designatedPosition, GUARDMODE_NORMAL, CMD_FROM_AI); + } + else { + ai->aiExit(getObject(), CMD_FROM_AI); + } + break; + case AICMD_ATTACK_POSITION: + if (!contained) { + ai->aiAttackPosition(&m_designatedPosition, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI); + } + else { + ai->aiExit(getObject(), CMD_FROM_AI); + } + break; + case AICMD_FORCE_ATTACK_OBJECT: + case AICMD_ATTACK_OBJECT: + if (!contained) { + ai->aiForceAttackObject(target, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER); + } + else { + ai->aiExit(getObject(), CMD_FROM_AI); + } + break; + case AICMD_ATTACKMOVE_TO_POSITION: + if (!contained) { + ai->aiAttackMoveToPosition(&m_designatedPosition, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI); + } + else + { + ai->aiExit(getObject(), CMD_FROM_AI); + } + break; + case AICMD_IDLE: + if (!contained) { + ai->aiEnter(getObject(), CMD_FROM_AI); + } + else { + ai->aiIdle(CMD_FROM_AI); + } + break; + } + } + } +} + +template +bool DroneCarrierAIUpdate::targetInRange(const T* target) +{ + if (target == nullptr) return false; + + Object* carrier = getObject(); + Weapon* primaryWeapon = carrier->getWeaponInWeaponSlot(PRIMARY_WEAPON); + + // set a limit on how far drones are send out + if (primaryWeapon == nullptr) { + return true; + } + + return primaryWeapon->isWithinAttackRange(carrier, target); +} + +//------------------------------------------------------------------------------------------------- +void DroneCarrierAIUpdate::propagateOrdersToDrones() +{ + for (const auto& slave_id : m_spawnIDs) + { + Object* drone = TheGameLogic->findObjectByID(slave_id); + if (drone != nullptr) { + propagateOrderToSpecificDrone(drone); + } + } +} + +/**********************************************/ +/* AI Update */ +//------------------------------------------------------------------------------------------------- +void DroneCarrierAIUpdate::aiDoCommand(const AICommandParms* parms) +{ + //forward commands to drones + if (parms->m_cmdSource != CMD_FROM_AI) + { + //DEBUG_LOG(("DroneCarrier: getting order: %d", parms->m_cmd)); + //Now the only time we care about anything is if we were ordered to attack something or attack move. + switch (parms->m_cmd) + { + case AICMD_GUARD_POSITION: + m_designatedTarget = INVALID_ID; + m_designatedPosition.set(&parms->m_pos); + m_designatedCommand = parms->m_cmd; + break; + case AICMD_ATTACK_POSITION: + m_designatedTarget = INVALID_ID; + m_designatedPosition.set(&parms->m_pos); + m_designatedCommand = parms->m_cmd; + break; + case AICMD_FORCE_ATTACK_OBJECT: + case AICMD_ATTACK_OBJECT: + m_designatedTarget = parms->m_obj ? parms->m_obj->getID() : INVALID_ID; + m_designatedPosition.zero(); + m_designatedCommand = parms->m_cmd; + break; + case AICMD_ATTACKMOVE_TO_POSITION: + m_designatedTarget = INVALID_ID; + m_designatedPosition.set(&parms->m_pos); + m_designatedCommand = parms->m_cmd; + break; + case AICMD_IDLE: + m_designatedTarget = INVALID_ID; + m_designatedPosition.zero(); + m_designatedCommand = parms->m_cmd; + break; + default: + m_designatedCommand = AICMD_NO_COMMAND; + break; + } + } + + AIUpdateInterface::aiDoCommand( parms ); +} + + +UpdateSleepTime DroneCarrierAIUpdate::update() +{ + const DroneCarrierAIUpdateModuleData* data = getDroneCarrierAIUpdateModuleData(); + + // initially fill + if (!m_initial_spawns) { + for (size_t i = 0; i < data->m_slots; i++) { + createSpawn(); + } + m_initial_spawns = true; + } + + UnsignedInt now = TheGameLogic->getFrame(); + + if (m_rebuild_time != 0 && m_rebuild_time <= now && !is_full()) + { + if (createSpawn()) { + if (is_full()) { + m_rebuild_time = 0; + } + else { + m_rebuild_time = now + data->m_respawn_time; + } + } + } + + if (now % 5 == 0) { + + // check for reloading drones each 5th frame + Object* carrier = getObject(); + if (carrier != nullptr) { + + ContainModuleInterface* cmi = carrier->getContain(); + + if (cmi != nullptr) { + DroneCarrierContain* drone_contain = dynamic_cast(cmi); + if (drone_contain != nullptr) { + drone_contain->updateContainedReloadingStatus(); + } + } + } + } + + Object* my_target = getCurrentVictim(); + const Coord3D* my_target_pos = getCurrentVictimPos(); + if (targetInRange(my_target) || targetInRange(my_target_pos)) { + // send out contained drones + deployDrones(); + + //update orders + if (now % 5 == 0) { //do every 5th frame + switch (m_designatedCommand) { + case AICMD_GUARD_POSITION: + case AICMD_ATTACK_POSITION: + case AICMD_FORCE_ATTACK_OBJECT: + case AICMD_ATTACK_OBJECT: + case AICMD_ATTACKMOVE_TO_POSITION: + propagateOrdersToDrones(); + break; + default: + break; + } + } + } + + return AIUpdateInterface::update(); +} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::crc(Xfer* xfer) +{ + + // extend base class + AIUpdateInterface::crc(xfer); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version +*/ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::xfer(Xfer* xfer) +{ + AsciiString name; + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion(&version, currentVersion); + + // extend base class + AIUpdateInterface::xfer(xfer); + + // spawn template + name = m_spawnTemplate ? m_spawnTemplate->getName() : AsciiString::TheEmptyString; + xfer->xferAsciiString(&name); + if (xfer->getXferMode() == XFER_LOAD) + { + m_spawnTemplate = NULL; + if (name.isEmpty() == FALSE) + { + m_spawnTemplate = TheThingFactory->findTemplate(name); + if (m_spawnTemplate == NULL) + { + DEBUG_CRASH(("SpawnBehavior::xfer - Unable to find template '%s'", name.str())); + throw SC_INVALID_DATA; + + } // end if + } // end if + } // end if + + // spawn ids + xfer->xferSTLObjectIDVector(&m_spawnIDs); + + xfer->xferUnsignedInt(&m_rebuild_time); + xfer->xferBool(&m_active); + xfer->xferBool(&m_initial_spawns); + + xfer->xferObjectID(&m_designatedTarget); + + if (xfer->getXferMode() == XFER_LOAD) { + Int commandType; + xfer->xferInt(&commandType); + m_designatedCommand = (AICommandType)commandType; + } + else { + Int commandType = m_designatedCommand; + xfer->xferInt(&commandType); + } + + xfer->xferCoord3D(&m_designatedPosition); + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierAIUpdate::loadPostProcess(void) +{ + + // extend base class + AIUpdateInterface::loadPostProcess(); + +} // end loadPostProcess diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DroneCarrierSlavedUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DroneCarrierSlavedUpdate.cpp new file mode 100644 index 0000000000..0c754edd79 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DroneCarrierSlavedUpdate.cpp @@ -0,0 +1,188 @@ +#include "GameLogic/Module/AIUpdate.h" +#include "GameLogic/Module/DroneCarrierSlavedUpdate.h" +#include "GameLogic/Module/DroneCarrierAIUpdate.h" +#include "GameLogic/Object.h" + +#include "Common/RandomValue.h" +#include "Common/Xfer.h" +#include "Common/Team.h" +#include "Common/MiscAudio.h" +#include "GameClient/Drawable.h" +#include "GameClient/ParticleSys.h" +#include "GameLogic/AIPathfind.h" +#include "GameLogic/Damage.h" +#include "GameLogic/GameLogic.h" +#include "GameLogic/Locomotor.h" +#include "GameLogic/PartitionManager.h" +#include "GameLogic/TerrainLogic.h" +#include "GameLogic/Module/BodyModule.h" +#include "GameLogic/Module/SlavedUpdate.h" +#include "GameLogic/Weapon.h" +#include "Common/ThingTemplate.h" + +// copies from SlavedUpdate.cpp +#define STRAY_MULTIPLIER 2.0f // Multiplier from stating diestance from tunnel, to max distance from +const Real CLOSE_ENOUGH = 15; // Our moveTo commands and pathfinding can't handle people in the way, so quit trying to hump someone on your spot +const Real CLOSE_ENOUGH_SQR = (CLOSE_ENOUGH * CLOSE_ENOUGH); + + +DroneCarrierSlavedUpdateModuleData::DroneCarrierSlavedUpdateModuleData() +{ + m_leashRange = 0.0f; +} + +void DroneCarrierSlavedUpdateModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + SlavedUpdateModuleData::buildFieldParse(p); + static const FieldParse dataFieldParse[] = + { + { "LeashRange", INI::parseReal, NULL, offsetof(DroneCarrierSlavedUpdateModuleData, m_leashRange) }, + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +DroneCarrierSlavedUpdate::DroneCarrierSlavedUpdate(Thing* thing, const ModuleData* moduleData) : SlavedUpdate(thing, moduleData) +{ +} + +//------------------------------------------------------------------------------------------------- +DroneCarrierSlavedUpdate::~DroneCarrierSlavedUpdate(void) +{ +} + +UpdateSleepTime DroneCarrierSlavedUpdate::update(void) +{ + if (m_framesToWait > 0) + { + m_framesToWait--; + } + if (m_repairState == REPAIRSTATE_NONE) + { + if (m_framesToWait > 0) + { + return UPDATE_SLEEP_NONE; + } + m_framesToWait = SLAVED_UPDATE_RATE; + } + + if (m_slaver == INVALID_ID) + return UPDATE_SLEEP_NONE; + + const DroneCarrierSlavedUpdateModuleData* data = getDroneCarrierSlavedUpdateModuleData(); + Object* me = getObject(); + if (!me) + { + return UPDATE_SLEEP_NONE; + } + AIUpdateInterface* myAI = me->getAIUpdateInterface(); + if (!myAI) + { + return UPDATE_SLEEP_NONE; + } + Locomotor* locomotor = myAI->getCurLocomotor(); + if (!locomotor) + { + return UPDATE_SLEEP_NONE; + } + + Object* master = TheGameLogic->findObjectByID(m_slaver); + if (!master || master->isEffectivelyDead() || master->isDisabledByType(DISABLED_UNMANNED)) + { + stopSlavedEffects(); + + //Let's disable the drone so it crashes instead! + //Added special case code in physics falling to ensure death. + me->setDisabled(DISABLED_UNMANNED); + if (me->getAI()) + me->getAI()->aiIdle(CMD_FROM_AI); + + return UPDATE_SLEEP_NONE; + } + else + { + Team* masterTeam = master->getTeam(); + Team* myTeam = me->getTeam(); + if (masterTeam->getRelationship(myTeam) != ALLIES) + {//slaver must have been hijacked or something.. // we will join his team + me->defect(masterTeam, 0); + } + } + + if (data->m_stayOnSameLayerAsMaster) + me->setLayer(master->getLayer()); + + + Object* target = NULL; + AIUpdateInterface* masterAI = master->getAIUpdateInterface(); + if (masterAI && DroneCarrierAIUpdate::isDroneCombatReady(me)) + { + target = masterAI->getCurrentVictim(); + } + + AIUpdateInterface* ai = me->getAI(); + if (ai == nullptr) return UPDATE_SLEEP_NONE; + + if (data->m_attackRange && target != nullptr) + { + //Check distance to master + Real dist = ThePartitionManager->getDistanceSquared(me, master->getPosition(), FROM_CENTER_2D); + if (data->m_leashRange > 0.0f && dist > sqr(data->m_leashRange)) + { + //Call back when too far away + ai->aiEnter(master, CMD_FROM_AI); + } + else { + doAttackLogic(target); + } + return UPDATE_SLEEP_NONE; + } + + // No Target, go home to carrier + if (!me->isContained()) { + if (ai->isIdle()) { + ai->aiEnter(master, CMD_FROM_AI); + } + } + return UPDATE_SLEEP_NONE; +} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierSlavedUpdate::crc(Xfer* xfer) +{ + + // extend base class + SlavedUpdate::crc(xfer); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ + // ------------------------------------------------------------------------------------------------ +void DroneCarrierSlavedUpdate::xfer(Xfer* xfer) +{ + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion(&version, currentVersion); + + // extend base class + SlavedUpdate::xfer(xfer); + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void DroneCarrierSlavedUpdate::loadPostProcess(void) +{ + + // extend base class + SlavedUpdate::loadPostProcess(); + +} // end loadPostProcess diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp index e4b615d1fc..54b0804a27 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp @@ -1064,6 +1064,24 @@ Bool WeaponSet::isOutOfAmmo() const } return true; } +//------------------------------------------------------------------------------------------------- +Bool WeaponSet::isFullAmmo() const +{ + for (Int i = 0; i < WEAPONSLOT_COUNT; i++) + { + const Weapon* weapon = m_weapons[i]; + if (weapon == NULL) + continue; + if (weapon->getStatus() == RELOADING_CLIP) + { + return false; + } + if (weapon->getRemainingAmmo() < weapon->getClipSize()) { + return false; + } + } + return true; +} //------------------------------------------------------------------------------------------------- const Weapon* WeaponSet::findAmmoPipShowingWeapon() const diff --git a/GeneralsMD/Code/GameEngineDevice/CMakeLists.txt b/GeneralsMD/Code/GameEngineDevice/CMakeLists.txt index 895c4d0122..45ff9e102b 100644 --- a/GeneralsMD/Code/GameEngineDevice/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngineDevice/CMakeLists.txt @@ -29,6 +29,7 @@ set(GAMEENGINEDEVICE_SRC Include/W3DDevice/GameClient/Module/W3DTracerDraw.h Include/W3DDevice/GameClient/Module/W3DTreeDraw.h Include/W3DDevice/GameClient/Module/W3DTruckDraw.h + Include/W3DDevice/GameClient/Module/W3DDependencyCarrierDraw.h Include/W3DDevice/GameClient/TerrainTex.h Include/W3DDevice/GameClient/TileData.h Include/W3DDevice/GameClient/W3DAssetManager.h @@ -118,6 +119,7 @@ set(GAMEENGINEDEVICE_SRC Source/W3DDevice/GameClient/Drawable/Draw/W3DTracerDraw.cpp Source/W3DDevice/GameClient/Drawable/Draw/W3DTreeDraw.cpp Source/W3DDevice/GameClient/Drawable/Draw/W3DTruckDraw.cpp + Source/W3DDevice/GameClient/Drawable/Draw/W3DDependencyCarrierDraw.cpp Source/W3DDevice/GameClient/FlatHeightMap.cpp Source/W3DDevice/GameClient/GUI/Gadget/W3DCheckBox.cpp Source/W3DDevice/GameClient/GUI/Gadget/W3DComboBox.cpp diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DDependencyCarrierDraw.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DDependencyCarrierDraw.h new file mode 100644 index 0000000000..24290d456e --- /dev/null +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DDependencyCarrierDraw.h @@ -0,0 +1,30 @@ +// FILE: W3DDependencyCarrierDraw.h ///////////////////////////////////////////////////////////////////////// +// Desc: Draw as dependent when garrisoned in carrier +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "Common/ModelState.h" +#include "Common/DrawModule.h" +#include "W3DDevice/GameClient/Module/W3DDependencyModelDraw.h" + +//------------------------------------------------------------------------------------------------- +class W3DDependencyCarrierDraw : public W3DDependencyModelDraw +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(W3DDependencyCarrierDraw, "W3DDependencyCarrierDraw") + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(W3DDependencyCarrierDraw, W3DDependencyModelDrawModuleData) + +public: + + W3DDependencyCarrierDraw(Thing* thing, const ModuleData* moduleData); + // virtual destructor prototype provided by memory pool declaration + + virtual void doDrawModule(const Matrix3D* transformMtx) override; + virtual void notifyDrawModuleDependencyCleared();///< if you were waiting for something before you drew, it's ready now + virtual void adjustTransformMtx(Matrix3D& mtx) const override; + +protected: + +}; diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/Common/Thing/W3DModuleFactory.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/Common/Thing/W3DModuleFactory.cpp index 5f66ef65c2..d04a0797da 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/Common/Thing/W3DModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/Common/Thing/W3DModuleFactory.cpp @@ -48,6 +48,7 @@ #include "W3DDevice/GameClient/Module/W3DTracerDraw.h" #include "W3DDevice/GameClient/Module/W3DTreeDraw.h" #include "W3DDevice/GameClient/Module/W3DPropDraw.h" +#include "W3DDevice/GameClient/Module/W3DDependencyCarrierDraw.h" //------------------------------------------------------------------------------------------------- /** Initialize method */ @@ -78,5 +79,6 @@ void W3DModuleFactory::init( void ) addModule( W3DTankTruckDraw ); addModule( W3DTreeDraw ); addModule( W3DPropDraw ); + addModule( W3DDependencyCarrierDraw ); } // end init diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDependencyCarrierDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDependencyCarrierDraw.cpp new file mode 100644 index 0000000000..690e60cd15 --- /dev/null +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDependencyCarrierDraw.cpp @@ -0,0 +1,167 @@ +// FILE: W3DDependencyCarrierDraw.cpp //////////////////////////////////////////////////////////////////////////// + +// Desc: Render stationed Objects with carriers +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "Common/Xfer.h" +#include "GameClient/Drawable.h" +#include "GameLogic/Object.h" +#include "GameLogic/Module/ContainModule.h" +#include "W3DDevice/GameClient/Module/W3DDependencyCarrierDraw.h" + +//------------------------------------------------------------------------------------------------- +W3DDependencyCarrierDraw::W3DDependencyCarrierDraw(Thing* thing, const ModuleData* moduleData) + : W3DDependencyModelDraw(thing, moduleData) +{ +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +W3DDependencyCarrierDraw::~W3DDependencyCarrierDraw() +{ +} + +//------------------------------------------------------------------------------------------------- +// All this does is stop the call path if we haven't been cleared to draw yet +void W3DDependencyCarrierDraw::doDrawModule(const Matrix3D* transformMtx) +{ + Drawable* myDrawable = getDrawable(); + if (!myDrawable) + return; + + const Object* me = myDrawable->getObject(); + if (!me) + return; + + if (!me->isContained()) { + W3DModelDraw::doDrawModule(transformMtx); + } + else { + + if (m_dependencyCleared) + { + // We've been cleared by the thing we were waiting to draw, so we can draw. + W3DModelDraw::doDrawModule(transformMtx); + m_dependencyCleared = FALSE; + + Drawable* theirDrawable = NULL; + + if (me->getContainedBy()) // no enclosing container check here, as carrier wants to draw units anyway + theirDrawable = me->getContainedBy()->getDrawable(); + + if (!theirDrawable) + return; + + myDrawable->imitateStealthLook(*theirDrawable); + + } + } +} + +//------------------------------------------------------------------------------------------------- +void W3DDependencyCarrierDraw::notifyDrawModuleDependencyCleared() +{ + m_dependencyCleared = TRUE; + + //this is called shortly before rendering so we set the hidden status to that of the parent if contained + const Object* me = getDrawable()->getObject(); + // No special adjustements when currently not contained + if (me && me->isContained()) { + const Object* container = me->getContainedBy(); + if (container && container->getDrawable()) { + me->getDrawable()->setDrawableHidden(container->getDrawable()->isDrawableEffectivelyHidden()); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void W3DDependencyCarrierDraw::adjustTransformMtx(Matrix3D& mtx) const +{ + W3DModelDraw::adjustTransformMtx(mtx); + + // We have an additional adjustment to make, we want to use a bone in our container if there is one + const Object* me = getDrawable()->getObject(); + + // No special adjustements when currently not contained + if (me && !me->isContained()) return; + + const W3DDependencyModelDrawModuleData* md = getW3DDependencyModelDrawModuleData(); + + if (md->m_attachToDrawableBoneInContainer.isNotEmpty() + && me + && me->getContainedBy() + // && !me->getContainedBy()->getContain()->isEnclosingContainerFor(me) // no enclosing check + ) + { + // If we are currently "riding on", then our client position is determined by the client position of + // a particular bone in our container object. Our logic position is updated by OpenContain. + const Drawable* theirDrawable = me->getContainedBy()->getDrawable(); + if (theirDrawable) + { + Matrix3D theirBoneMtx; + + AsciiString boneName; + + short slot = me->getContainedBy()->getContain()->getRiderSlot(me->getID()); + 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(); + DEBUG_LOG(("m_attachToDrawableBoneInContainer %s not found", md->m_attachToDrawableBoneInContainer.str())); + } + } + } +} + + +//------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void W3DDependencyCarrierDraw::crc(Xfer* xfer) +{ + + // extend base class + W3DDependencyModelDraw::crc(xfer); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ + // ------------------------------------------------------------------------------------------------ +void W3DDependencyCarrierDraw::xfer(Xfer* xfer) +{ + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion(&version, currentVersion); + + // extend base class + W3DDependencyModelDraw::xfer(xfer); + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void W3DDependencyCarrierDraw::loadPostProcess(void) +{ + + // extend base class + W3DDependencyModelDraw::loadPostProcess(); + +} // end loadPostProcess