summaryrefslogtreecommitdiff
path: root/src/uqm/menu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/menu.c')
-rw-r--r--src/uqm/menu.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/src/uqm/menu.c b/src/uqm/menu.c
new file mode 100644
index 0000000..fc46e3b
--- /dev/null
+++ b/src/uqm/menu.c
@@ -0,0 +1,603 @@
+//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 "menustat.h"
+
+#include "colors.h"
+#include "controls.h"
+#include "units.h"
+#include "options.h"
+#include "setup.h"
+#include "gamestr.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/tasklib.h"
+#include "libs/log.h"
+
+static BYTE GetEndMenuState (BYTE BaseState);
+static BYTE GetBeginMenuState (BYTE BaseState);
+static BYTE FixMenuState (BYTE BadState);
+static BYTE NextMenuState (BYTE BaseState, BYTE CurState);
+static BYTE PreviousMenuState (BYTE BaseState, BYTE CurState);
+static BOOLEAN GetAlternateMenu (BYTE *BaseState, BYTE *CurState);
+static BYTE ConvertAlternateMenu (BYTE BaseState, BYTE NewState);
+
+
+/* Draw the blue background for PC Menu Text, with a border around it.
+ * The specified rectangle includes the border. */
+static void
+DrawPCMenuFrame (RECT *r)
+{
+ // Draw the top and left of the outer border.
+ // This actually draws a rectangle, but the bottom and right parts
+ // are drawn over in the next step.
+ SetContextForeGroundColor (PCMENU_TOP_LEFT_BORDER_COLOR);
+ DrawRectangle (r);
+
+ // Draw the right and bottom of the outer border.
+ // This actually draws a rectangle, with the top and left segments just
+ // within the total area, but these segments are drawn over in the next
+ // step.
+ r->corner.x++;
+ r->corner.y++;
+ r->extent.height--;
+ r->extent.width--;
+ SetContextForeGroundColor (PCMENU_BOTTOM_RIGHT_BORDER_COLOR);
+ DrawRectangle (r);
+
+ // Draw the background.
+ r->extent.height--;
+ r->extent.width--;
+ SetContextForeGroundColor (PCMENU_BACKGROUND_COLOR);
+ DrawFilledRectangle (r);
+}
+
+#define ALT_MANIFEST 0x80
+#define ALT_EXIT_MANIFEST 0x81
+
+static UNICODE pm_crew_str[128];
+static UNICODE pm_fuel_str[128];
+
+/* Actually display the menu text */
+static void
+DrawPCMenu (BYTE beg_index, BYTE end_index, BYTE NewState, BYTE hilite, RECT *r)
+{
+#define PC_MENU_HEIGHT 8
+ BYTE pos;
+ COUNT i;
+ int num_items;
+ FONT OldFont;
+ TEXT t;
+ UNICODE buf[256];
+
+ pos = beg_index + NewState;
+ num_items = 1 + end_index - beg_index;
+ r->corner.x -= 1;
+ r->extent.width += 1;
+ DrawFilledRectangle (r);
+ if (num_items * PC_MENU_HEIGHT > r->extent.height)
+ log_add (log_Error, "Warning, no room for all menu items!");
+ else
+ r->corner.y += (r->extent.height - num_items * PC_MENU_HEIGHT) / 2;
+ r->extent.height = num_items * PC_MENU_HEIGHT + 4;
+ DrawPCMenuFrame (r);
+ OldFont = SetContextFont (StarConFont);
+ t.align = ALIGN_LEFT;
+ t.baseline.x = r->corner.x + 2;
+ t.baseline.y = r->corner.y + PC_MENU_HEIGHT -1;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ r->corner.x++;
+ r->extent.width -= 2;
+ for (i = beg_index; i <= end_index; i++)
+ {
+ utf8StringCopy (buf, sizeof buf,
+ (i == PM_FUEL) ? pm_fuel_str :
+ (i == PM_CREW) ? pm_crew_str :
+ GAME_STRING (MAINMENU_STRING_BASE + i));
+ if (hilite && pos == i)
+ {
+ // Currently selected menu option.
+
+ // Draw the background of the selection.
+ SetContextForeGroundColor (PCMENU_SELECTION_BACKGROUND_COLOR);
+ r->corner.y = t.baseline.y - PC_MENU_HEIGHT + 2;
+ r->extent.height = PC_MENU_HEIGHT - 1;
+ DrawFilledRectangle (r);
+
+ // Draw the text of the selected item.
+ SetContextForeGroundColor (PCMENU_SELECTION_TEXT_COLOR);
+ font_DrawText (&t);
+ }
+ else
+ {
+ // Draw the text of an unselected item.
+ SetContextForeGroundColor (PCMENU_TEXT_COLOR);
+ font_DrawText (&t);
+ }
+ t.baseline.y += PC_MENU_HEIGHT;
+ }
+ SetContextFont (OldFont);
+}
+
+/* Determine the last text item to display */
+static BYTE
+GetEndMenuState (BYTE BaseState)
+{
+ switch (BaseState)
+ {
+ case PM_SCAN:
+ case PM_STARMAP:
+ return PM_NAVIGATE;
+ break;
+ case PM_MIN_SCAN:
+ return PM_LAUNCH_LANDER;
+ break;
+ case PM_SAVE_GAME:
+ return PM_EXIT_GAME_MENU;
+ break;
+ case PM_CONVERSE:
+ return PM_ENCOUNTER_GAME_MENU;
+ break;
+ case PM_FUEL:
+ return PM_EXIT_OUTFIT;
+ break;
+ case PM_CREW:
+ return PM_EXIT_SHIPYARD;
+ break;
+ case PM_SOUND_ON:
+ return PM_EXIT_SETTINGS;
+ break;
+ case PM_ALT_SCAN:
+ case PM_ALT_STARMAP:
+ return PM_ALT_NAVIGATE;
+ break;
+ case PM_ALT_CARGO:
+ return PM_ALT_EXIT_MANIFEST;
+ break;
+ case PM_ALT_MSCAN:
+ return PM_ALT_EXIT_SCAN;
+ break;
+ }
+ return BaseState;
+}
+
+static BYTE
+GetBeginMenuState (BYTE BaseState)
+{
+ return BaseState;
+}
+
+/* Correct Menu State for cases where the Menu shouldn't move */
+static BYTE
+FixMenuState (BYTE BadState)
+{
+ switch (BadState)
+ {
+ case PM_SOUND_ON:
+ if (GLOBAL (glob_flags) & SOUND_DISABLED)
+ return PM_SOUND_OFF;
+ else
+ return PM_SOUND_ON;
+ case PM_MUSIC_ON:
+ if (GLOBAL (glob_flags) & MUSIC_DISABLED)
+ return PM_MUSIC_OFF;
+ else
+ return PM_MUSIC_ON;
+ case PM_CYBORG_OFF:
+ return (PM_CYBORG_OFF +
+ ((BYTE)(GLOBAL (glob_flags) & COMBAT_SPEED_MASK) >>
+ COMBAT_SPEED_SHIFT));
+ }
+ return BadState;
+}
+
+/* Choose the next menu to hilight in the 'forward' direction */
+static BYTE
+NextMenuState (BYTE BaseState, BYTE CurState)
+{
+ BYTE NextState;
+ BYTE AdjBase = BaseState;
+
+ if (BaseState == PM_STARMAP)
+ AdjBase--;
+
+ switch (AdjBase + CurState)
+ {
+ case PM_SOUND_ON:
+ case PM_SOUND_OFF:
+ NextState = PM_MUSIC_ON;
+ break;
+ case PM_MUSIC_ON:
+ case PM_MUSIC_OFF:
+ NextState = PM_CYBORG_OFF;
+ break;
+ case PM_CYBORG_OFF:
+ case PM_CYBORG_NORMAL:
+ case PM_CYBORG_DOUBLE:
+ case PM_CYBORG_SUPER:
+ NextState = PM_CHANGE_CAPTAIN;
+ break;
+ default:
+ NextState = AdjBase + CurState + 1;
+ }
+ if (NextState > GetEndMenuState (BaseState))
+ NextState = GetBeginMenuState (BaseState);
+ return (FixMenuState (NextState) - AdjBase);
+}
+
+/* Choose the next menu to hilight in the 'back' direction */
+BYTE
+PreviousMenuState (BYTE BaseState, BYTE CurState)
+{
+ SWORD NextState;
+ BYTE AdjBase = BaseState;
+
+ if (BaseState == PM_STARMAP)
+ AdjBase--;
+
+ switch (AdjBase + CurState)
+ {
+ case PM_SOUND_OFF:
+ NextState = PM_EXIT_SETTINGS;
+ break;
+ case PM_MUSIC_ON:
+ case PM_MUSIC_OFF:
+ NextState = PM_SOUND_ON;
+ break;
+ case PM_CYBORG_OFF:
+ case PM_CYBORG_NORMAL:
+ case PM_CYBORG_DOUBLE:
+ case PM_CYBORG_SUPER:
+ NextState = PM_MUSIC_ON;
+ break;
+ case PM_CHANGE_CAPTAIN:
+ NextState = PM_CYBORG_OFF;
+ break;
+ default:
+ NextState = AdjBase + CurState - 1;
+ }
+ if (NextState < GetBeginMenuState (BaseState))
+ NextState = GetEndMenuState (BaseState);
+ return (FixMenuState ((BYTE)NextState) - AdjBase);
+}
+
+
+/* When using PC hierarchy, convert 3do->PC */
+static BOOLEAN
+GetAlternateMenu (BYTE *BaseState, BYTE *CurState)
+{
+ BYTE AdjBase = *BaseState;
+ BYTE adj = 0;
+ if (*BaseState == PM_STARMAP)
+ {
+ AdjBase--;
+ adj = 1;
+ }
+ if (*CurState & 0x80)
+ {
+ switch (*CurState)
+ {
+ case ALT_MANIFEST:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_MANIFEST - PM_ALT_SCAN - adj;
+ return TRUE;
+ case ALT_EXIT_MANIFEST:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_EXIT_MANIFEST - PM_ALT_CARGO;
+ return TRUE;
+ }
+ log_add (log_Error, "Unknown state combination: %d, %d",
+ *BaseState, *CurState);
+ return FALSE;
+ }
+ else
+ {
+ switch (AdjBase + *CurState)
+ {
+ case PM_SCAN:
+ *BaseState = PM_ALT_SCAN;
+ *CurState = PM_ALT_SCAN - PM_ALT_SCAN;
+ return TRUE;
+ case PM_STARMAP:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_STARMAP - PM_ALT_SCAN - adj;
+ return TRUE;
+ case PM_DEVICES:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_DEVICES - PM_ALT_CARGO;
+ return TRUE;
+ case PM_CARGO:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_CARGO - PM_ALT_CARGO;
+ return TRUE;
+ case PM_ROSTER:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_ROSTER - PM_ALT_CARGO;
+ return TRUE;
+ case PM_GAME_MENU:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_GAME_MENU - PM_ALT_SCAN - adj;
+ return TRUE;
+ case PM_NAVIGATE:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_NAVIGATE - PM_ALT_SCAN - adj;
+ return TRUE;
+ case PM_MIN_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_MSCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_ENE_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_ESCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_BIO_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_BSCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_EXIT_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_EXIT_SCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_AUTO_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_ASCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_LAUNCH_LANDER:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_DISPATCH - PM_ALT_MSCAN;
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
+
+/* When using PC hierarchy, convert PC->3DO */
+static BYTE
+ConvertAlternateMenu (BYTE BaseState, BYTE NewState)
+{
+ switch (BaseState + NewState)
+ {
+ case PM_ALT_SCAN:
+ return (PM_SCAN - PM_SCAN);
+ case PM_ALT_STARMAP:
+ return (PM_STARMAP - PM_SCAN);
+ case PM_ALT_MANIFEST:
+ return (ALT_MANIFEST);
+ case PM_ALT_GAME_MENU:
+ return (PM_GAME_MENU - PM_SCAN);
+ case PM_ALT_NAVIGATE:
+ return (PM_NAVIGATE - PM_SCAN);
+ case PM_ALT_CARGO:
+ return (PM_CARGO - PM_SCAN);
+ case PM_ALT_DEVICES:
+ return (PM_DEVICES - PM_SCAN);
+ case PM_ALT_ROSTER:
+ return (PM_ROSTER - PM_SCAN);
+ case PM_ALT_EXIT_MANIFEST:
+ return (ALT_EXIT_MANIFEST);
+ case PM_ALT_MSCAN:
+ return (PM_MIN_SCAN - PM_MIN_SCAN);
+ case PM_ALT_ESCAN:
+ return (PM_ENE_SCAN - PM_MIN_SCAN);
+ case PM_ALT_BSCAN:
+ return (PM_BIO_SCAN - PM_MIN_SCAN);
+ case PM_ALT_ASCAN:
+ return (PM_AUTO_SCAN - PM_MIN_SCAN);
+ case PM_ALT_DISPATCH:
+ return (PM_LAUNCH_LANDER - PM_MIN_SCAN);
+ case PM_ALT_EXIT_SCAN:
+ return (PM_EXIT_SCAN - PM_MIN_SCAN);
+ }
+ return (NewState);
+}
+
+BOOLEAN
+DoMenuChooser (MENU_STATE *pMS, BYTE BaseState)
+{
+ BYTE NewState = pMS->CurState;
+ BYTE OrigBase = BaseState;
+ BOOLEAN useAltMenu = FALSE;
+ if (optWhichMenu == OPT_PC)
+ useAltMenu = GetAlternateMenu (&BaseState, &NewState);
+ if (PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_UP])
+ NewState = PreviousMenuState (BaseState, NewState);
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT] ||
+ PulsedInputState.menu[KEY_MENU_DOWN])
+ NewState = NextMenuState (BaseState, NewState);
+ else if (useAltMenu && PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ NewState = ConvertAlternateMenu (BaseState, NewState);
+ if (NewState == ALT_MANIFEST)
+ {
+ DrawMenuStateStrings (PM_ALT_CARGO, 0);
+ pMS->CurState = PM_CARGO - PM_SCAN;
+ return TRUE;
+ }
+ if (NewState == ALT_EXIT_MANIFEST)
+ {
+ if (OrigBase == PM_SCAN)
+ DrawMenuStateStrings (PM_ALT_SCAN,
+ PM_ALT_MANIFEST - PM_ALT_SCAN);
+ else
+ DrawMenuStateStrings (PM_ALT_STARMAP,
+ PM_ALT_MANIFEST - PM_ALT_STARMAP);
+ pMS->CurState = ALT_MANIFEST;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if ((optWhichMenu == OPT_PC) &&
+ PulsedInputState.menu[KEY_MENU_CANCEL] &&
+ (BaseState == PM_ALT_CARGO))
+ {
+ if (OrigBase == PM_SCAN)
+ DrawMenuStateStrings (PM_ALT_SCAN,
+ PM_ALT_MANIFEST - PM_ALT_SCAN);
+ else
+ DrawMenuStateStrings (PM_ALT_STARMAP,
+ PM_ALT_MANIFEST - PM_ALT_STARMAP);
+ pMS->CurState = ALT_MANIFEST;
+ return TRUE;
+ }
+ else
+ return FALSE;
+
+ DrawMenuStateStrings (BaseState, NewState);
+ if (useAltMenu)
+ NewState = ConvertAlternateMenu (BaseState, NewState);
+ pMS->CurState = NewState;
+ SleepThread (ONE_SECOND / 20);
+ return TRUE;
+}
+
+void
+DrawMenuStateStrings (BYTE beg_index, SWORD NewState)
+{
+ BYTE end_index;
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+ BYTE hilite = 1;
+ extern FRAME PlayFrame;
+
+ if (NewState < 0)
+ {
+ NewState = -NewState;
+ hilite = 0;
+ }
+
+ if (optWhichMenu == OPT_PC)
+ {
+ BYTE tmpState = (BYTE)NewState;
+ GetAlternateMenu (&beg_index, &tmpState);
+ NewState = tmpState;
+ }
+
+ if (beg_index == PM_STARMAP)
+ NewState--;
+ end_index = GetEndMenuState (beg_index);
+
+ s.frame = 0;
+ if (NewState <= end_index - beg_index)
+ s.frame = SetAbsFrameIndex (PlayFrame, beg_index + NewState);
+
+ PreUpdateFlashRect ();
+ 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;
+ BatchGraphics ();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ if (s.frame && optWhichMenu == OPT_PC)
+ {
+ if (beg_index == PM_CREW)
+ snprintf (pm_crew_str, sizeof pm_crew_str, "%s(%d)",
+ GAME_STRING (MAINMENU_STRING_BASE + PM_CREW),
+ GLOBAL (CrewCost));
+ if (beg_index == PM_FUEL)
+ snprintf (pm_fuel_str, sizeof pm_fuel_str, "%s(%d)",
+ GAME_STRING (MAINMENU_STRING_BASE + PM_FUEL),
+ GLOBAL (FuelCost));
+ if (beg_index == PM_SOUND_ON)
+ {
+ end_index = beg_index + 5;
+ switch (beg_index + NewState)
+ {
+ case PM_SOUND_ON:
+ case PM_SOUND_OFF:
+ NewState = 0;
+ break;
+ case PM_MUSIC_ON:
+ case PM_MUSIC_OFF:
+ NewState = 1;
+ break;
+ case PM_CYBORG_OFF:
+ case PM_CYBORG_NORMAL:
+ case PM_CYBORG_DOUBLE:
+ case PM_CYBORG_SUPER:
+ NewState = 2;
+ break;
+ case PM_CHANGE_CAPTAIN:
+ NewState = 3;
+ break;
+ case PM_CHANGE_SHIP:
+ NewState = 4;
+ break;
+ case PM_EXIT_SETTINGS:
+ NewState = 5;
+ break;
+ }
+ }
+ r.extent.height = RADAR_HEIGHT + 11;
+ DrawPCMenu (beg_index, end_index, (BYTE)NewState, hilite, &r);
+ s.frame = 0;
+ }
+ else
+ {
+ if (optWhichMenu == OPT_PC)
+ {
+ r.corner.x -= 1;
+ r.extent.width += 1;
+ r.extent.height = RADAR_HEIGHT + 11;
+ }
+ else
+ r.extent.height = 11;
+ DrawFilledRectangle (&r);
+ }
+ if (s.frame)
+ {
+ DrawStamp (&s);
+ switch (beg_index + NewState)
+ {
+ TEXT t;
+ UNICODE buf[20];
+
+ case PM_CREW:
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ snprintf (buf, sizeof buf, "%u", GLOBAL (CrewCost));
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (THREEDOMENU_TEXT_COLOR);
+ font_DrawText (&t);
+ break;
+ case PM_FUEL:
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ snprintf (buf, sizeof buf, "%u", GLOBAL (FuelCost));
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (THREEDOMENU_TEXT_COLOR);
+ font_DrawText (&t);
+ break;
+ }
+ }
+ UnbatchGraphics ();
+ SetContext (OldContext);
+ PostUpdateFlashRect ();
+}
+