summaryrefslogtreecommitdiff
path: root/src/uqm/ships/vux/vux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ships/vux/vux.c')
-rw-r--r--src/uqm/ships/vux/vux.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/uqm/ships/vux/vux.c b/src/uqm/ships/vux/vux.c
new file mode 100644
index 0000000..83e6c47
--- /dev/null
+++ b/src/uqm/ships/vux/vux.c
@@ -0,0 +1,398 @@
+//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 "vux.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 40
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 8
+#define MAX_THRUST /* DISPLAY_TO_WORLD (5) */ 21
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 7
+#define THRUST_WAIT 4
+#define TURN_WAIT 6
+#define SHIP_MASS 6
+
+// Laser
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define VUX_OFFSET 12
+#define LASER_BASE 150
+#define LASER_RANGE DISPLAY_TO_WORLD (LASER_BASE + VUX_OFFSET)
+
+// Limpet
+#define SPECIAL_ENERGY_COST 2
+#define SPECIAL_WAIT 7
+#define LIMPET_SPEED 25
+#define LIMPET_OFFSET 8
+#define LIMPET_LIFE 80
+#define LIMPET_HITS 1
+#define LIMPET_DAMAGE 0
+#define MIN_THRUST_INCREMENT DISPLAY_TO_WORLD (1)
+
+// Aggressive Entry
+#define WARP_OFFSET 46
+ /* How far outside of the laser range can the ship warp in. */
+#define MAXX_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1)
+#define MAXY_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1)
+ /* Originally, the warp distance was:
+ * DISPLAY_TO_WORLD (SPACE_HEIGHT << 1)
+ * where SPACE_HEIGHT = SCREEN_HEIGHT - (SAFE_Y * 2)
+ * But in reality this should be relative to the laser-range. */
+
+static RACE_DESC vux_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_SPECIAL | IMMEDIATE_WEAPON,
+ 12, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ VUX_RACE_STRINGS,
+ VUX_ICON_MASK_PMAP_ANIM,
+ VUX_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 900 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 4412, 1558,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ VUX_BIG_MASK_PMAP_ANIM,
+ VUX_MED_MASK_PMAP_ANIM,
+ VUX_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SLIME_MASK_PMAP_ANIM,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ LIMPETS_BIG_MASK_PMAP_ANIM,
+ LIMPETS_MED_MASK_PMAP_ANIM,
+ LIMPETS_SML_MASK_PMAP_ANIM,
+ },
+ {
+ VUX_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ VUX_VICTORY_SONG,
+ VUX_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+
+static void
+limpet_preprocess (ELEMENT *ElementPtr)
+{
+ COUNT facing, orig_facing;
+ SIZE delta_facing;
+
+ facing = orig_facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)
+ ));
+ if ((delta_facing = TrackShip (ElementPtr, &facing)) > 0)
+ {
+ facing = orig_facing + delta_facing;
+ SetVelocityVector (&ElementPtr->velocity, LIMPET_SPEED, facing);
+ }
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->next.image.frame);
+
+ ElementPtr->state_flags |= CHANGING;
+}
+
+static void
+limpet_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ STAMP s;
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ if (++RDPtr->characteristics.turn_wait == 0)
+ --RDPtr->characteristics.turn_wait;
+ if (++RDPtr->characteristics.thrust_wait == 0)
+ --RDPtr->characteristics.thrust_wait;
+ if (RDPtr->characteristics.thrust_increment <= MIN_THRUST_INCREMENT)
+ {
+ RDPtr->characteristics.max_thrust =
+ RDPtr->characteristics.thrust_increment << 1;
+ }
+ else
+ {
+ COUNT num_thrusts;
+
+ num_thrusts = RDPtr->characteristics.max_thrust /
+ RDPtr->characteristics.thrust_increment;
+ --RDPtr->characteristics.thrust_increment;
+ RDPtr->characteristics.max_thrust =
+ RDPtr->characteristics.thrust_increment * num_thrusts;
+ }
+ RDPtr->cyborg_control.ManeuverabilityIndex = 0;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ ProcessSound (SetAbsSoundIndex (
+ /* LIMPET_AFFIXES */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), ElementPtr1);
+ s.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.weapon[0], (COUNT)TFB_Random ()
+ );
+ ModifySilhouette (ElementPtr1, &s, MODIFY_IMAGE);
+ }
+
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ ElementPtr0->state_flags |= COLLISION | DISAPPEARING;
+
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+spawn_limpets (ELEMENT *ElementPtr)
+{
+ HELEMENT Limpet;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ MissileBlock.face = StarShipPtr->ShipFacing + HALF_CIRCLE;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = LIMPET_OFFSET;
+ MissileBlock.speed = LIMPET_SPEED;
+ MissileBlock.hit_points = LIMPET_HITS;
+ MissileBlock.damage = LIMPET_DAMAGE;
+ MissileBlock.life = LIMPET_LIFE;
+ MissileBlock.preprocess_func = limpet_preprocess;
+ MissileBlock.blast_offs = 0;
+
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ Limpet = initialize_missile (&MissileBlock);
+ if (Limpet)
+ {
+ ELEMENT *LimpetPtr;
+
+ LockElement (Limpet, &LimpetPtr);
+ LimpetPtr->collision_func = limpet_collision;
+ SetElementStarShip (LimpetPtr, StarShipPtr);
+ UnlockElement (Limpet);
+
+ PutElement (Limpet);
+ }
+}
+
+static COUNT
+initialize_horrific_laser (ELEMENT *ShipPtr, HELEMENT LaserArray[])
+{
+ STARSHIP *StarShipPtr;
+ LASER_BLOCK LaserBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ LaserBlock.face = StarShipPtr->ShipFacing;
+ LaserBlock.cx = ShipPtr->next.location.x;
+ LaserBlock.cy = ShipPtr->next.location.y;
+ LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = VUX_OFFSET;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x0A), 0x0A);
+ LaserArray[0] = initialize_laser (&LaserBlock);
+
+ return (1);
+}
+
+static void
+vux_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ lpEvalDesc->MoveState = PURSUE;
+ if (ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr != 0
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == ENTICE)
+ {
+ if ((ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr->state_flags
+ & FINITE_LIFE)
+ && !(ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr->state_flags
+ & CREW_OBJECT))
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState = AVOID;
+ else
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState = PURSUE;
+ }
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr != 0
+ && 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;
+}
+
+static void
+vux_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* LAUNCH_LIMPET */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ spawn_limpets (ElementPtr);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+static void
+vux_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ COUNT facing;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ facing = StarShipPtr->ShipFacing;
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER
+ && TrackShip (ElementPtr, &facing) >= 0)
+ {
+ ELEMENT *OtherShipPtr;
+
+ LockElement (ElementPtr->hTarget, &OtherShipPtr);
+
+ do
+ {
+ SIZE dx, dy;
+
+ ElementPtr->current.location.x =
+ (OtherShipPtr->current.location.x -
+ (MAXX_ENTRY_DIST >> 1)) +
+ ((COUNT)TFB_Random () % MAXX_ENTRY_DIST);
+ ElementPtr->current.location.y =
+ (OtherShipPtr->current.location.y -
+ (MAXY_ENTRY_DIST >> 1)) +
+ ((COUNT)TFB_Random () % MAXY_ENTRY_DIST);
+ dx = OtherShipPtr->current.location.x -
+ ElementPtr->current.location.x;
+ dy = OtherShipPtr->current.location.y -
+ ElementPtr->current.location.y;
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (dx, dy))
+ );
+ ElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ facing);
+
+ ElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN (ElementPtr->current.location.x));
+ ElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN (ElementPtr->current.location.y));
+ } while (CalculateGravity (ElementPtr)
+ || TimeSpaceMatterConflict (ElementPtr));
+
+ UnlockElement (ElementPtr->hTarget);
+ ElementPtr->hTarget = 0;
+
+ ElementPtr->next = ElementPtr->current;
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+
+ StarShipPtr->ShipFacing = facing;
+ }
+
+ StarShipPtr->RaceDescPtr->preprocess_func = 0;
+ }
+}
+
+RACE_DESC*
+init_vux (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ vux_desc.preprocess_func = vux_preprocess;
+ vux_desc.postprocess_func = vux_postprocess;
+ vux_desc.init_weapon_func = initialize_horrific_laser;
+ vux_desc.cyborg_control.intelligence_func = vux_intelligence;
+
+ RaceDescPtr = &vux_desc;
+
+ return (RaceDescPtr);
+}
+