summaryrefslogtreecommitdiff
path: root/src/uqm/ships/lastbat/lastbat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ships/lastbat/lastbat.c')
-rw-r--r--src/uqm/ships/lastbat/lastbat.c926
1 files changed, 926 insertions, 0 deletions
diff --git a/src/uqm/ships/lastbat/lastbat.c b/src/uqm/ships/lastbat/lastbat.c
new file mode 100644
index 0000000..9d44742
--- /dev/null
+++ b/src/uqm/ships/lastbat/lastbat.c
@@ -0,0 +1,926 @@
+//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 "lastbat.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/globdata.h"
+#include "uqm/battle.h"
+ // For BATTLE_FRAME_RATE
+#include "libs/mathlib.h"
+#include "libs/timelib.h"
+
+#define num_generators characteristics.max_thrust
+
+// Core characteristics
+#define MAX_CREW 1
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 6
+#define MAX_THRUST 0
+#define THRUST_INCREMENT 0
+#define TURN_WAIT 0
+#define THRUST_WAIT 0
+#define SHIP_MASS (MAX_SHIP_MASS * 10)
+#define TURRET_WAIT 0 /* Controls animation of the Sa-Matra's central
+ * 'furnace', a new frame is displayed once every
+ * TURRET_WAIT frames. */
+
+// Yellow comet
+#define WEAPON_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 10)
+#define COMET_DAMAGE 2
+#define COMET_OFFSET 0
+#define COMET_HITS 12
+#define COMET_SPEED DISPLAY_TO_WORLD (12)
+#define COMET_LIFE 2
+#define COMET_TURN_WAIT 3
+#define MAX_COMETS 3
+#define WEAPON_ENERGY_COST 2
+ /* Used for samatra_desc.weapon_energy_cost, but the value isn't
+ * actually used. */
+
+// Green sentinel
+#define SPECIAL_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 3)
+#define SENTINEL_SPEED DISPLAY_TO_WORLD (8)
+#define SENTINEL_LIFE 2
+#define SENTINEL_OFFSET 0
+#define SENTINEL_HITS 10
+#define SENTINEL_DAMAGE 1
+#define TRACK_WAIT 1
+#define ANIMATION_WAIT 1
+#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (10))
+#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4)
+#define MAX_SENTINELS 4
+#define SPECIAL_ENERGY_COST 3
+ /* Used for samatra_desc.special_energy_cost, but the value isn't
+ * actually used. */
+
+// Blue force field
+#define GATE_DAMAGE 1
+#define GATE_HITS 100
+
+// Red generators
+#define GENERATOR_HITS 15
+#define MAX_GENERATORS 8
+
+static RACE_DESC samatra_desc =
+{
+ { /* SHIP_INFO */
+ /* FIRES_FORE | */ IMMEDIATE_WEAPON | CREW_IMMUNE,
+ 16, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ 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,
+ },
+ {
+ {
+ SAMATRA_BIG_MASK_ANIM,
+ SAMATRA_MED_MASK_PMAP_ANIM,
+ SAMATRA_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SENTINEL_BIG_MASK_ANIM,
+ SENTINEL_MED_MASK_PMAP_ANIM,
+ SENTINEL_SML_MASK_PMAP_ANIM,
+ },
+ {
+ GENERATOR_BIG_MASK_ANIM,
+ GENERATOR_MED_MASK_PMAP_ANIM,
+ GENERATOR_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SAMATRA_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ NULL_RESOURCE,
+ SAMATRA_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ 0,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static HELEMENT spawn_comet (ELEMENT *ElementPtr);
+
+static void
+comet_preprocess (ELEMENT *ElementPtr)
+{
+ COUNT frame_index;
+
+ frame_index = GetFrameIndex (ElementPtr->current.image.frame) + 1;
+ if (frame_index < 29)
+ {
+ if (frame_index == 25)
+ {
+ SIZE cur_delta_x, cur_delta_y;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->RaceDescPtr->characteristics.weapon_wait;
+ spawn_comet (ElementPtr);
+ ElementPtr->state_flags |= NONSOLID;
+
+ GetCurrentVelocityComponents (&ElementPtr->velocity,
+ &cur_delta_x, &cur_delta_y);
+ SetVelocityComponents (&ElementPtr->velocity,
+ cur_delta_x / 2, cur_delta_y / 2);
+ }
+ ++ElementPtr->life_span;
+ }
+
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (
+ ElementPtr->current.image.frame, frame_index
+ );
+ ElementPtr->state_flags |= CHANGING;
+}
+
+static void
+comet_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->playerNr == RPG_PLAYER_NUM)
+ {
+ BYTE old_hits;
+ COUNT old_life;
+ HELEMENT hBlastElement;
+
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ ElementPtr0->mass_points = COMET_DAMAGE;
+ else
+ ElementPtr0->mass_points = 50;
+
+ old_hits = ElementPtr0->hit_points;
+ old_life = ElementPtr0->life_span;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ ElementPtr0->hit_points = old_hits;
+ ElementPtr0->life_span = old_life;
+ ElementPtr0->state_flags &= ~(DISAPPEARING | NONSOLID | COLLISION);
+
+ if (hBlastElement)
+ {
+ RemoveElement (hBlastElement);
+ FreeElement (hBlastElement);
+ }
+ }
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ --StarShipPtr->RaceDescPtr->characteristics.weapon_wait;
+ }
+ }
+}
+
+static HELEMENT
+spawn_comet (ELEMENT *ElementPtr)
+{
+ MISSILE_BLOCK MissileBlock;
+ HELEMENT hComet;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = 0;
+ MissileBlock.index = 24;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = 0;
+ MissileBlock.speed = 0;
+ MissileBlock.hit_points = COMET_HITS;
+ MissileBlock.damage = COMET_DAMAGE;
+ MissileBlock.life = COMET_LIFE;
+ MissileBlock.preprocess_func = comet_preprocess;
+ MissileBlock.blast_offs = COMET_OFFSET;
+ hComet = initialize_missile (&MissileBlock);
+
+ if (hComet)
+ {
+ ELEMENT *CometPtr;
+
+ PutElement (hComet);
+
+ LockElement (hComet, &CometPtr);
+ CometPtr->collision_func = comet_collision;
+ SetElementStarShip (CometPtr, StarShipPtr);
+ {
+ COUNT facing;
+
+ CometPtr->turn_wait = ElementPtr->turn_wait;
+ CometPtr->hTarget = ElementPtr->hTarget;
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ CometPtr->turn_wait = 0;
+ facing = (COUNT)TFB_Random ();
+ SetVelocityVector (&CometPtr->velocity,
+ COMET_SPEED, facing);
+ }
+ else
+ {
+ CometPtr->velocity = ElementPtr->velocity;
+ CometPtr->hit_points = ElementPtr->hit_points;
+ facing = ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&CometPtr->velocity)
+ );
+ }
+
+ if (CometPtr->turn_wait)
+ --CometPtr->turn_wait;
+ else
+ {
+ facing = NORMALIZE_FACING (facing);
+ if (TrackShip (CometPtr, &facing) > 0)
+ SetVelocityVector (&CometPtr->velocity,
+ COMET_SPEED, facing);
+ CometPtr->turn_wait = COMET_TURN_WAIT;
+ }
+ }
+ UnlockElement (hComet);
+ }
+
+ return (hComet);
+}
+
+static void
+turret_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) % 10) + 1);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = TURRET_WAIT;
+ }
+}
+
+static void
+gate_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->playerNr == RPG_PLAYER_NUM)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->num_generators == 0)
+ {
+ if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ ElementPtr0->state_flags |= COLLISION;
+
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && GetPrimType (
+ &GLOBAL (DisplayArray[ElementPtr0->PrimIndex])
+ ) == STAMPFILL_PRIM
+ && GET_GAME_STATE (BOMB_CARRIER))
+ {
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ }
+ }
+ else
+ {
+ HELEMENT hBlastElement;
+
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ ElementPtr0->mass_points = GATE_DAMAGE;
+ else
+ ElementPtr0->mass_points = 50;
+
+ ElementPtr0->hit_points = GATE_HITS;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->state_flags &= ~(DISAPPEARING | NONSOLID | COLLISION);
+ ElementPtr0->life_span = NORMAL_LIFE;
+
+ if (hBlastElement)
+ {
+ RemoveElement (hBlastElement);
+ FreeElement (hBlastElement);
+ }
+ }
+ }
+}
+
+static void
+gate_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->num_generators == 0)
+ {
+ ElementPtr->mass_points = SHIP_MASS;
+ ElementPtr->state_flags &= ~FINITE_LIFE;
+ ElementPtr->life_span = NORMAL_LIFE + 1;
+ ElementPtr->preprocess_func = 0;
+ SetPrimColor (
+ &GLOBAL (DisplayArray[ElementPtr->PrimIndex]),
+ BLACK_COLOR
+ );
+ SetPrimType (
+ &GLOBAL (DisplayArray[ElementPtr->PrimIndex]),
+ STAMPFILL_PRIM
+ );
+ }
+ else
+ {
+ ++ElementPtr->life_span;
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ if (GetFrameIndex (ElementPtr->next.image.frame) == 0)
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (
+ ElementPtr->next.image.frame, 11
+ );
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void
+generator_death (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & FINITE_LIFE))
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ --StarShipPtr->RaceDescPtr->num_generators;
+ ElementPtr->state_flags |= FINITE_LIFE | NONSOLID;
+ ElementPtr->preprocess_func = 0;
+ ElementPtr->turn_wait = 12;
+ ElementPtr->thrust_wait = 0;
+
+ ElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, 10 - 1);
+ }
+
+ if (ElementPtr->thrust_wait)
+ {
+ --ElementPtr->thrust_wait;
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ ++ElementPtr->life_span;
+ }
+ else if (ElementPtr->turn_wait--)
+ {
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ ++ElementPtr->life_span;
+
+ ElementPtr->next.image.frame = IncFrameIndex (
+ ElementPtr->current.image.frame
+ );
+
+ ElementPtr->thrust_wait = 1;
+ }
+}
+
+static void
+generator_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else if ((ElementPtr->turn_wait =
+ (BYTE)((GENERATOR_HITS
+ - ElementPtr->hit_points) / 5)) < 3)
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) + 1) % 10);
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void
+generator_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ {
+ ElementPtr0->state_flags |= COLLISION;
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+sentinel_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ ++ElementPtr->life_span;
+
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) + 1) % 6);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->thrust_wait = ANIMATION_WAIT;
+ }
+
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT facing;
+ HELEMENT hTarget;
+
+ if (!(ElementPtr->state_flags & NONSOLID))
+ facing = ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)
+ );
+ else
+ {
+ ElementPtr->state_flags &= ~NONSOLID;
+ facing = (COUNT)TFB_Random ();
+ SetVelocityVector (&ElementPtr->velocity,
+ SENTINEL_SPEED, facing);
+ }
+ facing = NORMALIZE_FACING (facing);
+ if (ElementPtr->hTarget == 0)
+ {
+ COUNT f;
+
+ f = facing;
+ TrackShip (ElementPtr, &f);
+ }
+
+ if (ElementPtr->hTarget == 0)
+ hTarget = StarShipPtr->hShip;
+ else if (StarShipPtr->hShip == 0)
+ hTarget = ElementPtr->hTarget;
+ else
+ {
+ SIZE delta_x0, delta_y0, delta_x1, delta_y1;
+ ELEMENT *ShipPtr;
+ ELEMENT *EnemyShipPtr;
+
+ LockElement (ElementPtr->hTarget, &EnemyShipPtr);
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ delta_x0 = ShipPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y0 = ShipPtr->current.location.y
+ - ElementPtr->current.location.y;
+
+ delta_x1 = ShipPtr->current.location.x
+ - EnemyShipPtr->current.location.x;
+ delta_y1 = ShipPtr->current.location.y
+ - EnemyShipPtr->current.location.y;
+ UnlockElement (StarShipPtr->hShip);
+
+ if ((long)delta_x0 * delta_x0
+ + (long)delta_y0 * delta_y0 >
+ (long)delta_x1 * delta_x1
+ + (long)delta_y1 * delta_y1)
+ hTarget = StarShipPtr->hShip;
+ else
+ hTarget = ElementPtr->hTarget;
+
+ UnlockElement (ElementPtr->hTarget);
+ }
+
+ if (hTarget)
+ {
+ COUNT num_frames;
+ SIZE delta_x, delta_y;
+ ELEMENT *TargetPtr;
+ VELOCITY_DESC TargetVelocity;
+
+ LockElement (hTarget, &TargetPtr);
+
+ delta_x = TargetPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = TargetPtr->current.location.y
+ - ElementPtr->current.location.y;
+ delta_y = WRAP_DELTA_Y (delta_y);
+
+ if ((num_frames = WORLD_TO_TURN (
+ square_root ((long)delta_x * delta_x
+ + (long)delta_y * delta_y)
+ )) == 0)
+ num_frames = 1;
+
+ TargetVelocity = TargetPtr->velocity;
+ GetNextVelocityComponents (&TargetVelocity,
+ &delta_x, &delta_y, num_frames);
+
+ delta_x = (TargetPtr->current.location.x + delta_x)
+ - ElementPtr->current.location.x;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = (TargetPtr->current.location.y + delta_y)
+ - ElementPtr->current.location.y;
+ delta_y = WRAP_DELTA_Y (delta_y);
+
+ UnlockElement (hTarget);
+
+ delta_x = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) - facing
+ );
+
+ if (delta_x > 0)
+ {
+ if (delta_x <= ANGLE_TO_FACING (HALF_CIRCLE))
+ ++facing;
+ else
+ --facing;
+ }
+
+ SetVelocityVector (&ElementPtr->velocity,
+ SENTINEL_SPEED, facing);
+ }
+
+ ElementPtr->turn_wait = TRACK_WAIT;
+ }
+}
+
+static void
+sentinel_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ COUNT angle;
+ STARSHIP *StarShipPtr;
+
+ if (ElementPtr1->playerNr == NPC_PLAYER_NUM)
+ {
+ if (ElementPtr0->preprocess_func == ElementPtr1->preprocess_func
+ && !(ElementPtr0->state_flags & DEFY_PHYSICS)
+ && (pPt0->x != ElementPtr0->IntersectControl.IntersectStamp.origin.x
+ || pPt0->y != ElementPtr0->IntersectControl.IntersectStamp.origin.y))
+ {
+ angle = ARCTAN (pPt0->x - pPt1->x, pPt0->y - pPt1->y);
+
+ SetVelocityComponents (&ElementPtr0->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (SENTINEL_SPEED)),
+ SINE (angle, WORLD_TO_VELOCITY (SENTINEL_SPEED)));
+ ElementPtr0->turn_wait = TRACK_WAIT;
+ ElementPtr0->state_flags |= COLLISION | DEFY_PHYSICS;
+ }
+ }
+ else
+ {
+ BYTE old_hits;
+ COUNT old_life;
+ HELEMENT hBlastElement;
+
+ old_hits = ElementPtr0->hit_points;
+ old_life = ElementPtr0->life_span;
+ ElementPtr0->blast_offset = 0;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->thrust_wait = 0;
+
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && ElementPtr1->crew_level
+ && !GRAVITY_MASS (ElementPtr1->mass_points + 1))
+ {
+ SIZE cur_delta_x, cur_delta_y;
+
+ ElementPtr0->life_span = old_life;
+ ElementPtr0->hit_points = old_hits;
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+ ElementPtr0->state_flags |= DEFY_PHYSICS;
+ ElementPtr0->turn_wait = (ONE_SECOND / BATTLE_FRAME_RATE) >> 1;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ if (ElementPtr1->turn_wait < COLLISION_TURN_WAIT)
+ ElementPtr1->turn_wait += COLLISION_TURN_WAIT;
+ if (ElementPtr1->thrust_wait < COLLISION_THRUST_WAIT)
+ ElementPtr1->thrust_wait += COLLISION_THRUST_WAIT;
+
+ angle = GetVelocityTravelAngle (&ElementPtr0->velocity);
+ DeltaVelocityComponents (&ElementPtr1->velocity,
+ COSINE (angle, RECOIL_VELOCITY),
+ SINE (angle, RECOIL_VELOCITY));
+ GetCurrentVelocityComponents (&ElementPtr1->velocity,
+ &cur_delta_x, &cur_delta_y);
+ if ((long)cur_delta_x * (long)cur_delta_x
+ + (long)cur_delta_y * (long)cur_delta_y
+ > (long)MAX_RECOIL_VELOCITY * (long)MAX_RECOIL_VELOCITY)
+ {
+ angle = ARCTAN (cur_delta_x, cur_delta_y);
+ SetVelocityComponents (&ElementPtr1->velocity,
+ COSINE (angle, MAX_RECOIL_VELOCITY),
+ SINE (angle, MAX_RECOIL_VELOCITY));
+ }
+
+ ZeroVelocityComponents (&ElementPtr0->velocity);
+ }
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ --StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ if (hBlastElement)
+ {
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+ BlastElementPtr->life_span = 6;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (
+ BlastElementPtr->current.image.farray[0], 6
+ );
+ UnlockElement (hBlastElement);
+ }
+ }
+ }
+}
+
+static void
+samatra_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+}
+
+static void
+samatra_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->num_generators)
+ {
+ if (StarShipPtr->weapon_counter == 0
+ && StarShipPtr->RaceDescPtr->characteristics.weapon_wait < MAX_COMETS
+ && spawn_comet (ElementPtr))
+ {
+ StarShipPtr->weapon_counter = WEAPON_WAIT;
+ }
+
+ if (StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->characteristics.special_wait < MAX_SENTINELS)
+ {
+ MISSILE_BLOCK MissileBlock;
+ HELEMENT hSentinel;
+
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = 0;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = 0;
+ MissileBlock.pixoffs = 0;
+ MissileBlock.speed = SENTINEL_SPEED;
+ MissileBlock.hit_points = SENTINEL_HITS;
+ MissileBlock.damage = SENTINEL_DAMAGE;
+ MissileBlock.life = SENTINEL_LIFE;
+ MissileBlock.preprocess_func = sentinel_preprocess;
+ MissileBlock.blast_offs = SENTINEL_OFFSET;
+ hSentinel = initialize_missile (&MissileBlock);
+
+ if (hSentinel)
+ {
+ ELEMENT *SentinelPtr;
+
+ LockElement (hSentinel, &SentinelPtr);
+ SentinelPtr->collision_func = sentinel_collision;
+ SentinelPtr->turn_wait = TRACK_WAIT + 2;
+ SetElementStarShip (SentinelPtr, StarShipPtr);
+ UnlockElement (hSentinel);
+
+ StarShipPtr->special_counter = SPECIAL_WAIT;
+
+ PutElement (hSentinel);
+ }
+ }
+ }
+}
+
+static void
+samatra_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->RaceDescPtr->characteristics.weapon_wait = 0;
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ ++ElementPtr->turn_wait;
+ ++ElementPtr->thrust_wait;
+ }
+ else
+ {
+ POINT offs[] =
+ {
+ {-127-9, -53+18},
+ { -38-9, -88+18},
+ { 44-9, -85+18},
+ { 127-9, -60+18},
+ { 124-9, 28+18},
+ { 73-9, 61+18},
+ { -87-9, 58+18},
+ {-136-9, 29+18},
+ };
+
+ for (StarShipPtr->RaceDescPtr->num_generators = 0;
+ StarShipPtr->RaceDescPtr->num_generators < MAX_GENERATORS;
+ ++StarShipPtr->RaceDescPtr->num_generators)
+ {
+ HELEMENT hGenerator;
+
+ hGenerator = AllocElement ();
+ if (hGenerator)
+ {
+ ELEMENT *GeneratorPtr;
+
+ LockElement (hGenerator, &GeneratorPtr);
+ GeneratorPtr->hit_points = GENERATOR_HITS;
+ GeneratorPtr->mass_points = MAX_SHIP_MASS * 10;
+ GeneratorPtr->life_span = NORMAL_LIFE;
+ GeneratorPtr->playerNr = ElementPtr->playerNr;
+ GeneratorPtr->state_flags = APPEARING | IGNORE_SIMILAR;
+ SetPrimType (
+ &GLOBAL (DisplayArray[GeneratorPtr->PrimIndex]),
+ STAMP_PRIM
+ );
+ GeneratorPtr->current.location.x =
+ ((LOG_SPACE_WIDTH >> 1)
+ + DISPLAY_TO_WORLD (offs[StarShipPtr->RaceDescPtr->num_generators].x))
+ & ~((SCALED_ONE << MAX_VIS_REDUCTION) - 1);
+ GeneratorPtr->current.location.y =
+ ((LOG_SPACE_HEIGHT >> 1)
+ + DISPLAY_TO_WORLD (offs[StarShipPtr->RaceDescPtr->num_generators].y))
+ & ~((SCALED_ONE << MAX_VIS_REDUCTION) - 1);
+ GeneratorPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ GeneratorPtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ (BYTE)TFB_Random () % 10
+ );
+
+ GeneratorPtr->preprocess_func = generator_preprocess;
+ GeneratorPtr->collision_func = generator_collision;
+ GeneratorPtr->death_func = generator_death;
+
+ SetElementStarShip (GeneratorPtr, StarShipPtr);
+ UnlockElement (hGenerator);
+
+ InsertElement (hGenerator, GetHeadElement ());
+ }
+ }
+
+ {
+ HELEMENT hTurret;
+
+ hTurret = AllocElement ();
+ if (hTurret)
+ {
+ ELEMENT *TurretPtr;
+
+ LockElement (hTurret, &TurretPtr);
+ TurretPtr->hit_points = 1;
+ TurretPtr->life_span = NORMAL_LIFE;
+ TurretPtr->playerNr = ElementPtr->playerNr;
+ TurretPtr->state_flags = APPEARING | IGNORE_SIMILAR | NONSOLID;
+ SetPrimType (
+ &GLOBAL (DisplayArray[TurretPtr->PrimIndex]),
+ STAMP_PRIM
+ );
+ TurretPtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ TurretPtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ TurretPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ TurretPtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship[0], 1
+ );
+
+ TurretPtr->preprocess_func = turret_preprocess;
+
+ SetElementStarShip (TurretPtr, StarShipPtr);
+ UnlockElement (hTurret);
+
+ InsertElement (hTurret, GetSuccElement (ElementPtr));
+ }
+ }
+
+ {
+ HELEMENT hGate;
+
+ hGate = AllocElement ();
+ if (hGate)
+ {
+ ELEMENT *GatePtr;
+
+ LockElement (hGate, &GatePtr);
+ GatePtr->hit_points = GATE_HITS;
+ GatePtr->mass_points = GATE_DAMAGE;
+ GatePtr->life_span = 2;
+ GatePtr->playerNr = ElementPtr->playerNr;
+ GatePtr->state_flags = APPEARING | FINITE_LIFE
+ | IGNORE_SIMILAR;
+ SetPrimType (
+ &GLOBAL (DisplayArray[GatePtr->PrimIndex]),
+ STAMP_PRIM
+ );
+ GatePtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ GatePtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ GatePtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ GatePtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship[0], 11
+ );
+
+ GatePtr->preprocess_func = gate_preprocess;
+ GatePtr->collision_func = gate_collision;
+
+ SetElementStarShip (GatePtr, StarShipPtr);
+ UnlockElement (hGate);
+
+ InsertElement (hGate, GetSuccElement (ElementPtr));
+ }
+ }
+
+ StarShipPtr->weapon_counter = WEAPON_WAIT >> 1;
+ StarShipPtr->special_counter = SPECIAL_WAIT >> 1;
+ }
+}
+
+RACE_DESC*
+init_samatra (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ samatra_desc.preprocess_func = samatra_preprocess;
+ samatra_desc.postprocess_func = samatra_postprocess;
+ samatra_desc.cyborg_control.intelligence_func = samatra_intelligence;
+
+ RaceDescPtr = &samatra_desc;
+
+ return (RaceDescPtr);
+}
+