This commit is contained in:
nashmuhandes 2017-02-19 21:52:33 +08:00
commit 56790c7590
63 changed files with 3672 additions and 3609 deletions

View file

@ -38,6 +38,7 @@ conversation
page
{
drop = <string>;
userstring = <string>; New field which can be used to pass data to custom conversation menu classes.
ifitem
{
item = <string>;
@ -63,10 +64,6 @@ either refuse loading dialogues with the 'ZDoom' namespace or if it does not
outright abort on incompatible namespaces fail with a type mismatch error on
one of the specified propeties.
In addition ZDoom defines one new field in the top level of a conversation block:
id = <integer>; Assigns a conversation ID for use in Thing_SetConversation or in UDMF's 'conversation' actor property.
ZDoom-format dialogues need to start with the line:
namespace = "ZDoom";
@ -86,6 +83,7 @@ conversation // Starts a dialog.
// the standard conversation ID ('actor' property) is used instead
// for this purpose but since 'ZDoom' namespace requires the actor
// to be a class name it needs a separate field for this.
class = <string>; //Override the default conversation menu class for this conversation.
page
{

View file

@ -679,15 +679,12 @@ set( FASTMATH_PCH_SOURCES
intermission/intermission.cpp
intermission/intermission_parse.cpp
menu/joystickmenu.cpp
menu/listmenu.cpp
menu/loadsavemenu.cpp
menu/menu.cpp
menu/menudef.cpp
menu/menuinput.cpp
menu/messagebox.cpp
menu/optionmenu.cpp
menu/playermenu.cpp
menu/readthis.cpp
menu/videomenu.cpp
timidity/common.cpp
timidity/instrum.cpp

View file

@ -207,7 +207,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetInt)
{
// Only menus are allowed to change CVARs.
PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar);
if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0;
if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0;
PARAM_INT(val);
UCVarValue v;
v.Int = val;
@ -219,7 +219,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetFloat)
{
// Only menus are allowed to change CVARs.
PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar);
if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0;
if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0;
PARAM_FLOAT(val);
UCVarValue v;
v.Float = (float)val;
@ -231,7 +231,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetString)
{
// Only menus are allowed to change CVARs.
PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar);
if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0;
if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0;
PARAM_STRING(val);
UCVarValue v;
v.String = val.GetChars();

View file

@ -666,7 +666,7 @@ void C_DoCommand (const char *cmd, int keynum)
// This is only accessible to the special menu item to run CCMDs.
DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand)
{
if (DMenu::CurrentMenu == nullptr) return 0;
if (CurrentMenu == nullptr) return 0;
PARAM_PROLOGUE;
PARAM_STRING(cmd);
C_DoCommand(cmd);

View file

@ -95,23 +95,6 @@ char *copystring (const char *s)
return b;
}
//============================================================================
//
// ncopystring
//
// If the string has no content, returns NULL. Otherwise, returns a copy.
//
//============================================================================
char *ncopystring (const char *string)
{
if (string == NULL || string[0] == 0)
{
return NULL;
}
return copystring (string);
}
//==========================================================================
//
// ReplaceString

View file

@ -38,7 +38,6 @@ int ParseHex(const char *str, FScriptPosition *sc = nullptr);
bool IsNum (const char *str); // [RH] added
char *copystring(const char *s);
char *ncopystring(const char *s);
void ReplaceString (char **ptr, const char *str);
bool CheckWildcards (const char *pattern, const char *text);

View file

@ -74,7 +74,7 @@ EXTERN_CVAR (Int, autosavecount)
#define SIMULATEERRORS 0
extern BYTE *demo_p; // [RH] Special "ticcmds" get recorded in demos
extern char savedescription[SAVESTRINGSIZE];
extern FString savedescription;
extern FString savegamefile;
extern short consistancy[MAXPLAYERS][BACKUPTICS];
@ -2419,8 +2419,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
savegamefile = s;
delete[] s;
s = ReadString (stream);
memset (savedescription, 0, sizeof(savedescription));
strncpy (savedescription, s, sizeof(savedescription));
savedescription = s;
if (player != consoleplayer)
{
// Paths sent over the network will be valid for the system that sent

View file

@ -729,7 +729,7 @@ void D_WriteUserInfoStrings (int pnum, BYTE **stream, bool compact)
break;
case NAME_Skin:
*stream += sprintf(*((char **)stream), "\\%s", D_EscapeUserInfo(skins[info->GetSkin()].name).GetChars());
*stream += sprintf(*((char **)stream), "\\%s", D_EscapeUserInfo(Skins[info->GetSkin()].Name).GetChars());
break;
default:
@ -828,7 +828,7 @@ void D_ReadUserInfoStrings (int pnum, BYTE **stream, bool update)
players[pnum].mo->state->sprite ==
GetDefaultByType (players[pnum].cls)->SpawnState->sprite)
{ // Only change the sprite if the player is using a standard one
players[pnum].mo->sprite = skins[info->GetSkin()].sprite;
players[pnum].mo->sprite = Skins[info->GetSkin()].sprite;
}
}
// Rebuild translation in case the new skin uses a different range
@ -898,7 +898,7 @@ void WriteUserInfo(FSerializer &arc, userinfo_t &info)
switch (pair->Key.GetIndex())
{
case NAME_Skin:
string = skins[info.GetSkin()].name;
string = Skins[info.GetSkin()].Name;
break;
case NAME_PlayerClass:
@ -986,7 +986,7 @@ CCMD (playerinfo)
// Print special info
Printf("%20s: %s\n", "Name", ui->GetName());
Printf("%20s: %s (%d)\n", "Team", ui->GetTeam() == TEAM_NONE ? "None" : Teams[ui->GetTeam()].GetName(), ui->GetTeam());
Printf("%20s: %s (%d)\n", "Skin", skins[ui->GetSkin()].name, ui->GetSkin());
Printf("%20s: %s (%d)\n", "Skin", Skins[ui->GetSkin()].Name.GetChars(), ui->GetSkin());
Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender());
Printf("%20s: %s (%d)\n", "PlayerClass",
ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->DisplayName.GetChars(),

View file

@ -298,6 +298,11 @@ struct userinfo_t : TMap<FName,FBaseCVar *>
return aim;
}
}
// Same but unfiltered.
double GetAutoaim() const
{
return *static_cast<FFloatCVar *>(*CheckKey(NAME_Autoaim));
}
const char *GetName() const
{
return *static_cast<FStringCVar *>(*CheckKey(NAME_Name));

View file

@ -227,7 +227,7 @@ int mousex;
int mousey;
FString savegamefile;
char savedescription[SAVESTRINGSIZE];
FString savedescription;
// [RH] Name of screenshot file to generate (usually NULL)
FString shotfile;
@ -1082,7 +1082,7 @@ void G_Ticker ()
G_DoSaveGame (true, savegamefile, savedescription);
gameaction = ga_nothing;
savegamefile = "";
savedescription[0] = '\0';
savedescription = "";
break;
case ga_autosave:
G_DoAutoSave ();
@ -1696,7 +1696,7 @@ static void G_QueueBody (AActor *body)
{
// Apply skin's scale to actor's scale, it will be lost otherwise
const AActor *const defaultActor = body->GetDefault();
const FPlayerSkin &skin = skins[skinidx];
const FPlayerSkin &skin = Skins[skinidx];
body->Scale.X *= skin.Scale.X / defaultActor->Scale.X;
body->Scale.Y *= skin.Scale.Y / defaultActor->Scale.Y;
@ -2069,8 +2069,7 @@ void G_SaveGame (const char *filename, const char *description)
else
{
savegamefile = filename;
strncpy (savedescription, description, sizeof(savedescription)-1);
savedescription[sizeof(savedescription)-1] = '\0';
savedescription = description;
sendsave = true;
}
}
@ -2120,7 +2119,7 @@ extern void P_CalcHeight (player_t *);
void G_DoAutoSave ()
{
char description[SAVESTRINGSIZE];
FString description;
FString file;
// Keep up to four autosaves at a time
UCVarValue num;
@ -2148,10 +2147,7 @@ void G_DoAutoSave ()
}
readableTime = myasctime ();
strcpy (description, "Autosave ");
strncpy (description+9, readableTime+4, 12);
description[9+12] = 0;
description.Format("Autosave %.12s", readableTime + 4);
G_DoSaveGame (false, file, description);
}
@ -2311,7 +2307,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
WriteZip(filename, savegame_filenames, savegame_content);
savegameManager.NotifyNewSave (filename.GetChars(), description, okForQuicksave);
savegameManager.NotifyNewSave (filename, description, okForQuicksave);
// delete the JSON buffers we created just above. Everything else will
// either still be needed or taken care of automatically.

View file

@ -1461,6 +1461,7 @@ void G_InitLevelLocals ()
level.LevelName = level.info->LookupLevelName();
level.NextMap = info->NextMap;
level.NextSecretMap = info->NextSecretMap;
level.F1Pic = info->F1Pic;
compatflags.Callback();
compatflags2.Callback();
@ -1909,6 +1910,7 @@ DEFINE_FIELD(FLevelLocals, LevelName)
DEFINE_FIELD(FLevelLocals, MapName)
DEFINE_FIELD(FLevelLocals, NextMap)
DEFINE_FIELD(FLevelLocals, NextSecretMap)
DEFINE_FIELD(FLevelLocals, F1Pic)
DEFINE_FIELD(FLevelLocals, maptype)
DEFINE_FIELD(FLevelLocals, Music)
DEFINE_FIELD(FLevelLocals, musicorder)
@ -1934,6 +1936,7 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND)
DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS)
DEFINE_FIELD_BIT(FLevelLocals, flags2, frozen, LEVEL2_FROZEN)
DEFINE_FIELD_BIT(FLevelLocals, flags2, infinite_flight, LEVEL2_INFINITE_FLIGHT)
DEFINE_FIELD_BIT(FLevelLocals, flags2, no_dlg_freeze, LEVEL2_CONV_SINGLE_UNFREEZE)
//==========================================================================
//

View file

@ -25,6 +25,7 @@ struct FLevelLocals
FString MapName; // the lump name (E1M1, MAP01, etc)
FString NextMap; // go here when using the regular exit
FString NextSecretMap; // map to go to when used secret exit
FString F1Pic;
EMapType maptype;
TStaticArray<vertex_t> vertexes;

View file

@ -1970,6 +1970,7 @@ static void ClearMapinfo()
DefaultSkill = -1;
DeinitIntermissions();
level.info = NULL;
level.F1Pic = "";
}
//==========================================================================

View file

@ -298,7 +298,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
// If a custom skin was in use, then reload it
// or else the base skin for the player class.
if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
(size_t)player->userinfo.GetSkin() < numskins)
(unsigned)player->userinfo.GetSkin() < Skins.Size())
{
skinindex = player->userinfo.GetSkin();

View file

@ -489,7 +489,7 @@ FTexture *FMugShot::GetFace(player_t *player, const char *default_face, int accu
if (CurrentState != NULL)
{
int skin = player->userinfo.GetSkin();
const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->morphTics ? ((APlayerPawn*)GetDefaultByType(player->MorphedPlayerClass))->Face.GetChars() : skins[skin].face);
const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->morphTics ? ((APlayerPawn*)GetDefaultByType(player->MorphedPlayerClass))->Face.GetChars() : Skins[skin].Face.GetChars());
return CurrentState->GetCurrentFrameTexture(default_face, skin_face, level, angle);
}
return NULL;

View file

@ -50,6 +50,8 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, Armor2Percent)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon1)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon2)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gametype)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, norandomplayerclass)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, infoPages)
const char *GameNames[17] =
@ -360,6 +362,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_INT(TextScreenX, "textscreenx")
GAMEINFOKEY_INT(TextScreenY, "textscreeny")
GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence")
GAMEINFOKEY_STRING(DefaultConversationMenuClass, "defaultconversationmenuclass")
GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont")
GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont")
GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont")

View file

@ -172,6 +172,7 @@ struct gameinfo_t
double gibfactor;
int TextScreenX;
int TextScreenY;
FName DefaultConversationMenuClass;
FName DefaultEndSequence;
FString mMapArrow, mCheatMapArrow;
FString mEasyKey, mCheatKey;

View file

