From be6daf5d787de17ce5fabf36ac2e9c5488d79f1b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 19:13:28 +0100 Subject: [PATCH 01/13] - changed instrument lookup in MUS files. Based on evidence from several songs in Eternal Doom the description in all known documents is wrong. The instruments are not stored in a 16-bit word but in an 8-bit byte, followed by some variable size data. Known variations are: * second byte is 0 - no additional data follows * second byte is 1 - a third byte for the 'bank' value follows. --- src/sound/music_mus_midiout.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 5f7d2d503..5f5d39d18 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -43,6 +43,7 @@ #include "doomdef.h" #include "m_swap.h" #include "files.h" +#include "s_sound.h" // MACROS ------------------------------------------------------------------ @@ -211,20 +212,34 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { WORD *work = (WORD *)alloca(MusHeader->NumInstruments * sizeof(WORD)); - const WORD *used = (WORD *)MusHeader + sizeof(MUSHeader) / sizeof(WORD); - int i, j; + const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); + int i, j, k; - for (i = j = 0; i < MusHeader->NumInstruments; ++i) + for (i = j = k = 0; i < MusHeader->NumInstruments; ++i) { - WORD instr = LittleShort(used[i]); + BYTE instr = used[k++]; + WORD val; if (instr < 128) { - work[j++] = instr; + val = instr; } - else if (used[i] >= 135 && used[i] <= 181) + else if (instr >= 135 && instr <= 181) { // Percussions are 100-based, not 128-based, eh? - work[j++] = instr - 100 + (1 << 14); + val = instr - 100 + (1 << 14); } + + BYTE moreparam = used[k++]; + if (moreparam == 1) + { + BYTE bank = used[k++]; + val |= (bank << 7); + } + else if (moreparam > 0) + { + // No information if this is even valid. Print a message so it can be investigated later + Printf("Unknown instrument data found in music\n"); + } + work[j++] = val; } MIDI->PrecacheInstruments(&work[0], j); } From 3c40d71c20805d96d36089559b1112c9ca682a9e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 20:32:19 +0100 Subject: [PATCH 02/13] - hopefully fixed the MUS precaching for good. According to blzut3, it looks like it is a byte followed by a variable length field. It can be any value 0-15 and will be followed by that many bytes one for each bank used. If the bank count is 0 then it is shorthand for using one bank (bank 0). --- src/sound/music_mus_midiout.cpp | 16 ++++++++-------- src/wildmidi/wildmidi_lib.cpp | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 5f5d39d18..e81adc07f 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -228,18 +228,18 @@ void MUSSong2::Precache() val = instr - 100 + (1 << 14); } - BYTE moreparam = used[k++]; - if (moreparam == 1) + int numbanks = used[k++]; + if (numbanks > 0) { - BYTE bank = used[k++]; - val |= (bank << 7); + for (int b = 0; b < numbanks; b++) + { + work[j++] = val | used[k++]; + } } - else if (moreparam > 0) + else { - // No information if this is even valid. Print a message so it can be investigated later - Printf("Unknown instrument data found in music\n"); + work[j++] = val; } - work[j++] = val; } MIDI->PrecacheInstruments(&work[0], j); } diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp index 94a446e17..0a527ec2a 100644 --- a/src/wildmidi/wildmidi_lib.cpp +++ b/src/wildmidi/wildmidi_lib.cpp @@ -2719,14 +2719,14 @@ midi *WildMidi_NewMidi() { } } - if ((((_mdi*)ret)->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width, - reverb_room_length, reverb_listen_posx, reverb_listen_posy)) - == NULL) { - _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0); + if ((((_mdi*)ret)->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width, + reverb_room_length, reverb_listen_posx, reverb_listen_posy)) + == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0); WildMidi_Close(ret); ret = NULL; - } - + } + return ret; } From c88ed426a805e191742067ad2654871a40bd15e0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Dec 2015 20:39:38 +0100 Subject: [PATCH 03/13] - oops, this somehow lost the shift operator... --- src/sound/music_mus_midiout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index e81adc07f..1ebaa2eb4 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -233,7 +233,7 @@ void MUSSong2::Precache() { for (int b = 0; b < numbanks; b++) { - work[j++] = val | used[k++]; + work[j++] = val | (used[k++] << 7); } } else From 1316120fe4ebf2ce47e6ebe595d20cd10901602d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 31 Dec 2015 14:35:34 +0100 Subject: [PATCH 04/13] - fixed: The MUS precacher did not handle invalid patches well. - increased the valid range of patch values for MUS. According to the original MIDI2MUS code it can handle numbers up to 188, not 181, and at least one track from Eternal Doom uses #183. --- src/sound/music_mus_midiout.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 1ebaa2eb4..eaf7e4afb 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -223,10 +223,17 @@ void MUSSong2::Precache() { val = instr; } - else if (instr >= 135 && instr <= 181) + else if (instr >= 135 && instr <= 188) { // Percussions are 100-based, not 128-based, eh? val = instr - 100 + (1 << 14); } + else + { + // skip it. + val = used[k++]; + k += val; + continue; + } int numbanks = used[k++]; if (numbanks > 0) From d8af2e558fde7e494bd1f5b636f8989efb8391fb Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 15:28:18 -0600 Subject: [PATCH 05/13] Fix potentiol buffer overrun in MUSSong2::Precache() --- src/sound/music_mus_midiout.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index eaf7e4afb..cc9bc1b69 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -211,11 +211,11 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { - WORD *work = (WORD *)alloca(MusHeader->NumInstruments * sizeof(WORD)); + TArray work(MusHeader->NumInstruments); const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); - int i, j, k; + int i, k; - for (i = j = k = 0; i < MusHeader->NumInstruments; ++i) + for (i = k = 0; i < MusHeader->NumInstruments; ++i) { BYTE instr = used[k++]; WORD val; @@ -240,15 +240,15 @@ void MUSSong2::Precache() { for (int b = 0; b < numbanks; b++) { - work[j++] = val | (used[k++] << 7); + work.Push(val | (used[k++] << 7)); } } else { - work[j++] = val; + work.Push(val); } } - MIDI->PrecacheInstruments(&work[0], j); + MIDI->PrecacheInstruments(&work[0], work.Size()); } //========================================================================== From 5e975ac9f66a955cf19255555b725ce9170d33da Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 31 Dec 2015 23:03:53 +0100 Subject: [PATCH 06/13] - extended $mididevice to add an optional parameter, which has the following meaning for the different MIDI devices: * OPL: specify the core to use for playing this song * FluidSynth: specify a soundfont that should be used for playing the song. * WildMidi: specify a config file that should be used for playing the song. * Timidity++: specify an executable that should be used for playing the song. At least under Windows this allows using Timidity++ with different configs if the executable and each single config are placed in different directories. * GUS: currently not operational, but should later also specify the config. This will need some work, because right now this is initialized only when the sound system is initialized. * all other: no function. These options should mainly be for end users who want to fine-tune how to play the music. --- src/oplsynth/mlopl_io.cpp | 5 +-- src/oplsynth/music_opl_mididevice.cpp | 4 ++- src/oplsynth/music_opldumper_mididevice.cpp | 1 + src/s_advsound.cpp | 36 ++++++++++++++++----- src/s_sound.cpp | 7 ++-- src/s_sound.h | 13 +++++++- src/sound/i_music.cpp | 31 +++++++++--------- src/sound/i_music.h | 3 +- src/sound/i_musicinterns.h | 23 ++++++------- src/sound/music_fluidsynth_mididevice.cpp | 12 +++++-- src/sound/music_hmi_midiout.cpp | 4 +-- src/sound/music_midi_timidity.cpp | 14 ++++---- src/sound/music_midistream.cpp | 14 ++++---- src/sound/music_mus_midiout.cpp | 4 +-- src/sound/music_mus_opl.cpp | 15 +++++++-- src/sound/music_smf_midiout.cpp | 4 +-- src/sound/music_timidity_mididevice.cpp | 5 +-- src/sound/music_wildmidi_mididevice.cpp | 10 +++--- src/sound/music_xmi_midiout.cpp | 4 +-- src/timidity/timidity.cpp | 3 +- src/timidity/timidity.h | 2 +- 21 files changed, 135 insertions(+), 79 deletions(-) diff --git a/src/oplsynth/mlopl_io.cpp b/src/oplsynth/mlopl_io.cpp index 691463470..4ec41d6c9 100644 --- a/src/oplsynth/mlopl_io.cpp +++ b/src/oplsynth/mlopl_io.cpp @@ -48,6 +48,7 @@ #define HALF_PI (PI*0.5) EXTERN_CVAR(Int, opl_core) +extern int current_opl_core; OPLio::~OPLio() { @@ -323,7 +324,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) { assert(numchips >= 1 && numchips <= countof(chips)); uint i; - IsOPL3 = (opl_core == 1 || opl_core == 2 || opl_core == 3); + IsOPL3 = (current_opl_core == 1 || current_opl_core == 2 || current_opl_core == 3); memset(chips, 0, sizeof(chips)); if (IsOPL3) @@ -332,7 +333,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) } for (i = 0; i < numchips; ++i) { - OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : (opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); + OPLEmul *chip = IsOPL3 ? (current_opl_core == 1 ? DBOPLCreate(stereo) : (current_opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); if (chip == NULL) { break; diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 79bb10226..aa6cda7ee 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -53,6 +53,7 @@ #endif // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +void OPL_SetCore(const char *args); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -76,8 +77,9 @@ CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); // //========================================================================== -OPLMIDIDevice::OPLMIDIDevice() +OPLMIDIDevice::OPLMIDIDevice(const char *args) { + OPL_SetCore(args); FullPan = opl_fullpan; FWadLump data = Wads.OpenLumpName("GENMIDI"); OPLloadBank(data); diff --git a/src/oplsynth/music_opldumper_mididevice.cpp b/src/oplsynth/music_opldumper_mididevice.cpp index 46dcdfef2..e6684e1a6 100644 --- a/src/oplsynth/music_opldumper_mididevice.cpp +++ b/src/oplsynth/music_opldumper_mididevice.cpp @@ -262,6 +262,7 @@ protected: //========================================================================== OPLDumperMIDIDevice::OPLDumperMIDIDevice(const char *filename) + : OPLMIDIDevice(NULL) { // Replace the standard OPL device with a disk writer. delete io; diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index cb0f26ae6..8170c1a30 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1364,16 +1364,36 @@ static void S_AddSNDINFO (int lump) case SI_MidiDevice: { sc.MustGetString(); FName nm = sc.String; + FScanner::SavedPos save = sc.SavePos(); + + sc.SetCMode(true); sc.MustGetString(); - if (sc.Compare("timidity")) MidiDevices[nm] = MDEV_TIMIDITY; - else if (sc.Compare("fmod") || sc.Compare("sndsys")) MidiDevices[nm] = MDEV_SNDSYS; - else if (sc.Compare("standard")) MidiDevices[nm] = MDEV_MMAPI; - else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL; - else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; - else if (sc.Compare("fluidsynth")) MidiDevices[nm] = MDEV_FLUIDSYNTH; - else if (sc.Compare("gus")) MidiDevices[nm] = MDEV_GUS; - else if (sc.Compare("wildmidi")) MidiDevices[nm] = MDEV_WILDMIDI; + MidiDeviceSetting devset; + if (sc.Compare("timidity")) devset.device = MDEV_TIMIDITY; + else if (sc.Compare("fmod") || sc.Compare("sndsys")) devset.device = MDEV_SNDSYS; + else if (sc.Compare("standard")) devset.device = MDEV_MMAPI; + else if (sc.Compare("opl")) devset.device = MDEV_OPL; + else if (sc.Compare("default")) devset.device = MDEV_DEFAULT; + else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH; + else if (sc.Compare("gus")) devset.device = MDEV_GUS; + else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); + + if (sc.CheckString(",")) + { + sc.SetCMode(false); + sc.MustGetString(); + devset.args = sc.String; + } + else + { + // This does not really do what one might expect, because the next token has already been parsed and can be a '$'. + // So in order to continue parsing without C-Mode, we need to reset and parse the last token again. + sc.SetCMode(false); + sc.RestorePos(save); + sc.MustGetString(); + } + MidiDevices[nm] = devset; } break; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 7b38e816c..f388246a9 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2421,11 +2421,8 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) { int lumpnum = -1; int length = 0; - int device = MDEV_DEFAULT; MusInfo *handle = NULL; - - int *devp = MidiDevices.CheckKey(musicname); - if (devp != NULL) device = *devp; + MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname); // Strip off any leading file:// component. if (strncmp(musicname, "file://", 7) == 0) @@ -2495,7 +2492,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } else { - mus_playing.handle = I_RegisterSong (reader, device); + mus_playing.handle = I_RegisterSong (reader, devp); } } diff --git a/src/s_sound.h b/src/s_sound.h index 16aa8b333..a72f3be0c 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -397,8 +397,19 @@ enum EMidiDevice MDEV_WILDMIDI = 6, }; +struct MidiDeviceSetting +{ + int device; + FString args; + + MidiDeviceSetting() + { + device = MDEV_DEFAULT; + } +}; + typedef TMap MusicAliasMap; -typedef TMap MidiDeviceMap; +typedef TMap MidiDeviceMap; extern MusicAliasMap MusicAliases; extern MidiDeviceMap MidiDevices; diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index a46401f3e..5723a18dc 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -311,21 +311,21 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) // //========================================================================== -static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype) +static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype, const char *args) { switch (miditype) { case MIDI_MUS: - return new MUSSong2(reader, devtype); + return new MUSSong2(reader, devtype, args); case MIDI_MIDI: - return new MIDISong2(reader, devtype); + return new MIDISong2(reader, devtype, args); case MIDI_HMI: - return new HMISong(reader, devtype); + return new HMISong(reader, devtype, args); case MIDI_XMI: - return new XMISong(reader, devtype); + return new XMISong(reader, devtype, args); default: return NULL; @@ -387,7 +387,7 @@ static EMIDIType IdentifyMIDIType(DWORD *id, int size) // //========================================================================== -MusInfo *I_RegisterSong (FileReader *reader, int device) +MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device) { MusInfo *info = NULL; const char *fmt; @@ -405,12 +405,6 @@ MusInfo *I_RegisterSong (FileReader *reader, int device) return 0; } -#ifndef _WIN32 - // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS - if (device == MDEV_MMAPI) - device = MDEV_SNDSYS; -#endif - // Check for gzip compression. Some formats are expected to have players // that can handle it, so it simplifies things if we make all songs // gzippable. @@ -447,10 +441,15 @@ MusInfo *I_RegisterSong (FileReader *reader, int device) EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); if (miditype != MIDI_NOTMIDI) { - EMidiDevice devtype = (EMidiDevice)device; + EMidiDevice devtype = device == NULL? MDEV_DEFAULT : (EMidiDevice)device->device; +#ifndef _WIN32 + // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS + if (devtype == MDEV_MMAPI) + devtype = MDEV_SNDSYS; +#endif retry_as_sndsys: - info = CreateMIDIStreamer(*reader, devtype, miditype); + info = CreateMIDIStreamer(*reader, devtype, miditype, device != NULL? device->args.GetChars() : ""); if (info != NULL && !info->IsValid()) { delete info; @@ -464,7 +463,7 @@ retry_as_sndsys: #ifdef _WIN32 if (info == NULL && devtype != MDEV_MMAPI && snd_mididevice >= 0) { - info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype); + info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype, ""); } #endif } @@ -475,7 +474,7 @@ retry_as_sndsys: (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL (id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF { - info = new OPLMUSSong (*reader); + info = new OPLMUSSong (*reader, device != NULL? device->args.GetChars() : ""); } // Check for game music else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0') diff --git a/src/sound/i_music.h b/src/sound/i_music.h index 03e0a3212..514400e5d 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -53,7 +53,8 @@ void I_SetMusicVolume (float volume); // Registers a song handle to song data. class MusInfo; -MusInfo *I_RegisterSong (FileReader *reader, int device); +struct MidiDeviceSetting; +MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device); MusInfo *I_RegisterCDSong (int track, int cdid = 0); MusInfo *I_RegisterURLSong (const char *url); diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 275253d84..52364ab58 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -185,7 +185,7 @@ public: class TimidityPPMIDIDevice : public PseudoMIDIDevice { public: - TimidityPPMIDIDevice(); + TimidityPPMIDIDevice(const char *args); ~TimidityPPMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -270,7 +270,7 @@ protected: class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock { public: - OPLMIDIDevice(); + OPLMIDIDevice(const char *args); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); void Close(); int GetTechnology() const; @@ -303,7 +303,7 @@ namespace Timidity { struct Renderer; } class TimidityMIDIDevice : public SoftSynthMIDIDevice { public: - TimidityMIDIDevice(); + TimidityMIDIDevice(const char *args); ~TimidityMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -337,7 +337,7 @@ protected: class WildMIDIDevice : public SoftSynthMIDIDevice { public: - WildMIDIDevice(); + WildMIDIDevice(const char *args); ~WildMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -366,7 +366,7 @@ struct fluid_synth_t; class FluidSynthMIDIDevice : public SoftSynthMIDIDevice { public: - FluidSynthMIDIDevice(); + FluidSynthMIDIDevice(const char *args); ~FluidSynthMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -431,7 +431,7 @@ protected: class MIDIStreamer : public MusInfo { public: - MIDIStreamer(EMidiDevice type); + MIDIStreamer(EMidiDevice type, const char *args); ~MIDIStreamer(); void MusicVolumeChanged(); @@ -517,6 +517,7 @@ protected: bool CallbackIsThreaded; int LoopLimit; FString DumpFilename; + FString Args; }; // MUS file played with a MIDI stream --------------------------------------- @@ -524,7 +525,7 @@ protected: class MUSSong2 : public MIDIStreamer { public: - MUSSong2(FileReader &reader, EMidiDevice type); + MUSSong2(FileReader &reader, EMidiDevice type, const char *args); ~MUSSong2(); MusInfo *GetOPLDumper(const char *filename); @@ -550,7 +551,7 @@ protected: class MIDISong2 : public MIDIStreamer { public: - MIDISong2(FileReader &reader, EMidiDevice type); + MIDISong2(FileReader &reader, EMidiDevice type, const char *args); ~MIDISong2(); MusInfo *GetOPLDumper(const char *filename); @@ -607,7 +608,7 @@ protected: class HMISong : public MIDIStreamer { public: - HMISong(FileReader &reader, EMidiDevice type); + HMISong(FileReader &reader, EMidiDevice type, const char *args); ~HMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -650,7 +651,7 @@ protected: class XMISong : public MIDIStreamer { public: - XMISong(FileReader &reader, EMidiDevice type); + XMISong(FileReader &reader, EMidiDevice type, const char *args); ~XMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -713,7 +714,7 @@ protected: class OPLMUSSong : public StreamSong { public: - OPLMUSSong (FileReader &reader); + OPLMUSSong (FileReader &reader, const char *args); ~OPLMUSSong (); void Play (bool looping, int subsong); bool IsPlaying (); diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/music_fluidsynth_mididevice.cpp index af8fe6667..3be4de56b 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/music_fluidsynth_mididevice.cpp @@ -255,7 +255,7 @@ CUSTOM_CVAR(Int, fluid_chorus_type, FLUID_CHORUS_DEFAULT_TYPE, CVAR_ARCHIVE|CVAR // //========================================================================== -FluidSynthMIDIDevice::FluidSynthMIDIDevice() +FluidSynthMIDIDevice::FluidSynthMIDIDevice(const char *args) { FluidSynth = NULL; FluidSettings = NULL; @@ -293,7 +293,15 @@ FluidSynthMIDIDevice::FluidSynthMIDIDevice() fluid_reverb_width, fluid_reverb_level); fluid_synth_set_chorus(FluidSynth, fluid_chorus_voices, fluid_chorus_level, fluid_chorus_speed, fluid_chorus_depth, fluid_chorus_type); - if (0 == LoadPatchSets(fluid_patchset)) + + // try loading a patch set that got specified with $mididevice. + int res = 0; + if (args != NULL && *args != 0) + { + res = LoadPatchSets(args); + } + + if (res == 0 && 0 == LoadPatchSets(fluid_patchset)) { #ifdef __unix__ // This is the standard location on Ubuntu. diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index 5fef706dd..ca3ae0905 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -128,8 +128,8 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -HMISong::HMISong (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Tracks(0) +HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Tracks(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index dbc56cc56..9f50b4155 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -72,7 +72,7 @@ CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //========================================================================== -TimidityPPMIDIDevice::TimidityPPMIDIDevice() +TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args) : DiskName("zmid"), #ifdef _WIN32 ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE), @@ -85,7 +85,13 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice() #ifndef _WIN32 WavePipe[0] = WavePipe[1] = -1; #endif - + + if (args == NULL || *args == 0) args = timidity_exe; + + CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", + args, *timidity_extargs, + *timidity_chorus, *timidity_reverb, *timidity_frequency); + if (DiskName == NULL) { Printf(PRINT_BOLD, "Could not create temp music file\n"); @@ -187,10 +193,6 @@ int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWO Validated = true; #endif // WIN32 - CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", - *timidity_exe, *timidity_extargs, - *timidity_chorus, *timidity_reverb, *timidity_frequency); - pipeSize = (timidity_pipe * timidity_frequency / 1000) << (timidity_stereo + !timidity_8bit); diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index a6fb5f4d8..579fd7851 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -89,12 +89,12 @@ static const BYTE StaticMIDIhead[] = // //========================================================================== -MIDIStreamer::MIDIStreamer(EMidiDevice type) +MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) : #ifdef _WIN32 PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), #endif - MIDI(0), Division(0), InitialTempo(500000), DeviceType(type) + MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), Args(args) { #ifdef _WIN32 BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -269,19 +269,19 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const #ifdef HAVE_FLUIDSYNTH case MDEV_FLUIDSYNTH: - return new FluidSynthMIDIDevice; + return new FluidSynthMIDIDevice(Args); #endif case MDEV_SNDSYS: return new SndSysMIDIDevice; case MDEV_GUS: - return new TimidityMIDIDevice; + return new TimidityMIDIDevice(Args); case MDEV_OPL: try { - return new OPLMIDIDevice; + return new OPLMIDIDevice(Args); } catch (CRecoverableError &err) { @@ -291,10 +291,10 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const } case MDEV_TIMIDITY: - return new TimidityPPMIDIDevice; + return new TimidityPPMIDIDevice(Args); case MDEV_WILDMIDI: - return new WildMIDIDevice; + return new WildMIDIDevice(Args); default: return NULL; diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index eaf7e4afb..fd1601ee4 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -93,8 +93,8 @@ static const BYTE CtrlTranslate[15] = // //========================================================================== -MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), MusBuffer(0) +MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), MusBuffer(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/sound/music_mus_opl.cpp b/src/sound/music_mus_opl.cpp index 3b5012ad9..562330ef9 100644 --- a/src/sound/music_mus_opl.cpp +++ b/src/sound/music_mus_opl.cpp @@ -20,16 +20,25 @@ CUSTOM_CVAR (Int, opl_numchips, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Int, opl_core, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Int, opl_core, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +int current_opl_core; -OPLMUSSong::OPLMUSSong (FileReader &reader) +// Get OPL core override from $mididevice +void OPL_SetCore(const char *args) +{ + current_opl_core = opl_core; + if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; +} + +OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args) { int samples = int(OPL_SAMPLE_RATE / 14); + OPL_SetCore(args); Music = new OPLmusicFile (&reader); m_Stream = GSnd->CreateStream (FillStream, samples*4, - (opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); + (current_opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); if (m_Stream == NULL) { Printf (PRINT_BOLD, "Could not create music stream.\n"); diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index 49fd12502..43755eb08 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -102,8 +102,8 @@ char MIDI_CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // //========================================================================== -MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Tracks(0) +MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Tracks(0) { int p; int i; diff --git a/src/sound/music_timidity_mididevice.cpp b/src/sound/music_timidity_mididevice.cpp index 9e5be625b..a63e1d723 100644 --- a/src/sound/music_timidity_mididevice.cpp +++ b/src/sound/music_timidity_mididevice.cpp @@ -86,10 +86,10 @@ struct FmtChunk // //========================================================================== -TimidityMIDIDevice::TimidityMIDIDevice() +TimidityMIDIDevice::TimidityMIDIDevice(const char *args) { Renderer = NULL; - Renderer = new Timidity::Renderer((float)SampleRate); + Renderer = new Timidity::Renderer((float)SampleRate, args); } //========================================================================== @@ -245,6 +245,7 @@ FString TimidityMIDIDevice::GetStats() //========================================================================== TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, int rate) + :TimidityMIDIDevice(NULL) { File = fopen(filename, "wb"); if (File != NULL) diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp index c3fd674c9..88550226a 100644 --- a/src/sound/music_wildmidi_mididevice.cpp +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -81,7 +81,7 @@ CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBAL // //========================================================================== -WildMIDIDevice::WildMIDIDevice() +WildMIDIDevice::WildMIDIDevice(const char *args) { Renderer = NULL; @@ -94,16 +94,18 @@ WildMIDIDevice::WildMIDIDevice() SampleRate = clamp(SampleRate, 11025, 65535); } - if (CurrentConfig.CompareNoCase(wildmidi_config) != 0 || SampleRate != WildMidi_GetSampleRate()) + if (args == NULL || *args == 0) args = wildmidi_config; + + if (CurrentConfig.CompareNoCase(args) != 0 || SampleRate != WildMidi_GetSampleRate()) { if (CurrentConfig.IsNotEmpty()) { WildMidi_Shutdown(); CurrentConfig = ""; } - if (!WildMidi_Init(wildmidi_config, SampleRate, 0)) + if (!WildMidi_Init(args, SampleRate, 0)) { - CurrentConfig = wildmidi_config; + CurrentConfig = args; } } if (CurrentConfig.IsNotEmpty()) diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index 6c179a16a..71246f518 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -108,8 +108,8 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -XMISong::XMISong (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Songs(0) +XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Songs(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 09b5ae7b5..807bc07b7 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -678,8 +678,9 @@ int LoadDMXGUS() return 0; } -Renderer::Renderer(float sample_rate) +Renderer::Renderer(float sample_rate, const char *args) { + // 'args' should be used to load a custom config or DMXGUS, but since setup currently requires a snd_reset call, this will need some refactoring first rate = sample_rate; patches = NULL; resample_buffer_size = 0; diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 59ba5f8ad..45e23a9f9 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -630,7 +630,7 @@ struct Renderer int voices; int lost_notes, cut_notes; - Renderer(float sample_rate); + Renderer(float sample_rate, const char *args); ~Renderer(); void HandleEvent(int status, int parm1, int parm2); From eed6680a6722994c2936cc4c5730e0e4e43dc637 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 13 Dec 2015 17:01:33 -0600 Subject: [PATCH 07/13] Added support for weapon states User#. - Added keybinds for the user state triggering. - Added WRF_USER# flags which must be specified in order to use. - # can be 1-4. --- src/d_player.h | 4 ++ src/g_shared/a_pickups.h | 1 + src/g_shared/a_weapons.cpp | 24 +++++++ src/namedef.h | 4 ++ src/p_pspr.cpp | 108 ++++++++++++++++++++++++++--- wadsrc/static/actors/constants.txt | 5 ++ wadsrc/static/menudef.txt | 8 ++- 7 files changed, 143 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e27bf1087..1e7eef61e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -221,6 +221,10 @@ enum WF_WEAPONRELOADOK = 1 << 5, // [XA] Okay to reload this weapon. WF_WEAPONZOOMOK = 1 << 6, // [XA] Okay to use weapon zoom function. WF_REFIRESWITCHOK = 1 << 7, // Mirror WF_WEAPONSWITCHOK for A_ReFire + WF_USER1OK = 1 << 8, // [MC] Allow pushing of custom state buttons 1-4 + WF_USER2OK = 1 << 9, + WF_USER3OK = 1 << 10, + WF_USER4OK = 1 << 11, }; #define WPIECE1 1 diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 74b10206b..80e1744e6 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -307,6 +307,7 @@ public: virtual FState *GetAltAtkState (bool hold); virtual FState *GetRelState (); virtual FState *GetZoomState (); + virtual FState *GetUserState(int state); virtual void PostMorphWeapon (); virtual void EndPowerup (); diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index fae232557..79546737e 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -703,6 +703,30 @@ FState *AWeapon::GetZoomState () return FindState(NAME_Zoom); } +//=========================================================================== +// +// AWeapon :: GetUserState +// +//=========================================================================== + +FState *AWeapon::GetUserState(int state) +{ + switch (state) + { + case 4: + return FindState(NAME_User4); + case 3: + return FindState(NAME_User3); + case 2: + return FindState(NAME_User2); + case 1: + return FindState(NAME_User1); + default: + return NULL; + } +} + + /* Weapon giver ***********************************************************/ IMPLEMENT_CLASS(AWeaponGiver) diff --git a/src/namedef.h b/src/namedef.h index 22dbe1b51..f9d436f28 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -221,6 +221,10 @@ xx(Flash) xx(AltFlash) xx(Reload) xx(Zoom) +xx(User1) +xx(User2) +xx(User3) +xx(User4) // State names used by ASwitchableDecoration xx(Active) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 63f3bc648..df05b9611 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -95,7 +95,8 @@ void P_SetPsprite (player_t *player, int position, FState *state, bool nofunctio if (position == ps_weapon && !nofunction) { // A_WeaponReady will re-set these as needed - player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK); + player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK | + WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK); } psp = &player->psprites[position]; @@ -349,6 +350,34 @@ void P_ZoomWeapon (player_t *player, FState *state) P_SetPsprite (player, ps_weapon, state); } +//-------------------------------------------------------------------------- - +// +// PROC P_UserStateWeapon +// +//--------------------------------------------------------------------------- + +void P_UserStateWeapon(player_t *player, FState *state, int userstate) +{ + if (!userstate) + return; + + AWeapon *weapon; + if (player->Bot == NULL && bot_observer) + return; + + weapon = player->ReadyWeapon; + if (weapon == NULL) + return; + + if (state == NULL) + { + state = weapon->GetUserState(userstate); + } + if (state != NULL) + P_SetPsprite(player, ps_weapon, state); +} + + //--------------------------------------------------------------------------- // // PROC P_DropWeapon @@ -573,13 +602,24 @@ void DoReadyWeaponToReload (AActor *self) void DoReadyWeaponToZoom (AActor *self) { - // Prepare for reload action. + // Prepare for zoom action. player_t *player; if (self && (player = self->player)) player->WeaponState |= WF_WEAPONZOOMOK; return; } +void DoReadyWeaponToUser(AActor *self, int userStates) +{ + // Prepare for user state action. + player_t *player; + if (self && (player = self->player) && userStates) + { + player->WeaponState |= userStates; + } + return; +} + // This function replaces calls to A_WeaponReady in other codepointers. void DoReadyWeapon(AActor *self) { @@ -588,18 +628,23 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToSwitch(self); DoReadyWeaponToReload(self); DoReadyWeaponToZoom(self); + DoReadyWeaponToUser(self, (WF_USER1OK + WF_USER2OK + WF_USER3OK + WF_USER4OK)); } enum EWRF_Options { - WRF_NoBob = 1, - WRF_NoSwitch = 2, - WRF_NoPrimary = 4, - WRF_NoSecondary = 8, + WRF_NoBob = 1, + WRF_NoSwitch = 1 << 1, + WRF_NoPrimary = 1 << 2, + WRF_NoSecondary = 1 << 3, WRF_NoFire = WRF_NoPrimary + WRF_NoSecondary, - WRF_AllowReload = 16, - WRF_AllowZoom = 32, - WRF_DisableSwitch = 64, + WRF_AllowReload = 1 << 4, + WRF_AllowZoom = 1 << 5, + WRF_DisableSwitch = 1 << 6, + WRF_User1 = 1 << 7, + WRF_User2 = 1 << 8, + WRF_User3 = 1 << 9, + WRF_User4 = 1 << 10, }; DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) @@ -613,6 +658,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) if ((paramflags & WRF_AllowReload)) DoReadyWeaponToReload(self); if ((paramflags & WRF_AllowZoom)) DoReadyWeaponToZoom(self); + int userStates = 0; + if (paramflags & WRF_User1) userStates |= WF_USER1OK; + if (paramflags & WRF_User2) userStates |= WF_USER2OK; + if (paramflags & WRF_User3) userStates |= WF_USER3OK; + if (paramflags & WRF_User4) userStates |= WF_USER4OK; + if (userStates) DoReadyWeaponToUser(self, userStates); + DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); } @@ -732,6 +784,40 @@ void P_CheckWeaponZoom (player_t *player) } } +//--------------------------------------------------------------------------- +// +// PROC P_CheckWeaponUserState +// +// The player can use the weapon's user state functionalities. +// +//--------------------------------------------------------------------------- + +void P_CheckWeaponUserState(player_t *player) +{ + AWeapon *weapon = player->ReadyWeapon; + + if (weapon == NULL) + return; + + // Check for user state(s). + if ((player->WeaponState & WF_USER1OK) && (player->cmd.ucmd.buttons & BT_USER1)) + { + P_UserStateWeapon(player, NULL, 1); + } + else if ((player->WeaponState & WF_USER2OK) && (player->cmd.ucmd.buttons & BT_USER2)) + { + P_UserStateWeapon(player, NULL, 2); + } + else if ((player->WeaponState & WF_USER3OK) && (player->cmd.ucmd.buttons & BT_USER3)) + { + P_UserStateWeapon(player, NULL, 3); + } + else if ((player->WeaponState & WF_USER4OK) && (player->cmd.ucmd.buttons & BT_USER4)) + { + P_UserStateWeapon(player, NULL, 4); + } +} + //--------------------------------------------------------------------------- // // PROC A_ReFire @@ -1103,6 +1189,10 @@ void P_MovePsprites (player_t *player) { P_CheckWeaponZoom (player); } + if (player->WeaponState & (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)) + { + P_CheckWeaponUserState(player); + } } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 956ed119f..32a45e54c 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -129,6 +129,11 @@ const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; const int WRF_ALLOWRELOAD = 16; const int WRF_ALLOWZOOM = 32; const int WRF_DISABLESWITCH = 64; +const int WRF_USER1 = 128; +const int WRF_USER2 = 256; +const int WRF_USER3 = 512; +const int WRF_USER4 = 1024; +const int WRF_ALLUSER = WRF_USER1 | WRF_USER2 | WRF_USER3 | WRF_USER4; // Morph constants const int MRF_ADDSTAMINA = 1; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 244b14691..087c067d1 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -435,8 +435,12 @@ OptionMenu "CustomizeControls" StaticText "Controls", 1 Control "Fire", "+attack" Control "Secondary Fire", "+altattack" - Control "Weapon Reload", "+reload" - Control "Weapon Zoom", "+zoom" + Control "Weapon Reload", "+reload" + Control "Weapon Zoom", "+zoom" + Control "Weapon State 1", "+user1" + Control "Weapon State 2", "+user2" + Control "Weapon State 3", "+user3" + Control "Weapon State 4", "+user4" Control "Use / Open", "+use" Control "Move forward", "+forward" Control "Move backward", "+back" From b09a81126fc5fb2979f7ef11c5f8bb93b3bfdb24 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Mon, 14 Dec 2015 23:50:58 -0600 Subject: [PATCH 08/13] - Changed WeaponState from 8-bit to 16-bit integer. - Because the flags for WF_USER#OK are 256 on up, this is required in order to work. --- src/d_player.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 1e7eef61e..4e29da8f0 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -409,7 +409,7 @@ public: int lastkilltime; // [RH] For multikills BYTE multicount; BYTE spreecount; // [RH] Keep track of killing sprees - BYTE WeaponState; + WORD WeaponState; AWeapon *ReadyWeapon; AWeapon *PendingWeapon; // WP_NOCHANGE if not changing From 6478b98eeaa06df4bcac4968f36221f18470c3df Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 15 Dec 2015 07:19:41 -0600 Subject: [PATCH 09/13] Update serialization to BYTE from WORD for older save games. --- src/p_user.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/p_user.cpp b/src/p_user.cpp index 331a978b7..15c5055c9 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3043,6 +3043,12 @@ void player_t::Serialize (FArchive &arc) WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1)); cheats &= ~((1 << 14) | (0x37 << 24)); } + if (SaveVersion < 4526) + { + BYTE oldWeaponState; + arc << oldWeaponState; + WeaponState = oldWeaponState; + } else { arc << WeaponState; From 3566d3157a6bb4f56f149a83fb475acb51adea2d Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 15 Dec 2015 09:04:52 -0600 Subject: [PATCH 10/13] Use | instead of +. --- src/p_pspr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index df05b9611..9f3a24bad 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -628,7 +628,7 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToSwitch(self); DoReadyWeaponToReload(self); DoReadyWeaponToZoom(self); - DoReadyWeaponToUser(self, (WF_USER1OK + WF_USER2OK + WF_USER3OK + WF_USER4OK)); + DoReadyWeaponToUser(self, (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)); } enum EWRF_Options From 4931c90839577c101593c3bdd62b9c76703b3875 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 15:44:52 -0600 Subject: [PATCH 11/13] Bump save version for bigger WeaponState property --- src/p_user.cpp | 2 +- src/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 15c5055c9..1eb838176 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3043,7 +3043,7 @@ void player_t::Serialize (FArchive &arc) WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1)); cheats &= ~((1 << 14) | (0x37 << 24)); } - if (SaveVersion < 4526) + if (SaveVersion < 4527) { BYTE oldWeaponState; arc << oldWeaponState; diff --git a/src/version.h b/src/version.h index c1288f02f..682c5165e 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4526 +#define SAVEVER 4527 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From afbf88cc639a3c29d98ed458fa7a1f48486f41bf Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 16:03:38 -0600 Subject: [PATCH 12/13] Remove WRF_ALLUSER. --- wadsrc/static/actors/constants.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 32a45e54c..1e4db4092 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -133,7 +133,6 @@ const int WRF_USER1 = 128; const int WRF_USER2 = 256; const int WRF_USER3 = 512; const int WRF_USER4 = 1024; -const int WRF_ALLUSER = WRF_USER1 | WRF_USER2 | WRF_USER3 | WRF_USER4; // Morph constants const int MRF_ADDSTAMINA = 1; From 1d759283c07e99ec8784ba01e6e286fbe03703fd Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 31 Dec 2015 16:41:09 -0600 Subject: [PATCH 13/13] Cleanup the zoom/reload/userX handling for A_WeaponReady - There was lots of code duplication. Consolidated it. - Renamed WRF_UserX to WRF_AllowUserX for consistancy. --- src/d_player.h | 2 +- src/g_shared/a_pickups.h | 4 +- src/g_shared/a_weapons.cpp | 40 +--- src/p_pspr.cpp | 287 ++++++++--------------------- wadsrc/static/actors/constants.txt | 8 +- 5 files changed, 82 insertions(+), 259 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 4e29da8f0..11611cded 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -225,7 +225,7 @@ enum WF_USER2OK = 1 << 9, WF_USER3OK = 1 << 10, WF_USER4OK = 1 << 11, -}; +}; #define WPIECE1 1 #define WPIECE2 2 diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 80e1744e6..413b01db3 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -305,9 +305,7 @@ public: virtual FState *GetReadyState (); virtual FState *GetAtkState (bool hold); virtual FState *GetAltAtkState (bool hold); - virtual FState *GetRelState (); - virtual FState *GetZoomState (); - virtual FState *GetUserState(int state); + virtual FState *GetStateForButtonName (FName button); virtual void PostMorphWeapon (); virtual void EndPowerup (); diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 79546737e..9c71ab0ea 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -683,47 +683,13 @@ FState *AWeapon::GetAltAtkState (bool hold) //=========================================================================== // -// AWeapon :: GetRelState +// AWeapon :: GetStateForButtonName // //=========================================================================== -FState *AWeapon::GetRelState () +FState *AWeapon::GetStateForButtonName (FName button) { - return FindState(NAME_Reload); -} - -//=========================================================================== -// -// AWeapon :: GetZoomState -// -//=========================================================================== - -FState *AWeapon::GetZoomState () -{ - return FindState(NAME_Zoom); -} - -//=========================================================================== -// -// AWeapon :: GetUserState -// -//=========================================================================== - -FState *AWeapon::GetUserState(int state) -{ - switch (state) - { - case 4: - return FindState(NAME_User4); - case 3: - return FindState(NAME_User3); - case 2: - return FindState(NAME_User2); - case 1: - return FindState(NAME_User1); - default: - return NULL; - } + return FindState(button); } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 9f3a24bad..ed7d675d3 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -38,6 +38,30 @@ // TYPES ------------------------------------------------------------------- +struct FGenericButtons +{ + int ReadyFlag; // Flag passed to A_WeaponReady + int StateFlag; // Flag set in WeaponState + int ButtonFlag; // Button to press + ENamedName StateName; // Name of the button/state +}; + +enum EWRF_Options +{ + WRF_NoBob = 1, + WRF_NoSwitch = 1 << 1, + WRF_NoPrimary = 1 << 2, + WRF_NoSecondary = 1 << 3, + WRF_NoFire = WRF_NoPrimary | WRF_NoSecondary, + WRF_AllowReload = 1 << 4, + WRF_AllowZoom = 1 << 5, + WRF_DisableSwitch = 1 << 6, + WRF_AllowUser1 = 1 << 7, + WRF_AllowUser2 = 1 << 8, + WRF_AllowUser3 = 1 << 9, + WRF_AllowUser4 = 1 << 10, +}; + // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -57,6 +81,16 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO); static FRandom pr_wpnreadysnd ("WpnReadySnd"); static FRandom pr_gunshot ("GunShot"); +static const FGenericButtons ButtonChecks[] = +{ + { WRF_AllowReload, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom }, + { WRF_AllowZoom, WF_WEAPONRELOADOK, BT_RELOAD, NAME_Reload }, + { WRF_AllowUser1, WF_USER1OK, BT_USER1, NAME_User1 }, + { WRF_AllowUser2, WF_USER2OK, BT_USER2, NAME_User2 }, + { WRF_AllowUser3, WF_USER3OK, BT_USER3, NAME_User3 }, + { WRF_AllowUser4, WF_USER4OK, BT_USER4, NAME_User4 }, +}; + // CODE -------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -290,94 +324,6 @@ void P_FireWeaponAlt (player_t *player, FState *state) } } -//--------------------------------------------------------------------------- -// -// PROC P_ReloadWeapon -// -//--------------------------------------------------------------------------- - -void P_ReloadWeapon (player_t *player, FState *state) -{ - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - { - return; - } - - weapon = player->ReadyWeapon; - if (weapon == NULL) - { - return; - } - - if (state == NULL) - { - state = weapon->GetRelState(); - } - // [XA] don't change state if still null, so if the modder sets - // WRF_RELOAD to true but forgets to define the Reload state, the weapon - // won't disappear. ;) - if (state != NULL) - P_SetPsprite (player, ps_weapon, state); -} - -//--------------------------------------------------------------------------- -// -// PROC P_ZoomWeapon -// -//--------------------------------------------------------------------------- - -void P_ZoomWeapon (player_t *player, FState *state) -{ - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - { - return; - } - - weapon = player->ReadyWeapon; - if (weapon == NULL) - { - return; - } - - if (state == NULL) - { - state = weapon->GetZoomState(); - } - // [XA] don't change state if still null. Same reasons as above. - if (state != NULL) - P_SetPsprite (player, ps_weapon, state); -} - -//-------------------------------------------------------------------------- - -// -// PROC P_UserStateWeapon -// -//--------------------------------------------------------------------------- - -void P_UserStateWeapon(player_t *player, FState *state, int userstate) -{ - if (!userstate) - return; - - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - return; - - weapon = player->ReadyWeapon; - if (weapon == NULL) - return; - - if (state == NULL) - { - state = weapon->GetUserState(userstate); - } - if (state != NULL) - P_SetPsprite(player, ps_weapon, state); -} - - //--------------------------------------------------------------------------- // // PROC P_DropWeapon @@ -591,33 +537,21 @@ void DoReadyWeaponToBob (AActor *self) } } -void DoReadyWeaponToReload (AActor *self) +void DoReadyWeaponToGeneric(AActor *self, int paramflags) { - // Prepare for reload action. - player_t *player; - if (self && (player = self->player)) - player->WeaponState |= WF_WEAPONRELOADOK; - return; -} + int flags = 0; -void DoReadyWeaponToZoom (AActor *self) -{ - // Prepare for zoom action. - player_t *player; - if (self && (player = self->player)) - player->WeaponState |= WF_WEAPONZOOMOK; - return; -} - -void DoReadyWeaponToUser(AActor *self, int userStates) -{ - // Prepare for user state action. - player_t *player; - if (self && (player = self->player) && userStates) + for (size_t i = 0; i < countof(ButtonChecks); ++i) { - player->WeaponState |= userStates; + if (paramflags & ButtonChecks[i].ReadyFlag) + { + flags |= ButtonChecks[i].StateFlag; + } + } + if (self != NULL && self->player != NULL) + { + self->player->WeaponState |= flags; } - return; } // This function replaces calls to A_WeaponReady in other codepointers. @@ -626,27 +560,9 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToBob(self); DoReadyWeaponToFire(self); DoReadyWeaponToSwitch(self); - DoReadyWeaponToReload(self); - DoReadyWeaponToZoom(self); - DoReadyWeaponToUser(self, (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)); + DoReadyWeaponToGeneric(self, ~0); } -enum EWRF_Options -{ - WRF_NoBob = 1, - WRF_NoSwitch = 1 << 1, - WRF_NoPrimary = 1 << 2, - WRF_NoSecondary = 1 << 3, - WRF_NoFire = WRF_NoPrimary + WRF_NoSecondary, - WRF_AllowReload = 1 << 4, - WRF_AllowZoom = 1 << 5, - WRF_DisableSwitch = 1 << 6, - WRF_User1 = 1 << 7, - WRF_User2 = 1 << 8, - WRF_User3 = 1 << 9, - WRF_User4 = 1 << 10, -}; - DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) { ACTION_PARAM_START(1); @@ -655,17 +571,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) DoReadyWeaponToSwitch(self, !(paramflags & WRF_NoSwitch)); if ((paramflags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self, !(paramflags & WRF_NoPrimary), !(paramflags & WRF_NoSecondary)); if (!(paramflags & WRF_NoBob)) DoReadyWeaponToBob(self); - if ((paramflags & WRF_AllowReload)) DoReadyWeaponToReload(self); - if ((paramflags & WRF_AllowZoom)) DoReadyWeaponToZoom(self); - - int userStates = 0; - if (paramflags & WRF_User1) userStates |= WF_USER1OK; - if (paramflags & WRF_User2) userStates |= WF_USER2OK; - if (paramflags & WRF_User3) userStates |= WF_USER3OK; - if (paramflags & WRF_User4) userStates |= WF_USER4OK; - if (userStates) DoReadyWeaponToUser(self, userStates); - - DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); + DoReadyWeaponToGeneric(self, paramflags); + DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); } //--------------------------------------------------------------------------- @@ -742,79 +649,40 @@ void P_CheckWeaponSwitch (player_t *player) //--------------------------------------------------------------------------- // -// PROC P_CheckWeaponReload +// PROC P_CheckWeaponButtons // -// The player can reload the weapon. +// Check extra button presses for weapons. // //--------------------------------------------------------------------------- -void P_CheckWeaponReload (player_t *player) +static void P_CheckWeaponButtons (player_t *player) { - AWeapon *weapon = player->ReadyWeapon; - - if (weapon == NULL) + if (player->Bot == NULL && bot_observer) + { return; - - // Check for reload. - if ((player->WeaponState & WF_WEAPONRELOADOK) && (player->cmd.ucmd.buttons & BT_RELOAD)) - { - P_ReloadWeapon (player, NULL); } -} - -//--------------------------------------------------------------------------- -// -// PROC P_CheckWeaponZoom -// -// The player can use the weapon's zoom function. -// -//--------------------------------------------------------------------------- - -void P_CheckWeaponZoom (player_t *player) -{ AWeapon *weapon = player->ReadyWeapon; - if (weapon == NULL) + { return; - - // Check for zoom. - if ((player->WeaponState & WF_WEAPONZOOMOK) && (player->cmd.ucmd.buttons & BT_ZOOM)) - { - P_ZoomWeapon (player, NULL); } -} - -//--------------------------------------------------------------------------- -// -// PROC P_CheckWeaponUserState -// -// The player can use the weapon's user state functionalities. -// -//--------------------------------------------------------------------------- - -void P_CheckWeaponUserState(player_t *player) -{ - AWeapon *weapon = player->ReadyWeapon; - - if (weapon == NULL) - return; - - // Check for user state(s). - if ((player->WeaponState & WF_USER1OK) && (player->cmd.ucmd.buttons & BT_USER1)) + // The button checks are ordered by precedence. The first one to match a + // button press and affect a state change wins. + for (size_t i = 0; i < countof(ButtonChecks); ++i) { - P_UserStateWeapon(player, NULL, 1); - } - else if ((player->WeaponState & WF_USER2OK) && (player->cmd.ucmd.buttons & BT_USER2)) - { - P_UserStateWeapon(player, NULL, 2); - } - else if ((player->WeaponState & WF_USER3OK) && (player->cmd.ucmd.buttons & BT_USER3)) - { - P_UserStateWeapon(player, NULL, 3); - } - else if ((player->WeaponState & WF_USER4OK) && (player->cmd.ucmd.buttons & BT_USER4)) - { - P_UserStateWeapon(player, NULL, 4); + if ((player->WeaponState & ButtonChecks[i].StateFlag) && + (player->cmd.ucmd.buttons & ButtonChecks[i].ButtonFlag)) + { + FState *state = weapon->GetStateForButtonName(ButtonChecks[i].StateName); + // [XA] don't change state if still null, so if the modder + // sets WRF_xxx to true but forgets to define the corresponding + // state, the weapon won't disappear. ;) + if (state != NULL) + { + P_SetPsprite(player, ps_weapon, state); + return; + } + } } } @@ -1181,18 +1049,9 @@ void P_MovePsprites (player_t *player) { P_CheckWeaponFire (player); } - if (player->WeaponState & WF_WEAPONRELOADOK) - { - P_CheckWeaponReload (player); - } - if (player->WeaponState & WF_WEAPONZOOMOK) - { - P_CheckWeaponZoom (player); - } - if (player->WeaponState & (WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK)) - { - P_CheckWeaponUserState(player); - } + + // Check custom buttons + P_CheckWeaponButtons(player); } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1e4db4092..213e2109d 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -129,10 +129,10 @@ const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; const int WRF_ALLOWRELOAD = 16; const int WRF_ALLOWZOOM = 32; const int WRF_DISABLESWITCH = 64; -const int WRF_USER1 = 128; -const int WRF_USER2 = 256; -const int WRF_USER3 = 512; -const int WRF_USER4 = 1024; +const int WRF_ALLOWUSER1 = 128; +const int WRF_ALLOWUSER2 = 256; +const int WRF_ALLOWUSER3 = 512; +const int WRF_ALLOWUSER4 = 1024; // Morph constants const int MRF_ADDSTAMINA = 1;