-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement ID24 DemoLoop specification (#2141)
* implement id24 demoloop specification * fixed various assorted issues from draft PR * added further fixes and simplifications to demoloop system * cleaner demoloop internals * implement outro_wipe field functionality * Revert "implement outro_wipe field functionality" This reverts commit 1dba7b2. * added demoloop TODO notice --------- Co-authored-by: Fabian Greffrath <fabian@greffrath.com>
- Loading branch information
1 parent
ffa9a39
commit f9b9f23
Showing
7 changed files
with
298 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// | ||
// Copyright (C) 2025 Guilherme Miranda | ||
// | ||
// This program is free software; you can redistribute it and/or | ||
// modify it under the terms of the GNU General Public License | ||
// as published by the Free Software Foundation; either version 2 | ||
// of the License, or (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// DESCRIPTION: | ||
// ID24 DemoLoop Specification - User customizable title screen sequence. | ||
// Though originally hardcoded on the original Doom engine this allows | ||
// for Doom modders to define custom sequences, using any provided custom | ||
// graphic-and-music, or DEMO lump. | ||
// | ||
|
||
#include "doomdef.h" | ||
#include "doomstat.h" | ||
|
||
#include "i_printf.h" | ||
#include "m_array.h" | ||
#include "m_json.h" | ||
#include "m_misc.h" | ||
|
||
#include "d_demoloop.h" | ||
|
||
// Doom | ||
static demoloop_entry_t demoloop_registered[] = { | ||
{ "TITLEPIC", "D_INTRO", 170, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO1", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "CREDIT", "", 200, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO2", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "HELP2", "", 200, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO3", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
}; | ||
|
||
// Ultimate Doom | ||
static demoloop_entry_t demoloop_retail[] = { | ||
{ "TITLEPIC", "D_INTRO", 170, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO1", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "CREDIT", "", 200, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO2", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "CREDIT", "", 200, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO3", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "DEMO4", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
}; | ||
|
||
// Doom II & Final Doom | ||
static demoloop_entry_t demoloop_commercial[] = { | ||
{ "TITLEPIC", "D_DM2TTL", 385, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO1", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "CREDIT", "", 200, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO2", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "TITLEPIC", "D_DM2TTL", 385, TYPE_ART, WIPE_MELT }, | ||
{ "DEMO3", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
{ "DEMO4", "", 0, TYPE_DEMO, WIPE_MELT }, | ||
}; | ||
|
||
demoloop_t demoloop = NULL; | ||
int demoloop_count = 0; | ||
|
||
void D_ParseDemoLoopEntry(json_t *entry) | ||
{ | ||
const char* primary_buffer = JS_GetStringValue(entry, "primarylump"); | ||
const char* secondary_buffer = JS_GetStringValue(entry, "secondarylump"); | ||
double seconds = JS_GetNumberValue(entry, "duration"); | ||
int type = JS_GetIntegerValue(entry, "type"); | ||
int outro_wipe = JS_GetIntegerValue(entry, "outro_wipe"); | ||
|
||
// We don't want a malformed entry to creep in, and break the titlescreen. | ||
// If one such entry does exist, skip it. | ||
// TODO: modify later to check locally for lump type. | ||
if (primary_buffer == NULL || type <= TYPE_NONE || type > TYPE_DEMO) | ||
{ | ||
primary_buffer = ""; | ||
type = TYPE_NONE; | ||
} | ||
|
||
// Similarly, but this time it isn't game-breaking. | ||
// Let it gracefully default to "closest vanilla behavior". | ||
if (outro_wipe <= WIPE_NONE || outro_wipe > WIPE_MELT) | ||
{ | ||
outro_wipe = WIPE_MELT; | ||
} | ||
|
||
demoloop_entry_t current_entry = {0}; | ||
|
||
// Remove pointer reference to in-memory JSON data. | ||
M_CopyLumpName(current_entry.primary_lump, primary_buffer); | ||
M_CopyLumpName(current_entry.secondary_lump, secondary_buffer); | ||
// Providing the time in seconds is much more intuitive for the end users. | ||
current_entry.duration = seconds * TICRATE; | ||
current_entry.type = type; | ||
current_entry.outro_wipe = outro_wipe; | ||
|
||
// Should there be a malformed entry, discard it. | ||
if (current_entry.type == TYPE_NONE) | ||
{ | ||
return; | ||
} | ||
|
||
array_push(demoloop, current_entry); | ||
} | ||
|
||
static void D_ParseDemoLoop(void) | ||
{ | ||
// Does the JSON lump even exist? | ||
json_t *json = JS_Open("DEMOLOOP", "demoloop", (version_t){1, 0, 0}); | ||
if (json == NULL) | ||
{ | ||
return; | ||
} | ||
|
||
// Does lump actually have any data? | ||
json_t *data = JS_GetObject(json, "data"); | ||
if (JS_IsNull(data) || !JS_IsObject(data)) | ||
{ | ||
I_Printf(VB_WARNING, "DEMOLOOP: data object not defined"); | ||
JS_Close("DEMOLOOP"); | ||
return; | ||
} | ||
|
||
// Does is it even have the definitions we are looking for? | ||
json_t *entry_list = JS_GetObject(data, "entries"); | ||
if (JS_IsNull(entry_list) || !JS_IsArray(entry_list)) | ||
{ | ||
I_Printf(VB_WARNING, "DEMOLOOP: no entries defined"); | ||
JS_Close("DEMOLOOP"); | ||
return; | ||
} | ||
|
||
// If so, now parse them. | ||
json_t *entry; | ||
JS_ArrayForEach(entry, entry_list) | ||
{ | ||
D_ParseDemoLoopEntry(entry); | ||
} | ||
demoloop_count = array_size(demoloop); | ||
|
||
// No need to keep in memory | ||
JS_Close("DEMOLOOP"); | ||
} | ||
|
||
static void D_GetDefaultDemoLoop(GameMode_t mode) | ||
{ | ||
switch(mode) | ||
{ | ||
case shareware: | ||
case registered: | ||
demoloop = demoloop_registered; | ||
demoloop_count = arrlen(demoloop_registered); | ||
break; | ||
|
||
case retail: | ||
demoloop = demoloop_retail; | ||
demoloop_count = arrlen(demoloop_retail); | ||
break; | ||
|
||
case commercial: | ||
demoloop = demoloop_commercial; | ||
demoloop_count = arrlen(demoloop_commercial); | ||
break; | ||
|
||
case indetermined: | ||
default: | ||
// How did we get here? | ||
demoloop = NULL; | ||
demoloop_count = 0; | ||
break; | ||
} | ||
|
||
return; | ||
} | ||
|
||
void D_SetupDemoLoop(void) | ||
{ | ||
D_ParseDemoLoop(); | ||
|
||
if (demoloop == NULL) | ||
{ | ||
D_GetDefaultDemoLoop(gamemode); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// | ||
// Copyright (C) 2025 Guilherme Miranda | ||
// | ||
// This program is free software; you can redistribute it and/or | ||
// modify it under the terms of the GNU General Public License | ||
// as published by the Free Software Foundation; either version 2 | ||
// of the License, or (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// DESCRIPTION: | ||
// ID24 DemoLoop Specification - User customizable title screen sequence. | ||
// Though originally hardcoded on the original Doom engine this allows | ||
// for Doom modders to define custom sequences, using any provided custom | ||
// graphic, music or DEMO lump. | ||
// | ||
|
||
#ifndef _D_DEMOLOOP_ | ||
#define _D_DEMOLOOP_ | ||
|
||
// Screen graphic or DEMO lump, NONE is used for fault tolerance. | ||
typedef enum | ||
{ | ||
TYPE_NONE = -1, | ||
TYPE_ART, | ||
TYPE_DEMO, | ||
} dl_type_t; | ||
|
||
// Immediate switch or screen melt, NONE is used for fault tolerance. | ||
// TODO: reimplement more cleanly at a later, more relevant moment | ||
typedef enum | ||
{ | ||
WIPE_NONE = -1, | ||
WIPE_IMMEDIATE, | ||
WIPE_MELT, | ||
} dl_wipe_t; | ||
|
||
// Individual demoloop units. | ||
typedef struct | ||
{ | ||
char primary_lump[9]; // Screen graphic or DEMO lump. | ||
char secondary_lump[9]; // Music lump for screen graphic. | ||
int duration; // Game tics. | ||
dl_type_t type; | ||
dl_wipe_t outro_wipe; | ||
} demoloop_entry_t; | ||
|
||
typedef demoloop_entry_t* demoloop_t; | ||
|
||
// Actual DemoLoop data structure. | ||
extern demoloop_t demoloop; | ||
// Formerly 7 for Ultimate Doom & Final Doom, 6 otherwise. | ||
extern int demoloop_count; | ||
|
||
// Perform both "D_ParseDemoLoop" and "D_GetDefaultDemoLoop". | ||
void D_SetupDemoLoop(void); | ||
|
||
#endif // _D_DEMOLOOP_ |
Oops, something went wrong.