summaryrefslogtreecommitdiff
path: root/src/uqm/supermelee/pickmele.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/supermelee/pickmele.c')
-rw-r--r--src/uqm/supermelee/pickmele.c948
1 files changed, 948 insertions, 0 deletions
diff --git a/src/uqm/supermelee/pickmele.c b/src/uqm/supermelee/pickmele.c
new file mode 100644
index 0000000..0ce6489
--- /dev/null
+++ b/src/uqm/supermelee/pickmele.c
@@ -0,0 +1,948 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define PICKMELE_INTERNAL
+#include "pickmele.h"
+
+#include "../battlecontrols.h"
+#include "../battle.h"
+#include "../build.h"
+#include "../controls.h"
+#include "../flash.h"
+#include "../igfxres.h"
+#include "../intel.h"
+#include "../master.h"
+#include "../nameref.h"
+#include "melee.h"
+#ifdef NETPLAY
+# include "netplay/netmelee.h"
+# include "netplay/netmisc.h"
+# include "netplay/notify.h"
+#endif
+#include "../races.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "libs/async.h"
+#include "libs/log.h"
+#include "libs/mathlib.h"
+
+
+#define NUM_PICKMELEE_ROWS 2
+#define NUM_PICKMELEE_COLUMNS 7
+
+#define PICK_X_OFFS 57
+#define PICK_Y_OFFS 24
+#define PICK_SIDE_OFFS 100
+
+#define NAME_AREA_HEIGHT 7
+#define MELEE_WIDTH 149
+#define MELEE_HEIGHT (48 + NAME_AREA_HEIGHT)
+
+#define PICKSHIP_TEAM_NAME_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+#define PICKSHIP_TEAM_START_VALUE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x04, 0x05, 0x1F), 0x4B)
+
+
+#ifdef NETPLAY
+static void reportShipSelected (GETMELEE_STATE *gms, COUNT index);
+#endif
+
+
+FRAME PickMeleeFrame;
+
+
+static FleetShipIndex
+PickMelee_GetShipIndex (BYTE row, BYTE col)
+{
+ return row * NUM_PICKMELEE_COLUMNS + col;
+}
+
+static BYTE
+PickMelee_GetShipRow (FleetShipIndex index)
+{
+ return index / NUM_PICKMELEE_COLUMNS;
+}
+
+static BYTE
+PickMelee_GetShipColumn (int index)
+{
+ return index % NUM_PICKMELEE_COLUMNS;
+}
+
+// Returns the <index>th ship in the queue, or 0 if it is not available.
+static HSTARSHIP
+MeleeShipByQueueIndex (const QUEUE *queue, COUNT index)
+{
+ HSTARSHIP hShip;
+ HSTARSHIP hNextShip;
+
+ for (hShip = GetHeadLink (queue); hShip != 0; hShip = hNextShip)
+ {
+ STARSHIP *StarShipPtr = LockStarShip (queue, hShip);
+ if (StarShipPtr->index == index)
+ {
+ hNextShip = hShip;
+ if (StarShipPtr->SpeciesID == NO_ID)
+ hShip = 0;
+ UnlockStarShip (queue, hNextShip);
+ break;
+ }
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockStarShip (queue, hShip);
+ }
+
+ return hShip;
+}
+
+// Returns the <index>th available ship in the queue.
+static HSTARSHIP
+MeleeShipByUsedIndex (const QUEUE *queue, COUNT index)
+{
+ HSTARSHIP hShip;
+ HSTARSHIP hNextShip;
+
+ for (hShip = GetHeadLink (queue); hShip != 0; hShip = hNextShip)
+ {
+ STARSHIP *StarShipPtr = LockStarShip (queue, hShip);
+ if ((StarShipPtr->SpeciesID != NO_ID) && index-- == 0)
+ {
+ UnlockStarShip (queue, hShip);
+ break;
+ }
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockStarShip (queue, hShip);
+ }
+
+ return hShip;
+}
+
+#if 0
+static COUNT
+queueIndexFromShip (HSTARSHIP hShip)
+{
+ COUNT result;
+ STARSHIP *StarShipPtr = LockStarShip (queue, hShip);
+ result = StarShipPtr->index;
+ UnlockStarShip (queue, hShip);
+}
+#endif
+
+// Pre: called does not hold the graphics lock
+static void
+PickMelee_ChangedSelection (GETMELEE_STATE *gms, COUNT playerI)
+{
+ RECT r;
+ r.corner.x = PICK_X_OFFS + ((ICON_WIDTH + 2) * gms->player[playerI].col);
+ r.corner.y = PICK_Y_OFFS + ((ICON_HEIGHT + 2) * gms->player[playerI].row)
+ + ((1 - playerI) * PICK_SIDE_OFFS);
+ r.extent.width = (ICON_WIDTH + 2);
+ r.extent.height = (ICON_HEIGHT + 2);
+ Flash_setRect (gms->player[playerI].flashContext, &r);
+}
+
+// Only returns false when there is no ship for the choice.
+bool
+setShipSelected(GETMELEE_STATE *gms, COUNT playerI, COUNT choice,
+ bool reportNetwork)
+{
+ HSTARSHIP ship;
+
+ assert (!gms->player[playerI].done);
+
+ if (choice == (COUNT) ~0)
+ {
+ // Random ship selection.
+ ship = MeleeShipByUsedIndex (&race_q[playerI],
+ gms->player[playerI].randomIndex);
+ }
+ else
+ {
+ // Explicit ship selection.
+ ship = MeleeShipByQueueIndex (&race_q[playerI], choice);
+ }
+
+ if (ship == 0)
+ return false;
+
+ gms->player[playerI].choice = choice;
+ gms->player[playerI].hBattleShip = ship;
+ PlayMenuSound (MENU_SOUND_SUCCESS);
+#ifdef NETPLAY
+ if (reportNetwork)
+ reportShipSelected (gms, choice);
+#else
+ (void) reportNetwork;
+#endif
+ gms->player[playerI].done = true;
+ return true;
+}
+
+// Returns FALSE if aborted.
+static BOOLEAN
+SelectShip_processInput (GETMELEE_STATE *gms, COUNT playerI,
+ BATTLE_INPUT_STATE inputState)
+{
+ if (inputState & BATTLE_WEAPON)
+ {
+ if (gms->player[playerI].col == NUM_PICKMELEE_COLUMNS &&
+ gms->player[playerI].row == 0)
+ {
+ // Random ship
+ (void) setShipSelected (gms, playerI, (COUNT) ~0, TRUE);
+ }
+ else if (gms->player[playerI].col == NUM_PICKMELEE_COLUMNS &&
+ gms->player[playerI].row == 1)
+ {
+ // Selected exit
+ if (ConfirmExit ())
+ return FALSE;
+ }
+ else
+ {
+ // Selection is on a ship slot.
+ COUNT slotNr = PickMelee_GetShipIndex (gms->player[playerI].row,
+ gms->player[playerI].col);
+ (void) setShipSelected (gms, playerI, slotNr, TRUE);
+ // If the choice is not valid, setShipSelected()
+ // will not set .done.
+ }
+ }
+ else
+ {
+ // Process motion commands.
+ COUNT new_row, new_col;
+
+ new_row = gms->player[playerI].row;
+ new_col = gms->player[playerI].col;
+ if (inputState & BATTLE_LEFT)
+ {
+ if (new_col-- == 0)
+ new_col = NUM_PICKMELEE_COLUMNS;
+ }
+ else if (inputState & BATTLE_RIGHT)
+ {
+ if (new_col++ == NUM_PICKMELEE_COLUMNS)
+ new_col = 0;
+ }
+ if (inputState & BATTLE_THRUST)
+ {
+ if (new_row-- == 0)
+ new_row = NUM_PICKMELEE_ROWS - 1;
+ }
+ else if (inputState & BATTLE_DOWN)
+ {
+ if (++new_row == NUM_PICKMELEE_ROWS)
+ new_row = 0;
+ }
+
+ if (new_row != gms->player[playerI].row ||
+ new_col != gms->player[playerI].col)
+ {
+ gms->player[playerI].row = new_row;
+ gms->player[playerI].col = new_col;
+
+ PlayMenuSound (MENU_SOUND_MOVE);
+ PickMelee_ChangedSelection (gms, playerI);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+selectShipHuman (HumanInputContext *context, GETMELEE_STATE *gms)
+{
+ BATTLE_INPUT_STATE inputState =
+ PulsedInputToBattleInput (context->playerNr);
+
+ return SelectShip_processInput (gms, context->playerNr, inputState);
+}
+
+BOOLEAN
+selectShipComputer (ComputerInputContext *context, GETMELEE_STATE *gms)
+{
+#define COMPUTER_SELECTION_DELAY (ONE_SECOND >> 1)
+ TimeCount now = GetTimeCounter ();
+ if (now < gms->player[context->playerNr].timeIn +
+ COMPUTER_SELECTION_DELAY)
+ return TRUE;
+
+ return SelectShip_processInput (gms, context->playerNr, BATTLE_WEAPON);
+ // Simulate selection of the random choice button.
+}
+
+#ifdef NETPLAY
+BOOLEAN
+selectShipNetwork (NetworkInputContext *context, GETMELEE_STATE *gms)
+{
+ flushPacketQueues ();
+ // Sets gms->player[context->playerNr].remoteSelected if input
+ // is received.
+ if (gms->player[context->playerNr].remoteSelected)
+ gms->player[context->playerNr].done = TRUE;
+
+ return TRUE;
+}
+#endif
+
+// Select a new ship from the fleet for battle.
+// Returns 'TRUE' if no choice has been made yet; this function is to be
+// called again later.
+// Returns 'FALSE' if a choice has been made. gms->hStarShip is set
+// to the chosen (or randomly selected) ship, or to 0 if 'exit' has
+// been chosen.
+/* TODO: Include player timeouts */
+static BOOLEAN
+DoGetMelee (GETMELEE_STATE *gms)
+{
+ BOOLEAN done;
+ COUNT playerI;
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+
+ if (!gms->Initialized)
+ {
+ gms->Initialized = TRUE;
+ return TRUE;
+ }
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gms->player[playerI].selecting)
+ continue;
+
+ if (!gms->player[playerI].done)
+ Flash_process (gms->player[playerI].flashContext);
+ }
+
+ SleepThread (ONE_SECOND / 120);
+
+#ifdef NETPLAY
+ netInput ();
+
+ if (!allConnected ())
+ goto aborted;
+#endif
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ goto aborted;
+
+ done = TRUE;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gms->player[playerI].selecting)
+ continue;
+
+ if (!gms->player[playerI].done) {
+ if (!PlayerInput[playerI]->handlers->selectShip (
+ PlayerInput[playerI], gms))
+ goto aborted;
+
+ if (gms->player[playerI].done)
+ {
+ Flash_terminate (gms->player[playerI].flashContext);
+ gms->player[playerI].flashContext = NULL;
+ }
+ else
+ done = FALSE;
+ }
+ }
+
+#ifdef NETPLAY
+ flushPacketQueues ();
+#endif
+ return !done;
+
+aborted:
+#ifdef NETPLAY
+ flushPacketQueues ();
+#endif
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gms->player[playerI].selecting)
+ continue;
+
+ gms->player[playerI].choice = 0;
+ gms->player[playerI].hBattleShip = 0;
+ }
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+ return FALSE;
+}
+
+static COUNT
+GetRaceQueueValue (const QUEUE *queue) {
+ COUNT result;
+ HSTARSHIP hBattleShip, hNextShip;
+
+ result = 0;
+ for (hBattleShip = GetHeadLink (queue);
+ hBattleShip != 0; hBattleShip = hNextShip)
+ {
+ STARSHIP *StarShipPtr = LockStarShip (queue, hBattleShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+
+ if (StarShipPtr->SpeciesID == NO_ID)
+ continue; // Not active any more.
+
+ result += StarShipPtr->ship_cost;
+
+ UnlockStarShip (queue, hBattleShip);
+ }
+
+ return result;
+}
+
+// Cross out the icon for the dead ship.
+// 'frame' is the PickMeleeFrame for the player.
+// 'shipI' is the index in the ship list.
+// Pre: caller holds the graphics lock.
+static void
+CrossOutShip (FRAME frame, COUNT shipNr)
+{
+ CONTEXT OldContext;
+ STAMP s;
+ BYTE row = PickMelee_GetShipRow (shipNr);
+ BYTE col = PickMelee_GetShipColumn (shipNr);
+
+ OldContext = SetContext (OffScreenContext);
+
+ SetContextFGFrame (frame);
+
+ s.origin.x = 3 + ((ICON_WIDTH + 2) * col);
+ s.origin.y = 9 + ((ICON_HEIGHT + 2) * row);
+ s.frame = SetAbsFrameIndex (StatusFrame, 3);
+ // Cross for through the ship image.
+ DrawStamp (&s);
+
+ SetContext (OldContext);
+}
+
+// Draw the value of the fleet in the top right of the PickMeleeFrame.
+// Pre: caller holds the graphics lock.
+static void
+UpdatePickMeleeFleetValue (FRAME frame, COUNT which_player)
+{
+ CONTEXT OldContext;
+ COUNT value;
+ RECT r;
+ TEXT t;
+ UNICODE buf[40];
+
+ value = GetRaceQueueValue (&race_q[which_player]);
+
+ OldContext = SetContext (OffScreenContext);
+ SetContextFGFrame (frame);
+
+ // Erase the old value text.
+ GetFrameRect (frame, &r);
+ r.extent.width -= 4;
+ t.baseline.x = r.extent.width;
+ r.corner.x = r.extent.width - (6 * 3);
+ r.corner.y = 2;
+ r.extent.width = (6 * 3);
+ r.extent.height = 7 - 2;
+ SetContextForeGroundColor (PICK_BG_COLOR);
+ DrawFilledRectangle (&r);
+
+ // Draw the new value text.
+ sprintf (buf, "%d", value);
+ t.baseline.y = 7;
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (PICK_VALUE_COLOR);
+ font_DrawText (&t);
+
+ SetContext (OldContext);
+}
+
+// Create a frame for each player to display their current fleet in,
+// to be used when selecting the next ship to fight with.
+void
+BuildPickMeleeFrame (void)
+{
+ STAMP s;
+ CONTEXT OldContext = SetContext (OffScreenContext);
+
+ if (PickMeleeFrame)
+ DestroyDrawable (ReleaseDrawable (PickMeleeFrame));
+
+ PickMeleeFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, MELEE_WIDTH, MELEE_HEIGHT, 2));
+ s.origin.x = 0;
+ s.origin.y = 0;
+
+ s.frame = CaptureDrawable (LoadGraphic (MELEE_PICK_MASK_PMAP_ANIM));
+ SetContextFGFrame (PickMeleeFrame);
+ DrawStamp (&s);
+
+ s.frame = IncFrameIndex (s.frame);
+ SetContextFGFrame (IncFrameIndex (PickMeleeFrame));
+ DrawStamp (&s);
+
+ DestroyDrawable (ReleaseDrawable (s.frame));
+
+ SetContext (OldContext);
+}
+
+// Put the ship icons in the PickMeleeFrame, and create a queue
+// for each player.
+// XXX TODO: split off creating the queue into a separate function.
+void
+FillPickMeleeFrame (MeleeSetup *setup)
+{
+ COUNT i;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (OffScreenContext);
+
+ for (i = 0; i < NUM_SIDES; ++i)
+ {
+ COUNT side;
+ COUNT sideI;
+ RECT r;
+ TEXT t;
+ STAMP s;
+ UNICODE buf[30];
+ FleetShipIndex index;
+
+ sideI = GetPlayerOrder (i);
+ side = !sideI;
+
+ s.frame = SetAbsFrameIndex (PickMeleeFrame, side);
+ SetContextFGFrame (s.frame);
+
+ GetFrameRect (s.frame, &r);
+ t.baseline.x = r.extent.width >> 1;
+ t.baseline.y = r.extent.height - NAME_AREA_HEIGHT + 4;
+
+ r.corner.x += 2;
+ r.corner.y += 2;
+ r.extent.width -= (2 * 2) + (ICON_WIDTH + 2) + 1;
+ r.extent.height -= (2 * 2) + NAME_AREA_HEIGHT;
+ SetContextForeGroundColor (PICK_BG_COLOR);
+ DrawFilledRectangle (&r);
+
+ r.corner.x += 2;
+ r.extent.width += (ICON_WIDTH + 2) - (2 * 2);
+ r.corner.y += r.extent.height;
+ r.extent.height = NAME_AREA_HEIGHT;
+ DrawFilledRectangle (&r);
+
+ // Team name at the bottom of the frame:
+ t.align = ALIGN_CENTER;
+ t.pStr = MeleeSetup_getTeamName (setup, sideI);
+ t.CharCount = (COUNT) ~0;
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (PICKSHIP_TEAM_NAME_TEXT_COLOR);
+ font_DrawText (&t);
+
+ // Total team value of the starting team:
+ sprintf (buf, "%u", MeleeSetup_getFleetValue (setup, sideI));
+ t.baseline.x = 4;
+ t.baseline.y = 7;
+ t.align = ALIGN_LEFT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (PICKSHIP_TEAM_START_VALUE_COLOR);
+ font_DrawText (&t);
+
+ assert (CountLinks (&race_q[side]) == 0);
+
+ for (index = 0; index < MELEE_FLEET_SIZE; index++)
+ {
+ MeleeShip StarShip;
+
+ StarShip = MeleeSetup_getShip (setup, sideI, index);
+ if (StarShip == MELEE_NONE)
+ continue;
+
+ {
+ BYTE row, col;
+ BYTE ship_cost;
+ HMASTERSHIP hMasterShip;
+ HSTARSHIP hBuiltShip;
+ MASTER_SHIP_INFO *MasterPtr;
+ STARSHIP *BuiltShipPtr;
+ BYTE captains_name_index;
+
+ hMasterShip = GetStarShipFromIndex (&master_q, StarShip);
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+
+ captains_name_index = NameCaptain (&race_q[side],
+ MasterPtr->SpeciesID);
+ hBuiltShip = Build (&race_q[side], MasterPtr->SpeciesID);
+
+ // Draw the icon.
+ row = PickMelee_GetShipRow (index);
+ col = PickMelee_GetShipColumn (index);
+ s.origin.x = 4 + ((ICON_WIDTH + 2) * col);
+ s.origin.y = 10 + ((ICON_HEIGHT + 2) * row);
+ s.frame = MasterPtr->ShipInfo.icons;
+ DrawStamp (&s);
+
+ ship_cost = MasterPtr->ShipInfo.ship_cost;
+ UnlockMasterShip (&master_q, hMasterShip);
+
+ BuiltShipPtr = LockStarShip (&race_q[side], hBuiltShip);
+ BuiltShipPtr->index = index;
+ BuiltShipPtr->ship_cost = ship_cost;
+ BuiltShipPtr->playerNr = side;
+ BuiltShipPtr->captains_name_index = captains_name_index;
+ // The next ones are not used in Melee
+ BuiltShipPtr->crew_level = 0;
+ BuiltShipPtr->max_crew = 0;
+ BuiltShipPtr->race_strings = 0;
+ BuiltShipPtr->icons = 0;
+ BuiltShipPtr->RaceDescPtr = 0;
+ UnlockStarShip (&race_q[side], hBuiltShip);
+ }
+ }
+ }
+
+ SetContext (OldContext);
+}
+
+void
+DestroyPickMeleeFrame (void)
+{
+ DestroyDrawable (ReleaseDrawable (PickMeleeFrame));
+ PickMeleeFrame = 0;
+}
+
+// Pre: caller holds the graphics lock.
+static void
+DrawPickMeleeFrame (COUNT which_player)
+{
+ CONTEXT oldContext;
+ STAMP s;
+
+ oldContext = SetContext (SpaceContext);
+ s.frame = SetAbsFrameIndex (PickMeleeFrame, which_player);
+ s.origin.x = PICK_X_OFFS - 3;
+ s.origin.y = PICK_Y_OFFS - 9 + ((1 - which_player) * PICK_SIDE_OFFS);
+ DrawStamp (&s);
+ // Draw the selection box to screen.
+
+ SetContext (oldContext);
+}
+
+// Pre: caller holds the graphics lock.
+void
+MeleeGameOver (void)
+{
+ COUNT playerI;
+ DWORD TimeOut;
+ BOOLEAN PressState, ButtonState;
+
+ // Show the battle result.
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ DrawPickMeleeFrame (playerI);
+
+
+#ifdef NETPLAY
+ negotiateReadyConnections(true, NetState_inSetup);
+#endif
+
+ TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
+
+ PressState = PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL];
+ do
+ {
+ UpdateInputState ();
+ ButtonState = PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL];
+ if (PressState)
+ {
+ PressState = ButtonState;
+ ButtonState = FALSE;
+ }
+
+ Async_process ();
+ TaskSwitch ();
+ } while (!(GLOBAL (CurrentActivity) & CHECK_ABORT) && (!ButtonState
+ && (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)
+ || GetTimeCounter () < TimeOut)));
+
+}
+
+void
+MeleeShipDeath (STARSHIP *ship)
+{
+ FRAME frame;
+
+ // Deactivate fleet position.
+ ship->SpeciesID = NO_ID;
+
+ frame = SetAbsFrameIndex (PickMeleeFrame, ship->playerNr);
+ CrossOutShip (frame, ship->index);
+ UpdatePickMeleeFleetValue (frame, ship->playerNr);
+}
+
+// Post: the NetState for all players is NetState_interBattle
+static BOOLEAN
+GetMeleeStarShips (COUNT playerMask, HSTARSHIP *ships)
+{
+ COUNT playerI;
+ BOOLEAN ok;
+ GETMELEE_STATE gmstate;
+ TimeCount now;
+ COUNT i;
+
+#ifdef NETPLAY
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn;
+
+ if ((playerMask & (1 << playerI)) == 0)
+ continue;
+
+ // XXX: This does not have to be done per connection.
+ conn = netConnections[playerI];
+ if (conn != NULL) {
+ BattleStateData *battleStateData;
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData (conn);
+ battleStateData->getMeleeState = &gmstate;
+ }
+ }
+#endif
+
+ ok = true;
+
+ now = GetTimeCounter ();
+ gmstate.InputFunc = DoGetMelee;
+ gmstate.Initialized = FALSE;
+ for (i = 0; i < NUM_PLAYERS; ++i)
+ {
+ // We have to use TFB_Random() results in specific order
+ playerI = GetPlayerOrder (i);
+ gmstate.player[playerI].selecting =
+ (playerMask & (1 << playerI)) != 0;
+ gmstate.player[playerI].ships_left = battle_counter[playerI];
+
+ // We determine in advance which ship would be chosen if the player
+ // wants a random ship, to keep it simple to keep network parties
+ // synchronised.
+ gmstate.player[playerI].randomIndex =
+ (COUNT)TFB_Random () % gmstate.player[playerI].ships_left;
+ gmstate.player[playerI].done = FALSE;
+
+ if (!gmstate.player[playerI].selecting)
+ continue;
+
+ gmstate.player[playerI].timeIn = now;
+ gmstate.player[playerI].row = 0;
+ gmstate.player[playerI].col = NUM_PICKMELEE_COLUMNS;
+#ifdef NETPLAY
+ gmstate.player[playerI].remoteSelected = FALSE;
+#endif
+
+ gmstate.player[playerI].flashContext =
+ Flash_createHighlight (ScreenContext, NULL);
+ Flash_setMergeFactors (gmstate.player[playerI].flashContext,
+ 2, 3, 2);
+ Flash_setFrameTime (gmstate.player[playerI].flashContext,
+ ONE_SECOND / 16);
+#ifdef NETPLAY
+ if (PlayerControl[playerI] & NETWORK_CONTROL)
+ Flash_setSpeed (gmstate.player[playerI].flashContext,
+ ONE_SECOND / 2, 0, ONE_SECOND / 2, 0);
+ else
+#endif
+ {
+ Flash_setSpeed (gmstate.player[playerI].flashContext,
+ 0, ONE_SECOND / 16, 0, ONE_SECOND / 16);
+ }
+ PickMelee_ChangedSelection (&gmstate, playerI);
+ Flash_start (gmstate.player[playerI].flashContext);
+ }
+
+#ifdef NETPLAY
+ {
+ // NB. gmstate.player[].randomIndex and gmstate.player[].done must
+ // be initialised before negotiateReadyConnections is completed, to
+ // ensure that they are initialised when the SelectShip packet
+ // arrives.
+ bool allOk = negotiateReadyConnections (true, NetState_selectShip);
+ if (!allOk)
+ {
+ // Some network connection has been reset.
+ ok = false;
+ }
+ }
+#endif
+ SetDefaultMenuRepeatDelay ();
+
+ SetContext (OffScreenContext);
+
+
+ DoInput (&gmstate, FALSE);
+ WaitForSoundEnd (0);
+
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gmstate.player[playerI].selecting)
+ continue;
+
+ if (gmstate.player[playerI].done)
+ {
+ // Flash rectangle is already terminated.
+ ships[playerI] = gmstate.player[playerI].hBattleShip;
+ }
+ else
+ {
+ Flash_terminate (gmstate.player[playerI].flashContext);
+ gmstate.player[playerI].flashContext = NULL;
+ ok = false;
+ }
+ }
+
+#ifdef NETPLAY
+ if (ok)
+ {
+ if (!negotiateReadyConnections (true, NetState_interBattle))
+ ok = false;
+ }
+ else
+ setStateConnections (NetState_interBattle);
+#endif
+
+ if (!ok)
+ {
+ // Aborting.
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ }
+
+#ifdef NETPLAY
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn;
+
+ if ((playerMask & (1 << playerI)) == 0)
+ continue;
+
+ // XXX: This does not have to be done per connection.
+ conn = netConnections[playerI];
+ if (conn != NULL && NetConnection_isConnected (conn))
+ {
+ BattleStateData *battleStateData;
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData (conn);
+ battleStateData->getMeleeState = NULL;
+ }
+ }
+#endif
+
+ return ok;
+}
+
+BOOLEAN
+GetInitialMeleeStarShips (HSTARSHIP *result)
+{
+ COUNT playerI;
+ COUNT playerMask;
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ FRAME frame;
+ frame = SetAbsFrameIndex (PickMeleeFrame, playerI);
+ UpdatePickMeleeFleetValue (frame, playerI);
+ DrawPickMeleeFrame (playerI);
+ }
+
+ // Fade in
+ SleepThreadUntil (FadeScreen (FadeAllToColor, ONE_SECOND / 2)
+ + ONE_SECOND / 60);
+ FlushColorXForms ();
+
+ playerMask = 0;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ playerMask |= (1 << playerI);
+
+ return GetMeleeStarShips (playerMask, result);
+}
+
+// Get the next ship to use in SuperMelee.
+BOOLEAN
+GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result)
+{
+ COUNT playerMask;
+ HSTARSHIP ships[NUM_PLAYERS];
+ BOOLEAN ok;
+
+ DrawPickMeleeFrame (which_player);
+
+ playerMask = 1 << which_player;
+ ok = GetMeleeStarShips (playerMask, ships);
+ if (ok)
+ *result = ships[which_player];
+
+ return ok;
+}
+
+#ifdef NETPLAY
+// Called when a ship selection has arrived from a remote player.
+bool
+updateMeleeSelection (GETMELEE_STATE *gms, COUNT playerI, COUNT ship)
+{
+ if (gms == NULL || !gms->player[playerI].selecting ||
+ gms->player[playerI].done)
+ {
+ // This happens when we get an update message from a connection
+ // for who we are not selecting a ship.
+ log_add (log_Warning, "Unexpected ship selection packet "
+ "received.\n");
+ return false;
+ }
+
+ if (!setShipSelected (gms, playerI, ship, false))
+ {
+ log_add (log_Warning, "Invalid ship selection received from remote "
+ "party.\n");
+ return false;
+ }
+
+ gms->player[playerI].remoteSelected = TRUE;
+ return true;
+}
+
+static void
+reportShipSelected (GETMELEE_STATE *gms, COUNT index)
+{
+ size_t playerI;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ Netplay_Notify_shipSelected (conn, index);
+ }
+ (void) gms;
+}
+#endif
+