@ -593,7 +593,7 @@ void DIntermissionScreenCast::Drawer ()
if (!(mDefaults->flags4 & MF4_NOSKIN) &&
mDefaults->SpawnState != NULL && caststate->sprite == mDefaults->SpawnState->sprite &&
mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)) &&
skins != NULL)
Skins.Size() > 0)
{
// Only use the skin sprite if this class has not been removed from the
// PlayerClasses list.
@ -601,7 +601,7 @@ void DIntermissionScreenCast::Drawer ()
{
if (PlayerClasses[i].Type == mClass)
{
FPlayerSkin *skin = &skins[players[consoleplayer].userinfo.GetSkin()];
FPlayerSkin *skin = &Skins[players[consoleplayer].userinfo.GetSkin()];
castsprite = skin->sprite;
if (!(mDefaults->flags4 & MF4_NOSKIN))

View file

@ -186,12 +186,12 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
{
opt->mSelectedItem = opt->mItems.Size() - 1;
}
opt->CalcIndent();
//opt->CalcIndent();
// If the joystick config menu is open, close it if the device it's open for is gone.
if (DMenu::CurrentMenu != nullptr && (DMenu::CurrentMenu->IsKindOf("JoystickConfigMenu")))
if (CurrentMenu != nullptr && (CurrentMenu->IsKindOf("JoystickConfigMenu")))
{
auto p = DMenu::CurrentMenu->PointerVar<IJoystickConfig>("mJoy");
auto p = CurrentMenu->PointerVar<IJoystickConfig>("mJoy");
if (p != nullptr)
{
unsigned i;
@ -204,7 +204,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
}
if (i == Joysticks.Size())
{
DMenu::CurrentMenu->Close();
CurrentMenu->Close();
}
}
}

View file

@ -1,281 +0,0 @@
/*
** listmenu.cpp
** A simple menu consisting of a list of items
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "v_video.h"
#include "v_font.h"
#include "cmdlib.h"
#include "gstrings.h"
#include "g_level.h"
#include "gi.h"
#include "d_gui.h"
#include "d_event.h"
#include "menu/menu.h"
IMPLEMENT_CLASS(DListMenu, false, false)
IMPLEMENT_POINTERS_START(DListMenu)
IMPLEMENT_POINTER(mFocusControl)
IMPLEMENT_POINTERS_END
//=============================================================================
//
//
//
//=============================================================================
DListMenu::DListMenu(DMenu *parent, DListMenuDescriptor *desc)
: DMenu(parent)
{
mDesc = NULL;
if (desc != NULL) Init(parent, desc);
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Init(DMenu *parent, DListMenuDescriptor *desc)
{
mParentMenu = parent;
GC::WriteBarrier(this, parent);
mDesc = desc;
if (desc->mCenter)
{
int center = 160;
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
int xpos = mDesc->mItems[i]->GetX();
int width = mDesc->mItems[i]->GetWidth();
int curx = mDesc->mSelectOfsX;
if (width > 0 && mDesc->mItems[i]->Selectable())
{
int left = 160 - (width - curx) / 2 - curx;
if (left < center) center = left;
}
}
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
int width = mDesc->mItems[i]->GetWidth();
if (width > 0)
{
mDesc->mItems[i]->SetX(center);
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
DMenuItemBase *DListMenu::GetItem(FName name)
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
FName nm = mDesc->mItems[i]->GetAction(NULL);
if (nm == name) return mDesc->mItems[i];
}
return NULL;
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::Responder (event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_KeyDown)
{
int ch = tolower (ev->data1);
for(unsigned i = mDesc->mSelectedItem + 1; i < mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->CheckHotkey(ch))
{
mDesc->mSelectedItem = i;
S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
}
}
for(int i = 0; i < mDesc->mSelectedItem; i++)
{
if (mDesc->mItems[i]->CheckHotkey(ch))
{
mDesc->mSelectedItem = i;
S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
}
}
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::MenuEvent (int mkey, bool fromcontroller)
{
int oldSelect = mDesc->mSelectedItem;
int startedAt = mDesc->mSelectedItem;
if (startedAt < 0) startedAt = 0;
switch (mkey)
{
case MKEY_Up:
do
{
if (--mDesc->mSelectedItem < 0) mDesc->mSelectedItem = mDesc->mItems.Size()-1;
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
if (mDesc->mSelectedItem == startedAt) mDesc->mSelectedItem = oldSelect;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
case MKEY_Down:
do
{
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) mDesc->mSelectedItem = 0;
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
if (mDesc->mSelectedItem == startedAt) mDesc->mSelectedItem = oldSelect;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
case MKEY_Enter:
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate())
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
}
return true;
default:
return Super::MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::MouseEvent(int type, int x, int y)
{
int sel = -1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
if (mFocusControl != NULL)
{
mFocusControl->MouseEvent(type, x, y);
return true;
}
else
{
if ((mDesc->mWLeft <= 0 || x > mDesc->mWLeft) &&
(mDesc->mWRight <= 0 || x < mDesc->mWRight))
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->CheckCoordinate(x, y))
{
if ((int)i != mDesc->mSelectedItem)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mDesc->mSelectedItem = i;
mDesc->mItems[i]->MouseEvent(type, x, y);
return true;
}
}
}
}
mDesc->mSelectedItem = -1;
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Ticker ()
{
Super::Ticker();
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
mDesc->mItems[i]->Ticker();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Drawer ()
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(mDesc->mSelectedItem == (int)i);
}
if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size())
mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector);
Super::Drawer();
}
//=============================================================================
//
// base class for menu items
//
//=============================================================================
IMPLEMENT_CLASS(DMenuItemBase, false, false)

File diff suppressed because it is too large Load diff

View file

@ -63,24 +63,23 @@
CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE)
CVAR(Int, m_use_mouse, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
DMenu *DMenu::CurrentMenu;
DEFINE_ACTION_FUNCTION(DMenu, GetCurrentMenu)
{
ACTION_RETURN_OBJECT(DMenu::CurrentMenu);
ACTION_RETURN_OBJECT(CurrentMenu);
}
int DMenu::MenuTime;
DEFINE_ACTION_FUNCTION(DMenu, MenuTime)
{
ACTION_RETURN_INT(DMenu::MenuTime);
ACTION_RETURN_INT(MenuTime);
}
FGameStartup GameStartupInfo;
@ -92,6 +91,8 @@ bool MenuButtonOrigin[NUM_MKEYS];
int BackbuttonTime;
float BackbuttonAlpha;
static bool MenuEnabled = true;
DMenu *CurrentMenu;
int MenuTime;
void M_InitVideoModes();
extern PClass *DefaultListMenuClass;
@ -140,7 +141,7 @@ void M_MarkMenus()
{
GC::Mark(pair->Value);
}
GC::Mark(DMenu::CurrentMenu);
GC::Mark(CurrentMenu);
}
//============================================================================
//
@ -159,6 +160,7 @@ DMenu::DMenu(DMenu *parent)
mParentMenu = parent;
mMouseCapture = false;
mBackbuttonSelected = false;
DontDim = false;
GC::WriteBarrier(this, parent);
}
@ -237,7 +239,7 @@ bool DMenu::MenuEvent (int mkey, bool fromcontroller)
{
Close();
S_Sound (CHAN_VOICE | CHAN_UI,
DMenu::CurrentMenu != nullptr? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
CurrentMenu != nullptr? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
return true;
}
}
@ -272,13 +274,13 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
void DMenu::Close ()
{
if (DMenu::CurrentMenu == nullptr) return; // double closing can happen in the save menu.
assert(DMenu::CurrentMenu == this);
DMenu::CurrentMenu = mParentMenu;
if (CurrentMenu == nullptr) return; // double closing can happen in the save menu.
assert(CurrentMenu == this);
CurrentMenu = mParentMenu;
Destroy();
if (DMenu::CurrentMenu != nullptr)
if (CurrentMenu != nullptr)
{
GC::WriteBarrier(DMenu::CurrentMenu);
GC::WriteBarrier(CurrentMenu);
}
else
{
@ -401,7 +403,7 @@ void DMenu::CallTicker()
void DMenu::Drawer ()
{
if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
if (this == CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
{
FTexture *tex = TexMan(gameinfo.mBackButton);
int w = tex->GetScaledWidth() * CleanXfac;
@ -444,26 +446,6 @@ DEFINE_ACTION_FUNCTION(DMenu, Close)
return 0;
}
DEFINE_ACTION_FUNCTION(DMenu, GetItem)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_NAME(name);
ACTION_RETURN_OBJECT(self->GetItem(name));
}
DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, GetItem)
{
PARAM_SELF_PROLOGUE(DOptionMenuDescriptor);
PARAM_NAME(name);
ACTION_RETURN_OBJECT(self->GetItem(name));
}
bool DMenu::DimAllowed()
{
return true;
}
bool DMenu::TranslateKeyboardEvents()
{
IFVIRTUAL(DMenu, TranslateKeyboardEvents)
@ -486,7 +468,7 @@ bool DMenu::TranslateKeyboardEvents()
void M_StartControlPanel (bool makeSound)
{
// intro might call this repeatedly
if (DMenu::CurrentMenu != nullptr)
if (CurrentMenu != nullptr)
return;
ResetButtonStates ();
@ -518,9 +500,9 @@ void M_StartControlPanel (bool makeSound)
void M_ActivateMenu(DMenu *menu)
{
if (menuactive == MENU_Off) menuactive = MENU_On;
if (DMenu::CurrentMenu != nullptr) DMenu::CurrentMenu->ReleaseCapture();
DMenu::CurrentMenu = menu;
GC::WriteBarrier(DMenu::CurrentMenu);
if (CurrentMenu != nullptr) CurrentMenu->ReleaseCapture();
CurrentMenu = menu;
GC::WriteBarrier(CurrentMenu);
}
DEFINE_ACTION_FUNCTION(DMenu, ActivateMenu)
@ -602,6 +584,11 @@ void M_SetMenu(FName menu, int param)
M_InitVideoModes();
break;
case NAME_Quitmenu:
// The separate menu class no longer exists but the name still needs support for existing mods.
C_DoCommand("menu_quit");
return;
}
// End of special checks
@ -629,8 +616,12 @@ void M_SetMenu(FName menu, int param)
if (cls == nullptr) cls = DefaultListMenuClass;
if (cls == nullptr) cls = PClass::FindClass("ListMenu");
DListMenu *newmenu = (DListMenu *)cls->CreateNew();
newmenu->Init(DMenu::CurrentMenu, ld);
DMenu *newmenu = (DMenu *)cls->CreateNew();
IFVIRTUALPTRNAME(newmenu, "ListMenu", Init)
{
VMValue params[3] = { newmenu, CurrentMenu, ld };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
M_ActivateMenu(newmenu);
}
}
@ -644,7 +635,7 @@ void M_SetMenu(FName menu, int param)
DMenu *newmenu = (DMenu*)cls->CreateNew();
IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init)
{
VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld };
VMValue params[3] = { newmenu, CurrentMenu, ld };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
M_ActivateMenu(newmenu);
@ -659,7 +650,7 @@ void M_SetMenu(FName menu, int param)
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
{
DMenu *newmenu = (DMenu*)menuclass->CreateNew();
newmenu->mParentMenu = DMenu::CurrentMenu;
newmenu->mParentMenu = CurrentMenu;
M_ActivateMenu(newmenu);
return;
}
@ -695,7 +686,7 @@ bool M_Responder (event_t *ev)
return false;
}
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off)
if (CurrentMenu != nullptr && menuactive != MENU_Off)
{
// There are a few input sources we are interested in:
//
@ -730,9 +721,9 @@ bool M_Responder (event_t *ev)
}
// pass everything else on to the current menu
return DMenu::CurrentMenu->CallResponder(ev);
return CurrentMenu->CallResponder(ev);
}
else if (DMenu::CurrentMenu->TranslateKeyboardEvents())
else if (CurrentMenu->TranslateKeyboardEvents())
{
ch = ev->data1;
keyup = ev->subtype == EV_GUI_KeyUp;
@ -751,7 +742,7 @@ bool M_Responder (event_t *ev)
default:
if (!keyup)
{
return DMenu::CurrentMenu->CallResponder(ev);
return CurrentMenu->CallResponder(ev);
}
break;
}
@ -834,11 +825,11 @@ bool M_Responder (event_t *ev)
{
MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
}
DMenu::CurrentMenu->CallMenuEvent(mkey, fromcontroller);
CurrentMenu->CallMenuEvent(mkey, fromcontroller);
return true;
}
}
return DMenu::CurrentMenu->CallResponder(ev) || !keyup;
return CurrentMenu->CallResponder(ev) || !keyup;
}
else if (MenuEnabled)
{
@ -879,10 +870,10 @@ bool M_Responder (event_t *ev)
void M_Ticker (void)
{
DMenu::MenuTime++;
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off)
MenuTime++;
if (CurrentMenu != nullptr && menuactive != MENU_Off)
{
DMenu::CurrentMenu->CallTicker();
CurrentMenu->CallTicker();
for (int i = 0; i < NUM_MKEYS; ++i)
{
@ -891,7 +882,7 @@ void M_Ticker (void)
if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0)
{
MenuButtonTickers[i] = KEY_REPEAT_RATE;
DMenu::CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]);
CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]);
}
}
}
@ -931,14 +922,14 @@ void M_Drawer (void)
}
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off)
if (CurrentMenu != nullptr && menuactive != MENU_Off)
{
if (DMenu::CurrentMenu->DimAllowed())
if (!CurrentMenu->DontDim)
{
screen->Dim(fade);
V_SetBorderNeedRefresh();
}
DMenu::CurrentMenu->CallDrawer();
CurrentMenu->CallDrawer();
}
}
@ -951,10 +942,10 @@ void M_Drawer (void)
void M_ClearMenus ()
{
M_DemoNoPlay = false;
if (DMenu::CurrentMenu != nullptr)
if (CurrentMenu != nullptr)
{
DMenu::CurrentMenu->Destroy();
DMenu::CurrentMenu = nullptr;
CurrentMenu->Destroy();
CurrentMenu = nullptr;
}
V_SetBorderNeedRefresh();
menuactive = MENU_Off;
@ -1192,11 +1183,11 @@ CCMD(reset2saved)
// This really should be in the script but we can't do scripted CCMDs yet.
CCMD(undocolorpic)
{
if (DMenu::CurrentMenu != NULL)
if (CurrentMenu != NULL)
{
IFVIRTUALPTR(DMenu::CurrentMenu, DMenu, ResetColor)
IFVIRTUALPTR(CurrentMenu, DMenu, ResetColor)
{
VMValue params[] = { (DObject*)DMenu::CurrentMenu };
VMValue params[] = { (DObject*)CurrentMenu };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
@ -1206,6 +1197,9 @@ CCMD(undocolorpic)
DEFINE_FIELD(DMenu, mParentMenu)
DEFINE_FIELD(DMenu, mMouseCapture);
DEFINE_FIELD(DMenu, mBackbuttonSelected);
DEFINE_FIELD(DMenu, DontDim);
DEFINE_FIELD(DMenuDescriptor, mMenuName)
DEFINE_FIELD(DMenuDescriptor, mNetgameMessage)
@ -1216,9 +1210,6 @@ DEFINE_FIELD(DMenuItemBase, mYpos)
DEFINE_FIELD(DMenuItemBase, mAction)
DEFINE_FIELD(DMenuItemBase, mEnabled)
DEFINE_FIELD(DListMenu, mDesc)
DEFINE_FIELD(DListMenu, mFocusControl)
DEFINE_FIELD(DListMenuDescriptor, mItems)
DEFINE_FIELD(DListMenuDescriptor, mSelectedItem)
DEFINE_FIELD(DListMenuDescriptor, mSelectOfsX)
@ -1318,50 +1309,6 @@ DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, con
return (DMenuItemBase*)p;
}
bool DMenuItemBase::CheckCoordinate(int x, int y)
{
IFVIRTUAL(DMenuItemBase, CheckCoordinate)
{
VMValue params[] = { (DObject*)this, x, y };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
void DMenuItemBase::Ticker()
{
IFVIRTUAL(DMenuItemBase, Ticker)
{
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
void DMenuItemBase::Drawer(bool selected)
{
IFVIRTUAL(DMenuItemBase, Drawer)
{
VMValue params[] = { (DObject*)this, selected };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
bool DMenuItemBase::Selectable()
{
IFVIRTUAL(DMenuItemBase, Selectable)
{
VMValue params[] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
bool DMenuItemBase::Activate()
{
IFVIRTUAL(DMenuItemBase, Activate)
@ -1374,18 +1321,6 @@ bool DMenuItemBase::Activate()
}
return false;
}
FName DMenuItemBase::GetAction(int *pparam)
{
IFVIRTUAL(DMenuItemBase, GetAction)
{
VMValue params[] = { (DObject*)this };
int retval[2];
VMReturn ret[2]; ret[0].IntAt(&retval[0]); ret[1].IntAt(&retval[1]);
GlobalVMStack.Call(func, params, countof(params), ret, 2, nullptr);
return ENamedName(retval[0]);
}
return NAME_None;
}
bool DMenuItemBase::SetString(int i, const char *s)
{
@ -1443,112 +1378,4 @@ bool DMenuItemBase::GetValue(int i, int *pvalue)
return false;
}
void DMenuItemBase::Enable(bool on)
{
IFVIRTUAL(DMenuItemBase, Enable)
{
VMValue params[] = { (DObject*)this, on };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
bool DMenuItemBase::MenuEvent(int mkey, bool fromcontroller)
{
IFVIRTUAL(DMenuItemBase, MenuEvent)
{
VMValue params[] = { (DObject*)this, mkey, fromcontroller };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
bool DMenuItemBase::MouseEvent(int type, int x, int y)
{
IFVIRTUAL(DMenuItemBase, MouseEvent)
{
VMValue params[] = { (DObject*)this, type, x, y };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
bool DMenuItemBase::CheckHotkey(int c)
{
IFVIRTUAL(DMenuItemBase, CheckHotkey)
{
VMValue params[] = { (DObject*)this, c };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
int DMenuItemBase::GetWidth()
{
IFVIRTUAL(DMenuItemBase, GetWidth)
{
VMValue params[] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return retval;
}
return false;
}
int DMenuItemBase::GetIndent()
{
IFVIRTUAL(DMenuItemBase, GetIndent)
{
VMValue params[] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return retval;
}
return false;
}
int DMenuItemBase::Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
IFVIRTUAL(DMenuItemBase, Draw)
{
VMValue params[] = { (DObject*)this, desc, y, indent, selected };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return retval;
}
return false;
}
void DMenuItemBase::DrawSelector(int xofs, int yofs, FTextureID tex)
{
if (tex.isNull())
{
if ((DMenu::MenuTime % 8) < 6)
{
screen->DrawText(ConFont, OptionSettings.mFontColorSelection,
(mXpos + xofs - 160) * CleanXfac + screen->GetWidth() / 2,
(mYpos + yofs - 100) * CleanYfac + screen->GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
else
{
screen->DrawTexture(TexMan(tex), mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE);
}
}
IMPLEMENT_CLASS(DMenuItemBase, false, false)

View file

@ -58,46 +58,58 @@ extern FGameStartup GameStartupInfo;
struct FSaveGameNode
{
char Title[SAVESTRINGSIZE];
FString SaveTitle;
FString Filename;
bool bOldVersion;
bool bMissingWads;
bool bNoDelete;
FSaveGameNode() { bNoDelete = false; }
bool bOldVersion = false;
bool bMissingWads = false;
bool bNoDelete = false;
};
struct SavegameManager
struct FSavegameManager
{
private:
TArray<FSaveGameNode*> SaveGames;
FSaveGameNode NewSaveNode;
int LastSaved = -1;
int LastAccessed = -1;
int WindowSize = 0;
FSaveGameNode *quickSaveSlot = nullptr;
FileReader *currentSavePic = nullptr;
TArray<char> SavePicData;
FTexture *SavePic = nullptr;
FBrokenLines *SaveComment = nullptr;
void ClearSaveGames();
public:
int WindowSize = 0;
FSaveGameNode *quickSaveSlot = nullptr;
~FSavegameManager();
private:
int InsertSaveNode(FSaveGameNode *node);
int RemoveSaveSlot(int index);
public:
void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave);
void ClearSaveGames();
void ReadSaveStrings();
void NotifyNewSave(const char *file, const char *title, bool okForQuicksave);
void UnloadSaveData();
int RemoveSaveSlot(int index);
void LoadSavegame(int Selected);
void DoSave(int Selected, const char *savegamestring);
void DeleteEntry(int Selected);
void ExtractSaveData(int index);
void UnloadSaveData();
unsigned ExtractSaveData(int index);
void ClearSaveStuff();
bool DrawSavePic(int x, int y, int w, int h);
void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor);
void SetFileInfo(int Selected);
unsigned SavegameCount();
FSaveGameNode *GetSavegame(int i);
void InsertNewSaveNode();
bool RemoveNewSaveNode();
};
extern SavegameManager savegameManager;
extern FSavegameManager savegameManager;
class DMenu;
extern DMenu *CurrentMenu;
extern int MenuTime;
//=============================================================================
//
@ -241,9 +253,7 @@ class DMenu : public DObject
DECLARE_CLASS (DMenu, DObject)
HAS_OBJECT_POINTERS
protected:
bool mMouseCapture;
bool mBackbuttonSelected;
public:
enum
@ -258,17 +268,16 @@ public:
BACKBUTTON_TIME = 4*TICRATE
};
static DMenu *CurrentMenu;
static int MenuTime;
TObjPtr<DMenu> mParentMenu;
bool mMouseCapture;
bool mBackbuttonSelected;
bool DontDim;
DMenu(DMenu *parent = NULL);
virtual bool Responder (event_t *ev);
virtual bool MenuEvent (int mkey, bool fromcontroller);
virtual void Ticker ();
virtual void Drawer ();
virtual bool DimAllowed ();
bool TranslateKeyboardEvents();
virtual void Close();
virtual bool MouseEvent(int type, int x, int y);
@ -277,8 +286,6 @@ public:
virtual bool CheckFocus(DMenuItemBase *fc) { return false; }
virtual void ReleaseFocus() {}
virtual DMenuItemBase *GetItem(FName name) { return nullptr; }
bool CallResponder(event_t *ev);
bool CallMenuEvent(int mkey, bool fromcontroller);
bool CallMouseEvent(int type, int x, int y);
@ -308,70 +315,15 @@ public:
FNameNoInit mAction;
bool mEnabled;
bool CheckCoordinate(int x, int y);
void Ticker();
void Drawer(bool selected);
bool Selectable();
bool Activate();
FName GetAction(int *pparam);
bool SetString(int i, const char *s);
bool GetString(int i, char *s, int len);
bool SetValue(int i, int value);
bool GetValue(int i, int *pvalue);
void Enable(bool on);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
bool CheckHotkey(int c);
int GetWidth();
int GetIndent();
int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected);
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
int GetY() { return mYpos; }
int GetX() { return mXpos; }
void SetX(int x) { mXpos = x; }
void DrawSelector(int xofs, int yofs, FTextureID tex);
};
//=============================================================================
//
// list menu class runs a menu described by a DListMenuDescriptor
//
//=============================================================================
class DListMenu : public DMenu
{
DECLARE_CLASS(DListMenu, DMenu)
HAS_OBJECT_POINTERS;
public:
DListMenuDescriptor *mDesc;
DMenuItemBase *mFocusControl;
DListMenu(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL);
virtual void Init(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL);
DMenuItemBase *GetItem(FName name);
bool Responder (event_t *ev);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker ();
void Drawer ();
void SetFocus(DMenuItemBase *fc)
{
mFocusControl = fc;
}
bool CheckFocus(DMenuItemBase *fc)
{
return mFocusControl == fc;
}
void ReleaseFocus()
{
mFocusControl = NULL;
}
};
//=============================================================================
//
//
@ -396,41 +348,10 @@ extern FOptionMap OptionValues;
//=============================================================================
//
// Input some text
//
//
//=============================================================================
class DTextEnterMenu : public DMenu
{
DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu)
public:
FString mEnterString;
unsigned int mEnterSize;
unsigned int mEnterPos;
int mSizeMode; // 1: size is length in chars. 2: also check string width
bool mInputGridOkay;
int InputGridX;
int InputGridY;
// [TP]
bool AllowColors;
// [TP] Added allowcolors
DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors = false);
void Drawer ();
bool MenuEvent (int mkey, bool fromcontroller);
bool Responder(event_t *ev);
bool MouseEvent(int type, int x, int y);
FString GetText();
};
struct event_t;
void M_EnableMenu (bool on) ;
bool M_Responder (event_t *ev);

View file

@ -147,7 +147,7 @@ static void DeinitMenus()
}
MenuDescriptors.Clear();
OptionValues.Clear();
DMenu::CurrentMenu = nullptr;
CurrentMenu = nullptr;
savegameManager.ClearSaveGames();
}
@ -290,7 +290,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
{
sc.MustGetString();
PClass *cls = PClass::FindClass(sc.String);
if (cls == nullptr || !cls->IsDescendantOf(RUNTIME_CLASS(DListMenu)))
if (cls == nullptr || !cls->IsDescendantOf("ListMenu"))
{
sc.ScriptError("Unknown menu class '%s'", sc.String);
}
@ -864,7 +864,6 @@ static void ParseOptionMenu(FScanner &sc)
ParseOptionMenuBody(sc, desc);
ReplaceMenu(sc, desc);
if (desc->mIndent == 0) desc->CalcIndent();
}
@ -1336,7 +1335,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
// Delete previous contents
for(unsigned i=0; i<ld->mItems.Size(); i++)
{
FName n = ld->mItems[i]->GetAction(nullptr);
FName n = ld->mItems[i]->mAction;
if (n == NAME_Startgame || n == NAME_StartgameConfirm)
{
ld->mItems.Resize(i);

View file

@ -1,396 +0,0 @@
/*
** menuinput.cpp
** The string input code
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "v_video.h"
#include "c_cvars.h"
#include "d_event.h"
#include "d_gui.h"
#include "v_font.h"
#include "v_palette.h"
#include "cmdlib.h"
// [TP] New #includes
#include "v_text.h"
IMPLEMENT_CLASS(DTextEnterMenu, true, false)
#define INPUTGRID_WIDTH 13
#define INPUTGRID_HEIGHT 5
// Heretic and Hexen do not, by default, come with glyphs for all of these
// characters. Oh well. Doom and Strife do.
static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] =
"ABCDEFGHIJKLM"
"NOPQRSTUVWXYZ"
"0123456789+-="
".,!?@'\":;[]()"
"<>^#$%&*/_ \b";
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
DEFINE_FIELD(DTextEnterMenu, mInputGridOkay)
//=============================================================================
//
//
//
//=============================================================================
// [TP] Added allowcolors
DTextEnterMenu::DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors)
: DMenu(parent)
{
mEnterString = textbuffer;
mEnterSize = maxlen < 0 ? UINT_MAX : unsigned(maxlen);
mSizeMode = sizemode;
mInputGridOkay = showgrid || m_showinputgrid;
if (mEnterString.IsNotEmpty())
{
InputGridX = INPUTGRID_WIDTH - 1;
InputGridY = INPUTGRID_HEIGHT - 1;
}
else
{
// If we are naming a new save, don't start the cursor on "end".
InputGridX = 0;
InputGridY = 0;
}
AllowColors = allowcolors; // [TP]
}
//=============================================================================
//
//
//
//=============================================================================
FString DTextEnterMenu::GetText()
{
return mEnterString;
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
// Save game and player name string input
if (ev->subtype == EV_GUI_Char)
{
mInputGridOkay = false;
if (mEnterString.Len() < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString.AppendFormat("%c", (char)ev->data1);
}
return true;
}
char ch = (char)ev->data1;
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
{
if (mEnterString.IsNotEmpty())
{
mEnterString.Truncate(mEnterString.Len() - 1);
}
}
else if (ev->subtype == EV_GUI_KeyDown)
{
if (ch == GK_ESCAPE)
{
DMenu *parent = mParentMenu;
Close();
parent->CallMenuEvent(MKEY_Abort, false);
return true;
}
else if (ch == '\r')
{
if (mEnterString.IsNotEmpty())
{
// [TP] If we allow color codes, colorize the string now.
if (AllowColors)
mEnterString = strbin1(mEnterString);
DMenu *parent = mParentMenu;
parent->CallMenuEvent(MKEY_Input, false);
Close();
return true;
}
}
}
if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat)
{
return true;
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::MouseEvent(int type, int x, int y)
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height;
const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y)
{
InputGridX = (x - screen_x) / cell_width;
InputGridY = (y - screen_y) / cell_height;
if (type == DMenu::MOUSE_Release)
{
if (CallMenuEvent(MKEY_Enter, true))
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
return true;
}
}
}
else
{
InputGridX = InputGridY = -1;
}
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
{
if (key == MKEY_Back)
{
mParentMenu->CallMenuEvent(MKEY_Abort, false);
return Super::MenuEvent(key, fromcontroller);
}
if (fromcontroller)
{
mInputGridOkay = true;
}
if (mInputGridOkay)
{
int ch;
if (InputGridX == -1 || InputGridY == -1)
{
InputGridX = InputGridY = 0;
}
switch (key)
{
case MKEY_Down:
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Up:
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Right:
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Left:
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Clear:
if (mEnterString.IsNotEmpty())
{
mEnterString.Truncate(mEnterString.Len() - 1);
}
return true;
case MKEY_Enter:
assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT);
if (mInputGridOkay)
{
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
if (ch == 0) // end
{
if (mEnterString.IsNotEmpty())
{
DMenu *parent = mParentMenu;
Close();
parent->CallMenuEvent(MKEY_Input, false);
return true;
}
}
else if (ch == '\b') // bs
{
if (mEnterString.IsNotEmpty())
{
mEnterString.Truncate(mEnterString.Len() - 1);
}
}
else if (mEnterString.Len() < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString += char(ch);
}
}
return true;
default:
break; // Keep GCC quiet
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DTextEnterMenu::Drawer ()
{
mParentMenu->CallDrawer();
if (mInputGridOkay)
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2;
// Darken the background behind the character grid.
// Unless we frame it with a border, I think it looks better to extend the
// background across the full width of the screen.
screen->Dim(0, 0.8f,
0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/,
screen->GetHeight() - INPUTGRID_HEIGHT * cell_height,
screen->GetWidth() /*13 * cell_width*/,
INPUTGRID_HEIGHT * cell_height);
if (InputGridX >= 0 && InputGridY >= 0)
{
// Highlight the background behind the selected character.
screen->Dim(MAKERGB(255,248,220), 0.6f,
InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2,
InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(),
cell_width, cell_height);
}
for (int y = 0; y < INPUTGRID_HEIGHT; ++y)
{
const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight();
for (int x = 0; x < INPUTGRID_WIDTH; ++x)
{
int width;
const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2;
const int ch = InputGridChars[y * INPUTGRID_WIDTH + x];
FTexture *pic = SmallFont->GetChar(ch, &width);
EColorRange color;
// The highlighted character is yellow; the rest are dark gray.
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
if (pic != NULL)
{
// Draw a normal character.
screen->DrawChar(SmallFont, color, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, ch, DTA_CleanNoMove, true, TAG_DONE);
}
else if (ch == ' ')
{
FRemapTable *remap = SmallFont->GetColorTranslation(color);
// Draw the space as a box outline. We also draw it 50% wider than it really is.
const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4;
const int x2 = x1 + width * 3 * CleanXfac / 2;
const int y1 = yy + top_padding;
const int y2 = y1 + SmallFont->GetHeight() * CleanYfac;
const int palentry = remap->Remap[remap->NumEntries * 2 / 3];
const uint32 palcolor = remap->Palette[remap->NumEntries * 2 / 3];
screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top
screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom
screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left
screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right
}
else if (ch == '\b' || ch == 0)
{
// Draw the backspace and end "characters".
const char *const str = ch == '\b' ? "BS" : "ED";
screen->DrawText(SmallFont, color,
xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2,
yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE);
}
}
}
}
Super::Drawer();
}
DEFINE_ACTION_FUNCTION(DTextEnterMenu, Open)
{
PARAM_PROLOGUE;
PARAM_OBJECT(parent, DMenu);
PARAM_STRING(text);
PARAM_INT(maxlen);
PARAM_INT(sizemode);
PARAM_BOOL(fromcontroller);
auto m = new DTextEnterMenu(parent, text.GetChars(), maxlen, sizemode, fromcontroller, false);
M_ActivateMenu(m);
ACTION_RETURN_OBJECT(m);
}
DEFINE_ACTION_FUNCTION(DTextEnterMenu, GetText)
{
PARAM_SELF_PROLOGUE(DTextEnterMenu);
ACTION_RETURN_STRING(self->GetText());
}

View file

@ -46,52 +46,15 @@
#include "c_dispatch.h"
#include "g_game.h"
EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd]
class DMessageBoxMenu : public DMenu
typedef void(*hfunc)();
DEFINE_ACTION_FUNCTION(DMessageBoxMenu, CallHandler)
{
DECLARE_CLASS(DMessageBoxMenu, DMenu)
FBrokenLines *mMessage;
int mMessageMode;
int messageSelection;
int mMouseLeft, mMouseRight, mMouseY;
FName mAction;
public:
DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None);
void OnDestroy() override;
void Init(DMenu *parent, const char *message, int messagemode, bool playsound = false);
void Drawer();
bool Responder(event_t *ev);
bool MenuEvent(int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void CloseSound();
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DMessageBoxMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action)
: DMenu(parent)
{
mAction = action;
messageSelection = 0;
mMouseLeft = 140;
mMouseY = INT_MIN;
int mr1 = 170 + SmallFont->StringWidth(GStrings["TXT_YES"]);
int mr2 = 170 + SmallFont->StringWidth(GStrings["TXT_NO"]);
mMouseRight = MAX(mr1, mr2);
Init(parent, message, messagemode, playsound);
PARAM_PROLOGUE;
PARAM_POINTERTYPE(Handler, hfunc);
Handler();
return 0;
}
//=============================================================================
@ -100,325 +63,15 @@ DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int message
//
//=============================================================================
void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound)
DMenu *CreateMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action = NAME_None, hfunc handler = nullptr)
{
mParentMenu = parent;
if (message != NULL)
{
if (*message == '$') message = GStrings(message+1);
mMessage = V_BreakLines(SmallFont, 300, message);
}
else mMessage = NULL;
mMessageMode = messagemode;
if (playsound)
{
S_StopSound (CHAN_VOICE);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE);
}
}
auto c = PClass::FindClass("MessageBoxMenu");
auto p = c->CreateNew();
VMValue params[] = { p, parent, FString(message), messagemode, playsound, action.GetIndex(), reinterpret_cast<void*>(handler) };
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::OnDestroy()
{
if (mMessage != NULL) V_FreeBrokenLines(mMessage);
mMessage = NULL;
Super::OnDestroy();
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::CloseSound()
{
S_Sound (CHAN_VOICE | CHAN_UI,
DMenu::CurrentMenu != NULL? "menu/backup" : "menu/dismiss", snd_menuvolume, ATTN_NONE);
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::HandleResult(bool res)
{
if (mParentMenu != NULL)
{
if (mMessageMode == 0)
{
if (mAction == NAME_None)
{
mParentMenu->CallMenuEvent(res? MKEY_MBYes : MKEY_MBNo, false);
Close();
}
else
{
Close();
if (res) M_SetMenu(mAction, -1);
}
CloseSound();
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::Drawer ()
{
int i, y;
PalEntry fade = 0;
int fontheight = SmallFont->GetHeight();
//V_SetBorderNeedRefresh();
//ST_SetNeedRefresh();
y = 100;
if (mMessage != NULL)
{
for (i = 0; mMessage[i].Width >= 0; i++)
y -= SmallFont->GetHeight () / 2;
for (i = 0; mMessage[i].Width >= 0; i++)
{
screen->DrawText (SmallFont, CR_UNTRANSLATED, 160 - mMessage[i].Width/2, y, mMessage[i].Text,
DTA_Clean, true, TAG_DONE);
y += fontheight;
}
}
if (mMessageMode == 0)
{
y += fontheight;
mMouseY = y;
screen->DrawText(SmallFont,
messageSelection == 0? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE);
screen->DrawText(SmallFont,
messageSelection == 1? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE);
if (messageSelection >= 0)
{
if ((DMenu::MenuTime%8) < 6)
{
screen->DrawText(ConFont, OptionSettings.mFontColorSelection,
(150 - 160) * CleanXfac + screen->GetWidth() / 2,
(y + (fontheight + 1) * messageSelection - 100 + fontheight/2 - 5) * CleanYfac + screen->GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown)
{
if (mMessageMode == 0)
{
int ch = tolower(ev->data1);
if (ch == 'n' || ch == ' ')
{
HandleResult(false);
return true;
}
else if (ch == 'y')
{
HandleResult(true);
return true;
}
}
else
{
Close();
return true;
}
return false;
}
else if (ev->type == EV_KeyDown)
{
Close();
return true;
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::MenuEvent(int mkey, bool fromcontroller)
{
if (mMessageMode == 0)
{
if (mkey == MKEY_Up || mkey == MKEY_Down)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
messageSelection = !messageSelection;
return true;
}
else if (mkey == MKEY_Enter)
{
// 0 is yes, 1 is no
HandleResult(!messageSelection);
return true;
}
else if (mkey == MKEY_Back)
{
HandleResult(false);
return true;
}
return false;
}
else
{
Close();
CloseSound();
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::MouseEvent(int type, int x, int y)
{
if (mMessageMode == 1)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}
else
{
int sel = -1;
int fh = SmallFont->GetHeight() + 1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + 2 * fh)
{
sel = y >= mMouseY + fh;
}
if (sel != -1 && sel != messageSelection)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
messageSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuitMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuitMenu, DMessageBoxMenu)
public:
DQuitMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuitMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DQuitMenu::DQuitMenu(bool playsound)
{
int messageindex = gametic % gameinfo.quitmessages.Size();
FString EndString;
const char *msg = gameinfo.quitmessages[messageindex];
if (msg[0] == '$')
{
if (msg[1] == '*')
{
EndString = GStrings(msg+2);
}
else
{
EndString.Format("%s\n\n%s", GStrings(msg+1), GStrings("DOSY"));
}
}
else EndString = gameinfo.quitmessages[messageindex];
Init(NULL, EndString, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuitMenu::HandleResult(bool res)
{
if (res)
{
if (!netgame)
{
if (gameinfo.quitSound.IsNotEmpty())
{
S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE);
I_WaitVBL (105);
}
}
ST_Endoom();
}
else
{
Close();
CloseSound();
}
auto f = dyn_cast<PFunction>(c->Symbols.FindSymbol("Init", false));
GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0);
return (DMenu*)p;
}
//=============================================================================
@ -430,70 +83,42 @@ void DQuitMenu::HandleResult(bool res)
CCMD (menu_quit)
{ // F10
M_StartControlPanel (true);
DMenu *newmenu = new DQuitMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
int messageindex = gametic % gameinfo.quitmessages.Size();
FString EndString;
const char *msg = gameinfo.quitmessages[messageindex];
if (msg[0] == '$')
{
if (msg[1] == '*')
{
EndString = GStrings(msg + 2);
}
else
{
EndString.Format("%s\n\n%s", GStrings(msg + 1), GStrings("DOSY"));
}
}
else EndString = gameinfo.quitmessages[messageindex];
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, EndString, 0, false, NAME_None, []()
{
if (!netgame)
{
if (gameinfo.quitSound.IsNotEmpty())
{
S_Sound(CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE);
I_WaitVBL(105);
}
}
ST_Endoom();
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DEndGameMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DEndGameMenu, DMessageBoxMenu)
public:
DEndGameMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DEndGameMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DEndGameMenu::DEndGameMenu(bool playsound)
{
Init(NULL, GStrings(netgame ? "NETEND" : "ENDGAME"), 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DEndGameMenu::HandleResult(bool res)
{
if (res)
{
M_ClearMenus ();
if (!netgame)
{
D_StartTitle ();
}
}
else
{
Close();
CloseSound();
}
}
//=============================================================================
//
//
@ -510,67 +135,18 @@ CCMD (menu_endgame)
//M_StartControlPanel (true);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
DMenu *newmenu = new DEndGameMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuickSaveMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuickSaveMenu, DMessageBoxMenu)
public:
DQuickSaveMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuickSaveMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DQuickSaveMenu::DQuickSaveMenu(bool playsound)
{
FString tempstring;
tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->Title);
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickSaveMenu::HandleResult(bool res)
{
if (res)
FString tempstring = GStrings(netgame ? "NETEND" : "ENDGAME");
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
G_SaveGame (savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}
else
{
Close();
CloseSound();
}
if (!netgame)
{
D_StartTitle();
}
});
M_ActivateMenu(newmenu);
}
//=============================================================================
@ -601,72 +177,23 @@ CCMD (quicksave)
// [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation)
{
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title);
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars());
return;
}
S_Sound(CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
DMenu *newmenu = new DQuickSaveMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuickLoadMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuickLoadMenu, DMessageBoxMenu)
public:
DQuickLoadMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuickLoadMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DQuickLoadMenu::DQuickLoadMenu(bool playsound)
{
FString tempstring;
tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars());
tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->Title);
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickLoadMenu::HandleResult(bool res)
{
if (res)
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
G_LoadGame (savegameManager.quickSaveSlot->Filename.GetChars());
S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars());
S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}
else
{
Close();
CloseSound();
}
});
M_ActivateMenu(newmenu);
}
//=============================================================================
@ -699,10 +226,17 @@ CCMD (quickload)
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars());
return;
}
FString tempstring;
tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars());
M_StartControlPanel(true);
DMenu *newmenu = new DQuickLoadMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars());
S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
});
M_ActivateMenu(newmenu);
}
@ -714,13 +248,13 @@ CCMD (quickload)
void M_StartMessage(const char *message, int messagemode, FName action)
{
if (DMenu::CurrentMenu == NULL)
if (CurrentMenu == NULL)
{
// only play a sound if no menu was active before
M_StartControlPanel(menuactive == MENU_Off);
}
DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = DMenu::CurrentMenu;
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = CurrentMenu;
M_ActivateMenu(newmenu);
}
@ -732,4 +266,4 @@ DEFINE_ACTION_FUNCTION(DMenu, StartMessage)
PARAM_NAME_DEF(action);
M_StartMessage(msg, mode, action);
return 0;
}
}

