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
1 change: 1 addition & 0 deletions Core/GameEngine/Include/GameLogic/Damage.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ enum DamageType CPP_11(: Int)
DAMAGE_JET_TORPEDO,
DAMAGE_ANTI_SHIP,
DAMAGE_CRYO,
DAMAGE_BLACK_HOLE,

// Specific damage types with special logic attached
DAMAGE_TORPEDO, ///< can only attack units over water
Expand Down
1 change: 1 addition & 0 deletions Core/GameEngine/Source/GameLogic/System/Damage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const char* const DamageTypeFlags::s_bitNameList[] =
"JET_TORPEDO",
"ANTI_SHIP",
"CRYO",
"BLACK_HOLE",
// Specific damage types with special logic attached
"TORPEDO",
"ANTI_TORPEDO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3069,6 +3069,14 @@ U32 AILCALLBACK streamingFileRead(void *file_handle, void *buffer, U32 bytes)

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
// Holds the Miles global lock for a scope. It is recursive, so this is safe on the Miles callback
// thread, which already holds it.
struct MilesLockScope
{
MilesLockScope() { AIL_lock(); }
~MilesLockScope() { AIL_unlock(); }
};

//-------------------------------------------------------------------------------------------------
AudioFileCache::AudioFileCache() : m_maxSize(0), m_currentlyUsedSize(0), m_mutexName("AudioFileCacheMutex")
{
Expand Down Expand Up @@ -3100,6 +3108,12 @@ AudioFileCache::~AudioFileCache()
//-------------------------------------------------------------------------------------------------
void *AudioFileCache::openFile( AudioEventRTS *eventToOpenFrom )
{
// Take the Miles lock before our own mutex. Miles runs the end-of-sample callbacks while holding
// its lock, and those callbacks reach closeFile/openFile and wait for our mutex. Making room in
// the cache stops playing samples, which waits for the Miles lock, so taking the two in the
// opposite order deadlocks as soon as an eviction meets a sample finishing at the same time.
MilesLockScope milesLock;

// Protect the entire openFile function
ScopedMutex mut(m_mutex);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum SpecialPowerType CPP_11(: Int)
SPECIAL_PUNISHER_CANNON,
SPECIAL_CHEMICAL_MISSILE,
SPECIAL_CHRONOSPHERE,
SPECIAL_RIFT_GENERATOR,

AIRF_SPECIAL_HOLO_PLANES,
AIRF_SPECIAL_PARADROP_AMERICA,
Expand Down
10 changes: 10 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/GameClient/Eva.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,16 @@ enum EvaMessage CPP_11(: Int)
Eva_SuperweaponReady_Ally_Chronosphere,
Eva_SuperweaponReady_Enemy_Chronosphere,

Eva_SuperweaponDetected_Own_Rift_Generator,
Eva_SuperweaponDetected_Ally_Rift_Generator,
Eva_SuperweaponDetected_Enemy_Rift_Generator,
Eva_SuperweaponLaunched_Own_Rift_Generator,
Eva_SuperweaponLaunched_Ally_Rift_Generator,
Eva_SuperweaponLaunched_Enemy_Rift_Generator,
Eva_SuperweaponReady_Own_Rift_Generator,
Eva_SuperweaponReady_Ally_Rift_Generator,
Eva_SuperweaponReady_Enemy_Rift_Generator,

EVA_COUNT,
EVA_FIRST = 0,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ class RiftSlowDeathBehaviorModuleData : public SlowDeathBehaviorModuleData
Real m_spinRate; ///< Tumble budget, rads per frame
Real m_hugeVehiclePullScalar; ///< Suction multiplier for KINDOF_HUGE_VEHICLE

Real m_damagePerSecond; ///< Damage dealt per second of exposure at full strength
Real m_damagePerSecond; ///< Damage dealt per second of exposure at full strength, inside MaximumDamageRadius
Real m_maximumDamageRadius; ///< Victims within this 2D distance take the full DamagePerSecond
Real m_outerDamagePerSecond; ///< Damage per second at Radius, falling off linearly from MaximumDamageRadius;
///< negative means same as DamagePerSecond (no falloff)
UnsignedInt m_damageInterval; ///< Frames between damage ticks
DamageType m_damageType; ///< Damage type used for the continuous damage
DeathType m_deathType; ///< Death type of the continuous damage outside ConsumeDeathRadius
Expand Down Expand Up @@ -130,11 +133,14 @@ class RiftSlowDeathBehavior : public SlowDeathBehavior
void doRiftTick(Real strength, UnsignedInt currFrame); ///< one pass over everything in range
void doCollapse(void); ///< release the held victims and blow them outwards

Real getDamagePerSecondAt(Real dist2D) const; ///< damage falloff between MaximumDamageRadius and Radius
Bool isPullable(const Object* victim, Bool isDebris = FALSE) const; ///< can this object be dragged around at all
Bool isInvulnerable(const Object* victim) const; ///< the rift can never destroy it
Bool usesLocomotorCapture(const Object* victim) const; ///< flyer whose locomotor keeps running while disabled
RiftVictim* findVictim(ObjectID id); ///< NULL if we are not holding it
void captureVictim(Object* victim); ///< one-shot setup when a victim is first grabbed
void beginPhysicsCapture(RiftVictim& v, Object* victim); ///< hand the victim from its AI over to physics
void markVictimDying(RiftVictim& v, Object* obj); ///< keep pulling it until it is destroyed
void steerVictim(RiftVictim& victim, Object* obj, const Coord3D& toRift, Real dist, Real strength);
void releaseVictim(const RiftVictim& victim, Object* obj); ///< undo what capture cannot self-heal
void releaseAllVictims(void);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ const char* const SpecialPowerMaskType::s_bitNameList[] =
"SPECIAL_PUNISHER_CANNON",
"SPECIAL_CHEMICAL_MISSILE",
"SPECIAL_CHRONOSPHERE",
"SPECIAL_RIFT_GENERATOR",

"AIRF_SPECIAL_HOLO_PLANES",
"AIRF_SPECIAL_PARADROP_AMERICA",
Expand Down
19 changes: 19 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/GameClient/Eva.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ const char *const TheEvaMessageNames[] =
"SUPERWEAPONREADY_ALLY_CHRONOSPHERE",
"SUPERWEAPONREADY_ENEMY_CHRONOSPHERE",

"SUPERWEAPONDETECTED_OWN_RIFT_GENERATOR",
"SUPERWEAPONDETECTED_ALLY_RIFT_GENERATOR",
"SUPERWEAPONDETECTED_ENEMY_RIFT_GENERATOR",
"SUPERWEAPONLAUNCHED_OWN_RIFT_GENERATOR",
"SUPERWEAPONLAUNCHED_ALLY_RIFT_GENERATOR",
"SUPERWEAPONLAUNCHED_ENEMY_RIFT_GENERATOR",
"SUPERWEAPONREADY_OWN_RIFT_GENERATOR",
"SUPERWEAPONREADY_ALLY_RIFT_GENERATOR",
"SUPERWEAPONREADY_ENEMY_RIFT_GENERATOR",

//****************************************************************************
//Kris: Don't forget to add another handler below -- it's ghey-ly implemented.
//****************************************************************************
Expand Down Expand Up @@ -288,6 +298,15 @@ const ShouldPlayFunc Eva::s_shouldPlayFuncs[] =
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
Eva::shouldPlayGenericHandler,
};


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ RiftSlowDeathBehaviorModuleData::RiftSlowDeathBehaviorModuleData(void)
m_hugeVehiclePullScalar = 1.0f;

m_damagePerSecond = 0.0f;
m_maximumDamageRadius = 0.0f;
m_outerDamagePerSecond = -1.0f;
m_damageInterval = LOGICFRAMES_PER_SECOND;
m_damageType = DAMAGE_EXPLOSION;
m_deathType = DEATH_NORMAL;
Expand Down Expand Up @@ -100,6 +102,8 @@ RiftSlowDeathBehaviorModuleData::RiftSlowDeathBehaviorModuleData(void)
{ "HugeVehiclePullScalar",INI::parseReal, NULL, offsetof(RiftSlowDeathBehaviorModuleData, m_hugeVehiclePullScalar) },

{ "DamagePerSecond", INI::parseReal, NULL, offsetof(RiftSlowDeathBehaviorModuleData, m_damagePerSecond) },
{ "MaximumDamageRadius", INI::parseReal, NULL, offsetof(RiftSlowDeathBehaviorModuleData, m_maximumDamageRadius) },
{ "OuterDamagePerSecond", INI::parseReal, NULL, offsetof(RiftSlowDeathBehaviorModuleData, m_outerDamagePerSecond) },
{ "DamageInterval", INI::parseDurationUnsignedInt, NULL, offsetof(RiftSlowDeathBehaviorModuleData, m_damageInterval) },
{ "DamageType", INI::parseIndexList, DamageTypeFlags::s_bitNameList, offsetof(RiftSlowDeathBehaviorModuleData, m_damageType) },
{ "DeathType", INI::parseIndexList, TheDeathNames, offsetof(RiftSlowDeathBehaviorModuleData, m_deathType) },
Expand Down Expand Up @@ -291,11 +295,11 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)
Object* self = getObject();

