This commit is contained in:
Rachael Alexanderson 2017-02-19 13:05:32 -05:00
commit 11d6f46e2d
25 changed files with 934 additions and 785 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

@ -1937,6 +1937,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

@ -52,6 +52,7 @@ 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)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mBackButton)
const char *GameNames[17] =
@ -362,6 +363,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

@ -160,56 +160,15 @@ DMenu::DMenu(DMenu *parent)
mParentMenu = parent;
mMouseCapture = false;
mBackbuttonSelected = false;
DontDim = false;
GC::WriteBarrier(this, parent);
}
bool DMenu::Responder (event_t *ev)
{
bool res = false;
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_LButtonDown)
{
res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
// make the menu's mouse handler believe that the current coordinate is outside the valid range
if (res) ev->data2 = -1;
res |= CallMouseEvent(MOUSE_Click, ev->data1, ev->data2);
if (res)
{
SetCapture();
}
}
else if (ev->subtype == EV_GUI_MouseMove)
{
BackbuttonTime = BACKBUTTON_TIME;
if (mMouseCapture || m_use_mouse == 1)
{
res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
if (res) ev->data2 = -1;
res |= CallMouseEvent(MOUSE_Move, ev->data1, ev->data2);
}
}
else if (ev->subtype == EV_GUI_LButtonUp)
{
if (mMouseCapture)
{
ReleaseCapture();
res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
if (res) ev->data2 = -1;
res |= CallMouseEvent(MOUSE_Release, ev->data1, ev->data2);
}
}
}
return false;
}
DEFINE_ACTION_FUNCTION(DMenu, Responder)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_POINTER(ev, event_t);
ACTION_RETURN_BOOL(self->Responder(ev));
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::CallResponder(event_t *ev)
{
@ -221,7 +180,7 @@ bool DMenu::CallResponder(event_t *ev)
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return !!retval;
}
else return Responder(ev);
else return false;
}
//=============================================================================
@ -230,29 +189,6 @@ bool DMenu::CallResponder(event_t *ev)
//
//=============================================================================
bool DMenu::MenuEvent (int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Back:
{
Close();
S_Sound (CHAN_VOICE | CHAN_UI,
CurrentMenu != nullptr? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
return true;
}
}
return false;
}
DEFINE_ACTION_FUNCTION(DMenu, MenuEvent)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(key);
PARAM_BOOL(fromcontroller);
ACTION_RETURN_BOOL(self->MenuEvent(key, fromcontroller));
}
bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
{
IFVIRTUAL(DMenu, MenuEvent)
@ -263,7 +199,7 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr);
return !!retval;
}
else return MenuEvent(mkey, fromcontroller);
else return false;
}
//=============================================================================
//
@ -271,6 +207,15 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
//
//=============================================================================
DEFINE_ACTION_FUNCTION(DMenu, SetMouseCapture)
{
PARAM_PROLOGUE;
PARAM_BOOL(on);
if (on) I_SetMouseCapture();
else I_ReleaseMouseCapture();
return 0;
}
void DMenu::Close ()
{
if (CurrentMenu == nullptr) return; // double closing can happen in the save menu.
@ -287,108 +232,19 @@ void DMenu::Close ()
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEvent(int type, int x, int y)
{
return true;
}
DEFINE_ACTION_FUNCTION(DMenu, MouseEvent)
DEFINE_ACTION_FUNCTION(DMenu, Close)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(type);
PARAM_INT(x);
PARAM_INT(y);
ACTION_RETURN_BOOL(self->MouseEvent(type, x, y));
}
bool DMenu::CallMouseEvent(int type, int x, int y)
{
IFVIRTUAL(DMenu, MouseEvent)
{
VMValue params[] = { (DObject*)this, type, x, y };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 4, &ret, 1, nullptr);
return !!retval;
}
else return MouseEvent (type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEventBack(int type, int x, int y)
{
if (m_show_backbutton >= 0)
{
FTexture *tex = TexMan(gameinfo.mBackButton);
if (tex != nullptr)
{
if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetScaledWidth() * CleanXfac;
if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetScaledHeight() * CleanYfac;
mBackbuttonSelected = ( x >= 0 && x < tex->GetScaledWidth() * CleanXfac &&
y >= 0 && y < tex->GetScaledHeight() * CleanYfac);
if (mBackbuttonSelected && type == MOUSE_Release)
{
if (m_use_mouse == 2) mBackbuttonSelected = false;
CallMenuEvent(MKEY_Back, true);
}
return mBackbuttonSelected;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::SetCapture()
{
if (!mMouseCapture)
{
mMouseCapture = true;
I_SetMouseCapture();
}
}
void DMenu::ReleaseCapture()
{
if (mMouseCapture)
{
mMouseCapture = false;
I_ReleaseMouseCapture();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::Ticker ()
{
}
DEFINE_ACTION_FUNCTION(DMenu, Ticker)
{
PARAM_SELF_PROLOGUE(DMenu);
self->Ticker();
self->Close();
return 0;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::CallTicker()
{
IFVIRTUAL(DMenu, Ticker)
@ -396,38 +252,9 @@ void DMenu::CallTicker()
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else Ticker();
}
void DMenu::Drawer ()
{
if (this == CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
{
FTexture *tex = TexMan(gameinfo.mBackButton);
int w = tex->GetScaledWidth() * CleanXfac;
int h = tex->GetScaledHeight() * CleanYfac;
int x = (!(m_show_backbutton&1))? 0:screen->GetWidth() - w;
int y = (!(m_show_backbutton&2))? 0:screen->GetHeight() - h;
if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1))
{
screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, MAKEARGB(40, 255,255,255), TAG_DONE);
}
else
{
screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha, TAG_DONE);
}
}
}
DEFINE_ACTION_FUNCTION(DMenu, Drawer)
{
PARAM_SELF_PROLOGUE(DMenu);
self->Drawer();
return 0;
}
void DMenu::CallDrawer()
{
IFVIRTUAL(DMenu, Drawer)
@ -435,19 +262,6 @@ void DMenu::CallDrawer()
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else Drawer();
}
DEFINE_ACTION_FUNCTION(DMenu, Close)
{
PARAM_SELF_PROLOGUE(DMenu);
self->Close();
return 0;
}
bool DMenu::DimAllowed()
{
return true;
}
bool DMenu::TranslateKeyboardEvents()
@ -504,7 +318,11 @@ void M_StartControlPanel (bool makeSound)
void M_ActivateMenu(DMenu *menu)
{
if (menuactive == MENU_Off) menuactive = MENU_On;
if (CurrentMenu != nullptr) CurrentMenu->ReleaseCapture();
if (CurrentMenu != nullptr && CurrentMenu->mMouseCapture)
{
CurrentMenu->mMouseCapture = false;
I_ReleaseMouseCapture();
}
CurrentMenu = menu;
GC::WriteBarrier(CurrentMenu);
}
@ -651,10 +469,15 @@ void M_SetMenu(FName menu, int param)
const PClass *menuclass = PClass::FindClass(menu);
if (menuclass != nullptr)
{
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
if (menuclass->IsDescendantOf("GenericMenu"))
{
DMenu *newmenu = (DMenu*)menuclass->CreateNew();
newmenu->mParentMenu = CurrentMenu;
IFVIRTUALPTRNAME(newmenu, "GenericMenu", Init)
{
VMValue params[3] = { newmenu, CurrentMenu };
GlobalVMStack.Call(func, params, 2, nullptr, 0);
}
M_ActivateMenu(newmenu);
return;
}
@ -928,7 +751,7 @@ void M_Drawer (void)
if (CurrentMenu != nullptr && menuactive != MENU_Off)
{
if (CurrentMenu->DimAllowed())
if (!CurrentMenu->DontDim)
{
screen->Dim(fade);
V_SetBorderNeedRefresh();
@ -943,13 +766,14 @@ void M_Drawer (void)
//
//=============================================================================
void M_ClearMenus ()
void M_ClearMenus()
{
M_DemoNoPlay = false;
if (CurrentMenu != nullptr)
while (CurrentMenu != nullptr)
{
DMenu* parent = CurrentMenu->mParentMenu;
CurrentMenu->Destroy();
CurrentMenu = nullptr;
CurrentMenu = parent;
}
V_SetBorderNeedRefresh();
menuactive = MENU_Off;
@ -1203,6 +1027,7 @@ 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)
@ -1292,7 +1117,7 @@ DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBi
return (DMenuItemBase*)p;
}
DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID tex, FName command, int param)
DMenuItemBase * CreateListMenuItemPatch(double x, double y, int height, int hotkey, FTextureID tex, FName command, int param)
{
auto c = PClass::FindClass("ListMenuItemPatchItem");
auto p = c->CreateNew();
@ -1302,7 +1127,7 @@ DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FT
return (DMenuItemBase*)p;
}
DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param)
DMenuItemBase * CreateListMenuItemText(double x, double y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param)
{
auto c = PClass::FindClass("ListMenuItemTextItem");
auto p = c->CreateNew();

View file

@ -138,11 +138,11 @@ class DListMenuDescriptor : public DMenuDescriptor
public:
TArray<DMenuItemBase *> mItems;
int mSelectedItem;
int mSelectOfsX;
int mSelectOfsY;
double mSelectOfsX;
double mSelectOfsY;
FTextureID mSelector;
int mDisplayTop;
int mXpos, mYpos;
double mXpos, mYpos;
int mWLeft, mWRight;
int mLinespacing; // needs to be stored for dynamically created menus
int mAutoselect; // this can only be set by internal menu creation functions
@ -263,42 +263,19 @@ public:
MOUSE_Release
};
enum
{
BACKBUTTON_TIME = 4*TICRATE
};
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);
virtual void SetFocus(DMenuItemBase *fc) {}
virtual bool CheckFocus(DMenuItemBase *fc) { return false; }
virtual void ReleaseFocus() {}
bool CallResponder(event_t *ev);
bool CallMenuEvent(int mkey, bool fromcontroller);
bool CallMouseEvent(int type, int x, int y);
void CallTicker();
void CallDrawer();
bool MouseEventBack(int type, int x, int y);
void SetCapture();
void ReleaseCapture();
bool HasCapture()
{
return mMouseCapture;
}
};
//=============================================================================
@ -311,7 +288,7 @@ class DMenuItemBase : public DObject
{
DECLARE_CLASS(DMenuItemBase, DObject)
public:
int mXpos, mYpos;
double mXpos, mYpos;
FNameNoInit mAction;
bool mEnabled;
@ -321,7 +298,7 @@ public:
bool SetValue(int i, int value);
bool GetValue(int i, int *pvalue);
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
int GetY() { return mYpos; }
double GetY() { return mYpos; }
};
//=============================================================================
@ -378,7 +355,7 @@ DMenuItemBase * CreateOptionMenuItemStaticText(const char *name, bool v);
DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int center);
DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings);
DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy);
DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID tex, FName command, int param);
DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param);
DMenuItemBase * CreateListMenuItemPatch(double x, double y, int height, int hotkey, FTextureID tex, FName command, int param);
DMenuItemBase * CreateListMenuItemText(double x, double y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param);
#endif

