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:
avolkov
2011-07-26 20:30:58 +00:00
parent 8ab9c69b55
commit 35281c14c0
12 changed files with 391 additions and 247 deletions
+2
View File
@@ -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
+6 -5
View File
@@ -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.)
+1
View File
@@ -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);
+3
View File
@@ -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);
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+102 -58
View File
@@ -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,34 +219,26 @@ 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))
{
ELEMENT *ShipPtr;
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
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;
// 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->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 */
@@ -246,7 +249,8 @@ new_pkunk (ELEMENT *ElementPtr)
StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
StarShipPtr->ship_input_state = 0;
StarShipPtr->cur_status_flags = 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;
@@ -258,12 +262,10 @@ new_pkunk (ELEMENT *ElementPtr)
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],
ElementPtr->current.image.frame = SetAbsFrameIndex (
StarShipPtr->RaceDescPtr->ship_data.ship[0],
StarShipPtr->ShipFacing);
SetPrimType (&(GLOBAL (DisplayArray))[
ElementPtr->PrimIndex
], STAMP_PRIM);
SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], STAMP_PRIM);
do
{
@@ -274,43 +276,60 @@ new_pkunk (ELEMENT *ElementPtr)
} while (CalculateGravity (ElementPtr)
|| TimeSpaceMatterConflict (ElementPtr));
// XXX: Hack: Set hTarget!=0 so that ship_preprocess() does not
// call ship_transition() for us.
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;
GetElementStarShip (ElementPtr, &StarShipPtr);
if (StarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
{
PKUNK_DATA *PkunkData;
ELEMENT *ShipPtr;
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;
GetElementStarShip (ElementPtr, &StarShipPtr);
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
ElementPtr->death_func = new_pkunk;
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)
#define TRANSITION_LIFE 1
@@ -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);
}
+116 -76
View File
@@ -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,21 +243,119 @@ 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)
{
HELEMENT hDestruct;
// 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.
// 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)
{
@@ -268,82 +369,21 @@ self_destruct (ELEMENT *ElementPtr)
DestructPtr->pParent = ElementPtr->pParent;
DestructPtr->hTarget = 0;
DestructPtr->death_func = self_destruct;
DestructPtr->death_func = self_destruct_kill_objects;
UnlockElement (hDestruct);
PutElement (hDestruct);
}
// Must kill off the remaining crew ourselves
DeltaCrew (ElementPtr, -(int)ElementPtr->crew_level);
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;
}
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;
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);
}
}
// 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;
+1 -1
View File
@@ -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
View File
@@ -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);
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;
STARSHIP *
FindAliveStarShip (ELEMENT *deadShip)
{
STARSHIP *aliveShip = NULL;
HELEMENT hElement, hNextElement;
// 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)
+6
View File
@@ -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)
}
+2 -2
View File
@@ -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);