View file

@ -49,32 +49,6 @@
#include "menu/menu.h"
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenuDescriptor::CalcIndent()
{
// calculate the menu indent
int widest = 0, thiswidth;
for (unsigned i = 0; i < mItems.Size(); i++)
{
thiswidth = mItems[i]->GetIndent();
if (thiswidth > widest) widest = thiswidth;
}
mIndent = widest + 4;
}
DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, CalcIndent)
{
PARAM_SELF_PROLOGUE(DOptionMenuDescriptor);
self->CalcIndent();
return 0;
}
//=============================================================================
//
//
@ -85,7 +59,7 @@ DMenuItemBase *DOptionMenuDescriptor::GetItem(FName name)
{
for(unsigned i=0;i<mItems.Size(); i++)
{
FName nm = mItems[i]->GetAction(NULL);
FName nm = mItems[i]->mAction;
if (nm == name) return mItems[i];
}
return NULL;

View file

@ -48,12 +48,10 @@
#include "r_data/r_translate.h"
#include "v_text.h"
EXTERN_CVAR (String, playerclass)
EXTERN_CVAR (String, name)
EXTERN_CVAR (Int, team)
EXTERN_CVAR (Float, autoaim)
EXTERN_CVAR(Int, team)
EXTERN_CVAR(Float, autoaim)
EXTERN_CVAR(Bool, neverswitchonpickup)
EXTERN_CVAR (Bool, cl_run)
EXTERN_CVAR(Bool, cl_run)
//=============================================================================
//
@ -61,365 +59,42 @@ EXTERN_CVAR (Bool, cl_run)
//
//=============================================================================
class DPlayerMenu : public DListMenu
DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorChanged)
{
DECLARE_CLASS(DPlayerMenu, DListMenu)
int PlayerClassIndex;
FPlayerClass *PlayerClass;
TArray<int> PlayerColorSets;
TArray<int> PlayerSkins;
int mRotation;
void PickPlayerClass ();
void UpdateColorsets();
void UpdateSkins();
void UpdateTranslation();
void SendNewColor (int red, int green, int blue);
void PlayerNameChanged(DMenuItemBase *li);
void ColorSetChanged (DMenuItemBase *li);
void ClassChanged (DMenuItemBase *li);
void AutoaimChanged (DMenuItemBase *li);
void SkinChanged (DMenuItemBase *li);
public:
DPlayerMenu() {}
void Init(DMenu *parent, DListMenuDescriptor *desc);
bool Responder (event_t *ev);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker ();
void Drawer ();
};
IMPLEMENT_CLASS(DPlayerMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
enum EPDFlags
{
ListMenuItemPlayerDisplay_PDF_ROTATION = 0x10001,
ListMenuItemPlayerDisplay_PDF_SKIN = 0x10002,
ListMenuItemPlayerDisplay_PDF_CLASS = 0x10003,
ListMenuItemPlayerDisplay_PDF_MODE = 0x10004,
ListMenuItemPlayerDisplay_PDF_TRANSLATE = 0x10005,
};
void DPlayerMenu::Init(DMenu *parent, DListMenuDescriptor *desc)
{
DMenuItemBase *li;
Super::Init(parent, desc);
PickPlayerClass();
mRotation = 0;
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(r);
PARAM_INT(g);
PARAM_INT(b);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, 0);
li->SetValue(ListMenuItemPlayerDisplay_PDF_MODE, 1);
li->SetValue(ListMenuItemPlayerDisplay_PDF_TRANSLATE, 1);
li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum());
if (PlayerClass != NULL && !(GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN) &&
players[consoleplayer].userinfo.GetPlayerClassNum() != -1)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, players[consoleplayer].userinfo.GetSkin());
}
char command[24];
players[consoleplayer].userinfo.ColorChanged(MAKERGB(r, g, b));
mysnprintf(command, countof(command), "color \"%02x %02x %02x\"", r, g, b);
C_DoCommand(command);
}
li = GetItem(NAME_Playerbox);
if (li != NULL)
{
li->SetString(0, name);
}
li = GetItem(NAME_Team);
if (li != NULL)
{
li->SetString(0, "None");
for(unsigned i=0;i<Teams.Size(); i++)
{
li->SetString(i+1, Teams[i].GetName());
}
li->SetValue(0, team == TEAM_NONE? 0 : team + 1);
}
int mycolorset = players[consoleplayer].userinfo.GetColorSet();
int color = players[consoleplayer].userinfo.GetColor();
UpdateColorsets();
li = GetItem(NAME_Red);
if (li != NULL)
{
li->Enable(mycolorset == -1);
li->SetValue(0, RPART(color));
}
li = GetItem(NAME_Green);
if (li != NULL)
{
li->Enable(mycolorset == -1);
li->SetValue(0, GPART(color));
}
li = GetItem(NAME_Blue);
if (li != NULL)
{
li->Enable(mycolorset == -1);
li->SetValue(0, BPART(color));
}
li = GetItem(NAME_Class);
if (li != NULL)
{
if (PlayerClasses.Size() == 1)
{
li->SetString(0, GetPrintableDisplayName(PlayerClasses[0].Type));
li->SetValue(0, 0);
}
else
{
// [XA] Remove the "Random" option if the relevant gameinfo flag is set.
if(!gameinfo.norandomplayerclass)
li->SetString(0, "Random");
for(unsigned i=0; i< PlayerClasses.Size(); i++)
{
const char *cls = GetPrintableDisplayName(PlayerClasses[i].Type);
li->SetString(gameinfo.norandomplayerclass ? i : i+1, cls);
}
int pclass = players[consoleplayer].userinfo.GetPlayerClassNum();
li->SetValue(0, gameinfo.norandomplayerclass && pclass >= 0 ? pclass : pclass + 1);
}
}
UpdateSkins();
li = GetItem(NAME_Gender);
if (li != NULL)
{
li->SetValue(0, players[consoleplayer].userinfo.GetGender());
}
li = GetItem(NAME_Autoaim);
if (li != NULL)
{
li->SetValue(0, (int)autoaim);
}
li = GetItem(NAME_Switch);
if (li != NULL)
{
li->SetValue(0, neverswitchonpickup);
}
li = GetItem(NAME_AlwaysRun);
if (li != NULL)
{
li->SetValue(0, cl_run);
}
if (mDesc->mSelectedItem < 0) mDesc->mSelectedItem = 1;
return 0;
}
//=============================================================================
//
//
// access to the player config is done natively, so that broader access
// functions do not need to be exported.
//
//=============================================================================
bool DPlayerMenu::Responder (event_t *ev)
DEFINE_ACTION_FUNCTION(DPlayerMenu, PlayerNameChanged)
{
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_Char && ev->data1 == ' ')
PARAM_SELF_PROLOGUE(DMenu);
PARAM_STRING(s);
const char *pp = s;
FString command("name \"");
if (self == CurrentMenu)
{
// turn the player sprite around
mRotation = 8 - mRotation;
DMenuItemBase *li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, mRotation);
}
return true;
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::UpdateTranslation()
{
int PlayerColor = players[consoleplayer].userinfo.GetColor();
int PlayerSkin = players[consoleplayer].userinfo.GetSkin();
int PlayerColorset = players[consoleplayer].userinfo.GetColorSet();
if (PlayerClass != NULL)
{
PlayerSkin = R_FindSkin (skins[PlayerSkin].name, int(PlayerClass - &PlayerClasses[0]));
R_GetPlayerTranslation(PlayerColor, GetColorSet(PlayerClass->Type, PlayerColorset),
&skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::PickPlayerClass()
{
/*
// What's the point of this? Aren't we supposed to edit the
// userinfo?
if (players[consoleplayer].mo != NULL)
{
PlayerClassIndex = players[consoleplayer].CurrentPlayerClass;
}
else
*/
{
int pclass = 0;
// [GRB] Pick a class from player class list
if (PlayerClasses.Size () > 1)
{
pclass = players[consoleplayer].userinfo.GetPlayerClassNum();
if (pclass < 0)
{
pclass = (MenuTime>>7) % PlayerClasses.Size ();
}
}
PlayerClassIndex = pclass;
}
PlayerClass = &PlayerClasses[PlayerClassIndex];
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::SendNewColor (int red, int green, int blue)
{
char command[24];
players[consoleplayer].userinfo.ColorChanged(MAKERGB(red,green,blue));
mysnprintf (command, countof(command), "color \"%02x %02x %02x\"", red, green, blue);
C_DoCommand (command);
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::UpdateColorsets()
{
DMenuItemBase *li = GetItem(NAME_Color);
if (li != NULL)
{
int sel = 0;
EnumColorSets(PlayerClass->Type, &PlayerColorSets);
li->SetString(0, "Custom");
for(unsigned i=0;i<PlayerColorSets.Size(); i++)
{
FPlayerColorSet *colorset = GetColorSet(PlayerClass->Type, PlayerColorSets[i]);
li->SetString(i+1, colorset->Name);
}
int mycolorset = players[consoleplayer].userinfo.GetColorSet();
if (mycolorset != -1)
{
for(unsigned i=0;i<PlayerColorSets.Size(); i++)
{
if (PlayerColorSets[i] == mycolorset)
{
sel = i+1;
}
}
}
li->SetValue(0, sel);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::UpdateSkins()
{
int sel = 0;
int skin;
DMenuItemBase *li = GetItem(NAME_Skin);
if (li != NULL)
{
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN ||
players[consoleplayer].userinfo.GetPlayerClassNum() == -1)
{
li->SetString(0, "Base");
li->SetValue(0, 0);
skin = 0;
}
else
{
PlayerSkins.Clear();
for(int i=0;i<(int)numskins; i++)
{
if (PlayerClass->CheckSkin(i))
{
int j = PlayerSkins.Push(i);
li->SetString(j, skins[i].name);
if (players[consoleplayer].userinfo.GetSkin() == i)
{
sel = j;
}
}
}
li->SetValue(0, sel);
skin = PlayerSkins[sel];
}
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, skin);
}
}
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::PlayerNameChanged(DMenuItemBase *li)
{
char pp[MAXPLAYERNAME+1];
const char *p;
if (li->GetString(0, pp, MAXPLAYERNAME))
{
FString command("name \"");
// Escape any backslashes or quotation marks before sending the name to the console.
for (p = pp; *p != '\0'; ++p)
for (auto p = pp; *p != '\0'; ++p)
{
if (*p == '"' || *p == '\\')
{
@ -428,41 +103,9 @@ void DPlayerMenu::PlayerNameChanged(DMenuItemBase *li)
command << *p;
}
command << '"';
C_DoCommand (command);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::ColorSetChanged (DMenuItemBase *li)
{
int sel;
if (li->GetValue(0, &sel))
{
int mycolorset = -1;
if (sel > 0) mycolorset = PlayerColorSets[sel-1];
DMenuItemBase *red = GetItem(NAME_Red);
DMenuItemBase *green = GetItem(NAME_Green);
DMenuItemBase *blue = GetItem(NAME_Blue);
// disable the sliders if a valid colorset is selected
if (red != NULL) red->Enable(mycolorset == -1);
if (green != NULL) green->Enable(mycolorset == -1);
if (blue != NULL) blue->Enable(mycolorset == -1);
char command[24];
players[consoleplayer].userinfo.ColorSetChanged(mycolorset);
mysnprintf(command, countof(command), "colorset %d", mycolorset);
C_DoCommand(command);
UpdateTranslation();
}
return 0;
}
//=============================================================================
@ -471,32 +114,18 @@ void DPlayerMenu::ColorSetChanged (DMenuItemBase *li)
//
//=============================================================================
void DPlayerMenu::ClassChanged (DMenuItemBase *li)
DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorSetChanged)
{
if (PlayerClasses.Size () == 1)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(sel);
if (self == CurrentMenu)
{
return;
}
int sel;
if (li->GetValue(0, &sel))
{
players[consoleplayer].userinfo.PlayerClassNumChanged(gameinfo.norandomplayerclass ? sel : sel-1);
PickPlayerClass();
cvar_set ("playerclass", sel == 0 && !gameinfo.norandomplayerclass ? "Random" : GetPrintableDisplayName(PlayerClass->Type).GetChars());
UpdateSkins();
UpdateColorsets();
UpdateTranslation();
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum());
}
players[consoleplayer].userinfo.ColorSetChanged(sel);
char command[24];
mysnprintf(command, countof(command), "colorset %d", sel);
C_DoCommand(command);
}
return 0;
}
//=============================================================================
@ -505,29 +134,36 @@ void DPlayerMenu::ClassChanged (DMenuItemBase *li)
//
//=============================================================================
void DPlayerMenu::SkinChanged (DMenuItemBase *li)
DEFINE_ACTION_FUNCTION(DPlayerMenu, ClassChanged)
{
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN ||
players[consoleplayer].userinfo.GetPlayerClassNum() == -1)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(sel);
PARAM_POINTER(cls, FPlayerClass);
if (self == CurrentMenu)
{
return;
players[consoleplayer].userinfo.PlayerClassNumChanged(gameinfo.norandomplayerclass ? sel : sel - 1);
cvar_set("playerclass", sel == 0 && !gameinfo.norandomplayerclass ? "Random" : GetPrintableDisplayName(cls->Type).GetChars());
}
return 0;
}
int sel;
if (li->GetValue(0, &sel))
//=============================================================================
//
//
//
//=============================================================================
DEFINE_ACTION_FUNCTION(DPlayerMenu, SkinChanged)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(sel);
if (self == CurrentMenu)
{
sel = PlayerSkins[sel];
players[consoleplayer].userinfo.SkinNumChanged(sel);
UpdateTranslation();
cvar_set ("skin", skins[sel].name);
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, sel);
}
cvar_set("skin", Skins[sel].Name);
}
return 0;
}
//=============================================================================
@ -536,14 +172,16 @@ void DPlayerMenu::SkinChanged (DMenuItemBase *li)
//
//=============================================================================
void DPlayerMenu::AutoaimChanged (DMenuItemBase *li)
DEFINE_ACTION_FUNCTION(DPlayerMenu, AutoaimChanged)
{
int sel;
if (li->GetValue(0, &sel))
PARAM_SELF_PROLOGUE(DMenu);
PARAM_FLOAT(val);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
autoaim = (float)sel;
autoaim = float(val);
}
return 0;
}
//=============================================================================
@ -552,145 +190,34 @@ void DPlayerMenu::AutoaimChanged (DMenuItemBase *li)
//
//=============================================================================
bool DPlayerMenu::MenuEvent (int mkey, bool fromcontroller)
DEFINE_ACTION_FUNCTION(DPlayerMenu, TeamChanged)
{
int v;
if (mDesc->mSelectedItem >= 0)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(val);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
DMenuItemBase *li = mDesc->mItems[mDesc->mSelectedItem];
if (li->MenuEvent(mkey, fromcontroller))
{
FName current = li->GetAction(NULL);
switch(current)
{
// item specific handling comes here
case NAME_Playerbox:
if (mkey == MKEY_Input)
{
PlayerNameChanged(li);
}
break;
case NAME_Team:
if (li->GetValue(0, &v))
{
team = v==0? TEAM_NONE : v-1;
}
break;
case NAME_Color:
ColorSetChanged(li);
break;
case NAME_Red:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (v, GPART(color), BPART(color));
}
break;
case NAME_Green:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), v, BPART(color));
}
break;
case NAME_Blue:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), GPART(color), v);
}
break;
case NAME_Class:
ClassChanged(li);
break;
case NAME_Skin:
SkinChanged(li);
break;
case NAME_Gender:
if (li->GetValue(0, &v))
{
cvar_set ("gender", v==0? "male" : v==1? "female" : "other");
}
break;
case NAME_Autoaim:
AutoaimChanged(li);
break;
case NAME_Switch:
if (li->GetValue(0, &v))
{
neverswitchonpickup = !!v;
}
break;
case NAME_AlwaysRun:
if (li->GetValue(0, &v))
{
cl_run = !!v;
}
break;
default:
break;
}
return true;
}
team = val == 0 ? TEAM_NONE : val - 1;
}
return Super::MenuEvent(mkey, fromcontroller);
return 0;
}
//=============================================================================
//
//
//
//=============================================================================
bool DPlayerMenu::MouseEvent(int type, int x, int y)
DEFINE_ACTION_FUNCTION(DPlayerMenu, GenderChanged)
{
int v;
DMenuItemBase *li = mFocusControl;
bool res = Super::MouseEvent(type, x, y);
if (li == NULL) li = mFocusControl;
if (li != NULL)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
// Check if the colors have changed
FName current = li->GetAction(NULL);
switch(current)
{
case NAME_Red:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (v, GPART(color), BPART(color));
}
break;
case NAME_Green:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), v, BPART(color));
}
break;
case NAME_Blue:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), GPART(color), v);
}
break;
case NAME_Autoaim:
AutoaimChanged(li);
break;
}
cvar_set("gender", v == 0 ? "male" : v == 1 ? "female" : "other");
}
return res;
return 0;
}
//=============================================================================
@ -699,10 +226,16 @@ bool DPlayerMenu::MouseEvent(int type, int x, int y)
//
//=============================================================================
void DPlayerMenu::Ticker ()
DEFINE_ACTION_FUNCTION(DPlayerMenu, SwitchOnPickupChanged)
{
Super::Ticker();
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
neverswitchonpickup = !!v;
}
return 0;
}
//=============================================================================
@ -711,20 +244,14 @@ void DPlayerMenu::Ticker ()
//
//=============================================================================
void DPlayerMenu::Drawer ()
DEFINE_ACTION_FUNCTION(DPlayerMenu, AlwaysRunChanged)
{
Super::Drawer();
const char *str = "PRESS " TEXTCOLOR_WHITE "SPACE";
screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 -
SmallFont->StringWidth (str)/2,
50 + 48 + 70, str,
DTA_Clean, true, TAG_DONE);
str = mRotation ? "TO SEE FRONT" : "TO SEE BACK";
screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 -
SmallFont->StringWidth (str)/2,
50 + 48 + 70 + SmallFont->GetHeight (), str,
DTA_Clean, true, TAG_DONE);
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
cl_run = !!v;
}
return 0;
}