View file

@ -305,11 +305,11 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
sc.MustGetString();
desc->mSelector = GetMenuTexture(sc.String);
sc.MustGetStringName(",");
sc.MustGetNumber();
desc->mSelectOfsX = sc.Number;
sc.MustGetFloat();
desc->mSelectOfsX = sc.Float;
sc.MustGetStringName(",");
sc.MustGetNumber();
desc->mSelectOfsY = sc.Number;
sc.MustGetFloat();
desc->mSelectOfsY = sc.Float;
}
else if (sc.Compare("Linespacing"))
{
@ -318,11 +318,11 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
}
else if (sc.Compare("Position"))
{
sc.MustGetNumber();
desc->mXpos = sc.Number;
sc.MustGetFloat();
desc->mXpos = sc.Float;
sc.MustGetStringName(",");
sc.MustGetNumber();
desc->mYpos = sc.Number;
sc.MustGetFloat();
desc->mYpos = sc.Float;
}
else if (sc.Compare("Centermenu"))
{
@ -369,7 +369,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
PClass *cls = PClass::FindClass(buildname);
if (cls != nullptr && cls->IsDescendantOf("ListMenuItem"))
{
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", false));
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", true));
if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method.
{
auto &args = func->Variants[0].Proto->ArgumentTypes;
@ -741,7 +741,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc)
PClass *cls = PClass::FindClass(buildname);
if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem"))
{
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", false));
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", true));
if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method.
{
auto &args = func->Variants[0].Proto->ArgumentTypes;
@ -973,13 +973,13 @@ static void BuildEpisodeMenu()
if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{
DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc);
int posy = ld->mYpos;
int posy = (int)ld->mYpos;
int topy = posy;
// Get lowest y coordinate of any static item in the menu
for(unsigned i = 0; i < ld->mItems.Size(); i++)
{
int y = ld->mItems[i]->GetY();
int y = (int)ld->mItems[i]->GetY();
if (y < topy) topy = y;
}
@ -1073,13 +1073,13 @@ static void BuildPlayerclassMenu()
// add player display
ld->mSelectedItem = ld->mItems.Size();
int posy = ld->mYpos;
int posy = (int)ld->mYpos;
int topy = posy;
// Get lowest y coordinate of any static item in the menu
for(unsigned i = 0; i < ld->mItems.Size(); i++)
{
int y = ld->mItems[i]->GetY();
int y = (int)ld->mItems[i]->GetY();
if (y < topy) topy = y;
}
@ -1333,8 +1333,8 @@ void M_StartupSkillMenu(FGameStartup *gs)
if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{
DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc);
int x = ld->mXpos;
int y = ld->mYpos;
int x = (int)ld->mXpos;
int y = (int)ld->mYpos;
// Delete previous contents
for(unsigned i=0; i<ld->mItems.Size(); i++)
@ -1363,7 +1363,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
// Get lowest y coordinate of any static item in the menu
for(unsigned i = 0; i < ld->mItems.Size(); i++)
{
int y = ld->mItems[i]->GetY();
int y = (int)ld->mItems[i]->GetY();
if (y < topy) topy = y;
}
@ -1380,7 +1380,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
{
ld->mItems[i]->OffsetPositionY(topdelta);
}
y = ld->mYpos = posy - topdelta;
ld->mYpos = y = posy - topdelta;
}
}
else

View file

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

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,6 @@ 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);
@ -125,9 +125,6 @@ static void TerminalResponse (const char *str);
static FStrifeDialogueNode *PrevNode;
#define NUM_RANDOM_LINES 10
#define NUM_RANDOM_GOODBYES 3
//============================================================================
//
// GetStrifeType
@ -352,7 +349,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
// 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;
@ -426,7 +423,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
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)
@ -690,8 +687,18 @@ static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
return false;
}
static void SendConversationReply(int node, int reply)
DEFINE_ACTION_FUNCTION(FStrifeDialogueReply, ShouldSkipReply)
{
PARAM_SELF_STRUCT_PROLOGUE(FStrifeDialogueReply);
PARAM_POINTER(player, player_t);
ACTION_RETURN_BOOL(ShouldSkipReply(self, player));
}
DEFINE_ACTION_FUNCTION(DConversationMenu, SendConversationReply)
{
PARAM_PROLOGUE;
PARAM_INT(node);
PARAM_INT(reply);
switch (node)
{
case -1:
@ -709,6 +716,7 @@ static void SendConversationReply(int node, int reply)
break;
}
StaticLastReply = reply;
return 0;
}
@ -733,438 +741,6 @@ public:
}
};
//============================================================================
//
// The conversation menu
//
//============================================================================
class DConversationMenu : public DMenu
{
DECLARE_CLASS(DConversationMenu, DMenu)
public:
FString mSpeaker;
DBrokenLines *mDialogueLines;
TArray<FString> mResponseLines;
TArray<unsigned int> mResponses;
bool mShowGold;
FStrifeDialogueNode *mCurNode;
int mYpos;
player_t *mPlayer;
int mSelection;
//=============================================================================
//
//
//
//=============================================================================
DConversationMenu(FStrifeDialogueNode *CurNode, player_t *player, int activereply)
{
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 = ".";
}
unsigned int count;
auto bl = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac - 24*2, toSay, true, &count);
mDialogueLines = new DBrokenLines(bl, count);
mSelection = -1;
FStrifeDialogueReply *reply;
int r = -1;
int i,j;
for (reply = CurNode->Children, i = 1; reply != NULL; reply = reply->Next)
{
r++;
if (ShouldSkipReply(reply, mPlayer))
{
continue;
}
if (activereply == r) mSelection = i - 1;
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->PrintAmount);
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);
}
if (mSelection == -1)
{
mSelection = r < activereply ? r + 1 : 0;
}
const char *goodbyestr = CurNode->Goodbye;
if (*goodbyestr == 0)
{
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.
mYpos = MIN<int> (140, 192 - mResponseLines.Size() * OptionSettings.mLinespacing);
i = 44 + count * (OptionSettings.mLinespacing + 2);
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;
}
}
//=============================================================================
//
//
//
//=============================================================================
void OnDestroy() override
{
mDialogueLines->Destroy();
mDialogueLines = NULL;
I_SetMusicVolume (1.f);
Super::OnDestroy();
}
bool DimAllowed()
{
return false;
}
int GetReplyNum()
{
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++;
}
return replynum;
}
//=============================================================================
//
//
//
//=============================================================================
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)
{
SendConversationReply(-1, GetReplyNum());
Close();
return true;
}
else if (mkey == MKEY_Enter)
{
int replynum = GetReplyNum();
if ((unsigned)mSelection >= mResponses.Size())
{
SendConversationReply(-1, replynum);
}
else
{
// Send dialogue and reply numbers across the wire.
SendConversationReply(mCurNode->ThisNodeNum, 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.IsNotEmpty())
{
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 = mDialogueLines->mCount;
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; i < mDialogueLines->mCount; ++i)
{
screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, mDialogueLines->mBroken[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 = ((MenuTime%8) < 4) || 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)
//============================================================================
//
// P_FreeStrifeConversations
@ -1184,7 +760,7 @@ void P_FreeStrifeConversations ()
ClassRoots.Clear();
PrevNode = NULL;
if (CurrentMenu != NULL && CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
if (CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
{
CurrentMenu->Close();
}
@ -1282,8 +858,21 @@ 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, StaticLastReply);
// 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.
@ -1293,8 +882,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
// And open the menu
M_StartControlPanel (false);
M_ActivateMenu(cmenu);
ConversationPauseTic = gametic + 20;
M_ActivateMenu((DMenu*)cmenu);
menuactive = MENU_OnNoPause;
}
}
@ -1531,8 +1119,7 @@ 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 && CurrentMenu != NULL &&
CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
if (demoplayback && CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
{
CurrentMenu->Close();
}
@ -1598,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

@ -28,11 +28,13 @@ struct FStrifeDialogueNode
PClassActor *SpeakerType;
FString SpeakerName;
FSoundID SpeakerVoice;
FTextureID Backdrop;
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

View file

@ -316,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:
@ -391,6 +398,7 @@ class USDFParser : public UDMFParserBase
{
PClassActor *type = NULL;
int dlgid = -1;
FName clsid;
unsigned int startpos = StrifeDialogues.Size();
while (!sc.CheckToken('}'))
@ -415,6 +423,13 @@ class USDFParser : public UDMFParserBase
dlgid = CheckInt(key);
}
break;
case NAME_Class:
if (namespace_bits == Zd)
{
clsid = CheckString(key);
}
break;
}
}
else
@ -440,6 +455,7 @@ class USDFParser : public UDMFParserBase
for(;startpos < StrifeDialogues.Size(); startpos++)
{
StrifeDialogues[startpos]->SpeakerType = type;
StrifeDialogues[startpos]->MenuClassName = clsid;
}
return true;
}

