diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index 971299c5677..bd7127b2ba4 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -322,6 +322,7 @@ set(GAMEENGINE_SRC # Include/GameLogic/Module/FireSpreadUpdate.h # Include/GameLogic/Module/FirestormDynamicGeometryInfoUpdate.h # Include/GameLogic/Module/FireWeaponCollide.h +# Include/GameLogic/Module/AdvancedCollide.h # Include/GameLogic/Module/FireWeaponPower.h # Include/GameLogic/Module/FireWeaponUpdate.h # Include/GameLogic/Module/FireWeaponWhenDamagedBehavior.h @@ -907,6 +908,7 @@ set(GAMEENGINE_SRC # Source/GameLogic/Object/Collide/CrateCollide/UnitCrateCollide.cpp # Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp # Source/GameLogic/Object/Collide/FireWeaponCollide.cpp +# Source/GameLogic/Object/Collide/AdvancedCollide.cpp # Source/GameLogic/Object/Collide/SquishCollide.cpp # Source/GameLogic/Object/Contain/CaveContain.cpp # Source/GameLogic/Object/Contain/GarrisonContain.cpp diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index 3adc7a00726..d91bedf41d1 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -143,6 +143,7 @@ static PoolSizeRec PoolSizes[] = { "FireSpreadUpdate", 2048, 128 }, { "FirestormDynamicGeometryInfoUpdate", 16, 16 }, { "FireWeaponCollide", 2048, 32 }, + { "AdvancedCollide", 512, 32 }, { "FireWeaponUpdate", 32, 32 }, { "FireWeaponAdvancedUpdate", 32, 32 }, { "FlammableUpdate", 512, 256 }, diff --git a/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index aa407c5bd06..7504e206edb 100644 --- a/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -230,7 +230,6 @@ #include "GameLogic/Module/ImmortalBody.h" #include "GameLogic/Module/StructureBody.h" #include "GameLogic/Module/HiveStructureBody.h" -#include "GameLogic/Module/ShieldBody.h" // contain includes // (none) diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index 9c66280369a..fa5f444cabc 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -326,6 +326,7 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/FireSpreadUpdate.h Include/GameLogic/Module/FirestormDynamicGeometryInfoUpdate.h Include/GameLogic/Module/FireWeaponCollide.h + Include/GameLogic/Module/AdvancedCollide.h Include/GameLogic/Module/FireWeaponPower.h Include/GameLogic/Module/FireWeaponUpdate.h Include/GameLogic/Module/FireWeaponAdvancedUpdate.h @@ -929,6 +930,7 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Collide/CrateCollide/UnitCrateCollide.cpp Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp Source/GameLogic/Object/Collide/FireWeaponCollide.cpp + Source/GameLogic/Object/Collide/AdvancedCollide.cpp Source/GameLogic/Object/Collide/SquishCollide.cpp Source/GameLogic/Object/Contain/CaveContain.cpp Source/GameLogic/Object/Contain/GarrisonContain.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AdvancedCollide.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AdvancedCollide.h new file mode 100644 index 00000000000..fe0a2db9e93 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AdvancedCollide.h @@ -0,0 +1,110 @@ +/* +** 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: AdvancedCollide.h /////////////////////////////////////////////////////////////////////////// +// Author: Andi W, Oct 25 +// Desc: Do something if we collide with objects or the ground +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef __AdvancedCollide_H_ +#define __AdvancedCollide_H_ + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "GameLogic/Module/CollideModule.h" +#include "GameLogic/Weapon.h" + +// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// +class Object; +class FXList; +class ObjectCreationList; + +//------------------------------------------------------------------------------------------------- +class AdvancedCollideModuleData : public CollideModuleData +{ +public: + const WeaponTemplate* m_collideWeaponTemplate; ///< Weapon To Fire + const FXList* m_fxlist; ///< FX to play + const ObjectCreationList* m_ocl; ///< object creaton list to make + + KindOfMaskType m_kindof; ///< the kind(s) of units that can be collided with + KindOfMaskType m_kindofnot; ///< the kind(s) of units that CANNOT be collided with + + ObjectStatusMaskType m_requiredStatus; + ObjectStatusMaskType m_forbiddenStatus; + ObjectStatusMaskType m_targetRequiredStatus; + ObjectStatusMaskType m_targetForbiddenStatus; + + Bool m_fireOnce; ///< trigger effects only once + Bool m_collideWithGround; ///< trigger on collide with Ground + Bool m_collideWithObjects; ///< trigger on collide with Objects + + Real m_triggerChance; ///< chance to trigger effects on a valid collission + Bool m_rollOnce; ///< Only Roll once to trigger effects; Or roll every frame. + + + AdvancedCollideModuleData() + { + m_collideWeaponTemplate = NULL; + m_fxlist = NULL; + m_ocl = NULL; + m_fireOnce = FALSE; + m_collideWithGround = FALSE; + m_collideWithObjects = TRUE; + m_triggerChance = 1.0; + m_rollOnce = FALSE; + } + + static void buildFieldParse(MultiIniFieldParse& p); +}; + +//------------------------------------------------------------------------------------------------- +class AdvancedCollide : public CollideModule +{ + + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( AdvancedCollide, AdvancedCollideModuleData ); + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( AdvancedCollide, "AdvancedCollide" ) + +public: + + AdvancedCollide( Thing *thing, const ModuleData* moduleData ); + // virtual destructor prototype provided by memory pool declaration + +protected: + + virtual void onCollide( Object *other, const Coord3D *loc, const Coord3D *normal ); + + virtual Bool shouldTrigger(Object* other); + +private: + Weapon* m_collideWeapon; + Bool m_everFired; + Real m_roll; + +}; + + +#endif + diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombCrateCollide.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombCrateCollide.h index 6f445d9c087..5be662c7ce7 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombCrateCollide.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombCrateCollide.h @@ -51,6 +51,7 @@ class StickyBombCrateCollideModuleData : public CrateCollideModuleData Bool m_allowMultiCollide; ///< if we are a crate, allow spawning multiple sticky bombs, or just single use Bool m_showInfiltrationEvent; ///< show an infiltration map event for the target player AsciiString m_stickyBombObjectName; ///< the object to create + Real m_triggerChance; ///< chance to create the bomb when triggering the module StickyBombCrateCollideModuleData() { @@ -58,6 +59,7 @@ class StickyBombCrateCollideModuleData : public CrateCollideModuleData m_allowMultiCollide = FALSE; m_showInfiltrationEvent = FALSE; m_stickyBombObjectName = AsciiString::TheEmptyString; + m_triggerChance = 1.0; } static void buildFieldParse(MultiIniFieldParse& p) @@ -70,6 +72,7 @@ class StickyBombCrateCollideModuleData : public CrateCollideModuleData { "AllowMultiCollide", INI::parseBool, NULL, offsetof(StickyBombCrateCollideModuleData, m_allowMultiCollide) }, { "ShowInfiltrationEvent", INI::parseBool, NULL, offsetof(StickyBombCrateCollideModuleData, m_showInfiltrationEvent) }, { "StickyBombObject", INI::parseAsciiString, NULL, offsetof(StickyBombCrateCollideModuleData, m_stickyBombObjectName) }, + { "ChanceToTriggerPercent", INI::parsePercentToReal, NULL, offsetof(StickyBombCrateCollideModuleData, m_triggerChance) }, { 0, 0, 0, 0 } }; p.add( dataFieldParse ); @@ -92,7 +95,7 @@ class StickyBombCrateCollide : public CrateCollide protected: /// This allows specific vetoes to certain types of crates and their data - virtual Bool isValidToExecute( const Object *other ) const; + // virtual Bool isValidToExecute( const Object *other ) const; /// This is the game logic execution function that all real CrateCollides will implement virtual Bool executeCrateBehavior( Object *other ); diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombUpdate.h index 1dbb45e4f92..e60e32a87fe 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/StickyBombUpdate.h @@ -50,7 +50,10 @@ class StickyBombUpdateModuleData : public UpdateModuleData AsciiString m_animBaseTemplate; AsciiString m_animTimedTemplate; - Bool m_showTimer; + Bool m_showTimer; ///< if this is disabled, only use animBase for timed bombs + + Bool m_hideAnimBase; ///< will be set automatically if String is Null + Bool m_hideAnimTimed; ///< will be set automatically if String is Null StickyBombUpdateModuleData() { @@ -62,6 +65,9 @@ class StickyBombUpdateModuleData : public UpdateModuleData m_showTimer = TRUE; } + static void parseAnimBaseName(INI* ini, void* instance, void* store, const void* userData); + static void parseAnimTimedName(INI* ini, void* instance, void* store, const void* userData); + static void buildFieldParse(MultiIniFieldParse& p) { UpdateModuleData::buildFieldParse(p); @@ -71,8 +77,8 @@ class StickyBombUpdateModuleData : public UpdateModuleData { "OffsetZ", INI::parseReal, NULL, offsetof( StickyBombUpdateModuleData, m_offsetZ ) }, { "GeometryBasedDamageWeapon",INI::parseWeaponTemplate, NULL, offsetof( StickyBombUpdateModuleData, m_geometryBasedDamageWeaponTemplate ) }, { "GeometryBasedDamageFX", INI::parseFXList, NULL, offsetof( StickyBombUpdateModuleData, m_geometryBasedDamageFX ) }, - { "Animation2DBase", INI::parseAsciiString, NULL, offsetof( StickyBombUpdateModuleData, m_animBaseTemplate) }, - { "Animation2DTimed", INI::parseAsciiString, NULL, offsetof( StickyBombUpdateModuleData, m_animTimedTemplate) }, + { "Animation2DBase", parseAnimBaseName, NULL, 0 }, + { "Animation2DTimed", parseAnimTimedName, NULL, 0 }, { "ShowTimer", INI::parseBool, NULL, offsetof( StickyBombUpdateModuleData, m_showTimer) }, { 0, 0, 0, 0 } }; @@ -109,6 +115,9 @@ class StickyBombUpdate : public UpdateModule Anim2DTemplate* getAnimBaseTemplate(); Anim2DTemplate* getAnimTimedTemplate(); + inline Bool showAnimBaseTemplate() { return !getStickyBombUpdateModuleData()->m_hideAnimBase; } + inline Bool showAnimTimedTemplate() { return !getStickyBombUpdateModuleData()->m_hideAnimTimed; } + private: ObjectID m_targetID; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index 4d62ac55417..b7d073b3083 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -245,6 +245,7 @@ // collide includes #include "GameLogic/Module/FireWeaponCollide.h" +#include "GameLogic/Module/AdvancedCollide.h" #include "GameLogic/Module/SquishCollide.h" #include "GameLogic/Module/ConvertToCarBombCrateCollide.h" @@ -543,6 +544,7 @@ void ModuleFactory::init( void ) // collide modules addModule( FireWeaponCollide ); + addModule( AdvancedCollide ); addModule( SquishCollide ); addModule( HealCrateCollide ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp index b429e288515..cadbc6a1e82 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp @@ -3744,8 +3744,8 @@ void Drawable::drawBombed(const IRegion2D* healthBarRegion) { if( update->isTimedBomb() ) { - //Timed bomb - if( !getIconInfo()->m_icon[ ICON_BOMB_TIMED ] ) + //Timed bomb - Base layer + if (!getIconInfo()->m_icon[ICON_BOMB_REMOTE] && update->showAnimBaseTemplate()) { Anim2DTemplate* templ = update->getAnimBaseTemplate(); @@ -3754,7 +3754,11 @@ void Drawable::drawBombed(const IRegion2D* healthBarRegion) getIconInfo()->m_icon[ICON_BOMB_REMOTE] = newInstance(Anim2D)(templ, TheAnim2DCollection); - templ = update->getAnimTimedTemplate(); + } + //Timed bomb - Timer + if( !getIconInfo()->m_icon[ ICON_BOMB_TIMED ] && update->showAnimTimedTemplate()) + { + Anim2DTemplate* templ = update->getAnimTimedTemplate(); if (templ == NULL) // Default icon templ = s_animationTemplates[ICON_BOMB_TIMED]; @@ -3785,7 +3789,9 @@ void Drawable::drawBombed(const IRegion2D* healthBarRegion) getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->setMinFrame(numFrames - seconds - 1); getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->reset(); } - if( getIconInfo()->m_icon[ ICON_BOMB_TIMED ] ) + Bool showTimedAnim = (getIconInfo()->m_icon[ICON_BOMB_TIMED]) != NULL; + Bool showBaseAnim = (getIconInfo()->m_icon[ICON_BOMB_REMOTE]) != NULL; + if( showTimedAnim || showBaseAnim) { // // we are going to draw the healing icon relative to the size of the health bar region @@ -3796,8 +3802,15 @@ void Drawable::drawBombed(const IRegion2D* healthBarRegion) Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x; Int barHeight = healthBarRegion->hi.y - healthBarRegion->lo.y; - Int frameWidth = getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->getCurrentFrameWidth(); - Int frameHeight = getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->getCurrentFrameHeight(); + Int frameWidth, frameHeight; + if (showTimedAnim) { + frameWidth = getIconInfo()->m_icon[ICON_BOMB_TIMED]->getCurrentFrameWidth(); + frameHeight = getIconInfo()->m_icon[ICON_BOMB_TIMED]->getCurrentFrameHeight(); + } + else { + frameWidth = getIconInfo()->m_icon[ICON_BOMB_REMOTE]->getCurrentFrameWidth(); + frameHeight = getIconInfo()->m_icon[ICON_BOMB_REMOTE]->getCurrentFrameHeight(); + } // adjust the width to be a % of the health bar region size Int size = REAL_TO_INT( barWidth * 0.65f ); @@ -3809,18 +3822,21 @@ void Drawable::drawBombed(const IRegion2D* healthBarRegion) screen.x = REAL_TO_INT( healthBarRegion->lo.x + (barWidth * 0.5f) - (frameWidth * 0.5f) ); screen.y = REAL_TO_INT( healthBarRegion->lo.y + barHeight * 0.5f ) + BOMB_ICON_EXTRA_OFFSET; - getIconInfo()->m_icon[ ICON_BOMB_REMOTE ]->draw( screen.x, screen.y, frameWidth, frameHeight ); - getIconInfo()->m_keepTillFrame[ ICON_BOMB_REMOTE ] = now + 1; - getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->draw( screen.x, screen.y, frameWidth, frameHeight ); - getIconInfo()->m_keepTillFrame[ ICON_BOMB_TIMED ] = now + 1; + if (showBaseAnim) { + getIconInfo()->m_icon[ICON_BOMB_REMOTE]->draw(screen.x, screen.y, frameWidth, frameHeight); + getIconInfo()->m_keepTillFrame[ICON_BOMB_REMOTE] = now + 1; + } + if (showTimedAnim) { + getIconInfo()->m_icon[ICON_BOMB_TIMED]->draw(screen.x, screen.y, frameWidth, frameHeight); + getIconInfo()->m_keepTillFrame[ICON_BOMB_TIMED] = now + 1; + } } } } else { //Remote charge - //Timed bomb - if( !getIconInfo()->m_icon[ ICON_BOMB_REMOTE ] ) + if( !getIconInfo()->m_icon[ ICON_BOMB_REMOTE ] && update->showAnimBaseTemplate()) { Anim2DTemplate* templ = update->getAnimBaseTemplate(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/AdvancedCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/AdvancedCollide.cpp new file mode 100644 index 00000000000..c89e11f82ad --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/AdvancedCollide.cpp @@ -0,0 +1,233 @@ +/* +** 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: AdvancedCollide.cpp /////////////////////////////////////////////////////////////////////////// +// Author: Andi W, Oct 25 +// Desc: Do something if we collide with objects or the ground +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine +#define DEFINE_OBJECT_STATUS_NAMES +#include "Common/Xfer.h" +#include "GameLogic/Object.h" +#include "GameClient/FXList.h" +#include "GameLogic/ObjectCreationList.h" +#include "GameLogic/Module/AdvancedCollide.h" + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void AdvancedCollideModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + CollideModuleData::buildFieldParse(p); + + static const FieldParse dataFieldParse[] = + { + { "CollideWeapon", INI::parseWeaponTemplate, NULL, offsetof( AdvancedCollideModuleData, m_collideWeaponTemplate ) }, + { "OCL", INI::parseObjectCreationList, NULL, offsetof(AdvancedCollideModuleData, m_ocl) }, + { "FX", INI::parseFXList, NULL, offsetof(AdvancedCollideModuleData, m_fxlist) }, + + { "FireOnce", INI::parseBool, NULL, offsetof( AdvancedCollideModuleData, m_fireOnce ) }, + { "CollideWithGround", INI::parseBool, NULL, offsetof( AdvancedCollideModuleData, m_collideWithGround) }, + { "CollideWithObjects", INI::parseBool, NULL, offsetof( AdvancedCollideModuleData, m_collideWithObjects) }, + + { "RequiredStatus", ObjectStatusMaskType::parseFromINI, NULL, offsetof( AdvancedCollideModuleData, m_requiredStatus ) }, + { "ForbiddenStatus", ObjectStatusMaskType::parseFromINI, NULL, offsetof( AdvancedCollideModuleData, m_forbiddenStatus ) }, + { "TargetRequiredStatus", ObjectStatusMaskType::parseFromINI, NULL, offsetof(AdvancedCollideModuleData, m_targetRequiredStatus) }, + { "TargetForbiddenStatus", ObjectStatusMaskType::parseFromINI, NULL, offsetof(AdvancedCollideModuleData, m_targetForbiddenStatus) }, + { "RequiredKindOf", KindOfMaskType::parseFromINI, NULL, offsetof(AdvancedCollideModuleData, m_kindof) }, + { "ForbiddenKindOf", KindOfMaskType::parseFromINI, NULL, offsetof(AdvancedCollideModuleData, m_kindofnot) }, + + { "ChanceToTriggerPercent", INI::parsePercentToReal, NULL, offsetof(AdvancedCollideModuleData, m_triggerChance) }, + { "RollOnceForTrigger", INI::parseBool, NULL, offsetof(AdvancedCollideModuleData, m_rollOnce) }, + + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +AdvancedCollide::AdvancedCollide( Thing *thing, const ModuleData* moduleData ) : + CollideModule( thing, moduleData ), + m_collideWeapon(NULL) +{ + const AdvancedCollideModuleData* d = getAdvancedCollideModuleData(); + if (d->m_collideWeaponTemplate != NULL) { + m_collideWeapon = TheWeaponStore->allocateNewWeapon(getAdvancedCollideModuleData()->m_collideWeaponTemplate, PRIMARY_WEAPON); + } + m_everFired = FALSE; + m_roll = -1; +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +AdvancedCollide::~AdvancedCollide( void ) +{ + if (m_collideWeapon) + deleteInstance(m_collideWeapon); +} + +//------------------------------------------------------------------------------------------------- +/** The die callback. */ +//------------------------------------------------------------------------------------------------- +void AdvancedCollide::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal ) +{ + const AdvancedCollideModuleData* d = getAdvancedCollideModuleData(); + + if( other == NULL && !d->m_collideWithGround) + return; + if (other != NULL && !d->m_collideWithObjects) + return; + + if(shouldTrigger(other)) + { + Object* me = getObject(); + + if (m_collideWeapon) { + m_collideWeapon->loadAmmoNow(me); + m_collideWeapon->fireWeapon(me, other); + } + + FXList::doFXObj(d->m_fxlist, me, other); + + ObjectCreationList::create(d->m_ocl, me, other); + + } +} + +//------------------------------------------------------------------------------------------------- +Bool AdvancedCollide::shouldTrigger(Object* other) +{ + const AdvancedCollideModuleData *d = getAdvancedCollideModuleData(); + + if (m_everFired && d->m_fireOnce) + return FALSE; // can only trigger once ever + + // Check Source Object + ObjectStatusMaskType status = getObject()->getStatusBits(); + + //We need all required status or else we fail + if( !status.testForAll( d->m_requiredStatus ) ) + return FALSE; + + //If we have any forbidden statii, then fail + if( status.testForAny( d->m_forbiddenStatus ) ) + return FALSE; + + // ----------------------- + // Check Target Object + if (other) { + status = other->getStatusBits(); + + //We need all required status or else we fail + if (!status.testForAll(d->m_targetRequiredStatus)) + return FALSE; + + //If we have any forbidden statii, then fail + if (status.testForAny(d->m_targetForbiddenStatus)) + return FALSE; + + // must match our kindof flags (if any) + if (!other->isKindOfMulti(d->m_kindof, d->m_kindofnot)) + return FALSE; + } + + if (d->m_triggerChance < 1.0) { + if (!d->m_rollOnce || m_roll < 0) { + m_roll = GameLogicRandomValueReal(0, 1); + } + + if (m_roll > d->m_triggerChance) + return FALSE; + } + + return TRUE; +} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void AdvancedCollide::crc( Xfer *xfer ) +{ + + // extend base class + CollideModule::crc( xfer ); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ +// ------------------------------------------------------------------------------------------------ +void AdvancedCollide::xfer( Xfer *xfer ) +{ + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion( &version, currentVersion ); + + // extend base class + CollideModule::xfer( xfer ); + + // weapon + Bool collideWeaponPresent = m_collideWeapon ? TRUE : FALSE; + xfer->xferBool( &collideWeaponPresent ); + if( collideWeaponPresent ) + { + + DEBUG_ASSERTCRASH( m_collideWeapon != NULL, + ("AdvancedCollide::xfer - m_collideWeapon present mismatch") ); + xfer->xferSnapshot( m_collideWeapon ); + + } // end else + else + { + + DEBUG_ASSERTCRASH( m_collideWeapon == NULL, + ("AdvancedCollide::Xfer - m_collideWeapon missing mismatch" )); + + } // end else + + // ever fired + xfer->xferBool( &m_everFired ); + + // trigger chance roll + xfer->xferReal(&m_roll); + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void AdvancedCollide::loadPostProcess( void ) +{ + + // extend base class + CollideModule::loadPostProcess(); + +} // end loadPostProcess diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/StickyBombCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/StickyBombCrateCollide.cpp index e377b4aeefb..b346a1b7d81 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/StickyBombCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/StickyBombCrateCollide.cpp @@ -25,43 +25,28 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// // // FILE: StickyBombCrateCollide.cpp -// Author: Kris Morness, June 2003 -// Desc: A crate (actually a saboteur - mobile crate) that resets the timer on the target supply dropzone. +// Author: Andi W - Sept 2025 +// Desc: Creates a Sticks Bomb on the object we collide with // /////////////////////////////////////////////////////////////////////////////////////////////////// - // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine -// #include "Common/GameAudio.h" -// #include "Common/MiscAudio.h" #include "Common/Player.h" -// #include "Common/PlayerList.h" #include "Common/Radar.h" -// #include "Common/SpecialPower.h" #include "Common/ThingFactory.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" -// #include "GameClient/Drawable.h" #include "GameClient/Eva.h" -// #include "GameClient/InGameUI.h" // useful for printing quick debug strings when we need to -//#include "GameLogic/ExperienceTracker.h" #include "GameLogic/Object.h" -//#include "GameLogic/PartitionManager.h" -//#include "GameLogic/ScriptEngine.h" #include "GameLogic/Module/AIUpdate.h" -//#include "GameLogic/Module/ContainModule.h" -//#include "GameLogic/Module/DozerAIUpdate.h" -//#include "GameLogic/Module/HijackerUpdate.h" -//#include "GameLogic/Module/OCLUpdate.h" #include "GameLogic/Module/StickyBombCrateCollide.h" #include "GameLogic/Module/StickyBombUpdate.h" -//#include "GameLogic/Module/SpecialPowerModule.h" @@ -79,29 +64,29 @@ StickyBombCrateCollide::~StickyBombCrateCollide( void ) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- -Bool StickyBombCrateCollide::isValidToExecute( const Object *other ) const -{ - if( !CrateCollide::isValidToExecute(other) ) - { - if (other != NULL) - DEBUG_LOG(("StickyBombCrateCollide::isValidToExecute >>> target '%s' Not Valid.", other->getTemplate()->getName().str())); - else - DEBUG_LOG(("StickyBombCrateCollide::isValidToExecute >>> collide with ground!")); - //Extend functionality. - return FALSE; - } - - // Do we need anything here? - DEBUG_LOG(("StickyBombCrateCollide::isValidToExecute >>> target '%s' Valid!", other->getTemplate()->getName().str())); - return TRUE; -} +//Bool StickyBombCrateCollide::isValidToExecute( const Object *other ) const +//{ +// if( !CrateCollide::isValidToExecute(other) ) +// { +// if (other != NULL) +// DEBUG_LOG(("StickyBombCrateCollide::isValidToExecute >>> target '%s' Not Valid.", other->getTemplate()->getName().str())); +// else +// DEBUG_LOG(("StickyBombCrateCollide::isValidToExecute >>> collide with ground!")); +// //Extend functionality. +// return FALSE; +// } +// +// // Do we need anything here? +// DEBUG_LOG(("StickyBombCrateCollide::isValidToExecute >>> target '%s' Valid!", other->getTemplate()->getName().str())); +// return TRUE; +//} //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Bool StickyBombCrateCollide::executeCrateBehavior( Object *other ) { - DEBUG_LOG(("StickyBombCrateCollide::executeCrateBehavior 0")); + //DEBUG_LOG(("StickyBombCrateCollide::executeCrateBehavior 0")); const StickyBombCrateCollideModuleData* md = getStickyBombCrateCollideModuleData(); if (m_hasCollided && !md->m_allowMultiCollide) @@ -131,6 +116,12 @@ Bool StickyBombCrateCollide::executeCrateBehavior( Object *other ) } + Real randomNumber = GameLogicRandomValueReal(0, 1); + if (randomNumber > md->m_triggerChance) { + m_hasCollided = TRUE; + return false; + } + if (md->m_showInfiltrationEvent) TheRadar->tryInfiltrationEvent( other ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StickyBombUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StickyBombUpdate.cpp index c461555ee27..848b7f0a9ee 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StickyBombUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StickyBombUpdate.cpp @@ -52,6 +52,25 @@ // PUBLIC ///////////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- +void StickyBombUpdateModuleData::parseAnimBaseName(INI* ini, void* instance, void* store, const void* /*userData*/) +{ + StickyBombUpdateModuleData* self = (StickyBombUpdateModuleData*)instance; + self->m_animBaseTemplate = ini->getNextAsciiString(); + if (stricmp(self->m_animBaseTemplate.str(), "NONE") == 0) { + self->m_hideAnimBase = TRUE; + } +} +//------------------------------------------------------------------------------------------------- +void StickyBombUpdateModuleData::parseAnimTimedName(INI* ini, void* instance, void* store, const void* /*userData*/) +{ + StickyBombUpdateModuleData* self = (StickyBombUpdateModuleData*)instance; + self->m_animTimedTemplate = ini->getNextAsciiString(); + if (stricmp(self->m_animTimedTemplate.str(), "NONE") == 0) { + self->m_hideAnimTimed = TRUE; + } +} +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- StickyBombUpdate::StickyBombUpdate( Thing *thing, const ModuleData *moduleData ) : UpdateModule( thing, moduleData ) { m_targetID = INVALID_ID;