View file

@ -1,149 +0,0 @@
/*
** readthis.cpp
** Help screens
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "v_video.h"
#include "g_level.h"
#include "gi.h"
#include "g_levellocals.h"
#include "textures/textures.h"
class DReadThisMenu : public DMenu
{
DECLARE_CLASS(DReadThisMenu, DMenu)
int mScreen;
int mInfoTic;
public:
DReadThisMenu(DMenu *parent = NULL);
void Drawer();
bool MenuEvent(int mkey, bool fromcontroller);
bool DimAllowed () { return false; }
bool MouseEvent(int type, int x, int y);
};
IMPLEMENT_CLASS(DReadThisMenu, false, false)
//=============================================================================
//
// Read This Menus
//
//=============================================================================
DReadThisMenu::DReadThisMenu(DMenu *parent)
: DMenu(parent)
{
mScreen = 1;
mInfoTic = gametic;
}
//=============================================================================
//
//
//
//=============================================================================
void DReadThisMenu::Drawer()
{
FTexture *tex = NULL, *prevpic = NULL;
double alpha;
// Did the mapper choose a custom help page via MAPINFO?
if ((level.info != NULL) && level.info->F1Pic.Len() != 0)
{
tex = TexMan.FindTexture(level.info->F1Pic);
mScreen = 1;
}
if (tex == NULL)
{
tex = TexMan[gameinfo.infoPages[mScreen-1].GetChars()];
}
if (mScreen > 1)
{
prevpic = TexMan[gameinfo.infoPages[mScreen-2].GetChars()];
}
screen->Dim(0, 1.0, 0,0, SCREENWIDTH, SCREENHEIGHT);
alpha = MIN((gametic - mInfoTic) * (3. / TICRATE), 1.);
if (alpha < 1. && prevpic != NULL)
{
screen->DrawTexture (prevpic, 0, 0, DTA_Fullscreen, true, TAG_DONE);
}
screen->DrawTexture (tex, 0, 0, DTA_Fullscreen, true, DTA_Alpha, alpha, TAG_DONE);
}
//=============================================================================
//
//
//
//=============================================================================
bool DReadThisMenu::MenuEvent(int mkey, bool fromcontroller)
{
if (mkey == MKEY_Enter)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
mScreen++;
mInfoTic = gametic;
if ((level.info != NULL && level.info->F1Pic.Len() != 0) || mScreen > int(gameinfo.infoPages.Size()))
{
Close();
}
return true;
}
else return Super::MenuEvent(mkey, fromcontroller);
}
//=============================================================================
//
//
//
//=============================================================================
bool DReadThisMenu::MouseEvent(int type, int x, int y)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}

View file

@ -653,6 +653,7 @@ xx(Link)
xx(Goodbye)
xx(Require)
xx(Exclude)
xx(Userstring)
// Special menus
xx(Mainmenu)

View file

@ -3919,7 +3919,7 @@ DEFINE_ACTION_FUNCTION(AActor, PlayerSkinCheck)
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_BOOL(self->player != NULL &&
skins[self->player->userinfo.GetSkin()].othergame);
Skins[self->player->userinfo.GetSkin()].othergame);
}
// [KS] *** Start of my modifications ***

View file

@ -61,6 +61,7 @@
#include "p_local.h"
#include "menu/menu.h"
#include "g_levellocals.h"
#include "virtual.h"
// The conversations as they exist inside a SCRIPTxx lump.
struct Response
@ -112,7 +113,7 @@ static FDialogueMap ClassRoots;
static int ConversationMenuY;
static int ConversationPauseTic;
static bool ShowGold;
static int StaticLastReply;
static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type);
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType);
@ -124,9 +125,6 @@ static void TerminalResponse (const char *str);
static FStrifeDialogueNode *PrevNode;
#define NUM_RANDOM_LINES 10
#define NUM_RANDOM_GOODBYES 3
//============================================================================
//
// GetStrifeType
@ -347,11 +345,11 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
}
// Convert the rest of the data to our own internal format.
node->Dialogue = ncopystring (speech.Dialogue);
node->Dialogue = speech.Dialogue;
// The speaker's portrait, if any.
speech.Dialogue[0] = 0; //speech.Backdrop[8] = 0;
node->Backdrop = TexMan.CheckForTexture (speech.Backdrop, FTexture::TEX_MiscPatch);
node->Backdrop = speech.Backdrop;
// The speaker's voice for this node, if any.
speech.Backdrop[0] = 0; //speech.Sound[8] = 0;
@ -360,7 +358,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
// The speaker's name, if any.
speech.Sound[0] = 0; //speech.Name[16] = 0;
node->SpeakerName = ncopystring(speech.Name);
node->SpeakerName = speech.Name;
// The item the speaker should drop when killed.
node->DropType = dyn_cast<PClassActor>(GetStrifeType(speech.DropType));
@ -422,10 +420,10 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
}
// Convert the rest of the data to our own internal format.
node->Dialogue = ncopystring (speech.Dialogue);
node->Dialogue = speech.Dialogue;
// The Teaser version doesn't have portraits.
node->Backdrop.SetInvalid();
node->Backdrop = "";
// The speaker's voice for this node, if any.
if (speech.VoiceNumber != 0)
@ -440,7 +438,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
// The speaker's name, if any.
speech.Dialogue[0] = 0; //speech.Name[16] = 0;
node->SpeakerName = ncopystring (speech.Name);
node->SpeakerName = speech.Name;
// The item the speaker should drop when killed.
node->DropType = dyn_cast<PClassActor>(GetStrifeType (speech.DropType));
@ -505,7 +503,7 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
// The message to record in the log for this reply.
reply->LogNumber = rsp->Log;
reply->LogString = NULL;
reply->LogString = "";
// The item to receive when this reply is used.
reply->GiveType = dyn_cast<PClassActor>(GetStrifeType (rsp->GiveType));
@ -520,31 +518,32 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
reply->ItemCheck[k].Item = inv;
reply->ItemCheck[k].Amount = rsp->Count[k];
}
reply->PrintAmount = reply->ItemCheck[0].Amount;
reply->ItemCheckRequire.Clear();
reply->ItemCheckExclude.Clear();
// If the first item check has a positive amount required, then
// add that to the reply string. Otherwise, use the reply as-is.
reply->Reply = copystring (rsp->Reply);
reply->Reply = rsp->Reply;
reply->NeedsGold = (rsp->Count[0] > 0);
// QuickYes messages are shown when you meet the item checks.
// QuickNo messages are shown when you don't.
if (rsp->Yes[0] == '_' && rsp->Yes[1] == 0)
{
reply->QuickYes = NULL;
reply->QuickYes = "";
}
else
{
reply->QuickYes = ncopystring (rsp->Yes);
reply->QuickYes = rsp->Yes;
}
if (reply->ItemCheck[0].Item != 0)
{
reply->QuickNo = ncopystring (rsp->No);
reply->QuickNo = rsp->No;
}
else
{
reply->QuickNo = NULL;
reply->QuickNo = "";
}
reply->Next = *replyptr;
*replyptr = reply;
@ -560,9 +559,6 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
FStrifeDialogueNode::~FStrifeDialogueNode ()
{
if (SpeakerName != NULL) delete[] SpeakerName;
if (Dialogue != NULL) delete[] Dialogue;
if (Goodbye != nullptr) delete[] Goodbye;
FStrifeDialogueReply *tokill = Children;
while (tokill != NULL)
{
@ -580,9 +576,6 @@ FStrifeDialogueNode::~FStrifeDialogueNode ()
FStrifeDialogueReply::~FStrifeDialogueReply ()
{
if (Reply != NULL) delete[] Reply;
if (QuickYes != NULL) delete[] QuickYes;
if (QuickNo != NULL) delete[] QuickNo;
}
//============================================================================
@ -672,7 +665,7 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
{
if (reply->Reply == nullptr)
if (reply->Reply.IsEmpty())
return true;
int i;
@ -694,428 +687,60 @@ static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
return false;
}
//============================================================================
//
// The conversation menu
//
//============================================================================
class DConversationMenu : public DMenu
DEFINE_ACTION_FUNCTION(FStrifeDialogueReply, ShouldSkipReply)
{
DECLARE_CLASS(DConversationMenu, DMenu)
PARAM_SELF_STRUCT_PROLOGUE(FStrifeDialogueReply);
PARAM_POINTER(player, player_t);
ACTION_RETURN_BOOL(ShouldSkipReply(self, player));
}
FString mSpeaker;
FBrokenLines *mDialogueLines;
TArray<FString> mResponseLines;
TArray<unsigned int> mResponses;
bool mShowGold;
FStrifeDialogueNode *mCurNode;
int mYpos;
player_t *mPlayer;
DEFINE_ACTION_FUNCTION(DConversationMenu, SendConversationReply)
{
PARAM_PROLOGUE;
PARAM_INT(node);
PARAM_INT(reply);
switch (node)
{
case -1:
Net_WriteByte(DEM_CONVNULL);
break;
case -2:
Net_WriteByte(DEM_CONVCLOSE);
break;
default:
Net_WriteByte(DEM_CONVREPLY);
Net_WriteWord(node);
Net_WriteByte(reply);
break;
}
StaticLastReply = reply;
return 0;
}
// Needed for the conversion process.
class DBrokenLines : public DObject
{
DECLARE_ABSTRACT_CLASS(DBrokenLines, DObject)
public:
static int mSelection;
FBrokenLines *mBroken;
unsigned int mCount;
//=============================================================================
//
//
//
//=============================================================================
DConversationMenu(FStrifeDialogueNode *CurNode, player_t *player)
DBrokenLines(FBrokenLines *broken, unsigned int count)
{
mCurNode = CurNode;
mPlayer = player;
mDialogueLines = NULL;
mShowGold = false;
// Format the speaker's message.
const char * toSay = CurNode->Dialogue;
if (strncmp (toSay, "RANDOM_", 7) == 0)
{
FString dlgtext;
dlgtext.Format("TXT_%s_%02d", toSay, 1+(pr_randomspeech() % NUM_RANDOM_LINES));
toSay = GStrings[dlgtext];
if (toSay == NULL)
{
toSay = GStrings["TXT_GOAWAY"]; // Ok, it's lame - but it doesn't look like an error to the player. ;)
}
}
else
{
// handle string table replacement
if (toSay[0] == '$')
{
toSay = GStrings(toSay + 1);
}
}
if (toSay == NULL)
{
toSay = ".";
}
mDialogueLines = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac - 24*2, toSay);
FStrifeDialogueReply *reply;
int i,j;
for (reply = CurNode->Children, i = 1; reply != NULL; reply = reply->Next)
{
if (ShouldSkipReply(reply, mPlayer))
{
continue;
}
mShowGold |= reply->NeedsGold;
const char *ReplyText = reply->Reply;
if (ReplyText[0] == '$')
{
ReplyText = GStrings(ReplyText + 1);
}
FString ReplyString = ReplyText;
if (reply->NeedsGold) ReplyString.AppendFormat(" for %u", reply->ItemCheck[0].Amount);
FBrokenLines *ReplyLines = V_BreakLines (SmallFont, 320-50-10, ReplyString);
mResponses.Push(mResponseLines.Size());
for (j = 0; ReplyLines[j].Width >= 0; ++j)
{
mResponseLines.Push(ReplyLines[j].Text);
}
++i;
V_FreeBrokenLines (ReplyLines);
}
const char *goodbyestr = CurNode->Goodbye;
if (goodbyestr == nullptr)
{
char goodbye[25];
mysnprintf(goodbye, countof(goodbye), "TXT_RANDOMGOODBYE_%d", 1 + (pr_randomspeech() % NUM_RANDOM_GOODBYES));
goodbyestr = GStrings[goodbye];
}
else if (strncmp(goodbyestr, "RANDOM_", 7) == 0)
{
FString byetext;
byetext.Format("TXT_%s_%02d", goodbyestr, 1 + (pr_randomspeech() % NUM_RANDOM_LINES));
goodbyestr = GStrings[byetext];
}
else if (goodbyestr[0] == '$')
{
goodbyestr = GStrings(goodbyestr + 1);
}
if (goodbyestr == nullptr) goodbyestr = "Bye.";
mResponses.Push(mResponseLines.Size());
mResponseLines.Push(FString(goodbyestr));
// Determine where the top of the reply list should be positioned.
i = OptionSettings.mLinespacing;
mYpos = MIN<int> (140, 192 - mResponseLines.Size() * i);
for (i = 0; mDialogueLines[i].Width >= 0; ++i)
{ }
i = 44 + i * 10;
if (mYpos - 100 < i - screen->GetHeight() / CleanYfac / 2)
{
mYpos = i - screen->GetHeight() / CleanYfac / 2 + 100;
}
ConversationMenuY = mYpos;
//ConversationMenu.indent = 50;
// Because replies can be selectively hidden mResponses.Size() won't be consistent.
// So make sure mSelection doesn't exceed mResponses.Size(). [FishyClockwork]
if (mSelection >= (int)mResponses.Size())
{
mSelection = mResponses.Size() - 1;
}
mBroken = broken;
mCount = count;
}
//=============================================================================
//
//
//
//=============================================================================
void OnDestroy() override
{
V_FreeBrokenLines(mDialogueLines);
mDialogueLines = NULL;
I_SetMusicVolume (1.f);
Super::OnDestroy();
V_FreeBrokenLines(mBroken);
}
bool DimAllowed()
{
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool MenuEvent(int mkey, bool fromcontroller)
{
if (demoplayback)
{ // During demo playback, don't let the user do anything besides close this menu.
if (mkey == MKEY_Back)
{
Close();
return true;
}
return false;
}
if (mkey == MKEY_Up)
{
if (--mSelection < 0) mSelection = mResponses.Size() - 1;
return true;
}
else if (mkey == MKEY_Down)
{
if (++mSelection >= (int)mResponses.Size()) mSelection = 0;
return true;
}
else if (mkey == MKEY_Back)
{
Net_WriteByte (DEM_CONVNULL);
Close();
return true;
}
else if (mkey == MKEY_Enter)
{
if ((unsigned)mSelection >= mResponses.Size())
{
Net_WriteByte(DEM_CONVCLOSE);
}
else
{
assert((unsigned)mCurNode->ThisNodeNum < StrifeDialogues.Size());
assert(StrifeDialogues[mCurNode->ThisNodeNum] == mCurNode);
// This is needed because mSelection represents the replies currently being displayed which will
// not match up with what's supposed to be selected if there are any hidden/skipped replies. [FishyClockwork]
FStrifeDialogueReply *reply = mCurNode->Children;
int replynum = mSelection;
for (int i = 0; i <= mSelection && reply != nullptr; reply = reply->Next)
{
if (ShouldSkipReply(reply, mPlayer))
replynum++;
else
i++;
}
// Send dialogue and reply numbers across the wire.
Net_WriteByte(DEM_CONVREPLY);
Net_WriteWord(mCurNode->ThisNodeNum);
Net_WriteByte(replynum);
}
Close();
return true;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool MouseEvent(int type, int x, int y)
{
int sel = -1;
int fh = OptionSettings.mLinespacing;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
if (x >= 24 && x <= 320-24 && y >= mYpos && y < mYpos + fh * (int)mResponseLines.Size())
{
sel = (y - mYpos) / fh;
for(unsigned i=0;i<mResponses.Size(); i++)
{
if ((int)mResponses[i] > sel)
{
sel = i-1;
break;
}
}
}
if (sel != -1 && sel != mSelection)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool Responder(event_t *ev)
{
if (demoplayback)
{ // No interaction during demo playback
return false;
}
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_Char && ev->data1 >= '0' && ev->data1 <= '9')
{ // Activate an item of type numberedmore (dialogue only)
mSelection = ev->data1 == '0' ? 9 : ev->data1 - '1';
return MenuEvent(MKEY_Enter, false);
}
return Super::Responder(ev);
}
//============================================================================
//
// DrawConversationMenu
//
//============================================================================
void Drawer()
{
const char *speakerName;
int x, y, linesize;
int width, fontheight;
player_t *cp = &players[consoleplayer];
assert (mDialogueLines != NULL);
assert (mCurNode != NULL);
FStrifeDialogueNode *CurNode = mCurNode;
if (CurNode == NULL)
{
Close ();
return;
}
// [CW] Freeze the game depending on MAPINFO options.
if (ConversationPauseTic < gametic && !multiplayer && !(level.flags2 & LEVEL2_CONV_SINGLE_UNFREEZE))
{
menuactive = MENU_On;
}
if (CurNode->Backdrop.isValid())
{
screen->DrawTexture (TexMan(CurNode->Backdrop), 0, 0, DTA_320x200, true, TAG_DONE);
}
x = 16 * screen->GetWidth() / 320;
y = 16 * screen->GetHeight() / 200;
linesize = 10 * CleanYfac;
// Who is talking to you?
if (CurNode->SpeakerName != NULL)
{
speakerName = CurNode->SpeakerName;
if (speakerName[0] == '$') speakerName = GStrings(speakerName+1);
}
else
{
speakerName = cp->ConversationNPC->GetTag("Person");
}
// Dim the screen behind the dialogue (but only if there is no backdrop).
if (!CurNode->Backdrop.isValid())
{
int i;
for (i = 0; mDialogueLines[i].Width >= 0; ++i)
{ }
screen->Dim (0, 0.45f, 14 * screen->GetWidth() / 320, 13 * screen->GetHeight() / 200,
308 * screen->GetWidth() / 320 - 14 * screen->GetWidth () / 320,
speakerName == NULL ? linesize * i + 6 * CleanYfac
: linesize * i + 6 * CleanYfac + linesize * 3/2);
}
// Dim the screen behind the PC's choices.
screen->Dim (0, 0.45f, (24-160) * CleanXfac + screen->GetWidth()/2,
(mYpos - 2 - 100) * CleanYfac + screen->GetHeight()/2,
272 * CleanXfac,
MIN<int>(mResponseLines.Size() * OptionSettings.mLinespacing + 4, 200 - mYpos) * CleanYfac);
if (speakerName != NULL)
{
screen->DrawText (SmallFont, CR_WHITE, x, y, speakerName,
DTA_CleanNoMove, true, TAG_DONE);
y += linesize * 3 / 2;
}
x = 24 * screen->GetWidth() / 320;
for (int i = 0; mDialogueLines[i].Width >= 0; ++i)
{
screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, mDialogueLines[i].Text,
DTA_CleanNoMove, true, TAG_DONE);
y += linesize;
}
if (ShowGold)
{
auto cointype = PClass::FindActor("Coin");
if (cointype)
{
AInventory *coin = cp->ConversationPC->FindInventory(cointype);
char goldstr[32];
mysnprintf(goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0);
screen->DrawText(SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE);
screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon),
3, 190, DTA_320x200, true,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE);
screen->DrawText(SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE);
screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon),
2, 189, DTA_320x200, true, TAG_DONE);
}
}
y = mYpos;
fontheight = OptionSettings.mLinespacing;
int response = 0;
for (unsigned i = 0; i < mResponseLines.Size(); i++, y += fontheight)
{
width = SmallFont->StringWidth(mResponseLines[i]);
x = 64;
screen->DrawText (SmallFont, CR_GREEN, x, y, mResponseLines[i], DTA_Clean, true, TAG_DONE);
if (i == mResponses[response])
{
char tbuf[16];
response++;
mysnprintf (tbuf, countof(tbuf), "%d.", response);
x = 50 - SmallFont->StringWidth (tbuf);
screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE);
if (response == mSelection+1)
{
int color = ((DMenu::MenuTime%8) < 4) || DMenu::CurrentMenu != this ? CR_RED:CR_GREY;
x = (50 + 3 - 160) * CleanXfac + screen->GetWidth() / 2;
int yy = (y + fontheight/2 - 5 - 100) * CleanYfac + screen->GetHeight() / 2;
screen->DrawText (ConFont, color, x, yy, "\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
}
}
};
IMPLEMENT_CLASS(DConversationMenu, true, false)
int DConversationMenu::mSelection; // needs to be preserved if the same dialogue is restarted
//============================================================================
//
// P_FreeStrifeConversations
@ -1135,9 +760,9 @@ void P_FreeStrifeConversations ()
ClassRoots.Clear();
PrevNode = NULL;
if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
if (CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
{
DMenu::CurrentMenu->Close();
CurrentMenu->Close();
}
}
@ -1233,19 +858,31 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
S_Sound (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM);
}
DConversationMenu *cmenu = new DConversationMenu(CurNode, pc->player);
// Create the menu. This may be a user-defined class so check if it is good to use.
FName cls = CurNode->MenuClassName;
if (cls == NAME_None) cls = gameinfo.DefaultConversationMenuClass;
if (cls == NAME_None) cls = "ConversationMenu";
auto mcls = PClass::FindClass(cls);
if (mcls == nullptr || !mcls->IsDescendantOf("ConversationMenu")) mcls = PClass::FindClass("ConversationMenu");
assert(mcls);
auto cmenu = mcls->CreateNew();
IFVIRTUALPTRNAME(cmenu, "ConversationMenu", Init)
{
VMValue params[] = { cmenu, CurNode, pc->player, StaticLastReply };
VMReturn ret(&ConversationMenuY);
GlobalVMStack.Call(func, params, countof(params), &ret, 1);
}
if (CurNode != PrevNode)
{ // Only reset the selection if showing a different menu.
DConversationMenu::mSelection = 0;
StaticLastReply = 0;
PrevNode = CurNode;
}
// And open the menu
M_StartControlPanel (false);
M_ActivateMenu(cmenu);
ConversationPauseTic = gametic + 20;
M_ActivateMenu((DMenu*)cmenu);
menuactive = MENU_OnNoPause;
}
}
@ -1315,7 +952,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
if (!CheckStrifeItem(player, reply->ItemCheck[i].Item, reply->ItemCheck[i].Amount))
{
// No, you don't. Say so and let the NPC animate negatively.
if (reply->QuickNo && isconsole)
if (reply->QuickNo.IsNotEmpty() && isconsole)
{
TerminalResponse(reply->QuickNo);
}
@ -1396,7 +1033,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
}
// Update the quest log, if needed.
if (reply->LogString != NULL)
if (reply->LogString.IsNotEmpty())
{
const char *log = reply->LogString;
if (log[0] == '$')
@ -1482,10 +1119,9 @@ void P_ConversationCommand (int netcode, int pnum, BYTE **stream)
// The conversation menus are normally closed by the menu code, but that
// doesn't happen during demo playback, so we need to do it here.
if (demoplayback && DMenu::CurrentMenu != NULL &&
DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
if (demoplayback && CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
{
DMenu::CurrentMenu->Close();
CurrentMenu->Close();
}
if (netcode == DEM_CONVREPLY)
{
@ -1549,3 +1185,28 @@ static void TerminalResponse (const char *str)
}
}
DEFINE_FIELD(FStrifeDialogueNode, DropType);
DEFINE_FIELD(FStrifeDialogueNode, ThisNodeNum);
DEFINE_FIELD(FStrifeDialogueNode, ItemCheckNode);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerType);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerName);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerVoice);
DEFINE_FIELD(FStrifeDialogueNode, Backdrop);
DEFINE_FIELD(FStrifeDialogueNode, Dialogue);
DEFINE_FIELD(FStrifeDialogueNode, Goodbye);
DEFINE_FIELD(FStrifeDialogueNode, Children);
DEFINE_FIELD(FStrifeDialogueNode, MenuClassName);
DEFINE_FIELD(FStrifeDialogueNode, UserData);
DEFINE_FIELD(FStrifeDialogueReply, Next);
DEFINE_FIELD(FStrifeDialogueReply, GiveType);
DEFINE_FIELD(FStrifeDialogueReply, ActionSpecial);
DEFINE_FIELD(FStrifeDialogueReply, Args);
DEFINE_FIELD(FStrifeDialogueReply, PrintAmount);
DEFINE_FIELD(FStrifeDialogueReply, Reply);
DEFINE_FIELD(FStrifeDialogueReply, QuickYes);
DEFINE_FIELD(FStrifeDialogueReply, QuickNo);
DEFINE_FIELD(FStrifeDialogueReply, LogString);
DEFINE_FIELD(FStrifeDialogueReply, NextNode);
DEFINE_FIELD(FStrifeDialogueReply, LogNumber);
DEFINE_FIELD(FStrifeDialogueReply, NeedsGold);

View file

@ -26,13 +26,15 @@ struct FStrifeDialogueNode
int ItemCheckNode; // index into StrifeDialogues
PClassActor *SpeakerType;
char *SpeakerName;
FString SpeakerName;
FSoundID SpeakerVoice;
FTextureID Backdrop;
char *Dialogue;
char *Goodbye = nullptr; // must init to null for binary scripts to work as intended
FString Backdrop;
FString Dialogue;
FString Goodbye; // must init to null for binary scripts to work as intended
FStrifeDialogueReply *Children;
FName MenuClassName;
FString UserData;
};
// FStrifeDialogueReply holds responses the player can give to the NPC
@ -44,15 +46,16 @@ struct FStrifeDialogueReply
PClassActor *GiveType;
int ActionSpecial;
int Args[5];
int PrintAmount;
TArray<FStrifeDialogueItemCheck> ItemCheck;
TArray<FStrifeDialogueItemCheck> ItemCheckRequire;
TArray<FStrifeDialogueItemCheck> ItemCheckExclude;
char *Reply;
char *QuickYes;
FString Reply;
FString QuickYes;
FString QuickNo;
FString LogString;
int NextNode; // index into StrifeDialogues
int LogNumber;
char *LogString;
char *QuickNo;
bool NeedsGold;
};

View file

@ -523,7 +523,7 @@ void AActor::PostSerialize()
!(flags4 & MF4_NOSKIN) &&
state->sprite == GetDefaultByType(player->cls)->SpawnState->sprite)
{ // Give player back the skin
sprite = skins[player->userinfo.GetSkin()].sprite;
sprite = Skins[player->userinfo.GetSkin()].sprite;
}
if (Speed == 0)
{
@ -667,9 +667,9 @@ bool AActor::SetState (FState *newstate, bool nofunction)
// for Dehacked, I would move sprite changing out of the states
// altogether, since actors rarely change their sprites after
// spawning.
if (player != NULL && skins != NULL)
if (player != NULL && Skins.Size() > 0)
{
sprite = skins[player->userinfo.GetSkin()].sprite;
sprite = Skins[player->userinfo.GetSkin()].sprite;
}
else if (newsprite != prevsprite)
{
@ -5399,7 +5399,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
}
// [GRB] Reset skin
p->userinfo.SkinNumChanged(R_FindSkin (skins[p->userinfo.GetSkin()].name, p->CurrentPlayerClass));
p->userinfo.SkinNumChanged(R_FindSkin (Skins[p->userinfo.GetSkin()].Name, p->CurrentPlayerClass));
if (!(mobj->flags2 & MF2_DONTTRANSLATE))
{
@ -5417,7 +5417,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
// [RH] Set player sprite based on skin
if (!(mobj->flags4 & MF4_NOSKIN))
{
mobj->sprite = skins[p->userinfo.GetSkin()].sprite;
mobj->sprite = Skins[p->userinfo.GetSkin()].sprite;
}
p->DesiredFOV = p->FOV = 90.f;

View file

@ -837,7 +837,7 @@ void CopyPlayer(player_t *dst, player_t *src, const char *name)
}
// Validate the skin
dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass));
dst->userinfo.SkinNumChanged(R_FindSkin(Skins[dst->userinfo.GetSkin()].Name, dst->CurrentPlayerClass));
// Make sure the player pawn points to the proper player struct.
if (dst->mo != nullptr)

View file

@ -4204,7 +4204,6 @@ static void P_Shutdown ()
{
// [ZZ] delete global event handlers
E_Shutdown(false);
R_DeinitSpriteData ();
P_DeinitKeyMessages ();
P_FreeLevelData ();
P_FreeExtraLevelData ();

View file

@ -74,9 +74,9 @@ DEFINE_ACTION_FUNCTION(FState, GetSpriteTexture)
}
else
{
sprframe = &SpriteFrames[sprites[skins[skin].sprite].spriteframes + self->GetFrame()];
scalex = skins[skin].Scale.X;
scaley = skins[skin].Scale.Y;
sprframe = &SpriteFrames[sprites[Skins[skin].sprite].spriteframes + self->GetFrame()];
scalex = Skins[skin].Scale.X;
scaley = Skins[skin].Scale.Y;
}
if (numret > 0) ret[0].SetInt(sprframe->Texture[rotation].GetIndex());
if (numret > 1) ret[1].SetInt(!!(sprframe->Flip & (1 << rotation)));

View file

@ -232,20 +232,21 @@ class USDFParser : public UDMFParserBase
// Todo: Finalize
if (reply->ItemCheck.Size() > 0)
{
if (reply->ItemCheck[0].Amount <= 0) reply->NeedsGold = false;
reply->PrintAmount = reply->ItemCheck[0].Amount;
if (reply->PrintAmount <= 0) reply->NeedsGold = false;
}
reply->Reply = ncopystring(ReplyString);
reply->QuickYes = ncopystring(QuickYes);
reply->Reply = ReplyString;
reply->QuickYes = QuickYes;
if (reply->ItemCheck.Size() > 0 && reply->ItemCheck[0].Item != NULL)
{
reply->QuickNo = ncopystring(QuickNo);
reply->QuickNo = QuickNo;
}
else
{
reply->QuickNo = NULL;
reply->QuickNo = "";
}
reply->LogString = ncopystring(LogString);
reply->LogString = LogString;
if(!closeDialog) reply->NextNode *= -1;
return true;
}
@ -315,7 +316,14 @@ class USDFParser : public UDMFParserBase
break;
case NAME_Panel:
node->Backdrop = TexMan.CheckForTexture (CheckString(key), FTexture::TEX_MiscPatch);
node->Backdrop = CheckString(key);
break;
case NAME_Userstring:
if (namespace_bits == Zd)
{
node->UserData = CheckString(key);
}
break;
case NAME_Voice:
@ -373,9 +381,9 @@ class USDFParser : public UDMFParserBase
}
}
}
node->SpeakerName = ncopystring(SpeakerName);
node->Dialogue = ncopystring(Dialogue);
node->Goodbye = ncopystring(Goodbye);
node->SpeakerName = SpeakerName;
node->Dialogue = Dialogue;
node->Goodbye = Goodbye;
return true;
}
@ -390,6 +398,7 @@ class USDFParser : public UDMFParserBase
{
PClassActor *type = NULL;
int dlgid = -1;
FName clsid;
unsigned int startpos = StrifeDialogues.Size();
while (!sc.CheckToken('}'))
@ -414,6 +423,13 @@ class USDFParser : public UDMFParserBase
dlgid = CheckInt(key);
}
break;
case NAME_Class:
if (namespace_bits == Zd)
{
clsid = CheckString(key);
}
break;
}
}
else
@ -439,6 +455,7 @@ class USDFParser : public UDMFParserBase
for(;startpos < StrifeDialogues.Size(); startpos++)
{
StrifeDialogues[startpos]->SpeakerType = type;
StrifeDialogues[startpos]->MenuClassName = clsid;
}
return true;
}

View file

@ -164,6 +164,13 @@ FString GetPrintableDisplayName(PClassActor *cls)
return cls->DisplayName;
}
DEFINE_ACTION_FUNCTION(APlayerPawn, GetPrintableDisplayName)
{
PARAM_PROLOGUE;
PARAM_CLASS(type, AActor);
ACTION_RETURN_STRING(type->DisplayName);
}
bool ValidatePlayerClass(PClassActor *ti, const char *name)
{
if (ti == NULL)
@ -578,6 +585,14 @@ void EnumColorSets(PClassActor *cls, TArray<int> *out)
qsort(&(*out)[0], out->Size(), sizeof(int), intcmp);
}
DEFINE_ACTION_FUNCTION(FPlayerClass, EnumColorSets)
{
PARAM_SELF_STRUCT_PROLOGUE(FPlayerClass);
PARAM_POINTER(out, TArray<int>);
EnumColorSets(self->Type, out);
return 0;
}
//==========================================================================
//
//
@ -597,6 +612,14 @@ FPlayerColorSet *GetColorSet(PClassActor *cls, int setnum)
return nullptr;
}
DEFINE_ACTION_FUNCTION(FPlayerClass, GetColorSetName)
{
PARAM_SELF_STRUCT_PROLOGUE(FPlayerClass);
PARAM_INT(setnum);
auto p = GetColorSet(self->Type, setnum);
ACTION_RETURN_INT(p ? p->Name.GetIndex() : 0);
}
//==========================================================================
//
//
@ -655,6 +678,49 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, GetNeverSwitch)
ACTION_RETURN_BOOL(self->userinfo.GetNeverSwitch());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetColor)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(self->userinfo.GetColor());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetColorSet)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(self->userinfo.GetColorSet());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetPlayerClassNum)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(self->userinfo.GetPlayerClassNum());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetSkin)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(self->userinfo.GetSkin());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetGender)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(self->userinfo.GetGender());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetAutoaim)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_FLOAT(self->userinfo.GetAutoaim());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetTeam)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(self->userinfo.GetTeam());
}
//===========================================================================
//
// APlayerPawn
@ -1228,9 +1294,9 @@ const char *APlayerPawn::GetSoundClass() const
if (player != NULL &&
(player->mo == NULL || !(player->mo->flags4 &MF4_NOSKIN)) &&
(unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
(size_t)player->userinfo.GetSkin() < numskins)
(unsigned)player->userinfo.GetSkin() < Skins.Size())
{
return skins[player->userinfo.GetSkin()].name;
return Skins[player->userinfo.GetSkin()].Name.GetChars();
}
return SoundClass != NAME_None? SoundClass.GetChars() : "player";
@ -1782,8 +1848,8 @@ void P_CheckPlayerSprite(AActor *actor, int &spritenum, DVector2 &scale)
{
// Convert from default scale to skin scale.
DVector2 defscale = actor->GetDefault()->Scale;
scale.X *= skins[player->userinfo.GetSkin()].Scale.X / defscale.X;
scale.Y *= skins[player->userinfo.GetSkin()].Scale.Y / defscale.Y;
scale.X *= Skins[player->userinfo.GetSkin()].Scale.X / defscale.X;
scale.Y *= Skins[player->userinfo.GetSkin()].Scale.Y / defscale.Y;
}
// Set the crouch sprite?
@ -1794,10 +1860,10 @@ void P_CheckPlayerSprite(AActor *actor, int &spritenum, DVector2 &scale)
crouchspriteno = player->mo->crouchsprite;
}
else if (!(actor->flags4 & MF4_NOSKIN) &&
(spritenum == skins[player->userinfo.GetSkin()].sprite ||
spritenum == skins[player->userinfo.GetSkin()].crouchsprite))
(spritenum == Skins[player->userinfo.GetSkin()].sprite ||
spritenum == Skins[player->userinfo.GetSkin()].crouchsprite))
{
crouchspriteno = skins[player->userinfo.GetSkin()].crouchsprite;
crouchspriteno = Skins[player->userinfo.GetSkin()].crouchsprite;
}
else
{ // no sprite -> squash the existing one

View file

@ -1171,7 +1171,7 @@ void R_BuildPlayerTranslation (int player)
D_GetPlayerColor (player, &h, &s, &v, &colorset);
R_CreatePlayerTranslation (h, s, v, colorset,
&skins[players[player].userinfo.GetSkin()],
&Skins[players[player].userinfo.GetSkin()],
translationtables[TRANSLATION_Players][player],
translationtables[TRANSLATION_PlayersExtra][player],
translationtables[TRANSLATION_RainPillar][player]
@ -1218,9 +1218,9 @@ DEFINE_ACTION_FUNCTION(_Translation, SetPlayerTranslation)
if (cls != nullptr)
{
PlayerSkin = R_FindSkin(skins[PlayerSkin].name, int(cls - &PlayerClasses[0]));
PlayerSkin = R_FindSkin(Skins[PlayerSkin].Name, int(cls - &PlayerClasses[0]));
R_GetPlayerTranslation(PlayerColor, GetColorSet(cls->Type, PlayerColorset),
&skins[PlayerSkin], translationtables[tgroup][tnum]);
&Skins[PlayerSkin], translationtables[tgroup][tnum]);
}
ACTION_RETURN_BOOL(true);
}

View file

@ -30,8 +30,7 @@ struct spriteframewithrotate : public spriteframe_t
// [RH] skin globals
FPlayerSkin *skins;
size_t numskins;
TArray<FPlayerSkin> Skins;
BYTE OtherGameSkinRemap[256];
PalEntry OtherGameSkinPalette[256];
@ -512,7 +511,7 @@ void R_InitSkins (void)
int sndlumps[NUMSKINSOUNDS];
char key[65];
DWORD intname, crouchname;
size_t i;
unsigned i;
int j, k, base;
int lastlump;
int aliasid;
@ -538,7 +537,7 @@ void R_InitSkins (void)
i++;
for (j = 0; j < NUMSKINSOUNDS; j++)
sndlumps[j] = -1;
skins[i].namespc = Wads.GetLumpNamespace (base);
Skins[i].namespc = Wads.GetLumpNamespace (base);
FScanner sc(base);
intname = 0;
@ -560,14 +559,13 @@ void R_InitSkins (void)
sc.GetString ();
if (0 == stricmp (key, "name"))
{
strncpy (skins[i].name, sc.String, 16);
for (j = 0; (size_t)j < i; j++)
Skins[i].Name = sc.String;
for (j = 0; (unsigned)j < i; j++)
{
if (stricmp (skins[i].name, skins[j].name) == 0)
if (Skins[i].Name.CompareNoCase(Skins[j].Name) == 0)
{
mysnprintf (skins[i].name, countof(skins[i].name), "skin%d", (int)i);
Printf (PRINT_BOLD, "Skin %s duplicated as %s\n",
skins[j].name, skins[i].name);
Skins[i].Name.Format("skin%u", i);
Printf (PRINT_BOLD, "Skin %s duplicated as %s\n", Skins[j].Name.GetChars(), Skins[i].Name.GetChars());
break;
}
}
@ -586,18 +584,16 @@ void R_InitSkins (void)
}
else if (0 == stricmp (key, "face"))
{
for (j = 2; j >= 0; j--)
skins[i].face[j] = toupper (sc.String[j]);
skins[i].face[3] = '\0';
Skins[i].Face = FString(sc.String, 3);
}
else if (0 == stricmp (key, "gender"))
{
skins[i].gender = D_GenderToInt (sc.String);
Skins[i].gender = D_GenderToInt (sc.String);
}
else if (0 == stricmp (key, "scale"))
{
skins[i].Scale.X = clamp(atof (sc.String), 1./65536, 256.);
skins[i].Scale.Y = skins[i].Scale.X;
Skins[i].Scale.X = clamp(atof (sc.String), 1./65536, 256.);
Skins[i].Scale.Y = Skins[i].Scale.X;
}
else if (0 == stricmp (key, "game"))
{
@ -615,7 +611,7 @@ void R_InitSkins (void)
if (gameinfo.gametype & GAME_DoomChex)
{
transtype = PClass::FindActor(NAME_HereticPlayer);
skins[i].othergame = true;
Skins[i].othergame = true;
}
else if (gameinfo.gametype != GAME_Heretic)
{
@ -634,7 +630,7 @@ void R_InitSkins (void)
if (gameinfo.gametype == GAME_Heretic)
{
transtype = PClass::FindActor(NAME_DoomPlayer);
skins[i].othergame = true;
Skins[i].othergame = true;
}
else if (!(gameinfo.gametype & GAME_DoomChex))
{
@ -659,7 +655,7 @@ void R_InitSkins (void)
}
else if (key[0] == '*')
{ // Player sound replacment (ZDoom extension)
int lump = Wads.CheckNumForName (sc.String, skins[i].namespc);
int lump = Wads.CheckNumForName (sc.String, Skins[i].namespc);
if (lump == -1)
{
lump = Wads.CheckNumForFullName (sc.String, true, ns_sounds);
@ -668,11 +664,11 @@ void R_InitSkins (void)
{
if (stricmp (key, "*pain") == 0)
{ // Replace all pain sounds in one go
aliasid = S_AddPlayerSound (skins[i].name, skins[i].gender,
aliasid = S_AddPlayerSound (Skins[i].Name, Skins[i].gender,
playersoundrefs[0], lump, true);
for (int l = 3; l > 0; --l)
{
S_AddPlayerSoundExisting (skins[i].name, skins[i].gender,
S_AddPlayerSoundExisting (Skins[i].Name, Skins[i].gender,
playersoundrefs[l], aliasid, true);
}
}
@ -681,7 +677,7 @@ void R_InitSkins (void)
int sndref = S_FindSoundNoHash (key);
if (sndref != 0)
{
S_AddPlayerSound (skins[i].name, skins[i].gender, sndref, lump, true);
S_AddPlayerSound (Skins[i].Name, Skins[i].gender, sndref, lump, true);
}
}
}
@ -692,7 +688,7 @@ void R_InitSkins (void)
{
if (stricmp (key, skinsoundnames[j][0]) == 0)
{
sndlumps[j] = Wads.CheckNumForName (sc.String, skins[i].namespc);
sndlumps[j] = Wads.CheckNumForName (sc.String, Skins[i].namespc);
if (sndlumps[j] == -1)
{ // Replacement not found, try finding it in the global namespace
sndlumps[j] = Wads.CheckNumForFullName (sc.String, true, ns_sounds);
@ -715,7 +711,7 @@ void R_InitSkins (void)
{
basetype = PClass::FindActor(NAME_HereticPlayer);
transtype = PClass::FindActor(NAME_DoomPlayer);
skins[i].othergame = true;
Skins[i].othergame = true;
}
else
{
@ -728,8 +724,8 @@ void R_InitSkins (void)
auto transdef = ((APlayerPawn*)GetDefaultByType(transtype));
auto basedef = ((APlayerPawn*)GetDefaultByType(basetype));
skins[i].range0start = transdef->ColorRangeStart;
skins[i].range0end = transdef->ColorRangeEnd;
Skins[i].range0start = transdef->ColorRangeStart;
Skins[i].range0end = transdef->ColorRangeEnd;
remove = true;
for (j = 0; j < (int)PlayerClasses.Size (); j++)
@ -750,8 +746,8 @@ void R_InitSkins (void)
if (!remove)
{
if (skins[i].name[0] == 0)
mysnprintf (skins[i].name, countof(skins[i].name), "skin%d", (int)i);
if (Skins[i].Name.IsEmpty())
Skins[i].Name.Format("skin%u", i);
// Now collect the sprite frames for this skin. If the sprite name was not
// specified, use whatever immediately follows the specifier lump.
@ -783,7 +779,7 @@ void R_InitSkins (void)
}
else
{
skins[i].crouchsprite = -1;
Skins[i].crouchsprite = -1;
break;
}
}
@ -806,7 +802,7 @@ void R_InitSkins (void)
if (spr == 0 && maxframe <= 0)
{
Printf (PRINT_BOLD, "Skin %s (#%d) has no frames. Removing.\n", skins[i].name, (int)i);
Printf (PRINT_BOLD, "Skin %s (#%u) has no frames. Removing.\n", Skins[i].Name.GetChars(), i);
remove = true;
break;
}
@ -814,16 +810,18 @@ void R_InitSkins (void)
Wads.GetLumpName (temp.name, base+1);
temp.name[4] = 0;
int sprno = (int)sprites.Push (temp);
if (spr==0) skins[i].sprite = sprno;
else skins[i].crouchsprite = sprno;
if (spr==0) Skins[i].sprite = sprno;
else Skins[i].crouchsprite = sprno;
R_InstallSprite (sprno, sprtemp, maxframe);
}
}
if (remove)
{
if (i < numskins-1)
memmove (&skins[i], &skins[i+1], sizeof(skins[0])*(numskins-i-1));
if (i < Skins.Size() - 1)
{
Skins.Delete(i);
}
i--;
continue;
}
@ -836,25 +834,25 @@ void R_InitSkins (void)
{
if (j == 0 || sndlumps[j] != sndlumps[j-1])
{
aliasid = S_AddPlayerSound (skins[i].name, skins[i].gender,
aliasid = S_AddPlayerSound (Skins[i].Name, Skins[i].gender,
playersoundrefs[j], sndlumps[j], true);
}
else
{
S_AddPlayerSoundExisting (skins[i].name, skins[i].gender,
S_AddPlayerSoundExisting (Skins[i].Name, Skins[i].gender,
playersoundrefs[j], aliasid, true);
}
}
}
// Make sure face prefix is a full 3 chars
if (skins[i].face[1] == 0 || skins[i].face[2] == 0)
if (Skins[i].Face.Len() < 3)
{
skins[i].face[0] = 0;
Skins[i].Face = "";
}
}
if (numskins > PlayerClasses.Size ())
if (Skins.Size() > PlayerClasses.Size ())
{ // The sound table may have changed, so rehash it.
S_HashSounds ();
S_ShrinkPlayerSoundLists ();
@ -869,9 +867,9 @@ int R_FindSkin (const char *name, int pclass)
return pclass;
}
for (unsigned i = PlayerClasses.Size(); i < numskins; i++)
for (unsigned i = PlayerClasses.Size(); i < Skins.Size(); i++)
{
if (strnicmp (skins[i].name, name, 16) == 0)
if (Skins[i].Name.CompareNoCase(name) == 0)
{
if (PlayerClasses[pclass].CheckSkin (i))
return i;
@ -887,8 +885,8 @@ CCMD (skins)
{
int i;
for (i = PlayerClasses.Size ()-1; i < (int)numskins; i++)
Printf ("% 3d %s\n", i-PlayerClasses.Size ()+1, skins[i].name);
for (i = PlayerClasses.Size() - 1; i < (int)Skins.Size(); i++)
Printf("% 3d %s\n", i - PlayerClasses.Size() + 1, Skins[i].Name.GetChars());
}
@ -914,6 +912,7 @@ void R_InitSprites ()
{
int lump, lastlump;
unsigned int i, j;
unsigned numskins;
// [RH] Create a standard translation to map skins between Heretic and Doom
if (gameinfo.gametype == GAME_DoomChex)
@ -934,15 +933,15 @@ void R_InitSprites ()
}
// [RH] Do some preliminary setup
if (skins != NULL) delete [] skins;
skins = new FPlayerSkin[numskins];
memset (skins, 0, sizeof(*skins) * numskins);
Skins.Clear();
Skins.Resize(numskins);
for (i = 0; i < numskins; i++)
{ // Assume Doom skin by default
auto type = ((APlayerPawn*)GetDefaultByType(PlayerClasses[0].Type));
skins[i].range0start = type->ColorRangeStart;
skins[i].range0end = type->ColorRangeEnd;
skins[i].Scale = type->Scale;
Skins[i].range0start = type->ColorRangeStart;
Skins[i].range0end = type->ColorRangeEnd;
Skins[i].Scale = type->Scale;
}
R_InitSpriteDefs ();
@ -956,33 +955,30 @@ void R_InitSprites ()
{
auto basetype = ((APlayerPawn*)GetDefaultByType(PlayerClasses[i].Type));
strcpy (skins[i].name, "Base");
Skins[i].Name = "Base";
if (basetype->Face == NAME_None)
{
skins[i].face[0] = 'S';
skins[i].face[1] = 'T';
skins[i].face[2] = 'F';
skins[i].face[3] = '\0';
Skins[i].Face = "STF";
}
else
{
strcpy(skins[i].face, basetype->Face);
Skins[i].Face = basetype->Face;
}
skins[i].range0start = basetype->ColorRangeStart;
skins[i].range0end = basetype->ColorRangeEnd;
skins[i].Scale = basetype->Scale;
skins[i].sprite = basetype->SpawnState->sprite;
skins[i].namespc = ns_global;
Skins[i].range0start = basetype->ColorRangeStart;
Skins[i].range0end = basetype->ColorRangeEnd;
Skins[i].Scale = basetype->Scale;
Skins[i].sprite = basetype->SpawnState->sprite;
Skins[i].namespc = ns_global;
PlayerClasses[i].Skins.Push (i);
if (memcmp (sprites[skins[i].sprite].name, "PLAY", 4) == 0)
if (memcmp (sprites[Skins[i].sprite].name, "PLAY", 4) == 0)
{
for (j = 0; j < sprites.Size (); j++)
{
if (memcmp (sprites[j].name, deh.PlayerSprite, 4) == 0)
{
skins[i].sprite = (int)j;
Skins[i].sprite = (int)j;
break;
}
}
@ -995,12 +991,14 @@ void R_InitSprites ()
gl_InitModels();
}
void R_DeinitSpriteData()
{
// Free skins
if (skins != NULL)
{
delete[] skins;
skins = NULL;
}
}
DEFINE_FIELD_NAMED(FPlayerSkin, Name, SkinName);
DEFINE_FIELD(FPlayerSkin, Face);
DEFINE_FIELD(FPlayerSkin, gender);
DEFINE_FIELD(FPlayerSkin, range0start);
DEFINE_FIELD(FPlayerSkin, range0end);
DEFINE_FIELD(FPlayerSkin, othergame);
DEFINE_FIELD(FPlayerSkin, Scale);
DEFINE_FIELD(FPlayerSkin, sprite);
DEFINE_FIELD(FPlayerSkin, crouchsprite);
DEFINE_FIELD(FPlayerSkin, namespc);

View file

@ -47,25 +47,23 @@ extern TArray<spriteframe_t> SpriteFrames;
class FPlayerSkin
{
public:
char name[17]; // 16 chars + NULL
char face[4]; // 3 chars ([MH] + NULL so can use as a C string)
BYTE gender; // This skin's gender (not really used)
BYTE range0start;
BYTE range0end;
bool othergame; // [GRB]
DVector2 Scale;
int sprite;
int crouchsprite;
int namespc; // namespace for this skin
FString Name;
FString Face;
BYTE gender = 0; // This skin's gender (not really used)
BYTE range0start = 0;
BYTE range0end = 0;
bool othergame = 0; // [GRB]
DVector2 Scale = { 1, 1 };
int sprite = 0;
int crouchsprite = 0;
int namespc = 0; // namespace for this skin
};
extern size_t numskins; // [RH]
extern FPlayerSkin * skins; // [RH]
extern TArray<FPlayerSkin> Skins;
extern BYTE OtherGameSkinRemap[256];
extern PalEntry OtherGameSkinPalette[256];
void R_InitSprites ();
void R_DeinitSpriteData ();
#endif

View file

@ -57,6 +57,8 @@
#include "v_video.h"
#include "c_bind.h"
#include "menu/menu.h"
#include "teaminfo.h"
#include "r_data/sprites.h"
static TArray<FPropertyInfo*> properties;
static TArray<AFuncDesc> AFTable;
@ -759,6 +761,14 @@ void InitThingdef()
playerclassstruct->Size = sizeof(FPlayerClass);
playerclassstruct->Align = alignof(FPlayerClass);
auto playerskinstruct = NewNativeStruct("PlayerSkin", nullptr);
playerskinstruct->Size = sizeof(FPlayerSkin);
playerskinstruct->Align = alignof(FPlayerSkin);
auto teamstruct = NewNativeStruct("Team", nullptr);
teamstruct->Size = sizeof(FTeam);
teamstruct->Align = alignof(FTeam);
// set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well...
// As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up.
sectorstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(linestruct, false)), false), myoffsetof(sector_t, Lines), VARF_Native);
@ -798,6 +808,14 @@ void InitThingdef()
PField *plrclsf = new PField("PlayerClasses", plrcls, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PlayerClasses);
Namespaces.GlobalNamespace->Symbols.AddSymbol(plrclsf);
auto plrskn = NewPointer(NewResizableArray(playerskinstruct), false);
PField *plrsknf = new PField("PlayerSkins", plrskn, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&Skins);
Namespaces.GlobalNamespace->Symbols.AddSymbol(plrsknf);
auto teamst = NewPointer(NewResizableArray(teamstruct), false);
PField *teamf = new PField("Teams", teamst, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&Teams);
Namespaces.GlobalNamespace->Symbols.AddSymbol(teamf);
auto bindcls = NewNativeStruct("KeyBindings", nullptr);
PField *binding = new PField("Bindings", bindcls, VARF_Native | VARF_Static, (intptr_t)&Bindings);
Namespaces.GlobalNamespace->Symbols.AddSymbol(binding);
@ -888,7 +906,13 @@ void InitThingdef()
fieldptr = new PField("OptionMenuSettings", NewStruct("FOptionMenuSettings", nullptr), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&OptionSettings);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("gametic", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gametic);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("demoplayback", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&demoplayback);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
// Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag.
// It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution'
// is to create a static variable from it and reference that in the script. Yuck!!!
@ -1195,10 +1219,20 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid)
ACTION_RETURN_STRING(s);
}
DEFINE_ACTION_FUNCTION(FStringStruct, Len)
DEFINE_ACTION_FUNCTION(FStringStruct, Left)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
ACTION_RETURN_INT((int)self->Len());
PARAM_UINT(len);
FString s = self->Left(len);
ACTION_RETURN_STRING(s);
}
DEFINE_ACTION_FUNCTION(FStringStruct, Truncate)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_UINT(len);
self->Truncate(len);
return 0;
}
// CharAt and CharCodeAt is how JS does it, and JS is similar here in that it doesn't have char type as int.
@ -1221,3 +1255,10 @@ DEFINE_ACTION_FUNCTION(FStringStruct, CharCodeAt)
ACTION_RETURN_INT(0);
ACTION_RETURN_INT((*self)[pos]);
}
DEFINE_ACTION_FUNCTION(FStringStruct, Filter)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
ACTION_RETURN_STRING(strbin1(*self));
}

View file

@ -8,6 +8,8 @@
#include "doomerrors.h"
#include "memarena.h"
class DObject;
extern FMemArena ClassDataAllocator;
#define MAX_RETURNS 8 // Maximum number of results a function called by script code can return
@ -1028,6 +1030,7 @@ void NullParam(const char *varname);
#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass());
#define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass());
#define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a;
#define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type x = (type )param[p].a;
#define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type)));
#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base)));
#define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x);
@ -1072,6 +1075,7 @@ void NullParam(const char *varname);
#define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x)
#define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x)
#define PARAM_POINTER(x,type) ++paramnum; PARAM_POINTER_AT(paramnum,x,type)
#define PARAM_POINTERTYPE(x,type) ++paramnum; PARAM_POINTERTYPE_AT(paramnum,x,type)
#define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type)
#define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
#define PARAM_POINTER_NOT_NULL(x,type) ++paramnum; PARAM_POINTER_NOT_NULL_AT(paramnum,x,type)

View file

@ -1361,22 +1361,19 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
break;
case AST_DynArrayType:
if (allowarraytypes)
{
auto atype = static_cast<ZCC_DynArrayType *>(ztype);
auto ftype = DetermineType(outertype, field, name, atype->ElementType, false, true);
if (ftype->GetRegType() == REGT_NIL || ftype->GetRegCount() > 1)
{
auto atype = static_cast<ZCC_DynArrayType *>(ztype);
auto ftype = DetermineType(outertype, field, name, atype->ElementType, false, true);
if (ftype->GetRegType() == REGT_NIL || ftype->GetRegCount() > 1)
{
Error(field, "%s: Base type for dynamic array types nust be integral, but got %s", name.GetChars(), ftype->DescriptiveName());
}
else
{
retval = NewDynArray(ftype);
}
break;
Error(field, "%s: Base type for dynamic array types nust be integral, but got %s", name.GetChars(), ftype->DescriptiveName());
}
else
{
retval = NewDynArray(ftype);
}
break;
}
case AST_ClassType:
{
auto ctype = static_cast<ZCC_ClassType *>(ztype);
@ -2135,7 +2132,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
{
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags = 0;
if (type->IsA(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3)
if ((type->IsA(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) || type->IsA(RUNTIME_CLASS(PDynArray)))
{
// Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly.
type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/);

View file

@ -333,3 +333,6 @@ CCMD (teamlist)
Printf ("End of team list.\n");
}
DEFINE_FIELD_NAMED(FTeam, m_Name, mName)

View file

@ -63,7 +63,9 @@ private:
void ParseTeamDefinition (FScanner &Scan);
void ClearTeams ();
public: // needed for script access.
FString m_Name;
private:
int m_iPlayerColor;
FString m_TextColor;
FString m_Logo;

View file

@ -399,6 +399,20 @@ void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h)
}
}
DEFINE_ACTION_FUNCTION(_Screen, Dim)
{
PARAM_PROLOGUE;
PARAM_INT(color);
PARAM_FLOAT(amount);
PARAM_INT(x1);
PARAM_INT(y1);
PARAM_INT(w);
PARAM_INT(h);
screen->Dim(color, float(amount), x1, y1, w, h);
return 0;
}
//==========================================================================
//
// DCanvas :: GetScreenshotBuffer

View file

@ -100,7 +100,4 @@ const char *GetVersionString();
#endif
// The maximum length of one save game description for the menus.
#define SAVESTRINGSIZE 24
#endif //__VERSION_H__

View file

@ -9,16 +9,21 @@
#include "zscript/menu/menuitembase.txt"
#include "zscript/menu/menu.txt"
#include "zscript/menu/messagebox.txt"
#include "zscript/menu/listmenu.txt"
#include "zscript/menu/listmenuitems.txt"
#include "zscript/menu/optionmenu.txt"
#include "zscript/menu/optionmenuitems.txt"
#include "zscript/menu/colorpickermenu.txt"
#include "zscript/menu/joystickmenu.txt"
#include "zscript/menu/loadsavemenu.txt"
#include "zscript/menu/playermenu.txt"
#include "zscript/menu/playerdisplay.txt"
#include "zscript/menu/playercontrols.txt"
#include "zscript/menu/textentermenu.txt"
#include "zscript/menu/videomenu.txt"
#include "zscript/menu/readthis.txt"
#include "zscript/menu/conversationmenu.txt"
#include "zscript/inventory/inventory.txt"
#include "zscript/inventory/inv_misc.txt"

View file

@ -158,15 +158,11 @@ enum DrawTextureTags
struct Screen native
{
int CleanWidth, CleanHeight;
int CleanXFac, CleanYFac;
int CleanWidth_1, CleanHeight_1;
int CleanXFac_1, CleanYFac_1;
native static Color PaletteColor(int index);
native static int GetWidth();
native static int GetHeight();
native static void Clear(int left, int top, int right, int bottom, Color color, int palcolor = -1);
native static void Dim(Color col, double amount, int x, int y, int w, int h);
native static void DrawHUDTexture(TextureID tex, double x, double y);
native static vararg void DrawTexture(TextureID tex, bool animate, double x, double y, ...);
@ -175,13 +171,6 @@ struct Screen native
native static void DrawFrame(int x, int y, int w, int h);
}
class BrokenLines : Object native
{
native int Count();
native int StringWidth(int line);
native String StringAt(int line);
}
struct Font native
{
enum EColorRange
@ -252,7 +241,7 @@ struct Font native
native static int FindFontColor(Name color);
native static Font FindFont(Name fontname);
native static Font GetFont(Name fontname);
native static BrokenLines BreakLines(String text, int maxlen);
native BrokenLines BreakLines(String text, int maxlen);
}
struct Translation
@ -309,6 +298,8 @@ struct GameInfoStruct native
native String ArmorIcon1;
native String ArmorIcon2;
native int gametype;
native bool norandomplayerclass;
native Array<Name> infoPages;
}
class Object native
@ -335,6 +326,13 @@ class Object native
virtual void OnDestroy() {}
}
class BrokenLines : Object native
{
native int Count();
native int StringWidth(int line);
native String StringAt(int line);
}
class Thinker : Object native
{
enum EStatnums
@ -439,6 +437,7 @@ struct LevelLocals native
native readonly String MapName;
native String NextMap;
native String NextSecretMap;
native String F1Pic;
native readonly int maptype;
native readonly String Music;
native readonly int musicorder;
@ -464,6 +463,7 @@ struct LevelLocals native
native bool nomonsters;
native bool frozen;
native bool infinite_flight;
native bool no_dlg_freeze;
// level_info_t *info cannot be done yet.
native String GetUDMFString(int type, int index, Name key);
@ -580,10 +580,12 @@ struct StringStruct native
native vararg void AppendFormat(String fmt, ...);
native void Replace(String pattern, String replacement);
native String Left(int len);
native String Mid(int pos = 0, int len = 2147483647);
native int Len();
native void Truncate(int newlen);
native String CharAt(int pos);
native int CharCodeAt(int pos);
native String Filter();
}
class Floor : Thinker native

View file

@ -0,0 +1,516 @@
/*
** conversationmenu.txt
** The Strife dialogue display
**
**---------------------------------------------------------------------------
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
struct StrifeDialogueNode native
{
native Class<Actor> DropType;
native int ThisNodeNum;
native int ItemCheckNode;
native Class<Actor> SpeakerType;
native String SpeakerName;
native Sound SpeakerVoice;
native String Backdrop;
native String Dialogue;
native String Goodbye;
native StrifeDialogueReply Children;
}
// FStrifeDialogueReply holds responses the player can give to the NPC
struct StrifeDialogueReply native
{
native StrifeDialogueReply Next;
native Class<Actor> GiveType;
native int ActionSpecial;
native int Args[5];
native int PrintAmount;
native String Reply;
native String QuickYes;
native String QuickNo;
native String LogString;
native int NextNode; // index into StrifeDialogues
native int LogNumber;
native bool NeedsGold;
native bool ShouldSkipReply(PlayerInfo player);
}
class ConversationMenu : Menu
{
String mSpeaker;
BrokenLines mDialogueLines;
Array<String> mResponseLines;
Array<uint> mResponses;
bool mShowGold;
StrifeDialogueNode mCurNode;
int mYpos;
PlayerInfo mPlayer;
int mSelection;
int ConversationPauseTic;
int SpeechWidth;
int ReplyWidth;
native static void SendConversationReply(int node, int reply);
const NUM_RANDOM_LINES = 10;
const NUM_RANDOM_GOODBYES = 3;
//=============================================================================
//
// returns the y position of the replies boy for positioning the terminal response.
//
//=============================================================================
virtual int Init(StrifeDialogueNode CurNode, PlayerInfo player, int activereply)
{
mCurNode = CurNode;
mPlayer = player;
mShowGold = false;
ConversationPauseTic = gametic + 20;
DontDim = true;
ReplyWidth = 320-50-10;
SpeechWidth = screen.GetWidth()/CleanXfac - 24*2;
FormatSpeakerMessage();
return FormatReplies(activereply);
}
//=============================================================================
//
//
//
//=============================================================================
virtual int FormatReplies(int activereply)
{
mSelection = -1;
StrifeDialogueReply reply;
int r = -1;
int i = 1,j;
for (reply = mCurNode.Children; reply != NULL; reply = reply.Next)
{
r++;
if (reply.ShouldSkipReply(mPlayer))
{
continue;
}
if (activereply == r) mSelection = i - 1;
mShowGold |= reply.NeedsGold;
let ReplyText = Stringtable.Localize(reply.Reply);
if (reply.NeedsGold) ReplyText.AppendFormat(" for %u", reply.PrintAmount);
let ReplyLines = SmallFont.BreakLines (ReplyText, ReplyWidth);
mResponses.Push(mResponseLines.Size());
for (j = 0; j < ReplyLines.Count(); ++j)
{
mResponseLines.Push(ReplyLines.StringAt(j));
}
++i;
ReplyLines.Destroy();
}
if (mSelection == -1)
{
mSelection = r < activereply ? r + 1 : 0;
}
let goodbyestr = mCurNode.Goodbye;
if (goodbyestr.Length() == 0)
{
goodbyestr = String.Format("$TXT_RANDOMGOODBYE_%d", Random[RandomSpeech](1, NUM_RANDOM_GOODBYES));
}
else if (goodbyestr.Left(7) == "RANDOM_")
{
goodbyestr = String.Format("$TXT_%s_%02d", goodbyestr, Random[RandomSpeech](1, NUM_RANDOM_LINES));
}
goodbyestr = Stringtable.Localize(goodbyestr);
if (goodbyestr.Length() == 0 || goodbyestr.CharAt(0) == "$") goodbyestr = "Bye.";
mResponses.Push(mResponseLines.Size());
mResponseLines.Push(goodbyestr);
// Determine where the top of the reply list should be positioned.
mYpos = MIN (140, 192 - mResponseLines.Size() * OptionMenuSettings.mLinespacing);
i = 44 + mResponseLines.Size() * OptionMenuSettings.mLinespacing;
if (mYpos - 100 < i - screen.GetHeight() / CleanYfac / 2)
{
mYpos = i - screen.GetHeight() / CleanYfac / 2 + 100;
}
if (mSelection >= mResponses.Size())
{
mSelection = mResponses.Size() - 1;
}
return mYpos;
}
//=============================================================================
//
//
//
//=============================================================================
virtual void FormatSpeakerMessage()
{
// Format the speaker's message.
String toSay = mCurNode.Dialogue;
if (toSay.Left(7) == "RANDOM_")
{
let dlgtext = String.Format("$TXT_%s_%02d", toSay, random[RandomSpeech](1, NUM_RANDOM_LINES));
toSay = Stringtable.Localize(dlgtext);
if (toSay.CharAt(0) == "$") toSay = Stringtable.Localize("$TXT_GOAWAY");
}
else
{
// handle string table replacement
toSay = Stringtable.Localize(toSay);
}
if (toSay.Length() == 0)
{
toSay = ".";
}
mDialogueLines = SmallFont.BreakLines(toSay, SpeechWidth);
}
//=============================================================================
//
//
//
//=============================================================================
override void OnDestroy()
{
mDialogueLines.Destroy();
SetMusicVolume (1);
Super.OnDestroy();
}
protected int GetReplyNum()
{
// This is needed because mSelection represents the replies currently being displayed which will
// not match up with what's supposed to be selected if there are any hidden/skipped replies. [FishyClockwork]
let reply = mCurNode.Children;
int replynum = mSelection;
for (int i = 0; i <= mSelection && reply != null; reply = reply.Next)
{
if (reply.ShouldSkipReply(mPlayer))
replynum++;
else
i++;
}
return replynum;
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent(int mkey, bool fromcontroller)
{
if (demoplayback)
{ // During demo playback, don't let the user do anything besides close this menu.
if (mkey == MKEY_Back)
{
Close();
return true;
}
return false;
}
if (mkey == MKEY_Up)
{
if (--mSelection < 0) mSelection = mResponses.Size() - 1;
return true;
}
else if (mkey == MKEY_Down)
{
if (++mSelection >= mResponses.Size()) mSelection = 0;
return true;
}
else if (mkey == MKEY_Back)
{
SendConversationReply(-1, GetReplyNum());
Close();
return true;
}
else if (mkey == MKEY_Enter)
{
int replynum = GetReplyNum();
if (mSelection >= mResponses.Size())
{
SendConversationReply(-2, replynum);
}
else
{
// Send dialogue and reply numbers across the wire.
SendConversationReply(mCurNode.ThisNodeNum, replynum);
}
Close();
return true;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
int sel = -1;
int fh = OptionMenuSettings.mLinespacing;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen.GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen.GetHeight() / 2)) / CleanYfac) + 100;
if (x >= 24 && x <= 320-24 && y >= mYpos && y < mYpos + fh * mResponseLines.Size())
{
sel = (y - mYpos) / fh;
for(int i = 0; i < mResponses.Size(); i++)
{
if (mResponses[i] > sel)
{
sel = i-1;
break;
}
}
}
if (sel != -1 && sel != mSelection)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
//=============================================================================
//
//
//
//=============================================================================
override bool Responder(InputEventData ev)
{
if (demoplayback)
{ // No interaction during demo playback
return false;
}
if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_Char && ev.data1 >= 48 && ev.data1 <= 57)
{ // Activate an item of type numberedmore (dialogue only)
mSelection = ev.data1 == 48 ? 9 : ev.data1 - 49;
return MenuEvent(MKEY_Enter, false);
}
return Super.Responder(ev);
}
//============================================================================
//
// Draw the backdrop, returns true if the text background should be dimmed
//
//============================================================================
virtual bool DrawBackdrop()
{
let tex = TexMan.CheckForTexture (mCurNode.Backdrop, TexMan.Type_MiscPatch);
if (tex.isValid())
{
screen.DrawTexture(tex, false, 0, 0, DTA_320x200, true);
return false;
}
return true;
}
//============================================================================
//
// Draw the speaker text
//
//============================================================================
virtual void DrawSpeakerText(bool dimbg)
{
String speakerName;
int linesize = OptionMenuSettings.mLinespacing * CleanYfac;
int cnt = mDialogueLines.Count();
// Who is talking to you?
if (mCurNode.SpeakerName.Length() > 0)
{
speakerName = Stringtable.Localize(mCurNode.SpeakerName);
}
else
{
speakerName = players[consoleplayer].ConversationNPC.GetTag("Person");
}
// Dim the screen behind the dialogue (but only if there is no backdrop).
if (dimbg)
{
int x = 14 * screen.GetWidth() / 320;
int y = 13 * screen.GetHeight() / 200;
int w = 294 * screen.GetWidth() / 320;
int h = linesize * cnt + 6 * CleanYfac;
if (speakerName.Length() > 0) h += linesize * 3 / 2;
screen.Dim(0, 0.45f, x, y, w, h);
}
int x = 16 * screen.GetWidth() / 320;
int y = 16 * screen.GetHeight() / 200;
if (speakerName.Length() > 0)
{
screen.DrawText(SmallFont, Font.CR_WHITE, x, y, speakerName, DTA_CleanNoMove, true);
y += linesize * 3 / 2;
}
x = 24 * screen.GetWidth() / 320;
for (int i = 0; i < cnt; ++i)
{
screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, x, y, mDialogueLines.StringAt(i), DTA_CleanNoMove, true);
y += linesize;
}
}
//============================================================================
//
// Draw the replies
//
//============================================================================
virtual void DrawReplies()
{
// Dim the screen behind the PC's choices.
screen.Dim(0, 0.45, (24 - 160) * CleanXfac + screen.GetWidth() / 2, (mYpos - 2 - 100) * CleanYfac + screen.GetHeight() / 2,
272 * CleanXfac, MIN(mResponseLines.Size() * OptionMenuSettings.mLinespacing + 4, 200 - mYpos) * CleanYfac);
int y = mYpos;
int fontheight = OptionMenuSettings.mLinespacing;
int response = 0;
for (int i = 0; i < mResponseLines.Size(); i++)
{
int width = SmallFont.StringWidth(mResponseLines[i]);
int x = 64;
screen.DrawText(SmallFont, Font.CR_GREEN, x, y, mResponseLines[i], DTA_Clean, true);
if (i == mResponses[response])
{
String tbuf;
response++;
tbuf = String.Format("%d.", response);
x = 50 - SmallFont.StringWidth(tbuf);
screen.DrawText(SmallFont, Font.CR_GREY, x, y, tbuf, DTA_Clean, true);
if (response == mSelection + 1)
{
int colr = ((MenuTime() % 8) < 4) || GetCurrentMenu() != self ? Font.CR_RED : Font.CR_GREY;
x = (50 + 3 - 160) * CleanXfac + screen.GetWidth() / 2;
int yy = (y + fontheight / 2 - 5 - 100) * CleanYfac + screen.GetHeight() / 2;
screen.DrawText(ConFont, colr, x, yy, "\xd", DTA_CellX, 8 * CleanXfac, DTA_CellY, 8 * CleanYfac);
}
}
y += fontheight;
}
}
virtual void DrawGold()
{
if (mShowGold)
{
let coin = players[consoleplayer].ConversationPC.FindInventory("Coin");
let icon = GetDefaultByType("Coin").Icon;
let goldstr = String.Format("%d", coin != NULL ? coin.Amount : 0);
screen.DrawText(SmallFont, Font.CR_GRAY, 21, 191, goldstr, DTA_320x200, true, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW);
screen.DrawTexture(icon, false, 3, 190, DTA_320x200, true, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW);
screen.DrawText(SmallFont, Font.CR_GRAY, 20, 190, goldstr, DTA_320x200, true);
screen.DrawTexture(icon, false, 2, 189, DTA_320x200, true);
}
}
//============================================================================
//
// DrawConversationMenu
//
//============================================================================
override void Drawer()
{
if (mCurNode == NULL)
{
Close ();
return;
}
bool dimbg = DrawBackdrop();
DrawSpeakerText(dimbg);
DrawReplies();
DrawGold();
}
//============================================================================
//
//
//
//============================================================================
override void Ticker()
{
// [CW] Freeze the game depending on MAPINFO options.
if (ConversationPauseTic < gametic && !multiplayer && !level.no_dlg_freeze)
{
menuactive = Menu.On;
}
}
}

View file

@ -40,14 +40,14 @@ class ListMenuDescriptor : MenuDescriptor native
//
//=============================================================================
class ListMenu : Menu native
class ListMenu : Menu
{
native ListMenuDescriptor mDesc;
native MenuItemBase mFocusControl;
ListMenuDescriptor mDesc;
MenuItemBase mFocusControl;
virtual void Init(Menu parent = NULL, ListMenuDescriptor desc = NULL)
{
mParentMenu = parent;
Super.Init(parent);
mDesc = desc;
if (desc.mCenter)
{
@ -76,7 +76,13 @@ class ListMenu : Menu native
}
}
MenuItemBase GetItem(Name name)
//=============================================================================
//
//
//
//=============================================================================
ListMenuItem GetItem(Name name)
{
for(int i = 0; i < mDesc.mItems.Size(); i++)
{
@ -86,12 +92,173 @@ class ListMenu : Menu native
return NULL;
}
//bool Responder (InputEventData ev);
//bool MenuEvent (int mkey, bool fromcontroller);
//bool MouseEvent(int type, int x, int y);
//void Ticker ();
//void Drawer ();
//=============================================================================
//
//
//
//=============================================================================
override bool Responder (InputEventData ev)
{
if (ev.type == InputEventData.GUI_Event)
{
if (ev.subtype == InputEventData.GUI_KeyDown)
{
// tolower
int ch = ev.data1;
ch = ch >= 65 && ch <91? ch + 32 : ch;
for(int i = mDesc.mSelectedItem + 1; i < mDesc.mItems.Size(); i++)
{
if (mDesc.mItems[i].CheckHotkey(ch))
{
mDesc.mSelectedItem = i;
MenuSound("menu/cursor");
return true;
}
}
for(int i = 0; i < mDesc.mSelectedItem; i++)
{
if (mDesc.mItems[i].CheckHotkey(ch))
{
mDesc.mSelectedItem = i;
MenuSound("menu/cursor");
return true;
}
}
}
}
return Super.Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
int oldSelect = mDesc.mSelectedItem;
int startedAt = mDesc.mSelectedItem;
if (startedAt < 0) startedAt = 0;
switch (mkey)
{
case MKEY_Up:
do
{
if (--mDesc.mSelectedItem < 0) mDesc.mSelectedItem = mDesc.mItems.Size()-1;
}
while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt);
if (mDesc.mSelectedItem == startedAt) mDesc.mSelectedItem = oldSelect;
MenuSound("menu/cursor");
return true;
case MKEY_Down:
do
{
if (++mDesc.mSelectedItem >= mDesc.mItems.Size()) mDesc.mSelectedItem = 0;
}
while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt);
if (mDesc.mSelectedItem == startedAt) mDesc.mSelectedItem = oldSelect;
MenuSound("menu/cursor");
return true;
case MKEY_Enter:
if (mDesc.mSelectedItem >= 0 && mDesc.mItems[mDesc.mSelectedItem].Activate())
{
MenuSound("menu/choose");
}
return true;
default:
return Super.MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
int sel = -1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen.GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen.GetHeight() / 2)) / CleanYfac) + 100;
if (mFocusControl != NULL)
{
mFocusControl.MouseEvent(type, x, y);
return true;
}
else
{
if ((mDesc.mWLeft <= 0 || x > mDesc.mWLeft) &&
(mDesc.mWRight <= 0 || x < mDesc.mWRight))
{
for(int i=0;i<mDesc.mItems.Size(); i++)
{
if (mDesc.mItems[i].CheckCoordinate(x, y))
{
if (i != mDesc.mSelectedItem)
{
//MenuSound("menu/cursor");
}
mDesc.mSelectedItem = i;
mDesc.mItems[i].MouseEvent(type, x, y);
return true;
}
}
}
}
mDesc.mSelectedItem = -1;
return Super.MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
override void Ticker ()
{
Super.Ticker();
for(int i=0;i<mDesc.mItems.Size(); i++)
{
mDesc.mItems[i].Ticker();
}
}
//=============================================================================
//
//
//
//=============================================================================
override void Drawer ()
{
for(int i=0;i<mDesc.mItems.Size(); i++)
{
if (mDesc.mItems[i].mEnabled) mDesc.mItems[i].Drawer(mDesc.mSelectedItem == i);
}
if (mDesc.mSelectedItem >= 0 && mDesc.mSelectedItem < mDesc.mItems.Size())
mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector);
Super.Drawer();
}
//=============================================================================
//
//
//
//=============================================================================
override void SetFocus(MenuItemBase fc)
{
mFocusControl = fc;
@ -106,3 +273,4 @@ class ListMenu : Menu native
}
}

View file

@ -52,7 +52,7 @@ class ListMenuItem : MenuItemBase
}
else
{
screen.DrawTexture (tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true);
screen.DrawTexture (tex, true, mXpos + xofs, mYpos + yofs, DTA_Clean, true);
}
}
}

View file

@ -0,0 +1,577 @@
/*
** loacpp
** The load game and save game menus
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
struct SaveGameNode native
{
native String SaveTitle;
native String Filename;
native bool bOldVersion;
native bool bMissingWads;
native bool bNoDelete;
}
struct SavegameManager native
{
native int WindowSize;
native SaveGameNode quickSaveSlot;
native static SavegameManager GetManager();
native void ReadSaveStrings();
native void UnloadSaveData();
native int RemoveSaveSlot(int index);
native void LoadSavegame(int Selected);
native void DoSave(int Selected, String savegamestring);
native int ExtractSaveData(int index);
native void ClearSaveStuff();
native bool DrawSavePic(int x, int y, int w, int h);
native void DrawSaveComment(Font font, int cr, int x, int y, int scalefactor);
native void SetFileInfo(int Selected);
native int SavegameCount();
native SaveGameNode GetSavegame(int i);
native void InsertNewSaveNode();
native bool RemoveNewSaveNode();
}
class LoadSaveMenu : ListMenu
{
SavegameManager manager;
int TopItem;
int Selected;
int savepicLeft;
int savepicTop;
int savepicWidth;
int savepicHeight;
int rowHeight;
int listboxLeft;
int listboxTop;
int listboxWidth;
int listboxRows;
int listboxHeight;
int listboxRight;
int listboxBottom;
int commentLeft;
int commentTop;
int commentWidth;
int commentHeight;
int commentRight;
int commentBottom;
bool mEntering;
TextEnterMenu mInput;
//=============================================================================
//
//
//
//=============================================================================
override void Init(Menu parent, ListMenuDescriptor desc)
{
Super.Init(parent, desc);
manager = SavegameManager.GetManager();
manager.ReadSaveStrings();
savepicLeft = 10;
savepicTop = 54*CleanYfac;
savepicWidth = 216*screen.GetWidth()/640;
savepicHeight = 135*screen.GetHeight()/400;
manager.WindowSize = savepicWidth / CleanXfac;
rowHeight = (SmallFont.GetHeight() + 1) * CleanYfac;
listboxLeft = savepicLeft + savepicWidth + 14;
listboxTop = savepicTop;
listboxWidth = screen.GetWidth() - listboxLeft - 10;
int listboxHeight1 = screen.GetHeight() - listboxTop - 10;
listboxRows = (listboxHeight1 - 1) / rowHeight;
listboxHeight = listboxRows * rowHeight + 1;
listboxRight = listboxLeft + listboxWidth;
listboxBottom = listboxTop + listboxHeight;
commentLeft = savepicLeft;
commentTop = savepicTop + savepicHeight + 16;
commentWidth = savepicWidth;
commentHeight = (51+(screen.GetHeight()>200?10:0))*CleanYfac;
commentRight = commentLeft + commentWidth;
commentBottom = commentTop + commentHeight;
}
//=============================================================================
//
//
//
//=============================================================================
override void OnDestroy()
{
manager.ClearSaveStuff ();
Super.OnDestroy();
}
//=============================================================================
//
//
//
//=============================================================================
override void Drawer ()
{
Super.Drawer();
SaveGameNode node;
int i;
int j;
bool didSeeSelected = false;
// Draw picture area
if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame)
{
return;
}
Screen.DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight);
if (!manager.DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight))
{
screen.Clear (savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0);
if (manager.SavegameCount() > 0)
{
String text = (Selected == -1 || !manager.GetSavegame(Selected).bOldVersion)? Stringtable.Localize("$MNU_NOPICTURE") : Stringtable.Localize("$MNU_DIFFVERSION");
int textlen = SmallFont.StringWidth(text) * CleanXfac;
screen.DrawText (SmallFont, Font.CR_GOLD, savepicLeft+(savepicWidth-textlen)/2,
savepicTop+(savepicHeight-rowHeight)/2, text, DTA_CleanNoMove, true);
}
}
// Draw comment area
Screen.DrawFrame (commentLeft, commentTop, commentWidth, commentHeight);
screen.Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0);
manager.DrawSaveComment(SmallFont, Font.CR_GOLD, commentLeft, commentTop, CleanYfac);
// Draw file area
Screen.DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight);
screen.Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0);
if (manager.SavegameCount() == 0)
{
String text = Stringtable.Localize("$MNU_NOFILES");
int textlen = SmallFont.StringWidth(text) * CleanXfac;
screen.DrawText (SmallFont, Font.CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, listboxTop+(listboxHeight-rowHeight)/2, text, DTA_CleanNoMove, true);
return;
}
j = TopItem;
for (i = 0; i < listboxRows && j < manager.SavegameCount(); i++)
{
int colr;
node = manager.GetSavegame(j);
if (node.bOldVersion)
{
colr = Font.CR_BLUE;
}
else if (node.bMissingWads)
{
colr = Font.CR_ORANGE;
}
else if (j == Selected)
{
colr = Font.CR_WHITE;
}
else
{
colr = Font.CR_TAN;
}
if (j == Selected)
{
screen.Clear (listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1), mEntering ? Color(255,255,0,0) : Color(255,0,0,255));
didSeeSelected = true;
if (!mEntering)
{
screen.DrawText (SmallFont, colr, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node.SaveTitle, DTA_CleanNoMove, true);
}
else
{
String s = mInput.GetText() .. SmallFont.GetCursor();
screen.DrawText (SmallFont, Font.CR_WHITE, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, s, DTA_CleanNoMove, true);
}
}
else
{
screen.DrawText (SmallFont, colr, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node.SaveTitle, DTA_CleanNoMove, true);
}
j++;
}
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Up:
if (manager.SavegameCount() > 1)
{
if (Selected == -1) Selected = TopItem;
else
{
if (--Selected < 0) Selected = manager.SavegameCount()-1;
if (Selected < TopItem) TopItem = Selected;
else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1);
}
manager.UnloadSaveData ();
manager.ExtractSaveData (Selected);
}
return true;
case MKEY_Down:
if (manager.SavegameCount() > 1)
{
if (Selected == -1) Selected = TopItem;
else
{
if (++Selected >= manager.SavegameCount()) Selected = 0;
if (Selected < TopItem) TopItem = Selected;
else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1);
}
manager.UnloadSaveData ();
manager.ExtractSaveData (Selected);
}
return true;
case MKEY_PageDown:
if (manager.SavegameCount() > 1)
{
if (TopItem >= manager.SavegameCount() - listboxRows)
{
TopItem = 0;
if (Selected != -1) Selected = 0;
}
else
{
TopItem = MIN(TopItem + listboxRows, manager.SavegameCount() - listboxRows);
if (TopItem > Selected && Selected != -1) Selected = TopItem;
}
manager.UnloadSaveData ();
manager.ExtractSaveData (Selected);
}
return true;
case MKEY_PageUp:
if (manager.SavegameCount() > 1)
{
if (TopItem == 0)
{
TopItem = MAX(0, manager.SavegameCount() - listboxRows);
if (Selected != -1) Selected = TopItem;
}
else
{
TopItem = MAX(TopItem - listboxRows, 0);
if (Selected >= TopItem + listboxRows) Selected = TopItem;
}
manager.UnloadSaveData ();
manager.ExtractSaveData (Selected);
}
return true;
case MKEY_Enter:
return false; // This event will be handled by the subclasses
case MKEY_MBYes:
{
if (Selected < manager.SavegameCount())
{
Selected = manager.RemoveSaveSlot (Selected);
}
return true;
}
default:
return Super.MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
if (x >= listboxLeft && x < listboxLeft + listboxWidth &&
y >= listboxTop && y < listboxTop + listboxHeight)
{
int lineno = (y - listboxTop) / rowHeight;
if (TopItem + lineno < manager.SavegameCount())
{
Selected = TopItem + lineno;
manager.UnloadSaveData ();
manager.ExtractSaveData (Selected);
if (type == MOUSE_Release)
{
if (MenuEvent(MKEY_Enter, true))
{
return true;
}
}
}
else Selected = -1;
}
else Selected = -1;
return Super.MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
override bool Responder (InputEventData ev)
{
if (ev.type == InputEventData.GUI_Event)
{
if (ev.subtype == InputEventData.GUI_KeyDown)
{
if (Selected < manager.SavegameCount())
{
switch (ev.data1)
{
case UIEvent.Key_F1:
manager.SetFileInfo(Selected);
return true;
case UIEvent.Key_DEL:
{
String EndString;
EndString = String.Format("%s%s%s%s?\n\n%s", Stringtable.Localize("$MNU_DELETESG"), TEXTCOLOR_WHITE, manager.GetSavegame(Selected).SaveTitle, TEXTCOLOR_NORMAL, Stringtable.Localize("$PRESSYN"));
StartMessage (EndString, 0);
}
return true;
}
}
}
else if (ev.subtype == InputEventData.GUI_WheelUp)
{
if (TopItem > 0) TopItem--;
return true;
}
else if (ev.subtype == InputEventData.GUI_WheelDown)
{
if (TopItem < manager.SavegameCount() - listboxRows) TopItem++;
return true;
}
}
return Super.Responder(ev);
}
}
class SaveMenu : LoadSaveMenu
{
//=============================================================================
//
//
//
//=============================================================================
override void Init(Menu parent, ListMenuDescriptor desc)
{
Super.Init(parent, desc);
manager.InsertNewSaveNode();
TopItem = 0;
Selected = manager.ExtractSaveData (-1);
}
//=============================================================================
//
//
//
//=============================================================================
override void OnDestroy()
{
if (manager.RemoveNewSaveNode())
{
Selected--;
}
Super.OnDestroy();
}
//=============================================================================
//
//
//
//=============================================================================
const SAVESTRINGSIZE = 32;
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (Super.MenuEvent(mkey, fromcontroller))
{
return true;
}
if (Selected == -1)
{
return false;
}
if (mkey == MKEY_Enter)
{
String SavegameString = (Selected != 0)? manager.GetSavegame(Selected).SaveTitle : "";
mInput = TextEnterMenu.Open(self, SavegameString, SAVESTRINGSIZE, 1, fromcontroller);
mInput.ActivateMenu();
mEntering = true;
}
else if (mkey == MKEY_Input)
{
mEntering = false;
manager.DoSave(Selected, mInput.GetText());
mInput = null;
}
else if (mkey == MKEY_Abort)
{
mEntering = false;
mInput = null;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
override bool Responder (InputEventData ev)
{
if (ev.subtype == InputEventData.GUI_KeyDown)
{
if (Selected != -1)
{
switch (ev.data1)
{
case UIEvent.Key_DEL:
// cannot delete 'new save game' item
if (Selected == 0) return true;
break;
case 78://'N':
Selected = TopItem = 0;
manager.UnloadSaveData ();
return true;
}
}
}
return Super.Responder(ev);
}
}
//=============================================================================
//
//
//
//=============================================================================
class LoadMenu : LoadSaveMenu
{
//=============================================================================
//
//
//
//=============================================================================
override void Init(Menu parent, ListMenuDescriptor desc)
{
Super.Init(parent, desc);
TopItem = 0;
Selected = manager.ExtractSaveData (-1);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (Super.MenuEvent(mkey, fromcontroller))
{
return true;
}
if (Selected == -1 || manager.SavegameCount() == 0)
{
return false;
}
if (mkey == MKEY_Enter)
{
manager.LoadSavegame(Selected);
return true;
}
return false;
}
}

View file

@ -87,6 +87,9 @@ class Menu : Object native
};
native Menu mParentMenu;
native bool mMouseCapture;
native bool mBackbuttonSelected;
native bool DontDim;
void Init(Menu parent)
{
@ -111,7 +114,6 @@ class Menu : Object native
native virtual void Ticker();
native virtual void Drawer();
native void Close();
native MenuItemBase GetItem(Name n);
native void ActivateMenu();
static void MenuSound(Sound snd)

View file

@ -0,0 +1,293 @@
/*
** messagebox.cpp
** Confirmation, notification screns
**
**---------------------------------------------------------------------------
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class MessageBoxMenu : Menu
{
BrokenLines mMessage;
voidptr Handler;
int mMessageMode;
int messageSelection;
int mMouseLeft, mMouseRight, mMouseY;
Name mAction;
native static void CallHandler(voidptr hnd);
//=============================================================================
//
//
//
//=============================================================================
void Init(Menu parent, String message, int messagemode, bool playsound = false, Name cmd = 'None', voidptr native_handler = null)
{
Super.Init(parent);
mAction = cmd;
messageSelection = 0;
mMouseLeft = 140;
mMouseY = 0x80000000;
int mr1 = 170 + SmallFont.StringWidth(Stringtable.Localize("$TXT_YES"));
int mr2 = 170 + SmallFont.StringWidth(Stringtable.Localize("$TXT_NO"));
mMouseRight = MAX(mr1, mr2);
mParentMenu = parent;
mMessage = SmallFont.BreakLines(message, 300);
mMessageMode = messagemode;
if (playsound)
{
MenuSound ("menu/prompt");
}
Handler = native_handler;
}
//=============================================================================
//
//
//
//=============================================================================
override void Drawer ()
{
int i, y;
int fontheight = SmallFont.GetHeight();
y = 100;
int c = mMessage.Count();
for (i = 0; i < c; i++)
y -= SmallFont.GetHeight () / 2;
for (i = 0; i < c; i++)
{
screen.DrawText (SmallFont, Font.CR_UNTRANSLATED, 160 - mMessage.StringWidth(i)/2, y, mMessage.StringAt(i), DTA_Clean, true);
y += fontheight;
}
if (mMessageMode == 0)
{
y += fontheight;
mMouseY = y;
screen.DrawText(SmallFont, messageSelection == 0? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, 160, y, Stringtable.Localize("$TXT_YES"), DTA_Clean, true);
screen.DrawText(SmallFont, messageSelection == 1? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, 160, y + fontheight + 1, Stringtable.Localize("$TXT_NO"), DTA_Clean, true);
if (messageSelection >= 0)
{
if ((MenuTime() % 8) < 6)
{
screen.DrawText(ConFont, OptionMenuSettings.mFontColorSelection,
(150 - 160) * CleanXfac + screen.GetWidth() / 2,
(y + (fontheight + 1) * messageSelection - 100 + fontheight/2 - 5) * CleanYfac + screen.GetHeight() / 2,
"\xd", DTA_CellX, 8 * CleanXfac, DTA_CellY, 8 * CleanYfac);
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
protected void CloseSound()
{
MenuSound (GetCurrentMenu() != NULL? "menu/backup" : "menu/dismiss");
}
//=============================================================================
//
//
//
//=============================================================================
virtual void HandleResult(bool res)
{
if (Handler != null)
{
if (res)
{
CallHandler(Handler);
}
else
{
Close();
CloseSound();
}
}
else if (mParentMenu != NULL)
{
if (mMessageMode == 0)
{
if (mAction == 'None')
{
mParentMenu.MenuEvent(res? MKEY_MBYes : MKEY_MBNo, false);
Close();
}
else
{
Close();
if (res) SetMenu(mAction, -1);
}
CloseSound();
}
}
}
//=============================================================================
//
//
//
//=============================================================================
override bool Responder(InputEventData ev)
{
if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_KeyDown)
{
if (mMessageMode == 0)
{
// tolower
int ch = ev.data1;
ch = ch >= 65 && ch <91? ch + 32 : ch;
if (ch == 78 /*'n'*/ || ch == 32)
{
HandleResult(false);
return true;
}
else if (ch == 89 /*'y'*/)
{
HandleResult(true);
return true;
}
}
else
{
Close();
return true;
}
return false;
}
else if (ev.type == InputEventData.KeyDown)
{
Close();
return true;
}
return Super.Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent(int mkey, bool fromcontroller)
{
if (mMessageMode == 0)
{
if (mkey == MKEY_Up || mkey == MKEY_Down)
{
MenuSound("menu/cursor");
messageSelection = !messageSelection;
return true;
}
else if (mkey == MKEY_Enter)
{
// 0 is yes, 1 is no
HandleResult(!messageSelection);
return true;
}
else if (mkey == MKEY_Back)
{
HandleResult(false);
return true;
}
return false;
}
else
{
Close();
CloseSound();
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
if (mMessageMode == 1)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}
else
{
int sel = -1;
int fh = SmallFont.GetHeight() + 1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen.GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen.GetHeight() / 2)) / CleanYfac) + 100;
if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + 2 * fh)
{
sel = y >= mMouseY + fh;
}
if (sel != -1 && sel != messageSelection)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
messageSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
}
}