Bool damageThisFrame = FALSE;
Real damageAmount = 0.0f;
if (d->m_damagePerSecond > 0.0f && currFrame >= m_nextDamageFrame)
Real damageTickScale = 0.0f; // turns a damage per second into this tick's damage
if ((d->m_damagePerSecond > 0.0f || d->m_outerDamagePerSecond > 0.0f) && currFrame >= m_nextDamageFrame)
{
UnsignedInt interval = d->m_damageInterval > 0 ? d->m_damageInterval : 1;
damageAmount = d->m_damagePerSecond * ((Real)interval / (Real)LOGICFRAMES_PER_SECOND) * strength;
damageTickScale = ((Real)interval / (Real)LOGICFRAMES_PER_SECOND) * strength;
m_nextDamageFrame = currFrame + interval;
damageThisFrame = TRUE;
}
Expand All @@ -304,7 +308,7 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)
damageInfo.in.m_damageType = d->m_damageType;
damageInfo.in.m_deathType = d->m_deathType;
damageInfo.in.m_sourceID = self->getID();
damageInfo.in.m_amount = damageAmount;
damageInfo.in.m_amount = 0.0f;
// no shockwave here: we move victims through physics directly, and attemptDamage refuses to
// shove anything airborne anyway, which is exactly what we have lifted
damageInfo.in.m_shockWaveAmount = 0.0f;
Expand Down Expand Up @@ -346,6 +350,10 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)

