summaryrefslogtreecommitdiff
path: root/src/uqm/ships/sis_ship/sis_ship.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ships/sis_ship/sis_ship.c')
-rw-r--r--src/uqm/ships/sis_ship/sis_ship.c1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/src/uqm/ships/sis_ship/sis_ship.c b/src/uqm/ships/sis_ship/sis_ship.c
new file mode 100644
index 0000000..d589827
--- /dev/null
+++ b/src/uqm/ships/sis_ship/sis_ship.c
@@ -0,0 +1,1002 @@
+//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 "sis_ship.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/controls.h"
+#include "uqm/globdata.h"
+#include "uqm/hyper.h"
+#include "libs/mathlib.h"
+
+/* Core characteristics.
+ * All of these are changed at init time by some module, except for
+ * MAX_ENERGY, THRUST_INCREMENT, and SHIP_MASS. */
+
+#define MAX_CREW MAX_CREW_SIZE
+ /* This value gets thrown out - actual max crew is determined by the
+ * number of crew pods. The minimum value is 1 (just the Captain). */
+
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+ /* Shiva furnaces increase this by 1 each. */
+#define SHIVA_ENERGY_REGEN_INC 1
+
+#define ENERGY_WAIT 10
+ /* Dynamos decrease this by 2 each, to a minimum of 4. */
+#define MIN_ENERGY_WAIT 4
+#define DYNAMO_UNIT_ENERGY_WAIT_DEC 2
+
+#define MAX_THRUST 10
+ /* Thrusters increase this and decrease THRUST_WAIT based on
+ * THRUST_INCREMENT, see InitDriveSlots near the bottom of this file
+ * for details. */
+#define THRUST_INCREMENT 4
+#define THRUST_WAIT 6
+#define TURN_WAIT 17
+ /* Turning jets decrease by 2 each */
+#define SHIP_MASS MAX_SHIP_MASS
+
+
+/* Primary weapon - energy cost and damage change at init time based on
+ * the number and type of weapon modules installed. */
+
+#define BLASTER_DAMAGE 2
+ /* This is the damage value for the basic ion bolt guns. Fusion
+ * blasters and hellbore cannons end up doing (BLASTER_DAMAGE * 2)
+ * and (BLASTER_DAMAGE * 3) damage, respectively, but this depends
+ * on enum values. */
+
+#define BLASTER_HITS 2 /* Hitpoints for ion bolt guns, see BLASTER_DAMAGE */
+
+#define WEAPON_ENERGY_COST 1
+ /* This value gets thrown out and reset in an ugly manner based on
+ * the enum that is used for module IDs. Bigger gun = higher value.
+ */
+#define WEAPON_WAIT 6
+#define BLASTER_SPEED DISPLAY_TO_WORLD (24)
+#define BLASTER_LIFE 12
+ /* This value is greatly increased, based in part on the enum used
+ * for module IDs (bigger gun == longer life). See the first half of
+ * InitWeaponSlots */
+#define MAX_TRACKING 3
+#define TRACKER_ENERGY_COST 3
+#define BLASTER_OFFSET 8
+#define SIS_VERT_OFFSET 28
+ /* Used for foward, spread, and rear slots */
+#define SIS_HORZ_OFFSET 20
+ /* Used for side slot */
+
+
+/* Secondary weapon */
+#define SPECIAL_ENERGY_COST 0
+ /* Increased by 1 for each point defense module */
+#define ANTIMISSILE_ENERGY_INC 1
+#define SPECIAL_WAIT 9
+#define LASER_RANGE (UWORD)100
+#define MAX_DEFENSE 8
+
+
+static RACE_DESC sis_desc =
+{
+ { /* SHIP_INFO */
+ 0,
+ 16, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ NULL_RESOURCE,
+ SIS_ICON_MASK_PMAP_ANIM,
+ NULL_RESOURCE,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SIS_BIG_MASK_PMAP_ANIM,
+ SIS_MED_MASK_PMAP_ANIM,
+ SIS_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BLASTER_BIG_MASK_PMAP_ANIM,
+ BLASTER_MED_MASK_PMAP_ANIM,
+ BLASTER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ SIS_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SIS_VICTORY_SONG,
+ SIS_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ BLASTER_SPEED * BLASTER_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+// Private per-instance SIS data
+typedef struct
+{
+ COUNT num_trackers;
+ COUNT num_blasters;
+ MISSILE_BLOCK MissileBlock[6];
+
+} SIS_DATA;
+
+static void InitWeaponSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *ModuleSlots);
+static void InitModuleSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *ModuleSlots);
+static void InitDriveSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *DriveSlots);
+static void InitJetSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *JetSlots);
+static void uninit_sis (RACE_DESC *pRaceDesc);
+
+
+// Local typedef
+typedef SIS_DATA CustomShipData_t;
+
+// Retrieve race-specific ship data from a race desc
+static CustomShipData_t *
+GetCustomShipData (RACE_DESC *pRaceDesc)
+{
+ return pRaceDesc->data;
+}
+
+// Set the race-specific data in a race desc
+// (Re)Allocates its own storage for the data.
+static void
+SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
+{
+ if (pRaceDesc->data == data)
+ return; // no-op
+
+ if (pRaceDesc->data) // Out with the old
+ {
+ HFree (pRaceDesc->data);
+ pRaceDesc->data = NULL;
+ }
+
+ if (data) // In with the new
+ {
+ CustomShipData_t* newData = HMalloc (sizeof (*data));
+ *newData = *data;
+ pRaceDesc->data = newData;
+ }
+}
+
+static void
+sis_hyper_preprocess (ELEMENT *ElementPtr)
+{
+ SIZE udx = 0, udy = 0;
+ SIZE dx = 0, dy = 0;
+ SIZE AccelerateDirection;
+ STARSHIP *StarShipPtr;
+
+ if (ElementPtr->state_flags & APPEARING)
+ ElementPtr->velocity = GLOBAL (velocity);
+
+ AccelerateDirection = 0;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->weapon_counter; /* no shooting in hyperspace! */
+ if ((GLOBAL (autopilot)).x == ~0
+ || (GLOBAL (autopilot)).y == ~0
+ || (StarShipPtr->cur_status_flags & (LEFT | RIGHT | THRUST)))
+ {
+LeaveAutoPilot:
+ (GLOBAL (autopilot)).x =
+ (GLOBAL (autopilot)).y = ~0;
+ if (!(StarShipPtr->cur_status_flags & THRUST)
+ || (GLOBAL_SIS (FuelOnBoard) == 0
+ && GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1))
+ {
+ AccelerateDirection = -1;
+ GetCurrentVelocityComponents (&ElementPtr->velocity,
+ &dx, &dy);
+ udx = dx << 4;
+ udy = dy << 4;
+
+ StarShipPtr->cur_status_flags &= ~THRUST;
+ }
+ }
+ else
+ {
+ SIZE facing;
+ POINT universe;
+
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ udx = (GLOBAL (autopilot)).x - universe.x;
+ udy = -((GLOBAL (autopilot)).y - universe.y);
+ if ((dx = udx) < 0)
+ dx = -dx;
+ if ((dy = udy) < 0)
+ dy = -dy;
+ if (dx <= 1 && dy <= 1)
+ goto LeaveAutoPilot;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (ARCTAN (udx, udy)));
+
+ /* This prevents ship from flying backwards on auto-pilot.
+ * It could also theoretically abort autopilot in a bad savegame */
+ if ((StarShipPtr->cur_status_flags & SHIP_AT_MAX_SPEED)
+ /*|| (ElementPtr->state_flags & APPEARING)*/ )
+ {
+ if (NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + ANGLE_TO_FACING (QUADRANT)
+ - facing) > ANGLE_TO_FACING (HALF_CIRCLE))
+ goto LeaveAutoPilot;
+
+ facing = StarShipPtr->ShipFacing;
+ }
+ else if ((int)facing != (int)StarShipPtr->ShipFacing
+ && ElementPtr->turn_wait == 0)
+ {
+ if (NORMALIZE_FACING (
+ StarShipPtr->ShipFacing - facing
+ ) >= ANGLE_TO_FACING (HALF_CIRCLE))
+ {
+ facing = NORMALIZE_FACING (facing - 1);
+ StarShipPtr->cur_status_flags |= RIGHT;
+ }
+ else if ((int)StarShipPtr->ShipFacing != (int)facing)
+ {
+ facing = NORMALIZE_FACING (facing + 1);
+ StarShipPtr->cur_status_flags |= LEFT;
+ }
+
+ if ((int)facing == (int)StarShipPtr->ShipFacing)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ }
+ }
+
+ GetCurrentVelocityComponents (&ElementPtr->velocity, &dx, &dy);
+ if ((GLOBAL_SIS (FuelOnBoard)
+ || GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1)
+ && (int)facing == (int)StarShipPtr->ShipFacing)
+ {
+ StarShipPtr->cur_status_flags |= SHIP_AT_MAX_SPEED;
+ AccelerateDirection = 1;
+ }
+ else
+ {
+ AccelerateDirection = -1;
+ udx = dx << 4;
+ udy = dy << 4;
+ }
+ }
+
+ if (ElementPtr->thrust_wait == 0 && AccelerateDirection)
+ {
+ COUNT dist;
+ SIZE speed, velocity_increment;
+
+ velocity_increment = WORLD_TO_VELOCITY (
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment);
+
+ if ((dist = square_root ((long)udx * udx + (long)udy * udy)) == 0)
+ dist = 1; /* prevent divide by zero */
+
+ speed = square_root ((long)dx * dx + (long)dy * dy);
+ if (AccelerateDirection < 0)
+ {
+ dy = (speed / velocity_increment - 1) * velocity_increment;
+ if (dy < speed - velocity_increment)
+ dy = speed - velocity_increment;
+ if ((speed = dy) < 0)
+ speed = 0;
+
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+ }
+ else
+ {
+ SIZE max_velocity;
+
+ AccelerateDirection = 0;
+
+ max_velocity = WORLD_TO_VELOCITY (
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust);
+
+ dy = (speed / velocity_increment + 1)
+ * velocity_increment;
+ if (dy < speed + velocity_increment)
+ dy = speed + velocity_increment;
+ if ((speed = dy) > max_velocity)
+ {
+ speed = max_velocity;
+ StarShipPtr->cur_status_flags |= SHIP_AT_MAX_SPEED;
+ }
+ }
+
+ dx = (SIZE)((long)udx * speed / (long)dist);
+ dy = (SIZE)((long)udy * speed / (long)dist);
+ SetVelocityComponents (&ElementPtr->velocity, dx, dy);
+
+ ElementPtr->thrust_wait =
+ StarShipPtr->RaceDescPtr->characteristics.thrust_wait;
+ }
+}
+
+static void
+sis_hyper_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GLOBAL (velocity) = ElementPtr->velocity;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (((StarShipPtr->cur_status_flags & WEAPON) ||
+ PulsedInputState.menu[KEY_MENU_CANCEL])
+ && StarShipPtr->special_counter == 0)
+ {
+#define MENU_DELAY 10
+ HyperspaceMenu ();
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+ StarShipPtr->special_counter = MENU_DELAY;
+ }
+}
+
+static void
+spawn_point_defense (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ HELEMENT hDefense;
+
+ hDefense = AllocElement ();
+ if (hDefense)
+ {
+ ELEMENT *DefensePtr;
+
+ LockElement (hDefense, &DefensePtr);
+ DefensePtr->playerNr = ElementPtr->playerNr;
+ DefensePtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
+ DefensePtr->death_func = spawn_point_defense;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ SetElementStarShip (DefensePtr, StarShipPtr);
+ UnlockElement (hDefense);
+
+ PutElement (hDefense);
+ }
+ }
+ else
+ {
+ BOOLEAN PaidFor;
+ HELEMENT hObject, hNextObject;
+ ELEMENT *ShipPtr;
+ Color LaserColor;
+ static const Color ColorRange[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x18, 0x00), 0x79),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1C, 0x00), 0x78),
+ };
+
+ PaidFor = FALSE;
+
+ LaserColor = ColorRange[
+ StarShipPtr->RaceDescPtr->characteristics.special_energy_cost
+ ];
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ for (hObject = GetTailElement (); hObject; hObject = hNextObject)
+ {
+ ELEMENT *ObjectPtr;
+
+ LockElement (hObject, &ObjectPtr);
+ hNextObject = GetPredElement (ObjectPtr);
+ if (ObjectPtr != ShipPtr && CollidingElement (ObjectPtr) &&
+ !OBJECT_CLOAKED (ObjectPtr))
+ {
+ SIZE delta_x, delta_y;
+
+ delta_x = ObjectPtr->next.location.x -
+ ShipPtr->next.location.x;
+ delta_y = ObjectPtr->next.location.y -
+ ShipPtr->next.location.y;
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x = WORLD_TO_DISPLAY (delta_x);
+ delta_y = WORLD_TO_DISPLAY (delta_y);
+ if ((UWORD)delta_x <= LASER_RANGE &&
+ (UWORD)delta_y <= LASER_RANGE &&
+ (UWORD)delta_x * (UWORD)delta_x +
+ (UWORD)delta_y * (UWORD)delta_y <=
+ LASER_RANGE * LASER_RANGE)
+ {
+ HELEMENT hPointDefense;
+ LASER_BLOCK LaserBlock;
+
+ if (!PaidFor)
+ {
+ if (!DeltaEnergy (ShipPtr,
+ -(StarShipPtr->RaceDescPtr->characteristics.special_energy_cost
+ << 2)))
+ break;
+
+ ProcessSound (SetAbsSoundIndex (
+ /* POINT_DEFENSE_LASER */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ PaidFor = TRUE;
+ }
+
+ LaserBlock.cx = ShipPtr->next.location.x;
+ LaserBlock.cy = ShipPtr->next.location.y;
+ LaserBlock.face = 0;
+ LaserBlock.ex = ObjectPtr->next.location.x
+ - ShipPtr->next.location.x;
+ LaserBlock.ey = ObjectPtr->next.location.y
+ - ShipPtr->next.location.y;
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = 0;
+ LaserBlock.color = LaserColor;
+ hPointDefense = initialize_laser (&LaserBlock);
+ if (hPointDefense)
+ {
+ ELEMENT *PDPtr;
+
+ LockElement (hPointDefense, &PDPtr);
+ PDPtr->mass_points =
+ StarShipPtr->RaceDescPtr->characteristics.special_energy_cost;
+ SetElementStarShip (PDPtr, StarShipPtr);
+ PDPtr->hTarget = 0;
+ UnlockElement (hPointDefense);
+
+ PutElement (hPointDefense);
+ }
+ }
+ }
+ UnlockElement (hObject);
+ }
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+static void
+sis_battle_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->characteristics.special_energy_cost == 0)
+ {
+ StarShipPtr->cur_status_flags &= ~SPECIAL;
+ StarShipPtr->special_counter = 2;
+ }
+ if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & (FIRES_FORE | FIRES_RIGHT | FIRES_AFT | FIRES_LEFT)))
+ {
+ StarShipPtr->cur_status_flags &= ~WEAPON;
+ StarShipPtr->weapon_counter = 2;
+ }
+}
+
+static void
+sis_battle_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->characteristics.special_energy_cost)
+ {
+ spawn_point_defense (ElementPtr);
+ }
+}
+
+static void
+blaster_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ HELEMENT hBlastElement;
+
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if (hBlastElement)
+ {
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+ switch (ElementPtr0->mass_points)
+ {
+ case BLASTER_DAMAGE * 1:
+ BlastElementPtr->life_span = 2;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr0->current.image.frame, 0);
+ BlastElementPtr->preprocess_func = NULL;
+ break;
+ case BLASTER_DAMAGE * 2:
+ BlastElementPtr->life_span = 6;
+ BlastElementPtr->current.image.frame =
+ IncFrameIndex (ElementPtr0->current.image.frame);
+ break;
+ case BLASTER_DAMAGE * 3:
+ BlastElementPtr->life_span = 7;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr0->current.image.frame, 20);
+ break;
+ }
+ UnlockElement (hBlastElement);
+ }
+}
+
+static void
+blaster_preprocess (ELEMENT *ElementPtr)
+{
+ BYTE wait;
+
+ switch (ElementPtr->mass_points)
+ {
+ case BLASTER_DAMAGE * 1:
+ if (GetFrameIndex (ElementPtr->current.image.frame) < 8)
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ break;
+ case BLASTER_DAMAGE * 3:
+ if (GetFrameIndex (ElementPtr->current.image.frame) < 19)
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ else
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, 16);
+ ElementPtr->state_flags |= CHANGING;
+ break;
+ }
+
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else if ((wait = HINIBBLE (ElementPtr->turn_wait)))
+ {
+ COUNT facing;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)));
+ if (TrackShip (ElementPtr, &facing) > 0)
+ SetVelocityVector (&ElementPtr->velocity, BLASTER_SPEED, facing);
+
+ ElementPtr->turn_wait = MAKE_BYTE (wait, wait);
+ }
+}
+
+static COUNT
+initialize_blasters (ELEMENT *ShipPtr, HELEMENT BlasterArray[])
+{
+ BYTE nt;
+ COUNT i;
+ STARSHIP *StarShipPtr;
+ SIS_DATA *SisData;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ SisData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+
+ nt = (BYTE)((4 - SisData->num_trackers) & 3);
+
+ for (i = 0; i < SisData->num_blasters; ++i)
+ {
+ MISSILE_BLOCK MissileBlock = SisData->MissileBlock[i];
+
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.face = NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + MissileBlock.face);
+
+ BlasterArray[i] = initialize_missile (&MissileBlock);
+ if (BlasterArray[i])
+ {
+ ELEMENT *BlasterPtr;
+
+ LockElement (BlasterArray[i], &BlasterPtr);
+ BlasterPtr->collision_func = blaster_collision;
+ BlasterPtr->turn_wait = MAKE_BYTE (nt, nt);
+ UnlockElement (BlasterArray[i]);
+ }
+
+ }
+
+ return SisData->num_blasters;
+}
+
+static void
+sis_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+ SIS_DATA *SisData;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ SisData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ if (StarShipPtr->RaceDescPtr->characteristics.special_energy_cost)
+ {
+ if (StarShipPtr->special_counter == 0
+ && ((lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= 2)
+ || (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr != NULL
+ && ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn <= 4)))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ lpEvalDesc->ObjectPtr = NULL;
+ }
+ else if (MANEUVERABILITY (&StarShipPtr->RaceDescPtr->cyborg_control)
+ < MEDIUM_SHIP
+ && 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];
+ if (SisData->num_trackers
+ && StarShipPtr->weapon_counter == 0
+ && !(StarShipPtr->ship_input_state & WEAPON)
+ && lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= 16)
+ {
+ COUNT direction_facing;
+ SIZE delta_x, delta_y;
+ UWORD fire_flags, ship_flags;
+ COUNT facing;
+
+ delta_x = lpEvalDesc->ObjectPtr->current.location.x
+ - ShipPtr->current.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->current.location.y
+ - ShipPtr->current.location.y;
+ direction_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)));
+
+ ship_flags = StarShipPtr->RaceDescPtr->ship_info.ship_flags;
+ for (fire_flags = FIRES_FORE, facing = StarShipPtr->ShipFacing;
+ fire_flags <= FIRES_LEFT;
+ fire_flags <<= 1, facing += QUADRANT)
+ {
+ if ((ship_flags & fire_flags) && NORMALIZE_FACING (
+ direction_facing - facing + ANGLE_TO_FACING (OCTANT)
+ ) <= ANGLE_TO_FACING (QUADRANT))
+ {
+ StarShipPtr->ship_input_state |= WEAPON;
+ break;
+ }
+ }
+ }
+}
+
+static void
+InitWeaponSlots (RACE_DESC *RaceDescPtr, const BYTE *ModuleSlots)
+{
+ COUNT i;
+ SIS_DATA *SisData = GetCustomShipData (RaceDescPtr);
+ MISSILE_BLOCK *lpMB = SisData->MissileBlock;
+
+ SisData->num_blasters = 0;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ COUNT which_gun;
+
+ if (i == 3)
+ i = NUM_MODULE_SLOTS - 1;
+
+ which_gun = ModuleSlots[(NUM_MODULE_SLOTS - 1) - i];
+
+ if (which_gun < GUN_WEAPON || which_gun > CANNON_WEAPON)
+ continue; /* not a gun */
+
+ which_gun -= GUN_WEAPON - 1;
+ RaceDescPtr->characteristics.weapon_energy_cost +=
+ which_gun * 2;
+
+ lpMB->flags = IGNORE_SIMILAR;
+ lpMB->blast_offs = BLASTER_OFFSET;
+ lpMB->speed = BLASTER_SPEED;
+ lpMB->preprocess_func = blaster_preprocess;
+ lpMB->hit_points = BLASTER_HITS * which_gun;
+ lpMB->damage = BLASTER_DAMAGE * which_gun;
+ lpMB->life = BLASTER_LIFE + ((BLASTER_LIFE >> 2) * (which_gun - 1));
+
+ if (which_gun == 1)
+ lpMB->index = 0;
+ else if (which_gun == 2)
+ lpMB->index = 9;
+ else
+ lpMB->index = 16;
+
+ switch (i)
+ {
+ case 0: /* NOSE WEAPON */
+ RaceDescPtr->ship_info.ship_flags |= FIRES_FORE;
+ lpMB->pixoffs = SIS_VERT_OFFSET;
+ lpMB->face = 0;
+ break;
+ case 1: /* SPREAD WEAPON */
+ RaceDescPtr->ship_info.ship_flags |= FIRES_FORE;
+ lpMB->pixoffs = SIS_VERT_OFFSET;
+ lpMB->face = +1;
+ /* copy it because there are two */
+ lpMB[1] = lpMB[0];
+ ++lpMB;
+ ++SisData->num_blasters;
+ lpMB->face = NORMALIZE_FACING (-1);
+ break;
+ case 2: /* SIDE WEAPON */
+ RaceDescPtr->ship_info.ship_flags |=
+ FIRES_LEFT | FIRES_RIGHT;
+ lpMB->pixoffs = SIS_HORZ_OFFSET;
+ lpMB->face = ANGLE_TO_FACING (QUADRANT);
+ /* copy it because there are two */
+ lpMB[1] = lpMB[0];
+ ++lpMB;
+ ++SisData->num_blasters;
+ lpMB->face = NORMALIZE_FACING (-ANGLE_TO_FACING (QUADRANT));
+ break;
+ case NUM_MODULE_SLOTS - 1: /* TAIL WEAPON */
+ RaceDescPtr->ship_info.ship_flags |= FIRES_AFT;
+ lpMB->pixoffs = SIS_VERT_OFFSET;
+ lpMB->face = ANGLE_TO_FACING (HALF_CIRCLE);
+ break;
+ }
+
+ ++lpMB;
+ ++SisData->num_blasters;
+ }
+}
+
+static void
+InitModuleSlots (RACE_DESC *RaceDescPtr, const BYTE *ModuleSlots)
+{
+ COUNT i;
+ COUNT num_trackers;
+ SIS_DATA *SisData = GetCustomShipData (RaceDescPtr);
+
+ RaceDescPtr->ship_info.max_crew = 0;
+ num_trackers = 0;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ BYTE which_mod;
+
+ which_mod = ModuleSlots[(NUM_MODULE_SLOTS - 1) - i];
+ switch (which_mod)
+ {
+ case CREW_POD:
+ RaceDescPtr->ship_info.max_crew += CREW_POD_CAPACITY;
+ break;
+ case TRACKING_SYSTEM:
+ ++num_trackers;
+ break;
+ case ANTIMISSILE_DEFENSE:
+ RaceDescPtr->characteristics.special_energy_cost +=
+ ANTIMISSILE_ENERGY_INC;
+ break;
+ case SHIVA_FURNACE:
+ RaceDescPtr->characteristics.energy_regeneration +=
+ SHIVA_ENERGY_REGEN_INC;
+ break;
+ case DYNAMO_UNIT:
+ RaceDescPtr->characteristics.energy_wait -=
+ DYNAMO_UNIT_ENERGY_WAIT_DEC;
+ if (RaceDescPtr->characteristics.energy_wait < MIN_ENERGY_WAIT)
+ RaceDescPtr->characteristics.energy_wait = MIN_ENERGY_WAIT;
+ break;
+ }
+ }
+
+ if (num_trackers > MAX_TRACKING)
+ num_trackers = MAX_TRACKING;
+ RaceDescPtr->characteristics.weapon_energy_cost +=
+ num_trackers * TRACKER_ENERGY_COST;
+ SisData->num_trackers = num_trackers;
+ if (RaceDescPtr->characteristics.special_energy_cost)
+ {
+ RaceDescPtr->ship_info.ship_flags |= POINT_DEFENSE;
+ if (RaceDescPtr->characteristics.special_energy_cost > MAX_DEFENSE)
+ RaceDescPtr->characteristics.special_energy_cost = MAX_DEFENSE;
+ }
+}
+
+static void
+InitDriveSlots (RACE_DESC *RaceDescPtr, const BYTE *DriveSlots)
+{
+ COUNT i;
+
+ // NB. RaceDescPtr->characteristics.max_thrust is already initialised.
+ RaceDescPtr->characteristics.thrust_wait = 0;
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ switch (DriveSlots[i])
+ {
+ case FUSION_THRUSTER:
+ RaceDescPtr->characteristics.max_thrust += 2;
+ ++RaceDescPtr->characteristics.thrust_wait;
+ break;
+ }
+ }
+ RaceDescPtr->characteristics.thrust_wait = (BYTE)(
+ THRUST_WAIT - (RaceDescPtr->characteristics.thrust_wait >> 1));
+ RaceDescPtr->characteristics.max_thrust =
+ ((RaceDescPtr->characteristics.max_thrust /
+ RaceDescPtr->characteristics.thrust_increment) + 1)
+ * RaceDescPtr->characteristics.thrust_increment;
+}
+
+static void
+InitJetSlots (RACE_DESC *RaceDescPtr, const BYTE *JetSlots)
+{
+ COUNT i;
+
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ switch (JetSlots[i])
+ {
+ case TURNING_JETS:
+ RaceDescPtr->characteristics.turn_wait -= 2;
+ break;
+ }
+ }
+}
+
+RACE_DESC*
+init_sis (void)
+{
+ RACE_DESC *RaceDescPtr;
+ COUNT i;
+ // The caller of this func will copy the struct
+ static RACE_DESC new_sis_desc;
+ SIS_DATA empty_data;
+ memset (&empty_data, 0, sizeof (empty_data));
+
+ /* copy initial ship settings to new_sis_desc */
+ new_sis_desc = sis_desc;
+
+ new_sis_desc.uninit_func = uninit_sis;
+
+ if (inHQSpace ())
+ {
+ for (i = 0; i < NUM_VIEWS; ++i)
+ {
+ new_sis_desc.ship_data.ship_rsc[i] = NULL_RESOURCE;
+ new_sis_desc.ship_data.weapon_rsc[i] = NULL_RESOURCE;
+ new_sis_desc.ship_data.special_rsc[i] = NULL_RESOURCE;
+ }
+ new_sis_desc.ship_info.icons_rsc = NULL_RESOURCE;
+ new_sis_desc.ship_data.captain_control.captain_rsc = NULL_RESOURCE;
+ new_sis_desc.ship_data.victory_ditty_rsc = NULL_RESOURCE;
+ new_sis_desc.ship_data.ship_sounds_rsc = NULL_RESOURCE;
+
+ new_sis_desc.ship_data.ship_rsc[0] = SIS_HYPER_MASK_PMAP_ANIM;
+
+ new_sis_desc.preprocess_func = sis_hyper_preprocess;
+ new_sis_desc.postprocess_func = sis_hyper_postprocess;
+
+ new_sis_desc.characteristics.max_thrust -= 4;
+ }
+ else
+ {
+ new_sis_desc.preprocess_func = sis_battle_preprocess;
+ new_sis_desc.postprocess_func = sis_battle_postprocess;
+ new_sis_desc.init_weapon_func = initialize_blasters;
+ new_sis_desc.cyborg_control.intelligence_func = sis_intelligence;
+
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 3)
+ SET_GAME_STATE (BOMB_CARRIER, 1);
+ }
+
+ SetCustomShipData (&new_sis_desc, &empty_data);
+ InitModuleSlots (&new_sis_desc, GLOBAL_SIS (ModuleSlots));
+ InitWeaponSlots (&new_sis_desc, GLOBAL_SIS (ModuleSlots));
+ InitDriveSlots (&new_sis_desc, GLOBAL_SIS (DriveSlots));
+ InitJetSlots (&new_sis_desc, GLOBAL_SIS (JetSlots));
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ {
+ new_sis_desc.ship_info.crew_level = new_sis_desc.ship_info.max_crew;
+ }
+ else
+ {
+ // Count the captain too.
+ new_sis_desc.ship_info.max_crew++;
+ new_sis_desc.ship_info.crew_level = GLOBAL_SIS (CrewEnlisted) + 1;
+ new_sis_desc.ship_info.ship_flags |= PLAYER_CAPTAIN;
+ }
+
+ new_sis_desc.ship_info.energy_level = new_sis_desc.ship_info.max_energy;
+
+ RaceDescPtr = &new_sis_desc;
+
+ return (RaceDescPtr);
+}
+
+static void
+uninit_sis (RACE_DESC *pRaceDesc)
+{
+ if (!inHQSpace ())
+ {
+ GLOBAL_SIS (CrewEnlisted) = pRaceDesc->ship_info.crew_level;
+ if (pRaceDesc->ship_info.ship_flags & PLAYER_CAPTAIN)
+ GLOBAL_SIS (CrewEnlisted)--;
+ }
+
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+