View file

@ -56,8 +56,6 @@ class OptionMenuDescriptor : MenuDescriptor native
native int mPosition;
native bool mDontDim;
native void CalcIndent();
native OptionMenuItem GetItem(Name iname);
void Reset()
{
// Reset the default settings (ignore all other values in the struct)
@ -66,6 +64,25 @@ class OptionMenuDescriptor : MenuDescriptor native
mIndent = 0;
mDontDim = 0;
}
//=============================================================================
//
//
//
//=============================================================================
void CalcIndent()
{
// calculate the menu indent
int widest = 0, thiswidth;
for (int i = 0; i < mItems.Size(); i++)
{
thiswidth = mItems[i].GetIndent();
if (thiswidth > widest) widest = thiswidth;
}
mIndent = widest + 4;
}
}
@ -87,11 +104,29 @@ class OptionMenu : Menu
{
mParentMenu = parent;
mDesc = desc;
DontDim = desc.mDontDim;
if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable();
mDesc.CalcIndent();
}
//=============================================================================
//
//
//
//=============================================================================
OptionMenuItem GetItem(Name name)
{
for(int i = 0; i < mDesc.mItems.Size(); i++)
{
Name nm = mDesc.mItems[i].GetAction();
if (nm == name) return mDesc.mItems[i];
}
return NULL;
}
//=============================================================================
//
//

