Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ static PoolSizeRec PoolSizes[] =
{ "DelayedUpgradeBehavior", 128, 64 },
{ "GenerateMinefieldBehavior", 32, 32 },
{ "HelicopterSlowDeathBehavior", 64, 32 },
{ "ShipSlowDeathBehavior", 64, 32 },
{ "ParkingPlaceBehavior", 32, 32 },
{ "FlightDeckBehavior", 8, 8 },
#ifdef ALLOW_SURRENDER
Expand Down
2 changes: 2 additions & 0 deletions GeneralsMD/Code/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ set(GAMEENGINE_SRC
Include/GameLogic/Module/HealCrateCollide.h
Include/GameLogic/Module/HeightDieUpdate.h
Include/GameLogic/Module/HelicopterSlowDeathUpdate.h
Include/GameLogic/Module/ShipSlowDeathBehavior.h
Include/GameLogic/Module/HelixContain.h
Include/GameLogic/Module/MultiAddOnContain.h
Include/GameLogic/Module/HighlanderBody.h
Expand Down Expand Up @@ -1064,6 +1065,7 @@ set(GAMEENGINE_SRC
Source/GameLogic/Object/Update/FloatUpdate.cpp
Source/GameLogic/Object/Update/HeightDieUpdate.cpp
Source/GameLogic/Object/Update/HelicopterSlowDeathUpdate.cpp
Source/GameLogic/Object/Update/ShipSlowDeathBehavior.cpp
Source/GameLogic/Object/Update/HijackerUpdate.cpp
Source/GameLogic/Object/Update/HordeUpdate.cpp
Source/GameLogic/Object/Update/LaserUpdate.cpp
Expand Down
4 changes: 3 additions & 1 deletion GeneralsMD/Code/GameEngine/Include/Common/Thing.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ class Thing : public MemoryPoolObject
Real getHeightAboveTerrain() const;
Real getHeightAboveTerrainOrWater() const;

Bool isOverWater() const;
Bool isOverWater() const; ///< Checks if the object is above or below a water surface
Bool isAboveWater() const; ///< Checks if the object is above (but not below) a water surface
Bool isBelowWater() const; ///< Checks if the object is below (but not above) a water surface

Bool isAboveTerrain() const { return getHeightAboveTerrain() > 0.0f; }
Bool isAboveTerrainOrWater() const { return getHeightAboveTerrainOrWater() > 0.0f; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@

enum ObjectID CPP_11(: Int);

class FXList;

enum PhysicsTurningType CPP_11(: Int)
{
TURN_NEGATIVE = -1,
Expand Down Expand Up @@ -65,6 +67,11 @@ class PhysicsBehaviorModuleData : public UpdateModuleData
Real m_pitchRollYawFactor;
Bool m_vehicleCrashAllowAirborne;
Real m_bounceFactor;

Bool m_doWaterPhysics; //< We only do water collission checks if this is set!
Real m_waterExtraFriction;
const FXList* m_waterImpactFX;


const WeaponTemplate* m_vehicleCrashesIntoBuildingWeaponTemplate;
const WeaponTemplate* m_vehicleCrashesIntoNonBuildingWeaponTemplate;
Expand Down Expand Up @@ -193,7 +200,10 @@ class PhysicsBehavior : public UpdateModule,
void setExtraFriction(Real b) { m_extraFriction = b; }

void setBounceSound(const AudioEventRTS* bounceSound);
void setWaterImpactSound(const AudioEventRTS* waterImpactSound);
void setWaterImpactFX(const FXList* waterImpactFX);
const AudioEventRTS* getBounceSound() { return m_bounceSound ? &m_bounceSound->m_event : TheAudio->getValidSilentAudioEvent(); }
const AudioEventRTS* getWaterImpactSound() { return m_waterImpactSound ? &m_waterImpactSound->m_event : TheAudio->getValidSilentAudioEvent(); }

/**
Reset all values (vel, accel, etc) to starting values.
Expand Down Expand Up @@ -238,6 +248,8 @@ class PhysicsBehavior : public UpdateModule,

void testStunnedUnitForDestruction(void);

Real getExtraFriction() const;

private:

enum PhysicsFlagsType
Expand All @@ -255,6 +267,7 @@ class PhysicsBehavior : public UpdateModule,
IS_IN_FREEFALL = 0x0200,
IS_IN_UPDATE = 0x0400,
IS_STUNNED = 0x0800,
WAS_ABOVE_WATER_LAST_FRAME = 0x1000,
};

/*
Expand All @@ -266,6 +279,7 @@ class PhysicsBehavior : public UpdateModule,
Real m_rollRate; ///< rate of rotation around forward vector
Real m_pitchRate; ///< rate or rotation around side vector
DynamicAudioEventRTS* m_bounceSound; ///< The sound for when this thing bounces, or NULL
DynamicAudioEventRTS* m_waterImpactSound; ///< The sound for when this thing hits the water surface, or NULL
Coord3D m_accel; ///< current acceleration
Coord3D m_prevAccel; ///< last frame's acceleration
Coord3D m_vel; ///< current velocity
Expand All @@ -284,6 +298,8 @@ class PhysicsBehavior : public UpdateModule,

Bool m_originalAllowBounce; ///< orignal state of allow bounce

const FXList* m_waterImpactFX;

void setFlag(PhysicsFlagsType f, Bool set) { if (set) m_flags |= f; else m_flags &= ~f; }
Bool getFlag(PhysicsFlagsType f) const { return (m_flags & f) != 0; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////

// FILE: ShipSlowDeathBehavior.h //////////////////////////////////////////////////////////////
// Author: Andi W, 01 2026
// Desc: ship slow deaths
///////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "Common/AudioEventRTS.h"
#include "GameLogic/Module/SlowDeathBehavior.h"

// FORWARD DECLARATIONS ///////////////////////////////////////////////////////////////////////////
class ParticleSystemTemplate;
enum ModelConditionFlagType CPP_11(: Int);

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
enum ShipToppleType CPP_11(: Int)
{
TOPPLE_NONE = 0,
TOPPLE_FRONT,
TOPPLE_BACK,
TOPPLE_SIDE_LEFT,
TOPPLE_SIDE_RIGHT,
TOPPLE_SIDE,
TOPPLE_RANDOM,

TOPPLE_COUNT
};

#ifdef DEFINE_SHIP_TOPPLE_NAMES
static const char* const TheShipToppleNames[] =
{
"NONE",
"FRONT",
"BACK",
"LEFT",
"RIGHT",
"SIDE",
"RANDOM",
NULL
};
static_assert(ARRAY_SIZE(TheShipToppleNames) == TOPPLE_COUNT + 1, "Incorrect array size");
#endif

//-------------------------------------------------------------------------------------------------
class ShipSlowDeathBehaviorModuleData : public SlowDeathBehaviorModuleData
{

public:

ShipSlowDeathBehaviorModuleData( void );

static void buildFieldParse(MultiIniFieldParse &p );

UnsignedInt m_initialDelay;
UnsignedInt m_initialDelayVariance;
ShipToppleType m_toppleType;

Real m_wobbleMaxPitch;
Real m_wobbleMaxYaw;
Real m_wobbleMaxRoll;
UnsignedInt m_wobbleInterval;

Real m_toppleFrontMinPitch;
Real m_toppleFrontMaxPitch;
Real m_toppleBackMinPitch;
Real m_toppleBackMaxPitch;
Real m_toppleSideMinRoll;
Real m_toppleSideMaxRoll;
Real m_toppleHeightMinOffset;
Real m_toppleHeightMaxOffset;
//Real m_toppleDamping;
Real m_toppleAngleCorrectionRate;
UnsignedInt m_toppleDuration;
UnsignedInt m_toppleDurationVariance;

Real m_toppleMinPushForce;
Real m_toppleMaxPushForce;
Real m_toppleMinPushForceSide;
Real m_toppleMaxPushForceSide;

//Real m_sinkWobbleMaxPitch;
//Real m_sinkWobbleMaxYaw;
//Real m_sinkWobbleMaxRoll;
//UnsignedInt m_sinkWobbleInterval;

Real m_sinkHowFast;

AudioEventRTS m_deathSound; ///< Sound played during death sequence.
const ObjectCreationList* m_oclEjectPilot; ///< OCL for ejecting pilot

const FXList* m_fxHitGround; ///< fx for hitting the ground
const ObjectCreationList* m_oclHitGround; ///< OCL at hit ground event

const FXList* m_fxStartTopple; ///< fx after init phase
const ObjectCreationList* m_oclStartTopple; ///< OCL after init phase

const FXList* m_fxStartSink; ///< fx after topple / before sinking phase
const ObjectCreationList* m_oclStartSink; ///< OCL after topple / before sinking phase

const ParticleSystemTemplate* m_attachParticleSystem; ///< particle system to attach while sinking

std::vector<AsciiString> m_attachParticleBoneNames;


ModelConditionFlagType m_conditionFlagInit;
ModelConditionFlagType m_conditionFlagTopple;
ModelConditionFlagType m_conditionFlagSink;
};

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
class ShipSlowDeathBehavior : public SlowDeathBehavior
{

MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ShipSlowDeathBehavior, "ShipSlowDeathBehavior" )
MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( ShipSlowDeathBehavior, ShipSlowDeathBehaviorModuleData )

public:

ShipSlowDeathBehavior( Thing *thing, const ModuleData* moduleData );
// virtual destructor prototype provided by memory pool declaration

virtual void beginSlowDeath( const DamageInfo *damageInfo ); ///< begin the slow death cycle
virtual UpdateSleepTime update();

protected:

void doInitPhase();
void doTopplePhase();
void doSinkPhase();

void doWobble();

void beginInitPhase();
void beginTopplePhase();
void beginSinkPhase();

// @todo propagate this up to SlowDeathBehavior. I don't wanna do it today, cause its 4/3. jkmcd
AudioEventRTS m_deathSound; ///< Sound played during death sequence.

UnsignedInt m_initStartFrame;
UnsignedInt m_toppleStartFrame;
UnsignedInt m_sinkStartFrame;
Bool m_shipSinkStarted;
Int m_chosenToppleType;

Real m_toppleVarianceRoll;

Real m_curPitch;
Real m_curYaw;
Real m_curRoll;
Real m_curZ;
};
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "GameLogic/Module/ChronoDeathBehavior.h"
#include "GameLogic/Module/SlowDeathBehavior.h"
#include "GameLogic/Module/HelicopterSlowDeathUpdate.h"
#include "GameLogic/Module/ShipSlowDeathBehavior.h"
#include "GameLogic/Module/NeutronMissileSlowDeathUpdate.h"
#include "GameLogic/Module/CaveContain.h"
#include "GameLogic/Module/OpenContain.h"
Expand Down Expand Up @@ -362,6 +363,7 @@ void ModuleFactory::init( void )
addModule( ChronoDeathBehavior );
addModule( SlowDeathBehavior );
addModule( HelicopterSlowDeathBehavior );
addModule( ShipSlowDeathBehavior );
addModule( NeutronMissileSlowDeathBehavior );
addModule( CaveContain );
addModule( OpenContain );
Expand Down
20 changes: 20 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/Thing/Thing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,26 @@ Bool Thing::isOverWater() const
return m_cachedIsOverWater;
}

// ------------------------------------------------------------------------------
Bool Thing::isBelowWater() const
{
if (!(m_cacheFlags & VALID_ALTITUDE_SEALEVEL))
{
getHeightAboveTerrainOrWater();
}
return m_cachedIsOverWater && m_cachedAltitudeAboveTerrainOrWater <= 0;
}

// ------------------------------------------------------------------------------
Bool Thing::isAboveWater() const
{
if (!(m_cacheFlags & VALID_ALTITUDE_SEALEVEL))
{
getHeightAboveTerrainOrWater();
}
return m_cachedIsOverWater && m_cachedAltitudeAboveTerrainOrWater > 0;
}


//=============================================================================
/** If we treat this as airborne, then they slide down slopes. This checks whether
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ class GenericObjectCreationNugget : public ObjectCreationNugget

GenericObjectCreationNugget() :
m_requiresLivePlayer(FALSE),
m_debrisTemplateName(AsciiString::TheEmptyString),
m_debrisToGenerate(1),
m_mass(0),
m_extraBounciness(0),
Expand Down Expand Up @@ -887,6 +888,7 @@ class GenericObjectCreationNugget : public ObjectCreationNugget
{
static const FieldParse myFieldParse[] =
{
{ "DebrisObject", INI::parseAsciiString, NULL, offsetof(GenericObjectCreationNugget, m_debrisTemplateName) },
{ "ModelNames", parseDebrisObjectNames, NULL, 0 },
{ "Mass", INI::parsePositiveNonZeroReal, NULL, offsetof( GenericObjectCreationNugget, m_mass ) },
{ "AnimationSet", parseAnimSet, NULL, offsetof( GenericObjectCreationNugget, m_animSets) },
Expand All @@ -895,6 +897,8 @@ class GenericObjectCreationNugget : public ObjectCreationNugget
{ "MinLODRequired", INI::parseStaticGameLODLevel, NULL, offsetof(GenericObjectCreationNugget, m_minLODRequired) },
{ "Shadow", INI::parseBitString32, TheShadowNames, offsetof( GenericObjectCreationNugget, m_shadowType ) },
{ "BounceSound", INI::parseAudioEventRTS, NULL, offsetof( GenericObjectCreationNugget, m_bounceSound) },
{ "WaterImpactSound", INI::parseAudioEventRTS, NULL, offsetof( GenericObjectCreationNugget, m_waterImpactSound) },
{ "WaterImpactFX", INI::parseFXList, NULL, offsetof( GenericObjectCreationNugget, m_waterImpactFX) },
{ 0, 0, 0, 0 }
};

Expand Down Expand Up @@ -1148,6 +1152,8 @@ class GenericObjectCreationNugget : public ObjectCreationNugget
objUp->setExtraFriction(m_extraFriction);
objUp->setAllowBouncing(true);
objUp->setBounceSound(&m_bounceSound);
objUp->setWaterImpactSound(&m_waterImpactSound);
objUp->setWaterImpactFX(m_waterImpactFX);
DUMPREAL(m_extraBounciness);
DUMPREAL(m_extraFriction);

Expand Down Expand Up @@ -1316,7 +1322,11 @@ class GenericObjectCreationNugget : public ObjectCreationNugget

Object* reallyCreate(const Coord3D *pos, const Matrix3D *mtx, Real orientation, const Object *sourceObj, UnsignedInt lifetimeFrames ) const
{
static const ThingTemplate* debrisTemplate = TheThingFactory->findTemplate("GenericDebris");
const ThingTemplate* debrisTemplate;
if (!m_debrisTemplateName.isEmpty())
debrisTemplate = TheThingFactory->findTemplate(m_debrisTemplateName);
else
debrisTemplate = TheThingFactory->findTemplate("GenericDebris");

if (m_names.empty())
return NULL;
Expand Down Expand Up @@ -1363,6 +1373,7 @@ class GenericObjectCreationNugget : public ObjectCreationNugget
continue;

tmpl = debrisTemplate;
DEBUG_LOG((">>> CREATE DEBRIS TEMPLATE: %s", debrisTemplate->getName().str()));
}
DEBUG_ASSERTCRASH(tmpl, ("Object %s not found",m_names[pick].str()));
if (!tmpl)
Expand Down Expand Up @@ -1458,6 +1469,7 @@ class GenericObjectCreationNugget : public ObjectCreationNugget
AsciiString m_animFinal;
};
std::vector<AsciiString> m_names;
AsciiString m_debrisTemplateName;
AsciiString m_putInContainer;
std::vector<AnimSet> m_animSets;
const FXList* m_fxFinal;
Expand Down Expand Up @@ -1488,6 +1500,8 @@ class GenericObjectCreationNugget : public ObjectCreationNugget
Real m_maxDistanceFormation;
Int m_objectCount; // how many objects will there be?
AudioEventRTS m_bounceSound;
AudioEventRTS m_waterImpactSound;
const FXList* m_waterImpactFX;
Bool m_requiresLivePlayer;
Bool m_experienceSink;
Bool m_inheritsWeaponBonus;
Expand Down
Loading