id24: intermissions (#1833)

* use cJSON library

* implement array_foreach, custom memory allocator for m_array

* use Doom 1 duration of the "entering" screen for id24 animation

* add player->visitedlevels array

* add U_BuildEpisodes function, try to emulate behaviour of KEX port and R&R
This commit is contained in:
Roman Fomin 2024-09-03 14:44:46 +07:00 committed by GitHub
parent dfc450d844
commit 6663fba661
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 845 additions and 24 deletions

View File

@ -51,7 +51,8 @@ jobs:
libopenal-dev \
libsndfile1-dev \
libfluidsynth-dev \
libxmp-dev
libxmp-dev \
libcjson-dev
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
@ -63,7 +64,8 @@ jobs:
openal-soft \
libsndfile \
fluid-synth \
libxmp
libxmp \
cjson
- name: Install dependencies (MSYS2)
if: matrix.config.shell == 'msys2 {0}'
@ -83,6 +85,7 @@ jobs:
${{ matrix.config.msys-env }}-libsndfile
${{ matrix.config.msys-env }}-fluidsynth
${{ matrix.config.msys-env }}-libxmp
${{ matrix.config.msys-env }}-cjson
- uses: actions/checkout@v4
@ -140,7 +143,8 @@ jobs:
libopenal-dev \
libsndfile1-dev \
libfluidsynth-dev \
libxmp-dev
libxmp-dev \
libcjson-dev
- uses: actions/checkout@v4

View File

@ -92,6 +92,7 @@ find_package(SndFile 1.0.29 REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_package(ALSA REQUIRED)
endif()
find_package(cJSON REQUIRED)
if(ALSA_FOUND)
set(HAVE_ALSA TRUE)

View File

@ -142,6 +142,7 @@ set(WOOF_SOURCES
w_file.c
w_zip.c
wi_stuff.c wi_stuff.h
wi_interlvl.c wi_interlvl.h
z_zone.c z_zone.h)
# Standard target definition
@ -187,7 +188,8 @@ target_link_libraries(woof PRIVATE
opl
textscreen
miniz
spng)
spng
cjson)
# Some platforms require standard libraries to be linked against.
if(HAVE_LIBM)

View File

@ -2345,6 +2345,7 @@ void D_DoomMain(void)
{
W_ProcessInWads("UMAPINFO", U_ParseMapInfo, true);
W_ProcessInWads("UMAPINFO", U_ParseMapInfo, false);
U_BuildEpisodes();
}
if (!M_CheckParm("-save"))

View File

@ -24,6 +24,7 @@
// of other structs: items (internal inventory),
// animation states (closely tied to the sprites
// used to represent them, unfortunately).
#include "doomtype.h"
#include "p_pspr.h"
#include "tables.h"
@ -52,6 +53,11 @@ typedef enum
} playerstate_t;
typedef struct
{
int episode;
int map;
} level_t;
//
// Player internal flags, for cheats and debug.
@ -214,6 +220,9 @@ typedef struct player_s
// Local angle for blending per-frame and per-tic turning.
angle_t ticangle, oldticangle;
int num_visitedlevels;
level_t *visitedlevels;
} player_t;
@ -245,7 +254,8 @@ typedef struct wbstartstruct_s
// previous and next levels, origin 0
int last;
int next;
int nextep; // for when MAPINFO progression crosses into another episode.
// for when MAPINFO progression crosses into another episode.
int nextep;
struct mapentry_s *lastmapinfo;
struct mapentry_s *nextmapinfo;
@ -265,6 +275,8 @@ typedef struct wbstartstruct_s
// [FG] total time for all completed levels
int totaltimes;
level_t *visitedlevels;
} wbstartstruct_t;

View File

