summaryrefslogtreecommitdiff
path: root/src/uqm/ships/androsyn/androsyn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ships/androsyn/androsyn.c')
-rw-r--r--src/uqm/ships/androsyn/androsyn.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/src/uqm/ships/androsyn/androsyn.c b/src/uqm/ships/androsyn/androsyn.c
new file mode 100644
index 0000000..1139d8d
--- /dev/null
+++ b/src/uqm/ships/androsyn/androsyn.c
@@ -0,0 +1,528 @@
+//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 "androsyn.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 24
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 8
+#define MAX_THRUST 24
+#define THRUST_INCREMENT 3
+#define TURN_WAIT 4
+#define THRUST_WAIT 0
+#define SHIP_MASS 6
+
+// Bubbles
+#define WEAPON_ENERGY_COST 3
+#define WEAPON_WAIT 0
+#define ANDROSYNTH_OFFSET 14
+#define MISSILE_OFFSET 3
+#define MISSILE_SPEED DISPLAY_TO_WORLD (8)
+#define MISSILE_LIFE 200
+#define MISSILE_HITS 3
+#define MISSILE_DAMAGE 2
+#define TRACK_WAIT 2
+
+// Blazer
+#define SPECIAL_ENERGY_COST 2
+#define BLAZER_DEGENERATION (-1)
+#define SPECIAL_WAIT 0
+#define BLAZER_OFFSET 10
+#define BLAZER_THRUST 60
+#define BLAZER_TURN_WAIT 1
+#define BLAZER_DAMAGE 3
+#define BLAZER_MASS 1
+
+static RACE_DESC androsynth_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_WEAPON,
+ 15, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ ANDROSYNTH_RACE_STRINGS,
+ ANDROSYNTH_ICON_MASK_PMAP_ANIM,
+ ANDROSYNTH_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ INFINITE_RADIUS, /* Initial sphere of influence radius */
+ // XXX: Why infinite radius? Bug?
+ { /* Known location (center of SoI) */
+ MAX_X_UNIVERSE >> 1, MAX_Y_UNIVERSE >> 1,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ ANDROSYNTH_BIG_MASK_PMAP_ANIM,
+ ANDROSYNTH_MED_MASK_PMAP_ANIM,
+ ANDROSYNTH_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BUBBLE_BIG_MASK_PMAP_ANIM,
+ BUBBLE_MED_MASK_PMAP_ANIM,
+ BUBBLE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BLAZER_BIG_MASK_PMAP_ANIM,
+ BLAZER_MED_MASK_PMAP_ANIM,
+ BLAZER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ ANDROSYNTH_CAPT_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ ANDROSYNTH_VICTORY_SONG,
+ ANDROSYNTH_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ LONG_RANGE_WEAPON >> 2,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+
+// Private per-instance ship data
+typedef struct
+{
+ ElementCollisionFunc* collision_func;
+} ANDROSYNTH_DATA;
+
+// Local typedef
+typedef ANDROSYNTH_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
+blazer_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ BYTE old_offs;
+ COUNT old_crew_level;
+ COUNT old_life;
+
+ old_crew_level = ElementPtr0->crew_level;
+ old_life = ElementPtr0->life_span;
+ old_offs = ElementPtr0->blast_offset;
+ ElementPtr0->blast_offset = BLAZER_OFFSET;
+ ElementPtr0->mass_points = BLAZER_DAMAGE;
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->mass_points = BLAZER_MASS;
+ ElementPtr0->blast_offset = old_offs;
+ ElementPtr0->life_span = old_life;
+ ElementPtr0->crew_level = old_crew_level;
+
+ ElementPtr0->state_flags &= ~(DISAPPEARING | NONSOLID);
+ collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static void
+bubble_preprocess (ELEMENT *ElementPtr)
+{
+ BYTE thrust_wait, turn_wait;
+
+ thrust_wait = HINIBBLE (ElementPtr->turn_wait);
+ turn_wait = LONIBBLE (ElementPtr->turn_wait);
+ if (thrust_wait > 0)
+ --thrust_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ thrust_wait = (BYTE)((COUNT)TFB_Random () & 3);
+ }
+
+ if (turn_wait > 0)
+ --turn_wait;
+ else
+ {
+ COUNT facing;
+ SIZE delta_facing;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)));
+ if ((delta_facing = TrackShip (ElementPtr, &facing)) == -1)
+ facing = (COUNT)TFB_Random ();
+ else if (delta_facing <= ANGLE_TO_FACING (HALF_CIRCLE))
+ facing += (COUNT)TFB_Random () & (ANGLE_TO_FACING (HALF_CIRCLE) - 1);
+ else
+ facing -= (COUNT)TFB_Random () & (ANGLE_TO_FACING (HALF_CIRCLE) - 1);
+ SetVelocityVector (&ElementPtr->velocity,
+ MISSILE_SPEED, facing);
+ turn_wait = TRACK_WAIT;
+ }
+
+ ElementPtr->turn_wait = MAKE_BYTE (turn_wait, thrust_wait);
+}
+
+static COUNT
+initialize_bubble (ELEMENT *ShipPtr, HELEMENT BubbleArray[])
+{
+ 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 = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = ANDROSYNTH_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = bubble_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ BubbleArray[0] = initialize_missile (&MissileBlock);
+
+ if (BubbleArray[0])
+ {
+ ELEMENT *BubblePtr;
+
+ LockElement (BubbleArray[0], &BubblePtr);
+ BubblePtr->turn_wait = 0;
+ UnlockElement (BubbleArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+androsynth_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ /* in blazer form */
+ if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ ObjectsOfConcern[CREW_OBJECT_INDEX].ObjectPtr = 0;
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
+ {
+ if ((lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ lpEvalDesc->MoveState = AVOID;
+ else
+ lpEvalDesc->ObjectPtr = 0;
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+ }
+ else
+ {
+ STARSHIP *pEnemyStarShip = NULL;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &pEnemyStarShip);
+ if (lpEvalDesc->which_turn <= 16
+ && (StarShipPtr->special_counter > 0
+ || StarShipPtr->RaceDescPtr->ship_info.energy_level < MAX_ENERGY / 3
+ || ((WEAPON_RANGE (&pEnemyStarShip->RaceDescPtr->cyborg_control) <= CLOSE_RANGE_WEAPON
+ && lpEvalDesc->ObjectPtr->crew_level > BLAZER_DAMAGE)
+ || (lpEvalDesc->ObjectPtr->crew_level > (BLAZER_DAMAGE * 3)
+ && MANEUVERABILITY (&pEnemyStarShip->RaceDescPtr->cyborg_control) > SLOW_SHIP))))
+ lpEvalDesc->MoveState = ENTICE;
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if ((ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 4)
+ || (lpEvalDesc->ObjectPtr
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >= MAX_ENERGY / 3
+ && (WEAPON_RANGE (&pEnemyStarShip->RaceDescPtr->cyborg_control) >=
+ WEAPON_RANGE (&StarShipPtr->RaceDescPtr->cyborg_control) << 1
+ || (lpEvalDesc->which_turn < 16
+ && (WEAPON_RANGE (&pEnemyStarShip->RaceDescPtr->cyborg_control) > CLOSE_RANGE_WEAPON
+ || lpEvalDesc->ObjectPtr->crew_level <= BLAZER_DAMAGE)
+ && (lpEvalDesc->ObjectPtr->crew_level <= (BLAZER_DAMAGE * 3)
+ || MANEUVERABILITY (&pEnemyStarShip->RaceDescPtr->cyborg_control) <=
+ SLOW_SHIP)))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+
+ if (!(StarShipPtr->ship_input_state & SPECIAL)
+ && StarShipPtr->weapon_counter == 0
+ && lpEvalDesc->ObjectPtr)
+ {
+ if (lpEvalDesc->which_turn <= 4)
+ StarShipPtr->ship_input_state |= WEAPON;
+ else if (lpEvalDesc->MoveState != PURSUE
+ && lpEvalDesc->which_turn <= 12)
+ {
+ COUNT travel_facing, direction_facing;
+ SIZE delta_x, delta_y,
+ ship_delta_x, ship_delta_y,
+ other_delta_x, other_delta_y;
+
+ GetCurrentVelocityComponents (&ShipPtr->velocity,
+ &ship_delta_x, &ship_delta_y);
+ GetCurrentVelocityComponents (&lpEvalDesc->ObjectPtr->velocity,
+ &other_delta_x, &other_delta_y);
+ delta_x = ship_delta_x - other_delta_x;
+ delta_y = ship_delta_y - other_delta_y;
+ travel_facing = ARCTAN (delta_x, delta_y);
+
+ delta_x =
+ lpEvalDesc->ObjectPtr->next.location.x -
+ ShipPtr->next.location.x;
+ delta_y =
+ lpEvalDesc->ObjectPtr->next.location.y -
+ ShipPtr->next.location.y;
+ direction_facing = ARCTAN (delta_x, delta_y);
+
+ if (NORMALIZE_ANGLE (travel_facing
+ - direction_facing + OCTANT) <= QUADRANT)
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+ }
+ }
+}
+
+static void
+androsynth_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ /* take care of blazer effect */
+ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ || StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ {
+ StarShipPtr->RaceDescPtr->characteristics.energy_regeneration =
+ (BYTE)BLAZER_DEGENERATION;
+ StarShipPtr->energy_counter = ENERGY_WAIT;
+
+ if (StarShipPtr->cur_status_flags & SPECIAL)
+ {
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
+ ElementPtr); /* COMET_ON */
+ ElementPtr->turn_wait = 0;
+ ElementPtr->thrust_wait = 0;
+ StarShipPtr->RaceDescPtr->characteristics.special_wait =
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait;
+ ElementPtr->mass_points = BLAZER_MASS;
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait
+ = BLAZER_TURN_WAIT;
+
+ /* Save the current collision func because we were not the
+ * ones who set it */
+ {
+ const ANDROSYNTH_DATA shipData = { ElementPtr->collision_func };
+ SetCustomShipData (StarShipPtr->RaceDescPtr, &shipData);
+ ElementPtr->collision_func = blazer_collision;
+ }
+ }
+ }
+
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ /* if blazer wasn't able to change back into ship
+ * give it a little more juice to try to get out
+ * of its predicament.
+ */
+ {
+ DeltaEnergy (ElementPtr, -BLAZER_DEGENERATION);
+ StarShipPtr->energy_counter = 1;
+ }
+ }
+}
+
+static void
+androsynth_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ STATUS_FLAGS cur_status_flags;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ cur_status_flags = StarShipPtr->cur_status_flags;
+ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
+ {
+ if (cur_status_flags & SPECIAL)
+ {
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level < SPECIAL_ENERGY_COST)
+ DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST); /* so text will flash */
+ else
+ {
+ cur_status_flags &= ~WEAPON;
+
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ }
+ }
+ else
+ {
+ cur_status_flags &= ~(THRUST | WEAPON | SPECIAL);
+
+ /* protection against vux */
+ if (StarShipPtr->RaceDescPtr->characteristics.turn_wait > BLAZER_TURN_WAIT)
+ {
+ StarShipPtr->RaceDescPtr->characteristics.special_wait +=
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait
+ - BLAZER_TURN_WAIT;
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait = BLAZER_TURN_WAIT;
+ }
+
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ cur_status_flags &= ~(LEFT | RIGHT
+ | SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ StarShipPtr->RaceDescPtr->characteristics.energy_regeneration = ENERGY_REGENERATION;
+ ElementPtr->mass_points = SHIP_MASS;
+ ElementPtr->collision_func =
+ GetCustomShipData (StarShipPtr->RaceDescPtr)->collision_func;
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ else
+ {
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else
+ {
+ COUNT facing;
+
+ facing = StarShipPtr->ShipFacing;
+ if (ElementPtr->turn_wait == 0
+ && (cur_status_flags & (LEFT | RIGHT)))
+ {
+ if (cur_status_flags & LEFT)
+ --facing;
+ else
+ ++facing;
+ }
+
+ SetVelocityVector (&ElementPtr->velocity,
+ BLAZER_THRUST, NORMALIZE_FACING (facing));
+ cur_status_flags |= SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED;
+ }
+ }
+ }
+ StarShipPtr->cur_status_flags = cur_status_flags;
+}
+
+static void
+uninit_androsynth (RACE_DESC *pRaceDesc)
+{
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+
+RACE_DESC*
+init_androsynth (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ androsynth_desc.uninit_func = uninit_androsynth;
+ androsynth_desc.preprocess_func = androsynth_preprocess;
+ androsynth_desc.postprocess_func = androsynth_postprocess;
+ androsynth_desc.init_weapon_func = initialize_bubble;
+ androsynth_desc.cyborg_control.intelligence_func = androsynth_intelligence;
+
+ RaceDescPtr = &androsynth_desc;
+
+ return (RaceDescPtr);
+}
+