summaryrefslogtreecommitdiff
path: root/src/uqm/weapon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/weapon.c')
-rw-r--r--src/uqm/weapon.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/src/uqm/weapon.c b/src/uqm/weapon.c
new file mode 100644
index 0000000..3f88f98
--- /dev/null
+++ b/src/uqm/weapon.c
@@ -0,0 +1,414 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "weapon.h"
+
+#include "colors.h"
+#include "globdata.h"
+#include "status.h"
+#include "init.h"
+#include "races.h"
+#include "ship.h"
+#include "setup.h"
+#include "sounds.h"
+#include "units.h"
+#include "libs/mathlib.h"
+
+#include <stdio.h>
+
+// A wrapper function for weapon_collision that discards the return value.
+// This makes its signature match ElementCollisionFunc.
+static void
+weapon_collision_cb (ELEMENT *WeaponElementPtr, POINT *pWPt,
+ ELEMENT *HitElementPtr, POINT *pHPt)
+{
+ weapon_collision (WeaponElementPtr, pWPt, HitElementPtr, pHPt);
+}
+
+
+HELEMENT
+initialize_laser (LASER_BLOCK *pLaserBlock)
+{
+ HELEMENT hLaserElement;
+
+ hLaserElement = AllocElement ();
+ if (hLaserElement)
+ {
+#define LASER_LIFE 1
+ ELEMENT *LaserElementPtr;
+
+ LockElement (hLaserElement, &LaserElementPtr);
+ LaserElementPtr->playerNr = pLaserBlock->sender;
+ LaserElementPtr->hit_points = 1;
+ LaserElementPtr->mass_points = 1;
+ LaserElementPtr->state_flags = APPEARING | FINITE_LIFE
+ | pLaserBlock->flags;
+ LaserElementPtr->life_span = LASER_LIFE;
+ LaserElementPtr->collision_func = weapon_collision_cb;
+ LaserElementPtr->blast_offset = 1;
+
+ LaserElementPtr->current.location.x = pLaserBlock->cx
+ + COSINE (FACING_TO_ANGLE (pLaserBlock->face),
+ DISPLAY_TO_WORLD (pLaserBlock->pixoffs));
+ LaserElementPtr->current.location.y = pLaserBlock->cy
+ + SINE (FACING_TO_ANGLE (pLaserBlock->face),
+ DISPLAY_TO_WORLD (pLaserBlock->pixoffs));
+ SetPrimType (&DisplayArray[LaserElementPtr->PrimIndex], LINE_PRIM);
+ SetPrimColor (&DisplayArray[LaserElementPtr->PrimIndex],
+ pLaserBlock->color);
+ LaserElementPtr->current.image.frame = DecFrameIndex (stars_in_space);
+ LaserElementPtr->current.image.farray = &stars_in_space;
+ SetVelocityComponents (&LaserElementPtr->velocity,
+ WORLD_TO_VELOCITY ((pLaserBlock->cx + pLaserBlock->ex)
+ - LaserElementPtr->current.location.x),
+ WORLD_TO_VELOCITY ((pLaserBlock->cy + pLaserBlock->ey)
+ - LaserElementPtr->current.location.y));
+ UnlockElement (hLaserElement);
+ }
+
+ return (hLaserElement);
+}
+
+HELEMENT
+initialize_missile (MISSILE_BLOCK *pMissileBlock)
+{
+ HELEMENT hMissileElement;
+
+ hMissileElement = AllocElement ();
+ if (hMissileElement)
+ {
+ SIZE delta_x, delta_y;
+ COUNT angle;
+ ELEMENT *MissileElementPtr;
+
+ LockElement (hMissileElement, &MissileElementPtr);
+ MissileElementPtr->hit_points = (BYTE)pMissileBlock->hit_points;
+ MissileElementPtr->mass_points = (BYTE)pMissileBlock->damage;
+ MissileElementPtr->playerNr = pMissileBlock->sender;
+ MissileElementPtr->state_flags = APPEARING | FINITE_LIFE
+ | pMissileBlock->flags;
+ MissileElementPtr->life_span = pMissileBlock->life;
+ SetPrimType (&DisplayArray[MissileElementPtr->PrimIndex], STAMP_PRIM);
+ MissileElementPtr->current.image.farray = pMissileBlock->farray;
+ MissileElementPtr->current.image.frame =
+ SetAbsFrameIndex (pMissileBlock->farray[0],
+ pMissileBlock->index);
+ MissileElementPtr->preprocess_func = pMissileBlock->preprocess_func;
+ MissileElementPtr->collision_func = weapon_collision_cb;
+ MissileElementPtr->blast_offset = (BYTE)pMissileBlock->blast_offs;
+
+ angle = FACING_TO_ANGLE (pMissileBlock->face);
+ MissileElementPtr->current.location.x = pMissileBlock->cx
+ + COSINE (angle, DISPLAY_TO_WORLD (pMissileBlock->pixoffs));
+ MissileElementPtr->current.location.y = pMissileBlock->cy
+ + SINE (angle, DISPLAY_TO_WORLD (pMissileBlock->pixoffs));
+
+ delta_x = COSINE (angle, WORLD_TO_VELOCITY (pMissileBlock->speed));
+ delta_y = SINE (angle, WORLD_TO_VELOCITY (pMissileBlock->speed));
+ SetVelocityComponents (&MissileElementPtr->velocity,
+ delta_x, delta_y);
+
+ MissileElementPtr->current.location.x -= VELOCITY_TO_WORLD (delta_x);
+ MissileElementPtr->current.location.y -= VELOCITY_TO_WORLD (delta_y);
+ UnlockElement (hMissileElement);
+ }
+
+ return (hMissileElement);
+}
+
+HELEMENT
+weapon_collision (ELEMENT *WeaponElementPtr, POINT *pWPt,
+ ELEMENT *HitElementPtr, POINT *pHPt)
+{
+ SIZE damage;
+ HELEMENT hBlastElement;
+
+ if (WeaponElementPtr->state_flags & COLLISION) /* if already did effect */
+ return ((HELEMENT)0);
+
+ damage = (SIZE)WeaponElementPtr->mass_points;
+ if (damage
+ && ((HitElementPtr->state_flags & FINITE_LIFE)
+ || HitElementPtr->life_span == NORMAL_LIFE))
+#ifdef NEVER
+ &&
+ /* lasers from the same ship can't hit each other */
+ (GetPrimType (&DisplayArray[HitElementPtr->PrimIndex]) != LINE_PRIM
+ || GetPrimType (&DisplayArray[WeaponElementPtr->PrimIndex]) != LINE_PRIM
+ || !elementsOfSamePlayer (HitElementPtr, WeaponElementPtr)))
+#endif /* NEVER */
+ {
+ do_damage (HitElementPtr, damage);
+ if (HitElementPtr->hit_points)
+ WeaponElementPtr->state_flags |= COLLISION;
+ }
+
+ if (!(HitElementPtr->state_flags & FINITE_LIFE)
+ || (!(/* WeaponElementPtr->state_flags
+ & */ HitElementPtr->state_flags & COLLISION)
+ && WeaponElementPtr->hit_points <= HitElementPtr->mass_points))
+ {
+ if (damage)
+ {
+ damage = TARGET_DAMAGED_FOR_1_PT + (damage >> 1);
+ if (damage > TARGET_DAMAGED_FOR_6_PLUS_PT)
+ damage = TARGET_DAMAGED_FOR_6_PLUS_PT;
+ ProcessSound (SetAbsSoundIndex (GameSounds, damage),
+ HitElementPtr);
+ }
+
+ if (GetPrimType (&DisplayArray[WeaponElementPtr->PrimIndex])
+ != LINE_PRIM)
+ WeaponElementPtr->state_flags |= DISAPPEARING;
+
+ WeaponElementPtr->hit_points = 0;
+ WeaponElementPtr->life_span = 0;
+ WeaponElementPtr->state_flags |= COLLISION | NONSOLID;
+
+ hBlastElement = AllocElement ();
+ if (hBlastElement)
+ {
+ COUNT blast_index;
+ SIZE blast_offs;
+ COUNT angle, num_blast_frames;
+ ELEMENT *BlastElementPtr;
+ extern FRAME blast[];
+
+ PutElement (hBlastElement);
+ LockElement (hBlastElement, &BlastElementPtr);
+ BlastElementPtr->playerNr = WeaponElementPtr->playerNr;
+ BlastElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ SetPrimType (&DisplayArray[BlastElementPtr->PrimIndex], STAMP_PRIM);
+
+ BlastElementPtr->current.location.x = DISPLAY_TO_WORLD (pWPt->x);
+ BlastElementPtr->current.location.y = DISPLAY_TO_WORLD (pWPt->y);
+
+ angle = GetVelocityTravelAngle (&WeaponElementPtr->velocity);
+ if ((blast_offs = WeaponElementPtr->blast_offset) > 0)
+ {
+ BlastElementPtr->current.location.x +=
+ COSINE (angle, DISPLAY_TO_WORLD (blast_offs));
+ BlastElementPtr->current.location.y +=
+ SINE (angle, DISPLAY_TO_WORLD (blast_offs));
+ }
+
+ blast_index =
+ NORMALIZE_FACING (ANGLE_TO_FACING (angle + HALF_CIRCLE));
+ blast_index = ((blast_index >> 2) << 1) +
+ (blast_index & 0x3 ? 1 : 0);
+
+ num_blast_frames =
+ GetFrameCount (WeaponElementPtr->next.image.frame);
+ if (num_blast_frames <= ANGLE_TO_FACING (FULL_CIRCLE))
+ {
+ BlastElementPtr->life_span = 2;
+ BlastElementPtr->current.image.farray = blast;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (blast[0], blast_index);
+ }
+ else
+ {
+ BlastElementPtr->life_span = num_blast_frames
+ - ANGLE_TO_FACING (FULL_CIRCLE);
+ BlastElementPtr->turn_wait = BlastElementPtr->next_turn = 0;
+ BlastElementPtr->preprocess_func = animation_preprocess;
+ BlastElementPtr->current.image.farray =
+ WeaponElementPtr->next.image.farray;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (
+ BlastElementPtr->current.image.farray[0],
+ ANGLE_TO_FACING (FULL_CIRCLE));
+ }
+
+ UnlockElement (hBlastElement);
+
+ return (hBlastElement);
+ }
+ }
+
+ (void) pHPt; /* Satisfying compiler (unused parameter) */
+ return ((HELEMENT)0);
+}
+
+FRAME
+ModifySilhouette (ELEMENT *ElementPtr, STAMP *modify_stamp,
+ BYTE modify_flags)
+{
+ FRAME f;
+ RECT r, or;
+ INTERSECT_CONTROL ShipIntersect, ObjectIntersect;
+ STARSHIP *StarShipPtr;
+ CONTEXT OldContext;
+
+ f = 0;
+ ObjectIntersect.IntersectStamp = *modify_stamp;
+ GetFrameRect (ObjectIntersect.IntersectStamp.frame, &or);
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (modify_flags & MODIFY_IMAGE)
+ {
+ ShipIntersect.IntersectStamp.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_info.icons, 1);
+ if (ShipIntersect.IntersectStamp.frame == 0)
+ return (0);
+
+ GetFrameRect (ShipIntersect.IntersectStamp.frame, &r);
+
+ ShipIntersect.IntersectStamp.origin.x = 0;
+ ShipIntersect.IntersectStamp.origin.y = 0;
+ ShipIntersect.EndPoint = ShipIntersect.IntersectStamp.origin;
+ do
+ {
+ ObjectIntersect.IntersectStamp.origin.x = ((COUNT)TFB_Random ()
+ % (r.extent.width - or.extent.width))
+ + ((or.extent.width - r.extent.width) >> 1);
+ ObjectIntersect.IntersectStamp.origin.y = ((COUNT)TFB_Random ()
+ % (r.extent.height - or.extent.height))
+ + ((or.extent.height - r.extent.height) >> 1);
+ ObjectIntersect.EndPoint = ObjectIntersect.IntersectStamp.origin;
+ } while (!DrawablesIntersect (&ObjectIntersect,
+ &ShipIntersect, MAX_TIME_VALUE));
+
+ ObjectIntersect.IntersectStamp.origin.x += STATUS_WIDTH >> 1;
+ ObjectIntersect.IntersectStamp.origin.y += 31;
+ }
+
+ ObjectIntersect.IntersectStamp.origin.y +=
+ status_y_offsets[ElementPtr->playerNr];
+
+ if (modify_flags & MODIFY_SWAP)
+ {
+ or.corner.x += ObjectIntersect.IntersectStamp.origin.x;
+ or.corner.y += ObjectIntersect.IntersectStamp.origin.y;
+ InitShipStatus (&StarShipPtr->RaceDescPtr->ship_info,
+ StarShipPtr, &or);
+ }
+ else
+ {
+ OldContext = SetContext (StatusContext);
+ DrawStamp (&ObjectIntersect.IntersectStamp);
+ SetContext (OldContext);
+ }
+
+ return (f);
+}
+
+// Find the closest possible target ship, to be set in Tracker->hTarget.
+// *pfacing will be turned one angle unit into the direction towards the
+// target.
+// The return value will be the actual number of angle units to turn, or
+// -1 if no target was found.
+// Cloaked ships won't be detected, except when the APPEARING flag is
+// set for the Tracker.
+SIZE
+TrackShip (ELEMENT *Tracker, COUNT *pfacing)
+{
+ SIZE best_delta_facing, best_delta;
+ HELEMENT hShip, hNextShip;
+ ELEMENT *Trackee;
+
+ best_delta = 0;
+ best_delta_facing = -1;
+
+ hShip = Tracker->hTarget;
+ if (hShip)
+ {
+ LockElement (hShip, &Trackee);
+ Tracker->hTarget = hNextShip = 0;
+
+ goto CheckTracking;
+ }
+
+ for (hShip = GetHeadElement (); hShip != 0; hShip = hNextShip)
+ {
+ LockElement (hShip, &Trackee);
+ hNextShip = GetSuccElement (Trackee);
+ if ((Trackee->state_flags & PLAYER_SHIP)
+ && !elementsOfSamePlayer (Trackee, Tracker)
+ && (!OBJECT_CLOAKED (Trackee)
+ || ((Tracker->state_flags & PLAYER_SHIP)
+ && (Tracker->state_flags & APPEARING))
+ ))
+ {
+ STARSHIP *StarShipPtr;
+
+CheckTracking:
+ GetElementStarShip (Trackee, &StarShipPtr);
+ if (Trackee->life_span
+ && StarShipPtr->RaceDescPtr->ship_info.crew_level)
+ {
+ SIZE delta_x, delta_y, delta_facing;
+
+ if (Tracker->state_flags & PRE_PROCESS)
+ {
+ delta_x = Trackee->next.location.x
+ - Tracker->next.location.x;
+ delta_y = Trackee->next.location.y
+ - Tracker->next.location.y;
+ }
+ else
+ {
+ delta_x = Trackee->current.location.x
+ - Tracker->current.location.x;
+ delta_y = Trackee->current.location.y
+ - Tracker->current.location.y;
+ }
+
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ delta_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) - *pfacing
+ );
+
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x += delta_y;
+ // 'delta_x + delta_y' is used as an approximation
+ // of the actual distance 'sqrt(sqr(delta_x) +
+ // sqr(delta_y))'.
+
+ if (best_delta == 0 || delta_x < best_delta)
+ {
+ best_delta = delta_x;
+ best_delta_facing = delta_facing;
+ Tracker->hTarget = hShip;
+ }
+ }
+ }
+ UnlockElement (hShip);
+ }
+
+ if (best_delta_facing > 0)
+ {
+ COUNT facing;
+
+ facing = *pfacing;
+ if (best_delta_facing == ANGLE_TO_FACING (HALF_CIRCLE))
+ facing += (((BYTE)TFB_Random () & 1) << 1) - 1;
+ else if (best_delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ ++facing;
+ else
+ --facing;
+ *pfacing = NORMALIZE_FACING (facing);
+ }
+
+ return (best_delta_facing);
+}
+