Add 'NAME' and 'ICON' control codes

This commit is contained in:
Lactozilla 2024-01-12 14:51:14 -03:00
parent 1b9bd6d275
commit e8b96f6dba
6 changed files with 221 additions and 104 deletions

View file

@ -2255,17 +2255,12 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
{
if (*word2 != '\0')
{
size_t j;
char name[256];
// HACK: Add yellow control char now
// so the drawing function doesn't call it repeatedly
char name[34];
name[0] = '\x82'; // color yellow
name[1] = 0;
strlcat(name, word2, sizeof(name));
strlcpy(name, word2, sizeof(name));
// Replace _ with ' '
for (j = 1; j < sizeof(name) && name[j]; j++)
for (size_t j = 0; j < sizeof(name) && name[j]; j++)
{
if (name[j] == '_')
name[j] = ' ';

View file

@ -249,7 +249,7 @@ typedef struct
char tag[33]; // page tag
char name[34]; // narrator name, extra char for color
char iconname[9]; // narrator icon lump
char iconname[256]; // narrator icon lump
boolean rightside; // narrator side, false = left, true = right
boolean iconflip; // narrator flip icon horizontally
UINT8 hidehud; // hide hud, 0 = show all, 1 = hide depending on prompt position (top/bottom), 2 = hide all

View file

@ -209,6 +209,11 @@ static huddrawlist_h luahuddrawlist_title;
static textwriter_t textwriter;
static void F_ResetTextWriter(textwriter_t *writer, const char *basetext)
{
P_ResetTextWriter(writer, basetext, strlen(basetext));
}
// =============
// INTRO SCENE
// =============
@ -414,7 +419,7 @@ void F_StartIntro(void)
gameaction = ga_nothing;
paused = false;
CON_ToggleOff();
P_ResetTextWriter(&textwriter, introtext[0]);
F_ResetTextWriter(&textwriter, introtext[0]);
intro_scenenum = 0;
finalecount = animtimer = stoptimer = 0;
@ -847,7 +852,7 @@ void F_IntroTicker(void)
return;
}
P_ResetTextWriter(&textwriter, introtext[++intro_scenenum]);
F_ResetTextWriter(&textwriter, introtext[++intro_scenenum]);
timetonext = introscenetime[intro_scenenum];
F_WipeStartScreen();
@ -3795,7 +3800,7 @@ static void F_AdvanceToNextScene(void)
scene->musswitchposition, 0, 0);
// Fade to the next
P_ResetTextWriter(&textwriter, scene->text);
F_ResetTextWriter(&textwriter, scene->text);
picnum = 0;
pic = &scene->pics[picnum];
@ -3851,7 +3856,7 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
paused = false;
CON_ToggleOff();
P_ResetTextWriter(&textwriter, cutscenes[cutscenenum]->scene[0].text);
F_ResetTextWriter(&textwriter, cutscenes[cutscenenum]->scene[0].text);
cutsceneover = false;
runningprecutscene = precutscene;

View file

@ -118,9 +118,10 @@ UINT8 P_CutsceneWriteText(textwriter_t *writer)
return 1;
}
void P_ResetTextWriter(textwriter_t *writer, const char *basetext)
void P_ResetTextWriter(textwriter_t *writer, const char *basetext, size_t basetextlength)
{
writer->basetext = basetext;
writer->basetextlength = basetextlength;
writer->writeptr = writer->baseptr = 0;
writer->textspeed = 9;
writer->textcount = TICRATE/2;
@ -229,14 +230,17 @@ typedef struct
static void F_GetPageTextGeometry(dialog_t *dialog, dialog_geometry_t *geo)
{
lumpnum_t iconlump = W_CheckNumForName(dialog->page->iconname);
lumpnum_t iconlump = LUMPERROR;
if (dialog->icon[0])
iconlump = W_CheckNumForLongName(dialog->icon);
INT32 pagelines = dialog->page->lines ? dialog->page->lines : 4;
boolean rightside = (iconlump != LUMPERROR && dialog->page->rightside);
boolean rightside = iconlump != LUMPERROR && dialog->page->rightside;
// Vertical calculations
INT32 boxh = pagelines*2;
INT32 texth = dialog->page->name[0] ? (pagelines-1)*2 : pagelines*2; // name takes up first line if it exists
INT32 texth = dialog->speaker[0] ? (pagelines-1)*2 : pagelines*2; // name takes up first line if it exists
INT32 namey = BASEVIDHEIGHT - ((boxh * 4) + (boxh/2)*4);
geo->texty = BASEVIDHEIGHT - ((texth * 4) + (texth/2)*4);
@ -390,6 +394,18 @@ void P_SetPicsMetaPage(textpage_t *page, textpage_t *metapage)
memcpy(page->pics, metapage->pics, sizeof(cutscene_pic_t) * page->numpics);
}
static void P_SetDialogSpeaker(dialog_t *dialog, const char *speaker)
{
char name[256];
// Add yellow control char
name[0] = '\x82';
name[1] = 0;
strlcat(name, speaker, sizeof(name));
strlcpy(dialog->speaker, name, sizeof(dialog->speaker));
}
#define READ_NUM(where) { \
int temp = 0; \
temp |= (*code) << 24; code++; \
@ -399,19 +415,35 @@ void P_SetPicsMetaPage(textpage_t *page, textpage_t *metapage)
where = temp; \
}
static char *BytecodeReadString(const UINT8 **code)
{
const UINT8 *c = *code;
UINT8 sz = *c++;
if (!sz)
return NULL;
char *str = Z_Malloc(sz + 1, PU_STATIC, NULL);
char *str_p = str;
while (sz--)
*str_p++ = *c++;
*str_p = '\0';
*code = c;
return str;
}
// Dialog control codes
enum
{
TP_OP_SPEED,
TP_OP_DELAY,
TP_OP_NAME,
TP_OP_ICON,
TP_OP_CONTROL = 0xFF
};
static const UINT8 *InterpretBytecode(const UINT8 *code, dialog_t *dialog, textwriter_t *writer)
{
(void)dialog;
int num = 0;
switch (*code++)
@ -425,6 +457,24 @@ static const UINT8 *InterpretBytecode(const UINT8 *code, dialog_t *dialog, textw
writer->textcount = num;
writer->numtowrite = 0;
break;
case TP_OP_NAME: {
char *speaker = BytecodeReadString(&code);
if (speaker)
{
P_SetDialogSpeaker(dialog, speaker);
Z_Free(speaker);
}
break;
}
case TP_OP_ICON: {
char *icon = BytecodeReadString(&code);
if (icon)
{
strlcpy(dialog->icon, icon, sizeof(dialog->icon));
Z_Free(icon);
}
break;
}
}
return code;
@ -440,6 +490,12 @@ static int SkipBytecode(const UINT8 *code)
case TP_OP_DELAY:
code += 4;
break;
case TP_OP_NAME:
case TP_OP_ICON: {
UINT8 n = *code++;
code += n;
break;
}
}
return code - baseptr;
@ -481,10 +537,10 @@ enum
static UINT8 P_DialogWriteText(dialog_t *dialog, textwriter_t *writer)
{
const UINT8 *baseptr = (const UINT8*)writer->basetext;
if (!baseptr)
if (!baseptr || writer->baseptr >= writer->basetextlength)
return DIALOG_WRITETEXT_DONE;
UINT8 lastchar = 0;
UINT8 lastchar = '\0';
writer->numtowrite = 1;
@ -500,21 +556,18 @@ static UINT8 P_DialogWriteText(dialog_t *dialog, textwriter_t *writer)
writer->numtowrite = 8 - writer->textspeed;
}
while (true)
while (writer->numtowrite > 0)
{
if (writer->numtowrite <= 0)
break;
const UINT8 *c = &baseptr[writer->baseptr];
if (!*c)
return DIALOG_WRITETEXT_DONE;
const UINT8 *code = (const UINT8*)c;
if (*code == TP_OP_CONTROL)
{
code = InterpretBytecode(code + 1, dialog, writer);
writer->baseptr = code - baseptr;
lastchar = '\0';
continue;
}
@ -523,7 +576,11 @@ static UINT8 P_DialogWriteText(dialog_t *dialog, textwriter_t *writer)
writer->writeptr++;
writer->baseptr++;
// Ignore other control codes (color)
if (writer->numtowrite <= 0)
break;
// Ignore non-printable characters
// (that is, decrement numtowrite if this character is printable.)
if (lastchar < 0x80)
--writer->numtowrite;
}
@ -537,7 +594,7 @@ static UINT8 P_DialogWriteText(dialog_t *dialog, textwriter_t *writer)
writer->textcount = writer->textspeed - 7;
}
if (!lastchar || isspace(lastchar))
if (lastchar == '\0' || isspace(lastchar))
return DIALOG_WRITETEXT_SILENT;
else
return DIALOG_WRITETEXT_TALK;
@ -554,17 +611,21 @@ static void P_DialogSetDisplayText(dialog_t *dialog)
V_WordWrapInPlace(geo.textx, geo.textr, 0, dialog->disptext);
}
void P_DialogSetText(dialog_t *dialog, char *pagetext, size_t textlength, INT32 numchars)
static char *P_ProcessPageText(char *pagetext, size_t textlength, size_t *outlength)
{
*outlength = textlength;
char *buf = Z_Calloc(textlength + 1, PU_STATIC, NULL);
memcpy(buf, pagetext, textlength);
buf[textlength] = '\0';
return buf;
}
void P_DialogSetText(dialog_t *dialog, char *pagetext, size_t textlength)
{
Z_Free(dialog->pagetext);
if (pagetext && pagetext[0])
{
dialog->pagetextlength = textlength;
dialog->pagetext = Z_Calloc(textlength + 1, PU_STATIC, NULL);
memcpy(dialog->pagetext, pagetext, textlength);
dialog->pagetext[textlength] = '\0';
}
dialog->pagetext = P_ProcessPageText(pagetext, textlength, &dialog->pagetextlength);
else
{
dialog->pagetextlength = 0;
@ -577,37 +638,24 @@ void P_DialogSetText(dialog_t *dialog, char *pagetext, size_t textlength, INT32
textwriter_t *writer = &dialog->writer;
P_ResetTextWriter(writer, dialog->pagetext);
P_ResetTextWriter(writer, dialog->pagetext, dialog->pagetextlength);
writer->textspeed = dialog->page->textspeed ? dialog->page->textspeed : TICRATE/5;
writer->textcount = 0; // no delay in beginning
writer->boostspeed = false; // don't print 8 characters to start
P_DialogSetDisplayText(dialog);
if (numchars <= 0)
return;
while (writer->writeptr < numchars)
{
P_DialogWriteText(dialog, writer);
const char *c = &writer->basetext[writer->baseptr];
if (!*c)
break;
}
}
static void P_PreparePageText(dialog_t *dialog, char *pagetext, size_t textlength)
{
P_DialogSetText(dialog, pagetext, textlength, 0);
P_DialogSetText(dialog, pagetext, textlength);
// \todo update control hot strings on re-config
// and somehow don't reset cutscene text counters
}
static void P_DialogStartPage(dialog_t *dialog)
static void P_DialogStartPage(player_t *player, dialog_t *dialog)
{
P_PreparePageText(dialog, dialog->page->text, dialog->page->textlength);
@ -658,7 +706,16 @@ static void P_DialogStartPage(dialog_t *dialog)
dialog->picmode = 0;
}
// Set up narrator
P_SetDialogSpeaker(dialog, dialog->page->name);
strlcpy(dialog->icon, dialog->page->iconname, sizeof(dialog->icon));
dialog->iconflip = dialog->page->iconflip;
// music change
if (P_IsLocalPlayer(player) || globaltextprompt)
{
if (dialog->page->musswitch[0])
{
S_ChangeMusic(dialog->page->musswitch,
@ -672,6 +729,7 @@ static void P_DialogStartPage(dialog_t *dialog)
true);
}
}
}
static boolean P_LoadNextPageAndPrompt(player_t *player, dialog_t *dialog, INT32 nextprompt, INT32 nextpage)
{
@ -735,7 +793,7 @@ static boolean P_LoadNextPageAndPrompt(player_t *player, dialog_t *dialog, INT32
return false;
}
P_DialogStartPage(dialog);
P_DialogStartPage(player, dialog);
return true;
}
@ -1009,7 +1067,7 @@ void P_StartTextPrompt(player_t *player, INT32 promptnum, INT32 pagenum, UINT16
dialog->prompt = textprompts[dialog->promptnum];
dialog->page = &dialog->prompt->page[dialog->pagenum];
P_DialogStartPage(dialog);
P_DialogStartPage(player, dialog);
}
else
{
@ -1216,7 +1274,7 @@ void F_TextPromptDrawer(void)
if (!dialog)
return;
lumpnum_t iconlump;
lumpnum_t iconlump = LUMPERROR;
dialog_geometry_t geo;
INT32 draw_flags = V_PERPLAYER;
@ -1225,7 +1283,6 @@ void F_TextPromptDrawer(void)
// Data
patch_t *patch;
iconlump = W_CheckNumForName(dialog->page->iconname);
F_GetPageTextGeometry(dialog, &geo);
boolean rightside = geo.rightside;
@ -1250,10 +1307,13 @@ void F_TextPromptDrawer(void)
V_DrawPromptBack(boxh, dialog->page->backcolor, draw_flags|snap_flags);
// Draw narrator icon
if (dialog->icon[0])
iconlump = W_CheckNumForLongName(dialog->icon);
if (iconlump != LUMPERROR)
{
INT32 iconx, icony, scale, scaledsize;
patch = W_CachePatchLongName(dialog->page->iconname, PU_PATCH_LOWPRIORITY);
patch = W_CachePatchLongName(dialog->icon, PU_PATCH_LOWPRIORITY);
// scale and center
if (patch->width > patch->height)
@ -1278,10 +1338,10 @@ void F_TextPromptDrawer(void)
icony = namey << FRACBITS;
}
if (dialog->page->iconflip)
if (dialog->iconflip)
iconx += FixedMul(patch->width, scale) << FRACBITS;
V_DrawFixedPatch(iconx, icony, scale, (draw_flags|snap_flags|(dialog->page->iconflip ? V_FLIP : 0)), patch, NULL);
V_DrawFixedPatch(iconx, icony, scale, (draw_flags|snap_flags|(dialog->iconflip ? V_FLIP : 0)), patch, NULL);
W_UnlockCachedPatch(patch);
}
@ -1291,8 +1351,8 @@ void F_TextPromptDrawer(void)
// Draw name
// Don't use V_YELLOWMAP here so that the name color can be changed with control codes
if (dialog->page->name[0])
V_DrawString(textx, namey, (draw_flags|snap_flags|V_ALLOWLOWERCASE), dialog->page->name);
if (dialog->speaker[0])
V_DrawString(textx, namey, (draw_flags|snap_flags|V_ALLOWLOWERCASE), dialog->speaker);
// Draw choices
if (dialog->showchoices)
@ -2103,6 +2163,14 @@ static void GetControlCodeEnd(char **input)
WRITE_CHAR((n >> 8) & 0xFF); \
WRITE_CHAR((n) & 0xFF); \
}
#define WRITE_STRING(str) { \
size_t maxlen = strlen(str); \
if (maxlen > 255) \
maxlen = 255; \
WRITE_CHAR(maxlen); \
for (size_t i = 0; i < maxlen; i++) \
WRITE_CHAR(str[i]); \
}
#define EXPECTED_NUMBER(which) CONS_Alert(CONS_WARNING, "Expected integer in '%s', got '%s' instead\n", which, param)
#define EXPECTED_PARAM(which) CONS_Alert(CONS_WARNING, "Expected parameter for '%s'\n", which)
@ -2143,6 +2211,32 @@ static void InterpretControlCode(char **input, UINT8 **buf, size_t *buffer_pos,
else
EXPECTED_PARAM("SPEED");
}
else if (MatchControlCode("NAME", input))
{
char *param = GetControlCodeParam(input);
if (param)
{
WRITE_OP(TP_OP_NAME);
WRITE_STRING(param);
Z_Free(param);
}
else
EXPECTED_PARAM("NAME");
}
else if (MatchControlCode("ICON", input))
{
char *param = GetControlCodeParam(input);
if (param)
{
WRITE_OP(TP_OP_ICON);
WRITE_STRING(param);
Z_Free(param);
}
else
EXPECTED_PARAM("ICON");
}
}
static boolean InterpretCutsceneControlCode(UINT8 chr, UINT8 **buf, size_t *buffer_pos, size_t *buffer_capacity)
@ -2287,11 +2381,7 @@ static void ParsePage(textprompt_t *prompt, tokenizer_t *sc)
GET_TOKEN();
char name[34];
name[0] = '\x82'; // color yellow
name[1] = 0;
strlcat(name, tkn, sizeof(name));
strlcpy(page->name, name, sizeof(page->name));
strlcpy(page->name, tkn, sizeof(page->name));
EXPECT_TOKEN(";");
}

