Skip to content

Commit

Permalink
Implement ID24 DemoLoop specification (#2141)
Browse files Browse the repository at this point in the history
* 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
elf-alchemist and fabiangreffrath authored Jan 16, 2025
1 parent ffa9a39 commit f9b9f23
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 88 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ Copyright:
© 2022-2024 Alaux;
© 2022-2024 ceski;
© 2023 Andrew Apted;
© 2023 liPillON.
© 2023 liPillON;
© 2025 Guilherme Miranda.
License: [GPL-2.0+](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)

Files: `src/i_flickstick.*, src/i_gyro.*`
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ include(WoofSettings)
set(WOOF_SOURCES
am_map.c am_map.h
d_deh.c d_deh.h
d_demoloop.c d_demoloop.h
d_englsh.h
d_event.h
d_french.h
Expand Down
187 changes: 187 additions & 0 deletions src/d_demoloop.c
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);
}
}
61 changes: 61 additions & 0 deletions src/d_demoloop.h
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_
Loading

0 comments on commit f9b9f23

Please sign in to comment.