From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/uqm/ships/chenjesu/chenjesu.c | 588 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 588 insertions(+) create mode 100644 src/uqm/ships/chenjesu/chenjesu.c (limited to 'src/uqm/ships/chenjesu/chenjesu.c') diff --git a/src/uqm/ships/chenjesu/chenjesu.c b/src/uqm/ships/chenjesu/chenjesu.c new file mode 100644 index 0000000..85a1014 --- /dev/null +++ b/src/uqm/ships/chenjesu/chenjesu.c @@ -0,0 +1,588 @@ +//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 "chenjesu.h" +#include "resinst.h" + +#include "uqm/globdata.h" +#include "libs/mathlib.h" + +// Core characteristics +#define MAX_CREW 36 +#define MAX_ENERGY 30 +#define ENERGY_REGENERATION 1 +#define ENERGY_WAIT 4 +#define MAX_THRUST /* DISPLAY_TO_WORLD (7) */ 27 +#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3 +#define THRUST_WAIT 4 +#define TURN_WAIT 6 +#define SHIP_MASS 10 + +// Photon Shard +#define WEAPON_ENERGY_COST 5 +#define WEAPON_WAIT 0 +#define CHENJESU_OFFSET 16 +#define MISSILE_OFFSET 0 +#define MISSILE_SPEED DISPLAY_TO_WORLD (16) +#define MISSILE_LIFE 90 + /* actually, it's as long as you hold the button down. */ +#define MISSILE_HITS 10 +#define MISSILE_DAMAGE 6 +#define NUM_SPARKLES 8 + +// Shrapnel +#define FRAGMENT_OFFSET 2 +#define NUM_FRAGMENTS 8 +#define FRAGMENT_LIFE 10 +#define FRAGMENT_SPEED MISSILE_SPEED +#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED) + /* This bit is for the cyborg only. */ +#define FRAGMENT_HITS 1 +#define FRAGMENT_DAMAGE 2 + +// DOGI +#define SPECIAL_ENERGY_COST MAX_ENERGY +#define SPECIAL_WAIT 0 +#define DOGGY_OFFSET 18 +#define DOGGY_SPEED DISPLAY_TO_WORLD (8) +#define ENERGY_DRAIN 10 +#define MAX_DOGGIES 4 +#define DOGGY_HITS 3 +#define DOGGY_MASS 4 + +static RACE_DESC chenjesu_desc = +{ + { /* SHIP_INFO */ + FIRES_FORE | SEEKING_SPECIAL | SEEKING_WEAPON, + 28, /* Super Melee cost */ + MAX_CREW, MAX_CREW, + MAX_ENERGY, MAX_ENERGY, + CHENJESU_RACE_STRINGS, + CHENJESU_ICON_MASK_PMAP_ANIM, + CHENJESU_MICON_MASK_PMAP_ANIM, + 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, + }, + { + { + CHENJESU_BIG_MASK_PMAP_ANIM, + CHENJESU_MED_MASK_PMAP_ANIM, + CHENJESU_SML_MASK_PMAP_ANIM, + }, + { + SPARK_BIG_MASK_PMAP_ANIM, + SPARK_MED_MASK_PMAP_ANIM, + SPARK_SML_MASK_PMAP_ANIM, + }, + { + DOGGY_BIG_MASK_PMAP_ANIM, + DOGGY_MED_MASK_PMAP_ANIM, + DOGGY_SML_MASK_PMAP_ANIM, + }, + { + CHENJESU_CAPTAIN_MASK_PMAP_ANIM, + NULL, NULL, NULL, NULL, NULL + }, + CHENJESU_VICTORY_SONG, + CHENJESU_SHIP_SOUNDS, + { NULL, NULL, NULL }, + { NULL, NULL, NULL }, + { NULL, NULL, NULL }, + NULL, NULL + }, + { + 0, + LONG_RANGE_WEAPON, + NULL, + }, + (UNINIT_FUNC *) NULL, + (PREPROCESS_FUNC *) NULL, + (POSTPROCESS_FUNC *) NULL, + (INIT_WEAPON_FUNC *) NULL, + 0, + 0, /* CodeRef */ +}; + +static void +crystal_postprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + MISSILE_BLOCK MissileBlock; + + GetElementStarShip (ElementPtr, &StarShipPtr); + MissileBlock.cx = ElementPtr->next.location.x; + MissileBlock.cy = ElementPtr->next.location.y; + MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon; + MissileBlock.index = 1; + MissileBlock.sender = ElementPtr->playerNr; + MissileBlock.flags = IGNORE_SIMILAR; + MissileBlock.pixoffs = 0; + MissileBlock.speed = FRAGMENT_SPEED; + MissileBlock.hit_points = FRAGMENT_HITS; + MissileBlock.damage = FRAGMENT_DAMAGE; + MissileBlock.life = FRAGMENT_LIFE; + MissileBlock.preprocess_func = NULL; + MissileBlock.blast_offs = FRAGMENT_OFFSET; + + for (MissileBlock.face = 0; + MissileBlock.face < ANGLE_TO_FACING (FULL_CIRCLE); + MissileBlock.face += + (ANGLE_TO_FACING (FULL_CIRCLE) / NUM_FRAGMENTS)) + { + HELEMENT hFragment; + + hFragment = initialize_missile (&MissileBlock); + if (hFragment) + { + ELEMENT *FragPtr; + + LockElement (hFragment, &FragPtr); + SetElementStarShip (FragPtr, StarShipPtr); + UnlockElement (hFragment); + PutElement (hFragment); + } + } + + ProcessSound (SetAbsSoundIndex ( + /* CRYSTAL_FRAGMENTS */ + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr); +} + +static void +crystal_preprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + if (StarShipPtr->cur_status_flags & WEAPON) + ++ElementPtr->life_span; /* keep it going while key pressed */ + else + { + ElementPtr->life_span = 1; + + ElementPtr->postprocess_func = crystal_postprocess; + } +} + +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 +crystal_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); + BlastElementPtr->current.location = ElementPtr1->current.location; + + BlastElementPtr->life_span = NUM_SPARKLES; + BlastElementPtr->turn_wait = BlastElementPtr->next_turn = 0; + { + BlastElementPtr->preprocess_func = animate; + } + + BlastElementPtr->current.image.farray = ElementPtr0->next.image.farray; + BlastElementPtr->current.image.frame = + SetAbsFrameIndex (BlastElementPtr->current.image.farray[0], + 2); /* skip stones */ + + UnlockElement (hBlastElement); + } +} + +static void +doggy_preprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + ++StarShipPtr->special_counter; + if (ElementPtr->thrust_wait > 0) /* could be non-zero after a collision */ + --ElementPtr->thrust_wait; + else + { + 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 = NORMALIZE_FACING (TFB_Random ()); + else + { + ELEMENT *ShipPtr; + + LockElement (ElementPtr->hTarget, &ShipPtr); + facing = NORMALIZE_FACING (ANGLE_TO_FACING ( + ARCTAN (ShipPtr->current.location.x - + ElementPtr->current.location.x, + ShipPtr->current.location.y - + ElementPtr->current.location.y) + )); + delta_facing = NORMALIZE_FACING (facing - + GetFrameIndex (ShipPtr->current.image.frame)); + UnlockElement (ElementPtr->hTarget); + + if (delta_facing > ANGLE_TO_FACING (HALF_CIRCLE - OCTANT) && + delta_facing < ANGLE_TO_FACING (HALF_CIRCLE + OCTANT)) + { + if (delta_facing >= ANGLE_TO_FACING (HALF_CIRCLE)) + facing -= ANGLE_TO_FACING (QUADRANT); + else + facing += ANGLE_TO_FACING (QUADRANT); + } + + facing = NORMALIZE_FACING (facing); + } + + if (facing != orig_facing) + SetVelocityVector (&ElementPtr->velocity, + DOGGY_SPEED, facing); + } +} + +static void +doggy_death (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + ProcessSound (SetAbsSoundIndex ( + /* DOGGY_DIES */ + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), ElementPtr); + + ElementPtr->state_flags &= ~DISAPPEARING; + ElementPtr->state_flags |= NONSOLID | FINITE_LIFE; + ElementPtr->life_span = 6; + { + ElementPtr->preprocess_func = animate; + } + ElementPtr->death_func = NULL; + ElementPtr->collision_func = NULL; + ZeroVelocityComponents (&ElementPtr->velocity); + + ElementPtr->turn_wait = ElementPtr->next_turn = 0; +} + +static void +doggy_collision (ELEMENT *ElementPtr0, POINT *pPt0, + ELEMENT *ElementPtr1, POINT *pPt1) +{ + collision (ElementPtr0, pPt0, ElementPtr1, pPt1); + if ((ElementPtr1->state_flags & PLAYER_SHIP) + && !elementsOfSamePlayer (ElementPtr0, ElementPtr1)) + { + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr0, &StarShipPtr); + ProcessSound (SetAbsSoundIndex ( + /* DOGGY_STEALS_ENERGY */ + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), ElementPtr0); + GetElementStarShip (ElementPtr1, &StarShipPtr); + if (StarShipPtr->RaceDescPtr->ship_info.energy_level < ENERGY_DRAIN) + DeltaEnergy (ElementPtr1, -StarShipPtr->RaceDescPtr->ship_info.energy_level); + else + DeltaEnergy (ElementPtr1, -ENERGY_DRAIN); + } + if (ElementPtr0->thrust_wait <= COLLISION_THRUST_WAIT) + ElementPtr0->thrust_wait += COLLISION_THRUST_WAIT << 1; +} + +static void +spawn_doggy (ELEMENT *ElementPtr) +{ + HELEMENT hDoggyElement; + + if ((hDoggyElement = AllocElement ()) != 0) + { + COUNT angle; + ELEMENT *DoggyElementPtr; + STARSHIP *StarShipPtr; + + ElementPtr->state_flags |= DEFY_PHYSICS; + + PutElement (hDoggyElement); + LockElement (hDoggyElement, &DoggyElementPtr); + DoggyElementPtr->hit_points = DOGGY_HITS; + DoggyElementPtr->mass_points = DOGGY_MASS; + DoggyElementPtr->thrust_wait = 0; + DoggyElementPtr->playerNr = ElementPtr->playerNr; + DoggyElementPtr->state_flags = APPEARING; + DoggyElementPtr->life_span = NORMAL_LIFE; + SetPrimType (&(GLOBAL (DisplayArray))[DoggyElementPtr->PrimIndex], + STAMP_PRIM); + { + DoggyElementPtr->preprocess_func = doggy_preprocess; + DoggyElementPtr->postprocess_func = NULL; + DoggyElementPtr->collision_func = doggy_collision; + DoggyElementPtr->death_func = doggy_death; + } + + GetElementStarShip (ElementPtr, &StarShipPtr); + angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE; + DoggyElementPtr->current.location.x = ElementPtr->next.location.x + + COSINE (angle, DISPLAY_TO_WORLD (CHENJESU_OFFSET + DOGGY_OFFSET)); + DoggyElementPtr->current.location.y = ElementPtr->next.location.y + + SINE (angle, DISPLAY_TO_WORLD (CHENJESU_OFFSET + DOGGY_OFFSET)); + DoggyElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.special; + DoggyElementPtr->current.image.frame = StarShipPtr->RaceDescPtr->ship_data.special[0]; + + SetVelocityVector (&DoggyElementPtr->velocity, + DOGGY_SPEED, NORMALIZE_FACING (ANGLE_TO_FACING (angle))); + + SetElementStarShip (DoggyElementPtr, StarShipPtr); + + ProcessSound (SetAbsSoundIndex ( + /* RELEASE_DOGGY */ + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 4), DoggyElementPtr); + + UnlockElement (hDoggyElement); + } +} + +static void +chenjesu_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, + COUNT ConcernCounter) +{ + EVALUATE_DESC *lpEvalDesc; + STARSHIP *StarShipPtr; + + GetElementStarShip (ShipPtr, &StarShipPtr); + StarShipPtr->ship_input_state &= ~SPECIAL; + + lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; + if (lpEvalDesc->ObjectPtr) + { + STARSHIP *EnemyStarShipPtr; + + GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr); + if ((lpEvalDesc->which_turn <= 16 + && MANEUVERABILITY ( + &EnemyStarShipPtr->RaceDescPtr->cyborg_control + ) >= MEDIUM_SHIP) + || (MANEUVERABILITY ( + &EnemyStarShipPtr->RaceDescPtr->cyborg_control + ) <= SLOW_SHIP + && WEAPON_RANGE ( + &EnemyStarShipPtr->RaceDescPtr->cyborg_control + ) >= LONG_RANGE_WEAPON * 3 / 4 + && (EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON))) + lpEvalDesc->MoveState = PURSUE; + } + + if (StarShipPtr->special_counter == 1 + && ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr + && ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == ENTICE + && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 8) + { + lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX]; + } + + ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); + + if (lpEvalDesc->ObjectPtr) + { + HELEMENT h, hNext; + ELEMENT *CrystalPtr; + + h = (StarShipPtr->old_status_flags & WEAPON) ? + GetTailElement () : (HELEMENT)0; + for (; h; h = hNext) + { + LockElement (h, &CrystalPtr); + hNext = GetPredElement (CrystalPtr); + if (!(CrystalPtr->state_flags & NONSOLID) + && CrystalPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.weapon + && CrystalPtr->preprocess_func + && CrystalPtr->life_span > 0 + && elementsOfSamePlayer (CrystalPtr, ShipPtr)) + { + if (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr) + { + COUNT which_turn; + + if ((which_turn = PlotIntercept (CrystalPtr, + ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr, + CrystalPtr->life_span, + FRAGMENT_RANGE / 2)) == 0 + || (which_turn == 1 + && PlotIntercept (CrystalPtr, + ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr, + CrystalPtr->life_span, 0) == 0)) + StarShipPtr->ship_input_state &= ~WEAPON; + else if (StarShipPtr->weapon_counter == 0) + { + StarShipPtr->ship_input_state |= WEAPON; + lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; + } + + UnlockElement (h); + break; + } + hNext = 0; + } + UnlockElement (h); + } + + if (h == 0) + { + if (StarShipPtr->old_status_flags & WEAPON) + { + StarShipPtr->ship_input_state &= ~WEAPON; + if (lpEvalDesc == &ObjectsOfConcern[ENEMY_WEAPON_INDEX]) + StarShipPtr->weapon_counter = 3; + } + else if (StarShipPtr->weapon_counter == 0 + && ship_weapons (ShipPtr, lpEvalDesc->ObjectPtr, FRAGMENT_RANGE / 2)) + StarShipPtr->ship_input_state |= WEAPON; + } + } + + if (StarShipPtr->special_counter < MAX_DOGGIES) + { + if (lpEvalDesc->ObjectPtr + && StarShipPtr->RaceDescPtr->ship_info.energy_level <= SPECIAL_ENERGY_COST + && !(StarShipPtr->ship_input_state & WEAPON)) + StarShipPtr->ship_input_state |= SPECIAL; + } +} + +static COUNT +initialize_crystal (ELEMENT *ShipPtr, HELEMENT CrystalArray[]) +{ + 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 = CHENJESU_OFFSET; + MissileBlock.speed = MISSILE_SPEED; + MissileBlock.hit_points = MISSILE_HITS; + MissileBlock.damage = MISSILE_DAMAGE; + MissileBlock.life = MISSILE_LIFE; + MissileBlock.preprocess_func = crystal_preprocess; + MissileBlock.blast_offs = MISSILE_OFFSET; + CrystalArray[0] = initialize_missile (&MissileBlock); + + if (CrystalArray[0]) + { + ELEMENT *CrystalPtr; + + LockElement (CrystalArray[0], &CrystalPtr); + CrystalPtr->collision_func = crystal_collision; + UnlockElement (CrystalArray[0]); + } + + return (1); +} + +static void +chenjesu_postprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + if ((StarShipPtr->cur_status_flags & SPECIAL) + && StarShipPtr->special_counter < MAX_DOGGIES + && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) + { + spawn_doggy (ElementPtr); + } + + StarShipPtr->special_counter = 1; + /* say there is one doggy, because ship_postprocess will + * decrement special_counter */ +} + +static void +chenjesu_preprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + if (StarShipPtr->special_counter > 1) /* only when STANDARD + * computer opponent + */ + StarShipPtr->special_counter += MAX_DOGGIES; + if (StarShipPtr->cur_status_flags + & StarShipPtr->old_status_flags + & WEAPON) + ++StarShipPtr->weapon_counter; +} + +RACE_DESC* +init_chenjesu (void) +{ + RACE_DESC *RaceDescPtr; + + chenjesu_desc.preprocess_func = chenjesu_preprocess; + chenjesu_desc.postprocess_func = chenjesu_postprocess; + chenjesu_desc.init_weapon_func = initialize_crystal; + chenjesu_desc.cyborg_control.intelligence_func = chenjesu_intelligence; + + RaceDescPtr = &chenjesu_desc; + + return (RaceDescPtr); +} + -- cgit v1.2.3