summaryrefslogtreecommitdiff
path: root/src/uqm/ship.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ship.c')
-rw-r--r--src/uqm/ship.c574
1 files changed, 574 insertions, 0 deletions
diff --git a/src/uqm/ship.c b/src/uqm/ship.c
new file mode 100644
index 0000000..747c929
--- /dev/null
+++ b/src/uqm/ship.c
@@ -0,0 +1,574 @@
+//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 "ship.h"
+
+#include "build.h"
+#include "collide.h"
+#include "colors.h"
+#include "globdata.h"
+#include "status.h"
+#include "hyper.h"
+#include "tactrans.h"
+#include "pickship.h"
+#include "intel.h"
+#include "init.h"
+#include "battle.h"
+#include "supermelee/pickmele.h"
+#include "races.h"
+#include "setup.h"
+#include "sounds.h"
+#include "libs/mathlib.h"
+
+
+void
+animation_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+
+STATUS_FLAGS
+inertial_thrust (ELEMENT *ElementPtr)
+{
+#define MAX_ALLOWED_SPEED WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (18))
+#define MAX_ALLOWED_SPEED_SQR ((DWORD)MAX_ALLOWED_SPEED * MAX_ALLOWED_SPEED)
+
+ COUNT CurrentAngle, TravelAngle;
+ COUNT max_thrust, thrust_increment;
+ VELOCITY_DESC *VelocityPtr;
+ STARSHIP *StarShipPtr;
+
+ VelocityPtr = &ElementPtr->velocity;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ CurrentAngle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ TravelAngle = GetVelocityTravelAngle (VelocityPtr);
+ thrust_increment = StarShipPtr->RaceDescPtr->characteristics.thrust_increment;
+ max_thrust = StarShipPtr->RaceDescPtr->characteristics.max_thrust;
+ if (thrust_increment == max_thrust)
+ { // inertialess acceleration (Skiff)
+ SetVelocityVector (VelocityPtr,
+ max_thrust, StarShipPtr->ShipFacing);
+ return (SHIP_AT_MAX_SPEED);
+ }
+ else if (TravelAngle == CurrentAngle
+ && (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ && !(StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL))
+ { // already maxed-out acceleration
+ return (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED));
+ }
+ else
+ {
+ SIZE delta_x, delta_y;
+ SIZE cur_delta_x, cur_delta_y;
+ DWORD desired_speed, max_speed;
+ DWORD current_speed;
+
+ thrust_increment = WORLD_TO_VELOCITY (thrust_increment);
+ GetCurrentVelocityComponents (VelocityPtr, &cur_delta_x, &cur_delta_y);
+ current_speed = VelocitySquared (cur_delta_x, cur_delta_y);
+ delta_x = cur_delta_x + COSINE (CurrentAngle, thrust_increment);
+ delta_y = cur_delta_y + SINE (CurrentAngle, thrust_increment);
+ desired_speed = VelocitySquared (delta_x, delta_y);
+ max_speed = VelocitySquared (WORLD_TO_VELOCITY (max_thrust), 0);
+
+ if (desired_speed <= max_speed)
+ { // normal acceleration
+ SetVelocityComponents (VelocityPtr, delta_x, delta_y);
+ }
+ else if (((StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL)
+ && desired_speed <= MAX_ALLOWED_SPEED_SQR)
+ || desired_speed < current_speed)
+ { // acceleration in a gravity well within max allowed
+ // deceleration after gravity whip
+ SetVelocityComponents (VelocityPtr, delta_x, delta_y);
+ return (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ }
+ else if (TravelAngle == CurrentAngle)
+ { // normal max acceleration, same vector
+ if (current_speed <= max_speed)
+ SetVelocityVector (VelocityPtr, max_thrust,
+ StarShipPtr->ShipFacing);
+ return (SHIP_AT_MAX_SPEED);
+ }
+ else
+ { // maxed-out acceleration at an angle to current travel vector
+ // thrusting at an angle while at max velocity only changes
+ // the travel vector, but does not really change the velocity
+
+ VELOCITY_DESC v = *VelocityPtr;
+
+ DeltaVelocityComponents (&v,
+ COSINE (CurrentAngle, thrust_increment >> 1)
+ - COSINE (TravelAngle, thrust_increment),
+ SINE (CurrentAngle, thrust_increment >> 1)
+ - SINE (TravelAngle, thrust_increment));
+ GetCurrentVelocityComponents (&v, &cur_delta_x, &cur_delta_y);
+ desired_speed = VelocitySquared (cur_delta_x, cur_delta_y);
+ if (desired_speed > max_speed)
+ {
+ if (desired_speed < current_speed)
+ *VelocityPtr = v;
+ return (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ }
+
+ *VelocityPtr = v;
+ }
+
+ return (0);
+ }
+}
+
+void
+ship_preprocess (ELEMENT *ElementPtr)
+{
+ STATUS_FLAGS cur_status_flags;
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ cur_status_flags =
+ StarShipPtr->cur_status_flags
+ & ~(LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ cur_status_flags |= StarShipPtr->ship_input_state
+ & (LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ }
+ else
+ { // Preprocessing for the first time
+ ElementPtr->crew_level = RDPtr->ship_info.crew_level;
+
+ if (ElementPtr->playerNr == NPC_PLAYER_NUM
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ { // Sa-Matra
+ STAMP s;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ s.origin.x = s.origin.y = 0;
+ s.frame = RDPtr->ship_data.captain_control.background;
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+ RDPtr->ship_data.captain_control.background = 0;
+ SetContext (OldContext);
+ }
+ else if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ {
+ CONTEXT OldContext;
+
+ InitShipStatus (&RDPtr->ship_info, StarShipPtr, NULL);
+ OldContext = SetContext (StatusContext);
+ DrawCaptainsWindow (StarShipPtr);
+ SetContext (OldContext);
+ if (RDPtr->preprocess_func)
+ (*RDPtr->preprocess_func) (ElementPtr);
+
+ // XXX: Hack: Pkunk sets hTarget!=0 when it reincarnates. In that
+ // case there is no ship_transition() but a Phoenix transition
+ // instead.
+ if (ElementPtr->hTarget == 0)
+ {
+ ship_transition (ElementPtr);
+ }
+ else
+ {
+ ElementPtr->hTarget = 0;
+ if (!PLRPlaying ((MUSIC_REF)~0) && OpponentAlive (StarShipPtr))
+ BattleSong (TRUE);
+ }
+ return;
+ }
+ else
+ {
+ ElementPtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ ElementPtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ ElementPtr->next.location = ElementPtr->current.location;
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+
+ if (hyper_transition (ElementPtr))
+ return;
+ }
+ }
+ StarShipPtr->cur_status_flags = cur_status_flags;
+
+ if (StarShipPtr->energy_counter)
+ --StarShipPtr->energy_counter;
+ else if (RDPtr->ship_info.energy_level < (BYTE)RDPtr->ship_info.max_energy
+ || (SBYTE)RDPtr->characteristics.energy_regeneration < 0)
+ DeltaEnergy (ElementPtr,
+ (SBYTE)RDPtr->characteristics.energy_regeneration);
+
+ if (RDPtr->preprocess_func)
+ {
+ (*RDPtr->preprocess_func) (ElementPtr);
+ cur_status_flags = StarShipPtr->cur_status_flags;
+ }
+
+ if (ElementPtr->turn_wait)
+ --ElementPtr->turn_wait;
+ else if (cur_status_flags & (LEFT | RIGHT))
+ {
+ if (cur_status_flags & LEFT)
+ StarShipPtr->ShipFacing =
+ NORMALIZE_FACING (StarShipPtr->ShipFacing - 1);
+ else
+ StarShipPtr->ShipFacing =
+ NORMALIZE_FACING (StarShipPtr->ShipFacing + 1);
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ StarShipPtr->ShipFacing);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = RDPtr->characteristics.turn_wait;
+ }
+
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else if (cur_status_flags & THRUST)
+ {
+ STATUS_FLAGS thrust_status;
+
+ thrust_status = inertial_thrust (ElementPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED
+ | SHIP_BEYOND_MAX_SPEED
+ | SHIP_IN_GRAVITY_WELL);
+ StarShipPtr->cur_status_flags |= thrust_status;
+
+ ElementPtr->thrust_wait = RDPtr->characteristics.thrust_wait;
+
+ if (!OBJECT_CLOAKED (ElementPtr)
+ && LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ {
+ spawn_ion_trail (ElementPtr);
+ }
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ PreProcessStatus (ElementPtr);
+}
+
+void
+ship_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ if (ElementPtr->crew_level == 0)
+ return;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ if (StarShipPtr->weapon_counter)
+ --StarShipPtr->weapon_counter;
+ else if ((StarShipPtr->cur_status_flags
+ & WEAPON) && DeltaEnergy (ElementPtr,
+ -RDPtr->characteristics.weapon_energy_cost))
+ {
+ COUNT num_weapons;
+ HELEMENT Weapon[6];
+
+ num_weapons = (*RDPtr->init_weapon_func) (ElementPtr, Weapon);
+
+ if (num_weapons)
+ {
+ HELEMENT *WeaponPtr;
+ STARSHIP *StarShipPtr;
+ BOOLEAN played_sfx = FALSE;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ WeaponPtr = &Weapon[0];
+ do
+ {
+ HELEMENT w;
+ w = *WeaponPtr++;
+ if (w)
+ {
+ ELEMENT *EPtr;
+
+ LockElement (w, &EPtr);
+ SetElementStarShip (EPtr, StarShipPtr);
+ if (!played_sfx)
+ {
+ ProcessSound (RDPtr->ship_data.ship_sounds, EPtr);
+ played_sfx = TRUE;
+ }
+ UnlockElement (w);
+
+ PutElement (w);
+ }
+ } while (--num_weapons);
+
+ if (!played_sfx)
+ ProcessSound (RDPtr->ship_data.ship_sounds, ElementPtr);
+ }
+
+ StarShipPtr->weapon_counter =
+ RDPtr->characteristics.weapon_wait;
+ }
+
+ if (StarShipPtr->special_counter)
+ --StarShipPtr->special_counter;
+
+ if (RDPtr->postprocess_func)
+ (*RDPtr->postprocess_func) (ElementPtr);
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ PostProcessStatus (ElementPtr);
+}
+
+void
+collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ {
+ ElementPtr0->state_flags |= COLLISION;
+ if (GRAVITY_MASS (ElementPtr1->mass_points))
+ {
+ // Collision with a planet.
+ SIZE damage;
+
+ damage = ElementPtr0->hit_points >> 2;
+ if (damage == 0)
+ damage = 1;
+ do_damage (ElementPtr0, 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), ElementPtr0);
+ }
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static BOOLEAN
+spawn_ship (STARSHIP *StarShipPtr)
+{
+ HELEMENT hShip;
+ RACE_DESC *RDPtr;
+
+ RDPtr = load_ship (StarShipPtr->SpeciesID, TRUE);
+ if (!RDPtr)
+ return FALSE;
+
+ StarShipPtr->RaceDescPtr = RDPtr;
+
+ StarShipPtr->ship_input_state = 0;
+ StarShipPtr->cur_status_flags = 0;
+ StarShipPtr->old_status_flags = 0;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
+ || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ if (StarShipPtr->crew_level == 0)
+ {
+ // SIS, already handled from sis_ship.c.
+ // RDPtr->ship_info.crew_level = GLOBAL_SIS (CrewEnlisted);
+ }
+ else
+ RDPtr->ship_info.crew_level = StarShipPtr->crew_level;
+
+ if (RDPtr->ship_info.crew_level > RDPtr->ship_info.max_crew)
+ RDPtr->ship_info.crew_level = RDPtr->ship_info.max_crew;
+ }
+
+ StarShipPtr->energy_counter = 0;
+ StarShipPtr->weapon_counter = 0;
+ StarShipPtr->special_counter = 0;
+
+ hShip = StarShipPtr->hShip;
+ if (hShip == 0)
+ {
+ hShip = AllocElement ();
+ if (hShip != 0)
+ InsertElement (hShip, GetHeadElement ());
+ }
+
+ StarShipPtr->hShip = hShip;
+ if (StarShipPtr->hShip != 0)
+ {
+ // Construct an ELEMENT for the STARSHIP
+ ELEMENT *ShipElementPtr;
+
+ LockElement (hShip, &ShipElementPtr);
+
+ ShipElementPtr->playerNr = StarShipPtr->playerNr;
+ ShipElementPtr->crew_level = 0;
+ ShipElementPtr->mass_points = RDPtr->characteristics.ship_mass;
+ ShipElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR;
+ ShipElementPtr->turn_wait = 0;
+ ShipElementPtr->thrust_wait = 0;
+ ShipElementPtr->life_span = NORMAL_LIFE;
+ ShipElementPtr->colorCycleIndex = 0;
+
+ SetPrimType (&DisplayArray[ShipElementPtr->PrimIndex], STAMP_PRIM);
+ ShipElementPtr->current.image.farray = RDPtr->ship_data.ship;
+
+ if (ShipElementPtr->playerNr == NPC_PLAYER_NUM
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ // This is the Sa-Matra
+ StarShipPtr->ShipFacing = 0;
+ ShipElementPtr->current.image.frame =
+ SetAbsFrameIndex (RDPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ ShipElementPtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ ShipElementPtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ ++ShipElementPtr->life_span;
+ }
+ else
+ {
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ());
+ if (inHQSpace ())
+ { // Only one ship is ever spawned in HyperSpace -- flagship
+ COUNT facing = GLOBAL (ShipFacing);
+ // XXX: Solar system reentry test depends on ShipFacing != 0
+ if (facing > 0)
+ --facing;
+
+ // XXX: This appears to set the facing to a random value
+ // for when the ship returns from an encounter back
+ // to HyperSpace. However, it is overwritten later
+ // in sis.c. See also r1614.
+ //GLOBAL (ShipFacing) = StarShipPtr->ShipFacing + 1;
+ StarShipPtr->ShipFacing = facing;
+ }
+ ShipElementPtr->current.image.frame =
+ SetAbsFrameIndex (RDPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ do
+ {
+ ShipElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
+ ShipElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
+ } while (CalculateGravity (ShipElementPtr)
+ || TimeSpaceMatterConflict (ShipElementPtr));
+ }
+
+ ShipElementPtr->preprocess_func = ship_preprocess;
+ ShipElementPtr->postprocess_func = ship_postprocess;
+ ShipElementPtr->death_func = ship_death;
+ ShipElementPtr->collision_func = collision;
+ ZeroVelocityComponents (&ShipElementPtr->velocity);
+
+ SetElementStarShip (ShipElementPtr, StarShipPtr);
+ ShipElementPtr->hTarget = 0;
+
+ UnlockElement (hShip);
+ }
+
+ return (hShip != 0);
+}
+
+// Select a new ship and spawn it.
+BOOLEAN
+GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side)
+{
+ HSTARSHIP hBattleShip;
+
+ hBattleShip = GetEncounterStarShip (LastStarShipPtr, which_side);
+ if (hBattleShip)
+ {
+ STARSHIP *StarShipPtr;
+
+ StarShipPtr = LockStarShip (&race_q[which_side], hBattleShip);
+ if (LastStarShipPtr)
+ {
+ if (StarShipPtr == LastStarShipPtr)
+ {
+ // Ship has been recycled (on infinite ship worlds).
+ LastStarShipPtr = 0;
+ }
+ else
+ StarShipPtr->hShip = LastStarShipPtr->hShip;
+ }
+
+ if (!spawn_ship (StarShipPtr))
+ {
+ UnlockStarShip (&race_q[which_side], hBattleShip);
+ return (FALSE);
+ }
+ UnlockStarShip (&race_q[which_side], hBattleShip);
+ }
+
+ if (LastStarShipPtr)
+ LastStarShipPtr->hShip = 0;
+
+ return (hBattleShip != 0);
+}
+
+BOOLEAN
+GetInitialStarShips (void)
+{
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ {
+ HSTARSHIP ships[NUM_PLAYERS];
+ COUNT i;
+
+ if (!GetInitialMeleeStarShips (ships))
+ return FALSE;
+
+ for (i = 0; i < NUM_PLAYERS; i++)
+ {
+ STARSHIP *StarShipPtr;
+ COUNT playerI = GetPlayerOrder (i);
+
+ StarShipPtr = LockStarShip (&race_q[playerI], ships[playerI]);
+ if (!spawn_ship (StarShipPtr))
+ {
+ UnlockStarShip (&race_q[playerI], ships[playerI]);
+ return FALSE;
+ }
+ UnlockStarShip (&race_q[playerI], ships[playerI]);
+ }
+ return TRUE;
+ }
+ else
+ {
+ int i;
+
+ for (i = NUM_PLAYERS; i > 0; --i)
+ {
+ if (!GetNextStarShip (NULL, i - 1))
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+