Refactoring of ship_death() code: Pkunk/Shofixti exception hacks removed (but not MAX_SHIP_MASS+1); both now have own death_func(); Pkunk ditty plays after a simultaneous destruction + reincarnation (bug #666); plus some comments
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3664 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
Changes towards version 0.8:
|
||||
- Fixed various Pkunk reincarnation and Shofixti Glory device interactions;
|
||||
Pkunk ditty plays in a simultaneous destruction (bug #666) - Alex
|
||||
- Preparing for linking with C++ code, from Scott A. Colcord
|
||||
- Fixed player's phrase leading to Tanaka's response about a solitary
|
||||
vigil (bug #859) - Alex
|
||||
|
||||
@@ -96,7 +96,8 @@ typedef struct state
|
||||
|
||||
typedef struct element ELEMENT;
|
||||
|
||||
typedef void (CollisionFunc) (ELEMENT *ElementPtr0, POINT *pPt0,
|
||||
typedef void (ElementProcessFunc) (ELEMENT *ElementPtr);
|
||||
typedef void (ElementCollisionFunc) (ELEMENT *ElementPtr0, POINT *pPt0,
|
||||
ELEMENT *ElementPtr1, POINT *pPt1);
|
||||
|
||||
// Any physical object in the simulation.
|
||||
@@ -105,10 +106,10 @@ struct element
|
||||
// LINK elements; must be first
|
||||
HELEMENT pred, succ;
|
||||
|
||||
void (*preprocess_func) (struct element *ElementPtr);
|
||||
void (*postprocess_func) (struct element *ElementPtr);
|
||||
CollisionFunc *collision_func;
|
||||
void (*death_func) (struct element *ElementPtr);
|
||||
ElementProcessFunc *preprocess_func;
|
||||
ElementProcessFunc *postprocess_func;
|
||||
ElementCollisionFunc *collision_func;
|
||||
ElementProcessFunc *death_func;
|
||||
|
||||
// Player this element belongs to
|
||||
// -1: neutral (planets, asteroids, crew, etc.)
|
||||
|
||||
@@ -1660,6 +1660,7 @@ CreateStarBackGround (void)
|
||||
RECT clipRect;
|
||||
FRAME frame;
|
||||
|
||||
// Use SpaceContext to find out the dimensions of the background
|
||||
oldContext = SetContext (SpaceContext);
|
||||
GetContextClipRect (&clipRect);
|
||||
|
||||
|
||||
@@ -193,6 +193,9 @@ ship_preprocess (ELEMENT *ElementPtr)
|
||||
if (RDPtr->preprocess_func)
|
||||
(*RDPtr->preprocess_func) (ElementPtr);
|
||||
|
||||
// XXX: Hack: Pkunk sets hTarget!=0 when it reincarnates. In that
|
||||
// case there is no ship_transition() but a Phoenix transition
|
||||
// instead.
|
||||
if (ElementPtr->hTarget == 0)
|
||||
{
|
||||
ship_transition (ElementPtr);
|
||||
|
||||
@@ -426,7 +426,7 @@ androsynth_preprocess (ELEMENT *ElementPtr)
|
||||
StarShipPtr->RaceDescPtr->characteristics.special_wait;
|
||||
StarShipPtr->RaceDescPtr->characteristics.energy_regeneration = ENERGY_REGENERATION;
|
||||
ElementPtr->mass_points = SHIP_MASS;
|
||||
ElementPtr->collision_func = (CollisionFunc *)
|
||||
ElementPtr->collision_func = (ElementCollisionFunc *)
|
||||
StarShipPtr->RaceDescPtr->data;
|
||||
ElementPtr->next.image.farray =
|
||||
StarShipPtr->RaceDescPtr->ship_data.ship;
|
||||
|
||||
@@ -452,7 +452,7 @@ RACE_DESC*
|
||||
init_mmrnmhrm (void)
|
||||
{
|
||||
RACE_DESC *RaceDescPtr;
|
||||
|
||||
// The caller of this func will copy the struct
|
||||
static RACE_DESC new_mmrnmhrm_desc;
|
||||
CHARACTERISTIC_STUFF *otherwing_desc;
|
||||
|
||||
|
||||
+138
-94
@@ -21,6 +21,7 @@
|
||||
#include "resinst.h"
|
||||
|
||||
#include "uqm/globdata.h"
|
||||
#include "uqm/tactrans.h"
|
||||
#include "libs/mathlib.h"
|
||||
|
||||
|
||||
@@ -112,6 +113,16 @@ static RACE_DESC pkunk_desc =
|
||||
0, /* CodeRef */
|
||||
};
|
||||
|
||||
// Private per-instance ship data
|
||||
typedef struct
|
||||
{
|
||||
HELEMENT hPhoenix;
|
||||
ElementProcessFunc *saved_preprocess_func;
|
||||
ElementProcessFunc *saved_postprocess_func;
|
||||
ElementProcessFunc *saved_death_func;
|
||||
|
||||
} PKUNK_DATA;
|
||||
|
||||
static void
|
||||
animate (ELEMENT *ElementPtr)
|
||||
{
|
||||
@@ -186,15 +197,15 @@ pkunk_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
|
||||
COUNT ConcernCounter)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
HELEMENT hPhoenix;
|
||||
PKUNK_DATA *PkunkData;
|
||||
|
||||
GetElementStarShip (ShipPtr, &StarShipPtr);
|
||||
hPhoenix = (HELEMENT) StarShipPtr->RaceDescPtr->data;
|
||||
if (hPhoenix && (StarShipPtr->control & STANDARD_RATING))
|
||||
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
|
||||
if (PkunkData->hPhoenix && (StarShipPtr->control & STANDARD_RATING))
|
||||
{
|
||||
RemoveElement (hPhoenix);
|
||||
FreeElement (hPhoenix);
|
||||
StarShipPtr->RaceDescPtr->data = 0;
|
||||
RemoveElement (PkunkData->hPhoenix);
|
||||
FreeElement (PkunkData->hPhoenix);
|
||||
PkunkData->hPhoenix = 0;
|
||||
}
|
||||
|
||||
if (StarShipPtr->RaceDescPtr->ship_info.energy_level <
|
||||
@@ -208,108 +219,116 @@ pkunk_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
|
||||
}
|
||||
|
||||
static void pkunk_preprocess (ELEMENT *ElementPtr);
|
||||
static void pkunk_postprocess (ELEMENT *ElementPtr);
|
||||
|
||||
static void
|
||||
new_pkunk (ELEMENT *ElementPtr)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
PKUNK_DATA *PkunkData;
|
||||
|
||||
GetElementStarShip (ElementPtr, &StarShipPtr);
|
||||
if (!(ElementPtr->state_flags & PLAYER_SHIP))
|
||||
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
|
||||
|
||||
ElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR;
|
||||
ElementPtr->mass_points = SHIP_MASS;
|
||||
// Restore the element processing callbacks after the explosion.
|
||||
// The callbacks were changed for the explosion sequence
|
||||
ElementPtr->preprocess_func = PkunkData->saved_preprocess_func;
|
||||
ElementPtr->postprocess_func = PkunkData->saved_postprocess_func;
|
||||
ElementPtr->death_func = PkunkData->saved_death_func;
|
||||
// preprocess_func() is called during the phoenix transition and
|
||||
// then cleared, so we need to restore it
|
||||
StarShipPtr->RaceDescPtr->preprocess_func = pkunk_preprocess;
|
||||
StarShipPtr->RaceDescPtr->ship_info.crew_level = MAX_CREW;
|
||||
StarShipPtr->RaceDescPtr->ship_info.energy_level = MAX_ENERGY;
|
||||
/* fix vux impairment */
|
||||
StarShipPtr->RaceDescPtr->characteristics.max_thrust = MAX_THRUST;
|
||||
StarShipPtr->RaceDescPtr->characteristics.thrust_increment = THRUST_INCREMENT;
|
||||
StarShipPtr->RaceDescPtr->characteristics.turn_wait = TURN_WAIT;
|
||||
StarShipPtr->RaceDescPtr->characteristics.thrust_wait = THRUST_WAIT;
|
||||
StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
|
||||
|
||||
StarShipPtr->ship_input_state = 0;
|
||||
// Pkunk wins in a simultaneous destruction if it reincarnates
|
||||
StarShipPtr->cur_status_flags &= PLAY_VICTORY_DITTY;
|
||||
StarShipPtr->old_status_flags = 0;
|
||||
StarShipPtr->energy_counter = 0;
|
||||
StarShipPtr->weapon_counter = 0;
|
||||
StarShipPtr->special_counter = 0;
|
||||
ElementPtr->crew_level = 0;
|
||||
ElementPtr->turn_wait = 0;
|
||||
ElementPtr->thrust_wait = 0;
|
||||
ElementPtr->life_span = NORMAL_LIFE;
|
||||
|
||||
StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ());
|
||||
ElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship;
|
||||
ElementPtr->current.image.frame = SetAbsFrameIndex (
|
||||
StarShipPtr->RaceDescPtr->ship_data.ship[0],
|
||||
StarShipPtr->ShipFacing);
|
||||
SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], STAMP_PRIM);
|
||||
|
||||
do
|
||||
{
|
||||
ELEMENT *ShipPtr;
|
||||
ElementPtr->current.location.x =
|
||||
WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
|
||||
ElementPtr->current.location.y =
|
||||
WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
|
||||
} while (CalculateGravity (ElementPtr)
|
||||
|| TimeSpaceMatterConflict (ElementPtr));
|
||||
|
||||
LockElement (StarShipPtr->hShip, &ShipPtr);
|
||||
ShipPtr->death_func = new_pkunk;
|
||||
UnlockElement (StarShipPtr->hShip);
|
||||
}
|
||||
else
|
||||
{
|
||||
ElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR;
|
||||
ElementPtr->mass_points = SHIP_MASS;
|
||||
ElementPtr->preprocess_func = StarShipPtr->RaceDescPtr->preprocess_func;
|
||||
ElementPtr->postprocess_func = StarShipPtr->RaceDescPtr->postprocess_func;
|
||||
ElementPtr->death_func =
|
||||
(void (*) (ELEMENT *ElementPtr))
|
||||
StarShipPtr->RaceDescPtr->init_weapon_func;
|
||||
StarShipPtr->RaceDescPtr->preprocess_func = pkunk_preprocess;
|
||||
StarShipPtr->RaceDescPtr->postprocess_func = pkunk_postprocess;
|
||||
StarShipPtr->RaceDescPtr->init_weapon_func = initialize_bug_missile;
|
||||
StarShipPtr->RaceDescPtr->ship_info.crew_level = MAX_CREW;
|
||||
StarShipPtr->RaceDescPtr->ship_info.energy_level = MAX_ENERGY;
|
||||
/* fix vux impairment */
|
||||
StarShipPtr->RaceDescPtr->characteristics.max_thrust = MAX_THRUST;
|
||||
StarShipPtr->RaceDescPtr->characteristics.thrust_increment = THRUST_INCREMENT;
|
||||
StarShipPtr->RaceDescPtr->characteristics.turn_wait = TURN_WAIT;
|
||||
StarShipPtr->RaceDescPtr->characteristics.thrust_wait = THRUST_WAIT;
|
||||
StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
|
||||
// XXX: Hack: Set hTarget!=0 so that ship_preprocess() does not
|
||||
// call ship_transition() for us.
|
||||
ElementPtr->hTarget = StarShipPtr->hShip;
|
||||
}
|
||||
|
||||
StarShipPtr->ship_input_state = 0;
|
||||
StarShipPtr->cur_status_flags = 0;
|
||||
StarShipPtr->old_status_flags = 0;
|
||||
StarShipPtr->energy_counter = 0;
|
||||
StarShipPtr->weapon_counter = 0;
|
||||
StarShipPtr->special_counter = 0;
|
||||
ElementPtr->crew_level = 0;
|
||||
ElementPtr->turn_wait = 0;
|
||||
ElementPtr->thrust_wait = 0;
|
||||
ElementPtr->life_span = NORMAL_LIFE;
|
||||
|
||||
StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ());
|
||||
ElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship;
|
||||
ElementPtr->current.image.frame =
|
||||
SetAbsFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0],
|
||||
StarShipPtr->ShipFacing);
|
||||
SetPrimType (&(GLOBAL (DisplayArray))[
|
||||
ElementPtr->PrimIndex
|
||||
], STAMP_PRIM);
|
||||
|
||||
do
|
||||
{
|
||||
ElementPtr->current.location.x =
|
||||
WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
|
||||
ElementPtr->current.location.y =
|
||||
WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
|
||||
} while (CalculateGravity (ElementPtr)
|
||||
|| TimeSpaceMatterConflict (ElementPtr));
|
||||
|
||||
ElementPtr->hTarget = StarShipPtr->hShip;
|
||||
}
|
||||
// This function is called when the ship dies but reincarnates.
|
||||
// The generic ship_death() function is not called for the ship in this case.
|
||||
static void
|
||||
pkunk_reincarnation_death (ELEMENT *ShipPtr)
|
||||
{
|
||||
// Simulate ship death
|
||||
StopAllBattleMusic ();
|
||||
StartShipExplosion (ShipPtr, true);
|
||||
// Once the explosion ends, we will get a brand new ship
|
||||
ShipPtr->death_func = new_pkunk;
|
||||
}
|
||||
|
||||
static void
|
||||
intercept_pkunk_death (ELEMENT *ElementPtr)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
|
||||
ElementPtr->state_flags &= ~DISAPPEARING;
|
||||
ElementPtr->life_span = 1;
|
||||
PKUNK_DATA *PkunkData;
|
||||
ELEMENT *ShipPtr;
|
||||
|
||||
GetElementStarShip (ElementPtr, &StarShipPtr);
|
||||
if (StarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
|
||||
{
|
||||
ELEMENT *ShipPtr;
|
||||
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
|
||||
|
||||
LockElement (StarShipPtr->hShip, &ShipPtr);
|
||||
if (GRAVITY_MASS (ShipPtr->mass_points + 1))
|
||||
{
|
||||
ElementPtr->state_flags |= DISAPPEARING;
|
||||
ElementPtr->life_span = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShipPtr->mass_points = MAX_SHIP_MASS + 1;
|
||||
StarShipPtr->RaceDescPtr->preprocess_func = ShipPtr->preprocess_func;
|
||||
StarShipPtr->RaceDescPtr->postprocess_func = ShipPtr->postprocess_func;
|
||||
StarShipPtr->RaceDescPtr->init_weapon_func =
|
||||
(COUNT (*) (ELEMENT *ElementPtr, HELEMENT Weapon[]))
|
||||
ShipPtr->death_func;
|
||||
|
||||
ElementPtr->death_func = new_pkunk;
|
||||
}
|
||||
UnlockElement (StarShipPtr->hShip);
|
||||
if (StarShipPtr->RaceDescPtr->ship_info.crew_level != 0)
|
||||
{ // Ship not dead yet.
|
||||
// Keep the Phoenix element alive.
|
||||
ElementPtr->state_flags &= ~DISAPPEARING;
|
||||
ElementPtr->life_span = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
LockElement (StarShipPtr->hShip, &ShipPtr);
|
||||
// GRAVITY_MASS() indicates a warp-out here. If Pkunk dies while warping
|
||||
// out, there is no reincarnation.
|
||||
if (!GRAVITY_MASS (ShipPtr->mass_points + 1))
|
||||
{
|
||||
// XXX: Hack: Set mass_points to indicate a reincarnation to
|
||||
// FindAliveStarShip()
|
||||
ShipPtr->mass_points = MAX_SHIP_MASS + 1;
|
||||
// Save the various element processing callbacks before the
|
||||
// explosion happens, because we were not the ones who set
|
||||
// these callbacks and they are about to be changed.
|
||||
PkunkData->saved_preprocess_func = ShipPtr->preprocess_func;
|
||||
PkunkData->saved_postprocess_func = ShipPtr->postprocess_func;
|
||||
PkunkData->saved_death_func = ShipPtr->death_func;
|
||||
|
||||
ShipPtr->death_func = pkunk_reincarnation_death;
|
||||
}
|
||||
UnlockElement (StarShipPtr->hShip);
|
||||
}
|
||||
|
||||
#define START_PHOENIX_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)
|
||||
@@ -425,15 +444,22 @@ static void
|
||||
pkunk_preprocess (ELEMENT *ElementPtr)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
PKUNK_DATA *PkunkData;
|
||||
|
||||
GetElementStarShip (ElementPtr, &StarShipPtr);
|
||||
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
|
||||
if (ElementPtr->state_flags & APPEARING)
|
||||
{
|
||||
HELEMENT hPhoenix = 0;
|
||||
|
||||
if ((BYTE)TFB_Random () & 1)
|
||||
if (TFB_Random () & 1)
|
||||
hPhoenix = AllocElement ();
|
||||
|
||||
// The hPhoenix element is created and placed at the head of the
|
||||
// queue so that it is preprocessed before any of the ships' elements
|
||||
// are, and so before death_func() is called for the dead Pkunk.
|
||||
// hPhoenix detects when the Pkunk ship dies and tweaks the ship,
|
||||
// starting the death + reincarnation sequence.
|
||||
if (hPhoenix)
|
||||
{
|
||||
ELEMENT *PhoenixPtr;
|
||||
@@ -450,12 +476,17 @@ pkunk_preprocess (ELEMENT *ElementPtr)
|
||||
UnlockElement (hPhoenix);
|
||||
InsertElement (hPhoenix, GetHeadElement ());
|
||||
}
|
||||
StarShipPtr->RaceDescPtr->data = (intptr_t) hPhoenix;
|
||||
PkunkData->hPhoenix = hPhoenix;
|
||||
|
||||
// XXX: Hack: new_pkunk() sets hTarget!=0 which indicates a
|
||||
// reincarnation to us.
|
||||
if (ElementPtr->hTarget == 0)
|
||||
StarShipPtr->RaceDescPtr->preprocess_func = 0;
|
||||
else
|
||||
{
|
||||
// A brand new ship is preprocessed only once
|
||||
StarShipPtr->RaceDescPtr->preprocess_func = 0;
|
||||
}
|
||||
else
|
||||
{ // Start the reincarnation sequence
|
||||
COUNT angle, facing;
|
||||
|
||||
ProcessSound (SetAbsSoundIndex (
|
||||
@@ -538,17 +569,31 @@ pkunk_postprocess (ELEMENT *ElementPtr)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
uninit_pkunk (RACE_DESC *pRaceDesc)
|
||||
{
|
||||
HFree ((void *)pRaceDesc->data);
|
||||
pRaceDesc->data = 0;
|
||||
}
|
||||
|
||||
RACE_DESC*
|
||||
init_pkunk (void)
|
||||
{
|
||||
RACE_DESC *RaceDescPtr;
|
||||
// The caller of this func will copy the struct
|
||||
static RACE_DESC new_pkunk_desc;
|
||||
|
||||
pkunk_desc.uninit_func = uninit_pkunk;
|
||||
pkunk_desc.preprocess_func = pkunk_preprocess;
|
||||
pkunk_desc.postprocess_func = pkunk_postprocess;
|
||||
pkunk_desc.init_weapon_func = initialize_bug_missile;
|
||||
pkunk_desc.cyborg_control.intelligence_func = pkunk_intelligence;
|
||||
|
||||
RaceDescPtr = &pkunk_desc;
|
||||
/* copy initial ship settings to the new descriptor */
|
||||
new_pkunk_desc = pkunk_desc;
|
||||
new_pkunk_desc.data = (intptr_t) HCalloc (sizeof (PKUNK_DATA));
|
||||
|
||||
RaceDescPtr = &new_pkunk_desc;
|
||||
|
||||
LastSound = 0;
|
||||
// We need to reinitialise it at least each battle, to ensure
|
||||
@@ -557,4 +602,3 @@ init_pkunk (void)
|
||||
|
||||
return (RaceDescPtr);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "resinst.h"
|
||||
|
||||
#include "uqm/globdata.h"
|
||||
#include "uqm/tactrans.h"
|
||||
#include "libs/mathlib.h"
|
||||
|
||||
|
||||
@@ -212,10 +213,10 @@ destruct_preprocess (ELEMENT *ElementPtr)
|
||||
PutElement (hDestruct);
|
||||
LockElement (hDestruct, &DestructPtr);
|
||||
SetElementStarShip (DestructPtr, StarShipPtr);
|
||||
DestructPtr->hit_points = DestructPtr->mass_points = 0;
|
||||
DestructPtr->hit_points = 0;
|
||||
DestructPtr->mass_points = 0;
|
||||
DestructPtr->playerNr = NEUTRAL_PLAYER_NUM;
|
||||
DestructPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
|
||||
DestructPtr->life_span = (NUM_EXPLOSION_FRAMES - 3) - 1;
|
||||
SetPrimType (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex],
|
||||
STAMPFILL_PRIM);
|
||||
SetPrimColor (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex],
|
||||
@@ -224,6 +225,8 @@ destruct_preprocess (ELEMENT *ElementPtr)
|
||||
StarShipPtr->RaceDescPtr->ship_data.special;
|
||||
DestructPtr->current.image.frame =
|
||||
StarShipPtr->RaceDescPtr->ship_data.special[0];
|
||||
DestructPtr->life_span = GetFrameCount (
|
||||
DestructPtr->current.image.frame);
|
||||
DestructPtr->current.location = ElementPtr->current.location;
|
||||
DestructPtr->preprocess_func = destruct_preprocess;
|
||||
DestructPtr->postprocess_func = NULL;
|
||||
@@ -240,110 +243,147 @@ destruct_preprocess (ELEMENT *ElementPtr)
|
||||
#define ORZ_MARINE(ptr) (ptr->preprocess_func == intruder_preprocess && \
|
||||
ptr->collision_func == marine_collision)
|
||||
|
||||
// XXX: This function should be split into two
|
||||
static void
|
||||
self_destruct_kill_objects (ELEMENT *ElementPtr)
|
||||
{
|
||||
// This is called during PostProcessQueue(), close to or at the end,
|
||||
// for the temporary destruct element to apply the effects of glory
|
||||
// explosion. The effects are not seen until the next frame.
|
||||
HELEMENT hElement, hNextElement;
|
||||
|
||||
for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement)
|
||||
{
|
||||
ELEMENT *ObjPtr;
|
||||
SIZE delta_x, delta_y;
|
||||
DWORD dist;
|
||||
|
||||
LockElement (hElement, &ObjPtr);
|
||||
hNextElement = GetSuccElement (ObjPtr);
|
||||
|
||||
if (!CollidingElement (ObjPtr) && !ORZ_MARINE (ObjPtr))
|
||||
{
|
||||
UnlockElement (hElement);
|
||||
continue;
|
||||
}
|
||||
|
||||
#define DESTRUCT_RANGE 180
|
||||
delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x;
|
||||
if (delta_x < 0)
|
||||
delta_x = -delta_x;
|
||||
delta_y = ObjPtr->next.location.y - ElementPtr->next.location.y;
|
||||
if (delta_y < 0)
|
||||
delta_y = -delta_y;
|
||||
delta_x = WORLD_TO_DISPLAY (delta_x);
|
||||
delta_y = WORLD_TO_DISPLAY (delta_y);
|
||||
dist = delta_x * delta_x + delta_y * delta_y;
|
||||
if (delta_x <= DESTRUCT_RANGE && delta_y <= DESTRUCT_RANGE
|
||||
&& dist <= DESTRUCT_RANGE * DESTRUCT_RANGE)
|
||||
{
|
||||
#define MAX_DESTRUCTION (DESTRUCT_RANGE / 10)
|
||||
int destruction = 1 + MAX_DESTRUCTION *
|
||||
(DESTRUCT_RANGE - square_root (dist)) / DESTRUCT_RANGE;
|
||||
|
||||
// XXX: Why not simply call do_damage()?
|
||||
if (ObjPtr->state_flags & PLAYER_SHIP)
|
||||
{
|
||||
if (!DeltaCrew (ObjPtr, -destruction))
|
||||
ObjPtr->life_span = 0;
|
||||
}
|
||||
else if (!GRAVITY_MASS (ObjPtr->mass_points))
|
||||
{
|
||||
if (destruction < ObjPtr->hit_points)
|
||||
ObjPtr->hit_points -= destruction;
|
||||
else
|
||||
{
|
||||
ObjPtr->hit_points = 0;
|
||||
ObjPtr->life_span = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnlockElement (hElement);
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called when the ship dies via Glory Device.
|
||||
// The generic ship_death() function is not called for the ship in this case.
|
||||
static void
|
||||
shofixti_destruct_death (ELEMENT *ShipPtr)
|
||||
{
|
||||
STARSHIP *StarShip;
|
||||
STARSHIP *winner;
|
||||
|
||||
GetElementStarShip (ShipPtr, &StarShip);
|
||||
|
||||
StopAllBattleMusic ();
|
||||
|
||||
StartShipExplosion (ShipPtr, false);
|
||||
// We process the explosion ourselves because it is different
|
||||
ShipPtr->preprocess_func = destruct_preprocess;
|
||||
|
||||
PlaySound (SetAbsSoundIndex (StarShip->RaceDescPtr->ship_data.ship_sounds,
|
||||
1), CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1);
|
||||
|
||||
winner = GetWinnerStarShip ();
|
||||
if (winner == NULL)
|
||||
{ // No winner determined yet
|
||||
winner = FindAliveStarShip (ShipPtr);
|
||||
if (winner == NULL)
|
||||
{ // No ships left alive after the Glory Device thus Shofixti wins
|
||||
winner = StarShip;
|
||||
}
|
||||
SetWinnerStarShip (winner);
|
||||
}
|
||||
else if (winner == StarShip)
|
||||
{ // This ship is the winner
|
||||
// It may have self-destructed before the ditty started playing,
|
||||
// and in that case, there should be no ditty
|
||||
StarShip->cur_status_flags &= ~PLAY_VICTORY_DITTY;
|
||||
}
|
||||
RecordShipDeath (ShipPtr);
|
||||
}
|
||||
|
||||
static void
|
||||
self_destruct (ELEMENT *ElementPtr)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
HELEMENT hDestruct;
|
||||
|
||||
GetElementStarShip (ElementPtr, &StarShipPtr);
|
||||
if (ElementPtr->state_flags & PLAYER_SHIP)
|
||||
|
||||
// Spawn a temporary element, which dies in this same frame, in order
|
||||
// to defer the effects of the glory explosion.
|
||||
// It will be the last element (or one of the last) for which the
|
||||
// death_func() will be called from PostProcessQueue() in this frame.
|
||||
// XXX: Why at the end? Why not just do it now?
|
||||
hDestruct = AllocElement ();
|
||||
if (hDestruct)
|
||||
{
|
||||
HELEMENT hDestruct;
|
||||
ELEMENT *DestructPtr;
|
||||
|
||||
// Spawn a temporary element, which dies in this same frame, in order
|
||||
// to defer the effects of the glory explosion.
|
||||
// It will be the last element (or one of the last) for which the
|
||||
// death_func will be called from PostProcessQueue() in this frame.
|
||||
hDestruct = AllocElement ();
|
||||
if (hDestruct)
|
||||
{
|
||||
ELEMENT *DestructPtr;
|
||||
LockElement (hDestruct, &DestructPtr);
|
||||
DestructPtr->playerNr = ElementPtr->playerNr;
|
||||
DestructPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
|
||||
DestructPtr->next.location = ElementPtr->next.location;
|
||||
DestructPtr->life_span = 0;
|
||||
DestructPtr->pParent = ElementPtr->pParent;
|
||||
DestructPtr->hTarget = 0;
|
||||
|
||||
LockElement (hDestruct, &DestructPtr);
|
||||
DestructPtr->playerNr = ElementPtr->playerNr;
|
||||
DestructPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
|
||||
DestructPtr->next.location = ElementPtr->next.location;
|
||||
DestructPtr->life_span = 0;
|
||||
DestructPtr->pParent = ElementPtr->pParent;
|
||||
DestructPtr->hTarget = 0;
|
||||
DestructPtr->death_func = self_destruct_kill_objects;
|
||||
|
||||
DestructPtr->death_func = self_destruct;
|
||||
UnlockElement (hDestruct);
|
||||
|
||||
UnlockElement (hDestruct);
|
||||
|
||||
PutElement (hDestruct);
|
||||
}
|
||||
|
||||
ElementPtr->state_flags |= NONSOLID;
|
||||
// The ship is now dead. It's death_func, i.e. ship_death(), will be
|
||||
// called the next frame.
|
||||
ElementPtr->life_span = 0;
|
||||
|
||||
ElementPtr->preprocess_func = destruct_preprocess;
|
||||
PutElement (hDestruct);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is called during PostProcessQueue(), close to or at the end,
|
||||
// for the temporary destruct element to apply the effects of glory
|
||||
// explosion. The effects are not seen until the next frame.
|
||||
HELEMENT hElement, hNextElement;
|
||||
|
||||
for (hElement = GetHeadElement ();
|
||||
hElement != 0; hElement = hNextElement)
|
||||
{
|
||||
ELEMENT *ObjPtr;
|
||||
// Must kill off the remaining crew ourselves
|
||||
DeltaCrew (ElementPtr, -(int)ElementPtr->crew_level);
|
||||
|
||||
LockElement (hElement, &ObjPtr);
|
||||
hNextElement = GetSuccElement (ObjPtr);
|
||||
|
||||
if (CollidingElement (ObjPtr) || ORZ_MARINE (ObjPtr))
|
||||
{
|
||||
#define DESTRUCT_RANGE 180
|
||||
SIZE delta_x, delta_y;
|
||||
DWORD dist;
|
||||
|
||||
if ((delta_x = ObjPtr->next.location.x
|
||||
- ElementPtr->next.location.x) < 0)
|
||||
delta_x = -delta_x;
|
||||
if ((delta_y = ObjPtr->next.location.y
|
||||
- ElementPtr->next.location.y) < 0)
|
||||
delta_y = -delta_y;
|
||||
delta_x = WORLD_TO_DISPLAY (delta_x);
|
||||
delta_y = WORLD_TO_DISPLAY (delta_y);
|
||||
if (delta_x <= DESTRUCT_RANGE && delta_y <= DESTRUCT_RANGE
|
||||
&& (dist = (DWORD)(delta_x * delta_x)
|
||||
+ (DWORD)(delta_y * delta_y)) <=
|
||||
(DWORD)(DESTRUCT_RANGE * DESTRUCT_RANGE))
|
||||
{
|
||||
#define MAX_DESTRUCTION (DESTRUCT_RANGE / 10)
|
||||
SIZE destruction;
|
||||
|
||||
destruction = ((MAX_DESTRUCTION
|
||||
* (DESTRUCT_RANGE - square_root (dist)))
|
||||
/ DESTRUCT_RANGE) + 1;
|
||||
|
||||
if (ObjPtr->state_flags & PLAYER_SHIP)
|
||||
{
|
||||
if (!DeltaCrew (ObjPtr, -destruction))
|
||||
ObjPtr->life_span = 0;
|
||||
}
|
||||
else if (!GRAVITY_MASS (ObjPtr->mass_points))
|
||||
{
|
||||
if ((BYTE)destruction < ObjPtr->hit_points)
|
||||
ObjPtr->hit_points -= (BYTE)destruction;
|
||||
else
|
||||
{
|
||||
ObjPtr->hit_points = 0;
|
||||
ObjPtr->life_span = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnlockElement (hElement);
|
||||
}
|
||||
}
|
||||
ElementPtr->state_flags |= NONSOLID;
|
||||
ElementPtr->life_span = 0;
|
||||
// The ship is now dead. It's death_func, i.e. shofixti_destruct_death(),
|
||||
// will be called the next frame.
|
||||
ElementPtr->death_func = shofixti_destruct_death;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -410,7 +450,7 @@ RACE_DESC*
|
||||
init_shofixti (void)
|
||||
{
|
||||
RACE_DESC *RaceDescPtr;
|
||||
|
||||
// The caller of this func will copy the struct
|
||||
static RACE_DESC new_shofixti_desc;
|
||||
|
||||
shofixti_desc.postprocess_func = shofixti_postprocess;
|
||||
|
||||
@@ -848,8 +848,8 @@ RACE_DESC*
|
||||
init_sis (void)
|
||||
{
|
||||
RACE_DESC *RaceDescPtr;
|
||||
|
||||
COUNT i;
|
||||
// The caller of this func will copy the struct
|
||||
static RACE_DESC new_sis_desc;
|
||||
|
||||
/* copy initial ship settings to new_sis_desc */
|
||||
|
||||
+95
-48
@@ -47,6 +47,8 @@ static void cleanup_dead_ship (ELEMENT *ElementPtr);
|
||||
|
||||
static BOOLEAN dittyIsPlaying;
|
||||
static STARSHIP *winnerStarShip;
|
||||
// Indicates which ship is the winner of the current battle.
|
||||
// The winner will be last to pick the next ship.
|
||||
|
||||
|
||||
BOOLEAN
|
||||
@@ -617,38 +619,37 @@ explosion_preprocess (ELEMENT *ShipPtr)
|
||||
}
|
||||
|
||||
void
|
||||
ship_death (ELEMENT *ShipPtr)
|
||||
StopAllBattleMusic (void)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
STARSHIP *VictoriousStarShipPtr;
|
||||
HELEMENT hElement, hNextElement;
|
||||
ELEMENT *ElementPtr;
|
||||
|
||||
StopDitty ();
|
||||
StopMusic ();
|
||||
}
|
||||
|
||||
GetElementStarShip (ShipPtr, &StarShipPtr);
|
||||
STARSHIP *
|
||||
FindAliveStarShip (ELEMENT *deadShip)
|
||||
{
|
||||
STARSHIP *aliveShip = NULL;
|
||||
HELEMENT hElement, hNextElement;
|
||||
|
||||
if (ShipPtr->mass_points <= MAX_SHIP_MASS)
|
||||
{ // Not running away and not reincarnating (Pkunk)
|
||||
// When a ship tries to run away, it is (dis)counted in DoRunAway(),
|
||||
// so when it dies while running away, we will not count it again
|
||||
assert (StarShipPtr->playerNr >= 0);
|
||||
battle_counter[StarShipPtr->playerNr]--;
|
||||
}
|
||||
|
||||
VictoriousStarShipPtr = NULL;
|
||||
// Find the remaining ship, if any, and see if it is still alive.
|
||||
for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
|
||||
{
|
||||
ELEMENT *ElementPtr;
|
||||
|
||||
LockElement (hElement, &ElementPtr);
|
||||
if ((ElementPtr->state_flags & PLAYER_SHIP)
|
||||
&& ElementPtr != ShipPtr
|
||||
&& ElementPtr != deadShip
|
||||
/* and not running away */
|
||||
&& ElementPtr->mass_points <= MAX_SHIP_MASS)
|
||||
&& ElementPtr->mass_points <= MAX_SHIP_MASS + 1)
|
||||
{
|
||||
GetElementStarShip (ElementPtr, &VictoriousStarShipPtr);
|
||||
if (VictoriousStarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
|
||||
VictoriousStarShipPtr = NULL;
|
||||
GetElementStarShip (ElementPtr, &aliveShip);
|
||||
assert (aliveShip != NULL);
|
||||
if (aliveShip->RaceDescPtr->ship_info.crew_level == 0
|
||||
/* reincarnating Pkunk is not actually dead */
|
||||
&& ElementPtr->mass_points != MAX_SHIP_MASS + 1)
|
||||
{
|
||||
aliveShip = NULL;
|
||||
}
|
||||
|
||||
UnlockElement (hElement);
|
||||
break;
|
||||
@@ -657,7 +658,58 @@ ship_death (ELEMENT *ShipPtr)
|
||||
UnlockElement (hElement);
|
||||
}
|
||||
|
||||
StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY;
|
||||
return aliveShip;
|
||||
}
|
||||
|
||||
STARSHIP *
|
||||
GetWinnerStarShip (void)
|
||||
{
|
||||
return winnerStarShip;
|
||||
}
|
||||
|
||||
void
|
||||
SetWinnerStarShip (STARSHIP *winner)
|
||||
{
|
||||
if (winner == NULL)
|
||||
return; // nothing to do
|
||||
|
||||
winner->cur_status_flags |= PLAY_VICTORY_DITTY;
|
||||
|
||||
// The winner is set once per battle. If both ships die, this function is
|
||||
// called twice, once for each ship. We need to preserve the winner
|
||||
// determined on the first call.
|
||||
if (winnerStarShip == NULL)
|
||||
winnerStarShip = winner;
|
||||
}
|
||||
|
||||
void
|
||||
RecordShipDeath (ELEMENT *deadShip)
|
||||
{
|
||||
STARSHIP *deadStarShip;
|
||||
|
||||
GetElementStarShip (deadShip, &deadStarShip);
|
||||
assert (deadStarShip != NULL);
|
||||
|
||||
if (deadShip->mass_points <= MAX_SHIP_MASS)
|
||||
{ // Not running away.
|
||||
// When a ship tries to run away, it is (dis)counted in DoRunAway(),
|
||||
// so when it dies while running away, we will not count it again
|
||||
assert (deadStarShip->playerNr >= 0);
|
||||
battle_counter[deadStarShip->playerNr]--;
|
||||
}
|
||||
|
||||
if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
|
||||
MeleeShipDeath (deadStarShip);
|
||||
}
|
||||
|
||||
void
|
||||
StartShipExplosion (ELEMENT *ShipPtr, bool playSound)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
|
||||
GetElementStarShip (ShipPtr, &StarShipPtr);
|
||||
|
||||
ZeroVelocityComponents (&ShipPtr->velocity);
|
||||
|
||||
DeltaEnergy (ShipPtr,
|
||||
-(SIZE)StarShipPtr->RaceDescPtr->ship_info.energy_level);
|
||||
@@ -665,43 +717,38 @@ ship_death (ELEMENT *ShipPtr)
|
||||
ShipPtr->life_span = NUM_EXPLOSION_FRAMES * 3;
|
||||
ShipPtr->state_flags &= ~DISAPPEARING;
|
||||
ShipPtr->state_flags |= FINITE_LIFE | NONSOLID;
|
||||
ShipPtr->preprocess_func = explosion_preprocess;
|
||||
ShipPtr->postprocess_func = PostProcessStatus;
|
||||
ShipPtr->death_func = cleanup_dead_ship;
|
||||
ShipPtr->hTarget = 0;
|
||||
ZeroVelocityComponents (&ShipPtr->velocity);
|
||||
if (ShipPtr->crew_level) /* only happens for shofixti self-destruct */
|
||||
{
|
||||
PlaySound (SetAbsSoundIndex (
|
||||
StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
|
||||
CalcSoundPosition (ShipPtr), ShipPtr,
|
||||
GAME_SOUND_PRIORITY + 1);
|
||||
|
||||
DeltaCrew (ShipPtr, -(SIZE)ShipPtr->crew_level);
|
||||
if (VictoriousStarShipPtr == NULL)
|
||||
{ // No ships left alive after a Shofixti Glory device,
|
||||
// thus Shofixti wins
|
||||
VictoriousStarShipPtr = StarShipPtr;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (playSound)
|
||||
{
|
||||
ShipPtr->preprocess_func = explosion_preprocess;
|
||||
|
||||
PlaySound (SetAbsSoundIndex (GameSounds, SHIP_EXPLODES),
|
||||
CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (VictoriousStarShipPtr != NULL)
|
||||
VictoriousStarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY;
|
||||
void
|
||||
ship_death (ELEMENT *ShipPtr)
|
||||
{
|
||||
STARSHIP *StarShipPtr;
|
||||
STARSHIP *winner;
|
||||
|
||||
// The winner is set once per battle. If both ships die, this function is
|
||||
// called twice, once for each ship. We need to preserve the winner
|
||||
// determined on the first call.
|
||||
if (winnerStarShip == NULL)
|
||||
winnerStarShip = VictoriousStarShipPtr;
|
||||
GetElementStarShip (ShipPtr, &StarShipPtr);
|
||||
|
||||
if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
|
||||
MeleeShipDeath (StarShipPtr);
|
||||
StopAllBattleMusic ();
|
||||
|
||||
// If the winning ship dies before the ditty starts, do not play it.
|
||||
// e.g. a ship can die after the opponent begins exploding but
|
||||
// before the explosion is over.
|
||||
StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY;
|
||||
|
||||
StartShipExplosion (ShipPtr, true);
|
||||
|
||||
winner = FindAliveStarShip (ShipPtr);
|
||||
SetWinnerStarShip (winner);
|
||||
RecordShipDeath (ShipPtr);
|
||||
}
|
||||
|
||||
#define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)
|
||||
|
||||
@@ -43,6 +43,12 @@ extern void flee_preprocess (ELEMENT *ElementPtr);
|
||||
|
||||
extern void StopDitty (void);
|
||||
extern void ResetWinnerStarShip (void);
|
||||
extern void StopAllBattleMusic (void);
|
||||
extern STARSHIP* FindAliveStarShip (ELEMENT *deadShip);
|
||||
extern STARSHIP* GetWinnerStarShip (void);
|
||||
extern void SetWinnerStarShip (STARSHIP *winner);
|
||||
extern void RecordShipDeath (ELEMENT *deadShip);
|
||||
extern void StartShipExplosion (ELEMENT *ShipPtr, bool playSound);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ initialize_laser (LASER_BLOCK *pLaserBlock)
|
||||
LaserElementPtr->state_flags = APPEARING | FINITE_LIFE
|
||||
| pLaserBlock->flags;
|
||||
LaserElementPtr->life_span = LASER_LIFE;
|
||||
LaserElementPtr->collision_func = (CollisionFunc*)weapon_collision;
|
||||
LaserElementPtr->collision_func = weapon_collision;
|
||||
LaserElementPtr->blast_offset = 1;
|
||||
|
||||
LaserElementPtr->current.location.x = pLaserBlock->cx
|
||||
@@ -100,7 +100,7 @@ initialize_missile (MISSILE_BLOCK *pMissileBlock)
|
||||
SetAbsFrameIndex (pMissileBlock->farray[0],
|
||||
pMissileBlock->index);
|
||||
MissileElementPtr->preprocess_func = pMissileBlock->preprocess_func;
|
||||
MissileElementPtr->collision_func = (CollisionFunc*)weapon_collision;
|
||||
MissileElementPtr->collision_func = weapon_collision;
|
||||
MissileElementPtr->blast_offset = (BYTE)pMissileBlock->blast_offs;
|
||||
|
||||
angle = FACING_TO_ANGLE (pMissileBlock->face);
|
||||
|
||||
Reference in New Issue
Block a user