diff options
Diffstat (limited to 'src/uqm/ship.c')
-rw-r--r-- | src/uqm/ship.c | 574 |
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; + } +} + |