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: 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 - Preparing for linking with C++ code, from Scott A. Colcord
- Fixed player's phrase leading to Tanaka's response about a solitary - Fixed player's phrase leading to Tanaka's response about a solitary
vigil (bug #859) - Alex vigil (bug #859) - Alex
+6 -5
View File
@@ -96,7 +96,8 @@ typedef struct state
typedef struct element ELEMENT; 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); ELEMENT *ElementPtr1, POINT *pPt1);
// Any physical object in the simulation. // Any physical object in the simulation.
@@ -105,10 +106,10 @@ struct element
// LINK elements; must be first // LINK elements; must be first
HELEMENT pred, succ; HELEMENT pred, succ;
void (*preprocess_func) (struct element *ElementPtr); ElementProcessFunc *preprocess_func;
void (*postprocess_func) (struct element *ElementPtr); ElementProcessFunc *postprocess_func;
CollisionFunc *collision_func; ElementCollisionFunc *collision_func;
void (*death_func) (struct element *ElementPtr); ElementProcessFunc *death_func;
// Player this element belongs to // Player this element belongs to
// -1: neutral (planets, asteroids, crew, etc.) // -1: neutral (planets, asteroids, crew, etc.)
+1
View File
@@ -1660,6 +1660,7 @@ CreateStarBackGround (void)
RECT clipRect; RECT clipRect;
FRAME frame; FRAME frame;
// Use SpaceContext to find out the dimensions of the background
oldContext = SetContext (SpaceContext); oldContext = SetContext (SpaceContext);
GetContextClipRect (&clipRect); GetContextClipRect (&clipRect);
+3
View File
@@ -193,6 +193,9 @@ ship_preprocess (ELEMENT *ElementPtr)
if (RDPtr->preprocess_func) if (RDPtr->preprocess_func)
(*RDPtr->preprocess_func) (ElementPtr); (*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) if (ElementPtr->hTarget == 0)
{ {
ship_transition (ElementPtr); ship_transition (ElementPtr);
+1 -1
View File
@@ -426,7 +426,7 @@ androsynth_preprocess (ELEMENT *ElementPtr)
StarShipPtr->RaceDescPtr->characteristics.special_wait; StarShipPtr->RaceDescPtr->characteristics.special_wait;
StarShipPtr->RaceDescPtr->characteristics.energy_regeneration = ENERGY_REGENERATION; StarShipPtr->RaceDescPtr->characteristics.energy_regeneration = ENERGY_REGENERATION;
ElementPtr->mass_points = SHIP_MASS; ElementPtr->mass_points = SHIP_MASS;
ElementPtr->collision_func = (CollisionFunc *) ElementPtr->collision_func = (ElementCollisionFunc *)
StarShipPtr->RaceDescPtr->data; StarShipPtr->RaceDescPtr->data;
ElementPtr->next.image.farray = ElementPtr->next.image.farray =
StarShipPtr->RaceDescPtr->ship_data.ship; StarShipPtr->RaceDescPtr->ship_data.ship;
+1 -1
View File
@@ -452,7 +452,7 @@ RACE_DESC*
init_mmrnmhrm (void) init_mmrnmhrm (void)
{ {
RACE_DESC *RaceDescPtr; RACE_DESC *RaceDescPtr;
// The caller of this func will copy the struct
static RACE_DESC new_mmrnmhrm_desc; static RACE_DESC new_mmrnmhrm_desc;
CHARACTERISTIC_STUFF *otherwing_desc; CHARACTERISTIC_STUFF *otherwing_desc;
+103 -59
View File
@@ -21,6 +21,7 @@
#include "resinst.h" #include "resinst.h"
#include "uqm/globdata.h" #include "uqm/globdata.h"
#include "uqm/tactrans.h"
#include "libs/mathlib.h" #include "libs/mathlib.h"
@@ -112,6 +113,16 @@ static RACE_DESC pkunk_desc =
0, /* CodeRef */ 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 static void
animate (ELEMENT *ElementPtr) animate (ELEMENT *ElementPtr)
{ {
@@ -186,15 +197,15 @@ pkunk_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
COUNT ConcernCounter) COUNT ConcernCounter)
{ {
STARSHIP *StarShipPtr; STARSHIP *StarShipPtr;
HELEMENT hPhoenix; PKUNK_DATA *PkunkData;
GetElementStarShip (ShipPtr, &StarShipPtr); GetElementStarShip (ShipPtr, &StarShipPtr);
hPhoenix = (HELEMENT) StarShipPtr->RaceDescPtr->data; PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
if (hPhoenix && (StarShipPtr->control & STANDARD_RATING)) if (PkunkData->hPhoenix && (StarShipPtr->control & STANDARD_RATING))
{ {
RemoveElement (hPhoenix); RemoveElement (PkunkData->hPhoenix);
FreeElement (hPhoenix); FreeElement (PkunkData->hPhoenix);
StarShipPtr->RaceDescPtr->data = 0; PkunkData->hPhoenix = 0;
} }
if (StarShipPtr->RaceDescPtr->ship_info.energy_level < 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_preprocess (ELEMENT *ElementPtr);
static void pkunk_postprocess (ELEMENT *ElementPtr);
static void static void
new_pkunk (ELEMENT *ElementPtr) new_pkunk (ELEMENT *ElementPtr)
{ {
STARSHIP *StarShipPtr; STARSHIP *StarShipPtr;
PKUNK_DATA *PkunkData;
GetElementStarShip (ElementPtr, &StarShipPtr); GetElementStarShip (ElementPtr, &StarShipPtr);
if (!(ElementPtr->state_flags & PLAYER_SHIP)) PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
{
ELEMENT *ShipPtr;
LockElement (StarShipPtr->hShip, &ShipPtr);
ShipPtr->death_func = new_pkunk;
UnlockElement (StarShipPtr->hShip);
}
else
{
ElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR; ElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR;
ElementPtr->mass_points = SHIP_MASS; ElementPtr->mass_points = SHIP_MASS;
ElementPtr->preprocess_func = StarShipPtr->RaceDescPtr->preprocess_func; // Restore the element processing callbacks after the explosion.
ElementPtr->postprocess_func = StarShipPtr->RaceDescPtr->postprocess_func; // The callbacks were changed for the explosion sequence
ElementPtr->death_func = ElementPtr->preprocess_func = PkunkData->saved_preprocess_func;
(void (*) (ELEMENT *ElementPtr)) ElementPtr->postprocess_func = PkunkData->saved_postprocess_func;
StarShipPtr->RaceDescPtr->init_weapon_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->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.crew_level = MAX_CREW;
StarShipPtr->RaceDescPtr->ship_info.energy_level = MAX_ENERGY; StarShipPtr->RaceDescPtr->ship_info.energy_level = MAX_ENERGY;
/* fix vux impairment */ /* fix vux impairment */
@@ -246,7 +249,8 @@ new_pkunk (ELEMENT *ElementPtr)
StarShipPtr->RaceDescPtr->characteristics.special_wait = 0; StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
StarShipPtr->ship_input_state = 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->old_status_flags = 0;
StarShipPtr->energy_counter = 0; StarShipPtr->energy_counter = 0;
StarShipPtr->weapon_counter = 0; StarShipPtr->weapon_counter = 0;
@@ -258,12 +262,10 @@ new_pkunk (ELEMENT *ElementPtr)
StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ()); StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ());
ElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship; ElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship;
ElementPtr->current.image.frame = ElementPtr->current.image.frame = SetAbsFrameIndex (
SetAbsFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0], StarShipPtr->RaceDescPtr->ship_data.ship[0],
StarShipPtr->ShipFacing); StarShipPtr->ShipFacing);
SetPrimType (&(GLOBAL (DisplayArray))[ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], STAMP_PRIM);
ElementPtr->PrimIndex
], STAMP_PRIM);
do do
{ {
@@ -274,42 +276,59 @@ new_pkunk (ELEMENT *ElementPtr)
} while (CalculateGravity (ElementPtr) } while (CalculateGravity (ElementPtr)
|| TimeSpaceMatterConflict (ElementPtr)); || TimeSpaceMatterConflict (ElementPtr));
// XXX: Hack: Set hTarget!=0 so that ship_preprocess() does not
// call ship_transition() for us.
ElementPtr->hTarget = StarShipPtr->hShip; 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 static void
intercept_pkunk_death (ELEMENT *ElementPtr) intercept_pkunk_death (ELEMENT *ElementPtr)
{ {
STARSHIP *StarShipPtr; STARSHIP *StarShipPtr;
PKUNK_DATA *PkunkData;
ElementPtr->state_flags &= ~DISAPPEARING;
ElementPtr->life_span = 1;
GetElementStarShip (ElementPtr, &StarShipPtr);
if (StarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
{
ELEMENT *ShipPtr; ELEMENT *ShipPtr;
LockElement (StarShipPtr->hShip, &ShipPtr); GetElementStarShip (ElementPtr, &StarShipPtr);
if (GRAVITY_MASS (ShipPtr->mass_points + 1)) PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
{
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; 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); UnlockElement (StarShipPtr->hShip);
}
} }
#define START_PHOENIX_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A) #define START_PHOENIX_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)
@@ -425,15 +444,22 @@ static void
pkunk_preprocess (ELEMENT *ElementPtr) pkunk_preprocess (ELEMENT *ElementPtr)
{ {
STARSHIP *StarShipPtr; STARSHIP *StarShipPtr;
PKUNK_DATA *PkunkData;
GetElementStarShip (ElementPtr, &StarShipPtr); GetElementStarShip (ElementPtr, &StarShipPtr);
PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data;
if (ElementPtr->state_flags & APPEARING) if (ElementPtr->state_flags & APPEARING)
{ {
HELEMENT hPhoenix = 0; HELEMENT hPhoenix = 0;
if ((BYTE)TFB_Random () & 1) if (TFB_Random () & 1)
hPhoenix = AllocElement (); 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) if (hPhoenix)
{ {
ELEMENT *PhoenixPtr; ELEMENT *PhoenixPtr;
@@ -450,12 +476,17 @@ pkunk_preprocess (ELEMENT *ElementPtr)
UnlockElement (hPhoenix); UnlockElement (hPhoenix);
InsertElement (hPhoenix, GetHeadElement ()); 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) 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; COUNT angle, facing;
ProcessSound (SetAbsSoundIndex ( 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* RACE_DESC*
init_pkunk (void) init_pkunk (void)
{ {
RACE_DESC *RaceDescPtr; 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.preprocess_func = pkunk_preprocess;
pkunk_desc.postprocess_func = pkunk_postprocess; pkunk_desc.postprocess_func = pkunk_postprocess;
pkunk_desc.init_weapon_func = initialize_bug_missile; pkunk_desc.init_weapon_func = initialize_bug_missile;
pkunk_desc.cyborg_control.intelligence_func = pkunk_intelligence; 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; LastSound = 0;
// We need to reinitialise it at least each battle, to ensure // We need to reinitialise it at least each battle, to ensure
@@ -557,4 +602,3 @@ init_pkunk (void)
return (RaceDescPtr); return (RaceDescPtr);
} }
+116 -76
View File
@@ -21,6 +21,7 @@
#include "resinst.h" #include "resinst.h"
#include "uqm/globdata.h" #include "uqm/globdata.h"
#include "uqm/tactrans.h"
#include "libs/mathlib.h" #include "libs/mathlib.h"
@@ -212,10 +213,10 @@ destruct_preprocess (ELEMENT *ElementPtr)
PutElement (hDestruct); PutElement (hDestruct);
LockElement (hDestruct, &DestructPtr); LockElement (hDestruct, &DestructPtr);
SetElementStarShip (DestructPtr, StarShipPtr); SetElementStarShip (DestructPtr, StarShipPtr);
DestructPtr->hit_points = DestructPtr->mass_points = 0; DestructPtr->hit_points = 0;
DestructPtr->mass_points = 0;
DestructPtr->playerNr = NEUTRAL_PLAYER_NUM; DestructPtr->playerNr = NEUTRAL_PLAYER_NUM;
DestructPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID; DestructPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
DestructPtr->life_span = (NUM_EXPLOSION_FRAMES - 3) - 1;
SetPrimType (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex], SetPrimType (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex],
STAMPFILL_PRIM); STAMPFILL_PRIM);
SetPrimColor (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex], SetPrimColor (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex],
@@ -224,6 +225,8 @@ destruct_preprocess (ELEMENT *ElementPtr)
StarShipPtr->RaceDescPtr->ship_data.special; StarShipPtr->RaceDescPtr->ship_data.special;
DestructPtr->current.image.frame = DestructPtr->current.image.frame =
StarShipPtr->RaceDescPtr->ship_data.special[0]; StarShipPtr->RaceDescPtr->ship_data.special[0];
DestructPtr->life_span = GetFrameCount (
DestructPtr->current.image.frame);
DestructPtr->current.location = ElementPtr->current.location; DestructPtr->current.location = ElementPtr->current.location;
DestructPtr->preprocess_func = destruct_preprocess; DestructPtr->preprocess_func = destruct_preprocess;
DestructPtr->postprocess_func = NULL; DestructPtr->postprocess_func = NULL;
@@ -240,21 +243,119 @@ destruct_preprocess (ELEMENT *ElementPtr)
#define ORZ_MARINE(ptr) (ptr->preprocess_func == intruder_preprocess && \ #define ORZ_MARINE(ptr) (ptr->preprocess_func == intruder_preprocess && \
ptr->collision_func == marine_collision) 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 static void
self_destruct (ELEMENT *ElementPtr) self_destruct (ELEMENT *ElementPtr)
{ {
STARSHIP *StarShipPtr; STARSHIP *StarShipPtr;
HELEMENT hDestruct;
GetElementStarShip (ElementPtr, &StarShipPtr); GetElementStarShip (ElementPtr, &StarShipPtr);
if (ElementPtr->state_flags & PLAYER_SHIP)
{
HELEMENT hDestruct;
// Spawn a temporary element, which dies in this same frame, in order // Spawn a temporary element, which dies in this same frame, in order
// to defer the effects of the glory explosion. // to defer the effects of the glory explosion.
// It will be the last element (or one of the last) for which the // 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 (); hDestruct = AllocElement ();
if (hDestruct) if (hDestruct)
{ {
@@ -268,82 +369,21 @@ self_destruct (ELEMENT *ElementPtr)
DestructPtr->pParent = ElementPtr->pParent; DestructPtr->pParent = ElementPtr->pParent;
DestructPtr->hTarget = 0; DestructPtr->hTarget = 0;
DestructPtr->death_func = self_destruct; DestructPtr->death_func = self_destruct_kill_objects;
UnlockElement (hDestruct); UnlockElement (hDestruct);
PutElement (hDestruct); PutElement (hDestruct);
} }
// Must kill off the remaining crew ourselves
DeltaCrew (ElementPtr, -(int)ElementPtr->crew_level);
ElementPtr->state_flags |= NONSOLID; 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->life_span = 0;
// The ship is now dead. It's death_func, i.e. shofixti_destruct_death(),
ElementPtr->preprocess_func = destruct_preprocess; // will be called the next frame.
} ElementPtr->death_func = shofixti_destruct_death;
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);
}
}
} }
static void static void
@@ -410,7 +450,7 @@ RACE_DESC*
init_shofixti (void) init_shofixti (void)
{ {
RACE_DESC *RaceDescPtr; RACE_DESC *RaceDescPtr;
// The caller of this func will copy the struct
static RACE_DESC new_shofixti_desc; static RACE_DESC new_shofixti_desc;
shofixti_desc.postprocess_func = shofixti_postprocess; shofixti_desc.postprocess_func = shofixti_postprocess;
+1 -1
View File
@@ -848,8 +848,8 @@ RACE_DESC*
init_sis (void) init_sis (void)
{ {
RACE_DESC *RaceDescPtr; RACE_DESC *RaceDescPtr;
COUNT i; COUNT i;
// The caller of this func will copy the struct
static RACE_DESC new_sis_desc; static RACE_DESC new_sis_desc;
/* copy initial ship settings to 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 BOOLEAN dittyIsPlaying;
static STARSHIP *winnerStarShip; static STARSHIP *winnerStarShip;
// Indicates which ship is the winner of the current battle.
// The winner will be last to pick the next ship.
BOOLEAN BOOLEAN
@@ -617,38 +619,37 @@ explosion_preprocess (ELEMENT *ShipPtr)
} }
void void
ship_death (ELEMENT *ShipPtr) StopAllBattleMusic (void)
{ {
STARSHIP *StarShipPtr;
STARSHIP *VictoriousStarShipPtr;
HELEMENT hElement, hNextElement;
ELEMENT *ElementPtr;
StopDitty (); StopDitty ();
StopMusic (); StopMusic ();
}
GetElementStarShip (ShipPtr, &StarShipPtr); STARSHIP *
FindAliveStarShip (ELEMENT *deadShip)
{
STARSHIP *aliveShip = NULL;
HELEMENT hElement, hNextElement;
if (ShipPtr->mass_points <= MAX_SHIP_MASS) // Find the remaining ship, if any, and see if it is still alive.
{ // 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;
for (hElement = GetHeadElement (); hElement; hElement = hNextElement) for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
{ {
ELEMENT *ElementPtr;
LockElement (hElement, &ElementPtr); LockElement (hElement, &ElementPtr);
if ((ElementPtr->state_flags & PLAYER_SHIP) if ((ElementPtr->state_flags & PLAYER_SHIP)
&& ElementPtr != ShipPtr && ElementPtr != deadShip
/* and not running away */ /* and not running away */
&& ElementPtr->mass_points <= MAX_SHIP_MASS) && ElementPtr->mass_points <= MAX_SHIP_MASS + 1)
{ {
GetElementStarShip (ElementPtr, &VictoriousStarShipPtr); GetElementStarShip (ElementPtr, &aliveShip);
if (VictoriousStarShipPtr->RaceDescPtr->ship_info.crew_level == 0) assert (aliveShip != NULL);
VictoriousStarShipPtr = 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); UnlockElement (hElement);
break; break;
@@ -657,7 +658,58 @@ ship_death (ELEMENT *ShipPtr)
UnlockElement (hElement); 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, DeltaEnergy (ShipPtr,
-(SIZE)StarShipPtr->RaceDescPtr->ship_info.energy_level); -(SIZE)StarShipPtr->RaceDescPtr->ship_info.energy_level);
@@ -665,43 +717,38 @@ ship_death (ELEMENT *ShipPtr)
ShipPtr->life_span = NUM_EXPLOSION_FRAMES * 3; ShipPtr->life_span = NUM_EXPLOSION_FRAMES * 3;
ShipPtr->state_flags &= ~DISAPPEARING; ShipPtr->state_flags &= ~DISAPPEARING;
ShipPtr->state_flags |= FINITE_LIFE | NONSOLID; ShipPtr->state_flags |= FINITE_LIFE | NONSOLID;
ShipPtr->preprocess_func = explosion_preprocess;
ShipPtr->postprocess_func = PostProcessStatus; ShipPtr->postprocess_func = PostProcessStatus;
ShipPtr->death_func = cleanup_dead_ship; ShipPtr->death_func = cleanup_dead_ship;
ShipPtr->hTarget = 0; 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 (playSound)
if (VictoriousStarShipPtr == NULL)
{ // No ships left alive after a Shofixti Glory device,
// thus Shofixti wins
VictoriousStarShipPtr = StarShipPtr;
}
}
else
{ {
ShipPtr->preprocess_func = explosion_preprocess;
PlaySound (SetAbsSoundIndex (GameSounds, SHIP_EXPLODES), PlaySound (SetAbsSoundIndex (GameSounds, SHIP_EXPLODES),
CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1); CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1);
} }
}
if (VictoriousStarShipPtr != NULL) void
VictoriousStarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY; ship_death (ELEMENT *ShipPtr)
{
STARSHIP *StarShipPtr;
STARSHIP *winner;
// The winner is set once per battle. If both ships die, this function is GetElementStarShip (ShipPtr, &StarShipPtr);
// called twice, once for each ship. We need to preserve the winner
// determined on the first call.
if (winnerStarShip == NULL)
winnerStarShip = VictoriousStarShipPtr;
if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) StopAllBattleMusic ();
MeleeShipDeath (StarShipPtr);
// 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) #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 StopDitty (void);
extern void ResetWinnerStarShip (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) #if defined(__cplusplus)
} }
+2 -2
View File
@@ -50,7 +50,7 @@ initialize_laser (LASER_BLOCK *pLaserBlock)
LaserElementPtr->state_flags = APPEARING | FINITE_LIFE LaserElementPtr->state_flags = APPEARING | FINITE_LIFE
| pLaserBlock->flags; | pLaserBlock->flags;
LaserElementPtr->life_span = LASER_LIFE; LaserElementPtr->life_span = LASER_LIFE;
LaserElementPtr->collision_func = (CollisionFunc*)weapon_collision; LaserElementPtr->collision_func = weapon_collision;
LaserElementPtr->blast_offset = 1; LaserElementPtr->blast_offset = 1;
LaserElementPtr->current.location.x = pLaserBlock->cx LaserElementPtr->current.location.x = pLaserBlock->cx
@@ -100,7 +100,7 @@ initialize_missile (MISSILE_BLOCK *pMissileBlock)
SetAbsFrameIndex (pMissileBlock->farray[0], SetAbsFrameIndex (pMissileBlock->farray[0],
pMissileBlock->index); pMissileBlock->index);
MissileElementPtr->preprocess_func = pMissileBlock->preprocess_func; MissileElementPtr->preprocess_func = pMissileBlock->preprocess_func;
MissileElementPtr->collision_func = (CollisionFunc*)weapon_collision; MissileElementPtr->collision_func = weapon_collision;
MissileElementPtr->blast_offset = (BYTE)pMissileBlock->blast_offs; MissileElementPtr->blast_offset = (BYTE)pMissileBlock->blast_offs;
angle = FACING_TO_ANGLE (pMissileBlock->face); angle = FACING_TO_ANGLE (pMissileBlock->face);