diff options
Diffstat (limited to 'src/uqm/ships/orz/orz.c')
-rw-r--r-- | src/uqm/ships/orz/orz.c | 1083 |
1 files changed, 1083 insertions, 0 deletions
diff --git a/src/uqm/ships/orz/orz.c b/src/uqm/ships/orz/orz.c new file mode 100644 index 0000000..1a95fec --- /dev/null +++ b/src/uqm/ships/orz/orz.c @@ -0,0 +1,1083 @@ +//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 "orz.h" +#include "resinst.h" + +#include "uqm/colors.h" +#include "uqm/globdata.h" +#include "libs/mathlib.h" + +// Core characteristics +#define MAX_CREW 16 +#define MAX_ENERGY 20 +#define ENERGY_REGENERATION 1 +#define ENERGY_WAIT 6 +#define MAX_THRUST 35 +#define THRUST_INCREMENT 5 +#define THRUST_WAIT 0 +#define TURN_WAIT 1 +#define SHIP_MASS 4 + +// Howitzer +#define WEAPON_ENERGY_COST (MAX_ENERGY / 3) +#define WEAPON_WAIT 4 +#define ORZ_OFFSET 9 +#define MISSILE_SPEED DISPLAY_TO_WORLD (30) +#define MISSILE_LIFE 12 +#define MISSILE_HITS 2 +#define MISSILE_DAMAGE 3 +#define MISSILE_OFFSET 1 + +// Marine +#define SPECIAL_ENERGY_COST 0 +#define SPECIAL_WAIT 12 +#define MARINE_MAX_THRUST 32 +#define MARINE_THRUST_INCREMENT 8 +#define MARINE_HIT_POINTS 3 +#define MARINE_MASS_POINTS 1 +#define MAX_MARINES 8 +#define MARINE_WAIT 12 +#define ION_LIFE 1 +#define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A) + +// Rotating Turret +#define TURRET_OFFSET 14 +#define TURRET_WAIT 3 + +static RACE_DESC orz_desc = +{ + { /* SHIP_INFO */ + FIRES_FORE | SEEKING_SPECIAL, + 23, /* Super Melee cost */ + MAX_CREW, MAX_CREW, + MAX_ENERGY, MAX_ENERGY, + ORZ_RACE_STRINGS, + ORZ_ICON_MASK_PMAP_ANIM, + ORZ_MICON_MASK_PMAP_ANIM, + NULL, NULL, NULL + }, + { /* FLEET_STUFF */ + 333 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */ + { /* Known location (center of SoI) */ + 3608, 2637, + }, + }, + { + MAX_THRUST, + THRUST_INCREMENT, + ENERGY_REGENERATION, + WEAPON_ENERGY_COST, + SPECIAL_ENERGY_COST, + ENERGY_WAIT, + TURN_WAIT, + THRUST_WAIT, + WEAPON_WAIT, + SPECIAL_WAIT, + SHIP_MASS, + }, + { + { + ORZ_BIG_MASK_PMAP_ANIM, + ORZ_MED_MASK_PMAP_ANIM, + ORZ_SML_MASK_PMAP_ANIM, + }, + { + HOWITZER_BIG_MASK_PMAP_ANIM, + HOWITZER_MED_MASK_PMAP_ANIM, + HOWITZER_SML_MASK_PMAP_ANIM, + }, + { + TURRET_BIG_MASK_PMAP_ANIM, + TURRET_MED_MASK_PMAP_ANIM, + TURRET_SML_MASK_PMAP_ANIM, + }, + { + ORZ_CAPTAIN_MASK_PMAP_ANIM, + NULL, NULL, NULL, NULL, NULL + }, + ORZ_VICTORY_SONG, + ORZ_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 void +howitzer_collision (ELEMENT *ElementPtr0, POINT *pPt0, + ELEMENT *ElementPtr1, POINT *pPt1) +{ + if (!elementsOfSamePlayer (ElementPtr0, ElementPtr1)) + weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1); +} + +static COUNT +initialize_turret_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[]) +{ + ELEMENT *TurretPtr; + 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; + + LockElement (GetSuccElement (ShipPtr), &TurretPtr); + if (TurretPtr->turn_wait == 0 + && (StarShipPtr->cur_status_flags & SPECIAL) + && (StarShipPtr->cur_status_flags & (LEFT | RIGHT))) + { + if (StarShipPtr->cur_status_flags & RIGHT) + ++TurretPtr->thrust_wait; + else + --TurretPtr->thrust_wait; + + TurretPtr->turn_wait = TURRET_WAIT + 1; + } + MissileBlock.face = MissileBlock.index = + NORMALIZE_FACING (StarShipPtr->ShipFacing + + TurretPtr->thrust_wait); + UnlockElement (GetSuccElement (ShipPtr)); + + MissileBlock.sender = ShipPtr->playerNr; + MissileBlock.flags = IGNORE_SIMILAR; + MissileBlock.pixoffs = TURRET_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; + MissileArray[0] = initialize_missile (&MissileBlock); + + if (MissileArray[0]) + { + ELEMENT *HowitzerPtr; + + LockElement (MissileArray[0], &HowitzerPtr); + HowitzerPtr->collision_func = howitzer_collision; + UnlockElement (MissileArray[0]); + } + + return (1); +} + +static BYTE +count_marines (STARSHIP *StarShipPtr, BOOLEAN FindSpot) +{ + BYTE num_marines, id_use[MAX_MARINES]; + HELEMENT hElement, hNextElement; + + num_marines = MAX_MARINES; + while (num_marines--) + id_use[num_marines] = 0; + + num_marines = 0; + for (hElement = GetTailElement (); hElement; hElement = hNextElement) + { + ELEMENT *ElementPtr; + + LockElement (hElement, &ElementPtr); + hNextElement = GetPredElement (ElementPtr); + if (ElementPtr->current.image.farray == + StarShipPtr->RaceDescPtr->ship_data.special + && ElementPtr->life_span + && !(ElementPtr->state_flags & (FINITE_LIFE | DISAPPEARING))) + { + if (ElementPtr->state_flags & NONSOLID) + { + id_use[ElementPtr->turn_wait] = 1; + } + + if (++num_marines == MAX_MARINES) + { + UnlockElement (hElement); + hNextElement = 0; + } + } + UnlockElement (hElement); + } + + if (FindSpot) + { + num_marines = 0; + while (id_use[num_marines]) + ++num_marines; + } + + return (num_marines); +} + +static void +orz_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, + COUNT ConcernCounter) +{ + ELEMENT *TurretPtr; + STARSHIP *StarShipPtr; + EVALUATE_DESC *lpEvalDesc; + + LockElement (GetSuccElement (ShipPtr), &TurretPtr); + + ++TurretPtr->turn_wait; + ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); + --TurretPtr->turn_wait; + + GetElementStarShip (ShipPtr, &StarShipPtr); + lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; + if (lpEvalDesc->ObjectPtr == 0) + StarShipPtr->ship_input_state &= ~SPECIAL; + else if (StarShipPtr->special_counter != 1) + { + STARSHIP *EnemyStarShipPtr; + + if (ShipPtr->turn_wait == 0 + && lpEvalDesc->MoveState == ENTICE + && lpEvalDesc->which_turn < 24 + && (StarShipPtr->cur_status_flags + & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED)) + && !(StarShipPtr->ship_input_state & THRUST) + && NORMALIZE_ANGLE ( + GetVelocityTravelAngle (&ShipPtr->velocity) + - ARCTAN ( + lpEvalDesc->ObjectPtr->next.location.x + - ShipPtr->next.location.x, + lpEvalDesc->ObjectPtr->next.location.y + - ShipPtr->next.location.y + ) + (QUADRANT - (OCTANT >> 1))) >= + ((QUADRANT - (OCTANT >> 1)) << 1)) + StarShipPtr->ship_input_state &= ~(LEFT | RIGHT); + + StarShipPtr->ship_input_state &= ~SPECIAL; + if (ShipPtr->turn_wait == 0 + && !(StarShipPtr->ship_input_state & (LEFT | RIGHT | WEAPON)) + && TurretPtr->turn_wait == 0) + { + SIZE delta_facing; + COUNT facing;//, orig_facing; + + facing = NORMALIZE_FACING (StarShipPtr->ShipFacing + + TurretPtr->thrust_wait); + if ((delta_facing = TrackShip (TurretPtr, &facing)) > 0) + { + StarShipPtr->ship_input_state |= SPECIAL; + if (delta_facing == ANGLE_TO_FACING (HALF_CIRCLE)) + delta_facing += (((BYTE)TFB_Random () & 1) << 1) - 1; + + if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE)) + StarShipPtr->ship_input_state |= RIGHT; + else + StarShipPtr->ship_input_state |= LEFT; + } + } + + GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr); + if (StarShipPtr->special_counter == 0 + && !(StarShipPtr->ship_input_state & WEAPON) + && StarShipPtr->RaceDescPtr->ship_info.crew_level > + (BYTE)(StarShipPtr->RaceDescPtr->ship_info.max_crew >> 2) + && !(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags + & POINT_DEFENSE) + && (MANEUVERABILITY ( + &EnemyStarShipPtr->RaceDescPtr->cyborg_control + ) < SLOW_SHIP + || lpEvalDesc->which_turn <= 12 + || count_marines (StarShipPtr, FALSE) < 2)) + { + StarShipPtr->ship_input_state |= WEAPON | SPECIAL; + } + } + + UnlockElement (GetSuccElement (ShipPtr)); +} + +static void +ion_preprocess (ELEMENT *ElementPtr) +{ + /* Originally, this table also contained the now commented out + * entries. It then used some if statements to skip over these. + * The current behaviour is the same as the old behavior. + */ + static const Color colorTable[] = + { + BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7a), + //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7b), + BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7c), + //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7d), + //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7e), + BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7f), + + //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x2a), + BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2b), + //BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2c), + BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2d), + //BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2e), + //BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2f), + }; + const size_t colorTabCount = sizeof colorTable / sizeof colorTable[0]; + + ElementPtr->colorCycleIndex++; + if (ElementPtr->colorCycleIndex != colorTabCount) + { + ElementPtr->life_span = ElementPtr->thrust_wait; + + SetPrimColor (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], + colorTable[ElementPtr->colorCycleIndex]); + + ElementPtr->state_flags &= ~DISAPPEARING; + ElementPtr->state_flags |= CHANGING; + } +} + +static void marine_preprocess (ELEMENT *ElementPtr); + +void +intruder_preprocess (ELEMENT *ElementPtr) +{ + HELEMENT hElement, hNextElement; + ELEMENT *ShipPtr; + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + LockElement (StarShipPtr->hShip, &ShipPtr); + if (ShipPtr->crew_level == 0 + && ShipPtr->life_span == 1 + && (ShipPtr->state_flags & (FINITE_LIFE | NONSOLID)) == + (FINITE_LIFE | NONSOLID)) + { + ElementPtr->life_span = 0; + ElementPtr->state_flags |= DISAPPEARING; + } + UnlockElement (StarShipPtr->hShip); + + if (ElementPtr->thrust_wait) + --ElementPtr->thrust_wait; + + for (hElement = GetHeadElement (); hElement; hElement = hNextElement) + { + LockElement (hElement, &ShipPtr); + if ((ShipPtr->state_flags & PLAYER_SHIP) + && !elementsOfSamePlayer (ShipPtr, ElementPtr)) + { + STAMP s; + + if (ElementPtr->thrust_wait == MARINE_WAIT) + { + --ElementPtr->thrust_wait; + + s.origin.x = 16 + (ElementPtr->turn_wait & 3) * 9; + s.origin.y = 14 + (ElementPtr->turn_wait >> 2) * 11; + s.frame = SetAbsFrameIndex (ElementPtr->next.image.farray[0], + GetFrameCount (ElementPtr->next.image.farray[0]) - 2); + ModifySilhouette (ShipPtr, &s, 0); + } + + ElementPtr->next.location = ShipPtr->next.location; + + if (ShipPtr->crew_level == 0 + || ElementPtr->life_span == 0) + { + UnlockElement (hElement); + hElement = 0; +LeftShip: + s.origin.x = 16 + (ElementPtr->turn_wait & 3) * 9; + s.origin.y = 14 + (ElementPtr->turn_wait >> 2) * 11; + s.frame = ElementPtr->next.image.frame; + ModifySilhouette (ShipPtr, &s, MODIFY_SWAP); + } + else if (ElementPtr->thrust_wait == 0) + { + BYTE randval; + + ElementPtr->thrust_wait = MARINE_WAIT; + + randval = (BYTE)TFB_Random (); + if (randval < (0x0100 / 16)) + { + ElementPtr->life_span = 0; + ElementPtr->state_flags |= DISAPPEARING; + + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 4), ElementPtr); + goto LeftShip; + } + else if (randval < (0x0100 / 2 + 0x0100 / 16)) + { + if (!DeltaCrew (ShipPtr, -1)) + ShipPtr->life_span = 0; + + ++ElementPtr->thrust_wait; + s.origin.x = 16 + (ElementPtr->turn_wait & 3) * 9; + s.origin.y = 14 + (ElementPtr->turn_wait >> 2) * 11; + s.frame = SetAbsFrameIndex (ElementPtr->next.image.farray[0], + GetFrameCount (ElementPtr->next.image.farray[0]) - 1); + ModifySilhouette (ShipPtr, &s, 0); + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), ElementPtr); + } + } + + UnlockElement (hElement); + break; + } + hNextElement = GetSuccElement (ShipPtr); + UnlockElement (hElement); + } + + if (hElement == 0 && ElementPtr->life_span) + { + ElementPtr->state_flags &= ~NONSOLID; + ElementPtr->state_flags |= CHANGING | CREW_OBJECT; + SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], + STAMP_PRIM); + + ElementPtr->current.image.frame = + ElementPtr->next.image.frame = + SetAbsFrameIndex ( + StarShipPtr->RaceDescPtr->ship_data.special[0], 21); + ElementPtr->thrust_wait = 0; + ElementPtr->turn_wait = + MAKE_BYTE (0, NORMALIZE_FACING ((BYTE)TFB_Random ())); + ElementPtr->preprocess_func = marine_preprocess; + } +} + +// XXX: merge this with spawn_ion_trail from tactrans.c? +static void +spawn_marine_ion_trail (ELEMENT *ElementPtr, STARSHIP *StarShipPtr, + COUNT facing) +{ + HELEMENT hIonElement; + + hIonElement = AllocElement (); + if (hIonElement) + { + COUNT angle; + ELEMENT *IonElementPtr; + + angle = FACING_TO_ANGLE (facing) + HALF_CIRCLE; + + InsertElement (hIonElement, GetHeadElement ()); + LockElement (hIonElement, &IonElementPtr); + IonElementPtr->playerNr = NEUTRAL_PLAYER_NUM; + IonElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID; + IonElementPtr->thrust_wait = ION_LIFE; + IonElementPtr->life_span = IonElementPtr->thrust_wait; + // When the element "dies", in the death_func + // 'cycle_ion_trail', it is given new life a number of + // times, by setting life_span to thrust_wait. + SetPrimType (&(GLOBAL (DisplayArray))[IonElementPtr->PrimIndex], + POINT_PRIM); + SetPrimColor (&(GLOBAL (DisplayArray))[IonElementPtr->PrimIndex], + START_ION_COLOR); + IonElementPtr->colorCycleIndex = 0; + IonElementPtr->current.location = ElementPtr->current.location; + IonElementPtr->current.location.x += + (COORD)COSINE (angle, DISPLAY_TO_WORLD (2)); + IonElementPtr->current.location.y += + (COORD)SINE (angle, DISPLAY_TO_WORLD (2)); + IonElementPtr->death_func = ion_preprocess; + + SetElementStarShip (IonElementPtr, StarShipPtr); + + { + /* normally done during preprocess, but because + * object is being inserted at head rather than + * appended after tail it may never get preprocessed. + */ + IonElementPtr->next = IonElementPtr->current; + --IonElementPtr->life_span; + IonElementPtr->state_flags |= PRE_PROCESS; + } + + UnlockElement (hIonElement); + } +} + +static void +marine_preprocess (ELEMENT *ElementPtr) +{ + ELEMENT *ShipPtr; + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + LockElement (StarShipPtr->hShip, &ShipPtr); + if (ShipPtr->crew_level == 0 + && ShipPtr->life_span == 1 + && (ShipPtr->state_flags & (FINITE_LIFE | NONSOLID)) == + (FINITE_LIFE | NONSOLID)) + { + ElementPtr->life_span = 0; + ElementPtr->state_flags |= DISAPPEARING | NONSOLID; + ElementPtr->turn_wait = 1; + } + UnlockElement (StarShipPtr->hShip); + + if (LONIBBLE (ElementPtr->turn_wait)) + --ElementPtr->turn_wait; + else + { + COUNT facing, pfacing = 0; + SIZE delta_x, delta_y, delta_facing; + HELEMENT hObject, hNextObject, hTarget; + ELEMENT *ObjectPtr; + + // XXX: thrust_wait is abused to store marine speed and + // gravity well flags + ElementPtr->thrust_wait &= ~(SHIP_IN_GRAVITY_WELL >> 6); + + hTarget = 0; + for (hObject = GetHeadElement (); + hObject; hObject = hNextObject) + { + LockElement (hObject, &ObjectPtr); + hNextObject = GetSuccElement (ObjectPtr); + if (GRAVITY_MASS (ObjectPtr->mass_points)) + { + delta_x = ObjectPtr->current.location.x + - ElementPtr->current.location.x; + delta_x = WRAP_DELTA_X (delta_x); + + delta_y = ObjectPtr->current.location.y + - ElementPtr->current.location.y; + delta_y = WRAP_DELTA_Y (delta_y); + if ((long)delta_x * delta_x + (long)delta_y * delta_y <= + (long)(DISPLAY_TO_WORLD (GRAVITY_THRESHOLD) + * DISPLAY_TO_WORLD (GRAVITY_THRESHOLD))) + { + pfacing = ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)); + delta_facing = NORMALIZE_FACING ( + pfacing - ANGLE_TO_FACING ( + GetVelocityTravelAngle (&ElementPtr->velocity)) + + ANGLE_TO_FACING (OCTANT)); + if (delta_facing <= ANGLE_TO_FACING (QUADRANT)) + { + hTarget = hObject; + hNextObject = 0; + } + + ElementPtr->thrust_wait |= (SHIP_IN_GRAVITY_WELL >> 6); + } + } + else if ((ObjectPtr->state_flags & PLAYER_SHIP) + && ObjectPtr->crew_level + && !OBJECT_CLOAKED (ObjectPtr)) + { + if (!elementsOfSamePlayer (ObjectPtr, ElementPtr)) + { + if (ElementPtr->state_flags & IGNORE_SIMILAR) + hTarget = hObject; + } + else if (hTarget == 0) + hTarget = hObject; + } + UnlockElement (hObject); + } + + facing = HINIBBLE (ElementPtr->turn_wait); + if (hTarget == 0) + delta_facing = -1; + else + { + LockElement (hTarget, &ObjectPtr); + delta_x = ObjectPtr->current.location.x + - ElementPtr->current.location.x; + delta_x = WRAP_DELTA_X (delta_x); + delta_y = ObjectPtr->current.location.y + - ElementPtr->current.location.y; + delta_y = WRAP_DELTA_Y (delta_y); + if (GRAVITY_MASS (ObjectPtr->mass_points)) + { + delta_facing = NORMALIZE_FACING (pfacing - facing + + ANGLE_TO_FACING (OCTANT)); + + if (delta_facing > ANGLE_TO_FACING (QUADRANT)) + delta_facing = 0; + else + { + if (delta_facing == ANGLE_TO_FACING (OCTANT)) + facing += (((SIZE)TFB_Random () & 1) << 1) - 1; + else if (delta_facing < ANGLE_TO_FACING (OCTANT)) + ++facing; + else + --facing; + } + } + else + { + COUNT num_frames; + VELOCITY_DESC ShipVelocity; + + if (elementsOfSamePlayer (ObjectPtr, ElementPtr) + && (ElementPtr->state_flags & IGNORE_SIMILAR)) + { + ElementPtr->next.image.frame = SetAbsFrameIndex ( + StarShipPtr->RaceDescPtr->ship_data.special[0], + 21); + ElementPtr->state_flags &= ~IGNORE_SIMILAR; + ElementPtr->state_flags |= CHANGING; + } + + num_frames = WORLD_TO_TURN ( + square_root ((long)delta_x * delta_x + + (long)delta_y * delta_y)); + if (num_frames == 0) + num_frames = 1; + + ShipVelocity = ObjectPtr->velocity; + GetNextVelocityComponents (&ShipVelocity, + &delta_x, &delta_y, num_frames); + + delta_x = (ObjectPtr->current.location.x + delta_x) + - ElementPtr->current.location.x; + delta_y = (ObjectPtr->current.location.y + delta_y) + - ElementPtr->current.location.y; + + delta_facing = NORMALIZE_FACING ( + ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) - facing); + + if (delta_facing > 0) + { + if (delta_facing == ANGLE_TO_FACING (HALF_CIRCLE)) + facing += (((BYTE)TFB_Random () & 1) << 1) - 1; + else if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE)) + ++facing; + else + --facing; + } + } + UnlockElement (hTarget); + } + + ElementPtr->turn_wait = MAKE_BYTE (0, NORMALIZE_FACING (facing)); + if (delta_facing == 0 + || ((ElementPtr->thrust_wait & (SHIP_BEYOND_MAX_SPEED >> 6)) + && !(ElementPtr->thrust_wait & (SHIP_IN_GRAVITY_WELL >> 6)))) + { + STATUS_FLAGS thrust_status; + COUNT OldFacing; + STATUS_FLAGS OldStatus; + COUNT OldIncrement, OldThrust; + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + + // XXX: Hack: abusing the primary STARSHIP struct in order + // to call inertial_thrust() for a marine + OldFacing = StarShipPtr->ShipFacing; + OldStatus = StarShipPtr->cur_status_flags; + OldIncrement = StarShipPtr->RaceDescPtr->characteristics. + thrust_increment; + OldThrust = StarShipPtr->RaceDescPtr->characteristics.max_thrust; + + StarShipPtr->ShipFacing = facing; + // XXX: thrust_wait is abused to store marine speed and + // gravity well flags + StarShipPtr->cur_status_flags = ElementPtr->thrust_wait << 6; + StarShipPtr->RaceDescPtr->characteristics.thrust_increment = + MARINE_THRUST_INCREMENT; + StarShipPtr->RaceDescPtr->characteristics.max_thrust = + MARINE_MAX_THRUST; + + thrust_status = inertial_thrust (ElementPtr); + + StarShipPtr->RaceDescPtr->characteristics.max_thrust = OldThrust; + StarShipPtr->RaceDescPtr->characteristics.thrust_increment = + OldIncrement; + StarShipPtr->cur_status_flags = OldStatus; + StarShipPtr->ShipFacing = OldFacing; + + if ((ElementPtr->thrust_wait & (SHIP_IN_GRAVITY_WELL >> 6)) + || delta_facing + || !(thrust_status + & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))) + { + spawn_marine_ion_trail (ElementPtr, StarShipPtr, facing); + } + + // XXX: thrust_wait is abused to store marine speed and + // gravity well flags + ElementPtr->thrust_wait = (BYTE)(thrust_status >> 6); + } + } +} + +void +marine_collision (ELEMENT *ElementPtr0, POINT *pPt0, + ELEMENT *ElementPtr1, POINT *pPt1) +{ + if (ElementPtr0->life_span + && !(ElementPtr0->state_flags & (NONSOLID | COLLISION)) + && !(ElementPtr1->state_flags & FINITE_LIFE)) + { + if (!elementsOfSamePlayer (ElementPtr0, ElementPtr1)) + { + ElementPtr0->turn_wait = + MAKE_BYTE (5, HINIBBLE (ElementPtr0->turn_wait)); + ElementPtr0->thrust_wait &= + ~((SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED) >> 6); + ElementPtr0->state_flags |= COLLISION; + } + + if (GRAVITY_MASS (ElementPtr1->mass_points)) + { + ElementPtr0->state_flags |= NONSOLID | FINITE_LIFE; + ElementPtr0->hit_points = 0; + ElementPtr0->life_span = 0; + } + else if ((ElementPtr1->state_flags & PLAYER_SHIP) + && ((ElementPtr1->state_flags & FINITE_LIFE) + || ElementPtr1->life_span == NORMAL_LIFE)) + { + ElementPtr1->state_flags &= ~COLLISION; + + if (!(ElementPtr0->state_flags & COLLISION)) + { + DeltaCrew (ElementPtr1, 1); + + ElementPtr0->state_flags |= + DISAPPEARING | NONSOLID | FINITE_LIFE; + ElementPtr0->hit_points = 0; + ElementPtr0->life_span = 0; + } + else if ((ElementPtr0->state_flags & IGNORE_SIMILAR) + && ElementPtr1->crew_level +#ifdef NEVER + && (BYTE)TFB_Random () <= (0x0100 / 3) +#endif /* NEVER */ + ) + { + STAMP s; + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr0, &StarShipPtr); + if (!DeltaCrew (ElementPtr1, -1)) + ElementPtr1->life_span = 0; + else + { + ElementPtr0->turn_wait = count_marines (StarShipPtr, TRUE); + ElementPtr0->thrust_wait = MARINE_WAIT; + ElementPtr0->next.image.frame = SetAbsFrameIndex ( + ElementPtr0->next.image.farray[0], + 22 + ElementPtr0->turn_wait + ); + ElementPtr0->state_flags |= NONSOLID; + ElementPtr0->state_flags &= ~CREW_OBJECT; + SetPrimType (&(GLOBAL (DisplayArray))[ + ElementPtr0->PrimIndex + ], NO_PRIM); + ElementPtr0->preprocess_func = intruder_preprocess; + + s.origin.x = 16 + (ElementPtr0->turn_wait & 3) * 9; + s.origin.y = 14 + (ElementPtr0->turn_wait >> 2) * 11; + s.frame = ElementPtr0->next.image.frame; + ModifySilhouette (ElementPtr1, &s, 0); + } + + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), + ElementPtr1); + } + + ElementPtr0->state_flags &= ~COLLISION; + } + } + (void) pPt0; /* Satisfying compiler (unused parameter) */ + (void) pPt1; /* Satisfying compiler (unused parameter) */ +} + +static void +animate (ELEMENT *ElementPtr) +{ + if (ElementPtr->turn_wait > 0) + --ElementPtr->turn_wait; + else + { + ElementPtr->next.image.frame = + IncFrameIndex (ElementPtr->current.image.frame); + ElementPtr->state_flags |= CHANGING; + + ElementPtr->turn_wait = ElementPtr->next_turn; + } +} + +static void +turret_postprocess (ELEMENT *ElementPtr) +{ + if (ElementPtr->life_span == 0) + { + STARSHIP *StarShipPtr; + + SetPrimType (&(GLOBAL (DisplayArray))[ + ElementPtr->PrimIndex], NO_PRIM); + + GetElementStarShip (ElementPtr, &StarShipPtr); + if (StarShipPtr->hShip) + { + COUNT facing; + HELEMENT hTurret, hSpaceMarine; + ELEMENT *ShipPtr; + + LockElement (StarShipPtr->hShip, &ShipPtr); + hTurret = AllocElement (); + if (hTurret) + { + ELEMENT *TurretPtr; + + LockElement (hTurret, &TurretPtr); + TurretPtr->playerNr = ElementPtr->playerNr; + TurretPtr->state_flags = FINITE_LIFE | NONSOLID + | IGNORE_SIMILAR | CHANGING | PRE_PROCESS + | POST_PROCESS; + TurretPtr->life_span = 1; + TurretPtr->current.image = ElementPtr->current.image; + TurretPtr->current.location = ShipPtr->next.location; + TurretPtr->turn_wait = ElementPtr->turn_wait; + TurretPtr->thrust_wait = ElementPtr->thrust_wait; + + if (TurretPtr->turn_wait) + --TurretPtr->turn_wait; + else if ((StarShipPtr->cur_status_flags & SPECIAL) + && (StarShipPtr->cur_status_flags & (LEFT | RIGHT))) + { + if (StarShipPtr->cur_status_flags & RIGHT) + ++TurretPtr->thrust_wait; + else + --TurretPtr->thrust_wait; + + TurretPtr->turn_wait = TURRET_WAIT; + } + facing = NORMALIZE_FACING (StarShipPtr->ShipFacing + + TurretPtr->thrust_wait); + StarShipPtr->RaceDescPtr->ship_info.ship_flags &= + ~(FIRES_FORE | FIRES_RIGHT | FIRES_AFT | FIRES_LEFT); + StarShipPtr->RaceDescPtr->ship_info.ship_flags |= FIRES_FORE + << (NORMALIZE_FACING (facing + ANGLE_TO_FACING (OCTANT)) + / ANGLE_TO_FACING (QUADRANT)); + TurretPtr->current.image.frame = SetAbsFrameIndex ( + TurretPtr->current.image.frame, facing); + facing = FACING_TO_ANGLE (facing); + if (StarShipPtr->cur_status_flags & WEAPON) + { + HELEMENT hTurretEffect; + ELEMENT *TurretEffectPtr; + + LockElement (GetTailElement (), &TurretEffectPtr); + if (TurretEffectPtr != ElementPtr + && elementsOfSamePlayer (TurretEffectPtr, ElementPtr) + && (TurretEffectPtr->state_flags & APPEARING) + && GetPrimType (&(GLOBAL (DisplayArray))[ + TurretEffectPtr->PrimIndex + ]) == STAMP_PRIM + && (hTurretEffect = AllocElement ())) + { + TurretPtr->current.location.x -= + COSINE (facing, DISPLAY_TO_WORLD (2)); + TurretPtr->current.location.y -= + SINE (facing, DISPLAY_TO_WORLD (2)); + + LockElement (hTurretEffect, &TurretEffectPtr); + TurretEffectPtr->playerNr = ElementPtr->playerNr; + TurretEffectPtr->state_flags = FINITE_LIFE + | NONSOLID | IGNORE_SIMILAR | APPEARING; + TurretEffectPtr->life_span = 4; + + TurretEffectPtr->current.location.x = + TurretPtr->current.location.x + + COSINE (facing, + DISPLAY_TO_WORLD (TURRET_OFFSET)); + TurretEffectPtr->current.location.y = + TurretPtr->current.location.y + + SINE (facing, + DISPLAY_TO_WORLD (TURRET_OFFSET)); + TurretEffectPtr->current.image.farray = + StarShipPtr->RaceDescPtr->ship_data.special; + TurretEffectPtr->current.image.frame = + SetAbsFrameIndex ( + StarShipPtr->RaceDescPtr->ship_data.special[0], + ANGLE_TO_FACING (FULL_CIRCLE)); + + TurretEffectPtr->preprocess_func = animate; + + SetElementStarShip (TurretEffectPtr, StarShipPtr); + + SetPrimType (&(GLOBAL (DisplayArray))[ + TurretEffectPtr->PrimIndex], STAMP_PRIM); + + UnlockElement (hTurretEffect); + PutElement (hTurretEffect); + } + UnlockElement (GetTailElement ()); + } + TurretPtr->next = TurretPtr->current; + + SetPrimType (&(GLOBAL (DisplayArray))[ + TurretPtr->PrimIndex], + GetPrimType (&(GLOBAL (DisplayArray))[ + ShipPtr->PrimIndex])); + SetPrimColor (&(GLOBAL (DisplayArray))[ + TurretPtr->PrimIndex], + GetPrimColor (&(GLOBAL (DisplayArray))[ + ShipPtr->PrimIndex])); + + TurretPtr->postprocess_func = ElementPtr->postprocess_func; + + SetElementStarShip (TurretPtr, StarShipPtr); + + UnlockElement (hTurret); + InsertElement (hTurret, GetSuccElement (ElementPtr)); + } + + if (StarShipPtr->special_counter == 0 + && (StarShipPtr->cur_status_flags & SPECIAL) + && (StarShipPtr->cur_status_flags & WEAPON) + && ShipPtr->crew_level > 1 + && count_marines (StarShipPtr, FALSE) < MAX_MARINES + && TrackShip (ShipPtr, &facing) >= 0 + && (hSpaceMarine = AllocElement ())) + { + ELEMENT *SpaceMarinePtr; + + LockElement (hSpaceMarine, &SpaceMarinePtr); + SpaceMarinePtr->playerNr = ElementPtr->playerNr; + SpaceMarinePtr->state_flags = IGNORE_SIMILAR | APPEARING + | CREW_OBJECT; + SpaceMarinePtr->life_span = NORMAL_LIFE; + SpaceMarinePtr->hit_points = MARINE_HIT_POINTS; + SpaceMarinePtr->mass_points = MARINE_MASS_POINTS; + + facing = FACING_TO_ANGLE (StarShipPtr->ShipFacing); + SpaceMarinePtr->current.location.x = + ShipPtr->current.location.x + - COSINE (facing, DISPLAY_TO_WORLD (TURRET_OFFSET)); + SpaceMarinePtr->current.location.y = + ShipPtr->current.location.y + - SINE (facing, DISPLAY_TO_WORLD (TURRET_OFFSET)); + SpaceMarinePtr->current.image.farray = + StarShipPtr->RaceDescPtr->ship_data.special; + SpaceMarinePtr->current.image.frame = SetAbsFrameIndex ( + StarShipPtr->RaceDescPtr->ship_data.special[0], 20); + + SpaceMarinePtr->turn_wait = + MAKE_BYTE (0, NORMALIZE_FACING ( + ANGLE_TO_FACING (facing + HALF_CIRCLE))); + SpaceMarinePtr->preprocess_func = marine_preprocess; + SpaceMarinePtr->collision_func = marine_collision; + + SetElementStarShip (SpaceMarinePtr, StarShipPtr); + + SetPrimType (&(GLOBAL (DisplayArray))[ + SpaceMarinePtr->PrimIndex], STAMP_PRIM); + + UnlockElement (hSpaceMarine); + PutElement (hSpaceMarine); + + DeltaCrew (ShipPtr, -1); + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), + SpaceMarinePtr); + + StarShipPtr->special_counter = + StarShipPtr->RaceDescPtr->characteristics.special_wait; + } + + UnlockElement (StarShipPtr->hShip); + } + } +} + +static void +orz_preprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + if (!(ElementPtr->state_flags & APPEARING)) + { + if (((StarShipPtr->cur_status_flags + | StarShipPtr->old_status_flags) & SPECIAL) + && (StarShipPtr->cur_status_flags & (LEFT | RIGHT)) + && ElementPtr->turn_wait == 0) + { + ++ElementPtr->turn_wait; + } + + if ((StarShipPtr->cur_status_flags & SPECIAL) + && (StarShipPtr->cur_status_flags & WEAPON) + && StarShipPtr->weapon_counter == 0) + { + ++StarShipPtr->weapon_counter; + } + } + else + { + HELEMENT hTurret; + + hTurret = AllocElement (); + if (hTurret) + { + ELEMENT *TurretPtr; + + LockElement (hTurret, &TurretPtr); + TurretPtr->playerNr = ElementPtr->playerNr; + TurretPtr->state_flags = FINITE_LIFE | NONSOLID | IGNORE_SIMILAR; + TurretPtr->life_span = 1; + TurretPtr->current.image.farray = + StarShipPtr->RaceDescPtr->ship_data.special; + TurretPtr->current.image.frame = SetAbsFrameIndex ( + StarShipPtr->RaceDescPtr->ship_data.special[0], + StarShipPtr->ShipFacing); + + TurretPtr->postprocess_func = turret_postprocess; + + SetElementStarShip (TurretPtr, StarShipPtr); + + UnlockElement (hTurret); + InsertElement (hTurret, GetSuccElement (ElementPtr)); + } + } +} + +RACE_DESC* +init_orz (void) +{ + RACE_DESC *RaceDescPtr; + + orz_desc.preprocess_func = orz_preprocess; + orz_desc.init_weapon_func = initialize_turret_missile; + orz_desc.cyborg_control.intelligence_func = orz_intelligence; + + RaceDescPtr = &orz_desc; + + return (RaceDescPtr); +} + |