View file

@ -63,6 +63,9 @@
static TArray<FPropertyInfo*> properties;
static TArray<AFuncDesc> AFTable;
static TArray<FieldDesc> FieldTable;
extern int BackbuttonTime;
extern float BackbuttonAlpha;
static AWeapon *wpnochg;
//==========================================================================
//
@ -909,11 +912,20 @@ void InitThingdef()
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);
fieldptr = new PField("BackbuttonTime", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&BackbuttonTime);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("BackbuttonAlpha", TypeFloat32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&BackbuttonAlpha);
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!!!
static AWeapon *wpnochg = WP_NOCHANGE;
wpnochg = WP_NOCHANGE;
fieldptr = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
@ -1216,6 +1228,14 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid)
ACTION_RETURN_STRING(s);
}
DEFINE_ACTION_FUNCTION(FStringStruct, Left)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_UINT(len);
FString s = self->Left(len);
ACTION_RETURN_STRING(s);
}
DEFINE_ACTION_FUNCTION(FStringStruct, Truncate)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);

View file

@ -23,6 +23,7 @@
#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

@ -300,6 +300,7 @@ struct GameInfoStruct native
native int gametype;
native bool norandomplayerclass;
native Array<Name> infoPages;
native String mBackButton;
}
class Object native
@ -463,6 +464,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);
@ -579,6 +581,7 @@ 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 void Truncate(int newlen);
native String CharAt(int pos);

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

