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:
- 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
- Reset input delay upon leaving Supermelee (bug #1022) - Alex
- Properly account for simultaneous destruction of last ships
+1 -7
View File
@@ -57,12 +57,6 @@ size_t battleInputOrder[NUM_SIDES];
#ifdef NETPLAY
BattleFrameCounter battleFrameCount;
// 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
static BOOLEAN
@@ -447,7 +441,7 @@ Battle (BattleFrameCallback *callback)
initChecksumBuffers ();
#endif /* NETPLAY_CHECKSUM */
battleFrameCount = 0;
currentDeadSide = (COUNT)~0;
ResetWinnerStarShip ();
setBattleStateConnections (&bs);
#endif /* NETPLAY */
-1
View File
@@ -44,7 +44,6 @@ extern BOOLEAN instantVictory;
extern BattleFrameCounter battleFrameCount;
#endif
#ifdef NETPLAY
extern COUNT currentDeadSide;
COUNT GetPlayerOrder (COUNT i);
#else
# define GetPlayerOrder(i) (i)
+113 -30
View File
@@ -46,6 +46,7 @@
static void cleanup_dead_ship (ELEMENT *ElementPtr);
static BOOLEAN dittyIsPlaying;
static STARSHIP *winnerStarShip;
BOOLEAN
@@ -96,6 +97,12 @@ DittyPlaying (void)
return dittyIsPlaying;
}
void
ResetWinnerStarShip (void)
{
winnerStarShip = NULL;
}
#ifdef NETPLAY
static void
readyToEnd2Callback (NetConnection *conn, void *arg)
@@ -243,16 +250,14 @@ battleEndReadyNetwork (NetworkInputContext *context)
// Returns true iff this side is ready to end the battle.
static inline bool
readyForBattleEnd (COUNT side)
readyForBattleEnd (void)
{
#ifndef NETPLAY
#if DEMO_MODE
// In Demo mode, the saved journal should be replayed with frame
// accuracy. PLRPlaying () isn't consistent enough.
(void) side;
return true;
#else /* !DEMO_MODE */
(void) side;
return !DittyPlaying ();
#endif /* !DEMO_MODE */
#else /* defined (NETPLAY) */
@@ -261,28 +266,11 @@ readyForBattleEnd (COUNT side)
if (DittyPlaying ())
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++)
if (!PlayerInput[playerI]->handlers->battleEndReady (
PlayerInput[playerI]))
return false;
currentDeadSide = (COUNT)~0;
// Another side may be handled.
return true;
#endif /* defined (NETPLAY) */
}
@@ -358,10 +346,20 @@ cleanup_dead_ship (ELEMENT *DeadShipPtr)
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 elements it owns (set up for deletion above) expire first.
DeadShipPtr->life_span =
MusicStarted ? (ONE_SECOND * 3) / BATTLE_FRAME_RATE : 1;
// Ditty does NOT play in the following circumstances:
// * 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->preprocess_func = preprocess_dead_ship;
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
new_ship (ELEMENT *DeadShipPtr)
{
@@ -380,13 +442,25 @@ new_ship (ELEMENT *DeadShipPtr)
GetElementStarShip (DeadShipPtr, &DeadStarShipPtr);
if (!readyForBattleEnd (DeadStarShipPtr->playerNr))
if (!readyForBattleEnd ())
{
DeadShipPtr->state_flags &= ~DISAPPEARING;
++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;
}
// Once a ship is being picked, we do not care about the winner anymore
winnerStarShip = NULL;
{
BOOLEAN RestartMusic;
@@ -563,7 +637,7 @@ ship_death (ELEMENT *ShipPtr)
battle_counter[StarShipPtr->playerNr]--;
}
VictoriousStarShipPtr = 0;
VictoriousStarShipPtr = NULL;
for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
{
LockElement (hElement, &ElementPtr);
@@ -574,9 +648,7 @@ ship_death (ELEMENT *ShipPtr)
{
GetElementStarShip (ElementPtr, &VictoriousStarShipPtr);
if (VictoriousStarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
VictoriousStarShipPtr = 0;
else
VictoriousStarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY;
VictoriousStarShipPtr = NULL;
UnlockElement (hElement);
break;
@@ -599,15 +671,17 @@ ship_death (ELEMENT *ShipPtr)
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 == 0)
StarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY;
if (VictoriousStarShipPtr == NULL)
{ // No ships left alive after a Shofixti Glory device,
// thus Shofixti wins
VictoriousStarShipPtr = StarShipPtr;
}
}
else
{
@@ -617,6 +691,15 @@ ship_death (ELEMENT *ShipPtr)
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)
MeleeShipDeath (StarShipPtr);
}
+1
View File
@@ -38,6 +38,7 @@ extern void spawn_ion_trail (ELEMENT *ElementPtr);
extern void flee_preprocess (ELEMENT *ElementPtr);
extern void StopDitty (void);
extern void ResetWinnerStarShip (void);
#endif /* _TACTRANS_H */