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/melnorme/melnorme.c | 658 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 658 insertions(+) create mode 100644 src/uqm/ships/melnorme/melnorme.c (limited to 'src/uqm/ships/melnorme/melnorme.c') diff --git a/src/uqm/ships/melnorme/melnorme.c b/src/uqm/ships/melnorme/melnorme.c new file mode 100644 index 0000000..8e5ab2b --- /dev/null +++ b/src/uqm/ships/melnorme/melnorme.c @@ -0,0 +1,658 @@ +//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 "melnorme.h" +#include "resinst.h" + +#include "uqm/globdata.h" +#include "libs/mathlib.h" + +// Core characteristics +#define MAX_CREW 20 +#define MAX_ENERGY MAX_ENERGY_SIZE +#define ENERGY_REGENERATION 1 +#define ENERGY_WAIT 4 +#define MAX_THRUST 36 +#define THRUST_INCREMENT 6 +#define THRUST_WAIT 4 +#define TURN_WAIT 4 +#define SHIP_MASS 7 + +// Blaster Pulse +#define WEAPON_ENERGY_COST 5 +#define WEAPON_WAIT 1 +#define MELNORME_OFFSET 24 +#define LEVEL_COUNTER 72 +#define MAX_PUMP 4 +#define PUMPUP_SPEED DISPLAY_TO_WORLD (45) +#define PUMPUP_LIFE 10 +#define PUMPUP_DAMAGE 2 +#define MIN_PUMPITUDE_ANIMS 3 +#define NUM_PUMP_ANIMS 5 +#define REVERSE_DIR (BYTE)(1 << 7) + +// Confusion Pulse +#define SPECIAL_ENERGY_COST 20 +#define SPECIAL_WAIT 20 +#define CMISSILE_SPEED DISPLAY_TO_WORLD (30) +#define CMISSILE_LIFE 20 +#define CMISSILE_HITS 200 +#define CMISSILE_DAMAGE 0 +#define CMISSILE_OFFSET 4 + +static RACE_DESC melnorme_desc = +{ + { /* SHIP_INFO */ + FIRES_FORE, + 18, /* Super Melee cost */ + MAX_CREW, MAX_CREW, + MAX_ENERGY, MAX_ENERGY, + MELNORME_RACE_STRINGS, + MELNORME_ICON_MASK_PMAP_ANIM, + MELNORME_MICON_MASK_PMAP_ANIM, + NULL, NULL, NULL + }, + { /* FLEET_STUFF */ + INFINITE_RADIUS, /* Initial sphere of influence radius */ + { /* Known location (center of SoI) */ + MAX_X_UNIVERSE >> 1, MAX_Y_UNIVERSE >> 1, + }, + }, + { + MAX_THRUST, + THRUST_INCREMENT, + ENERGY_REGENERATION, + WEAPON_ENERGY_COST, + SPECIAL_ENERGY_COST, + ENERGY_WAIT, + TURN_WAIT, + THRUST_WAIT, + WEAPON_WAIT, + SPECIAL_WAIT, + SHIP_MASS, + }, + { + { + MELNORME_BIG_MASK_PMAP_ANIM, + MELNORME_MED_MASK_PMAP_ANIM, + MELNORME_SML_MASK_PMAP_ANIM, + }, + { + PUMPUP_BIG_MASK_PMAP_ANIM, + PUMPUP_MED_MASK_PMAP_ANIM, + PUMPUP_SML_MASK_PMAP_ANIM, + }, + { + CONFUSE_BIG_MASK_PMAP_ANIM, + CONFUSE_MED_MASK_PMAP_ANIM, + CONFUSE_SML_MASK_PMAP_ANIM, + }, + { + MELNORME_CAPTAIN_MASK_PMAP_ANIM, + NULL, NULL, NULL, NULL, NULL + }, + MELNORME_VICTORY_SONG, + MELNORME_SHIP_SOUNDS, + { NULL, NULL, NULL }, + { NULL, NULL, NULL }, + { NULL, NULL, NULL }, + NULL, NULL + }, + { + 0, + PUMPUP_SPEED * PUMPUP_LIFE, + NULL, + }, + (UNINIT_FUNC *) NULL, + (PREPROCESS_FUNC *) NULL, + (POSTPROCESS_FUNC *) NULL, + (INIT_WEAPON_FUNC *) NULL, + 0, + 0, /* CodeRef */ +}; + +static void +pump_up_preprocess (ELEMENT *ElementPtr) +{ + if (--ElementPtr->thrust_wait & 1) + { + COUNT frame_index; + + frame_index = GetFrameIndex (ElementPtr->current.image.frame); + if (((ElementPtr->turn_wait & REVERSE_DIR) + && (frame_index % NUM_PUMP_ANIMS) != 0) + || (!(ElementPtr->turn_wait & REVERSE_DIR) + && ((frame_index + 1) % NUM_PUMP_ANIMS) == 0)) + { + --frame_index; + ElementPtr->turn_wait |= REVERSE_DIR; + } + else + { + ++frame_index; + ElementPtr->turn_wait &= ~REVERSE_DIR; + } + + ElementPtr->next.image.frame = SetAbsFrameIndex ( + ElementPtr->current.image.frame, frame_index); + + ElementPtr->state_flags |= CHANGING; + } +} + +static COUNT initialize_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[]); + +static void +pump_up_postprocess (ELEMENT *ElementPtr) +{ + if (ElementPtr->state_flags & APPEARING) + { + ZeroVelocityComponents (&ElementPtr->velocity); + } + else + { + HELEMENT hPumpUp; + ELEMENT *EPtr; + ELEMENT *ShipPtr; + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + LockElement (StarShipPtr->hShip, &ShipPtr); + initialize_pump_up (ShipPtr, &hPumpUp); + DeltaEnergy (ShipPtr, 0); + UnlockElement (StarShipPtr->hShip); + + LockElement (hPumpUp, &EPtr); + + EPtr->current.image.frame = ElementPtr->current.image.frame; + EPtr->turn_wait = ElementPtr->turn_wait; + EPtr->thrust_wait = ElementPtr->thrust_wait; + if (--EPtr->thrust_wait == 0) + { + if ((EPtr->turn_wait & ~REVERSE_DIR) < MAX_PUMP - 1) + { + ++EPtr->turn_wait; + EPtr->current.image.frame = SetRelFrameIndex ( + EPtr->current.image.frame, NUM_PUMP_ANIMS); + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), + EPtr); + } + EPtr->thrust_wait = LEVEL_COUNTER; + } + + EPtr->mass_points = EPtr->hit_points = + (PUMPUP_DAMAGE << (ElementPtr->turn_wait & ~REVERSE_DIR)); + SetElementStarShip (EPtr, StarShipPtr); + + if (EPtr->thrust_wait & 1) + { + COUNT frame_index; + + frame_index = GetFrameIndex (EPtr->current.image.frame); + if (((EPtr->turn_wait & REVERSE_DIR) + && (frame_index % NUM_PUMP_ANIMS) != 0) + || (!(EPtr->turn_wait & REVERSE_DIR) + && ((frame_index + 1) % NUM_PUMP_ANIMS) == 0)) + { + --frame_index; + EPtr->turn_wait |= REVERSE_DIR; + } + else + { + ++frame_index; + EPtr->turn_wait &= ~REVERSE_DIR; + } + + EPtr->current.image.frame = SetAbsFrameIndex ( + EPtr->current.image.frame, frame_index); + } + + if (StarShipPtr->cur_status_flags & StarShipPtr->old_status_flags + & WEAPON) + { + StarShipPtr->weapon_counter = WEAPON_WAIT; + } + else + { + COUNT angle; + + EPtr->life_span = PUMPUP_LIFE; + EPtr->preprocess_func = pump_up_preprocess; + EPtr->postprocess_func = 0; + + angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing); + SetVelocityComponents (&EPtr->velocity, + COSINE (angle, WORLD_TO_VELOCITY (PUMPUP_SPEED)), + SINE (angle, WORLD_TO_VELOCITY (PUMPUP_SPEED))); + + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), EPtr); + } + + UnlockElement (hPumpUp); + PutElement (hPumpUp); + + SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], + NO_PRIM); + ElementPtr->state_flags |= NONSOLID; + } +} + +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 +pump_up_collision (ELEMENT *ElementPtr0, POINT *pPt0, + ELEMENT *ElementPtr1, POINT *pPt1) +{ + RECT r; + BYTE old_thrust_wait; + HELEMENT hBlastElement; + + GetFrameRect (ElementPtr0->next.image.frame, &r); + + old_thrust_wait = ElementPtr0->thrust_wait; + ElementPtr0->blast_offset = r.extent.width >> 1; + hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1); + ElementPtr0->thrust_wait = old_thrust_wait; + + if (hBlastElement) + { + ELEMENT *BlastElementPtr; + + LockElement (hBlastElement, &BlastElementPtr); + + BlastElementPtr->life_span = + MIN_PUMPITUDE_ANIMS + + (ElementPtr0->turn_wait & ~REVERSE_DIR); + 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], + MAX_PUMP * NUM_PUMP_ANIMS); + + UnlockElement (hBlastElement); + } +} + +static COUNT +initialize_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[]) +{ + 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 = MELNORME_OFFSET; + MissileBlock.speed = DISPLAY_TO_WORLD (MELNORME_OFFSET); + MissileBlock.hit_points = PUMPUP_DAMAGE; + MissileBlock.damage = PUMPUP_DAMAGE; + MissileBlock.life = 2; + MissileBlock.preprocess_func = 0; + MissileBlock.blast_offs = 0; + PumpUpArray[0] = initialize_missile (&MissileBlock); + + if (PumpUpArray[0]) + { + ELEMENT *PumpUpPtr; + + LockElement (PumpUpArray[0], &PumpUpPtr); + PumpUpPtr->postprocess_func = pump_up_postprocess; + PumpUpPtr->collision_func = pump_up_collision; + PumpUpPtr->thrust_wait = LEVEL_COUNTER; + UnlockElement (PumpUpArray[0]); + } + + return (1); +} + +static void +confuse_preprocess (ELEMENT *ElementPtr) +{ + if (!(ElementPtr->state_flags & NONSOLID)) + { + ElementPtr->next.image.frame = SetAbsFrameIndex ( + ElementPtr->current.image.frame, + (GetFrameIndex (ElementPtr->current.image.frame) + 1) & 7); + ElementPtr->state_flags |= CHANGING; + } + else if (ElementPtr->hTarget == 0) + { + ElementPtr->life_span = 0; + ElementPtr->state_flags |= DISAPPEARING; + } + else + { + ELEMENT *eptr; + + LockElement (ElementPtr->hTarget, &eptr); + + ElementPtr->next.location = eptr->next.location; + + if (ElementPtr->turn_wait) + { + HELEMENT hEffect; + STARSHIP *StarShipPtr; + + if (GetFrameIndex (ElementPtr->next.image.frame = + IncFrameIndex (ElementPtr->current.image.frame)) == 0) + ElementPtr->next.image.frame = + SetRelFrameIndex (ElementPtr->next.image.frame, -8); + + GetElementStarShip (eptr, &StarShipPtr); + StarShipPtr->ship_input_state = + (StarShipPtr->ship_input_state + & ~(LEFT | RIGHT | SPECIAL)) + | ElementPtr->turn_wait; + + hEffect = AllocElement (); + if (hEffect) + { + LockElement (hEffect, &eptr); + eptr->playerNr = ElementPtr->playerNr; + eptr->state_flags = FINITE_LIFE | NONSOLID | CHANGING; + eptr->life_span = 1; + eptr->current = eptr->next = ElementPtr->next; + eptr->preprocess_func = confuse_preprocess; + SetPrimType (&(GLOBAL (DisplayArray))[eptr->PrimIndex], + STAMP_PRIM); + + GetElementStarShip (ElementPtr, &StarShipPtr); + SetElementStarShip (eptr, StarShipPtr); + eptr->hTarget = ElementPtr->hTarget; + + UnlockElement (hEffect); + PutElement (hEffect); + } + } + + UnlockElement (ElementPtr->hTarget); + } +} + +static void +confusion_collision (ELEMENT *ElementPtr0, POINT *pPt0, + ELEMENT *ElementPtr1, POINT *pPt1) +{ + if (ElementPtr1->state_flags & PLAYER_SHIP) + { + HELEMENT hConfusionElement, hNextElement; + ELEMENT *ConfusionPtr; + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr0, &StarShipPtr); + for (hConfusionElement = GetHeadElement (); + hConfusionElement; hConfusionElement = hNextElement) + { + LockElement (hConfusionElement, &ConfusionPtr); + if (elementsOfSamePlayer (ConfusionPtr, ElementPtr0) + && ConfusionPtr->current.image.farray == + StarShipPtr->RaceDescPtr->ship_data.special + && (ConfusionPtr->state_flags & NONSOLID)) + { + UnlockElement (hConfusionElement); + break; + } + hNextElement = GetSuccElement (ConfusionPtr); + UnlockElement (hConfusionElement); + } + + if (hConfusionElement || (hConfusionElement = AllocElement ())) + { + LockElement (hConfusionElement, &ConfusionPtr); + + if (ConfusionPtr->state_flags == 0) /* not allocated before */ + { + InsertElement (hConfusionElement, GetHeadElement ()); + + ConfusionPtr->current = ElementPtr0->next; + ConfusionPtr->current.image.frame = SetAbsFrameIndex ( + ConfusionPtr->current.image.frame, 8 + ); + ConfusionPtr->next = ConfusionPtr->current; + ConfusionPtr->playerNr = ElementPtr0->playerNr; + ConfusionPtr->state_flags = FINITE_LIFE | NONSOLID | CHANGING; + ConfusionPtr->preprocess_func = confuse_preprocess; + SetPrimType ( + &(GLOBAL (DisplayArray))[ConfusionPtr->PrimIndex], + NO_PRIM + ); + + SetElementStarShip (ConfusionPtr, StarShipPtr); + GetElementStarShip (ElementPtr1, &StarShipPtr); + ConfusionPtr->hTarget = StarShipPtr->hShip; + } + + ConfusionPtr->life_span = 400; + ConfusionPtr->turn_wait = + (BYTE)(1 << ((BYTE)TFB_Random () & 1)); /* LEFT or RIGHT */ + + UnlockElement (hConfusionElement); + } + + ElementPtr0->hit_points = 0; + ElementPtr0->life_span = 0; + ElementPtr0->state_flags |= DISAPPEARING | COLLISION | NONSOLID; + } + (void) pPt0; /* Satisfying compiler (unused parameter) */ + (void) pPt1; /* Satisfying compiler (unused parameter) */ +} + +static COUNT +initialize_confusion (ELEMENT *ShipPtr, HELEMENT ConfusionArray[]) +{ + STARSHIP *StarShipPtr; + MISSILE_BLOCK ConfusionBlock; + + GetElementStarShip (ShipPtr, &StarShipPtr); + ConfusionBlock.cx = ShipPtr->next.location.x; + ConfusionBlock.cy = ShipPtr->next.location.y; + ConfusionBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special; + ConfusionBlock.index = 0; + ConfusionBlock.face = StarShipPtr->ShipFacing; + ConfusionBlock.sender = ShipPtr->playerNr; + ConfusionBlock.flags = IGNORE_SIMILAR; + ConfusionBlock.pixoffs = MELNORME_OFFSET; + ConfusionBlock.speed = CMISSILE_SPEED; + ConfusionBlock.hit_points = CMISSILE_HITS; + ConfusionBlock.damage = CMISSILE_DAMAGE; + ConfusionBlock.life = CMISSILE_LIFE; + ConfusionBlock.preprocess_func = confuse_preprocess; + ConfusionBlock.blast_offs = CMISSILE_OFFSET; + ConfusionArray[0] = initialize_missile (&ConfusionBlock); + + if (ConfusionArray[0]) + { + ELEMENT *CMissilePtr; + + LockElement (ConfusionArray[0], &CMissilePtr); + CMissilePtr->collision_func = confusion_collision; + SetElementStarShip (CMissilePtr, StarShipPtr); + UnlockElement (ConfusionArray[0]); + } + return (1); +} + +static COUNT +initialize_test_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[]) +{ + STARSHIP *StarShipPtr; + MISSILE_BLOCK MissileBlock; + //ELEMENT *PumpUpPtr; + + 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 = MELNORME_OFFSET; + MissileBlock.speed = PUMPUP_SPEED; + MissileBlock.hit_points = PUMPUP_DAMAGE; + MissileBlock.damage = PUMPUP_DAMAGE; + MissileBlock.life = PUMPUP_LIFE; + MissileBlock.preprocess_func = 0; + MissileBlock.blast_offs = 0; + PumpUpArray[0] = initialize_missile (&MissileBlock); + + return (1); +} + +static void +melnorme_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, + COUNT ConcernCounter) +{ + BYTE old_count; + STARSHIP *StarShipPtr; + EVALUATE_DESC *lpEvalDesc; + + GetElementStarShip (ShipPtr, &StarShipPtr); + + StarShipPtr->RaceDescPtr->init_weapon_func = initialize_test_pump_up; + old_count = StarShipPtr->weapon_counter; + + if (StarShipPtr->weapon_counter == WEAPON_WAIT) + StarShipPtr->weapon_counter = 0; + + lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX]; + if (lpEvalDesc->ObjectPtr) + { + if (StarShipPtr->RaceDescPtr->ship_info.energy_level < SPECIAL_ENERGY_COST + + WEAPON_ENERGY_COST + && !(StarShipPtr->old_status_flags & WEAPON)) + lpEvalDesc->MoveState = ENTICE; + else + { + STARSHIP *EnemyStarShipPtr; + + GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr); + if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags + & IMMEDIATE_WEAPON)) + lpEvalDesc->MoveState = PURSUE; + } + } + ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter); + + if (StarShipPtr->weapon_counter == 0 + && (old_count != 0 + || ((StarShipPtr->special_counter + || StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST + + WEAPON_ENERGY_COST) + && !(StarShipPtr->ship_input_state & WEAPON)))) + StarShipPtr->ship_input_state ^= WEAPON; + + StarShipPtr->ship_input_state &= ~SPECIAL; + if (StarShipPtr->special_counter == 0 + && StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST) + { + BYTE old_input_state; + + old_input_state = StarShipPtr->ship_input_state; + + StarShipPtr->RaceDescPtr->init_weapon_func = initialize_confusion; + + ++ShipPtr->turn_wait; + ++ShipPtr->thrust_wait; + ship_intelligence (ShipPtr, ObjectsOfConcern, ENEMY_SHIP_INDEX + 1); + --ShipPtr->thrust_wait; + --ShipPtr->turn_wait; + + if (StarShipPtr->ship_input_state & WEAPON) + { + StarShipPtr->ship_input_state &= ~WEAPON; + StarShipPtr->ship_input_state |= SPECIAL; + } + + StarShipPtr->ship_input_state = (unsigned char)(old_input_state + | (StarShipPtr->ship_input_state & SPECIAL)); + } + + StarShipPtr->weapon_counter = old_count; + + StarShipPtr->RaceDescPtr->init_weapon_func = initialize_pump_up; +} + +static void +melnorme_postprocess (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + if ((StarShipPtr->cur_status_flags & SPECIAL) + && StarShipPtr->special_counter == 0 + && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)) + { + HELEMENT Confusion; + + initialize_confusion (ElementPtr, &Confusion); + if (Confusion) + { + ELEMENT *CMissilePtr; + LockElement (Confusion, &CMissilePtr); + + ProcessSound (SetAbsSoundIndex ( + StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), CMissilePtr); + + UnlockElement (Confusion); + PutElement (Confusion); + StarShipPtr->special_counter = + StarShipPtr->RaceDescPtr->characteristics.special_wait; + } + } +} + +RACE_DESC* +init_melnorme (void) +{ + RACE_DESC *RaceDescPtr; + + melnorme_desc.postprocess_func = melnorme_postprocess; + melnorme_desc.init_weapon_func = initialize_pump_up; + melnorme_desc.cyborg_control.intelligence_func = melnorme_intelligence; + + RaceDescPtr = &melnorme_desc; + + return (RaceDescPtr); +} + -- cgit v1.2.3