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
9 changes: 6 additions & 3 deletions GeneralsMD/Code/GameEngine/Include/Common/GameCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,13 @@ inline Real stdAngleDiff(Real a1, Real a2)
return normalizeAngle(a1 - a2);
}

// ------------------------------------------------------------------------
// normalized angle difference between a1 and a2, respecting negative values and wraparound
inline Real stdAngleDiffMod(Real a1, Real a2) {
return normalizeAngle(nmod(a1 - a2, 2 * PI));
}
extern Real stdAngleDiffMod(Real a1, Real a2);

// ------------------------------------------------------------------------
// normalize an angle between 0 and 2PI
extern Real normalizeAngle2PI(Real angle);


// ------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class DumbProjectileBehaviorModuleData : public UpdateModuleData
Real m_flightPathAdjustDistPerFrame;
Bool m_applyLauncherBonus;

Real m_dynamicHeightMinScale;
Real m_dynamicHeightMinRange;

DumbProjectileBehaviorModuleData();

static void buildFieldParse(MultiIniFieldParse& p);
Expand Down
3 changes: 3 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/GameLogic/TurretAI.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ class TurretAI : public MemoryPoolObject, public Snapshot, public NotifyWeaponFi
void stopRotOrPitchSound(); ///< stop turret rotation sound
void removeSelfAsTargeter();

Bool getTurretRotationDir(Real desiredAngle, Real minAngle, Real maxAngle); ///< Min/Max turn angle checks


#ifdef INTER_TURRET_DELAY
void getOtherTurretWeaponInfo(Int& numSelf, Int& numSelfReloading, Int& numSelfReady, Int& numOther, Int& numOtherReloading, Int& numOtherReady) const;
#endif
Expand Down
13 changes: 12 additions & 1 deletion GeneralsMD/Code/GameEngine/Source/Common/System/GameCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,16 @@ Real normalizeAngle(Real angle)
return angle;
}

// ----------------------------------------------------------
Real normalizeAngle2PI(Real angle)
{
angle = nmod(angle, TWO_PI);
if (angle < 0) angle += TWO_PI;
return angle;
}


