From: <av...@us...> - 2011-07-26 20:31:05
|
Revision: 3664 http://sc2.svn.sourceforge.net/sc2/?rev=3664&view=rev Author: avolkov Date: 2011-07-26 20:30:58 +0000 (Tue, 26 Jul 2011) Log Message: ----------- 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 Modified Paths: -------------- trunk/sc2/ChangeLog trunk/sc2/src/uqm/element.h trunk/sc2/src/uqm/planets/solarsys.c trunk/sc2/src/uqm/ship.c trunk/sc2/src/uqm/ships/androsyn/androsyn.c trunk/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c trunk/sc2/src/uqm/ships/pkunk/pkunk.c trunk/sc2/src/uqm/ships/shofixti/shofixti.c trunk/sc2/src/uqm/ships/sis_ship/sis_ship.c trunk/sc2/src/uqm/tactrans.c trunk/sc2/src/uqm/tactrans.h trunk/sc2/src/uqm/weapon.c Modified: trunk/sc2/ChangeLog =================================================================== --- trunk/sc2/ChangeLog 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/ChangeLog 2011-07-26 20:30:58 UTC (rev 3664) @@ -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 Modified: trunk/sc2/src/uqm/element.h =================================================================== --- trunk/sc2/src/uqm/element.h 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/element.h 2011-07-26 20:30:58 UTC (rev 3664) @@ -96,7 +96,8 @@ 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 @@ // 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.) Modified: trunk/sc2/src/uqm/planets/solarsys.c =================================================================== --- trunk/sc2/src/uqm/planets/solarsys.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/planets/solarsys.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -1660,6 +1660,7 @@ RECT clipRect; FRAME frame; + // Use SpaceContext to find out the dimensions of the background oldContext = SetContext (SpaceContext); GetContextClipRect (&clipRect); Modified: trunk/sc2/src/uqm/ship.c =================================================================== --- trunk/sc2/src/uqm/ship.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/ship.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -193,6 +193,9 @@ 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); Modified: trunk/sc2/src/uqm/ships/androsyn/androsyn.c =================================================================== --- trunk/sc2/src/uqm/ships/androsyn/androsyn.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/ships/androsyn/androsyn.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -426,7 +426,7 @@ 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; Modified: trunk/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c =================================================================== --- trunk/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/ships/mmrnmhrm/mmrnmhrm.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -452,7 +452,7 @@ 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; Modified: trunk/sc2/src/uqm/ships/pkunk/pkunk.c =================================================================== --- trunk/sc2/src/uqm/ships/pkunk/pkunk.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/ships/pkunk/pkunk.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -21,6 +21,7 @@ #include "resinst.h" #include "uqm/globdata.h" +#include "uqm/tactrans.h" #include "libs/mathlib.h" @@ -112,6 +113,16 @@ 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 @@ 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 @@ } 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; - 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; + 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; - 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->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); + 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)); + 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; - } + // 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; + PKUNK_DATA *PkunkData; + ELEMENT *ShipPtr; - ElementPtr->state_flags &= ~DISAPPEARING; - ElementPtr->life_span = 1; + GetElementStarShip (ElementPtr, &StarShipPtr); + PkunkData = (PKUNK_DATA *) StarShipPtr->RaceDescPtr->data; + + 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; + } - GetElementStarShip (ElementPtr, &StarShipPtr); - if (StarShipPtr->RaceDescPtr->ship_info.crew_level == 0) + 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)) { - ELEMENT *ShipPtr; + // 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; - 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); + 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 @@ 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 @@ 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) + { + // A brand new ship is preprocessed only once StarShipPtr->RaceDescPtr->preprocess_func = 0; + } else - { + { // Start the reincarnation sequence COUNT angle, facing; ProcessSound (SetAbsSoundIndex ( @@ -538,18 +569,32 @@ } } +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 // that NetPlay is synchronised if one player played another @@ -557,4 +602,3 @@ return (RaceDescPtr); } - Modified: trunk/sc2/src/uqm/ships/shofixti/shofixti.c =================================================================== --- trunk/sc2/src/uqm/ships/shofixti/shofixti.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/ships/shofixti/shofixti.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -21,6 +21,7 @@ #include "resinst.h" #include "uqm/globdata.h" +#include "uqm/tactrans.h" #include "libs/mathlib.h" @@ -212,10 +213,10 @@ 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 @@ 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 @@ #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 (ELEMENT *ElementPtr) +self_destruct_kill_objects (ELEMENT *ElementPtr) { - STARSHIP *StarShipPtr; + // 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; - GetElementStarShip (ElementPtr, &StarShipPtr); - if (ElementPtr->state_flags & PLAYER_SHIP) + for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { - 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. - hDestruct = AllocElement (); - if (hDestruct) + ELEMENT *ObjPtr; + SIZE delta_x, delta_y; + DWORD dist; + + LockElement (hElement, &ObjPtr); + hNextElement = GetSuccElement (ObjPtr); + + if (!CollidingElement (ObjPtr) && !ORZ_MARINE (ObjPtr)) { - ELEMENT *DestructPtr; + UnlockElement (hElement); + continue; + } - 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; +#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; - DestructPtr->death_func = self_destruct; + // 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 (hDestruct); + UnlockElement (hElement); + } +} - PutElement (hDestruct); +// 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); +} - 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; +static void +self_destruct (ELEMENT *ElementPtr) +{ + STARSHIP *StarShipPtr; + HELEMENT hDestruct; - ElementPtr->preprocess_func = destruct_preprocess; - } - else + GetElementStarShip (ElementPtr, &StarShipPtr); + + // 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) { - // 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; + ELEMENT *DestructPtr; - for (hElement = GetHeadElement (); - hElement != 0; hElement = hNextElement) - { - ELEMENT *ObjPtr; + 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 (hElement, &ObjPtr); - hNextElement = GetSuccElement (ObjPtr); + DestructPtr->death_func = self_destruct_kill_objects; - if (CollidingElement (ObjPtr) || ORZ_MARINE (ObjPtr)) - { -#define DESTRUCT_RANGE 180 - SIZE delta_x, delta_y; - DWORD dist; + UnlockElement (hDestruct); - 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; + PutElement (hDestruct); + } - destruction = ((MAX_DESTRUCTION - * (DESTRUCT_RANGE - square_root (dist))) - / DESTRUCT_RANGE) + 1; + // Must kill off the remaining crew ourselves + DeltaCrew (ElementPtr, -(int)ElementPtr->crew_level); - 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 @@ 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; Modified: trunk/sc2/src/uqm/ships/sis_ship/sis_ship.c =================================================================== --- trunk/sc2/src/uqm/ships/sis_ship/sis_ship.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/ships/sis_ship/sis_ship.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -848,8 +848,8 @@ 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 */ Modified: trunk/sc2/src/uqm/tactrans.c =================================================================== --- trunk/sc2/src/uqm/tactrans.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/tactrans.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -47,6 +47,8 @@ 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 @@ } 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; @@ -656,52 +657,98 @@ hNextElement = GetSuccElement (ElementPtr); UnlockElement (hElement); } + + return aliveShip; +} - StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY; +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); 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) Modified: trunk/sc2/src/uqm/tactrans.h =================================================================== --- trunk/sc2/src/uqm/tactrans.h 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/tactrans.h 2011-07-26 20:30:58 UTC (rev 3664) @@ -43,6 +43,12 @@ 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) } Modified: trunk/sc2/src/uqm/weapon.c =================================================================== --- trunk/sc2/src/uqm/weapon.c 2011-07-24 19:58:25 UTC (rev 3663) +++ trunk/sc2/src/uqm/weapon.c 2011-07-26 20:30:58 UTC (rev 3664) @@ -50,7 +50,7 @@ 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 @@ 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); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |