//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;
}