if (damageThisFrame)
{
// falloff is measured on the ground, like Radius itself
Real dist2D = sqrtf(toRift.x * toRift.x + toRift.y * toRift.y);
damageInfo.in.m_amount = getDamagePerSecondAt(dist2D) * damageTickScale;

// only things that can actually fall in may die the consume death
damageInfo.in.m_deathType = (pullable && dist <= d->m_consumeDeathRadius) ? d->m_consumeDeathType : d->m_deathType;
// the same DamageInfo is reused for every victim, so clear what the last one reported
Expand All @@ -354,6 +362,7 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)

if (d->m_OCLstructureDebris != NULL
&& victim->isKindOf(KINDOF_STRUCTURE)
&& !isInvulnerable(victim)
&& !damageInfo.out.m_noEffect
&& damageInfo.out.m_actualDamageDealt > 0.0f
&& GameLogicRandomValueReal(0.0f, 1.0f) < d->m_structureDebrisChance)
Expand Down Expand Up @@ -442,18 +451,7 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)

if ((v.m_savedFlags & RiftVictim::RIFTSAVE_DYING) != 0 || dist <= d->m_consumeDeathRadius)
{
if ((v.m_savedFlags & RiftVictim::RIFTSAVE_DYING) == 0)
{
v.m_savedFlags |= RiftVictim::RIFTSAVE_DYING;

// a dead flyer's AI no longer drives its locomotor, so take it over through physics
if ((v.m_savedFlags & RiftVictim::RIFTSAVE_USES_LOCOMOTOR) != 0)
{
v.m_savedFlags &= ~RiftVictim::RIFTSAVE_USES_LOCOMOTOR;
beginPhysicsCapture(v, obj);
}
}

markVictimDying(v, obj);
steerVictim(v, obj, toRift, dist, strength);
continue;
}
Expand All @@ -476,14 +474,17 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)
if (victim == NULL)
continue;

for (Int i = (Int)m_victims.size() - 1; i >= 0; --i)
// Keep holding it: its death (e.g. a shrinking ChronoDeathBehavior) plays out over time, and
// letting go here would drop it out of the singularity under gravity instead of being
// swallowed. The dead branch above keeps steering it until it is destroyed.
RiftVictim* held = findVictim(*it);
if (held == NULL)
{
if (m_victims[i].m_id == *it)
{
m_victims.erase(m_victims.begin() + i);
break;
}
captureVictim(victim);
held = findVictim(*it);
}
if (held != NULL)
markVictimDying(*held, victim);

FXList::doFXPos(d->m_FXconsume, victim->getPosition());
ObjectCreationList::create(d->m_OCLconsume, self, victim->getPosition(), NULL, 0.0f);
Expand All @@ -492,6 +493,30 @@ void RiftSlowDeathBehavior::doRiftTick(Real strength, UnsignedInt currFrame)

} // end doRiftTick

