mirror of
https://github.com/electronicarts/CnC_Generals_Zero_Hour.git
synced 2025-12-16 23:51:41 -05:00
Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
152
Generals/Code/GameEngine/Source/GameLogic/Object/Armor.cpp
Normal file
152
Generals/Code/GameEngine/Source/GameLogic/Object/Armor.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ArmorTemplate.cpp ///////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, November 2001
|
||||
// Desc: ArmorTemplate descriptions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_DAMAGE_NAMES // for DamageNames[]
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "GameLogic/Armor.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
ArmorStore* TheArmorStore = NULL; ///< the ArmorTemplate store definition
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ArmorTemplate::ArmorTemplate()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ArmorTemplate::clear()
|
||||
{
|
||||
for (int i = 0; i < DAMAGE_NUM_TYPES; i++)
|
||||
{
|
||||
m_damageCoefficient[i] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real ArmorTemplate::adjustDamage(DamageType t, Real damage) const
|
||||
{
|
||||
if (t == DAMAGE_UNRESISTABLE)
|
||||
return damage;
|
||||
|
||||
damage *= m_damageCoefficient[t];
|
||||
|
||||
if (damage < 0.0f)
|
||||
damage = 0.0f;
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------Static
|
||||
/*static*/ void ArmorTemplate::parseArmorCoefficients( INI* ini, void *instance, void* /* store */, const void* userData )
|
||||
{
|
||||
ArmorTemplate* self = (ArmorTemplate*) instance;
|
||||
|
||||
const char* damageName = ini->getNextToken();
|
||||
Real pct = INI::scanPercentToReal(ini->getNextToken());
|
||||
|
||||
if (stricmp(damageName, "Default") == 0)
|
||||
{
|
||||
for (Int i = 0; i < DAMAGE_NUM_TYPES; i++)
|
||||
{
|
||||
self->m_damageCoefficient[i] = pct;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DamageType dt = (DamageType)INI::scanIndexList(damageName, TheDamageNames);
|
||||
self->m_damageCoefficient[dt] = pct;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ArmorStore::ArmorStore()
|
||||
{
|
||||
m_armorTemplates.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ArmorStore::~ArmorStore()
|
||||
{
|
||||
m_armorTemplates.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const ArmorTemplate* ArmorStore::findArmorTemplate(AsciiString name) const
|
||||
{
|
||||
NameKeyType namekey = TheNameKeyGenerator->nameToKey(name);
|
||||
ArmorTemplateMap::const_iterator it = m_armorTemplates.find(namekey);
|
||||
if (it == m_armorTemplates.end())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &(*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static */ void ArmorStore::parseArmorDefinition(INI *ini)
|
||||
{
|
||||
static const FieldParse myFieldParse[] =
|
||||
{
|
||||
{ "Armor", ArmorTemplate::parseArmorCoefficients, NULL, 0 }
|
||||
};
|
||||
|
||||
const char *c = ini->getNextToken();
|
||||
NameKeyType key = TheNameKeyGenerator->nameToKey(c);
|
||||
ArmorTemplate& armorTmpl = TheArmorStore->m_armorTemplates[key];
|
||||
armorTmpl.clear();
|
||||
ini->initFromINI(&armorTmpl, myFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void INI::parseArmorDefinition(INI *ini)
|
||||
{
|
||||
ArmorStore::parseArmorDefinition(ini);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: AutoHealBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Thing.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/ParticleSys.h"
|
||||
#include "GameClient/Anim2D.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/Module/AutoHealBehavior.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
struct AutoHealPlayerScanHelper
|
||||
{
|
||||
KindOfMaskType m_kindOfToTest;
|
||||
Object *m_theHealer;
|
||||
ObjectPointerList *m_objectList;
|
||||
};
|
||||
|
||||
static void checkForAutoHeal( Object *testObj, void *userData )
|
||||
{
|
||||
AutoHealPlayerScanHelper *helper = (AutoHealPlayerScanHelper*)userData;
|
||||
ObjectPointerList *listToAddTo = helper->m_objectList;
|
||||
|
||||
if( testObj->isEffectivelyDead() )
|
||||
return;
|
||||
|
||||
if( testObj->getControllingPlayer() != helper->m_theHealer->getControllingPlayer() )
|
||||
return;
|
||||
|
||||
if( testObj->isOffMap() )
|
||||
return;
|
||||
|
||||
if( !testObj->isAnyKindOf(helper->m_kindOfToTest) )
|
||||
return;
|
||||
|
||||
if( testObj->getBodyModule()->getHealth() >= testObj->getBodyModule()->getMaxHealth() )
|
||||
return;
|
||||
|
||||
listToAddTo->push_back(testObj);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AutoHealBehavior::AutoHealBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
|
||||
{
|
||||
const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData();
|
||||
|
||||
m_radiusParticleSystemID = INVALID_PARTICLE_SYSTEM_ID;
|
||||
m_soonestHealFrame = 0;
|
||||
m_stopped = false;
|
||||
Object *obj = getObject();
|
||||
|
||||
{
|
||||
if( d->m_radiusParticleSystemTmpl )
|
||||
{
|
||||
ParticleSystem *particleSystem;
|
||||
|
||||
particleSystem = TheParticleSystemManager->createParticleSystem( d->m_radiusParticleSystemTmpl );
|
||||
if( particleSystem )
|
||||
{
|
||||
particleSystem->setPosition( obj->getPosition() );
|
||||
m_radiusParticleSystemID = particleSystem->getSystemID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d->m_initiallyActive)
|
||||
{
|
||||
giveSelfUpgrade();
|
||||
// start these guys with random phasings so that we don't
|
||||
// have all of 'em check on the same frame.
|
||||
UnsignedInt delay = getAutoHealBehaviorModuleData()->m_healingDelay;
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP(GameLogicRandomValue(1, delay)));
|
||||
}
|
||||
else
|
||||
{
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AutoHealBehavior::~AutoHealBehavior( void )
|
||||
{
|
||||
|
||||
if( m_radiusParticleSystemID != INVALID_PARTICLE_SYSTEM_ID )
|
||||
TheParticleSystemManager->destroyParticleSystemByID( m_radiusParticleSystemID );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::stopHealing()
|
||||
{
|
||||
m_stopped = true;
|
||||
m_soonestHealFrame = FOREVER;
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::undoUpgrade()
|
||||
{
|
||||
m_soonestHealFrame = 0;
|
||||
setUpgradeExecuted( FALSE );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Damage has been dealt, this is an opportunity to reach to that damage */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
if (m_stopped)
|
||||
return;
|
||||
|
||||
const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData();
|
||||
if (isUpgradeActive() && d->m_radius == 0.0f)
|
||||
{
|
||||
// if this is nonzero, getting damaged resets our healing process. so go to
|
||||
// sleep for this long.
|
||||
if (d->m_startHealingDelay > 0)
|
||||
{
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP(d->m_startHealingDelay));
|
||||
}
|
||||
else if( TheGameLogic->getFrame() > m_soonestHealFrame )
|
||||
{
|
||||
// We can only force an immediate wake if we are ready to heal. Otherwise we will
|
||||
// heal on a timer AND at every damage input.
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The update callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime AutoHealBehavior::update( void )
|
||||
{
|
||||
if (m_stopped)
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
|
||||
Object *obj = getObject();
|
||||
const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData();
|
||||
|
||||
// do not heal if our status bit is not on.
|
||||
// do not heal if our status is effectively dead. There ain't no coming back, man!
|
||||
if (!isUpgradeActive() || obj->isEffectivelyDead())
|
||||
{
|
||||
DEBUG_ASSERTCRASH(isUpgradeActive(), ("hmm, this should not be possible"));
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
|
||||
//DEBUG_LOG(("doing auto heal %d\n",TheGameLogic->getFrame()));
|
||||
|
||||
if( d->m_affectsWholePlayer )
|
||||
{
|
||||
// Even newer system, I can ignore radius and iterate objects on the owning player. Faster than scanning range 10,000,000
|
||||
ObjectPointerList objectsToHeal;
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer )
|
||||
{
|
||||
AutoHealPlayerScanHelper helper;
|
||||
helper.m_kindOfToTest = getAutoHealBehaviorModuleData()->m_kindOf;
|
||||
helper.m_objectList = &objectsToHeal;
|
||||
helper.m_theHealer = getObject();
|
||||
|
||||
// Smack all objects with this function, and we will end up with a list of Objects deserving of pulseHealObject
|
||||
owningPlayer->iterateObjects( checkForAutoHeal, &helper );
|
||||
|
||||
for( ObjectPointerListIterator iter = objectsToHeal.begin(); iter != objectsToHeal.end(); ++iter )
|
||||
{
|
||||
pulseHealObject(*iter);
|
||||
}
|
||||
objectsToHeal.clear();
|
||||
}
|
||||
return UPDATE_SLEEP(d->m_healingDelay);
|
||||
}
|
||||
else if( d->m_radius == 0.0f )
|
||||
{
|
||||
//ORIGINAL SYSTEM -- JUST HEAL SELF!
|
||||
|
||||
// do not heal if we are at max health already
|
||||
BodyModuleInterface *body = obj->getBodyModule();
|
||||
if( body->getHealth() < body->getMaxHealth() )
|
||||
{
|
||||
pulseHealObject( obj );
|
||||
return UPDATE_SLEEP(d->m_healingDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
// go to sleep forever -- we'll wake back up when we are damaged again
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//EXPANDED SYSTEM -- HEAL FRIENDLIES IN RADIUS
|
||||
// setup scan filters
|
||||
PartitionFilterRelationship relationship( obj, PartitionFilterRelationship::ALLOW_ALLIES );
|
||||
PartitionFilterSameMapStatus filterMapStatus(obj);
|
||||
PartitionFilterAlive filterAlive;
|
||||
PartitionFilter *filters[] = { &relationship, &filterAlive, &filterMapStatus, NULL };
|
||||
|
||||
// scan objects in our region
|
||||
ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( obj->getPosition(), d->m_radius, FROM_CENTER_2D, filters );
|
||||
MemoryPoolObjectHolder hold( iter );
|
||||
for( obj = iter->first(); obj; obj = iter->next() )
|
||||
{
|
||||
// do not heal if we are at max health already
|
||||
BodyModuleInterface *body = obj->getBodyModule();
|
||||
if( body->getHealth() < body->getMaxHealth() && obj->isAnyKindOf( d->m_kindOf ) )
|
||||
{
|
||||
pulseHealObject( obj );
|
||||
|
||||
if( d->m_singleBurst && TheGameLogic->getDrawIconUI() )
|
||||
{
|
||||
if( TheAnim2DCollection && TheGlobalData->m_getHealedAnimationName.isEmpty() == FALSE )
|
||||
{
|
||||
Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( TheGlobalData->m_getHealedAnimationName );
|
||||
|
||||
if ( animTemplate )
|
||||
{
|
||||
Coord3D iconPosition;
|
||||
iconPosition.set(obj->getPosition()->x,
|
||||
obj->getPosition()->y,
|
||||
obj->getPosition()->z + obj->getGeometryInfo().getMaxHeightAbovePosition() );
|
||||
TheInGameUI->addWorldAnimation( animTemplate, &iconPosition, WORLD_ANIM_FADE_ON_EXPIRE,
|
||||
TheGlobalData->m_getHealedAnimationDisplayTimeInSeconds,
|
||||
TheGlobalData->m_getHealedAnimationZRisePerSecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // end for obj
|
||||
|
||||
return UPDATE_SLEEP( d->m_singleBurst ? UPDATE_SLEEP_FOREVER : d->m_healingDelay );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::pulseHealObject( Object *obj )
|
||||
{
|
||||
if (m_stopped)
|
||||
return;
|
||||
|
||||
const AutoHealBehaviorModuleData *data = getAutoHealBehaviorModuleData();
|
||||
|
||||
|
||||
if ( data->m_radius == 0.0f )
|
||||
obj->attemptHealing(data->m_healingAmount, getObject());
|
||||
else
|
||||
obj->attemptHealingFromSoleBenefactor( data->m_healingAmount, getObject(), data->m_healingDelay );
|
||||
|
||||
|
||||
if( data->m_unitHealPulseParticleSystemTmpl )
|
||||
{
|
||||
ParticleSystem *system = TheParticleSystemManager->createParticleSystem( data->m_unitHealPulseParticleSystemTmpl );
|
||||
if( system )
|
||||
{
|
||||
system->setPosition( obj->getPosition() );
|
||||
}
|
||||
}
|
||||
|
||||
m_soonestHealFrame = TheGameLogic->getFrame() + data->m_healingDelay;// In case onDamage tries to wake us up early
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
// extend base class
|
||||
UpgradeMux::upgradeMuxCRC( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// extend base class
|
||||
UpgradeMux::upgradeMuxXfer( xfer );
|
||||
|
||||
// particle system id
|
||||
xfer->xferUser( &m_radiusParticleSystemID, sizeof( ParticleSystemID ) );
|
||||
|
||||
// Timer safety
|
||||
xfer->xferUnsignedInt( &m_soonestHealFrame );
|
||||
|
||||
// stopped
|
||||
xfer->xferBool( &m_stopped );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AutoHealBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
// extend base class
|
||||
UpgradeMux::upgradeMuxLoadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BehaviorModule.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, September 2002
|
||||
// Desc: Implementaion for anything in the base BehaviorModule
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/BehaviorModule.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BehaviorModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// call base class
|
||||
ObjectModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BehaviorModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// call base class
|
||||
ObjectModule::xfer( xfer );
|
||||
|
||||
} // xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BehaviorModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// call base class
|
||||
ObjectModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BridgeScaffoldBehavior.cpp ///////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, September 2002
|
||||
// Desc: Bridge scaffold
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BridgeScaffoldBehavior.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BridgeScaffoldBehavior::BridgeScaffoldBehavior( Thing *thing, const ModuleData *moduleData )
|
||||
: UpdateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
m_targetMotion = STM_STILL;
|
||||
m_createPos.zero();
|
||||
m_riseToPos.zero();
|
||||
m_buildPos.zero();
|
||||
m_targetPos.zero();
|
||||
m_lateralSpeed = 1.0f;
|
||||
m_verticalSpeed = 1.0f;
|
||||
|
||||
} // end BridgeScaffoldBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BridgeScaffoldBehavior::~BridgeScaffoldBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~BridgeScaffoldBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Set all of the target positions that we're care about as a moving scaffold object */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeScaffoldBehavior::setPositions( const Coord3D *createPos,
|
||||
const Coord3D *riseToPos,
|
||||
const Coord3D *buildPos )
|
||||
{
|
||||
|
||||
m_createPos = *createPos;
|
||||
m_riseToPos = *riseToPos;
|
||||
m_buildPos = *buildPos;
|
||||
|
||||
} // end setPositions
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Set us moving to the right target position for the requested motion type */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeScaffoldBehavior::setMotion( ScaffoldTargetMotion targetMotion )
|
||||
{
|
||||
|
||||
// save the target motion type
|
||||
m_targetMotion = targetMotion;
|
||||
|
||||
// given the target motion, pick a destination target
|
||||
switch( m_targetMotion )
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
case STM_RISE:
|
||||
case STM_TEAR_DOWN_ACROSS:
|
||||
m_targetPos = m_riseToPos;
|
||||
break;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
case STM_BUILD_ACROSS:
|
||||
m_targetPos = m_buildPos;
|
||||
break;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
case STM_SINK:
|
||||
m_targetPos = m_createPos;
|
||||
break;
|
||||
|
||||
} // end switch
|
||||
|
||||
} // end setMotion
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Whatever our current state of motion is, reverse it */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeScaffoldBehavior::reverseMotion( void )
|
||||
{
|
||||
|
||||
switch( m_targetMotion )
|
||||
{
|
||||
|
||||
case STM_STILL:
|
||||
setMotion( STM_TEAR_DOWN_ACROSS );
|
||||
break;
|
||||
|
||||
case STM_RISE:
|
||||
setMotion( STM_SINK );
|
||||
break;
|
||||
|
||||
case STM_BUILD_ACROSS:
|
||||
setMotion( STM_TEAR_DOWN_ACROSS );
|
||||
break;
|
||||
|
||||
case STM_TEAR_DOWN_ACROSS:
|
||||
setMotion( STM_BUILD_ACROSS );
|
||||
break;
|
||||
|
||||
case STM_SINK:
|
||||
setMotion( STM_RISE );
|
||||
break;
|
||||
|
||||
} // end switch
|
||||
|
||||
} // end reverseMotion
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** The update method */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime BridgeScaffoldBehavior::update( void )
|
||||
{
|
||||
|
||||
// do nothing if we're not in motion
|
||||
if( m_targetMotion == STM_STILL )
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
// get our info
|
||||
Object *us = getObject();
|
||||
const Coord3D *ourPos = us->getPosition();
|
||||
|
||||
// compute direction vector from our position to the target position
|
||||
Coord3D dirV;
|
||||
dirV.x = m_targetPos.x - ourPos->x;
|
||||
dirV.y = m_targetPos.y - ourPos->y;
|
||||
dirV.z = m_targetPos.z - ourPos->z;
|
||||
|
||||
// use normalized direction vector "v" to do the pulling movement
|
||||
Coord3D v = dirV;
|
||||
v.normalize();
|
||||
|
||||
// depending on our motion type, we move at different speeds
|
||||
Real topSpeed = 1.0f;
|
||||
Coord3D *start, *end;
|
||||
switch( m_targetMotion )
|
||||
{
|
||||
|
||||
case STM_RISE:
|
||||
topSpeed = m_verticalSpeed;
|
||||
start = &m_createPos;
|
||||
end = &m_riseToPos;
|
||||
break;
|
||||
|
||||
case STM_SINK:
|
||||
topSpeed = m_verticalSpeed;
|
||||
start = &m_riseToPos;
|
||||
end = &m_createPos;
|
||||
break;
|
||||
|
||||
case STM_BUILD_ACROSS:
|
||||
topSpeed = m_lateralSpeed;
|
||||
start = &m_riseToPos;
|
||||
end = &m_buildPos;
|
||||
break;
|
||||
|
||||
case STM_TEAR_DOWN_ACROSS:
|
||||
topSpeed = m_lateralSpeed;
|
||||
start = &m_buildPos;
|
||||
end = &m_riseToPos;
|
||||
break;
|
||||
|
||||
} // end switch
|
||||
|
||||
// adjust speed so it's slower at the end of motion
|
||||
Coord3D speedVector;
|
||||
speedVector.x = end->x - start->x;
|
||||
speedVector.y = end->y - start->y;
|
||||
speedVector.z = end->z - start->z;
|
||||
Real totalDistance = speedVector.length() * 0.25f;
|
||||
speedVector.x = end->x - ourPos->x;
|
||||
speedVector.y = end->y - ourPos->y;
|
||||
speedVector.z = end->z - ourPos->z;
|
||||
Real ourDistance = speedVector.length();
|
||||
Real speed = (ourDistance / totalDistance) * topSpeed;
|
||||
Real minSpeed = topSpeed * 0.08f;
|
||||
if( speed < minSpeed )
|
||||
speed = minSpeed;
|
||||
if( speed > topSpeed )
|
||||
speed = topSpeed;
|
||||
|
||||
//
|
||||
// make sure that speed can't get so incredibly small that we never finish our
|
||||
// movement no matter what the speed and distance are
|
||||
//
|
||||
if( speed < 0.001f )
|
||||
speed = 0.001f;
|
||||
|
||||
// compute the new position given the speed
|
||||
Coord3D newPos;
|
||||
newPos.x = v.x * speed + ourPos->x;
|
||||
newPos.y = v.y * speed + ourPos->y;
|
||||
newPos.z = v.z * speed + ourPos->z;
|
||||
|
||||
//
|
||||
// will this new position push us beyond our target destination, we will take the vector
|
||||
// from the new position to the destination and the vector from our current present position
|
||||
// tot he destination and dot them togehter ... if the result is < 0 then we have will
|
||||
// overshoot the distance if we use the new position
|
||||
//
|
||||
Coord3D tooFarVector;
|
||||
tooFarVector.x = m_targetPos.x - newPos.x;
|
||||
tooFarVector.y = m_targetPos.y - newPos.y;
|
||||
tooFarVector.z = m_targetPos.z - newPos.z;
|
||||
if( tooFarVector.x * dirV.x + tooFarVector.y * dirV.y + tooFarVector.z * dirV.z <= 0.0f )
|
||||
{
|
||||
|
||||
// use the destination position
|
||||
newPos = m_targetPos;
|
||||
|
||||
//
|
||||
// we have reached our target position, switch motion to the next position in
|
||||
// the chain (which may be stay still and don't move anymore)
|
||||
//
|
||||
switch( m_targetMotion )
|
||||
{
|
||||
|
||||
case STM_RISE: setMotion( STM_BUILD_ACROSS ); break;
|
||||
case STM_BUILD_ACROSS: setMotion( STM_STILL ); break;
|
||||
case STM_TEAR_DOWN_ACROSS: setMotion( STM_SINK ); break;
|
||||
|
||||
case STM_SINK:
|
||||
{
|
||||
|
||||
// we are done with a sinking motion, destroy the scaffold object as our job is done
|
||||
TheGameLogic->destroyObject( us );
|
||||
break;
|
||||
|
||||
} // end case
|
||||
|
||||
} // end switch
|
||||
|
||||
} // end if
|
||||
|
||||
// set the new position
|
||||
us->setPosition( &newPos );
|
||||
|
||||
// do not sleep
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** STATIC MEMBER:
|
||||
* Helper function to retrieve a bridge scaffold interface from an object if one is present */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BridgeScaffoldBehaviorInterface *BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( Object *obj )
|
||||
{
|
||||
|
||||
// santiy
|
||||
if( obj == NULL )
|
||||
return NULL;
|
||||
|
||||
// get the bridge tower behavior interface
|
||||
BridgeScaffoldBehaviorInterface *bridgeScaffoldInterface = NULL;
|
||||
BehaviorModule **bmi;
|
||||
for( bmi = obj->getBehaviorModules(); *bmi; ++bmi )
|
||||
{
|
||||
|
||||
bridgeScaffoldInterface = (*bmi)->getBridgeScaffoldBehaviorInterface();
|
||||
if( bridgeScaffoldInterface )
|
||||
return bridgeScaffoldInterface;
|
||||
|
||||
} // end for bmi
|
||||
|
||||
// interface not found
|
||||
return NULL;
|
||||
|
||||
} // end getBridgeScaffoldBehaviorInterfaceFromObject
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeScaffoldBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeScaffoldBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// target motion
|
||||
xfer->xferUser( &m_targetMotion, sizeof( ScaffoldTargetMotion ) );
|
||||
|
||||
// create pos
|
||||
xfer->xferCoord3D( &m_createPos );
|
||||
|
||||
// rise to pos
|
||||
xfer->xferCoord3D( &m_riseToPos );
|
||||
|
||||
// build pos
|
||||
xfer->xferCoord3D( &m_buildPos );
|
||||
|
||||
// lateral speed
|
||||
xfer->xferReal( &m_lateralSpeed );
|
||||
|
||||
// vertical speed
|
||||
xfer->xferReal( &m_verticalSpeed );
|
||||
|
||||
// current target pos
|
||||
xfer->xferCoord3D( &m_targetPos );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeScaffoldBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BridgeTowerBehavior.cpp //////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, July 2002
|
||||
// Desc: Behavior module for the towers attached to bridges that can be targeted
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/BridgeBehavior.h"
|
||||
#include "GameLogic/Module/BridgeTowerBehavior.h"
|
||||
#include "GameLogic/TerrainLogic.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BridgeTowerBehavior::BridgeTowerBehavior( Thing *thing, const ModuleData *moduleData )
|
||||
: BehaviorModule( thing, moduleData )
|
||||
{
|
||||
|
||||
m_bridgeID = INVALID_ID;
|
||||
|
||||
} // end BridgeTowerBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BridgeTowerBehavior::~BridgeTowerBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~BridgeTowerBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::setBridge( Object *bridge )
|
||||
{
|
||||
|
||||
if( bridge == NULL )
|
||||
m_bridgeID = INVALID_ID;
|
||||
else
|
||||
m_bridgeID = bridge->getID();
|
||||
|
||||
} // end setBridge
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ObjectID BridgeTowerBehavior::getBridgeID( void )
|
||||
{
|
||||
|
||||
return m_bridgeID;
|
||||
|
||||
} // end getBridge
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::setTowerType( BridgeTowerType type )
|
||||
{
|
||||
|
||||
m_type = type;
|
||||
|
||||
} // end setTowerType
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
Object *bridge = TheGameLogic->findObjectByID( getBridgeID() );
|
||||
|
||||
// sanity
|
||||
if( bridge == NULL )
|
||||
return;
|
||||
|
||||
//
|
||||
// get our body info so we now how much damage percent is being done to us ... we need this
|
||||
// so that we can propagate the same damage percentage amont the towers and the bridge
|
||||
//
|
||||
BodyModuleInterface *body = getObject()->getBodyModule();
|
||||
Real damagePercentage = damageInfo->in.m_amount / body->getMaxHealth();
|
||||
|
||||
// get the bridge behavior module for our bridge
|
||||
BehaviorModule **bmi;
|
||||
BridgeBehaviorInterface *bridgeInterface = NULL;
|
||||
for( bmi = bridge->getBehaviorModules(); *bmi; ++bmi )
|
||||
{
|
||||
|
||||
bridgeInterface = (*bmi)->getBridgeBehaviorInterface();
|
||||
if( bridgeInterface )
|
||||
break;
|
||||
|
||||
} // end for bmi
|
||||
DEBUG_ASSERTCRASH( bridgeInterface != NULL, ("BridgeTowerBehavior::onDamage - no 'BridgeBehaviorInterface' found\n") );
|
||||
if( bridgeInterface )
|
||||
{
|
||||
|
||||
//
|
||||
// damage each of the other towers if the source of this damage isn't from the bridge
|
||||
// or other towers
|
||||
//
|
||||
Object *source = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
if( source == NULL ||
|
||||
(source->isKindOf( KINDOF_BRIDGE ) == FALSE &&
|
||||
source->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE) )
|
||||
{
|
||||
|
||||
for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
|
||||
{
|
||||
Object *tower;
|
||||
|
||||
tower = TheGameLogic->findObjectByID( bridgeInterface->getTowerID( (BridgeTowerType)i ) );
|
||||
if( tower && tower != getObject() )
|
||||
{
|
||||
BodyModuleInterface *towerBody = tower->getBodyModule();
|
||||
DamageInfo towerDamage;
|
||||
|
||||
towerDamage.in.m_amount = damagePercentage * towerBody->getMaxHealth();
|
||||
towerDamage.in.m_sourceID = getObject()->getID(); // we're now the source
|
||||
towerDamage.in.m_damageType = damageInfo->in.m_damageType;
|
||||
towerDamage.in.m_deathType = damageInfo->in.m_deathType;
|
||||
tower->attemptDamage( &towerDamage );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
//
|
||||
// damage bridge object, but make sure it's done through the bridge interface
|
||||
// so that it doesn't automatically propagate that damage to the towers
|
||||
//
|
||||
BodyModuleInterface *bridgeBody = bridge->getBodyModule();
|
||||
DamageInfo bridgeDamage;
|
||||
|
||||
bridgeDamage.in.m_amount = damagePercentage * bridgeBody->getMaxHealth();
|
||||
bridgeDamage.in.m_sourceID = getObject()->getID(); // we're now the source
|
||||
bridgeDamage.in.m_damageType = damageInfo->in.m_damageType;
|
||||
bridgeDamage.in.m_deathType = damageInfo->in.m_deathType;
|
||||
bridge->attemptDamage( &bridgeDamage );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end onDamage
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::onHealing( DamageInfo *damageInfo )
|
||||
{
|
||||
Object *bridge = TheGameLogic->findObjectByID( getBridgeID() );
|
||||
|
||||
// sanity
|
||||
if( bridge == NULL )
|
||||
return;
|
||||
|
||||
//
|
||||
// get our body info so we now how much healing percent is being done to us ... we need this
|
||||
// so that we can propagate the same healing percentage amont the towers and the bridge
|
||||
//
|
||||
BodyModuleInterface *body = getObject()->getBodyModule();
|
||||
Real healingPercentage = damageInfo->in.m_amount / body->getMaxHealth();
|
||||
|
||||
// get the bridge behavior module for our bridge
|
||||
BehaviorModule **bmi;
|
||||
BridgeBehaviorInterface *bridgeInterface = NULL;
|
||||
for( bmi = bridge->getBehaviorModules(); *bmi; ++bmi )
|
||||
{
|
||||
|
||||
bridgeInterface = (*bmi)->getBridgeBehaviorInterface();
|
||||
if( bridgeInterface )
|
||||
break;
|
||||
|
||||
} // end for bmi
|
||||
DEBUG_ASSERTCRASH( bridgeInterface != NULL, ("BridgeTowerBehavior::onHealing - no 'BridgeBehaviorInterface' found\n") );
|
||||
if( bridgeInterface )
|
||||
{
|
||||
|
||||
//
|
||||
// heal each of the other towers if the source of this healing isn't from the bridge
|
||||
// or other towers
|
||||
//
|
||||
Object *source = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
if( source == NULL ||
|
||||
(source->isKindOf( KINDOF_BRIDGE ) == FALSE &&
|
||||
source->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE) )
|
||||
{
|
||||
|
||||
for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
|
||||
{
|
||||
Object *tower;
|
||||
|
||||
tower = TheGameLogic->findObjectByID( bridgeInterface->getTowerID( (BridgeTowerType)i ) );
|
||||
if( tower && tower != getObject() )
|
||||
{
|
||||
BodyModuleInterface *towerBody = tower->getBodyModule();
|
||||
tower->attemptHealing(healingPercentage * towerBody->getMaxHealth(), getObject());
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
//
|
||||
// heal bridge object, but make sure it's done through the bridge interface
|
||||
// so that it doesn't automatically propagate that healing to the towers.
|
||||
//
|
||||
BodyModuleInterface *bridgeBody = bridge->getBodyModule();
|
||||
bridge->attemptHealing(healingPercentage * bridgeBody->getMaxHealth(), getObject());
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end onHealing
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::onBodyDamageStateChange( const DamageInfo* damageInfo,
|
||||
BodyDamageType oldState,
|
||||
BodyDamageType newState )
|
||||
{
|
||||
|
||||
} // end onBodyDamageStateChange
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
|
||||
// kill the bridge object, this will kill all the towers
|
||||
Object *bridge = TheGameLogic->findObjectByID( getBridgeID() );
|
||||
if( bridge )
|
||||
bridge->kill();
|
||||
|
||||
} // end onDie
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Given an object, return a bridge tower interface if that object has one */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BridgeTowerBehaviorInterface *BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL || obj->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE )
|
||||
return NULL;
|
||||
|
||||
BehaviorModule **bmi;
|
||||
BridgeTowerBehaviorInterface *bridgeTowerInterface = NULL;
|
||||
for( bmi = obj->getBehaviorModules(); *bmi; ++bmi )
|
||||
{
|
||||
|
||||
bridgeTowerInterface = (*bmi)->getBridgeTowerBehaviorInterface();
|
||||
if( bridgeTowerInterface )
|
||||
return bridgeTowerInterface;
|
||||
|
||||
} // end for bmi
|
||||
|
||||
// interface not found
|
||||
return NULL;
|
||||
|
||||
} // getBridgeTowerBehaviorInterfaceFromObject
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
// xfer bridge object ID
|
||||
xfer->xferObjectID( &m_bridgeID );
|
||||
|
||||
// xfer tower type
|
||||
xfer->xferUser( &m_type, sizeof( BridgeTowerType ) );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BridgeTowerBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,772 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DumbProjectileBehavior.cpp
|
||||
// Author: Steven Johnson, July 2002
|
||||
// Desc:
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/BezierSegment.h"
|
||||
#include "Common/GameCommon.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/ContainModule.h"
|
||||
#include "GameLogic/Module/DumbProjectileBehavior.h"
|
||||
#include "GameLogic/Module/MissileAIUpdate.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Int DEFAULT_MAX_LIFESPAN = 10 * LOGICFRAMES_PER_SECOND;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
DumbProjectileBehaviorModuleData::DumbProjectileBehaviorModuleData() :
|
||||
m_maxLifespan(DEFAULT_MAX_LIFESPAN),
|
||||
m_detonateCallsKill(FALSE),
|
||||
m_orientToFlightPath(TRUE),
|
||||
m_tumbleRandomly(FALSE),
|
||||
m_firstHeight(0.0f),
|
||||
m_secondHeight(0.0f),
|
||||
m_firstPercentIndent(0.0f),
|
||||
m_secondPercentIndent(0.0f),
|
||||
m_garrisonHitKillCount(0),
|
||||
m_garrisonHitKillFX(NULL),
|
||||
m_flightPathAdjustDistPerFrame(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void DumbProjectileBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
UpdateModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "MaxLifespan", INI::parseDurationUnsignedInt, NULL, offsetof( DumbProjectileBehaviorModuleData, m_maxLifespan ) },
|
||||
{ "TumbleRandomly", INI::parseBool, NULL, offsetof( DumbProjectileBehaviorModuleData, m_tumbleRandomly ) },
|
||||
{ "DetonateCallsKill", INI::parseBool, NULL, offsetof( DumbProjectileBehaviorModuleData, m_detonateCallsKill ) },
|
||||
{ "OrientToFlightPath", INI::parseBool, NULL, offsetof( DumbProjectileBehaviorModuleData, m_orientToFlightPath ) },
|
||||
|
||||
{ "FirstHeight", INI::parseReal, NULL, offsetof( DumbProjectileBehaviorModuleData, m_firstHeight ) },
|
||||
{ "SecondHeight", INI::parseReal, NULL, offsetof( DumbProjectileBehaviorModuleData, m_secondHeight ) },
|
||||
{ "FirstPercentIndent", INI::parsePercentToReal, NULL, offsetof( DumbProjectileBehaviorModuleData, m_firstPercentIndent ) },
|
||||
{ "SecondPercentIndent", INI::parsePercentToReal, NULL, offsetof( DumbProjectileBehaviorModuleData, m_secondPercentIndent ) },
|
||||
|
||||
{ "GarrisonHitKillRequiredKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( DumbProjectileBehaviorModuleData, m_garrisonHitKillKindof ) },
|
||||
{ "GarrisonHitKillForbiddenKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( DumbProjectileBehaviorModuleData, m_garrisonHitKillKindofNot ) },
|
||||
{ "GarrisonHitKillCount", INI::parseUnsignedInt, NULL, offsetof( DumbProjectileBehaviorModuleData, m_garrisonHitKillCount ) },
|
||||
{ "GarrisonHitKillFX", INI::parseFXList, NULL, offsetof( DumbProjectileBehaviorModuleData, m_garrisonHitKillFX ) },
|
||||
|
||||
{ "FlightPathAdjustDistPerSecond", INI::parseVelocityReal, NULL, offsetof( DumbProjectileBehaviorModuleData, m_flightPathAdjustDistPerFrame ) },
|
||||
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DumbProjectileBehavior::DumbProjectileBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
|
||||
{
|
||||
m_launcherID = INVALID_ID;
|
||||
m_victimID = INVALID_ID;
|
||||
m_detonationWeaponTmpl = NULL;
|
||||
m_lifespanFrame = 0;
|
||||
m_flightPath.clear();
|
||||
m_flightPathSegments = 0;
|
||||
m_flightPathSpeed = 0;
|
||||
m_flightPathStart.zero();
|
||||
m_flightPathEnd.zero();
|
||||
m_currentFlightPathStep = 0;
|
||||
m_extraBonusFlags = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DumbProjectileBehavior::~DumbProjectileBehavior()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
inline Bool within(Real min, Real val, Real max)
|
||||
{
|
||||
return min <= val && val <= max;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
inline Bool notWithin(Real min, Real val, Real max)
|
||||
{
|
||||
return val < min || val > max;
|
||||
}
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
|
||||
#define NO_ONLY_RETURN_CLIPPED_PITCHES
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static Bool calcTrajectory(
|
||||
const Coord3D& start, // in: where the projectile starts
|
||||
const Coord3D& end, // in: where the projectile wants to end up
|
||||
Real velocity, // in: the initial speed of the projectile
|
||||
Real minPitch, // in: min pitch (-PI/2)
|
||||
Real maxPitch, // in: max pitch (-PI/2)
|
||||
Bool preferShortPitch, // in: prefer the shorter or longer path?
|
||||
Real& angle, // out: the angle to aim
|
||||
Real& pitch // out: the pitch to aim for
|
||||
)
|
||||
{
|
||||
Bool exactTarget = false;
|
||||
|
||||
angle = 0.0f;
|
||||
pitch = 0.0f;
|
||||
|
||||
if (velocity <= 0.0f)
|
||||
{
|
||||
DEBUG_CRASH(("cant get there from here (1)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
Real dx = end.x - start.x;
|
||||
Real dy = end.y - start.y;
|
||||
Real dz = end.z - start.z;
|
||||
|
||||
// calculating the angle is trivial.
|
||||
angle = atan2(dy, dx);
|
||||
|
||||
// calculating the pitch requires a bit more effort.
|
||||
Real horizDistSqr = sqr(dx) + sqr(dy);
|
||||
Real horizDist = sqrt(horizDistSqr);
|
||||
|
||||
// calc the two possible pitches that will cover the given horizontal range.
|
||||
// (this is actually only true if dz==0, but is a good first guess)
|
||||
Real gravity = fabs(TheGlobalData->m_gravity);
|
||||
Real gravityTwoDZ = gravity * 2.0f * dz;
|
||||
|
||||
// let's start by aiming directly for it. we know this isn't right (unless gravity
|
||||
// is zero, which it's not) but is a good starting point...
|
||||
Real theta = atan2(dz, horizDist);
|
||||
// if the angle isn't pretty shallow, we can get a better initial guess by using
|
||||
// the code below...
|
||||
const Real SHALLOW_ANGLE = 0.5f * PI / 180.0f;
|
||||
if (fabs(theta) > SHALLOW_ANGLE)
|
||||
{
|
||||
Real t = horizDist / velocity;
|
||||
Real vz = (dz/t + 0.5f*gravity*t);
|
||||
Real sineOfAngle = clamp(-1.0f, vz / velocity, 1.0f);
|
||||
theta = ASin(sineOfAngle)*0.5f;
|
||||
}
|
||||
|
||||
/*
|
||||
this is, in theory, the "right" formula for dz==0, but I
|
||||
get better results with the stuff above:
|
||||
|
||||
Real sineOfAngle = (gravity * horizDist) / sqr(velocity);
|
||||
if (sineOfAngle > 1.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Real theta = ASin(sineOfAngle)*0.5f;
|
||||
*/
|
||||
|
||||
Real pitches[2];
|
||||
Real cosPitches[2];
|
||||
Real sinPitches[2];
|
||||
Real theta_min = max(minPitch, -PI/2);
|
||||
Real theta_max = min(maxPitch, PI/2);
|
||||
const Real MIN_ANGLE_DIFF = (PI/(180.0f*16.0f)); // 1/16th of a degree. yes, we need that accuracy.
|
||||
//Int numLoops = 0;
|
||||
while (theta_max > theta_min + MIN_ANGLE_DIFF)
|
||||
{
|
||||
//++numLoops;
|
||||
pitches[0] = theta; // shallower angle
|
||||
pitches[1] = (theta >= 0.0) ? (PI/2 - theta) : (-PI/2 - theta); // steeper angle
|
||||
|
||||
DEBUG_ASSERTCRASH(pitches[0]<=PI/2&&pitches[0]>=-PI/2,("bad pitches[0] %f\n",rad2deg(pitches[0])));
|
||||
DEBUG_ASSERTCRASH(pitches[1]<=PI/2&&pitches[1]>=-PI/2,("bad pitches[1] %f\n",rad2deg(pitches[1])));
|
||||
|
||||
// calc the horiz-speed & time for each.
|
||||
// note that time can only be negative for 90<angle<270, and since we
|
||||
// ruled those out above, we're gold.
|
||||
sinPitches[0] = Sin(pitches[0]);
|
||||
sinPitches[1] = Sin(pitches[1]);
|
||||
cosPitches[0] = Cos(pitches[0]);
|
||||
cosPitches[1] = Cos(pitches[1]);
|
||||
Real t0 = (horizDist / (velocity * cosPitches[0]));
|
||||
Real t1 = (horizDist / (velocity * cosPitches[1]));
|
||||
|
||||
t0 = MAX(0,t0);
|
||||
t1 = MAX(0,t1);
|
||||
|
||||
|
||||
DEBUG_ASSERTCRASH(t0>=0&&t1>=0,("neg time"));
|
||||
|
||||
Int preferred = ((t0 < t1) == (preferShortPitch)) ? 0 : 1;
|
||||
|
||||
// ok, NOW... since dz is virtually NEVER zero, do a little approximation
|
||||
// to get a better guess. (solving the equations directly are hard and
|
||||
// it's simpler to approximate)
|
||||
Bool tooClose = false;
|
||||
Real vx, vz, root;
|
||||
|
||||
vz = velocity*sinPitches[preferred];
|
||||
|
||||
root = sqr(vz) - gravityTwoDZ;
|
||||
if (root < 0.0f)
|
||||
{
|
||||
// oops, no solution for our preferred pitch. try the other one.
|
||||
if (preferred == 0)
|
||||
tooClose = true; // if this fails for the shallow case, it's 'cuz the result is too close
|
||||
preferred = 1 - preferred;
|
||||
vz = velocity*sinPitches[preferred];
|
||||
root = sqr(vz) - gravityTwoDZ;
|
||||
if (root < 0.0f)
|
||||
{
|
||||
DEBUG_CRASH(("cant get there from here (2)"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ONLY_RETURN_CLIPPED_PITCHES
|
||||
// if we get this far, we have some plausible solution...
|
||||
// it actually may be off, but let's go ahead and set the result
|
||||
// (along with the "exact" flag) so that the caller will want to
|
||||
// try to use the result. (we'll probably iterate and overwrite
|
||||
// this result with a more precise one.)
|
||||
if (within(minPitch, pitches[preferred], maxPitch))
|
||||
{
|
||||
pitch = pitches[preferred];
|
||||
exactTarget = true;
|
||||
}
|
||||
#else
|
||||
// if we get this far, we have some plausible solution...
|
||||
// it actually may be off, but let's go ahead and set the result
|
||||
// (along with the "exact" flag) so that the caller will want to
|
||||
// try to use the result. (we'll probably iterate and overwrite
|
||||
// this result with a more precise one.) NOTE however that we
|
||||
// don't validate it's within the proper range... caller must do that!
|
||||
pitch = pitches[preferred];
|
||||
exactTarget = true;
|
||||
#endif
|
||||
|
||||
vx = velocity*cosPitches[preferred];
|
||||
Real actualRange = (vx*(vz + sqrt(root)))/gravity;
|
||||
const Real CLOSE_ENOUGH_RANGE = 5.0f;
|
||||
if (tooClose || (actualRange < horizDist - CLOSE_ENOUGH_RANGE))
|
||||
{
|
||||
theta_min = theta;
|
||||
theta = (theta + theta_max) * 0.5f;
|
||||
}
|
||||
else if (actualRange > horizDist + CLOSE_ENOUGH_RANGE)
|
||||
{
|
||||
theta_max = theta;
|
||||
theta = (theta + theta_min) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//DEBUG_LOG(("took %d loops to find a match\n",numLoops));
|
||||
if (exactTarget)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif NOT_IN_USE
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Prepares the missile for launch via proper weapon-system channels.
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DumbProjectileBehavior::projectileLaunchAtObjectOrPosition(
|
||||
const Object* victim,
|
||||
const Coord3D* victimPos,
|
||||
const Object* launcher,
|
||||
WeaponSlotType wslot,
|
||||
Int specificBarrelToUse,
|
||||
const WeaponTemplate* detWeap,
|
||||
const ParticleSystemTemplate* exhaustSysOverride
|
||||
)
|
||||
{
|
||||
const DumbProjectileBehaviorModuleData* d = getDumbProjectileBehaviorModuleData();
|
||||
|
||||
DEBUG_ASSERTCRASH(specificBarrelToUse>=0, ("specificBarrelToUse must now be explicit"));
|
||||
|
||||
m_launcherID = launcher ? launcher->getID() : INVALID_ID;
|
||||
m_extraBonusFlags = launcher ? launcher->getWeaponBonusCondition() : 0;
|
||||
m_victimID = victim ? victim->getID() : INVALID_ID;
|
||||
m_detonationWeaponTmpl = detWeap;
|
||||
m_lifespanFrame = TheGameLogic->getFrame() + d->m_maxLifespan;
|
||||
|
||||
Object* projectile = getObject();
|
||||
|
||||
Weapon::positionProjectileForLaunch(projectile, launcher, wslot, specificBarrelToUse);
|
||||
|
||||
projectileFireAtObjectOrPosition( victim, victimPos, detWeap, exhaustSysOverride );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// The actual firing of the missile once setup. Uses a Bezier curve with points parameterized in ini
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DumbProjectileBehavior::projectileFireAtObjectOrPosition( const Object *victim, const Coord3D *victimPos, const WeaponTemplate *detWeap, const ParticleSystemTemplate* exhaustSysOverride )
|
||||
{
|
||||
const DumbProjectileBehaviorModuleData* d = getDumbProjectileBehaviorModuleData();
|
||||
Object* projectile = getObject();
|
||||
Real weaponSpeed = detWeap ? detWeap->getWeaponSpeed() : 0.0f;
|
||||
Real minWeaponSpeed = detWeap ? detWeap->getMinWeaponSpeed() : 0.0f;
|
||||
|
||||
// if an object, aim at the center, not the ground part
|
||||
Coord3D victimPosToUse;
|
||||
if (victim)
|
||||
victim->getGeometryInfo().getCenterPosition(*victim->getPosition(), victimPosToUse);
|
||||
else
|
||||
victimPosToUse = *victimPos;
|
||||
|
||||
if( detWeap && detWeap->isScaleWeaponSpeed() )
|
||||
{
|
||||
// Some weapons want to scale their start speed to the range
|
||||
Real minRange = detWeap->getMinimumAttackRange();
|
||||
Real maxRange = detWeap->getUnmodifiedAttackRange();
|
||||
Real range = sqrt(ThePartitionManager->getDistanceSquared( projectile, &victimPosToUse, FROM_CENTER_2D ) );
|
||||
Real rangeRatio = (range - minRange) / (maxRange - minRange);
|
||||
m_flightPathSpeed = (rangeRatio * (weaponSpeed - minWeaponSpeed)) + minWeaponSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flightPathSpeed = weaponSpeed;
|
||||
}
|
||||
|
||||
PhysicsBehavior* physics = projectile->getPhysics();
|
||||
if ( d->m_tumbleRandomly && physics)
|
||||
{
|
||||
physics->setPitchRate( GameLogicRandomValueReal( -1.0f/PI, 1.0f/PI ) );
|
||||
physics->setYawRate( GameLogicRandomValueReal( -1.0f/PI, 1.0f/PI ) );
|
||||
physics->setRollRate( GameLogicRandomValueReal( -1.0f/PI, 1.0f/PI ) );
|
||||
}
|
||||
|
||||
m_flightPathStart = *getObject()->getPosition();
|
||||
m_flightPathEnd = victimPosToUse;
|
||||
if (!calcFlightPath(true))
|
||||
{
|
||||
//Can only fail if wildly incorrect points
|
||||
TheGameLogic->destroyObject( projectile );
|
||||
return;
|
||||
}
|
||||
m_currentFlightPathStep = 0;// We are at the first point, because the launching put us there
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool DumbProjectileBehavior::calcFlightPath(Bool recalcNumSegments)
|
||||
{
|
||||
const DumbProjectileBehaviorModuleData* d = getDumbProjectileBehaviorModuleData();
|
||||
|
||||
Coord3D controlPoints[4];
|
||||
//First point is us, last point is them
|
||||
controlPoints[0] = m_flightPathStart;
|
||||
controlPoints[3] = m_flightPathEnd;
|
||||
|
||||
Real highestInterveningTerrain;
|
||||
Bool onMap = ThePartitionManager->estimateTerrainExtremesAlongLine( controlPoints[0], controlPoints[3], NULL, &highestInterveningTerrain, NULL, NULL );
|
||||
if( !onMap )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// X and Y for inner points are along the line between us, so normalize and scale a vector between us, but
|
||||
// only use the x and y of the result
|
||||
Vector3 targetVector;// 0 origin vector between me and him
|
||||
targetVector.X = controlPoints[3].x - controlPoints[0].x;
|
||||
targetVector.Y = controlPoints[3].y - controlPoints[0].y;
|
||||
targetVector.Z = controlPoints[3].z - controlPoints[0].z;
|
||||
|
||||
Real targetDistance = targetVector.Length();
|
||||
targetVector.Normalize();
|
||||
Vector3 firstPointAlongLine = targetVector * (targetDistance * d->m_firstPercentIndent );
|
||||
Vector3 secondPointAlongLine = targetVector * (targetDistance * d->m_secondPercentIndent );
|
||||
|
||||
controlPoints[1].x = firstPointAlongLine.X + controlPoints[0].x;// add world start to offset along the origin based vector
|
||||
controlPoints[1].y = firstPointAlongLine.Y + controlPoints[0].y;
|
||||
controlPoints[2].x = secondPointAlongLine.X + controlPoints[0].x;
|
||||
controlPoints[2].y = secondPointAlongLine.Y + controlPoints[0].y;
|
||||
|
||||
// Z's are determined using the highest intervening height so they won't hit hills, low end bounded by current Zs
|
||||
highestInterveningTerrain = max( highestInterveningTerrain, controlPoints[0].z );
|
||||
highestInterveningTerrain = max( highestInterveningTerrain, controlPoints[3].z );
|
||||
controlPoints[1].z = highestInterveningTerrain + d->m_firstHeight;
|
||||
controlPoints[2].z = highestInterveningTerrain + d->m_secondHeight;
|
||||
|
||||
// With four control points, we have a curve. We will decide how many frames we want to take to get to the target,
|
||||
// and fill our vector with those curve points.
|
||||
BezierSegment flightCurve( controlPoints );
|
||||
if (recalcNumSegments)
|
||||
{
|
||||
Real flightDistance = flightCurve.getApproximateLength();
|
||||
m_flightPathSegments = ceil( flightDistance / m_flightPathSpeed );
|
||||
}
|
||||
flightCurve.getSegmentPoints( m_flightPathSegments, &m_flightPath );
|
||||
DEBUG_ASSERTCRASH(m_flightPathSegments == m_flightPath.size(), ("m_flightPathSegments mismatch"));
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if( TheGlobalData->m_debugProjectilePath )
|
||||
displayFlightPath();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool DumbProjectileBehavior::projectileHandleCollision( Object *other )
|
||||
{
|
||||
const DumbProjectileBehaviorModuleData* d = getDumbProjectileBehaviorModuleData();
|
||||
|
||||
if (other != NULL)
|
||||
{
|
||||
Object *projectileLauncher = TheGameLogic->findObjectByID( projectileGetLauncherID() );
|
||||
|
||||
// if it's not the specific thing we were targeting, see if we should incidentally collide...
|
||||
if (!m_detonationWeaponTmpl->shouldProjectileCollideWith(projectileLauncher, getObject(), other, m_victimID))
|
||||
{
|
||||
//DEBUG_LOG(("ignoring projectile collision with %s at frame %d\n",other->getTemplate()->getName().str(),TheGameLogic->getFrame()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (d->m_garrisonHitKillCount > 0)
|
||||
{
|
||||
ContainModuleInterface* contain = other->getContain();
|
||||
if( contain && contain->getContainCount() > 0 && contain->isGarrisonable() && !contain->isImmuneToClearBuildingAttacks() )
|
||||
{
|
||||
Int numKilled = 0;
|
||||
|
||||
// garrisonable buildings subvert the normal process here.
|
||||
const ContainedItemsList* items = contain->getContainedItemsList();
|
||||
if (items)
|
||||
{
|
||||
for (ContainedItemsList::const_iterator it = items->begin(); *it != NULL && numKilled < d->m_garrisonHitKillCount; )
|
||||
{
|
||||
Object* thingToKill = *it++;
|
||||
if (!thingToKill->isEffectivelyDead() && thingToKill->isKindOfMulti(d->m_garrisonHitKillKindof, d->m_garrisonHitKillKindofNot))
|
||||
{
|
||||
//DEBUG_LOG(("Killed a garrisoned unit (%08lx %s) via Flash-Bang!\n",thingToKill,thingToKill->getTemplate()->getName().str()));
|
||||
if (projectileLauncher)
|
||||
projectileLauncher->scoreTheKill( thingToKill );
|
||||
thingToKill->kill();
|
||||
++numKilled;
|
||||
}
|
||||
} // next contained item
|
||||
} // if items
|
||||
|
||||
if (numKilled > 0)
|
||||
{
|
||||
// note, fx is played at center of building, not at grenade's location
|
||||
FXList::doFXObj(d->m_garrisonHitKillFX, other, NULL);
|
||||
|
||||
// don't do the normal explosion; just destroy ourselves & return
|
||||
TheGameLogic->destroyObject(getObject());
|
||||
|
||||
return true;
|
||||
}
|
||||
} // if a garrisonable thing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// collided with something... blow'd up!
|
||||
detonate();
|
||||
|
||||
// mark ourself as "no collisions" (since we might still exist in slow death mode)
|
||||
getObject()->setStatus(OBJECT_STATUS_NO_COLLISIONS);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DumbProjectileBehavior::detonate()
|
||||
{
|
||||
Object* obj = getObject();
|
||||
if (m_detonationWeaponTmpl)
|
||||
{
|
||||
TheWeaponStore->handleProjectileDetonation(m_detonationWeaponTmpl, obj, obj->getPosition(), m_extraBonusFlags);
|
||||
|
||||
if ( getDumbProjectileBehaviorModuleData()->m_detonateCallsKill )
|
||||
{
|
||||
// don't call kill(); do it manually, so we can specify DEATH_DETONATED
|
||||
DamageInfo damageInfo;
|
||||
damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
|
||||
damageInfo.in.m_deathType = DEATH_DETONATED;
|
||||
damageInfo.in.m_sourceID = INVALID_ID;
|
||||
damageInfo.in.m_amount = obj->getBodyModule()->getMaxHealth();
|
||||
obj->attemptDamage( &damageInfo );
|
||||
}
|
||||
else
|
||||
{
|
||||
TheGameLogic->destroyObject( obj );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't call kill(); do it manually, so we can specify DEATH_DETONATED
|
||||
DamageInfo damageInfo;
|
||||
damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
|
||||
damageInfo.in.m_deathType = DEATH_DETONATED;
|
||||
damageInfo.in.m_sourceID = INVALID_ID;
|
||||
damageInfo.in.m_amount = obj->getBodyModule()->getMaxHealth();
|
||||
obj->attemptDamage( &damageInfo );
|
||||
}
|
||||
|
||||
if (obj->getDrawable())
|
||||
obj->getDrawable()->setDrawableHidden(true);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Simulate one frame of a missile's behavior
|
||||
*/
|
||||
UpdateSleepTime DumbProjectileBehavior::update()
|
||||
{
|
||||
const DumbProjectileBehaviorModuleData* d = getDumbProjectileBehaviorModuleData();
|
||||
|
||||
if (m_lifespanFrame != 0 && TheGameLogic->getFrame() >= m_lifespanFrame)
|
||||
{
|
||||
// lifetime demands detonation
|
||||
detonate();
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
if( m_currentFlightPathStep >= m_flightPath.size() )
|
||||
{
|
||||
// No more steps to use. Would go out of bounds on vector, so have to do something.
|
||||
// We could allow physics to take over and make us fall, but the point of this whole task
|
||||
// is to guarentee where the shell explodes. This way, it _will_ explode at the target point.
|
||||
detonate();
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
if (m_victimID != INVALID_ID && d->m_flightPathAdjustDistPerFrame > 0.0f)
|
||||
{
|
||||
Object* victim = TheGameLogic->findObjectByID(m_victimID);
|
||||
if (victim)
|
||||
{
|
||||
Coord3D newVictimPos;
|
||||
victim->getGeometryInfo().getCenterPosition(*victim->getPosition(), newVictimPos);
|
||||
Coord3D delta;
|
||||
delta.x = newVictimPos.x - m_flightPathEnd.x;
|
||||
delta.y = newVictimPos.y - m_flightPathEnd.y;
|
||||
delta.z = newVictimPos.z - m_flightPathEnd.z;
|
||||
Real distVictimMovedSqr = sqr(delta.x) + sqr(delta.y) + sqr(delta.z);
|
||||
if (distVictimMovedSqr > 0.1f)
|
||||
{
|
||||
Real distVictimMoved = sqrtf(distVictimMovedSqr);
|
||||
if (distVictimMoved > d->m_flightPathAdjustDistPerFrame)
|
||||
distVictimMoved = d->m_flightPathAdjustDistPerFrame;
|
||||
delta.normalize();
|
||||
m_flightPathEnd.x += distVictimMoved * delta.x;
|
||||
m_flightPathEnd.y += distVictimMoved * delta.y;
|
||||
m_flightPathEnd.z += distVictimMoved * delta.z;
|
||||
if (!calcFlightPath(false))
|
||||
{
|
||||
DEBUG_CRASH(("Hmm, recalc of flight path returned false... should this happen?"));
|
||||
detonate();
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise, continue to force the flight path
|
||||
Coord3D flightStep = m_flightPath[m_currentFlightPathStep];
|
||||
|
||||
if (d->m_orientToFlightPath && (!d->m_tumbleRandomly) && m_currentFlightPathStep > 0)
|
||||
{
|
||||
// this seems reasonable; however, if this object has a PhysicsBehavior on it, this calc will be wrong,
|
||||
// since Physics is applying gravity, which we duly ignore, but the prevPos won't be what we expect.
|
||||
// get it from the flight path instead. (srj)
|
||||
//Coord3D prevPos = *getObject()->getPosition();
|
||||
|
||||
Coord3D prevPos = m_flightPath[m_currentFlightPathStep - 1];
|
||||
|
||||
Vector3 curDir(flightStep.x - prevPos.x, flightStep.y - prevPos.y, flightStep.z - prevPos.z);
|
||||
curDir.Normalize(); // buildTransformMatrix wants it this way
|
||||
|
||||
Matrix3D orientMtx;
|
||||
orientMtx.buildTransformMatrix(Vector3(flightStep.x, flightStep.y, flightStep.z), curDir);
|
||||
getObject()->setTransformMatrix(&orientMtx);
|
||||
}
|
||||
else
|
||||
{
|
||||
getObject()->setPosition(&flightStep);
|
||||
}
|
||||
|
||||
// note that we want to use getHighestLayerForDestination() here, so that anything even slightly
|
||||
// below the bridge translates into GROUND. (getLayerForDestination just does a "closest" check)
|
||||
PathfindLayerEnum oldLayer = getObject()->getLayer();
|
||||
PathfindLayerEnum newLayer = TheTerrainLogic->getHighestLayerForDestination(getObject()->getPosition());
|
||||
getObject()->setLayer(newLayer);
|
||||
|
||||
if (oldLayer != LAYER_GROUND && newLayer == LAYER_GROUND)
|
||||
{
|
||||
// see if we' still in the bridge's xy area
|
||||
Coord3D tmp = *getObject()->getPosition();
|
||||
tmp.z = 9999.0f;
|
||||
PathfindLayerEnum testLayer = TheTerrainLogic->getHighestLayerForDestination(&tmp);
|
||||
if (testLayer == oldLayer)
|
||||
{
|
||||
// ensure we are slightly above the bridge, to account for fudge & sloppy art
|
||||
const Real FUDGE = 2.0f;
|
||||
tmp.z = TheTerrainLogic->getLayerHeight(tmp.x, tmp.y, testLayer) + FUDGE;
|
||||
getObject()->setPosition(&tmp);
|
||||
// blow'd up!
|
||||
detonate();
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
++m_currentFlightPathStep;
|
||||
|
||||
return UPDATE_SLEEP_NONE;//This no longer flys with physics, so it needs to not sleep
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** displayFlightPath for debugging */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
void DumbProjectileBehavior::displayFlightPath()
|
||||
{
|
||||
extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
|
||||
for( Int pointIndex = 0; pointIndex < m_flightPath.size(); ++pointIndex )
|
||||
{
|
||||
addIcon(&m_flightPath[pointIndex], TheGlobalData->m_debugProjectileTileWidth,
|
||||
TheGlobalData->m_debugProjectileTileDuration,
|
||||
TheGlobalData->m_debugProjectileTileColor);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DumbProjectileBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DumbProjectileBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// launcher
|
||||
xfer->xferObjectID( &m_launcherID );
|
||||
|
||||
// victim ID
|
||||
xfer->xferObjectID( &m_victimID );
|
||||
|
||||
xfer->xferInt( &m_flightPathSegments );
|
||||
xfer->xferReal( &m_flightPathSpeed );
|
||||
xfer->xferCoord3D( &m_flightPathStart );
|
||||
xfer->xferCoord3D( &m_flightPathEnd );
|
||||
|
||||
// weapon template
|
||||
AsciiString weaponTemplateName = AsciiString::TheEmptyString;
|
||||
if( m_detonationWeaponTmpl )
|
||||
weaponTemplateName = m_detonationWeaponTmpl->getName();
|
||||
xfer->xferAsciiString( &weaponTemplateName );
|
||||
if( xfer->getXferMode() == XFER_LOAD )
|
||||
{
|
||||
|
||||
if( weaponTemplateName == AsciiString::TheEmptyString )
|
||||
m_detonationWeaponTmpl = NULL;
|
||||
else
|
||||
{
|
||||
|
||||
// find template
|
||||
m_detonationWeaponTmpl = TheWeaponStore->findWeaponTemplate( weaponTemplateName );
|
||||
|
||||
// sanity
|
||||
if( m_detonationWeaponTmpl == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "DumbProjectileBehavior::xfer - Unknown weapon template '%s'\n",
|
||||
weaponTemplateName.str() ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end else
|
||||
|
||||
} // end if
|
||||
|
||||
// lifespan frame
|
||||
xfer->xferUnsignedInt( &m_lifespanFrame );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DumbProjectileBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: FireWeaponWhenDamagedBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#define DEFINE_SLOWDEATHPHASE_NAMES
|
||||
|
||||
#include "Common/Thing.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/FireWeaponWhenDamagedBehavior.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
const Int MAX_IDX = 32;
|
||||
|
||||
const Real BEGIN_MIDPOINT_RATIO = 0.35f;
|
||||
const Real END_MIDPOINT_RATIO = 0.65f;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FireWeaponWhenDamagedBehavior::FireWeaponWhenDamagedBehavior( Thing *thing, const ModuleData* moduleData ) :
|
||||
UpdateModule( thing, moduleData ),
|
||||
m_reactionWeaponPristine( NULL ),
|
||||
m_reactionWeaponDamaged( NULL ),
|
||||
m_reactionWeaponReallyDamaged( NULL ),
|
||||
m_reactionWeaponRubble( NULL ),
|
||||
m_continuousWeaponPristine( NULL ),
|
||||
m_continuousWeaponDamaged( NULL ),
|
||||
m_continuousWeaponReallyDamaged( NULL ),
|
||||
m_continuousWeaponRubble( NULL )
|
||||
{
|
||||
|
||||
const FireWeaponWhenDamagedBehaviorModuleData *d = getFireWeaponWhenDamagedBehaviorModuleData();
|
||||
const Object* obj = getObject();
|
||||
|
||||
if ( d->m_reactionWeaponPristine )
|
||||
{
|
||||
m_reactionWeaponPristine = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_reactionWeaponPristine, PRIMARY_WEAPON);
|
||||
m_reactionWeaponPristine->reloadAmmo( obj );
|
||||
}
|
||||
if ( d->m_reactionWeaponDamaged )
|
||||
{
|
||||
m_reactionWeaponDamaged = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_reactionWeaponDamaged, PRIMARY_WEAPON);
|
||||
m_reactionWeaponDamaged->reloadAmmo( obj );
|
||||
}
|
||||
if ( d->m_reactionWeaponReallyDamaged )
|
||||
{
|
||||
m_reactionWeaponReallyDamaged = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_reactionWeaponReallyDamaged, PRIMARY_WEAPON);
|
||||
m_reactionWeaponReallyDamaged->reloadAmmo( obj );
|
||||
}
|
||||
if ( d->m_reactionWeaponRubble )
|
||||
{
|
||||
m_reactionWeaponRubble = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_reactionWeaponRubble, PRIMARY_WEAPON);
|
||||
m_reactionWeaponRubble->reloadAmmo( obj );
|
||||
}
|
||||
|
||||
|
||||
if ( d->m_continuousWeaponPristine )
|
||||
{
|
||||
m_continuousWeaponPristine = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_continuousWeaponPristine, PRIMARY_WEAPON);
|
||||
m_continuousWeaponPristine->reloadAmmo( obj );
|
||||
}
|
||||
if ( d->m_continuousWeaponDamaged )
|
||||
{
|
||||
m_continuousWeaponDamaged = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_continuousWeaponDamaged, PRIMARY_WEAPON);
|
||||
m_continuousWeaponDamaged->reloadAmmo( obj );
|
||||
}
|
||||
if ( d->m_continuousWeaponReallyDamaged )
|
||||
{
|
||||
m_continuousWeaponReallyDamaged = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_continuousWeaponReallyDamaged, PRIMARY_WEAPON);
|
||||
m_continuousWeaponReallyDamaged->reloadAmmo( obj );
|
||||
}
|
||||
if ( d->m_continuousWeaponRubble )
|
||||
{
|
||||
m_continuousWeaponRubble = TheWeaponStore->allocateNewWeapon(
|
||||
d->m_continuousWeaponRubble, PRIMARY_WEAPON);
|
||||
m_continuousWeaponRubble->reloadAmmo( obj );
|
||||
}
|
||||
|
||||
if (d->m_initiallyActive)
|
||||
{
|
||||
giveSelfUpgrade();
|
||||
}
|
||||
|
||||
if (isUpgradeActive() &&
|
||||
(d->m_continuousWeaponPristine != NULL ||
|
||||
d->m_continuousWeaponDamaged != NULL ||
|
||||
d->m_continuousWeaponReallyDamaged != NULL ||
|
||||
d->m_continuousWeaponRubble != NULL))
|
||||
{
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FireWeaponWhenDamagedBehavior::~FireWeaponWhenDamagedBehavior( void )
|
||||
{
|
||||
if (m_reactionWeaponPristine)
|
||||
m_reactionWeaponPristine->deleteInstance();
|
||||
if (m_reactionWeaponDamaged)
|
||||
m_reactionWeaponDamaged->deleteInstance();
|
||||
if (m_reactionWeaponReallyDamaged)
|
||||
m_reactionWeaponReallyDamaged->deleteInstance();
|
||||
if (m_reactionWeaponRubble)
|
||||
m_reactionWeaponRubble->deleteInstance();
|
||||
|
||||
if (m_continuousWeaponPristine)
|
||||
m_continuousWeaponPristine->deleteInstance();
|
||||
if (m_continuousWeaponDamaged)
|
||||
m_continuousWeaponDamaged->deleteInstance();
|
||||
if (m_continuousWeaponReallyDamaged)
|
||||
m_continuousWeaponReallyDamaged->deleteInstance();
|
||||
if (m_continuousWeaponRubble)
|
||||
m_continuousWeaponRubble->deleteInstance();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Damage has been dealt, this is an opportunity to reach to that damage */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDamagedBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isUpgradeActive())
|
||||
return;
|
||||
|
||||
const FireWeaponWhenDamagedBehaviorModuleData* d = getFireWeaponWhenDamagedBehaviorModuleData();
|
||||
|
||||
// right type?
|
||||
if (!getDamageTypeFlag(d->m_damageTypes, damageInfo->in.m_damageType))
|
||||
return;
|
||||
|
||||
// right amount? (use actual [post-armor] damage dealt)
|
||||
if (damageInfo->out.m_actualDamageDealt < d->m_damageAmount)
|
||||
return;
|
||||
|
||||
const Object *obj = getObject();
|
||||
BodyDamageType bdt = obj->getBodyModule()->getDamageState();
|
||||
|
||||
if ( bdt == BODY_RUBBLE )
|
||||
{
|
||||
if( m_reactionWeaponRubble && m_reactionWeaponRubble->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_reactionWeaponRubble->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
|
||||
}
|
||||
else if ( bdt == BODY_REALLYDAMAGED )
|
||||
{
|
||||
if( m_reactionWeaponReallyDamaged && m_reactionWeaponReallyDamaged->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_reactionWeaponReallyDamaged->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
}
|
||||
else if ( bdt == BODY_DAMAGED )
|
||||
{
|
||||
if( m_reactionWeaponDamaged && m_reactionWeaponDamaged->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_reactionWeaponDamaged->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
}
|
||||
else // not damaged yet
|
||||
{
|
||||
if( m_reactionWeaponPristine && m_reactionWeaponPristine->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_reactionWeaponPristine->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** if object fires weapon constantly, figure out which one and do it */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime FireWeaponWhenDamagedBehavior::update( void )
|
||||
{
|
||||
if (!isUpgradeActive())
|
||||
{
|
||||
DEBUG_ASSERTCRASH(isUpgradeActive(), ("hmm, this should not be possible"));
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
|
||||
const Object *obj = getObject();
|
||||
BodyDamageType bdt = obj->getBodyModule()->getDamageState();
|
||||
|
||||
if ( bdt == BODY_RUBBLE )
|
||||
{
|
||||
if( m_continuousWeaponRubble && m_continuousWeaponRubble->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_continuousWeaponRubble->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
|
||||
}
|
||||
else if ( bdt == BODY_REALLYDAMAGED )
|
||||
{
|
||||
if( m_continuousWeaponReallyDamaged && m_continuousWeaponReallyDamaged->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_continuousWeaponReallyDamaged->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
}
|
||||
else if ( bdt == BODY_DAMAGED )
|
||||
{
|
||||
if( m_continuousWeaponDamaged && m_continuousWeaponDamaged->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_continuousWeaponDamaged->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
}
|
||||
else // not damaged yet
|
||||
{
|
||||
if( m_continuousWeaponPristine && m_continuousWeaponPristine->getStatus() == READY_TO_FIRE )
|
||||
{
|
||||
m_continuousWeaponPristine->forceFireWeapon( obj, obj->getPosition() );
|
||||
}
|
||||
}
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDamagedBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
// extend upgrade mux
|
||||
UpgradeMux::upgradeMuxCRC( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDamagedBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// extend upgrade mux
|
||||
UpgradeMux::upgradeMuxXfer( xfer );
|
||||
|
||||
Bool weaponPresent;
|
||||
|
||||
// reaction pristine
|
||||
weaponPresent = m_reactionWeaponPristine ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_reactionWeaponPristine );
|
||||
|
||||
// reaction damaged
|
||||
weaponPresent = m_reactionWeaponDamaged ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_reactionWeaponDamaged );
|
||||
|
||||
// reaction really damaged
|
||||
weaponPresent = m_reactionWeaponReallyDamaged ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_reactionWeaponReallyDamaged );
|
||||
|
||||
// reaction rubble
|
||||
weaponPresent = m_reactionWeaponRubble ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_reactionWeaponRubble );
|
||||
|
||||
// continuous pristine
|
||||
weaponPresent = m_continuousWeaponPristine ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_continuousWeaponPristine );
|
||||
|
||||
// continuous damaged
|
||||
weaponPresent = m_continuousWeaponDamaged ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_continuousWeaponDamaged );
|
||||
|
||||
// continuous really damaged
|
||||
weaponPresent = m_continuousWeaponReallyDamaged ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_continuousWeaponReallyDamaged );
|
||||
|
||||
// continuous rubble
|
||||
weaponPresent = m_continuousWeaponRubble ? TRUE : FALSE;
|
||||
xfer->xferBool( &weaponPresent );
|
||||
if( weaponPresent )
|
||||
xfer->xferSnapshot( m_continuousWeaponRubble );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDamagedBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
// extend upgrade mux
|
||||
UpgradeMux::upgradeMuxLoadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: FireWeaponWhenDeadBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#define DEFINE_SLOWDEATHPHASE_NAMES
|
||||
|
||||
#include "Common/Thing.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/FireWeaponWhenDeadBehavior.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "Common/Player.h"
|
||||
|
||||
const Int MAX_IDX = 32;
|
||||
|
||||
const Real BEGIN_MIDPOINT_RATIO = 0.35f;
|
||||
const Real END_MIDPOINT_RATIO = 0.65f;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FireWeaponWhenDeadBehavior::FireWeaponWhenDeadBehavior( Thing *thing, const ModuleData* moduleData ) :
|
||||
BehaviorModule( thing, moduleData )
|
||||
{
|
||||
if (getFireWeaponWhenDeadBehaviorModuleData()->m_initiallyActive)
|
||||
{
|
||||
giveSelfUpgrade();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FireWeaponWhenDeadBehavior::~FireWeaponWhenDeadBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDeadBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
const FireWeaponWhenDeadBehaviorModuleData* d = getFireWeaponWhenDeadBehaviorModuleData();
|
||||
|
||||
if (!isUpgradeActive())
|
||||
return;
|
||||
|
||||
// right type?
|
||||
if (!d->m_dieMuxData.isDieApplicable(getObject(), damageInfo))
|
||||
return;
|
||||
|
||||
// This will never apply until built. Otherwise canceling construction sets it off, and killing
|
||||
// a one hitpoint one percent building will too.
|
||||
if( BitTest( getObject()->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == TRUE )
|
||||
return;
|
||||
|
||||
|
||||
Int64 activation, conflicting;
|
||||
getUpgradeActivationMasks( activation, conflicting );
|
||||
|
||||
if( getObject()->getObjectCompletedUpgradeMask() & conflicting )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( getObject()->getControllingPlayer()->getCompletedUpgradeMask() & conflicting )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->m_deathWeapon)
|
||||
{
|
||||
// fire the default weapon
|
||||
TheWeaponStore->createAndFireTempWeapon(d->m_deathWeapon, getObject(), getObject()->getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDeadBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
// extend upgrade mux
|
||||
UpgradeMux::upgradeMuxCRC( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDeadBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
// extend upgrade mux
|
||||
UpgradeMux::upgradeMuxXfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponWhenDeadBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
// extend upgrade mux
|
||||
UpgradeMux::upgradeMuxLoadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GenerateMinefieldBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#define DEFINE_SLOWDEATHPHASE_NAMES
|
||||
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/Thing.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/GenerateMinefieldBehavior.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectIter.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GenerateMinefieldBehaviorModuleData::GenerateMinefieldBehaviorModuleData()
|
||||
{
|
||||
m_mineName.clear();
|
||||
m_genFX = NULL;
|
||||
m_distanceAroundObject = TheGlobalData->m_standardMinefieldDistance;
|
||||
m_minesPerSquareFoot = TheGlobalData->m_standardMinefieldDensity;
|
||||
m_onDeath = false;
|
||||
m_borderOnly = true;
|
||||
m_alwaysCircular = false;
|
||||
m_smartBorder = false;
|
||||
m_smartBorderSkipInterior = true;
|
||||
m_randomJitter = 0.0f;
|
||||
m_skipIfThisMuchUnderStructure = 0.33f;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void GenerateMinefieldBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "MineName", INI::parseAsciiString, NULL, offsetof( GenerateMinefieldBehaviorModuleData, m_mineName ) },
|
||||
{ "GenerationFX", INI::parseFXList, NULL, offsetof( GenerateMinefieldBehaviorModuleData, m_genFX ) },
|
||||
{ "DistanceAroundObject", INI::parseReal, NULL, offsetof( GenerateMinefieldBehaviorModuleData, m_distanceAroundObject ) },
|
||||
{ "MinesPerSquareFoot", INI::parseReal, NULL, offsetof( GenerateMinefieldBehaviorModuleData, m_minesPerSquareFoot ) },
|
||||
{ "GenerateOnlyOnDeath", INI::parseBool, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_onDeath) },
|
||||
{ "BorderOnly", INI::parseBool, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_borderOnly) },
|
||||
{ "SmartBorder", INI::parseBool, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_smartBorder) },
|
||||
{ "SmartBorderSkipInterior", INI::parseBool, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_smartBorderSkipInterior) },
|
||||
{ "AlwaysCircular", INI::parseBool, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_alwaysCircular) },
|
||||
{ "RandomJitter", INI::parsePercentToReal, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_randomJitter) },
|
||||
{ "SkipIfThisMuchUnderStructure", INI::parsePercentToReal, NULL, offsetof(GenerateMinefieldBehaviorModuleData, m_skipIfThisMuchUnderStructure) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
BehaviorModuleData::buildFieldParse(p);
|
||||
p.add(dataFieldParse);
|
||||
p.add(UpgradeMuxData::getFieldParse(), offsetof( GenerateMinefieldBehaviorModuleData, m_upgradeMuxData ));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GenerateMinefieldBehavior::GenerateMinefieldBehavior( Thing *thing, const ModuleData* moduleData ) : BehaviorModule( thing, moduleData )
|
||||
{
|
||||
m_target.zero();
|
||||
m_generated = false;
|
||||
m_hasTarget = false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GenerateMinefieldBehavior::~GenerateMinefieldBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::upgradeImplementation()
|
||||
{
|
||||
placeMines();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
const GenerateMinefieldBehaviorModuleData* d = getGenerateMinefieldBehaviorModuleData();
|
||||
|
||||
if (d->m_onDeath)
|
||||
{
|
||||
placeMines();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::setMinefieldTarget(const Coord3D* pos)
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
m_hasTarget = true;
|
||||
m_target = *pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hasTarget = false;
|
||||
m_target.zero();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Coord3D* GenerateMinefieldBehavior::getMinefieldTarget() const
|
||||
{
|
||||
return m_hasTarget ? &m_target : getObject()->getPosition();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static Bool isAnythingTooClose2D(const std::vector<Object*>& v, const Coord3D& pos, Real minDistSqr)
|
||||
{
|
||||
for (std::vector<Object*>::const_iterator it = v.begin(); it != v.end(); ++it)
|
||||
{
|
||||
const Coord3D* p = (*it)->getPosition();
|
||||
Real distSqr = sqr(p->x - pos.x) + sqr(p->y - pos.y);
|
||||
if (distSqr < minDistSqr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void offsetBySmallRandomAmount(Coord3D& pt, Real maxAmt)
|
||||
{
|
||||
pt.x += GameLogicRandomValueReal(-maxAmt, maxAmt);
|
||||
pt.y += GameLogicRandomValueReal(-maxAmt, maxAmt);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Object* GenerateMinefieldBehavior::placeMineAt(const Coord3D& pt, const ThingTemplate* mineTemplate, Team* team, const Object* producer)
|
||||
{
|
||||
Coord3D tmp = pt;
|
||||
tmp.z = 99999.0f;
|
||||
PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination(&tmp);
|
||||
|
||||
if (layer == LAYER_GROUND && TheTerrainLogic->isUnderwater(pt.x, pt.y))
|
||||
return NULL;
|
||||
|
||||
if (layer == LAYER_GROUND && TheTerrainLogic->isCliffCell(pt.x, pt.y))
|
||||
return NULL;
|
||||
|
||||
Real orient = GameLogicRandomValueReal(-PI, PI);
|
||||
|
||||
// if the mine will be "mostly" under a structure, don't place it.
|
||||
// for now, "mostly" means "central third of radius would overlap"
|
||||
const GenerateMinefieldBehaviorModuleData* d = getGenerateMinefieldBehaviorModuleData();
|
||||
GeometryInfo geom = mineTemplate->getTemplateGeometryInfo();
|
||||
Real mineRadius = mineTemplate->getTemplateGeometryInfo().getBoundingCircleRadius();
|
||||
geom.expandFootprint(mineRadius * -(1.0f - d->m_skipIfThisMuchUnderStructure));
|
||||
ObjectIterator *iter = ThePartitionManager->iteratePotentialCollisions( &pt, geom, orient );
|
||||
MemoryPoolObjectHolder hold(iter);
|
||||
for (Object* them = iter->first(); them; them = iter->next())
|
||||
{
|
||||
if (them->isKindOf(KINDOF_STRUCTURE))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Object* mine = TheThingFactory->newObject(mineTemplate, team);
|
||||
mine->setPosition(&pt);
|
||||
mine->setOrientation(orient);
|
||||
mine->setProducer(producer);
|
||||
|
||||
for (BehaviorModule** bmi = mine->getBehaviorModules(); *bmi; ++bmi)
|
||||
{
|
||||
LandMineInterface* lmi = (*bmi)->getLandMineInterface();
|
||||
if (lmi)
|
||||
{
|
||||
lmi->setScootParms(*producer->getPosition(), pt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mine;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::placeMinesAlongLine(const Coord3D& posStart, const Coord3D& posEnd, const ThingTemplate* mineTemplate, Bool skipOneAtStart)
|
||||
{
|
||||
const Object* obj = getObject();
|
||||
const GenerateMinefieldBehaviorModuleData* d = getGenerateMinefieldBehaviorModuleData();
|
||||
Team* team = obj->getControllingPlayer()->getDefaultTeam();
|
||||
|
||||
Real dx = posEnd.x - posStart.x;
|
||||
Real dy = posEnd.y - posStart.y;
|
||||
Real len = sqrt(sqr(dx) + sqr(dy));
|
||||
Real mineRadius = mineTemplate->getTemplateGeometryInfo().getBoundingCircleRadius();
|
||||
Real mineDiameter = mineRadius * 2.0f;
|
||||
Real mineJitter = mineRadius*d->m_randomJitter;
|
||||
Int numMines = REAL_TO_INT_CEIL(len / mineDiameter);
|
||||
if (numMines < 1)
|
||||
numMines = 1;
|
||||
Real inc = len/numMines;
|
||||
for (Real place = skipOneAtStart ? inc : 0; place <= len; place += inc)
|
||||
{
|
||||
Coord3D pt;
|
||||
pt.x = posStart.x + place * dx / len;
|
||||
pt.y = posStart.y + place * dy / len;
|
||||
pt.z = TheTerrainLogic->getGroundHeight( pt.x, pt.y );
|
||||
offsetBySmallRandomAmount(pt, mineJitter);
|
||||
placeMineAt(pt, mineTemplate, team, obj);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void makeCorner(const Coord3D& pos, Real majorRadius, Real minorRadius, const Matrix3D& mtx, Coord3D& corner)
|
||||
{
|
||||
Vector3 tmp;
|
||||
tmp.X = majorRadius;
|
||||
tmp.Y = minorRadius;
|
||||
tmp.Z = 0;
|
||||
Matrix3D::Transform_Vector(mtx, tmp, &tmp);
|
||||
corner.x = tmp.X;
|
||||
corner.y = tmp.Y;
|
||||
corner.z = tmp.Z;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::placeMinesAroundRect(const Coord3D& pos, Real majorRadius, Real minorRadius, const ThingTemplate* mineTemplate)
|
||||
{
|
||||
const Object* obj = getObject();
|
||||
const Matrix3D* mtx = obj->getTransformMatrix();
|
||||
|
||||
Coord3D pt[4];
|
||||
makeCorner(pos, majorRadius, minorRadius, *mtx, pt[0]);
|
||||
makeCorner(pos, -majorRadius, minorRadius, *mtx, pt[1]);
|
||||
makeCorner(pos, -majorRadius, -minorRadius, *mtx, pt[2]);
|
||||
makeCorner(pos, majorRadius, -minorRadius, *mtx, pt[3]);
|
||||
|
||||
placeMinesAlongLine(pt[0], pt[1], mineTemplate, true);
|
||||
placeMinesAlongLine(pt[1], pt[2], mineTemplate, true);
|
||||
placeMinesAlongLine(pt[2], pt[3], mineTemplate, true);
|
||||
placeMinesAlongLine(pt[3], pt[0], mineTemplate, true);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::placeMinesAroundCircle(const Coord3D& pos, Real radius, const ThingTemplate* mineTemplate)
|
||||
{
|
||||
const Object* obj = getObject();
|
||||
const GenerateMinefieldBehaviorModuleData* d = getGenerateMinefieldBehaviorModuleData();
|
||||
Team* team = obj->getControllingPlayer()->getDefaultTeam();
|
||||
|
||||
Real circum = 2.0f * PI * radius;
|
||||
Real mineRadius = mineTemplate->getTemplateGeometryInfo().getBoundingCircleRadius();
|
||||
Real mineDiameter = mineRadius * 2.0f;
|
||||
Real mineJitter = mineRadius*d->m_randomJitter;
|
||||
Int numMines = REAL_TO_INT_CEIL(circum / mineDiameter);
|
||||
if (numMines < 1)
|
||||
numMines = 1;
|
||||
Real angleInc = (2*PI)/numMines;
|
||||
Real angleLim = (2*PI) - angleInc*0.5f;
|
||||
for (Real angle = 0; angle < angleLim; angle += angleInc)
|
||||
{
|
||||
Coord3D pt;
|
||||
pt.x = pos.x + radius * Cos(angle);
|
||||
pt.y = pos.y + radius * Sin(angle);
|
||||
pt.z = TheTerrainLogic->getGroundHeight( pt.x, pt.y );
|
||||
offsetBySmallRandomAmount(pt, mineJitter);
|
||||
placeMineAt(pt, mineTemplate, team, obj);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::placeMinesInFootprint(const GeometryInfo& geom, const ThingTemplate* mineTemplate)
|
||||
{
|
||||
const Object* obj = getObject();
|
||||
const GenerateMinefieldBehaviorModuleData* d = getGenerateMinefieldBehaviorModuleData();
|
||||
Team* team = obj->getControllingPlayer()->getDefaultTeam();
|
||||
|
||||
Real area = geom.getFootprintArea();
|
||||
Int numMines = REAL_TO_INT_CEIL(d->m_minesPerSquareFoot * area);
|
||||
if (numMines < 1)
|
||||
numMines = 1;
|
||||
|
||||
const Coord3D* target = getMinefieldTarget();
|
||||
std::vector<Object*> minesCreatedSoFar;
|
||||
Real minDistSqr = sqr(mineTemplate->getTemplateGeometryInfo().getBoundingCircleRadius() * 2.0f);
|
||||
for (int i = 0; i < numMines; ++i)
|
||||
{
|
||||
Coord3D pt;
|
||||
Int maxRetry = 100;
|
||||
do
|
||||
{
|
||||
geom.makeRandomOffsetWithinFootprint(pt);
|
||||
pt.x += target->x;
|
||||
pt.y += target->y;
|
||||
pt.z += target->z;
|
||||
--maxRetry;
|
||||
} while (isAnythingTooClose2D(minesCreatedSoFar, pt, minDistSqr) && maxRetry > 0);
|
||||
DEBUG_ASSERTCRASH(maxRetry>0,("ran out of retries %f",minDistSqr));
|
||||
|
||||
if (getObject()->getGeometryInfo().isPointInFootprint(*target, pt))
|
||||
continue;
|
||||
|
||||
Object* mine = placeMineAt(pt, mineTemplate, team, obj); // can return null.
|
||||
if (mine)
|
||||
minesCreatedSoFar.push_back(mine);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::placeMines()
|
||||
{
|
||||
if (m_generated)
|
||||
return;
|
||||
|
||||
m_generated = true;
|
||||
|
||||
const Object* obj = getObject();
|
||||
const GenerateMinefieldBehaviorModuleData* d = getGenerateMinefieldBehaviorModuleData();
|
||||
const ThingTemplate* mineTemplate = TheThingFactory->findTemplate(d->m_mineName);
|
||||
if (!mineTemplate)
|
||||
{
|
||||
DEBUG_CRASH(("mine %s not found\n",d->m_mineName.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
const Coord3D* target = getMinefieldTarget();
|
||||
if (d->m_smartBorder)
|
||||
{
|
||||
GeometryInfo geom = obj->getGeometryInfo();
|
||||
|
||||
if (!d->m_smartBorderSkipInterior)
|
||||
{
|
||||
geom = mineTemplate->getTemplateGeometryInfo();
|
||||
placeMineAt(*target, mineTemplate, obj->getControllingPlayer()->getDefaultTeam(), obj);
|
||||
}
|
||||
|
||||
if (d->m_alwaysCircular)
|
||||
geom.set(GEOMETRY_CYLINDER, false, 1, geom.getBoundingCircleRadius(), geom.getBoundingCircleRadius());
|
||||
|
||||
Real mineRadius = mineTemplate->getTemplateGeometryInfo().getBoundingCircleRadius();
|
||||
Real mineDiameter = mineRadius * 2.0f;
|
||||
geom.expandFootprint(mineRadius);
|
||||
do
|
||||
{
|
||||
if (geom.getGeomType() == GEOMETRY_BOX && !d->m_alwaysCircular)
|
||||
{
|
||||
placeMinesAroundRect(*target, geom.getMajorRadius(), geom.getMinorRadius(), mineTemplate);
|
||||
}
|
||||
else
|
||||
{
|
||||
placeMinesAroundCircle(*target, geom.getMajorRadius(), mineTemplate);
|
||||
}
|
||||
geom.expandFootprint(mineDiameter);
|
||||
} while (geom.getBoundingCircleRadius() < d->m_distanceAroundObject);
|
||||
}
|
||||
else if (d->m_borderOnly)
|
||||
{
|
||||
GeometryInfo geom = obj->getGeometryInfo();
|
||||
geom.expandFootprint(d->m_distanceAroundObject);
|
||||
|
||||
if (geom.getGeomType() == GEOMETRY_BOX && !d->m_alwaysCircular)
|
||||
{
|
||||
placeMinesAroundRect(*target, geom.getMajorRadius(), geom.getMinorRadius(), mineTemplate);
|
||||
}
|
||||
else
|
||||
{
|
||||
placeMinesAroundCircle(*target, geom.getMajorRadius(), mineTemplate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GeometryInfo geom = obj->getGeometryInfo();
|
||||
geom.expandFootprint(d->m_distanceAroundObject);
|
||||
|
||||
if (d->m_alwaysCircular)
|
||||
geom.set(GEOMETRY_CYLINDER, false, 1, geom.getBoundingCircleRadius(), geom.getBoundingCircleRadius());
|
||||
|
||||
placeMinesInFootprint(geom, mineTemplate);
|
||||
}
|
||||
|
||||
FXList::doFXObj(d->m_genFX, obj);
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
// extend base class
|
||||
UpgradeMux::upgradeMuxCRC( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
// mux "base class"
|
||||
UpgradeMux::upgradeMuxXfer( xfer );
|
||||
|
||||
// generated
|
||||
xfer->xferBool( &m_generated );
|
||||
xfer->xferBool( &m_hasTarget );
|
||||
xfer->xferCoord3D( &m_target );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GenerateMinefieldBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
// extend base class
|
||||
UpgradeMux::upgradeMuxLoadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: InstantDeathBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#define DEFINE_SLOWDEATHPHASE_NAMES
|
||||
|
||||
#include "Common/Thing.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/GameLOD.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/InstantDeathBehavior.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
InstantDeathBehaviorModuleData::InstantDeathBehaviorModuleData()
|
||||
{
|
||||
// redundant.
|
||||
//m_fx.clear();
|
||||
//m_ocls.clear();
|
||||
//m_weapons.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseFX( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
InstantDeathBehaviorModuleData* self = (InstantDeathBehaviorModuleData*)instance;
|
||||
for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
|
||||
{
|
||||
const FXList *fxl = TheFXListStore->findFXList((token)); // could be null! this is OK!
|
||||
self->m_fx.push_back(fxl);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseOCL( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
InstantDeathBehaviorModuleData* self = (InstantDeathBehaviorModuleData*)instance;
|
||||
for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
|
||||
{
|
||||
const ObjectCreationList *ocl = TheObjectCreationListStore->findObjectCreationList(token); // could be null! this is OK!
|
||||
self->m_ocls.push_back(ocl);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseWeapon( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
InstantDeathBehaviorModuleData* self = (InstantDeathBehaviorModuleData*)instance;
|
||||
for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
|
||||
{
|
||||
const WeaponTemplate *wt = TheWeaponStore->findWeaponTemplate(token); // could be null! this is OK!
|
||||
self->m_weapons.push_back(wt);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void InstantDeathBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
DieModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "FX", parseFX, NULL, 0 },
|
||||
{ "OCL", parseOCL, NULL, 0 },
|
||||
{ "Weapon", parseWeapon, NULL, 0 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
InstantDeathBehavior::InstantDeathBehavior( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
InstantDeathBehavior::~InstantDeathBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void InstantDeathBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
|
||||
AIUpdateInterface* ai = getObject()->getAIUpdateInterface();
|
||||
if (ai)
|
||||
{
|
||||
// has another AI already handled us. (hopefully another InstantDeathBehavior)
|
||||
if (ai->isAiInDeadState())
|
||||
return;
|
||||
ai->markAsDead();
|
||||
}
|
||||
|
||||
const InstantDeathBehaviorModuleData* d = getInstantDeathBehaviorModuleData();
|
||||
|
||||
Int idx, listSize;
|
||||
|
||||
listSize = d->m_fx.size();
|
||||
if (listSize > 0)
|
||||
{
|
||||
idx = GameLogicRandomValue(0, listSize-1);
|
||||
const FXListVec& v = d->m_fx;
|
||||
DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
|
||||
const FXList* fxl = v[idx];
|
||||
FXList::doFXObj(fxl, getObject(), NULL);
|
||||
}
|
||||
|
||||
listSize = d->m_ocls.size();
|
||||
if (listSize > 0)
|
||||
{
|
||||
idx = GameLogicRandomValue(0, listSize-1);
|
||||
const OCLVec& v = d->m_ocls;
|
||||
DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
|
||||
const ObjectCreationList* ocl = v[idx];
|
||||
ObjectCreationList::create(ocl, getObject(), NULL);
|
||||
}
|
||||
|
||||
listSize = d->m_weapons.size();
|
||||
if (listSize > 0)
|
||||
{
|
||||
idx = GameLogicRandomValue(0, listSize-1);
|
||||
const WeaponTemplateVec& v = d->m_weapons;
|
||||
DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
|
||||
const WeaponTemplate* wt = v[idx];
|
||||
if (wt)
|
||||
{
|
||||
TheWeaponStore->createAndFireTempWeapon(wt, getObject(), getObject()->getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
TheGameLogic->destroyObject(getObject());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InstantDeathBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InstantDeathBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InstantDeathBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: JetSlowDeathBehavior.cpp /////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day
|
||||
// Desc: Death sequence for jets
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Locomotor.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/JetSlowDeathBehavior.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
JetSlowDeathBehaviorModuleData::JetSlowDeathBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
m_fxOnGroundDeath = NULL;
|
||||
m_oclOnGroundDeath = NULL;
|
||||
|
||||
m_fxInitialDeath = NULL;
|
||||
m_oclInitialDeath = NULL;
|
||||
|
||||
m_delaySecondaryFromInitialDeath = 0;
|
||||
m_fxSecondary = NULL;
|
||||
m_oclSecondary = NULL;
|
||||
|
||||
m_fxHitGround = NULL;
|
||||
m_oclHitGround = NULL;
|
||||
|
||||
m_delayFinalBlowUpFromHitGround = 0;
|
||||
m_fxFinalBlowUp = NULL;
|
||||
m_oclFinalBlowUp = NULL;
|
||||
|
||||
m_rollRate = 0.0f;
|
||||
m_rollRateDelta = 1.0f;
|
||||
m_pitchRate = 0.0f;
|
||||
m_fallHowFast = 0.0f;
|
||||
|
||||
} // end JetSlowDeathBehaviorModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void JetSlowDeathBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
SlowDeathBehaviorModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
|
||||
{ "FXOnGroundDeath", INI::parseFXList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_fxOnGroundDeath ) },
|
||||
{ "OCLOnGroundDeath", INI::parseObjectCreationList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_oclOnGroundDeath ) },
|
||||
|
||||
{ "FXInitialDeath", INI::parseFXList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_fxInitialDeath ) },
|
||||
{ "OCLInitialDeath", INI::parseObjectCreationList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_oclInitialDeath ) },
|
||||
|
||||
{ "DelaySecondaryFromInitialDeath", INI::parseDurationUnsignedInt, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_delaySecondaryFromInitialDeath ) },
|
||||
{ "FXSecondary", INI::parseFXList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_fxSecondary ) },
|
||||
{ "OCLSecondary", INI::parseObjectCreationList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_oclSecondary ) },
|
||||
|
||||
{ "FXHitGround", INI::parseFXList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_fxHitGround ) },
|
||||
{ "OCLHitGround", INI::parseObjectCreationList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_oclHitGround ) },
|
||||
|
||||
{ "DelayFinalBlowUpFromHitGround", INI::parseDurationUnsignedInt, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_delayFinalBlowUpFromHitGround ) },
|
||||
{ "FXFinalBlowUp", INI::parseFXList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_fxFinalBlowUp ) },
|
||||
{ "OCLFinalBlowUp", INI::parseObjectCreationList, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_oclFinalBlowUp ) },
|
||||
|
||||
{ "DeathLoopSound", INI::parseAudioEventRTS, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_deathLoopSound ) },
|
||||
|
||||
// @todo srj -- RollRate and RollRateDelta and PitchRate should use parseAngularVelocityReal
|
||||
{ "RollRate", INI::parseReal, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_rollRate ) },
|
||||
{ "RollRateDelta", INI::parsePercentToReal, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_rollRateDelta ) },
|
||||
{ "PitchRate", INI::parseReal, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_pitchRate ) },
|
||||
{ "FallHowFast", INI::parsePercentToReal, NULL, offsetof( JetSlowDeathBehaviorModuleData, m_fallHowFast ) },
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
JetSlowDeathBehavior::JetSlowDeathBehavior( Thing *thing, const ModuleData *moduleData )
|
||||
: SlowDeathBehavior( thing, moduleData )
|
||||
{
|
||||
|
||||
m_timerDeathFrame = 0;
|
||||
m_timerOnGroundFrame = 0;
|
||||
m_rollRate = 0.0f;
|
||||
|
||||
} // end JetSlowDeathBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
JetSlowDeathBehavior::~JetSlowDeathBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~JetSlowDeathBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void JetSlowDeathBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
Object *us = getObject();
|
||||
|
||||
// if the jet is on the ground we do just our ground fx death
|
||||
if( us->isSignificantlyAboveTerrain() == FALSE )
|
||||
{
|
||||
const JetSlowDeathBehaviorModuleData *modData = getJetSlowDeathBehaviorModuleData();
|
||||
|
||||
// execute fx
|
||||
FXList::doFXObj( modData->m_fxOnGroundDeath, us );
|
||||
|
||||
// execute ocl
|
||||
ObjectCreationList::create( modData->m_oclOnGroundDeath, us, NULL );
|
||||
|
||||
// destroy object
|
||||
TheGameLogic->destroyObject( us );
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// extend base class for slow death and begin the slow death behavior
|
||||
SlowDeathBehavior::onDie( damageInfo );
|
||||
|
||||
} // end else
|
||||
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void JetSlowDeathBehavior::beginSlowDeath( const DamageInfo *damageInfo )
|
||||
{
|
||||
|
||||
// extend functionality
|
||||
SlowDeathBehavior::beginSlowDeath( damageInfo );
|
||||
|
||||
// get our info
|
||||
Object *us = getObject();
|
||||
const JetSlowDeathBehaviorModuleData *modData = getJetSlowDeathBehaviorModuleData();
|
||||
|
||||
// record the frame we died on
|
||||
m_timerDeathFrame = TheGameLogic->getFrame();
|
||||
|
||||
// do some effects
|
||||
FXList::doFXObj( modData->m_fxInitialDeath, us );
|
||||
ObjectCreationList::create( modData->m_oclInitialDeath, us, NULL );
|
||||
|
||||
// start audio loop playing
|
||||
m_deathLoopSound = modData->m_deathLoopSound;
|
||||
if( m_deathLoopSound.getEventName().isEmpty() == FALSE )
|
||||
{
|
||||
|
||||
m_deathLoopSound.setObjectID( us->getID() );
|
||||
m_deathLoopSound.setPlayingHandle( TheAudio->addAudioEvent( &m_deathLoopSound ) );
|
||||
|
||||
} // end if
|
||||
|
||||
// initialize our roll rate to that defined as the initial value in the module data
|
||||
m_rollRate = modData->m_rollRate;
|
||||
|
||||
// set the locomotor so that the plane starts falling
|
||||
Locomotor *locomotor = us->getAIUpdateInterface()->getCurLocomotor();
|
||||
locomotor->setMaxLift( -TheGlobalData->m_gravity * (1.0f - modData->m_fallHowFast) );
|
||||
|
||||
// do not allow the jet to turn anymore
|
||||
locomotor->setMaxTurnRate( 0.0f );
|
||||
|
||||
} // end beginSlowDeath
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime JetSlowDeathBehavior::update( void )
|
||||
{
|
||||
|
||||
// extend functionality of base class
|
||||
SlowDeathBehavior::update();
|
||||
|
||||
// if the death is not activated, do nothing else
|
||||
if( isSlowDeathActivated() == FALSE )
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
// get object info
|
||||
Object *us = getObject();
|
||||
const JetSlowDeathBehaviorModuleData *modData = getJetSlowDeathBehaviorModuleData();
|
||||
|
||||
// roll us around in the air
|
||||
PhysicsBehavior *physics = us->getPhysics();
|
||||
DEBUG_ASSERTCRASH( physics, ("JetSlowDeathBehavior::beginSlowDeath - '%s' has no physics\n",
|
||||
us->getTemplate()->getName().str()) );
|
||||
if( physics )
|
||||
physics->setRollRate( m_rollRate );
|
||||
|
||||
// adjust the roll rate over time
|
||||
m_rollRate *= modData->m_rollRateDelta;
|
||||
|
||||
// do effects for death while in the air
|
||||
if( m_timerOnGroundFrame == 0 )
|
||||
{
|
||||
|
||||
PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(us->getPosition());
|
||||
us->setLayer(layer);
|
||||
Real height;
|
||||
if (layer == LAYER_GROUND)
|
||||
{
|
||||
// (this is more efficient than getGroundHeight because the info is cached)
|
||||
height = us->getHeightAboveTerrain();
|
||||
}
|
||||
else
|
||||
{
|
||||
Real layerHeight = TheTerrainLogic->getLayerHeight( us->getPosition()->x, us->getPosition()->y, layer );
|
||||
height = us->getPosition()->z - layerHeight;
|
||||
// slop a little bit for bridges, since we tend to end up fractionally
|
||||
// above 'em, and it's easier to just slop it here
|
||||
if (height >= 0.0f && height <= 1.0f)
|
||||
height = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Bool hitATree = FALSE;
|
||||
// Here we want to make sure we crash if we collide with a tree on the way down
|
||||
PhysicsBehavior *phys = us->getPhysics();
|
||||
if ( m_timerOnGroundFrame == 0 && phys )
|
||||
{
|
||||
ObjectID treeID = phys->getLastCollidee();
|
||||
Object *tree = TheGameLogic->findObjectByID( treeID );
|
||||
if ( tree )
|
||||
{
|
||||
if (tree->isKindOf( KINDOF_SHRUBBERY ) )
|
||||
hitATree = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// when we've hit the ground, we're totally done
|
||||
if( height <= 0.0f || hitATree )
|
||||
{
|
||||
|
||||
// stop the death looping sound at the right time
|
||||
TheAudio->removeAudioEvent( m_deathLoopSound.getPlayingHandle() );
|
||||
|
||||
// do some effects
|
||||
FXList::doFXObj( modData->m_fxHitGround, us );
|
||||
ObjectCreationList::create( modData->m_oclHitGround, us, NULL );
|
||||
|
||||
// we are now on the ground
|
||||
m_timerOnGroundFrame = TheGameLogic->getFrame();
|
||||
|
||||
// start us rolling on another axis too
|
||||
if( physics )
|
||||
physics->setPitchRate( modData->m_pitchRate );
|
||||
|
||||
} // end if
|
||||
|
||||
// timers for the secondary effect
|
||||
if( m_timerDeathFrame != 0 &&
|
||||
TheGameLogic->getFrame() - m_timerDeathFrame >= modData->m_delaySecondaryFromInitialDeath )
|
||||
{
|
||||
|
||||
// do some effects
|
||||
FXList::doFXObj( modData->m_fxSecondary, us );
|
||||
ObjectCreationList::create( modData->m_oclSecondary, us, NULL );
|
||||
|
||||
// clear the death frame timer since we've already executed the event now
|
||||
m_timerDeathFrame = 0;
|
||||
|
||||
} //end if
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
// we are on the ground, pay attention to the final explosion timers
|
||||
if( TheGameLogic->getFrame() - m_timerOnGroundFrame >= modData->m_delayFinalBlowUpFromHitGround )
|
||||
{
|
||||
|
||||
// do some effects
|
||||
FXList::doFXObj( modData->m_fxFinalBlowUp, us );
|
||||
ObjectCreationList::create( modData->m_oclFinalBlowUp, us, NULL );
|
||||
|
||||
// we're all done now
|
||||
TheGameLogic->destroyObject( us );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end else
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void JetSlowDeathBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SlowDeathBehavior::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void JetSlowDeathBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SlowDeathBehavior::xfer( xfer );
|
||||
|
||||
// timer death frame
|
||||
xfer->xferUnsignedInt( &m_timerDeathFrame );
|
||||
|
||||
// on ground frame
|
||||
xfer->xferUnsignedInt( &m_timerOnGroundFrame );
|
||||
|
||||
// roll rate
|
||||
xfer->xferReal( &m_rollRate );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void JetSlowDeathBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SlowDeathBehavior::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MinefieldBehavior.cpp //////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, June 2002
|
||||
// Desc: Minefield behavior
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#define DEFINE_RELATIONSHIP_NAMES
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Module/MinefieldBehavior.h"
|
||||
#include "GameLogic/Module/AutoHealBehavior.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// detonation never puts our health below this, since we probably auto-regen
|
||||
const Real MIN_HEALTH = 0.1f;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
MinefieldBehaviorModuleData::MinefieldBehaviorModuleData()
|
||||
{
|
||||
m_detonationWeapon = NULL;
|
||||
m_detonatedBy = (1 << ENEMIES) | (1 << NEUTRAL);
|
||||
m_stopsRegenAfterCreatorDies = true;
|
||||
m_regenerates = false;
|
||||
m_workersDetonate = false;
|
||||
m_creatorDeathCheckRate = LOGICFRAMES_PER_SECOND;
|
||||
m_scootFromStartingPointTime = 0;
|
||||
m_repeatDetonateMoveThresh = 1.0f;
|
||||
m_numVirtualMines = 1;
|
||||
m_healthPercentToDrainPerSecond = 0.0f;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void MinefieldBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
|
||||
UpdateModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "DetonationWeapon", INI::parseWeaponTemplate, NULL, offsetof( MinefieldBehaviorModuleData, m_detonationWeapon ) },
|
||||
{ "DetonatedBy", INI::parseBitString32, TheRelationshipNames, offsetof( MinefieldBehaviorModuleData, m_detonatedBy ) },
|
||||
{ "StopsRegenAfterCreatorDies", INI::parseBool, NULL, offsetof( MinefieldBehaviorModuleData, m_stopsRegenAfterCreatorDies ) },
|
||||
{ "Regenerates", INI::parseBool, NULL, offsetof( MinefieldBehaviorModuleData, m_regenerates ) },
|
||||
{ "WorkersDetonate", INI::parseBool, NULL, offsetof( MinefieldBehaviorModuleData, m_workersDetonate ) },
|
||||
{ "CreatorDeathCheckRate", INI::parseDurationUnsignedInt, NULL, offsetof( MinefieldBehaviorModuleData, m_creatorDeathCheckRate ) },
|
||||
{ "ScootFromStartingPointTime", INI::parseDurationUnsignedInt, NULL, offsetof( MinefieldBehaviorModuleData, m_scootFromStartingPointTime ) },
|
||||
{ "NumVirtualMines", INI::parseUnsignedInt, NULL, offsetof( MinefieldBehaviorModuleData, m_numVirtualMines ) },
|
||||
{ "RepeatDetonateMoveThresh", INI::parseReal, NULL, offsetof( MinefieldBehaviorModuleData, m_repeatDetonateMoveThresh ) },
|
||||
{ "DegenPercentPerSecondAfterCreatorDies", INI::parsePercentToReal, NULL, offsetof( MinefieldBehaviorModuleData, m_healthPercentToDrainPerSecond ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MinefieldBehavior::MinefieldBehavior( Thing *thing, const ModuleData* moduleData )
|
||||
: UpdateModule( thing, moduleData )
|
||||
{
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
m_nextDeathCheckFrame = 0;
|
||||
m_scootFramesLeft = 0;
|
||||
m_scootVel.zero();
|
||||
m_scootAccel.zero();
|
||||
m_detonators.clear();
|
||||
m_ignoreDamage = false;
|
||||
m_regenerates = d->m_regenerates;
|
||||
m_draining = false;
|
||||
m_virtualMinesRemaining = d->m_numVirtualMines;
|
||||
for (Int i = 0; i < MAX_IMMUNITY; ++i)
|
||||
{
|
||||
m_immunes[i].id = INVALID_ID;
|
||||
m_immunes[i].collideTime = 0;
|
||||
}
|
||||
|
||||
// start off awake, and we will calcSleepTime from here on
|
||||
setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
|
||||
|
||||
// mines aren't auto-acquirable
|
||||
getObject()->setStatus(OBJECT_STATUS_NO_ATTACK_FROM_AI);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MinefieldBehavior::~MinefieldBehavior()
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime MinefieldBehavior::calcSleepTime()
|
||||
{
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
|
||||
// if we're draining we have to update every frame
|
||||
if (m_draining)
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
// if we're scooting we need to update every frame
|
||||
if( m_scootFramesLeft > 0 )
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
// if there is anybody in our immulity monitoring we need to update every frame
|
||||
for( Int i = 0; i < MAX_IMMUNITY; ++i )
|
||||
if( m_immunes[ i ].id != INVALID_ID )
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
UnsignedInt sleepTime = FOREVER;
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
//
|
||||
// sleep until the next death check frame we already have figured outif we care
|
||||
// about it (that is, when our creator dies)
|
||||
//
|
||||
if (m_regenerates && d->m_stopsRegenAfterCreatorDies)
|
||||
sleepTime = min( sleepTime, m_nextDeathCheckFrame - now );
|
||||
|
||||
// if we don't want to sleep forever, prevent 0 frame sleeps
|
||||
if( sleepTime == 0 )
|
||||
sleepTime = 1;
|
||||
|
||||
// sleep forever
|
||||
return UPDATE_SLEEP( sleepTime );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime MinefieldBehavior::update()
|
||||
{
|
||||
Object* obj = getObject();
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
if (m_scootFramesLeft > 0)
|
||||
{
|
||||
Coord3D pt = *obj->getPosition();
|
||||
|
||||
m_scootVel.x += m_scootAccel.x;
|
||||
m_scootVel.y += m_scootAccel.y;
|
||||
m_scootVel.z += m_scootAccel.z;
|
||||
|
||||
pt.x += m_scootVel.x;
|
||||
pt.y += m_scootVel.y;
|
||||
pt.z += m_scootVel.z;
|
||||
|
||||
// srj sez: scooting mines always go on the highest layer.
|
||||
Coord3D tmp = pt;
|
||||
tmp.z = 99999.0f;
|
||||
PathfindLayerEnum newLayer = TheTerrainLogic->getHighestLayerForDestination(&tmp);
|
||||
obj->setLayer(newLayer);
|
||||
|
||||
Real ground = TheTerrainLogic->getLayerHeight( pt.x, pt.y, newLayer );
|
||||
|
||||
if (newLayer != LAYER_GROUND)
|
||||
{
|
||||
// ensure we are slightly above the bridge, to account for fudge & sloppy art
|
||||
const Real FUDGE = 1.0f;
|
||||
ground += FUDGE;
|
||||
}
|
||||
|
||||
if (pt.z < ground || m_scootFramesLeft <= 1)
|
||||
pt.z = ground;
|
||||
|
||||
obj->setPosition(&pt);
|
||||
|
||||
--m_scootFramesLeft;
|
||||
}
|
||||
|
||||
// check for expired immunities.
|
||||
for (Int i = 0; i < MAX_IMMUNITY; ++i)
|
||||
{
|
||||
if (m_immunes[i].id == INVALID_ID)
|
||||
continue;
|
||||
|
||||
if (TheGameLogic->findObjectByID(m_immunes[i].id) == NULL ||
|
||||
now > m_immunes[i].collideTime + 2)
|
||||
{
|
||||
//DEBUG_LOG(("expiring an immunity %d\n",m_immunes[i].id));
|
||||
m_immunes[i].id = INVALID_ID; // he's dead, jim.
|
||||
m_immunes[i].collideTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (now >= m_nextDeathCheckFrame)
|
||||
{
|
||||
|
||||
// check to see if there is an enemy building on me... since enemy buildings can be build on top of me
|
||||
|
||||
// check to see if the building that made me is gone, and whether therefore, I should go
|
||||
if (m_regenerates && d->m_stopsRegenAfterCreatorDies)
|
||||
{
|
||||
m_nextDeathCheckFrame = now + d->m_creatorDeathCheckRate;
|
||||
ObjectID producerID = getObject()->getProducerID();
|
||||
if (producerID != INVALID_ID)
|
||||
{
|
||||
Object* producer = TheGameLogic->findObjectByID(producerID);
|
||||
if (producer == NULL || producer->isEffectivelyDead())
|
||||
{
|
||||
m_regenerates = false;
|
||||
m_draining = true;
|
||||
static const NameKeyType key_AutoHealBehavior = NAMEKEY("AutoHealBehavior");
|
||||
AutoHealBehavior* ahb = (AutoHealBehavior*)obj->findUpdateModule( key_AutoHealBehavior );
|
||||
if (ahb)
|
||||
ahb->stopHealing();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_draining)
|
||||
{
|
||||
DamageInfo damageInfo;
|
||||
damageInfo.in.m_amount = (obj->getBodyModule()->getMaxHealth() * d->m_healthPercentToDrainPerSecond) / LOGICFRAMES_PER_SECOND;
|
||||
damageInfo.in.m_sourceID = obj->getID();
|
||||
damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
|
||||
damageInfo.in.m_deathType = DEATH_NORMAL;
|
||||
obj->attemptDamage( &damageInfo );
|
||||
}
|
||||
|
||||
return calcSleepTime();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::detonateOnce(const Coord3D& position)
|
||||
{
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
if (d->m_detonationWeapon)
|
||||
{
|
||||
Object* obj = getObject();
|
||||
TheWeaponStore->createAndFireTempWeapon(d->m_detonationWeapon, obj, &position);
|
||||
}
|
||||
|
||||
if (m_virtualMinesRemaining > 0)
|
||||
--m_virtualMinesRemaining;
|
||||
|
||||
if (!m_regenerates && m_virtualMinesRemaining == 0)
|
||||
{
|
||||
TheGameLogic->destroyObject(getObject());
|
||||
}
|
||||
else
|
||||
{
|
||||
Real percent = (Real)m_virtualMinesRemaining / (Real)d->m_numVirtualMines;
|
||||
BodyModuleInterface* body = getObject()->getBodyModule();
|
||||
Real health = body->getHealth();
|
||||
Real desired = percent * body->getMaxHealth();
|
||||
if (desired < MIN_HEALTH)
|
||||
desired = MIN_HEALTH;
|
||||
Real amount = health - desired;
|
||||
|
||||
if (amount > 0.0f)
|
||||
{
|
||||
m_ignoreDamage = true;
|
||||
|
||||
//body->internalChangeHealth(desired - health);
|
||||
//can't use this, AutoHeal won't work unless we go thru normal damage stuff
|
||||
|
||||
DamageInfo extraDamageInfo;
|
||||
extraDamageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
|
||||
extraDamageInfo.in.m_deathType = DEATH_NONE;
|
||||
extraDamageInfo.in.m_sourceID = getObject()->getID();
|
||||
extraDamageInfo.in.m_amount = amount;
|
||||
getObject()->attemptDamage(&extraDamageInfo);
|
||||
|
||||
m_ignoreDamage = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_virtualMinesRemaining == 0)
|
||||
{
|
||||
getObject()->setModelConditionState(MODELCONDITION_RUBBLE);
|
||||
getObject()->setStatus(OBJECT_STATUS_MASKED);
|
||||
}
|
||||
else
|
||||
{
|
||||
getObject()->clearModelConditionState(MODELCONDITION_RUBBLE);
|
||||
getObject()->clearStatus(OBJECT_STATUS_MASKED);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Real calcDistSquared(const Coord3D& a, const Coord3D& b)
|
||||
{
|
||||
return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
|
||||
{
|
||||
if (other == NULL || other->isEffectivelyDead())
|
||||
return;
|
||||
|
||||
if (m_virtualMinesRemaining == 0)
|
||||
return;
|
||||
|
||||
Object* obj = getObject();
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
// is this guy in our immune list?
|
||||
// NOTE NOTE NOTE, must always do this check FIRST so that 'collideTime' is updated...
|
||||
for (Int i = 0; i < MAX_IMMUNITY; ++i)
|
||||
{
|
||||
if (m_immunes[i].id == other->getID())
|
||||
{
|
||||
//DEBUG_LOG(("ignoring due to immunity %d\n",m_immunes[i].id));
|
||||
m_immunes[i].collideTime = now;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!d->m_workersDetonate)
|
||||
{
|
||||
// infantry+dozer=worker.
|
||||
if (other->isKindOf(KINDOF_INFANTRY) && other->isKindOf(KINDOF_DOZER))
|
||||
return;
|
||||
}
|
||||
|
||||
Int requiredMask = 0;
|
||||
Relationship r = obj->getRelationship(other);
|
||||
if (r == ALLIES) requiredMask = (1 << ALLIES);
|
||||
else if (r == ENEMIES) requiredMask = (1 << ENEMIES);
|
||||
else if (r == NEUTRAL) requiredMask = (1 << NEUTRAL);
|
||||
if ((d->m_detonatedBy & requiredMask) == 0)
|
||||
return;
|
||||
|
||||
// are we active?
|
||||
if (m_scootFramesLeft > 0)
|
||||
return;
|
||||
|
||||
// things that are in the process of clearing mines are immune to mine detonation,
|
||||
// even if we aren't the specific mine they are trying to clear. (however, they must
|
||||
// have a real mine they area trying to clear... it's possible they could be trying to
|
||||
// clear a position where there is no mine, in which case we grant them no immunity, muwahahaha)
|
||||
AIUpdateInterface* otherAI = other->getAI();
|
||||
if (otherAI && otherAI->isClearingMines() && otherAI->getGoalObject() != NULL)
|
||||
{
|
||||
// mine-clearers are granted immunity to us for as long as they continuously
|
||||
// collide, even if no longer clearing mines. (this prevents the problem
|
||||
// of a guy who touches two close-together mines while clearing, then puts up his
|
||||
// detector and is blown to smithereens by the other one.)
|
||||
for (Int i = 0; i < MAX_IMMUNITY; ++i)
|
||||
{
|
||||
if (m_immunes[i].id == INVALID_ID || m_immunes[i].id == other->getID())
|
||||
{
|
||||
//DEBUG_LOG(("add/update immunity %d\n",m_immunes[i].id));
|
||||
m_immunes[i].id = other->getID();
|
||||
m_immunes[i].collideTime = now;
|
||||
|
||||
// wake up
|
||||
setWakeFrame( obj, calcSleepTime() );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if we detonated another one nearby, we have to move a little bit to detonate another one.
|
||||
Bool found = false;
|
||||
for (std::vector<DetonatorInfo>::iterator it = m_detonators.begin(); it != m_detonators.end(); ++it)
|
||||
{
|
||||
if (other->getID() == it->id)
|
||||
{
|
||||
found = TRUE;
|
||||
Real distSqr = calcDistSquared(*other->getPosition(), it->where);
|
||||
if (distSqr <= sqr(d->m_repeatDetonateMoveThresh))
|
||||
{
|
||||
// too close. punt for now.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// far enough. update the loc, then break out and blow up.
|
||||
it->where = *other->getPosition();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
// add him to the list.
|
||||
DetonatorInfo detInfo;
|
||||
detInfo.id = other->getID();
|
||||
detInfo.where = *other->getPosition();
|
||||
m_detonators.push_back(detInfo);
|
||||
}
|
||||
|
||||
Coord3D detPt = *other->getPosition();
|
||||
obj->getGeometryInfo().clipPointToFootprint(*obj->getPosition(), detPt);
|
||||
detonateOnce(detPt);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
if (m_ignoreDamage)
|
||||
return;
|
||||
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
|
||||
// detonate as many times as neccessary for our virtual mine count to match our health
|
||||
BodyModuleInterface* body = getObject()->getBodyModule();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Real virtualMinesExpectedF = ((Real)d->m_numVirtualMines * body->getHealth() / body->getMaxHealth());
|
||||
Int virtualMinesExpected =
|
||||
damageInfo->in.m_damageType == DAMAGE_HEALING ?
|
||||
REAL_TO_INT_FLOOR(virtualMinesExpectedF) :
|
||||
REAL_TO_INT_CEIL(virtualMinesExpectedF);
|
||||
if (virtualMinesExpected > d->m_numVirtualMines)
|
||||
virtualMinesExpected = d->m_numVirtualMines;
|
||||
if (m_virtualMinesRemaining < virtualMinesExpected)
|
||||
{
|
||||
m_virtualMinesRemaining = virtualMinesExpected;
|
||||
}
|
||||
else if (m_virtualMinesRemaining > virtualMinesExpected)
|
||||
{
|
||||
if (m_draining &&
|
||||
damageInfo->in.m_sourceID == getObject()->getID() &&
|
||||
damageInfo->in.m_damageType == DAMAGE_UNRESISTABLE)
|
||||
{
|
||||
// don't detonate.... just ditch a mine
|
||||
--m_virtualMinesRemaining;
|
||||
}
|
||||
else
|
||||
{
|
||||
detonateOnce(*getObject()->getPosition());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_virtualMinesRemaining == 0)
|
||||
{
|
||||
// oops, if someone did weapon damage they may have nuked our health to zero,
|
||||
// which would be bad if we regen. prevent this. (srj)
|
||||
if (m_regenerates && body->getHealth() < MIN_HEALTH)
|
||||
{
|
||||
body->internalChangeHealth(MIN_HEALTH - body->getHealth());
|
||||
}
|
||||
|
||||
getObject()->setModelConditionState(MODELCONDITION_RUBBLE);
|
||||
getObject()->setStatus(OBJECT_STATUS_MASKED);
|
||||
}
|
||||
else
|
||||
{
|
||||
getObject()->clearModelConditionState(MODELCONDITION_RUBBLE);
|
||||
getObject()->clearStatus(OBJECT_STATUS_MASKED);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::onHealing( DamageInfo *damageInfo )
|
||||
{
|
||||
onDamage(damageInfo);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
TheGameLogic->destroyObject(getObject());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::disarm()
|
||||
{
|
||||
if (!m_regenerates)
|
||||
{
|
||||
TheGameLogic->destroyObject(getObject());
|
||||
return;
|
||||
}
|
||||
|
||||
// detonation never puts our health below this, since we probably auto-regen
|
||||
const Real MIN_HEALTH = 0.1f;
|
||||
|
||||
BodyModuleInterface* body = getObject()->getBodyModule();
|
||||
Real desired = MIN_HEALTH;
|
||||
Real amount = body->getHealth() - desired;
|
||||
|
||||
m_ignoreDamage = true;
|
||||
|
||||
//body->internalChangeHealth(desired - health);
|
||||
//can't use this, AutoHeal won't work unless we go thru normal damage stuff
|
||||
|
||||
DamageInfo extraDamageInfo;
|
||||
extraDamageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
|
||||
extraDamageInfo.in.m_deathType = DEATH_NONE;
|
||||
extraDamageInfo.in.m_sourceID = getObject()->getID();
|
||||
extraDamageInfo.in.m_amount = amount;
|
||||
getObject()->attemptDamage(&extraDamageInfo);
|
||||
|
||||
m_ignoreDamage = false;
|
||||
|
||||
m_virtualMinesRemaining = 0;
|
||||
getObject()->setModelConditionState(MODELCONDITION_RUBBLE);
|
||||
getObject()->setStatus(OBJECT_STATUS_MASKED);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::setScootParms(const Coord3D& start, const Coord3D& end)
|
||||
{
|
||||
Object* obj = getObject();
|
||||
const MinefieldBehaviorModuleData* d = getMinefieldBehaviorModuleData();
|
||||
UnsignedInt scootFromStartingPointTime = d->m_scootFromStartingPointTime;
|
||||
|
||||
Coord3D endOnGround = end;
|
||||
endOnGround.z = TheTerrainLogic->getGroundHeight( endOnGround.x, endOnGround.y );
|
||||
if (start.z > endOnGround.z)
|
||||
{
|
||||
// figure out how long it will take to fall, and replace scoot time with that
|
||||
UnsignedInt fallingTime = REAL_TO_INT_CEIL(sqrtf(2.0f * (start.z - endOnGround.z) / fabs(TheGlobalData->m_gravity)));
|
||||
// we can scoot after we land, but don't want to stop scooting before we land
|
||||
if (scootFromStartingPointTime < fallingTime)
|
||||
scootFromStartingPointTime = fallingTime;
|
||||
}
|
||||
|
||||
if (scootFromStartingPointTime == 0)
|
||||
{
|
||||
obj->setPosition(&endOnGround);
|
||||
m_scootFramesLeft = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// x = x0 + vt + 0.5at^2
|
||||
// thus 2(dx - vt)/t^2 = a
|
||||
Real dx = endOnGround.x - start.x;
|
||||
Real dy = endOnGround.y - start.y;
|
||||
Real dz = endOnGround.z - start.z;
|
||||
Real dist = sqrt(sqr(dx) + sqr(dy));
|
||||
if (dist <= 0.1f && fabs(dz) <= 0.1f)
|
||||
{
|
||||
obj->setPosition(&endOnGround);
|
||||
m_scootFramesLeft = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Real t = (Real)scootFromStartingPointTime;
|
||||
Real scootFromStartingPointSpeed = dist / t;
|
||||
Real accelMag = fabs(2.0f * (dist - scootFromStartingPointSpeed*t)/sqr(t));
|
||||
Real dxNorm = (dist <= 0.1f) ? 0.0f : (dx / dist);
|
||||
Real dyNorm = (dist <= 0.1f) ? 0.0f : (dy / dist);
|
||||
m_scootVel.x = dxNorm * scootFromStartingPointSpeed;
|
||||
m_scootVel.y = dyNorm * scootFromStartingPointSpeed;
|
||||
m_scootAccel.x = -dxNorm * accelMag;
|
||||
m_scootAccel.y = -dyNorm * accelMag;
|
||||
m_scootAccel.z = TheGlobalData->m_gravity;
|
||||
obj->setPosition(&start);
|
||||
m_scootFramesLeft = scootFromStartingPointTime;
|
||||
|
||||
// we need to wake ourselves up because we could be lying here sleeping forever
|
||||
setWakeFrame( obj, calcSleepTime() );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// mines remaining
|
||||
/// @todo srj -- ensure health, appearance, etc are correct for save/reload! post-MP!
|
||||
xfer->xferUnsignedInt( &m_virtualMinesRemaining );
|
||||
|
||||
// next death check frame
|
||||
xfer->xferUnsignedInt( &m_nextDeathCheckFrame );
|
||||
|
||||
// scoot frames left
|
||||
xfer->xferUnsignedInt( &m_scootFramesLeft );
|
||||
|
||||
// scoot velocity
|
||||
xfer->xferCoord3D( &m_scootVel );
|
||||
|
||||
// scoot acceleration
|
||||
xfer->xferCoord3D( &m_scootAccel );
|
||||
|
||||
xfer->xferBool( &m_ignoreDamage );
|
||||
xfer->xferBool( &m_regenerates );
|
||||
xfer->xferBool( &m_draining );
|
||||
|
||||
// immunities
|
||||
UnsignedByte maxImmunity = MAX_IMMUNITY;
|
||||
xfer->xferUnsignedByte( &maxImmunity );
|
||||
if( maxImmunity != MAX_IMMUNITY )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "MinefieldBehavior::xfer - MAX_IMMUNITY has changed size, you must version this code and then you can remove this error message\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
for( UnsignedByte i = 0; i < maxImmunity; ++i )
|
||||
{
|
||||
|
||||
// object id
|
||||
xfer->xferObjectID( &m_immunes[ i ].id );
|
||||
|
||||
// collide time
|
||||
xfer->xferUnsignedInt( &m_immunes[ i ].collideTime );
|
||||
|
||||
} // end for, i
|
||||
|
||||
if( xfer->getXferMode() == XFER_LOAD )
|
||||
m_detonators.clear();
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MinefieldBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: OverchargeBehavior.cpp ///////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, June 2002
|
||||
// Desc: Objects with this behavior module will get the ability to produce more power
|
||||
// for a short amount of time, during this "overcharge" state object health is
|
||||
// slowly reduced
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Radar.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/OverchargeBehavior.h"
|
||||
#include "GameLogic/Module/PowerPlantUpdate.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
OverchargeBehaviorModuleData::OverchargeBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
m_healthPercentToDrainPerSecond = 0.0f;
|
||||
m_notAllowedWhenHealthBelowPercent = 0.0f;
|
||||
|
||||
} // end OverchargeBehaviorModuleData
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void OverchargeBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
|
||||
UpdateModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "HealthPercentToDrainPerSecond", INI::parsePercentToReal, NULL, offsetof( OverchargeBehaviorModuleData, m_healthPercentToDrainPerSecond ) },
|
||||
{ "NotAllowedWhenHealthBelowPercent", INI::parsePercentToReal, NULL, offsetof( OverchargeBehaviorModuleData, m_notAllowedWhenHealthBelowPercent ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
OverchargeBehavior::OverchargeBehavior( Thing *thing, const ModuleData* moduleData )
|
||||
: UpdateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
m_overchargeActive = FALSE;
|
||||
|
||||
// start off sleeping forever until we become active
|
||||
setWakeFrame( getObject(), UPDATE_SLEEP_FOREVER );
|
||||
|
||||
} // end OverchargeBehavior
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
OverchargeBehavior::~OverchargeBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~OverchargeBehavior
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime OverchargeBehavior::update( void )
|
||||
{
|
||||
|
||||
// if the overcharge is active we need to take away some life
|
||||
if( m_overchargeActive )
|
||||
{
|
||||
Object *us = getObject();
|
||||
|
||||
// get mod data
|
||||
const OverchargeBehaviorModuleData *modData = getOverchargeBehaviorModuleData();
|
||||
|
||||
// do some damage
|
||||
BodyModuleInterface *body = us->getBodyModule();
|
||||
DamageInfo damageInfo;
|
||||
damageInfo.in.m_amount = (body->getMaxHealth() * modData->m_healthPercentToDrainPerSecond) / LOGICFRAMES_PER_SECOND;
|
||||
damageInfo.in.m_sourceID = us->getID();
|
||||
damageInfo.in.m_damageType = DAMAGE_PENALTY;
|
||||
damageInfo.in.m_deathType = DEATH_NORMAL;
|
||||
us->attemptDamage( &damageInfo );
|
||||
|
||||
// see if our health is below the allowable threshold
|
||||
if( body->getHealth() < body->getMaxHealth() * modData->m_notAllowedWhenHealthBelowPercent )
|
||||
{
|
||||
|
||||
// turn off the overcharge
|
||||
enable( FALSE );
|
||||
|
||||
// do some UI info for the local user if this is theirs
|
||||
if( ThePlayerList->getLocalPlayer() == us->getControllingPlayer() )
|
||||
{
|
||||
|
||||
// print msg
|
||||
TheInGameUI->message( "GUI:OverchargeExhausted" );
|
||||
|
||||
// do radar event
|
||||
TheRadar->createEvent( us->getPosition(), RADAR_EVENT_INFORMATION );
|
||||
|
||||
} // end of
|
||||
|
||||
// do nothing else
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Flip the state of our 'overcharge-ness' */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::toggle( void )
|
||||
{
|
||||
|
||||
// just toggle using enable()
|
||||
enable( !m_overchargeActive );
|
||||
|
||||
} // end toggle
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Enable or disable an overcharge */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::enable( Bool enable )
|
||||
{
|
||||
Object *us = getObject();
|
||||
|
||||
if( enable == FALSE )
|
||||
{
|
||||
|
||||
// if we're turned on, turn off
|
||||
if( m_overchargeActive == TRUE )
|
||||
{
|
||||
|
||||
// make sure to NOT extend rods for purpose of maintaining proper model condition
|
||||
PowerPlantUpdateInterface *ppui;
|
||||
for( BehaviorModule **umi = getObject()->getBehaviorModules(); *umi; ++umi)
|
||||
{
|
||||
ppui = (*umi)->getPowerPlantUpdateInterface();
|
||||
if( ppui )
|
||||
ppui->extendRods(FALSE);
|
||||
}
|
||||
|
||||
Player *player = us->getControllingPlayer();
|
||||
if ( player )
|
||||
player->removePowerBonus(us);
|
||||
|
||||
// we are no longer active
|
||||
m_overchargeActive = FALSE;
|
||||
|
||||
// sleep forever
|
||||
setWakeFrame( us, UPDATE_SLEEP_FOREVER );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// if we're turned off, turn on
|
||||
if( m_overchargeActive == FALSE )
|
||||
{
|
||||
|
||||
// make sure to extend rods for purpose of maintaining proper model condition
|
||||
PowerPlantUpdateInterface *ppui;
|
||||
for( BehaviorModule **umi = getObject()->getBehaviorModules(); *umi; ++umi)
|
||||
{
|
||||
ppui = (*umi)->getPowerPlantUpdateInterface();
|
||||
if( ppui )
|
||||
ppui->extendRods(TRUE);
|
||||
}
|
||||
|
||||
// add the power bonus
|
||||
Player *player = us->getControllingPlayer();
|
||||
if ( player )
|
||||
player->addPowerBonus(us);
|
||||
|
||||
// we are now active
|
||||
m_overchargeActive = TRUE;
|
||||
|
||||
// need to update every frame now
|
||||
setWakeFrame( us, UPDATE_SLEEP_NONE );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end else
|
||||
|
||||
} // end enable
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::onDelete( void )
|
||||
{
|
||||
|
||||
// if we haven't been upgraded there is nothing to clean up
|
||||
if( m_overchargeActive == FALSE )
|
||||
return;
|
||||
|
||||
// remove the power bonus from the player
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
if( player )
|
||||
player->removePowerBonus( getObject() );
|
||||
|
||||
m_overchargeActive = FALSE;
|
||||
|
||||
} // end onDelete
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::onCapture( Player *oldOwner, Player *newOwner )
|
||||
{
|
||||
|
||||
// do nothing if we haven't upgraded yet
|
||||
if( m_overchargeActive == FALSE )
|
||||
return;
|
||||
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// remove power bonus from old owner
|
||||
if( oldOwner )
|
||||
oldOwner->removePowerBonus( getObject() );
|
||||
|
||||
// add power bonus to the new owner
|
||||
if( newOwner )
|
||||
newOwner->addPowerBonus( getObject() );
|
||||
|
||||
} // end onCapture
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// overcharge active
|
||||
xfer->xferBool( &m_overchargeActive );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverchargeBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
// Our effect is a fire and forget effect, not an upgrade state that is itself saved, so need to re-fire.
|
||||
if( m_overchargeActive && getObject()->getControllingPlayer() )
|
||||
getObject()->getControllingPlayer()->addPowerBonus( getObject() );
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: POWTruckBehavior.cpp /////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day
|
||||
// Desc: POW Truck
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/POWTruckAIUpdate.h"
|
||||
#include "GameLogic/Module/POWTruckBehavior.h"
|
||||
|
||||
#ifdef ALLOW_SURRENDER
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
POWTruckBehaviorModuleData::POWTruckBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
} // end POWTruckBehaviorModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void POWTruckBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
OpenContainModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
|
||||
};
|
||||
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
POWTruckBehavior::POWTruckBehavior( Thing *thing, const ModuleData *moduleData )
|
||||
: OpenContain( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end POWTruckBehavior
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
POWTruckBehavior::~POWTruckBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~POWTruckBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void POWTruckBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
|
||||
{
|
||||
Object *us = getObject();
|
||||
|
||||
// sanity
|
||||
if( other == NULL )
|
||||
return;
|
||||
|
||||
// if other isn't slated to be picked up by us, ignore
|
||||
AIUpdateInterface *otherAi = other->getAIUpdateInterface();
|
||||
if( otherAi == NULL || otherAi->isSurrendered() == FALSE )
|
||||
return;
|
||||
|
||||
// get our AI info
|
||||
AIUpdateInterface *ourAI = us->getAIUpdateInterface();
|
||||
DEBUG_ASSERTCRASH( ourAI, ("POWTruckBehavior::onCollide - '%s' has no AI\n",
|
||||
us->getTemplate()->getName().str()) );
|
||||
POWTruckAIUpdateInterface *powTruckAI = ourAI->getPOWTruckAIUpdateInterface();
|
||||
DEBUG_ASSERTCRASH( powTruckAI, ("POWTruckBehavior::onCollide - '%s' has no POWTruckAI\n",
|
||||
us->getTemplate()->getName().str()) );
|
||||
|
||||
// pick up the prisoner
|
||||
powTruckAI->loadPrisoner( other );
|
||||
|
||||
} // end onCollide
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void POWTruckBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void POWTruckBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void POWTruckBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PoisonedBehavior.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, July 2002
|
||||
// Desc: Behavior that reacts to poison Damage by continuously damaging us further in an Update
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/Module/PoisonedBehavior.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// tinting is all handled in drawable, now, Graham look near the bottom of Drawable::UpdateDrawable()
|
||||
//static const RGBColor poisonedTint = {0.0f, 1.0f, 0.0f};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PoisonedBehaviorModuleData::PoisonedBehaviorModuleData()
|
||||
{
|
||||
m_poisonDamageIntervalData = 0; // How often I retake poison damage dealt me
|
||||
m_poisonDurationData = 0; // And how long after the last poison dose I am poisoned
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PoisonedBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "PoisonDamageInterval", INI::parseDurationUnsignedInt, NULL, offsetof(PoisonedBehaviorModuleData, m_poisonDamageIntervalData) },
|
||||
{ "PoisonDuration", INI::parseDurationUnsignedInt, NULL, offsetof(PoisonedBehaviorModuleData, m_poisonDurationData) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
UpdateModuleData::buildFieldParse(p);
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PoisonedBehavior::PoisonedBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
|
||||
{
|
||||
m_poisonDamageFrame = 0;
|
||||
m_poisonOverallStopFrame = 0;
|
||||
m_poisonDamageAmount = 0.0f;
|
||||
m_deathType = DEATH_POISONED;
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PoisonedBehavior::~PoisonedBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Damage has been dealt, this is an opportunity to react to that damage */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
if( damageInfo->in.m_damageType == DAMAGE_POISON )
|
||||
startPoisonedEffects( damageInfo );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::onHealing( DamageInfo *damageInfo )
|
||||
{
|
||||
stopPoisonedEffects();
|
||||
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime PoisonedBehavior::update()
|
||||
{
|
||||
const PoisonedBehaviorModuleData* d = getPoisonedBehaviorModuleData();
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
if( m_poisonOverallStopFrame == 0 )
|
||||
{
|
||||
DEBUG_CRASH(("hmm, this should not happen"));
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
//we aren't poisoned, so nevermind
|
||||
}
|
||||
|
||||
if (m_poisonDamageFrame != 0 && now >= m_poisonDamageFrame)
|
||||
{
|
||||
// If it is time to do damage, then do it and reset the damage timer
|
||||
DamageInfo damage;
|
||||
damage.in.m_amount = m_poisonDamageAmount;
|
||||
damage.in.m_sourceID = INVALID_ID;
|
||||
damage.in.m_damageType = DAMAGE_UNRESISTABLE; // Not poison, as that will infect us again
|
||||
damage.in.m_deathType = m_deathType;
|
||||
getObject()->attemptDamage( &damage );
|
||||
|
||||
m_poisonDamageFrame = now + d->m_poisonDamageIntervalData;
|
||||
}
|
||||
|
||||
// If we are now at zero we need to turn off our special effects...
|
||||
// unless the poison killed us, then we continue to be a pulsating toxic pus ball
|
||||
if( m_poisonOverallStopFrame != 0 &&
|
||||
now >= m_poisonOverallStopFrame &&
|
||||
!getObject()->isEffectivelyDead())
|
||||
{
|
||||
stopPoisonedEffects();
|
||||
}
|
||||
|
||||
return calcSleepTime();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime PoisonedBehavior::calcSleepTime()
|
||||
{
|
||||
// UPDATE_SLEEP requires a count-of-frames, not an absolute-frame, so subtract 'now'
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
if (m_poisonOverallStopFrame == 0 || m_poisonOverallStopFrame == now)
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
return frameToSleepTime(m_poisonDamageFrame, m_poisonOverallStopFrame);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::startPoisonedEffects( const DamageInfo *damageInfo )
|
||||
{
|
||||
const PoisonedBehaviorModuleData* d = getPoisonedBehaviorModuleData();
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
// We are going to take the damage dealt by the original poisoner every so often for a while.
|
||||
m_poisonDamageAmount = damageInfo->out.m_actualDamageDealt;
|
||||
|
||||
m_poisonOverallStopFrame = now + d->m_poisonDurationData;
|
||||
|
||||
// If we are getting re-poisoned, don't reset the damage counter if running, but do set it if unset
|
||||
if( m_poisonDamageFrame != 0 )
|
||||
m_poisonDamageFrame = min( m_poisonDamageFrame, now + d->m_poisonDamageIntervalData );
|
||||
else
|
||||
m_poisonDamageFrame = now + d->m_poisonDamageIntervalData;
|
||||
|
||||
m_deathType = damageInfo->in.m_deathType;
|
||||
|
||||
Drawable *myDrawable = getObject()->getDrawable();
|
||||
if( myDrawable )
|
||||
myDrawable->setTintStatus( TINT_STATUS_POISONED );// Graham, It has changed, see UpdateDrawable()
|
||||
|
||||
setWakeFrame(getObject(), calcSleepTime());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::stopPoisonedEffects()
|
||||
{
|
||||
m_poisonDamageFrame = 0;
|
||||
m_poisonOverallStopFrame = 0;
|
||||
m_poisonDamageAmount = 0.0f;
|
||||
|
||||
Drawable *myDrawable = getObject()->getDrawable();
|
||||
if( myDrawable )
|
||||
myDrawable->clearTintStatus( TINT_STATUS_POISONED );// Graham, It has changed, see UpdateDrawable()
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
const XferVersion currentVersion = 2;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// poisoned damage frame
|
||||
xfer->xferUnsignedInt( &m_poisonDamageFrame );
|
||||
|
||||
// poison overall stop frame
|
||||
xfer->xferUnsignedInt( &m_poisonOverallStopFrame );
|
||||
|
||||
// poison damage amount
|
||||
xfer->xferReal( &m_poisonDamageAmount );
|
||||
|
||||
if (version >= 2)
|
||||
{
|
||||
xfer->xferUser(&m_deathType, sizeof(m_deathType));
|
||||
}
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PoisonedBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PrisonBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, August 2002
|
||||
// Desc: Prison Behavior
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameClient/GameClient.h"
|
||||
#include "GameClient/Line2D.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/PrisonBehavior.h"
|
||||
|
||||
#ifdef ALLOW_SURRENDER
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
class PrisonVisual : public MemoryPoolObject
|
||||
{
|
||||
|
||||
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( PrisonVisual, "PrisonVisual" )
|
||||
|
||||
public:
|
||||
|
||||
PrisonVisual( void );
|
||||
// virtual destructor prototype provied by memory pool object
|
||||
|
||||
ObjectID m_objectID; ///< object that is contained
|
||||
DrawableID m_drawableID; ///< associated visual prisoner drawable
|
||||
PrisonVisual *m_next; ///< next
|
||||
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PrisonVisual::PrisonVisual( void )
|
||||
{
|
||||
|
||||
m_objectID = INVALID_ID;
|
||||
m_drawableID = INVALID_DRAWABLE_ID;
|
||||
m_next = NULL;
|
||||
|
||||
} // end PrisonVisual
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PrisonVisual::~PrisonVisual( void )
|
||||
{
|
||||
|
||||
} // end ~PrisonVisual
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PrisonBehaviorModuleData::PrisonBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
m_showPrisoners = FALSE;
|
||||
|
||||
} // end PrisonBehaviorModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PrisonBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
OpenContainModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
|
||||
{ "ShowPrisoners", INI::parseBool, NULL, offsetof( PrisonBehaviorModuleData, m_showPrisoners ) },
|
||||
{ "YardBonePrefix", INI::parseAsciiString, NULL, offsetof( PrisonBehaviorModuleData, m_prisonYardBonePrefix ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PrisonBehavior::PrisonBehavior( Thing *thing, const ModuleData *moduleData )
|
||||
: OpenContain( thing, moduleData )
|
||||
{
|
||||
|
||||
m_visualList = NULL;
|
||||
|
||||
} // end PrisonBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PrisonBehavior::~PrisonBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~PrisonBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::onDelete( void )
|
||||
{
|
||||
|
||||
// extend functionality
|
||||
OpenContain::onDelete();
|
||||
|
||||
// delete our list
|
||||
Drawable *draw;
|
||||
PrisonVisual *visual;
|
||||
while( m_visualList )
|
||||
{
|
||||
|
||||
// delete drawable if found
|
||||
draw = TheGameClient->findDrawableByID( m_visualList->m_drawableID );
|
||||
if( draw )
|
||||
TheGameClient->destroyDrawable( draw );
|
||||
|
||||
// delete element and set next to head
|
||||
visual = m_visualList->m_next;
|
||||
m_visualList->deleteInstance();
|
||||
m_visualList = visual;
|
||||
|
||||
} // end while
|
||||
|
||||
} // end onDelete
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::onContaining( Object *obj )
|
||||
{
|
||||
|
||||
// extend functionality
|
||||
OpenContain::onContaining( obj );
|
||||
|
||||
// objects inside a prison are held
|
||||
obj->setDisabled( DISABLED_HELD );
|
||||
|
||||
// if we show visuals, make one
|
||||
const PrisonBehaviorModuleData *modData = getPrisonBehaviorModuleData();
|
||||
if( modData->m_showPrisoners )
|
||||
addVisual( obj );
|
||||
|
||||
} // end onContaining
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::onRemoving( Object *obj )
|
||||
{
|
||||
|
||||
// if we show visuals, remove one
|
||||
const PrisonBehaviorModuleData *modData = getPrisonBehaviorModuleData();
|
||||
if( modData->m_showPrisoners )
|
||||
removeVisual( obj );
|
||||
|
||||
// object is no longer held inside a garrisoned building
|
||||
obj->clearDisabled( DISABLED_HELD );
|
||||
|
||||
// extend functionality
|
||||
OpenContain::onRemoving( obj );
|
||||
|
||||
} // end onRemoving
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Pick a random location inside the prison yard */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::pickVisualLocation( Coord3D *pos )
|
||||
{
|
||||
Object *us = getObject();
|
||||
const PrisonBehaviorModuleData *modData = getPrisonBehaviorModuleData();
|
||||
Int i;
|
||||
|
||||
// sanity
|
||||
if( pos == NULL )
|
||||
return;
|
||||
|
||||
// initialize the picked location to that of the prison center
|
||||
Coord3D pickedLocation = *us->getPosition();
|
||||
|
||||
// get the positions of the bones that make up the prison yard area
|
||||
const Int MAX_YARD_BONES = 16;
|
||||
Coord3D yardPositions[ MAX_YARD_BONES ];
|
||||
Int yardBones = us->getMultiLogicalBonePosition( modData->m_prisonYardBonePrefix.str(),
|
||||
MAX_YARD_BONES,
|
||||
yardPositions,
|
||||
NULL );
|
||||
|
||||
//
|
||||
// we must have at least 3 bone locations to make a yard polygon, otherwise we'll
|
||||
// default to the object position
|
||||
//
|
||||
if( yardBones >= 3 )
|
||||
{
|
||||
|
||||
// find the bounding region of the yard area
|
||||
Region2D yardRegion;
|
||||
yardRegion.lo.x = yardPositions[ 0 ].x;
|
||||
yardRegion.lo.y = yardPositions[ 0 ].y;
|
||||
yardRegion.hi.x = yardPositions[ 0 ].x;
|
||||
yardRegion.hi.y = yardPositions[ 0 ].y;
|
||||
for( i = 1; i < yardBones; i++ )
|
||||
{
|
||||
|
||||
if( yardPositions[ i ].x < yardRegion.lo.x )
|
||||
yardRegion.lo.x = yardPositions[ i ].x;
|
||||
if( yardPositions[ i ].y < yardRegion.lo.y )
|
||||
yardRegion.lo.y = yardPositions[ i ].y;
|
||||
if( yardPositions[ i ].x > yardRegion.hi.x )
|
||||
yardRegion.hi.x = yardPositions[ i ].x;
|
||||
if( yardPositions[ i ].y > yardRegion.hi.y )
|
||||
yardRegion.hi.y = yardPositions[ i ].y;
|
||||
|
||||
} // end for i
|
||||
|
||||
//
|
||||
// now that we have a yard region, the default visual position will be in the middle
|
||||
// of the yard region instead of the position of our object
|
||||
//
|
||||
pickedLocation.x = yardRegion.lo.x + yardRegion.width() / 2.0f;
|
||||
pickedLocation.y = yardRegion.lo.y + yardRegion.height() / 2.0f;
|
||||
// NOTE: pickedLocation.z is left alone at the object center Z
|
||||
|
||||
// loop till we find a valid location that is inside the yard area
|
||||
Int maxTries = 32;
|
||||
Coord3D loc;
|
||||
for( i = 0; i < maxTries; ++i )
|
||||
{
|
||||
|
||||
// pick a location
|
||||
loc.x = GameLogicRandomValueReal( yardRegion.lo.x, yardRegion.hi.x );
|
||||
loc.y = GameLogicRandomValueReal( yardRegion.lo.y, yardRegion.hi.y );
|
||||
loc.z = pickedLocation.z;
|
||||
|
||||
// must be inside the yard polygon
|
||||
if( PointInsideArea2D( &loc, yardPositions, yardBones ) == TRUE )
|
||||
{
|
||||
|
||||
// use this location, leave Z alone as the center of the prison
|
||||
pickedLocation = loc;
|
||||
break; // exit for i
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end if
|
||||
|
||||
// return the location picked
|
||||
*pos = pickedLocation;
|
||||
|
||||
} // end pickVisualLocation
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Add prisoner visual to the prison yard */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::addVisual( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
// create a drawable
|
||||
Drawable *draw = TheThingFactory->newDrawable( obj->getTemplate() );
|
||||
|
||||
// set the color of the drawable to that of the object
|
||||
if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
|
||||
draw->setIndicatorColor( obj->getNightIndicatorColor() );
|
||||
else
|
||||
draw->setIndicatorColor( obj->getIndicatorColor() );
|
||||
|
||||
// pick a location insid the prison yard
|
||||
Coord3D pos;
|
||||
pickVisualLocation( &pos );
|
||||
|
||||
// place drawable withing the prison yard area
|
||||
draw->setPosition( &pos );
|
||||
draw->setOrientation( GameLogicRandomValueReal( 0, TWO_PI ) );
|
||||
DrawableInfo *drawInfo=draw->getDrawableInfo();
|
||||
drawInfo->m_shroudStatusObjectID=getObject()->getID();
|
||||
|
||||
// record this object/drawable pair
|
||||
PrisonVisual *visual = newInstance(PrisonVisual);
|
||||
visual->m_objectID = obj->getID();
|
||||
visual->m_drawableID = draw->getID();
|
||||
visual->m_next = m_visualList;
|
||||
m_visualList = visual;
|
||||
|
||||
} // end addVisual
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Remove prisoner visual from the prison yard */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::removeVisual( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
// initialize a drawable ID to invalid
|
||||
DrawableID drawableID = INVALID_DRAWABLE_ID;
|
||||
|
||||
// find visual info in our list, once found, take this opportunity to remove it from that list
|
||||
PrisonVisual *visual, *prevVisual = NULL;
|
||||
for( visual = m_visualList; visual; visual = visual->m_next )
|
||||
{
|
||||
|
||||
// is this the one we're looking for
|
||||
if( visual->m_objectID == obj->getID() )
|
||||
{
|
||||
|
||||
// record the information we need here
|
||||
drawableID = visual->m_drawableID;
|
||||
|
||||
// remove from list
|
||||
if( prevVisual )
|
||||
prevVisual->m_next = visual->m_next;
|
||||
else
|
||||
m_visualList = visual->m_next;
|
||||
|
||||
// delete the element
|
||||
visual->deleteInstance();
|
||||
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
// keep a pointer to the previous element
|
||||
prevVisual = visual;
|
||||
|
||||
} // end for
|
||||
|
||||
// find the drawable visual and destroy it
|
||||
Drawable *draw = TheGameClient->findDrawableByID( drawableID );
|
||||
if( draw )
|
||||
TheGameClient->destroyDrawable( draw );
|
||||
|
||||
} // end removeVisual
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
// count and data for the prison visuals
|
||||
UnsignedShort visualCount = 0;
|
||||
PrisonVisual *visual;
|
||||
for( visual = m_visualList; visual; visual = visual->m_next )
|
||||
visualCount++;
|
||||
xfer->xferUnsignedShort( &visualCount );
|
||||
if( xfer->getXferMode() == XFER_SAVE )
|
||||
{
|
||||
|
||||
// write all data
|
||||
for( visual = m_visualList; visual; visual = visual->m_next )
|
||||
{
|
||||
|
||||
// object id
|
||||
xfer->xferObjectID( &visual->m_objectID );
|
||||
|
||||
// drawable id
|
||||
xfer->xferDrawableID( &visual->m_drawableID );
|
||||
|
||||
} // end for, visual
|
||||
|
||||
} // end if, save
|
||||
else
|
||||
{
|
||||
|
||||
// the visual list should be empty
|
||||
if( m_visualList != NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "PrisonBehavior::xfer - the visual list should be empty but is not\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// read each item
|
||||
for( UnsignedShort i = 0; i < visualCount; ++i )
|
||||
{
|
||||
|
||||
// allocate a new visual and tie to list
|
||||
visual = newInstance(PrisonVisual);
|
||||
visual->m_next = m_visualList;
|
||||
m_visualList = visual;
|
||||
|
||||
// read object id
|
||||
xfer->xferObjectID( &visual->m_objectID );
|
||||
|
||||
// read drawable id
|
||||
xfer->xferDrawableID( &visual->m_drawableID );
|
||||
|
||||
} // end for, i
|
||||
|
||||
} // end else, load
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PrisonBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PropagandaCenterBehavior.cpp /////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, August 2002
|
||||
// Desc: Propaganda Center Behavior
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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/Xfer.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/PropagandaCenterBehavior.h"
|
||||
|
||||
#ifdef ALLOW_SURRENDER
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PropagandaCenterBehaviorModuleData::PropagandaCenterBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
m_brainwashDuration = 0;
|
||||
|
||||
} // end PropagandaCenterBehaviorModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PropagandaCenterBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
PrisonBehaviorModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
|
||||
{ "BrainwashDuration", INI::parseDurationUnsignedInt, NULL, offsetof( PropagandaCenterBehaviorModuleData, m_brainwashDuration ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PropagandaCenterBehavior::PropagandaCenterBehavior( Thing *thing, const ModuleData *moduleData )
|
||||
: PrisonBehavior( thing, moduleData )
|
||||
{
|
||||
|
||||
m_brainwashingSubjectID = INVALID_ID;
|
||||
m_brainwashingSubjectStartFrame = 0;
|
||||
|
||||
} // end PropagandaCenterBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PropagandaCenterBehavior::~PropagandaCenterBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~PropagandaCenterBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaCenterBehavior::onDelete( void )
|
||||
{
|
||||
|
||||
// extend functionality
|
||||
PrisonBehavior::onDelete();
|
||||
|
||||
//
|
||||
// go through our list of brainwashed objects, and if they are still under our
|
||||
// control, return them to their original owners
|
||||
//
|
||||
for( BrainwashedIDListContIterator it = m_brainwashedList.begin();
|
||||
it != m_brainwashedList.end();
|
||||
++it )
|
||||
{
|
||||
Object *obj;
|
||||
|
||||
// get this object
|
||||
obj = TheGameLogic->findObjectByID( *it );
|
||||
if( obj )
|
||||
{
|
||||
|
||||
// return this object under the control of the original owner
|
||||
obj->restoreOriginalTeam();
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for
|
||||
|
||||
// clear the list
|
||||
m_brainwashedList.clear();
|
||||
|
||||
} // end onDelete
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime PropagandaCenterBehavior::update( void )
|
||||
{
|
||||
Object *us = getObject();
|
||||
const PropagandaCenterBehaviorModuleData *modData = getPropagandaCenterBehaviorModuleData();
|
||||
|
||||
// extend functionality
|
||||
PrisonBehavior::update();
|
||||
|
||||
// if we have a prisoner inside, continue the brainwashing on them (one at a time)
|
||||
if( m_brainwashingSubjectID != INVALID_ID )
|
||||
{
|
||||
Object *brainwashingSubject = TheGameLogic->findObjectByID( m_brainwashingSubjectID );
|
||||
|
||||
if( brainwashingSubject )
|
||||
{
|
||||
|
||||
// if we've been in here long enough, we come out brainwashed
|
||||
if( TheGameLogic->getFrame() - m_brainwashingSubjectStartFrame >= modData->m_brainwashDuration )
|
||||
{
|
||||
|
||||
// only can exit if the prison allows us to
|
||||
ExitDoorType exitDoor = reserveDoorForExit(brainwashingSubject->getTemplate(), brainwashingSubject);
|
||||
if(exitDoor != DOOR_NONE_AVAILABLE)
|
||||
{
|
||||
|
||||
// place this object under the control of the player
|
||||
Player *player = us->getControllingPlayer();
|
||||
DEBUG_ASSERTCRASH( player, ("Brainwashing: No controlling player for '%s'\n", us->getTemplate()->getName().str()) );
|
||||
if( player )
|
||||
brainwashingSubject->setTemporaryTeam( player->getDefaultTeam() );
|
||||
|
||||
// remove any surrender status from this object
|
||||
AIUpdateInterface *ai = brainwashingSubject->getAIUpdateInterface();
|
||||
if( ai )
|
||||
ai->setSurrendered( NULL, FALSE );
|
||||
|
||||
// add this object to our brainwashed list if we're not already in it
|
||||
for( BrainwashedIDListIterator it = m_brainwashedList.begin();
|
||||
it != m_brainwashedList.end(); ++it )
|
||||
if( *it == brainwashingSubject->getID() )
|
||||
break; // exit for
|
||||
|
||||
if( it == m_brainwashedList.end() )
|
||||
m_brainwashedList.push_front( brainwashingSubject->getID() );
|
||||
|
||||
// exit the prison
|
||||
exitObjectViaDoor( brainwashingSubject, exitDoor );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if,
|
||||
|
||||
} // end if
|
||||
|
||||
// if we have no brainwashing subject, hook one up if we have people inside us
|
||||
if( m_brainwashingSubjectID == INVALID_ID )
|
||||
{
|
||||
|
||||
// find the first object in our containment list
|
||||
if( getContainList().begin() != getContainList().end() )
|
||||
{
|
||||
Object *obj = getContainList().front();
|
||||
|
||||
if( obj )
|
||||
{
|
||||
|
||||
// assign brainwashing subject on this frame
|
||||
m_brainwashingSubjectID = obj->getID();
|
||||
m_brainwashingSubjectStartFrame = TheGameLogic->getFrame();
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaCenterBehavior::onRemoving( Object *obj )
|
||||
{
|
||||
|
||||
// if we're removing the brainwashing subject, NULL the pointer
|
||||
if( m_brainwashingSubjectID == obj->getID() )
|
||||
{
|
||||
|
||||
m_brainwashingSubjectID = INVALID_ID;
|
||||
m_brainwashingSubjectStartFrame = 0;
|
||||
|
||||
} // end if
|
||||
|
||||
// extend functionality
|
||||
PrisonBehavior::onRemoving( obj );
|
||||
|
||||
} // end onRemoving
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaCenterBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
PrisonBehavior::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaCenterBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
PrisonBehavior::xfer( xfer );
|
||||
|
||||
// brainwashing subject
|
||||
xfer->xferObjectID( &m_brainwashingSubjectID );
|
||||
|
||||
// brainwashing subject start frame
|
||||
xfer->xferUnsignedInt( &m_brainwashingSubjectStartFrame );
|
||||
|
||||
// brainwashed list size and data
|
||||
xfer->xferSTLObjectIDList( &m_brainwashedList );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaCenterBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
PrisonBehavior::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PropagandaTowerBehavior.cpp //////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, August 2002
|
||||
// Desc: Behavior module for PropagandaTower
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Upgrade.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameLogic/Module/PropagandaTowerBehavior.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
|
||||
// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
|
||||
enum ObjectID;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** This class is used to track objects as they exit our area of influence */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
class ObjectTracker : public MemoryPoolObject
|
||||
{
|
||||
|
||||
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ObjectTracker, "ObjectTracker" );
|
||||
|
||||
public:
|
||||
|
||||
ObjectTracker( void ) { objectID = INVALID_ID; next = NULL; }
|
||||
|
||||
ObjectID objectID;
|
||||
ObjectTracker *next;
|
||||
|
||||
};
|
||||
ObjectTracker::~ObjectTracker( void ) { }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PropagandaTowerBehaviorModuleData::PropagandaTowerBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
m_scanRadius = 1.0f;
|
||||
m_scanDelayInFrames = 100;
|
||||
m_autoHealPercentPerSecond = 0.01f;
|
||||
m_upgradedAutoHealPercentPerSecond = 0.02f;
|
||||
m_pulseFX = NULL;
|
||||
m_upgradeRequired = NULL;
|
||||
m_upgradedPulseFX = NULL;
|
||||
|
||||
} // end PropagandaTowerBehaviorModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PropagandaTowerBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
UpdateModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "Radius", INI::parseReal, NULL, offsetof( PropagandaTowerBehaviorModuleData, m_scanRadius ) },
|
||||
{ "DelayBetweenUpdates", INI::parseDurationUnsignedInt, NULL, offsetof( PropagandaTowerBehaviorModuleData, m_scanDelayInFrames ) },
|
||||
{ "HealPercentEachSecond", INI::parsePercentToReal, NULL, offsetof( PropagandaTowerBehaviorModuleData, m_autoHealPercentPerSecond ) },
|
||||
{ "UpgradedHealPercentEachSecond", INI::parsePercentToReal,NULL, offsetof( PropagandaTowerBehaviorModuleData, m_upgradedAutoHealPercentPerSecond ) },
|
||||
{ "PulseFX", INI::parseFXList, NULL, offsetof( PropagandaTowerBehaviorModuleData, m_pulseFX ) },
|
||||
{ "UpgradeRequired", INI::parseAsciiString, NULL, offsetof( PropagandaTowerBehaviorModuleData, m_upgradeRequired ) },
|
||||
{ "UpgradedPulseFX", INI::parseFXList, NULL, offsetof( PropagandaTowerBehaviorModuleData, m_upgradedPulseFX ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PropagandaTowerBehavior::PropagandaTowerBehavior( Thing *thing, const ModuleData *modData )
|
||||
: UpdateModule( thing, modData )
|
||||
{
|
||||
//Added By Sadullah Nader
|
||||
//Initializations inserted
|
||||
m_lastScanFrame = 0;
|
||||
//
|
||||
m_insideList = NULL;
|
||||
setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
|
||||
|
||||
} // end PropagandaTowerBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
PropagandaTowerBehavior::~PropagandaTowerBehavior( void )
|
||||
{
|
||||
|
||||
} // end ~PropagandaTowerBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Module is being deleted */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::onDelete( void )
|
||||
{
|
||||
|
||||
// remove any benefits from anybody in our area of influence
|
||||
removeAllInfluence();
|
||||
|
||||
} // end onDelete
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Resolve */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::onObjectCreated( void )
|
||||
{
|
||||
const PropagandaTowerBehaviorModuleData *modData = getPropagandaTowerBehaviorModuleData();
|
||||
|
||||
// convert module upgrade name to a pointer
|
||||
m_upgradeRequired = TheUpgradeCenter->findUpgrade( modData->m_upgradeRequired );
|
||||
|
||||
} // end onObjectCreated
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::onCapture( Player *oldOwner, Player *newOwner )
|
||||
{
|
||||
// We don't function for the neutral player.
|
||||
if( newOwner == ThePlayerList->getNeutralPlayer() )
|
||||
{
|
||||
removeAllInfluence();
|
||||
setWakeFrame( getObject(), UPDATE_SLEEP_FOREVER );
|
||||
}
|
||||
else
|
||||
setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** The update callback */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime PropagandaTowerBehavior::update( void )
|
||||
{
|
||||
/// @todo srj use SLEEPY_UPDATE here
|
||||
const PropagandaTowerBehaviorModuleData *modData = getPropagandaTowerBehaviorModuleData();
|
||||
|
||||
//Sep 27, 2002 (Kris): Added this code to prevent the tower from working while under construction.
|
||||
Object *self = getObject();
|
||||
if( BitTest( self->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) )
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
if( self->testStatus(OBJECT_STATUS_SOLD) )
|
||||
{
|
||||
removeAllInfluence();
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
|
||||
if( self->isEffectivelyDead() )
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
|
||||
if( self->isDisabled() )
|
||||
{
|
||||
// We need to let go of everyone if we are EMPd or underpowered or yadda, but not if we are only held
|
||||
|
||||
DisabledMaskType allButHeld = MAKE_DISABLED_MASK( DISABLED_HELD );
|
||||
FLIP_DISABLEDMASK(allButHeld);
|
||||
|
||||
if( TEST_DISABLEDMASK_ANY(self->getDisabledFlags(), allButHeld) )
|
||||
{
|
||||
removeAllInfluence();
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if( self->getContainedBy() && self->getContainedBy()->getContainedBy() )
|
||||
{
|
||||
// If our container is contained, we turn the heck off. Seems like a weird specific check, but all of
|
||||
// attacking is guarded by the same check in isPassengersAllowedToFire. We similarly work in a container,
|
||||
// but not in a double container.
|
||||
removeAllInfluence();
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
// if it's not time to scan, nothing to do
|
||||
UnsignedInt currentFrame = TheGameLogic->getFrame();
|
||||
if( currentFrame - m_lastScanFrame >= modData->m_scanDelayInFrames )
|
||||
{
|
||||
|
||||
// do a scan
|
||||
doScan();
|
||||
m_lastScanFrame = currentFrame;
|
||||
|
||||
} // end if
|
||||
|
||||
// go through any objects in our area of influence and do the effect logic on them
|
||||
Object *obj;
|
||||
ObjectTracker *curr = NULL, *prev = NULL, *next = NULL;
|
||||
for( curr = m_insideList; curr; curr = next )
|
||||
{
|
||||
|
||||
// get the next link
|
||||
next = curr->next;
|
||||
|
||||
// find this object
|
||||
obj = TheGameLogic->findObjectByID( curr->objectID );
|
||||
if ((obj) &&
|
||||
(obj->isKindOf(KINDOF_SCORE) || obj->isKindOf(KINDOF_SCORE_CREATE) || obj->isKindOf(KINDOF_SCORE_DESTROY) || obj->isKindOf(KINDOF_MP_COUNT_FOR_VICTORY)))
|
||||
{
|
||||
|
||||
// give any bonus to this object
|
||||
effectLogic( obj, TRUE, getPropagandaTowerBehaviorModuleData() );
|
||||
|
||||
// record this element as the previous one found in the list
|
||||
prev = curr;
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
//
|
||||
// actual object wasn't found, remove this entry from our inside list so we don't
|
||||
// have to search through it again
|
||||
//
|
||||
if( prev )
|
||||
prev->next = curr->next;
|
||||
else
|
||||
m_insideList = curr->next;
|
||||
curr->deleteInstance();
|
||||
|
||||
} // end else
|
||||
|
||||
} // end for, curr
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** The death callback */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
|
||||
// remove any benefits from anybody in our area of influence
|
||||
removeAllInfluence();
|
||||
|
||||
} // end onDie
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Grant or remove effect to this object */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::effectLogic( Object *obj, Bool giving,
|
||||
const PropagandaTowerBehaviorModuleData *modData )
|
||||
{
|
||||
Bool effectUpgraded = getObject()->getControllingPlayer()->hasUpgradeComplete( m_upgradeRequired );
|
||||
|
||||
// if giving the effect
|
||||
if( giving )
|
||||
{
|
||||
if ( obj->hasAnyDamageWeapon() == TRUE )
|
||||
{
|
||||
if( obj->testWeaponBonusCondition( WEAPONBONUSCONDITION_ENTHUSIASTIC ) == FALSE )
|
||||
obj->setWeaponBonusCondition( WEAPONBONUSCONDITION_ENTHUSIASTIC );
|
||||
|
||||
|
||||
if (effectUpgraded)
|
||||
{
|
||||
if (obj->testWeaponBonusCondition( WEAPONBONUSCONDITION_SUBLIMINAL ) == FALSE)
|
||||
obj->setWeaponBonusCondition( WEAPONBONUSCONDITION_SUBLIMINAL );
|
||||
}
|
||||
|
||||
} // hasdamageweapon
|
||||
|
||||
|
||||
// grant health to this object as well
|
||||
BodyModuleInterface *body = obj->getBodyModule();
|
||||
if( body )
|
||||
{
|
||||
Real healthPercent;
|
||||
if(effectUpgraded)
|
||||
healthPercent = modData->m_upgradedAutoHealPercentPerSecond;
|
||||
else
|
||||
healthPercent = modData->m_autoHealPercentPerSecond;
|
||||
|
||||
Real amount = healthPercent / LOGICFRAMES_PER_SECOND * body->getMaxHealth();
|
||||
|
||||
// Dustin wants the healing effect not to stack from multiple propaganda towers...
|
||||
// To accomplish this, I'll give every object a single healing-sender (ID)
|
||||
// Any given healing recipient (object) can only receive healing from one particular healing sender
|
||||
// and cannot change healing senders until the previous one expires (its scandelay)
|
||||
|
||||
// obj->attemptHealing(amount, getObject()); // the regular way to give healing...
|
||||
obj->attemptHealingFromSoleBenefactor( amount, getObject(), modData->m_scanDelayInFrames );//the non-stacking way
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// taking effect away
|
||||
obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_ENTHUSIASTIC );
|
||||
obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_SUBLIMINAL );
|
||||
|
||||
} // end else
|
||||
|
||||
} // end effectLogic
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Remove all influence from objects we've given bonuses to */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::removeAllInfluence( void )
|
||||
{
|
||||
ObjectTracker *o;
|
||||
|
||||
// go through all objects we've given bonuses to and remove them
|
||||
Object *obj;
|
||||
for( o = m_insideList; o; o = o->next )
|
||||
{
|
||||
|
||||
obj = TheGameLogic->findObjectByID( o->objectID );
|
||||
if( obj )
|
||||
effectLogic( obj, FALSE, getPropagandaTowerBehaviorModuleData() );
|
||||
|
||||
} // end for
|
||||
|
||||
// delete the list of objects under our influence
|
||||
while( m_insideList )
|
||||
{
|
||||
|
||||
o = m_insideList->next;
|
||||
m_insideList->deleteInstance();
|
||||
m_insideList = o;
|
||||
|
||||
} // end while
|
||||
|
||||
} // end removeAllInfluence
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Do a scan */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::doScan( void )
|
||||
{
|
||||
const PropagandaTowerBehaviorModuleData *modData = getPropagandaTowerBehaviorModuleData();
|
||||
Object *us = getObject();
|
||||
ObjectTracker *newInsideList = NULL;
|
||||
|
||||
// The act of scanning is when we play our effect
|
||||
Bool upgradePresent = FALSE;
|
||||
if( m_upgradeRequired )
|
||||
{
|
||||
|
||||
// see if we have the upgrade
|
||||
switch( m_upgradeRequired->getUpgradeType() )
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
case UPGRADE_TYPE_PLAYER:
|
||||
{
|
||||
Player *player = us->getControllingPlayer();
|
||||
|
||||
upgradePresent = player->hasUpgradeComplete( m_upgradeRequired );
|
||||
break;
|
||||
|
||||
} // end player upgrade
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
case UPGRADE_TYPE_OBJECT:
|
||||
{
|
||||
|
||||
upgradePresent = us->hasUpgrade( m_upgradeRequired );
|
||||
break;
|
||||
|
||||
} // end object upgrade
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
default:
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "PropagandaTowerBehavior::doScan - Unknown upgrade type '%d'\n",
|
||||
m_upgradeRequired->getUpgradeType() ));
|
||||
break;
|
||||
|
||||
} // end default
|
||||
|
||||
} // end switch
|
||||
|
||||
} // end if
|
||||
|
||||
// play the right pulse
|
||||
if( upgradePresent == TRUE )
|
||||
FXList::doFXObj( modData->m_upgradedPulseFX, us );
|
||||
else
|
||||
FXList::doFXObj( modData->m_pulseFX, us );
|
||||
|
||||
// setup scan filters
|
||||
PartitionFilterRelationship relationship( us, PartitionFilterRelationship::ALLOW_ALLIES );
|
||||
PartitionFilterAlive filterAlive;
|
||||
PartitionFilterSameMapStatus filterMapStatus(us);
|
||||
PartitionFilterAcceptByKindOf filterOutBuildings(KINDOFMASK_NONE, MAKE_KINDOF_MASK(KINDOF_STRUCTURE));
|
||||
PartitionFilter *filters[] = { &relationship,
|
||||
&filterAlive,
|
||||
&filterMapStatus,
|
||||
&filterOutBuildings,
|
||||
NULL
|
||||
};
|
||||
|
||||
// scan objects in our region
|
||||
ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( us->getPosition(),
|
||||
modData->m_scanRadius,
|
||||
FROM_CENTER_2D,
|
||||
filters );
|
||||
MemoryPoolObjectHolder hold( iter );
|
||||
Object *obj;
|
||||
ObjectTracker *newEntry;
|
||||
for( obj = iter->first(); obj; obj = iter->next() )
|
||||
{
|
||||
|
||||
// ignore ourselves, as a tower we're not interesting anyway
|
||||
if( obj == us )
|
||||
continue;
|
||||
|
||||
// record this object as being in the new "in list"
|
||||
newEntry = newInstance(ObjectTracker);
|
||||
newEntry->objectID = obj->getID();
|
||||
newEntry->next = newInsideList;
|
||||
newInsideList = newEntry;
|
||||
|
||||
} // end for obj
|
||||
|
||||
//
|
||||
// now that we have a list of objects that are in our area of influence, look through
|
||||
// the objects that were last recorded as in our area of influence and remove any
|
||||
// bonus we've given them (if they are within the area of effect of another tower it's
|
||||
// OK, they'll get the bonus back again when that tower does a scan which won't be too long
|
||||
//
|
||||
for( ObjectTracker *curr = m_insideList; curr; curr = curr->next )
|
||||
{
|
||||
|
||||
// find this entry in the new list
|
||||
ObjectTracker *o = NULL;
|
||||
for( o = newInsideList; o; o = o->next )
|
||||
if( o->objectID == curr->objectID )
|
||||
break;
|
||||
|
||||
// if entry wasn't there, remove the bonus from this object
|
||||
if( o == NULL )
|
||||
{
|
||||
|
||||
obj = TheGameLogic->findObjectByID( curr->objectID );
|
||||
if( obj )
|
||||
effectLogic( obj, FALSE, modData );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for
|
||||
|
||||
// delete the inside list we have recoreded
|
||||
ObjectTracker *next;
|
||||
while( m_insideList )
|
||||
{
|
||||
|
||||
next = m_insideList->next;
|
||||
m_insideList->deleteInstance();
|
||||
m_insideList = next;
|
||||
|
||||
} // end while
|
||||
|
||||
// set the new inside list to the one we're recording
|
||||
m_insideList = newInsideList;
|
||||
|
||||
} // end doScan
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// last scan frame
|
||||
xfer->xferUnsignedInt( &m_lastScanFrame );
|
||||
|
||||
// inside list tracking
|
||||
ObjectTracker *trackerEntry;
|
||||
UnsignedShort insideCount = 0;
|
||||
for( trackerEntry = m_insideList; trackerEntry; trackerEntry = trackerEntry->next )
|
||||
insideCount++;
|
||||
xfer->xferUnsignedShort( &insideCount );
|
||||
if( xfer->getXferMode() == XFER_SAVE )
|
||||
{
|
||||
|
||||
// write all entries
|
||||
for( trackerEntry = m_insideList; trackerEntry; trackerEntry = trackerEntry->next )
|
||||
{
|
||||
|
||||
// object id
|
||||
xfer->xferObjectID( &trackerEntry->objectID );
|
||||
|
||||
} // end for
|
||||
|
||||
} // end if, save
|
||||
else
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( m_insideList != NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "PropagandaTowerBehavior::xfer - m_insideList should be empty but is not\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// read all entries
|
||||
for( UnsignedShort i = 0; i < insideCount; ++i )
|
||||
{
|
||||
|
||||
// allocate new tracker entry and tie to list
|
||||
trackerEntry = newInstance(ObjectTracker);
|
||||
trackerEntry->next = m_insideList;
|
||||
m_insideList = trackerEntry;
|
||||
|
||||
// read object id
|
||||
xfer->xferObjectID( &trackerEntry->objectID );
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end else, load
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PropagandaTowerBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: RebuildHoleBehavior.cpp //////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, June 2002
|
||||
// Desc: GLA Hole behavior that reconstructs a building after death
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/RebuildHoleBehavior.h"
|
||||
#include "GameLogic/Module/StickyBombUpdate.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
RebuildHoleBehaviorModuleData::RebuildHoleBehaviorModuleData( void )
|
||||
{
|
||||
|
||||
m_workerRespawnDelay = 0.0f;
|
||||
m_holeHealthRegenPercentPerSecond = 0.1f;
|
||||
|
||||
} // end RebuildHoleBehaviorModuleData
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void RebuildHoleBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
|
||||
UpdateModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "WorkerObjectName", INI::parseAsciiString, NULL, offsetof( RebuildHoleBehaviorModuleData, m_workerTemplateName ) },
|
||||
{ "WorkerRespawnDelay", INI::parseDurationReal, NULL, offsetof( RebuildHoleBehaviorModuleData, m_workerRespawnDelay ) },
|
||||
{ "HoleHealthRegen%PerSecond", INI::parsePercentToReal, NULL, offsetof( RebuildHoleBehaviorModuleData, m_holeHealthRegenPercentPerSecond ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
RebuildHoleBehavior::RebuildHoleBehavior( Thing *thing, const ModuleData* moduleData )
|
||||
: UpdateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
m_workerID = INVALID_ID;
|
||||
m_reconstructingID = INVALID_ID;
|
||||
m_spawnerObjectID = INVALID_ID;
|
||||
m_workerWaitCounter = 0;
|
||||
m_workerTemplate = NULL;
|
||||
m_rebuildTemplate = NULL;
|
||||
|
||||
} // end RebuildHoleBehavior
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
RebuildHoleBehavior::~RebuildHoleBehavior( void )
|
||||
{
|
||||
// ensure that our generated worker is destroyed,
|
||||
// just in case someone decides to destroy (not kill) us...
|
||||
if( m_workerID != INVALID_ID )
|
||||
{
|
||||
Object *worker = TheGameLogic->findObjectByID(m_workerID);
|
||||
if( worker )
|
||||
{
|
||||
TheGameLogic->destroyObject(worker);
|
||||
m_workerID = INVALID_ID;
|
||||
}
|
||||
}
|
||||
|
||||
} // end ~RebuildHoleBehavior
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** we need to start all the timers and ID ties to make a new worker at the correct time */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::newWorkerRespawnProcess( Object *existingWorker )
|
||||
{
|
||||
const RebuildHoleBehaviorModuleData *modData = getRebuildHoleBehaviorModuleData();
|
||||
|
||||
// if we have an existing worker, get rid of it
|
||||
if( existingWorker )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(existingWorker->getID() == m_workerID, ("m_workerID mismatch in RebuildHole"));
|
||||
TheGameLogic->destroyObject( existingWorker );
|
||||
}
|
||||
m_workerID = INVALID_ID;
|
||||
|
||||
// set the timer for the next worker respawn
|
||||
m_workerWaitCounter = modData->m_workerRespawnDelay;
|
||||
|
||||
//
|
||||
// this method is called when a worker needs to be respawned from the hole. One of those
|
||||
// situations is where the building was killed. Since during building reconstruction
|
||||
// we made the hole "effectively not here" we will always want to make the hole
|
||||
// "here again" (able to be selected, targeted by the AI etc) because it's the
|
||||
// "focus" of this small area again
|
||||
//
|
||||
getObject()->maskObject( FALSE );
|
||||
|
||||
} // end newWorkerRespawnProcess
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::startRebuildProcess( const ThingTemplate *rebuild, ObjectID spawnerID )
|
||||
{
|
||||
|
||||
// save what we're gonna do
|
||||
m_rebuildTemplate = rebuild;
|
||||
|
||||
// store the object that spawned this hole (even though it's likely being destroyed)
|
||||
m_spawnerObjectID = spawnerID;
|
||||
|
||||
// start the spawning process for a worker
|
||||
newWorkerRespawnProcess( NULL );
|
||||
|
||||
} /// end startRebuildProcess
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::transferBombs( Object *reconstruction )
|
||||
{
|
||||
|
||||
Object *self = getObject();
|
||||
|
||||
Object *obj = TheGameLogic->getFirstObject();
|
||||
while( obj )
|
||||
{
|
||||
if( obj->isKindOf( KINDOF_MINE ) )
|
||||
{
|
||||
static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
|
||||
StickyBombUpdate *update = (StickyBombUpdate*)obj->findUpdateModule( key_StickyBombUpdate );
|
||||
if( update && update->getTargetObject() == self )
|
||||
{
|
||||
update->setTargetObject( reconstruction );
|
||||
}
|
||||
}
|
||||
obj = obj->getNextObject();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime RebuildHoleBehavior::update( void )
|
||||
{
|
||||
const RebuildHoleBehaviorModuleData *modData = getRebuildHoleBehaviorModuleData();
|
||||
Object *hole = getObject();
|
||||
Object *reconstructing = NULL;
|
||||
Object *worker = NULL;
|
||||
|
||||
// get the worker object if we have one
|
||||
if( m_workerID != 0 )
|
||||
{
|
||||
|
||||
// get the worker
|
||||
worker = TheGameLogic->findObjectByID( m_workerID );
|
||||
|
||||
// if the worker is no longer there, start the respawning process for a worker again
|
||||
if( worker == NULL )
|
||||
newWorkerRespawnProcess( NULL );
|
||||
|
||||
} // end if
|
||||
|
||||
// if we have a reconstructing object built, get the actual object pointer
|
||||
if( m_reconstructingID != 0 )
|
||||
{
|
||||
|
||||
// get object pointer
|
||||
reconstructing = TheGameLogic->findObjectByID( m_reconstructingID );
|
||||
|
||||
//
|
||||
// if that object does not exist anymore, we need to kill a worker if we have one
|
||||
// and start the spawning process over again
|
||||
//
|
||||
if( reconstructing == NULL )
|
||||
{
|
||||
|
||||
newWorkerRespawnProcess( worker );
|
||||
m_reconstructingID = INVALID_ID;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
// see if it's time for us to spawn a worker
|
||||
if( worker == NULL && m_workerWaitCounter > 0 )
|
||||
{
|
||||
|
||||
// decrement counter and respawn if it's time
|
||||
if( --m_workerWaitCounter == 0 )
|
||||
{
|
||||
|
||||
// resolve the worker template pointer if necessary
|
||||
if( m_workerTemplate == NULL )
|
||||
m_workerTemplate = TheThingFactory->findTemplate( modData->m_workerTemplateName );
|
||||
|
||||
// create a worker
|
||||
worker = TheThingFactory->newObject( m_workerTemplate, hole->getTeam() );
|
||||
if( worker )
|
||||
{
|
||||
|
||||
// set the position of the worker to that of the hole
|
||||
worker->setPosition( hole->getPosition() );
|
||||
|
||||
// save the ID of the worker spawned
|
||||
m_workerID = worker->getID();
|
||||
|
||||
//
|
||||
// tell the worker to begin construction of a building if one does not
|
||||
// exist yet. If one does, have construction resume
|
||||
//
|
||||
AIUpdateInterface *ai = worker->getAIUpdateInterface();
|
||||
if( ai )
|
||||
{
|
||||
|
||||
if( reconstructing == NULL )
|
||||
reconstructing = ai->construct( m_rebuildTemplate,
|
||||
hole->getPosition(),
|
||||
hole->getOrientation(),
|
||||
hole->getControllingPlayer(),
|
||||
TRUE );
|
||||
else
|
||||
ai->aiResumeConstruction( reconstructing, CMD_FROM_AI );
|
||||
|
||||
for ( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
|
||||
{
|
||||
// Just like the building transfers attackers to the hole when it creates us, we need to transfer
|
||||
// attackers to our replacement building before we mask ourselves.
|
||||
AIUpdateInterface* ai = obj->getAI();
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
ai->transferAttack(hole->getID(), reconstructing->getID());
|
||||
}
|
||||
|
||||
// save the id of what we are reconstructing
|
||||
m_reconstructingID = reconstructing->getID();
|
||||
|
||||
// we want to prevent the player from selecting and doing things with this worker
|
||||
worker->setStatus( OBJECT_STATUS_UNSELECTABLE );
|
||||
|
||||
//
|
||||
// we want to prevent the player and the AI from selecting or targeting the hole
|
||||
// cause the focus in this area (while reconstruction is happening) is the
|
||||
// actual reconstructing building
|
||||
//
|
||||
hole->maskObject( TRUE );
|
||||
|
||||
transferBombs( reconstructing );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if, worker
|
||||
|
||||
} // end if, time to spawn a worker
|
||||
|
||||
} // end if, check for working respawn
|
||||
|
||||
// holes get auto-healed when they're sittin around
|
||||
BodyModuleInterface *body = hole->getBodyModule();
|
||||
if( body->getHealth() < body->getMaxHealth() )
|
||||
{
|
||||
DamageInfo healingInfo;
|
||||
|
||||
// do some healing
|
||||
healingInfo.in.m_amount = (modData->m_holeHealthRegenPercentPerSecond / LOGICFRAMES_PER_SECOND) *
|
||||
body->getMaxHealth();
|
||||
healingInfo.in.m_sourceID = hole->getID();
|
||||
healingInfo.in.m_damageType = DAMAGE_HEALING;
|
||||
healingInfo.in.m_deathType = DEATH_NONE;
|
||||
body->attemptHealing( &healingInfo );
|
||||
|
||||
} // end if
|
||||
|
||||
// when re-construction is complete, we remove this hole and worker
|
||||
if( reconstructing &&
|
||||
BitTest( reconstructing->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE )
|
||||
{
|
||||
// Transfer hole name to new building
|
||||
TheScriptEngine->transferObjectName( hole->getName(), reconstructing );
|
||||
|
||||
// make the worker go away
|
||||
if( worker )
|
||||
TheGameLogic->destroyObject( worker );
|
||||
|
||||
// make the hole go away
|
||||
TheGameLogic->destroyObject( hole );
|
||||
|
||||
} // end if
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if( m_workerID != INVALID_ID )
|
||||
{
|
||||
// Our rebuilding building and us the hole can be killed in the same frame, which means we may not have
|
||||
// deleted our generated worker since we do that in our update.
|
||||
Object *worker = TheGameLogic->findObjectByID(m_workerID);
|
||||
if( worker )
|
||||
{
|
||||
TheGameLogic->destroyObject(worker);
|
||||
m_workerID = INVALID_ID;
|
||||
}
|
||||
}
|
||||
|
||||
Object *obj = getObject();
|
||||
|
||||
// destroy us
|
||||
TheGameLogic->destroyObject( obj );
|
||||
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Helper method to get interface given an object */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ RebuildHoleBehaviorInterface* RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( Object *obj )
|
||||
{
|
||||
RebuildHoleBehaviorInterface *rhbi = NULL;
|
||||
|
||||
if( obj )
|
||||
{
|
||||
|
||||
for( BehaviorModule **i = obj->getBehaviorModules(); *i; ++i )
|
||||
{
|
||||
|
||||
rhbi = (*i)->getRebuildHoleBehaviorInterface();
|
||||
if( rhbi )
|
||||
break; // exit for
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end if, obj
|
||||
|
||||
return rhbi;
|
||||
|
||||
} // end getRebuildHoleBehaviorInterfaceFromObject
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version,
|
||||
* 2: Added spawner id */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 2;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// worker ID
|
||||
xfer->xferObjectID( &m_workerID );
|
||||
|
||||
// reconstructing id
|
||||
xfer->xferObjectID( &m_reconstructingID );
|
||||
|
||||
// spawner ID
|
||||
if( version >= 2 )
|
||||
xfer->xferObjectID( &m_spawnerObjectID );
|
||||
|
||||
// worker wait counter
|
||||
xfer->xferUnsignedInt( &m_workerWaitCounter );
|
||||
|
||||
// worker template
|
||||
AsciiString workerName = m_workerTemplate ? m_workerTemplate->getName() : AsciiString::TheEmptyString;
|
||||
xfer->xferAsciiString( &workerName );
|
||||
if( xfer->getXferMode() == XFER_LOAD )
|
||||
{
|
||||
|
||||
if( workerName != AsciiString::TheEmptyString )
|
||||
{
|
||||
|
||||
m_workerTemplate = TheThingFactory->findTemplate( workerName );
|
||||
if( m_workerTemplate == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "RebuildHoleBehavior::xfer - Unable to find template '%s'\n",
|
||||
workerName.str() ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
else
|
||||
m_workerTemplate = NULL;
|
||||
|
||||
} // end if
|
||||
|
||||
// rebuild template
|
||||
AsciiString rebuildName = m_rebuildTemplate ? m_rebuildTemplate->getName() : AsciiString::TheEmptyString;
|
||||
xfer->xferAsciiString( &rebuildName );
|
||||
if( xfer->getXferMode() == XFER_LOAD )
|
||||
{
|
||||
|
||||
if( rebuildName != AsciiString::TheEmptyString )
|
||||
{
|
||||
|
||||
m_rebuildTemplate = TheThingFactory->findTemplate( rebuildName );
|
||||
if( m_rebuildTemplate == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "RebuildHoleBehavior::xfer - Unable to find template '%s'\n",
|
||||
rebuildName.str() ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
else
|
||||
m_rebuildTemplate = NULL;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SlowDeathBehavior.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#define DEFINE_SLOWDEATHPHASE_NAMES
|
||||
#include "Common/GameLOD.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Thing.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Module/SlowDeathBehavior.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/SlavedUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
const Real BEGIN_MIDPOINT_RATIO = 0.35f;
|
||||
const Real END_MIDPOINT_RATIO = 0.65f;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SlowDeathBehaviorModuleData::SlowDeathBehaviorModuleData()
|
||||
{
|
||||
m_sinkRate = 0;
|
||||
m_probabilityModifier = 10;
|
||||
m_modifierBonusPerOverkillPercent = 0;
|
||||
m_sinkDelay = 0;
|
||||
m_sinkDelayVariance = 0;
|
||||
m_destructionDelay = 0;
|
||||
m_destructionDelayVariance = 0;
|
||||
m_destructionAltitude = -10;
|
||||
m_maskOfLoadedEffects = 0; //assume no ocl, fx, or weapons.
|
||||
m_flingForce = 0;
|
||||
m_flingForceVariance = 0;
|
||||
m_flingPitch = 0;
|
||||
m_flingPitchVariance = 0;
|
||||
// redundant.
|
||||
//m_fx.clear();
|
||||
//m_ocls.clear();
|
||||
//m_weapons.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseFX( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
SlowDeathBehaviorModuleData* self = (SlowDeathBehaviorModuleData*)instance;
|
||||
SlowDeathPhaseType sdphase = (SlowDeathPhaseType)INI::scanIndexList(ini->getNextToken(), TheSlowDeathPhaseNames);
|
||||
for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
|
||||
{
|
||||
const FXList *fxl = TheFXListStore->findFXList((token)); // could be null! this is OK!
|
||||
self->m_fx[sdphase].push_back(fxl);
|
||||
if (fxl)
|
||||
self->m_maskOfLoadedEffects |= SlowDeathBehaviorModuleData::HAS_FX;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseOCL( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
SlowDeathBehaviorModuleData* self = (SlowDeathBehaviorModuleData*)instance;
|
||||
SlowDeathPhaseType sdphase = (SlowDeathPhaseType)INI::scanIndexList(ini->getNextToken(), TheSlowDeathPhaseNames);
|
||||
for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
|
||||
{
|
||||
const ObjectCreationList *ocl = TheObjectCreationListStore->findObjectCreationList(token); // could be null! this is OK!
|
||||
self->m_ocls[sdphase].push_back(ocl);
|
||||
if (ocl)
|
||||
self->m_maskOfLoadedEffects |= SlowDeathBehaviorModuleData::HAS_OCL;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseWeapon( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
SlowDeathBehaviorModuleData* self = (SlowDeathBehaviorModuleData*)instance;
|
||||
SlowDeathPhaseType sdphase = (SlowDeathPhaseType)INI::scanIndexList(ini->getNextToken(), TheSlowDeathPhaseNames);
|
||||
for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
|
||||
{
|
||||
const WeaponTemplate *wt = TheWeaponStore->findWeaponTemplate(token); // could be null! this is OK!
|
||||
self->m_weapons[sdphase].push_back(wt);
|
||||
if (wt)
|
||||
self->m_maskOfLoadedEffects |= SlowDeathBehaviorModuleData::HAS_WEAPON;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void SlowDeathBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
UpdateModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "SinkRate", INI::parseVelocityReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_sinkRate ) },
|
||||
{ "ProbabilityModifier", INI::parseInt, NULL, offsetof( SlowDeathBehaviorModuleData, m_probabilityModifier ) },
|
||||
{ "ModifierBonusPerOverkillPercent", INI::parsePercentToReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_modifierBonusPerOverkillPercent ) },
|
||||
{ "SinkDelay", INI::parseDurationUnsignedInt, NULL, offsetof( SlowDeathBehaviorModuleData, m_sinkDelay ) },
|
||||
{ "SinkDelayVariance", INI::parseDurationUnsignedInt, NULL, offsetof( SlowDeathBehaviorModuleData, m_sinkDelayVariance ) },
|
||||
{ "DestructionDelay", INI::parseDurationUnsignedInt, NULL, offsetof( SlowDeathBehaviorModuleData, m_destructionDelay ) },
|
||||
{ "DestructionDelayVariance", INI::parseDurationUnsignedInt, NULL, offsetof( SlowDeathBehaviorModuleData, m_destructionDelayVariance ) },
|
||||
{ "DestructionAltitude", INI::parseReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_destructionAltitude ) },
|
||||
{ "FX", parseFX, NULL, 0 },
|
||||
{ "OCL", parseOCL, NULL, 0 },
|
||||
{ "Weapon", parseWeapon, NULL, 0 },
|
||||
{ "FlingForce", INI::parseReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_flingForce) },
|
||||
{ "FlingForceVariance", INI::parseReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_flingForceVariance) },
|
||||
{ "FlingPitch", INI::parseAngleReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_flingPitch) },
|
||||
{ "FlingPitchVariance", INI::parseAngleReal, NULL, offsetof( SlowDeathBehaviorModuleData, m_flingPitchVariance) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
p.add(DieMuxData::getFieldParse(), offsetof( SlowDeathBehaviorModuleData, m_dieMuxData ));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SlowDeathBehavior::SlowDeathBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
|
||||
{
|
||||
m_flags = 0;
|
||||
m_sinkFrame = 0;
|
||||
m_midpointFrame = 0;
|
||||
m_destructionFrame = 0;
|
||||
m_acceleratedTimeScale = 1.0f;
|
||||
|
||||
if (getSlowDeathBehaviorModuleData()->m_probabilityModifier < 1)
|
||||
{
|
||||
DEBUG_CRASH(("ProbabilityModifer must be >= 1.\n"));
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SlowDeathBehavior::~SlowDeathBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int SlowDeathBehavior::getProbabilityModifier( const DamageInfo *damageInfo ) const
|
||||
{
|
||||
// Calculating how far past dead we were allows us to pick more spectacular deaths when
|
||||
// severly killed, and more sedate ones when only slightly killed.
|
||||
// eg ( 200 hp max, had 10 left, took 50 damage, 40 overkill, (40/200) * 100 = 20 overkill %)
|
||||
Int overkillDamage = damageInfo->out.m_actualDamageDealt - damageInfo->out.m_actualDamageClipped;
|
||||
Real overkillPercent = (float)overkillDamage / (float)getObject()->getBodyModule()->getMaxHealth();
|
||||
Int overkillModifier = overkillPercent * getSlowDeathBehaviorModuleData()->m_modifierBonusPerOverkillPercent;
|
||||
|
||||
return max( getSlowDeathBehaviorModuleData()->m_probabilityModifier + overkillModifier, 1 );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void calcRandomForce(Real minMag, Real maxMag, Real minPitch, Real maxPitch, Coord3D& force)
|
||||
{
|
||||
Real angle = GameLogicRandomValueReal(-PI, PI);
|
||||
Real pitch = GameLogicRandomValueReal(minPitch, maxPitch);
|
||||
Real mag = GameLogicRandomValueReal(minMag, maxMag);
|
||||
|
||||
Matrix3D mtx(1);
|
||||
mtx.Scale(mag);
|
||||
mtx.Rotate_Z(angle);
|
||||
mtx.Rotate_Y(-pitch);
|
||||
|
||||
Vector3 v = mtx.Get_X_Vector();
|
||||
|
||||
force.x = v.X;
|
||||
force.y = v.Y;
|
||||
force.z = v.Z;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SlowDeathBehavior::beginSlowDeath(const DamageInfo *damageInfo)
|
||||
{
|
||||
if (!isSlowDeathActivated())
|
||||
{
|
||||
const SlowDeathBehaviorModuleData* d = getSlowDeathBehaviorModuleData();
|
||||
Object* obj = getObject();
|
||||
|
||||
if (d->m_sinkRate && obj->isKindOf(KINDOF_INFANTRY))
|
||||
{
|
||||
|
||||
Drawable *draw = getObject()->getDrawable();
|
||||
if ( draw )
|
||||
{
|
||||
// this object sinks slowly after it dies so don't draw a
|
||||
// floating shadow decal on the ground above it.
|
||||
obj->getDrawable()->setShadowsEnabled(false);
|
||||
draw->setTerrainDecalFadeTarget( 0.0f, -0.2f );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Ask game detail manager if we need to speedup all deaths to improve performance
|
||||
Real timeScale = TheGameLODManager->getSlowDeathScale();
|
||||
m_acceleratedTimeScale = 1.0f; // assume normal death speed.
|
||||
|
||||
if (timeScale == 0.0f && !d->hasNonLodEffects())
|
||||
{
|
||||
// Deaths happen instantly so just delete the object and return
|
||||
TheGameLogic->destroyObject(obj);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timescale is some non-zero value so we may need to speed up death
|
||||
if( getObject()->isKindOf( KINDOF_HULK ) && TheGameLogic->getHulkMaxLifetimeOverride() != -1 )
|
||||
{
|
||||
//Scripts don't want hulks around, so start sinking immediately!
|
||||
m_sinkFrame = 1;
|
||||
m_midpointFrame = (LOGICFRAMES_PER_SECOND/2) + 1;
|
||||
m_destructionFrame = LOGICFRAMES_PER_SECOND + 1;
|
||||
m_acceleratedTimeScale = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sinkFrame = timeScale * (d->m_sinkDelay + GameLogicRandomValue(0, d->m_sinkDelayVariance));
|
||||
m_destructionFrame = timeScale * (d->m_destructionDelay + GameLogicRandomValue(0, d->m_destructionDelayVariance));
|
||||
m_midpointFrame = GameLogicRandomValue( BEGIN_MIDPOINT_RATIO * m_destructionFrame, END_MIDPOINT_RATIO * m_destructionFrame );
|
||||
m_acceleratedTimeScale = timeScale;
|
||||
}
|
||||
}
|
||||
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
if (d->m_flingForce > 0)
|
||||
{
|
||||
|
||||
|
||||
//Just in case this is a stingersoldier or other HELD object, lets set them free so they will fly
|
||||
// with their own physics during slow death
|
||||
if( obj->isDisabledByType( DISABLED_HELD ) )
|
||||
{
|
||||
static NameKeyType key_SlavedUpdate = NAMEKEY( "SlavedUpdate" );
|
||||
SlavedUpdate* slave = (SlavedUpdate*)obj->findUpdateModule( key_SlavedUpdate );
|
||||
if( slave )
|
||||
{
|
||||
slave->onSlaverDie( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsBehavior* physics = obj->getPhysics();
|
||||
if (physics)
|
||||
{
|
||||
// make sure we are at least a bit above the ground
|
||||
const Real MIN_ALTITUDE = 1.0f;
|
||||
Real altitude = obj->getHeightAboveTerrain();
|
||||
if (altitude < MIN_ALTITUDE)
|
||||
{
|
||||
Coord3D pos = *obj->getPosition();
|
||||
pos.z += MIN_ALTITUDE;
|
||||
obj->setPosition(&pos);
|
||||
}
|
||||
|
||||
Coord3D force;
|
||||
calcRandomForce(d->m_flingForce, d->m_flingForce + d->m_flingForceVariance,
|
||||
d->m_flingPitch, d->m_flingPitch + d->m_flingPitchVariance, force);
|
||||
physics->setAllowToFall(true);
|
||||
physics->applyForce(&force);
|
||||
physics->setExtraBounciness(-1.0); // we don't want this guy to bounce at all
|
||||
physics->setExtraFriction(-3 * SECONDS_PER_LOGICFRAME_REAL); // reduce his ground friction a bit
|
||||
physics->setAllowBouncing(true);
|
||||
Real orientation = atan2(force.y, force.x);
|
||||
physics->setAngles(orientation, 0, 0);
|
||||
obj->getDrawable()->setModelConditionState(MODELCONDITION_EXPLODED_FLAILING);
|
||||
m_flags |= (1<<FLUNG_INTO_AIR);
|
||||
}
|
||||
setWakeFrame(obj, UPDATE_SLEEP_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't need to wake up immediately, but only when the first of these
|
||||
// counters wants to trigger....
|
||||
Int whenToWakeTime = m_sinkFrame;
|
||||
if (whenToWakeTime > m_destructionFrame)
|
||||
whenToWakeTime = m_destructionFrame;
|
||||
if (whenToWakeTime > m_midpointFrame)
|
||||
whenToWakeTime = m_midpointFrame;
|
||||
setWakeFrame(obj, UPDATE_SLEEP(whenToWakeTime));
|
||||
}
|
||||
m_sinkFrame += now;
|
||||
m_destructionFrame += now;
|
||||
m_midpointFrame += now;
|
||||
|
||||
m_flags |= (1<<SLOW_DEATH_ACTIVATED);
|
||||
|
||||
doPhaseStuff(SDPHASE_INITIAL);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SlowDeathBehavior::doPhaseStuff(SlowDeathPhaseType sdphase)
|
||||
{
|
||||
const SlowDeathBehaviorModuleData* d = getSlowDeathBehaviorModuleData();
|
||||
Int idx, listSize;
|
||||
|
||||
if (!d->m_maskOfLoadedEffects)
|
||||
return; //has no ocl, fx, or weapons.
|
||||
|
||||
listSize = d->m_fx[sdphase].size();
|
||||
if (listSize > 0)
|
||||
{
|
||||
idx = GameLogicRandomValue(0, listSize-1);
|
||||
const FXListVec& v = d->m_fx[sdphase];
|
||||
DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
|
||||
const FXList* fxl = v[idx];
|
||||
FXList::doFXObj(fxl, getObject(), NULL);
|
||||
}
|
||||
|
||||
listSize = d->m_ocls[sdphase].size();
|
||||
if (listSize > 0)
|
||||
{
|
||||
idx = GameLogicRandomValue(0, listSize-1);
|
||||
const OCLVec& v = d->m_ocls[sdphase];
|
||||
DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
|
||||
const ObjectCreationList* ocl = v[idx];
|
||||
ObjectCreationList::create(ocl, getObject(), NULL);
|
||||
}
|
||||
|
||||
listSize = d->m_weapons[sdphase].size();
|
||||
if (listSize > 0)
|
||||
{
|
||||
idx = GameLogicRandomValue(0, listSize-1);
|
||||
const WeaponTemplateVec& v = d->m_weapons[sdphase];
|
||||
DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
|
||||
const WeaponTemplate* wt = v[idx];
|
||||
if (wt)
|
||||
{
|
||||
TheWeaponStore->createAndFireTempWeapon(wt, getObject(), getObject()->getPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime SlowDeathBehavior::update()
|
||||
{
|
||||
//DEBUG_LOG(("updating SlowDeathBehavior %08lx\n",this));
|
||||
DEBUG_ASSERTCRASH(isSlowDeathActivated(), ("hmm, this should not be possible"));
|
||||
|
||||
const SlowDeathBehaviorModuleData* d = getSlowDeathBehaviorModuleData();
|
||||
Object* obj = getObject();
|
||||
|
||||
Real timeScale = TheGameLODManager->getSlowDeathScale();
|
||||
|
||||
// Check if we have normal time scale but LODManager is requeseting acceleration
|
||||
if (timeScale != 1.0f && m_acceleratedTimeScale == 1.0f && !d->hasNonLodEffects())
|
||||
{
|
||||
// speed of deaths has been increased since beginning of death
|
||||
// so adjust it to current levels.
|
||||
if (timeScale == 0)
|
||||
{
|
||||
// instant death
|
||||
TheGameLogic->destroyObject(obj);
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
m_sinkFrame = (Real)m_sinkFrame * timeScale;
|
||||
m_midpointFrame = (Real)m_midpointFrame * timeScale;
|
||||
m_destructionFrame = (Real)m_destructionFrame * timeScale;
|
||||
m_acceleratedTimeScale = timeScale;
|
||||
};
|
||||
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
|
||||
if ((m_flags & (1<<FLUNG_INTO_AIR)) != 0)
|
||||
{
|
||||
if ((m_flags & (1<<BOUNCED)) == 0)
|
||||
{
|
||||
++m_sinkFrame;
|
||||
++m_midpointFrame;
|
||||
++m_destructionFrame;
|
||||
if (!obj->isAboveTerrain())
|
||||
{
|
||||
obj->clearAndSetModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_EXPLODED_FLAILING),
|
||||
MAKE_MODELCONDITION_MASK(MODELCONDITION_EXPLODED_BOUNCING));
|
||||
m_flags |= (1<<BOUNCED);
|
||||
}
|
||||
|
||||
// Here we want to make sure we die if we collide with a tree on the way down
|
||||
PhysicsBehavior *phys = obj->getPhysics();
|
||||
if ( phys )
|
||||
{
|
||||
ObjectID treeID = phys->getLastCollidee();
|
||||
Object *tree = TheGameLogic->findObjectByID( treeID );
|
||||
if ( tree )
|
||||
{
|
||||
if (tree->isKindOf( KINDOF_SHRUBBERY ) )
|
||||
{
|
||||
obj->setDisabled( DISABLED_HELD );
|
||||
obj->clearModelConditionFlags( MAKE_MODELCONDITION_MASK(MODELCONDITION_EXPLODED_FLAILING) );
|
||||
obj->clearModelConditionFlags( MAKE_MODELCONDITION_MASK(MODELCONDITION_EXPLODED_BOUNCING) );
|
||||
obj->setModelConditionFlags( MAKE_MODELCONDITION_MASK(MODELCONDITION_PARACHUTING) ); //looks like he is snagged in a tree
|
||||
obj->setPositionZ( obj->getPosition()->z - (d->m_sinkRate * 50.0f) );// make him sink faster
|
||||
if ( !obj->isAboveTerrain() )
|
||||
TheGameLogic->destroyObject(obj);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( (now >= m_sinkFrame && d->m_sinkRate > 0.0f) )
|
||||
{
|
||||
// disable Physics (if any) so that we can control the sink...
|
||||
obj->setDisabled( DISABLED_HELD );
|
||||
Coord3D pos = *obj->getPosition();
|
||||
pos.z -= d->m_sinkRate / m_acceleratedTimeScale;
|
||||
obj->setPosition( &pos );
|
||||
}
|
||||
|
||||
if( now >= m_midpointFrame && (m_flags & (1<<MIDPOINT_EXECUTED)) == 0 )
|
||||
{
|
||||
doPhaseStuff(SDPHASE_MIDPOINT);
|
||||
m_flags |= (1<<MIDPOINT_EXECUTED);
|
||||
}
|
||||
|
||||
if (now >= m_destructionFrame)
|
||||
{
|
||||
doPhaseStuff(SDPHASE_FINAL);
|
||||
TheGameLogic->destroyObject(obj);
|
||||
}
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SlowDeathBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
|
||||
AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
|
||||
if (ai)
|
||||
{
|
||||
// has another AI already handled us. (hopefully another SlowDeathBehavior)
|
||||
if (ai->isAiInDeadState())
|
||||
return;
|
||||
ai->markAsDead();
|
||||
}
|
||||
|
||||
// deselect this unit for all players.
|
||||
TheGameLogic->deselectObject(getObject(), PLAYERMASK_ALL, TRUE);
|
||||
|
||||
Int total = 0;
|
||||
for (BehaviorModule** update = getObject()->getBehaviorModules(); *update; ++update)
|
||||
{
|
||||
SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface();
|
||||
if (sdu != NULL && sdu->isDieApplicable(damageInfo))
|
||||
{
|
||||
total += sdu->getProbabilityModifier( damageInfo );
|
||||
}
|
||||
}
|
||||
DEBUG_ASSERTCRASH(total > 0, ("Hmm, this is wrong"));
|
||||
|
||||
|
||||
// this returns a value from 1...total, inclusive
|
||||
Int roll = GameLogicRandomValue(1, total);
|
||||
|
||||
for (/* UpdateModuleInterface** */ update = getObject()->getBehaviorModules(); *update; ++update)
|
||||
{
|
||||
SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface();
|
||||
if (sdu != NULL && sdu->isDieApplicable(damageInfo))
|
||||
{
|
||||
roll -= sdu->getProbabilityModifier( damageInfo );
|
||||
if (roll <= 0)
|
||||
{
|
||||
sdu->beginSlowDeath(damageInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("We should never get here"));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SlowDeathBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SlowDeathBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// sink frame
|
||||
xfer->xferUnsignedInt( &m_sinkFrame );
|
||||
|
||||
// midpoint frame
|
||||
xfer->xferUnsignedInt( &m_midpointFrame );
|
||||
|
||||
// destruction frame
|
||||
xfer->xferUnsignedInt( &m_destructionFrame );
|
||||
|
||||
// accelerated time scale
|
||||
xfer->xferReal( &m_acceleratedTimeScale );
|
||||
|
||||
// flags
|
||||
xfer->xferUnsignedInt( &m_flags );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SlowDeathBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SupplyWarehouseCripplingBehavior.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, Septemmber 2002
|
||||
// Desc: Behavior that Disables the building on ReallyDamaged edge state, and manages an Update timer to heal
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/SupplyWarehouseCripplingBehavior.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyWarehouseCripplingBehaviorModuleData::SupplyWarehouseCripplingBehaviorModuleData()
|
||||
{
|
||||
m_selfHealSupression = 0; ///< Time since last damage until I can start to heal
|
||||
m_selfHealDelay = 0; ///< Once I am okay to heal, how often to do so
|
||||
m_selfHealAmount = 0; ///< And how much
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void SupplyWarehouseCripplingBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "SelfHealSupression", INI::parseDurationUnsignedInt, NULL, offsetof(SupplyWarehouseCripplingBehaviorModuleData, m_selfHealSupression) },
|
||||
{ "SelfHealDelay", INI::parseDurationUnsignedInt, NULL, offsetof(SupplyWarehouseCripplingBehaviorModuleData, m_selfHealDelay) },
|
||||
{ "SelfHealAmount", INI::parseReal, NULL, offsetof(SupplyWarehouseCripplingBehaviorModuleData, m_selfHealAmount) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
UpdateModuleData::buildFieldParse(p);
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyWarehouseCripplingBehavior::SupplyWarehouseCripplingBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
|
||||
{
|
||||
m_healingSupressedUntilFrame = 0;
|
||||
m_nextHealingFrame = 0;
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyWarehouseCripplingBehavior::~SupplyWarehouseCripplingBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Damage has been dealt, this is an opportunity to react to that damage */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::onDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
resetSelfHealSupression();
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP(m_healingSupressedUntilFrame - now));// we got hit, time to get up for work after a quick snooze
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::onBodyDamageStateChange(const DamageInfo* damageInfo, BodyDamageType oldState, BodyDamageType newState)
|
||||
{
|
||||
if( newState == BODY_REALLYDAMAGED )
|
||||
startCrippledEffects();
|
||||
else if( oldState == BODY_REALLYDAMAGED )
|
||||
stopCrippledEffects();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime SupplyWarehouseCripplingBehavior::update()
|
||||
{
|
||||
// Supression is handled by sleeping the module, so if I am here, I know it is time to heal.
|
||||
const SupplyWarehouseCripplingBehaviorModuleData* md = getSupplyWarehouseCripplingBehaviorModuleData();
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
m_nextHealingFrame = now + md->m_selfHealDelay;
|
||||
|
||||
getObject()->attemptHealing(md->m_selfHealAmount, NULL);
|
||||
|
||||
if( getObject()->getBodyModule()->getHealth() == getObject()->getBodyModule()->getMaxHealth() )
|
||||
return UPDATE_SLEEP_FOREVER;// this can't be in onHealing, as the healing comes from here
|
||||
// in the update, and sleep settings in onHealing would be overridden by my return value.
|
||||
|
||||
// Delay between heals is also handled by sleeping the module. How cool is that?
|
||||
return UPDATE_SLEEP(m_nextHealingFrame - now);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::resetSelfHealSupression()
|
||||
{
|
||||
const SupplyWarehouseCripplingBehaviorModuleData* md = getSupplyWarehouseCripplingBehaviorModuleData();
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
m_healingSupressedUntilFrame = now + md->m_selfHealSupression;
|
||||
m_nextHealingFrame = m_healingSupressedUntilFrame;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::startCrippledEffects()
|
||||
{
|
||||
DockUpdateInterface *myDock = getObject()->getDockUpdateInterface();
|
||||
myDock->setDockCrippled( TRUE );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::stopCrippledEffects()
|
||||
{
|
||||
DockUpdateInterface *myDock = getObject()->getDockUpdateInterface();
|
||||
myDock->setDockCrippled( FALSE );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// healing supressed until frame
|
||||
xfer->xferUnsignedInt( &m_healingSupressedUntilFrame );
|
||||
|
||||
// next healing frame
|
||||
xfer->xferUnsignedInt( &m_nextHealingFrame );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCripplingBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: TechBuildingBehavior.cpp /////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, October 2002
|
||||
// Desc: Tech building basic behavior
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.H"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/Module/TechBuildingBehavior.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
TechBuildingBehaviorModuleData::TechBuildingBehaviorModuleData( void )
|
||||
{
|
||||
m_pulseFX = NULL;
|
||||
m_pulseFXRate = 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void TechBuildingBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
UpdateModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "PulseFX", INI::parseFXList, NULL, offsetof( TechBuildingBehaviorModuleData, m_pulseFX ) },
|
||||
{ "PulseFXRate", INI::parseDurationUnsignedInt, NULL, offsetof( TechBuildingBehaviorModuleData, m_pulseFXRate ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
TechBuildingBehavior::TechBuildingBehavior( Thing *thing, const ModuleData *modData )
|
||||
: UpdateModule( thing, modData )
|
||||
{
|
||||
|
||||
//
|
||||
// setup ourselves so we do at least one update evaluation after the module
|
||||
// is in the world
|
||||
//
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
TechBuildingBehavior::~TechBuildingBehavior( void )
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime TechBuildingBehavior::update( void )
|
||||
{
|
||||
Object *us = getObject();
|
||||
const TechBuildingBehaviorModuleData* d = getTechBuildingBehaviorModuleData();
|
||||
Bool captured = false;
|
||||
|
||||
// update our model condition for the captured status
|
||||
Player *player = us->getControllingPlayer();
|
||||
if( player && player->isPlayableSide() )
|
||||
{
|
||||
us->setModelConditionState( MODELCONDITION_CAPTURED );
|
||||
captured = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
us->clearModelConditionState( MODELCONDITION_CAPTURED );
|
||||
captured = false;
|
||||
}
|
||||
|
||||
// if we have a pulse fx, and are owned, sleep only a little while, otherwise sleep forever
|
||||
if (d->m_pulseFX != NULL && d->m_pulseFXRate > 0 && captured)
|
||||
{
|
||||
FXList::doFXObj( d->m_pulseFX, us );
|
||||
return UPDATE_SLEEP(d->m_pulseFXRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// now sleep forever my dear
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TechBuildingBehavior::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
|
||||
//
|
||||
// put us on the team of the neutral player so no player has any bonus from us
|
||||
//
|
||||
Object *us = getObject();
|
||||
us->setTeam( ThePlayerList->getNeutralPlayer()->getDefaultTeam() );
|
||||
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TechBuildingBehavior::onCapture( Player *oldOwner, Player *newOwner )
|
||||
{
|
||||
|
||||
// wake up next frame so we can re-evaluate our captured status
|
||||
setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
|
||||
|
||||
} // end onCapture
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TechBuildingBehavior::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TechBuildingBehavior::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TechBuildingBehavior::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
1312
Generals/Code/GameEngine/Source/GameLogic/Object/Body/ActiveBody.cpp
Normal file
1312
Generals/Code/GameEngine/Source/GameLogic/Object/Body/ActiveBody.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BodyModule.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, September 2002
|
||||
// Desc: BodyModule base class
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BodyModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// call base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BodyModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// call base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
// damage scalar
|
||||
xfer->xferReal( &m_damageScalar );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BodyModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// call base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: HighlanderBody.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, November 2002
|
||||
// Desc: Takes damage according to armor, but can't die from normal damage. Can die from Unresistable though
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameLogic/Module/HighlanderBody.h"
|
||||
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HighlanderBody::HighlanderBody( Thing *thing, const ModuleData* moduleData )
|
||||
: ActiveBody( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HighlanderBody::~HighlanderBody( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HighlanderBody::attemptDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
// Bind to one hitpoint remaining afterwards, unless it is Unresistable damage
|
||||
if( damageInfo->in.m_damageType != DAMAGE_UNRESISTABLE )
|
||||
damageInfo->in.m_amount = min( damageInfo->in.m_amount, getHealth() - 1 );
|
||||
|
||||
ActiveBody::attemptDamage(damageInfo);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HighlanderBody::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
ActiveBody::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HighlanderBody::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
ActiveBody::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HighlanderBody::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
ActiveBody::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: HiveStructureBody.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Desc: Hive structure bodies are structure bodies with the ability to propagate specified
|
||||
// damage types to slaves when available. If there are no slaves, the the structure
|
||||
// will take the damage.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
|
||||
#include "GameLogic/Module/HiveStructureBody.h"
|
||||
#include "GameLogic/Module/SpawnBehavior.h"
|
||||
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HiveStructureBodyModuleData::HiveStructureBodyModuleData()
|
||||
{
|
||||
m_damageTypesToPropagateToSlaves = DAMAGE_TYPE_FLAGS_NONE;
|
||||
m_damageTypesToSwallow = DAMAGE_TYPE_FLAGS_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HiveStructureBody::HiveStructureBody( Thing *thing, const ModuleData* moduleData )
|
||||
: StructureBody( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HiveStructureBody::~HiveStructureBody( void )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
void HiveStructureBody::attemptDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
const HiveStructureBodyModuleData *data = getHiveStructureBodyModuleData();
|
||||
Object *hive = getObject();
|
||||
|
||||
if( getDamageTypeFlag( data->m_damageTypesToPropagateToSlaves, damageInfo->in.m_damageType ) )
|
||||
{
|
||||
//We have the right type of damage types incoming to propagate to slaves. Do we have slaves?
|
||||
SpawnBehaviorInterface *spawnInterface = hive->getSpawnBehaviorInterface();
|
||||
if( spawnInterface )
|
||||
{
|
||||
//We found the spawn interface, now get some slaves!
|
||||
Object *shooter = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
if( shooter )
|
||||
{
|
||||
Object *slave = spawnInterface->getClosestSlave( shooter->getPosition() );
|
||||
if( slave )
|
||||
{
|
||||
//Propagate damage and return!
|
||||
slave->attemptDamage( damageInfo );
|
||||
return;
|
||||
}
|
||||
else if( getDamageTypeFlag( data->m_damageTypesToSwallow, damageInfo->in.m_damageType ) )
|
||||
{
|
||||
//no slave to give to, so eat it
|
||||
damageInfo->out.m_actualDamageDealt = 0.0f;
|
||||
damageInfo->out.m_actualDamageClipped = 0.0f;
|
||||
damageInfo->out.m_noEffect = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH( ("%s has a HiveStructureBody module, which requires a SpawnBehavior module. Thus it is unable to propagate damage to slaves.", hive->getTemplate()->getName().str() ) );
|
||||
}
|
||||
}
|
||||
|
||||
//Nothing to propagate (either different damage type or no slaves),
|
||||
//so damage me instead!
|
||||
StructureBody::attemptDamage( damageInfo );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
void HiveStructureBody::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend parent class
|
||||
StructureBody::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HiveStructureBody::xfer( Xfer *xfer )
|
||||
{
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// parent class
|
||||
StructureBody::xfer( xfer );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
void HiveStructureBody::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend parent class
|
||||
StructureBody::loadPostProcess();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ImmortalBody.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, April 2002
|
||||
// Desc: Just like Active Body, but won't let health drop below 1
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/Module/ImmortalBody.h"
|
||||
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ImmortalBody::ImmortalBody( Thing *thing, const ModuleData* moduleData )
|
||||
: ActiveBody( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ImmortalBody::~ImmortalBody( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ImmortalBody::internalChangeHealth( Real delta )
|
||||
{
|
||||
// Don't let anything changes us to below one hit point
|
||||
delta = max( delta, -getHealth() + 1 );
|
||||
|
||||
// extend functionality, but I go first because I can't let you die and then fix it, I must prevent
|
||||
ActiveBody::internalChangeHealth( delta );
|
||||
|
||||
// nothing -- never mark it as dead.
|
||||
DEBUG_ASSERTCRASH( (getHealth() > 0 && !getObject()->isEffectivelyDead() ), ("Immortal objects should never get marked as dead!"));
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ImmortalBody::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
ActiveBody::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ImmortalBody::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
ActiveBody::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ImmortalBody::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
ActiveBody::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: InactiveBody.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: An Inactive body module doesn't have any of the data storage for
|
||||
// health and damage etc ... it's an "Inactive" object that isn't
|
||||
// affected by matters of the body ... it's all in the mind!!!!
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/InactiveBody.h"
|
||||
#include "GameLogic/Module/DieModule.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
InactiveBody::InactiveBody( Thing *thing, const ModuleData* moduleData )
|
||||
: BodyModule( thing, moduleData ), m_dieCalled(false)
|
||||
{
|
||||
getObject()->setEffectivelyDead(true);
|
||||
} // end InactiveBody
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
InactiveBody::~InactiveBody( void )
|
||||
{
|
||||
|
||||
} // end ~InactiveBody
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real InactiveBody::estimateDamage( DamageInfoInput& damageInfo ) const
|
||||
{
|
||||
// Inactive bodies have no health so no damage can really be done
|
||||
Real amount = 0.0f;
|
||||
|
||||
// exception!
|
||||
if (damageInfo.m_damageType == DAMAGE_UNRESISTABLE)
|
||||
{
|
||||
amount = damageInfo.m_amount;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::attemptDamage( DamageInfo *damageInfo )
|
||||
{
|
||||
if( damageInfo == NULL )
|
||||
return;
|
||||
|
||||
if( damageInfo->in.m_damageType == DAMAGE_HEALING )
|
||||
{
|
||||
// Healing and Damage are separate, so this shouldn't happen
|
||||
attemptHealing( damageInfo );
|
||||
return;
|
||||
}
|
||||
|
||||
// Inactive bodies have no health so no damage can really be done
|
||||
damageInfo->out.m_actualDamageDealt = 0.0f;
|
||||
damageInfo->out.m_actualDamageClipped = 0.0f;
|
||||
damageInfo->out.m_noEffect = true;
|
||||
|
||||
// exception: damage type KILL always wipes us out
|
||||
if (damageInfo->in.m_damageType == DAMAGE_UNRESISTABLE)
|
||||
{
|
||||
|
||||
DEBUG_ASSERTCRASH(!getObject()->getTemplate()->isPrerequisite(), ("Prerequisites should not have InactiveBody"));
|
||||
|
||||
damageInfo->out.m_noEffect = false;
|
||||
|
||||
// since we have no Health, we do not call DamageModules, nor do DamageFX.
|
||||
// however, we DO process DieModules.
|
||||
if (!m_dieCalled)
|
||||
{
|
||||
getObject()->onDie( damageInfo );
|
||||
m_dieCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::attemptHealing( DamageInfo *damageInfo )
|
||||
{
|
||||
if( damageInfo == NULL )
|
||||
return;
|
||||
|
||||
if( damageInfo->in.m_damageType != DAMAGE_HEALING )
|
||||
{
|
||||
// Healing and Damage are separate, so this shouldn't happen
|
||||
attemptDamage( damageInfo );
|
||||
return;
|
||||
}
|
||||
|
||||
// Inactive bodies have no health so no damage can really be done
|
||||
damageInfo->out.m_actualDamageDealt = 0.0f;
|
||||
damageInfo->out.m_actualDamageClipped = 0.0f;
|
||||
damageInfo->out.m_noEffect = true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::internalChangeHealth( Real delta )
|
||||
{
|
||||
|
||||
// Inactive bodies have no health to increase or decrease
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real InactiveBody::getHealth() const
|
||||
{
|
||||
|
||||
// Inactive bodies have no health to get
|
||||
return 0.0f;
|
||||
|
||||
} // end getHealth
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BodyDamageType InactiveBody::getDamageState() const
|
||||
{
|
||||
return BODY_PRISTINE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::setDamageState( BodyDamageType ) ///< control damage state directly. Will adjust hitpoints.
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BodyModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// base class
|
||||
BodyModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InactiveBody::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BodyModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: StructureBody.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Structure bodies are active bodies specifically for structures that are built
|
||||
// and/or interactable (is that a world) with the player.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/Module/StructureBody.h"
|
||||
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
StructureBody::StructureBody( Thing *thing, const ModuleData* moduleData )
|
||||
: ActiveBody( thing, moduleData )
|
||||
{
|
||||
|
||||
m_constructorObjectID = INVALID_ID;
|
||||
|
||||
} // end StructureBody
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
StructureBody::~StructureBody( void )
|
||||
{
|
||||
|
||||
} // end ~StructureBody
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void StructureBody::setConstructorObject( Object *obj )
|
||||
{
|
||||
|
||||
if( obj )
|
||||
m_constructorObjectID = obj->getID();
|
||||
|
||||
} // end setConstructorObject
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void StructureBody::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
ActiveBody::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void StructureBody::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// base class
|
||||
ActiveBody::xfer( xfer );
|
||||
|
||||
// constructor object id
|
||||
xfer->xferObjectID( &m_constructorObjectID );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void StructureBody::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
ActiveBody::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CollideModule.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, September 2002
|
||||
// Desc: Collide module base class implementations
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INLCUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/CollideModule.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CollideModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CollideModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// call base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CollideModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// call base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ConvertToCarBombCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A crate that gives a level of experience to all within n distance
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Radar.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/ConvertToCarBombCrateCollide.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ConvertToCarBombCrateCollide::ConvertToCarBombCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ConvertToCarBombCrateCollide::~ConvertToCarBombCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ConvertToCarBombCrateCollide::isValidToExecute( const Object *other ) const
|
||||
{
|
||||
if( !CrateCollide::isValidToExecute(other) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( other->isEffectivelyDead() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( other->isKindOf( KINDOF_AIRCRAFT ) || other->isKindOf( KINDOF_BOAT ) )
|
||||
{
|
||||
//Can't make carbombs out of planes and boats!
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( other->getStatusBits() & OBJECT_STATUS_IS_CARBOMB )
|
||||
{
|
||||
return FALSE;// oops, sorry, I'll convert the next one.
|
||||
}
|
||||
|
||||
// Check to see if this other object has a carbomb weapon set that isn't in use.
|
||||
WeaponSetFlags flags;
|
||||
flags.set( WEAPONSET_CARBOMB );
|
||||
const WeaponTemplateSet* set = other->getTemplate()->findWeaponTemplateSet( flags );
|
||||
if( !set )
|
||||
{
|
||||
//This unit has no weapon set!
|
||||
return FALSE;
|
||||
}
|
||||
if( !set->testWeaponSetFlag( WEAPONSET_CARBOMB ) )
|
||||
{
|
||||
//This unit has a weaponset, but the best match code above chose a different
|
||||
//weaponset.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Also make sure that the car isn't already a carbomb!
|
||||
if( other->testWeaponSetFlag( WEAPONSET_CARBOMB ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ConvertToCarBombCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
//Check to make sure that the other object is also the goal object in the AIUpdateInterface
|
||||
//in order to prevent an unintentional conversion simply by having the terrorist walk too close
|
||||
//to it.
|
||||
//Assume ai is valid because CrateCollide::isValidToExecute(other) checks it.
|
||||
Object *obj = getObject();
|
||||
AIUpdateInterface* ai = obj->getAIUpdateInterface();
|
||||
if (ai && ai->getGoalObject() != other)
|
||||
return false;
|
||||
|
||||
other->setWeaponSetFlag( WEAPONSET_CARBOMB );
|
||||
|
||||
FXList::doFXObj( getConvertToCarBombCrateCollideModuleData()->m_fxList, other );
|
||||
|
||||
other->defect( getObject()->getControllingPlayer()->getDefaultTeam(), 0);
|
||||
|
||||
//In order to make things easier for the designers, we are going to transfer the terrorist name
|
||||
//to the car... so the designer can control the car with their scripts.
|
||||
TheScriptEngine->transferObjectName( getObject()->getName(), other );
|
||||
|
||||
//This is kinda special... we will endow our new ride with our vision and shroud range, since we are driving
|
||||
other->setVisionRange(getObject()->getVisionRange());
|
||||
other->setShroudClearingRange(getObject()->getShroudClearingRange());
|
||||
other->setStatus( OBJECT_STATUS_IS_CARBOMB );
|
||||
|
||||
ExperienceTracker *exp = other->getExperienceTracker();
|
||||
if (exp)
|
||||
{
|
||||
exp->setVeterancyLevel(obj->getExperienceTracker()->getVeterancyLevel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
TheRadar->removeObject( other );
|
||||
TheRadar->addObject( other );
|
||||
|
||||
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ConvertToCarBombCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ConvertToCarBombCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ConvertToCarBombCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FILE: ConvertToHijackedVehicleCrateCollide.cpp
|
||||
// Author: Mark Lorenzen, July 2002
|
||||
// Desc: A crate (actually a terrorist - mobile crate) that makes the target vehicle switch
|
||||
// sides, and kills its driver
|
||||
// @todo Needs to set the science of that vehicle (dozer) so still can build same stuff as always
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Radar.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/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/ConvertToHijackedVehicleCrateCollide.h"
|
||||
#include "GameLogic/Module/HijackerUpdate.h"
|
||||
#include "GameLogic/Module/ContainModule.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
#include "GameLogic/Module/DozerAIUpdate.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ConvertToHijackedVehicleCrateCollide::ConvertToHijackedVehicleCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ConvertToHijackedVehicleCrateCollide::~ConvertToHijackedVehicleCrateCollide( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ConvertToHijackedVehicleCrateCollide::isValidToExecute( const Object *other ) const
|
||||
{
|
||||
if( !CrateCollide::isValidToExecute(other) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( other->isEffectivelyDead() )
|
||||
{
|
||||
return FALSE;// can't hijack a dead vehicle
|
||||
}
|
||||
|
||||
if( other->isKindOf( KINDOF_AIRCRAFT ) || other->isKindOf( KINDOF_BOAT ) )
|
||||
{
|
||||
//Can't hijack planes and boats!
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( other->getStatusBits() & OBJECT_STATUS_HIJACKED )
|
||||
{
|
||||
return FALSE;// oops, sorry, I'll jack the next one.
|
||||
}
|
||||
|
||||
Relationship r = getObject()->getRelationship( other );
|
||||
//Only hijack enemy objects
|
||||
if( r != ENEMIES )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( other->isKindOf( KINDOF_TRANSPORT ) )
|
||||
{
|
||||
//Kris: Allow empty transports to be hijacked.
|
||||
if( other->getContain() && other->getContain()->getContainCount() > 0 )
|
||||
{
|
||||
return FALSE;// dustin sez: do not jack vehicles that may carry hostile passengers
|
||||
}
|
||||
}
|
||||
|
||||
//Kris: Make sure you can't hijack any aircraft (or hijack-enter).
|
||||
if( other->isKindOf( KINDOF_AIRCRAFT ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//VeterancyLevel veterancyLevel = other->getVeterancyLevel();
|
||||
//if( veterancyLevel >= LEVEL_ELITE )
|
||||
//{
|
||||
// return FALSE;
|
||||
//}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ConvertToHijackedVehicleCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
//Check to make sure that the other object is also the goal object in the AIUpdateInterface
|
||||
//in order to prevent an unintentional conversion simply by having the terrorist walk too close
|
||||
//to it.
|
||||
//Assume ai is valid because CrateCollide::isValidToExecute(other) checks it.
|
||||
Object *obj = getObject();
|
||||
AIUpdateInterface* ai = obj->getAIUpdateInterface();
|
||||
if (ai && ai->getGoalObject() != other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TheRadar->tryInfiltrationEvent( other );
|
||||
|
||||
//Before the actual defection takes place, play the "vehicle stolen" EVA
|
||||
//event if the local player is the victim!
|
||||
if( other->isLocallyControlled() )
|
||||
{
|
||||
TheEva->setShouldPlay( EVA_VehicleStolen );
|
||||
}
|
||||
|
||||
other->setTeam( obj->getControllingPlayer()->getDefaultTeam() );
|
||||
other->setStatus( OBJECT_STATUS_HIJACKED );// I claim this car in the name of the GLA
|
||||
|
||||
AIUpdateInterface* targetAI = other->getAIUpdateInterface();
|
||||
targetAI->aiMoveToPosition( other->getPosition(), CMD_FROM_AI );
|
||||
targetAI->aiIdle( CMD_FROM_AI );
|
||||
|
||||
|
||||
//Just in case this target is a dozer, lets make him stop al his dozer tasks, like building and repairing,
|
||||
//So the previous owner does not benefit from these tasks
|
||||
DozerAIInterface * dozerAI = targetAI->getDozerAIInterface();
|
||||
if ( dozerAI )
|
||||
{
|
||||
for (UnsignedInt task = DOZER_TASK_FIRST; task < DOZER_NUM_TASKS; ++task)
|
||||
{
|
||||
dozerAI->cancelTask( (DozerTask)task );
|
||||
}
|
||||
}
|
||||
|
||||
AudioEventRTS hijackEvent( "HijackDriver", obj->getID() );
|
||||
TheAudio->addAudioEvent( &hijackEvent );
|
||||
|
||||
//In order to make things easier for the designers, we are going to transfer the hijacker's name
|
||||
//to the car... so the designer can control the car with their scripts.
|
||||
TheScriptEngine->transferObjectName( obj->getName(), other );
|
||||
|
||||
ExperienceTracker *targetExp = other->getExperienceTracker();
|
||||
ExperienceTracker *jackerExp = obj->getExperienceTracker();
|
||||
if ( targetExp && jackerExp )
|
||||
{
|
||||
VeterancyLevel highestLevel = MAX(targetExp->getVeterancyLevel(),jackerExp->getVeterancyLevel());
|
||||
jackerExp->setVeterancyLevel( highestLevel );
|
||||
targetExp->setVeterancyLevel( highestLevel );
|
||||
}
|
||||
|
||||
|
||||
Bool targetCanEject = FALSE;
|
||||
BehaviorModule **dmi = NULL;
|
||||
for( dmi = other->getBehaviorModules(); *dmi; ++dmi )
|
||||
{
|
||||
if( (*dmi)->getEjectPilotDieInterface() )
|
||||
{
|
||||
targetCanEject = TRUE;
|
||||
break;
|
||||
}
|
||||
} // end for dmi
|
||||
|
||||
if ( ! targetCanEject )
|
||||
{
|
||||
TheGameLogic->destroyObject( obj );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// I we have made it this far, we are going to ride in this vehicle for a while
|
||||
// get the name of the hijackerupdate
|
||||
static NameKeyType key_HijackerUpdate = NAMEKEY( "HijackerUpdate" );
|
||||
HijackerUpdate *hijackerUpdate = (HijackerUpdate*)obj->findUpdateModule( key_HijackerUpdate );
|
||||
if( hijackerUpdate )
|
||||
{
|
||||
hijackerUpdate->setTargetObject( other );
|
||||
hijackerUpdate->setIsInVehicle( TRUE );
|
||||
hijackerUpdate->setUpdate( TRUE );
|
||||
|
||||
// flag bits so hijacker won't be selectible or collideable
|
||||
//while within the vehicle
|
||||
obj->setStatus( OBJECT_STATUS_NO_COLLISIONS );
|
||||
obj->setStatus( OBJECT_STATUS_MASKED );
|
||||
obj->setStatus( OBJECT_STATUS_UNSELECTABLE );
|
||||
}
|
||||
|
||||
// THIS BLOCK HIDES THE HIJACKER AND REMOVES HIM FROM PARTITION MANAGER
|
||||
// remove object from its group (if any)
|
||||
obj->leaveGroup();
|
||||
if( ai )
|
||||
{
|
||||
//By setting him to idle, we will prevent him from entering the target after this gets called.
|
||||
ai->aiIdle( CMD_FROM_AI );
|
||||
}
|
||||
|
||||
//This is kinda special... we will endow our new ride with our vision and shroud range, since we are driving
|
||||
other->setVisionRange(getObject()->getVisionRange());
|
||||
other->setShroudClearingRange(getObject()->getShroudClearingRange());
|
||||
|
||||
// remove rider from partition manager
|
||||
ThePartitionManager->unRegisterObject( obj );
|
||||
|
||||
// hide the drawable associated with rider
|
||||
if( obj->getDrawable() )
|
||||
obj->getDrawable()->setDrawableHidden( true );
|
||||
|
||||
// By returning FALSE, we will not remove the object (Hijacker)
|
||||
return FALSE;
|
||||
// return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ConvertToHijackedVehicleCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ConvertToHijackedVehicleCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ConvertToHijackedVehicleCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: Abstract base Class Crate Collide
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/BitFlagsIO.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Anim2D.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/CrateCollide.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CrateCollideModuleData::CrateCollideModuleData()
|
||||
{
|
||||
m_isForbidOwnerPlayer = FALSE;
|
||||
m_executeAnimationDisplayTimeInSeconds = 0.0f;
|
||||
m_executeAnimationZRisePerSecond = 0.0f;
|
||||
m_executeAnimationFades = TRUE;
|
||||
m_isBuildingPickup = FALSE;
|
||||
m_isHumanOnlyPickup = FALSE;
|
||||
m_executeFX = NULL;
|
||||
m_pickupScience = SCIENCE_INVALID;
|
||||
|
||||
// Added By Sadullah Nader
|
||||
// Initializations missing and needed
|
||||
|
||||
m_executionAnimationTemplate = AsciiString::TheEmptyString;
|
||||
|
||||
// End Add
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CrateCollideModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
ModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "RequiredKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( CrateCollideModuleData, m_kindof ) },
|
||||
{ "ForbiddenKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( CrateCollideModuleData, m_kindofnot ) },
|
||||
{ "ForbidOwnerPlayer", INI::parseBool, NULL, offsetof( CrateCollideModuleData, m_isForbidOwnerPlayer ) },
|
||||
{ "BuildingPickup", INI::parseBool, NULL, offsetof( CrateCollideModuleData, m_isBuildingPickup ) },
|
||||
{ "HumanOnly", INI::parseBool, NULL, offsetof( CrateCollideModuleData, m_isHumanOnlyPickup ) },
|
||||
{ "PickupScience", INI::parseScience, NULL, offsetof( CrateCollideModuleData, m_pickupScience ) },
|
||||
{ "ExecuteFX", INI::parseFXList, NULL, offsetof( CrateCollideModuleData, m_executeFX ) },
|
||||
{ "ExecuteAnimation", INI::parseAsciiString, NULL, offsetof( CrateCollideModuleData, m_executionAnimationTemplate ) },
|
||||
{ "ExecuteAnimationTime", INI::parseReal, NULL, offsetof( CrateCollideModuleData, m_executeAnimationDisplayTimeInSeconds ) },
|
||||
{ "ExecuteAnimationZRise", INI::parseReal, NULL, offsetof( CrateCollideModuleData, m_executeAnimationZRisePerSecond ) },
|
||||
{ "ExecuteAnimationFades", INI::parseBool, NULL, offsetof( CrateCollideModuleData, m_executeAnimationFades ) },
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CrateCollide::CrateCollide( Thing *thing, const ModuleData* moduleData ) : CollideModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CrateCollide::~CrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The collide event.
|
||||
* Note that when other is NULL it means "collide with ground" */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CrateCollide::onCollide( Object *other, const Coord3D *, const Coord3D * )
|
||||
{
|
||||
const CrateCollideModuleData *modData = getCrateCollideModuleData();
|
||||
// If the crate can be picked up, perform the game logic and destroy the crate.
|
||||
if( isValidToExecute( other ) )
|
||||
{
|
||||
if( executeCrateBehavior( other ) )
|
||||
{
|
||||
if( modData->m_executeFX != NULL )
|
||||
{
|
||||
// Note: We pass in other here, because the crate is owned by the neutral player, and
|
||||
// we want to do things that only the other person can see.
|
||||
FXList::doFXObj( modData->m_executeFX, other );
|
||||
}
|
||||
|
||||
TheGameLogic->destroyObject( getObject() );
|
||||
}
|
||||
|
||||
// play animation in the world at this spot if there is one
|
||||
if( TheAnim2DCollection && modData->m_executionAnimationTemplate.isEmpty() == FALSE && TheGameLogic->getDrawIconUI() )
|
||||
{
|
||||
Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( modData->m_executionAnimationTemplate );
|
||||
|
||||
TheInGameUI->addWorldAnimation( animTemplate,
|
||||
getObject()->getPosition(),
|
||||
WORLD_ANIM_FADE_ON_EXPIRE,
|
||||
modData->m_executeAnimationDisplayTimeInSeconds,
|
||||
modData->m_executeAnimationZRisePerSecond );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool CrateCollide::isValidToExecute( const Object *other ) const
|
||||
{
|
||||
//The ground never picks up a crate
|
||||
if( other == NULL )
|
||||
return FALSE;
|
||||
|
||||
//Nothing Neutral can pick up any type of crate
|
||||
if( other->isNeutralControlled() )
|
||||
return FALSE;
|
||||
|
||||
const CrateCollideModuleData* md = getCrateCollideModuleData();
|
||||
Bool validBuildingAttempt = md->m_isBuildingPickup && other->isKindOf( KINDOF_STRUCTURE );
|
||||
|
||||
// Must be a "Unit" type thing. Real Game Object, not just Object
|
||||
if( other->getAIUpdateInterface() == NULL && !validBuildingAttempt )// Building exception flag for Drop Zone
|
||||
return FALSE;
|
||||
|
||||
// must match our kindof flags (if any)
|
||||
if (md && !other->isKindOfMulti(md->m_kindof, md->m_kindofnot))
|
||||
return FALSE;
|
||||
|
||||
if( other->isEffectivelyDead() )
|
||||
return FALSE;
|
||||
|
||||
// crates cannot be claimed while in the air, except by buildings
|
||||
if( getObject()->isAboveTerrain() && !validBuildingAttempt )
|
||||
return FALSE;
|
||||
|
||||
if( md->m_isForbidOwnerPlayer && (getObject()->getControllingPlayer() == other->getControllingPlayer()) )
|
||||
return FALSE; // Design has decreed this to not be picked up by the dead guy's team.
|
||||
|
||||
if( md->m_isHumanOnlyPickup && other->getControllingPlayer() && (other->getControllingPlayer()->getPlayerType() != PLAYER_HUMAN) )
|
||||
return FALSE; // Human only mission crate
|
||||
|
||||
if( (md->m_pickupScience != SCIENCE_INVALID) && other->getControllingPlayer() && !other->getControllingPlayer()->hasScience(md->m_pickupScience) )
|
||||
return FALSE; // Science required to pick this up
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CollideModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CollideModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CollideModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MoneyCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A crate that heals everything owned by the collider
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/HealCrateCollide.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HealCrateCollide::HealCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
HealCrateCollide::~HealCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool HealCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
Player* cratePlayer = other->getControllingPlayer();
|
||||
cratePlayer->healAllObjects();
|
||||
|
||||
//Play a crate pickup sound.
|
||||
AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateHeal;
|
||||
soundToPlay.setPosition( other->getPosition() );
|
||||
TheAudio->addAudioEvent(&soundToPlay);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HealCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HealCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HealCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MoneyCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A crate that gives x money to the collider
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/MoneyCrateCollide.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MoneyCrateCollide::MoneyCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MoneyCrateCollide::~MoneyCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool MoneyCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
UnsignedInt money = getMoneyCrateCollideModuleData()->m_moneyProvided;
|
||||
|
||||
other->getControllingPlayer()->getMoney()->deposit( money );
|
||||
other->getControllingPlayer()->getScoreKeeper()->addMoneyEarned( money );
|
||||
|
||||
//Play a crate pickup sound.
|
||||
AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateMoney;
|
||||
soundToPlay.setObjectID( other->getID() );
|
||||
TheAudio->addAudioEvent(&soundToPlay);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MoneyCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MoneyCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MoneyCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SalvageCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: The Savlage system can give a Weaponset bonus, a level, or money. Salvagers create them
|
||||
// by killing marked units, and only WeaponSalvagers can get the WeaponSet bonus
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "GameLogic/Module/SalvageCrateCollide.h"
|
||||
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Kindof.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SalvageCrateCollide::SalvageCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SalvageCrateCollide::~SalvageCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SalvageCrateCollide::isValidToExecute( const Object *other ) const
|
||||
{
|
||||
if( ! CrateCollide::isValidToExecute( other ) )
|
||||
return FALSE;
|
||||
|
||||
// Only salvage units can pick up a Salvage crate
|
||||
if( ! other->getTemplate()->isKindOf( KINDOF_SALVAGER ) )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SalvageCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
if( eligibleForWeaponSet( other ) && testWeaponChance() )
|
||||
{
|
||||
doWeaponSet( other );
|
||||
|
||||
//Play the salvage installation crate pickup sound.
|
||||
AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateSalvage;
|
||||
soundToPlay.setObjectID( other->getID() );
|
||||
TheAudio->addAudioEvent( &soundToPlay );
|
||||
|
||||
//Play the unit voice acknowledgement for upgrading weapons.
|
||||
//Already handled by the "move order"
|
||||
//const AudioEventRTS *soundToPlayPtr = other->getTemplate()->getPerUnitSound( "VoiceSalvage" );
|
||||
//soundToPlay = *soundToPlayPtr;
|
||||
//soundToPlay.setObjectID( other->getID() );
|
||||
//TheAudio->addAudioEvent( &soundToPlay );
|
||||
}
|
||||
else if( eligibleForLevel( other ) && testLevelChance() )
|
||||
{
|
||||
doLevelGain( other );
|
||||
|
||||
//Sound will play in
|
||||
//soundToPlay = TheAudio->getMiscAudio()->m_unitPromoted;
|
||||
}
|
||||
else // just assume the testMoneyChance
|
||||
{
|
||||
doMoney( other );
|
||||
AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateMoney;
|
||||
soundToPlay.setObjectID( other->getID() );
|
||||
TheAudio->addAudioEvent(&soundToPlay);
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool SalvageCrateCollide::eligibleForWeaponSet( Object *other )
|
||||
{
|
||||
if( other == NULL )
|
||||
return FALSE;
|
||||
|
||||
// A kindof marks eligibility, and you must not be fully upgraded
|
||||
if( !other->isKindOf(KINDOF_WEAPON_SALVAGER) )
|
||||
return FALSE;
|
||||
if( other->testWeaponSetFlag(WEAPONSET_CRATEUPGRADE_TWO) )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool SalvageCrateCollide::eligibleForLevel( Object *other )
|
||||
{
|
||||
if( other == NULL )
|
||||
return FALSE;
|
||||
|
||||
// Sorry, you are max level
|
||||
if( other->getExperienceTracker()->getVeterancyLevel() == LEVEL_HEROIC )
|
||||
return FALSE;
|
||||
|
||||
// Sorry, you can't gain levels
|
||||
if( !other->getExperienceTracker()->isTrainable() )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool SalvageCrateCollide::testWeaponChance()
|
||||
{
|
||||
const SalvageCrateCollideModuleData *md = getSalvageCrateCollideModuleData();
|
||||
if( md->m_weaponChance == 1.0f )
|
||||
return TRUE; // don't waste a random number for a 100%
|
||||
|
||||
Real randomNumber = GameLogicRandomValueReal( 0, 1 );
|
||||
if( randomNumber < md->m_weaponChance )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Bool SalvageCrateCollide::testLevelChance()
|
||||
{
|
||||
const SalvageCrateCollideModuleData *md = getSalvageCrateCollideModuleData();
|
||||
if( md->m_levelChance == 1.0f )
|
||||
return TRUE; // don't waste a random number for a 100%
|
||||
|
||||
Real randomNumber = GameLogicRandomValueReal( 0, 1 );
|
||||
if( randomNumber < md->m_levelChance )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void SalvageCrateCollide::doWeaponSet( Object *other )
|
||||
{
|
||||
if( other->testWeaponSetFlag( WEAPONSET_CRATEUPGRADE_ONE ) )
|
||||
{
|
||||
other->clearWeaponSetFlag( WEAPONSET_CRATEUPGRADE_ONE );
|
||||
other->setWeaponSetFlag( WEAPONSET_CRATEUPGRADE_TWO );
|
||||
}
|
||||
else
|
||||
{
|
||||
other->setWeaponSetFlag( WEAPONSET_CRATEUPGRADE_ONE );
|
||||
}
|
||||
}
|
||||
|
||||
void SalvageCrateCollide::doLevelGain( Object *other )
|
||||
{
|
||||
other->getExperienceTracker()->gainExpForLevel( 1 );
|
||||
}
|
||||
|
||||
void SalvageCrateCollide::doMoney( Object *other )
|
||||
{
|
||||
const SalvageCrateCollideModuleData *md = getSalvageCrateCollideModuleData();
|
||||
|
||||
Int money;
|
||||
if( md->m_minimumMoney != md->m_maximumMoney )// Random value doesn't like to get a constant range
|
||||
money = GameLogicRandomValue( md->m_minimumMoney, md->m_maximumMoney );
|
||||
else
|
||||
money = md->m_minimumMoney;
|
||||
|
||||
if( money > 0 )
|
||||
{
|
||||
other->getControllingPlayer()->getMoney()->deposit( money );
|
||||
other->getControllingPlayer()->getScoreKeeper()->addMoneyEarned( money );
|
||||
|
||||
//Display cash income floating over the crate. Position is me, everything else is them.
|
||||
UnicodeString moneyString;
|
||||
moneyString.format( TheGameText->fetch( "GUI:AddCash" ), money );
|
||||
Coord3D pos;
|
||||
pos.set( getObject()->getPosition() );
|
||||
pos.z += 10.0f; //add a little z to make it show up above the unit.
|
||||
Color color = other->getControllingPlayer()->getPlayerColor() | GameMakeColor( 0, 0, 0, 230 );
|
||||
TheInGameUI->addFloatingText( moneyString, &pos, color );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SalvageCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SalvageCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SalvageCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ShroudCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A crate that clears the shroud for the pickerupper
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/ShroudCrateCollide.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ShroudCrateCollide::ShroudCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ShroudCrateCollide::~ShroudCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ShroudCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
Player* cratePlayer = other->getControllingPlayer();
|
||||
ThePartitionManager->revealMapForPlayer( cratePlayer->getPlayerIndex() );
|
||||
|
||||
//Play a crate pickup sound.
|
||||
AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateShroud;
|
||||
soundToPlay.setObjectID( other->getID() );
|
||||
TheAudio->addAudioEvent(&soundToPlay);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ShroudCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ShroudCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ShroudCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: UnitCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A crate that gives n units of type m the the pickerupper
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/UnitCrateCollide.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UnitCrateCollide::UnitCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UnitCrateCollide::~UnitCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool UnitCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
UnsignedInt unitCount = getUnitCrateCollideModuleData()->m_unitCount;
|
||||
ThingTemplate const *unitType = TheThingFactory->findTemplate( getUnitCrateCollideModuleData()->m_unitType );
|
||||
|
||||
if( unitType == NULL )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for( Int unitIndex = 0; unitIndex < unitCount; unitIndex++ )
|
||||
{
|
||||
Team *creationTeam = other->getControllingPlayer()->getDefaultTeam();
|
||||
Object *newObj = TheThingFactory->newObject( unitType, creationTeam );
|
||||
if( newObj )
|
||||
{
|
||||
Coord3D creationPoint = *other->getPosition();
|
||||
/// @todo As a user of the future findLegalPositionAround, I wouldn't mind not having to specify range. I just want a non colliding point.
|
||||
FindPositionOptions fpOptions;
|
||||
fpOptions.minRadius = 0.0f;
|
||||
fpOptions.maxRadius = 20.0f;
|
||||
ThePartitionManager->findPositionAround( &creationPoint,
|
||||
&fpOptions,
|
||||
&creationPoint );
|
||||
|
||||
newObj->setOrientation( other->getOrientation() );
|
||||
newObj->setPosition( &creationPoint );
|
||||
}
|
||||
}
|
||||
|
||||
//Play a crate pickup sound.
|
||||
AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateFreeUnit;
|
||||
soundToPlay.setObjectID( other->getID() );
|
||||
TheAudio->addAudioEvent(&soundToPlay);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void UnitCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void UnitCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void UnitCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: VeterancyCrateCollide.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A crate that gives a level of experience to all within n distance
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/VeterancyCrateCollide.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
VeterancyCrateCollide::VeterancyCrateCollide( Thing *thing, const ModuleData* moduleData ) : CrateCollide( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
VeterancyCrateCollide::~VeterancyCrateCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int VeterancyCrateCollide::getLevelsToGain() const
|
||||
{
|
||||
const VeterancyCrateCollideModuleData* d = getVeterancyCrateCollideModuleData();
|
||||
if (!d || !d->m_addsOwnerVeterancy)
|
||||
return 1;
|
||||
|
||||
// this requires that "regular" is 0, vet is 1, etc.
|
||||
return (Int)getObject()->getVeterancyLevel();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const
|
||||
{
|
||||
const VeterancyCrateCollideModuleData* d = getVeterancyCrateCollideModuleData();
|
||||
if( !d )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!CrateCollide::isValidToExecute(other))
|
||||
return false;
|
||||
|
||||
if (other->isEffectivelyDead())
|
||||
return false;
|
||||
|
||||
if( other->isSignificantlyAboveTerrain() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Int levelsToGain = getLevelsToGain();
|
||||
if (levelsToGain <= 0)
|
||||
return false;
|
||||
|
||||
const ExperienceTracker *et = other->getExperienceTracker();
|
||||
if( !et || !et->isTrainable() )
|
||||
{
|
||||
//If the other unit can't gain experience, then we can't help promote it!
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!et || !et->canGainExpForLevel(levelsToGain))
|
||||
return false;
|
||||
|
||||
if( d->m_isPilot )
|
||||
{
|
||||
if( other->getControllingPlayer() != getObject()->getControllingPlayer() )
|
||||
{
|
||||
//This is a pilot and we are checking to make sure the pilot is entering a vehicle on
|
||||
//the same team. If it's not, then don't allow it.. this is particularly the case for
|
||||
//pilots attempting to enter civilian vehicles.
|
||||
return false;
|
||||
}
|
||||
|
||||
if( other->isUsingAirborneLocomotor() )
|
||||
{
|
||||
// Can't upgrade a helicopter or plane, but we will think we can for a moment while it
|
||||
// is on the ground from being built.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool VeterancyCrateCollide::executeCrateBehavior( Object *other )
|
||||
{
|
||||
//Make sure the pilot is actually *TRYING* to enter the object
|
||||
//unlike other crates
|
||||
AIUpdateInterface *ai = (AIUpdateInterface*)getObject()->getAIUpdateInterface();
|
||||
const VeterancyCrateCollideModuleData *md = getVeterancyCrateCollideModuleData();
|
||||
|
||||
if( !ai || ai->getGoalObject() != other )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Int levelsToGain = getLevelsToGain();
|
||||
Real range = md->m_rangeOfEffect;
|
||||
if (range == 0)
|
||||
{
|
||||
// do just the collider
|
||||
if (other != NULL)
|
||||
{
|
||||
other->getExperienceTracker()->gainExpForLevel( levelsToGain, ( ! md->m_isPilot) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PartitionFilterSamePlayer othersPlayerFilter( other->getControllingPlayer() );
|
||||
PartitionFilterSameMapStatus filterMapStatus(other);
|
||||
PartitionFilter *filters[] = { &othersPlayerFilter, &filterMapStatus, NULL };
|
||||
ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( other, range, FROM_CENTER_2D, filters, ITER_FASTEST );
|
||||
MemoryPoolObjectHolder hold(iter);
|
||||
|
||||
for( Object *potentialObject = iter->first(); potentialObject; potentialObject = iter->next() )
|
||||
{
|
||||
// This function will give just enough exp for the Object to gain a level, if it can
|
||||
potentialObject->getExperienceTracker()->gainExpForLevel( levelsToGain, ( ! md->m_isPilot) );
|
||||
}
|
||||
}
|
||||
|
||||
//In order to make things easier for the designers, we are going to transfer the terrorist name
|
||||
//to the car... so the designer can control the car with their scripts.
|
||||
if( md->m_isPilot )
|
||||
{
|
||||
TheScriptEngine->transferObjectName( getObject()->getName(), other );
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void VeterancyCrateCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void VeterancyCrateCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CrateCollide::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void VeterancyCrateCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CrateCollide::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: FireWeaponCollide.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood April 2002
|
||||
// Desc: Shoot something that collides with me every frame with my weapon
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// 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 "GameLogic/Module/FireWeaponCollide.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FireWeaponCollideModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
CollideModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "CollideWeapon", INI::parseWeaponTemplate, NULL, offsetof( FireWeaponCollideModuleData, m_collideWeaponTemplate ) },
|
||||
{ "FireOnce", INI::parseBool, NULL, offsetof( FireWeaponCollideModuleData, m_fireOnce ) },
|
||||
{ "RequiredStatus", INI::parseBitString32, TheObjectStatusBitNames, offsetof( FireWeaponCollideModuleData, m_requiredStatus ) },
|
||||
{ "ForbiddenStatus", INI::parseBitString32, TheObjectStatusBitNames, offsetof( FireWeaponCollideModuleData, m_forbiddenStatus ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FireWeaponCollide::FireWeaponCollide( Thing *thing, const ModuleData* moduleData ) :
|
||||
CollideModule( thing, moduleData ),
|
||||
m_collideWeapon(NULL)
|
||||
{
|
||||
m_collideWeapon = TheWeaponStore->allocateNewWeapon(getFireWeaponCollideModuleData()->m_collideWeaponTemplate, PRIMARY_WEAPON);
|
||||
m_everFired = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FireWeaponCollide::~FireWeaponCollide( void )
|
||||
{
|
||||
if (m_collideWeapon)
|
||||
m_collideWeapon->deleteInstance();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FireWeaponCollide::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
|
||||
{
|
||||
if( other == NULL )
|
||||
return; //Don't shoot the ground
|
||||
|
||||
Object *me = getObject();
|
||||
|
||||
// This will fire at you every frame, because multiple people could be colliding and we want
|
||||
// to hurt them all. Another solution would be to keep a Map of other->objetIDs and
|
||||
// delays for each individually. However, this solution here is so quick and simple that it
|
||||
// warrants the "do it eventually if we need it" clause.
|
||||
if( shouldFireWeapon() )
|
||||
{
|
||||
m_collideWeapon->loadAmmoNow( me );
|
||||
m_collideWeapon->fireWeapon( me, other );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool FireWeaponCollide::shouldFireWeapon()
|
||||
{
|
||||
const FireWeaponCollideModuleData *d = getFireWeaponCollideModuleData();
|
||||
|
||||
UnsignedInt status = getObject()->getStatusBits();
|
||||
|
||||
if( (status & d->m_requiredStatus) != d->m_requiredStatus )
|
||||
return FALSE;
|
||||
|
||||
if( (status & d->m_forbiddenStatus) != 0 )
|
||||
return FALSE;
|
||||
|
||||
if( m_everFired && d->m_fireOnce )
|
||||
return FALSE;// can only fire once ever
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CollideModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponCollide::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,
|
||||
("FireWeaponCollide::xfer - m_collideWeapon present mismatch\n") );
|
||||
xfer->xferSnapshot( m_collideWeapon );
|
||||
|
||||
} // end else
|
||||
else
|
||||
{
|
||||
|
||||
DEBUG_ASSERTCRASH( m_collideWeapon == NULL,
|
||||
("FireWeaponCollide::Xfer - m_collideWeapon missing mismatch\n" ));
|
||||
|
||||
} // end else
|
||||
|
||||
// ever fired
|
||||
xfer->xferBool( &m_everFired );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FireWeaponCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CollideModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SquishCollide.cpp //////////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/SquishCollide.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Module/HijackerUpdate.h"
|
||||
#include "GameLogic/Module/SpecialAbilityUpdate.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SquishCollide::SquishCollide( Thing *thing, const ModuleData* moduleData ) : CollideModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SquishCollide::~SquishCollide( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Do the collision */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SquishCollide::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
|
||||
{
|
||||
// Note that other == null means "collide with ground"
|
||||
if (other == NULL)
|
||||
return;
|
||||
|
||||
Object *self = getObject();
|
||||
AIUpdateInterface *ai = self->getAI();
|
||||
if( ai && ai->getGoalObject() == other )
|
||||
{
|
||||
//We are actually targeting the other object to do something to! Don't allow them to crush us in the following
|
||||
//special circumstances:
|
||||
|
||||
//Hijacking!
|
||||
static NameKeyType key_HijackerUpdate = NAMEKEY( "HijackerUpdate" );
|
||||
HijackerUpdate *hijackUpdate = (HijackerUpdate*)self->findUpdateModule( key_HijackerUpdate );
|
||||
if( hijackUpdate )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//TNT hunter placing a charge!
|
||||
SpecialAbilityUpdate *saUpdate = self->findSpecialAbilityUpdate( SPECIAL_TANKHUNTER_TNT_ATTACK );
|
||||
if( saUpdate && saUpdate->isActive() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// order matters: we want to know if IT considers ME to be an ally (a reversal of the usual situation)
|
||||
if( other->getCrusherLevel() > 0 && other->getRelationship(getObject()) != ALLIES)
|
||||
{
|
||||
PhysicsBehavior *otherPhysics = other->getPhysics();
|
||||
if (otherPhysics == NULL)
|
||||
return;
|
||||
|
||||
// use a 1.0 crush radius so the tank has to actually hit the infantry.
|
||||
GeometryInfo myGeom = self->getGeometryInfo();
|
||||
myGeom.setMajorRadius(1.0f);
|
||||
myGeom.setMinorRadius(1.0f);
|
||||
if (!ThePartitionManager->geomCollidesWithGeom(other->getPosition(), other->getGeometryInfo(), other->getOrientation(),
|
||||
self->getPosition(), myGeom, self->getOrientation())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only squish if tank is moving toward victim
|
||||
const Coord3D *vel = otherPhysics->getVelocity();
|
||||
const Coord3D *pos = other->getPosition();
|
||||
const Coord3D *myPos = getObject()->getPosition();
|
||||
Coord3D to;
|
||||
|
||||
to.x = myPos->x - pos->x;
|
||||
to.y = myPos->y - pos->y;
|
||||
to.z = myPos->z - pos->z;
|
||||
|
||||
if (to.x * vel->x + to.y * vel->y > 0.0f)
|
||||
{
|
||||
DamageInfo damageInfo;
|
||||
damageInfo.in.m_damageType = DAMAGE_CRUSH;
|
||||
damageInfo.in.m_deathType = DEATH_CRUSHED;
|
||||
damageInfo.in.m_sourceID = other->getID();
|
||||
damageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT; // make sure they die
|
||||
getObject()->attemptDamage( &damageInfo );
|
||||
getObject()->friend_setUndetectedDefector( FALSE );// My secret is out
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SquishCollide::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CollideModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SquishCollide::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CollideModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SquishCollide::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CollideModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CaveContain.cpp ////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, July 2002
|
||||
// Desc: A version of OpenContain that overrides where the passengers are stored: one of CaveSystem's
|
||||
// entries. Changing entry is a script or ini command. All queries about capacity and
|
||||
// contents are also redirected. They change sides like Garrison too.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Team.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/TunnelTracker.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/CaveContain.h"
|
||||
#include "GameLogic/CaveSystem.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CaveContain::CaveContain( Thing *thing, const ModuleData* moduleData ) : OpenContain( thing, moduleData )
|
||||
{
|
||||
m_needToRunOnBuildComplete = true;
|
||||
m_caveIndex = 0;
|
||||
m_originalTeam = NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CaveContain::~CaveContain()
|
||||
{
|
||||
}
|
||||
|
||||
void CaveContain::addToContainList( Object *obj )
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
myTracker->addToContainList( obj );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Remove 'obj' from the m_containList of objects in this module.
|
||||
* This will trigger an onRemoving event for the object that this module
|
||||
* is a part of and an onRemovedFrom event for the object being removed */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::removeFromContain( Object *obj, Bool exposeStealthUnits )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
//
|
||||
// we can only remove this object from the contains list of this module if
|
||||
// it is actually contained by this module
|
||||
//
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
|
||||
if( ! myTracker->isInContainer( obj ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This must come before the onRemov*, because CaveContain's version has a edge-0 triggered event.
|
||||
// If that were to go first, the number would still be 1 at that time. Noone else cares about
|
||||
// order.
|
||||
myTracker->removeFromContain( obj, exposeStealthUnits );
|
||||
|
||||
// trigger an onRemoving event for 'm_object' no longer containing 'itemToRemove->m_object'
|
||||
if (getObject()->getContain())
|
||||
getObject()->getContain()->onRemoving( obj );
|
||||
|
||||
// trigger an onRemovedFrom event for 'remove'
|
||||
obj->onRemovedFrom( getObject() );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Remove all contained objects from the contained list */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::removeAllContained( Bool exposeStealthUnits )
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
const ContainedItemsList *fullList = myTracker->getContainedItemsList();
|
||||
|
||||
Object *obj;
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = (*fullList).begin();
|
||||
while( it != (*fullList).end() )
|
||||
{
|
||||
obj = *it;
|
||||
it++;
|
||||
removeFromContain( obj, exposeStealthUnits );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Iterate the contained list and call the callback on each of the objects */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
myTracker->iterateContained( func, userData, reverse );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::onContaining( Object *obj )
|
||||
{
|
||||
OpenContain::onContaining(obj);
|
||||
// objects inside a building are held
|
||||
obj->setDisabled( DISABLED_HELD );
|
||||
|
||||
//
|
||||
// the team of the building is now the same as those that have garrisoned it, be sure
|
||||
// to save our original team tho so that we can revert back to it when all the
|
||||
// occupants are gone
|
||||
//
|
||||
recalcApparentControllingPlayer();
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::onRemoving( Object *obj )
|
||||
{
|
||||
OpenContain::onRemoving(obj);
|
||||
// object is no longer held inside a garrisoned building
|
||||
obj->clearDisabled( DISABLED_HELD );
|
||||
|
||||
/// place the object in the world at position of the container m_object
|
||||
ThePartitionManager->registerObject( obj );
|
||||
obj->setPosition( getObject()->getPosition() );
|
||||
if( obj->getDrawable() )
|
||||
{
|
||||
obj->getDrawable()->setDrawableHidden( false );
|
||||
}
|
||||
|
||||
doUnloadSound();
|
||||
|
||||
if( getContainCount() == 0 )
|
||||
{
|
||||
|
||||
// put us back on our original team
|
||||
// (hokey exception: if our team is null, don't bother -- this
|
||||
// usually means we are being called during game-teardown and
|
||||
// the teams are no longer valid...)
|
||||
if (getObject()->getTeam() != NULL)
|
||||
{
|
||||
changeTeamOnAllConnectedCaves( m_originalTeam, FALSE );
|
||||
m_originalTeam = NULL;
|
||||
}
|
||||
|
||||
// change the state back from garrisoned
|
||||
Drawable *draw = getObject()->getDrawable();
|
||||
if( draw )
|
||||
{
|
||||
draw->clearModelConditionState( MODELCONDITION_GARRISONED );
|
||||
}
|
||||
|
||||
} // end if
|
||||
}
|
||||
|
||||
Bool CaveContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
return myTracker->isValidContainerFor( obj, checkCapacity );
|
||||
}
|
||||
|
||||
UnsignedInt CaveContain::getContainCount() const
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
return myTracker->getContainCount();
|
||||
}
|
||||
|
||||
Int CaveContain::getContainMax( void ) const
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
return myTracker->getContainMax();
|
||||
}
|
||||
|
||||
const ContainedItemsList* CaveContain::getContainedItemsList() const
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
return myTracker->getContainedItemsList();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
// override the onDie we inherit from OpenContain. no super call.
|
||||
if (!getCaveContainModuleData()->m_dieMuxData.isDieApplicable(getObject(), damageInfo))
|
||||
return;
|
||||
|
||||
if( BitTest( getObject()->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) )
|
||||
return;//it never registered itself as a tunnel
|
||||
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
|
||||
TheCaveSystem->unregisterCave( m_caveIndex );
|
||||
|
||||
myTracker->onTunnelDestroyed( getObject() );
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::onCreate( void )
|
||||
{
|
||||
m_caveIndex = getCaveContainModuleData()->m_caveIndexData;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::onBuildComplete( void )
|
||||
{
|
||||
if( ! shouldDoOnBuildComplete() )
|
||||
return;
|
||||
|
||||
m_needToRunOnBuildComplete = false;
|
||||
|
||||
TheCaveSystem->registerNewCave( m_caveIndex );
|
||||
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
|
||||
myTracker->onTunnelCreated( getObject() );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::tryToSetCaveIndex( Int newIndex )
|
||||
{
|
||||
if( TheCaveSystem->canSwitchIndexToIndex( m_caveIndex, newIndex ) )
|
||||
{
|
||||
TunnelTracker *myOldTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
TheCaveSystem->unregisterCave( m_caveIndex );
|
||||
myOldTracker->onTunnelDestroyed( getObject() );
|
||||
|
||||
m_caveIndex = newIndex;
|
||||
|
||||
TheCaveSystem->registerNewCave( m_caveIndex );
|
||||
TunnelTracker *myNewTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
myNewTracker->onTunnelCreated( getObject() );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CaveContain::recalcApparentControllingPlayer( void )
|
||||
{
|
||||
//Record original team first time through.
|
||||
if( m_originalTeam == NULL )
|
||||
{
|
||||
m_originalTeam = getObject()->getTeam();
|
||||
}
|
||||
|
||||
// (hokey trick: if our team is null, nuke originalTeam -- this
|
||||
// usually means we are being called during game-teardown and
|
||||
// the teams are no longer valid...)
|
||||
if (getObject()->getTeam() == NULL)
|
||||
m_originalTeam = NULL;
|
||||
|
||||
// This is called from onContaining, so a one is the edge trigger to do capture stuff
|
||||
if( getContainCount() == 1 )
|
||||
{
|
||||
ContainedItemsList::const_iterator it = getContainedItemsList()->begin();
|
||||
Object *rider = *it;
|
||||
|
||||
// This also gets called during reset from the PlayerList, so we might not actually have players.
|
||||
// Check like the hokey trick mentioned above
|
||||
if( rider->getControllingPlayer() )
|
||||
changeTeamOnAllConnectedCaves( rider->getControllingPlayer()->getDefaultTeam(), TRUE );
|
||||
|
||||
}
|
||||
else if( getContainCount() == 0 )
|
||||
{
|
||||
// And a 0 is the edge trigger to do uncapture stuff
|
||||
changeTeamOnAllConnectedCaves( m_originalTeam, FALSE );
|
||||
}
|
||||
|
||||
// Handle the team color that is rendered
|
||||
const Player* controller = getApparentControllingPlayer(ThePlayerList->getLocalPlayer());
|
||||
if (controller)
|
||||
{
|
||||
if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
|
||||
getObject()->getDrawable()->setIndicatorColor( controller->getPlayerNightColor() );
|
||||
else
|
||||
getObject()->getDrawable()->setIndicatorColor( controller->getPlayerColor() );
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
static CaveInterface* findCave(Object* obj)
|
||||
{
|
||||
for (BehaviorModule** i = obj->getBehaviorModules(); *i; ++i)
|
||||
{
|
||||
CaveInterface* c = (*i)->getCaveInterface();
|
||||
if (c != NULL)
|
||||
return c;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
void CaveContain::changeTeamOnAllConnectedCaves( Team *newTeam, Bool setOriginalTeams )
|
||||
{
|
||||
TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex );
|
||||
const std::list<ObjectID> *allCaves = myTracker->getContainerList();
|
||||
for( std::list<ObjectID>::const_iterator iter = allCaves->begin(); iter != allCaves->end(); iter++ )
|
||||
{
|
||||
// For each ID, look it up and change its team. We all get captured together.
|
||||
Object *currentCave = TheGameLogic->findObjectByID( *iter );
|
||||
if( currentCave )
|
||||
{
|
||||
// This is a distributed Garrison in terms of capturing, so when one node
|
||||
// triggers the change, he needs to tell everyone, so anyone can do the un-change.
|
||||
CaveInterface *caveModule = findCave(currentCave);
|
||||
if( caveModule == NULL )
|
||||
continue;
|
||||
if( setOriginalTeams )
|
||||
caveModule->setOriginalTeam( currentCave->getTeam() );
|
||||
else
|
||||
caveModule->setOriginalTeam( NULL );
|
||||
|
||||
// Now do the actual switch for this one.
|
||||
|
||||
currentCave->defect( newTeam, 0 );
|
||||
// currentCave->setTeam( newTeam );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
void CaveContain::setOriginalTeam( Team *oldTeam )
|
||||
{
|
||||
m_originalTeam = oldTeam;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CaveContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CaveContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
// need to run on build complete
|
||||
xfer->xferBool( &m_needToRunOnBuildComplete );
|
||||
|
||||
// cave index
|
||||
xfer->xferInt( &m_caveIndex );
|
||||
|
||||
// original team
|
||||
TeamID teamID = m_originalTeam ? m_originalTeam->getID() : TEAM_ID_INVALID;
|
||||
xfer->xferUser( &teamID, sizeof( TeamID ) );
|
||||
if( xfer->getXferMode() == XFER_LOAD )
|
||||
{
|
||||
|
||||
if( teamID != TEAM_ID_INVALID )
|
||||
{
|
||||
|
||||
m_originalTeam = TheTeamFactory->findTeamByID( teamID );
|
||||
if( m_originalTeam == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "CaveContain::xfer - Unable to find original team by id\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
else
|
||||
m_originalTeam = NULL;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CaveContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: HealContain.cpp //////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day
|
||||
// Desc: Objects that are contained inside a heal contain ... get healed! oh my!
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/HealContain.h"
|
||||
#include "GameLogic/Module/UpdateModule.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
HealContainModuleData::HealContainModuleData( void )
|
||||
{
|
||||
|
||||
m_framesForFullHeal = 0;
|
||||
|
||||
} // end HealContainModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void HealContainModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
|
||||
OpenContainModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "TimeForFullHeal", INI::parseDurationUnsignedInt, NULL, offsetof( HealContainModuleData, m_framesForFullHeal ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
HealContain::HealContain( Thing *thing, const ModuleData *moduleData )
|
||||
: OpenContain( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end HealContain
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
HealContain::~HealContain( void )
|
||||
{
|
||||
|
||||
} // end ~HealContain
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Per frame update */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime HealContain::update( void )
|
||||
{
|
||||
|
||||
// extending functionality
|
||||
/*UpdateSleepTime result =*/ OpenContain::update();
|
||||
|
||||
// get the module data
|
||||
const HealContainModuleData *modData = getHealContainModuleData();
|
||||
|
||||
//
|
||||
// for each of our objects, we give them a little health each frame so that when the
|
||||
// the TimeTillHealed is up, the object will exit and be fully healed
|
||||
//
|
||||
Bool doneHealing;
|
||||
Object *obj;
|
||||
ContainedItemsList::const_iterator it = getContainList().begin();
|
||||
while( it != getContainList().end() )
|
||||
{
|
||||
|
||||
// get the object
|
||||
obj = *it;
|
||||
|
||||
// increment the iterator, which allows failure to not cause an infinite loop
|
||||
++it;
|
||||
|
||||
// do the healing on this object
|
||||
doneHealing = doHeal( obj, modData->m_framesForFullHeal );
|
||||
|
||||
// if we're done healing, we need to remove us from the healing container
|
||||
if( doneHealing == TRUE )
|
||||
{
|
||||
ExitDoorType exitDoor = reserveDoorForExit(obj->getTemplate(), obj);
|
||||
if (exitDoor != DOOR_NONE_AVAILABLE)
|
||||
exitObjectViaDoor( obj, exitDoor );
|
||||
} // end if
|
||||
|
||||
} // end for, it
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
} // end update
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Do the healing for a single object for a single frame. */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool HealContain::doHeal( Object *obj, UnsignedInt framesForFullHeal )
|
||||
{
|
||||
Bool doneHealing = FALSE;
|
||||
|
||||
// setup the healing damageInfo structure with all but the amount
|
||||
DamageInfo healInfo;
|
||||
healInfo.in.m_damageType = DAMAGE_HEALING;
|
||||
healInfo.in.m_deathType = DEATH_NONE;
|
||||
healInfo.in.m_sourceID = getObject()->getID();
|
||||
|
||||
// get body module of the thing to heal
|
||||
BodyModuleInterface *body = obj->getBodyModule();
|
||||
|
||||
// if we've been in here long enough ... set our health to max
|
||||
if( TheGameLogic->getFrame() - obj->getContainedByFrame() >= framesForFullHeal )
|
||||
{
|
||||
|
||||
// set the amount to max just to be sure we're at the top
|
||||
healInfo.in.m_amount = body->getMaxHealth();
|
||||
|
||||
// set max health
|
||||
body->attemptHealing( &healInfo );
|
||||
|
||||
// we're done healing
|
||||
doneHealing = TRUE;
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
//
|
||||
// given the *whole* time it would take to heal this object, lets pretend that the
|
||||
// object is at zero health ... and give it a sliver of health as if it were at 0 health
|
||||
// and would be fully healed at 'framesForFullHeal'
|
||||
//
|
||||
healInfo.in.m_amount = body->getMaxHealth() / (Real)framesForFullHeal;
|
||||
|
||||
// do the healing
|
||||
body->attemptHealing( &healInfo );
|
||||
|
||||
} // end else
|
||||
|
||||
// return if we're done healing
|
||||
return doneHealing;
|
||||
|
||||
} // end doHeal
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HealContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HealContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void HealContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MobNexusContain.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Mark Lorenzen, August 2002
|
||||
// Desc: Contain module for mob units.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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/StealthUpdate.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/MobNexusContain.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
MobNexusContainModuleData::MobNexusContainModuleData()
|
||||
{
|
||||
|
||||
m_slotCapacity = 0;
|
||||
m_scatterNearbyOnExit = true;
|
||||
m_orientLikeContainerOnExit = false;
|
||||
m_keepContainerVelocityOnExit = false;
|
||||
m_exitPitchRate = 0.0f;
|
||||
m_initialPayload.count = 0;
|
||||
m_healthRegen = 0.0f;
|
||||
|
||||
//
|
||||
// by default we say that MobNexae can have infantry inside them, this will be totally
|
||||
// overwritten by any data provided from the INI entry tho
|
||||
//
|
||||
m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContainModuleData::parseInitialPayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
MobNexusContainModuleData* self = (MobNexusContainModuleData*)instance;
|
||||
const char* name = ini->getNextToken();
|
||||
const char* countStr = ini->getNextTokenOrNull();
|
||||
Int count = countStr ? INI::scanInt(countStr) : 1;
|
||||
|
||||
self->m_initialPayload.name.set(name);
|
||||
self->m_initialPayload.count = count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContainModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
OpenContainModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "Slots", INI::parseInt, NULL, offsetof( MobNexusContainModuleData, m_slotCapacity ) },
|
||||
{ "ScatterNearbyOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_scatterNearbyOnExit ) },
|
||||
{ "OrientLikeContainerOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_orientLikeContainerOnExit ) },
|
||||
{ "KeepContainerVelocityOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_keepContainerVelocityOnExit ) },
|
||||
{ "ExitBone", INI::parseAsciiString, NULL, offsetof( MobNexusContainModuleData, m_exitBone ) },
|
||||
{ "ExitPitchRate", INI::parseAngularVelocityReal, NULL, offsetof( MobNexusContainModuleData, m_exitPitchRate ) },
|
||||
{ "InitialPayload", parseInitialPayload, NULL, 0 },
|
||||
{ "HealthRegen%PerSec", INI::parseReal, NULL, offsetof( MobNexusContainModuleData, m_healthRegen ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int MobNexusContain::getContainMax( void ) const
|
||||
{
|
||||
if (getMobNexusContainModuleData())
|
||||
return getMobNexusContainModuleData()->m_slotCapacity;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MobNexusContain::MobNexusContain( Thing *thing, const ModuleData *moduleData ) :
|
||||
OpenContain( thing, moduleData )
|
||||
{
|
||||
m_extraSlotsInUse = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MobNexusContain::~MobNexusContain( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
can this container contain this kind of object?
|
||||
and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
|
||||
*/
|
||||
Bool MobNexusContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
|
||||
{
|
||||
|
||||
// sanity
|
||||
if (!rider)
|
||||
return false;
|
||||
|
||||
//The point of this new code is to determine when something has a negative 1 contain max, to
|
||||
//look at the object inside of it to use that as the valid check. There is a case, when a
|
||||
//paratrooper (an infantry contained in a parachute). In this case, when we pass this object
|
||||
//to contain in a --- plane, we want to check the infantry, not the parachute.
|
||||
// const MobNexusContainModuleData *modData = getMobNexusContainModuleData();
|
||||
|
||||
//Check if we are a fake container, and if so, get an object inside it to see what kind this object *is*.
|
||||
if( rider->getContain() && rider->getContain()->isSpecialZeroSlotContainer() )
|
||||
{
|
||||
//Report the first thing inside it!
|
||||
const ContainedItemsList *items = rider->getContain()->getContainedItemsList();
|
||||
if( items )
|
||||
{
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = items->begin();
|
||||
if( *it )
|
||||
{
|
||||
//Replace the object we are checking with the *first* object contained within it.
|
||||
rider = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//blech! This case may or may not occur... in which case, just use the supplied object.
|
||||
}
|
||||
|
||||
|
||||
// extend functionality
|
||||
if( OpenContain::isValidContainerFor( rider, checkCapacity ) == false )
|
||||
return false;
|
||||
|
||||
// only allied objects can be MobNexused.
|
||||
// order matters: we want to know if IT considers ME to be an ally (a reversal of the usual situation)
|
||||
if (rider->getRelationship(getObject()) != ALLIES)
|
||||
return false;
|
||||
|
||||
Int mobNexusSlotCount = rider->getTransportSlotCount();
|
||||
|
||||
// if 0, this object isn't MobNexusable.
|
||||
if (mobNexusSlotCount == 0)
|
||||
return false;
|
||||
|
||||
if (checkCapacity)
|
||||
{
|
||||
return (m_extraSlotsInUse + getContainCount() + mobNexusSlotCount <= getContainMax());
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::onContaining( Object *rider )
|
||||
{
|
||||
OpenContain::onContaining(rider);
|
||||
|
||||
// objects inside a MobNexus are held
|
||||
rider->setDisabled( DISABLED_HELD );
|
||||
|
||||
Int mobNexusSlotCount = rider->getTransportSlotCount();
|
||||
|
||||
DEBUG_ASSERTCRASH(mobNexusSlotCount > 0, ("Hmm, this object isnt MobNexusable"));
|
||||
m_extraSlotsInUse += mobNexusSlotCount - 1;
|
||||
DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Hmm, bad slot count"));
|
||||
|
||||
//
|
||||
// when we go from holding nothing to holding something we have a model condition
|
||||
// to visually show the change
|
||||
//
|
||||
if( getContainCount() == 1 )
|
||||
{
|
||||
Drawable *draw = getObject()->getDrawable();
|
||||
|
||||
if( draw )
|
||||
draw->setModelConditionState( MODELCONDITION_LOADED );
|
||||
|
||||
} // end if
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::onRemoving( Object *rider )
|
||||
{
|
||||
OpenContain::onRemoving(rider);
|
||||
|
||||
// object is no longer held inside a MobNexus
|
||||
rider->clearDisabled( DISABLED_HELD );
|
||||
|
||||
const MobNexusContainModuleData* d = getMobNexusContainModuleData();
|
||||
|
||||
if (!d->m_exitBone.isEmpty())
|
||||
{
|
||||
Drawable* draw = getObject()->getDrawable();
|
||||
if (draw)
|
||||
{
|
||||
Coord3D bonePos, worldPos;
|
||||
if (draw->getPristineBonePositions(d->m_exitBone.str(), 0, &bonePos, NULL, 1) == 1)
|
||||
{
|
||||
getObject()->convertBonePosToWorldPos(&bonePos, NULL, &worldPos, NULL);
|
||||
rider->setPosition(&worldPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_scatterNearbyOnExit)
|
||||
scatterToNearbyPosition(rider);
|
||||
|
||||
Int mobNexusSlotCount = rider->getTransportSlotCount();
|
||||
DEBUG_ASSERTCRASH(mobNexusSlotCount > 0, ("This object isnt MobNexusable"));
|
||||
m_extraSlotsInUse -= mobNexusSlotCount - 1;
|
||||
DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Bad slot count, MobNexus"));
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::onObjectCreated()
|
||||
{
|
||||
MobNexusContainModuleData* self = (MobNexusContainModuleData*)getMobNexusContainModuleData();
|
||||
|
||||
Int count = self->m_initialPayload.count;
|
||||
const ThingTemplate* payloadTemplate = TheThingFactory->findTemplate( self->m_initialPayload.name );
|
||||
Object* object = getObject();
|
||||
|
||||
for( int i = 0; i < count; i++ )
|
||||
{
|
||||
//We are creating a MobNexus that comes with a initial payload, so add it now!
|
||||
|
||||
Object* payload = TheThingFactory->newObject( payloadTemplate, object->getControllingPlayer()->getDefaultTeam() );
|
||||
if( object->getContain() && object->getContain()->isValidContainerFor( payload, true ) )
|
||||
{
|
||||
object->getContain()->addToContain( payload );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime MobNexusContain::update()
|
||||
{
|
||||
MobNexusContainModuleData *moduleData = (MobNexusContainModuleData*)getModuleData();
|
||||
|
||||
if( moduleData && moduleData->m_healthRegen )
|
||||
{
|
||||
ContainModuleInterface *contain = getObject()->getContain();
|
||||
if( contain )
|
||||
{
|
||||
//This MobNexus has a health regeneration value, so go through and heal all inside.
|
||||
const ContainedItemsList* items = contain->getContainedItemsList();
|
||||
if( items )
|
||||
{
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = items->begin();
|
||||
|
||||
while( *it )
|
||||
{
|
||||
Object *object = *it;
|
||||
|
||||
//Advance to the next iterator
|
||||
it++;
|
||||
|
||||
//Determine if we need healing or not.
|
||||
BodyModuleInterface *body = object->getBodyModule();
|
||||
if( body->getHealth() < body->getMaxHealth() )
|
||||
{
|
||||
//Calculate the health to be regenerated on each unit.
|
||||
Real regen = body->getMaxHealth() * moduleData->m_healthRegen / 100.0f * SECONDS_PER_LOGICFRAME_REAL;
|
||||
|
||||
//Perform the actual healing for this frame.
|
||||
// DamageInfo damageInfo;
|
||||
// damageInfo.in.m_damageType = DAMAGE_HEALING;
|
||||
// damageInfo.in.m_deathType = DEATH_NONE;
|
||||
// damageInfo.in.m_sourceID = getObject()->getID();
|
||||
// damageInfo.in.m_amount = regen;
|
||||
// object->attemptDamage( &damageInfo );
|
||||
object->attemptHealing( regen, getObject() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OpenContain::update(); //extend
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ExitDoorType MobNexusContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
|
||||
{
|
||||
if( specificObject == NULL )
|
||||
return DOOR_1;// I can, in general, exit people.
|
||||
|
||||
// This is an override, not an extend. I will check for game legality for
|
||||
// okaying the call to exitObjectViaDoor.
|
||||
Object *me = getObject();
|
||||
|
||||
// this is present solely for some MobNexuss to override, so that they can land before
|
||||
// allowing people to exit...
|
||||
AIUpdateInterface* ai = me->getAIUpdateInterface();
|
||||
if (ai && ai->getAiFreeToExit(me) != FREE_TO_EXIT)
|
||||
return DOOR_NONE_AVAILABLE;
|
||||
|
||||
// I can always kick people out if I am in the air, I know what I'm doing
|
||||
if( me->isUsingAirborneLocomotor() )
|
||||
return DOOR_1;
|
||||
|
||||
const Coord3D *myPosition = me->getPosition();
|
||||
if( !specificObject->getAIUpdateInterface() )
|
||||
{
|
||||
return DOOR_NONE_AVAILABLE;
|
||||
}
|
||||
const Locomotor *hisLocomotor = specificObject->getAIUpdateInterface()->getCurLocomotor();
|
||||
// He can't get to this spot naturally, so I can't force him there. (amphib MobNexus)
|
||||
if( ! TheAI->pathfinder()->validMovementTerrain(me->getLayer(), hisLocomotor, myPosition ) )
|
||||
return DOOR_NONE_AVAILABLE;
|
||||
|
||||
return DOOR_1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::unreserveDoorForExit( ExitDoorType exitDoor )
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool MobNexusContain::tryToEvacuate( Bool exposeStealthedUnits )
|
||||
{
|
||||
Bool exitedAnyone = false;
|
||||
ContainedItemsList::const_iterator it = getContainList().begin();
|
||||
while( it != getContainList().end() )
|
||||
{
|
||||
// get the object
|
||||
Object *obj = *it;
|
||||
// increment the iterator, since removal will pull this guy from the list somewhere else
|
||||
// and we might not actually kick anyone so we don't want to loop forever.
|
||||
++it;
|
||||
|
||||
ExitDoorType exitDoor = reserveDoorForExit(obj->getTemplate(), obj);
|
||||
if(exitDoor != DOOR_NONE_AVAILABLE)
|
||||
{
|
||||
exitObjectViaDoor( obj, exitDoor );
|
||||
exitedAnyone = true;
|
||||
|
||||
if( obj->isKindOf( KINDOF_STEALTH_GARRISON ) && exposeStealthedUnits )
|
||||
{
|
||||
static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
|
||||
StealthUpdate* stealth = (StealthUpdate*)obj->findUpdateModule( key_StealthUpdate );
|
||||
if( stealth )
|
||||
{
|
||||
stealth->markAsDetected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return exitedAnyone;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC - I like the word nexus */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
// extra slots in use
|
||||
xfer->xferInt( &m_extraSlotsInUse );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MobNexusContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: OverlordContain.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, September, 2002
|
||||
// Desc: Contain module that acts as transport normally, but when full it redirects queries to the first passenger
|
||||
// All of this redirection stuff makes it so that while I am normally a transport
|
||||
// for Overlord subObjects, once I have a passenger, _I_ become a transport of their type.
|
||||
// So, the answer to this question depends on if it is my passenger asking, or theirs.
|
||||
// As always, I can't use convience functions that get redirected on a ? like this.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/ControlBar.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/OverlordContain.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
OverlordContainModuleData::OverlordContainModuleData()
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverlordContainModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
TransportContainModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
OverlordContain::OverlordContain( Thing *thing, const ModuleData *moduleData ) :
|
||||
TransportContain( thing, moduleData )
|
||||
{
|
||||
m_redirectionActivated = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
OverlordContain::~OverlordContain( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::onBodyDamageStateChange( const DamageInfo* damageInfo,
|
||||
BodyDamageType oldState,
|
||||
BodyDamageType newState) ///< state change callback
|
||||
{
|
||||
// I can't use any convienience functions, as they will all get routed to the bunker I may carry.
|
||||
// I want just me.
|
||||
// Oh, and I don't want this function trying to do death. That is more complicated and will be handled
|
||||
// on my death.
|
||||
if( newState != BODY_RUBBLE && m_containListSize == 1 )
|
||||
{
|
||||
Object *myGuy = m_containList.front();
|
||||
myGuy->getBodyModule()->setDamageState( newState );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ContainModuleInterface *OverlordContain::getRedirectedContain() const
|
||||
{
|
||||
// Naturally, I can not use a redirectible convienience function
|
||||
// to answer if I am redirecting yet.
|
||||
|
||||
// If I am empty, say no.
|
||||
if( m_containListSize < 1 )
|
||||
return NULL;
|
||||
|
||||
if( !m_redirectionActivated )
|
||||
return NULL;// Shut off early to allow death to happen without my bunker having
|
||||
// trouble finding me to say goodbye as messages get sucked up the pipe to him.
|
||||
|
||||
Object *myGuy = m_containList.front();
|
||||
if( myGuy )
|
||||
return myGuy->getContain();
|
||||
|
||||
return NULL;// Or say no if they have no contain.
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::onDie( damageInfo );
|
||||
return;
|
||||
}
|
||||
//Everything is fine if I am empty or carrying a regular guy. If I have a redirected contain
|
||||
// set up, then I need to handle the order of death explicitly, or things will become confused
|
||||
// when I stop redirecting in the middle of the process. Or I will get confused as my commands
|
||||
// get sucked up the pipe.
|
||||
|
||||
// So this is an extend that lets me control the order of death.
|
||||
|
||||
deactivateRedirectedContain();
|
||||
Object *myGuy = m_containList.front();
|
||||
myGuy->kill();
|
||||
|
||||
TransportContain::onDie( damageInfo );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::onDelete( void )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::onDelete( );
|
||||
return;
|
||||
}
|
||||
|
||||
// Without my throwing the redirect switch, teardown deletion will get confused and fire off a bunch of asserts
|
||||
getRedirectedContain()->removeAllContained();
|
||||
|
||||
deactivateRedirectedContain();
|
||||
removeAllContained();
|
||||
|
||||
TransportContain::onDelete( );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::onCapture( Player *oldOwner, Player *newOwner )
|
||||
{
|
||||
if( m_containListSize < 1 )
|
||||
return;
|
||||
|
||||
// Need to capture our specific rider. He will then kick passengers out if he is a Transport
|
||||
Object *myGuy = m_containList.front();
|
||||
myGuy->setTeam( newOwner->getDefaultTeam() );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool OverlordContain::isGarrisonable() const
|
||||
{
|
||||
if( getRedirectedContain() == NULL )
|
||||
return FALSE;
|
||||
|
||||
return getRedirectedContain()->isGarrisonable();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool OverlordContain::isKickOutOnCapture()
|
||||
{
|
||||
if( getRedirectedContain() == NULL )
|
||||
return FALSE;// Me the Overlord doesn't want to
|
||||
|
||||
return getRedirectedContain()->isKickOutOnCapture();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::addToContainList( Object *obj )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::addToContainList( obj );
|
||||
return;
|
||||
}
|
||||
|
||||
getRedirectedContain()->addToContainList( obj );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::addToContain( Object *obj )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::addToContain( obj );
|
||||
return;
|
||||
}
|
||||
|
||||
getRedirectedContain()->addToContain( obj );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Remove 'obj' from the m_containList of objects in this module.
|
||||
* This will trigger an onRemoving event for the object that this module
|
||||
* is a part of and an onRemovedFrom event for the object being removed */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::removeFromContain( Object *obj, Bool exposeStealthUnits )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::removeFromContain( obj, exposeStealthUnits );
|
||||
return;
|
||||
}
|
||||
|
||||
getRedirectedContain()->removeFromContain( obj, exposeStealthUnits );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Remove all contained objects from the contained list */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::removeAllContained( Bool exposeStealthUnits )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::removeAllContained( exposeStealthUnits );
|
||||
return;
|
||||
}
|
||||
|
||||
const ContainedItemsList *fullList = getRedirectedContain()->getContainedItemsList();
|
||||
|
||||
Object *obj;
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = (*fullList).begin();
|
||||
while( it != (*fullList).end() )
|
||||
{
|
||||
obj = *it;
|
||||
it++;
|
||||
removeFromContain( obj, exposeStealthUnits );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Iterate the contained list and call the callback on each of the objects */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::iterateContained( func, userData, reverse );
|
||||
return;
|
||||
}
|
||||
|
||||
getRedirectedContain()->iterateContained( func, userData, reverse );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::onContaining( Object *obj )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::onContaining( obj );
|
||||
activateRedirectedContain();//Am now carrying something
|
||||
return;
|
||||
}
|
||||
|
||||
OpenContain::onContaining(obj);
|
||||
|
||||
getRedirectedContain()->onContaining( obj );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::onRemoving( Object *obj )
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
TransportContain::onRemoving( obj );
|
||||
return;
|
||||
}
|
||||
|
||||
OpenContain::onRemoving(obj);
|
||||
|
||||
getRedirectedContain()->onRemoving( obj );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool OverlordContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
return TransportContain::isValidContainerFor( obj, checkCapacity );
|
||||
|
||||
return getRedirectedContain()->isValidContainerFor( obj, checkCapacity );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UnsignedInt OverlordContain::getContainCount() const
|
||||
{
|
||||
ContainModuleInterface* redir = getRedirectedContain();
|
||||
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( redir == NULL )
|
||||
return TransportContain::getContainCount( );
|
||||
|
||||
return redir->getContainCount();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool OverlordContain::getContainerPipsToShow(Int& numTotal, Int& numFull)
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
{
|
||||
numTotal = 0;
|
||||
numFull = 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return getRedirectedContain()->getContainerPipsToShow(numTotal, numFull);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int OverlordContain::getContainMax( ) const
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
return TransportContain::getContainMax( );
|
||||
|
||||
return getRedirectedContain()->getContainMax();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const ContainedItemsList* OverlordContain::getContainedItemsList() const
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
return TransportContain::getContainedItemsList( );
|
||||
|
||||
return getRedirectedContain()->getContainedItemsList();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool OverlordContain::isEnclosingContainerFor( const Object *obj ) const
|
||||
{
|
||||
// All of this redirection stuff makes it so that while I am normally a transport
|
||||
// for Overlord subObjects, once I have a passenger, _I_ become a transport of their type.
|
||||
// So, the answer to this question depends on if it is my passenger asking, or theirs.
|
||||
// As always, I can't use convience functions that get redirected on a ? like this.
|
||||
if( m_containListSize > 0 && obj == m_containList.front() )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool OverlordContain::isDisplayedOnControlBar() const
|
||||
{
|
||||
// Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
|
||||
if( getRedirectedContain() == NULL )
|
||||
return FALSE;//No need to call up inheritance, this is a module based question, and I say no.
|
||||
|
||||
return getRedirectedContain()->isDisplayedOnControlBar();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Object *OverlordContain::friend_getRider() const
|
||||
{
|
||||
// The draw order dependency bug for riders means that our draw module needs to cheat to get
|
||||
//around it. So this is another function that knows it is getting around redirection to ask
|
||||
// an Overlord specific function.
|
||||
|
||||
if( m_containListSize > 0 )
|
||||
return m_containList.front();
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::activateRedirectedContain()
|
||||
{
|
||||
m_redirectionActivated = TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::deactivateRedirectedContain()
|
||||
{
|
||||
m_redirectionActivated = FALSE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// if my object gets selected, then my visible passengers should, too
|
||||
// this gets called from
|
||||
void OverlordContain::clientVisibleContainedFlashAsSelected()
|
||||
{
|
||||
// THIS OVERRIDES GRAHAMS NASTY OVERRIDE THING
|
||||
// SO WE CAN FLASH THE PORTABLE BUNKER INSTEAD OF ITS OCCUPANTS
|
||||
const ContainedItemsList* items = TransportContain::getContainedItemsList();
|
||||
|
||||
if( items )
|
||||
{
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = items->begin();
|
||||
|
||||
while( *it )
|
||||
{
|
||||
Object *object = *it;
|
||||
if ( object && object->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
|
||||
{
|
||||
Drawable *draw = object->getDrawable();
|
||||
if ( draw )
|
||||
{
|
||||
draw->flashAsSelected(); //WOW!
|
||||
}
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
TransportContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
TransportContain::xfer( xfer );
|
||||
|
||||
// redirection activated
|
||||
xfer->xferBool( &m_redirectionActivated );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OverlordContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
TransportContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ParachuteContain.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, March 2002
|
||||
// Desc: Contain module for transport units.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER 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/RandomValue.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameLogic/Locomotor.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/ParachuteContain.h"
|
||||
#include "GameLogic/Module/PhysicsUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
|
||||
const Real NO_START_Z = 1e10;
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
|
||||
// PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ParachuteContainModuleData::ParachuteContainModuleData() :
|
||||
m_pitchRateMax(0),
|
||||
m_rollRateMax(0),
|
||||
m_lowAltitudeDamping(0.2f),
|
||||
m_paraOpenDist(0.0f),
|
||||
m_freeFallDamagePercent(0.5f),
|
||||
m_killWhenLandingInWaterSlop(10.0f)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContainModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
OpenContainModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "PitchRateMax", INI::parseAngularVelocityReal, NULL, offsetof( ParachuteContainModuleData, m_pitchRateMax ) },
|
||||
{ "RollRateMax", INI::parseAngularVelocityReal, NULL, offsetof( ParachuteContainModuleData, m_rollRateMax ) },
|
||||
{ "LowAltitudeDamping", INI::parseReal, NULL, offsetof( ParachuteContainModuleData, m_lowAltitudeDamping ) },
|
||||
{ "ParachuteOpenDist", INI::parseReal, NULL, offsetof( ParachuteContainModuleData, m_paraOpenDist ) },
|
||||
{ "KillWhenLandingInWaterSlop", INI::parseReal, NULL, offsetof( ParachuteContainModuleData, m_killWhenLandingInWaterSlop ) },
|
||||
{ "FreeFallDamagePercent", INI::parsePercentToReal, NULL, offsetof( ParachuteContainModuleData, m_freeFallDamagePercent ) },
|
||||
{ "ParachuteOpenSound", INI::parseAudioEventRTS, NULL, offsetof( ParachuteContainModuleData, m_parachuteOpenSound ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
// PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ParachuteContain::ParachuteContain( Thing *thing, const ModuleData *moduleData ) :
|
||||
OpenContain( thing, moduleData )
|
||||
{
|
||||
m_opened = false;
|
||||
m_needToUpdateParaBones = true;
|
||||
m_needToUpdateRiderBones = true;
|
||||
m_pitch = 0;
|
||||
m_roll = 0;
|
||||
m_pitchRate = 0;
|
||||
m_rollRate = 0;
|
||||
m_isLandingOverrideSet = FALSE;
|
||||
m_startZ = NO_START_Z;
|
||||
|
||||
//Added By Sadullah Nader
|
||||
//Initializations
|
||||
m_landingOverride.zero();
|
||||
m_paraAttachBone.zero();
|
||||
m_paraAttachOffset.zero();
|
||||
m_paraSwayBone.zero();
|
||||
m_paraSwayOffset.zero();
|
||||
m_riderAttachBone.zero();
|
||||
m_riderAttachOffset.zero();
|
||||
m_riderSwayBone.zero();
|
||||
m_riderSwayOffset.zero();
|
||||
|
||||
//
|
||||
const ParachuteContainModuleData* d = getParachuteContainModuleData();
|
||||
if (d)
|
||||
{
|
||||
m_pitchRate = GameLogicRandomValueReal(-d->m_pitchRateMax, d->m_pitchRateMax);
|
||||
m_rollRate = GameLogicRandomValueReal(-d->m_rollRateMax, d->m_rollRateMax);
|
||||
}
|
||||
|
||||
getObject()->setStatus(OBJECT_STATUS_PARACHUTING);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ParachuteContain::~ParachuteContain( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
this is called whenever a drawable is bound to the object.
|
||||
drawable is NOT guaranteed to be non-null.
|
||||
*/
|
||||
void ParachuteContain::onDrawableBoundToObject()
|
||||
{
|
||||
Drawable* draw = getObject()->getDrawable();
|
||||
if (draw)
|
||||
draw->setDrawableHidden(!m_opened);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::calcSwayMtx(const Coord3D* offset, Matrix3D* mtx)
|
||||
{
|
||||
mtx->Make_Identity();
|
||||
mtx->Translate(offset->x, offset->y, offset->z);
|
||||
mtx->In_Place_Pre_Rotate_X(m_roll);
|
||||
mtx->In_Place_Pre_Rotate_Y(m_pitch);
|
||||
mtx->Translate(-offset->x, -offset->y, -offset->z);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::updateBonePositions()
|
||||
{
|
||||
if (m_needToUpdateParaBones)
|
||||
{
|
||||
m_needToUpdateParaBones = false; // yeah, even if not found.
|
||||
|
||||
Drawable* parachuteDraw = getObject()->getDrawable();
|
||||
if (parachuteDraw)
|
||||
{
|
||||
if (parachuteDraw->getPristineBonePositions( "PARA_COG", 0, &m_paraSwayBone, NULL, 1) != 1)
|
||||
{
|
||||
DEBUG_CRASH(("PARA_COG not found\n"));
|
||||
m_paraSwayBone.zero();
|
||||
}
|
||||
|
||||
if (parachuteDraw->getPristineBonePositions( "PARA_ATTCH", 0, &m_paraAttachBone, NULL, 1 ) != 1)
|
||||
{
|
||||
DEBUG_CRASH(("PARA_ATTCH not found\n"));
|
||||
m_paraAttachBone.zero();
|
||||
}
|
||||
}
|
||||
//DEBUG_LOG(("updating para bone positions %d...\n",TheGameLogic->getFrame()));
|
||||
}
|
||||
|
||||
if (m_needToUpdateRiderBones)
|
||||
{
|
||||
|
||||
m_needToUpdateRiderBones = false; // yeah, even if not found.
|
||||
|
||||
Object* rider = (getContainCount() > 0) ? getContainList().front() : NULL;
|
||||
Drawable* riderDraw = rider ? rider->getDrawable() : NULL;
|
||||
if (riderDraw)
|
||||
{
|
||||
if (riderDraw->getPristineBonePositions( "PARA_MAN", 0, &m_riderAttachBone, NULL, 1) != 1)
|
||||
{
|
||||
//DEBUG_LOG(("*** No parachute-attach bone... using object height!\n"));
|
||||
m_riderAttachBone.zero();
|
||||
m_riderAttachBone.z += riderDraw->getDrawableGeometryInfo().getMaxHeightAbovePosition();
|
||||
}
|
||||
}
|
||||
|
||||
//DEBUG_LOG(("updating rider bone positions %d...\n",TheGameLogic->getFrame()));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::updateOffsetsFromBones()
|
||||
{
|
||||
const Coord3D* objPos = getObject()->getPosition();
|
||||
|
||||
getObject()->convertBonePosToWorldPos(&m_paraSwayBone, NULL, &m_paraSwayOffset, NULL);
|
||||
m_paraSwayOffset.x -= objPos->x;
|
||||
m_paraSwayOffset.y -= objPos->y;
|
||||
m_paraSwayOffset.z -= objPos->z;
|
||||
|
||||
getObject()->convertBonePosToWorldPos(&m_paraAttachBone, NULL, &m_paraAttachOffset, NULL);
|
||||
m_paraAttachOffset.x -= objPos->x;
|
||||
m_paraAttachOffset.y -= objPos->y;
|
||||
m_paraAttachOffset.z -= objPos->z;
|
||||
|
||||
Object* rider = (getContainCount() > 0) ? getContainList().front() : NULL;
|
||||
if (rider)
|
||||
{
|
||||
const Coord3D* riderPos = rider->getPosition();
|
||||
|
||||
rider->convertBonePosToWorldPos(&m_riderAttachBone, NULL, &m_riderAttachOffset, NULL);
|
||||
m_riderAttachOffset.x -= riderPos->x;
|
||||
m_riderAttachOffset.y -= riderPos->y;
|
||||
m_riderAttachOffset.z -= riderPos->z;
|
||||
|
||||
m_riderAttachOffset.x = m_paraAttachOffset.x - m_riderAttachOffset.x;
|
||||
m_riderAttachOffset.y = m_paraAttachOffset.y - m_riderAttachOffset.y;
|
||||
m_riderAttachOffset.z = m_paraAttachOffset.z - m_riderAttachOffset.z;
|
||||
|
||||
m_riderSwayOffset.x = m_paraSwayOffset.x - m_riderAttachOffset.x;
|
||||
m_riderSwayOffset.y = m_paraSwayOffset.y - m_riderAttachOffset.y;
|
||||
m_riderSwayOffset.z = m_paraSwayOffset.z - m_riderAttachOffset.z;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
can this container contain this kind of object?
|
||||
and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
|
||||
*/
|
||||
Bool ParachuteContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
|
||||
{
|
||||
if (!rider)
|
||||
return false;
|
||||
|
||||
// extend functionality
|
||||
if( OpenContain::isValidContainerFor( rider, checkCapacity ) == false )
|
||||
return false;
|
||||
|
||||
Int transportSlotCount = rider->getTransportSlotCount();
|
||||
|
||||
// if 0, this object isn't transportable.
|
||||
// (exception: infantry are always transportable by parachutes, regardless
|
||||
// of this.... this allows us to paradrop pilots, but not transport them
|
||||
// by other means)
|
||||
if (transportSlotCount == 0 && !rider->isKindOf(KINDOF_INFANTRY) && !rider->isKindOf(KINDOF_PARACHUTABLE))
|
||||
return false;
|
||||
|
||||
// we can only "hold" one item at a time.
|
||||
if (getContainCount() > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::containReactToTransformChange()
|
||||
{
|
||||
// a bit of a cheese festival here... hidden is a flag, not a counter, so when we are dumped
|
||||
// from a plane, we might get drawn for a frame before our update is called, meaning we
|
||||
// should be briefly visible. put this here to ensure we stay hidden appropriately.
|
||||
Drawable* draw = getObject()->getDrawable();
|
||||
if (draw)
|
||||
draw->setDrawableHidden(!m_opened);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime ParachuteContain::update( void )
|
||||
{
|
||||
OpenContain::update();
|
||||
|
||||
Object* parachute = getObject();
|
||||
if( parachute->isDisabledByType( DISABLED_HELD ) )
|
||||
{
|
||||
return UPDATE_SLEEP_NONE; // my, that was easy
|
||||
}
|
||||
|
||||
AIUpdateInterface *parachuteAI = parachute->getAI();
|
||||
|
||||
Drawable* draw = parachute->getDrawable();
|
||||
const ParachuteContainModuleData* d = getParachuteContainModuleData();
|
||||
Object* rider = (getContainCount() > 0) ? getContainList().front() : NULL;
|
||||
|
||||
if (m_startZ == NO_START_Z) {
|
||||
m_startZ = parachute->getPosition()->z;
|
||||
Real groundHeight = TheTerrainLogic->getGroundHeight(parachute->getPosition()->x, parachute->getPosition()->y);
|
||||
if (m_startZ-groundHeight < 2*d->m_paraOpenDist) {
|
||||
// Oh dear - we ejected too close to the ground, and there isn't enough
|
||||
// room to open the chute. Well, since it's only a game, we'll fudge
|
||||
// a little so that the pilot doesn't slam into the ground & stick.
|
||||
m_startZ = groundHeight+2*d->m_paraOpenDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_opened)
|
||||
{
|
||||
// see if we need to open.
|
||||
if (fabs(m_startZ - parachute->getPosition()->z) >= d->m_paraOpenDist)
|
||||
{
|
||||
m_opened = true;
|
||||
parachute->clearAndSetModelConditionState(MODELCONDITION_FREEFALL, MODELCONDITION_PARACHUTING);
|
||||
m_needToUpdateParaBones = true;
|
||||
if (rider)
|
||||
{
|
||||
rider->clearAndSetModelConditionState(MODELCONDITION_FREEFALL, MODELCONDITION_PARACHUTING);
|
||||
m_needToUpdateRiderBones = true;
|
||||
|
||||
AudioEventRTS soundToPlay = d->m_parachuteOpenSound;
|
||||
soundToPlay.setObjectID( rider->getID() );
|
||||
TheAudio->addAudioEvent( &soundToPlay );
|
||||
}
|
||||
|
||||
|
||||
// When a parachute opens, it should look for a good place to land. This could be explicitly set
|
||||
// by a DeliverPayload, otherwise any place clear is good.
|
||||
if( parachuteAI )
|
||||
{
|
||||
Coord3D target = *parachute->getPosition();
|
||||
if( m_isLandingOverrideSet )
|
||||
{
|
||||
target = m_landingOverride;
|
||||
if( parachuteAI->getCurLocomotor() )
|
||||
parachuteAI->getCurLocomotor()->setUltraAccurate( TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPositionOptions fpOptions;
|
||||
fpOptions.minRadius = 0.0f;
|
||||
fpOptions.maxRadius = 100.0f;
|
||||
fpOptions.relationshipObject = NULL;
|
||||
fpOptions.flags = FPF_NONE;
|
||||
ThePartitionManager->findPositionAround( &target, &fpOptions, &target );
|
||||
}
|
||||
parachuteAI->aiMoveToPosition( &target, CMD_FROM_AI );
|
||||
}
|
||||
}
|
||||
}
|
||||
draw->setDrawableHidden(!m_opened);
|
||||
|
||||
if (!m_opened || getContainCount() == 0)
|
||||
{
|
||||
// unopened, or empty, chutes, don't collide with anything, to simplify
|
||||
// ejections, paradrops, landings, etc...
|
||||
parachute->setStatus(OBJECT_STATUS_NO_COLLISIONS);
|
||||
if (rider)
|
||||
rider->setStatus(OBJECT_STATUS_NO_COLLISIONS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// opened/nonempty chutes DO collide...
|
||||
parachute->clearStatus(OBJECT_STATUS_NO_COLLISIONS);
|
||||
if (rider)
|
||||
rider->clearStatus(OBJECT_STATUS_NO_COLLISIONS);
|
||||
}
|
||||
|
||||
AIUpdateInterface* ai = parachute->getAIUpdateInterface();
|
||||
if (ai && !parachute->isEffectivelyDead())
|
||||
{
|
||||
ai->chooseLocomotorSet(m_opened ? LOCOMOTORSET_NORMAL : LOCOMOTORSET_FREEFALL);
|
||||
|
||||
Locomotor* locomotor = ai->getCurLocomotor();
|
||||
if (locomotor)
|
||||
{
|
||||
// damp the swaying a bunch when we get close, so that things land vertically (or nearly so)
|
||||
Real altitudeDamping = 0;
|
||||
if (getContainCount() > 0)
|
||||
{
|
||||
Object* rider = getContainList().front();
|
||||
const Real ALTITUDE_DAMP_START = 20.0f;
|
||||
if (rider->getHeightAboveTerrain() <= ALTITUDE_DAMP_START)
|
||||
altitudeDamping = d->m_lowAltitudeDamping;
|
||||
}
|
||||
|
||||
if (m_opened)
|
||||
{
|
||||
const Real PITCH_STIFFNESS = locomotor->getPitchStiffness();
|
||||
const Real ROLL_STIFFNESS = locomotor->getRollStiffness();
|
||||
const Real PITCH_DAMPING = locomotor->getPitchDamping() + altitudeDamping;
|
||||
const Real ROLL_DAMPING = locomotor->getRollDamping() + altitudeDamping;
|
||||
m_pitchRate += ((-PITCH_STIFFNESS * m_pitch) + (-PITCH_DAMPING * m_pitchRate)); // spring/damper
|
||||
m_rollRate += ((-ROLL_STIFFNESS * m_roll) + (-ROLL_DAMPING * m_rollRate)); // spring/damper
|
||||
|
||||
m_pitch += m_pitchRate;
|
||||
m_roll += m_rollRate;
|
||||
|
||||
if( m_isLandingOverrideSet )
|
||||
{
|
||||
// Need to wait until after opening to do this, or else we are sending the message to the Freefall locomotor.
|
||||
locomotor->setCloseEnoughDist( 10.0 );
|
||||
locomotor->setCloseEnoughDist3D( FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
if (draw)
|
||||
{
|
||||
updateBonePositions();
|
||||
updateOffsetsFromBones();
|
||||
|
||||
Matrix3D tmp;
|
||||
calcSwayMtx(&m_paraSwayOffset, &tmp);
|
||||
draw->setInstanceMatrix(&tmp);
|
||||
}
|
||||
|
||||
positionContainedObjectsRelativeToContainer();
|
||||
}
|
||||
}
|
||||
|
||||
// allow us to land on bridges!
|
||||
const Coord3D* paraPos = getObject()->getPosition();
|
||||
PathfindLayerEnum newLayer = TheTerrainLogic->getHighestLayerForDestination(paraPos);
|
||||
getObject()->setLayer(newLayer);
|
||||
if (rider)
|
||||
rider->setLayer(newLayer);
|
||||
|
||||
// If we have lost our passenger for whatever reason, die early. Otherwise we just sit around forever.
|
||||
if( getContainCount() == 0 )
|
||||
getObject()->kill();
|
||||
|
||||
// the collide system doesn't always collide us with the ground if we fall into water.
|
||||
// so force the issue.
|
||||
Real waterZ;
|
||||
if (!getObject()->isEffectivelyDead()
|
||||
&& getObject()->getLayer() == LAYER_GROUND
|
||||
&& TheTerrainLogic->isUnderwater(paraPos->x, paraPos->y, &waterZ)
|
||||
&& (paraPos->z - waterZ) < d->m_killWhenLandingInWaterSlop)
|
||||
{
|
||||
getObject()->kill();
|
||||
}
|
||||
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::onContaining( Object *rider )
|
||||
{
|
||||
OpenContain::onContaining(rider);
|
||||
|
||||
// objects inside a transport are held
|
||||
rider->setDisabled( DISABLED_HELD );
|
||||
rider->setStatus(OBJECT_STATUS_PARACHUTING);
|
||||
|
||||
rider->clearAndSetModelConditionState(MODELCONDITION_PARACHUTING, MODELCONDITION_FREEFALL);
|
||||
m_needToUpdateRiderBones = true;
|
||||
|
||||
// position him correctly.
|
||||
positionRider(rider);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::onRemoving( Object *rider )
|
||||
{
|
||||
OpenContain::onRemoving(rider);
|
||||
|
||||
const ParachuteContainModuleData* d = getParachuteContainModuleData();
|
||||
|
||||
// object is no longer held inside a transport
|
||||
rider->clearDisabled( DISABLED_HELD );
|
||||
rider->clearStatus(OBJECT_STATUS_PARACHUTING);
|
||||
|
||||
// mark parachute as "no-collisions"... it is just ephemeral at this point,
|
||||
// and having the chute collide with the soldier (and both bounce apart) is
|
||||
// just dumb-lookin'...
|
||||
getObject()->setStatus(OBJECT_STATUS_NO_COLLISIONS);
|
||||
|
||||
// position him correctly.
|
||||
positionRider(rider);
|
||||
|
||||
rider->clearModelConditionFlags(MAKE_MODELCONDITION_MASK2(MODELCONDITION_FREEFALL, MODELCONDITION_PARACHUTING));
|
||||
m_needToUpdateRiderBones = true;
|
||||
|
||||
// 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);
|
||||
|
||||
Coord3D force;
|
||||
force.zero();
|
||||
physics->applyForce(&force); // force its physics to wake up... should be done when DISABLED_HELD is cleared, but it not, and scared to do it now.
|
||||
}
|
||||
|
||||
AIUpdateInterface* riderAI = rider->getAIUpdateInterface();
|
||||
if (riderAI)
|
||||
{
|
||||
Player* controller = rider->getControllingPlayer();
|
||||
if (controller && controller->isSkirmishAIPlayer())
|
||||
riderAI->aiHunt(CMD_FROM_AI); // hunt, as per Dustin's request.
|
||||
else
|
||||
riderAI->aiIdle(CMD_FROM_AI); // become idle.
|
||||
}
|
||||
|
||||
// if we land in the water, we die. alas.
|
||||
const Coord3D* riderPos = rider->getPosition();
|
||||
Real waterZ, terrainZ;
|
||||
if (TheTerrainLogic->isUnderwater(riderPos->x, riderPos->y, &waterZ, &terrainZ)
|
||||
&& riderPos->z <= waterZ + d->m_killWhenLandingInWaterSlop
|
||||
&& rider->getLayer() == LAYER_GROUND)
|
||||
{
|
||||
// don't call kill(); do it manually, so we can specify DEATH_FLOODED
|
||||
DamageInfo damageInfo;
|
||||
damageInfo.in.m_damageType = DAMAGE_WATER; // use this instead of UNRESISTABLE so we don't get a dusty damage effect
|
||||
damageInfo.in.m_deathType = DEATH_FLOODED;
|
||||
damageInfo.in.m_sourceID = INVALID_ID;
|
||||
damageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT;
|
||||
rider->attemptDamage( &damageInfo );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::positionRider(Object* rider)
|
||||
{
|
||||
updateBonePositions();
|
||||
updateOffsetsFromBones();
|
||||
|
||||
Coord3D pos = *getObject()->getPosition();
|
||||
///DUMPCOORD3D(&pos);
|
||||
pos.x += m_riderAttachOffset.x;
|
||||
pos.y += m_riderAttachOffset.y;
|
||||
pos.z += m_riderAttachOffset.z;
|
||||
//DUMPCOORD3D(&pos);
|
||||
rider->setPosition(&pos);
|
||||
|
||||
Real alt = rider->getHeightAboveTerrain();
|
||||
if (alt < 0.0f)
|
||||
{
|
||||
// don't let him go below ground.
|
||||
pos.z -= alt;
|
||||
rider->setPosition(&pos);
|
||||
}
|
||||
|
||||
rider->setOrientation(getObject()->getOrientation());
|
||||
|
||||
Drawable* draw = rider->getDrawable();
|
||||
if (draw)
|
||||
{
|
||||
if( rider->isDisabledByType( DISABLED_HELD ) )
|
||||
{
|
||||
Matrix3D tmp;
|
||||
calcSwayMtx(&m_riderSwayOffset, &tmp);
|
||||
draw->setInstanceMatrix(&tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
draw->setInstanceMatrix(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::positionContainedObjectsRelativeToContainer()
|
||||
{
|
||||
for(ContainedItemsList::const_iterator it = getContainList().begin(); it != getContainList().end(); ++it)
|
||||
{
|
||||
positionRider(*it);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::setOverrideDestination( const Coord3D *override )
|
||||
{
|
||||
// Instead of trying to float straight down, I am going to nail this spot.
|
||||
m_landingOverride = *override;
|
||||
m_isLandingOverrideSet = TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
// if we are airborne when killed, the guy falls screaming to his death...
|
||||
if (getObject()->isSignificantlyAboveTerrain())
|
||||
{
|
||||
Object* rider = (getContainCount() > 0) ? getContainList().front() : NULL;
|
||||
if (rider)
|
||||
{
|
||||
removeAllContained();
|
||||
const ParachuteContainModuleData* d = getParachuteContainModuleData();
|
||||
if (d->m_freeFallDamagePercent > 0.0f)
|
||||
{
|
||||
// do some damage just for losing your parachute.
|
||||
// not very realistic, but practical to help ensure that
|
||||
// you really do die from going "splat" on the ground.
|
||||
DamageInfo extraDamageInfo;
|
||||
extraDamageInfo.in.m_damageType = DAMAGE_FALLING;
|
||||
extraDamageInfo.in.m_deathType = DEATH_SPLATTED;
|
||||
extraDamageInfo.in.m_sourceID = damageInfo->in.m_sourceID;
|
||||
extraDamageInfo.in.m_amount = rider->getBodyModule()->getMaxHealth() * d->m_freeFallDamagePercent;
|
||||
rider->attemptDamage(&extraDamageInfo);
|
||||
}
|
||||
PhysicsBehavior* physics = rider->getPhysics();
|
||||
if (physics)
|
||||
{
|
||||
physics->setAllowToFall(true);
|
||||
physics->setIsInFreeFall(true); // bwah ha ha
|
||||
|
||||
Coord3D force;
|
||||
force.zero();
|
||||
physics->applyForce(&force); // force its physics to wake up... should be done when DISABLED_HELD is cleared, but it not, and scared to do it now.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OpenContain::onDie(damageInfo);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
|
||||
{
|
||||
// Note that other == null means "collide with ground"
|
||||
if( other == NULL )
|
||||
{
|
||||
// if we're in a container (eg, a transport plane), just ignore this...
|
||||
if( getObject()->getContainedBy() != NULL )
|
||||
return;
|
||||
|
||||
removeAllContained();
|
||||
|
||||
// TheGameLogic->destroyObject(obj);
|
||||
// kill it, so that the chute's SlowDeath will trigger!
|
||||
getObject()->kill();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
// pitch
|
||||
xfer->xferReal( &m_pitch );
|
||||
|
||||
// roll
|
||||
xfer->xferReal( &m_roll );
|
||||
|
||||
// pitch rage
|
||||
xfer->xferReal( &m_pitchRate );
|
||||
|
||||
// roll rate
|
||||
xfer->xferReal( &m_rollRate );
|
||||
|
||||
// start Z
|
||||
xfer->xferReal( &m_startZ );
|
||||
|
||||
// is landing override set
|
||||
xfer->xferBool( &m_isLandingOverrideSet );
|
||||
|
||||
// landing override
|
||||
xfer->xferCoord3D( &m_landingOverride );
|
||||
|
||||
// rider attach bone
|
||||
xfer->xferCoord3D( &m_riderAttachBone );
|
||||
|
||||
// rider sway bone
|
||||
xfer->xferCoord3D( &m_riderSwayBone );
|
||||
|
||||
// para attach bone
|
||||
xfer->xferCoord3D( &m_paraAttachBone );
|
||||
|
||||
// para sway bone
|
||||
xfer->xferCoord3D( &m_paraSwayBone );
|
||||
|
||||
// rider attach offset
|
||||
xfer->xferCoord3D( &m_riderAttachOffset );
|
||||
|
||||
// rider sway offset
|
||||
xfer->xferCoord3D( &m_riderSwayOffset );
|
||||
|
||||
// para attach offset
|
||||
xfer->xferCoord3D( &m_paraAttachOffset );
|
||||
|
||||
// para sway offset
|
||||
xfer->xferCoord3D( &m_paraSwayOffset );
|
||||
|
||||
// need to update rider bones
|
||||
xfer->xferBool( &m_needToUpdateRiderBones );
|
||||
|
||||
// need to update para bones
|
||||
xfer->xferBool( &m_needToUpdateParaBones );
|
||||
|
||||
// opened
|
||||
xfer->xferBool( &m_opened );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ParachuteContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: RailedTransportContain.cpp ///////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, August 2002
|
||||
// Desc: Railed Transport Contain Module
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/RailedTransportContain.h"
|
||||
#include "GameLogic/Module/RailedTransportDockUpdate.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static RailedTransportDockUpdateInterface *getRailedTransportDockUpdateInterface( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return NULL;
|
||||
|
||||
// find us our dock interface
|
||||
RailedTransportDockUpdateInterface *rtdui = NULL;
|
||||
for( BehaviorModule **u = obj->getBehaviorModules(); *u; ++u )
|
||||
if( (rtdui = (*u)->getRailedTransportDockUpdateInterface()) != NULL )
|
||||
break;
|
||||
|
||||
return rtdui;
|
||||
|
||||
} // end getRailedTransportDockUpdateInterface
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
RailedTransportContain::RailedTransportContain( Thing *thing, const ModuleData *moduleData )
|
||||
: TransportContain( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end RailedTransportContain
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
RailedTransportContain::~RailedTransportContain( void )
|
||||
{
|
||||
|
||||
} // end ~RailedTransportContain
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RailedTransportContain::onRemoving( Object *obj )
|
||||
{
|
||||
|
||||
// extend functionality
|
||||
TransportContain::onRemoving( obj );
|
||||
|
||||
// once we have removed all our contents we are "open" for docking again for more transportation
|
||||
if( getContainCount() == 0 )
|
||||
{
|
||||
DockUpdateInterface *dui = getObject()->getDockUpdateInterface();
|
||||
if( dui )
|
||||
dui->setDockOpen( TRUE );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end onRemoving
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool RailedTransportContain::isSpecificRiderFreeToExit( Object *obj )
|
||||
{
|
||||
Object *us = getObject();
|
||||
|
||||
// if we are in transit we cannot exit, when we're in transit, the dock is closed
|
||||
DockUpdateInterface *dui = us->getDockUpdateInterface();
|
||||
if( dui && dui->isDockOpen() == FALSE )
|
||||
return FALSE;
|
||||
|
||||
// we can now exit, note we're not extending the base class cause *we* handle it all
|
||||
return TRUE;
|
||||
|
||||
} // end isSpecificRiderFreeToExit
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RailedTransportContain::exitObjectViaDoor( Object *newObj, ExitDoorType exitDoor )
|
||||
{
|
||||
RailedTransportDockUpdateInterface *rtdui = getRailedTransportDockUpdateInterface();
|
||||
|
||||
if( rtdui == NULL )
|
||||
return;
|
||||
|
||||
// tell the railed dock to exit ONE object, this one
|
||||
rtdui->unloadSingleObject( newObj );
|
||||
|
||||
} // end exitObjectViaDoor
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RailedTransportContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
TransportContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RailedTransportContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
TransportContain::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RailedTransportContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
TransportContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: TransportContain.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, March 2002
|
||||
// Desc: Contain module for transport units.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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/Object.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
TransportContainModuleData::TransportContainModuleData()
|
||||
{
|
||||
|
||||
m_slotCapacity = 0;
|
||||
m_scatterNearbyOnExit = true;
|
||||
m_orientLikeContainerOnExit = false;
|
||||
m_keepContainerVelocityOnExit = false;
|
||||
m_goAggressiveOnExit = FALSE;
|
||||
m_resetMoodCheckTimeOnExit = true;
|
||||
m_destroyRidersWhoAreNotFreeToExit = false;
|
||||
m_exitPitchRate = 0.0f;
|
||||
m_initialPayload.count = 0;
|
||||
m_healthRegen = 0.0f;
|
||||
m_exitDelay = 0;
|
||||
|
||||
//
|
||||
// by default we say that transports can have infantry inside them, this will be totally
|
||||
// overwritten by any data provided from the INI entry tho
|
||||
//
|
||||
m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContainModuleData::parseInitialPayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
TransportContainModuleData* self = (TransportContainModuleData*)instance;
|
||||
const char* name = ini->getNextToken();
|
||||
const char* countStr = ini->getNextTokenOrNull();
|
||||
Int count = countStr ? INI::scanInt(countStr) : 1;
|
||||
|
||||
self->m_initialPayload.name.set(name);
|
||||
self->m_initialPayload.count = count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContainModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
OpenContainModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "Slots", INI::parseInt, NULL, offsetof( TransportContainModuleData, m_slotCapacity ) },
|
||||
{ "ScatterNearbyOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_scatterNearbyOnExit ) },
|
||||
{ "OrientLikeContainerOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_orientLikeContainerOnExit ) },
|
||||
{ "KeepContainerVelocityOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_keepContainerVelocityOnExit ) },
|
||||
{ "GoAggressiveOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_goAggressiveOnExit ) },
|
||||
{ "ResetMoodCheckTimeOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_resetMoodCheckTimeOnExit ) },
|
||||
{ "DestroyRidersWhoAreNotFreeToExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_destroyRidersWhoAreNotFreeToExit ) },
|
||||
{ "ExitBone", INI::parseAsciiString, NULL, offsetof( TransportContainModuleData, m_exitBone ) },
|
||||
{ "ExitPitchRate", INI::parseAngularVelocityReal, NULL, offsetof( TransportContainModuleData, m_exitPitchRate ) },
|
||||
{ "InitialPayload", parseInitialPayload, NULL, 0 },
|
||||
{ "HealthRegen%PerSec", INI::parseReal, NULL, offsetof( TransportContainModuleData, m_healthRegen ) },
|
||||
{ "ExitDelay", INI::parseDurationUnsignedInt, NULL, offsetof( TransportContainModuleData, m_exitDelay ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int TransportContain::getContainMax( void ) const
|
||||
{
|
||||
if (getTransportContainModuleData())
|
||||
return getTransportContainModuleData()->m_slotCapacity;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TransportContain::TransportContain( Thing *thing, const ModuleData *moduleData ) :
|
||||
OpenContain( thing, moduleData )
|
||||
{
|
||||
m_extraSlotsInUse = 0;
|
||||
m_frameExitNotBusy = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TransportContain::~TransportContain( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
can this container contain this kind of object?
|
||||
and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
|
||||
*/
|
||||
Bool TransportContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
|
||||
{
|
||||
|
||||
// sanity
|
||||
if (!rider)
|
||||
return false;
|
||||
|
||||
// The point of this new code is to determine when something is a "fake" container, to
|
||||
// look at the object inside of it to use that as the valid check. There is a case, when a
|
||||
// paratrooper (an infantry contained in a parachute). In this case, when we pass this object
|
||||
// to contain in a transport plane, we want to check the infantry, not the parachute.
|
||||
if (rider->getContain() && rider->getContain()->isSpecialZeroSlotContainer())
|
||||
{
|
||||
// Report the first thing inside it!
|
||||
const ContainedItemsList *items = rider->getContain()->getContainedItemsList();
|
||||
if (items && !items->empty())
|
||||
{
|
||||
if (items->front())
|
||||
{
|
||||
// Replace the object we are checking with the *first* object contained within it.
|
||||
rider = items->front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// extend functionality
|
||||
if( OpenContain::isValidContainerFor( rider, checkCapacity ) == false )
|
||||
return false;
|
||||
|
||||
// // only allied objects can be transported.
|
||||
// // order matters: we want to know if I consider it to be an ally, not vice versa
|
||||
// if (getObject()->getRelationship(rider) != ALLIES)
|
||||
// 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)
|
||||
{
|
||||
return (m_extraSlotsInUse + getContainCount() + transportSlotCount <= getContainMax());
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransportContain::onContaining( Object *rider )
|
||||
{
|
||||
OpenContain::onContaining(rider);
|
||||
|
||||
// objects inside a transport are held
|
||||
rider->setDisabled( DISABLED_HELD );
|
||||
|
||||
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 go from holding nothing to holding something we have a model condition
|
||||
// to visually show the change
|
||||
//
|
||||
if( getContainCount() == 1 )
|
||||
{
|
||||
Drawable *draw = getObject()->getDrawable();
|
||||
|
||||
if( draw )
|
||||
draw->setModelConditionState( MODELCONDITION_LOADED );
|
||||
|
||||
} // end if
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransportContain::onRemoving( Object *rider )
|
||||
{
|
||||
OpenContain::onRemoving(rider);
|
||||
|
||||
// object is no longer held inside a transport
|
||||
rider->clearDisabled( DISABLED_HELD );
|
||||
|
||||
const TransportContainModuleData* d = getTransportContainModuleData();
|
||||
|
||||
if (!d->m_exitBone.isEmpty())
|
||||
{
|
||||
Drawable* draw = getObject()->getDrawable();
|
||||
if (draw)
|
||||
{
|
||||
Coord3D bonePos, worldPos;
|
||||
if (draw->getPristineBonePositions(d->m_exitBone.str(), 0, &bonePos, NULL, 1) == 1)
|
||||
{
|
||||
getObject()->convertBonePosToWorldPos(&bonePos, NULL, &worldPos, NULL);
|
||||
rider->setPosition(&worldPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
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( AI_AGGRESSIVE );
|
||||
}
|
||||
if (getObject()->isEffectivelyDead()) {
|
||||
scatterToNearbyPosition(rider);
|
||||
}
|
||||
if( d->m_resetMoodCheckTimeOnExit && rider->getAI() )
|
||||
{
|
||||
rider->getAI()->wakeUpAndAttemptToTarget();
|
||||
}
|
||||
|
||||
m_frameExitNotBusy = TheGameLogic->getFrame() + d->m_exitDelay;
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::createPayload()
|
||||
{
|
||||
TransportContainModuleData* self = (TransportContainModuleData*)getTransportContainModuleData();
|
||||
|
||||
Int count = self->m_initialPayload.count;
|
||||
const ThingTemplate* payloadTemplate = TheThingFactory->findTemplate( self->m_initialPayload.name );
|
||||
Object* object = getObject();
|
||||
ContainModuleInterface *contain = object->getContain();
|
||||
|
||||
if( contain )
|
||||
{
|
||||
contain->enableLoadSounds( FALSE );
|
||||
for( int i = 0; i < count; i++ )
|
||||
{
|
||||
//We are creating a transport that comes with a initial payload, so add it now!
|
||||
Object* payload = TheThingFactory->newObject( payloadTemplate, object->getControllingPlayer()->getDefaultTeam() );
|
||||
if( contain->isValidContainerFor( payload, true ) )
|
||||
{
|
||||
contain->addToContain( payload );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str() ) );
|
||||
}
|
||||
}
|
||||
contain->enableLoadSounds( TRUE );
|
||||
}
|
||||
|
||||
m_payloadCreated = TRUE;
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime TransportContain::update()
|
||||
{
|
||||
const TransportContainModuleData *moduleData = getTransportContainModuleData();
|
||||
|
||||
if( m_payloadCreated == FALSE )
|
||||
createPayload();
|
||||
|
||||
if( moduleData && moduleData->m_healthRegen )
|
||||
{
|
||||
ContainModuleInterface *contain = getObject()->getContain();
|
||||
if( contain )
|
||||
{
|
||||
//This transport has a health regeneration value, so go through and heal all inside.
|
||||
const ContainedItemsList* items = contain->getContainedItemsList();
|
||||
if( items )
|
||||
{
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = items->begin();
|
||||
|
||||
while( *it )
|
||||
{
|
||||
Object *object = *it;
|
||||
|
||||
//Advance to the next iterator
|
||||
it++;
|
||||
|
||||
//Determine if we need healing or not.
|
||||
BodyModuleInterface *body = object->getBodyModule();
|
||||
if( body->getHealth() < body->getMaxHealth() )
|
||||
{
|
||||
//Calculate the health to be regenerated on each unit.
|
||||
Real regen = body->getMaxHealth() * moduleData->m_healthRegen / 100.0f * SECONDS_PER_LOGICFRAME_REAL;
|
||||
|
||||
//Perform the actual healing for this frame.
|
||||
// DamageInfo damageInfo;
|
||||
// damageInfo.in.m_damageType = DAMAGE_HEALING;
|
||||
// damageInfo.in.m_deathType = DEATH_NONE;
|
||||
// damageInfo.in.m_sourceID = getObject()->getID();
|
||||
// damageInfo.in.m_amount = regen;
|
||||
// object->attemptDamage( &damageInfo );
|
||||
object->attemptHealing( regen, getObject() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OpenContain::update(); //extend
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::unreserveDoorForExit( ExitDoorType exitDoor )
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::killRidersWhoAreNotFreeToExit()
|
||||
{
|
||||
const TransportContainModuleData* d = getTransportContainModuleData();
|
||||
ContainedItemsList::const_iterator it = getContainList().begin();
|
||||
while( it != getContainList().end() )
|
||||
{
|
||||
// get the object
|
||||
Object *obj = *it;
|
||||
// increment the iterator, since death will pull this guy from the list somewhere else
|
||||
++it;
|
||||
|
||||
if (!isSpecificRiderFreeToExit(obj)) // only TransportContain has a meaningful failure for isFreeToExit
|
||||
{
|
||||
// If we cannot exit this guy legally, kill the bastard before removeAllContained forces him out.
|
||||
if (d->m_destroyRidersWhoAreNotFreeToExit)
|
||||
TheGameLogic->destroyObject(obj);
|
||||
else
|
||||
obj->kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool TransportContain::isSpecificRiderFreeToExit(Object* specificObject)
|
||||
{
|
||||
if( specificObject == NULL )
|
||||
return TRUE; // I can, in general, exit people.
|
||||
|
||||
// This is a override, not an extend. I will check for game legality for
|
||||
// okaying the call to exitObjectViaDoor.
|
||||
const Object* me = getObject();
|
||||
|
||||
// this is present solely for some transports to override, so that they can land before
|
||||
// allowing people to exit...
|
||||
const AIUpdateInterface* ai = me->getAIUpdateInterface();
|
||||
if (ai && ai->getAiFreeToExit(specificObject) != FREE_TO_EXIT)
|
||||
return FALSE;
|
||||
|
||||
// I can always kick people out if I am in the air, I know what I'm doing
|
||||
if (me->isUsingAirborneLocomotor())
|
||||
return TRUE;
|
||||
|
||||
const Coord3D *myPosition = me->getPosition();
|
||||
if (!specificObject->getAIUpdateInterface())
|
||||
return FALSE;
|
||||
|
||||
const Locomotor *hisLocomotor = specificObject->getAIUpdateInterface()->getCurLocomotor();
|
||||
if( hisLocomotor == FALSE )
|
||||
return FALSE;
|
||||
|
||||
// He can't get to this spot naturally, so I can't force him there. (amphib transport)
|
||||
if (!TheAI->pathfinder()->validMovementTerrain(me->getLayer(), hisLocomotor, myPosition))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ExitDoorType TransportContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
|
||||
{
|
||||
return isSpecificRiderFreeToExit(specificObject) ? DOOR_1 : DOOR_NONE_AVAILABLE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Bool TransportContain::isExitBusy() const ///< Contain style exiters are getting the ability to space out exits, so ask this before reserveDoor as a kind of no-commitment check.
|
||||
{
|
||||
return TheGameLogic->getFrame() < m_frameExitNotBusy;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::onCapture( Player *oldOwner, Player *newOwner )
|
||||
{
|
||||
if( oldOwner != newOwner )
|
||||
{
|
||||
if( getObject()->isDisabledByType( DISABLED_UNMANNED ) )
|
||||
{
|
||||
//If this vehicle was sniped, then instantly eject everyone (otherwise, the next guy to eject will recapture it).
|
||||
removeAllContained();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Use standard
|
||||
orderAllPassengersToExit( CMD_FROM_AI );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
// payload created
|
||||
xfer->xferBool( &m_payloadCreated );
|
||||
|
||||
// extra slots in use
|
||||
xfer->xferInt( &m_extraSlotsInUse );
|
||||
|
||||
// frame exit not busy
|
||||
xfer->xferUnsignedInt( &m_frameExitNotBusy );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransportContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: TunnelContain.cpp ////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, March 2002
|
||||
// Desc: A version of OpenContain that overrides where the passengers are stored: the Owning Player's
|
||||
// TunnelTracker. All queries about capacity and contents are also redirected.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/TunnelTracker.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/OpenContain.h"
|
||||
#include "GameLogic/Module/TunnelContain.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TunnelContain::TunnelContain( Thing *thing, const ModuleData* moduleData ) : OpenContain( thing, moduleData )
|
||||
{
|
||||
m_needToRunOnBuildComplete = true;
|
||||
m_isCurrentlyRegistered = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TunnelContain::~TunnelContain()
|
||||
{
|
||||
}
|
||||
|
||||
void TunnelContain::addToContainList( Object *obj )
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
owningPlayer->getTunnelSystem()->addToContainList( obj );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Remove 'obj' from the m_containList of objects in this module.
|
||||
* This will trigger an onRemoving event for the object that this module
|
||||
* is a part of and an onRemovedFrom event for the object being removed */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::removeFromContain( Object *obj, Bool exposeStealthUnits )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
// trigger an onRemoving event for 'm_object' no longer containing 'itemToRemove->m_object'
|
||||
if( getObject()->getContain() )
|
||||
{
|
||||
getObject()->getContain()->onRemoving( obj );
|
||||
}
|
||||
|
||||
// trigger an onRemovedFrom event for 'remove'
|
||||
obj->onRemovedFrom( getObject() );
|
||||
|
||||
//
|
||||
// we can only remove this object from the contains list of this module if
|
||||
// it is actually contained by this module
|
||||
//
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer == NULL )
|
||||
return; //game tear down. We do the onRemove* stuff first because this is allowed to fail but that still needs to be done
|
||||
|
||||
if( ! owningPlayer->getTunnelSystem()->isInContainer( obj ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
owningPlayer->getTunnelSystem()->removeFromContain( obj, exposeStealthUnits );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Remove all contained objects from the contained list */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::removeAllContained( Bool exposeStealthUnits )
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
const ContainedItemsList *fullList = owningPlayer->getTunnelSystem()->getContainedItemsList();
|
||||
|
||||
Object *obj;
|
||||
ContainedItemsList::const_iterator it;
|
||||
it = (*fullList).begin();
|
||||
while( it != (*fullList).end() )
|
||||
{
|
||||
obj = *it;
|
||||
it++;
|
||||
removeFromContain( obj, exposeStealthUnits );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Iterate the contained list and call the callback on each of the objects */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
owningPlayer->getTunnelSystem()->iterateContained( func, userData, reverse );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onContaining( Object *obj )
|
||||
{
|
||||
OpenContain::onContaining(obj);
|
||||
|
||||
// objects inside a building are held
|
||||
obj->setDisabled( DISABLED_HELD );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onRemoving( Object *obj )
|
||||
{
|
||||
OpenContain::onRemoving(obj);
|
||||
|
||||
// object is no longer held inside a garrisoned building
|
||||
obj->clearDisabled( DISABLED_HELD );
|
||||
|
||||
/// place the object in the world at position of the container m_object
|
||||
ThePartitionManager->registerObject( obj );
|
||||
obj->setPosition( getObject()->getPosition() );
|
||||
if( obj->getDrawable() )
|
||||
{
|
||||
obj->setSafeOcclusionFrame(TheGameLogic->getFrame()+obj->getTemplate()->getOcclusionDelay());
|
||||
obj->getDrawable()->setDrawableHidden( false );
|
||||
}
|
||||
|
||||
doUnloadSound();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onSelling()
|
||||
{
|
||||
// A TunnelContain tells everyone to leave if this is the last tunnel
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer == NULL )
|
||||
return;
|
||||
TunnelTracker *tunnelTracker = owningPlayer->getTunnelSystem();
|
||||
if( tunnelTracker == NULL )
|
||||
return;
|
||||
|
||||
// We are the last tunnel, so kick everyone out. This makes tunnels act like Palace and Bunker
|
||||
// rather than killing the occupants as if the last tunnel died.
|
||||
if( tunnelTracker->friend_getTunnelCount() == 1 )
|
||||
removeAllContained(FALSE);// Can't be order to exit, as I have no time to organize their exits.
|
||||
// If they don't go right now, I will delete them in a moment
|
||||
|
||||
// Unregister after the kick out, or else the unregistering will activate a cavein-kill.
|
||||
// We need to do this in case someone sells their last two tunnels at the same time.
|
||||
if( m_isCurrentlyRegistered )
|
||||
{
|
||||
tunnelTracker->onTunnelDestroyed( getObject() );
|
||||
m_isCurrentlyRegistered = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool TunnelContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
return owningPlayer->getTunnelSystem()->isValidContainerFor( obj, checkCapacity );
|
||||
}
|
||||
|
||||
UnsignedInt TunnelContain::getContainCount() const
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer && owningPlayer->getTunnelSystem() )
|
||||
{
|
||||
return owningPlayer->getTunnelSystem()->getContainCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int TunnelContain::getContainMax( void ) const
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
return owningPlayer->getTunnelSystem()->getContainMax();
|
||||
}
|
||||
|
||||
const ContainedItemsList* TunnelContain::getContainedItemsList() const
|
||||
{
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
return owningPlayer->getTunnelSystem()->getContainedItemsList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::scatterToNearbyPosition(Object* obj)
|
||||
{
|
||||
Object *theContainer = getObject();
|
||||
|
||||
//
|
||||
// for now we will just set the position of the object that is being removed from us
|
||||
// at a random angle away from our center out some distance
|
||||
//
|
||||
|
||||
//
|
||||
// pick an angle that is in the view of the current camera position so that
|
||||
// the thing will come out "toward" the player and they can see it
|
||||
// NOPE, can't do that ... all players screen angles will be different, unless
|
||||
// we maintain the angle of each players screen in the player structure or something
|
||||
//
|
||||
Real angle = GameLogicRandomValueReal( 0.0f, 2.0f * PI );
|
||||
// angle = TheTacticalView->getAngle();
|
||||
// angle -= GameLogicRandomValueReal( PI / 3.0f, 2.0f * (PI / 3.0F) );
|
||||
|
||||
Real minRadius = theContainer->getGeometryInfo().getBoundingCircleRadius();
|
||||
Real maxRadius = minRadius + minRadius / 2.0f;
|
||||
const Coord3D *containerPos = theContainer->getPosition();
|
||||
Real dist = GameLogicRandomValueReal( minRadius, maxRadius );
|
||||
|
||||
Coord3D pos;
|
||||
pos.x = dist * Cos( angle ) + containerPos->x;
|
||||
pos.y = dist * Sin( angle ) + containerPos->y;
|
||||
pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
|
||||
|
||||
// set orientation
|
||||
obj->setOrientation( angle );
|
||||
|
||||
AIUpdateInterface *ai = obj->getAIUpdateInterface();
|
||||
if( ai )
|
||||
{
|
||||
// set position of the object at center of building and move them toward pos
|
||||
obj->setPosition( theContainer->getPosition() );
|
||||
ai->ignoreObstacle(theContainer);
|
||||
ai->aiMoveToPosition( &pos, CMD_FROM_AI );
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// no ai, just set position at the target pos
|
||||
obj->setPosition( &pos );
|
||||
|
||||
} // end else
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
// override the onDie we inherit from OpenContain. no super call.
|
||||
if (!getTunnelContainModuleData()->m_dieMuxData.isDieApplicable(getObject(), damageInfo))
|
||||
return;
|
||||
|
||||
if( !m_isCurrentlyRegistered )
|
||||
return;//it isn't registered as a tunnel
|
||||
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer == NULL )
|
||||
return;
|
||||
TunnelTracker *tunnelTracker = owningPlayer->getTunnelSystem();
|
||||
if( tunnelTracker == NULL )
|
||||
return;
|
||||
|
||||
tunnelTracker->onTunnelDestroyed( getObject() );
|
||||
m_isCurrentlyRegistered = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onDelete( void )
|
||||
{
|
||||
// Being sold is a straight up delete. no death
|
||||
|
||||
if( !m_isCurrentlyRegistered )
|
||||
return;//it isn't registered as a tunnel
|
||||
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer == NULL )
|
||||
return;
|
||||
TunnelTracker *tunnelTracker = owningPlayer->getTunnelSystem();
|
||||
if( tunnelTracker == NULL )
|
||||
return;
|
||||
|
||||
tunnelTracker->onTunnelDestroyed( getObject() );
|
||||
m_isCurrentlyRegistered = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onCreate( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::onBuildComplete( void )
|
||||
{
|
||||
if( ! shouldDoOnBuildComplete() )
|
||||
return;
|
||||
|
||||
m_needToRunOnBuildComplete = false;
|
||||
|
||||
Player *owningPlayer = getObject()->getControllingPlayer();
|
||||
if( owningPlayer == NULL )
|
||||
return;
|
||||
TunnelTracker *tunnelTracker = owningPlayer->getTunnelSystem();
|
||||
if( tunnelTracker == NULL )
|
||||
return;
|
||||
|
||||
tunnelTracker->onTunnelCreated( getObject() );
|
||||
m_isCurrentlyRegistered = TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Per frame update */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime TunnelContain::update( void )
|
||||
{
|
||||
// extending functionality to heal the units within the tunnel system
|
||||
OpenContain::update();
|
||||
const TunnelContainModuleData *modData = getTunnelContainModuleData();
|
||||
|
||||
Object *obj = getObject();
|
||||
Player *controllingPlayer = NULL;
|
||||
if (obj)
|
||||
{
|
||||
controllingPlayer = obj->getControllingPlayer();
|
||||
}
|
||||
if (controllingPlayer)
|
||||
{
|
||||
TunnelTracker *tunnelSystem = controllingPlayer->getTunnelSystem();
|
||||
if (tunnelSystem)
|
||||
{
|
||||
tunnelSystem->healObjects(modData->m_framesForFullHeal);
|
||||
}
|
||||
|
||||
// check for attacked.
|
||||
BodyModuleInterface *body = obj->getBodyModule();
|
||||
if (body) {
|
||||
const DamageInfo *info = body->getLastDamageInfo();
|
||||
if (info) {
|
||||
if (body->getLastDamageTimestamp() + LOGICFRAMES_PER_SECOND > TheGameLogic->getFrame()) {
|
||||
// winner.
|
||||
ObjectID attackerID = info->in.m_sourceID;
|
||||
Object *attacker = TheGameLogic->findObjectByID(attackerID);
|
||||
if( attacker )
|
||||
{
|
||||
if (obj->getRelationship(attacker) == ENEMIES) {
|
||||
tunnelSystem->updateNemesis(attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return UPDATE_SLEEP_NONE;
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
OpenContain::xfer( xfer );
|
||||
|
||||
// need to run on build complete
|
||||
xfer->xferBool( &m_needToRunOnBuildComplete );
|
||||
|
||||
// Currently registered with owning player
|
||||
xfer->xferBool( &m_isCurrentlyRegistered );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TunnelContain::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
OpenContain::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CreateModule.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, October 2002
|
||||
// Desc: Create module base class
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/CreateModule.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CreateModule::CreateModule( Thing *thing, const ModuleData* moduleData )
|
||||
: BehaviorModule( thing, moduleData ),
|
||||
m_needToRunOnBuildComplete(TRUE)
|
||||
{
|
||||
|
||||
} // end createModule
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
CreateModule::~CreateModule()
|
||||
{
|
||||
|
||||
} // end ~CreateModule
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
// need to run on build complete
|
||||
xfer->xferBool( &m_needToRunOnBuildComplete );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // ene loadPostProcess
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GrantUpgradeCreate.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Kris Morness, April 2002
|
||||
// Desc: GrantUpgrade create module
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_OBJECT_STATUS_NAMES
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Upgrade.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/GrantUpgradeCreate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
GrantUpgradeCreateModuleData::GrantUpgradeCreateModuleData()
|
||||
{
|
||||
m_upgradeName = "";
|
||||
m_exemptStatus = OBJECT_STATUS_NONE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GrantUpgradeCreateModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
CreateModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "UpgradeToGrant", INI::parseAsciiString, NULL, offsetof( GrantUpgradeCreateModuleData, m_upgradeName ) },
|
||||
{ "ExemptStatus", INI::parseBitString32, TheObjectStatusBitNames, offsetof( GrantUpgradeCreateModuleData, m_exemptStatus ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GrantUpgradeCreate::GrantUpgradeCreate( Thing *thing, const ModuleData* moduleData ) : CreateModule( thing, moduleData )
|
||||
{
|
||||
} // end GrantUpgradeCreate
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GrantUpgradeCreate::~GrantUpgradeCreate( void )
|
||||
{
|
||||
|
||||
} // end ~GrantUpgradeCreate
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The create callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GrantUpgradeCreate::onCreate( void )
|
||||
{
|
||||
|
||||
ObjectStatusBits exemptStatus = (ObjectStatusBits)getGrantUpgradeCreateModuleData()->m_exemptStatus;
|
||||
ObjectStatusBits currentStatus = (ObjectStatusBits)getObject()->getStatusBits();
|
||||
if( BitTest( exemptStatus, OBJECT_STATUS_UNDER_CONSTRUCTION ) == TRUE )
|
||||
{
|
||||
if( BitTest( currentStatus, OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE )
|
||||
{
|
||||
const UpgradeTemplate *upgradeTemplate = TheUpgradeCenter->findUpgrade( getGrantUpgradeCreateModuleData()->m_upgradeName );
|
||||
if( !upgradeTemplate )
|
||||
{
|
||||
DEBUG_ASSERTCRASH( 0, ("GrantUpdateCreate for %s can't find upgrade template %s.", getObject()->getName(), getGrantUpgradeCreateModuleData()->m_upgradeName ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if( upgradeTemplate->getUpgradeType() == UPGRADE_TYPE_PLAYER )
|
||||
{
|
||||
// get the player
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
player->addUpgrade( upgradeTemplate, UPGRADE_STATUS_COMPLETE );
|
||||
}
|
||||
else
|
||||
{
|
||||
getObject()->giveUpgrade( upgradeTemplate );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end onCreate
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GrantUpgradeCreate::onBuildComplete( void )
|
||||
{
|
||||
if( ! shouldDoOnBuildComplete() )
|
||||
return;
|
||||
|
||||
CreateModule::onBuildComplete(); // extend
|
||||
|
||||
const UpgradeTemplate *upgradeTemplate = TheUpgradeCenter->findUpgrade( getGrantUpgradeCreateModuleData()->m_upgradeName );
|
||||
if( !upgradeTemplate )
|
||||
{
|
||||
DEBUG_ASSERTCRASH( 0, ("GrantUpdateCreate for %s can't find upgrade template %s.", getObject()->getName(), getGrantUpgradeCreateModuleData()->m_upgradeName ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if( upgradeTemplate->getUpgradeType() == UPGRADE_TYPE_PLAYER )
|
||||
{
|
||||
// get the player
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
player->addUpgrade( upgradeTemplate, UPGRADE_STATUS_COMPLETE );
|
||||
}
|
||||
else
|
||||
{
|
||||
getObject()->giveUpgrade( upgradeTemplate );
|
||||
}
|
||||
} // end onBuildComplete
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GrantUpgradeCreate::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GrantUpgradeCreate::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CreateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GrantUpgradeCreate::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PreorderCreate.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell, December 2002
|
||||
// Desc: When a building is created, set the preorder status if necessary
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/PreorderCreate.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PreorderCreate::PreorderCreate( Thing *thing, const ModuleData* moduleData ) : CreateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PreorderCreate::~PreorderCreate( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PreorderCreate::onCreate( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PreorderCreate::onBuildComplete( void )
|
||||
{
|
||||
if (getObject()->getControllingPlayer()->didPlayerPreorder())
|
||||
{
|
||||
getObject()->setModelConditionState( MODELCONDITION_PREORDER );
|
||||
}
|
||||
else
|
||||
{
|
||||
getObject()->clearModelConditionState( MODELCONDITION_PREORDER );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PreorderCreate::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PreorderCreate::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CreateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PreorderCreate::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SpecialPowerCreate.h /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell, May 2002
|
||||
// Desc: When a building is created, tell special powers to start counting down
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/SpecialPowerCreate.h"
|
||||
#include "GameLogic/Module/SpecialPowerModule.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerCreate::SpecialPowerCreate( Thing *thing, const ModuleData* moduleData ) : CreateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerCreate::~SpecialPowerCreate( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCreate::onCreate( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCreate::onBuildComplete( void )
|
||||
{
|
||||
if( ! shouldDoOnBuildComplete() )
|
||||
return;
|
||||
|
||||
CreateModule::onBuildComplete(); // extend
|
||||
|
||||
for (BehaviorModule** m = getObject()->getBehaviorModules(); *m; ++m)
|
||||
{
|
||||
SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
|
||||
if (!sp)
|
||||
continue;
|
||||
|
||||
sp->onSpecialPowerCreation();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCreate::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCreate::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CreateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCreate::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SupplyCenterCreate.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood Feb 2002
|
||||
// Desc: When a Supply Center is created, it needs to update all the Resource brains in all players
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/ResourceGatheringManager.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/SupplyCenterCreate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyCenterCreate::SupplyCenterCreate( Thing *thing, const ModuleData* moduleData ) : CreateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyCenterCreate::~SupplyCenterCreate( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SupplyCenterCreate::onCreate( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SupplyCenterCreate::onBuildComplete( void )
|
||||
{
|
||||
if( ! shouldDoOnBuildComplete() )
|
||||
return;
|
||||
|
||||
CreateModule::onBuildComplete(); // extend
|
||||
|
||||
if( ThePlayerList == NULL )
|
||||
return;
|
||||
|
||||
for( Int playerIndex = ThePlayerList->getPlayerCount() - 1; playerIndex >= 0; playerIndex-- )
|
||||
{
|
||||
Player *currentPlayer = ThePlayerList->getNthPlayer( playerIndex );
|
||||
if( currentPlayer == NULL )
|
||||
continue;
|
||||
ResourceGatheringManager *manager = currentPlayer->getResourceGatheringManager();
|
||||
if( manager == NULL )
|
||||
continue;
|
||||
manager->addSupplyCenter( getObject() );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyCenterCreate::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyCenterCreate::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CreateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyCenterCreate::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SupplyWarehouseCreate.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood Feb 2002
|
||||
// Desc: When a Supply Center is created, it needs to update all the Resource brains in all players
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/ResourceGatheringManager.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/SupplyWarehouseCreate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyWarehouseCreate::SupplyWarehouseCreate( Thing *thing, const ModuleData* moduleData ) : CreateModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SupplyWarehouseCreate::~SupplyWarehouseCreate( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCreate::onCreate( void )
|
||||
{
|
||||
// Warehouses are never Built.
|
||||
if( ThePlayerList == NULL )
|
||||
return;
|
||||
|
||||
for( Int playerIndex = ThePlayerList->getPlayerCount() - 1; playerIndex >= 0; playerIndex-- )
|
||||
{
|
||||
Player *currentPlayer = ThePlayerList->getNthPlayer( playerIndex );
|
||||
if( currentPlayer == NULL )
|
||||
continue;
|
||||
ResourceGatheringManager *manager = currentPlayer->getResourceGatheringManager();
|
||||
if( manager == NULL )
|
||||
continue;
|
||||
manager->addSupplyWarehouse( getObject() );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCreate::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCreate::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CreateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SupplyWarehouseCreate::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: VeterancyGainCreate.cpp //////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, August 2002
|
||||
// Desc: On creation, will set Object's veterancy level if required Science is present.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[]
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/VeterancyGainCreate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
VeterancyGainCreateModuleData::VeterancyGainCreateModuleData()
|
||||
{
|
||||
m_startingLevel = LEVEL_REGULAR;
|
||||
m_scienceRequired = SCIENCE_INVALID;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void VeterancyGainCreateModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
CreateModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "StartingLevel", INI::parseIndexList, TheVeterancyNames, offsetof( VeterancyGainCreateModuleData, m_startingLevel ) },
|
||||
{ "ScienceRequired", INI::parseScience, NULL, offsetof( VeterancyGainCreateModuleData, m_scienceRequired ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
VeterancyGainCreate::VeterancyGainCreate( Thing *thing, const ModuleData* moduleData ) : CreateModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
VeterancyGainCreate::~VeterancyGainCreate( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The create callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void VeterancyGainCreate::onCreate( void )
|
||||
{
|
||||
|
||||
// When produced normally, this Object will ask the Player if the correct Science is known for it
|
||||
// to set its level to the given level
|
||||
|
||||
const VeterancyGainCreateModuleData *md = getVeterancyGainCreateModuleData();
|
||||
Player *myPlayer = getObject()->getControllingPlayer();
|
||||
if( myPlayer && (md->m_scienceRequired == SCIENCE_INVALID ||
|
||||
myPlayer->hasScience( md->m_scienceRequired )) )
|
||||
{
|
||||
ExperienceTracker* myExp = getObject()->getExperienceTracker();
|
||||
if( myExp && myExp->isTrainable() )
|
||||
{
|
||||
// srj sez: use "setMin" here so that we never lose levels
|
||||
myExp->setMinVeterancyLevel( md->m_startingLevel );// sVL can override isTrainable, but this module should not.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void VeterancyGainCreate::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void VeterancyGainCreate::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
CreateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void VeterancyGainCreate::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
CreateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BoneFXDamage.cpp ///////////////////////////////////////////////////////////////////
|
||||
// Author: Bryan Cleveland, March 2002
|
||||
// Desc: Damage module that goes with BoneFX update
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BoneFXDamage.h"
|
||||
#include "GameLogic/Module/BoneFXUpdate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BoneFXDamage::BoneFXDamage( Thing *thing, const ModuleData* moduleData )
|
||||
: DamageModule( thing, moduleData )
|
||||
{
|
||||
} // end BoneFXDamage
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BoneFXDamage::~BoneFXDamage( void )
|
||||
{
|
||||
|
||||
} // end ~BoneFXDamage
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BoneFXDamage::onObjectCreated()
|
||||
{
|
||||
static NameKeyType key_BoneFXUpdate = NAMEKEY("BoneFXUpdate");
|
||||
BoneFXUpdate* bfxu = (BoneFXUpdate*)getObject()->findUpdateModule(key_BoneFXUpdate);
|
||||
if (bfxu == NULL)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(bfxu != NULL, ("BoneFXDamage requires BoneFXUpdate"));
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Switching damage states */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BoneFXDamage::onBodyDamageStateChange( const DamageInfo *damageInfo,
|
||||
BodyDamageType oldState,
|
||||
BodyDamageType newState )
|
||||
{
|
||||
static NameKeyType key_BoneFXUpdate = NAMEKEY("BoneFXUpdate");
|
||||
BoneFXUpdate* bfxu = (BoneFXUpdate*)getObject()->findUpdateModule(key_BoneFXUpdate);
|
||||
if (bfxu)
|
||||
bfxu->changeBodyDamageState(oldState, newState);
|
||||
|
||||
} // end onBodyDamageStateChange
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BoneFXDamage::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DamageModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BoneFXDamage::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DamageModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BoneFXDamage::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DamageModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DamageModule.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, September 2002
|
||||
// Desc: Damage Module base class
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/DamageModule.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamageModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamageModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// call base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamageModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// call base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: TransitionDamageFX.cpp ///////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, March 2002
|
||||
// Desc: Damage module capable of launching various effects on damage transitions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Module/TransitionDamageFX.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TransitionDamageFXModuleData::TransitionDamageFXModuleData( void )
|
||||
{
|
||||
Int i, j;
|
||||
|
||||
for( i = 0; i < BODYDAMAGETYPE_COUNT; i++ )
|
||||
{
|
||||
|
||||
for( j = 0; j < DAMAGE_MODULE_MAX_FX; j++ )
|
||||
{
|
||||
|
||||
m_fxList[ i ][ j ].fx = NULL;
|
||||
m_fxList[ i ][ j ].locInfo.loc.x = 0.0f;
|
||||
m_fxList[ i ][ j ].locInfo.loc.y = 0.0f;
|
||||
m_fxList[ i ][ j ].locInfo.loc.z = 0.0f;
|
||||
m_fxList[ i ][ j ].locInfo.locType = FX_DAMAGE_LOC_TYPE_COORD;
|
||||
m_fxList[ i ][ j ].locInfo.randomBone = FALSE;
|
||||
m_OCL[ i ][ j ].ocl = NULL;
|
||||
m_OCL[ i ][ j ].locInfo.loc.x = 0.0f;
|
||||
m_OCL[ i ][ j ].locInfo.loc.y = 0.0f;
|
||||
m_OCL[ i ][ j ].locInfo.loc.z = 0.0f;
|
||||
m_OCL[ i ][ j ].locInfo.locType = FX_DAMAGE_LOC_TYPE_COORD;
|
||||
m_OCL[ i ][ j ].locInfo.randomBone = FALSE;
|
||||
m_particleSystem[ i ][ j ].particleSysTemplate = NULL;
|
||||
m_particleSystem[ i ][ j ].locInfo.loc.x = 0.0f;
|
||||
m_particleSystem[ i ][ j ].locInfo.loc.y = 0.0f;
|
||||
m_particleSystem[ i ][ j ].locInfo.loc.z = 0.0f;
|
||||
m_particleSystem[ i ][ j ].locInfo.locType = FX_DAMAGE_LOC_TYPE_COORD;
|
||||
m_particleSystem[ i ][ j ].locInfo.randomBone = FALSE;
|
||||
|
||||
} // end for j
|
||||
|
||||
} // end for i
|
||||
|
||||
m_damageFXTypes = DAMAGE_TYPE_FLAGS_ALL;
|
||||
m_damageOCLTypes = DAMAGE_TYPE_FLAGS_ALL;
|
||||
m_damageParticleTypes = DAMAGE_TYPE_FLAGS_ALL;
|
||||
|
||||
} // end TransitionDamageFXModuleData
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse fx location info ... that is a named bone or a coord3d position */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseFXLocInfo( INI *ini, void *instance, FXLocInfo *locInfo )
|
||||
{
|
||||
const char *token = ini->getNextToken( ini->getSepsColon() );
|
||||
|
||||
if( stricmp( token, "bone" ) == 0 )
|
||||
{
|
||||
|
||||
// save bone name and location type
|
||||
locInfo->boneName = AsciiString( ini->getNextToken() );
|
||||
locInfo->locType = FX_DAMAGE_LOC_TYPE_BONE;
|
||||
|
||||
//
|
||||
// bones are followed by RandomBone:<Yes|No>, if random bone is yes, the bone name is
|
||||
// assumed to be a "base bone name" and we will find all the bones with that prefix
|
||||
// when picking an effect position. If it's no, the bone name is assumed to be explicit
|
||||
//
|
||||
token = ini->getNextToken( ini->getSepsColon() );
|
||||
if( stricmp( token, "randombone" ) != 0 )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "parseFXLocInfo: Bone name not followed by RandomBone specifier\nPress IGNORE to see which INI file and line # is incorrect." ));
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// parse the Bool definition
|
||||
ini->parseBool( ini, instance, &locInfo->randomBone, NULL );
|
||||
|
||||
} // end if
|
||||
else if( stricmp( token, "loc" ) == 0 )
|
||||
{
|
||||
|
||||
// save location and location type
|
||||
locInfo->loc.x = ini->scanReal( ini->getNextSubToken("X") );
|
||||
locInfo->loc.y = ini->scanReal( ini->getNextSubToken("Y") );
|
||||
locInfo->loc.z = ini->scanReal( ini->getNextSubToken("Z") );
|
||||
locInfo->locType = FX_DAMAGE_LOC_TYPE_COORD;
|
||||
|
||||
} // end else
|
||||
else
|
||||
{
|
||||
|
||||
// error
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end else
|
||||
|
||||
} // end parseFXLocInfo
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** In the form of:
|
||||
* FXListSlot = <<Bone:BoneName BoneRandom:<Yes|No>> | <Loc: X:x Y:y Z:z>> FXList:FXListName */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFXModuleData::parseFXList( INI *ini, void *instance,
|
||||
void *store, const void *userData )
|
||||
{
|
||||
const char *token;
|
||||
FXDamageFXListInfo *info = (FXDamageFXListInfo *)store;
|
||||
|
||||
// parse the location bone or location
|
||||
parseFXLocInfo( ini, instance, &info->locInfo );
|
||||
|
||||
// make sure we have an "FXList:" token
|
||||
token = ini->getNextToken( ini->getSepsColon() );
|
||||
if( stricmp( token, "fxlist" ) != 0 )
|
||||
{
|
||||
|
||||
// error
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// parse the fx list name
|
||||
ini->parseFXList( ini, instance, &info->fx, NULL );
|
||||
|
||||
} // end parseFXList
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** In the form of:
|
||||
* OCLSlot = <<Bone:BoneName BoneRandom:<Yes|No>> | <Loc: X:x Y:y Z:z>> OCL:OCLName */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFXModuleData::parseObjectCreationList( INI *ini, void *instance,
|
||||
void *store, const void *userData )
|
||||
{
|
||||
const char *token;
|
||||
FXDamageOCLInfo *info = (FXDamageOCLInfo *)store;
|
||||
|
||||
// parse the location bone or location
|
||||
parseFXLocInfo( ini, instance, &info->locInfo );
|
||||
|
||||
// make sure we have an "OCL:" token
|
||||
token = ini->getNextToken( ini->getSepsColon() );
|
||||
if( stricmp( token, "ocl" ) != 0 )
|
||||
{
|
||||
|
||||
// error
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// parse the ocl name
|
||||
ini->parseObjectCreationList( ini, instance, store, &info->ocl );
|
||||
|
||||
} // end parseObjectCreationList
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** In the form of:
|
||||
* ParticleSlot = <<Bone:BoneName BoneRandom:<Yes|No>> | <Loc: X:x Y:y Z:z>> PSys:PSysName */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFXModuleData::parseParticleSystem( INI *ini, void *instance,
|
||||
void *store, const void *userData )
|
||||
{
|
||||
const char *token;
|
||||
FXDamageParticleSystemInfo *info = (FXDamageParticleSystemInfo *)store;
|
||||
|
||||
// parse the location bone or location
|
||||
parseFXLocInfo( ini, instance, &info->locInfo );
|
||||
|
||||
// make sure we have an "PSys:" token
|
||||
token = ini->getNextToken( ini->getSepsColon() );
|
||||
if( stricmp( token, "psys" ) != 0 )
|
||||
{
|
||||
|
||||
// error
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// parse the particle system name
|
||||
ini->parseParticleSystemTemplate( ini, instance, store, &info->particleSysTemplate );
|
||||
|
||||
} // end parseParticleSystem
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TransitionDamageFX::TransitionDamageFX( Thing *thing, const ModuleData* moduleData )
|
||||
: DamageModule( thing, moduleData )
|
||||
{
|
||||
Int i, j;
|
||||
|
||||
for( i = 0; i < BODYDAMAGETYPE_COUNT; i++ )
|
||||
for( j = 0; j < DAMAGE_MODULE_MAX_FX; j++ )
|
||||
m_particleSystemID[ i ][ j ] = INVALID_PARTICLE_SYSTEM_ID;
|
||||
|
||||
} // end TransitionDamageFX
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TransitionDamageFX::~TransitionDamageFX( void )
|
||||
{
|
||||
|
||||
} // end ~TransitionDamageFX
|
||||
|
||||
/*
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFX::onDelete( void )
|
||||
{
|
||||
|
||||
//
|
||||
// we would in theory delete any particle systems we have created and attached, but the
|
||||
// particle system will automatically delete itself when the object is destroyed
|
||||
//
|
||||
|
||||
} // end onDelete
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Given an FXLoc info struct, return the effect position that we are supposed to use.
|
||||
* The position is local to to the object */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
|
||||
{
|
||||
|
||||
DEBUG_ASSERTCRASH( locInfo, ("getLocalEffectPos: locInfo is NULL\n") );
|
||||
|
||||
if( locInfo->locType == FX_DAMAGE_LOC_TYPE_BONE && draw )
|
||||
{
|
||||
|
||||
if( locInfo->randomBone == FALSE )
|
||||
{
|
||||
Coord3D pos;
|
||||
|
||||
// get the bone position
|
||||
Int count = draw->getPristineBonePositions( locInfo->boneName.str(), 0, &pos, NULL, 1 );
|
||||
|
||||
// sanity, if bone not found revert back to location defined in struct (which is 0,0,0)
|
||||
if( count == 0 )
|
||||
return locInfo->loc;
|
||||
|
||||
// return the position retrieved
|
||||
return pos;
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
const Int MAX_BONES = 32;
|
||||
Coord3D positions[ MAX_BONES ];
|
||||
|
||||
// get the bone positions
|
||||
Int boneCount;
|
||||
boneCount = draw->getPristineBonePositions( locInfo->boneName.str(), 1, positions, NULL, MAX_BONES );
|
||||
|
||||
// sanity, if bone not found revert back to location defined in struct (which is 0,0,0)
|
||||
if( boneCount == 0 )
|
||||
return locInfo->loc;
|
||||
|
||||
// pick one of the bone positions
|
||||
Int pick = GameLogicRandomValue( 0, boneCount - 1 );
|
||||
return positions[ pick ];
|
||||
|
||||
} // end else
|
||||
|
||||
} // end if
|
||||
else
|
||||
return locInfo->loc;
|
||||
|
||||
} // end getLocalEffectPos
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Switching damage states */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFX::onBodyDamageStateChange( const DamageInfo* damageInfo,
|
||||
BodyDamageType oldState,
|
||||
BodyDamageType newState )
|
||||
{
|
||||
Object *damageSource = NULL;
|
||||
Int i;
|
||||
Drawable *draw = getObject()->getDrawable();
|
||||
const TransitionDamageFXModuleData *modData = getTransitionDamageFXModuleData();
|
||||
|
||||
// get the source of the damage if present
|
||||
if( damageInfo )
|
||||
damageSource = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
|
||||
// remove any particle systems that might be emitting from our old state
|
||||
for( i = 0; i < DAMAGE_MODULE_MAX_FX; i++ )
|
||||
{
|
||||
|
||||
if( m_particleSystemID[ oldState ][ i ] != INVALID_PARTICLE_SYSTEM_ID )
|
||||
{
|
||||
|
||||
TheParticleSystemManager->destroyParticleSystemByID( m_particleSystemID[ oldState ][ i ] );
|
||||
m_particleSystemID[ oldState ][ i ] = INVALID_PARTICLE_SYSTEM_ID;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
//
|
||||
// when we are transitioning to a "worse" state we will play a set of effects for that
|
||||
// new state to make the transition
|
||||
//
|
||||
if( IS_CONDITION_WORSE( newState, oldState ) )
|
||||
{
|
||||
const ParticleSystemTemplate *pSystemT;
|
||||
Coord3D pos;
|
||||
|
||||
// if we are restricted by the damage type executing effect, bail out of here
|
||||
const DamageInfo *lastDamageInfo = getObject()->getBodyModule()->getLastDamageInfo();
|
||||
|
||||
for( i = 0; i < DAMAGE_MODULE_MAX_FX; i++ )
|
||||
{
|
||||
|
||||
// play fx list for our new state
|
||||
if( modData->m_fxList[ newState ][ i ].fx )
|
||||
{
|
||||
|
||||
if( lastDamageInfo == NULL ||
|
||||
getDamageTypeFlag( modData->m_damageFXTypes, lastDamageInfo->in.m_damageType ) )
|
||||
{
|
||||
|
||||
pos = getLocalEffectPos( &modData->m_fxList[ newState ][ i ].locInfo, draw );
|
||||
getObject()->convertBonePosToWorldPos( &pos, NULL, &pos, NULL );
|
||||
FXList::doFXPos( modData->m_fxList[ newState ][ i ].fx, &pos );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
// do any object creation list for our new state
|
||||
if( modData->m_OCL[ newState ][ i ].ocl )
|
||||
{
|
||||
|
||||
if( lastDamageInfo == NULL ||
|
||||
getDamageTypeFlag( modData->m_damageOCLTypes, lastDamageInfo->in.m_damageType ) )
|
||||
{
|
||||
|
||||
pos = getLocalEffectPos( &modData->m_OCL[ newState ][ i ].locInfo, draw );
|
||||
getObject()->convertBonePosToWorldPos( &pos, NULL, &pos, NULL );
|
||||
ObjectCreationList::create( modData->m_OCL[ newState ][ i ].ocl,
|
||||
getObject(), &pos, damageSource->getPosition() );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
// get the template of the system to create
|
||||
pSystemT = modData->m_particleSystem[ newState ][ i ].particleSysTemplate;
|
||||
if( pSystemT )
|
||||
{
|
||||
|
||||
if( lastDamageInfo == NULL ||
|
||||
getDamageTypeFlag( modData->m_damageParticleTypes, lastDamageInfo->in.m_damageType ) )
|
||||
{
|
||||
|
||||
// create a new particle system based on the template provided
|
||||
ParticleSystem* pSystem = TheParticleSystemManager->createParticleSystem( pSystemT );
|
||||
if( pSystem )
|
||||
{
|
||||
|
||||
// get the what is the position we're going to playe the effect at
|
||||
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw );
|
||||
|
||||
//
|
||||
// set position on system given any bone position provided, the bone position is
|
||||
// local to the object and that's what we want for the particle system ... the
|
||||
// transormation into world space using the object position is taken care of in
|
||||
// the particle system attachToObject method
|
||||
//
|
||||
pSystem->setPosition( &pos );
|
||||
|
||||
// attach to object
|
||||
pSystem->attachToObject( getObject() );
|
||||
|
||||
// save the id of this particle system so we can remove it later if it still exists
|
||||
m_particleSystemID[ newState ][ i ] = pSystem->getSystemID();
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end if
|
||||
|
||||
} // end onBodyDamageStateChange
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFX::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DamageModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFX::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DamageModule::xfer( xfer );
|
||||
|
||||
// particle systems ids
|
||||
xfer->xferUser( m_particleSystemID, sizeof( ParticleSystemID ) * BODYDAMAGETYPE_COUNT * DAMAGE_MODULE_MAX_FX );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TransitionDamageFX::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DamageModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DestroyModule.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, October 2002
|
||||
// Desc: Destroy module base class
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/DestroyModule.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DestroyModule::DestroyModule( Thing *thing, const ModuleData* moduleData )
|
||||
: BehaviorModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end DestroyModule
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DestroyModule::~DestroyModule( void )
|
||||
{
|
||||
|
||||
} // end ~DestroyModule
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DestroyModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DestroyModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DestroyModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CreateCrateDie.cpp /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, February 2002
|
||||
// Desc: A chance to create a crate on death according to certain condition checks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/CrateSystem.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/CreateCrateDie.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CreateCrateDie::CreateCrateDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CreateCrateDie::~CreateCrateDie( void )
|
||||
{
|
||||
}
|
||||
|
||||
void CreateCrateDieModuleData::parseCrateData( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
CreateCrateDieModuleData* self = (CreateCrateDieModuleData*)instance;
|
||||
|
||||
AsciiString crateName = ini->getNextToken();
|
||||
|
||||
self->m_crateNameList.push_back( crateName );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CreateCrateDie::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
|
||||
CrateTemplate const *currentCrateData = NULL;
|
||||
Object *killer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
Object *me = getObject();
|
||||
|
||||
if( killer && killer->getRelationship( me ) == ALLIES )
|
||||
return; //Nope, no crate for killing ally at all.
|
||||
|
||||
for( AsciiStringListConstIterator iter = getCreateCrateDieModuleData()->m_crateNameList.begin();
|
||||
iter != getCreateCrateDieModuleData()->m_crateNameList.end();
|
||||
iter++
|
||||
)
|
||||
{
|
||||
currentCrateData = TheCrateSystem->findCrateTemplate( *iter );
|
||||
if( currentCrateData )
|
||||
{
|
||||
if( ! testCreationChance( currentCrateData ) )
|
||||
continue;// always test this
|
||||
|
||||
if( (currentCrateData->m_veterancyLevel != LEVEL_INVALID) && ! testVeterancyLevel( currentCrateData ) )
|
||||
continue; //If this is set up to test and it fails
|
||||
|
||||
if( KINDOFMASK_ANY_SET(currentCrateData->m_killedByTypeKindof) && !testKillerType( currentCrateData, killer ) )
|
||||
continue; //If this is set up to test and it fails
|
||||
|
||||
if( (currentCrateData->m_killerScience != SCIENCE_INVALID) && !testKillerScience( currentCrateData, killer ) )
|
||||
continue; //If this is set up to test and it fails
|
||||
|
||||
Object *crate = createCrate( currentCrateData );//Make the crate
|
||||
if( crate )
|
||||
{
|
||||
// Design needs to set ownership of crates sometimes
|
||||
if( currentCrateData->m_isOwnedByMaker )
|
||||
{
|
||||
crate->setTeam( me->getControllingPlayer()->getDefaultTeam() );
|
||||
}
|
||||
|
||||
if (killer)
|
||||
{
|
||||
// If the killer is a computer controlled player, notify that the crate exists.
|
||||
if (killer->getControllingPlayer() &&
|
||||
killer->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)
|
||||
{
|
||||
AIUpdateInterface *ai = killer->getAIUpdateInterface();
|
||||
if (ai)
|
||||
{
|
||||
ai->notifyCrate( crate->getID() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool CreateCrateDie::testCreationChance( CrateTemplate const *currentCrateData )
|
||||
{
|
||||
Real testAgainst = currentCrateData->m_creationChance;
|
||||
Real testWith = GameLogicRandomValueReal( 0, 1 );
|
||||
|
||||
return testWith < testAgainst;
|
||||
}
|
||||
|
||||
Bool CreateCrateDie::testVeterancyLevel( CrateTemplate const *currentCrateData )
|
||||
{
|
||||
VeterancyLevel testAgainst = currentCrateData->m_veterancyLevel;
|
||||
VeterancyLevel testWith = getObject()->getVeterancyLevel();
|
||||
|
||||
return testAgainst == testWith;
|
||||
}
|
||||
|
||||
Bool CreateCrateDie::testKillerType( CrateTemplate const *currentCrateData, Object *killer )
|
||||
{
|
||||
if( killer == NULL )
|
||||
return FALSE;
|
||||
|
||||
// Must match the whole group of bits set in the KilledBy description (most likely One).
|
||||
if( ! killer->getTemplate()->isKindOfMulti( currentCrateData->m_killedByTypeKindof, KINDOFMASK_NONE ) )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool CreateCrateDie::testKillerScience( CrateTemplate const *currentCrateData, Object *killer )
|
||||
{
|
||||
if( killer == NULL )
|
||||
return FALSE;
|
||||
|
||||
// killer's player must have the listed science
|
||||
Player *killerPlayer = killer->getControllingPlayer();
|
||||
|
||||
if( killerPlayer == NULL )
|
||||
return FALSE;
|
||||
|
||||
if( ! killerPlayer->hasScience( currentCrateData->m_killerScience ) )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Object *CreateCrateDie::createCrate( CrateTemplate const *currentCrateData )
|
||||
{
|
||||
Coord3D centerPoint = *getObject()->getPosition();
|
||||
PathfindLayerEnum layer = getObject()->getLayer();
|
||||
|
||||
// CreationChance is used for the success of this block, but this block can have any number of potential actual crates
|
||||
Real multipleCratePick = GameLogicRandomValueReal( 0, 1 );
|
||||
Real multipleCrateRunningTotal = 0;
|
||||
AsciiString crateName = "";
|
||||
|
||||
for( crateCreationEntryConstIterator iter = currentCrateData->m_possibleCrates.begin();
|
||||
iter != currentCrateData->m_possibleCrates.end();
|
||||
iter++
|
||||
)
|
||||
{
|
||||
multipleCrateRunningTotal += (*iter).crateChance;
|
||||
if( multipleCrateRunningTotal > multipleCratePick )
|
||||
{
|
||||
// Run through the list of possibles, and if the sum of the chances is greater than my random pick,
|
||||
// then this is the correct one. (This simulates contiguous %, or weighted distribution)
|
||||
crateName = (*iter).crateName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// At this point, I could very well have a "" for the type, if the Designer didn't make the sum of chances 1
|
||||
|
||||
ThingTemplate const *crateType = TheThingFactory->findTemplate( crateName );
|
||||
if( crateType == NULL )
|
||||
return NULL;
|
||||
|
||||
Bool spotFound = FALSE;
|
||||
Coord3D creationPoint;
|
||||
FindPositionOptions fpOptions;
|
||||
|
||||
fpOptions.minRadius = 0.0f;
|
||||
fpOptions.maxRadius = 5.0f;
|
||||
fpOptions.relationshipObject = getObject();
|
||||
fpOptions.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS; // So the dead guy won't block, nor will his dead hulk.
|
||||
if (layer != LAYER_GROUND) {
|
||||
creationPoint = centerPoint;
|
||||
spotFound = true;
|
||||
} else if( ThePartitionManager->findPositionAround( ¢erPoint, &fpOptions, &creationPoint ) )
|
||||
{
|
||||
spotFound = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the tight ignore units scan fails, then try a great big scan so we appear on the edge
|
||||
// of the large dead thing (building rubble)
|
||||
fpOptions.minRadius = 0.0f;
|
||||
fpOptions.maxRadius = 125.0f;
|
||||
fpOptions.relationshipObject = NULL;
|
||||
fpOptions.flags = FPF_NONE;
|
||||
if( ThePartitionManager->findPositionAround( ¢erPoint, &fpOptions, &creationPoint ) )
|
||||
{
|
||||
spotFound = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if( spotFound )
|
||||
{
|
||||
Object *newCrate = TheThingFactory->newObject( crateType, NULL );
|
||||
newCrate->setPosition( &creationPoint );
|
||||
newCrate->setOrientation( GameLogicRandomValueReal( 0, 2*PI ) );
|
||||
newCrate->setLayer(layer);
|
||||
|
||||
Drawable *crateDrawable = newCrate->getDrawable();
|
||||
|
||||
if( crateDrawable )
|
||||
{
|
||||
crateDrawable->setTerrainDecal(TERRAIN_DECAL_CRATE);
|
||||
crateDrawable->setTerrainDecalSize(2.5f * newCrate->getGeometryInfo().getMajorRadius(),
|
||||
2.5f * newCrate->getGeometryInfo().getMajorRadius() ) ;
|
||||
crateDrawable->setTerrainDecalFadeTarget(1.0f, 0.03f);
|
||||
}
|
||||
|
||||
return newCrate;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateCrateDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateCrateDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateCrateDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CreateObjectDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Michael S. Booth, January 2002
|
||||
// Desc: Create an object upon this object's death
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_OBJECT_STATUS_NAMES
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/CreateObjectDie.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
CreateObjectDieModuleData::CreateObjectDieModuleData()
|
||||
{
|
||||
|
||||
m_ocl = NULL;
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void CreateObjectDieModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
DieModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "CreationList", INI::parseObjectCreationList, NULL, offsetof( CreateObjectDieModuleData, m_ocl ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CreateObjectDie::CreateObjectDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CreateObjectDie::~CreateObjectDie( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CreateObjectDie::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
|
||||
Object *damageDealer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
|
||||
ObjectCreationList::create(getCreateObjectDieModuleData()->m_ocl, getObject(), damageDealer);
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateObjectDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateObjectDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CreateObjectDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CrushDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Crush Die module
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/CrushDie.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Figure out which crush point was hit so the correct crushed object can be swapped in
|
||||
|
||||
// Figure out which crush point was hit so the correct crushed object can be swapped in
|
||||
static CrushEnum crushLocationCheck( Object* crusherObject, Object* victimObject )
|
||||
{
|
||||
if( (crusherObject == NULL) || (victimObject == NULL) )
|
||||
return NO_CRUSH;
|
||||
|
||||
Bool frontCrushed = victimObject->getBodyModule()->getFrontCrushed();
|
||||
Bool backCrushed = victimObject->getBodyModule()->getBackCrushed();
|
||||
|
||||
// const Coord3D *dir = crusherObject->getUnitDirectionVector2D();
|
||||
const Coord3D *otherDir = victimObject->getUnitDirectionVector2D();
|
||||
const Coord3D *pos = crusherObject->getPosition();
|
||||
const Coord3D *otherPos = victimObject->getPosition();
|
||||
|
||||
Real crushPointOffsetDistance = victimObject->getGeometryInfo().getMajorRadius() * 0.5;
|
||||
|
||||
Coord3D crushPointOffset;
|
||||
crushPointOffset.x = otherDir->x * crushPointOffsetDistance;
|
||||
crushPointOffset.y = otherDir->y * crushPointOffsetDistance;
|
||||
crushPointOffset.z = 0;
|
||||
|
||||
Coord3D comparisonCoord;
|
||||
Real dx, dy;
|
||||
|
||||
CrushEnum retval = NO_CRUSH;
|
||||
Real bestDist = 99999;
|
||||
|
||||
// PhysicsCollide has already done the logic of which point to smoosh and waited until we crossed that point
|
||||
// so at this point we just need to know which crush point is closest.
|
||||
|
||||
if( !frontCrushed && !backCrushed )
|
||||
{
|
||||
// Check the middle crush point
|
||||
comparisonCoord = *otherPos;//copy so can move to each crush point
|
||||
|
||||
dx = comparisonCoord.x - pos->x;
|
||||
dy = comparisonCoord.y - pos->y;
|
||||
Real dist = (Real)( dx*dx + dy*dy );
|
||||
|
||||
//otherwise we want to make sure we get the closest valid crush point
|
||||
retval = TOTAL_CRUSH;
|
||||
bestDist = dist;
|
||||
}
|
||||
|
||||
if( !frontCrushed )
|
||||
{
|
||||
// Check the front point.
|
||||
comparisonCoord = *otherPos;
|
||||
comparisonCoord.x += crushPointOffset.x;
|
||||
comparisonCoord.y += crushPointOffset.y;
|
||||
|
||||
dx = comparisonCoord.x - pos->x;
|
||||
dy = comparisonCoord.y - pos->y;
|
||||
Real dist = (Real)( dx*dx + dy*dy );
|
||||
|
||||
if( dist < bestDist )//closer
|
||||
{
|
||||
if( backCrushed )
|
||||
{
|
||||
retval = TOTAL_CRUSH;
|
||||
bestDist = dist;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = FRONT_END_CRUSH;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !backCrushed )
|
||||
{
|
||||
// Check back point
|
||||
comparisonCoord = *otherPos;
|
||||
comparisonCoord.x -= crushPointOffset.x;
|
||||
comparisonCoord.y -= crushPointOffset.y;
|
||||
|
||||
dx = comparisonCoord.x - pos->x;
|
||||
dy = comparisonCoord.y - pos->y;
|
||||
Real dist = (Real)( dx*dx + dy*dy );
|
||||
|
||||
if( dist < bestDist )//closer
|
||||
{
|
||||
if( frontCrushed )
|
||||
{
|
||||
retval = TOTAL_CRUSH;
|
||||
bestDist = dist;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = BACK_END_CRUSH;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CrushDie::CrushDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CrushDie::~CrushDie( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CrushDie::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
|
||||
DEBUG_ASSERTCRASH(damageInfo->in.m_damageType == DAMAGE_CRUSH, ("this should only be used for crush damage\n"));
|
||||
if (damageInfo->in.m_damageType != DAMAGE_CRUSH)
|
||||
return;
|
||||
|
||||
Object *damageDealer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
DEBUG_ASSERTCRASH(damageDealer,("You must have a damageDealer source for this effect"));
|
||||
|
||||
CrushEnum crushType = damageDealer ? crushLocationCheck(damageDealer, getObject()) : TOTAL_CRUSH;
|
||||
|
||||
if (crushType != NO_CRUSH)
|
||||
{
|
||||
if (getCrushDieModuleData()->m_crushSounds[crushType].getEventName().isEmpty() == false)
|
||||
{
|
||||
// be sure that 0==never, 100==always
|
||||
// MDC: moving to GameLogicRandomValue. This does not need to be synced, but having it so makes searches *so* much nicer.
|
||||
if (GameLogicRandomValue(0, 99) < getCrushDieModuleData()->m_crushSoundPercent[crushType])
|
||||
{
|
||||
AudioEventRTS crushSound(getCrushDieModuleData()->m_crushSounds[crushType]);
|
||||
crushSound.setObjectID(getObject()->getID());
|
||||
TheAudio->addAudioEvent(&crushSound);
|
||||
}
|
||||
}
|
||||
{
|
||||
Object *me = getObject();
|
||||
|
||||
if (me)
|
||||
{
|
||||
me->getBodyModule()->setFrontCrushed(crushType == TOTAL_CRUSH || crushType == FRONT_END_CRUSH);
|
||||
me->getBodyModule()->setBackCrushed(crushType == TOTAL_CRUSH || crushType == BACK_END_CRUSH);
|
||||
|
||||
ModelConditionFlags newCrushed;
|
||||
newCrushed.set(MODELCONDITION_FRONTCRUSHED, (crushType == TOTAL_CRUSH || crushType == FRONT_END_CRUSH));
|
||||
newCrushed.set(MODELCONDITION_BACKCRUSHED, crushType == TOTAL_CRUSH || crushType == BACK_END_CRUSH);
|
||||
|
||||
me->getDrawable()->clearAndSetModelConditionFlags(
|
||||
MAKE_MODELCONDITION_MASK2(MODELCONDITION_BACKCRUSHED, MODELCONDITION_FRONTCRUSHED),
|
||||
newCrushed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CrushDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CrushDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CrushDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
149
Generals/Code/GameEngine/Source/GameLogic/Object/Die/DamDie.cpp
Normal file
149
Generals/Code/GameEngine/Source/GameLogic/Object/Die/DamDie.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DamDie.cpp ///////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, April 2002
|
||||
// Desc: The big water dam dying
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/ParticleSys.h"
|
||||
#include "GameClient/TerrainVisual.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/DamDie.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DamDieModuleData::DamDieModuleData( void )
|
||||
{
|
||||
|
||||
} // end DamDieModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void DamDieModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
|
||||
DieModuleData::buildFieldParse( p );
|
||||
|
||||
// static const FieldParse dataFieldParse[] =
|
||||
// {
|
||||
// { 0, 0, 0, 0 }
|
||||
// };
|
||||
//
|
||||
// p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DamDie::DamDie( Thing *thing, const ModuleData *moduleData )
|
||||
:DieModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end DamDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DamDie::~DamDie( void )
|
||||
{
|
||||
|
||||
} // end ~DamDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
|
||||
// enable all the water wave objects on the map
|
||||
Object *obj;
|
||||
for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
|
||||
{
|
||||
|
||||
// only care aboue water waves
|
||||
if( obj->isKindOf( KINDOF_WAVEGUIDE ) == FALSE )
|
||||
continue;
|
||||
|
||||
// clear any disabled status of the water wave
|
||||
obj->clearDisabled( DISABLED_DEFAULT );
|
||||
|
||||
} // end for, obj
|
||||
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DamDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DestroyDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Default Die module
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/DestroyDie.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DestroyDie::DestroyDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DestroyDie::~DestroyDie( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DestroyDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
TheGameLogic->destroyObject(getObject());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DestroyDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DestroyDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DestroyDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DieModule.cpp ////////////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_OBJECT_STATUS_NAMES
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/DieModule.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DieMuxData::DieMuxData() :
|
||||
m_deathTypes(DEATH_TYPE_FLAGS_ALL),
|
||||
m_veterancyLevels(VETERANCY_LEVEL_FLAGS_ALL),
|
||||
m_exemptStatus(OBJECT_STATUS_NONE),
|
||||
m_requiredStatus(OBJECT_STATUS_NONE)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const FieldParse* DieMuxData::getFieldParse()
|
||||
{
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "DeathTypes", INI::parseDeathTypeFlags, NULL, offsetof( DieMuxData, m_deathTypes ) },
|
||||
{ "VeterancyLevels", INI::parseVeterancyLevelFlags, NULL, offsetof( DieMuxData, m_veterancyLevels ) },
|
||||
{ "ExemptStatus", INI::parseBitString32, TheObjectStatusBitNames, offsetof( DieMuxData, m_exemptStatus ) },
|
||||
{ "RequiredStatus", INI::parseBitString32, TheObjectStatusBitNames, offsetof( DieMuxData, m_requiredStatus ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
return dataFieldParse;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool DieMuxData::isDieApplicable(const Object* obj, const DamageInfo *damageInfo) const
|
||||
{
|
||||
// wrong death type? punt
|
||||
if (!getDeathTypeFlag(m_deathTypes, damageInfo->in.m_deathType))
|
||||
return false;
|
||||
|
||||
// wrong vet level? punt
|
||||
if (!getVeterancyLevelFlag(m_veterancyLevels, obj->getVeterancyLevel()))
|
||||
return false;
|
||||
|
||||
// all 'exempt' bits must be clear for us to run.
|
||||
if ((obj->getStatusBits() & m_exemptStatus) != 0)
|
||||
return false;
|
||||
|
||||
// all 'required' bits must be set for us to run.
|
||||
if ((obj->getStatusBits() & m_requiredStatus) != m_requiredStatus)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DieModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DieModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// call base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DieModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// call base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: EjectPilotDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, April 2002
|
||||
// Desc: Create an object upon this object's death
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/EjectPilotDie.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
EjectPilotDieModuleData::EjectPilotDieModuleData() :
|
||||
m_oclInAir(NULL),
|
||||
m_oclOnGround(NULL),
|
||||
m_invulnerableTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void EjectPilotDieModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
DieModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "AirCreationList", INI::parseObjectCreationList, NULL, offsetof( EjectPilotDieModuleData, m_oclInAir ) },
|
||||
{ "GroundCreationList", INI::parseObjectCreationList, NULL, offsetof( EjectPilotDieModuleData, m_oclOnGround ) },
|
||||
{ "InvulnerableTime", INI::parseDurationUnsignedInt, NULL, offsetof(EjectPilotDieModuleData, m_invulnerableTime ) },
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
EjectPilotDie::EjectPilotDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
EjectPilotDie::~EjectPilotDie( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void EjectPilotDie::ejectPilot(const ObjectCreationList* ocl, const Object* dyingObject, const Object* damageDealer)
|
||||
{
|
||||
if (!ocl || !dyingObject)
|
||||
return; // it's OK for damageDealer to be null
|
||||
|
||||
ObjectCreationList::create(ocl, dyingObject, damageDealer);
|
||||
|
||||
AudioEventRTS voiceEject = *(dyingObject->getTemplate()->getPerUnitSound("VoiceEject"));
|
||||
voiceEject.setPosition( dyingObject->getPosition() );
|
||||
voiceEject.setPlayerIndex( dyingObject->getControllingPlayer()->getPlayerIndex() );
|
||||
TheAudio->addAudioEvent(&voiceEject);
|
||||
|
||||
AudioEventRTS soundEject = *(dyingObject->getTemplate()->getPerUnitSound("SoundEject"));
|
||||
soundEject.setPosition( dyingObject->getPosition() );
|
||||
TheAudio->addAudioEvent(&soundEject);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void EjectPilotDie::onDie( const DamageInfo * damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
Object* damageDealer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
const EjectPilotDieModuleData* d = getEjectPilotDieModuleData();
|
||||
const ObjectCreationList* ocl = getObject()->isSignificantlyAboveTerrain() ? d->m_oclInAir : d->m_oclOnGround;
|
||||
ejectPilot(ocl, getObject(), damageDealer);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void EjectPilotDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void EjectPilotDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void EjectPilotDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: FXListDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, Jan 2002
|
||||
// Desc: Simple Die module
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_DAMAGE_NAMES
|
||||
#include "Common/INI.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/FXListDie.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FXListDie::FXListDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FXListDie::~FXListDie( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FXListDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
const FXListDieModuleData* d = getFXListDieModuleData();
|
||||
if (d->m_defaultDeathFX)
|
||||
{
|
||||
// if the object has any ambient sound(s), kill 'em now.
|
||||
TheAudio->stopAllAmbientsBy(getObject());
|
||||
|
||||
if (d->m_orientToObject)
|
||||
{
|
||||
Object *damageDealer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
|
||||
FXList::doFXObj(getFXListDieModuleData()->m_defaultDeathFX, getObject(), damageDealer);
|
||||
}
|
||||
else
|
||||
{
|
||||
FXList::doFXPos(getFXListDieModuleData()->m_defaultDeathFX, getObject()->getPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FXListDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FXListDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FXListDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: KeepObjectDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Kris Morness, November 2002
|
||||
// Desc: Die module for things that want to leave rubble in the world and don't have other die
|
||||
// modules. This fixes civilian buildings that don't have garrison contains. Garrison
|
||||
// contains have a die module built in, so these buildings need something. Without it
|
||||
// they default to the destroydie module which outright removes the object.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/KeepObjectDie.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
KeepObjectDie::KeepObjectDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
KeepObjectDie::~KeepObjectDie( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void KeepObjectDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if( !isDieApplicable(damageInfo) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void KeepObjectDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void KeepObjectDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void KeepObjectDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: RebuildHoleExposeDie.cpp /////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, June 2002
|
||||
// Desc: When a structure dies with this module, a rebuild hole will be created in place
|
||||
// of the structure that will automatically rebuild the structure
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDE FILES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/AI.h"
|
||||
#include "GameLogic/AIPathfind.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/RebuildHoleBehavior.h"
|
||||
#include "GameLogic/Module/RebuildHoleExposeDie.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
RebuildHoleExposeDieModuleData::RebuildHoleExposeDieModuleData()
|
||||
{
|
||||
|
||||
m_holeMaxHealth = 0.0f;
|
||||
m_transferAttackers = true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void RebuildHoleExposeDieModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
DieModuleData::buildFieldParse(p);
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "HoleName", INI::parseAsciiString, NULL, offsetof( RebuildHoleExposeDieModuleData, m_holeName ) },
|
||||
{ "HoleMaxHealth", INI::parseReal, NULL, offsetof( RebuildHoleExposeDieModuleData, m_holeMaxHealth ) },
|
||||
{ "TransferAttackers", INI::parseBool, NULL, offsetof( RebuildHoleExposeDieModuleData, m_transferAttackers ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
RebuildHoleExposeDie::RebuildHoleExposeDie( Thing *thing, const ModuleData* moduleData )
|
||||
: DieModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end RebuildHoleExposeDie
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
RebuildHoleExposeDie::~RebuildHoleExposeDie( void )
|
||||
{
|
||||
|
||||
} // end ~RebuildHoleExposeDie
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleExposeDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
const RebuildHoleExposeDieModuleData *modData = getRebuildHoleExposeDieModuleData();
|
||||
Object *us = getObject();
|
||||
|
||||
//
|
||||
// if we are being constructed from either the first time or from a hole reconstruction
|
||||
// we do not "spawn" a hole object
|
||||
//
|
||||
if( us->getControllingPlayer() != ThePlayerList->getNeutralPlayer() && (us->getControllingPlayer()->isPlayerActive()) &&
|
||||
(BitTest( us->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE ))
|
||||
{
|
||||
Object *hole;
|
||||
|
||||
// create the hole
|
||||
hole = TheThingFactory->newObject( TheThingFactory->findTemplate( modData->m_holeName ),
|
||||
getObject()->getTeam() );
|
||||
|
||||
// put the hole at our position and angle
|
||||
hole->setPosition( us->getPosition() );
|
||||
hole->setOrientation( us->getOrientation() );
|
||||
|
||||
//
|
||||
// modify the hole extents to be the same as ours because we need to preserve the
|
||||
// same amount of space for the rebuilding process
|
||||
//
|
||||
hole->setGeometryInfo( us->getGeometryInfo() );
|
||||
|
||||
//
|
||||
// Transfer the building's name to the hole
|
||||
//
|
||||
TheScriptEngine->transferObjectName( us->getName(), hole );
|
||||
|
||||
//
|
||||
// add to pathfind map, this really should be wrapped up somewhere in the creation
|
||||
// pipeline of the object automagically!
|
||||
//
|
||||
TheAI->pathfinder()->addObjectToPathfindMap( hole );
|
||||
|
||||
// set the health of the hole to that defined by our data
|
||||
BodyModuleInterface *body = hole->getBodyModule();
|
||||
body->setMaxHealth( modData->m_holeMaxHealth );
|
||||
|
||||
// set the information in the hole about what to build
|
||||
RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( hole );
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( rhbi, ("RebuildHoleExposeDie: No Rebuild Hole Behavior interface on hole\n") );
|
||||
|
||||
// start the rebuild process
|
||||
if( rhbi )
|
||||
rhbi->startRebuildProcess( us->getTemplate(), us->getID() );
|
||||
|
||||
if (modData->m_transferAttackers)
|
||||
{
|
||||
for ( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
|
||||
{
|
||||
AIUpdateInterface* ai = obj->getAI();
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
ai->transferAttack(us->getID(), hole->getID());
|
||||
}
|
||||
}
|
||||
|
||||
} // end if
|
||||
|
||||
} // end onDie
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleExposeDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleExposeDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void RebuildHoleExposeDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SpecialPowerCompletionDie.cpp ////////////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell, May 2002
|
||||
// Desc: Die method responsible for telling TheScriptEngine that a special power has been completed
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/SpecialPowerCompletionDie.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerCompletionDie::SpecialPowerCompletionDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
m_creatorID = INVALID_ID;
|
||||
m_creatorSet = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerCompletionDie::~SpecialPowerCompletionDie( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCompletionDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
notifyScriptEngine();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCompletionDie::notifyScriptEngine( void )
|
||||
{
|
||||
if (m_creatorID != INVALID_ID)
|
||||
{
|
||||
TheScriptEngine->notifyOfCompletedSpecialPower(
|
||||
getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getSpecialPowerCompletionDieModuleData()->m_specialPowerTemplate->getName(),
|
||||
m_creatorID);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCompletionDie::setCreator( ObjectID creatorID )
|
||||
{
|
||||
if (!m_creatorSet)
|
||||
{
|
||||
m_creatorSet = TRUE;
|
||||
m_creatorID = creatorID;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCompletionDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCompletionDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
// creator id
|
||||
xfer->xferObjectID( &m_creatorID );
|
||||
|
||||
// creator set
|
||||
xfer->xferBool( &m_creatorSet );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerCompletionDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: UpgradeDie.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Kris Morness, August 2002
|
||||
// Desc: When object dies, the parent object is freed of the specified object upgrade field.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Upgrade.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/UpgradeDie.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpgradeDie::UpgradeDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpgradeDie::~UpgradeDie( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The die callback. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void UpgradeDie::onDie( const DamageInfo *damageInfo )
|
||||
{
|
||||
if (!isDieApplicable(damageInfo))
|
||||
return;
|
||||
//Look for the object that created me.
|
||||
Object *producer = TheGameLogic->findObjectByID( getObject()->getProducerID() );
|
||||
if( producer )
|
||||
{
|
||||
//Okay, we found our parent... now look for the upgrade.
|
||||
const UpgradeTemplate *upgrade = TheUpgradeCenter->findUpgrade( getUpgradeDieModuleData()->m_upgradeName );
|
||||
|
||||
if( upgrade )
|
||||
{
|
||||
//We found the upgrade, now see if the parent object has it set...
|
||||
if( producer->hasUpgrade( upgrade ) )
|
||||
{
|
||||
//Cool, now remove it.
|
||||
producer->removeUpgrade( upgrade );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ASSERTCRASH( 0, ("Object %s just died, but is trying to free upgrade %s in it's producer %s%s",
|
||||
getObject()->getTemplate()->getName().str(),
|
||||
getUpgradeDieModuleData()->m_upgradeName.str(),
|
||||
producer->getTemplate()->getName().str(),
|
||||
", which the producer doesn't have. This is used in cases where the producer builds an upgrade that can die... like ranger building scout drones.") );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void UpgradeDie::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void UpgradeDie::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
DieModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void UpgradeDie::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
DieModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ExperienceTracker.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, February 2002
|
||||
// Desc: Keeps track of experience points so Veterance levels can be gained
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "GameLogic/ExperienceTracker.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ExperienceTracker::ExperienceTracker(Object *parent) :
|
||||
m_parent(parent),
|
||||
m_currentLevel(LEVEL_REGULAR),
|
||||
m_experienceSink(INVALID_ID),
|
||||
m_experienceScalar( 1.0f ),
|
||||
m_currentExperience(0) // Added By Sadullah Nader
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ExperienceTracker::~ExperienceTracker()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int ExperienceTracker::getExperienceValue( const Object* killer ) const
|
||||
{
|
||||
// No experience for killing an ally, cheater.
|
||||
if( killer->getRelationship( m_parent ) == ALLIES )
|
||||
return 0;
|
||||
|
||||
return m_parent->getTemplate()->getExperienceValue(m_currentLevel);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ExperienceTracker::isTrainable() const
|
||||
{
|
||||
return m_parent->getTemplate()->isTrainable();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ExperienceTracker::isAcceptingExperiencePoints() const
|
||||
{
|
||||
return isTrainable() || (m_experienceSink != INVALID_ID);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ExperienceTracker::setExperienceSink( ObjectID sink )
|
||||
{
|
||||
m_experienceSink = sink;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Set Level to AT LEAST this... if we are already >= this level, do nothing.
|
||||
void ExperienceTracker::setMinVeterancyLevel( VeterancyLevel newLevel )
|
||||
{
|
||||
// This does not check for IsTrainable, because this function is for explicit setting,
|
||||
// so the setter is assumed to know what they are doing. The game function
|
||||
// of addExperiencePoints cares about Trainability.
|
||||
if (m_currentLevel < newLevel)
|
||||
{
|
||||
VeterancyLevel oldLevel = m_currentLevel;
|
||||
m_currentLevel = newLevel;
|
||||
m_currentExperience = m_parent->getTemplate()->getExperienceRequired(m_currentLevel); //Minimum for this level
|
||||
if (m_parent)
|
||||
m_parent->onVeterancyLevelChanged( oldLevel, newLevel );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ExperienceTracker::setVeterancyLevel( VeterancyLevel newLevel )
|
||||
{
|
||||
// This does not check for IsTrainable, because this function is for explicit setting,
|
||||
// so the setter is assumed to know what they are doing. The game function
|
||||
// of addExperiencePoints cares about Trainability, if flagged thus.
|
||||
if (m_currentLevel != newLevel)
|
||||
{
|
||||
VeterancyLevel oldLevel = m_currentLevel;
|
||||
m_currentLevel = newLevel;
|
||||
m_currentExperience = m_parent->getTemplate()->getExperienceRequired(m_currentLevel); //Minimum for this level
|
||||
if (m_parent)
|
||||
m_parent->onVeterancyLevelChanged( oldLevel, newLevel );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ExperienceTracker::gainExpForLevel(Int levelsToGain, Bool canScaleForBonus)
|
||||
{
|
||||
Int newLevel = (Int)m_currentLevel + levelsToGain;
|
||||
if (newLevel > LEVEL_LAST)
|
||||
newLevel = LEVEL_LAST;
|
||||
// gain what levels we can, even if we can't use 'em all
|
||||
if (newLevel > m_currentLevel)
|
||||
{
|
||||
Int experienceNeeded = m_parent->getTemplate()->getExperienceRequired(newLevel) - m_currentExperience;
|
||||
addExperiencePoints( experienceNeeded, canScaleForBonus );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ExperienceTracker::canGainExpForLevel(Int levelsToGain) const
|
||||
{
|
||||
Int newLevel = (Int)m_currentLevel + levelsToGain;
|
||||
// return true if we can gain levels, even if we can't gain ALL the levels requested
|
||||
if (newLevel > LEVEL_LAST)
|
||||
newLevel = LEVEL_LAST;
|
||||
return (newLevel > m_currentLevel);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ExperienceTracker::addExperiencePoints( Int experienceGain, Bool canScaleForBonus)
|
||||
{
|
||||
if( m_experienceSink != INVALID_ID )
|
||||
{
|
||||
// I have been set up to give my experience to someone else
|
||||
Object *sinkPointer = TheGameLogic->findObjectByID( m_experienceSink );
|
||||
if( sinkPointer )
|
||||
{
|
||||
// Not a fatal failure if not valid, he died when I was in the air.
|
||||
sinkPointer->getExperienceTracker()->addExperiencePoints( experienceGain * m_experienceScalar, canScaleForBonus );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( !isTrainable() )
|
||||
return; //safety
|
||||
|
||||
VeterancyLevel oldLevel = m_currentLevel;
|
||||
|
||||
Int amountToGain = experienceGain;
|
||||
if ( canScaleForBonus )
|
||||
amountToGain *= m_experienceScalar;
|
||||
|
||||
|
||||
m_currentExperience += amountToGain;
|
||||
|
||||
Int levelIndex = 0;
|
||||
while( ( (levelIndex + 1) < LEVEL_COUNT)
|
||||
&& m_currentExperience >= m_parent->getTemplate()->getExperienceRequired(levelIndex + 1)
|
||||
)
|
||||
{
|
||||
// If there is a higher level to qualify for, and I qualify for it, advance the index
|
||||
levelIndex++;
|
||||
}
|
||||
|
||||
m_currentLevel = (VeterancyLevel)levelIndex;
|
||||
|
||||
if( oldLevel != m_currentLevel )
|
||||
{
|
||||
// Edge trigger special level gain effects.
|
||||
m_parent->onVeterancyLevelChanged( oldLevel, m_currentLevel );
|
||||
}
|
||||
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ExperienceTracker::setExperienceAndLevel( Int experienceIn )
|
||||
{
|
||||
if( m_experienceSink != INVALID_ID )
|
||||
{
|
||||
// I have been set up to give my experience to someone else
|
||||
Object *sinkPointer = TheGameLogic->findObjectByID( m_experienceSink );
|
||||
if( sinkPointer )
|
||||
{
|
||||
// Not a fatal failure if not valid, he died when I was in the air.
|
||||
sinkPointer->getExperienceTracker()->setExperienceAndLevel( experienceIn );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( !isTrainable() )
|
||||
return; //safety
|
||||
|
||||
VeterancyLevel oldLevel = m_currentLevel;
|
||||
|
||||
m_currentExperience = experienceIn;
|
||||
|
||||
Int levelIndex = 0;
|
||||
while( ( (levelIndex + 1) < LEVEL_COUNT)
|
||||
&& m_currentExperience >= m_parent->getTemplate()->getExperienceRequired(levelIndex + 1)
|
||||
)
|
||||
{
|
||||
// If there is a level to qualify for, and I qualify for it, advance the index
|
||||
levelIndex++;
|
||||
}
|
||||
|
||||
m_currentLevel = (VeterancyLevel)levelIndex;
|
||||
|
||||
if( oldLevel != m_currentLevel )
|
||||
{
|
||||
// Edge trigger special level gain effects.
|
||||
m_parent->onVeterancyLevelChanged( oldLevel, m_currentLevel ); //<<== paradox! this may be a level lost!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ExperienceTracker::crc( Xfer *xfer )
|
||||
{
|
||||
xfer->xferInt( &m_currentExperience );
|
||||
xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) );
|
||||
} // end crc
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version
|
||||
*/
|
||||
// ----------------------------------------------------------------------------
|
||||
void ExperienceTracker::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// no need to save the m_parent pointer, it is connected on allocation time
|
||||
// m_parent
|
||||
|
||||
// current level
|
||||
xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) );
|
||||
|
||||
// current experience
|
||||
xfer->xferInt( &m_currentExperience );
|
||||
|
||||
// experience sink
|
||||
xfer->xferObjectID( &m_experienceSink );
|
||||
|
||||
// experience scalar
|
||||
xfer->xferReal( &m_experienceScalar );
|
||||
|
||||
} // end xfer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ExperienceTracker::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: FiringTracker.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood, February 2002
|
||||
// Desc: Keeps track of shots fired and people targeted for weapons that want a history of such a thing
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/AudioHandleSpecialValues.h"
|
||||
#include "Common/GameType.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameLogic/FiringTracker.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Module/ObjectHelper.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FiringTracker::FiringTracker(Thing* thing, const ModuleData *modData) : UpdateModule( thing, modData )
|
||||
{
|
||||
m_consecutiveShots = 0;
|
||||
m_victimID = INVALID_ID;
|
||||
m_frameToStartCooldown = 0;
|
||||
m_frameToForceReload = 0;
|
||||
m_frameToStopLoopingSound = 0;
|
||||
m_audioHandle = AHSV_NoSound;
|
||||
setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FiringTracker::~FiringTracker()
|
||||
{
|
||||
// no need to protect this.
|
||||
TheAudio->removeAudioEvent( m_audioHandle );
|
||||
m_audioHandle = AHSV_NoSound;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int FiringTracker::getNumConsecutiveShotsAtVictim( const Object *victim ) const
|
||||
{
|
||||
if( victim == NULL )
|
||||
return 0;// safety, this function is for asking about shots at a victim
|
||||
|
||||
if( victim->getID() != m_victimID )
|
||||
return 0;// nope, not shooting at him
|
||||
|
||||
return m_consecutiveShots;// this is how any times I have shot at this hoser right now
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FiringTracker::shotFired(const Weapon* weaponFired, ObjectID victimID)
|
||||
{
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
if( victimID == m_victimID )
|
||||
{
|
||||
// Shooting at the same guy
|
||||
++m_consecutiveShots;
|
||||
}
|
||||
else if( now < m_frameToStartCooldown )
|
||||
{
|
||||
// Switching targets within the coast time is valid, and we will not spin down
|
||||
++m_consecutiveShots;
|
||||
m_victimID = victimID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start the count over for the new guy
|
||||
m_consecutiveShots = 1;
|
||||
m_victimID = victimID;
|
||||
}
|
||||
|
||||
// Push back the time that we should force a reload with each shot
|
||||
UnsignedInt autoReloadDelay = weaponFired->getAutoReloadWhenIdleFrames();
|
||||
if( autoReloadDelay > 0 )
|
||||
m_frameToForceReload = now + autoReloadDelay;
|
||||
|
||||
UnsignedInt coast = weaponFired->getContinuousFireCoastFrames();
|
||||
if (coast)
|
||||
m_frameToStartCooldown = weaponFired->getPossibleNextShotFrame() + coast;
|
||||
else
|
||||
m_frameToStartCooldown = 0;
|
||||
|
||||
Int shotsNeededOne = weaponFired->getContinuousFireOneShotsNeeded();
|
||||
Int shotsNeededTwo = weaponFired->getContinuousFireTwoShotsNeeded();
|
||||
|
||||
if( getObject()->testWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN ) )
|
||||
{
|
||||
// Can either go up or down from here.
|
||||
if( m_consecutiveShots < shotsNeededOne )
|
||||
coolDown();
|
||||
else if( m_consecutiveShots > shotsNeededTwo )
|
||||
speedUp();
|
||||
}
|
||||
else if( getObject()->testWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST ) )
|
||||
{
|
||||
// Only place I can go here from here is all the way down
|
||||
if( m_consecutiveShots < shotsNeededTwo )
|
||||
{
|
||||
coolDown();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check to go up
|
||||
if( m_consecutiveShots > shotsNeededOne )
|
||||
speedUp();
|
||||
}
|
||||
|
||||
UnsignedInt fireSoundLoopTime = weaponFired->getFireSoundLoopTime();
|
||||
if (fireSoundLoopTime != 0)
|
||||
{
|
||||
// If the sound has stopped playing, then we need to re-add it.
|
||||
if (m_frameToStopLoopingSound == 0 || !TheAudio->isCurrentlyPlaying(m_audioHandle))
|
||||
{
|
||||
AudioEventRTS audio = weaponFired->getFireSound();
|
||||
audio.setObjectID(getObject()->getID());
|
||||
m_audioHandle = TheAudio->addAudioEvent( &audio );
|
||||
}
|
||||
m_frameToStopLoopingSound = now + fireSoundLoopTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioEventRTS fireAndForgetSound = weaponFired->getFireSound();
|
||||
fireAndForgetSound.setObjectID(getObject()->getID());
|
||||
TheAudio->addAudioEvent(&fireAndForgetSound);
|
||||
m_frameToStopLoopingSound = 0;
|
||||
}
|
||||
|
||||
|
||||
setWakeFrame(getObject(), calcTimeToSleep());
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime FiringTracker::update()
|
||||
{
|
||||
//DEBUG_ASSERTCRASH(m_frameToStartCooldown != 0 || m_frameToStopLoopingSound != 0, ("hmm, should be asleep"));
|
||||
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
|
||||
// I have been idle long enough that I should reload, so I do not hang around with a near empty clip forever.
|
||||
if( m_frameToForceReload != 0 && now >= m_frameToForceReload )
|
||||
{
|
||||
getObject()->reloadAllAmmo(TRUE);
|
||||
m_frameToForceReload = 0;
|
||||
}
|
||||
|
||||
// If it has been too long since I fired. I have to start over.
|
||||
// (don't call if we don't need to cool down... it's expensive!)
|
||||
|
||||
if (m_frameToStopLoopingSound != 0)
|
||||
{
|
||||
if (now >= m_frameToStopLoopingSound)
|
||||
{
|
||||
TheAudio->removeAudioEvent( m_audioHandle );
|
||||
m_audioHandle = AHSV_NoSound;
|
||||
m_frameToStopLoopingSound = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_frameToStartCooldown != 0 && now > m_frameToStartCooldown )
|
||||
{
|
||||
m_frameToStartCooldown = now + LOGICFRAMES_PER_SECOND;
|
||||
coolDown();// if this is the coolest call to cooldown, it will set m_frameToStartCooldown to zero
|
||||
return UPDATE_SLEEP(LOGICFRAMES_PER_SECOND);
|
||||
}
|
||||
|
||||
UpdateSleepTime sleepTime = calcTimeToSleep();
|
||||
|
||||
return sleepTime;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime FiringTracker::calcTimeToSleep()
|
||||
{
|
||||
// Figure out the longest amount of time we can sleep as unneeded
|
||||
|
||||
// If all the timers are off, then we aren't needed at all
|
||||
if (m_frameToStopLoopingSound == 0 && m_frameToStartCooldown == 0 && m_frameToForceReload == 0)
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
|
||||
// Otherwise, we need to wake up to service the shortest timer
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
UnsignedInt sleepTime = UPDATE_SLEEP_FOREVER;
|
||||
if( m_frameToStopLoopingSound != 0 )
|
||||
{
|
||||
if( m_frameToStopLoopingSound <= now )
|
||||
sleepTime = UPDATE_SLEEP_NONE;
|
||||
else if( (m_frameToStopLoopingSound - now) < sleepTime )
|
||||
sleepTime = m_frameToStopLoopingSound - now;
|
||||
}
|
||||
if( m_frameToStartCooldown != 0 )
|
||||
{
|
||||
if( m_frameToStartCooldown <= now )
|
||||
sleepTime = UPDATE_SLEEP_NONE;
|
||||
else if( (m_frameToStartCooldown - now) < sleepTime )
|
||||
sleepTime = m_frameToStartCooldown - now;
|
||||
}
|
||||
if( m_frameToForceReload != 0 )
|
||||
{
|
||||
if( m_frameToForceReload <= now )
|
||||
sleepTime = UPDATE_SLEEP_NONE;
|
||||
else if( (m_frameToForceReload - now) < sleepTime )
|
||||
sleepTime = m_frameToForceReload - now;
|
||||
}
|
||||
|
||||
return UPDATE_SLEEP(sleepTime);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FiringTracker::speedUp()
|
||||
{
|
||||
ModelConditionFlags clr, set;
|
||||
Object *self = getObject();
|
||||
|
||||
if( self->testWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST ) )
|
||||
{
|
||||
//self->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN );
|
||||
//self->clearModelConditionState( MODELCONDITION_CONTINUOUS_FIRE_MEAN );
|
||||
//self->clearModelConditionState( MODELCONDITION_CONTINUOUS_FIRE_SLOW );
|
||||
}
|
||||
else if(self->testWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN ) )
|
||||
{
|
||||
const AudioEventRTS *soundToPlayPtr = self->getTemplate()->getPerUnitSound( "VoiceRapidFire" );
|
||||
AudioEventRTS soundToPlay = *soundToPlayPtr;
|
||||
soundToPlay.setObjectID( self->getID() );
|
||||
TheAudio->addAudioEvent( &soundToPlay );
|
||||
|
||||
// These flags are exclusive, not cumulative
|
||||
self->setWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST );
|
||||
set.set(MODELCONDITION_CONTINUOUS_FIRE_FAST);
|
||||
|
||||
// These flags are exclusive, not cumulative
|
||||
self->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN );
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_MEAN);
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_SLOW);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
self->setWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN );
|
||||
set.set(MODELCONDITION_CONTINUOUS_FIRE_MEAN);
|
||||
|
||||
// These flags are exclusive, not cumulative
|
||||
self->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST );
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_FAST);
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_SLOW);
|
||||
|
||||
}
|
||||
|
||||
self->clearAndSetModelConditionFlags(clr, set);
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FiringTracker::coolDown()
|
||||
{
|
||||
ModelConditionFlags clr, set;
|
||||
|
||||
if( getObject()->testWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST )
|
||||
|| getObject()->testWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN ))
|
||||
{
|
||||
|
||||
// Straight to zero from wherever it is
|
||||
set.set(MODELCONDITION_CONTINUOUS_FIRE_SLOW);
|
||||
|
||||
getObject()->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST );
|
||||
getObject()->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN );
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_FAST);
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_MEAN);
|
||||
|
||||
|
||||
}
|
||||
else // we stop, now
|
||||
{
|
||||
|
||||
getObject()->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_FAST );
|
||||
getObject()->clearWeaponBonusCondition( WEAPONBONUSCONDITION_CONTINUOUS_FIRE_MEAN );
|
||||
// Just chillin, nothing to change
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_FAST);
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_MEAN);
|
||||
clr.set(MODELCONDITION_CONTINUOUS_FIRE_SLOW);
|
||||
|
||||
m_frameToStartCooldown = 0;
|
||||
}
|
||||
|
||||
getObject()->clearAndSetModelConditionFlags(clr, set);
|
||||
|
||||
// Start everything over
|
||||
m_consecutiveShots = 0;
|
||||
m_victimID = INVALID_ID;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FiringTracker::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// object helper base class
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FiringTracker::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// object helper base class
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
// consecutive shots
|
||||
xfer->xferInt( &m_consecutiveShots );
|
||||
|
||||
// victim id
|
||||
xfer->xferObjectID( &m_victimID );
|
||||
|
||||
// frame to start cooldown
|
||||
xfer->xferUnsignedInt( &m_frameToStartCooldown );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FiringTracker::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// object helper back class
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
224
Generals/Code/GameEngine/Source/GameLogic/Object/GhostObject.cpp
Normal file
224
Generals/Code/GameEngine/Source/GameLogic/Object/GhostObject.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE GhostObject.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Simple base object
|
||||
// Author: Michael S. Booth, October 2000
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/GhostObject.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
GhostObjectManager *TheGhostObjectManager=NULL;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GhostObject::GhostObject(void):
|
||||
//Added By Sadullah Nader
|
||||
//Initializations missing and needed
|
||||
m_parentAngle(0.0f),
|
||||
m_parentGeometryIsSmall(0.0f),
|
||||
m_parentGeometryMajorRadius(0.0f),
|
||||
m_parentGeometryminorRadius(0.0f),
|
||||
m_parentObject(NULL),
|
||||
m_partitionData(NULL)
|
||||
{
|
||||
m_parentPosition.zero();
|
||||
// End Initializations
|
||||
} // end Object
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
GhostObject::~GhostObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObject::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObject::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// parent object
|
||||
ObjectID parentObjectID = INVALID_ID;
|
||||
if( m_parentObject )
|
||||
parentObjectID = m_parentObject->getID();
|
||||
xfer->xferObjectID( &parentObjectID );
|
||||
if( xfer->getXferMode() == XFER_LOAD )
|
||||
{
|
||||
|
||||
// tie up parent object pointer
|
||||
m_parentObject = TheGameLogic->findObjectByID( parentObjectID );
|
||||
|
||||
// sanity
|
||||
if( parentObjectID != INVALID_ID && m_parentObject == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "GhostObject::xfer - Unable to connect m_parentObject\n" ));
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
// parent geometry type
|
||||
xfer->xferUser( &m_parentGeometryType, sizeof( GeometryType ) );
|
||||
|
||||
// parent geometry is small
|
||||
xfer->xferBool( &m_parentGeometryIsSmall );
|
||||
|
||||
// parent geometry major radius
|
||||
xfer->xferReal( &m_parentGeometryMajorRadius );
|
||||
|
||||
// parent geometry minor radius
|
||||
xfer->xferReal( &m_parentGeometryminorRadius );
|
||||
|
||||
// parent angle
|
||||
xfer->xferReal( &m_parentAngle );
|
||||
|
||||
// parent position
|
||||
xfer->xferCoord3D( &m_parentPosition );
|
||||
|
||||
// partition data
|
||||
///@todo write me ---> !!!!!
|
||||
// PartitionData *m_partitionData; ///< our PartitionData
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObject::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
GhostObjectManager::GhostObjectManager(void)
|
||||
{
|
||||
m_lockGhostObjects = FALSE;
|
||||
m_saveLockGhostObjects = FALSE;
|
||||
m_localPlayer = 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
GhostObjectManager::~GhostObjectManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::reset(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
GhostObject *GhostObjectManager::addGhostObject(Object *object, PartitionData *pd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::removeGhostObject(GhostObject *mod)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::updateOrphanedObjects(int *playerIndexList, int numNonLocalPlayers)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::releasePartitionData(void)
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::restorePartitionData(void)
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method:
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// local player
|
||||
xfer->xferInt( &m_localPlayer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GhostObjectManager::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ObjectDefectionHelper.cpp ////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, Steven Johnson - September 2002
|
||||
// Desc: Object helper module
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/ObjectDefectionHelper.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ObjectDefectionHelper::~ObjectDefectionHelper( void )
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime ObjectDefectionHelper::update()
|
||||
{
|
||||
Object* obj = getObject();
|
||||
Drawable* draw = obj->getDrawable();
|
||||
|
||||
// if we are here, we should be an undetected defector, but
|
||||
// just in case someone has changed us behind our backs...
|
||||
if (!obj->getIsUndetectedDefector())
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
if (now >= m_defectionDetectionEnd)
|
||||
{
|
||||
obj->friend_setUndetectedDefector(FALSE);
|
||||
|
||||
// timer has reached zero, so we flash white once!!!! -- lorenzen
|
||||
if (draw && m_doDefectorFX)
|
||||
{
|
||||
RGBColor white = {1,1,1};
|
||||
if (draw)
|
||||
draw->flashAsSelected( &white ); //Whew! that's easier, now, isn't it!
|
||||
|
||||
|
||||
AudioEventRTS defectorVulnerableSound = TheAudio->getMiscAudio()->m_defectorTimerDingSound;
|
||||
defectorVulnerableSound.setObjectID( obj->getID() );
|
||||
TheAudio->addAudioEvent(&defectorVulnerableSound);
|
||||
}
|
||||
return UPDATE_SLEEP_FOREVER; // hey, we're done.
|
||||
}
|
||||
|
||||
// dead or attacking... our cover is blown.
|
||||
if (obj->isEffectivelyDead() ||
|
||||
(obj->getStatusBits() & OBJECT_STATUS_IS_FIRING_WEAPON)!= 0)
|
||||
{
|
||||
// PLEASE NOTE:
|
||||
// checking the is_attacking statusbit above, only handles weapon related attacks...
|
||||
// I also set m_undetectedDefector = FALSE in the groupDoSpecialPower[...]( ) functions;
|
||||
obj->friend_setUndetectedDefector(FALSE);
|
||||
return UPDATE_SLEEP_FOREVER; // hey, we're done.
|
||||
}
|
||||
|
||||
if (draw && m_doDefectorFX) // skip fx if merely 'invulnerable'
|
||||
{
|
||||
Bool lastPhase = ( ((Int)m_defectionDetectionFlashPhase) & 1 );// were we in a flashy phase last frame?
|
||||
UnsignedInt timeLeft = m_defectionDetectionEnd - now;
|
||||
m_defectionDetectionFlashPhase += 0.5f * ( 1.0f - ((Real)timeLeft / DEFECTION_DETECTION_TIME_MAX ) );
|
||||
Bool thisPhase = ( ((Int)m_defectionDetectionFlashPhase) & 1 );// are we in a flashy phase this frame?
|
||||
|
||||
if ( lastPhase && ( ! thisPhase ) )
|
||||
{
|
||||
draw->flashAsSelected(); //Whew! that's easier, now, isn't it!
|
||||
|
||||
AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound;
|
||||
defectorTimerSound.setObjectID( obj->getID() );
|
||||
TheAudio->addAudioEvent(&defectorTimerSound);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return UPDATE_SLEEP_NONE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectDefectionHelper::startDefectionTimer(UnsignedInt numFrames, Bool withDefectorFX)
|
||||
{
|
||||
Object* obj = getObject();
|
||||
|
||||
if (!obj->getIsUndetectedDefector())
|
||||
{
|
||||
setWakeFrame(obj, UPDATE_SLEEP_FOREVER);
|
||||
return;
|
||||
}
|
||||
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
m_defectionDetectionStart = now;
|
||||
m_defectionDetectionEnd = now + numFrames;
|
||||
m_defectionDetectionFlashPhase = 0.0f;
|
||||
m_doDefectorFX = withDefectorFX;
|
||||
|
||||
setWakeFrame(obj, UPDATE_SLEEP_NONE);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectDefectionHelper::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// object helper crc
|
||||
ObjectHelper::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info;
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectDefectionHelper::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::xfer( xfer );
|
||||
|
||||
// detection start
|
||||
xfer->xferUnsignedInt( &m_defectionDetectionStart );
|
||||
|
||||
// detection end
|
||||
xfer->xferUnsignedInt( &m_defectionDetectionEnd );
|
||||
|
||||
// flash phase
|
||||
xfer->xferReal( &m_defectionDetectionFlashPhase );
|
||||
|
||||
// do defector fx
|
||||
xfer->xferBool( &m_doDefectorFX );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectDefectionHelper::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ObjectHelper.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, Steven Johnson, September 2002
|
||||
// Desc: Object helper module base class
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/ObjectHelper.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ObjectHelper::~ObjectHelper( void )
|
||||
{
|
||||
|
||||
} // end ~ObjectHelper
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectHelper::sleepUntil(UnsignedInt when)
|
||||
{
|
||||
if (getObject()->getStatusBits() & OBJECT_STATUS_DESTROYED)
|
||||
return;
|
||||
|
||||
// note the setWakeFrame(NEVER) actually awakens immediately, since NEVER==0.
|
||||
// when we get NEVER in this case, we really want to sleep forever.
|
||||
// so just special case it.
|
||||
UpdateSleepTime wakeDelay = (when == NEVER || when == FOREVER) ?
|
||||
UPDATE_SLEEP_FOREVER :
|
||||
UPDATE_SLEEP(when - TheGameLogic->getFrame());
|
||||
setWakeFrame(getObject(), wakeDelay);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectHelper::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// update module crc
|
||||
UpdateModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/* Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial Version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectHelper::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// update module xfer
|
||||
UpdateModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectHelper::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// update module post process
|
||||
UpdateModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ObjectRepulsorHelper.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, December 2002
|
||||
// Desc: Repulsor helper
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/ObjectRepulsorHelper.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ObjectRepulsorHelper::~ObjectRepulsorHelper( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime ObjectRepulsorHelper::update()
|
||||
{
|
||||
// if we ever get here, clear this.
|
||||
getObject()->setStatus(OBJECT_STATUS_REPULSOR, FALSE);
|
||||
|
||||
// then go back to sleep until we are forcibly awakened.
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectRepulsorHelper::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// object helper crc
|
||||
ObjectHelper::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info;
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectRepulsorHelper::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectRepulsorHelper::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ObjectSMCHelper.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, Steven Johnson, September 2002
|
||||
// Desc: SMC helper
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/ObjectSMCHelper.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ObjectSMCHelper::~ObjectSMCHelper( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UpdateSleepTime ObjectSMCHelper::update()
|
||||
{
|
||||
// if we ever get here, stop this.
|
||||
getObject()->clearSpecialModelConditionStates();
|
||||
|
||||
// then go back to sleep until we are forcibly awakened.
|
||||
return UPDATE_SLEEP_FOREVER;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectSMCHelper::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// object helper crc
|
||||
ObjectHelper::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info;
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectSMCHelper::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectSMCHelper::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ObjectSMCHelper.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, Steven Johnson, September 2002
|
||||
// Desc: Weapon status helper
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/ObjectWeaponStatusHelper.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
ObjectWeaponStatusHelper::~ObjectWeaponStatusHelper( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectWeaponStatusHelper::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// object helper crc
|
||||
ObjectHelper::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info;
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectWeaponStatusHelper::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectWeaponStatusHelper::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// object helper base class
|
||||
ObjectHelper::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
2768
Generals/Code/GameEngine/Source/GameLogic/Object/Locomotor.cpp
Normal file
2768
Generals/Code/GameEngine/Source/GameLogic/Object/Locomotor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
5415
Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp
Normal file
5415
Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
208
Generals/Code/GameEngine/Source/GameLogic/Object/ObjectTypes.cpp
Normal file
208
Generals/Code/GameEngine/Source/GameLogic/Object/ObjectTypes.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ObjectTypes.cpp //////////////////////////////////////////////////////////////////////////
|
||||
// Author: John McDonald, Jr.
|
||||
// September 2002
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "GameLogic/ObjectTypes.h"
|
||||
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ObjectTypes::ObjectTypes()
|
||||
{
|
||||
// Nada
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ObjectTypes::ObjectTypes(const AsciiString& listName) : m_listName(listName)
|
||||
{
|
||||
// Nada
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ObjectTypes::addObjectType(const AsciiString &objectType)
|
||||
{
|
||||
if (isInSet(objectType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_objectTypes.push_back(objectType);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ObjectTypes::removeObjectType(const AsciiString &objectType)
|
||||
{
|
||||
if (!isInSet(objectType)) {
|
||||
DEBUG_CRASH(("Attempted to remove '%s' from '%s', but it wasn't there.", objectType.str(), m_listName.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiStringVec::iterator it = std::find(m_objectTypes.begin(), m_objectTypes.end(), objectType);
|
||||
|
||||
m_objectTypes.erase(it);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const AsciiString& ObjectTypes::getListName() const
|
||||
{
|
||||
return m_listName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ObjectTypes::setListName(const AsciiString& listName)
|
||||
{
|
||||
m_listName = listName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ObjectTypes::isInSet(const AsciiString& objectType) const
|
||||
{
|
||||
return (std::find(m_objectTypes.begin(), m_objectTypes.end(), objectType) != m_objectTypes.end());
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ObjectTypes::isInSet(const ThingTemplate* objectType) const
|
||||
{
|
||||
if (!objectType) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return isInSet(objectType->getName());
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int ObjectTypes::prepForPlayerCounting( std::vector<const ThingTemplate *>& templates, std::vector<Int>& counts)
|
||||
{
|
||||
AsciiStringVecIt it;
|
||||
for (it = m_objectTypes.begin(); it != m_objectTypes.end(); ++it) {
|
||||
const ThingTemplate *templ = TheThingFactory->findTemplate(*it);
|
||||
if (templ) {
|
||||
templates.push_back(templ);
|
||||
}
|
||||
}
|
||||
|
||||
Int retVal = templates.size();
|
||||
counts.resize(retVal);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ObjectTypes::canBuildAny(Player *player)
|
||||
{
|
||||
AsciiStringVecIt it;
|
||||
for (it = m_objectTypes.begin(); it != m_objectTypes.end(); ++it) {
|
||||
const ThingTemplate *templ = TheThingFactory->findTemplate(*it);
|
||||
if (templ && player->canBuild(templ)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectTypes::crc(Xfer *xfer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Version Info:
|
||||
* 1: Initial version
|
||||
*/
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectTypes::xfer(Xfer *xfer)
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// list name
|
||||
xfer->xferAsciiString( &m_listName );
|
||||
|
||||
// size of object types vector
|
||||
UnsignedShort objectTypesCount = m_objectTypes.size();
|
||||
xfer->xferUnsignedShort( &objectTypesCount );
|
||||
|
||||
// object types data
|
||||
if( xfer->getXferMode() == XFER_SAVE )
|
||||
{
|
||||
|
||||
// iterate vector
|
||||
AsciiStringVecIt it;
|
||||
for( it = m_objectTypes.begin(); it != m_objectTypes.end(); ++it )
|
||||
{
|
||||
|
||||
// write type name
|
||||
xfer->xferAsciiString( &(*it) );
|
||||
|
||||
} // end for, it
|
||||
|
||||
} // end if, save
|
||||
else
|
||||
{
|
||||
|
||||
// sanity, the vector should be empty when loading
|
||||
if( m_objectTypes.empty() == FALSE )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "ObjectTypes::xfer - m_objectTypes vector should be emtpy but is not!\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// read all data
|
||||
AsciiString typeName;
|
||||
for( UnsignedShort i = 0; i < objectTypesCount; ++i )
|
||||
{
|
||||
|
||||
// read name
|
||||
xfer->xferAsciiString( &typeName );
|
||||
|
||||
// put on vector
|
||||
m_objectTypes.push_back( typeName );
|
||||
|
||||
} // end for, i
|
||||
|
||||
} // end else, load
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ObjectTypes::loadPostProcess()
|
||||
{
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// SimpleObjectIterator
|
||||
// Implementation of a simple object iterator
|
||||
// Author: Steven Johnson, September 2001
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "GameLogic/ObjectIter.h"
|
||||
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
|
||||
/// @todo Doxygenize this file
|
||||
|
||||
SimpleObjectIterator::ClumpCompareProc SimpleObjectIterator::theClumpCompareProcs[] =
|
||||
{
|
||||
NULL, // "fastest" gets no proc
|
||||
SimpleObjectIterator::sortNearToFar,
|
||||
SimpleObjectIterator::sortFarToNear,
|
||||
SimpleObjectIterator::sortCheapToExpensive,
|
||||
SimpleObjectIterator::sortExpensiveToCheap
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
SimpleObjectIterator::Clump::Clump()
|
||||
{
|
||||
m_nextClump = NULL;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
SimpleObjectIterator::Clump::~Clump()
|
||||
{
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
SimpleObjectIterator::SimpleObjectIterator()
|
||||
{
|
||||
m_firstClump = NULL;
|
||||
m_curClump = NULL;
|
||||
m_clumpCount = 0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
SimpleObjectIterator::~SimpleObjectIterator()
|
||||
{
|
||||
makeEmpty();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void SimpleObjectIterator::insert(Object *obj, Real numeric)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(obj, ("sorry, no nulls allowed here"));
|
||||
|
||||
Clump *clump = newInstance(Clump)();
|
||||
|
||||
clump->m_nextClump = m_firstClump;
|
||||
m_firstClump = clump;
|
||||
|
||||
clump->m_obj = obj;
|
||||
clump->m_numeric = numeric;
|
||||
|
||||
++m_clumpCount;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
Object *SimpleObjectIterator::nextWithNumeric(Real *num)
|
||||
{
|
||||
Object *obj = NULL;
|
||||
if (num)
|
||||
*num = 0.0f;
|
||||
|
||||
if (m_curClump)
|
||||
{
|
||||
obj = m_curClump->m_obj;
|
||||
if (num)
|
||||
*num = m_curClump->m_numeric;
|
||||
m_curClump = m_curClump->m_nextClump;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void SimpleObjectIterator::reset()
|
||||
{
|
||||
m_curClump = m_firstClump;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void SimpleObjectIterator::makeEmpty()
|
||||
{
|
||||
while (m_firstClump)
|
||||
{
|
||||
Clump *next = m_firstClump->m_nextClump;
|
||||
m_firstClump->deleteInstance();
|
||||
m_firstClump = next;
|
||||
--m_clumpCount;
|
||||
}
|
||||
DEBUG_ASSERTCRASH(m_clumpCount == 0, ("hmm"));
|
||||
|
||||
m_firstClump = NULL;
|
||||
m_curClump = NULL;
|
||||
m_clumpCount = 0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void SimpleObjectIterator::sort(IterOrderType order)
|
||||
{
|
||||
if (m_clumpCount == 0)
|
||||
return;
|
||||
|
||||
#ifdef INTENSE_DEBUG
|
||||
{
|
||||
DEBUG_LOG(("\n\n---------- BEFORE sort for %d -----------\n",order));
|
||||
for (Clump *p = m_firstClump; p; p = p->m_nextClump)
|
||||
{
|
||||
DEBUG_LOG((" obj %08lx numeric %f\n",p->m_obj,p->m_numeric));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ClumpCompareProc cmpProc = theClumpCompareProcs[order];
|
||||
|
||||
if (!cmpProc)
|
||||
return; // my, that was easy
|
||||
|
||||
// do a basic mergesort, which works nicely for linked lists,
|
||||
// and is reasonably efficient (N log N).
|
||||
|
||||
for ( Int n = 1 ; ; n *= 2 )
|
||||
{
|
||||
Clump *to_do = m_firstClump;
|
||||
Clump *tail = NULL;
|
||||
m_firstClump = NULL;
|
||||
|
||||
Int mergeCount = 0;
|
||||
|
||||
while (to_do)
|
||||
{
|
||||
++mergeCount;
|
||||
|
||||
Int to_do_count = 0;
|
||||
|
||||
// make two lists of length 'n' (to_do is one, sub is the other)
|
||||
Clump *sub = to_do;
|
||||
for (Int i = 0; i < n; i++)
|
||||
{
|
||||
++to_do_count;
|
||||
sub = sub->m_nextClump;
|
||||
if (!sub)
|
||||
break;
|
||||
}
|
||||
Int subCount = sub ? n : 0;
|
||||
|
||||
// merge the two lists.
|
||||
DEBUG_ASSERTCRASH(to_do_count + subCount >= 0, ("uhoh"));
|
||||
while (to_do_count + subCount > 0) {
|
||||
|
||||
DEBUG_ASSERTCRASH(to_do_count + subCount >= 0, ("uhoh"));
|
||||
|
||||
Clump *tmp;
|
||||
|
||||
// bleah, coalesce into more elegant test case
|
||||
if (subCount == 0)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(to_do_count > 0, ("hmm, expected nonzero to_do_count"));
|
||||
tmp = to_do;
|
||||
to_do = to_do->m_nextClump;
|
||||
--to_do_count;
|
||||
}
|
||||
else if (to_do_count == 0)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(subCount > 0, ("hmm, expected nonzero subCount"));
|
||||
tmp = sub;
|
||||
sub = sub->m_nextClump;
|
||||
--subCount;
|
||||
}
|
||||
else if ((*cmpProc)(to_do, sub) <= 0.0f)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(to_do_count > 0, ("hmm, expected nonzero to_do_count"));
|
||||
tmp = to_do;
|
||||
to_do = to_do->m_nextClump;
|
||||
--to_do_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ASSERTCRASH(subCount > 0, ("hmm, expected nonzero subCount"));
|
||||
tmp = sub;
|
||||
sub = sub->m_nextClump;
|
||||
--subCount;
|
||||
}
|
||||
if (!sub) subCount = 0;
|
||||
if (!to_do) to_do_count = 0;
|
||||
|
||||
if (tail)
|
||||
tail->m_nextClump = tmp;
|
||||
else
|
||||
m_firstClump = tmp;
|
||||
tail = tmp;
|
||||
}
|
||||
|
||||
to_do = sub;
|
||||
}
|
||||
if (tail)
|
||||
tail->m_nextClump = NULL;
|
||||
|
||||
if (mergeCount <= 1) // when we have done just one (or none) swap, we're done
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef INTENSE_DEBUG
|
||||
{
|
||||
DEBUG_LOG(("\n\n---------- sort for %d -----------\n",order));
|
||||
for (Clump *p = m_firstClump; p; p = p->m_nextClump)
|
||||
{
|
||||
DEBUG_LOG((" obj %08lx numeric %f\n",p->m_obj,p->m_numeric));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// always reset after sorting, to prevent weirdness
|
||||
reset();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real SimpleObjectIterator::sortNearToFar(Clump *a, Clump *b)
|
||||
{
|
||||
return a->m_numeric - b->m_numeric;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real SimpleObjectIterator::sortFarToNear(Clump *a, Clump *b)
|
||||
{
|
||||
return b->m_numeric - a->m_numeric;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real SimpleObjectIterator::sortCheapToExpensive(Clump *a, Clump *b)
|
||||
{
|
||||
return a->m_obj->getTemplate()->friend_getBuildCost() -
|
||||
b->m_obj->getTemplate()->friend_getBuildCost();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real SimpleObjectIterator::sortExpensiveToCheap(Clump *a, Clump *b)
|
||||
{
|
||||
return b->m_obj->getTemplate()->friend_getBuildCost() -
|
||||
a->m_obj->getTemplate()->friend_getBuildCost();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BaikonurLaunchPower.h /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Created: November 2002
|
||||
//
|
||||
// Filename: BaikonurLaunchPower.h
|
||||
//
|
||||
// Author: Kris Morness
|
||||
//
|
||||
// Purpose: Triggers the beginning of the launch for the baikonur launch tower.
|
||||
// This is used only by script to trigger the GLA end game.
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/BaikonurLaunchPower.h"
|
||||
|
||||
BaikonurLaunchPowerModuleData::BaikonurLaunchPowerModuleData( void )
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void BaikonurLaunchPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "DetonationObject", INI::parseAsciiString, NULL, offsetof( BaikonurLaunchPowerModuleData, m_detonationObject ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BaikonurLaunchPower::BaikonurLaunchPower( Thing *thing, const ModuleData *moduleData )
|
||||
: SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BaikonurLaunchPower::~BaikonurLaunchPower( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BaikonurLaunchPower::doSpecialPower( UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPower( commandOptions );
|
||||
|
||||
getObject()->setModelConditionState( MODELCONDITION_DOOR_1_OPENING );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BaikonurLaunchPower::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
const BaikonurLaunchPowerModuleData *data = getBaikonurLaunchPowerModuleData();
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtLocation( loc, commandOptions );
|
||||
|
||||
//Create the detonation
|
||||
const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_detonationObject );
|
||||
if( thing )
|
||||
{
|
||||
Object *detonation = TheThingFactory->newObject( thing, getObject()->getTeam() );
|
||||
if( detonation )
|
||||
{
|
||||
detonation->setPosition( loc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BaikonurLaunchPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BaikonurLaunchPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void BaikonurLaunchPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CashBountyPower.cpp /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// created: Aug 2002
|
||||
//
|
||||
// Filename: CashBountyPower.cpp
|
||||
//
|
||||
// author: Steven Johnson
|
||||
//
|
||||
// purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/CashBountyPower.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CashBountyPowerModuleData::CashBountyPowerModuleData()
|
||||
{
|
||||
#ifdef NOT_IN_USE
|
||||
m_upgrades.clear();
|
||||
#endif
|
||||
m_defaultBounty = 0.0;
|
||||
}
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseBountyUpgradePair( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
CashBountyPowerModuleData::Upgrades up;
|
||||
|
||||
INI::parseScience(ini, NULL, &up.m_science, NULL);
|
||||
INI::parsePercentToReal(ini, NULL, &up.m_bounty, NULL);
|
||||
|
||||
std::vector<CashBountyPowerModuleData::Upgrades>* s = (std::vector<CashBountyPowerModuleData::Upgrades>*)store;
|
||||
s->push_back(up);
|
||||
}
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/* static */ void CashBountyPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
#ifdef NOT_IN_USE
|
||||
{ "UpgradeBounty", parseBountyUpgradePair, NULL, offsetof( CashBountyPowerModuleData, m_upgrades ) },
|
||||
#endif
|
||||
{ "Bounty", INI::parsePercentToReal, NULL, offsetof( CashBountyPowerModuleData, m_defaultBounty ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CashBountyPower::CashBountyPower( Thing *thing, const ModuleData* moduleData ) :
|
||||
SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
} // end CashBountyPower
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CashBountyPower::~CashBountyPower()
|
||||
{
|
||||
|
||||
} // end ~CashBountyPower
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CashBountyPower::onObjectCreated()
|
||||
{
|
||||
Player* controller = getObject()->getControllingPlayer();
|
||||
if (controller && controller->hasScience(getRequiredScience()))
|
||||
{
|
||||
Real bounty = findBounty();
|
||||
if (bounty > controller->getCashBounty())
|
||||
controller->setCashBounty(bounty);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real CashBountyPower::findBounty() const
|
||||
{
|
||||
const CashBountyPowerModuleData* d = getCashBountyPowerModuleData();
|
||||
#ifdef NOT_IN_USE
|
||||
const Player* controller = getObject()->getControllingPlayer();
|
||||
if (controller != NULL)
|
||||
{
|
||||
for (std::vector<CashBountyPowerModuleData::Upgrades>::const_iterator it = d->m_upgrades.begin();
|
||||
it != d->m_upgrades.end();
|
||||
++it)
|
||||
{
|
||||
if (controller->hasScience(it->m_science))
|
||||
return it->m_bounty;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return d->m_defaultBounty;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CashBountyPower::onSpecialPowerCreation()
|
||||
{
|
||||
SpecialPowerModule::onSpecialPowerCreation();
|
||||
|
||||
Player* controller = getObject()->getControllingPlayer();
|
||||
if (controller)
|
||||
{
|
||||
Real bounty = findBounty();
|
||||
if (bounty > controller->getCashBounty())
|
||||
controller->setCashBounty(bounty);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashBountyPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashBountyPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashBountyPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CashHackSpecialPower.cpp /////////////////////////////////////////////////////////////
|
||||
// Author: Amit Kumar, July 2002
|
||||
// Desc: The Cash Hack will steal money from an enemy player
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Team.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/CashHackSpecialPower.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
CashHackSpecialPowerModuleData::CashHackSpecialPowerModuleData( void )
|
||||
{
|
||||
m_upgrades.clear();
|
||||
m_defaultAmountToSteal = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseCashHackUpgradePair( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
CashHackSpecialPowerModuleData::Upgrades up;
|
||||
|
||||
INI::parseScience(ini, NULL, &up.m_science, NULL);
|
||||
INI::parseInt(ini, NULL, &up.m_amountToSteal, NULL);
|
||||
|
||||
std::vector<CashHackSpecialPowerModuleData::Upgrades>* s = (std::vector<CashHackSpecialPowerModuleData::Upgrades>*)store;
|
||||
s->push_back(up);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/*static*/ void CashHackSpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "UpgradeMoneyAmount", parseCashHackUpgradePair, NULL, offsetof( CashHackSpecialPowerModuleData, m_upgrades ) },
|
||||
{ "MoneyAmount", INI::parseInt, NULL, offsetof( CashHackSpecialPowerModuleData, m_defaultAmountToSteal ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
CashHackSpecialPower::CashHackSpecialPower( Thing *thing, const ModuleData *moduleData )
|
||||
: SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end CashHackSpecialPower
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
CashHackSpecialPower::~CashHackSpecialPower( void )
|
||||
{
|
||||
|
||||
} // end ~CashHackSpecialPower
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashHackSpecialPower::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// only allowed at objects
|
||||
return;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int CashHackSpecialPower::findAmountToSteal() const
|
||||
{
|
||||
const CashHackSpecialPowerModuleData* d = getCashHackSpecialPowerModuleData();
|
||||
const Player* controller = getObject()->getControllingPlayer();
|
||||
if (controller != NULL)
|
||||
{
|
||||
for (std::vector<CashHackSpecialPowerModuleData::Upgrades>::const_iterator it = d->m_upgrades.begin();
|
||||
it != d->m_upgrades.end();
|
||||
++it)
|
||||
{
|
||||
if (controller->hasScience(it->m_science))
|
||||
return it->m_amountToSteal;
|
||||
}
|
||||
}
|
||||
return d->m_defaultAmountToSteal;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashHackSpecialPower::doSpecialPowerAtObject( Object *victim, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// sanity
|
||||
if (!victim)
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtObject( victim, commandOptions );
|
||||
|
||||
// get our module data
|
||||
Object *self = getObject();
|
||||
|
||||
//Steal a thousand cash from the other team!
|
||||
Money *targetMoney = victim->getControllingPlayer()->getMoney();
|
||||
Money *selfMoney = self->getControllingPlayer()->getMoney();
|
||||
if( targetMoney && selfMoney )
|
||||
{
|
||||
UnsignedInt cash = targetMoney->countMoney();
|
||||
UnsignedInt desiredAmount = findAmountToSteal();
|
||||
//Check to see if they have 1000 cash, otherwise, take the remainder!
|
||||
cash = min( desiredAmount, cash );
|
||||
if( cash > 0 )
|
||||
{
|
||||
//Steal the cash
|
||||
targetMoney->withdraw( cash );
|
||||
selfMoney->deposit( cash );
|
||||
self->getControllingPlayer()->getScoreKeeper()->addMoneyEarned( cash );
|
||||
|
||||
//Display cash income floating over the blacklotus
|
||||
UnicodeString moneyString;
|
||||
moneyString.format( TheGameText->fetch( "GUI:AddCash" ), cash );
|
||||
Coord3D pos;
|
||||
pos.zero();
|
||||
pos.add( self->getPosition() );
|
||||
pos.z += 20.0f; //add a little z to make it show up above the unit.
|
||||
TheInGameUI->addFloatingText( moneyString, &pos, GameMakeColor( 0, 255, 0, 255 ) );
|
||||
|
||||
//Display cash lost floating over the target
|
||||
moneyString.format( TheGameText->fetch( "GUI:LoseCash" ), cash );
|
||||
pos.zero();
|
||||
pos.add( victim->getPosition() );
|
||||
pos.z += 30.0f; //add a little z to make it show up above the unit.
|
||||
TheInGameUI->addFloatingText( moneyString, &pos, GameMakeColor( 255, 0, 0, 255 ) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashHackSpecialPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashHackSpecialPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CashHackSpecialPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CleanupAreaPower.cpp /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Created: September 2002
|
||||
//
|
||||
// Author: Kris Morness
|
||||
//
|
||||
// Makes use of the cleanup hazard update by augmenting the cleanup range
|
||||
// until there is nothing left to cleanup at which time it goes idle.
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Module/CleanupAreaPower.h"
|
||||
#include "GameLogic/Module/CleanupHazardUpdate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CleanupAreaPowerModuleData::CleanupAreaPowerModuleData()
|
||||
{
|
||||
m_cleanupMoveRange = 0.0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CleanupAreaPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "MaxMoveDistanceFromLocation", INI::parseReal, NULL, offsetof( CleanupAreaPowerModuleData, m_cleanupMoveRange ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CleanupAreaPower::CleanupAreaPower( Thing *thing, const ModuleData* moduleData ) : SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
CleanupAreaPower::~CleanupAreaPower()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CleanupAreaPower::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
Object *obj = getObject();
|
||||
const CleanupAreaPowerModuleData *data = getCleanupAreaPowerModuleData();
|
||||
|
||||
static NameKeyType key_CleanupHazardUpdate = NAMEKEY( "CleanupHazardUpdate" );
|
||||
CleanupHazardUpdate *update = (CleanupHazardUpdate*)obj->findUpdateModule( key_CleanupHazardUpdate );
|
||||
if( update )
|
||||
{
|
||||
update->setCleanupAreaParameters( loc, data->m_cleanupMoveRange );
|
||||
}
|
||||
else
|
||||
{
|
||||
//This case will only happen should the ambulance not have a cleanuphazard update module.
|
||||
DEBUG_CRASH( ("%s is attempting to use CleanupAreaPower, but requires a CleanupHazardUpdate module to work!", obj->getTemplate()->getName().str() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CleanupAreaPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Xfer method
|
||||
// Version Info:
|
||||
// 1: Initial version
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CleanupAreaPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void CleanupAreaPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FILE: DefectorSpecialPower.cpp
|
||||
// Author: Mark Lorenzen, JULY 2002
|
||||
// Desc: General can click command cursor on any enemy, and it becomes his
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
#include "Common/Team.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameLogic/Module/DefectorSpecialPower.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DefectorSpecialPowerModuleData::DefectorSpecialPowerModuleData( void )
|
||||
{
|
||||
|
||||
m_fatCursorRadius = 0.0f;
|
||||
|
||||
} // end DefectorSpecialPowerModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
//static
|
||||
|
||||
void DefectorSpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "FatCursorRadius", INI::parseReal, NULL, offsetof( DefectorSpecialPowerModuleData, m_fatCursorRadius ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DefectorSpecialPower::DefectorSpecialPower( Thing *thing, const ModuleData *moduleData )
|
||||
: SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end DefectorSpecialPower
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DefectorSpecialPower::~DefectorSpecialPower( void )
|
||||
{
|
||||
|
||||
} // end ~DefectorSpecialPower
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void DefectorSpecialPower::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// only allowed at objects
|
||||
return;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DefectorSpecialPower::doSpecialPowerAtObject( Object *objectToMakeDefector, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// sanity check
|
||||
if (!objectToMakeDefector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// another sanity check
|
||||
const Object *self = getObject();
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtObject( objectToMakeDefector, commandOptions );
|
||||
|
||||
//AIUpdateInterface *hisAI = objectToMakeDefector->getAIUpdateInterface();
|
||||
//if (hisAI)
|
||||
//{
|
||||
// how do I get at SpecialPowerTemplate::getDetectionTime() from here?
|
||||
const SpecialPowerTemplate *specPowTemp = getSpecialPowerTemplate();
|
||||
UnsignedInt time = specPowTemp->getDetectionTime();
|
||||
|
||||
|
||||
objectToMakeDefector->defect(self->getControllingPlayer()->getDefaultTeam(), time );// @todo lorenzen hook into the new AIUpdateI methods
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DefectorSpecialPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DefectorSpecialPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DefectorSpecialPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DemoralizeSpecialPower.cpp ///////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, July 2002
|
||||
// Desc: Demoralize
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#ifdef ALLOW_DEMORALIZE
|
||||
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/AIUpdate.h"
|
||||
#include "GameLogic/Module/ContainModule.h"
|
||||
#include "GameLogic/Module/DemoralizeSpecialPower.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DemoralizeSpecialPowerModuleData::DemoralizeSpecialPowerModuleData( void )
|
||||
{
|
||||
|
||||
m_baseRange = 0.0f;
|
||||
m_bonusRangePerCaptured = 0.0f;
|
||||
m_maxRange = 0.0f;
|
||||
m_baseDurationInFrames = 0;
|
||||
m_bonusDurationPerCapturedInFrames = 0;
|
||||
m_maxDurationInFrames = 0;
|
||||
m_fxList = NULL;
|
||||
|
||||
} // end DemoralizeSpecialPowerModuleData
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DemoralizeSpecialPowerModuleData::buildFieldParse( MultiIniFieldParse &p )
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "BaseRange", INI::parseReal, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_baseRange ) },
|
||||
{ "BonusRangePerCaptured", INI::parseReal, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_bonusRangePerCaptured ) },
|
||||
{ "MaxRange", INI::parseReal, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_maxRange ) },
|
||||
{ "BaseDuration", INI::parseDurationUnsignedInt, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_baseDurationInFrames ) },
|
||||
{ "BonusDurationPerCaptured", INI::parseDurationUnsignedInt, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_bonusDurationPerCapturedInFrames ) },
|
||||
{ "MaxDuration", INI::parseDurationUnsignedInt, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_maxDurationInFrames ) },
|
||||
{ "FXList", INI::parseFXList, NULL, offsetof( DemoralizeSpecialPowerModuleData, m_fxList ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add( dataFieldParse );
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DemoralizeSpecialPower::DemoralizeSpecialPower( Thing *thing, const ModuleData *moduleData )
|
||||
: SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end DemoralizeSpecialPower
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
DemoralizeSpecialPower::~DemoralizeSpecialPower( void )
|
||||
{
|
||||
|
||||
} // end ~DemoralizeSpecialPower
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DemoralizeSpecialPower::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// sanity
|
||||
if( loc == NULL )
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtLocation( loc, commandOptions );
|
||||
|
||||
// the source of my fiendish power
|
||||
Object *source = getObject();
|
||||
|
||||
// get module data
|
||||
const DemoralizeSpecialPowerModuleData *modData = getDemoralizeSpecialPowerModuleData();
|
||||
|
||||
//
|
||||
// the demoralize special power increases in range and duration of effect the more
|
||||
// units we have captured within us
|
||||
//
|
||||
UnsignedInt duration = modData->m_baseDurationInFrames;
|
||||
Real range = modData->m_baseRange;
|
||||
ContainModuleInterface *contain = source->getContain();
|
||||
if( contain )
|
||||
{
|
||||
|
||||
// for every captured unit we get a bonus
|
||||
duration += contain->getContainCount() * modData->m_bonusDurationPerCapturedInFrames;
|
||||
|
||||
// cap at the max
|
||||
if( duration > modData->m_maxDurationInFrames )
|
||||
duration = modData->m_maxDurationInFrames;
|
||||
|
||||
// increase the range for every prisoner we got
|
||||
range += contain->getContainCount() * modData->m_bonusRangePerCaptured;
|
||||
|
||||
// cap at the max
|
||||
if( range > modData->m_maxRange )
|
||||
range = modData->m_maxRange;
|
||||
|
||||
} // end if
|
||||
|
||||
// scan the objects in the area range and demoralize them
|
||||
PartitionFilterRelationship filter1( source, PartitionFilterRelationship::ALLOW_ENEMIES |
|
||||
PartitionFilterRelationship::ALLOW_NEUTRAL );
|
||||
PartitionFilterAcceptByKindOf filter2( MAKE_KINDOF_MASK( KINDOF_INFANTRY ),
|
||||
KINDOFMASK_NONE );
|
||||
PartitionFilterSameMapStatus filterMapStatus(source);
|
||||
PartitionFilter *filters[] = { &filter1, &filter2, &filterMapStatus, NULL };
|
||||
ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( loc,
|
||||
range,
|
||||
FROM_CENTER_2D,
|
||||
filters );
|
||||
|
||||
MemoryPoolObjectHolder hold( iter );
|
||||
AIUpdateInterface *ai;
|
||||
for( Object *obj = iter->first(); obj; obj = iter->next() )
|
||||
{
|
||||
|
||||
// demoralize only affects with ai of course
|
||||
ai = obj->getAIUpdateInterface();
|
||||
if( ai )
|
||||
ai->setDemoralized( duration );
|
||||
|
||||
} // end for obj
|
||||
|
||||
// play an effect at the destination location
|
||||
if( modData->m_fxList )
|
||||
{
|
||||
|
||||
// execute FX
|
||||
FXList::doFXPos( modData->m_fxList, loc );
|
||||
|
||||
} // end if
|
||||
|
||||
} // end doSpecialPowerAtLocation
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DemoralizeSpecialPower::doSpecialPowerAtObject( const Object *obj, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
if( obj )
|
||||
doSpecialPowerAtLocation( obj->getPosition(), commandOptions );
|
||||
|
||||
} // end doSpecialPowerAtObject
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DemoralizeSpecialPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DemoralizeSpecialPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void DemoralizeSpecialPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
#endif // #ifdef ALLOW_DEMORALIZE
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: OCLSpecialPower.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, April 2002
|
||||
// Desc: Special powers that are driven by object creation lists
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/TerrainLogic.h"
|
||||
#include "GameLogic/Module/OCLSpecialPower.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// MODULE DATA ////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
CREATE_ABOVE_LOCATION_HEIGHT = 300
|
||||
};
|
||||
|
||||
static const char* TheOCLCreateLocTypeNames[] =
|
||||
{
|
||||
"CREATE_AT_EDGE_NEAR_SOURCE",
|
||||
"CREATE_AT_EDGE_NEAR_TARGET",
|
||||
"CREATE_AT_LOCATION",
|
||||
"USE_OWNER_OBJECT",
|
||||
"CREATE_ABOVE_LOCATION",
|
||||
"CREATE_AT_EDGE_FARTHEST_FROM_TARGET",
|
||||
NULL
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
OCLSpecialPowerModuleData::OCLSpecialPowerModuleData( void )
|
||||
{
|
||||
m_defaultOCL = NULL;
|
||||
m_upgradeOCL.clear();
|
||||
m_createLoc = CREATE_AT_EDGE_NEAR_SOURCE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseOCLUpgradePair( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
OCLSpecialPowerModuleData::Upgrades up;
|
||||
|
||||
INI::parseScience(ini, NULL, &up.m_science, NULL);
|
||||
INI::parseObjectCreationList(ini, NULL, &up.m_ocl, NULL);
|
||||
|
||||
std::vector<OCLSpecialPowerModuleData::Upgrades>* s = (std::vector<OCLSpecialPowerModuleData::Upgrades>*)store;
|
||||
s->push_back(up);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/* static */ void OCLSpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
SpecialPowerModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "UpgradeOCL", parseOCLUpgradePair, NULL, offsetof( OCLSpecialPowerModuleData, m_upgradeOCL ) },
|
||||
{ "OCL", INI::parseObjectCreationList, NULL, offsetof( OCLSpecialPowerModuleData, m_defaultOCL ) },
|
||||
{ "CreateLocation", INI::parseIndexList, TheOCLCreateLocTypeNames, offsetof( OCLSpecialPowerModuleData, m_createLoc ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// MODULE /////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
OCLSpecialPower::OCLSpecialPower( Thing *thing, const ModuleData *moduleData )
|
||||
: SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const ObjectCreationList* OCLSpecialPower::findOCL() const
|
||||
{
|
||||
const OCLSpecialPowerModuleData* d = getOCLSpecialPowerModuleData();
|
||||
const Player* controller = getObject()->getControllingPlayer();
|
||||
if (controller != NULL)
|
||||
{
|
||||
for (std::vector<OCLSpecialPowerModuleData::Upgrades>::const_iterator it = d->m_upgradeOCL.begin();
|
||||
it != d->m_upgradeOCL.end();
|
||||
++it)
|
||||
{
|
||||
if (controller->hasScience(it->m_science))
|
||||
return it->m_ocl;
|
||||
}
|
||||
}
|
||||
return d->m_defaultOCL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
OCLSpecialPower::~OCLSpecialPower( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Execute the power */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void OCLSpecialPower::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// sanity
|
||||
if( loc == NULL )
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtLocation( loc, commandOptions );
|
||||
|
||||
const ObjectCreationList* ocl = findOCL();
|
||||
|
||||
// get the module data
|
||||
const OCLSpecialPowerModuleData *modData = getOCLSpecialPowerModuleData();
|
||||
|
||||
// at what point will the "deliverer" come in
|
||||
Coord3D creationCoord;
|
||||
switch (modData->m_createLoc)
|
||||
{
|
||||
case CREATE_AT_EDGE_NEAR_SOURCE:
|
||||
creationCoord = TheTerrainLogic->findClosestEdgePoint( getObject()->getPosition() );
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, loc );
|
||||
break;
|
||||
case CREATE_AT_EDGE_NEAR_TARGET:
|
||||
creationCoord = TheTerrainLogic->findClosestEdgePoint(loc);
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, loc );
|
||||
break;
|
||||
case CREATE_AT_EDGE_FARTHEST_FROM_TARGET:
|
||||
creationCoord = TheTerrainLogic->findFarthestEdgePoint(loc);
|
||||
creationCoord.z += CREATE_ABOVE_LOCATION_HEIGHT;
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, loc );
|
||||
break;
|
||||
case CREATE_AT_LOCATION:
|
||||
// this is the case where the special power stuff originates at the location of the mouse click
|
||||
creationCoord = *loc;
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, loc );
|
||||
break;
|
||||
case USE_OWNER_OBJECT:
|
||||
creationCoord.set( loc );
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, loc, false );
|
||||
break;
|
||||
case CREATE_ABOVE_LOCATION:
|
||||
// this is the case where the special power stuff originates above the location of the mouse click
|
||||
creationCoord = *loc;
|
||||
creationCoord.z += CREATE_ABOVE_LOCATION_HEIGHT;
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, loc );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OCLSpecialPower::doSpecialPowerAtObject( Object *obj, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// convert to a location
|
||||
if( !obj )
|
||||
return;
|
||||
doSpecialPowerAtLocation( obj->getPosition(), commandOptions );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OCLSpecialPower::doSpecialPower( UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
Coord3D creationCoord;
|
||||
creationCoord.set( getObject()->getPosition() );
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtLocation( &creationCoord, commandOptions );
|
||||
|
||||
const ObjectCreationList* ocl = findOCL();
|
||||
ObjectCreationList::create( ocl, getObject(), &creationCoord, &creationCoord, false );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OCLSpecialPower::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OCLSpecialPower::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void OCLSpecialPower::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SpecialAbility.cpp /////////////////////////////////////////////////////////////
|
||||
// Author: Kris Morness, July 2002
|
||||
// Desc: This is the class that handles processing of any special attack from a unit. There are
|
||||
// many different styles and rules for various attacks.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/SpecialAbility.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SpecialAbility::SpecialAbility( Thing *thing, const ModuleData *moduleData )
|
||||
: SpecialPowerModule( thing, moduleData )
|
||||
{
|
||||
|
||||
} // end SpecialAbility
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SpecialAbility::~SpecialAbility( void )
|
||||
{
|
||||
|
||||
} // end ~SpecialAbility
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialAbility::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// sanity
|
||||
if( loc == NULL )
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtLocation( loc, commandOptions );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialAbility::doSpecialPowerAtObject( Object *obj, UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPowerAtObject( obj, commandOptions );
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialAbility::doSpecialPower( UnsignedInt commandOptions )
|
||||
{
|
||||
if (getObject()->isDisabled())
|
||||
return;
|
||||
|
||||
// call the base class action cause we are *EXTENDING* functionality
|
||||
SpecialPowerModule::doSpecialPower( commandOptions );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialAbility::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialAbility::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::xfer( xfer );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialAbility::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
SpecialPowerModule::loadPostProcess();
|
||||
|
||||
} // end loadPostProcess
|
||||
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SpecialPowerModule.cpp ///////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, April 2002
|
||||
// Desc: Special power module interface
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Science.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/Module/DeletionUpdate.h"
|
||||
#include "GameLogic/Module/UpdateModule.h"
|
||||
#include "GameLogic/Module/SpecialPowerModule.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
|
||||
#include "GameClient/Eva.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameClient/ControlBar.h"
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerModuleData::SpecialPowerModuleData()
|
||||
{
|
||||
|
||||
m_specialPowerTemplate = NULL;
|
||||
m_updateModuleStartsAttack = false;
|
||||
m_startsPaused = FALSE;
|
||||
|
||||
} // end SpecialPowerModuleData
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/* static */ void SpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
|
||||
{
|
||||
BehaviorModuleData::buildFieldParse( p );
|
||||
|
||||
static const FieldParse dataFieldParse[] =
|
||||
{
|
||||
{ "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( SpecialPowerModuleData, m_specialPowerTemplate ) },
|
||||
{ "UpdateModuleStartsAttack", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_updateModuleStartsAttack ) },
|
||||
{ "StartsPaused", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_startsPaused ) },
|
||||
{ "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerModuleData, m_initiateSound ) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
p.add(dataFieldParse);
|
||||
|
||||
} // end buildFieldParse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleData )
|
||||
: BehaviorModule( thing, moduleData )
|
||||
{
|
||||
|
||||
m_availableOnFrame = 0;
|
||||
m_pausedCount = 0;
|
||||
m_pausedOnFrame = 0;
|
||||
m_pausedPercent = 0.0f;
|
||||
|
||||
// we won't be able to use the power for X number of frames now
|
||||
|
||||
// if we're pre-built, start counting down
|
||||
if( BitTest( getObject()->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE )
|
||||
{
|
||||
//A sharedNSync special only startPowerRecharges when first scienced or when executed,
|
||||
//Since a new modue with same SPTemplates may construct at any time.
|
||||
if ( getSpecialPowerTemplate()->isSharedNSync() == FALSE )
|
||||
startPowerRecharge();
|
||||
}
|
||||
// WE USED TO DO THE POLL-EVERYBODY-AND-VOTE-ON-WHO-TO-SYNC-TO THING HERE,
|
||||
// BUT NO MORE, NOW IT IS HANDLED IN PLAYER
|
||||
|
||||
// Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then
|
||||
const SpecialPowerModuleData *md = (const SpecialPowerModuleData *)moduleData;
|
||||
if( md->m_startsPaused )
|
||||
pauseCountdown( TRUE );
|
||||
|
||||
resolveSpecialPower();
|
||||
|
||||
// Now, if we find that we have just come into being,
|
||||
// but there is already a science granted for our shared superweapon,
|
||||
// lets make sure TheIngameUI knows about our public timer
|
||||
// add this weapon to the UI if it has a public timer for all to see
|
||||
if( m_pausedCount == 0 &&
|
||||
getSpecialPowerTemplate()->isSharedNSync() == TRUE &&
|
||||
getSpecialPowerTemplate()->hasPublicTimer() == TRUE &&
|
||||
getObject()->getControllingPlayer() &&
|
||||
getObject()->isKindOf( KINDOF_STRUCTURE ) )
|
||||
{
|
||||
TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getPowerName(),
|
||||
getObject()->getID(),
|
||||
getSpecialPowerModuleData()->m_specialPowerTemplate );
|
||||
}
|
||||
|
||||
|
||||
} // end SpecialPowerModule
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const AudioEventRTS& SpecialPowerModule::getInitiateSound() const
|
||||
{
|
||||
return getSpecialPowerModuleData()->m_initiateSound;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerModule::~SpecialPowerModule()
|
||||
{
|
||||
|
||||
if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
|
||||
getObject()->getControllingPlayer() )
|
||||
TheInGameUI->removeSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getPowerName(),
|
||||
getObject()->getID(),
|
||||
getSpecialPowerModuleData()->m_specialPowerTemplate );
|
||||
|
||||
} // end ~SpecialPowerModule
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::resolveSpecialPower( void )
|
||||
{
|
||||
/*
|
||||
|
||||
// if we're pre-built, and not from a command center, and a building register us with the UI
|
||||
if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
|
||||
TheGameLogic->getFrame() == 0 && getObject()->getControllingPlayer() &&
|
||||
getObject()->isKindOf( KINDOF_COMMANDCENTER ) == FALSE &&
|
||||
getObject()->isKindOf( KINDOF_STRUCTURE ) )
|
||||
{
|
||||
//KM: The KINDOF_STRUCTURE check was made to prevent scripted bombers from registering their
|
||||
// special powers as public timers.
|
||||
TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getPowerName(),
|
||||
getObject()->getID(),
|
||||
getSpecialPowerModuleData()->m_specialPowerTemplate );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::onSpecialPowerCreation( void )
|
||||
{
|
||||
// THIS gets called by addScience(), that is, when the General has purchased a new special power,
|
||||
// and this module is thus activated.
|
||||
|
||||
// start a power recharge going
|
||||
startPowerRecharge();
|
||||
|
||||
// Dustin wants these special powers to start ready to fire,
|
||||
// so here (and only here) we will expressly set them to ready-now.
|
||||
if ( getSpecialPowerTemplate()->isSharedNSync())
|
||||
{
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
if ( player )
|
||||
{
|
||||
player->expressSpecialPowerReadyFrame( getSpecialPowerTemplate(), TheGameLogic->getFrame() );
|
||||
m_availableOnFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
|
||||
}
|
||||
}
|
||||
|
||||
// Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then
|
||||
const SpecialPowerModuleData *md = getSpecialPowerModuleData();
|
||||
if( md->m_startsPaused )
|
||||
pauseCountdown( TRUE );
|
||||
|
||||
// add this weapon to the UI if it has a public timer for all to see
|
||||
if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
|
||||
getObject()->getControllingPlayer() &&
|
||||
getObject()->isKindOf( KINDOF_STRUCTURE ) )
|
||||
{
|
||||
TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getPowerName(),
|
||||
getObject()->getID(),
|
||||
getSpecialPowerModuleData()->m_specialPowerTemplate );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ScienceType SpecialPowerModule::getRequiredScience( void ) const
|
||||
{
|
||||
|
||||
return getSpecialPowerModuleData()->m_specialPowerTemplate->getRequiredScience();
|
||||
} // end ~SpecialPowerModule
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const SpecialPowerTemplate * SpecialPowerModule::getSpecialPowerTemplate( void ) const
|
||||
{
|
||||
|
||||
return getSpecialPowerModuleData()->m_specialPowerTemplate;
|
||||
} // end ~SpecialPowerModule
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString SpecialPowerModule::getPowerName( void ) const
|
||||
{
|
||||
|
||||
return getSpecialPowerModuleData()->m_specialPowerTemplate->getName();
|
||||
} // end ~SpecialPowerModule
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Is this module designed for the power identier template passed in? */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SpecialPowerModule::isModuleForPower( const SpecialPowerTemplate *specialPowerTemplate ) const
|
||||
{
|
||||
|
||||
// get the module data
|
||||
const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
|
||||
|
||||
//
|
||||
// if the special power template defined in the module data matches the template we want
|
||||
// to check then we are for it!
|
||||
//
|
||||
return modData->m_specialPowerTemplate == specialPowerTemplate;
|
||||
|
||||
} // end canExecutePower
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Is this special power ready to use */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SpecialPowerModule::isReady() const
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// this is a cheat ... remove this for release!
|
||||
if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
const Object* obj = getObject();
|
||||
const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
|
||||
|
||||
if ( obj && modData )
|
||||
{
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
if ( player )
|
||||
{
|
||||
if ( modData->m_specialPowerTemplate->isSharedNSync())
|
||||
return (TheGameLogic->getFrame() >= player->getOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate ) );
|
||||
}
|
||||
}
|
||||
|
||||
return (m_pausedCount == 0) && (TheGameLogic->getFrame() >= m_availableOnFrame);
|
||||
|
||||
} // end isReady
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Get the percentage ready a special power is to use
|
||||
* 1.0f = ready now
|
||||
* 0.5f = 50% ready
|
||||
* 0.2f = 20% ready
|
||||
* etc ... */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real SpecialPowerModule::getPercentReady() const
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
|
||||
return 1.0f;
|
||||
#endif
|
||||
|
||||
// easy case ... is ready
|
||||
if( isReady() )
|
||||
return 1.0f;
|
||||
|
||||
if ( m_pausedCount > 0 )
|
||||
return m_pausedPercent;
|
||||
|
||||
// get the module data
|
||||
const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
|
||||
|
||||
// sanity
|
||||
if( modData->m_specialPowerTemplate == NULL )
|
||||
return 0.0f;
|
||||
|
||||
UnsignedInt readyFrame = m_availableOnFrame;
|
||||
|
||||
//unless
|
||||
const Object* obj = getObject();
|
||||
if ( obj )
|
||||
{
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
if ( player )
|
||||
{
|
||||
if ( modData->m_specialPowerTemplate->isSharedNSync())
|
||||
{
|
||||
readyFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate the percent
|
||||
Real percent = 1.0f - ((readyFrame - TheGameLogic->getFrame()) /
|
||||
(Real)modData->m_specialPowerTemplate->getReloadTime());
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** A special power has been used ... start the recharge process by computing the frame
|
||||
* we will become fully available on in the future again */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::startPowerRecharge()
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// this is a cheat ... remove this for release!
|
||||
if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
|
||||
return;
|
||||
#endif
|
||||
|
||||
const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
|
||||
|
||||
// sanity
|
||||
if( modData->m_specialPowerTemplate == NULL )
|
||||
{
|
||||
DEBUG_CRASH(("special power not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
Object* obj = getObject();
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
Player* player = getObject()->getControllingPlayer();
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
//Here, we make sure that general specials work as one between command centers
|
||||
// only factory type faction buildings should do this, and only with generals powers (in general)
|
||||
// but there are no restrictions on the use of SharedNSync on specialPowerTemplates at large
|
||||
if ( modData->m_specialPowerTemplate->isSharedNSync() )
|
||||
{
|
||||
player->resetOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate );
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the frame we will be 100% available on now
|
||||
m_availableOnFrame = TheGameLogic->getFrame() + getSpecialPowerTemplate()->getReloadTime();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, UnsignedInt commandOptions, Int locationCount )
|
||||
{
|
||||
Bool valid = false;
|
||||
// tell our update modules that we intend to do this special power.
|
||||
for( BehaviorModule** u = getObject()->getBehaviorModules(); *u; ++u )
|
||||
{
|
||||
SpecialPowerUpdateInterface* spu = (*u)->getSpecialPowerUpdateInterface();
|
||||
if( spu )
|
||||
{
|
||||
//Validate that we are calling the correct module!
|
||||
if( isModuleForPower( getSpecialPowerModuleData()->m_specialPowerTemplate ) )
|
||||
{
|
||||
spu->initiateIntentToDoSpecialPower( getSpecialPowerModuleData()->m_specialPowerTemplate, targetObj, targetPos, commandOptions, locationCount );
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we depend on our update module to trigger the special power, make sure we have the
|
||||
//appropriate update module!
|
||||
if( !valid && getSpecialPowerModuleData()->m_updateModuleStartsAttack )
|
||||
{
|
||||
DEBUG_CRASH( ("Object does not contain a special power module to execute. Did you forget to add it to the object INI?\n"));
|
||||
//DEBUG_CRASH(( "Object does not contain special power module (%s) to execute. Did you forget to add it to the object INI?\n",
|
||||
// command->m_specialPower->getName().str() ));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::triggerSpecialPower( const Coord3D *location )
|
||||
{
|
||||
aboutToDoSpecialPower( location ); // do BEFORE recharge
|
||||
|
||||
createViewObject(location);
|
||||
|
||||
// we won't be able to use the power for X number of frames now
|
||||
startPowerRecharge();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::createViewObject( const Coord3D *location )
|
||||
{
|
||||
const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
|
||||
const SpecialPowerTemplate *powerTemplate = modData->m_specialPowerTemplate;
|
||||
|
||||
if( modData == NULL || powerTemplate == NULL )
|
||||
return;
|
||||
|
||||
Real visionRange = powerTemplate->getViewObjectRange();
|
||||
UnsignedInt visionDuration = powerTemplate->getViewObjectDuration();
|
||||
|
||||
if( visionRange == 0 || visionDuration == 0 )
|
||||
return; // We don't want a view object at all.
|
||||
|
||||
AsciiString objectName = TheGlobalData->m_specialPowerViewObjectName;
|
||||
if( objectName.isEmpty() )
|
||||
return;
|
||||
|
||||
const ThingTemplate *viewObjectTemplate = TheThingFactory->findTemplate( objectName );
|
||||
if( viewObjectTemplate == NULL )
|
||||
return;
|
||||
|
||||
Object *viewObject = TheThingFactory->newObject( viewObjectTemplate, getObject()->getControllingPlayer()->getDefaultTeam() );
|
||||
|
||||
if( viewObject == NULL )
|
||||
return;
|
||||
|
||||
viewObject->setPosition( location );
|
||||
viewObject->setShroudClearingRange( visionRange );
|
||||
|
||||
static NameKeyType key_DeletionUpdate = NAMEKEY("DeletionUpdate");
|
||||
DeletionUpdate* dup = (DeletionUpdate*)viewObject->findUpdateModule(key_DeletionUpdate);
|
||||
if( dup )
|
||||
{
|
||||
dup->setLifetimeRange( visionDuration, visionDuration );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::markSpecialPowerTriggered( const Coord3D *location )
|
||||
{
|
||||
triggerSpecialPower(location);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::aboutToDoSpecialPower( const Coord3D *location )
|
||||
{
|
||||
// Tell the scripting engine!
|
||||
TheScriptEngine->notifyOfTriggeredSpecialPower(
|
||||
getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getSpecialPowerModuleData()->m_specialPowerTemplate->getName(),
|
||||
getObject()->getID());
|
||||
|
||||
// Let EVA do her thing
|
||||
SpecialPowerType type = getSpecialPowerModuleData()->m_specialPowerTemplate->getSpecialPowerType();
|
||||
|
||||
// Only play the EVA sounds if this is not the local player, and the local player doesn't consider the
|
||||
// person an enemy.
|
||||
// Kris: Actually, all players need to hear these warnings.
|
||||
//Player *localPlayer = ThePlayerList->getLocalPlayer();
|
||||
//if (localPlayer != getObject()->getControllingPlayer() && localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES) {
|
||||
if (type == SPECIAL_PARTICLE_UPLINK_CANNON)
|
||||
TheEva->setShouldPlay(EVA_SuperweaponLaunched_ParticleCannon);
|
||||
else if (type == SPECIAL_NEUTRON_MISSILE)
|
||||
TheEva->setShouldPlay(EVA_SuperweaponLaunched_Nuke);
|
||||
else if (type == SPECIAL_SCUD_STORM)
|
||||
TheEva->setShouldPlay(EVA_SuperweaponLaunched_ScudStorm);
|
||||
//}
|
||||
|
||||
// get module data
|
||||
const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
|
||||
|
||||
// play our initiate sound if we have one
|
||||
AudioEventRTS audioEvent = *modData->m_specialPowerTemplate->getInitiateSound();
|
||||
audioEvent.setObjectID(getObject()->getID());
|
||||
TheAudio->addAudioEvent( &audioEvent );
|
||||
|
||||
// play sound at target location if specified
|
||||
if( location )
|
||||
{
|
||||
|
||||
AudioEventRTS soundAtLocation = *modData->m_specialPowerTemplate->getInitiateAtTargetSound();
|
||||
soundAtLocation.setPosition( location );
|
||||
soundAtLocation.setPlayerIndex(getObject()->getControllingPlayer()->getPlayerIndex());
|
||||
TheAudio->addAudioEvent( &soundAtLocation );
|
||||
|
||||
} // end if
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//By default, special powers are not triggered by it's update module -- in which case
|
||||
//it triggers it and resets its timer immediately. When the update module triggers it,
|
||||
//then all we do is initiate the special power, and trust that the update module will
|
||||
//do the rest.
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::doSpecialPower( UnsignedInt commandOptions )
|
||||
{
|
||||
if (m_pausedCount > 0 || getObject()->isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//This tells the update module that we want to do our special power. The update modules
|
||||
//will then start processing each frame.
|
||||
initiateIntentToDoSpecialPower( NULL, NULL, commandOptions );
|
||||
|
||||
//Only trigger the special power immediately if the updatemodule doesn't start the attack.
|
||||
//An example of a case that wouldn't trigger immediately is for a unit that needs to
|
||||
//close to range before firing the special attack. A case that would trigger immediately
|
||||
//is the napalm strike. If we don't call this now, it's up to the update module to do so.
|
||||
if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
|
||||
{
|
||||
triggerSpecialPower( NULL );// Location-less trigger
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::doSpecialPowerAtObject( Object *obj, UnsignedInt commandOptions )
|
||||
{
|
||||
if (m_pausedCount > 0 || getObject()->isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//This tells the update module that we want to do our special power. The update modules
|
||||
//will then start processing each frame.
|
||||
initiateIntentToDoSpecialPower( obj, NULL, commandOptions );
|
||||
|
||||
//Only trigger the special power immediately if the updatemodule doesn't start the attack.
|
||||
//An example of a case that wouldn't trigger immediately is for a unit that needs to
|
||||
//close to range before firing the special attack. A case that would trigger immediately
|
||||
//is the napalm strike. If we don't call this now, it's up to the update module to do so.
|
||||
if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
|
||||
{
|
||||
triggerSpecialPower( obj->getPosition() );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
|
||||
{
|
||||
if (m_pausedCount > 0 || getObject()->isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//This tells the update module that we want to do our special power. The update modules
|
||||
//will then start processing each frame.
|
||||
initiateIntentToDoSpecialPower( NULL, loc, commandOptions );
|
||||
|
||||
//Only trigger the special power immediately if the updatemodule doesn't start the attack.
|
||||
//An example of a case that wouldn't trigger immediately is for a unit that needs to
|
||||
//close to range before firing the special attack. A case that would trigger immediately
|
||||
//is the napalm strike. If we don't call this now, it's up to the update module to do so.
|
||||
if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
|
||||
{
|
||||
triggerSpecialPower( loc );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::doSpecialPowerAtMultipleLocations( const Coord3D *locations, Int locCount, UnsignedInt commandOptions )
|
||||
{
|
||||
if (m_pausedCount > 0 || getObject()->isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//This tells the update module that we want to do our special power. The update modules
|
||||
//will then start processing each frame.
|
||||
initiateIntentToDoSpecialPower( NULL, locations, commandOptions, locCount );
|
||||
|
||||
//Only trigger the special power immediately if the updatemodule doesn't start the attack.
|
||||
//An example of a case that wouldn't trigger immediately is for a unit that needs to
|
||||
//close to range before firing the special attack. A case that would trigger immediately
|
||||
//is the napalm strike. If we don't call this now, it's up to the update module to do so.
|
||||
if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
|
||||
{
|
||||
triggerSpecialPower( NULL );// This type doesn't create view objects
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::pauseCountdown( Bool pause )
|
||||
{
|
||||
if (pause)// If pausing
|
||||
{
|
||||
if( m_pausedCount == 0 )
|
||||
{
|
||||
// Only record this with the first pausing, otherwise upon final unpausing you would get credited the time
|
||||
// between pauses as time served.
|
||||
m_pausedOnFrame = TheGameLogic->getFrame();
|
||||
m_pausedPercent = getPercentReady();
|
||||
}
|
||||
|
||||
++m_pausedCount;
|
||||
}
|
||||
else if( m_pausedCount > 0 )//Else if unpausing, but only if I am in fact paused, so multiple unpauses don't break.
|
||||
{
|
||||
--m_pausedCount;
|
||||
|
||||
// And only update the ready time if we are fully unpaused now.
|
||||
if( m_pausedCount == 0 )
|
||||
{
|
||||
m_availableOnFrame += (TheGameLogic->getFrame() - m_pausedOnFrame);
|
||||
}
|
||||
}
|
||||
} // end pauseCountdown
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UnsignedInt SpecialPowerModule::getReadyFrame( void ) const
|
||||
{
|
||||
if ( getSpecialPowerTemplate()->isSharedNSync() )
|
||||
{
|
||||
const Object* obj = getObject();
|
||||
if ( obj )
|
||||
{
|
||||
Player *player = getObject()->getControllingPlayer();
|
||||
if ( player )
|
||||
return player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pausedCount > 0 || getObject()->isDisabled())
|
||||
{
|
||||
Int pausedFrames = TheGameLogic->getFrame() - m_pausedOnFrame;
|
||||
return m_availableOnFrame + pausedFrames;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_availableOnFrame;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::crc( xfer );
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::xfer( xfer );
|
||||
|
||||
// available on frame
|
||||
xfer->xferUnsignedInt( &m_availableOnFrame );
|
||||
|
||||
// paused by script
|
||||
xfer->xferInt( &m_pausedCount );
|
||||
|
||||
// paused on frame
|
||||
xfer->xferUnsignedInt( &m_pausedOnFrame );
|
||||
|
||||
// paused percent
|
||||
xfer->xferReal( &m_pausedPercent );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerModule::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// extend base class
|
||||
BehaviorModule::loadPostProcess();
|
||||
|
||||
|
||||
|
||||
// Now, if we find that we have just come into being,
|
||||
// but there is already a science granted for our shared superweapon,
|
||||
// lets make sure TheIngameUI knows about our public timer
|
||||
// add this weapon to the UI if it has a public timer for all to see
|
||||
if( m_pausedCount == 0 &&
|
||||
getSpecialPowerTemplate()->isSharedNSync() == TRUE &&
|
||||
getSpecialPowerTemplate()->hasPublicTimer() == TRUE &&
|
||||
getObject()->getControllingPlayer() &&
|
||||
getObject()->isKindOf( KINDOF_STRUCTURE ) )
|
||||
{
|
||||
TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
|
||||
getPowerName(),
|
||||
getObject()->getID(),
|
||||
getSpecialPowerModuleData()->m_specialPowerTemplate );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // end loadPostProcess
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user