//-------------------------------------------------------------------------------------------------
extern Real stdAngleDiffMod(Real a1, Real a2) {
return WWMath::Normalize_Angle(nmod(a1 - a2, TWO_PI));
//return normalizeAngle(nmod(a1 - a2, 2 * PI));
}
2 changes: 1 addition & 1 deletion GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ Bool Drawable::getCurrentWorldspaceClientBonePositions(const char* boneName, Mat
//-------------------------------------------------------------------------------------------------
void Drawable::setTerrainDecal(TerrainDecalType type)
{
DEBUG_LOG(("Drawable::setTerrainDecal - type = %d\n", type));
//DEBUG_LOG(("Drawable::setTerrainDecal - type = %d\n", type));
if (m_terrainDecalType == type)
return;

Expand Down
94 changes: 78 additions & 16 deletions GeneralsMD/Code/GameEngine/Source/GameClient/FXList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,41 @@ void FXNugget::doFXObj(const Object* primary, const Object* secondary) const
doFXPos(p, mtx, speed, s);
}

//-------------------------------------------------------------------------------------------------
static const char* const AllowedSurfaceNames[] =
{
"ALL",
"LAND",
"WATER",
NULL
};

enum AllowedSurfaceType CPP_11(: Int) {
SURFACE_ALL = 0,
SURFACE_LAND,
SURFACE_WATER
};

//-------------------------------------------------------------------------------------------------
class SoundFXNugget : public FXNugget
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(SoundFXNugget, "SoundFXNugget")

public:

SoundFXNugget()
{
m_soundName.clear();
m_maxAllowedHeight = INFINITY;
m_minAllowedHeight = -INFINITY;
m_allowedSurfaceType = SURFACE_ALL;
}

virtual void doFXPos(const Coord3D *primary, const Matrix3D* /*primaryMtx*/, const Real /*primarySpeed*/, const Coord3D * /*secondary*/, const Real /*overrideRadius*/ ) const
{
if (!isValidSurface(primary))
return;

AudioEventRTS sound(m_soundName);

if (primary)
Expand All @@ -149,6 +175,9 @@ class SoundFXNugget : public FXNugget

virtual void doFXObj(const Object* primary, const Object* secondary = NULL) const
{
if (!isValidSurface(primary->getPosition()))
return;

AudioEventRTS sound(m_soundName);
if (primary)
{
Expand All @@ -159,12 +188,15 @@ class SoundFXNugget : public FXNugget
TheAudio->addAudioEvent(&sound);
}


static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
{
static const FieldParse myFieldParse[] =
{
{ "Name", INI::parseAsciiString, NULL, offsetof( SoundFXNugget, m_soundName ) },
{ "MinAllowedHeight", INI::parseReal, NULL, offsetof(SoundFXNugget, m_minAllowedHeight) },
{ "MaxAllowedHeight", INI::parseReal, NULL, offsetof(SoundFXNugget, m_maxAllowedHeight) },
{ "AllowedSurface", INI::parseIndexList, AllowedSurfaceNames, offsetof(SoundFXNugget, m_allowedSurfaceType) },

{ 0, 0, 0, 0 }
};

Expand All @@ -174,7 +206,52 @@ class SoundFXNugget : public FXNugget
}

private:


bool isValidSurface(const Coord3D* primary) const //@TODO unify code with ParticleSystemFXNugget
{
if (!primary)
return false;

// Evaluate Height and allowed surfaces first.
if ((TheTerrainLogic != NULL) && (m_minAllowedHeight > -INFINITY || m_maxAllowedHeight < INFINITY || m_allowedSurfaceType != SURFACE_ALL )) {

Real groundHeight = 0;
PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(primary);

if (layer != LAYER_GROUND) { // Bridge
if (m_allowedSurfaceType == SURFACE_WATER) return false; // No water effects over bridges.
groundHeight = TheTerrainLogic->getLayerHeight(primary->x, primary->y, layer);
}
else { // Ground (Water or Land)
Real waterZ = 0;
Real terrainZ = 0;
if (m_allowedSurfaceType != SURFACE_ALL || TheGlobalData->m_heightAboveTerrainIncludesWater) { // Do water check
Bool isWater = TheTerrainLogic->isUnderwater(primary->x, primary->y, &waterZ, &terrainZ);

if (isWater) {
if (m_allowedSurfaceType == SURFACE_LAND) return false;
groundHeight = waterZ;
}
else {
if (m_allowedSurfaceType == SURFACE_WATER) return false;
groundHeight = terrainZ;
}
}
}

Real zOffset = primary->z - groundHeight;
if (zOffset < m_minAllowedHeight || zOffset > m_maxAllowedHeight) return false;
}

return true;
}

AsciiString m_soundName;

Real m_maxAllowedHeight;
Real m_minAllowedHeight;
AllowedSurfaceType m_allowedSurfaceType;
};
EMPTY_DTOR(SoundFXNugget)

Expand Down Expand Up @@ -566,21 +643,6 @@ class TerrainScorchFXNugget : public FXNugget
};
EMPTY_DTOR(TerrainScorchFXNugget)

//-------------------------------------------------------------------------------------------------
static const char* const AllowedSurfaceNames[] =
{
"ALL",
"LAND",
"WATER",
NULL
};

enum AllowedSurfaceType CPP_11(: Int) {
SURFACE_ALL = 0,
SURFACE_LAND,
SURFACE_WATER
};

//-------------------------------------------------------------------------------------------------
class ParticleSystemFXNugget : public FXNugget
{
Expand Down
122 changes: 112 additions & 10 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,88 @@ void TurretAI::loadPostProcess( void )
}
}

//----------------------------------------------------------------------------------------------------------
static bool IsInArc(Real a, Real min, Real max)
{
if (min <= max)
return a >= min && a <= max;
else
return a >= min || a <= max;
}
// --
static Real CCWDistance(Real from, Real to)
{
Real d = to - from;
if (d < 0) d += TWO_PI;
return d;
}
// --
static bool ccwLeavesAllowedArc(Real from, Real to, Real min, Real max)
{
if (min > max) { // simplify
max += TWO_PI;
}
if (from > to) {
to += TWO_PI;
}

Real disallowedCenter = normalizeAngle2PI(((max + min) / 2.0) + PI);
//DEBUG_LOG((">>> ccw check: from = %f, to = %f, min = %f, max = %f. disCenter = %f",
// from * 180 / PI, to * 180 / PI, min * 180 / PI, max * 180 / PI, disallowedCenter * 180 / PI));

return (from < disallowedCenter && to > disallowedCenter);
}
// -------
// return True if CCW, False if CW
Bool TurretAI::getTurretRotationDir(Real desiredAngle, Real minAngle, Real maxAngle)
{
Real origAngle = getTurretAngle();

origAngle = normalizeAngle2PI(origAngle);
desiredAngle = normalizeAngle2PI(desiredAngle);

Bool wantCCW = stdAngleDiffMod(desiredAngle, origAngle) > 0;

// If allowed arc < 180�, shortest path is always safe
Real diff = maxAngle - minAngle;
if (abs(diff) < PI)
//if (stdAngleDiffMod(maxAngle, minAngle) < PI)
return wantCCW;

minAngle = normalizeAngle2PI(minAngle);
maxAngle = normalizeAngle2PI(maxAngle);

//minAngle = WWMath::Normalize_Angle(minAngle);
//maxAngle = WWMath::Normalize_Angle(maxAngle);

// Check if preferred direction leaves allowed arc
//DEBUG_LOG((">>> curAngle = %f, targetAngle = %f, shortest dir = %d",
// origAngle*180/PI, desiredAngle*180/PI, wantCCW));

if (wantCCW)
{
if (ccwLeavesAllowedArc(origAngle, desiredAngle, minAngle, maxAngle)) {
//DEBUG_LOG((">>> >>> CCW check failed -> must turn CW"));
return false; // must go CW
}
}
else
{
// CW is reverse CCW
if (ccwLeavesAllowedArc(desiredAngle, origAngle, minAngle, maxAngle)) {
//DEBUG_LOG((">>> >>> CW check failed -> must turn CCW"));
return true; // must go CCW
}
}

return wantCCW;
}

