From 3a332d9f2b85f80e72bcc9f94dc26b6a8aed982c Mon Sep 17 00:00:00 2001 From: Fernando Carmona Varo Date: Mon, 12 Aug 2019 19:30:50 +0200 Subject: [PATCH] Add MUSINFO support Based on prboom-plus support. However, unlike prboom-plus this will also support the music files configured in prboom.cfg, if configured to do so. Another difference is that the doom id 14165 is used for changing the music to the default level track. Which is what ZDoom does. See: https://zdoom.org/wiki/Classes:MusicChanger Also, support for 14100 was added too since crispy/doom retro do that. See: https://zdoom.org/wiki/MUSINFO --- Makefile.common | 3 +- src/info.c | 32 ++++++++++ src/info.h | 2 + src/p_mobj.c | 148 +++++++++++++++++++++++------------------- src/p_mobj.h | 5 +- src/p_setup.c | 4 ++ src/p_tick.c | 4 ++ src/s_sound.c | 1 - src/s_sound.h | 1 + src/u_musinfo.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ src/u_musinfo.h | 41 ++++++++++++ src/w_wad.c | 8 ++- src/w_wad.h | 1 + 13 files changed, 348 insertions(+), 69 deletions(-) create mode 100644 src/u_musinfo.c create mode 100644 src/u_musinfo.h diff --git a/Makefile.common b/Makefile.common index 73afc5d4..1d08edb3 100644 --- a/Makefile.common +++ b/Makefile.common @@ -113,8 +113,9 @@ SOURCES_C += $(CORE_DIR)/am_map.c \ $(CORE_DIR)/flplayer.c \ $(CORE_DIR)/midifile.c \ $(CORE_DIR)/madplayer.c \ - $(CORE_DIR)/u_mapinfo.c \ $(CORE_DIR)/u_scanner.c \ + $(CORE_DIR)/u_mapinfo.c \ + $(CORE_DIR)/u_musinfo.c \ ifeq ($(WANT_FLUIDSYNTH), 1) diff --git a/src/info.c b/src/info.c index c32c845c..7d0a8994 100644 --- a/src/info.c +++ b/src/info.c @@ -5526,4 +5526,36 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { 0, // maxattackrange 200, // minmissilechance }, + + // Will change music according to MUSINFO lump + { // MT_MUSICCHANGER + "MusicChanger", // actorname + 14165, // doomednum // 14100-14164 should also be reserved + S_TNT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + MT_NULL, // droppeditem + 0, // speed + 16, // radius + 16, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL, // raisestate + 0, // meleethreshold + 0, // maxattackrange + 200, // minmissilechance + }, }; diff --git a/src/info.h b/src/info.h index ecdb89b4..7a090e25 100644 --- a/src/info.h +++ b/src/info.h @@ -1411,6 +1411,8 @@ typedef enum { MT_PUSH, /* controls push source - phares */ MT_PULL, /* controls pull source - phares 3/20/98 */ + MT_MUSICCHANGER, /* MUSINFO Music Changer thing */ + /* proff 11/22/98: Andy Baker's stealth monsters (next 12) * cph - moved below the MBF stuff, no need to displace them */ MT_STEALTHBABY, diff --git a/src/p_mobj.c b/src/p_mobj.c index eb3071ce..6ed02bff 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -47,6 +47,7 @@ #include "p_inter.h" #include "lprintf.h" #include "r_demo.h" +#include "u_musinfo.h" // // P_SetMobjState @@ -729,45 +730,50 @@ void P_MobjThinker (mobj_t* mobj) // removed old code which looked at target references // (we use pointer reference counting now) + if (mobj->type == MT_MUSICCHANGER) + { + P_MusInfoMobjThinker(mobj); + return; + } + mobj->PrevX = mobj->x; mobj->PrevY = mobj->y; mobj->PrevZ = mobj->z; // momentum movement if (mobj->momx | mobj->momy || mobj->flags & MF_SKULLFLY) - { - P_XYMovement(mobj); - if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed - return; // killough - mobj was removed - } + { + P_XYMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } if (mobj->z != mobj->floorz || mobj->momz) - { - P_ZMovement(mobj); - if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed - return; // killough - mobj was removed - } - else - if (!(mobj->momx | mobj->momy) && !sentient(mobj)) - { // non-sentient objects at rest - mobj->intflags |= MIF_ARMED; // arm a mine which has come to rest + { + P_ZMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + else if (!(mobj->momx | mobj->momy) && !sentient(mobj)) + { // non-sentient objects at rest + mobj->intflags |= MIF_ARMED; // arm a mine which has come to rest - // killough 9/12/98: objects fall off ledges if they are hanging off - // slightly push off of ledge if hanging more than halfway off + // killough 9/12/98: objects fall off ledges if they are hanging off + // slightly push off of ledge if hanging more than halfway off - if (mobj->z > mobj->dropoffz && // Only objects contacting dropoff - !(mobj->flags & MF_NOGRAVITY) && // Only objects which fall - !comp[comp_falloff]) // Not in old demos - P_ApplyTorque(mobj); // Apply torque - else - mobj->intflags &= ~MIF_FALLING, mobj->gear = 0; // Reset torque - } + if (mobj->z > mobj->dropoffz && // Only objects contacting dropoff + !(mobj->flags & MF_NOGRAVITY) && // Only objects which fall + !comp[comp_falloff]) // Not in old demos + P_ApplyTorque(mobj); // Apply torque + else + mobj->intflags &= ~MIF_FALLING, mobj->gear = 0; // Reset torque + } // cycle through states, // calling action functions at transitions if (mobj->tics != -1) - { + { mobj->tics--; // you can cycle through multiple states in a tic @@ -775,9 +781,9 @@ void P_MobjThinker (mobj_t* mobj) if (!mobj->tics) if (!P_SetMobjState (mobj, mobj->state->nextstate) ) return; // freed itself - } + } else - { + { // check for nightmare respawn @@ -799,7 +805,7 @@ void P_MobjThinker (mobj_t* mobj) return; P_NightmareRespawn (mobj); - } + } } @@ -1156,19 +1162,21 @@ void P_SpawnMapThing (const mapthing_t* mthing) fixed_t y; fixed_t z; int options = mthing->options; /* cph 2001/07/07 - make writable copy */ + short thingtype = mthing->type; + int iden_num = 0; // killough 2/26/98: Ignore type-0 things as NOPs // phares 5/14/98: Ignore Player 5-8 starts (for now) - switch(mthing->type) - { - case 0: - case DEN_PLAYER5: - case DEN_PLAYER6: - case DEN_PLAYER7: - case DEN_PLAYER8: - return; - } + switch(thingtype) + { + case 0: + case DEN_PLAYER5: + case DEN_PLAYER6: + case DEN_PLAYER7: + case DEN_PLAYER8: + return; + } // killough 11/98: clear flags unused by Doom // @@ -1183,54 +1191,54 @@ void P_SpawnMapThing (const mapthing_t* mthing) options & MTF_RESERVED)) { if (!demo_compatibility) // cph - Add warning about bad thing flags lprintf(LO_WARN, "P_SpawnMapThing: correcting bad flags (%u) (thing type %d)\n", - options, mthing->type); + options, thingtype); options &= MTF_EASY|MTF_NORMAL|MTF_HARD|MTF_AMBUSH|MTF_NOTSINGLE; } // count deathmatch start positions // doom2.exe has at most 10 deathmatch starts - if (mthing->type == 11) - { + if (thingtype == 11) + { if (!(!compatibility || deathmatch_p-deathmatchstarts < 10)) { - return; - } else { - // 1/11/98 killough -- new code removes limit on deathmatch starts: + return; + } else { + // 1/11/98 killough -- new code removes limit on deathmatch starts: - size_t offset = deathmatch_p - deathmatchstarts; + size_t offset = deathmatch_p - deathmatchstarts; - if (offset >= num_deathmatchstarts) + if (offset >= num_deathmatchstarts) { - num_deathmatchstarts = num_deathmatchstarts ? + num_deathmatchstarts = num_deathmatchstarts ? num_deathmatchstarts*2 : 16; - deathmatchstarts = realloc(deathmatchstarts, + deathmatchstarts = realloc(deathmatchstarts, num_deathmatchstarts * sizeof(*deathmatchstarts)); - deathmatch_p = deathmatchstarts + offset; + deathmatch_p = deathmatchstarts + offset; } - memcpy(deathmatch_p++, mthing, sizeof(*mthing)); - (deathmatch_p-1)->options = 1; - return; - } + memcpy(deathmatch_p++, mthing, sizeof(*mthing)); + (deathmatch_p-1)->options = 1; + return; } + } // check for players specially - if (mthing->type <= 4 && mthing->type > 0) // killough 2/26/98 -- fix crashes - { + if (thingtype <= 4 && thingtype > 0) // killough 2/26/98 -- fix crashes + { // save spots for respawning in coop games - playerstarts[mthing->type-1] = *mthing; + playerstarts[thingtype-1] = *mthing; /* cph 2006/07/24 - use the otherwise-unused options field to flag that * this start is present (so we know which elements of the array are filled * in, in effect). Also note that the call below to P_SpawnPlayer must use * the playerstarts version with this field set */ - playerstarts[mthing->type-1].options = 1; + playerstarts[thingtype-1].options = 1; if (!deathmatch) - P_SpawnPlayer (mthing->type-1, &playerstarts[mthing->type-1]); + P_SpawnPlayer (thingtype-1, &playerstarts[thingtype-1]); return; - } + } // check for apropriate skill level @@ -1257,18 +1265,27 @@ void P_SpawnMapThing (const mapthing_t* mthing) // find which type to spawn - // killough 8/23/98: use table for faster lookup - i = P_FindDoomedNum(mthing->type); + // Thing types from 14100 to 14164 are used for MUSINFO entities + // this means they are actually the same MusicChanger thingtype but + // each different type should have a different ambient music id. + // -- See https://doomwiki.org/wiki/MUSINFO -- + if (thingtype >= 14100 && thingtype <= 14164) + { + iden_num = thingtype - 14100; // Ambient music to change + i = MT_MUSICCHANGER; + } + else // killough 8/23/98: use table for faster lookup + i = P_FindDoomedNum(thingtype); // phares 5/16/98: // Do not abort because of an unknown thing. Ignore it, but post a // warning message for the player. if (i == NUMMOBJTYPES) - { + { doom_printf("Unknown Thing type %i at (%i, %i)",mthing->type,mthing->x,mthing->y); return; - } + } // don't spawn keycards and players in deathmatch @@ -1292,6 +1309,7 @@ void P_SpawnMapThing (const mapthing_t* mthing) mobj = P_SpawnMobj (x,y,z, i); mobj->spawnpoint = *mthing; + mobj->iden_num = iden_num; if (mobj->tics > 0) mobj->tics = 1 + (P_Random (pr_spawnthing) % mobj->tics); @@ -1299,10 +1317,10 @@ void P_SpawnMapThing (const mapthing_t* mthing) if (!(mobj->flags & MF_FRIEND) && options & MTF_FRIEND && mbf_features) - { - mobj->flags |= MF_FRIEND; // killough 10/98: - P_UpdateThinker(&mobj->thinker); // transfer friendliness flag - } + { + mobj->flags |= MF_FRIEND; // killough 10/98: + P_UpdateThinker(&mobj->thinker); // transfer friendliness flag + } /* killough 7/20/98: exclude friends */ if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) diff --git a/src/p_mobj.h b/src/p_mobj.h index 10f69aaf..95290dff 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -324,7 +324,7 @@ typedef struct mobj_s int tics; // state tic counter state_t* state; - uint64_t flags; + uint64_t flags; int intflags; // killough 9/15/98: internal flags int health; @@ -380,6 +380,9 @@ typedef struct mobj_s fixed_t PrevY; fixed_t PrevZ; + // Extra id based on thing type that's used in MUSINFO + short iden_num; + fixed_t pad; // cph - needed so I can get the size unambiguously on amd64 // SEE WARNING ABOVE ABOUT POINTER FIELDS!!! diff --git a/src/p_setup.c b/src/p_setup.c index 6212bcb4..bbf1bc3d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -50,6 +50,7 @@ #include "v_video.h" #include "r_demo.h" #include "r_fps.h" +#include "u_musinfo.h" // // MAP related Lookup tables. @@ -1880,6 +1881,9 @@ void P_SetupLevel(int episode, int map, int playermask, skill_t skill) if (gamemode==commercial) P_SpawnBrainTargets(); + // load MUSINFO from the map, if it exists + U_ParseMusInfo(lumpname); + // clear special respawning que iquehead = iquetail = 0; diff --git a/src/p_tick.c b/src/p_tick.c index 55792393..5a88fa63 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -37,6 +37,7 @@ #include "p_tick.h" #include "p_map.h" #include "r_fps.h" +#include "u_musinfo.h" int leveltime; @@ -261,6 +262,9 @@ static void P_RunThinkers (void) currentthinker->function(currentthinker); } newthinkerpresent = FALSE; + + // Dedicated thinkers + P_MapMusicThinker(); } /* diff --git a/src/s_sound.c b/src/s_sound.c index d4d62be0..d6fcaa01 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -112,7 +112,6 @@ int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, int *vol, int *sep, int *pitch); static int S_getChannel(void *origin, sfxinfo_t *sfxinfo, int is_pickup); -static void S_ChangeMusicByName(char* lumpname, int looping); // Initializes sound stuff, including volume // Sets channels, SFX and music volume, diff --git a/src/s_sound.h b/src/s_sound.h index 4c935970..453128a0 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -71,6 +71,7 @@ void S_StartMusic(int music_id); // Start music using from sounds.h, and set whether looping void S_ChangeMusic(int music_id, int looping); +void S_ChangeMusicByName(char* lumpname, int looping); // Stops the music fer sure. void S_StopMusic(void); diff --git a/src/u_musinfo.c b/src/u_musinfo.c new file mode 100644 index 00000000..9d8a2d03 --- /dev/null +++ b/src/u_musinfo.c @@ -0,0 +1,167 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: MUSINFO Support + * + *-----------------------------------------------------------------------------*/ + +// killough 3/7/98: modified to allow arbitrary listeners in spy mode +// killough 5/2/98: reindented, removed useless code, beautified +// ferk 10/1/19: cleanup/refactor, reuse u_scanner, support id:0 default music like ZDoom + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "doomtype.h" +#include "d_main.h" +#include "p_mobj.h" +#include "m_misc.h" +#include "sounds.h" +#include "s_sound.h" +#include "i_sound.h" +#include "r_defs.h" +#include "w_wad.h" +#include "lprintf.h" +#include "u_scanner.h" + +#include "u_musinfo.h" + +#define MAX_MUS_ENTRIES 64 + +// +//MUSINFO lump +// + +typedef struct musinfo_s +{ + mobj_t *mapthing; + mobj_t *lastmapthing; + int tics; + int items[MAX_MUS_ENTRIES]; +} musinfo_t; + +musinfo_t musinfo; + +// +// U_ParseMusInfo +// Parses MUSINFO lump. +// +void U_ParseMusInfo(const char *mapid) +{ + memset(&musinfo, 0, sizeof(musinfo)); + + S_music[NUMMUSIC].lumpnum = -1; + + int musinfolump = W_GetNumForName("MUSINFO"); + if (musinfolump != -1) + { + const char *data = W_CacheLumpNum(musinfolump); + int datalength = W_LumpLength(musinfolump); + int lumpnum; + int inMap = false; + + u_scanner_t s = U_ScanOpen(data, datalength); + while (U_HasTokensLeft(&s)) + { + if (inMap || U_CheckToken(&s, TK_Identifier)) + { + if (!inMap) + inMap = true; + + // If there's a new map as identifier, stop search + if (U_CheckToken(&s, TK_Identifier) && + (s.string[0] == 'E' || s.string[0] == 'e' || + s.string[0] == 'M' || s.string[0] == 'm')) + { + break; + } + + U_MustGetInteger(&s); + + // Check number in range + if (s.number > 0 && s.number < MAX_MUS_ENTRIES) + { + if (U_MustGetToken(&s, TK_Identifier)) + { + lumpnum = W_CheckNumForName(s.string); + + if (lumpnum >= 0) + musinfo.items[s.number] = lumpnum; + else + U_Error(&s, "U_ParseMusInfo: Unknown MUS lump %s", s.string); + } + } + else + U_Error(&s, "U_ParseMusInfo: Number not in range 1 to %d", MAX_MUS_ENTRIES); + } + else + U_GetNextToken(&s, TRUE); + } + + U_ScanClose(&s); + } +} + +// Thinker for Music Changer mapthing +// It'll configure the music to play when player is in the same sector +void P_MusInfoMobjThinker(mobj_t *thing) +{ + if (musinfo.mapthing != thing && + thing->subsector->sector == players[displayplayer].mo->subsector->sector) + { + musinfo.lastmapthing = musinfo.mapthing; + musinfo.mapthing = thing; + musinfo.tics = 30; + } +} + +void P_MapMusicThinker(void) +{ + if (musinfo.tics < 0 || !musinfo.mapthing) + return; + + if (musinfo.tics > 0) + musinfo.tics--; + else if (musinfo.lastmapthing != musinfo.mapthing) + { + int musitem = musinfo.mapthing->iden_num; + + if (musitem == 0) // play default level music + { + S_Start(); + } + else if (musitem > 0 && musitem < MAX_MUS_ENTRIES) + { + char* musicname = W_GetNameForNum(musinfo.items[musitem]); + if (musicname) + S_ChangeMusicByName(musicname, true); + } + musinfo.tics = -1; + } +} diff --git a/src/u_musinfo.h b/src/u_musinfo.h new file mode 100644 index 00000000..d345f995 --- /dev/null +++ b/src/u_musinfo.h @@ -0,0 +1,41 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * MUSINFO support + *-----------------------------------------------------------------------------*/ + +#ifndef __U_MUSINFO__ +#define __U_MUSINFO__ + +#include "p_mobj.h" + +void U_ParseMusInfo(const char *mapid); +void P_MusInfoMobjThinker(mobj_t *thing); +void P_MapMusicThinker(void); + +#endif diff --git a/src/w_wad.c b/src/w_wad.c index a35c951c..3c157903 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -411,7 +411,13 @@ int W_GetNumForName (const char* name) // killough -- const added return i; } - +// W_GetNameForNum +// Returns the name for the giiven lump number +// +char* W_GetNameForNum (const int lump) +{ + return (lump >=0 && lump < numlumps)? lumpinfo[lump].name : NULL; +} // W_ListNumFromName // calls W_FindNumFromName and returns the lumps in ascending order diff --git a/src/w_wad.h b/src/w_wad.h index 82a4e6a3..cc5d68f8 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -137,6 +137,7 @@ int W_ListNumFromName(const char *name, int lump); static INLINE int (W_CheckNumForName)(const char *name, lumpinfo_namespace_t ns) { return (W_FindNumFromName)(name, ns, -1); } int W_GetNumForName (const char* name); +char* W_GetNameForNum (const int lump); int W_LumpLength (int lump); void W_ReadLump (int lump, void *dest); // CPhipps - modified for 'new' lump locking