summaryrefslogtreecommitdiff
path: root/src/uqm/shipyard.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/shipyard.c')
-rw-r--r--src/uqm/shipyard.c1495
1 files changed, 1495 insertions, 0 deletions
diff --git a/src/uqm/shipyard.c b/src/uqm/shipyard.c
new file mode 100644
index 0000000..f317ba3
--- /dev/null
+++ b/src/uqm/shipyard.c
@@ -0,0 +1,1495 @@
+//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.
+ */
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "menustat.h"
+#include "fmv.h"
+#include "gameopt.h"
+#include "gamestr.h"
+#include "supermelee/melee.h"
+#include "master.h"
+#include "options.h"
+#include "races.h"
+#include "nameref.h"
+#include "resinst.h"
+#include "settings.h"
+#include "starbase.h"
+#include "setup.h"
+#include "sis.h"
+#include "units.h"
+#include "sounds.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/inplib.h"
+
+
+#ifdef USE_3DO_HANGAR
+// 3DO 4x3 hangar layout
+# define HANGAR_SHIPS_ROW 4
+# define HANGAR_Y 64
+# define HANGAR_DY 44
+
+static const COORD hangar_x_coords[HANGAR_SHIPS_ROW] =
+{
+ 19, 60, 116, 157
+};
+
+#else // use PC hangar
+// modified PC 6x2 hangar layout
+# define HANGAR_SHIPS_ROW 6
+# define HANGAR_Y 88
+# define HANGAR_DY 84
+
+static const COORD hangar_x_coords[HANGAR_SHIPS_ROW] =
+{
+ 0, 38, 76, 131, 169, 207
+};
+#endif // USE_3DO_HANGAR
+
+#define HANGAR_SHIPS 12
+#define HANGAR_ROWS (HANGAR_SHIPS / HANGAR_SHIPS_ROW)
+#define HANGAR_ANIM_RATE 15 // fps
+
+enum
+{
+ SHIPYARD_CREW,
+ SHIPYARD_SAVELOAD,
+ SHIPYARD_EXIT
+};
+
+// Editing mode for DoModifyShips()
+typedef enum {
+ DMS_Mode_navigate, // Navigating the ship slots.
+ DMS_Mode_addEscort, // Selecting a ship to add to an empty slot.
+ DMS_Mode_editCrew, // Hiring or dismissing crew.
+ DMS_Mode_exit, // Leaving DoModifyShips() mode.
+} DMS_Mode;
+
+static COUNT ShipCost[] =
+{
+ RACE_SHIP_COST
+};
+
+
+static void
+animatePowerLines (MENU_STATE *pMS)
+{
+ static STAMP s;
+ static COLORMAP ColorMap;
+ static TimeCount NextTime = 0;
+ TimeCount Now = GetTimeCounter ();
+
+ if (pMS)
+ { // Init animation
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame, 24);
+ ColorMap = SetAbsColorMapIndex (pMS->CurString, 0);
+ }
+
+ if (Now >= NextTime || pMS)
+ {
+ NextTime = Now + (ONE_SECOND / HANGAR_ANIM_RATE);
+
+ SetColorMap (GetColorMapAddress (ColorMap));
+ DrawStamp (&s);
+ // Advance colomap cycle
+ ColorMap = SetRelColorMapIndex (ColorMap, 1);
+ }
+}
+
+static void
+on_input_frame (void)
+{
+ CONTEXT oldContext;
+
+ oldContext = SetContext (SpaceContext);
+ animatePowerLines (NULL);
+ SetContext (oldContext);
+}
+
+#ifdef WANT_SHIP_SPINS
+static void
+SpinStarShip (MENU_STATE *pMS, HFLEETINFO hStarShip)
+{
+ int Index;
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ Index = FindMasterShipIndex (FleetPtr->SpeciesID);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+
+ if (Index >= 0 && Index < NUM_MELEE_SHIPS)
+ {
+ DoShipSpin (Index, pMS->hMusic);
+ }
+}
+#endif
+
+// Count the ships which can be built by the player.
+static COUNT
+GetAvailableRaceCount (void)
+{
+ COUNT Index;
+ HFLEETINFO hStarShip, hNextShip;
+
+ Index = 0;
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ if (FleetPtr->allied_state == GOOD_GUY)
+ ++Index;
+
+ hNextShip = _GetSuccLink (FleetPtr);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ return Index;
+}
+
+static HFLEETINFO
+GetAvailableRaceFromIndex (BYTE Index)
+{
+ HFLEETINFO hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ if (FleetPtr->allied_state == GOOD_GUY && Index-- == 0)
+ {
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ return hStarShip;
+ }
+
+ hNextShip = _GetSuccLink (FleetPtr);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ return 0;
+}
+
+static void
+DrawRaceStrings (MENU_STATE *pMS, BYTE NewRaceItem)
+{
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+
+
+ OldContext = SetContext (StatusContext);
+ GetContextClipRect (&r);
+ s.origin.x = RADAR_X - r.corner.x;
+ s.origin.y = RADAR_Y - r.corner.y;
+ r.corner.x = s.origin.x - 1;
+ r.corner.y = s.origin.y - 11;
+ r.extent.width = RADAR_WIDTH + 2;
+ r.extent.height = 11;
+ BatchGraphics ();
+ ClearSISRect (CLEAR_SIS_RADAR);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ DrawFilledRectangle (&r);
+ r.corner = s.origin;
+ r.extent.width = RADAR_WIDTH;
+ r.extent.height = RADAR_HEIGHT;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ if (NewRaceItem != (BYTE)~0)
+ {
+ TEXT t;
+ HFLEETINFO hStarShip;
+ FLEET_INFO *FleetPtr;
+ UNICODE buf[30];
+
+ hStarShip = GetAvailableRaceFromIndex (NewRaceItem);
+ NewRaceItem = GetIndexFromStarShip (&GLOBAL (avail_race_q),
+ hStarShip);
+
+ // Draw the ship name, above the ship image.
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame, 3 + NewRaceItem);
+ DrawStamp (&s);
+
+ // Draw the ship image.
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ s.frame = FleetPtr->melee_icon;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ s.origin.x += (RADAR_WIDTH >> 1);
+ s.origin.y += (RADAR_HEIGHT >> 1);
+ DrawStamp (&s);
+
+ // Print the ship cost.
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ sprintf (buf, "%u", ShipCost[NewRaceItem]);
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0x02));
+ font_DrawText (&t);
+ }
+ UnbatchGraphics ();
+ SetContext (OldContext);
+
+}
+
+#define SHIP_WIN_WIDTH 34
+#define SHIP_WIN_HEIGHT (SHIP_WIN_WIDTH + 6)
+#define SHIP_WIN_FRAMES ((SHIP_WIN_WIDTH >> 1) + 1)
+
+// Print the crew count of an escort ship on top of its (already drawn)
+// image, either as '30' (full), '28/30' (partially full), or 'SCRAP'
+// (empty).
+// pRect is the rectangle of the ship image.
+static void
+ShowShipCrew (SHIP_FRAGMENT *StarShipPtr, const RECT *pRect)
+{
+ RECT r;
+ TEXT t;
+ UNICODE buf[80];
+ HFLEETINFO hTemplate;
+ FLEET_INFO *TemplatePtr;
+ COUNT maxCrewLevel;
+
+ hTemplate = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ StarShipPtr->race_id);
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ maxCrewLevel = TemplatePtr->crew_level;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+
+ if (StarShipPtr->crew_level >= maxCrewLevel)
+ sprintf (buf, "%u", StarShipPtr->crew_level);
+ else if (StarShipPtr->crew_level == 0)
+ // XXX: "SCRAP" needs to be moved to starcon.txt
+ utf8StringCopy (buf, sizeof (buf), "SCRAP");
+ else
+ sprintf (buf, "%u/%u", StarShipPtr->crew_level, maxCrewLevel);
+
+ r = *pRect;
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + r.extent.height - 1;
+ t.align = ALIGN_CENTER;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ if (r.corner.y)
+ {
+ r.corner.y = t.baseline.y - 6;
+ r.extent.width = SHIP_WIN_WIDTH;
+ r.extent.height = 6;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ }
+ SetContextForeGroundColor ((StarShipPtr->crew_level != 0) ?
+ (BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)):
+ (BUILD_COLOR (MAKE_RGB15 (0x12, 0x00, 0x00), 0x2B)));
+ font_DrawText (&t);
+}
+
+static void
+ShowCombatShip (MENU_STATE *pMS, COUNT which_window,
+ SHIP_FRAGMENT *YankedStarShipPtr)
+{
+ COUNT i;
+ COUNT num_ships;
+ HSHIPFRAG hStarShip, hNextShip;
+ SHIP_FRAGMENT *StarShipPtr;
+ struct
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+ POINT finished_s;
+ STAMP ship_s;
+ STAMP lfdoor_s;
+ STAMP rtdoor_s;
+ } ship_win_info[MAX_BUILT_SHIPS], *pship_win_info;
+
+ num_ships = 1;
+ pship_win_info = &ship_win_info[0];
+ if (YankedStarShipPtr)
+ {
+ pship_win_info->StarShipPtr = YankedStarShipPtr;
+
+ pship_win_info->lfdoor_s.origin.x = -(SHIP_WIN_WIDTH >> 1);
+ pship_win_info->rtdoor_s.origin.x = (SHIP_WIN_WIDTH >> 1);
+ pship_win_info->lfdoor_s.origin.y = 0;
+ pship_win_info->rtdoor_s.origin.y = 0;
+ pship_win_info->lfdoor_s.frame = IncFrameIndex (pMS->ModuleFrame);
+ pship_win_info->rtdoor_s.frame =
+ IncFrameIndex (pship_win_info->lfdoor_s.frame);
+
+ pship_win_info->ship_s.origin.x = (SHIP_WIN_WIDTH >> 1) + 1;
+ pship_win_info->ship_s.origin.y = (SHIP_WIN_WIDTH >> 1);
+ pship_win_info->ship_s.frame = YankedStarShipPtr->melee_icon;
+
+ pship_win_info->finished_s.x = hangar_x_coords[
+ which_window % HANGAR_SHIPS_ROW];
+ pship_win_info->finished_s.y = HANGAR_Y + (HANGAR_DY *
+ (which_window / HANGAR_SHIPS_ROW));
+ }
+ else
+ {
+ if (which_window == (COUNT)~0)
+ {
+ hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ num_ships = CountLinks (&GLOBAL (built_ship_q));
+ }
+ else
+ {
+ HSHIPFRAG hTailShip;
+
+ hTailShip = GetTailLink (&GLOBAL (built_ship_q));
+ RemoveQueue (&GLOBAL (built_ship_q), hTailShip);
+
+ hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ while (hStarShip)
+ {
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ if (StarShipPtr->index > which_window)
+ {
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ break;
+ }
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ hStarShip = hNextShip;
+ }
+ InsertQueue (&GLOBAL (built_ship_q), hTailShip, hStarShip);
+
+ hStarShip = hTailShip;
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ StarShipPtr->index = which_window;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ for (i = 0; i < num_ships; ++i)
+ {
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+
+ pship_win_info->StarShipPtr = StarShipPtr;
+ // XXX BUG: this looks wrong according to the original
+ // semantics of LockShipFrag(): StarShipPtr is not valid
+ // anymore after UnlockShipFrag() is called, but it is
+ // used thereafter.
+
+ pship_win_info->lfdoor_s.origin.x = -1;
+ pship_win_info->rtdoor_s.origin.x = 1;
+ pship_win_info->lfdoor_s.origin.y = 0;
+ pship_win_info->rtdoor_s.origin.y = 0;
+ pship_win_info->lfdoor_s.frame = IncFrameIndex (pMS->ModuleFrame);
+ pship_win_info->rtdoor_s.frame =
+ IncFrameIndex (pship_win_info->lfdoor_s.frame);
+
+ pship_win_info->ship_s.origin.x = (SHIP_WIN_WIDTH >> 1) + 1;
+ pship_win_info->ship_s.origin.y = (SHIP_WIN_WIDTH >> 1);
+ pship_win_info->ship_s.frame = StarShipPtr->melee_icon;
+
+ which_window = StarShipPtr->index;
+ pship_win_info->finished_s.x = hangar_x_coords[
+ which_window % HANGAR_SHIPS_ROW];
+ pship_win_info->finished_s.y = HANGAR_Y + (HANGAR_DY *
+ (which_window / HANGAR_SHIPS_ROW));
+ ++pship_win_info;
+
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hStarShip = hNextShip;
+ }
+ }
+
+ if (num_ships)
+ {
+ BOOLEAN AllDoorsFinished;
+ DWORD TimeIn;
+ RECT r;
+ CONTEXT OldContext;
+ RECT OldClipRect;
+ int j;
+
+ AllDoorsFinished = FALSE;
+ r.corner.x = r.corner.y = 0;
+ r.extent.width = SHIP_WIN_WIDTH;
+ r.extent.height = SHIP_WIN_HEIGHT;
+ FlushInput ();
+ TimeIn = GetTimeCounter ();
+
+ for (j = 0; (j < SHIP_WIN_FRAMES) && !AllDoorsFinished; j++)
+ {
+ SleepThreadUntil (TimeIn + ONE_SECOND / 24);
+ TimeIn = GetTimeCounter ();
+ if (AnyButtonPress (FALSE))
+ {
+ if (YankedStarShipPtr != 0)
+ { // Fully close the doors
+ ship_win_info[0].lfdoor_s.origin.x = 0;
+ ship_win_info[0].rtdoor_s.origin.x = 0;
+ }
+ AllDoorsFinished = TRUE;
+ }
+
+ OldContext = SetContext (SpaceContext);
+ GetContextClipRect (&OldClipRect);
+ SetContextBackGroundColor (BLACK_COLOR);
+
+ BatchGraphics ();
+ pship_win_info = &ship_win_info[0];
+ for (i = 0; i < num_ships; ++i)
+ {
+ {
+ RECT ClipRect;
+
+ ClipRect.corner.x = SIS_ORG_X +
+ pship_win_info->finished_s.x;
+ ClipRect.corner.y = SIS_ORG_Y +
+ pship_win_info->finished_s.y;
+ ClipRect.extent.width = SHIP_WIN_WIDTH;
+ ClipRect.extent.height = SHIP_WIN_HEIGHT;
+ SetContextClipRect (&ClipRect);
+
+ ClearDrawable ();
+ DrawStamp (&pship_win_info->ship_s);
+ ShowShipCrew (pship_win_info->StarShipPtr, &r);
+ if (!AllDoorsFinished || YankedStarShipPtr)
+ {
+ DrawStamp (&pship_win_info->lfdoor_s);
+ DrawStamp (&pship_win_info->rtdoor_s);
+ if (YankedStarShipPtr)
+ { // Close the doors
+ ++pship_win_info->lfdoor_s.origin.x;
+ --pship_win_info->rtdoor_s.origin.x;
+ }
+ else
+ { // Open the doors
+ --pship_win_info->lfdoor_s.origin.x;
+ ++pship_win_info->rtdoor_s.origin.x;
+ }
+ }
+ }
+ ++pship_win_info;
+ }
+
+ SetContextClipRect (&OldClipRect);
+#ifndef USE_3DO_HANGAR
+ animatePowerLines (NULL);
+#endif
+ UnbatchGraphics ();
+ SetContext (OldContext);
+ }
+ }
+}
+
+static void
+CrewTransaction (SIZE crew_delta)
+{
+ if (crew_delta)
+ {
+ SIZE crew_bought;
+
+ crew_bought = (SIZE)MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1)) + crew_delta;
+ if (crew_bought < 0)
+ {
+ if (crew_delta < 0)
+ crew_bought = 0;
+ else
+ crew_bought = 0x7FFF;
+ }
+ else if (crew_delta > 0)
+ {
+ if (crew_bought >= CREW_EXPENSE_THRESHOLD
+ && crew_bought - crew_delta < CREW_EXPENSE_THRESHOLD)
+ {
+ GLOBAL (CrewCost) += 2;
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+ }
+ }
+ else
+ {
+ if (crew_bought < CREW_EXPENSE_THRESHOLD
+ && crew_bought - crew_delta >= CREW_EXPENSE_THRESHOLD)
+ {
+ GLOBAL (CrewCost) -= 2;
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+ }
+ }
+ if (CheckAlliance (SHOFIXTI_SHIP) != GOOD_GUY)
+ {
+ SET_GAME_STATE (CREW_PURCHASED0, LOBYTE (crew_bought));
+ SET_GAME_STATE (CREW_PURCHASED1, HIBYTE (crew_bought));
+ }
+ }
+}
+
+static void
+DMS_FlashFlagShip (void)
+{
+ RECT r;
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = 61;
+ SetFlashRect (&r);
+}
+
+static void
+DMS_GetEscortShipRect (RECT *rOut, BYTE slotNr)
+{
+ BYTE row = slotNr / HANGAR_SHIPS_ROW;
+ BYTE col = slotNr % HANGAR_SHIPS_ROW;
+
+ rOut->corner.x = hangar_x_coords[col];
+ rOut->corner.y = HANGAR_Y + (HANGAR_DY * row);
+ rOut->extent.width = SHIP_WIN_WIDTH;
+ rOut->extent.height = SHIP_WIN_HEIGHT;
+}
+
+static void
+DMS_FlashEscortShip (BYTE slotNr)
+{
+ RECT r;
+ DMS_GetEscortShipRect (&r, slotNr);
+ SetFlashRect (&r);
+}
+
+static void
+DMS_FlashFlagShipCrewCount (void)
+{
+ RECT r;
+ SetContext (StatusContext);
+ GetGaugeRect (&r, TRUE);
+ SetFlashRect (&r);
+ SetContext (SpaceContext);
+}
+
+static void
+DMS_FlashEscortShipCrewCount (BYTE slotNr)
+{
+ RECT r;
+ BYTE row = slotNr / HANGAR_SHIPS_ROW;
+ BYTE col = slotNr % HANGAR_SHIPS_ROW;
+
+ r.corner.x = hangar_x_coords[col];
+ r.corner.y = (HANGAR_Y + (HANGAR_DY * row)) + (SHIP_WIN_HEIGHT - 6);
+ r.extent.width = SHIP_WIN_WIDTH;
+ r.extent.height = 5;
+
+ SetContext (SpaceContext);
+ SetFlashRect (&r);
+}
+
+// Helper function for DoModifyShips(). Called to change the flash
+// rectangle to the currently selected ship (flagship or escort ship).
+static void
+DMS_FlashActiveShip (MENU_STATE *pMS)
+{
+ if (HINIBBLE (pMS->CurState))
+ {
+ // Flash the flag ship.
+ DMS_FlashFlagShip ();
+ }
+ else
+ {
+ // Flash the current escort ship slot.
+ DMS_FlashEscortShip (pMS->CurState);
+ }
+}
+
+// Helper function for DoModifyShips(). Called to switch between
+// the various edit modes.
+// XXX: right now, this only switches the sound and flash rectangle.
+// Perhaps we should move more of the code to modify other aspects
+// here too.
+static void
+DMS_SetMode (MENU_STATE *pMS, DMS_Mode mode)
+{
+ switch (mode) {
+ case DMS_Mode_navigate:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DMS_FlashActiveShip (pMS);
+ break;
+ case DMS_Mode_addEscort:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetFlashRect (SFR_MENU_ANY);
+ break;
+ case DMS_Mode_editCrew:
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN,
+ MENU_SOUND_SELECT | MENU_SOUND_CANCEL);
+ if (HINIBBLE (pMS->CurState))
+ {
+ // Enter crew editing mode for the flagship.
+ DMS_FlashFlagShipCrewCount ();
+ }
+ else
+ {
+ // Enter crew editing mode for an escort ship.
+ DMS_FlashEscortShipCrewCount (pMS->CurState);
+ }
+ break;
+ case DMS_Mode_exit:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetFlashRect (SFR_MENU_3DO);
+ break;
+ }
+}
+
+#define MODIFY_CREW_FLAG (1 << 8)
+#ifdef WANT_SHIP_SPINS
+// Helper function for DoModifyShips(), called when the player presses the
+// special button.
+// It works both when the cursor is over an escort ship, while not editing
+// the crew, and when a new ship is added.
+// hStarShip is the ship in the slot under the cursor (or 0 if no such ship).
+static BOOLEAN
+DMS_SpinShip (MENU_STATE *pMS, HSHIPFRAG hStarShip)
+{
+ HFLEETINFO hSpinShip = 0;
+ CONTEXT OldContext;
+ RECT OldClipRect;
+
+ // No spinning the flagship.
+ if (HINIBBLE (pMS->CurState) != 0)
+ return FALSE;
+
+ // We must either be hovering over a used ship slot, or adding a new
+ // ship to the fleet.
+ if ((hStarShip == 0) == !(pMS->delta_item & MODIFY_CREW_FLAG))
+ return FALSE;
+
+ if (!hStarShip)
+ {
+ // Selecting a ship to build.
+ hSpinShip = GetAvailableRaceFromIndex (LOBYTE (pMS->delta_item));
+ if (!hSpinShip)
+ return FALSE;
+ }
+ else
+ {
+ // Hovering over an escort ship.
+ SHIP_FRAGMENT *FragPtr = LockShipFrag (
+ &GLOBAL (built_ship_q), hStarShip);
+ hSpinShip = GetStarShipFromIndex (
+ &GLOBAL (avail_race_q), FragPtr->race_id);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ SetFlashRect (NULL);
+
+ OldContext = SetContext (ScreenContext);
+ GetContextClipRect (&OldClipRect);
+
+ SpinStarShip (pMS, hSpinShip);
+
+ SetContextClipRect (&OldClipRect);
+ SetContext (OldContext);
+
+ return TRUE;
+}
+#endif /* WANT_SHIP_SPINS */
+
+// Helper function for DoModifyShips(), called when the player presses the
+// up button when modifying the crew of the flagship.
+// Buy crew for the flagship.
+// Returns the change in crew (1 on success, 0 on failure).
+static SIZE
+DMS_HireFlagShipCrew (void)
+{
+ RECT r;
+
+ if (GetCPodCapacity (&r.corner) <= GetCrewCount ())
+ {
+ // At capacity.
+ return 0;
+ }
+
+ if (GLOBAL_SIS (ResUnits) < (DWORD)GLOBAL (CrewCost))
+ {
+ // Not enough RUs.
+ return 0;
+ }
+
+ // Draw a crew member.
+ DrawPoint (&r.corner);
+
+ // Update the crew counter and RU. Note that the crew counter is
+ // flashing.
+ PreUpdateFlashRect ();
+ DeltaSISGauges (1, 0, -GLOBAL (CrewCost));
+ PostUpdateFlashRect ();
+
+ return 1;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// down button when modifying the crew of the flagship.
+// Dismiss crew from the flagship.
+// Returns the change in crew (-1 on success, 0 on failure).
+static SIZE
+DMS_DismissFlagShipCrew (void)
+{
+ SIZE crew_bought;
+ RECT r;
+
+ if (GetCrewCount () == 0)
+ {
+ // No crew to dismiss.
+ return 0;
+ }
+
+ crew_bought = (SIZE)MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1));
+
+ // Update the crew counter and RU. Note that the crew counter is
+ // flashing.
+ PreUpdateFlashRect ();
+ DeltaSISGauges (-1, 0, GLOBAL (CrewCost) -
+ (crew_bought == CREW_EXPENSE_THRESHOLD ? 2 : 0));
+ PostUpdateFlashRect ();
+
+ // Remove the pixel representing the crew member.
+ GetCPodCapacity (&r.corner);
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawPoint (&r.corner);
+
+ return -1;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// up button when modifying the crew of an escort ship.
+// Buy crew for an escort ship
+// Returns the change in crew (1 on success, 0 on failure).
+static SIZE
+DMS_HireEscortShipCrew (SHIP_FRAGMENT *StarShipPtr)
+{
+ COUNT templateMaxCrew;
+ RECT r;
+
+ {
+ // XXX Split this off into a separate function?
+ HFLEETINFO hTemplate = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ StarShipPtr->race_id);
+ FLEET_INFO *TemplatePtr =
+ LockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ templateMaxCrew = TemplatePtr->crew_level;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ }
+
+ if (GLOBAL_SIS (ResUnits) < (DWORD)GLOBAL (CrewCost))
+ {
+ // Not enough money to hire a crew member.
+ return 0;
+ }
+
+ if (StarShipPtr->crew_level >= StarShipPtr->max_crew)
+ {
+ // This ship cannot handle more crew.
+ return 0;
+ }
+
+ if (StarShipPtr->crew_level >= templateMaxCrew)
+ {
+ // A ship of this type cannot handle more crew.
+ return 0;
+ }
+
+ if (StarShipPtr->crew_level > 0)
+ {
+ DeltaSISGauges (0, 0, -GLOBAL (CrewCost));
+ }
+ else
+ {
+ // Buy a ship.
+ DeltaSISGauges (0, 0, -(COUNT)ShipCost[StarShipPtr->race_id]);
+ }
+
+ ++StarShipPtr->crew_level;
+
+ PreUpdateFlashRect ();
+ DMS_GetEscortShipRect (&r, StarShipPtr->index);
+ ShowShipCrew (StarShipPtr, &r);
+ PostUpdateFlashRect ();
+
+ return 1;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// down button when modifying the crew of an escort ship.
+// Dismiss crew from an escort ship
+// Returns the change in crew (-1 on success, 0 on failure).
+static SIZE
+DMS_DismissEscortShipCrew (SHIP_FRAGMENT *StarShipPtr)
+{
+ SIZE crew_delta = 0;
+ RECT r;
+
+ if (StarShipPtr->crew_level > 0)
+ {
+ if (StarShipPtr->crew_level > 1)
+ {
+ // The ship was not at 'scrap'.
+ // Give one crew member worth of RU.
+ SIZE crew_bought = (SIZE)MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1));
+
+ DeltaSISGauges (0, 0, GLOBAL (CrewCost)
+ - (crew_bought == CREW_EXPENSE_THRESHOLD ? 2 : 0));
+ }
+ else
+ {
+ // With the last crew member, the ship will be scrapped.
+ // Give RU for the ship.
+ DeltaSISGauges (0, 0, (COUNT)ShipCost[StarShipPtr->race_id]);
+ }
+ crew_delta = -1;
+ --StarShipPtr->crew_level;
+ }
+ else
+ { // no crew to dismiss
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+
+ PreUpdateFlashRect ();
+ DMS_GetEscortShipRect (&r, StarShipPtr->index);
+ ShowShipCrew (StarShipPtr, &r);
+ PostUpdateFlashRect ();
+
+ return crew_delta;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// up or down button when modifying the crew of the flagship or of an escort
+// ship.
+// 'hStarShip' is the currently escort ship, or 0 if no ship is
+// selected.
+// 'dy' is -1 if the 'up' button was pressed, or '1' if the down button was
+// pressed.
+static void
+DMS_ModifyCrew (MENU_STATE *pMS, HSHIPFRAG hStarShip, SBYTE dy)
+{
+ SIZE crew_delta = 0;
+ SHIP_FRAGMENT *StarShipPtr = NULL;
+
+ if (hStarShip)
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (hStarShip == 0)
+ {
+ // Add/Dismiss crew for the flagship.
+ if (dy < 0)
+ {
+ // Add crew for the flagship.
+ crew_delta = DMS_HireFlagShipCrew ();
+ }
+ else
+ {
+ // Dismiss crew from the flagship.
+ crew_delta = DMS_DismissFlagShipCrew ();
+ }
+
+ if (crew_delta != 0)
+ DMS_FlashFlagShipCrewCount ();
+ }
+ else
+ {
+ // Add/Dismiss crew for an escort ship.
+ if (dy < 0)
+ {
+ // Add crew for an escort ship.
+ crew_delta = DMS_HireEscortShipCrew (StarShipPtr);
+ }
+ else
+ {
+ // Dismiss crew from an escort ship.
+ crew_delta = DMS_DismissEscortShipCrew (StarShipPtr);
+ }
+
+ if (crew_delta != 0)
+ DMS_FlashEscortShipCrewCount (StarShipPtr->index);
+ }
+
+ if (crew_delta == 0)
+ PlayMenuSound (MENU_SOUND_FAILURE);
+
+ if (hStarShip)
+ {
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ // Clear out the bought ship index so that flash rects work
+ // correctly.
+ pMS->delta_item &= MODIFY_CREW_FLAG;
+ }
+
+ CrewTransaction (crew_delta);
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// select button when the cursor is over an empty escort ship slot.
+// Try to add the currently selected ship as an escort ship.
+static void
+DMS_TryAddEscortShip (MENU_STATE *pMS)
+{
+ HFLEETINFO shipInfo = GetAvailableRaceFromIndex (
+ LOBYTE (pMS->delta_item));
+ COUNT Index = GetIndexFromStarShip (&GLOBAL (avail_race_q), shipInfo);
+
+ if (GLOBAL_SIS (ResUnits) >= (DWORD)ShipCost[Index]
+ && CloneShipFragment (Index, &GLOBAL (built_ship_q), 1))
+ {
+ ShowCombatShip (pMS, pMS->CurState, NULL);
+ // Reset flash rectangle
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA,
+ -((int)ShipCost[Index]));
+ DMS_SetMode (pMS, DMS_Mode_editCrew);
+ }
+ else
+ {
+ // not enough RUs to build, or cloning the ship failed.
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+}
+
+// Helper function for DoModifyShips(), called when the player is in the
+// mode to add a new escort ship to the fleet (after pressing select on an
+// empty slot).
+// LOBYTE (pMS->delta_item) is used to store the currently highlighted ship.
+// Returns FALSE if the flash rectangle needs to be updated.
+static void
+DMS_AddEscortShip (MENU_STATE *pMS, BOOLEAN special, BOOLEAN select,
+ BOOLEAN cancel, SBYTE dx, SBYTE dy)
+{
+ assert (pMS->delta_item & MODIFY_CREW_FLAG);
+
+#ifdef WANT_SHIP_SPINS
+ if (special)
+ {
+ HSHIPFRAG hStarShip = GetEscortByStarShipIndex (pMS->delta_item);
+ if (DMS_SpinShip (pMS, hStarShip))
+ DMS_SetMode (pMS, DMS_Mode_addEscort);
+ return;
+ }
+#else
+ (void) special; // Satisfying compiler.
+#endif /* WANT_SHIP_SPINS */
+
+ if (cancel)
+ {
+ // Cancel selecting an escort ship.
+ pMS->delta_item &= ~MODIFY_CREW_FLAG;
+ SetFlashRect (NULL);
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else if (select)
+ {
+ // Selected a ship to be inserted in an empty escort
+ // ship slot.
+ DMS_TryAddEscortShip (pMS);
+ }
+ else if (dx || dy)
+ {
+ // Motion key pressed while selecting a ship to be
+ // inserted in an empty escort ship slot.
+ COUNT availableCount = GetAvailableRaceCount ();
+ BYTE currentShip = LOBYTE (pMS->delta_item);
+ if (dx < 0 || dy < 0)
+ {
+ if (currentShip-- == 0)
+ currentShip = availableCount - 1;
+ }
+ else if (dx > 0 || dy > 0)
+ {
+ if (++currentShip == availableCount)
+ currentShip = 0;
+ }
+
+ if (currentShip != LOBYTE (pMS->delta_item))
+ {
+ PreUpdateFlashRect ();
+ DrawRaceStrings (pMS, currentShip);
+ PostUpdateFlashRect ();
+ pMS->delta_item = currentShip | MODIFY_CREW_FLAG;
+ }
+ }
+}
+
+// Helper function for DoModifyShips(), called when the player presses
+// 'select' or 'cancel' after selling all the crew.
+static void
+DMS_ScrapEscortShip (MENU_STATE *pMS, HSHIPFRAG hStarShip)
+{
+ SHIP_FRAGMENT *StarShipPtr =
+ LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ BYTE slotNr;
+
+ SetFlashRect (NULL);
+ ShowCombatShip (pMS, pMS->CurState, StarShipPtr);
+
+ slotNr = StarShipPtr->index;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
+ FreeShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ // refresh SIS display
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+
+ SetContext (SpaceContext);
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+}
+
+// Helper function for DoModifyShips(), called when the player presses
+// one of the motion keys when not in crew modification mode.
+static BYTE
+DMS_MoveCursor (BYTE curState, SBYTE dx, SBYTE dy)
+{
+ BYTE row = LONIBBLE(curState) / HANGAR_SHIPS_ROW;
+ BYTE col = LONIBBLE(curState) % HANGAR_SHIPS_ROW;
+ BOOLEAN isFlagShipSelected = (HINIBBLE(curState) != 0);
+
+ if (dy)
+ {
+ // Vertical motion.
+
+ // We consider the flagship an extra row (on the bottom),
+ // to ease operations.
+ if (isFlagShipSelected)
+ row = HANGAR_ROWS;
+
+ // Move up/down, wrapping around:
+ row = (row + (HANGAR_ROWS + 1) + dy) % (HANGAR_ROWS + 1);
+
+ // If we moved to the 'extra row', this means the flag ship.
+ isFlagShipSelected = (row == HANGAR_ROWS);
+ if (isFlagShipSelected)
+ row = 0;
+ }
+ else if (dx)
+ {
+ // Horizontal motion.
+ if (!isFlagShipSelected)
+ {
+ // Moving horizontally through the escort ship slots,
+ // wrapping around if necessary.
+ col = (col + HANGAR_SHIPS_ROW + dx) % HANGAR_SHIPS_ROW;
+ }
+ }
+
+ return MAKE_BYTE(row * HANGAR_SHIPS_ROW + col,
+ isFlagShipSelected ? 0xf : 0);
+}
+
+// Helper function for DoModifyShips(), called every time DoModifyShip() is
+// called when we are in crew editing mode.
+static void
+DMS_EditCrewMode (MENU_STATE *pMS, HSHIPFRAG hStarShip,
+ BOOLEAN select, BOOLEAN cancel, SBYTE dy)
+{
+ if (select || cancel)
+ {
+ // Leave crew editing mode.
+ if (hStarShip != 0)
+ {
+ // Exiting crew editing mode for an escort ship.
+ SHIP_FRAGMENT *StarShipPtr = LockShipFrag (
+ &GLOBAL (built_ship_q), hStarShip);
+ COUNT crew_level = StarShipPtr->crew_level;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (crew_level == 0)
+ {
+ // Scrapping the escort ship before exiting crew edit
+ // mode.
+ DMS_ScrapEscortShip (pMS, hStarShip);
+ }
+ }
+
+ pMS->delta_item &= ~MODIFY_CREW_FLAG;
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else if (dy)
+ {
+ // Hire or dismiss crew for the flagship or an escort
+ // ship.
+ DMS_ModifyCrew (pMS, hStarShip, dy);
+ }
+}
+
+// Helper function for DoModifyShips(), called every time DoModifyShip() is
+// called when we are in the mode where you can select a ship or empty slot.
+static void
+DMS_NavigateShipSlots (MENU_STATE *pMS, BOOLEAN special, BOOLEAN select,
+ BOOLEAN cancel, SBYTE dx, SBYTE dy)
+{
+ HSHIPFRAG hStarShip = GetEscortByStarShipIndex (pMS->CurState);
+
+ if (dx || dy)
+ {
+ // Moving through the ship slots.
+ BYTE NewState = DMS_MoveCursor (pMS->CurState, dx, dy);
+ if (NewState != pMS->CurState)
+ {
+ pMS->CurState = NewState;
+ DMS_FlashActiveShip(pMS);
+ }
+ }
+
+#ifndef WANT_SHIP_SPINS
+ (void) special; // Satisfying compiler.
+#else
+ if (special)
+ {
+ if (DMS_SpinShip (pMS, hStarShip))
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else
+#endif /* WANT_SHIP_SPINS */
+ if (select)
+ {
+ if (hStarShip == 0 && HINIBBLE (pMS->CurState) == 0)
+ {
+ // Select button was pressed over an empty escort
+ // ship slot. Switch to 'add escort ship' mode.
+ pMS->delta_item = MODIFY_CREW_FLAG;
+ DrawRaceStrings (pMS, 0);
+ DMS_SetMode (pMS, DMS_Mode_addEscort);
+ }
+ else
+ {
+ // Select button was pressed over an escort ship or
+ // the flagship. Entering crew editing mode
+ pMS->delta_item |= MODIFY_CREW_FLAG;
+ DMS_SetMode (pMS, DMS_Mode_editCrew);
+ }
+ }
+ else if (cancel)
+ {
+ // Leave escort ship editor.
+ pMS->InputFunc = DoShipyard;
+ pMS->CurState = SHIPYARD_CREW;
+ DrawMenuStateStrings (PM_CREW, pMS->CurState);
+ DMS_SetMode (pMS, DMS_Mode_exit);
+ }
+}
+
+/* In this routine, the least significant byte of pMS->CurState is used
+ * to store the current selected ship index
+ * a special case for the row is hi-nibble == -1 (0xf), which specifies
+ * SIS as the selected ship
+ * some bitwise math is still done to scroll through ships, for it to work
+ * ships per row number must divide 0xf0 without remainder
+ */
+static BOOLEAN
+DoModifyShips (MENU_STATE *pMS)
+{
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pMS->InputFunc = DoShipyard;
+ return TRUE;
+ }
+
+ if (!pMS->Initialized)
+ {
+ pMS->InputFunc = DoModifyShips;
+ pMS->Initialized = TRUE;
+ pMS->CurState = MAKE_BYTE (0, 0xF);
+ pMS->delta_item = 0;
+
+ SetContext (SpaceContext);
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else
+ {
+ BOOLEAN special = (PulsedInputState.menu[KEY_MENU_SPECIAL] != 0);
+ BOOLEAN select = (PulsedInputState.menu[KEY_MENU_SELECT] != 0);
+ BOOLEAN cancel = (PulsedInputState.menu[KEY_MENU_CANCEL] != 0);
+ SBYTE dx = 0;
+ SBYTE dy = 0;
+
+ if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ dx = 1;
+ if (PulsedInputState.menu[KEY_MENU_LEFT])
+ dx = -1;
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ dy = -1;
+ if (PulsedInputState.menu[KEY_MENU_DOWN])
+ dy = 1;
+
+
+ if (!(pMS->delta_item & MODIFY_CREW_FLAG))
+ {
+ // Navigating through the ship slots.
+ DMS_NavigateShipSlots (pMS, special, select, cancel, dx, dy);
+ }
+ else
+ {
+ // Add an escort ship or edit the crew of a ship.
+ HSHIPFRAG hStarShip = GetEscortByStarShipIndex (pMS->CurState);
+
+ if (hStarShip == 0 && HINIBBLE (pMS->CurState) == 0)
+ {
+ // Cursor is over an empty escort ship slot, while we're
+ // in 'add escort ship' mode.
+ DMS_AddEscortShip (pMS, special, select, cancel, dx, dy);
+ }
+ else
+ {
+ // Crew editing mode.
+ DMS_EditCrewMode (pMS, hStarShip, select, cancel, dy);
+ }
+ }
+
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static void
+DrawBluePrint (MENU_STATE *pMS)
+{
+ COUNT num_frames;
+ STAMP s;
+ FRAME ModuleFrame;
+
+ ModuleFrame = CaptureDrawable (LoadGraphic (SISBLU_MASK_ANIM));
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = DecFrameIndex (ModuleFrame);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x16), 0x01));
+ DrawFilledStamp (&s);
+
+ for (num_frames = 0; num_frames < NUM_DRIVE_SLOTS; ++num_frames)
+ {
+ DrawShipPiece (ModuleFrame, GLOBAL_SIS (DriveSlots[num_frames]),
+ num_frames, TRUE);
+ }
+ for (num_frames = 0; num_frames < NUM_JET_SLOTS; ++num_frames)
+ {
+ DrawShipPiece (ModuleFrame, GLOBAL_SIS (JetSlots[num_frames]),
+ num_frames, TRUE);
+ }
+ for (num_frames = 0; num_frames < NUM_MODULE_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (ModuleSlots[num_frames]);
+
+ if (!(pMS->CurState == SHIPYARD && which_piece == CREW_POD))
+ DrawShipPiece (ModuleFrame, which_piece, num_frames, TRUE);
+ }
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09));
+ for (num_frames = 0; num_frames < NUM_MODULE_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (ModuleSlots[num_frames]);
+ if (pMS->CurState == SHIPYARD && which_piece == CREW_POD)
+ DrawShipPiece (ModuleFrame, which_piece, num_frames, TRUE);
+ }
+
+ {
+ num_frames = GLOBAL_SIS (CrewEnlisted);
+ GLOBAL_SIS (CrewEnlisted) = 0;
+
+ while (num_frames--)
+ {
+ POINT pt;
+
+ GetCPodCapacity (&pt);
+ DrawPoint (&pt);
+
+ ++GLOBAL_SIS (CrewEnlisted);
+ }
+ }
+ {
+ RECT r;
+
+ num_frames = GLOBAL_SIS (TotalElementMass);
+ GLOBAL_SIS (TotalElementMass) = 0;
+
+ r.extent.width = 9;
+ r.extent.height = 1;
+ while (num_frames)
+ {
+ COUNT m;
+
+ m = num_frames < SBAY_MASS_PER_ROW ?
+ num_frames : SBAY_MASS_PER_ROW;
+ GLOBAL_SIS (TotalElementMass) += m;
+ GetSBayCapacity (&r.corner);
+ DrawFilledRectangle (&r);
+ num_frames -= m;
+ }
+ }
+ if (GLOBAL_SIS (FuelOnBoard) > FUEL_RESERVE)
+ {
+ DWORD FuelVolume;
+ RECT r;
+
+ FuelVolume = GLOBAL_SIS (FuelOnBoard) - FUEL_RESERVE;
+ GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
+
+ r.extent.width = 3;
+ r.extent.height = 1;
+ while (FuelVolume)
+ {
+ COUNT m;
+
+ GetFTankCapacity (&r.corner);
+ DrawPoint (&r.corner);
+ r.corner.x += r.extent.width + 1;
+ DrawPoint (&r.corner);
+ r.corner.x -= r.extent.width;
+ SetContextForeGroundColor (
+ SetContextBackGroundColor (BLACK_COLOR));
+ DrawFilledRectangle (&r);
+ m = FuelVolume < FUEL_VOLUME_PER_ROW ?
+ (COUNT)FuelVolume : FUEL_VOLUME_PER_ROW;
+ GLOBAL_SIS (FuelOnBoard) += m;
+ FuelVolume -= m;
+ }
+ }
+
+ DestroyDrawable (ReleaseDrawable (ModuleFrame));
+}
+
+BOOLEAN
+DoShipyard (MENU_STATE *pMS)
+{
+ BOOLEAN select, cancel;
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ goto ExitShipyard;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ if (!pMS->Initialized)
+ {
+ pMS->InputFunc = DoShipyard;
+
+ {
+ STAMP s;
+ RECT r, old_r;
+
+ pMS->ModuleFrame = CaptureDrawable (
+ LoadGraphic (SHIPYARD_PMAP_ANIM));
+
+ pMS->CurString = CaptureColorMap (
+ LoadColorMap (HANGAR_COLOR_TAB));
+
+ pMS->hMusic = LoadMusic (SHIPYARD_MUSIC);
+
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ DrawSISFrame ();
+ DrawSISMessage (GAME_STRING (STARBASE_STRING_BASE + 3));
+ DrawSISTitle (GAME_STRING (STARBASE_STRING_BASE));
+ SetContext (SpaceContext);
+ DrawBluePrint (pMS);
+
+ pMS->CurState = SHIPYARD_CREW;
+ DrawMenuStateStrings (PM_CREW, pMS->CurState);
+
+ SetContext (SpaceContext);
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame, 0);
+#ifdef USE_3DO_HANGAR
+ DrawStamp (&s);
+#else // PC hangar
+ // the PC ship dock needs to overwrite the border
+ // expand the clipping rect by 1 pixel
+ GetContextClipRect (&old_r);
+ r = old_r;
+ r.corner.x--;
+ r.extent.width += 2;
+ r.extent.height += 1;
+ SetContextClipRect (&r);
+ DrawStamp (&s);
+ SetContextClipRect (&old_r);
+ animatePowerLines (pMS);
+#endif // USE_3DO_HANGAR
+
+ SetContextFont (TinyFont);
+
+ ScreenTransition (3, NULL);
+ UnbatchGraphics ();
+
+ PlayMusic (pMS->hMusic, TRUE, 1);
+
+ ShowCombatShip (pMS, (COUNT)~0, NULL);
+
+ SetInputCallback (on_input_frame);
+
+ SetFlashRect (SFR_MENU_3DO);
+ }
+
+ pMS->Initialized = TRUE;
+ }
+ else if (cancel || (select && pMS->CurState == SHIPYARD_EXIT))
+ {
+ExitShipyard:
+ SetInputCallback (NULL);
+
+ DestroyDrawable (ReleaseDrawable (pMS->ModuleFrame));
+ pMS->ModuleFrame = 0;
+ DestroyColorMap (ReleaseColorMap (pMS->CurString));
+ pMS->CurString = 0;
+
+ return FALSE;
+ }
+ else if (select)
+ {
+ if (pMS->CurState != SHIPYARD_SAVELOAD)
+ {
+ pMS->Initialized = FALSE;
+ DoModifyShips (pMS);
+ }
+ else
+ {
+ // Clearing FlashRect is not necessary
+ if (!GameOptions ())
+ goto ExitShipyard;
+ DrawMenuStateStrings (PM_CREW, pMS->CurState);
+ SetFlashRect (SFR_MENU_3DO);
+ }
+ }
+ else
+ {
+ DoMenuChooser (pMS, PM_CREW);
+ }
+
+ return TRUE;
+}
+