//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 "umgah.h" #include "resinst.h" #include "libs/mathlib.h" // Core characteristics #define MAX_CREW 10 #define MAX_ENERGY 30 #define ENERGY_REGENERATION MAX_ENERGY #define ENERGY_WAIT 150 #define MAX_THRUST /* DISPLAY_TO_WORLD (5) */ 18 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 6 #define THRUST_WAIT 3 #define TURN_WAIT 4 #define SHIP_MASS 1 // Antimatter cone #define WEAPON_ENERGY_COST 0 #define WEAPON_WAIT 0 #define UMGAH_OFFSET 0 #define CONE_OFFSET 0 #define CONE_SPEED 0 #define CONE_HITS 100 #define CONE_DAMAGE 1 #define CONE_LIFE 1 // Retropropulsion #define SPECIAL_ENERGY_COST 1 #define SPECIAL_WAIT 2 #define JUMP_DIST DISPLAY_TO_WORLD (40) static RACE_DESC umgah_desc = { { /* SHIP_INFO */ FIRES_FORE | IMMEDIATE_WEAPON, 7, /* Super Melee cost */ MAX_CREW, MAX_CREW, MAX_ENERGY, MAX_ENERGY, UMGAH_RACE_STRINGS, UMGAH_ICON_MASK_PMAP_ANIM, UMGAH_MICON_MASK_PMAP_ANIM, NULL, NULL, NULL }, { /* FLEET_STUFF */ 833 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */ { /* Known location (center of SoI) */ 1798, 6000, }, }, { MAX_THRUST, THRUST_INCREMENT, ENERGY_REGENERATION, WEAPON_ENERGY_COST, SPECIAL_ENERGY_COST, ENERGY_WAIT, TURN_WAIT, THRUST_WAIT, WEAPON_WAIT, SPECIAL_WAIT, SHIP_MASS, }, { { UMGAH_BIG_MASK_PMAP_ANIM, UMGAH_MED_MASK_PMAP_ANIM, UMGAH_SML_MASK_PMAP_ANIM, }, { SPRITZ_MASK_PMAP_ANIM, NULL_RESOURCE, NULL_RESOURCE, }, { CONE_BIG_MASK_ANIM, CONE_MED_MASK_ANIM, CONE_SML_MASK_ANIM, }, { UMGAH_CAPTAIN_MASK_PMAP_ANIM, NULL, NULL, NULL, NULL, NULL }, UMGAH_VICTORY_SONG, UMGAH_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 { UWORD prevFacing; } UMGAH_DATA; // Local typedef typedef UMGAH_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 cone_preprocess (ELEMENT *ElementPtr) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); StarShipPtr->RaceDescPtr->ship_data.special[0] = SetRelFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0], ANGLE_TO_FACING (FULL_CIRCLE)); ElementPtr->state_flags |= APPEARING; } static void cone_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { HELEMENT hBlastElement; hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1); if (hBlastElement) { RemoveElement (hBlastElement); FreeElement (hBlastElement); ElementPtr0->state_flags &= ~DISAPPEARING; } } static void umgah_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, COUNT ConcernCounter) { EVALUATE_DESC *lpEvalDesc; STARSHIP *StarShipPtr; STARSHIP *EnemyStarShipPtr; GetElementStarShip (ShipPtr, &StarShipPtr); lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX]; if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE) { if (lpEvalDesc->which_turn > 3 || (StarShipPtr->old_status_flags & SPECIAL)) lpEvalDesc->ObjectPtr = 0; else if ((lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE) && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)) lpEvalDesc->MoveState = AVOID; else lpEvalDesc->MoveState = PURSUE; } lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; if (StarShipPtr->special_counter || ObjectsOfConcern[GRAVITY_MASS_INDEX].ObjectPtr || lpEvalDesc->ObjectPtr == 0) { StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = CLOSE_RANGE_WEAPON; ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); if (lpEvalDesc->which_turn < 16) StarShipPtr->ship_input_state |= WEAPON; StarShipPtr->ship_input_state &= ~SPECIAL; } else { BYTE this_turn; SIZE delta_x, delta_y; BOOLEAN EnemyBehind, EnoughJuice; if (lpEvalDesc->which_turn >= 0xFF + 1) this_turn = 0xFF; else this_turn = (BYTE)lpEvalDesc->which_turn; EnoughJuice = (BOOLEAN)(WORLD_TO_TURN ( JUMP_DIST * StarShipPtr->RaceDescPtr->ship_info.energy_level / SPECIAL_ENERGY_COST ) > this_turn); delta_x = lpEvalDesc->ObjectPtr->next.location.x - ShipPtr->next.location.x; delta_y = lpEvalDesc->ObjectPtr->next.location.y - ShipPtr->next.location.y; EnemyBehind = (BOOLEAN)(NORMALIZE_ANGLE ( ARCTAN (delta_x, delta_y) - (FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE) + (OCTANT + (OCTANT >> 2)) ) <= ((OCTANT + (OCTANT >> 2)) << 1)); GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr); if (EnoughJuice && ((StarShipPtr->old_status_flags & SPECIAL) || EnemyBehind || (this_turn > 6 && MANEUVERABILITY ( &EnemyStarShipPtr->RaceDescPtr->cyborg_control ) <= SLOW_SHIP) || (this_turn >= 16 && this_turn <= 24))) StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = (LONG_RANGE_WEAPON << 3); else StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = CLOSE_RANGE_WEAPON; ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); if (StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange == CLOSE_RANGE_WEAPON) StarShipPtr->ship_input_state &= ~SPECIAL; else { BOOLEAN LinedUp; StarShipPtr->ship_input_state &= ~THRUST; LinedUp = (BOOLEAN)(ShipPtr->turn_wait == 0 && !(StarShipPtr->old_status_flags & (LEFT | RIGHT))); if (((StarShipPtr->old_status_flags & SPECIAL) && this_turn <= StarShipPtr->RaceDescPtr->characteristics.special_wait) || (!(StarShipPtr->old_status_flags & SPECIAL) && EnemyBehind && (LinedUp || this_turn < 16))) { StarShipPtr->ship_input_state |= SPECIAL; StarShipPtr->RaceDescPtr->characteristics.special_wait = this_turn; /* don't want him backing straight into ship */ if (this_turn <= 8 && LinedUp) { if (TFB_Random () & 1) StarShipPtr->ship_input_state |= LEFT; else StarShipPtr->ship_input_state |= RIGHT; } } else if (StarShipPtr->old_status_flags & SPECIAL) { StarShipPtr->ship_input_state &= ~(SPECIAL | LEFT | RIGHT); StarShipPtr->ship_input_state |= THRUST; } } if (this_turn < 16 && !EnemyBehind) StarShipPtr->ship_input_state |= WEAPON; } if (!(StarShipPtr->ship_input_state & SPECIAL)) StarShipPtr->RaceDescPtr->characteristics.special_wait = 0xFF; } static COUNT initialize_cone (ELEMENT *ShipPtr, HELEMENT ConeArray[]) { STARSHIP *StarShipPtr; UMGAH_DATA* UmgahData; MISSILE_BLOCK MissileBlock; GetElementStarShip (ShipPtr, &StarShipPtr); MissileBlock.cx = ShipPtr->next.location.x; MissileBlock.cy = ShipPtr->next.location.y; MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special; MissileBlock.face = StarShipPtr->ShipFacing; MissileBlock.sender = ShipPtr->playerNr; MissileBlock.flags = IGNORE_SIMILAR; MissileBlock.pixoffs = UMGAH_OFFSET; MissileBlock.speed = CONE_SPEED; MissileBlock.hit_points = CONE_HITS; MissileBlock.damage = CONE_DAMAGE; MissileBlock.life = CONE_LIFE; MissileBlock.preprocess_func = cone_preprocess; MissileBlock.blast_offs = CONE_OFFSET; // This func is called every frame while the player is holding down WEAPON // Don't reset the cone FRAME to the first image every time UmgahData = GetCustomShipData (StarShipPtr->RaceDescPtr); if (!UmgahData || StarShipPtr->ShipFacing != UmgahData->prevFacing) { const UMGAH_DATA shipData = {StarShipPtr->ShipFacing}; SetCustomShipData (StarShipPtr->RaceDescPtr, &shipData); StarShipPtr->RaceDescPtr->ship_data.special[0] = SetAbsFrameIndex ( StarShipPtr->RaceDescPtr->ship_data.special[0], StarShipPtr->ShipFacing); } MissileBlock.index = GetFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0]); ConeArray[0] = initialize_missile (&MissileBlock); if (ConeArray[0]) { ELEMENT *ConePtr; LockElement (ConeArray[0], &ConePtr); ConePtr->collision_func = cone_collision; ConePtr->state_flags &= ~APPEARING; ConePtr->next = ConePtr->current; InitIntersectStartPoint (ConePtr); InitIntersectEndPoint (ConePtr); ConePtr->IntersectControl.IntersectStamp.frame = StarShipPtr->RaceDescPtr->ship_data.special[0]; UnlockElement (ConeArray[0]); } return (1); } static void umgah_postprocess (ELEMENT *ElementPtr) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); if (StarShipPtr->special_counter > 0) { StarShipPtr->special_counter = 0; ZeroVelocityComponents (&ElementPtr->velocity); } } static void umgah_preprocess (ELEMENT *ElementPtr) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); if (ElementPtr->state_flags & APPEARING) { // Reset the value just in case SetCustomShipData (StarShipPtr->RaceDescPtr, NULL); } else { if (ElementPtr->thrust_wait == 0 && (StarShipPtr->cur_status_flags & SPECIAL) && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) { COUNT facing; ProcessSound (SetAbsSoundIndex ( /* ZIP_BACKWARDS */ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr); facing = FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE; DeltaVelocityComponents (&ElementPtr->velocity, COSINE (facing, WORLD_TO_VELOCITY (JUMP_DIST)), SINE (facing, WORLD_TO_VELOCITY (JUMP_DIST))); StarShipPtr->cur_status_flags &= ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED); StarShipPtr->special_counter = SPECIAL_WAIT; } } } static void uninit_umgah (RACE_DESC *pRaceDesc) { SetCustomShipData (pRaceDesc, NULL); } RACE_DESC* init_umgah (void) { RACE_DESC *RaceDescPtr; umgah_desc.uninit_func = uninit_umgah; umgah_desc.preprocess_func = umgah_preprocess; umgah_desc.postprocess_func = umgah_postprocess; umgah_desc.init_weapon_func = initialize_cone; umgah_desc.cyborg_control.intelligence_func = umgah_intelligence; RaceDescPtr = &umgah_desc; return (RaceDescPtr); }