From 1402590807efc244bd2757528d0f5fc15357c5cc Mon Sep 17 00:00:00 2001 From: avolkov Date: Tue, 1 Jun 2010 18:44:44 +0000 Subject: [PATCH] Account for ship death in Supermelee early to keep the scores correct after a simultaneous destruction of last ships in each fleet; some ship selection refactoring; bug #437 git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3543 8092fc87-c524-0410-9efc-e669fe64eaf9 --- sc2/ChangeLog | 2 + sc2/src/uqm/encount.c | 82 +++++++++++++- sc2/src/uqm/encount.h | 3 + sc2/src/uqm/init.c | 20 ++-- sc2/src/uqm/pickship.c | 182 ++++++++++-------------------- sc2/src/uqm/supermelee/pickmele.c | 17 +-- sc2/src/uqm/supermelee/pickmele.h | 2 +- sc2/src/uqm/tactrans.c | 16 ++- 8 files changed, 172 insertions(+), 152 deletions(-) diff --git a/sc2/ChangeLog b/sc2/ChangeLog index ac4555548..953e21e95 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,6 @@ Changes towards version 0.7: +- Properly account for simultaneous destruction of last ships + in each fleet in Supermelee (bug #437) - Alex - Do not match singular stars when given a prefix in star search (bug #1071) - Alex - Preserve character case when editing with joystick (bug #1080) - Alex diff --git a/sc2/src/uqm/encount.c b/sc2/src/uqm/encount.c index 0ae24f8a4..dffd7d0b6 100644 --- a/sc2/src/uqm/encount.c +++ b/sc2/src/uqm/encount.c @@ -89,6 +89,15 @@ DoSelectAction (MENU_STATE *pMS) return (TRUE); } +static QUEUE * +GetShipFragQueueForPlayer (COUNT playerNr) +{ + if (playerNr == RPG_PLAYER_NUM) + return &GLOBAL (built_ship_q); + else + return &GLOBAL (npc_built_ship_q); +} + // Called by comm code to intialize battle fleets during encounter void BuildBattle (COUNT which_player) @@ -106,10 +115,9 @@ BuildBattle (COUNT which_player) return; } - if (which_player == RPG_PLAYER_NUM) - pQueue = &GLOBAL (built_ship_q); - else - { + if (which_player != RPG_PLAYER_NUM) + { // This function is called first for the NPC character + // and this is when a centerpiece is loaded switch (LOBYTE (GLOBAL (CurrentActivity))) { case IN_LAST_BATTLE: @@ -124,8 +132,8 @@ BuildBattle (COUNT which_player) load_gravity_well (GET_GAME_STATE (BATTLE_PLANET)); break; } - pQueue = &GLOBAL (npc_built_ship_q); } + pQueue = GetShipFragQueueForPlayer (which_player); ReinitQueue (&race_q[which_player]); for (hStarShip = GetHeadLink (pQueue); @@ -181,6 +189,70 @@ BuildBattle (COUNT which_player) } } +BOOLEAN +FleetIsInfinite (COUNT playerNr) +{ + QUEUE *pQueue; + HSHIPFRAG hShipFrag; + SHIP_FRAGMENT *FragPtr; + BOOLEAN ret; + + pQueue = GetShipFragQueueForPlayer (playerNr); + hShipFrag = GetHeadLink (pQueue); + if (!hShipFrag) + { // Ship queue is empty in SuperMelee or for RPG player w/o escorts + return FALSE; + } + + FragPtr = LockShipFrag (pQueue, hShipFrag); + ret = (FragPtr->crew_level == INFINITE_FLEET); + UnlockShipFrag (pQueue, hShipFrag); + + return ret; +} + +void +UpdateShipFragCrew (STARSHIP *StarShipPtr) +{ + QUEUE *frag_q; + HSHIPFRAG hShipFrag, hNextFrag; + SHIP_FRAGMENT *frag; + QUEUE *ship_q; + HSTARSHIP hStarShip, hNextShip; + STARSHIP *ship; + + frag_q = GetShipFragQueueForPlayer (StarShipPtr->playerNr); + ship_q = &race_q[StarShipPtr->playerNr]; + + // Find a SHIP_FRAGMENT that corresponds to the given STARSHIP + // The ships and fragments are in the same order in two queues + // XXX: It would probably be simpler to keep HSHIPFRAG in STARSHIP struct + for (hShipFrag = GetHeadLink (frag_q), hStarShip = GetHeadLink (ship_q); + hShipFrag != 0 && hStarShip != 0; + hShipFrag = hNextFrag, hStarShip = hNextShip) + { + ship = LockStarShip (ship_q, hStarShip); + hNextShip = _GetSuccLink (ship); + frag = LockShipFrag (frag_q, hShipFrag); + hNextFrag = _GetSuccLink (frag); + + if (ship == StarShipPtr) + { + assert (frag->crew_level != INFINITE_FLEET); + + // Record crew left after the battle */ + frag->crew_level = ship->crew_level; + + UnlockShipFrag (frag_q, hShipFrag); + UnlockStarShip (ship_q, hStarShip); + break; + } + + UnlockShipFrag (frag_q, hShipFrag); + UnlockStarShip (ship_q, hStarShip); + } +} + /* * Encountering an alien. * Draws the encounter screen, plays the red alert music, and diff --git a/sc2/src/uqm/encount.h b/sc2/src/uqm/encount.h index b3e628065..aed519e61 100644 --- a/sc2/src/uqm/encount.h +++ b/sc2/src/uqm/encount.h @@ -28,6 +28,7 @@ typedef struct encounter ENCOUNTER; #include "libs/gfxlib.h" #include "planets/planets.h" #include "element.h" +#include "races.h" typedef HLINK HENCOUNTER; @@ -151,6 +152,8 @@ extern void EncounterBattle (void); extern void BuildBattle (COUNT which_player); extern COUNT InitEncounter (void); extern COUNT UninitEncounter (void); +extern BOOLEAN FleetIsInfinite (COUNT playerNr); +extern void UpdateShipFragCrew (STARSHIP *); // XXX: in comm.h, temporary, until solsys generation code is redone extern COUNT InitCommunication (CONVERSATION which_comm); diff --git a/sc2/src/uqm/init.c b/sc2/src/uqm/init.c index 46fabc17d..b2c77886b 100644 --- a/sc2/src/uqm/init.c +++ b/sc2/src/uqm/init.c @@ -25,6 +25,7 @@ #include "pickship.h" #include "process.h" #include "globdata.h" +#include "encount.h" #include "hyper.h" #include "init.h" #include "port.h" @@ -268,7 +269,7 @@ void UninitShips (void) { COUNT crew_retrieved; - SIZE i; + int i; HELEMENT hElement, hNextElement; STARSHIP *SPtr[NUM_PLAYERS]; @@ -325,20 +326,17 @@ UninitShips (void) GLOBAL (CurrentActivity) &= ~IN_BATTLE; - if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) - { - } - else if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER + if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER && !(GLOBAL (CurrentActivity) & CHECK_ABORT)) { - // XXX: This has no purpose for SuperMelee - // In full-game, the sole purpose of this is to record the crew - // left in the last ship standing. The crew left is first recorded - // into STARSHIP.crew_level just a few lines above here. + // Encounter battle in full game. + // Record the crew left in the last ship standing. The crew left + // is first recorded into STARSHIP.crew_level just a few lines + // above here. for (i = NUM_PLAYERS - 1; i >= 0; --i) { - if (SPtr[i]) - GetEncounterStarShip (SPtr[i], i); + if (SPtr[i] && !FleetIsInfinite (i)) + UpdateShipFragCrew (SPtr[i]); } } diff --git a/sc2/src/uqm/pickship.c b/sc2/src/uqm/pickship.c index 510f0ff91..7b68c82b3 100644 --- a/sc2/src/uqm/pickship.c +++ b/sc2/src/uqm/pickship.c @@ -23,6 +23,7 @@ #include "controls.h" #include "menustat.h" #include "supermelee/pickmele.h" +#include "encount.h" #include "battle.h" #include "races.h" #include "resinst.h" @@ -290,7 +291,9 @@ OldContext = SetContext (SpaceContext); if (hBattleShip) { if (hBattleShip == GetTailLink (&race_q[0])) - battle_counter[0] = 1; + { // Player chose SIS. There will be no more choices. + battle_counter[RPG_PLAYER_NUM] = 1; + } WaitForSoundEnd (0); } @@ -302,154 +305,89 @@ SetContext (OldContext); return (hBattleShip); } -static QUEUE * -GetShipQueueForSide (COUNT sideNr) -{ - if (sideNr == 0) - return &GLOBAL (built_ship_q); - else - return &GLOBAL (npc_built_ship_q); -} - // Get the next ship to use. HSTARSHIP GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player) { - HSTARSHIP hBattleShip; - if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) { - // Get the next ship from the battle group. - hBattleShip = GetHeadLink (&race_q[which_player]); + assert (which_player == RPG_PLAYER_NUM); + // SIS for the Hyperspace flight + return GetHeadLink (&race_q[which_player]); } else if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) { // Let the player chose their own ship. (May be a computer player). + HSTARSHIP hBattleShip; - if (!(GLOBAL (CurrentActivity) & IN_BATTLE)) - { - // XXX: This check is needed, because UninitShips() calls - // this function after the battle is over to record - // the crew left in the last ship standing. - hBattleShip = 0; - } - else if (!MeleeShipDeath (LastStarShipPtr, which_player)) - hBattleShip = 0; - // Game over. - else - { - if (!GetNextMeleeStarShip (which_player, &hBattleShip)) - hBattleShip = 0; + if (battle_counter[0] == 0 || battle_counter[1] == 0) + { // One side is out of ships. Game over. + return 0; } + + if (!GetNextMeleeStarShip (which_player, &hBattleShip)) + return 0; + + return hBattleShip; } else { // Full game. - HSHIPFRAG hStarShip; - SHIP_FRAGMENT *FragPtr; - - if (LastStarShipPtr == 0) - { - // First time picking a ship. - if (which_player == 0 && battle_counter[0] > 1) - { - // The player in a full game, still having a ship left. - hBattleShip = GetArmadaStarShip (); + if (which_player == RPG_PLAYER_NUM) + { // Human player in a full game. + if (LastStarShipPtr == 0 && battle_counter[which_player] == 1) + { // First time picking a ship and player has no escorts + // SIS is the last ship in queue (though there is only one) + return GetTailLink (&race_q[which_player]); } - else - { - hBattleShip = GetHeadLink (&race_q[which_player]); - if (which_player == 1) - { - // Select the next ship for the computer. - hStarShip = GetHeadLink (&GLOBAL (npc_built_ship_q)); - FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), - hStarShip); - if (FragPtr->crew_level == INFINITE_FLEET) - { - // Infinite number of ships. - battle_counter[1]++; - } - UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip); + else if (battle_counter[which_player]) + { // Player still has ships left + return GetArmadaStarShip (); + } + else if (LastStarShipPtr != 0) + { // last ship was the flagship +#define RUN_AWAY_FUEL_COST (5 * FUEL_TANK_SCALE) + if (LastStarShipPtr->crew_level == 0) + { // Died in the line of duty + GLOBAL_SIS (CrewEnlisted) = (COUNT)~0; + } + else + { // Player ran away + if (GLOBAL_SIS (FuelOnBoard) > RUN_AWAY_FUEL_COST) + GLOBAL_SIS (FuelOnBoard) -= RUN_AWAY_FUEL_COST; + else + GLOBAL_SIS (FuelOnBoard) = 0; } } + return 0; } else - { - QUEUE *pQueue; - HSHIPFRAG hNextShip; - - pQueue = GetShipQueueForSide (which_player); - - hBattleShip = GetHeadLink (&race_q[which_player]); - for (hStarShip = GetHeadLink (pQueue); - hStarShip != 0; hStarShip = hNextShip) + { // NPC player in a full game + if (FleetIsInfinite (which_player)) { - STARSHIP *SPtr; - - SPtr = LockStarShip (&race_q[which_player], hBattleShip); - hNextShip = _GetSuccLink (SPtr); - UnlockStarShip (&race_q[which_player], hBattleShip); - hBattleShip = hNextShip; - - FragPtr = LockShipFrag (pQueue, hStarShip); - if (SPtr == LastStarShipPtr) - { - if (FragPtr->crew_level != INFINITE_FLEET) - { - /* Record crew left after the battle */ - FragPtr->crew_level = SPtr->crew_level; - if (GLOBAL (CurrentActivity) & IN_BATTLE) - SPtr->SpeciesID = NO_ID; - // deactivates the ship - } - else /* if infinite ships */ - { - hBattleShip = GetTailLink (&race_q[which_player]); - /* XXX: Note that if Syreen had a homeworld you could - * fight, all Syreen ships there would be crewed to - * the maximum, instead of the normal level */ - SPtr->crew_level = FragPtr->max_crew; - SPtr->playerNr = which_player; - SPtr->captains_name_index = PickCaptainName (); - - battle_counter[1]++; - } - UnlockShipFrag (pQueue, hStarShip); - break; + if (LastStarShipPtr != 0) + { // The current STARSHIP is reused for the next one; + // update with new info + // XXX: Note that if Syreen had a homeworld you could + // fight, all Syreen ships there would be crewed to + // the maximum, instead of the normal level + LastStarShipPtr->crew_level = LastStarShipPtr->max_crew; + LastStarShipPtr->playerNr = which_player; + LastStarShipPtr->captains_name_index = PickCaptainName (); } - hNextShip = _GetSuccLink (FragPtr); - UnlockShipFrag (pQueue, hStarShip); + battle_counter[which_player]++; + + return GetHeadLink (&race_q[which_player]); } - if (which_player == 0) - { - // Player in a full game. - if (battle_counter[0]) - hBattleShip = GetArmadaStarShip (); - else /* last ship was flagship */ - { -#define RUN_AWAY_FUEL_COST (5 * FUEL_TANK_SCALE) - hBattleShip = 0; - if (LastStarShipPtr->crew_level == 0) - { - /* Died in the line of duty */ - GLOBAL_SIS (CrewEnlisted) = (COUNT)~0; - } - else - { - // Player ran away. - if (GLOBAL_SIS (FuelOnBoard) > RUN_AWAY_FUEL_COST) - GLOBAL_SIS (FuelOnBoard) -= RUN_AWAY_FUEL_COST; - else - GLOBAL_SIS (FuelOnBoard) = 0; - } - } - } + // Get the next ship for the computer + if (LastStarShipPtr != 0) + return _GetSuccLink (LastStarShipPtr); + + // Get the very first ship for the computer + return GetHeadLink (&race_q[which_player]); } } - - return (hBattleShip); } void diff --git a/sc2/src/uqm/supermelee/pickmele.c b/sc2/src/uqm/supermelee/pickmele.c index 11c135dad..bf5248321 100644 --- a/sc2/src/uqm/supermelee/pickmele.c +++ b/sc2/src/uqm/supermelee/pickmele.c @@ -682,24 +682,17 @@ MeleeGameOver (void) LockMutex (GraphicsLock); } -BOOLEAN -MeleeShipDeath (STARSHIP *ship, COUNT which_player) { +void +MeleeShipDeath (STARSHIP *ship) +{ FRAME frame; // Deactivate fleet position. ship->SpeciesID = NO_ID; - frame = SetAbsFrameIndex (PickMeleeFrame, which_player); + frame = SetAbsFrameIndex (PickMeleeFrame, ship->playerNr); CrossOutShip (frame, ship->index); - UpdatePickMeleeFleetValue (frame, which_player); - - if (battle_counter[0] == 0 || battle_counter[1] == 0) - { - // One side is out of ships. Game over. - return FALSE; - } - - return TRUE; + UpdatePickMeleeFleetValue (frame, ship->playerNr); } // Post: the NetState for all players is NetState_interBattle diff --git a/sc2/src/uqm/supermelee/pickmele.h b/sc2/src/uqm/supermelee/pickmele.h index 90a025a21..a214dfba9 100644 --- a/sc2/src/uqm/supermelee/pickmele.h +++ b/sc2/src/uqm/supermelee/pickmele.h @@ -24,7 +24,7 @@ typedef struct getmelee_struct GETMELEE_STATE; #include "meleesetup.h" #include "libs/compiler.h" -BOOLEAN MeleeShipDeath (STARSHIP *ship, COUNT which_player); +void MeleeShipDeath (STARSHIP *); void BuildPickMeleeFrame (void); void DestroyPickMeleeFrame (void); void FillPickMeleeFrame (MeleeSetup *setup); diff --git a/sc2/src/uqm/tactrans.c b/sc2/src/uqm/tactrans.c index 70585380e..56d2d02d7 100644 --- a/sc2/src/uqm/tactrans.c +++ b/sc2/src/uqm/tactrans.c @@ -27,6 +27,7 @@ #include "status.h" #include "battle.h" #include "init.h" +#include "supermelee/pickmele.h" #ifdef NETPLAY # include "supermelee/netplay/netmelee.h" # include "supermelee/netplay/netmisc.h" @@ -36,6 +37,7 @@ # include "supermelee/netplay/packetq.h" #endif #include "races.h" +#include "encount.h" #include "settings.h" #include "sounds.h" #include "libs/mathlib.h" @@ -279,7 +281,7 @@ new_ship (ELEMENT *DeadShipPtr) if (DeadStarShipPtr->crew_level) { // We've just warped out. new_ship() will still be called - // a few times, to process the trace left behind (I assume). + // a few times, as for any "dead" ship. StopMusic (); // Even after much investigation, I could find no purpose for @@ -339,6 +341,8 @@ new_ship (ELEMENT *DeadShipPtr) UnlockElement (hElement); } + // The ship will be "alive" for at least 2 more frames to make sure + // the elements it owns (set up for deletion above) expire first. DeadShipPtr->life_span = MusicStarted ? (ONE_SECOND * 3) / BATTLE_FRAME_RATE : 1; DeadShipPtr->death_func = new_ship; @@ -391,6 +395,13 @@ new_ship (ELEMENT *DeadShipPtr) } #endif /* NETPLAY */ + if (!FleetIsInfinite (DeadStarShipPtr->playerNr)) + { // This may be a dead ship (crew_level == 0) or a warped out ship + UpdateShipFragCrew (DeadStarShipPtr); + // Deactivate the ship (cannot be selected) + DeadStarShipPtr->SpeciesID = NO_ID; + } + if (GetNextStarShip (DeadStarShipPtr, DeadStarShipPtr->playerNr)) { #ifdef NETPLAY @@ -574,6 +585,9 @@ ship_death (ELEMENT *ShipPtr) PlaySound (SetAbsSoundIndex (GameSounds, SHIP_EXPLODES), CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1); } + + if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) + MeleeShipDeath (StarShipPtr); } #define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)