Fixed ship picking order after a simultaneous destruction: a dead winner (e.g. Shofixti) is kept alive longer than its dead opponent; in a tie, readyForBattleEnd() is called once per frame for the first ship found dead (currentDeadSide guard removed); fixes bugs #1087, #1088

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3551 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2010-07-11 00:22:27 +00:00
parent 871fea4f9c
commit 3fe05b2562
5 changed files with 117 additions and 38 deletions
+2
View File
@@ -1,4 +1,6 @@
Changes towards version 0.7: Changes towards version 0.7:
- Fixed ship picking order after a simultaneous destruction, e.g.
Shofixti picks last after Glory device (bugs #1087, #1088) - Alex
- Game no longer locks up after quickly escaping melee (bug #1003) - Alex - Game no longer locks up after quickly escaping melee (bug #1003) - Alex
- Reset input delay upon leaving Supermelee (bug #1022) - Alex - Reset input delay upon leaving Supermelee (bug #1022) - Alex
- Properly account for simultaneous destruction of last ships - Properly account for simultaneous destruction of last ships
+1 -7
View File
@@ -57,12 +57,6 @@ size_t battleInputOrder[NUM_SIDES];
#ifdef NETPLAY #ifdef NETPLAY
BattleFrameCounter battleFrameCount; BattleFrameCounter battleFrameCount;
// Used for synchronisation purposes during netplay. // Used for synchronisation purposes during netplay.
COUNT currentDeadSide;
// When a ship has been destroyed, each side of a network
// connection waits until the other side is ready.
// When two ships die at the same time, this is handled for one
// ship after the other. This variable indicate for which player
// we're currently doing this.
#endif #endif
static BOOLEAN static BOOLEAN
@@ -447,7 +441,7 @@ Battle (BattleFrameCallback *callback)
initChecksumBuffers (); initChecksumBuffers ();
#endif /* NETPLAY_CHECKSUM */ #endif /* NETPLAY_CHECKSUM */
battleFrameCount = 0; battleFrameCount = 0;
currentDeadSide = (COUNT)~0; ResetWinnerStarShip ();
setBattleStateConnections (&bs); setBattleStateConnections (&bs);
#endif /* NETPLAY */ #endif /* NETPLAY */
-1
View File
@@ -44,7 +44,6 @@ extern BOOLEAN instantVictory;
extern BattleFrameCounter battleFrameCount; extern BattleFrameCounter battleFrameCount;
#endif #endif
#ifdef NETPLAY #ifdef NETPLAY
extern COUNT currentDeadSide;
COUNT GetPlayerOrder (COUNT i); COUNT GetPlayerOrder (COUNT i);
#else #else
# define GetPlayerOrder(i) (i) # define GetPlayerOrder(i) (i)
+113 -30
View File
@@ -46,6 +46,7 @@
static void cleanup_dead_ship (ELEMENT *ElementPtr); static void cleanup_dead_ship (ELEMENT *ElementPtr);
static BOOLEAN dittyIsPlaying; static BOOLEAN dittyIsPlaying;
static STARSHIP *winnerStarShip;
BOOLEAN BOOLEAN
@@ -96,6 +97,12 @@ DittyPlaying (void)
return dittyIsPlaying; return dittyIsPlaying;
} }
void
ResetWinnerStarShip (void)
{
winnerStarShip = NULL;
}
#ifdef NETPLAY #ifdef NETPLAY
static void static void
readyToEnd2Callback (NetConnection *conn, void *arg) readyToEnd2Callback (NetConnection *conn, void *arg)
@@ -243,16 +250,14 @@ battleEndReadyNetwork (NetworkInputContext *context)
// Returns true iff this side is ready to end the battle. // Returns true iff this side is ready to end the battle.
static inline bool static inline bool
readyForBattleEnd (COUNT side) readyForBattleEnd (void)
{ {
#ifndef NETPLAY #ifndef NETPLAY
#if DEMO_MODE #if DEMO_MODE
// In Demo mode, the saved journal should be replayed with frame // In Demo mode, the saved journal should be replayed with frame
// accuracy. PLRPlaying () isn't consistent enough. // accuracy. PLRPlaying () isn't consistent enough.
(void) side;
return true; return true;
#else /* !DEMO_MODE */ #else /* !DEMO_MODE */
(void) side;
return !DittyPlaying (); return !DittyPlaying ();
#endif /* !DEMO_MODE */ #endif /* !DEMO_MODE */
#else /* defined (NETPLAY) */ #else /* defined (NETPLAY) */
@@ -261,28 +266,11 @@ readyForBattleEnd (COUNT side)
if (DittyPlaying ()) if (DittyPlaying ())
return false; return false;
// We can only handle one dead ship at a time. So 'deadSide' is set
// to the side we're handling now. (COUNT)~0 means we're not handling
// any side yet.
if (currentDeadSide == (COUNT)~0)
{
// Not handling any side yet.
currentDeadSide = side;
}
else if (side != currentDeadSide)
{
// We're handing another side at the moment.
return false;
}
for (playerI = 0; playerI < NUM_PLAYERS; playerI++) for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
if (!PlayerInput[playerI]->handlers->battleEndReady ( if (!PlayerInput[playerI]->handlers->battleEndReady (
PlayerInput[playerI])) PlayerInput[playerI]))
return false; return false;
currentDeadSide = (COUNT)~0;
// Another side may be handled.
return true; return true;
#endif /* defined (NETPLAY) */ #endif /* defined (NETPLAY) */
} }
@@ -358,10 +346,20 @@ cleanup_dead_ship (ELEMENT *DeadShipPtr)
UnlockElement (hElement); UnlockElement (hElement);
} }
#define MIN_DITTY_FRAME_COUNT ((ONE_SECOND * 3) / BATTLE_FRAME_RATE)
// The ship will be "alive" for at least 2 more frames to make sure // 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. // the elements it owns (set up for deletion above) expire first.
DeadShipPtr->life_span = // Ditty does NOT play in the following circumstances:
MusicStarted ? (ONE_SECOND * 3) / BATTLE_FRAME_RATE : 1; // * The winning ship dies before the loser finishes exploding
// * At the moment the losing ship dies, the winner has started
// the warp out sequence
DeadShipPtr->life_span = MusicStarted ? MIN_DITTY_FRAME_COUNT : 1;
if (DeadStarShipPtr == winnerStarShip)
{ // This ship died but won the battle. We need to keep it alive
// longer than the dead opponent ship so that the winning player
// picks last.
DeadShipPtr->life_span = MIN_DITTY_FRAME_COUNT + 1;
}
DeadShipPtr->death_func = new_ship; DeadShipPtr->death_func = new_ship;
DeadShipPtr->preprocess_func = preprocess_dead_ship; DeadShipPtr->preprocess_func = preprocess_dead_ship;
DeadShipPtr->state_flags &= ~DISAPPEARING; DeadShipPtr->state_flags &= ~DISAPPEARING;
@@ -373,6 +371,70 @@ cleanup_dead_ship (ELEMENT *DeadShipPtr)
} }
} }
static void
setMinShipLifeSpan (ELEMENT *ship, COUNT life_span)
{
if (ship->death_func == new_ship)
{ // The ship has finished exploding or warping out, and now
// we can work with the remaining element
assert (ship->state_flags & FINITE_LIFE);
assert (!(ship->state_flags & DISAPPEARING));
if (ship->life_span < life_span)
ship->life_span = life_span;
}
}
static void
setMinStarShipLifeSpan (STARSHIP *starShip, COUNT life_span)
{
ELEMENT *ship;
LockElement (starShip->hShip, &ship);
setMinShipLifeSpan (ship, life_span);
UnlockElement (starShip->hShip);
}
static void
checkOtherShipLifeSpan (ELEMENT *deadShip)
{
STARSHIP *deadStarShip;
GetElementStarShip (deadShip, &deadStarShip);
if (winnerStarShip != NULL && deadStarShip != winnerStarShip
&& winnerStarShip->RaceDescPtr->ship_info.crew_level == 0)
{ // The opponent ship also died but won anyway (e.g. Glory device)
// We need to keep the opponent ship alive longer so that the
// winning player picks last.
setMinStarShipLifeSpan (winnerStarShip, deadShip->life_span + 1);
}
else if (winnerStarShip == NULL)
{ // Both died at the same time, or the loser has already expired
HELEMENT hElement, hNextElement;
// Find the other dead ship(s) and keep them alive for at least as
// long as this ship.
for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
{
ELEMENT *element;
STARSHIP *starShip;
LockElement (hElement, &element);
hNextElement = GetSuccElement (element);
GetElementStarShip (element, &starShip);
if (starShip != NULL && element != deadShip
&& starShip->RaceDescPtr->ship_info.crew_level == 0)
{ // This is another dead ship
setMinShipLifeSpan (element, deadShip->life_span);
}
UnlockElement (hElement);
}
}
}
// This function is called when dead ship element's life_span reaches 0
void void
new_ship (ELEMENT *DeadShipPtr) new_ship (ELEMENT *DeadShipPtr)
{ {
@@ -380,13 +442,25 @@ new_ship (ELEMENT *DeadShipPtr)
GetElementStarShip (DeadShipPtr, &DeadStarShipPtr); GetElementStarShip (DeadShipPtr, &DeadStarShipPtr);
if (!readyForBattleEnd (DeadStarShipPtr->playerNr)) if (!readyForBattleEnd ())
{ {
DeadShipPtr->state_flags &= ~DISAPPEARING; DeadShipPtr->state_flags &= ~DISAPPEARING;
++DeadShipPtr->life_span; ++DeadShipPtr->life_span;
// Keep the winner alive longer, or in a simultaneous destruction
// tie, keep the other dead ship alive so that readyForBattleEnd()
// is called for only one ship at a time.
// When a ship has been destroyed, each side of a network
// connection waits until the other side is ready.
// When two ships die at the same time, this is handled for one
// ship after the other.
checkOtherShipLifeSpan (DeadShipPtr);
return; return;
} }
// Once a ship is being picked, we do not care about the winner anymore
winnerStarShip = NULL;
{ {
BOOLEAN RestartMusic; BOOLEAN RestartMusic;
@@ -563,7 +637,7 @@ ship_death (ELEMENT *ShipPtr)
battle_counter[StarShipPtr->playerNr]--; battle_counter[StarShipPtr->playerNr]--;
} }
VictoriousStarShipPtr = 0; VictoriousStarShipPtr = NULL;
for (hElement = GetHeadElement (); hElement; hElement = hNextElement) for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
{ {
LockElement (hElement, &ElementPtr); LockElement (hElement, &ElementPtr);
@@ -574,9 +648,7 @@ ship_death (ELEMENT *ShipPtr)
{ {
GetElementStarShip (ElementPtr, &VictoriousStarShipPtr); GetElementStarShip (ElementPtr, &VictoriousStarShipPtr);
if (VictoriousStarShipPtr->RaceDescPtr->ship_info.crew_level == 0) if (VictoriousStarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
VictoriousStarShipPtr = 0; VictoriousStarShipPtr = NULL;
else
VictoriousStarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY;
UnlockElement (hElement); UnlockElement (hElement);
break; break;
@@ -599,15 +671,17 @@ ship_death (ELEMENT *ShipPtr)
ZeroVelocityComponents (&ShipPtr->velocity); ZeroVelocityComponents (&ShipPtr->velocity);
if (ShipPtr->crew_level) /* only happens for shofixti self-destruct */ if (ShipPtr->crew_level) /* only happens for shofixti self-destruct */
{ {
PlaySound (SetAbsSoundIndex ( PlaySound (SetAbsSoundIndex (
StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
CalcSoundPosition (ShipPtr), ShipPtr, CalcSoundPosition (ShipPtr), ShipPtr,
GAME_SOUND_PRIORITY + 1); GAME_SOUND_PRIORITY + 1);
DeltaCrew (ShipPtr, -(SIZE)ShipPtr->crew_level); DeltaCrew (ShipPtr, -(SIZE)ShipPtr->crew_level);
if (VictoriousStarShipPtr == 0) if (VictoriousStarShipPtr == NULL)
StarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY; { // No ships left alive after a Shofixti Glory device,
// thus Shofixti wins
VictoriousStarShipPtr = StarShipPtr;
}
} }
else else
{ {
@@ -617,6 +691,15 @@ ship_death (ELEMENT *ShipPtr)
CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1); CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1);
} }
if (VictoriousStarShipPtr != NULL)
VictoriousStarShipPtr->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 = VictoriousStarShipPtr;
if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
MeleeShipDeath (StarShipPtr); MeleeShipDeath (StarShipPtr);
} }
+1
View File
@@ -38,6 +38,7 @@ extern void spawn_ion_trail (ELEMENT *ElementPtr);
extern void flee_preprocess (ELEMENT *ElementPtr); extern void flee_preprocess (ELEMENT *ElementPtr);
extern void StopDitty (void); extern void StopDitty (void);
extern void ResetWinnerStarShip (void);
#endif /* _TACTRANS_H */ #endif /* _TACTRANS_H */