@ -1822,6 +1822,14 @@ frommapinfo:
StatCopy(&wminfo);
}
for (int i = 0; i < MAXPLAYERS; ++i)
{
level_t level = {gameepisode, gamemap};
array_push(players[i].visitedlevels, level);
players[i].num_visitedlevels = array_size(players[i].visitedlevels);
}
wminfo.visitedlevels = players[consoleplayer].visitedlevels;
WI_Start (&wminfo);
}
@ -2133,7 +2141,7 @@ static void G_DoPlayDemo(void)
// killough 2/22/98: version id string format for savegames
#define VERSIONID "MBF %d"
#define CURRENT_SAVE_VERSION "Woof 13.0.0"
#define CURRENT_SAVE_VERSION "Woof 15.0.0"
static char *savename = NULL;
@ -2432,6 +2440,7 @@ static void G_DoLoadGame(void)
CheckSaveVersion(vcheck, saveg_mbf);
CheckSaveVersion("Woof 6.0.0", saveg_woof600);
CheckSaveVersion("Woof 13.0.0", saveg_woof1300);
CheckSaveVersion(CURRENT_SAVE_VERSION, saveg_current);
// killough 2/22/98: Friendly savegame version difference message

View File

@ -18,6 +18,9 @@
// array_push(), array_grow() and array_free() may change the buffer pointer,
// and any previously-taken pointers should be considered invalidated.
#ifndef M_ARRAY_H
#define M_ARRAY_H
#include <stddef.h>
#include <stdlib.h>
@ -27,6 +30,18 @@
# define M_ARRAY_INIT_CAPACITY 8
#endif
#ifndef M_ARRAY_MALLOC
# define M_ARRAY_MALLOC(size) malloc((size))
#endif
#ifndef M_ARRAY_REALLOC
# define M_ARRAY_REALLOC(ptr, size) I_Realloc((ptr), (size))
#endif
#ifndef M_ARRAY_FREE
# define M_ARRAY_FREE(ptr) free((ptr))
#endif
typedef struct
{
int capacity;
@ -73,16 +88,19 @@ inline static void array_clear(const void *v)
(v)[array_ptr((v))->size++] = (e); \
} while (0)
#define array_free(v) \
do \
{ \
if (v) \
{ \
free(array_ptr((v))); \
(v) = NULL; \
} \
#define array_free(v) \
do \
{ \
if (v) \
{ \
M_ARRAY_FREE(array_ptr((v))); \
(v) = NULL; \
} \
} while (0)
#define array_foreach(ptr, v) \
for (ptr = (v); ptr != &(v)[array_size((v))]; ++ptr)
inline static void *M_ArrayGrow(void *v, size_t esize, int n)
{
m_array_buffer_t *p;
@ -90,15 +108,18 @@ inline static void *M_ArrayGrow(void *v, size_t esize, int n)
if (v)
{
p = array_ptr(v);
p = I_Realloc(p, sizeof(m_array_buffer_t) + (p->capacity + n) * esize);
p = M_ARRAY_REALLOC(p, sizeof(m_array_buffer_t)
+ (p->capacity + n) * esize);
p->capacity += n;
}
else
{
p = malloc(sizeof(m_array_buffer_t) + n * esize);
p = M_ARRAY_MALLOC(sizeof(m_array_buffer_t) + n * esize);
p->capacity = n;
p->size = 0;
}
return p->buffer;
}
#endif // M_ARRAY_H

View File

@ -28,6 +28,7 @@
#include "doomstat.h"
#include "i_system.h"
#include "info.h"
#include "m_array.h"
#include "m_random.h"
#include "p_enemy.h"
#include "p_maputl.h"
@ -973,6 +974,27 @@ static void saveg_read_player_t(player_t *str)
str->slope = 0;
str->maxkilldiscount = 0;
}
if (saveg_compat > saveg_woof1300)
{
// [Woof!]: int num_visitedlevels;
str->num_visitedlevels = saveg_read32();
// [Woof!]: level_t *visitedlevels;
array_clear(str->visitedlevels);
for (int i = 0; i < str->num_visitedlevels; ++i)
{
level_t level = {0};
level.episode = saveg_read32();
level.map = saveg_read32();
array_push(str->visitedlevels, level);
}
}
else
{
str->num_visitedlevels = 0;
array_clear(str->visitedlevels);
}
}
static void saveg_write_player_t(player_t *str)
@ -1125,6 +1147,17 @@ static void saveg_write_player_t(player_t *str)
// [Woof!]: int maxkilldiscount;
saveg_write32(str->maxkilldiscount);
// [Woof!]: int num_visitedlevels;
saveg_write32(str->num_visitedlevels);
// [Woof!]: level_t *visitedlevels;
level_t *level;
array_foreach(level, str->visitedlevels)
{
saveg_write32(level->episode);
saveg_write32(level->map);
}
}

