Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Jisk/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
117 results
Select Git revision
Show changes
Commits on Source (45)
===============================================================================
USDF-SRB2: SRB2 Strife Dialog Format v1.0
based on GZDoom Strife Dialog Format v1.0 - 2019
Copyright (c) 2024 Sonic Team Junior
uses GZDoom Strife Dialog Format v1.0 as a template,
original document Copyright (c) 2019 Rachael Alexanderson.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
===============================================================================
=======================================
I. Grammar / Syntax
=======================================
No changes.
=======================================
II. Implementation Semantics
=======================================
No changes.
=======================================
III. Changes to GZSDF spec
=======================================
SRB2 Strife Dialogue Format implements most of SRB2's text prompt features,
while using the GZSDF format as a base.
SRB2-format dialogues need to start with this line:
namespace = "srb2";
---------------------
III.A : Conversations
---------------------
conversation // Starts a dialog.
{
id = <string|int>; // Assigns an ID to a dialogue.
// If int, this corresponds to an SRB2 text prompt number.
page // Starts a new page. Pages are automatically numbered starting at 1.
{
pagename = <string>; // Name of the page. For use with next.
name = <string>; // Name of the speaker.
icon = <string>; // Icon lump.
textsound = <string>; // The sound that is played when the text is typed.
dialog = <string>; // Dialog of the page.
nextpage = <string|int>; // Sets the next page.
nextconversation = <string|int>; // Sets the next conversation.
nexttag = <string>; // Jumps to a page with this tag.
duration = <integer>; // Duration in frames of this page.
textspeed = <integer>; // The speed which the text is typed at.
// Default: 1/5th of a second
textlines = <integer>; // How many lines of text to display.
// Default: 4
iconside = <integer>; // Which side to display the icon.
// This must be either "left" or "right".
// Default: "left"
flipicon = <bool>; // Whether to flip the icon horizontally.
// Default: false
displayhud = <string>; // Whether or not to display the HUD. This must one of:
// - "show"
// - "hide"
// - "hideall"
// Default: "hide"
backcolor = <string>; // The color to use for the text box background.
// Default: "gray"
alignchoices = <string>; // Which side the dialog choices are displayed at.
// This must be either "left" or "right".
// Default: "right"
picturesequence = <string>; // How to play the picture sequence. This must one of:
// - "persist"
// - "loop"
// - "hide"
// Default: "persist"
music = <string>; // Which music to play.
musictrack = <integer>; // Which music track to play.
loopmusic = <bool>; // Whether to loop the music.
// Default: false
executelinedef = <integer>; // Executes every linedef with this tag. Execution is ignored if 0.
restoremusic = <bool>; // Whether to restore the map's music.
closedialog = <bool>; // Should the dialog be closed after this page?
// Default: false
// Choices shall be automatically numbered.
choice
{
text = <string>; // Name of the choice.
nextpage = <string|int>; // Sets the next page.
nextconversation = <string|int>; // Sets the next conversation.
nexttag = <string>; // Jumps to a page with this tag.
executelinedef = <integer>; // Executes every linedef with this tag. Execution is ignored if 0.
highlighted = <bool>; // Whether this is the choice to be highlighted
// when the list of choices appear.
// Default: false
nochoice = <bool>; // Is this the "No" choice?
// Default: false
closedialog = <bool>; // Should the dialog be closed upon selecting this choice?
// Default: false
}
// Used to configure a picture sequence.
picture
{
name = <string>; // Lump name of the picture.
x = <integer>; // X coordinate.
y = <integer>; // Y coordinate.
duration = <integer>; // Duration.
hires = <bool>; // Should the picture be displayed at 0.5 scale?
// Default: false
start = <bool>; // Is this the first picture in the sequence?
// Default: false
looppoint = <bool>; // Is this the loop point in the sequence?
// Default: false
}
}
}
===============================================================================
EOF
===============================================================================
\ No newline at end of file
......@@ -37,8 +37,11 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
m_random.c
m_tokenizer.c
m_queue.c
m_writebuffer.c
info.c
p_ceilng.c
p_dialog.c
p_dialogscript.c
p_enemy.c
p_floor.c
p_inter.c
......@@ -76,6 +79,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
r_portal.c
screen.c
taglist.c
usdf.c
v_video.c
s_sound.c
sounds.c
......
......@@ -31,8 +31,11 @@ m_perfstats.c
m_random.c
m_tokenizer.c
m_queue.c
m_writebuffer.c
info.c
p_ceilng.c
p_dialog.c
p_dialogscript.c
p_enemy.c
p_floor.c
p_inter.c
......@@ -70,6 +73,7 @@ r_picformats.c
r_portal.c
screen.c
taglist.c
usdf.c
v_video.c
s_sound.c
sounds.c
......
......@@ -970,7 +970,7 @@ boolean CON_Responder(event_t *ev)
// let go keyup events, don't eat them
if (ev->type != ev_keydown && ev->type != ev_text && ev->type != ev_console)
{
if (ev->key == gamecontrol[GC_CONSOLE][0] || ev->key == gamecontrol[GC_CONSOLE][1])
if (G_IsGameControl(ev->key, GC_CONSOLE))
consdown = false;
return false;
}
......@@ -983,7 +983,7 @@ boolean CON_Responder(event_t *ev)
if (modeattacking || metalrecording || marathonmode)
return false;
if ((key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) && !shiftdown)
if (G_IsGameControl(key, GC_CONSOLE) && !shiftdown)
{
if (consdown) // ignore repeat
return true;
......
......@@ -48,6 +48,7 @@
#include "m_misc.h"
#include "p_setup.h"
#include "p_saveg.h"
#include "usdf.h"
#include "r_main.h"
#include "r_local.h"
#include "r_translation.h"
......@@ -556,7 +557,6 @@ static void D_Display(void)
if (gamestate == GS_LEVEL)
{
ST_Drawer();
F_TextPromptDrawer();
HU_Drawer();
}
else
......@@ -1523,6 +1523,10 @@ void D_SRB2Main(void)
savedata.lives = 0; // flag this as not-used
// Parse all DIALOGUE lumps
for (UINT16 w = 0; w < numwadfiles; w++)
P_LoadDialogueLumps(w);
//------------------------------------------------ COMMAND LINE PARAMS
// this must be done after loading gamedata,
......
......@@ -609,6 +609,9 @@ typedef struct player_s
botmem_t botmem;
boolean blocked;
boolean promptactive;
struct dialog_s *textprompt;
tic_t jointime; // Timer when player joins game to change skin/color
tic_t quittime; // Time elapsed since user disconnected, zero if connected
tic_t lastinputtime; // the last tic the player has made any input
......
......@@ -26,6 +26,8 @@
#include "st_stuff.h"
#include "i_system.h"
#include "p_setup.h"
#include "p_dialog.h"
#include "usdf.h"
#include "r_data.h"
#include "r_textures.h"
#include "r_draw.h"
......@@ -52,53 +54,10 @@
fixed_t get_number(const char *word)
{
return LUA_EvalMath(word);
/*// DESPERATELY NEEDED: Order of operations support! :x
fixed_t i = find_const(&word);
INT32 o;
while(*word) {
o = operation_pad(&word);
if (o != -1)
i = OPERATIONS[o].v(i,find_const(&word));
else
break;
}
return i;*/
}
#define PARAMCHECK(n) do { if (!params[n]) { deh_warning("Too few parameters, need %d", n); return; }} while (0)
/* ======================================================================== */
// Load a dehacked file format
/* ======================================================================== */
/* a sample to see
Thing 1 (Player) { // MT_PLAYER
INT32 doomednum; ID # = 3232 -1, // doomednum
INT32 spawnstate; Initial frame = 32 "PLAY", // spawnstate
INT32 spawnhealth; Hit points = 3232 100, // spawnhealth
INT32 seestate; First moving frame = 32 "PLAY_RUN1", // seestate
INT32 seesound; Alert sound = 32 sfx_None, // seesound
INT32 reactiontime; Reaction time = 3232 0, // reactiontime
INT32 attacksound; Attack sound = 32 sfx_None, // attacksound
INT32 painstate; Injury frame = 32 "PLAY_PAIN", // painstate
INT32 painchance; Pain chance = 3232 255, // painchance
INT32 painsound; Pain sound = 32 sfx_plpain, // painsound
INT32 meleestate; Close attack frame = 32 "NULL", // meleestate
INT32 missilestate; Far attack frame = 32 "PLAY_ATK1", // missilestate
INT32 deathstate; Death frame = 32 "PLAY_DIE1", // deathstate
INT32 xdeathstate; Exploding frame = 32 "PLAY_XDIE1", // xdeathstate
INT32 deathsound; Death sound = 32 sfx_pldeth, // deathsound
INT32 speed; Speed = 3232 0, // speed
INT32 radius; Width = 211812352 16*FRACUNIT, // radius
INT32 height; Height = 211812352 56*FRACUNIT, // height
INT32 dispoffset; DispOffset = 0 0, // dispoffset
INT32 mass; Mass = 3232 100, // mass
INT32 damage; Missile damage = 3232 0, // damage
INT32 activesound; Action sound = 32 sfx_None, // activesound
INT32 flags; Bits = 3232 MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH,
INT32 raisestate; Respawn frame = 32 S_NULL // raisestate
}, */
#ifdef HWRENDER
static INT32 searchvalue(const char *s)
{
......@@ -1887,6 +1846,34 @@ void readlevelheader(MYFILE *f, INT32 num)
Z_Free(s);
}
static boolean ParseCutscenePic(cutscene_pic_t *pic, UINT16 usi, const char *word, char *word2)
{
if (fastcmp(word, "NAME"))
{
strlcpy(pic->name, word2, sizeof(pic->name));
}
else if (fastcmp(word, "HIRES"))
{
pic->hires = (UINT8)(usi || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word, "DURATION"))
{
pic->duration = usi;
}
else if (fastcmp(word, "XCOORD"))
{
pic->xcoord = usi;
}
else if (fastcmp(word, "YCOORD"))
{
pic->ycoord = usi;
}
else
return false;
return true;
}
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
{
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
......@@ -1969,7 +1956,6 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
i = atoi(word2);
usi = (UINT16)i;
if (fastcmp(word, "NUMBEROFPICS"))
{
cutscenes[num]->scene[scenenum].numpics = (UINT8)i;
......@@ -1977,40 +1963,21 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
else if (fastncmp(word, "PIC", 3))
{
picid = (UINT8)atoi(word + 3);
if (picid > 8 || picid == 0)
if (picid > MAX_CUTSCENE_PICS || picid == 0)
{
deh_warning("CutSceneScene %d: unknown word '%s'", num, word);
continue;
}
--picid;
if (fastcmp(word+4, "NAME"))
{
strncpy(cutscenes[num]->scene[scenenum].picname[picid], word2, 8);
}
else if (fastcmp(word+4, "HIRES"))
{
cutscenes[num]->scene[scenenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word+4, "DURATION"))
{
cutscenes[num]->scene[scenenum].picduration[picid] = usi;
}
else if (fastcmp(word+4, "XCOORD"))
{
cutscenes[num]->scene[scenenum].xcoord[picid] = usi;
}
else if (fastcmp(word+4, "YCOORD"))
{
cutscenes[num]->scene[scenenum].ycoord[picid] = usi;
}
else
cutscene_pic_t *pic = &cutscenes[num]->scene[scenenum].pics[picid];
if (!ParseCutscenePic(pic, usi, word+4, word2))
deh_warning("CutSceneScene %d: unknown word '%s'", num, word);
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7);
cutscenes[num]->scene[scenenum].musswitch[6] = 0;
strlcpy(cutscenes[num]->scene[scenenum].musswitch, word2, sizeof(cutscenes[num]->scene[scenenum].musswitch));
}
else if (fastcmp(word, "MUSICTRACK"))
{
......@@ -2114,6 +2081,25 @@ void readcutscene(MYFILE *f, INT32 num)
Z_Free(s);
}
static char *gettextpromptpicnum(const char *word)
{
const char *num_start = word;
const char *num_end = num_start;
while (isdigit(*num_end))
num_end++;
size_t count = num_end - num_start;
char *buf = malloc(count + 1);
if (!buf)
return NULL;
strncpy(buf, num_start, count);
buf[count] = '\0';
return buf;
}
static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
{
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
......@@ -2121,7 +2107,8 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
char *word2;
INT32 i;
UINT16 usi;
UINT8 picid;
textpage_t *page = &textprompts[num]->page[pagenum];
do
{
......@@ -2139,8 +2126,6 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
if (fastcmp(word, "PAGETEXT"))
{
char *pagetext = NULL;
char *buffer;
const int bufferlen = 4096;
for (i = 0; i < MAXLINELEN; i++)
{
......@@ -2153,8 +2138,9 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
if (!pagetext)
{
Z_Free(textprompts[num]->page[pagenum].text);
textprompts[num]->page[pagenum].text = NULL;
Z_Free(page->text);
page->text = NULL;
page->textlength = 0;
continue;
}
......@@ -2168,22 +2154,17 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
}
}
buffer = Z_Malloc(4096, PU_STATIC, NULL);
strcpy(buffer, pagetext);
const int bufferlen = 4096;
// \todo trim trailing whitespace before the #
// and also support # at the end of a PAGETEXT with no line break
char *buffer = Z_Malloc(bufferlen, PU_STATIC, NULL);
strcat(buffer,
myhashfgets(pagetext, bufferlen
- strlen(buffer) - 1, f));
strlcpy(buffer, pagetext, bufferlen);
strlcat(buffer, myhashfgets(pagetext, MAXLINELEN, f), bufferlen);
// A text prompt overwriting another one...
Z_Free(textprompts[num]->page[pagenum].text);
textprompts[num]->page[pagenum].text = Z_StrDup(buffer);
Z_Free(page->text);
Z_Free(buffer);
page->text = P_ConvertSOCPageDialog(buffer, &page->textlength);
continue;
}
......@@ -2202,206 +2183,169 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
// copypasta from readcutscenescene
if (fastcmp(word, "NUMBEROFPICS"))
{
textprompts[num]->page[pagenum].numpics = (UINT8)i;
if (i < 0 || i > MAX_PROMPT_PICS)
{
deh_warning("textpromptscene %d: invalid number of pictures %d (maximum is %d)'", num, i, MAX_PROMPT_PICS);
continue;
}
page->numpics = (UINT8)i;
if (!page->numpics)
Z_Free(page->pics);
else
page->pics = Z_Realloc(page->pics, sizeof(cutscene_pic_t) * page->numpics, PU_STATIC, NULL);
}
else if (fastcmp(word, "PICMODE"))
{
UINT8 picmode = 0; // PROMPT_PIC_PERSIST
if (usi == 1 || word2[0] == 'L') picmode = PROMPT_PIC_LOOP;
else if (usi == 2 || word2[0] == 'D' || word2[0] == 'H') picmode = PROMPT_PIC_DESTROY;
textprompts[num]->page[pagenum].picmode = picmode;
page->picmode = picmode;
}
else if (fastcmp(word, "PICTOLOOP"))
textprompts[num]->page[pagenum].pictoloop = (UINT8)i;
page->pictoloop = (UINT8)i;
else if (fastcmp(word, "PICTOSTART"))
textprompts[num]->page[pagenum].pictostart = (UINT8)i;
page->pictostart = (UINT8)i;
else if (fastcmp(word, "PICSMETAPAGE"))
{
if (usi && usi <= textprompts[num]->numpages)
{
UINT8 metapagenum = usi - 1;
textprompts[num]->page[pagenum].numpics = textprompts[num]->page[metapagenum].numpics;
textprompts[num]->page[pagenum].picmode = textprompts[num]->page[metapagenum].picmode;
textprompts[num]->page[pagenum].pictoloop = textprompts[num]->page[metapagenum].pictoloop;
textprompts[num]->page[pagenum].pictostart = textprompts[num]->page[metapagenum].pictostart;
for (picid = 0; picid < MAX_PROMPT_PICS; picid++)
{
strncpy(textprompts[num]->page[pagenum].picname[picid], textprompts[num]->page[metapagenum].picname[picid], 8);
textprompts[num]->page[pagenum].pichires[picid] = textprompts[num]->page[metapagenum].pichires[picid];
textprompts[num]->page[pagenum].picduration[picid] = textprompts[num]->page[metapagenum].picduration[picid];
textprompts[num]->page[pagenum].xcoord[picid] = textprompts[num]->page[metapagenum].xcoord[picid];
textprompts[num]->page[pagenum].ycoord[picid] = textprompts[num]->page[metapagenum].ycoord[picid];
}
}
if (usi > 0 && usi <= textprompts[num]->numpages)
P_SetPicsMetaPage(page, &textprompts[num]->page[usi - 1]);
}
else if (fastncmp(word, "PIC", 3))
{
picid = (UINT8)atoi(word + 3);
char *word3 = word+3;
char *num_text = gettextpromptpicnum(word3);
if (!num_text)
continue;
UINT16 picid = (UINT16)atoi(num_text);
if (picid > MAX_PROMPT_PICS || picid == 0)
{
deh_warning("textpromptscene %d: unknown word '%s'", num, word);
free(num_text);
continue;
}
--picid;
if (fastcmp(word+4, "NAME"))
{
strncpy(textprompts[num]->page[pagenum].picname[picid], word2, 8);
}
else if (fastcmp(word+4, "HIRES"))
{
textprompts[num]->page[pagenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word+4, "DURATION"))
{
textprompts[num]->page[pagenum].picduration[picid] = usi;
}
else if (fastcmp(word+4, "XCOORD"))
{
textprompts[num]->page[pagenum].xcoord[picid] = usi;
}
else if (fastcmp(word+4, "YCOORD"))
{
textprompts[num]->page[pagenum].ycoord[picid] = usi;
}
else
if (!page->pics || picid >= page->numpics)
page->pics = Z_Realloc(page->pics, sizeof(cutscene_pic_t) * (picid+1), PU_STATIC, NULL);
word3 += strlen(num_text);
free(num_text);
if (!ParseCutscenePic(&page->pics[picid], usi, word3, word2))
deh_warning("textpromptscene %d: unknown word '%s'", num, word);
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7);
textprompts[num]->page[pagenum].musswitch[6] = 0;
strlcpy(page->musswitch, word2, sizeof(page->musswitch));
}
else if (fastcmp(word, "MUSICTRACK"))
{
textprompts[num]->page[pagenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK;
page->musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK;
}
else if (fastcmp(word, "MUSICLOOP"))
{
textprompts[num]->page[pagenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
page->musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
}
// end copypasta from readcutscenescene
else if (fastcmp(word, "NAME"))
{
Z_Free(page->name);
page->name = NULL;
if (*word2 != '\0')
{
INT32 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;
strncat(name, word2, 33);
name[33] = 0;
strlcpy(name, word2, sizeof(name));
// Replace _ with ' '
for (j = 0; j < 32 && name[j]; j++)
for (size_t j = 0; j < sizeof(name) && name[j]; j++)
{
if (name[j] == '_')
name[j] = ' ';
}
strncpy(textprompts[num]->page[pagenum].name, name, 32);
page->name = Z_StrDup(name);
}
else
*textprompts[num]->page[pagenum].name = '\0';
}
else if (fastcmp(word, "ICON"))
strncpy(textprompts[num]->page[pagenum].iconname, word2, 8);
strlcpy(page->iconname, word2, sizeof(page->iconname));
else if (fastcmp(word, "ICONALIGN"))
textprompts[num]->page[pagenum].rightside = (i || word2[0] == 'R');
page->rightside = (i || word2[0] == 'R');
else if (fastcmp(word, "ICONFLIP"))
textprompts[num]->page[pagenum].iconflip = (i || word2[0] == 'T' || word2[0] == 'Y');
page->iconflip = (i || word2[0] == 'T' || word2[0] == 'Y');
else if (fastcmp(word, "LINES"))
textprompts[num]->page[pagenum].lines = usi;
{
if (i <= 0)
deh_warning("PromptPage %d: line count must be 1 or higher", num);
else
page->lines = usi;
}
else if (fastcmp(word, "BACKCOLOR"))
{
INT32 backcolor;
if (i == 0 || fastcmp(word2, "WHITE")) backcolor = 0;
else if (i == 1 || fastcmp(word2, "GRAY") || fastcmp(word2, "GREY") ||
fastcmp(word2, "BLACK")) backcolor = 1;
else if (i == 2 || fastcmp(word2, "SEPIA")) backcolor = 2;
else if (i == 3 || fastcmp(word2, "BROWN")) backcolor = 3;
else if (i == 4 || fastcmp(word2, "PINK")) backcolor = 4;
else if (i == 5 || fastcmp(word2, "RASPBERRY")) backcolor = 5;
else if (i == 6 || fastcmp(word2, "RED")) backcolor = 6;
else if (i == 7 || fastcmp(word2, "CREAMSICLE")) backcolor = 7;
else if (i == 8 || fastcmp(word2, "ORANGE")) backcolor = 8;
else if (i == 9 || fastcmp(word2, "GOLD")) backcolor = 9;
else if (i == 10 || fastcmp(word2, "YELLOW")) backcolor = 10;
else if (i == 11 || fastcmp(word2, "EMERALD")) backcolor = 11;
else if (i == 12 || fastcmp(word2, "GREEN")) backcolor = 12;
else if (i == 13 || fastcmp(word2, "CYAN") || fastcmp(word2, "AQUA")) backcolor = 13;
else if (i == 14 || fastcmp(word2, "STEEL")) backcolor = 14;
else if (i == 15 || fastcmp(word2, "PERIWINKLE")) backcolor = 15;
else if (i == 16 || fastcmp(word2, "BLUE")) backcolor = 16;
else if (i == 17 || fastcmp(word2, "PURPLE")) backcolor = 17;
else if (i == 18 || fastcmp(word2, "LAVENDER")) backcolor = 18;
else if (i >= 256 && i < 512) backcolor = i; // non-transparent palette index
else if (i < 0) backcolor = INT32_MAX; // CONS_BACKCOLOR user-configured
else backcolor = 1; // default gray
textprompts[num]->page[pagenum].backcolor = backcolor;
INT32 backcolor = -1;
if (i >= 256 && i < 512)
backcolor = i; // non-transparent palette index
else if (i < 0)
backcolor = INT32_MAX; // CONS_BACKCOLOR user-configured
else
{
strlwr(word2);
backcolor = P_ParsePromptBackColor(word2);
}
if (backcolor < 0)
backcolor = 1; // default gray
page->backcolor = backcolor;
}
else if (fastcmp(word, "ALIGN"))
{
UINT8 align = 0; // left
if (usi == 1 || word2[0] == 'R') align = 1;
else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2;
textprompts[num]->page[pagenum].align = align;
page->align = align;
}
else if (fastcmp(word, "VERTICALALIGN"))
{
UINT8 align = 0; // top
if (usi == 1 || word2[0] == 'B') align = 1;
else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2;
textprompts[num]->page[pagenum].verticalalign = align;
page->verticalalign = align;
}
else if (fastcmp(word, "TEXTSPEED"))
textprompts[num]->page[pagenum].textspeed = get_number(word2);
page->textspeed = get_number(word2);
else if (fastcmp(word, "TEXTSFX"))
textprompts[num]->page[pagenum].textsfx = get_number(word2);
page->textsfx = get_number(word2);
else if (fastcmp(word, "HIDEHUD"))
{
UINT8 hidehud = 0;
if ((word2[0] == 'F' && (word2[1] == 'A' || !word2[1])) || word2[0] == 'N') hidehud = 0; // false
else if (usi == 1 || word2[0] == 'T' || word2[0] == 'Y') hidehud = 1; // true (hide appropriate HUD elements)
else if (usi == 2 || word2[0] == 'A' || (word2[0] == 'F' && word2[1] == 'O')) hidehud = 2; // force (hide all HUD elements)
textprompts[num]->page[pagenum].hidehud = hidehud;
page->hidehud = hidehud;
}
else if (fastcmp(word, "METAPAGE"))
{
if (usi && usi <= textprompts[num]->numpages)
{
UINT8 metapagenum = usi - 1;
strncpy(textprompts[num]->page[pagenum].name, textprompts[num]->page[metapagenum].name, 32);
strncpy(textprompts[num]->page[pagenum].iconname, textprompts[num]->page[metapagenum].iconname, 8);
textprompts[num]->page[pagenum].rightside = textprompts[num]->page[metapagenum].rightside;
textprompts[num]->page[pagenum].iconflip = textprompts[num]->page[metapagenum].iconflip;
textprompts[num]->page[pagenum].lines = textprompts[num]->page[metapagenum].lines;
textprompts[num]->page[pagenum].backcolor = textprompts[num]->page[metapagenum].backcolor;
textprompts[num]->page[pagenum].align = textprompts[num]->page[metapagenum].align;
textprompts[num]->page[pagenum].verticalalign = textprompts[num]->page[metapagenum].verticalalign;
textprompts[num]->page[pagenum].textspeed = textprompts[num]->page[metapagenum].textspeed;
textprompts[num]->page[pagenum].textsfx = textprompts[num]->page[metapagenum].textsfx;
textprompts[num]->page[pagenum].hidehud = textprompts[num]->page[metapagenum].hidehud;
// music: don't copy, else each page change may reset the music
}
if (usi > 0 && usi <= textprompts[num]->numpages)
P_SetMetaPage(page, &textprompts[num]->page[usi - 1]);
}
else if (fastcmp(word, "TAG"))
strncpy(textprompts[num]->page[pagenum].tag, word2, 33);
strlcpy(page->tag, word2, sizeof(page->tag));
else if (fastcmp(word, "NEXTPROMPT"))
textprompts[num]->page[pagenum].nextprompt = usi;
page->nextprompt = usi;
else if (fastcmp(word, "NEXTPAGE"))
textprompts[num]->page[pagenum].nextpage = usi;
page->nextpage = usi;
else if (fastcmp(word, "NEXTTAG"))
strncpy(textprompts[num]->page[pagenum].nexttag, word2, 33);
strlcpy(page->nexttag, word2, sizeof(page->nexttag));
else if (fastcmp(word, "TIMETONEXT"))
textprompts[num]->page[pagenum].timetonext = get_number(word2);
page->timetonext = get_number(word2);
else
deh_warning("PromptPage %d: unknown word '%s'", num, word);
}
......@@ -2458,13 +2402,13 @@ void readtextprompt(MYFILE *f, INT32 num)
{
if (1 <= value && value <= MAX_PAGES)
{
textprompts[num]->page[value - 1].backcolor = 1; // default to gray
textprompts[num]->page[value - 1].hidehud = 1; // hide appropriate HUD elements
textpage_t *page = &textprompts[num]->page[value - 1];
if (page->lines == 0)
P_InitTextPromptPage(page);
readtextpromptpage(f, num, value - 1);
}
else
deh_warning("Page number %d out of range (1 - %d)", value, MAX_PAGES);
}
else
deh_warning("Prompt %d: unknown word '%s', Page <num> expected.", num, word);
......
......@@ -162,15 +162,22 @@ extern tic_t countdowntimer;
extern boolean countdowntimeup;
extern boolean exitfadestarted;
typedef struct
{
char name[256];
UINT8 hires;
INT16 xcoord;
INT16 ycoord;
UINT16 duration;
} cutscene_pic_t;
#define MAX_CUTSCENE_PICS 8
typedef struct
{
UINT8 numpics;
char picname[8][8];
UINT8 pichires[8];
char *text;
UINT16 xcoord[8];
UINT16 ycoord[8];
UINT16 picduration[8];
cutscene_pic_t pics[MAX_CUTSCENE_PICS];
UINT8 musicloop;
UINT16 textxpos;
UINT16 textypos;
......@@ -199,29 +206,50 @@ extern cutscene_t *cutscenes[128];
#define MAX_PROMPTS (TUTORIAL_PROMPT+TUTORIAL_AREAS*TUTORIAL_AREA_PROMPTS*3) // 3 control modes
#define MAX_PAGES 128
#define PROMPT_PIC_PERSIST 0
#define PROMPT_PIC_LOOP 1
#define PROMPT_PIC_DESTROY 2
#define MAX_PROMPT_PICS 8
enum
{
PROMPT_PIC_PERSIST,
PROMPT_PIC_LOOP,
PROMPT_PIC_DESTROY
};
#define MAX_PROMPT_PICS 512
#define MAX_PROMPT_CHOICES 12
typedef struct
{
UINT8 numpics;
UINT16 nextprompt; // next prompt to jump to, one-based. 0 = current prompt
UINT8 nextpage; // next page to jump to, one-based. 0 = next page within prompt->numpages
char *nextpromptname;
char *nextpagename;
char nexttag[33]; // next tag to jump to. If set, this overrides nextprompt and nextpage.
INT16 exectag;
boolean endprompt;
char *text;
} promptchoice_t;
typedef struct
{
char *pagename;
UINT16 numpics;
UINT8 picmode; // sequence mode after displaying last pic, 0 = persist, 1 = loop, 2 = destroy
UINT8 pictoloop; // if picmode == loop, which pic to loop to?
UINT8 pictostart; // initial pic number to show
char picname[MAX_PROMPT_PICS][8];
UINT8 pichires[MAX_PROMPT_PICS];
UINT16 xcoord[MAX_PROMPT_PICS]; // gfx
UINT16 ycoord[MAX_PROMPT_PICS]; // gfx
UINT16 picduration[MAX_PROMPT_PICS];
cutscene_pic_t *pics;
char musswitch[7];
UINT16 musswitchflags;
UINT8 musicloop;
boolean restoremusic;
INT16 exectag;
boolean endprompt;
char tag[33]; // page tag
char name[34]; // narrator name, extra char for color
char iconname[8]; // narrator icon lump
char *name; // narrator name
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
......@@ -229,17 +257,26 @@ typedef struct
INT32 backcolor; // see CON_SetupBackColormap: 0-11, INT32_MAX for user-defined (CONS_BACKCOLOR)
UINT8 align; // text alignment, 0 = left, 1 = right, 2 = center
UINT8 verticalalign; // vertical text alignment, 0 = top, 1 = bottom, 2 = middle
UINT8 textspeed; // text speed, delay in tics between characters.
SINT8 textspeed; // text speed, delay in tics between characters.
sfxenum_t textsfx; // sfx_ id for printing text
UINT8 nextprompt; // next prompt to jump to, one-based. 0 = current prompt
UINT16 nextprompt; // next prompt to jump to, one-based. 0 = current prompt
UINT8 nextpage; // next page to jump to, one-based. 0 = next page within prompt->numpages
char *nextpromptname;
char *nextpagename;
char nexttag[33]; // next tag to jump to. If set, this overrides nextprompt and nextpage.
INT32 timetonext; // time in tics to jump to next page automatically. 0 = don't jump automatically
char *text;
size_t textlength;
INT32 numchoices;
promptchoice_t *choices;
INT32 startchoice;
INT32 nochoice;
boolean choicesleftside;
} textpage_t;
typedef struct
{
char *name;
textpage_t page[MAX_PAGES];
INT32 numpages; // Number of pages in this prompt
} textprompt_t;
......
......@@ -37,6 +37,7 @@
#include "m_cond.h"
#include "p_local.h"
#include "p_setup.h"
#include "p_dialog.h"
#include "st_stuff.h" // hud hiding
#include "fastcmp.h"
#include "console.h"
......@@ -55,7 +56,6 @@ static INT32 timetonext; // Delay between screen changes
static INT32 continuetime; // Short delay when continuing
static tic_t animtimer; // Used for some animation timings
static INT16 skullAnimCounter; // Prompts: Chevron animation
static INT32 deplete;
static tic_t stoptimer;
......@@ -202,111 +202,108 @@ static patch_t *endescp[5]; // escape pod + flame
static INT32 sparkloffs[3][2]; // eggrock explosions/blackrock sparkles
static INT32 sparklloop;
//
// PROMPT STATE
//
boolean promptactive = false;
static mobj_t *promptmo;
static INT16 promptpostexectag;
static boolean promptblockcontrols;
static char *promptpagetext = NULL;
static INT32 callpromptnum = INT32_MAX;
static INT32 callpagenum = INT32_MAX;
static INT32 callplayer = INT32_MAX;
//
// CUTSCENE TEXT WRITING
//
static const char *cutscene_basetext = NULL;
static char cutscene_disptext[1024];
static INT32 cutscene_baseptr = 0;
static INT32 cutscene_writeptr = 0;
static INT32 cutscene_textcount = 0;
static INT32 cutscene_textspeed = 0;
static UINT8 cutscene_boostspeed = 0;
// STJR Intro
char stjrintro[9] = "STJRI000";
static huddrawlist_h luahuddrawlist_title;
static textwriter_t textwriter;
static void WriterTextBufferAlloc(textwriter_t *writer)
{
if (!writer->disptextsize)
writer->disptextsize = 16;
size_t oldsize = writer->disptextsize;
while (((unsigned)writer->writeptr) + 1 >= writer->disptextsize)
writer->disptextsize *= 2;
if (!writer->disptext || oldsize != writer->disptextsize)
writer->disptext = Z_Realloc(writer->disptext, writer->disptextsize, PU_STATIC, NULL);
}
//
// This alters the text string cutscene_disptext.
// This alters the text string writer->disptext.
// Use the typical string drawing functions to display it.
// Returns 0 if \0 is reached (end of input)
//
static UINT8 F_WriteText(void)
static boolean F_WriteText(textwriter_t *writer)
{
INT32 numtowrite = 1;
const char *c;
if (cutscene_boostspeed)
const int max_speed = 8;
if (writer->boostspeed)
{
// for custom cutscene speedup mode
numtowrite = 8;
numtowrite = max_speed;
}
else
{
// Don't draw any characters if the count was 1 or more when we started
if (--cutscene_textcount >= 0)
return 1;
if (--writer->textcount >= 0)
return true;
if (cutscene_textspeed < 7)
numtowrite = 8 - cutscene_textspeed;
if (writer->textspeed < max_speed-1)
numtowrite = max_speed - writer->textspeed;
}
for (;numtowrite > 0;++cutscene_baseptr)
for (;numtowrite > 0;++writer->baseptr)
{
c = &cutscene_basetext[cutscene_baseptr];
c = &writer->basetext[writer->baseptr];
if (!c || !*c || *c=='#')
return 0;
return false;
// \xA0 - \xAF = change text speed
if ((UINT8)*c >= 0xA0 && (UINT8)*c <= 0xAF)
{
cutscene_textspeed = (INT32)((UINT8)*c - 0xA0);
writer->textspeed = (INT32)((UINT8)*c - 0xA0);
continue;
}
// \xB0 - \xD2 = delay character for up to one second (35 tics)
else if ((UINT8)*c >= 0xB0 && (UINT8)*c <= (0xB0+TICRATE-1))
{
cutscene_textcount = (INT32)((UINT8)*c - 0xAF);
writer->textcount = (INT32)((UINT8)*c - 0xAF);
numtowrite = 0;
continue;
}
cutscene_disptext[cutscene_writeptr++] = *c;
WriterTextBufferAlloc(writer);
writer->disptext[writer->writeptr++] = *c;
// Ignore other control codes (color)
if ((UINT8)*c < 0x80)
--numtowrite;
}
// Reset textcount for next tic based on speed
// if it wasn't already set by a delay.
if (cutscene_textcount < 0)
if (writer->textcount < 0)
{
cutscene_textcount = 0;
if (cutscene_textspeed > 7)
cutscene_textcount = cutscene_textspeed - 7;
writer->textcount = 0;
if (writer->textspeed > max_speed-1)
writer->textcount = writer->textspeed - (max_speed-1);
}
return 1;
return true;
}
static void F_NewCutscene(const char *basetext)
static void F_ResetTextWriter(textwriter_t *writer, const char *basetext)
{
cutscene_basetext = basetext;
memset(cutscene_disptext,0,sizeof(cutscene_disptext));
cutscene_writeptr = cutscene_baseptr = 0;
cutscene_textspeed = 9;
cutscene_textcount = TICRATE/2;
P_ResetTextWriter(writer, basetext, strlen(basetext));
}
// =============
// INTRO SCENE
// =============
#define NUMINTROSCENES 17
INT32 intro_scenenum = 0;
INT32 intro_curtime = 0;
static INT32 intro_scenenum = 0;
static INT32 intro_curtime = 0;
const char *introtext[NUMINTROSCENES];
......@@ -505,10 +502,10 @@ void F_StartIntro(void)
gameaction = ga_nothing;
paused = false;
CON_ToggleOff();
F_NewCutscene(introtext[0]);
F_ResetTextWriter(&textwriter, introtext[0]);
intro_scenenum = 0;
finalecount = animtimer = skullAnimCounter = stoptimer = 0;
finalecount = animtimer = stoptimer = 0;
timetonext = introscenetime[intro_scenenum];
}
......@@ -835,7 +832,8 @@ void F_IntroDrawer(void)
V_DrawRightAlignedString(BASEVIDWIDTH-4, BASEVIDHEIGHT-12, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), "\x86""Press ""\x82""ENTER""\x86"" to skip...");
}
V_DrawString(cx, cy, V_ALLOWLOWERCASE, cutscene_disptext);
if (textwriter.disptext)
V_DrawString(cx, cy, V_ALLOWLOWERCASE, textwriter.disptext);
}
//
......@@ -848,7 +846,7 @@ void F_IntroTicker(void)
timetonext--;
F_WriteText();
F_WriteText(&textwriter);
// check for skipping
if (keypressed)
......@@ -937,7 +935,7 @@ void F_IntroTicker(void)
return;
}
F_NewCutscene(introtext[++intro_scenenum]);
F_ResetTextWriter(&textwriter, introtext[++intro_scenenum]);
timetonext = introscenetime[intro_scenenum];
F_WipeStartScreen();
......@@ -2489,7 +2487,7 @@ void F_StartTitleScreen(void)
// IWAD dependent stuff.
animtimer = skullAnimCounter = 0;
animtimer = 0;
demoDelayLeft = demoDelayTime;
demoIdleLeft = demoIdleTime;
......@@ -3834,7 +3832,7 @@ boolean F_ContinueResponder(event_t *event)
// CUSTOM CUTSCENES
// ==================
static INT32 scenenum, cutnum;
static INT32 picxpos, picypos, picnum, pictime, picmode, numpics, pictoloop;
static INT32 picxpos, picypos, picnum, pictime;
static INT32 textxpos, textypos;
static boolean cutsceneover = false;
static boolean runningprecutscene = false, precutresetplayer = false, precutFLS = false;
......@@ -3870,32 +3868,38 @@ static void F_AdvanceToNextScene(void)
timetonext = 0;
stoptimer = 0;
picnum = 0;
picxpos = cutscenes[cutnum]->scene[scenenum].xcoord[picnum];
picypos = cutscenes[cutnum]->scene[scenenum].ycoord[picnum];
if (cutscenes[cutnum]->scene[scenenum].musswitch[0])
S_ChangeMusicEx(cutscenes[cutnum]->scene[scenenum].musswitch,
cutscenes[cutnum]->scene[scenenum].musswitchflags,
cutscenes[cutnum]->scene[scenenum].musicloop,
cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0);
scene_t *scene = &cutscenes[cutnum]->scene[scenenum];
cutscene_pic_t *pic = &scene->pics[picnum];
picxpos = pic->xcoord;
picypos = pic->ycoord;
if (scene->musswitch[0])
S_ChangeMusicEx(scene->musswitch,
scene->musswitchflags,
scene->musicloop,
scene->musswitchposition, 0, 0);
// Fade to the next
F_NewCutscene(cutscenes[cutnum]->scene[scenenum].text);
F_ResetTextWriter(&textwriter, scene->text);
picnum = 0;
picxpos = cutscenes[cutnum]->scene[scenenum].xcoord[picnum];
picypos = cutscenes[cutnum]->scene[scenenum].ycoord[picnum];
textxpos = cutscenes[cutnum]->scene[scenenum].textxpos;
textypos = cutscenes[cutnum]->scene[scenenum].textypos;
pic = &scene->pics[picnum];
picxpos = pic->xcoord;
picypos = pic->ycoord;
textxpos = scene->textxpos;
textypos = scene->textypos;
animtimer = pictime = cutscenes[cutnum]->scene[scenenum].picduration[picnum];
animtimer = pictime = pic->duration;
if (rendermode != render_none)
{
F_CutsceneDrawer();
F_WipeEndScreen();
F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true);
F_RunWipe(scene->fadeoutid, true);
}
}
......@@ -3935,7 +3939,7 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
paused = false;
CON_ToggleOff();
F_NewCutscene(cutscenes[cutscenenum]->scene[0].text);
F_ResetTextWriter(&textwriter, cutscenes[cutscenenum]->scene[0].text);
cutsceneover = false;
runningprecutscene = precutscene;
......@@ -3944,24 +3948,29 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
scenenum = picnum = 0;
cutnum = cutscenenum;
picxpos = cutscenes[cutnum]->scene[0].xcoord[0];
picypos = cutscenes[cutnum]->scene[0].ycoord[0];
textxpos = cutscenes[cutnum]->scene[0].textxpos;
textypos = cutscenes[cutnum]->scene[0].textypos;
pictime = cutscenes[cutnum]->scene[0].picduration[0];
scene_t *scene = &cutscenes[cutnum]->scene[scenenum];
cutscene_pic_t *pic = &scene->pics[picnum];
picxpos = pic->xcoord;
picypos = pic->ycoord;
textxpos = scene->textxpos;
textypos = scene->textypos;
pictime = pic->duration;
keypressed = false;
finalecount = 0;
timetonext = 0;
animtimer = cutscenes[cutnum]->scene[0].picduration[0]; // Picture duration
animtimer = pic->duration; // Picture duration
stoptimer = 0;
if (cutscenes[cutnum]->scene[0].musswitch[0])
S_ChangeMusicEx(cutscenes[cutnum]->scene[0].musswitch,
cutscenes[cutnum]->scene[0].musswitchflags,
cutscenes[cutnum]->scene[0].musicloop,
cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0);
if (scene->musswitch[0])
S_ChangeMusicEx(scene->musswitch,
scene->musswitchflags,
scene->musicloop,
scene->musswitchposition, 0, 0);
else
S_StopMusic();
S_StopSounds();
......@@ -3972,19 +3981,22 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
//
void F_CutsceneDrawer(void)
{
V_DrawFill(0,0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
cutscene_pic_t *pic = &cutscenes[cutnum]->scene[scenenum].pics[picnum];
if (cutscenes[cutnum]->scene[scenenum].picname[picnum][0] != '\0')
if (pic->name[0] != '\0')
{
if (cutscenes[cutnum]->scene[scenenum].pichires[picnum])
if (pic->hires)
V_DrawSmallScaledPatch(picxpos, picypos, 0,
W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_PATCH_LOWPRIORITY));
W_CachePatchLongName(pic->name, PU_PATCH_LOWPRIORITY));
else
V_DrawScaledPatch(picxpos,picypos, 0,
W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_PATCH_LOWPRIORITY));
W_CachePatchLongName(pic->name, PU_PATCH_LOWPRIORITY));
}
V_DrawString(textxpos, textypos, V_ALLOWLOWERCASE, cutscene_disptext);
if (textwriter.disptext)
V_DrawString(textxpos, textypos, V_ALLOWLOWERCASE, textwriter.disptext);
}
void F_CutsceneTicker(void)
......@@ -3998,7 +4010,7 @@ void F_CutsceneTicker(void)
// advance animation
finalecount++;
cutscene_boostspeed = 0;
textwriter.boostspeed = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
......@@ -4008,7 +4020,7 @@ void F_CutsceneTicker(void)
if (players[i].cmd.buttons & BT_SPIN)
{
keypressed = false;
cutscene_boostspeed = 1;
textwriter.boostspeed = 1;
if (timetonext)
timetonext = 2;
}
......@@ -4019,12 +4031,17 @@ void F_CutsceneTicker(void)
animtimer--;
if (animtimer <= 0)
{
if (picnum < 7 && cutscenes[cutnum]->scene[scenenum].picname[picnum+1][0] != '\0')
scene_t *scene = &cutscenes[cutnum]->scene[scenenum];
cutscene_pic_t *next_pic = NULL;
if (picnum < MAX_CUTSCENE_PICS-1 && scene->pics[picnum+1].name[0] != '\0')
{
picnum++;
picxpos = cutscenes[cutnum]->scene[scenenum].xcoord[picnum];
picypos = cutscenes[cutnum]->scene[scenenum].ycoord[picnum];
pictime = cutscenes[cutnum]->scene[scenenum].picduration[picnum];
next_pic = &scene->pics[picnum];
picxpos = next_pic->xcoord;
picypos = next_pic->ycoord;
pictime = next_pic->duration;
animtimer = pictime;
}
else
......@@ -4037,7 +4054,7 @@ void F_CutsceneTicker(void)
if (++stoptimer > 2 && timetonext == 1)
F_AdvanceToNextScene();
else if (!timetonext && !F_WriteText())
else if (!timetonext && !F_WriteText(&textwriter))
timetonext = 5*TICRATE + 1;
}
......@@ -4049,631 +4066,6 @@ boolean F_CutsceneResponder(event_t *event)
return false;
}
// ==================
// TEXT PROMPTS
// ==================
static void F_GetPageTextGeometry(UINT8 *pagelines, boolean *rightside, INT32 *boxh, INT32 *texth, INT32 *texty, INT32 *namey, INT32 *chevrony, INT32 *textx, INT32 *textr)
{
// reuse:
// cutnum -> promptnum
// scenenum -> pagenum
lumpnum_t iconlump = W_CheckNumForName(textprompts[cutnum]->page[scenenum].iconname);
*pagelines = textprompts[cutnum]->page[scenenum].lines ? textprompts[cutnum]->page[scenenum].lines : 4;
*rightside = (iconlump != LUMPERROR && textprompts[cutnum]->page[scenenum].rightside);
// Vertical calculations
*boxh = *pagelines*2;
*texth = textprompts[cutnum]->page[scenenum].name[0] ? (*pagelines-1)*2 : *pagelines*2; // name takes up first line if it exists
*texty = BASEVIDHEIGHT - ((*texth * 4) + (*texth/2)*4);
*namey = BASEVIDHEIGHT - ((*boxh * 4) + (*boxh/2)*4);
*chevrony = BASEVIDHEIGHT - (((1*2) * 4) + ((1*2)/2)*4); // force on last line
// Horizontal calculations
// Shift text to the right if we have a character icon on the left side
// Add 4 margin against icon
*textx = (iconlump != LUMPERROR && !*rightside) ? ((*boxh * 4) + (*boxh/2)*4) + 4 : 4;
*textr = *rightside ? BASEVIDWIDTH - (((*boxh * 4) + (*boxh/2)*4) + 4) : BASEVIDWIDTH-4;
}
static fixed_t F_GetPromptHideHudBound(void)
{
UINT8 pagelines;
boolean rightside;
INT32 boxh, texth, texty, namey, chevrony;
INT32 textx, textr;
if (cutnum == INT32_MAX || scenenum == INT32_MAX || !textprompts[cutnum] || scenenum >= textprompts[cutnum]->numpages ||
!textprompts[cutnum]->page[scenenum].hidehud ||
(splitscreen && textprompts[cutnum]->page[scenenum].hidehud != 2)) // don't hide on splitscreen, unless hide all is forced
return 0;
else if (textprompts[cutnum]->page[scenenum].hidehud == 2) // hide all
return BASEVIDHEIGHT;
F_GetPageTextGeometry(&pagelines, &rightside, &boxh, &texth, &texty, &namey, &chevrony, &textx, &textr);
// calc boxheight (see V_DrawPromptBack)
boxh *= vid.dup;
boxh = (boxh * 4) + (boxh/2)*5; // 4 lines of space plus gaps between and some leeway
// return a coordinate to check
// if negative: don't show hud elements below this coordinate (visually)
// if positive: don't show hud elements above this coordinate (visually)
return 0 - boxh; // \todo: if prompt at top of screen (someday), make this return positive
}
boolean F_GetPromptHideHudAll(void)
{
if (cutnum == INT32_MAX || scenenum == INT32_MAX || !textprompts[cutnum] || scenenum >= textprompts[cutnum]->numpages ||
!textprompts[cutnum]->page[scenenum].hidehud ||
(splitscreen && textprompts[cutnum]->page[scenenum].hidehud != 2)) // don't hide on splitscreen, unless hide all is forced
return false;
else if (textprompts[cutnum]->page[scenenum].hidehud == 2) // hide all
return true;
else
return false;
}
boolean F_GetPromptHideHud(fixed_t y)
{
INT32 ybound;
boolean fromtop;
fixed_t ytest;
if (!promptactive)
return false;
ybound = F_GetPromptHideHudBound();
fromtop = (ybound >= 0);
ytest = (fromtop ? ybound : BASEVIDHEIGHT + ybound);
return (fromtop ? y < ytest : y >= ytest); // true means hide
}
static void F_PreparePageText(char *pagetext)
{
UINT8 pagelines;
boolean rightside;
INT32 boxh, texth, texty, namey, chevrony;
INT32 textx, textr;
F_GetPageTextGeometry(&pagelines, &rightside, &boxh, &texth, &texty, &namey, &chevrony, &textx, &textr);
if (promptpagetext)
Z_Free(promptpagetext);
promptpagetext = (pagetext && pagetext[0]) ? V_WordWrap(textx, textr, 0, pagetext) : Z_StrDup("");
F_NewCutscene(promptpagetext);
cutscene_textspeed = textprompts[cutnum]->page[scenenum].textspeed ? textprompts[cutnum]->page[scenenum].textspeed : TICRATE/5;
cutscene_textcount = 0; // no delay in beginning
cutscene_boostspeed = 0; // don't print 8 characters to start
// \todo update control hot strings on re-config
// and somehow don't reset cutscene text counters
}
static void F_AdvanceToNextPage(void)
{
INT32 nextprompt = textprompts[cutnum]->page[scenenum].nextprompt ? textprompts[cutnum]->page[scenenum].nextprompt - 1 : INT32_MAX,
nextpage = textprompts[cutnum]->page[scenenum].nextpage ? textprompts[cutnum]->page[scenenum].nextpage - 1 : INT32_MAX,
oldcutnum = cutnum;
if (textprompts[cutnum]->page[scenenum].nexttag[0])
F_GetPromptPageByNamedTag(textprompts[cutnum]->page[scenenum].nexttag, &nextprompt, &nextpage);
// determine next prompt
if (nextprompt != INT32_MAX)
{
if (nextprompt <= MAX_PROMPTS && textprompts[nextprompt])
cutnum = nextprompt;
else
cutnum = INT32_MAX;
}
// determine next page
if (nextpage != INT32_MAX)
{
if (cutnum != INT32_MAX)
{
scenenum = nextpage;
if (scenenum >= MAX_PAGES || scenenum > textprompts[cutnum]->numpages-1)
scenenum = INT32_MAX;
}
}
else
{
if (cutnum != oldcutnum)
scenenum = 0;
else if (scenenum + 1 < MAX_PAGES && scenenum < textprompts[cutnum]->numpages-1)
scenenum++;
else
scenenum = INT32_MAX;
}
// close the prompt if either num is invalid
if (cutnum == INT32_MAX || scenenum == INT32_MAX)
F_EndTextPrompt(false, false);
else
{
// on page mode, number of tics before allowing boost
// on timer mode, number of tics until page advances
timetonext = textprompts[cutnum]->page[scenenum].timetonext ? textprompts[cutnum]->page[scenenum].timetonext : TICRATE/10;
F_PreparePageText(textprompts[cutnum]->page[scenenum].text);
// gfx
picnum = textprompts[cutnum]->page[scenenum].pictostart;
numpics = textprompts[cutnum]->page[scenenum].numpics;
picmode = textprompts[cutnum]->page[scenenum].picmode;
pictoloop = textprompts[cutnum]->page[scenenum].pictoloop > 0 ? textprompts[cutnum]->page[scenenum].pictoloop - 1 : 0;
picxpos = textprompts[cutnum]->page[scenenum].xcoord[picnum];
picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum];
animtimer = pictime = textprompts[cutnum]->page[scenenum].picduration[picnum];
// music change
if (textprompts[cutnum]->page[scenenum].musswitch[0])
S_ChangeMusic(textprompts[cutnum]->page[scenenum].musswitch,
textprompts[cutnum]->page[scenenum].musswitchflags,
textprompts[cutnum]->page[scenenum].musicloop);
}
}
void F_EndTextPrompt(boolean forceexec, boolean noexec)
{
boolean promptwasactive = promptactive;
promptactive = false;
callpromptnum = callpagenum = callplayer = INT32_MAX;
if (promptwasactive)
{
if (promptmo && promptmo->player && promptblockcontrols)
promptmo->reactiontime = TICRATE/4; // prevent jumping right away // \todo account freeze realtime for this)
// \todo reset frozen realtime?
}
// \todo net safety, maybe loop all player thinkers?
if ((promptwasactive || forceexec) && !noexec && promptpostexectag)
{
if (tmthing) // edge case where starting an invalid prompt immediately on level load will make P_MapStart fail
P_LinedefExecute(promptpostexectag, promptmo, NULL);
else
{
P_MapStart();
P_LinedefExecute(promptpostexectag, promptmo, NULL);
P_MapEnd();
}
}
}
void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime)
{
INT32 i;
// if splitscreen and we already have a prompt active, ignore.
// \todo Proper per-player splitscreen support (individual prompts)
if (promptactive && splitscreen && promptnum == callpromptnum && pagenum == callpagenum)
return;
// \todo proper netgame support
if (netgame)
{
F_EndTextPrompt(true, false); // run the post-effects immediately
return;
}
// We share vars, so no starting text prompts over cutscenes or title screens!
keypressed = false;
finalecount = 0;
timetonext = 0;
animtimer = 0;
stoptimer = 0;
skullAnimCounter = 0;
// Set up state
promptmo = mo;
promptpostexectag = postexectag;
promptblockcontrols = blockcontrols;
(void)freezerealtime; // \todo freeze player->realtime, maybe this needs to cycle through player thinkers
// Initialize current prompt and scene
callpromptnum = promptnum;
callpagenum = pagenum;
cutnum = (promptnum < MAX_PROMPTS && textprompts[promptnum]) ? promptnum : INT32_MAX;
scenenum = (cutnum != INT32_MAX && pagenum < MAX_PAGES && pagenum <= textprompts[cutnum]->numpages-1) ? pagenum : INT32_MAX;
promptactive = (cutnum != INT32_MAX && scenenum != INT32_MAX);
if (promptactive)
{
// on page mode, number of tics before allowing boost
// on timer mode, number of tics until page advances
timetonext = textprompts[cutnum]->page[scenenum].timetonext ? textprompts[cutnum]->page[scenenum].timetonext : TICRATE/10;
F_PreparePageText(textprompts[cutnum]->page[scenenum].text);
// gfx
picnum = textprompts[cutnum]->page[scenenum].pictostart;
numpics = textprompts[cutnum]->page[scenenum].numpics;
picmode = textprompts[cutnum]->page[scenenum].picmode;
pictoloop = textprompts[cutnum]->page[scenenum].pictoloop > 0 ? textprompts[cutnum]->page[scenenum].pictoloop - 1 : 0;
picxpos = textprompts[cutnum]->page[scenenum].xcoord[picnum];
picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum];
animtimer = pictime = textprompts[cutnum]->page[scenenum].picduration[picnum];
// music change
if (textprompts[cutnum]->page[scenenum].musswitch[0])
S_ChangeMusic(textprompts[cutnum]->page[scenenum].musswitch,
textprompts[cutnum]->page[scenenum].musswitchflags,
textprompts[cutnum]->page[scenenum].musicloop);
// get the calling player
if (promptblockcontrols && mo && mo->player)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (players[i].mo == mo)
{
callplayer = i;
break;
}
}
}
}
else
F_EndTextPrompt(true, false); // run the post-effects immediately
}
static boolean F_GetTextPromptTutorialTag(char *tag, INT32 length)
{
INT32 gcs = gcs_custom;
boolean suffixed = true;
if (!tag || !tag[0] || !tutorialmode)
return false;
if (!strncmp(tag, "TAM", 3)) // Movement
gcs = G_GetControlScheme(gamecontrol, gcl_movement, num_gcl_movement);
else if (!strncmp(tag, "TAC", 3)) // Camera
{
// Check for gcl_movement so we can differentiate between FPS and Platform schemes.
gcs = G_GetControlScheme(gamecontrol, gcl_movement, num_gcl_movement);
if (gcs == gcs_custom) // try again, maybe we'll get a match
gcs = G_GetControlScheme(gamecontrol, gcl_camera, num_gcl_camera);
if (gcs == gcs_fps && !cv_usemouse.value)
gcs = gcs_platform; // Platform (arrow) scheme is stand-in for no mouse
}
else if (!strncmp(tag, "TAD", 3)) // Movement and Camera
gcs = G_GetControlScheme(gamecontrol, gcl_movement_camera, num_gcl_movement_camera);
else if (!strncmp(tag, "TAJ", 3)) // Jump
gcs = G_GetControlScheme(gamecontrol, gcl_jump, num_gcl_jump);
else if (!strncmp(tag, "TAS", 3)) // Spin
gcs = G_GetControlScheme(gamecontrol, gcl_spin, num_gcl_spin);
else if (!strncmp(tag, "TAA", 3)) // Char ability
gcs = G_GetControlScheme(gamecontrol, gcl_jump, num_gcl_jump);
else if (!strncmp(tag, "TAW", 3)) // Shield ability
gcs = G_GetControlScheme(gamecontrol, gcl_jump_spin, num_gcl_jump_spin);
else
gcs = G_GetControlScheme(gamecontrol, gcl_tutorial_used, num_gcl_tutorial_used);
switch (gcs)
{
case gcs_fps:
// strncat(tag, "FPS", length);
suffixed = false;
break;
case gcs_platform:
strncat(tag, "PLATFORM", length);
break;
default:
strncat(tag, "CUSTOM", length);
break;
}
return suffixed;
}
void F_GetPromptPageByNamedTag(const char *tag, INT32 *promptnum, INT32 *pagenum)
{
INT32 nosuffixpromptnum = INT32_MAX, nosuffixpagenum = INT32_MAX;
INT32 tutorialpromptnum = (tutorialmode) ? TUTORIAL_PROMPT-1 : 0;
boolean suffixed = false, found = false;
char suffixedtag[33];
*promptnum = *pagenum = INT32_MAX;
if (!tag || !tag[0])
return;
strncpy(suffixedtag, tag, 33);
suffixedtag[32] = 0;
if (tutorialmode)
suffixed = F_GetTextPromptTutorialTag(suffixedtag, 33);
for (*promptnum = 0 + tutorialpromptnum; *promptnum < MAX_PROMPTS; (*promptnum)++)
{
if (!textprompts[*promptnum])
continue;
for (*pagenum = 0; *pagenum < textprompts[*promptnum]->numpages && *pagenum < MAX_PAGES; (*pagenum)++)
{
if (suffixed && fastcmp(suffixedtag, textprompts[*promptnum]->page[*pagenum].tag))
{
// this goes first because fastcmp ends early if first string is shorter
found = true;
break;
}
else if (nosuffixpromptnum == INT32_MAX && nosuffixpagenum == INT32_MAX && fastcmp(tag, textprompts[*promptnum]->page[*pagenum].tag))
{
if (suffixed)
{
nosuffixpromptnum = *promptnum;
nosuffixpagenum = *pagenum;
// continue searching for the suffixed tag
}
else
{
found = true;
break;
}
}
}
if (found)
break;
}
if (suffixed && !found && nosuffixpromptnum != INT32_MAX && nosuffixpagenum != INT32_MAX)
{
found = true;
*promptnum = nosuffixpromptnum;
*pagenum = nosuffixpagenum;
}
if (!found)
CONS_Debug(DBG_GAMELOGIC, "Text prompt: Can't find a page with named tag %s or suffixed tag %s\n", tag, suffixedtag);
}
void F_TextPromptDrawer(void)
{
// reuse:
// cutnum -> promptnum
// scenenum -> pagenum
lumpnum_t iconlump;
UINT8 pagelines;
boolean rightside;
INT32 boxh, texth, texty, namey, chevrony;
INT32 textx, textr;
// Data
patch_t *patch;
if (!promptactive)
return;
iconlump = W_CheckNumForName(textprompts[cutnum]->page[scenenum].iconname);
F_GetPageTextGeometry(&pagelines, &rightside, &boxh, &texth, &texty, &namey, &chevrony, &textx, &textr);
// Draw gfx first
if (picnum >= 0 && picnum < numpics && textprompts[cutnum]->page[scenenum].picname[picnum][0] != '\0')
{
if (textprompts[cutnum]->page[scenenum].pichires[picnum])
V_DrawSmallScaledPatch(picxpos, picypos, 0,
W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_PATCH_LOWPRIORITY));
else
V_DrawScaledPatch(picxpos,picypos, 0,
W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_PATCH_LOWPRIORITY));
}
// Draw background
V_DrawPromptBack(boxh, textprompts[cutnum]->page[scenenum].backcolor);
// Draw narrator icon
if (iconlump != LUMPERROR)
{
INT32 iconx, icony, scale, scaledsize;
patch = W_CachePatchName(textprompts[cutnum]->page[scenenum].iconname, PU_PATCH_LOWPRIORITY);
// scale and center
if (patch->width > patch->height)
{
scale = FixedDiv(((boxh * 4) + (boxh/2)*4) - 4, patch->width);
scaledsize = FixedMul(patch->height, scale);
iconx = (rightside ? BASEVIDWIDTH - (((boxh * 4) + (boxh/2)*4)) : 4) << FRACBITS;
icony = ((namey-4) << FRACBITS) + FixedDiv(BASEVIDHEIGHT - namey + 4 - scaledsize, 2); // account for 4 margin
}
else if (patch->height > patch->width)
{
scale = FixedDiv(((boxh * 4) + (boxh/2)*4) - 4, patch->height);
scaledsize = FixedMul(patch->width, scale);
iconx = (rightside ? BASEVIDWIDTH - (((boxh * 4) + (boxh/2)*4)) : 4) << FRACBITS;
icony = namey << FRACBITS;
iconx += FixedDiv(FixedMul(patch->height, scale) - scaledsize, 2);
}
else
{
scale = FixedDiv(((boxh * 4) + (boxh/2)*4) - 4, patch->width);
iconx = (rightside ? BASEVIDWIDTH - (((boxh * 4) + (boxh/2)*4)) : 4) << FRACBITS;
icony = namey << FRACBITS;
}
if (textprompts[cutnum]->page[scenenum].iconflip)
iconx += FixedMul(patch->width, scale) << FRACBITS;
V_DrawFixedPatch(iconx, icony, scale, (V_SNAPTOBOTTOM|(textprompts[cutnum]->page[scenenum].iconflip ? V_FLIP : 0)), patch, NULL);
W_UnlockCachedPatch(patch);
}
// Draw text
V_DrawString(textx, texty, (V_SNAPTOBOTTOM|V_ALLOWLOWERCASE), cutscene_disptext);
// Draw name
// Don't use V_YELLOWMAP here so that the name color can be changed with control codes
if (textprompts[cutnum]->page[scenenum].name[0])
V_DrawString(textx, namey, (V_SNAPTOBOTTOM|V_ALLOWLOWERCASE), textprompts[cutnum]->page[scenenum].name);
// Draw chevron
if (promptblockcontrols && !timetonext)
V_DrawString(textr-8, chevrony + (skullAnimCounter/5), (V_SNAPTOBOTTOM|V_YELLOWMAP), "\x1B"); // down arrow
}
#define nocontrolallowed(j) {\
players[j].powers[pw_nocontrol] = 1;\
if (players[j].mo)\
{\
if (players[j].mo->state == states+S_PLAY_STND && players[j].mo->tics != -1)\
players[j].mo->tics++;\
else if (players[j].mo->state == states+S_PLAY_WAIT)\
P_SetMobjState(players[j].mo, S_PLAY_STND);\
}\
}
void F_TextPromptTicker(void)
{
INT32 i;
if (!promptactive || paused || P_AutoPause())
return;
// advance animation
finalecount++;
cutscene_boostspeed = 0;
// for the chevron
if (--skullAnimCounter <= 0)
skullAnimCounter = 8;
// button handling
if (textprompts[cutnum]->page[scenenum].timetonext)
{
if (promptblockcontrols) // same procedure as below, just without the button handling
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (netgame && i != serverplayer && !IsPlayerAdmin(i))
continue;
else if (splitscreen) {
// Both players' controls are locked,
// But only consoleplayer can advance the prompt.
// \todo Proper per-player splitscreen support (individual prompts)
if (i == consoleplayer || i == secondarydisplayplayer)
nocontrolallowed(i)
}
else if (i == consoleplayer)
nocontrolallowed(i)
if (!splitscreen)
break;
}
}
if (timetonext >= 1)
timetonext--;
if (!timetonext)
F_AdvanceToNextPage();
F_WriteText();
}
else
{
if (promptblockcontrols)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (netgame && i != serverplayer && !IsPlayerAdmin(i))
continue;
else if (splitscreen) {
// Both players' controls are locked,
// But only the triggering player can advance the prompt.
if (i == consoleplayer || i == secondarydisplayplayer)
{
players[i].powers[pw_nocontrol] = 1;
if (callplayer == consoleplayer || callplayer == secondarydisplayplayer)
{
if (i != callplayer)
continue;
}
else if (i != consoleplayer)
continue;
}
else
continue;
}
else if (i == consoleplayer)
nocontrolallowed(i)
else
continue;
if ((players[i].cmd.buttons & BT_SPIN) || (players[i].cmd.buttons & BT_JUMP))
{
if (timetonext > 1)
timetonext--;
else if (cutscene_baseptr) // don't set boost if we just reset the string
cutscene_boostspeed = 1; // only after a slight delay
if (keypressed)
{
if (!splitscreen)
break;
else
continue;
}
if (!timetonext) // is 0 when finished generating text
{
F_AdvanceToNextPage();
if (promptactive)
S_StartSound(NULL, sfx_menu1);
}
keypressed = true; // prevent repeat events
}
else if (!(players[i].cmd.buttons & BT_SPIN) && !(players[i].cmd.buttons & BT_JUMP))
keypressed = false;
if (!splitscreen)
break;
}
}
// generate letter-by-letter text
if (scenenum >= MAX_PAGES ||
!textprompts[cutnum]->page[scenenum].text ||
!textprompts[cutnum]->page[scenenum].text[0] ||
!F_WriteText())
timetonext = !promptblockcontrols; // never show the chevron if we can't toggle pages
}
// gfx
if (picnum >= 0 && picnum < numpics)
{
if (animtimer <= 0)
{
boolean persistanimtimer = false;
if (picnum < numpics-1 && textprompts[cutnum]->page[scenenum].picname[picnum+1][0] != '\0')
picnum++;
else if (picmode == PROMPT_PIC_LOOP)
picnum = pictoloop;
else if (picmode == PROMPT_PIC_DESTROY)
picnum = -1;
else // if (picmode == PROMPT_PIC_PERSIST)
persistanimtimer = true;
if (!persistanimtimer && picnum >= 0)
{
picxpos = textprompts[cutnum]->page[scenenum].xcoord[picnum];
picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum];
pictime = textprompts[cutnum]->page[scenenum].picduration[picnum];
animtimer = pictime;
}
}
else
animtimer--;
}
}
// ================
// WAITINGPLAYERS
// ================
......
......@@ -34,7 +34,6 @@ void F_IntroTicker(void);
void F_TitleScreenTicker(boolean run);
void F_CutsceneTicker(void);
void F_TitleDemoTicker(void);
void F_TextPromptTicker(void);
// Called by main loop.
void F_GameEndDrawer(void);
......@@ -56,13 +55,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
void F_CutsceneDrawer(void);
void F_EndCutScene(void);
void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime);
void F_GetPromptPageByNamedTag(const char *tag, INT32 *promptnum, INT32 *pagenum);
void F_TextPromptDrawer(void);
void F_EndTextPrompt(boolean forceexec, boolean noexec);
boolean F_GetPromptHideHudAll(void);
boolean F_GetPromptHideHud(fixed_t y);
void F_StartGameEnd(void);
void F_StartIntro(void);
void F_StartTitleScreen(void);
......
......@@ -20,6 +20,7 @@
#include "f_finale.h"
#include "p_setup.h"
#include "p_saveg.h"
#include "p_dialog.h"
#include "i_time.h"
#include "i_system.h"
#include "am_map.h"
......@@ -158,6 +159,8 @@ boolean exitfadestarted = false;
cutscene_t *cutscenes[128];
textprompt_t *textprompts[MAX_PROMPTS];
struct dialog_s *globaltextprompt = NULL;
INT16 nextmapoverride;
UINT8 skipstats;
INT16 nextgametype = -1;
......@@ -2010,9 +2013,9 @@ static boolean ViewpointSwitchResponder(event_t *ev)
UINT8 canSwitchView = 0;
INT32 direction = 0;
if (ev->key == KEY_F12 || ev->key == gamecontrol[GC_VIEWPOINTNEXT][0] || ev->key == gamecontrol[GC_VIEWPOINTNEXT][1])
if (ev->key == KEY_F12 || G_IsGameControl(ev->key, GC_VIEWPOINTNEXT))
direction = 1;
if (ev->key == gamecontrol[GC_VIEWPOINTPREV][0] || ev->key == gamecontrol[GC_VIEWPOINTPREV][1])
if (G_IsGameControl(ev->key, GC_VIEWPOINTPREV))
direction = -1;
// This enabled reverse-iterating with shift+F12, sadly I had to
// disable this in case your shift key is bound to a control =((
......@@ -2083,6 +2086,122 @@ static boolean ViewpointSwitchResponder(event_t *ev)
return true;
}
static boolean G_TextPromptResponder(event_t *ev)
{
if (ev->type != ev_keydown)
return false;
player_t *player = NULL;
dialog_t *dialog = NULL;
UINT8 localplayer = 0;
INT32 key = KEY_NULL;
if (!splitscreen)
{
player = &players[consoleplayer];
if (!player->promptactive)
return false;
dialog = P_GetPlayerDialog(player);
if (!dialog || !dialog->showchoices)
return false;
}
else
{
// Check P2
if (G_IsGameControlP2(ev->key, GC_FORWARD))
{
key = KEY_UPARROW;
localplayer = 1;
}
else if (G_IsGameControlP2(ev->key, GC_BACKWARD))
{
key = KEY_DOWNARROW;
localplayer = 1;
}
else if (G_IsGameControlP2(ev->key, GC_JUMP))
{
key = KEY_ENTER;
localplayer = 1;
}
else if (G_IsGameControlP2(ev->key, GC_SPIN))
{
key = KEY_BACKSPACE;
localplayer = 1;
}
}
// Check P1
if (localplayer == 0)
{
if (G_IsGameControl(ev->key, GC_FORWARD))
key = KEY_UPARROW;
else if (G_IsGameControl(ev->key, GC_BACKWARD))
key = KEY_DOWNARROW;
else if (G_IsGameControl(ev->key, GC_JUMP))
key = KEY_ENTER;
else if (G_IsGameControl(ev->key, GC_SPIN))
key = KEY_BACKSPACE;
}
// No key was hit
if (key == KEY_NULL)
return false;
// If on splitscreen, get the player that might correspond to this key press
if (splitscreen)
{
player = (localplayer == 1) ? &players[secondarydisplayplayer] : &players[consoleplayer];
if (!player->promptactive)
return false;
dialog = P_GetPlayerDialog(player);
if (!dialog || !dialog->showchoices)
return false;
}
// Get the player that started this prompt
// If this is not a global text prompt, it will be the same player.
player_t *promptplayer = globaltextprompt ? globaltextprompt->player : player;
if (player != promptplayer)
return false;
if (key == KEY_UPARROW)
{
INT32 choice = dialog->curchoice - 1;
if (choice < 0)
choice = dialog->numchoices - 1;
D_SendTextPromptChoice(choice, localplayer);
return true;
}
else if (key == KEY_DOWNARROW)
{
INT32 choice = dialog->curchoice + 1;
if (choice >= dialog->numchoices)
choice = 0;
D_SendTextPromptChoice(choice, localplayer);
return true;
}
else if (!ev->repeated)
{
// Ignore repeated key events if the key was jump or spin
if (key == KEY_ENTER)
{
D_SendTextPromptConfirm(dialog->curchoice, localplayer);
return true;
}
else if (dialog->nochoice > 0 && dialog->nochoice <= dialog->numchoices && key == KEY_BACKSPACE)
{
D_SendTextPromptChoice(dialog->nochoice, localplayer);
return true;
}
}
return false;
}
//
// G_Responder
// Get info needed to make ticcmd_ts for the players.
......@@ -2173,12 +2292,13 @@ boolean G_Responder(event_t *ev)
// update keys current state
G_MapEventsToControls(ev);
if (G_TextPromptResponder(ev))
return true;
switch (ev->type)
{
case ev_keydown:
if (ev->key == gamecontrol[GC_PAUSE][0]
|| ev->key == gamecontrol[GC_PAUSE][1]
|| ev->key == KEY_PAUSE)
if (G_IsGameControl(ev->key, GC_PAUSE) || ev->key == KEY_PAUSE)
{
if (modeattacking && !demoplayback && (gamestate == GS_LEVEL))
{
......@@ -2207,8 +2327,7 @@ boolean G_Responder(event_t *ev)
}
}
}
if (ev->key == gamecontrol[GC_CAMTOGGLE][0]
|| ev->key == gamecontrol[GC_CAMTOGGLE][1])
if (G_IsGameControl(ev->key, GC_CAMTOGGLE))
{
if (!camtoggledelay)
{
......@@ -2216,8 +2335,7 @@ boolean G_Responder(event_t *ev)
CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1);
}
}
if (ev->key == gamecontrolbis[GC_CAMTOGGLE][0]
|| ev->key == gamecontrolbis[GC_CAMTOGGLE][1])
if (G_IsGameControlP2(ev->key, GC_CAMTOGGLE))
{
if (!camtoggledelay2)
{
......@@ -2422,7 +2540,6 @@ void G_Ticker(boolean run)
F_TitleDemoTicker();
P_Ticker(run); // tic the game
ST_Ticker(run);
F_TextPromptTicker();
AM_Ticker();
HU_Ticker();
......
......@@ -45,7 +45,7 @@ extern INT16 rw_maximums[NUM_WEAPONS];
extern INT32 pausedelay;
extern boolean pausebreakkey;
extern boolean promptactive;
extern struct dialog_s *globaltextprompt;
extern consvar_t cv_pauseifunfocused;
......
......@@ -102,6 +102,16 @@ static dclick_t joy2dclicks[JOYBUTTONS + JOYHATS*4];
// protos
static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt);
boolean G_IsGameControl(INT32 key, gamecontrols_e gc)
{
return key == gamecontrol[gc][0] || key == gamecontrol[gc][1];
}
boolean G_IsGameControlP2(INT32 key, gamecontrols_e gc)
{
return key == gamecontrolbis[gc][0] || key == gamecontrolbis[gc][1];
}
//
// Remaps the inputs to game controls.
//
......
......@@ -179,6 +179,9 @@ extern const INT32 gcl_jump_spin[num_gcl_jump_spin];
// remaps the input event to a game control.
void G_MapEventsToControls(event_t *ev);
boolean G_IsGameControl(INT32 key, gamecontrols_e gc);
boolean G_IsGameControlP2(INT32 key, gamecontrols_e gc);
// returns the name of a key
const char *G_KeyNumToName(INT32 keynum);
INT32 G_KeyNameToNum(const char *keystr);
......
......@@ -1203,9 +1203,9 @@ patch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
lumpcache_t *lumpcache = wadfiles[wadnum]->patchcache;
if (!lumpcache[lumpnum])
{
void *ptr = Z_Calloc(sizeof(patch_t), PU_PATCH, &lumpcache[lumpnum]);
Patch_Create(NULL, 0, ptr);
Patch_AllocateHardwarePatch(ptr);
patch_t *patch = Patch_Create(NULL, 0);
Z_SetUser(patch, &lumpcache[lumpnum]);
Patch_AllocateHardwarePatch(patch);
}
return (patch_t *)(lumpcache[lumpnum]);
}
......
......@@ -115,7 +115,6 @@ void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option)
flags = PF_Translucent|PF_NoDepthTest;
// clip it since it is used for bunny scroll in doom I
HWD.pfnDrawPolygon(NULL, v, 4, flags);
}
......@@ -338,7 +337,215 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = hwrPatch->max_t;
// clip it since it is used for bunny scroll in doom I
flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
if (alphalevel)
{
FSurfaceInfo Surf;
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; // V_HUDTRANSHALF
else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; // V_HUDTRANS
else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; // V_HUDTRANSDOUBLE
else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags);
}
else
HWD.pfnDrawPolygon(NULL, v, 4, flags);
}
void HWR_DrawTexture(INT32 texturenum, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option)
{
FOutVector v[4];
FBITFIELD flags;
float cx = FIXED_TO_FLOAT(x);
float cy = FIXED_TO_FLOAT(y);
UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
UINT8 blendmode = ((option & V_BLENDMASK) >> V_BLENDSHIFT);
// 3--2
// | /|
// |/ |
// 0--1
float dup, fscalew, fscaleh, fwidth, fheight;
UINT8 perplayershuffle = 0;
if (texturenum < 0 || texturenum >= numtextures)
return;
// make texture ready in hardware cache
HWR_GetTexture(texturenum);
INT32 texwidth = textures[texturenum]->width;
INT32 texheight = textures[texturenum]->height;
dup = (float)vid.dup;
switch (option & V_SCALEPATCHMASK)
{
case V_NOSCALEPATCH:
dup = 1.0f;
break;
case V_SMALLSCALEPATCH:
dup = (float)vid.smalldup;
break;
case V_MEDSCALEPATCH:
dup = (float)vid.meddup;
break;
}
fscalew = fscaleh = FIXED_TO_FLOAT(pscale);
if (vscale != pscale)
fscaleh = FIXED_TO_FLOAT(vscale);
if (option & V_FLIP)
cx -= (float)(texwidth) * fscalew;
if (splitscreen && (option & V_PERPLAYER))
{
float adjusty = ((option & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f;
fscaleh /= 2;
cy /= 2;
#ifdef QUADS
if (splitscreen > 1) // 3 or 4 players
{
float adjustx = ((option & V_NOSCALESTART) ? vid.width : BASEVIDWIDTH)/2.0f;
fscalew /= 2;
cx /= 2;
if (stplyr == &players[displayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
option &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (stplyr == &players[secondarydisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
cx += adjustx;
option &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
}
else if (stplyr == &players[thirddisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
cy += adjusty;
option &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
}
else if (stplyr == &players[fourthdisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
cx += adjustx;
cy += adjusty;
option &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
}
}
else
#endif
// 2 players
{
if (stplyr == &players[displayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle = 1;
option &= ~V_SNAPTOBOTTOM;
}
else //if (stplyr == &players[secondarydisplayplayer])
{
if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle = 2;
cy += adjusty;
option &= ~V_SNAPTOTOP;
}
}
}
if (!(option & V_NOSCALESTART))
{
cx = cx * dup;
cy = cy * dup;
if (!(option & V_SCALEPATCHMASK))
{
// centre screen
if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dup) > 1.0E-36f)
{
if (option & V_SNAPTORIGHT)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dup));
else if (!(option & V_SNAPTOLEFT))
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dup))/2;
if (perplayershuffle & 4)
cx -= ((float)vid.width - ((float)BASEVIDWIDTH * dup))/4;
else if (perplayershuffle & 8)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dup))/4;
}
if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dup) > 1.0E-36f)
{
if (option & V_SNAPTOBOTTOM)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dup));
else if (!(option & V_SNAPTOTOP))
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dup))/2;
if (perplayershuffle & 1)
cy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dup))/4;
else if (perplayershuffle & 2)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dup))/4;
}
}
}
if (pscale != FRACUNIT || vscale != FRACUNIT || (splitscreen && option & V_PERPLAYER))
{
fwidth = (float)(texwidth) * fscalew * dup;
fheight = (float)(texheight) * fscaleh * dup;
}
else
{
fwidth = (float)(texwidth) * dup;
fheight = (float)(texheight) * dup;
}
// positions of the cx, cy, are between 0 and vid.width/vid.height now, we need them to be between -1 and 1
cx = -1 + (cx / (vid.width/2));
cy = 1 - (cy / (vid.height/2));
// fwidth and fheight are similar
fwidth /= vid.width / 2;
fheight /= vid.height / 2;
// set the polygon vertices to the right positions
v[0].x = v[3].x = cx;
v[2].x = v[1].x = cx + fwidth;
v[0].y = v[1].y = cy;
v[2].y = v[3].y = cy - fheight;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
if (option & V_FLIP)
{
v[0].s = v[3].s = 1.0f;
v[2].s = v[1].s = 0.0f;
}
else
{
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
}
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
if (alphalevel)
......@@ -585,18 +792,8 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
return;
v[2].y = v[3].y = 0; // Clamp the polygon edge vertex position
// Now for the UV-map... Uh-oh, math time!
// On second thought, a basic linear interpolation suffices
//float full_height = fheight;
//float cropped_height = fheight - cy;
//float remaining_height = cy;
//float cropped_percentage = (fheight - cy) / fheight;
//float remaining_percentage = cy / fheight;
//v[2].t = v[3].t = lerp(v[2].t, v[0].t, cropped_percentage);
// By swapping v[2] and v[0], we can use remaining_percentage for less operations
//v[2].t = v[3].t = lerp(v[0].t, v[2].t, remaining_percentage);
// Now for the UV-map... Uh-oh, math time!
v[2].t = v[3].t = flerp(v[0].t, v[2].t, cy/fheight);
}
}
......@@ -611,16 +808,8 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
return;
v[0].y = v[1].y = 0; // Clamp the polygon edge vertex position
// Now for the UV-map... Uh-oh, math time!
// On second thought, a basic linear interpolation suffices
//float full_height = fheight;
//float cropped_height = cy;
//float remaining_height = fheight - cy;
//float cropped_percentage = cy / fheight;
//float remaining_percentage = (fheight - cy) / fheight;
//v[0].t = v[1].t = lerp(v[0].t, v[2].t, cropped_percentage);
// Now for the UV-map... Uh-oh, math time!
v[0].t = v[1].t = flerp(v[0].t, v[2].t, cy/fheight);
}
}
......@@ -628,7 +817,6 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
#undef flerp
}
// clip it since it is used for bunny scroll in doom I
flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
if (alphalevel)
......
......@@ -42,6 +42,7 @@ void HWR_SetViewSize(void);
void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option);
void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap);
void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h);
void HWR_DrawTexture(INT32 texturenum, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option);
void HWR_MakePatch(const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap);
void HWR_CreatePlanePolygons(INT32 bspnum);
void HWR_CreateStaticLightmaps(INT32 bspnum);
......
......@@ -379,7 +379,10 @@ static void md2_loadTexture(md2_t *model)
Z_Free(grPatch->mipmap->data);
}
else
model->grpatch = patch = Patch_Create(NULL, 0, NULL);
model->grpatch = patch = Patch_Create(NULL, 0);
if (!patch)
return;
if (!patch->hardware)
Patch_AllocateHardwarePatch(patch);
......@@ -444,7 +447,10 @@ static void md2_loadBlendTexture(md2_t *model)
Z_Free(grPatch->mipmap->data);
}
else
model->blendgrpatch = patch = Patch_Create(NULL, 0, NULL);
model->blendgrpatch = patch = Patch_Create(NULL, 0);
if (!patch)
return;
if (!patch->hardware)
Patch_AllocateHardwarePatch(patch);
......
......@@ -1073,7 +1073,7 @@ boolean HU_Responder(event_t *ev)
return false;
// enter chat mode
if ((ev->key == gamecontrol[GC_TALKKEY][0] || ev->key == gamecontrol[GC_TALKKEY][1])
if (G_IsGameControl(ev->key, GC_TALKKEY)
&& netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise.
{
chat_on = true;
......@@ -1084,7 +1084,7 @@ boolean HU_Responder(event_t *ev)
typelines = 1;
return true;
}
if ((ev->key == gamecontrol[GC_TEAMKEY][0] || ev->key == gamecontrol[GC_TEAMKEY][1])
if (G_IsGameControl(ev->key, GC_TEAMKEY)
&& netgame && !OLD_MUTE)
{
chat_on = true;
......@@ -1167,8 +1167,7 @@ boolean HU_Responder(event_t *ev)
I_UpdateMouseGrab();
}
else if (c == KEY_ESCAPE
|| ((c == gamecontrol[GC_TALKKEY][0] || c == gamecontrol[GC_TALKKEY][1]
|| c == gamecontrol[GC_TEAMKEY][0] || c == gamecontrol[GC_TEAMKEY][1])
|| ((G_IsGameControl(c, GC_TALKKEY) || G_IsGameControl(c, GC_TEAMKEY))
&& c >= KEY_MOUSE1)) // If it's not a keyboard key, then the chat button is used as a toggle.
{
chat_on = false;
......
......@@ -15,6 +15,7 @@
#include "p_local.h"
#include "p_setup.h" // So we can have P_SetupLevelSky
#include "p_slopes.h" // P_GetSlopeZAt
#include "p_dialog.h"
#include "z_zone.h"
#include "r_main.h"
#include "r_draw.h"
......@@ -1857,6 +1858,79 @@ static int lib_pPlayerShouldUseSpinHeight(lua_State *L)
return 1;
}
static int lib_pStartTextPrompt(lua_State *L)
{
player_t *player;
INT32 promptnum = INT32_MAX, pagenum = 1;
const char *promptname = NULL, *pagename = NULL;
boolean blockcontrols;
boolean allplayers;
player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
if (lua_type(L, 2) == LUA_TSTRING)
promptname = luaL_checkstring(L, 2);
else
promptnum = luaL_checkinteger(L, 2);
if (lua_type(L, 3) == LUA_TSTRING)
pagename = luaL_checkstring(L, 3);
else if (!lua_isnoneornil(L, 3))
pagenum = luaL_checkinteger(L, 3);
blockcontrols = lua_optboolean(L, 4);
allplayers = lua_optboolean(L, 5);
NOHUD
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (promptname) {
promptnum = P_GetTextPromptByName(promptname);
if (promptnum == -1)
return luaL_error(L, "no text prompt named '%s'", promptname);
}
else {
if (promptnum <= 0 || promptnum > MAX_PROMPTS)
return luaL_error(L, "text prompt %d out of range (1 - %d)", promptnum, MAX_PROMPTS);
promptnum--;
}
textprompt_t *textprompt = textprompts[promptnum];
if (!textprompt)
return luaL_error(L, "invalid text prompt %d", promptnum);
if (pagename) {
pagenum = P_GetPromptPageByName(textprompt, pagename);
if (pagenum == -1)
return luaL_error(L, "no text prompt page named '%s'", pagename);
}
else {
if (pagenum <= 0 || pagenum > textprompt->numpages)
return luaL_error(L, "text prompt page %d out of range (1 - %d)", pagenum, textprompt->numpages);
pagenum--;
}
P_StartTextPrompt(player, promptnum, pagenum, 0, blockcontrols, false, allplayers);
return 0;
}
static int lib_pEndTextPrompt(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean noexec = lua_optboolean(L, 2);
NOHUD
INLEVEL
P_EndTextPrompt(player, false, noexec);
return 0;
}
// P_MAP
///////////
......@@ -4382,6 +4456,10 @@ static luaL_Reg lib[] = {
{"P_PlayerCanEnterSpinGaps",lib_pPlayerCanEnterSpinGaps},
{"P_PlayerShouldUseSpinHeight",lib_pPlayerShouldUseSpinHeight},
// p_dialog
{"P_StartTextPrompt",lib_pStartTextPrompt},
{"P_EndTextPrompt",lib_pEndTextPrompt},
// p_map
{"P_CheckPosition",lib_pCheckPosition},
{"P_TryMove",lib_pTryMove},
......