mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-25 21:31:10 +00:00
- scriptified the remaining parts of the conversationmenu.
- do not resolve the backdrop texture to a texture ID at load time. This will allow custom menu classes to use this info differently. - added a new ZSDF userstring property to dialog pages to give mods more means for customization. - allow overriding the conversation menu class both globally through MAPINFO and per conversation in ZSDF.
This commit is contained in:
parent
d85b9cdd71
commit
e05242e44d
10 changed files with 197 additions and 191 deletions
|
@ -38,6 +38,7 @@ conversation
|
||||||
page
|
page
|
||||||
{
|
{
|
||||||
drop = <string>;
|
drop = <string>;
|
||||||
|
userstring = <string>; New field which can be used to pass data to custom conversation menu classes.
|
||||||
ifitem
|
ifitem
|
||||||
{
|
{
|
||||||
item = <string>;
|
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
|
outright abort on incompatible namespaces fail with a type mismatch error on
|
||||||
one of the specified propeties.
|
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:
|
ZDoom-format dialogues need to start with the line:
|
||||||
|
|
||||||
namespace = "ZDoom";
|
namespace = "ZDoom";
|
||||||
|
@ -86,6 +83,7 @@ conversation // Starts a dialog.
|
||||||
// the standard conversation ID ('actor' property) is used instead
|
// the standard conversation ID ('actor' property) is used instead
|
||||||
// for this purpose but since 'ZDoom' namespace requires the actor
|
// for this purpose but since 'ZDoom' namespace requires the actor
|
||||||
// to be a class name it needs a separate field for this.
|
// to be a class name it needs a separate field for this.
|
||||||
|
class = <string>; //Override the default conversation menu class for this conversation.
|
||||||
|
|
||||||
page
|
page
|
||||||
{
|
{
|
||||||
|
|
|
@ -362,6 +362,7 @@ void FMapInfoParser::ParseGameInfo()
|
||||||
GAMEINFOKEY_INT(TextScreenX, "textscreenx")
|
GAMEINFOKEY_INT(TextScreenX, "textscreenx")
|
||||||
GAMEINFOKEY_INT(TextScreenY, "textscreeny")
|
GAMEINFOKEY_INT(TextScreenY, "textscreeny")
|
||||||
GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence")
|
GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence")
|
||||||
|
GAMEINFOKEY_STRING(DefaultConversationMenuClass, "defaultconversationmenuclass")
|
||||||
GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont")
|
GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont")
|
||||||
GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont")
|
GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont")
|
||||||
GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont")
|
GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont")
|
||||||
|
|
1
src/gi.h
1
src/gi.h
|
@ -172,6 +172,7 @@ struct gameinfo_t
|
||||||
double gibfactor;
|
double gibfactor;
|
||||||
int TextScreenX;
|
int TextScreenX;
|
||||||
int TextScreenY;
|
int TextScreenY;
|
||||||
|
FName DefaultConversationMenuClass;
|
||||||
FName DefaultEndSequence;
|
FName DefaultEndSequence;
|
||||||
FString mMapArrow, mCheatMapArrow;
|
FString mMapArrow, mCheatMapArrow;
|
||||||
FString mEasyKey, mCheatKey;
|
FString mEasyKey, mCheatKey;
|
||||||
|
|
|
@ -653,6 +653,7 @@ xx(Link)
|
||||||
xx(Goodbye)
|
xx(Goodbye)
|
||||||
xx(Require)
|
xx(Require)
|
||||||
xx(Exclude)
|
xx(Exclude)
|
||||||
|
xx(Userstring)
|
||||||
|
|
||||||
// Special menus
|
// Special menus
|
||||||
xx(Mainmenu)
|
xx(Mainmenu)
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
#include "p_local.h"
|
#include "p_local.h"
|
||||||
#include "menu/menu.h"
|
#include "menu/menu.h"
|
||||||
#include "g_levellocals.h"
|
#include "g_levellocals.h"
|
||||||
|
#include "virtual.h"
|
||||||
|
|
||||||
// The conversations as they exist inside a SCRIPTxx lump.
|
// The conversations as they exist inside a SCRIPTxx lump.
|
||||||
struct Response
|
struct Response
|
||||||
|
@ -124,9 +125,6 @@ static void TerminalResponse (const char *str);
|
||||||
|
|
||||||
static FStrifeDialogueNode *PrevNode;
|
static FStrifeDialogueNode *PrevNode;
|
||||||
|
|
||||||
#define NUM_RANDOM_LINES 10
|
|
||||||
#define NUM_RANDOM_GOODBYES 3
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
//
|
//
|
||||||
// GetStrifeType
|
// GetStrifeType
|
||||||
|
@ -351,7 +349,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
|
||||||
|
|
||||||
// The speaker's portrait, if any.
|
// The speaker's portrait, if any.
|
||||||
speech.Dialogue[0] = 0; //speech.Backdrop[8] = 0;
|
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.
|
// The speaker's voice for this node, if any.
|
||||||
speech.Backdrop[0] = 0; //speech.Sound[8] = 0;
|
speech.Backdrop[0] = 0; //speech.Sound[8] = 0;
|
||||||
|
@ -425,7 +423,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
|
||||||
node->Dialogue = speech.Dialogue;
|
node->Dialogue = speech.Dialogue;
|
||||||
|
|
||||||
// The Teaser version doesn't have portraits.
|
// The Teaser version doesn't have portraits.
|
||||||
node->Backdrop.SetInvalid();
|
node->Backdrop = "";
|
||||||
|
|
||||||
// The speaker's voice for this node, if any.
|
// The speaker's voice for this node, if any.
|
||||||
if (speech.VoiceNumber != 0)
|
if (speech.VoiceNumber != 0)
|
||||||
|
@ -743,153 +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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(DConversationMenu, true, false)
|
|
||||||
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
//
|
//
|
||||||
// P_FreeStrifeConversations
|
// P_FreeStrifeConversations
|
||||||
|
@ -909,7 +760,7 @@ void P_FreeStrifeConversations ()
|
||||||
ClassRoots.Clear();
|
ClassRoots.Clear();
|
||||||
|
|
||||||
PrevNode = NULL;
|
PrevNode = NULL;
|
||||||
if (CurrentMenu != NULL && CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
|
if (CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
|
||||||
{
|
{
|
||||||
CurrentMenu->Close();
|
CurrentMenu->Close();
|
||||||
}
|
}
|
||||||
|
@ -1007,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);
|
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)
|
if (CurNode != PrevNode)
|
||||||
{ // Only reset the selection if showing a different menu.
|
{ // Only reset the selection if showing a different menu.
|
||||||
|
@ -1018,7 +882,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
|
||||||
|
|
||||||
// And open the menu
|
// And open the menu
|
||||||
M_StartControlPanel (false);
|
M_StartControlPanel (false);
|
||||||
M_ActivateMenu(cmenu);
|
M_ActivateMenu((DMenu*)cmenu);
|
||||||
menuactive = MENU_OnNoPause;
|
menuactive = MENU_OnNoPause;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1255,8 +1119,7 @@ void P_ConversationCommand (int netcode, int pnum, BYTE **stream)
|
||||||
|
|
||||||
// The conversation menus are normally closed by the menu code, but that
|
// 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.
|
// doesn't happen during demo playback, so we need to do it here.
|
||||||
if (demoplayback && CurrentMenu != NULL &&
|
if (demoplayback && CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
|
||||||
CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
|
|
||||||
{
|
{
|
||||||
CurrentMenu->Close();
|
CurrentMenu->Close();
|
||||||
}
|
}
|
||||||
|
@ -1332,6 +1195,8 @@ DEFINE_FIELD(FStrifeDialogueNode, Backdrop);
|
||||||
DEFINE_FIELD(FStrifeDialogueNode, Dialogue);
|
DEFINE_FIELD(FStrifeDialogueNode, Dialogue);
|
||||||
DEFINE_FIELD(FStrifeDialogueNode, Goodbye);
|
DEFINE_FIELD(FStrifeDialogueNode, Goodbye);
|
||||||
DEFINE_FIELD(FStrifeDialogueNode, Children);
|
DEFINE_FIELD(FStrifeDialogueNode, Children);
|
||||||
|
DEFINE_FIELD(FStrifeDialogueNode, MenuClassName);
|
||||||
|
DEFINE_FIELD(FStrifeDialogueNode, UserData);
|
||||||
|
|
||||||
DEFINE_FIELD(FStrifeDialogueReply, Next);
|
DEFINE_FIELD(FStrifeDialogueReply, Next);
|
||||||
DEFINE_FIELD(FStrifeDialogueReply, GiveType);
|
DEFINE_FIELD(FStrifeDialogueReply, GiveType);
|
||||||
|
@ -1345,14 +1210,3 @@ DEFINE_FIELD(FStrifeDialogueReply, LogString);
|
||||||
DEFINE_FIELD(FStrifeDialogueReply, NextNode);
|
DEFINE_FIELD(FStrifeDialogueReply, NextNode);
|
||||||
DEFINE_FIELD(FStrifeDialogueReply, LogNumber);
|
DEFINE_FIELD(FStrifeDialogueReply, LogNumber);
|
||||||
DEFINE_FIELD(FStrifeDialogueReply, NeedsGold);
|
DEFINE_FIELD(FStrifeDialogueReply, NeedsGold);
|
||||||
|
|
||||||
|
|
||||||
DEFINE_FIELD(DConversationMenu, mSpeaker);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mDialogueLines);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mResponseLines);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mResponses);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mShowGold);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mCurNode);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mYpos);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mPlayer);
|
|
||||||
DEFINE_FIELD(DConversationMenu, mSelection);
|
|
||||||
|
|
|
@ -28,11 +28,13 @@ struct FStrifeDialogueNode
|
||||||
PClassActor *SpeakerType;
|
PClassActor *SpeakerType;
|
||||||
FString SpeakerName;
|
FString SpeakerName;
|
||||||
FSoundID SpeakerVoice;
|
FSoundID SpeakerVoice;
|
||||||
FTextureID Backdrop;
|
FString Backdrop;
|
||||||
FString Dialogue;
|
FString Dialogue;
|
||||||
FString Goodbye; // must init to null for binary scripts to work as intended
|
FString Goodbye; // must init to null for binary scripts to work as intended
|
||||||
|
|
||||||
FStrifeDialogueReply *Children;
|
FStrifeDialogueReply *Children;
|
||||||
|
FName MenuClassName;
|
||||||
|
FString UserData;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FStrifeDialogueReply holds responses the player can give to the NPC
|
// FStrifeDialogueReply holds responses the player can give to the NPC
|
||||||
|
|
|
@ -316,7 +316,14 @@ class USDFParser : public UDMFParserBase
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAME_Panel:
|
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;
|
break;
|
||||||
|
|
||||||
case NAME_Voice:
|
case NAME_Voice:
|
||||||
|
@ -391,6 +398,7 @@ class USDFParser : public UDMFParserBase
|
||||||
{
|
{
|
||||||
PClassActor *type = NULL;
|
PClassActor *type = NULL;
|
||||||
int dlgid = -1;
|
int dlgid = -1;
|
||||||
|
FName clsid;
|
||||||
unsigned int startpos = StrifeDialogues.Size();
|
unsigned int startpos = StrifeDialogues.Size();
|
||||||
|
|
||||||
while (!sc.CheckToken('}'))
|
while (!sc.CheckToken('}'))
|
||||||
|
@ -415,6 +423,13 @@ class USDFParser : public UDMFParserBase
|
||||||
dlgid = CheckInt(key);
|
dlgid = CheckInt(key);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NAME_Class:
|
||||||
|
if (namespace_bits == Zd)
|
||||||
|
{
|
||||||
|
clsid = CheckString(key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -440,6 +455,7 @@ class USDFParser : public UDMFParserBase
|
||||||
for(;startpos < StrifeDialogues.Size(); startpos++)
|
for(;startpos < StrifeDialogues.Size(); startpos++)
|
||||||
{
|
{
|
||||||
StrifeDialogues[startpos]->SpeakerType = type;
|
StrifeDialogues[startpos]->SpeakerType = type;
|
||||||
|
StrifeDialogues[startpos]->MenuClassName = clsid;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1219,6 +1219,14 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid)
|
||||||
ACTION_RETURN_STRING(s);
|
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)
|
DEFINE_ACTION_FUNCTION(FStringStruct, Truncate)
|
||||||
{
|
{
|
||||||
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
||||||
|
|
|
@ -580,6 +580,7 @@ struct StringStruct native
|
||||||
native vararg void AppendFormat(String fmt, ...);
|
native vararg void AppendFormat(String fmt, ...);
|
||||||
|
|
||||||
native void Replace(String pattern, String replacement);
|
native void Replace(String pattern, String replacement);
|
||||||
|
native String Left(int len);
|
||||||
native String Mid(int pos = 0, int len = 2147483647);
|
native String Mid(int pos = 0, int len = 2147483647);
|
||||||
native void Truncate(int newlen);
|
native void Truncate(int newlen);
|
||||||
native String CharAt(int pos);
|
native String CharAt(int pos);
|
||||||
|
|
|
@ -41,7 +41,7 @@ struct StrifeDialogueNode native
|
||||||
native Class<Actor> SpeakerType;
|
native Class<Actor> SpeakerType;
|
||||||
native String SpeakerName;
|
native String SpeakerName;
|
||||||
native Sound SpeakerVoice;
|
native Sound SpeakerVoice;
|
||||||
native TextureID Backdrop;
|
native String Backdrop;
|
||||||
native String Dialogue;
|
native String Dialogue;
|
||||||
native String Goodbye;
|
native String Goodbye;
|
||||||
|
|
||||||
|
@ -68,22 +68,146 @@ struct StrifeDialogueReply native
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ConversationMenu : Menu native
|
class ConversationMenu : Menu
|
||||||
{
|
{
|
||||||
native String mSpeaker;
|
String mSpeaker;
|
||||||
native BrokenLines mDialogueLines;
|
BrokenLines mDialogueLines;
|
||||||
native Array<String> mResponseLines;
|
Array<String> mResponseLines;
|
||||||
native Array<uint> mResponses;
|
Array<uint> mResponses;
|
||||||
native bool mShowGold;
|
bool mShowGold;
|
||||||
native StrifeDialogueNode mCurNode;
|
StrifeDialogueNode mCurNode;
|
||||||
native int mYpos;
|
int mYpos;
|
||||||
native PlayerInfo mPlayer;
|
PlayerInfo mPlayer;
|
||||||
native int mSelection;
|
int mSelection;
|
||||||
|
int ConversationPauseTic;
|
||||||
|
|
||||||
|
int SpeechWidth;
|
||||||
|
int ReplyWidth;
|
||||||
|
|
||||||
native static void SendConversationReply(int node, int reply);
|
native static void SendConversationReply(int node, int reply);
|
||||||
|
|
||||||
//ConversationPauseTic = gametic + 20;
|
const NUM_RANDOM_LINES = 10;
|
||||||
// DontDim = true;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
//
|
//
|
||||||
|
@ -233,9 +357,10 @@ class ConversationMenu : Menu native
|
||||||
|
|
||||||
virtual bool DrawBackdrop()
|
virtual bool DrawBackdrop()
|
||||||
{
|
{
|
||||||
if (mCurNode.Backdrop.isValid())
|
let tex = TexMan.CheckForTexture (mCurNode.Backdrop, TexMan.Type_MiscPatch);
|
||||||
|
if (tex.isValid())
|
||||||
{
|
{
|
||||||
screen.DrawTexture(mCurNode.Backdrop, false, 0, 0, DTA_320x200, true);
|
screen.DrawTexture(tex, false, 0, 0, DTA_320x200, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -381,9 +506,8 @@ class ConversationMenu : Menu native
|
||||||
|
|
||||||
override void Ticker()
|
override void Ticker()
|
||||||
{
|
{
|
||||||
// will be reactivated later.
|
|
||||||
// [CW] Freeze the game depending on MAPINFO options.
|
// [CW] Freeze the game depending on MAPINFO options.
|
||||||
//if (ConversationPauseTic < gametic && !multiplayer && !level.no_dlg_freeze)
|
if (ConversationPauseTic < gametic && !multiplayer && !level.no_dlg_freeze)
|
||||||
{
|
{
|
||||||
menuactive = Menu.On;
|
menuactive = Menu.On;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue