summaryrefslogtreecommitdiff
path: root/src/uqm/ships/urquan/urquan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ships/urquan/urquan.c')
-rw-r--r--src/uqm/ships/urquan/urquan.c554
1 files changed, 554 insertions, 0 deletions
diff --git a/src/uqm/ships/urquan/urquan.c b/src/uqm/ships/urquan/urquan.c
new file mode 100644
index 0000000..9df87d0
--- /dev/null
+++ b/src/uqm/ships/urquan/urquan.c
@@ -0,0 +1,554 @@
+//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 "urquan.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+
+#include <stdlib.h>
+
+// Core characteristics
+#define MAX_CREW MAX_CREW_SIZE
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST 30
+#define THRUST_INCREMENT 6
+#define THRUST_WAIT 6
+#define TURN_WAIT 4
+#define SHIP_MASS 10
+
+// Fusion blast
+#define WEAPON_ENERGY_COST 6
+#define WEAPON_WAIT 6
+#define MISSILE_SPEED DISPLAY_TO_WORLD (20)
+#define MISSILE_LIFE 20
+#define MISSILE_HITS 10
+#define MISSILE_DAMAGE 6
+#define MISSILE_OFFSET 8
+#define URQUAN_OFFSET 32
+
+// Fighters
+#define SPECIAL_ENERGY_COST 8
+#define SPECIAL_WAIT 9
+#define FIGHTER_OFFSET 4
+#define FIGHTER_SPEED DISPLAY_TO_WORLD (8)
+#define ONE_WAY_FLIGHT 125
+#define TRACK_THRESHOLD 6
+#define FIGHTER_LIFE (ONE_WAY_FLIGHT + ONE_WAY_FLIGHT + 150)
+#define FIGHTER_HITS 1
+#define FIGHTER_MASS 0
+#define FIGHTER_WEAPON_WAIT 8
+#define FIGHTER_LASER_RANGE DISPLAY_TO_WORLD (40 + FIGHTER_OFFSET)
+
+static RACE_DESC urquan_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_SPECIAL,
+ 30, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ URQUAN_RACE_STRINGS,
+ URQUAN_ICON_MASK_PMAP_ANIM,
+ URQUAN_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 2666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 5750, 6000,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ URQUAN_BIG_MASK_PMAP_ANIM,
+ URQUAN_MED_MASK_PMAP_ANIM,
+ URQUAN_SML_MASK_PMAP_ANIM,
+ },
+ {
+ FUSION_BIG_MASK_PMAP_ANIM,
+ FUSION_MED_MASK_PMAP_ANIM,
+ FUSION_SML_MASK_PMAP_ANIM,
+ },
+ {
+ FIGHTER_BIG_MASK_PMAP_ANIM,
+ FIGHTER_MED_MASK_PMAP_ANIM,
+ FIGHTER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ URQUAN_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ URQUAN_VICTORY_SONG,
+ URQUAN_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_SPEED * MISSILE_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_fusion (ELEMENT *ShipPtr, HELEMENT FusionArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = URQUAN_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ FusionArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+fighter_postprocess (ELEMENT *ElementPtr)
+{
+ HELEMENT Laser;
+ STARSHIP *StarShipPtr;
+ LASER_BLOCK LaserBlock;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LaserBlock.cx = ElementPtr->next.location.x;
+ LaserBlock.cy = ElementPtr->next.location.y;
+ LaserBlock.face = ElementPtr->thrust_wait;
+ LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), FIGHTER_LASER_RANGE);
+ LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), FIGHTER_LASER_RANGE);
+ LaserBlock.sender = ElementPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = FIGHTER_OFFSET;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E);
+ Laser = initialize_laser (&LaserBlock);
+ if (Laser)
+ {
+ ELEMENT *LaserPtr;
+
+ LockElement (Laser, &LaserPtr);
+ SetElementStarShip (LaserPtr, StarShipPtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ /* FIGHTER_ZAP */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), LaserPtr);
+
+ UnlockElement (Laser);
+ PutElement (Laser);
+ }
+
+ ElementPtr->postprocess_func = 0;
+ ElementPtr->thrust_wait = FIGHTER_WEAPON_WAIT;
+}
+
+static void
+fighter_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ ++StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ if (FIGHTER_LIFE - ElementPtr->life_span > TRACK_THRESHOLD
+ && !(ElementPtr->state_flags & CHANGING))
+ {
+ BOOLEAN Enroute;
+ COUNT orig_facing, facing;
+ SIZE delta_x, delta_y;
+ ELEMENT *eptr;
+
+ Enroute = TRUE;
+
+ delta_x = StarShipPtr->RaceDescPtr->ship_info.crew_level;
+ delta_y = ElementPtr->life_span;
+
+ orig_facing = facing =
+ GetFrameIndex (ElementPtr->current.image.frame);
+ if (((delta_y & 1) || ElementPtr->hTarget
+ || TrackShip (ElementPtr, &facing) >= 0)
+ && (delta_x == 0 || delta_y >= ONE_WAY_FLIGHT))
+ ElementPtr->state_flags |= IGNORE_SIMILAR;
+ else if (delta_x)
+ {
+ LockElement (StarShipPtr->hShip, &eptr);
+ delta_x = eptr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y = eptr->current.location.y
+ - ElementPtr->current.location.y;
+ UnlockElement (StarShipPtr->hShip);
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+
+#ifdef NEVER
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ if (delta_x <= LASER_RANGE && delta_y <= LASER_RANGE)
+#endif /* NEVER */
+ ElementPtr->state_flags &= ~IGNORE_SIMILAR;
+
+ Enroute = FALSE;
+ }
+
+ if (ElementPtr->thrust_wait > 0)
+ --ElementPtr->thrust_wait;
+
+ if (ElementPtr->hTarget)
+ {
+ LockElement (ElementPtr->hTarget, &eptr);
+ delta_x = eptr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y = eptr->current.location.y
+ - ElementPtr->current.location.y;
+ UnlockElement (ElementPtr->hTarget);
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+
+ if (ElementPtr->thrust_wait == 0
+ && abs (delta_x) < FIGHTER_LASER_RANGE * 3 / 4
+ && abs (delta_y) < FIGHTER_LASER_RANGE * 3 / 4
+ && delta_x * delta_x + delta_y * delta_y <
+ (FIGHTER_LASER_RANGE * 3 / 4) * (FIGHTER_LASER_RANGE * 3 / 4))
+ {
+ ElementPtr->thrust_wait =
+ (BYTE)NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+ ElementPtr->postprocess_func = fighter_postprocess;
+ }
+
+ if (Enroute)
+ {
+ facing = GetFrameIndex (eptr->current.image.frame);
+ if (ElementPtr->turn_wait & LEFT)
+ {
+ delta_x += COSINE (FACING_TO_ANGLE (facing - 4),
+ DISPLAY_TO_WORLD (30));
+ delta_y += SINE (FACING_TO_ANGLE (facing - 4),
+ DISPLAY_TO_WORLD (30));
+ }
+ else
+ {
+ delta_x += COSINE (FACING_TO_ANGLE (facing + 4),
+ DISPLAY_TO_WORLD (30));
+ delta_y += SINE (FACING_TO_ANGLE (facing + 4),
+ DISPLAY_TO_WORLD (30));
+ }
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+ }
+ }
+ ElementPtr->state_flags |= CHANGING;
+
+ if (facing != orig_facing)
+ ElementPtr->next.image.frame = SetAbsFrameIndex (
+ ElementPtr->next.image.frame, facing
+ );
+ SetVelocityVector (
+ &ElementPtr->velocity, FIGHTER_SPEED, facing
+ );
+ }
+}
+
+static void
+fighter_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (GRAVITY_MASS (ElementPtr1->mass_points))
+ {
+ HELEMENT hFighterElement;
+
+ hFighterElement = AllocElement ();
+ if (hFighterElement)
+ {
+ COUNT primIndex, travel_facing;
+ SIZE delta_facing;
+ ELEMENT *FighterElementPtr;
+
+ LockElement (hFighterElement, &FighterElementPtr);
+ primIndex = FighterElementPtr->PrimIndex;
+ *FighterElementPtr = *ElementPtr0;
+ FighterElementPtr->PrimIndex = primIndex;
+ (GLOBAL (DisplayArray))[primIndex] =
+ (GLOBAL (DisplayArray))[ElementPtr0->PrimIndex];
+ FighterElementPtr->state_flags &= ~PRE_PROCESS;
+ FighterElementPtr->state_flags |= CHANGING;
+ FighterElementPtr->next = FighterElementPtr->current;
+ travel_facing = GetVelocityTravelAngle (
+ &FighterElementPtr->velocity
+ );
+ delta_facing = NORMALIZE_ANGLE (
+ ARCTAN (pPt1->x - pPt0->x, pPt1->y - pPt0->y)
+ - travel_facing);
+ if (delta_facing == 0)
+ {
+ if (FighterElementPtr->turn_wait & LEFT)
+ travel_facing -= QUADRANT;
+ else
+ travel_facing += QUADRANT;
+ }
+ else if (delta_facing <= HALF_CIRCLE)
+ travel_facing -= QUADRANT;
+ else
+ travel_facing += QUADRANT;
+
+ travel_facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ NORMALIZE_ANGLE (travel_facing)
+ ));
+ FighterElementPtr->next.image.frame =
+ SetAbsFrameIndex (FighterElementPtr->next.image.frame,
+ travel_facing);
+ SetVelocityVector (&FighterElementPtr->velocity,
+ FIGHTER_SPEED, travel_facing);
+ UnlockElement (hFighterElement);
+
+ PutElement (hFighterElement);
+ }
+
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION;
+ }
+ else if (ElementPtr0->pParent != ElementPtr1->pParent)
+ {
+ ElementPtr0->blast_offset = 0;
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION;
+ }
+ else if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* FIGHTERS_RETURN */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), ElementPtr1);
+ DeltaCrew (ElementPtr1, 1);
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION;
+ }
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ ElementPtr0->state_flags |= NONSOLID;
+
+ --StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+static void
+spawn_fighters (ELEMENT *ElementPtr)
+{
+ SIZE i;
+ COUNT facing;
+ SIZE delta_x, delta_y;
+ HELEMENT hFighterElement;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ facing = StarShipPtr->ShipFacing + ANGLE_TO_FACING (HALF_CIRCLE);
+ delta_x = COSINE (FACING_TO_ANGLE (facing), DISPLAY_TO_WORLD (14));
+ delta_y = SINE (FACING_TO_ANGLE (facing), DISPLAY_TO_WORLD (14));
+
+ i = ElementPtr->crew_level > 2 ? 2 : 1;
+ while (i-- && (hFighterElement = AllocElement ()))
+ {
+ SIZE sx, sy;
+ COUNT fighter_facing;
+ ELEMENT *FighterElementPtr;
+
+ DeltaCrew (ElementPtr, -1);
+
+ PutElement (hFighterElement);
+ LockElement (hFighterElement, &FighterElementPtr);
+ FighterElementPtr->hit_points = FIGHTER_HITS;
+ FighterElementPtr->mass_points = FIGHTER_MASS;
+ FighterElementPtr->thrust_wait = TRACK_THRESHOLD + 1;
+ FighterElementPtr->playerNr = ElementPtr->playerNr;
+ FighterElementPtr->state_flags = APPEARING | FINITE_LIFE
+ | CREW_OBJECT | IGNORE_SIMILAR;
+ FighterElementPtr->life_span = FIGHTER_LIFE;
+ SetPrimType (&(GLOBAL (DisplayArray))[FighterElementPtr->PrimIndex],
+ STAMP_PRIM);
+ {
+ FighterElementPtr->preprocess_func = fighter_preprocess;
+ FighterElementPtr->postprocess_func = 0;
+ FighterElementPtr->collision_func = fighter_collision;
+ FighterElementPtr->death_func = NULL;
+ }
+
+ FighterElementPtr->current.location = ElementPtr->next.location;
+ if (i == 1)
+ {
+ FighterElementPtr->turn_wait = LEFT;
+ fighter_facing = NORMALIZE_FACING (facing + 2);
+ FighterElementPtr->current.location.x += delta_x - delta_y;
+ FighterElementPtr->current.location.y += delta_y + delta_x;
+ }
+ else
+ {
+ FighterElementPtr->turn_wait = RIGHT;
+ fighter_facing = NORMALIZE_FACING (facing - 2);
+ FighterElementPtr->current.location.x += delta_x + delta_y;
+ FighterElementPtr->current.location.y += delta_y - delta_x;
+ }
+ sx = COSINE (FACING_TO_ANGLE (fighter_facing),
+ WORLD_TO_VELOCITY (FIGHTER_SPEED));
+ sy = SINE (FACING_TO_ANGLE (fighter_facing),
+ WORLD_TO_VELOCITY (FIGHTER_SPEED));
+ SetVelocityComponents (&FighterElementPtr->velocity, sx, sy);
+ FighterElementPtr->current.location.x -= VELOCITY_TO_WORLD (sx);
+ FighterElementPtr->current.location.y -= VELOCITY_TO_WORLD (sy);
+
+ FighterElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ FighterElementPtr->current.image.frame =
+ SetAbsFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0],
+ fighter_facing);
+ SetElementStarShip (FighterElementPtr, StarShipPtr);
+ UnlockElement (hFighterElement);
+ }
+}
+
+static void
+urquan_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = PURSUE;
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr
+ && lpEvalDesc->MoveState == ENTICE
+ && (!(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)
+ || lpEvalDesc->which_turn <= 8)
+ && (!(lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ || (lpEvalDesc->ObjectPtr->mass_points >= 4
+ && lpEvalDesc->which_turn == 2
+ && ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 16)))
+ lpEvalDesc->MoveState = PURSUE;
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ {
+ STARSHIP *EnemyStarShipPtr = NULL;
+
+ if (lpEvalDesc->ObjectPtr)
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr
+ && StarShipPtr->RaceDescPtr->ship_info.crew_level >
+ (StarShipPtr->RaceDescPtr->ship_info.max_crew >> 2)
+ && !(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & POINT_DEFENSE)
+ && (StarShipPtr->RaceDescPtr->characteristics.special_wait < 6
+ || (MANEUVERABILITY (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) <= SLOW_SHIP
+ && !(EnemyStarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED))
+ || (lpEvalDesc->which_turn <= 12
+ && (StarShipPtr->ship_input_state & (LEFT | RIGHT))
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
+ (BYTE)(StarShipPtr->RaceDescPtr->ship_info.max_energy >> 1))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ }
+
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
+}
+
+static void
+urquan_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && ElementPtr->crew_level > 1
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* LAUNCH_FIGHTERS */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ spawn_fighters (ElementPtr);
+
+ StarShipPtr->special_counter = SPECIAL_WAIT;
+ }
+}
+
+RACE_DESC*
+init_urquan (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ urquan_desc.postprocess_func = urquan_postprocess;
+ urquan_desc.init_weapon_func = initialize_fusion;
+ urquan_desc.cyborg_control.intelligence_func = urquan_intelligence;
+
+ RaceDescPtr = &urquan_desc;
+
+ return (RaceDescPtr);
+}
+