View File

@ -56,7 +56,8 @@ typedef enum saveg_compat_e
saveg_mbf,
saveg_woof510,
saveg_woof600,
saveg_current, // saveg_woof1300
saveg_woof1300,
saveg_current, // saveg_woof1500
} saveg_compat_t;
extern saveg_compat_t saveg_compat;

View File

@ -23,7 +23,9 @@
#include "doomdef.h"
#include "doomstat.h"
#include "doomtype.h"
#include "i_system.h"
#include "m_array.h"
#include "m_misc.h"
#include "u_mapinfo.h"
#include "u_scanner.h"
@ -40,6 +42,8 @@ umapinfo_t U_mapinfo;
umapinfo_t default_mapinfo;
static level_t *secretlevels;
static const char *const ActorNames[] =
{
"DoomPlayer", "ZombieMan", "ShotgunGuy", "Archvile", "ArchvileFire",
@ -273,6 +277,14 @@ static void UpdateMapEntry(mapentry_t *mape, mapentry_t *newe)
{
strcpy(mape->enterpic, newe->enterpic);
}
if (newe->exitanim[0])
{
strcpy(mape->exitanim, newe->exitanim);
}
if (newe->enteranim[0])
{
strcpy(mape->enteranim, newe->enteranim);
}
if (newe->interbackdrop[0])
{
strcpy(mape->interbackdrop, newe->interbackdrop);
@ -466,6 +478,7 @@ static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape)
}
M_AddEpisode(mape->mapname, lumpname, alttext, key);
mape->flags |= MapInfo_Episode;
if (alttext)
{
@ -489,11 +502,13 @@ static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape)
else if (!strcasecmp(pname, "nextsecret"))
{
status = ParseLumpName(s, mape->nextsecret);
if (!G_ValidateMapName(mape->nextsecret, NULL, NULL))
level_t level = {0};
if (!G_ValidateMapName(mape->nextsecret, &level.episode, &level.map))
{
U_Error(s, "Invalid map name %s", mape->nextsecret);
status = 0;
}
array_push(secretlevels, level);
}
else if (!strcasecmp(pname, "levelpic"))
{
@ -517,6 +532,7 @@ static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape)
if (s->sc_boolean)
{
strcpy(mape->endpic, "$CAST");
mape->flags |= MapInfo_Endgame;
}
else
{
@ -529,6 +545,7 @@ static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape)
if (s->sc_boolean)
{
strcpy(mape->endpic, "$BUNNY");
mape->flags |= MapInfo_Endgame;
}
else
{
@ -541,6 +558,7 @@ static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape)
if (s->sc_boolean)
{
strcpy(mape->endpic, "!");
mape->flags |= MapInfo_Endgame;
}
else
{
@ -555,6 +573,14 @@ static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape)
{
status = ParseLumpName(s, mape->enterpic);
}
else if (!strcasecmp(pname, "exitanim"))
{
status = ParseLumpName(s, mape->exitanim);
}
else if (!strcasecmp(pname, "enteranim"))
{
status = ParseLumpName(s, mape->enteranim);
}
else if (!strcasecmp(pname, "nointermission"))
{
if (U_MustGetToken(s, TK_BoolConst))
@ -847,3 +873,45 @@ boolean U_CheckField(char *str)
{
return str && str[0] && strcmp(str, "-");
}
boolean U_IsSecretMap(int episode, int map)
{
level_t *level;
array_foreach(level, secretlevels)
{
if (level->episode == episode && level->map == map)
{
return true;
}
}
return false;
}
void U_BuildEpisodes(void)
{
boolean episode_started;
int current_map_number = 1, all_number = 1;
for (int i = 0; i < U_mapinfo.mapcount; ++i)
{
mapentry_t *mape = &U_mapinfo.maps[i];
if (mape->flags & MapInfo_Episode)
{
episode_started = true;
current_map_number = 1;
}
if (episode_started)
{
mape->map_number = current_map_number++;
}
if (mape->flags & MapInfo_Endgame)
{
episode_started = false;
}
mape->all_number = all_number++;
}
}

View File

@ -30,6 +30,13 @@ typedef struct
int tag;
} bossaction_t;
typedef enum
{
MapInfo_None = 0x0001,
MapInfo_Episode = 0x0002,
MapInfo_Endgame = 0x0004,
} mapinfo_flags_t;
typedef struct mapentry_s
{
char *mapname;
@ -46,9 +53,14 @@ typedef struct mapentry_s
char endpic[9];
char exitpic[9];
char enterpic[9];
char exitanim[9];
char enteranim[9];
char interbackdrop[9];
char intermusic[9];
int partime;
mapinfo_flags_t flags;
int map_number;
int all_number;
boolean nointermission;
int numbossactions;
bossaction_t *bossactions;
@ -64,6 +76,7 @@ extern umapinfo_t U_mapinfo;
extern umapinfo_t default_mapinfo;
extern boolean EpiCustom;
mapentry_t *G_LookupMapinfo(int episode, int map);
boolean U_CheckField(char *str);
@ -72,4 +85,8 @@ void U_ParseMapDefInfo(int lumpnum);
void U_ParseMapInfo(int lumpnum);
void U_BuildEpisodes(void);
boolean U_IsSecretMap(int episode, int map);
#endif

217
src/wi_interlvl.c Normal file
View File

@ -0,0 +1,217 @@
//
// Copyright(C) 2024 Roman Fomin
//
// 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.
#include "wi_interlvl.h"
#include <stdlib.h>
#include <string.h>
#include "doomtype.h"
#include "i_printf.h"
#include "w_wad.h"
#include "z_zone.h"
#define M_ARRAY_MALLOC(size) Z_Malloc((size), PU_LEVEL, NULL)
#define M_ARRAY_REALLOC(ptr, size) Z_Realloc((ptr), (size), PU_LEVEL, NULL)
#define M_ARRAY_FREE(ptr) Z_Free((ptr))
#include "m_array.h"
#include "cjson/cJSON.h"
static char *WI_StringDuplicate(const char *orig)
{
return strcpy(Z_Malloc(strlen(orig) + 1, PU_LEVEL, NULL), orig);
}
static boolean ParseCondition(cJSON *json, interlevelcond_t *out)
{
cJSON *condition = cJSON_GetObjectItemCaseSensitive(json, "condition");
if (!cJSON_IsNumber(condition))
{
return false;
}
out->condition = condition->valueint;
cJSON *param = cJSON_GetObjectItemCaseSensitive(json, "param");
if (!cJSON_IsNumber(param))
{
return false;
}
out->param = param->valueint;
return true;
}
static boolean ParseFrame(cJSON *json, interlevelframe_t *out)
{
cJSON *image_lump = cJSON_GetObjectItemCaseSensitive(json, "image");
if (!cJSON_IsString(image_lump))
{
return false;
}
out->image_lump = WI_StringDuplicate(image_lump->valuestring);
cJSON *type = cJSON_GetObjectItemCaseSensitive(json, "type");
if (!cJSON_IsNumber(type))
{
return false;
}
out->type = type->valueint;
cJSON *duration = cJSON_GetObjectItemCaseSensitive(json, "duration");
if (!cJSON_IsNumber(duration))
{
return false;
}
out->duration = duration->valuedouble;
cJSON *maxduration = cJSON_GetObjectItemCaseSensitive(json, "maxduration");
if (!cJSON_IsNumber(maxduration))
{
return false;
}
out->maxduration = maxduration->valuedouble;
return true;
}
static boolean ParseAnim(cJSON *json, interlevelanim_t *out)
{
cJSON *js_frames = cJSON_GetObjectItemCaseSensitive(json, "frames");
cJSON *js_frame = NULL;
interlevelframe_t *frames = NULL;
cJSON_ArrayForEach(js_frame, js_frames)
{
interlevelframe_t frame = {0};
if (ParseFrame(js_frame, &frame))
{
array_push(frames, frame);
}
}
out->frames = frames;
cJSON *x_pos = cJSON_GetObjectItemCaseSensitive(json, "x");
cJSON *y_pos = cJSON_GetObjectItemCaseSensitive(json, "y");
if (!cJSON_IsNumber(x_pos) || !cJSON_IsNumber(y_pos))
{
return false;
}
out->x_pos = x_pos->valueint;
out->y_pos = y_pos->valueint;
cJSON *js_conditions = cJSON_GetObjectItemCaseSensitive(json, "conditions");
cJSON *js_condition = NULL;
interlevelcond_t *conditions = NULL;
cJSON_ArrayForEach(js_condition, js_conditions)
{
interlevelcond_t condition = {0};
if (ParseCondition(js_condition, &condition))
{
array_push(conditions, condition);
}
}
out->conditions = conditions;
return true;
}
static void ParseLevelLayer(cJSON *json, interlevellayer_t *out)
{
cJSON *js_anims = cJSON_GetObjectItemCaseSensitive(json, "anims");
cJSON *js_anim = NULL;
interlevelanim_t *anims = NULL;
cJSON_ArrayForEach(js_anim, js_anims)
{
interlevelanim_t anim = {0};
if (ParseAnim(js_anim, &anim))
{
array_push(anims, anim);
}
}
out->anims = anims;
cJSON *js_conditions = cJSON_GetObjectItemCaseSensitive(json, "conditions");
cJSON *js_condition = NULL;
interlevelcond_t *conditions = NULL;
cJSON_ArrayForEach(js_condition, js_conditions)
{
interlevelcond_t condition = {0};
if (ParseCondition(js_condition, &condition))
{
array_push(conditions, condition);
}
}
out->conditions = conditions;
}
interlevel_t *WI_ParseInterlevel(const char *lumpname)
{
interlevel_t *out = Z_Calloc(1, sizeof(*out), PU_LEVEL, NULL);
cJSON *json = cJSON_Parse(W_CacheLumpName(lumpname, PU_CACHE));
if (json == NULL)
{
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
{
char error_buf[32] = {0};
memcpy(error_buf, error_ptr, sizeof(error_buf) - 1);
I_Printf(VB_ERROR, "WI_ParseInterlevel: Error before: %s\n",
error_buf);
}
free(out);
cJSON_Delete(json);
return NULL;
}
cJSON *data = cJSON_GetObjectItemCaseSensitive(json, "data");
if (!cJSON_IsObject(data))
{
free(out);
cJSON_Delete(json);
return NULL;
}
cJSON *music = cJSON_GetObjectItemCaseSensitive(data, "music");
cJSON *backgroundimage =
cJSON_GetObjectItemCaseSensitive(data, "backgroundimage");
if (!cJSON_IsString(music) || !cJSON_IsString(backgroundimage))
{
free(out);
cJSON_Delete(json);
return NULL;
}
out->music_lump = WI_StringDuplicate(music->valuestring);
out->background_lump = WI_StringDuplicate(backgroundimage->valuestring);
cJSON *js_layers = cJSON_GetObjectItemCaseSensitive(data, "layers");
cJSON *js_layer = NULL;
interlevellayer_t *layers = NULL;
cJSON_ArrayForEach(js_layer, js_layers)
{
interlevellayer_t layer = {0};
ParseLevelLayer(js_layer, &layer);
array_push(layers, layer);
}
out->layers = layers;
cJSON_Delete(json);
return out;
}

90
src/wi_interlvl.h Normal file
View File

@ -0,0 +1,90 @@
//
// Copyright(C) 2024 Roman Fomin
//
// 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.
#ifndef WI_INTERLVL
#define WI_INTERLVL
typedef enum
{
AnimCondition_None,
AnimCondition_MapNumGreater, // Checks: Current/next map number.
// Parameter: map number
AnimCondition_MapNumEqual, // Checks: Current/next map number.
// Parameter: map number
AnimCondition_MapVisited, // Checks: Visited flag for map number.
// Parameter: map number
AnimCondition_MapNotSecret, // Checks: Current/next map.
// Parameter: none
AnimCondition_SecretVisited, // Checks: Any secret map visited.
// Parameter: none
AnimCondition_Tally, // Checks: Victory screen type
// Parameter: none
AnimCondition_IsEntering, // Checks: Victory screen type
// Parameter: none
} animcondition_t;
typedef enum
{
Frame_None = 0x0000,
Frame_Infinite = 0x0001,
Frame_FixedDuration = 0x0002,
Frame_RandomDuration = 0x0004,
Frame_RandomStart = 0x1000,
} frametype_t;
typedef struct interlevelcond_s
{
animcondition_t condition;
int param;
} interlevelcond_t;
typedef struct interlevelframe_s
{
char *image_lump;
int image_lumpnum;
frametype_t type;
double duration;
double maxduration;
} interlevelframe_t;
typedef struct interlevelanim_s
{
interlevelframe_t *frames;
interlevelcond_t *conditions;
int x_pos;
int y_pos;
} interlevelanim_t;
typedef struct interlevellayer_s
{
interlevelanim_t *anims;
interlevelcond_t *conditions;
} interlevellayer_t;
typedef struct interlevel_s
{
char *music_lump;
char *background_lump;
interlevellayer_t *layers;
} interlevel_t;
interlevel_t *WI_ParseInterlevel(const char *lumpname);
#endif

View File

@ -42,6 +42,7 @@
#include "v_fmt.h"
#include "v_video.h"
#include "w_wad.h"
#include "wi_interlvl.h"
#include "wi_stuff.h"
#include "z_zone.h"
@ -385,10 +386,296 @@ static int num_lnames;
static const char *exitpic, *enterpic;
#define M_ARRAY_MALLOC(size) Z_Malloc((size), PU_LEVEL, NULL)
#define M_ARRAY_REALLOC(ptr, size) Z_Realloc((ptr), (size), PU_LEVEL, NULL)
#define M_ARRAY_FREE(ptr) Z_Free((ptr))
#include "m_array.h"
typedef struct
{
interlevelframe_t *frames;
int x_pos;
int y_pos;
int frame_index;
boolean frame_start;
int duration_left;
} wi_animationstate_t;
typedef struct
{
interlevel_t *interlevel_exiting;
interlevel_t *interlevel_entering;
wi_animationstate_t *exiting_states;
wi_animationstate_t *entering_states;
wi_animationstate_t *states;
int background_lumpnum;
} wi_animation_t;
static wi_animation_t *animation;
//
// CODE
//
static boolean CheckConditions(interlevelcond_t *conditions,
boolean enteringcondition)
{
boolean conditionsmet = true;
int map_number, map, episode;
{
mapentry_t *mape;
if (enteringcondition)
{
mape = wbs->nextmapinfo;
map = wbs->next + 1;
episode = wbs->nextep + 1;
}
else
{
mape = wbs->lastmapinfo;
map = wbs->last + 1;
episode = wbs->epsd + 1;
}
map_number = mape->map_number ? mape->map_number : mape->all_number;
}
interlevelcond_t *cond;
array_foreach(cond, conditions)
{
switch (cond->condition)
{
case AnimCondition_MapNumGreater:
conditionsmet = (map_number > cond->param);
break;
case AnimCondition_MapNumEqual:
conditionsmet = (map_number == cond->param);
break;
case AnimCondition_MapVisited:
conditionsmet = false;
level_t *level;
array_foreach(level, wbs->visitedlevels)
{
mapentry_t *mape =
G_LookupMapinfo(level->episode, level->map);
if ((mape->map_number && mape->map_number == cond->param)
|| mape->all_number == cond->param)
{
conditionsmet = true;
break;
}
}
break;
case AnimCondition_MapNotSecret:
conditionsmet = !U_IsSecretMap(episode, map);
break;
case AnimCondition_SecretVisited:
conditionsmet = wbs->didsecret;
break;
case AnimCondition_Tally:
conditionsmet = !enteringcondition;
break;
case AnimCondition_IsEntering:
conditionsmet = enteringcondition;
break;
default:
break;
}
}
return conditionsmet;
}
static void UpdateAnimationStates(wi_animationstate_t *states)
{
wi_animationstate_t *state;
array_foreach(state, states)
{
interlevelframe_t *frame = &state->frames[state->frame_index];
int lumpnum = W_CheckNumForName(frame->image_lump);
if (lumpnum < 0)
{
lumpnum = (W_CheckNumForName)(frame->image_lump, ns_sprites);
}
frame->image_lumpnum = lumpnum;
if (frame->type & Frame_Infinite)
{
continue;
}
if (state->duration_left == 0)
{
int tics = 1;
switch (frame->type)
{
case Frame_RandomStart:
if (state->frame_start)
{
int maxtics = frame->duration * TICRATE;
tics = M_Random() % maxtics;
break;
}
// fall through
case Frame_FixedDuration:
tics = frame->duration * TICRATE;
break;
case Frame_RandomDuration:
{
int maxtics = frame->maxduration * TICRATE;
int mintics = frame->duration * TICRATE;
tics = M_Random() % maxtics;
tics = BETWEEN(mintics, maxtics, tics);
}
break;
default:
break;
}
state->duration_left = MAX(tics, 1);
if (!state->frame_start)
{
state->frame_index++;
if (state->frame_index == array_size(state->frames))
{
state->frame_index = 0;
}
}
}
state->duration_left--;
state->frame_start = false;
}
}
static boolean UpdateAnimation(boolean enteringcondition)
{
if (!animation)
{
return false;
}
animation->states = NULL;
if (!enteringcondition && animation->interlevel_exiting)
{
animation->states = animation->exiting_states;
animation->background_lumpnum =
W_CheckNumForName(animation->interlevel_exiting->background_lump);
}
else if (animation->interlevel_entering)
{
animation->states = animation->entering_states;
animation->background_lumpnum =
W_CheckNumForName(animation->interlevel_entering->background_lump);
}
UpdateAnimationStates(animation->states);
return true;
}
static boolean DrawAnimation(void)
{
if (!animation)
{
return false;
}
V_DrawPatchFullScreen(
V_CachePatchNum(animation->background_lumpnum, PU_CACHE));
wi_animationstate_t *state;
array_foreach(state, animation->states)
{
interlevelframe_t *frame = &state->frames[state->frame_index];
patch_t *patch = V_CachePatchNum(frame->image_lumpnum, PU_CACHE);
V_DrawPatch(state->x_pos, state->y_pos, patch);
}
return true;
}
static wi_animationstate_t *SetupAnimationStates(interlevellayer_t *layers,
boolean enteringcondition)
{
wi_animationstate_t *states = NULL;
interlevellayer_t *layer;
array_foreach(layer, layers)
{
if (!CheckConditions(layer->conditions, enteringcondition))
{
continue;
}
interlevelanim_t *anim;
array_foreach(anim, layer->anims)
{
if (!CheckConditions(anim->conditions, enteringcondition))
{
continue;
}
wi_animationstate_t state = {0};
state.x_pos = anim->x_pos;
state.y_pos = anim->y_pos;
state.frames = anim->frames;
state.frame_start = true;
array_push(states, state);
}
}
return states;
}
static boolean SetupAnimation(void)
{
if (!animation)
{
return false;
}
if (animation->interlevel_exiting)
{
animation->exiting_states =
SetupAnimationStates(animation->interlevel_exiting->layers, false);
}
if (animation->interlevel_entering)
{
animation->entering_states =
SetupAnimationStates(animation->interlevel_entering->layers, true);
}
return true;
}
static boolean NextLocAnimation(void)
{
if (animation && animation->entering_states
&& !(demorecording || demoplayback))
{
return true;
}
return false;
}
// ====================================================================
// WI_slamBackground
@ -596,6 +883,11 @@ static void WI_initAnimatedBack(boolean firstcall)
int i;
anim_t* a;
if (SetupAnimation())
{
return;
}
if (exitpic)
return;
if (enterpic && entering)
@ -616,7 +908,7 @@ static void WI_initAnimatedBack(boolean firstcall)
// via WI_initShowNextLoc. Fixes notable blinking of Tower of Babel drawing
// and the rest of animations from being restarted.
if (firstcall)
a->ctr = -1;
a->ctr = -1;
// specify the next time to draw it
if (a->type == ANIM_ALWAYS)
@ -642,6 +934,11 @@ static void WI_updateAnimatedBack(void)
int i;
anim_t* a;
if (UpdateAnimation(state != StatCount))
{
return;
}
if (exitpic)
return;
if (enterpic && state != StatCount)
@ -704,6 +1001,11 @@ static void WI_drawAnimatedBack(void)
int i;
anim_t* a;
if (DrawAnimation())
{
return;
}
if (exitpic)
return;
if (enterpic && state != StatCount)
@ -1267,6 +1569,8 @@ static void WI_updateDeathmatchStats(void)
{
S_StartSound(0, sfx_slop);
if (NextLocAnimation())
WI_initShowNextLoc();
if ( gamemode == commercial)
WI_initNoState();
else
@ -1570,6 +1874,9 @@ static void WI_updateNetgameStats(void)
if (acceleratestage)
{
S_StartSound(0, sfx_sgcock);
if (NextLocAnimation())
WI_initShowNextLoc();
if ( gamemode == commercial )
WI_initNoState();
else
@ -1788,7 +2095,9 @@ static void WI_updateStats(void)
{
S_StartSound(0, sfx_sgcock);
if (gamemode == commercial)
if (NextLocAnimation())
WI_initShowNextLoc();
else if (gamemode == commercial)
WI_initNoState();
else
WI_initShowNextLoc();
@ -2218,8 +2527,43 @@ void WI_Start(wbstartstruct_t* wbstartstruct)
{
WI_initVariables(wbstartstruct);
exitpic = (wbs->lastmapinfo && wbs->lastmapinfo->exitpic[0]) ? wbs->lastmapinfo->exitpic : NULL;
enterpic = (wbs->nextmapinfo && wbs->nextmapinfo->enterpic[0]) ? wbs->nextmapinfo->enterpic : NULL;
exitpic = NULL;
enterpic = NULL;
animation = NULL;
if (wbs->lastmapinfo)
{
if (wbs->lastmapinfo->exitpic[0])
{
exitpic = wbs->lastmapinfo->exitpic;
}
if (wbs->lastmapinfo->exitanim[0])
{
if (!animation)
{
animation = Z_Calloc(1, sizeof(*animation), PU_LEVEL, NULL);
}
animation->interlevel_exiting =
WI_ParseInterlevel(wbs->lastmapinfo->exitanim);
}
}
if (wbs->nextmapinfo)
{
if (wbs->nextmapinfo->enterpic[0])
{
enterpic = wbs->nextmapinfo->enterpic;
}
if (wbs->nextmapinfo->enteranim[0])
{
if (!animation)
{
animation = Z_Calloc(1, sizeof(*animation), PU_LEVEL, NULL);
}
animation->interlevel_entering =
WI_ParseInterlevel(wbs->nextmapinfo->enteranim);
}
}
WI_loadData();

View File

@ -41,6 +41,7 @@
{
"name": "libxmp",
"default-features": false
}
},
"cjson"
]
}