Skip to content

Commit

Permalink
Fix flamers shooting into outer space due to unsigned underflow and f…
Browse files Browse the repository at this point in the history
…ix penetrating projectiles going 100 times too far.

Also, renamed the confusingly named partially-unsigned vectors {startX, startY, srcHeight} and {tarX, tarY, altChange - srcHeight} to signed vectors src and dst, respectively.
  • Loading branch information
Cyp committed Oct 23, 2010
1 parent 5c431a3 commit 1481ec6
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 55 deletions.
85 changes: 34 additions & 51 deletions src/projectile.c
Expand Up @@ -379,10 +379,8 @@ int32_t projCalcIndirectVelocities(const int32_t dx, const int32_t dz, int32_t v
BOOL proj_SendProjectile(WEAPON *psWeap, BASE_OBJECT *psAttacker, int player, Vector3i target, BASE_OBJECT *psTarget, BOOL bVisible, int weapon_slot)
{
PROJECTILE *psProj = malloc(sizeof(PROJECTILE));
SDWORD tarHeight, srcHeight;
int32_t altChange, dx, dy, dz;
int32_t dx, dy, dz;
uint32_t dxy;
Vector3i muzzle;
WEAPON_STATS *psStats = &asWeaponStats[psWeap->nStat];

ASSERT_OR_RETURN( false, psWeap->nStat < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", psWeap->nStat, numWeaponStats);
Expand All @@ -394,33 +392,30 @@ BOOL proj_SendProjectile(WEAPON *psWeap, BASE_OBJECT *psAttacker, int player, Ve
{
// if there isn't an attacker just start at the target position
// NB this is for the script function to fire the las sats
muzzle = target;
psProj->src = target;
}
else if (psAttacker->type == OBJ_DROID && weapon_slot >= 0)
{
calcDroidMuzzleLocation( (DROID *) psAttacker, &muzzle, weapon_slot);
calcDroidMuzzleLocation((DROID *)psAttacker, &psProj->src, weapon_slot);
/*update attack runs for VTOL droid's each time a shot is fired*/
updateVtolAttackRun((DROID *)psAttacker, weapon_slot);
}
else if (psAttacker->type == OBJ_STRUCTURE && weapon_slot >= 0)
{
calcStructureMuzzleLocation( (STRUCTURE *) psAttacker, &muzzle, weapon_slot);
calcStructureMuzzleLocation((STRUCTURE *)psAttacker, &psProj->src, weapon_slot);
}
else // incase anything wants a projectile
{
muzzle = psAttacker->pos;
psProj->src = psAttacker->pos;
}

/* Initialise the structure */
psProj->id = ProjectileTrackerID |(gameTime2 >>4); // make unique id
psProj->type = OBJ_PROJECTILE;
psProj->psWStats = psStats;

psProj->pos = muzzle;
psProj->startX = muzzle.x;
psProj->startY = muzzle.y;
psProj->tarX = target.x;
psProj->tarY = target.y;
psProj->pos = psProj->src;
psProj->dst = target;

psProj->player = player;
psProj->bVisible = false;
Expand Down Expand Up @@ -468,23 +463,16 @@ BOOL proj_SendProjectile(WEAPON *psWeap, BASE_OBJECT *psAttacker, int player, Ve
{
scoreUpdateVar(WD_SHOTS_ON_TARGET);

tarHeight = psTarget->pos.z + gameRand(establishTargetHeight(psTarget));
psProj->dst.z = psTarget->pos.z + gameRand(establishTargetHeight(psTarget));
}
else
{
tarHeight = target.z;
scoreUpdateVar(WD_SHOTS_OFF_TARGET);
}

srcHeight = muzzle.z;
altChange = tarHeight - srcHeight;

psProj->srcHeight = srcHeight;
psProj->altChange = altChange;

dx = ((SDWORD)psProj->tarX) - muzzle.x;
dy = ((SDWORD)psProj->tarY) - muzzle.y;
dz = tarHeight - muzzle.z;
dx = psProj->dst.x - psProj->src.x;
dy = psProj->dst.y - psProj->src.y;
dz = psProj->dst.z - psProj->src.z;

/* roll never set */
psProj->rot.roll = 0;
Expand Down Expand Up @@ -685,7 +673,7 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
/* we want a delay between Las-Sats firing and actually hitting in multiPlayer
magic number but that's how long the audio countdown message lasts! */
if (bMultiPlayer && psStats->weaponSubClass == WSC_LAS_SAT &&
timeSoFar < LAS_SAT_DELAY * GAME_TICKS_PER_SEC)
(unsigned)timeSoFar < LAS_SAT_DELAY * GAME_TICKS_PER_SEC)
{
return;
}
Expand Down Expand Up @@ -733,22 +721,22 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
if (psStats->movementModel == MM_HOMINGDIRECT && psProj->psDest)
{
/* If it's homing and it has a target (not a miss)... */
move.x = psProj->psDest->pos.x - psProj->startX;
move.y = psProj->psDest->pos.y - psProj->startY;
move.z = psProj->psDest->pos.z + establishTargetHeight(psProj->psDest)/2 - psProj->srcHeight;
move.x = psProj->psDest->pos.x - psProj->src.x;
move.y = psProj->psDest->pos.y - psProj->src.y;
move.z = psProj->psDest->pos.z + establishTargetHeight(psProj->psDest)/2 - psProj->src.z;
}
else
{
move.x = psProj->tarX - psProj->startX;
move.y = psProj->tarY - psProj->startY;
move.x = psProj->dst.x - psProj->src.x;
move.y = psProj->dst.y - psProj->src.y;
// LASSAT doesn't have a z
if(psStats->weaponSubClass == WSC_LAS_SAT)
{
move.z = 0;
}
else if (!bIndirect)
{
move.z = psProj->altChange;
move.z = psProj->dst.z - psProj->src.z;
}
else
{
Expand All @@ -775,23 +763,23 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
distancePercent = PERCENT(currentDistance, targetDistance);

/* Calculate next position */
nextPos.x = psProj->startX + move.x * currentDistance/targetDistance;
nextPos.y = psProj->startY + move.y * currentDistance/targetDistance;
nextPos.x = psProj->src.x + move.x * currentDistance/targetDistance;
nextPos.y = psProj->src.y + move.y * currentDistance/targetDistance;
if (!bIndirect)
{
nextPos.z = psProj->srcHeight + move.z * currentDistance/targetDistance;
nextPos.z = psProj->src.z + move.z * currentDistance/targetDistance;
}
else
{
nextPos.z = psProj->srcHeight + move.z;
nextPos.z = psProj->src.z + move.z;
}

/* impact if about to go off map else update coordinates */
if (!worldOnMap(nextPos.x, nextPos.y))
{
psProj->state = PROJ_IMPACT;
psProj->tarX = psProj->pos.x;
psProj->tarY = psProj->pos.y;
psProj->dst.x = psProj->pos.x;
psProj->dst.y = psProj->pos.y;
setProjectileDestination(psProj, NULL);
debug(LOG_NEVER, "**** projectile(%i) off map - removed ****\n", psProj->id);
return;
Expand Down Expand Up @@ -906,21 +894,16 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
{
WEAPON asWeap;
// Determine position to fire a missile at
// (must be at least 0 because we don't use signed integers
// this shouldn't be larger than the height and width of the map either)
Vector3i newDest = {
psProj->startX + move.x * distanceExtensionFactor,
psProj->startY + move.y * distanceExtensionFactor,
psProj->srcHeight + move.z * distanceExtensionFactor
psProj->src.x + move.x * distanceExtensionFactor/100,
psProj->src.y + move.y * distanceExtensionFactor/100,
psProj->src.z + move.z * distanceExtensionFactor/100
};
memset(&asWeap, 0, sizeof(asWeap));
asWeap.nStat = psStats - asWeaponStats;

ASSERT(distanceExtensionFactor != 0, "Unitialized variable used! distanceExtensionFactor is not initialized.");

newDest.x = clip(newDest.x, 0, world_coord(mapWidth - 1));
newDest.y = clip(newDest.y, 0, world_coord(mapHeight - 1));

// Assume we damaged the chosen target
setProjectileDamaged(psProj, closestCollisionObject);

Expand Down Expand Up @@ -1230,7 +1213,7 @@ static void proj_ImpactFunc( PROJECTILE *psObj )
if ((((psStats->surfaceToAir & SHOOT_IN_AIR) && bTargetInAir) || ((psStats->surfaceToAir & SHOOT_ON_GROUND) && !bTargetInAir))
&& Vector3i_InSphere(psCurrD->pos, psObj->pos, psStats->radius))
{
int dice = gameRand(100);
unsigned dice = gameRand(100);
if (dice < weaponRadiusHit(psStats, psObj->player))
{
unsigned int damage = calcDamage(
Expand Down Expand Up @@ -1278,7 +1261,7 @@ static void proj_ImpactFunc( PROJECTILE *psObj )
// Check whether it is in hit radius
if (Vector3i_InCircle(psCurrS->pos, psObj->pos, psStats->radius))
{
int dice = gameRand(100);
unsigned dice = gameRand(100);
if (dice < weaponRadiusHit(psStats, psObj->player))
{
unsigned int damage = calcDamage(weaponRadDamage(psStats, psObj->player), psStats->weaponEffect, (BASE_OBJECT *)psCurrS);
Expand Down Expand Up @@ -1324,7 +1307,7 @@ static void proj_ImpactFunc( PROJECTILE *psObj )
// Check whether it is in hit radius
if (Vector3i_InCircle(psCurrF->pos, psObj->pos, psStats->radius))
{
int dice = gameRand(100);
unsigned dice = gameRand(100);
if (dice < weaponRadiusHit(psStats, psObj->player))
{
debug(LOG_NEVER, "Damage to object %d, player %d\n",
Expand Down Expand Up @@ -1534,7 +1517,7 @@ static void proj_checkBurnDamage( BASE_OBJECT *apsList, PROJECTILE *psProj)
/* Within the bounding box, now check the radius */
xDiff = psCurr->pos.x - psProj->pos.x;
yDiff = psCurr->pos.y - psProj->pos.y;
if (xDiff*xDiff + yDiff*yDiff <= radSquared)
if ((uint32_t)(xDiff*xDiff + yDiff*yDiff) <= radSquared)
{
/* The object is in the fire */
psCurr->inFire |= IN_FIRE;
Expand Down Expand Up @@ -1760,7 +1743,7 @@ static HIT_SIDE getHitSide(PROJECTILE *psObj, BASE_OBJECT *psTarget)
int impactAngle;

// If we hit the top of the droid
if (psObj->altChange > 300)
if (psObj->dst.z - psObj->src.z > 300)
{
return HIT_SIDE_TOP;
}
Expand All @@ -1772,8 +1755,8 @@ static HIT_SIDE getHitSide(PROJECTILE *psObj, BASE_OBJECT *psTarget)
// We hit an actual `side'
else
{
deltaX = psObj->startX - psTarget->pos.x;
deltaY = psObj->startY - psTarget->pos.y;
deltaX = psObj->src.x - psTarget->pos.x;
deltaY = psObj->src.y - psTarget->pos.y;

/*
* Work out the impact angle. It is easiest to understand if you
Expand Down
6 changes: 2 additions & 4 deletions src/projectiledef.h
Expand Up @@ -52,11 +52,9 @@ typedef struct PROJECTILE
BASE_OBJECT ** psDamaged; ///< the targets that have already been dealt damage to (don't damage the same target twice)
unsigned psNumDamaged;

UDWORD startX, startY; ///< Where projectile started
UDWORD tarX, tarY; ///< The target coordinates
Vector3i src; ///< Where projectile started
Vector3i dst; ///< The target coordinates
SDWORD vXY, vZ; ///< axis velocities
UDWORD srcHeight; ///< Height of origin
SDWORD altChange; ///< Change in altitude
SPACETIME prevSpacetime; ///< Location of projectile in previous tick.
UDWORD expectedDamageCaused; ///< Expected damage that this projectile will cause to the target.
} PROJECTILE;
Expand Down

0 comments on commit 1481ec6

Please sign in to comment.