View file

@ -136,7 +136,7 @@ class OptionMenuItemCommand : OptionMenuItemSubmenu
// don't execute if no menu is active
if (m == null) return false;
// don't execute if this item cannot be found in the current menu.
if (m.mDesc.GetItem(mAction) != self) return false;
if (m.GetItem(mAction) != self) return false;
Menu.MenuSound("menu/choose");
DoCommand(mAction);
return true;

View file

@ -0,0 +1,595 @@
/*
** playermenu.cpp
** The player setup menu
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class PlayerMenu : ListMenu
{
int mRotation;
int PlayerClassIndex;
PlayerClass mPlayerClass;
Array<int> PlayerColorSets;
Array<int> mPlayerSkins;
// All write function for the player config are native to prevent abuse.
protected native void AutoaimChanged(float val);
protected native void TeamChanged(int val);
protected native void AlwaysRunChanged(int val);
protected native void GenderChanged(int val);
protected native void SwitchOnPickupChanged(int val);
protected native void ColorChanged(int red, int green, int blue);
protected native void ColorSetChanged(int red);
protected native void PlayerNameChanged(String name);
protected native void SkinChanged (int val);
protected native void ClassChanged(int sel, PlayerClass cls);
//=============================================================================
//
//
//
//=============================================================================
protected void UpdateTranslation()
{
Translation.SetPlayerTranslation(TRANSLATION_Players, MAXPLAYERS, consoleplayer, mPlayerClass);
}
protected void SendNewColor (int red, int green, int blue)
{
ColorChanged(red, green, blue);
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
override void Init(Menu parent, ListMenuDescriptor desc)
{
MenuItemBase li;
PlayerInfo p = players[consoleplayer];
Super.Init(parent, desc);
PickPlayerClass();
mRotation = 0;
li = GetItem('Playerdisplay');
if (li != NULL)
{
li.SetValue(ListMenuItemPlayerDisplay.PDF_ROTATION, 0);
li.SetValue(ListMenuItemPlayerDisplay.PDF_MODE, 1);
li.SetValue(ListMenuItemPlayerDisplay.PDF_TRANSLATE, 1);
li.SetValue(ListMenuItemPlayerDisplay.PDF_CLASS, p.GetPlayerClassNum());
if (mPlayerClass != NULL && !(GetDefaultByType (mPlayerClass.Type).bNoSkin) &&
p.GetPlayerClassNum() != -1)
{
li.SetValue(ListMenuItemPlayerDisplay.PDF_SKIN, p.GetSkin());
}
}
li = GetItem('Playerbox');
if (li != NULL)
{
li.SetString(0, p.GetUserName());
}
li = GetItem('Team');
if (li != NULL)
{
li.SetString(0, "None");
for(int i=0;i<Teams.Size(); i++)
{
li.SetString(i+1, Teams[i].mName);
}
int myteam = players[consoleplayer].GetTeam();
li.SetValue(0, myteam == Team.NoTeam? 0 : myteam + 1);
}
int mycolorset = p.GetColorSet();
Color colr = p.GetColor();
UpdateColorsets();
li = GetItem('Red');
if (li != NULL)
{
li.Enable(mycolorset == -1);
li.SetValue(0, colr.r);
}
li = GetItem('Green');
if (li != NULL)
{
li.Enable(mycolorset == -1);
li.SetValue(0, colr.g);
}
li = GetItem('Blue');
if (li != NULL)
{
li.Enable(mycolorset == -1);
li.SetValue(0, colr.b);
}
li = GetItem('Class');
if (li != NULL)
{
if (PlayerClasses.Size() == 1)
{
li.SetString(0, PlayerPawn.GetPrintableDisplayName(PlayerClasses[0].Type));
li.SetValue(0, 0);
}
else
{
// [XA] Remove the "Random" option if the relevant gameinfo flag is set.
if(!gameinfo.norandomplayerclass)
li.SetString(0, "Random");
for(int i=0; i< PlayerClasses.Size(); i++)
{
let cls = PlayerPawn.GetPrintableDisplayName(PlayerClasses[i].Type);
li.SetString(gameinfo.norandomplayerclass ? i : i+1, cls);
}
int pclass = p.GetPlayerClassNum();
li.SetValue(0, gameinfo.norandomplayerclass && pclass >= 0 ? pclass : pclass + 1);
}
}
UpdateSkins();
li = GetItem('Gender');
if (li != NULL)
{
li.SetValue(0, p.GetGender());
}
li = GetItem('Autoaim');
if (li != NULL)
{
li.SetValue(0, int(p.GetAutoaim()));
}
li = GetItem('Switch');
if (li != NULL)
{
li.SetValue(0, p.GetNeverSwitch());
}
li = GetItem('AlwaysRun');
if (li != NULL)
{
li.SetValue(0, cl_run);
}
if (mDesc.mSelectedItem < 0) mDesc.mSelectedItem = 1;
}
//=============================================================================
//
//
//
//=============================================================================
protected void PickPlayerClass(int pick = -100)
{
int pclass = 0;
// [GRB] Pick a class from player class list
if (PlayerClasses.Size () > 1)
{
pclass = pick == -100? players[consoleplayer].GetPlayerClassNum() : pick;
if (pclass < 0)
{
pclass = (MenuTime() >> 7) % PlayerClasses.Size ();
}
}
PlayerClassIndex = pclass;
mPlayerClass = PlayerClasses[PlayerClassIndex];
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
protected void UpdateColorsets()
{
let li = GetItem('Color');
if (li != NULL)
{
int sel = 0;
mPlayerClass.EnumColorSets(PlayerColorSets);
li.SetString(0, "Custom");
for(int i = 0; i < PlayerColorSets.Size(); i++)
{
let cname = mPlayerClass.GetColorSetName(PlayerColorSets[i]);
li.SetString(i+1, cname);
}
int mycolorset = players[consoleplayer].GetColorSet();
if (mycolorset != -1)
{
for(int i = 0; i < PlayerColorSets.Size(); i++)
{
if (PlayerColorSets[i] == mycolorset)
{
sel = i + 1;
}
}
}
li.SetValue(0, sel);
}
}
//=============================================================================
//
//
//
//=============================================================================
protected void UpdateSkins()
{
int sel = 0;
int skin;
let li = GetItem('Skin');
if (li != NULL)
{
if (GetDefaultByType (mPlayerClass.Type).bNoSkin || players[consoleplayer].GetPlayerClassNum() == -1)
{
li.SetString(0, "Base");
li.SetValue(0, 0);
skin = 0;
}
else
{
mPlayerSkins.Clear();
for (int i = 0; i < PlayerSkins.Size(); i++)
{
if (mPlayerClass.CheckSkin(i))
{
int j = mPlayerSkins.Push(i);
li.SetString(j, PlayerSkins[i].SkinName);
if (players[consoleplayer].GetSkin() == i)
{
sel = j;
}
}
}
li.SetValue(0, sel);
skin = mPlayerSkins[sel];
}
li = GetItem('Playerdisplay');
if (li != NULL)
{
li.SetValue(ListMenuItemPlayerDisplay.PDF_SKIN, skin);
}
}
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void ChangeClass (MenuItemBase li)
{
if (PlayerClasses.Size () == 1)
{
return;
}
bool res;
int sel;
[res, sel] = li.GetValue(0);
if (res)
{
PickPlayerClass(gameinfo.norandomplayerclass ? sel : sel-1);
ClassChanged(sel, mPlayerClass);
UpdateSkins();
UpdateColorsets();
UpdateTranslation();
li = GetItem('Playerdisplay');
if (li != NULL)
{
li.SetValue(ListMenuItemPlayerDisplay.PDF_CLASS, players[consoleplayer].GetPlayerClassNum());
}
}
}
//=============================================================================
//
//
//
//=============================================================================
protected void ChangeSkin (MenuItemBase li)
{
if (GetDefaultByType (mPlayerClass.Type).bNoSkin || players[consoleplayer].GetPlayerClassNum() == -1)
{
return;
}
bool res;
int sel;
[res, sel] = li.GetValue(0);
if (res)
{
sel = mPlayerSkins[sel];
SkinChanged(sel);
UpdateTranslation();
li = GetItem('Playerdisplay');
if (li != NULL)
{
li.SetValue(ListMenuItemPlayerDisplay.PDF_SKIN, sel);
}
}
}
//=============================================================================
//
//
//
//=============================================================================
override bool Responder (InputEventData ev)
{
if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_Char && ev.data1 == 32)
{
// turn the player sprite around
mRotation = 8 - mRotation;
MenuItemBase li = GetItem('Playerdisplay');
if (li != NULL)
{
li.SetValue(ListMenuItemPlayerDisplay.PDF_ROTATION, mRotation);
}
return true;
}
return Super.Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
int v;
bool res;
String s;
if (mDesc.mSelectedItem >= 0)
{
let li = mDesc.mItems[mDesc.mSelectedItem];
if (li.MenuEvent(mkey, fromcontroller))
{
Name ctrl = li.GetAction();
switch(ctrl)
{
// item specific handling comes here
case 'Playerbox':
if (mkey == MKEY_Input)
{
[res, s] = li.GetString(0);
if (res) PlayerNameChanged(s);
}
break;
case 'Team':
[res, v] = li.GetValue(0);
if (res) TeamChanged(v);
break;
case 'Color':
[res, v] = li.GetValue(0);
if (res)
{
int mycolorset = -1;
if (v > 0) mycolorset = PlayerColorSets[v - 1];
let red = GetItem('Red');
let green = GetItem('Green');
let blue = GetItem('Blue');
// disable the sliders if a valid colorset is selected
if (red != NULL) red.Enable(mycolorset == -1);
if (green != NULL) green.Enable(mycolorset == -1);
if (blue != NULL) blue.Enable(mycolorset == -1);
ColorSetChanged(v - 1);
UpdateTranslation();
}
break;
case 'Red':
[res, v] = li.GetValue(0);
if (res)
{
Color colr = players[consoleplayer].GetColor();
SendNewColor (v, colr.g, colr.b);
}
break;
case 'Green':
[res, v] = li.GetValue(0);
if (res)
{
Color colr = players[consoleplayer].GetColor();
SendNewColor (colr.r, v, colr.b);
}
break;
case 'Blue':
[res, v] = li.GetValue(0);
if (res)
{
Color colr = players[consoleplayer].GetColor();
SendNewColor (colr.r, colr.g, v);
}
break;
case 'Class':
[res, v] = li.GetValue(0);
if (res)
{
ChangeClass(li);
}
break;
case 'Skin':
ChangeSkin(li);
break;
case 'Gender':
[res, v] = li.GetValue(0);
if (res)
{
GenderChanged(v);
}
break;
case 'Autoaim':
[res, v] = li.GetValue(0);
if (res)
{
AutoaimChanged(v);
}
break;
case 'Switch':
[res, v] = li.GetValue(0);
if (res)
{
SwitchOnPickupChanged(v);
}
break;
case 'AlwaysRun':
[res, v] = li.GetValue(0);
if (res)
{
AlwaysRunChanged(v);
}
break;
default:
break;
}
return true;
}
}
return Super.MenuEvent(mkey, fromcontroller);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
let li = mFocusControl;
bool res = Super.MouseEvent(type, x, y);
if (li == NULL) li = mFocusControl;
if (li != NULL)
{
// Check if the colors have changed
Name ctrl = li.GetAction();
bool resv;
int v;
[resv, v]= li.GetValue(0);
switch(ctrl)
{
case 'Red':
if (resv)
{
Color colr = players[consoleplayer].GetColor();
SendNewColor (v, colr.g, colr.b);
}
break;
case 'Green':
if (resv)
{
Color colr = players[consoleplayer].GetColor();
SendNewColor (colr.r, v, colr.b);
}
break;
case 'Blue':
if (resv)
{
Color colr = players[consoleplayer].GetColor();
SendNewColor (colr.r, colr.g, v);
}
break;
case 'Autoaim':
AutoaimChanged(v);
break;
}
}
return res;
}
//=============================================================================
//
//
//
//=============================================================================
override void Drawer ()
{
Super.Drawer();
String str = "PRESS " .. TEXTCOLOR_WHITE .. "SPACE";
screen.DrawText (SmallFont, Font.CR_GOLD, 320 - 32 - 32 - SmallFont.StringWidth (str)/2, 50 + 48 + 70, str, DTA_Clean, true);
str = mRotation ? "TO SEE FRONT" : "TO SEE BACK";
screen.DrawText (SmallFont, Font.CR_GOLD, 320 - 32 - 32 - SmallFont.StringWidth (str)/2, 50 + 48 + 70 + SmallFont.GetHeight (), str, DTA_Clean, true);
}
}

View file

@ -0,0 +1,124 @@
/*
** readthis.cpp
** Help screens
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class ReadThisMenu : Menu
{
int mScreen;
int mInfoTic;
//=============================================================================
//
//
//
//=============================================================================
override void Drawer()
{
double alpha;
TextureID tex, prevpic;
if (mScreen == 0)
{
mScreen = 1;
mInfoTic = gametic;
}
// Did the mapper choose a custom help page via MAPINFO?
if (level.F1Pic.Length() != 0)
{
tex = TexMan.CheckForTexture(level.F1Pic, TexMan.Type_MiscPatch);
mScreen = 1;
}
if (!tex.IsValid())
{
tex = TexMan.CheckForTexture(gameinfo.infoPages[mScreen-1], TexMan.Type_MiscPatch);
}
if (mScreen > 1)
{
prevpic = TexMan.CheckForTexture(gameinfo.infoPages[mScreen-2], TexMan.Type_MiscPatch);
}
screen.Dim(0, 1.0, 0,0, screen.GetWidth(), screen.GetHeight());
alpha = MIN((gametic - mInfoTic) * (3. / Thinker.TICRATE), 1.);
if (alpha < 1. && prevpic.IsValid())
{
screen.DrawTexture (prevpic, false, 0, 0, DTA_Fullscreen, true);
}
else alpha = 1;
screen.DrawTexture (tex, false, 0, 0, DTA_Fullscreen, true, DTA_Alpha, alpha);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent(int mkey, bool fromcontroller)
{
if (mkey == MKEY_Enter)
{
MenuSound("menu/choose");
mScreen++;
mInfoTic = gametic;
if (level.F1Pic.Length() != 0 || mScreen > gameinfo.infoPages.Size())
{
Close();
}
return true;
}
else return Super.MenuEvent(mkey, fromcontroller);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}
}

View file

@ -1,15 +1,365 @@
/*
** menuinput.cpp
** The string input code
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// This is only the parts that are needed to make the menu fully work right now. More to come later.
class TextEnterMenu : Menu native
class TextEnterMenu : Menu
{
native bool mInputGridOkay;
const INPUTGRID_WIDTH = 13;
const INPUTGRID_HEIGHT = 5;
native static TextEnterMenu Open(Menu parent, String text, int maxlen, int sizemode, bool fromcontroller);
native String GetText();
const Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-=.,!?@'\":;[]()<>^#$%&*/_ \b";
String mEnterString;
int mEnterSize;
int mEnterPos;
int mSizeMode; // 1: size is length in chars. 2: also check string width
bool mInputGridOkay;
int InputGridX;
int InputGridY;
bool AllowColors;
//=============================================================================
//
//
//
//=============================================================================
// [TP] Added allowcolors
private void Init(Menu parent, String textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors)
{
Super.init(parent);
mEnterString = textbuffer;
mEnterSize = maxlen < 0 ? 0x7fffffff : maxlen;
mSizeMode = sizemode;
mInputGridOkay = showgrid || m_showinputgrid;
if (mEnterString.Length() > 0)
{
InputGridX = INPUTGRID_WIDTH - 1;
InputGridY = INPUTGRID_HEIGHT - 1;
}
else
{
// If we are naming a new save, don't start the cursor on "end".
InputGridX = 0;
InputGridY = 0;
}
AllowColors = allowcolors; // [TP]
}
static TextEnterMenu Open(Menu parent, String textbuffer, int maxlen, int sizemode, bool showgrid = false, bool allowcolors = false)
{
let me = new("TextEnterMenu");
me.Init(parent, textbuffer, maxlen, sizemode, showgrid, allowcolors);
return me;
}
//=============================================================================
//
//
//
//=============================================================================
String GetText()
{
return mEnterString;
}
override bool TranslateKeyboardEvents()
{
return mInputGridOkay;
}
//=============================================================================
//
//
//
//=============================================================================
override bool Responder(InputEventData ev)
{
if (ev.type == InputEventData.GUI_Event)
{
// Save game and player name string input
if (ev.subtype == InputEventData.GUI_Char)
{
mInputGridOkay = false;
if (mEnterString.Length() < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString.AppendFormat("%c", ev.data1);
}
return true;
}
int ch = ev.data1;
if ((ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) && ch == 8)
{
if (mEnterString.Length() > 0)
{
mEnterString.Truncate(mEnterString.Length() - 1);
}
}
else if (ev.subtype == InputEventData.GUI_KeyDown)
{
if (ch == UIEvent.Key_ESCAPE)
{
Menu parent = mParentMenu;
Close();
parent.MenuEvent(MKEY_Abort, false);
return true;
}
else if (ch == 13)
{
if (mEnterString.Length() > 0)
{
// [TP] If we allow color codes, colorize the string now.
if (AllowColors)
mEnterString = mEnterString.Filter();
Menu parent = mParentMenu;
parent.MenuEvent(MKEY_Input, false);
Close();
return true;
}
}
}
if (ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat)
{
return true;
}
}
return Super.Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MouseEvent(int type, int x, int y)
{
if (mMouseCapture || m_use_mouse == 1)
{
int cell_width = 18 * CleanXfac;
int cell_height = 12 * CleanYfac;
int screen_y = screen.GetHeight() - INPUTGRID_HEIGHT * cell_height;
int screen_x = (screen.GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y)
{
InputGridX = (x - screen_x) / cell_width;
InputGridY = (y - screen_y) / cell_height;
if (type == MOUSE_Release)
{
if (MenuEvent(MKEY_Enter, true))
{
MenuSound("menu/choose");
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
return true;
}
}
}
else
{
InputGridX = InputGridY = -1;
}
}
return Super.MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
override bool MenuEvent (int key, bool fromcontroller)
{
String InputGridChars = Chars;
if (key == MKEY_Back)
{
mParentMenu.MenuEvent(MKEY_Abort, false);
return Super.MenuEvent(key, fromcontroller);
}
if (fromcontroller)
{
mInputGridOkay = true;
}
if (mInputGridOkay)
{
int ch;
if (InputGridX == -1 || InputGridY == -1)
{
InputGridX = InputGridY = 0;
}
switch (key)
{
case MKEY_Down:
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Up:
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Right:
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Left:
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Clear:
if (mEnterString.Length() > 0)
{
mEnterString.Truncate(mEnterString.Length() - 1);
}
return true;
case MKEY_Enter:
if (mInputGridOkay)
{
String c = InputGridChars.CharAt(InputGridX + InputGridY * INPUTGRID_WIDTH);
int ch = c.CharCodeAt(0);
if (ch == 0) // end
{
if (mEnterString.Length() > 0)
{
Menu parent = mParentMenu;
Close();
parent.MenuEvent(MKEY_Input, false);
return true;
}
}
else if (ch == 8) // bs
{
if (mEnterString.Length() > 0)
{
mEnterString.Truncate(mEnterString.Length() - 1);
}
}
else if (mEnterString.Length() < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString = mEnterString .. c;
}
}
return true;
default:
break; // Keep GCC quiet
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
override void Drawer ()
{
mParentMenu.Drawer();
if (mInputGridOkay)
{
String InputGridChars = Chars;
int cell_width = 18 * CleanXfac;
int cell_height = 12 * CleanYfac;
int top_padding = cell_height / 2 - SmallFont.GetHeight() * CleanYfac / 2;
// Darken the background behind the character grid.
screen.Dim(0, 0.8, 0, screen.GetHeight() - INPUTGRID_HEIGHT * cell_height, screen.GetWidth(), INPUTGRID_HEIGHT * cell_height);
if (InputGridX >= 0 && InputGridY >= 0)
{
// Highlight the background behind the selected character.
screen.Dim(Color(255,248,220), 0.6,
InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen.GetWidth() / 2,
InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen.GetHeight(),
cell_width, cell_height);
}
for (int y = 0; y < INPUTGRID_HEIGHT; ++y)
{
int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen.GetHeight();
for (int x = 0; x < INPUTGRID_WIDTH; ++x)
{
int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen.GetWidth() / 2;
int ch = InputGridChars.CharCodeAt(y * INPUTGRID_WIDTH + x);
int width = SmallFont.GetCharWidth(ch);
// The highlighted character is yellow; the rest are dark gray.
int colr = (x == InputGridX && y == InputGridY) ? Font.CR_YELLOW : Font.CR_DARKGRAY;
Color palcolor = (x == InputGridX && y == InputGridY) ? Color(160, 120, 0) : Color(120, 120, 120);
if (ch > 32)
{
// Draw a normal character.
screen.DrawChar(SmallFont, colr, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, ch, DTA_CleanNoMove, true);
}
else if (ch == 32)
{
// Draw the space as a box outline. We also draw it 50% wider than it really is.
int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4;
int x2 = x1 + width * 3 * CleanXfac / 2;
int y1 = yy + top_padding;
int y2 = y1 + SmallFont.GetHeight() * CleanYfac;
screen.Clear(x1, y1, x2, y1+CleanYfac, palcolor); // top
screen.Clear(x1, y2, x2, y2+CleanYfac, palcolor); // bottom
screen.Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palcolor); // left
screen.Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palcolor); // right
}
else if (ch == 8 || ch == 0)
{
// Draw the backspace and end "characters".
String str = ch == 8 ? "BS" : "ED";
screen.DrawText(SmallFont, colr,
xx + cell_width/2 - SmallFont.StringWidth(str)*CleanXfac/2,
yy + top_padding, str, DTA_CleanNoMove, true);
}
}
}
}
Super.Drawer();
}
}

View file

@ -136,6 +136,8 @@ class PlayerPawn : Actor native
native int GetMaxHealth();
native bool ResetAirSupply (bool playgasp = false);
native void CheckWeaponSwitch(class<Inventory> item);
native static String GetPrintableDisplayName(Class<Actor> cls);
}
class PlayerChunk : PlayerPawn
@ -293,7 +295,6 @@ struct PlayerInfo native // this is what internally is known as player_t
/* these are not doable yet
ticcmd_t cmd;
usercmd_t original_cmd;
userinfo_t userinfo;
*/
@ -307,11 +308,18 @@ userinfo_t userinfo;
native PSprite FindPSprite(int id);
native void SetLogNumber (int text);
native void SetLogText (String text);
native String GetUserName();
native bool GetNeverSwitch();
native void DropWeapon();
native void BringUpWeapon();
native String GetUserName();
native Color GetColor();
native int GetColorSet();
native int GetPlayerClassNum();
native int GetSkin();
native bool GetNeverSwitch();
native int GetGender();
native int GetTeam();
native float GetAutoaim();
}
struct PlayerClass native
@ -321,4 +329,27 @@ struct PlayerClass native
native Array<int> Skins;
native bool CheckSkin(int skin);
native void EnumColorsets(out Array<int> data);
native Name GetColorsetName(int setnum);
}
struct PlayerSkin native
{
native readonly String SkinName;
native readonly String Face;
native readonly uint8 gender;
native readonly uint8 range0start;
native readonly uint8 range0end;
native readonly bool othergame;
native readonly Vector2 Scale;
native readonly int sprite;
native readonly int crouchsprite;
native readonly int namespc;
};
struct Team native
{
const NoTeam = 255;
const Max = 16;
native String mName;
}