From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/uqm/weapon.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 414 insertions(+) create mode 100644 src/uqm/weapon.c (limited to 'src/uqm/weapon.c') 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 + +// 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); +} + -- cgit v1.2.3