@ -4,11 +4,11 @@ class ListMenuDescriptor : MenuDescriptor native
{
native Array<ListMenuItem> mItems;
native int mSelectedItem;
native int mSelectOfsX;
native int mSelectOfsY;
native double mSelectOfsX;
native double mSelectOfsY;
native TextureID mSelector;
native int mDisplayTop;
native int mXpos, mYpos;
native double mXpos, mYpos;
native int mWLeft, mWRight;
native int mLinespacing; // needs to be stored for dynamically created menus
native int mAutoselect; // this can only be set by internal menu creation functions
@ -47,20 +47,20 @@ class ListMenu : Menu
virtual void Init(Menu parent = NULL, ListMenuDescriptor desc = NULL)
{
mParentMenu = parent;
Super.Init(parent);
mDesc = desc;
if (desc.mCenter)
{
int center = 160;
double center = 160;
for(int i=0; i < mDesc.mItems.Size(); i++)
{
int xpos = mDesc.mItems[i].GetX();
double xpos = mDesc.mItems[i].GetX();
int width = mDesc.mItems[i].GetWidth();
int curx = mDesc.mSelectOfsX;
double curx = mDesc.mSelectOfsX;
if (width > 0 && mDesc.mItems[i].Selectable())
{
int left = 160 - (width - curx) / 2 - curx;
double left = 160 - (width - curx) / 2 - curx;
if (left < center) center = left;
}
}

View file

@ -35,7 +35,7 @@
class ListMenuItem : MenuItemBase
{
void DrawSelector(int xofs, int yofs, TextureID tex)
void DrawSelector(double xofs, double yofs, TextureID tex)
{
if (tex.isNull())
{
@ -68,7 +68,7 @@ class ListMenuItemStaticPatch : ListMenuItem
TextureID mTexture;
bool mCentered;
void Init(int x, int y, TextureID patch, bool centered = false)
void Init(double x, double y, TextureID patch, bool centered = false)
{
Super.Init(x, y);
mTexture = patch;
@ -82,17 +82,17 @@ class ListMenuItemStaticPatch : ListMenuItem
return;
}
int x = mXpos;
double x = mXpos;
Vector2 vec = TexMan.GetScaledSize(mTexture);
if (mYpos >= 0)
{
if (mCentered) x -= int(vec.X) / 2;
if (mCentered) x -= vec.X / 2;
screen.DrawTexture (mTexture, true, x, mYpos, DTA_Clean, true);
}
else
{
int x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1);
if (mCentered) x -= (int(vec.X) * CleanXfac)/2;
x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1);
if (mCentered) x -= (vec.X * CleanXfac)/2;
screen.DrawTexture (mTexture, true, x, -mYpos*CleanYfac, DTA_CleanNoMove, true);
}
}
@ -100,7 +100,7 @@ class ListMenuItemStaticPatch : ListMenuItem
class ListMenuItemStaticPatchCentered : ListMenuItemStaticPatch
{
void Init(int x, int y, TextureID patch)
void Init(double x, double y, TextureID patch)
{
Super.Init(x, y, patch, true);
}
@ -119,7 +119,7 @@ class ListMenuItemStaticText : ListMenuItem
int mColor;
bool mCentered;
void Init(ListMenuDescriptor desc, int x, int y, String text, int color = Font.CR_UNTRANSLATED)
void Init(ListMenuDescriptor desc, double x, double y, String text, int color = Font.CR_UNTRANSLATED)
{
Super.Init(x, y);
mText = text;
@ -128,7 +128,7 @@ class ListMenuItemStaticText : ListMenuItem
mCentered = false;
}
void InitDirect(int x, int y, String text, Font font, int color = Font.CR_UNTRANSLATED, bool centered = false)
void InitDirect(double x, double y, String text, Font font, int color = Font.CR_UNTRANSLATED, bool centered = false)
{
Super.Init(x, y);
mText = text;
@ -144,13 +144,13 @@ class ListMenuItemStaticText : ListMenuItem
String text = Stringtable.Localize(mText);
if (mYpos >= 0)
{
int x = mXpos;
double x = mXpos;
if (mCentered) x -= mFont.StringWidth(text)/2;
screen.DrawText(mFont, mColor, x, mYpos, text, DTA_Clean, true);
}
else
{
int x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1);
double x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1);
if (mCentered) x -= (mFont.StringWidth(text) * CleanXfac)/2;
screen.DrawText (mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true);
}
@ -160,7 +160,7 @@ class ListMenuItemStaticText : ListMenuItem
class ListMenuItemStaticTextCentered : ListMenuItemStaticText
{
void Init(ListMenuDescriptor desc, int x, int y, String text, int color = -1)
void Init(ListMenuDescriptor desc, double x, double y, String text, int color = -1)
{
Super.Init(desc, x, y, text, color);
mCentered = true;
@ -179,7 +179,7 @@ class ListMenuItemSelectable : ListMenuItem
int mHeight;
int mParam;
protected void Init(int x, int y, int height, Name childmenu, int param = -1)
protected void Init(double x, double y, int height, Name childmenu, int param = -1)
{
Super.Init(x, y, childmenu);
mHeight = height;
@ -250,7 +250,7 @@ class ListMenuItemTextItem : ListMenuItemSelectable
mHotkey = hotkey.CharCodeAt(0);
}
void InitDirect(int x, int y, int height, String hotkey, String text, Font font, int color, int color2, Name child, int param = 0)
void InitDirect(double x, double y, int height, String hotkey, String text, Font font, int color, int color2, Name child, int param = 0)
{
Super.Init(x, y, height, child, param);
mText = text;
@ -288,7 +288,7 @@ class ListMenuItemPatchItem : ListMenuItemSelectable
mTexture = patch;
}
void InitDirect(int x, int y, int height, TextureID patch, String hotkey, Name child, int param = 0)
void InitDirect(double x, double y, int height, TextureID patch, String hotkey, Name child, int param = 0)
{
Super.Init(x, y, height, child, param);
mHotkey = hotkey.CharCodeAt(0);

View file

@ -89,32 +89,192 @@ class Menu : Object native
native Menu mParentMenu;
native bool mMouseCapture;
native bool mBackbuttonSelected;
native bool DontDim;
void Init(Menu parent)
{
mParentMenu = parent;
}
native static int MenuTime();
native static void SetVideoMode();
native static Menu GetCurrentMenu();
native static void SetMenu(Name mnu, int param = 0);
native static void StartMessage(String msg, int mode = 0, Name command = 'none');
native static void SetMouseCapture(bool on);
native void Close();
native void ActivateMenu();
//=============================================================================
//
//
//
//=============================================================================
void Init(Menu parent)
{
mParentMenu = parent;
mMouseCapture = false;
mBackbuttonSelected = false;
DontDim = false;
}
//=============================================================================
//
//
//
//=============================================================================
virtual bool MenuEvent (int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Back:
Close();
MenuSound (GetCurrentMenu() != null? "menu/backup" : "menu/clear");
return true;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
protected bool MouseEventBack(int type, int x, int y)
{
if (m_show_backbutton >= 0)
{
let tex = TexMan.CheckForTexture(gameinfo.mBackButton, TexMan.Type_MiscPatch);
if (tex.IsValid())
{
Vector2 v = TexMan.GetScaledSize(tex);
int w = int(v.X + 0.5) * CleanXfac;
int h = int(v.Y + 0.5) * CleanYfac;
if (m_show_backbutton&1) x -= screen.GetWidth() - w;
if (m_show_backbutton&2) y -= screen.GetHeight() - h;
mBackbuttonSelected = ( x >= 0 && x < w && y >= 0 && y < h);
if (mBackbuttonSelected && type == MOUSE_Release)
{
if (m_use_mouse == 2) mBackbuttonSelected = false;
MenuEvent(MKEY_Back, true);
}
return mBackbuttonSelected;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
virtual bool Responder(InputEventData ev)
{
bool res = false;
if (ev.type == InputEventData.GUI_Event)
{
if (ev.subtype == InputEventData.GUI_LButtonDown)
{
res = MouseEventBack(MOUSE_Click, ev.data1, ev.data2);
// make the menu's mouse handler believe that the current coordinate is outside the valid range
if (res) ev.data2 = -1;
res |= MouseEvent(MOUSE_Click, ev.data1, ev.data2);
if (res)
{
SetCapture(true);
}
}
else if (ev.subtype == InputEventData.GUI_MouseMove)
{
BackbuttonTime = 4*Thinker.TICRATE;
if (mMouseCapture || m_use_mouse == 1)
{
res = MouseEventBack(MOUSE_Move, ev.data1, ev.data2);
if (res) ev.data2 = -1;
res |= MouseEvent(MOUSE_Move, ev.data1, ev.data2);
}
}
else if (ev.subtype == InputEventData.GUI_LButtonUp)
{
if (mMouseCapture)
{
SetCapture(false);
res = MouseEventBack(MOUSE_Release, ev.data1, ev.data2);
if (res) ev.data2 = -1;
res |= MouseEvent(MOUSE_Release, ev.data1, ev.data2);
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
virtual void Drawer ()
{
if (self == GetCurrentMenu() && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
{
let tex = TexMan.CheckForTexture(gameinfo.mBackButton, TexMan.Type_MiscPatch);
if (tex.IsValid())
{
Vector2 v = TexMan.GetScaledSize(tex);
int w = int(v.X + 0.5) * CleanXfac;
int h = int(v.Y + 0.5) * CleanYfac;
int x = (!(m_show_backbutton&1))? 0:screen.GetWidth() - w;
int y = (!(m_show_backbutton&2))? 0:screen.GetHeight() - h;
if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1))
{
screen.DrawTexture(tex, true, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, Color(40, 255,255,255));
}
else
{
screen.DrawTexture(tex, true, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha);
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void SetCapture(bool on)
{
if (mMouseCapture != on)
{
mMouseCapture = on;
SetMouseCapture(on);
}
}
//=============================================================================
//
//
//
//=============================================================================
virtual bool TranslateKeyboardEvents() { return true; }
virtual void SetFocus(MenuItemBase fc) {}
virtual bool CheckFocus(MenuItemBase fc) { return false; }
virtual void ReleaseFocus() {}
virtual void ResetColor() {}
virtual bool MouseEvent(int type, int mx, int my) { return false; }
virtual void Ticker() {}
//=============================================================================
//
//
//
//=============================================================================
native virtual bool Responder(InputEventData ev);
native virtual bool MenuEvent (int mkey, bool fromcontroller);
native virtual bool MouseEvent(int type, int mx, int my);
native virtual void Ticker();
native virtual void Drawer();
native void Close();
native void ActivateMenu();
static void MenuSound(Sound snd)
{
S_Sound (snd, CHAN_VOICE | CHAN_UI, snd_menuvolume, ATTN_NONE);
@ -136,3 +296,11 @@ class MenuDescriptor : Object native
native static MenuDescriptor GetDescriptor(Name n);
}
// This class is only needed to give it a virtual Init method that doesn't belong to Menu itself
class GenericMenu : Menu
{
virtual void Init(Menu parent)
{
Super.Init(parent);
}
}

View file

@ -6,11 +6,11 @@
class MenuItemBase : Object native
{
protected native int mXpos, mYpos;
protected native double mXpos, mYpos;
protected native Name mAction;
native bool mEnabled;
void Init(int xpos = 0, int ypos = 0, Name actionname = 'None')
void Init(double xpos = 0, double ypos = 0, Name actionname = 'None')
{
mXpos = xpos;
mYpos = ypos;
@ -36,10 +36,10 @@ class MenuItemBase : Object native
virtual int GetIndent() { return 0; }
virtual int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) { return indent; }
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
int GetY() { return mYpos; }
int GetX() { return mXpos; }
void SetX(int x) { mXpos = x; }
void OffsetPositionY(double ydelta) { mYpos += ydelta; }
double GetY() { return mYpos; }
double GetX() { return mXpos; }
void SetX(double x) { mXpos = x; }
}
// this is only used to parse font color ranges in MENUDEF

View file

@ -181,12 +181,12 @@ class MessageBoxMenu : Menu
int ch = ev.data1;
ch = ch >= 65 && ch <91? ch + 32 : ch;
if (ch == 78 /*'n'*/ || ch == 32)
if (ch == 110 /*'n'*/ || ch == 32)
{
HandleResult(false);
return true;
}
else if (ch == 89 /*'y'*/)
else if (ch == 121 /*'y'*/)
{
HandleResult(true);
return true;

View file

@ -104,6 +104,7 @@ class OptionMenu : Menu
{
mParentMenu = parent;
mDesc = desc;
DontDim = desc.mDontDim;
if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable();
mDesc.CalcIndent();
}

View file

@ -1129,6 +1129,7 @@ class OptionMenuItemTextField : OptionMenuFieldBase
{
Menu.MenuSound("menu/choose");
mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), GetCVarString(), -1, 2, fromcontroller);
mEnter.ActivateMenu();
return true;
}
else if (mkey == Menu.MKEY_Input)

View file

@ -71,7 +71,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable
//
//=============================================================================
void InitDirect(int x, int y, int height, int frameofs, String text, Font font, int color, Name command)
void InitDirect(double x, double y, int height, int frameofs, String text, Font font, int color, Name command)
{
Super.Init(x, y, height, command);
mText = text;
@ -113,7 +113,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable
//
//=============================================================================
protected void DrawBorder (int x, int y, int len)
protected void DrawBorder (double x, double y, int len)
{
let left = TexMan.CheckForTexture("M_LSLEFT", TexMan.Type_MiscPatch);
let mid = TexMan.CheckForTexture("M_LSCNTR", TexMan.Type_MiscPatch);
@ -141,7 +141,9 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable
}
else
{
screen.Clear(x, y, x + len, y + SmallFont.GetHeight() * 3/2, 0);
int xx = int(x - 160) * CleanXfac + screen.GetWidth()/2;
int yy = int(y - 100) * CleanXfac + screen.GetHeight()/2;
screen.Clear(xx, yy, xx + len*CleanXfac, yy + SmallFont.GetHeight() * CleanYfac * 3/2, 0);
}
}
}
@ -161,7 +163,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable
}
// Draw player name box
int x = mXpos + mFont.StringWidth(text) + 16 + mFrameSize;
double x = mXpos + mFont.StringWidth(text) + 16 + mFrameSize;
DrawBorder (x, mYpos - mFrameSize, MAXPLAYERNAME+1);
if (!mEnter)
{
@ -186,6 +188,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable
{
Menu.MenuSound ("menu/choose");
mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), mPlayerName, MAXPLAYERNAME, 2, fromcontroller);
mEnter.ActivateMenu();
return true;
}
else if (mkey == Menu.MKEY_Input)
@ -246,7 +249,7 @@ class ListMenuItemValueText : ListMenuItemSelectable
//
//=============================================================================
void InitDirect(int x, int y, int height, String text, Font font, int color, int valuecolor, Name command, Name values)
void InitDirect(double x, double y, int height, String text, Font font, int color, int valuecolor, Name command, Name values)
{
Super.Init(x, y, height, command);
mText = text;
@ -337,7 +340,7 @@ class ListMenuItemValueText : ListMenuItemSelectable
String text = Stringtable.Localize(mText);
screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true);
int x = mXpos + mFont.StringWidth(text) + 8;
double x = mXpos + mFont.StringWidth(text) + 8;
if (mSelections.Size() > 0)
{
screen.DrawText(mFont, mFontColor2, x, mYpos, mSelections[mSelection], DTA_Clean, true);
@ -385,7 +388,7 @@ class ListMenuItemSlider : ListMenuItemSelectable
//
//=============================================================================
void InitDirect(int x, int y, int height, String text, Font font, int color, Name command, int min, int max, int step)
void InitDirect(double x, double y, int height, String text, Font font, int color, Name command, int min, int max, int step)
{
Super.Init(x, y, height, command);
mText = text;
@ -463,7 +466,7 @@ class ListMenuItemSlider : ListMenuItemSelectable
lm.ReleaseFocus();
}
int slide_left = SmallFont.StringWidth ("Green") + 8 + mXpos;
int slide_left = SmallFont.StringWidth ("Green") + 8 + int(mXpos);
int slide_right = slide_left + 12*8; // 12 char cells with 8 pixels each.
if (type == Menu.MOUSE_Click)
@ -491,7 +494,7 @@ class ListMenuItemSlider : ListMenuItemSelectable
//
//=============================================================================
protected void DrawSlider (int x, int y)
protected void DrawSlider (double x, double y)
{
int range = mMaxrange - mMinrange;
int cur = mSelection - mMinrange;
@ -515,8 +518,8 @@ class ListMenuItemSlider : ListMenuItemSelectable
screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true);
int x = SmallFont.StringWidth ("Green") + 8 + mXpos;
int x2 = SmallFont.StringWidth (text) + 8 + mXpos;
double x = SmallFont.StringWidth ("Green") + 8 + mXpos;
double x2 = SmallFont.StringWidth (text) + 8 + mXpos;
DrawSlider (MAX(x2, x), mYpos);
}
}

View file

@ -252,8 +252,8 @@ class ListMenuItemPlayerDisplay : ListMenuItem
}
else
{
int x = (mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1);
int y = (mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1);
int x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1);
int y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1);
screen.DrawTexture(mBackdrop, false, x, y - 1,
DTA_DestWidth, 72 * CleanXfac,

View file

@ -33,7 +33,7 @@
**
*/
class ReadThisMenu : Menu
class ReadThisMenu : GenericMenu
{
int mScreen;
int mInfoTic;
@ -43,18 +43,19 @@ class ReadThisMenu : Menu
//
//
//=============================================================================
override void Init(Menu parent)
{
Super.Init(parent);
mScreen = 1;
mInfoTic = gametic;
}
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)
{