//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 "mycon.h" #include "resinst.h" // Core characteristics #define MAX_CREW 20 #define MAX_ENERGY 40 #define ENERGY_REGENERATION 1 #define ENERGY_WAIT 4 #define MAX_THRUST /* DISPLAY_TO_WORLD (7) */ 27 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 9 #define THRUST_WAIT 6 #define TURN_WAIT 6 #define SHIP_MASS 7 // Plasmoid #define WEAPON_ENERGY_COST 20 #define WEAPON_WAIT 5 #define MYCON_OFFSET 24 #define MISSILE_OFFSET 0 #define NUM_PLASMAS 11 #define NUM_GLOBALLS 8 #define PLASMA_DURATION 13 #define MISSILE_LIFE (NUM_PLASMAS * PLASMA_DURATION) #define MISSILE_SPEED DISPLAY_TO_WORLD (8) #define MISSILE_DAMAGE 10 #define TRACK_WAIT 1 // Regenerate #define SPECIAL_ENERGY_COST MAX_ENERGY #define SPECIAL_WAIT 0 #define REGENERATION_AMOUNT 4 static RACE_DESC mycon_desc = { { /* SHIP_INFO */ FIRES_FORE | SEEKING_WEAPON, 21, /* Super Melee cost */ MAX_CREW, MAX_CREW, MAX_ENERGY, MAX_ENERGY, MYCON_RACE_STRINGS, MYCON_ICON_MASK_PMAP_ANIM, MYCON_MICON_MASK_PMAP_ANIM, NULL, NULL, NULL }, { /* FLEET_STUFF */ 1070 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */ { /* Known location (center of SoI) */ 6392, 2200, }, }, { MAX_THRUST, THRUST_INCREMENT, ENERGY_REGENERATION, WEAPON_ENERGY_COST, SPECIAL_ENERGY_COST, ENERGY_WAIT, TURN_WAIT, THRUST_WAIT, WEAPON_WAIT, SPECIAL_WAIT, SHIP_MASS, }, { { MYCON_BIG_MASK_PMAP_ANIM, MYCON_MED_MASK_PMAP_ANIM, MYCON_SML_MASK_PMAP_ANIM, }, { PLASMA_BIG_MASK_PMAP_ANIM, PLASMA_MED_MASK_PMAP_ANIM, PLASMA_SML_MASK_PMAP_ANIM, }, { NULL_RESOURCE, NULL_RESOURCE, NULL_RESOURCE, }, { MYCON_CAPTAIN_MASK_PMAP_ANIM, NULL, NULL, NULL, NULL, NULL }, MYCON_VICTORY_SONG, MYCON_SHIP_SOUNDS, { NULL, NULL, NULL }, { NULL, NULL, NULL }, { NULL, NULL, NULL }, NULL, NULL }, { 0, DISPLAY_TO_WORLD (800), NULL, }, (UNINIT_FUNC *) NULL, (PREPROCESS_FUNC *) NULL, (POSTPROCESS_FUNC *) NULL, (INIT_WEAPON_FUNC *) NULL, 0, 0, /* CodeRef */ }; static void plasma_preprocess (ELEMENT *ElementPtr) { COUNT plasma_index; if (ElementPtr->mass_points > ElementPtr->hit_points) ElementPtr->life_span = ElementPtr->hit_points * PLASMA_DURATION; else ElementPtr->hit_points = (BYTE)((ElementPtr->life_span * MISSILE_DAMAGE + (MISSILE_LIFE - 1)) / MISSILE_LIFE); ElementPtr->mass_points = ElementPtr->hit_points; plasma_index = NUM_PLASMAS - ((ElementPtr->life_span + (PLASMA_DURATION - 1)) / PLASMA_DURATION); if (plasma_index != GetFrameIndex (ElementPtr->next.image.frame)) { ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->next.image.frame, plasma_index); ElementPtr->state_flags |= CHANGING; } if (ElementPtr->turn_wait > 0) --ElementPtr->turn_wait; else { COUNT facing; facing = NORMALIZE_FACING (ANGLE_TO_FACING ( GetVelocityTravelAngle (&ElementPtr->velocity) )); if (TrackShip (ElementPtr, &facing) > 0) SetVelocityVector (&ElementPtr->velocity, MISSILE_SPEED, facing); ElementPtr->turn_wait = TRACK_WAIT; } } static void plasma_blast_preprocess (ELEMENT *ElementPtr) { if (ElementPtr->life_span >= ElementPtr->thrust_wait) ElementPtr->next.image.frame = IncFrameIndex (ElementPtr->next.image.frame); else ElementPtr->next.image.frame = DecFrameIndex (ElementPtr->next.image.frame); if (ElementPtr->hTarget) { ELEMENT *ShipPtr; LockElement (ElementPtr->hTarget, &ShipPtr); ElementPtr->next.location = ShipPtr->next.location; UnlockElement (ElementPtr->hTarget); } ElementPtr->state_flags |= CHANGING; } static void plasma_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { SIZE old_mass; HELEMENT hBlastElement; old_mass = (SIZE)ElementPtr0->mass_points; if ((ElementPtr0->pParent != ElementPtr1->pParent || (ElementPtr1->state_flags & PLAYER_SHIP)) && (hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1))) { SIZE num_animations; ELEMENT *BlastElementPtr; LockElement (hBlastElement, &BlastElementPtr); BlastElementPtr->pParent = ElementPtr0->pParent; if (!(ElementPtr1->state_flags & PLAYER_SHIP)) BlastElementPtr->hTarget = 0; else { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr1, &StarShipPtr); BlastElementPtr->hTarget = StarShipPtr->hShip; } BlastElementPtr->current.location = ElementPtr1->current.location; if ((num_animations = (old_mass * NUM_GLOBALLS + (MISSILE_DAMAGE - 1)) / MISSILE_DAMAGE) == 0) num_animations = 1; BlastElementPtr->thrust_wait = (BYTE)num_animations; BlastElementPtr->life_span = (num_animations << 1) - 1; { BlastElementPtr->preprocess_func = plasma_blast_preprocess; } BlastElementPtr->current.image.farray = ElementPtr0->next.image.farray; BlastElementPtr->current.image.frame = SetAbsFrameIndex (BlastElementPtr->current.image.farray[0], NUM_PLASMAS); UnlockElement (hBlastElement); } } static void mycon_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, COUNT ConcernCounter) { STARSHIP *StarShipPtr; EVALUATE_DESC *lpEvalDesc; lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX]; if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE) { if ((lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE) && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)) lpEvalDesc->MoveState = AVOID; else lpEvalDesc->MoveState = PURSUE; } ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); GetElementStarShip (ShipPtr, &StarShipPtr); if (ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == PURSUE) StarShipPtr->ship_input_state &= ~THRUST; /* don't pursue seekers */ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; if (StarShipPtr->weapon_counter == 0 && lpEvalDesc->ObjectPtr && (lpEvalDesc->which_turn <= 16 || ShipPtr->crew_level == StarShipPtr->RaceDescPtr->ship_info.max_crew)) { COUNT travel_facing, direction_facing; SIZE delta_x, delta_y; travel_facing = NORMALIZE_FACING ( ANGLE_TO_FACING (GetVelocityTravelAngle (&ShipPtr->velocity) + HALF_CIRCLE) ); delta_x = lpEvalDesc->ObjectPtr->current.location.x - ShipPtr->current.location.x; delta_y = lpEvalDesc->ObjectPtr->current.location.y - ShipPtr->current.location.y; direction_facing = NORMALIZE_FACING ( ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) ); if (NORMALIZE_FACING (direction_facing - StarShipPtr->ShipFacing + ANGLE_TO_FACING (QUADRANT)) <= ANGLE_TO_FACING (HALF_CIRCLE) && (!(StarShipPtr->cur_status_flags & (SHIP_BEYOND_MAX_SPEED | SHIP_IN_GRAVITY_WELL)) || NORMALIZE_FACING (direction_facing - travel_facing + ANGLE_TO_FACING (OCTANT)) <= ANGLE_TO_FACING (QUADRANT))) StarShipPtr->ship_input_state |= WEAPON; } if (StarShipPtr->special_counter == 0) { StarShipPtr->ship_input_state &= ~SPECIAL; StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = DISPLAY_TO_WORLD (800); if (ShipPtr->crew_level < StarShipPtr->RaceDescPtr->ship_info.max_crew) { StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = MISSILE_SPEED * MISSILE_LIFE; if (StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST && !(StarShipPtr->ship_input_state & WEAPON)) StarShipPtr->ship_input_state |= SPECIAL; } } } static COUNT initialize_plasma (ELEMENT *ShipPtr, HELEMENT PlasmaArray[]) { 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 = 0; MissileBlock.pixoffs = MYCON_OFFSET; MissileBlock.speed = MISSILE_SPEED; MissileBlock.hit_points = MISSILE_DAMAGE; MissileBlock.damage = MISSILE_DAMAGE; MissileBlock.life = MISSILE_LIFE; MissileBlock.preprocess_func = plasma_preprocess; MissileBlock.blast_offs = MISSILE_OFFSET; PlasmaArray[0] = initialize_missile (&MissileBlock); if (PlasmaArray[0]) { ELEMENT *PlasmaPtr; LockElement (PlasmaArray[0], &PlasmaPtr); PlasmaPtr->collision_func = plasma_collision; PlasmaPtr->turn_wait = TRACK_WAIT + 2; UnlockElement (PlasmaArray[0]); } return (1); } static void mycon_postprocess (ELEMENT *ElementPtr) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); if ((StarShipPtr->cur_status_flags & SPECIAL) && StarShipPtr->special_counter == 0 && ElementPtr->crew_level != StarShipPtr->RaceDescPtr->ship_info.max_crew && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) { SIZE add_crew; ProcessSound (SetAbsSoundIndex ( /* GROW_NEW_CREW */ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr); if ((add_crew = REGENERATION_AMOUNT) > StarShipPtr->RaceDescPtr->ship_info.max_crew - ElementPtr->crew_level) add_crew = StarShipPtr->RaceDescPtr->ship_info.max_crew - ElementPtr->crew_level; DeltaCrew (ElementPtr, add_crew); StarShipPtr->special_counter = StarShipPtr->RaceDescPtr->characteristics.special_wait; } } RACE_DESC* init_mycon (void) { RACE_DESC *RaceDescPtr; mycon_desc.postprocess_func = mycon_postprocess; mycon_desc.init_weapon_func = initialize_plasma; mycon_desc.cyborg_control.intelligence_func = mycon_intelligence; RaceDescPtr = &mycon_desc; return (RaceDescPtr); }