diff --git a/src/combat.c b/src/combat.c index ec45eff81..deeb209b0 100644 --- a/src/combat.c +++ b/src/combat.c @@ -176,11 +176,18 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in return; } + if (!psTarget->visible[psAttacker->player]) + { + // Can't see it - can't hit it + objTrace(psAttacker->id, "combFire(%u[%s]->%u): Object has no indirect sight of target", psAttacker->id, psStats->pName, psTarget->id); + return; + } + /* Check we can see the target */ if (psAttacker->type == OBJ_DROID && !isVtolDroid((DROID *)psAttacker) && (proj_Direct(psStats) || actionInsideMinRange(psDroid, psTarget, psStats))) { - if(!visibleObject(psAttacker, psTarget, true)) + if(!lineOfFire(psAttacker, psTarget, true)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): Droid has no direct line of sight to target", @@ -193,7 +200,7 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in proj_Direct(psStats)) { // a bunker can't shoot through walls - if (!visibleObject(psAttacker, psTarget, true)) + if (!lineOfFire(psAttacker, psTarget, true)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): Structure has no direct line of sight to target", @@ -204,7 +211,7 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in else if ( proj_Direct(psStats) ) { // VTOL or tall building - if (!visibleObject(psAttacker, psTarget, false)) + if (!lineOfFire(psAttacker, psTarget, false)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): Tall object has no direct line of sight to target", @@ -212,17 +219,6 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in return; } } - else - { - // Indirect fire - if (!psTarget->visible[psAttacker->player]) - { - // Can't get an indirect LOS - can't hit it with the weapon - objTrace(psAttacker->id, "combFire(%u[%s]->%u): Object has no indirect sight of target", - psAttacker->id, psStats->pName, psTarget->id); - return; - } - } // if the turret doesn't turn, check if the attacker is in alignment with the target if (psAttacker->type == OBJ_DROID && !psStats->rotate) diff --git a/src/visibility.c b/src/visibility.c index 8edd26a2e..510171a50 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -755,3 +755,53 @@ bool scrTileIsVisible(SDWORD player, SDWORD x, SDWORD y) return scrTileVisible[player][x][y]; } + +/* Check whether psViewer can fire directly at psTarget. + * psTarget can be any type of BASE_OBJECT (e.g. a tree). + */ +bool lineOfFire(const BASE_OBJECT* psViewer, const BASE_OBJECT* psTarget, bool wallsBlock) +{ + Vector3i pos, dest, diff; + int range, distSq; + + ASSERT(psViewer != NULL, "Invalid shooter pointer!"); + ASSERT(psTarget != NULL, "Invalid target pointer!"); + if (!psViewer || !psTarget) + { + return false; + } + + // FIXME HACK Needed since we got those ugly Vector3uw floating around in BASE_OBJECT... + pos = Vector3uw_To3i(psViewer->pos); + dest = Vector3uw_To3i(psTarget->pos); + diff = Vector3i_Sub(dest, pos); + range = objSensorRange(psViewer); + + distSq = Vector3i_ScalarP(diff, diff); + if (distSq == 0) + { + // Should never be on top of each other, but ... + return true; + } + + // initialise the callback variables + { + VisibleObjectHelp_t help = { true, wallsBlock, distSq, pos.z + visObjHeight(psViewer), { map_coord(dest.x), map_coord(dest.y) }, 0, 0, -UBYTE_MAX * GRAD_MUL * ELEVATION_SCALE, 0, { 0, 0 } }; + int targetGrad, top; + + // Cast a ray from the viewer to the target + rayCast(pos, diff, range, rayLOSCallback, &help); + + if (gWall != NULL && gNumWalls != NULL) // Out globals are set + { + *gWall = help.wall; + *gNumWalls = help.numWalls; + } + + // See if the target can be seen + top = dest.z + visObjHeight(psTarget) - help.startHeight; + targetGrad = top * GRAD_MUL / help.lastDist; + + return targetGrad >= help.currGrad; + } +} diff --git a/src/visibility.h b/src/visibility.h index f8b08b0bf..183fcca78 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -45,6 +45,9 @@ extern void visTilesUpdate(BASE_OBJECT *psObj, RAY_CALLBACK callback); */ extern bool visibleObject(const BASE_OBJECT* psViewer, const BASE_OBJECT* psTarget, bool wallsBlock); +/** Can shooter hit target with direct fire weapon? */ +bool lineOfFire(const BASE_OBJECT* psViewer, const BASE_OBJECT* psTarget, bool wallsBlock); + // Find the wall that is blocking LOS to a target (if any) extern STRUCTURE* visGetBlockingWall(const BASE_OBJECT* psViewer, const BASE_OBJECT* psTarget);