//----------------------------------------------------------------------------------------------------------
Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Real relThresh)
{
desiredAngle = normalizeAngle(desiredAngle);
// desiredAngle = normalizeAngle(desiredAngle);
desiredAngle = WWMath::Normalize_Angle(desiredAngle);

// rotate turret back to zero angle
Real origAngle = getTurretAngle();
Expand All @@ -405,11 +483,11 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
// Real angleDiff = normalizeAngle(desiredAngle - actualAngle);
Real angleDiff = stdAngleDiffMod(desiredAngle, actualAngle);

Real minAngle = getMinTurretAngle();
Real maxAngle = getMaxTurretAngle();

// ---
if (hasLimitedTurretAngle()) {
Real minAngle = getMinTurretAngle();
Real maxAngle = getMaxTurretAngle();

if (maxAngle < minAngle) { // This might be a backwards facing configuration
maxAngle = nmod(maxAngle, 2.0 * PI);
desiredAngle = nmod(desiredAngle, 2.0 * PI);
Expand All @@ -432,6 +510,7 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
if (!isWithinLimit) {
// angleDiff = normalizeAngle(desiredAngle - actualAngle);
angleDiff = stdAngleDiffMod(desiredAngle, actualAngle);

// Are we close enough to the desired angle to just snap there?
if (fabs(angleDiff) < turnRate)
{
Expand All @@ -442,7 +521,13 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
}
else
{
if (angleDiff > 0)
bool rotate_ccw = false;
if (hasLimitedTurretAngle())
rotate_ccw = getTurretRotationDir(desiredAngle, minAngle, maxAngle);
else
rotate_ccw = (angleDiff > 0);

if (rotate_ccw)
actualAngle += turnRate;
else
actualAngle -= turnRate;
Expand All @@ -451,7 +536,8 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
m_playRotSound = true;
}

m_angle = normalizeAngle(actualAngle);
//m_angle = normalizeAngle(actualAngle);
m_angle = WWMath::Normalize_Angle(actualAngle);

if (m_angle != origAngle)
getOwner()->reactToTurretChange(m_whichTurret, origAngle, m_pitch);
Expand All @@ -460,7 +546,8 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
}
}
// -----
desiredAngle = normalizeAngle(desiredAngle);
//desiredAngle = normalizeAngle(desiredAngle);
//desiredAngle = WWMath::Normalize_Angle(desiredAngle);

// Are we close enough to the desired angle to just snap there?
if (fabs(angleDiff) < turnRate)
Expand All @@ -472,7 +559,14 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
}
else
{
if (angleDiff > 0)
// for limited angle check if we need to take the longer route to the target
bool rotate_ccw = false;
if (hasLimitedTurretAngle())
rotate_ccw = getTurretRotationDir(desiredAngle, minAngle, maxAngle);
else
rotate_ccw = (angleDiff > 0);

if (rotate_ccw)
actualAngle += turnRate;
else
actualAngle -= turnRate;
Expand All @@ -481,7 +575,8 @@ Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Rea
m_playRotSound = true;
}

m_angle = normalizeAngle(actualAngle);
//m_angle = normalizeAngle(actualAngle);
m_angle = WWMath::Normalize_Angle(actualAngle);

if( m_angle != origAngle )
getOwner()->reactToTurretChange( m_whichTurret, origAngle, m_pitch );
Expand Down Expand Up @@ -1220,7 +1315,14 @@ StateReturnType TurretAIAimTurretState::update()
Real dist = v.length();
if (range<1) range = 1; // paranoia. jba.
// As the unit gets closer, reduce the pitch so we don't shoot over him.
Real groundPitch = turret->getGroundUnitPitch() * (dist/range);

Real groundPitch;
if (dist > range) {
groundPitch = turret->getGroundUnitPitch();
}
else {
groundPitch = turret->getGroundUnitPitch() * (dist / range);
}
desiredPitch = actualPitch+groundPitch;
if (desiredPitch < turret->getMinPitch()) {
desiredPitch = turret->getMinPitch();
Expand Down
Loading