// ------------------------------------------------------------------------------------------------
/** Full DamagePerSecond out to MaximumDamageRadius, then linear down to OuterDamagePerSecond at
* Radius. */
// ------------------------------------------------------------------------------------------------
Real RiftSlowDeathBehavior::getDamagePerSecondAt(Real dist2D) const
{
const RiftSlowDeathBehaviorModuleData* d = getRiftSlowDeathBehaviorModuleData();

if (d->m_outerDamagePerSecond < 0.0f || dist2D <= d->m_maximumDamageRadius)
return d->m_damagePerSecond;

// guarded: MaximumDamageRadius >= Radius leaves no falloff band to divide by
Real band = d->m_radius - d->m_maximumDamageRadius;
if (band <= 0.0f)
return d->m_damagePerSecond;

Real t = (dist2D - d->m_maximumDamageRadius) / band;
if (t > 1.0f)
t = 1.0f;

return d->m_damagePerSecond + (d->m_outerDamagePerSecond - d->m_damagePerSecond) * t;

} // end getDamagePerSecondAt

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Bool RiftSlowDeathBehavior::isPullable(const Object* victim, Bool isDebris) const
Expand All @@ -508,22 +533,8 @@ Bool RiftSlowDeathBehavior::isPullable(const Object* victim, Bool isDebris) cons

// invulnerable things are left alone. Our own debris is exempt: it usually has an InactiveBody,
// which always reports itself as indestructible.
if (!isDebris)
{
const BodyModuleInterface* body = victim->getBodyModule();
if ((body != NULL && body->isIndestructible()) || victim->getIsUndetectedDefector())
return FALSE;

// these take damage but can never die, so the event horizon could never consume them
static const NameKeyType key_ImmortalBody = NAMEKEY("ImmortalBody");
static const NameKeyType key_HighlanderBody = NAMEKEY("HighlanderBody");
for (BehaviorModule** m = victim->getBehaviorModules(); *m; ++m)
{
NameKeyType key = (*m)->getModuleNameKey();
if (key == key_ImmortalBody || key == key_HighlanderBody)
return FALSE;
}
}
if (!isDebris && isInvulnerable(victim))
return FALSE;

if (victim->isAnyKindOf(d->m_noPullKindOf))
return FALSE;
Expand All @@ -535,6 +546,30 @@ Bool RiftSlowDeathBehavior::isPullable(const Object* victim, Bool isDebris) cons

} // end isPullable

// ------------------------------------------------------------------------------------------------
/** Things the rift can never destroy. Note that ImmortalBody and HighlanderBody still report the
* damage they take, e.g. the ImmortalBody of GenericBridge and its towers. */
// ------------------------------------------------------------------------------------------------
Bool RiftSlowDeathBehavior::isInvulnerable(const Object* victim) const
{
const BodyModuleInterface* body = victim->getBodyModule();
if ((body != NULL && body->isIndestructible()) || victim->getIsUndetectedDefector())
return TRUE;

// these take damage but can never die
static const NameKeyType key_ImmortalBody = NAMEKEY("ImmortalBody");
static const NameKeyType key_HighlanderBody = NAMEKEY("HighlanderBody");
for (BehaviorModule** m = victim->getBehaviorModules(); *m; ++m)
{
NameKeyType key = (*m)->getModuleNameKey();
if (key == key_ImmortalBody || key == key_HighlanderBody)
return TRUE;
}

return FALSE;

} // end isInvulnerable

// ------------------------------------------------------------------------------------------------
/** Flyers whose locomotor template is flagged LocomotorWorksWhenDisabled keep steering themselves
* even while disabled, and their handleBehaviorZ rewrites the z position every frame. Fighting
Expand Down Expand Up @@ -639,6 +674,25 @@ void RiftSlowDeathBehavior::beginPhysicsCapture(RiftVictim& v, Object* victim)

} // end beginPhysicsCapture

// ------------------------------------------------------------------------------------------------
/** From now on the victim is pulled until it is destroyed, dead or not. */
// ------------------------------------------------------------------------------------------------
void RiftSlowDeathBehavior::markVictimDying(RiftVictim& v, Object* obj)
{
if ((v.m_savedFlags & RiftVictim::RIFTSAVE_DYING) != 0)
return;

v.m_savedFlags |= RiftVictim::RIFTSAVE_DYING;

// a dead flyer's AI no longer drives its locomotor, so take it over through physics
if ((v.m_savedFlags & RiftVictim::RIFTSAVE_USES_LOCOMOTOR) != 0)
{
v.m_savedFlags &= ~RiftVictim::RIFTSAVE_USES_LOCOMOTOR;
beginPhysicsCapture(v, obj);
}

} // end markVictimDying

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void RiftSlowDeathBehavior::steerVictim(RiftVictim& victim, Object* obj, const Coord3D& toRift,
Expand Down
Loading