rockbox/apps/plugins/doom/m_menu.c
William Wilgus 7100090f99 Remove NVRAM infavor of a separate cfg file
remove nvram and use the existing settings framework for it

add a crc check to the user_settings data to see if we need to save
the user setting file or if we can just save the status file (resume.cfg)

move volume to the system_status struct so we don't write the whole settings file
over volume changes

allow user to still export volume with save sound settings

allow the user to also export pitch and speed

name the file .resume.cfg

Rename all the SYSTEM_STATUS save file variables to TLAs to save space and
discourage tinkering

Cleanup DEBUG_AVAIL_SETTINGS output

when saving user_settings it calls status_save as well this cause the resume
file to be written twice. instead remove the callback for status_save
when setting_save is called

remove header text when saving .resume.cfg

convert status_save() to status_save(bool force)
add SYSTEM_STATUS_UPDATE_TICKS

for ATA device set this to 5 minutes
since we arlready wait for the disk to be up before saving
we don't want to miss our window

for all other every 15 minutes

that way if the battery is too low by the time shutdown comes around you
don't lose much progress

Change-Id: I27214ffd6e5d5494ee5ca83b14f04a41ba426ad7
2025-01-21 00:04:32 -05:00

1862 lines
35 KiB
C

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// 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.
//
// $Log$
// Revision 1.7 2006/12/13 04:44:17 kkurbjun
// Dehacked and BEX support for Doom - currently only supports a DEHACKED file in a WAD (not as a standalone file yet).
//
// Revision 1.6 2006-04-16 23:14:04 kkurbjun
// Fix run so that it stays enabled across level loads. Removed some unused code and added some back in for hopeful future use.
//
// Revision 1.5 2006-04-04 23:58:37 kkurbjun
// Make savegame strings more informative
//
// Revision 1.4 2006-04-04 23:13:50 kkurbjun
// Fix up configurable keys, edit exit string, more work needs to be done on menu keys
//
// Revision 1.3 2006-04-03 20:03:02 kkurbjun
// Updates doom menu w/ new graphics, now requires rockdoom.wad: http://alamode.mines.edu/~kkurbjun/rockdoom.wad
//
// Revision 1.2 2006-04-03 00:28:13 kkurbjun
// Fixes graphic errors in scaling code, note sure about the fix in hu_lib.c though. I havn't seen any corrupted text but it may still need a proper fix.
//
// Revision 1.1 2006-03-28 15:44:01 dave
// Patch #2969 - Doom! Currently only working on the H300.
//
//
// DESCRIPTION:
// DOOM selection menu, options, episode etc.
// Sliders and icons. Kinda widget stuff.
//
//-----------------------------------------------------------------------------
#include "doomdef.h"
#include "dstrings.h"
#include "d_main.h"
#include "i_system.h"
#include "i_video.h"
#include "z_zone.h"
#include "v_video.h"
#include "w_wad.h"
#include "r_main.h"
#include "d_deh.h"
#include "hu_stuff.h"
#include "g_game.h"
#include "m_argv.h"
#include "m_swap.h"
#include "s_sound.h"
#include "doomstat.h"
// Data.
#include "sounds.h"
#include "m_menu.h"
#include "rockmacros.h"
extern patchnum_t hu_font[HU_FONTSIZE];
extern boolean message_dontfuckwithme;
extern boolean chat_on; // in heads-up code
//
// defaulted values
//
int mouseSensitivity; // has default
// Show messages has default, 0 = off, 1 = on
int showMessages;
// Blocky mode, has default, 0 = high, 1 = normal
int screenblocks; // has default
// temp for screenblocks (0-9)
int screenSize;
// -1 = no quicksave slot picked!
int quickSaveSlot;
// 1 = message to be printed
int messageToPrint;
// ...and here is the message string!
const char* messageString;
// message x & y
int messx;
int messy;
int messageLastMenuActive;
// timed message = no input from user
boolean messageNeedsInput;
void (*messageRoutine)(int response);
#define SAVESTRINGSIZE 24
// we are going to be entering a savegame string
int saveStringEnter;
int saveSlot; // which slot to save in
int saveCharIndex; // which char we're editing
// old save description before edit
char saveOldString[SAVESTRINGSIZE];
boolean inhelpscreens;
boolean menuactive;
#define SKULLXOFF -32
#define LINEHEIGHT 16
extern boolean sendpause;
char savegamestrings[10][SAVESTRINGSIZE];
char endstring[170];
//
// MENU TYPEDEFS
//
typedef struct
{
// 0 = no cursor here, 1 = ok, 2 = arrows ok
short status;
char name[10];
// choice = menu item #.
// if status = 2,
// choice=0:leftarrow,1:rightarrow
void (*routine)(int choice);
// hotkey in menu
char alphaKey;
}
menuitem_t;
typedef struct menu_s
{
short numitems; // # of menu items
struct menu_s* prevMenu; // previous menu
menuitem_t* menuitems; // menu items
void (*routine)(void); // draw routine ROCKBOX
short x;
short y; // x,y of menu
short lastOn; // last item user was on in menu
}
menu_t;
short itemOn; // menu item skull is on
short skullAnimCounter; // skull animation counter
short whichSkull; // which skull to draw
int systemvol;
// graphic name of skulls
// warning: initializer-string for array of chars is too long
char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"};
// current menudef
menu_t* currentMenu;
//
// PROTOTYPES
//
void M_NewGame(int choice);
void M_Episode(int choice);
void M_ChooseSkill(int choice);
void M_LoadGame(int choice);
void M_SaveGame(int choice);
void M_Options(int choice);
void M_EndGame(int choice);
void M_ReadThis(int choice);
void M_ReadThis2(int choice);
void M_QuitDOOM(int choice);
void M_ChangeMessages(int choice);
void M_ChangeGamma(int choice);
void M_SfxVol(int choice);
void M_MusicVol(int choice);
void M_SystemVol(int choice);
void M_SizeDisplay(int choice);
void M_StartGame(int choice);
void M_Sound(int choice);
void M_FinishReadThis(int choice);
void M_LoadSelect(int choice);
void M_SaveSelect(int choice);
void M_ReadSaveStrings(void);
void M_QuickSave(void);
void M_QuickLoad(void);
void M_DrawMainMenu(void);
void M_DrawReadThis1(void);
void M_DrawReadThis2(void);
void M_DrawNewGame(void);
void M_DrawEpisode(void);
void M_DrawOptions(void);
void M_DrawSound(void);
void M_DrawLoad(void);
void M_DrawSave(void);
void M_DrawSaveLoadBorder(int x,int y);
void M_SetupNextMenu(menu_t *menudef);
void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
void M_DrawEmptyCell(menu_t *menu,int item);
void M_DrawSelCell(menu_t *menu,int item);
void M_WriteText(int x, int y, char *string);
int M_StringWidth(const char* string);
int M_StringHeight(const char* string);
void M_StartControlPanel(void);
void M_StartMessage(const char *string,void *routine,boolean input);
void M_StopMessage(void);
void M_ClearMenus (void);
//
// DOOM MENU
//
enum
{
newgame = 0,
options,
loadgame,
savegame,
readthis,
quitdoom,
main_end
};
unsigned main_e;
menuitem_t MainMenu[]=
{
{1,"M_NGAME",M_NewGame,'n'},
{1,"M_OPTION",M_Options,'o'},
{1,"M_LOADG",M_LoadGame,'l'},
{1,"M_SAVEG",M_SaveGame,'s'},
// Another hickup with Special edition.
{1,"M_RDTHIS",M_ReadThis,'r'},
{1,"M_QUITG",M_QuitDOOM,'q'}
};
menu_t MainDef =
{
main_end,
NULL,
MainMenu,
M_DrawMainMenu,
97,64,
0
};
//
// EPISODE SELECT
//
enum
{
ep1,
ep2,
ep3,
ep4,
ep_end
};
unsigned episodes_e;
menuitem_t EpisodeMenu[]=
{
{1,"M_EPI1", M_Episode,'k'},
{1,"M_EPI2", M_Episode,'t'},
{1,"M_EPI3", M_Episode,'i'},
{1,"M_EPI4", M_Episode,'t'}
};
menu_t EpiDef =
{
ep_end, // # of menu items
&MainDef, // previous menu
EpisodeMenu, // menuitem_t ->
M_DrawEpisode, // drawing routine ->
48,63, // x,y
ep1 // lastOn
};
//
// NEW GAME
//
enum
{
killthings,
toorough,
hurtme,
violence,
nightmare,
newg_end
};
unsigned newgame_e;
menuitem_t NewGameMenu[]=
{
{1,"M_JKILL", M_ChooseSkill, 'i'},
{1,"M_ROUGH", M_ChooseSkill, 'h'},
{1,"M_HURT", M_ChooseSkill, 'h'},
{1,"M_ULTRA", M_ChooseSkill, 'u'},
{1,"M_NMARE", M_ChooseSkill, 'n'}
};
menu_t NewDef =
{
newg_end, // # of menu items
&EpiDef, // previous menu
NewGameMenu, // menuitem_t ->
M_DrawNewGame, // drawing routine ->
48,63, // x,y
hurtme // lastOn
};
//
// OPTIONS MENU
//
enum
{
endgame,
messages,
scrnsize,
option_empty1,
gamasens,
option_empty2,
soundvol,
opt_end
};
unsigned options_e;
menuitem_t OptionsMenu[]=
{
{1,"M_ENDGAM", M_EndGame,'e'},
{1,"M_MESSG", M_ChangeMessages,'m'},
{2,"M_SCRNSZ", M_SizeDisplay,'s'},
{-1,"",0,0},
{2,"M_GAMMA", M_ChangeGamma,'m'},
{-1,"",0,0},
{1,"M_SVOL", M_Sound,'s'}
};
menu_t OptionsDef =
{
opt_end,
&MainDef,
OptionsMenu,
M_DrawOptions,
60,37,
0
};
//
// Read This! MENU 1 & 2
//
enum
{
rdthsempty1,
read1_end
};
unsigned read_e;
menuitem_t ReadMenu1[] =
{
{1,"",M_ReadThis2,0}
};
menu_t ReadDef1 =
{
read1_end,
&MainDef,
ReadMenu1,
M_DrawReadThis1,
280,185,
0
};
enum
{
rdthsempty2,
read2_end
};
unsigned read_e2;
menuitem_t ReadMenu2[]=
{
{1,"",M_FinishReadThis,0}
};
menu_t ReadDef2 =
{
read2_end,
&ReadDef1,
ReadMenu2,
M_DrawReadThis2,
330,175,
0
};
//
// SOUND VOLUME MENU
//
enum
{
sfx_vol,
sfx_empty1,
music_vol,
sfx_empty2,
system_vol,
sfx_empty3,
sound_end
};
unsigned sound_e;
menuitem_t SoundMenu[]=
{
{2,"M_SFXVOL",M_SfxVol,'s'},
{-1,"",0,0}, //ROCKBOX
{2,"M_MUSVOL",M_MusicVol,'m'},
{-1,"",0,0}, //ROCKBOX
{2,"M_SYSVOL",M_SystemVol,'z'},
{-1,"",0,0} //ROCKBOX
};
menu_t SoundDef =
{
sound_end,
&OptionsDef,
SoundMenu,
M_DrawSound,
80,64,
0
};
//
// LOAD GAME MENU
//
enum
{
load1,
load2,
load3,
load4,
load5,
load6,
load_end
};
unsigned load_e;
menuitem_t LoadMenu[]=
{
{1,"", M_LoadSelect,'1'},
{1,"", M_LoadSelect,'2'},
{1,"", M_LoadSelect,'3'},
{1,"", M_LoadSelect,'4'},
{1,"", M_LoadSelect,'5'},
{1,"", M_LoadSelect,'6'}
};
menu_t LoadDef =
{
load_end,
&MainDef,
LoadMenu,
M_DrawLoad,
80,54,
0
};
//
// SAVE GAME MENU
//
menuitem_t SaveMenu[]=
{
{1,"", M_SaveSelect,'1'},
{1,"", M_SaveSelect,'2'},
{1,"", M_SaveSelect,'3'},
{1,"", M_SaveSelect,'4'},
{1,"", M_SaveSelect,'5'},
{1,"", M_SaveSelect,'6'}
};
menu_t SaveDef =
{
load_end,
&MainDef,
SaveMenu,
M_DrawSave,
80,54,
0
};
//
// M_ReadSaveStrings
// read the strings from the savegame files
//
void M_ReadSaveStrings(void)
{
int handle;
int i;
char name[256];
for (i = 0;i < load_end;i++)
{
if (M_CheckParm("-cdrom"))
snprintf(name,sizeof(name),"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",i);
else
snprintf(name,sizeof(name),SAVEGAMENAME"%d.dsg",i);
handle = open (name, O_RDONLY | 0);
if (handle == -1)
{
strcpy(&savegamestrings[i][0],EMPTYSTRING);
LoadMenu[i].status = 0;
continue;
}
read (handle, &savegamestrings[i], SAVESTRINGSIZE);
close (handle);
LoadMenu[i].status = 1;
}
}
#define LOADGRAPHIC_Y 8
//
// M_LoadGame & Cie.
//
void M_DrawLoad(void)
{
int i;
V_DrawNamePatch(72 ,LOADGRAPHIC_Y, 0, "M_LOADG", CR_DEFAULT, VPT_STRETCH);
for (i = 0;i < load_end; i++)
{
M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
}
}
//
// Draw border for the savegame description
//
void M_DrawSaveLoadBorder(int x,int y)
{
int i;
V_DrawNamePatch(x-8, y+7, 0, "M_LSLEFT", CR_DEFAULT, VPT_STRETCH);
for (i = 0;i < 24;i++)
{
V_DrawNamePatch(x, y+7, 0, "M_LSCNTR", CR_DEFAULT, VPT_STRETCH);
x += 8;
}
V_DrawNamePatch(x, y+7, 0, "M_LSRGHT", CR_DEFAULT, VPT_STRETCH);
}
//
// User wants to load this game
//
void M_LoadSelect(int choice)
{
char name[256];
if (M_CheckParm("-cdrom"))
snprintf(name,sizeof(name),"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",choice);
else
snprintf(name,sizeof(name),SAVEGAMENAME"%d.dsg",choice);
G_LoadGame (choice, false);
M_ClearMenus ();
}
//
// Selected from DOOM menu
//
void M_LoadGame (int choice)
{
(void)choice;
if (netgame)
{
M_StartMessage(LOADNET,NULL,false);
return;
}
M_SetupNextMenu(&LoadDef);
M_ReadSaveStrings();
}
//
// M_SaveGame & Cie.
//
void M_DrawSave(void)
{
int i;
V_DrawNamePatch(72, LOADGRAPHIC_Y, 0, "M_SAVEG", CR_DEFAULT, VPT_STRETCH);
for (i = 0;i < load_end; i++)
{
M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
}
if (saveStringEnter)
{
i = M_StringWidth(savegamestrings[saveSlot]);
M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_");
}
}
//
// M_Responder calls this when user is finished
//
void M_DoSave(int slot)
{
G_SaveGame (slot,savegamestrings[slot]);
M_ClearMenus ();
// PICK QUICKSAVE SLOT YET?
if (quickSaveSlot == -2)
quickSaveSlot = slot;
}
//
// User wants to save. Start string input for M_Responder
//
void M_SaveSelect(int choice)
{
// we are going to be intercepting all chars
saveStringEnter = 1;
saveSlot = choice;
snprintf(savegamestrings[choice], sizeof(savegamestrings[choice]),
(gamemode==shareware||gamemode==registered||gamemode==retail) ?
*mapnames[(gameepisode-1)*9+gamemap-1] : (gamemission==doom2) ?
*mapnames2[gamemap-1] : (gamemission==pack_plut) ?
*mapnamesp[gamemap-1] : (gamemission==pack_tnt) ?
*mapnamest[gamemap-1] : "Unknown Location", choice);
if (!strcmp(savegamestrings[choice],s_EMPTYSTRING))
savegamestrings[choice][0] = 0;
saveCharIndex = strlen(savegamestrings[choice]);
}
//
// Selected from DOOM menu
//
void M_SaveGame (int choice)
{
(void)choice;
if (!usergame)
{
M_StartMessage(s_SAVEDEAD,NULL,false);
return;
}
if (gamestate != GS_LEVEL)
return;
M_SetupNextMenu(&SaveDef);
M_ReadSaveStrings();
}
//
// M_QuickSave
//
char tempstring[80];
void M_QuickSaveResponse(int ch)
{
if (ch == 'y')
{
M_DoSave(quickSaveSlot);
S_StartSound(NULL,sfx_swtchx);
}
}
void M_QuickSave(void)
{
if (!usergame)
{
S_StartSound(NULL,sfx_oof);
return;
}
if (gamestate != GS_LEVEL)
return;
if (quickSaveSlot < 0)
{
M_StartControlPanel();
M_ReadSaveStrings();
M_SetupNextMenu(&SaveDef);
quickSaveSlot = -2; // means to pick a slot now
return;
}
snprintf(tempstring,sizeof(tempstring),s_QSPROMPT,savegamestrings[quickSaveSlot]);
M_StartMessage(tempstring,M_QuickSaveResponse,true);
}
//
// M_QuickLoad
//
void M_QuickLoadResponse(int ch)
{
if (ch == 'y')
{
M_LoadSelect(quickSaveSlot);
S_StartSound(NULL,sfx_swtchx);
}
}
void M_QuickLoad(void)
{
if (netgame)
{
M_StartMessage(QLOADNET,NULL,false);
return;
}
if (quickSaveSlot < 0)
{
M_StartMessage(QSAVESPOT,NULL,false);
return;
}
snprintf(tempstring, sizeof(tempstring), QLPROMPT,savegamestrings[quickSaveSlot]);
M_StartMessage(tempstring,M_QuickLoadResponse,true);
}
//
// Read This Menus
// Had a "quick hack to fix romero bug"
//
void M_DrawReadThis1(void)
{
inhelpscreens = true;
switch ( gamemode )
{
case commercial:
V_DrawNamePatch(0, 0, 0, "HELP", CR_DEFAULT, VPT_STRETCH);
break;
case shareware:
case registered:
case retail:
V_DrawNamePatch(0, 0, 0, "HELP1", CR_DEFAULT, VPT_STRETCH);
break;
default:
break;
}
return;
}
//
// Read This Menus - optional second page.
//
void M_DrawReadThis2(void)
{
inhelpscreens = true;
switch ( gamemode )
{
case retail:
case commercial:
// This hack keeps us from having to change menus.
V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH);
break;
case shareware:
case registered:
V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH);
break;
default:
break;
}
return;
}
//
// Change Sfx & Music volumes
//
void M_DrawSound(void)
{
int sysmax=(rb->sound_max(SOUND_VOLUME)-rb->sound_min(SOUND_VOLUME));
V_DrawNamePatch(60, 38, 0, "M_SVOL", CR_DEFAULT, VPT_STRETCH);
M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
16,snd_SfxVolume);
M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
16,snd_MusicVolume);
M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(system_vol+1),
16,(sysmax+systemvol)/5);
}
void M_Sound(int choice)
{
(void) choice;
M_SetupNextMenu(&SoundDef);
}
void M_SfxVol(int choice)
{
switch(choice)
{
case 0:
if (snd_SfxVolume)
snd_SfxVolume--;
break;
case 1:
if (snd_SfxVolume < 15)
snd_SfxVolume++;
break;
}
S_SetSfxVolume(snd_SfxVolume /* *8 */);
}
void M_MusicVol(int choice)
{
switch(choice)
{
case 0:
if (snd_MusicVolume)
snd_MusicVolume--;
break;
case 1:
if (snd_MusicVolume < 15)
snd_MusicVolume++;
break;
}
S_SetMusicVolume(snd_MusicVolume /* *8 */);
}
void M_SystemVol(int choice)
{
switch(choice)
{
case 0:
if (systemvol-5>rb->sound_min(SOUND_VOLUME))
{
systemvol-=5;
rb->sound_set(SOUND_VOLUME, systemvol);
rb->global_status->volume = systemvol;
}
break;
case 1:
if (systemvol+5<rb->sound_max(SOUND_VOLUME))
{
systemvol+=5;
rb->sound_set(SOUND_VOLUME, systemvol);
rb->global_status->volume = systemvol;
}
break;
}
}
//
// M_DrawMainMenu
//
void M_DrawMainMenu(void)
{
V_DrawNamePatch(94, 2, 0, "M_DOOM", CR_DEFAULT, VPT_STRETCH);
}
//
// M_NewGame
//
void M_DrawNewGame(void)
{
// CPhipps - patch drawing updated
V_DrawNamePatch(96, 14, 0, "M_NEWG", CR_DEFAULT, VPT_STRETCH);
V_DrawNamePatch(54, 38, 0, "M_SKILL",CR_DEFAULT, VPT_STRETCH);
}
void M_NewGame(int choice)
{
(void) choice;
if (netgame && !demoplayback)
{
M_StartMessage(s_NEWGAME,NULL,false);
return;
}
if ( gamemode == commercial )
M_SetupNextMenu(&NewDef);
else
M_SetupNextMenu(&EpiDef);
}
//
// M_Episode
//
int epi;
void M_DrawEpisode(void)
{
// CPhipps - patch drawing updated
V_DrawNamePatch(54, 38, 0, "M_EPISOD", CR_DEFAULT, VPT_STRETCH);
}
void M_VerifyNightmare(int ch)
{
if (ch != key_menu_enter)
return;
G_DeferedInitNew(nightmare,epi+1,1);
M_ClearMenus ();
}
void M_ChooseSkill(int choice)
{
if (choice == nightmare)
{
M_StartMessage(s_NIGHTMARE,M_VerifyNightmare,true);
return;
}
//jff 3/24/98 remember last skill selected
// killough 10/98 moved to here
defaultskill = choice+1;
G_DeferedInitNew(choice,epi+1,1);
M_ClearMenus ();
}
void M_Episode(int choice)
{
if ( (gamemode == shareware)
&& choice)
{
M_StartMessage(s_SWSTRING,NULL,false); // Ty 03/27/98 - externalized
M_SetupNextMenu(&ReadDef1);
return;
}
// Yet another hack...
if ( (gamemode == registered)
&& (choice > 2))
{
/* Digita */
// fprintf( stderr,
// "M_Episode: 4th episode requires UltimateDOOM\n");
choice = 0;
}
epi = choice;
M_SetupNextMenu(&NewDef);
}
//
// M_Options
//
char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
void M_DrawOptions(void)
{
// CPhipps - patch drawing updated
V_DrawNamePatch(108, 15, 0, "M_OPTTTL", CR_DEFAULT, VPT_STRETCH);
V_DrawNamePatch(OptionsDef.x + 120, OptionsDef.y+LINEHEIGHT*messages, 0,
msgNames[showMessages], CR_DEFAULT, VPT_STRETCH);
M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(gamasens+1),
4,usegamma);
M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
9,screenSize);
}
void M_Options(int choice)
{
(void)choice;
M_SetupNextMenu(&OptionsDef);
}
//
// Toggle messages on/off
//
void M_ChangeMessages(int choice)
{
(void)choice;
showMessages = 1 - showMessages;
if (!showMessages)
players[consoleplayer].message = s_MSGOFF;
else
players[consoleplayer].message = s_MSGON ;
message_dontfuckwithme = true;
}
//
// M_EndGame
//
void M_EndGameResponse(int ch)
{
if (ch != key_menu_enter)
return;
// killough 5/26/98: make endgame quit if recording or playing back demo
if (demorecording || singledemo)
G_CheckDemoStatus();
currentMenu->lastOn = itemOn;
M_ClearMenus ();
D_StartTitle ();
}
void M_EndGame(int choice)
{
(void)choice;
if (!usergame)
{
S_StartSound(NULL,sfx_oof);
return;
}
if (netgame)
{
M_StartMessage(s_NETEND,NULL,false);
return;
}
M_StartMessage(s_ENDGAME,M_EndGameResponse,true);
}
//
// M_ReadThis
//
void M_ReadThis(int choice)
{
(void)choice;
M_SetupNextMenu(&ReadDef1);
}
void M_ReadThis2(int choice)
{
(void)choice;
M_SetupNextMenu(&ReadDef2);
}
void M_FinishReadThis(int choice)
{
(void)choice;
M_SetupNextMenu(&MainDef);
}
//
// M_QuitDOOM
//
int quitsounds[8] =
{
sfx_pldeth,
sfx_dmpain,
sfx_popain,
sfx_slop,
sfx_telept,
sfx_posit1,
sfx_posit3,
sfx_sgtatk
};
int quitsounds2[8] =
{
sfx_vilact,
sfx_getpow,
sfx_boscub,
sfx_slop,
sfx_skeswg,
sfx_kntdth,
sfx_bspact,
sfx_sgtatk
};
void M_QuitResponse(int ch)
{
if (ch != key_menu_enter)
return;
if (!netgame)
{
if (gamemode == commercial)
S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
else
S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
I_WaitVBL(105);
}
I_Quit ();
}
void M_QuitDOOM(int choice)
{
(void)choice;
// We pick index 0 which is language sensitive,
// or one at random, between 1 and maximum number.
if (language != english )
snprintf(endstring,sizeof(endstring),"%s\n\n%s",s_DOSY, endmsg[0] );
else
snprintf(endstring,sizeof(endstring),"%s\n\n%s", endmsg[gametic%(NUM_QUITMESSAGES-1)+1], s_DOSY);
M_StartMessage(endstring,M_QuitResponse,true);
}
void M_ChangeGamma(int choice)
{
switch(choice)
{
case 0:
if (usegamma)
usegamma--;
break;
case 1:
if (usegamma < 4)
usegamma++;
break;
}
V_SetPalette (0);
}
void M_SizeDisplay(int choice)
{
switch(choice)
{
case 0:
if (screenSize > 0)
{
screenblocks--;
screenSize--;
}
break;
case 1:
if (screenSize < 8)
{
screenblocks++;
screenSize++;
}
break;
}
R_SetViewSize (screenblocks);
}
//
// Menu Functions
//
void
M_DrawThermo
( int x,
int y,
int thermWidth,
int thermDot )
{
int xx;
int i;
xx = x;
V_DrawNamePatch(xx, y, 0, "M_THERML", CR_DEFAULT, VPT_STRETCH);
xx += 8;
for (i=0;i<thermWidth;i++)
{
V_DrawNamePatch(xx, y, 0, "M_THERMM", CR_DEFAULT, VPT_STRETCH);
xx += 8;
}
V_DrawNamePatch(xx, y, 0, "M_THERMR", CR_DEFAULT, VPT_STRETCH);
V_DrawNamePatch((x+8)+thermDot*8,y,0,"M_THERMO",CR_DEFAULT,VPT_STRETCH);
}
void
M_DrawEmptyCell
( menu_t* menu,
int item )
{
// CPhipps - patch drawing updated
V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
"M_CELL1", CR_DEFAULT, VPT_STRETCH);
}
void
M_DrawSelCell
( menu_t* menu,
int item )
{
// CPhipps - patch drawing updated
V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
"M_CELL2", CR_DEFAULT, VPT_STRETCH);
}
void
M_StartMessage
( const char* string,
void* routine,
boolean input )
{
messageLastMenuActive = menuactive;
messageToPrint = 1;
messageString = string;
messageRoutine = routine;
messageNeedsInput = input;
menuactive = true;
return;
}
void M_StopMessage(void)
{
menuactive = messageLastMenuActive;
messageToPrint = 0;
}
//
// Find string width from hu_font chars
//
int M_StringWidth(const char* string)
{
int i, c, w = 0;
for (i = 0;(size_t)i < strlen(string);i++)
w += (c = toupper(string[i]) - HU_FONTSTART) < 0 || c >= HU_FONTSIZE ?
4 : SHORT(hu_font[c].width);
return w;
}
//
// Find string height from hu_font chars
//
int M_StringHeight(const char* string)
{
int i, h, height = h = SHORT(hu_font[0].height);
for (i = 0;string[i];i++) // killough 1/31/98
if (string[i] == '\n')
h += height;
return h;
}
//
// Write a string using the hu_font
//
void
M_WriteText
( int x,
int y,
char* string)
{
int w;
char* ch;
int c;
int cx;
int cy;
ch = string;
cx = x;
cy = y;
while(1)
{
c = *ch++;
if (!c)
break;
if (c == '\n')
{
cx = x;
cy += 12;
continue;
}
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c>= HU_FONTSIZE)
{
cx += 4;
continue;
}
w = SHORT (hu_font[c].width);
if (cx+w > 320)
break;
// proff/nicolas 09/20/98 -- changed for hi-res
// CPhipps - patch drawing updated
V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
cx+=w;
}
}
//
// CONTROL PANEL
//
//
// M_Responder
//
boolean M_Responder (event_t* ev)
{
int ch;
int i;
// static int joywait = 0;
// static int mousewait = 0;
// static int mousey = 0;
// static int lasty = 0;
// static int mousex = 0;
// static int lastx = 0;
ch = -1;
// Process joystick input
/* if (ev->type == ev_joystick && joywait < I_GetTime())
{
if (ev->data3 == -1)
{
ch = KEY_UPARROW;
joywait = I_GetTime() + 5;
}
else if (ev->data3 == 1)
{
ch = KEY_DOWNARROW;
joywait = I_GetTime() + 5;
}
if (ev->data2 == -1)
{
ch = KEY_LEFTARROW;
joywait = I_GetTime() + 2;
}
else if (ev->data2 == 1)
{
ch = KEY_RIGHTARROW;
joywait = I_GetTime() + 2;
}
if (ev->data1&1)
{
ch = key_menu_enter;
joywait = I_GetTime() + 5;
}
if (ev->data1&2)
{
ch = KEY_BACKSPACE;
joywait = I_GetTime() + 5;
}
}
else
{
// Process mouse input
if (ev->type == ev_mouse && mousewait < I_GetTime())
{
mousey += ev->data3;
if (mousey < lasty-30)
{
ch = KEY_DOWNARROW;
mousewait = I_GetTime() + 5;
mousey = lasty -= 30;
}
else if (mousey > lasty+30)
{
ch = KEY_UPARROW;
mousewait = I_GetTime() + 5;
mousey = lasty += 30;
}
mousex += ev->data2;
if (mousex < lastx-30)
{
ch = KEY_LEFTARROW;
mousewait = I_GetTime() + 5;
mousex = lastx -= 30;
}
else if (mousex > lastx+30)
{
ch = KEY_RIGHTARROW;
mousewait = I_GetTime() + 5;
mousex = lastx += 30;
}
if (ev->data1&1)
{
ch = key_menu_enter;
mousewait = I_GetTime() + 15;
}
if (ev->data1&2)
{
ch = KEY_BACKSPACE;
mousewait = I_GetTime() + 15;
}
}
else */if (ev->type == ev_keydown)
{
ch = ev->data1;
}
// }
if (ch == -1)
return false;
// Save Game string input
if (saveStringEnter)
{
switch(ch)
{
case KEY_BACKSPACE:
if (saveCharIndex > 0)
{
saveCharIndex--;
savegamestrings[saveSlot][saveCharIndex] = 0;
}
break;
case KEY_ESCAPE:
saveStringEnter = 0;
strcpy(&savegamestrings[saveSlot][0],saveOldString);
break;
case KEY_ENTER:
saveStringEnter = 0;
if (savegamestrings[saveSlot][0])
M_DoSave(saveSlot);
break;
default:
ch = toupper(ch);
if (ch != 32)
if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
break;
if (ch >= 32 && ch <= 127 &&
saveCharIndex < SAVESTRINGSIZE-1 &&
M_StringWidth(savegamestrings[saveSlot]) <
(SAVESTRINGSIZE-2)*8)
{
savegamestrings[saveSlot][saveCharIndex++] = ch;
savegamestrings[saveSlot][saveCharIndex] = 0;
}
break;
}
return true;
}
// Take care of any messages that need input
if (messageToPrint)
{
if (messageNeedsInput == true &&
!(ch == ' ' || ch == 'n' || ch == key_menu_enter || ch == key_menu_escape))
return false;
menuactive = messageLastMenuActive;
messageToPrint = 0;
if (messageRoutine)
messageRoutine(ch);
menuactive = false;
S_StartSound(NULL,sfx_swtchx);
return true;
}
/*
if (ch == KEY_F1) // devparm &&
{
G_ScreenShot ();
return true;
}
*/
// F-Keys
if (!menuactive)
{
if (ch == key_autorun) // Autorun // V
{
autorun = !autorun;
return true;
}
switch(ch)
{
/*
case KEY_F1: // Help key
M_StartControlPanel ();
if ( gamemode == retail )
currentMenu = &ReadDef2;
else
currentMenu = &ReadDef1;
itemOn = 0;
S_StartSound(NULL,sfx_swtchn);
return true;
case KEY_F6: // Quicksave
S_StartSound(NULL,sfx_swtchn);
M_QuickSave();
return true;
case KEY_F9: // Quickload
S_StartSound(NULL,sfx_swtchn);
M_QuickLoad();
return true;
*/
}
}
// Pop-up menu?
if (!menuactive)
{
if (ch == key_menu_escape)
{
M_StartControlPanel ();
S_StartSound(NULL,sfx_swtchn);
return true;
}
return false;
}
// Keys usable within menu
switch (ch)
{
case KEY_DOWNARROW:
do
{
if (itemOn+1 > currentMenu->numitems-1)
itemOn = 0;
else
itemOn++;
S_StartSound(NULL,sfx_pstop);
}
while(currentMenu->menuitems[itemOn].status==-1);
return true;
case KEY_UPARROW:
do
{
if (!itemOn)
itemOn = currentMenu->numitems-1;
else
itemOn--;
S_StartSound(NULL,sfx_pstop);
}
while(currentMenu->menuitems[itemOn].status==-1);
return true;
case KEY_LEFTARROW:
if (currentMenu->menuitems[itemOn].routine &&
currentMenu->menuitems[itemOn].status == 2)
{
S_StartSound(NULL,sfx_stnmov);
currentMenu->menuitems[itemOn].routine(0);
}
return true;
case KEY_RIGHTARROW:
if (currentMenu->menuitems[itemOn].routine &&
currentMenu->menuitems[itemOn].status == 2)
{
S_StartSound(NULL,sfx_stnmov);
currentMenu->menuitems[itemOn].routine(1);
}
return true;
case KEY_ENTER:
if (currentMenu->menuitems[itemOn].routine &&
currentMenu->menuitems[itemOn].status)
{
currentMenu->lastOn = itemOn;
if (currentMenu->menuitems[itemOn].status == 2)
{
currentMenu->menuitems[itemOn].routine(1); // right arrow
S_StartSound(NULL,sfx_stnmov);
}
else
{
currentMenu->menuitems[itemOn].routine(itemOn);
S_StartSound(NULL,sfx_pistol);
}
}
return true;
case KEY_ESCAPE:
currentMenu->lastOn = itemOn;
M_ClearMenus ();
S_StartSound(NULL,sfx_swtchx);
return true;
case KEY_BACKSPACE:
currentMenu->lastOn = itemOn;
if (currentMenu->prevMenu)
{
currentMenu = currentMenu->prevMenu;
itemOn = currentMenu->lastOn;
S_StartSound(NULL,sfx_swtchn);
}
return true;
default:
for (i = itemOn+1;i < currentMenu->numitems;i++)
if (currentMenu->menuitems[i].alphaKey == ch)
{
itemOn = i;
S_StartSound(NULL,sfx_pstop);
return true;
}
for (i = 0;i <= itemOn;i++)
if (currentMenu->menuitems[i].alphaKey == ch)
{
itemOn = i;
S_StartSound(NULL,sfx_pstop);
return true;
}
break;
}
return false;
}
//
// M_StartControlPanel
//
void M_StartControlPanel (void)
{
// intro might call this repeatedly
if (menuactive)
return;
menuactive = 1;
currentMenu = &MainDef; // JDC
itemOn = currentMenu->lastOn; // JDC
}
//
// M_Drawer
// Called after the view has been rendered,
// but before it has been blitted.
//
void M_Drawer (void)
{
static short x;
static short y;
unsigned short i;
short max;
char string[40];
int start;
inhelpscreens = false;
// Horiz. & Vertically center string and print it.
if (messageToPrint)
{
start = 0;
y = 100 - M_StringHeight(messageString)/2;
while(*(messageString+start))
{
for (i = 0;i < strlen(messageString+start);i++)
if (*(messageString+start+i) == '\n')
{
memset(string,0,40);
strncpy(string,messageString+start,i);
start += i+1;
break;
}
if (i == strlen(messageString+start))
{
strcpy(string,messageString+start);
start += i;
}
x = 160 - M_StringWidth(string)/2;
M_WriteText(x,y,string);
y += SHORT(hu_font[0].height);
}
return;
}
if (!menuactive)
return;
if (currentMenu->routine)
currentMenu->routine(); // call Draw routine
// DRAW MENU
x = currentMenu->x;
y = currentMenu->y;
max = currentMenu->numitems;
for (i=0;i<max;i++)
{
if (currentMenu->menuitems[i].name[0])
V_DrawNamePatch(x,y,0,currentMenu->menuitems[i].name,
CR_DEFAULT, VPT_STRETCH);
y += LINEHEIGHT;
}
// DRAW SKULL
// CPhipps - patch drawing updated
V_DrawNamePatch(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,0,
skullName[whichSkull], CR_DEFAULT, VPT_STRETCH);
}
//
// M_ClearMenus
//
void M_ClearMenus (void)
{
menuactive = 0;
// if (!netgame && usergame && paused)
// sendpause = true;
}
//
// M_SetupNextMenu
//
void M_SetupNextMenu(menu_t *menudef)
{
currentMenu = menudef;
itemOn = currentMenu->lastOn;
}
//
// M_Ticker
//
void M_Ticker (void)
{
if (--skullAnimCounter <= 0)
{
whichSkull ^= 1;
skullAnimCounter = 8;
}
}
//
// M_Init
//
void M_Init (void)
{
currentMenu = &MainDef;
menuactive = 0;
itemOn = currentMenu->lastOn;
whichSkull = 0;
skullAnimCounter = 10;
screenSize = screenblocks - 3;
messageToPrint = 0;
messageString = NULL;
messageLastMenuActive = menuactive;
quickSaveSlot = -1;
// Here we could catch other version dependencies,
// like HELP1/2, and four episodes.
switch ( gamemode )
{
case commercial:
// This is used because DOOM 2 had only one HELP
// page. I use CREDIT as second page now, but
// kept this hack for educational purposes.
MainMenu[readthis] = MainMenu[quitdoom];
MainDef.numitems--;
MainDef.y += 8;
NewDef.prevMenu = &MainDef;
ReadDef1.routine = M_DrawReadThis1;
ReadDef1.x = 330;
ReadDef1.y = 165;
ReadMenu1[0].routine = M_FinishReadThis;
break;
case shareware:
// Episode 2 and 3 are handled,
// branching to an ad screen.
case registered:
// We need to remove the fourth episode.
EpiDef.numitems--;
break;
case retail:
// We are fine.
default:
break;
}
}