View file

@ -25,10 +25,11 @@
typedef struct
{
const char *basetext;
size_t basetextlength;
char *disptext;
size_t disptextsize;
INT32 baseptr;
INT32 writeptr;
UINT32 baseptr;
UINT32 writeptr;
INT32 textcount;
INT32 textspeed;
INT32 numtowrite;
@ -36,7 +37,7 @@ typedef struct
} textwriter_t;
UINT8 P_CutsceneWriteText(textwriter_t *writer);
void P_ResetTextWriter(textwriter_t *writer, const char *basetext);
void P_ResetTextWriter(textwriter_t *writer, const char *basetext, size_t basetextlength);
//
// PROMPT STATE
@ -45,23 +46,32 @@ typedef struct dialog_s
{
INT32 promptnum;
INT32 pagenum;
textprompt_t *prompt;
textpage_t *page;
INT32 timetonext;
player_t *callplayer;
textwriter_t writer;
INT16 postexectag;
boolean blockcontrols;
char *pagetext;
size_t pagetextlength;
char *disptext;
size_t disptextsize;
player_t *callplayer;
boolean blockcontrols;
INT32 timetonext;
INT16 postexectag;
INT32 picnum;
INT32 pictoloop;
INT32 pictimer;
INT32 picmode;
INT32 numpics;
cutscene_pic_t *pics;
char speaker[256];
char icon[256];
boolean iconflip;
boolean showchoices;
INT32 curchoice;
INT32 numchoices;
@ -77,7 +87,7 @@ void P_EndTextPrompt(player_t *player, boolean forceexec, boolean noexec);
void P_EndAllTextPrompts(boolean forceexec, boolean noexec);
void P_RunDialog(player_t *player);
void P_FreeDialog(dialog_t *dialog);
void P_DialogSetText(dialog_t *dialog, char *pagetext, size_t textlength, INT32 numchars);
void P_DialogSetText(dialog_t *dialog, char *pagetext, size_t textlength);
void P_DialogUpdateLongestChoice(dialog_t *dialog);
boolean P_SetCurrentDialogChoice(player_t *player, INT32 choice);

View file

@ -580,7 +580,7 @@ static void P_NetUnArchivePlayers(void)
if (globaltextprompt)
players[i].textprompt = globaltextprompt;
else
else if (players[i].promptactive)
{
players[i].textprompt = Z_Calloc(sizeof(dialog_t), PU_LEVEL, NULL);
P_NetUnArchiveDialog(players[i].textprompt);
@ -4951,17 +4951,23 @@ static void P_NetArchiveDialog(dialog_t *dialog)
WRITEINT32(save_p, dialog->picnum);
WRITEINT32(save_p, dialog->pictoloop);
WRITEINT32(save_p, dialog->pictimer);
WRITEINT32(save_p, dialog->writer.writeptr);
WRITEUINT32(save_p, dialog->writer.baseptr);
WRITEUINT32(save_p, dialog->writer.writeptr);
WRITEINT32(save_p, dialog->writer.textcount);
WRITEINT32(save_p, dialog->writer.textspeed);
WRITEINT32(save_p, dialog->writer.boostspeed);
WRITEUINT8(save_p, (UINT8)dialog->writer.boostspeed);
WRITESTRINGN(save_p, dialog->speaker, sizeof(dialog->speaker) - 1);
WRITESTRINGN(save_p, dialog->icon, sizeof(dialog->icon) - 1);
WRITEUINT8(save_p, (UINT8)dialog->iconflip);
}
static void P_NetUnArchiveDialog(dialog_t *dialog)
{
UINT8 playernum;
INT32 numchars, textcount, textspeed, boostspeed;
UINT32 baseptr, writeptr;
INT32 textcount, textspeed;
char speaker[256], icon[256];
boolean iconflip, boostspeed;
if (dialog == NULL)
I_Error("P_NetUnArchiveDialog: dialog == NULL");
@ -4993,10 +4999,15 @@ static void P_NetUnArchiveDialog(dialog_t *dialog)
dialog->picnum = READINT32(save_p);
dialog->pictoloop = READINT32(save_p);
dialog->pictimer = READINT32(save_p);
numchars = READINT32(save_p);
baseptr = READUINT32(save_p);
writeptr = READUINT32(save_p);
textcount = READINT32(save_p);
textspeed = READINT32(save_p);
boostspeed = READINT32(save_p);
boostspeed = (boolean)READUINT8(save_p);
READSTRINGN(save_p, speaker, sizeof(speaker) - 1);
READSTRINGN(save_p, icon, sizeof(icon) - 1);
iconflip = (boolean)READUINT8(save_p);
if (dialog->promptnum < 0 || dialog->promptnum >= MAX_PROMPTS || !textprompts[dialog->promptnum])
I_Error("Invalid text prompt %d from server", dialog->promptnum);
@ -5020,8 +5031,14 @@ static void P_NetUnArchiveDialog(dialog_t *dialog)
I_Error("Invalid text prompt nochoice %d from server", dialog->nochoice);
}
P_DialogSetText(dialog, dialog->page->text, dialog->page->textlength, numchars);
P_DialogSetText(dialog, dialog->page->text, dialog->page->textlength);
strlcpy(dialog->speaker, speaker, sizeof(dialog->speaker));
strlcpy(dialog->icon, icon, sizeof(dialog->icon));
dialog->iconflip = iconflip;
dialog->writer.baseptr = baseptr;
dialog->writer.writeptr = writeptr;
dialog->writer.textcount = textcount;
dialog->writer.textspeed = textspeed;
dialog->writer.boostspeed = boostspeed;