- Update to ZDoom r858:

- Added FMOD_OPENONLY to the callback version of CreateStream() to prevent it
  from doing prebuffering of the song. This was causing the Linux version to
  hang while waiting for input from the pipe, since Timidity hadn't been
  started yet. I tried using a select call in the FillStream() method, but it
  always seems to return the pipe as having nothing available. Unfortunately,
  the game still falls all over itself if Timidity isn't available. Instead
  of execvp failing nicely, X errors kill the game. I don't know why it's
  doing that. My advice for Linux music: Skip Timidity++ and get a DLS patch
  set (/WINDOWS/system32/drivers/gm.dls is probably the most common by far)
  and set the snd_midipatchset cvar to point to it. It's faster and also
  sounds a whole lot better than the crappy freepats Ubuntu wants to install
  with Timidity++ (thank goodness I have the official patches from a real
  GUS so I don't need to use them).
- GCC fixes.
- Fixed: After starting new music the music volume has to be reset so that
  the song's relative volume takes effect.
- Removed the arbitrary 1024 bytes limit when the file being played is a MIDI
  file. I had a D_DM2TTL that's only 990 bytes.
- Restructured I_RegisterSong so that $mididevice works again and also supports
  selecting FMOD.
- Added Jim' Linux fix.
- Added MartinHowe's fix for mugshot display in status bars.
- The garbage collector is now run one last time just before exiting the game.
- Removed movie volume from the sound menu and renamed some of the other
  options to give the MIDI device name more room to display itself.
- Moved the midi device selection into the main sound menu.
- Added FMOD as MIDI device -1, to replace the MIDI mapper. This is still the
  default device. By default, it uses exactly the same DLS instruments as the
  Microsoft GS Wavetable Synth. If you have another set DLS level 1 patch set
  you want to use, set the snd_midipatchset cvar to specify where it should
  load the instruments from.
- Changed the ProduceMIDI function to store its output into a TArray<BYTE>.
  An overloaded version wraps around it to continue to supply file-writing
  support for external Timidity++ usage.
- Added an FMOD credits banner to comply with their non-commercial license.
- Reimplemented the snd_buffersize cvar for the FMOD Ex sound system. Rather
  than a time in ms, this is now the length in samples of the DSP buffer.
  Also added the snd_buffercount cvar to offer complete control over the
  call to FMOD::System::setDSPBufferSize(). Note that with any snd_samplerate
  below about 44kHz, you will need to set snd_buffersize to avoid long
  latencies.
- Reimplemented the snd_output cvar for the FMOD Ex sound system.
- Changed snd_samplerate default to 0. This now means to use the default
  sample rate.
- Made snd_output, snd_output_format, snd_speakermode, snd_resampler, and
  snd_hrtf available through the menu.
- Split the HRTF effect selection into its own cvar: snd_hrtf.
- Removed 96000 Hz option from the menu. It's still available through the
  cvar, if desired.
- Fixed: If Windows sound init failed, retry with DirectSound. (Apparently,
  WASAPI doesn't work with more than two speakers and PCM-Float output at the
  same time.)
- Fixed: Area sounds only played from the front speakers once you got within
  the 2D panning area.


git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@79 b0f79afe-0144-0410-b225-9a4edf0717df
This commit is contained in:
Christoph Oelckers 2008-03-27 18:31:46 +00:00
parent f0ef4540b6
commit a2b23a7e43
58 changed files with 1685 additions and 641 deletions

View file

@ -12,19 +12,20 @@ CFLAGS += `pkg-config gtk+-2.0 --cflags`
CFLAGS += -DHAVE_FILELENGTH -D__forceinline=inline `sdl-config --cflags` CFLAGS += -DHAVE_FILELENGTH -D__forceinline=inline `sdl-config --cflags`
CFLAGS += -DNEED_STRUPR CFLAGS += -DNEED_STRUPR
CFLAGS += -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp CFLAGS += -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp
LDFLAGS += -lFLAC++ -lFLAC -lz -lmodplug -lfmod `sdl-config --libs` LDFLAGS += -lFLAC++ -lFLAC -lz -lmodplug -lfmodex `sdl-config --libs`
LDFLAGS += -ljpeg `pkg-config gtk+-2.0 --libs` LDFLAGS += -ljpeg `pkg-config gtk+-2.0 --libs`
LDFLAGS += -lGL -lGLU LDFLAGS += -lGL -lGLU
NASMFLAGS += -f elf -DM_TARGET_LINUX NASMFLAGS += -f elf -DM_TARGET_LINUX
SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ fragglescript/ thingdef/ Linux/ sdl/ gl/ gl/r_render/ textures/) SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ fragglescript/ thingdef/ Linux/ sdl/ gl/ gl/r_render/ textures/ xlat/)
INCLUDES = $(addprefix -I,$(SRCDIRS)) INCLUDES = $(addprefix -I,$(SRCDIRS))
INCLUDES += -Isnes_spc/snes_spc/ -I/usr/include/fmodex/
CFLAGS += $(INCLUDES) CFLAGS += $(INCLUDES)
RELEASEOBJ ?= releaseobj RELEASEOBJ ?= releaseobj
DEBUGOBJ ?= debugobj DEBUGOBJ ?= debugobj
CPPSRCS = $(wildcard $(addsuffix *.cpp,$(SRCDIRS))) CPPSRCS = $(wildcard $(addsuffix *.cpp,$(SRCDIRS)))
CSRCS = $(wildcard $(addsuffix *.c,$(SRCDIRS))) CSRCS = $(filter-out src/xlat/xlat_parser.c, $(wildcard $(addsuffix *.c,$(SRCDIRS))))
ifndef NOASM ifndef NOASM
ASRCS = $(wildcard src/*.nas) ASRCS = $(wildcard src/*.nas)
CFLAGS += -DUSEASM=1 CFLAGS += -DUSEASM=1
@ -78,11 +79,11 @@ endef
all: $(ZDOOMBIN) toolsandpk3 zdoom.pk3 all: $(ZDOOMBIN) toolsandpk3 zdoom.pk3
$(ZDOOMBIN): ccdv $(OBJDIR) $(if $(RESTART),deps) $(OBJS) $(ZDOOMBIN): ccdv $(OBJDIR) $(if $(RESTART),deps) $(OBJS) snes_spc/libsnes_spc.a
ifndef RESTART ifndef RESTART
$(CCDV) $(CXX) $(LDFLAGS) $(OBJDIR)/autostart.o \ $(CCDV) $(CXX) $(LDFLAGS) $(OBJDIR)/autostart.o \
$(filter-out %/autostart.o %/autozend.o,$(OBJS)) \ $(filter-out %/autostart.o %/autozend.o,$(OBJS)) \
$(OBJDIR)/autozend.o -o $(ZDOOMBIN) snes_spc/libsnes_spc.a $(OBJDIR)/autozend.o -o $(ZDOOMBIN)
endif endif
# include any of the dep files that already exist # include any of the dep files that already exist
@ -119,23 +120,36 @@ ifdef RESTART
@make -f $(firstword $(MAKEFILE_LIST)) RESTART= @make -f $(firstword $(MAKEFILE_LIST)) RESTART=
endif endif
# This file needs special handling because GCC misoptimizes it otherwise.
$(OBJDIR)/fmopl.o: src/oplsynth/fmopl.cpp
$(CCDV) $(CXX) $(CXXFLAGS) -fno-tree-dominator-opts -fno-tree-fre -c -o $@ $<
src/xlat/xlat_parser.h src/xlat/xlat_parser.c: tools/lemon/lemon src/xlat/xlat_parser.y
$(CCDV) tools/lemon/lemon -s src/xlat/xlat_parser.y
$(OBJDIR): $(OBJDIR):
mkdir $(OBJDIR) mkdir $(OBJDIR)
toolsandpk3: tools/makewad/makewad tools/dehsupp/dehsupp tools/xlatcc/xlatcc toolsandpk3: tools/makewad/makewad src/svnrevision_gz.h
make -C wadsrc/ make -C wadsrc/
zdoom.pk3: toolsandpk3 zdoom.pk3: toolsandpk3
ln -sf wadsrc/gzdoom.pk3 ./ ln -sf wadsrc/gzdoom.pk3 ./
snes_spc/libsnes_spc.a:
$(MAKE) -C snes_spc/
tools/makewad/makewad: ccdv tools/makewad/makewad: ccdv
make -C tools/makewad/ make -C tools/makewad/
tools/dehsupp/dehsupp: ccdv tools/lemon/lemon:
make -C tools/dehsupp/ $(MAKE) -C tools/lemon/
tools/xlatcc/xlatcc: ccdv tools/updaterevision/updaterevision: ccdv
make -C tools/xlatcc/ make -C tools/updaterevision/
src/svnrevision_gz.h: tools/updaterevision/updaterevision
tools/updaterevision/updaterevision src src/svnrevision_gz.h
ccdv: ccdv-posix.c ccdv: ccdv-posix.c
@gcc -Os -s ccdv-posix.c -o ccdv @gcc -Os -s ccdv-posix.c -o ccdv

View file

@ -304,11 +304,7 @@ int main(int argc, char **argv)
snprintf(gAction, sizeof(gAction), "Running %s", Basename(argv[1])); snprintf(gAction, sizeof(gAction), "Running %s", Basename(argv[1]));
memset(gArgsStr, 0, sizeof(gArgsStr)); memset(gArgsStr, 0, sizeof(gArgsStr));
if(strcmp(gAction+8, "ar") == 0) if(strcmp(gAction+8, "cc") == 0 ||
{
snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i + 1]));
}
else if(strcmp(gAction+8, "cc") == 0 ||
strcmp(gAction+8, "ld") == 0 || strcmp(gAction+8, "ld") == 0 ||
strcmp(gAction+8, "gcc") == 0 || strcmp(gAction+8, "gcc") == 0 ||
strcmp(gAction+8, "g++") == 0 || strcmp(gAction+8, "g++") == 0 ||

View file

@ -769,7 +769,7 @@ public:
enum { S_NULL = 2, S_GENERICFREEZEDEATH = 3 }; enum { S_NULL = 2, S_GENERICFREEZEDEATH = 3 };
TArray<TObjPtr<AActor>> dynamiclights; TArray<TObjPtr<AActor> > dynamiclights;
void * lightassociations; void * lightassociations;
bool hasmodel; bool hasmodel;
subsector_s * subsector; subsector_s * subsector;

View file

@ -88,7 +88,6 @@ enum
static bool waitingforspawn[MAXPLAYERS]; static bool waitingforspawn[MAXPLAYERS];
DCajunMaster::~DCajunMaster() DCajunMaster::~DCajunMaster()
{ {
ForgetBots(); ForgetBots();

View file

@ -865,7 +865,7 @@ int VPrintf (int printlevel, const char *format, va_list parms)
FString outline; FString outline;
outline.VFormat (format, parms); outline.VFormat (format, parms);
return PrintString (printlevel, outline); return PrintString (printlevel, outline.GetChars());
} }
int STACK_ARGS Printf (int printlevel, const char *format, ...) int STACK_ARGS Printf (int printlevel, const char *format, ...)

View file

@ -242,7 +242,20 @@ static const char *IWADNames[] =
"hexen.wad", "hexen.wad",
"hexdd.wad", "hexdd.wad",
"strife1.wad", "strife1.wad",
"strife0.wad", "strife0.wad",
#ifdef unix
"DOOM2.WAD", // Also look for all-uppercase names
"PLUTONIA.WAD",
"TNT.WAD",
"DOOM.WAD",
"DOOM1.WAD",
"HERETIC.WAD",
"HERETIC1.WAD",
"HEXEN.WAD",
"HEXDD.WAD",
"STRIFE1.WAD",
"STRIFE0.WAD",
#endif
NULL NULL
}; };

View file

@ -53,6 +53,7 @@
#include "a_sharedglobal.h" #include "a_sharedglobal.h"
#include "st_start.h" #include "st_start.h"
#include "teaminfo.h" #include "teaminfo.h"
#include "p_conversation.h"
int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backSide, int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backSide,
int arg0, int arg1, int arg2, int always, bool wantResultCode, bool net); int arg0, int arg1, int arg2, int always, bool wantResultCode, bool net);
@ -2361,6 +2362,10 @@ void Net_DoCommand (int type, BYTE **stream, int player)
} }
break; break;
case DEM_CONVERSATION:
P_ConversationCommand (player, stream);
break;
default: default:
I_Error ("Unknown net command: %d", type); I_Error ("Unknown net command: %d", type);
break; break;
@ -2451,6 +2456,31 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = 3 + *(*stream + 2) * 4; skip = 3 + *(*stream + 2) * 4;
break; break;
case DEM_CONVERSATION:
{
t = **stream;
skip = 1;
switch (t)
{
case CONV_ANIMATE:
skip += 1;
break;
case CONV_GIVEINVENTORY:
skip += strlen ((char *)(*stream + skip)) + 1;
break;
case CONV_TAKEINVENTORY:
skip += strlen ((char *)(*stream + skip)) + 3;
break;
default:
break;
}
}
break;
default: default:
return; return;
} }

View file

@ -320,6 +320,11 @@ public:
fixed_t crouchoffset; fixed_t crouchoffset;
fixed_t crouchviewdelta; fixed_t crouchviewdelta;
// [CW] I moved these here for multiplayer conversation support.
AActor *ConversationNPC, *ConversationPC;
angle_t ConversationNPCAngle;
bool ConversationFaceTalker;
fixed_t GetDeltaViewHeight() const fixed_t GetDeltaViewHeight() const
{ {
return (mo->ViewHeight + crouchviewdelta - viewheight) >> 3; return (mo->ViewHeight + crouchviewdelta - viewheight) >> 3;

View file

@ -150,6 +150,7 @@ enum EDemoCommand
DEM_ADDCONTROLLER, // 48 Player to add to the controller list. DEM_ADDCONTROLLER, // 48 Player to add to the controller list.
DEM_DELCONTROLLER, // 49 Player to remove from the controller list. DEM_DELCONTROLLER, // 49 Player to remove from the controller list.
DEM_KILLCLASSCHEAT, // 50 String: Class to kill. DEM_KILLCLASSCHEAT, // 50 String: Class to kill.
DEM_CONVERSATION, // 51 Make conversations work.
}; };
// The following are implemented by cht_DoCheat in m_cheat.cpp // The following are implemented by cht_DoCheat in m_cheat.cpp

View file

@ -418,16 +418,19 @@ void DObject::Destroy ()
size_t DObject::PropagateMark() size_t DObject::PropagateMark()
{ {
const PClass *info = GetClass(); const PClass *info = GetClass();
const size_t *offsets = info->FlatPointers; if (!PClass::bShutdown)
if (offsets == NULL)
{ {
const_cast<PClass *>(info)->BuildFlatPointers(); const size_t *offsets = info->FlatPointers;
offsets = info->FlatPointers; if (offsets == NULL)
} {
while (*offsets != ~(size_t)0) const_cast<PClass *>(info)->BuildFlatPointers();
{ offsets = info->FlatPointers;
GC::Mark((DObject **)((BYTE *)this + *offsets)); }
offsets++; while (*offsets != ~(size_t)0)
{
GC::Mark((DObject **)((BYTE *)this + *offsets));
offsets++;
}
} }
return info->Size; return info->Size;
} }

View file

@ -298,10 +298,14 @@ static void MarkRoot()
if (playeringame[i]) if (playeringame[i])
players[i].PropagateMark(); players[i].PropagateMark();
} }
if (SectorMarker == NULL) if (SectorMarker == NULL && sectors != NULL)
{ {
SectorMarker = new DSectorMarker; SectorMarker = new DSectorMarker;
} }
else if (sectors == NULL)
{
SectorMarker = NULL;
}
else else
{ {
SectorMarker->SecNum = 0; SectorMarker->SecNum = 0;

View file

@ -41,6 +41,7 @@
TArray<PClass *> PClass::m_RuntimeActors; TArray<PClass *> PClass::m_RuntimeActors;
TArray<PClass *> PClass::m_Types; TArray<PClass *> PClass::m_Types;
PClass *PClass::TypeHash[PClass::HASH_SIZE]; PClass *PClass::TypeHash[PClass::HASH_SIZE];
bool PClass::bShutdown;
// A harmless non_NULL FlatPointer for classes without pointers. // A harmless non_NULL FlatPointer for classes without pointers.
static const size_t TheEnd = ~0u; static const size_t TheEnd = ~0u;
@ -110,6 +111,7 @@ void PClass::StaticShutdown ()
{ {
delete[] uniqueFPs[i]; delete[] uniqueFPs[i];
} }
bShutdown = true;
} }
void PClass::StaticFreeData (PClass *type) void PClass::StaticFreeData (PClass *type)

View file

@ -138,6 +138,8 @@ struct PClass
enum { HASH_SIZE = 256 }; enum { HASH_SIZE = 256 };
static PClass *TypeHash[HASH_SIZE]; static PClass *TypeHash[HASH_SIZE];
static bool bShutdown;
}; };
#endif #endif

View file

@ -476,7 +476,7 @@ DFsSection *FParser::looping_section()
SDWORD rover_index = Script->MakeIndex(Rover); SDWORD rover_index = Script->MakeIndex(Rover);
for(n=0; n<SECTIONSLOTS; n++) for(n=0; n<SECTIONSLOTS; n++)
{ {
DFsSection *current = Script->sections[n]; DFsSection *current = Script->sections[n];
// check all the sections in this hashchain // check all the sections in this hashchain
@ -494,7 +494,7 @@ DFsSection *FParser::looping_section()
} }
current = current->next; current = current->next;
} }
} }
return best; // return the best one found return best; // return the best one found
} }
@ -510,10 +510,10 @@ void FParser::SF_Continue(void)
DFsSection *section; DFsSection *section;
if(!(section = looping_section()) ) // no loop found if(!(section = looping_section()) ) // no loop found
{ {
script_error("continue() not in loop\n"); script_error("continue() not in loop\n");
return; return;
} }
Rover = Script->SectionEnd(section); // jump to the closing brace Rover = Script->SectionEnd(section); // jump to the closing brace
} }
@ -529,10 +529,10 @@ void FParser::SF_Break(void)
DFsSection *section; DFsSection *section;
if(!(section = looping_section()) ) if(!(section = looping_section()) )
{ {
script_error("break() not in loop\n"); script_error("break() not in loop\n");
return; return;
} }
Rover = Script->SectionEnd(section) + 1; // jump out of the loop Rover = Script->SectionEnd(section) + 1; // jump out of the loop
} }
@ -761,12 +761,12 @@ void FParser::SF_PlayerName(void)
int plnum; int plnum;
if(!t_argc) if(!t_argc)
{ {
player_t *pl=NULL; player_t *pl=NULL;
if (Script->trigger) pl = Script->trigger->player; if (Script->trigger) pl = Script->trigger->player;
if(pl) plnum = pl - players; if(pl) plnum = pl - players;
else plnum=-1; else plnum=-1;
} }
else else
plnum = T_GetPlayerNum(t_argv[0]); plnum = T_GetPlayerNum(t_argv[0]);
@ -820,8 +820,17 @@ void FParser::SF_PlayerObj(void)
void FParser::SF_Player(void) void FParser::SF_Player(void)
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_int; t_return.type = svt_int;
if(mo && mo->player) // haleyjd: added mo->player if(mo && mo->player) // haleyjd: added mo->player
@ -924,11 +933,17 @@ void FParser::SF_RemoveObj(void)
void FParser::SF_KillObj(void) void FParser::SF_KillObj(void)
{ {
// use trigger object if not specified
AActor *mo; AActor *mo;
if(t_argc)
if(t_argc) mo = actorvalue(t_argv[0]); {
else mo = Script->trigger; // default to trigger object mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger; // default to trigger object
}
if(mo) if(mo)
{ {
// ensure the thing can be killed // ensure the thing can be killed
@ -949,8 +964,17 @@ void FParser::SF_KillObj(void)
void FParser::SF_ObjX(void) void FParser::SF_ObjX(void)
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_fixed; // haleyjd: SoM's fixed-point fix t_return.type = svt_fixed; // haleyjd: SoM's fixed-point fix
t_return.value.f = mo ? mo->x : 0; // null ptr check t_return.value.f = mo ? mo->x : 0; // null ptr check
} }
@ -963,8 +987,17 @@ void FParser::SF_ObjX(void)
void FParser::SF_ObjY(void) void FParser::SF_ObjY(void)
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_fixed; // haleyjd t_return.type = svt_fixed; // haleyjd
t_return.value.f = mo ? mo->y : 0; // null ptr check t_return.value.f = mo ? mo->y : 0; // null ptr check
} }
@ -977,8 +1010,17 @@ void FParser::SF_ObjY(void)
void FParser::SF_ObjZ(void) void FParser::SF_ObjZ(void)
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_fixed; // haleyjd t_return.type = svt_fixed; // haleyjd
t_return.value.f = mo ? mo->z : 0; // null ptr check t_return.value.f = mo ? mo->z : 0; // null ptr check
} }
@ -992,8 +1034,17 @@ void FParser::SF_ObjZ(void)
void FParser::SF_ObjAngle(void) void FParser::SF_ObjAngle(void)
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_fixed; // haleyjd: fixed-point -- SoM again :) t_return.type = svt_fixed; // haleyjd: fixed-point -- SoM again :)
t_return.value.f = mo ? (fixed_t)AngleToFixed(mo->angle) : 0; // null ptr check t_return.value.f = mo ? (fixed_t)AngleToFixed(mo->angle) : 0; // null ptr check
} }
@ -1097,8 +1148,16 @@ void FParser::SF_DamageObj(void)
void FParser::SF_ObjSector(void) void FParser::SF_ObjSector(void)
{ {
// use trigger object if not specified // use trigger object if not specified
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_int; t_return.type = svt_int;
t_return.value.i = mo ? mo->Sector->tag : 0; // nullptr check t_return.value.i = mo ? mo->Sector->tag : 0; // nullptr check
} }
@ -1113,8 +1172,16 @@ void FParser::SF_ObjSector(void)
void FParser::SF_ObjHealth(void) void FParser::SF_ObjHealth(void)
{ {
// use trigger object if not specified // use trigger object if not specified
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_int; t_return.type = svt_int;
t_return.value.i = mo ? mo->health : 0; t_return.value.i = mo ? mo->health : 0;
} }
@ -1155,7 +1222,7 @@ void FParser::SF_ObjFlag(void)
// make the new flag // make the new flag
mo->flags |= (!!intvalue(t_argv[2])) << flagnum; mo->flags |= (!!intvalue(t_argv[2])) << flagnum;
} }
} }
t_return.type = svt_int; t_return.type = svt_int;
if (mo && flagnum<26) if (mo && flagnum<26)
@ -1352,7 +1419,7 @@ void FParser::SF_PointToDist(void)
// Doing this in floating point is actually faster with modern computers! // Doing this in floating point is actually faster with modern computers!
float x = floatvalue(t_argv[2]) - floatvalue(t_argv[0]); float x = floatvalue(t_argv[2]) - floatvalue(t_argv[0]);
float y = floatvalue(t_argv[3]) - floatvalue(t_argv[1]); float y = floatvalue(t_argv[3]) - floatvalue(t_argv[1]);
t_return.type = svt_fixed; t_return.type = svt_fixed;
t_return.value.f = (fixed_t)(sqrtf(x*x+y*y)*65536.f); t_return.value.f = (fixed_t)(sqrtf(x*x+y*y)*65536.f);
} }
@ -1875,7 +1942,7 @@ void DLightLevel::Tick()
} }
else else
{ {
// decrease lightlevel // decrease lightlevel
if(m_Sector->lightlevel - speed <= destlevel) if(m_Sector->lightlevel - speed <= destlevel)
{ {
// stop changing light level // stop changing light level
@ -1987,7 +2054,7 @@ void FParser::SF_SectorColormap(void)
int i = -1; int i = -1;
if(t_argc<2) if(t_argc<2)
{ script_error("insufficient arguments to function\n"); return; } { script_error("insufficient arguments to function\n"); return; }
tagnum = intvalue(t_argv[0]); tagnum = intvalue(t_argv[0]);
@ -1995,7 +2062,7 @@ void FParser::SF_SectorColormap(void)
secnum = T_FindSectorFromTag(tagnum, -1); secnum = T_FindSectorFromTag(tagnum, -1);
if(secnum < 0) if(secnum < 0)
{ script_error("sector not found with tagnum %i\n", tagnum); return;} { script_error("sector not found with tagnum %i\n", tagnum); return;}
sector = &sectors[secnum]; sector = &sectors[secnum];
@ -2463,15 +2530,17 @@ void FParser::SF_Gamemode(void)
void FParser::SF_IsPlayerObj(void) void FParser::SF_IsPlayerObj(void)
{ {
// use trigger object if not specified
AActor *mo; AActor *mo;
if(t_argc)
if(!t_argc) {
mo = actorvalue(t_argv[0]);
}
else
{ {
mo = Script->trigger; mo = Script->trigger;
} }
else
mo = actorvalue(t_argv[0]);
t_return.type = svt_int; t_return.type = svt_int;
t_return.value.i = (mo && mo->player) ? 1 : 0; t_return.value.i = (mo && mo->player) ? 1 : 0;
} }
@ -2770,9 +2839,9 @@ void FParser::SF_PlayerWeapon()
"PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" }; "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" };
int playernum; int playernum;
int weaponnum; int weaponnum;
int newweapon; int newweapon;
if (CheckArgs(2)) if (CheckArgs(2))
{ {
@ -3014,7 +3083,7 @@ void FParser::SF_MoveCamera(void)
// I have to use floats for the math where angles are divided // I have to use floats for the math where angles are divided
// by fixed values. // by fixed values.
double fangledist, fanglestep, fmovestep; double fangledist, fanglestep, fmovestep;
int angledir; int angledir;
AActor* target; AActor* target;
int moved; int moved;
int quad1, quad2; int quad1, quad2;
@ -3177,17 +3246,21 @@ void FParser::SF_MoveCamera(void)
void FParser::SF_ObjAwaken(void) void FParser::SF_ObjAwaken(void)
{ {
AActor *mo; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
if(!t_argc) if(mo)
mo = Script->trigger; {
else mo->Activate(Script->trigger);
mo = actorvalue(t_argv[0]); }
if(mo)
{
mo->Activate(Script->trigger);
}
} }
//========================================================================== //==========================================================================
@ -3316,9 +3389,9 @@ void FParser::SF_SpawnExplosion()
void FParser::SF_RadiusAttack() void FParser::SF_RadiusAttack()
{ {
AActor *spot; AActor *spot;
AActor *source; AActor *source;
int damage; int damage;
if (CheckArgs(3)) if (CheckArgs(3))
{ {
@ -3367,13 +3440,22 @@ void FParser::SF_SetObjPosition()
void FParser::SF_TestLocation() void FParser::SF_TestLocation()
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if (mo) if(t_argc)
{ {
t_return.type = svt_int; mo = actorvalue(t_argv[0]);
t_return.value.f = !!P_TestMobjLocation(mo); }
} else
{
mo = Script->trigger;
}
if (mo)
{
t_return.type = svt_int;
t_return.value.f = !!P_TestMobjLocation(mo);
}
} }
//========================================================================== //==========================================================================
@ -3384,7 +3466,16 @@ void FParser::SF_TestLocation()
void FParser::SF_HealObj() //no pain sound void FParser::SF_HealObj() //no pain sound
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
if(t_argc < 2) if(t_argc < 2)
{ {
@ -3411,8 +3502,17 @@ void FParser::SF_HealObj() //no pain sound
void FParser::SF_ObjDead() void FParser::SF_ObjDead()
{ {
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; // use trigger object if not specified
AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
t_return.type = svt_int; t_return.type = svt_int;
if(mo && (mo->health <= 0 || mo->flags&MF_CORPSE)) if(mo && (mo->health <= 0 || mo->flags&MF_CORPSE))
t_return.value.i = 1; t_return.value.i = 1;
@ -3428,8 +3528,8 @@ void FParser::SF_ObjDead()
void FParser::SF_SpawnMissile() void FParser::SF_SpawnMissile()
{ {
AActor *mobj; AActor *mobj;
AActor *target; AActor *target;
const PClass * PClass; const PClass * PClass;
if (CheckArgs(3)) if (CheckArgs(3))
@ -3450,9 +3550,9 @@ void FParser::SF_SpawnMissile()
void FParser::SF_MapThingNumExist() void FParser::SF_MapThingNumExist()
{ {
TArray<TObjPtr<AActor>> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
int intval; int intval;
if (CheckArgs(1)) if (CheckArgs(1))
{ {
@ -3479,10 +3579,10 @@ void FParser::SF_MapThingNumExist()
void FParser::SF_MapThings() void FParser::SF_MapThings()
{ {
TArray<TObjPtr<AActor>> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
t_return.type = svt_int; t_return.type = svt_int;
t_return.value.i = SpawnedThings.Size(); t_return.value.i = SpawnedThings.Size();
} }
@ -3495,7 +3595,7 @@ void FParser::SF_MapThings()
void FParser::SF_ObjState() void FParser::SF_ObjState()
{ {
int state; int state;
AActor *mo; AActor *mo = NULL;
if (CheckArgs(1)) if (CheckArgs(1))
{ {
@ -3691,7 +3791,15 @@ void FParser::SF_LineAttack()
void FParser::SF_ObjType() void FParser::SF_ObjType()
{ {
// use trigger object if not specified // use trigger object if not specified
AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; AActor *mo;
if(t_argc)
{
mo = actorvalue(t_argv[0]);
}
else
{
mo = Script->trigger;
}
for(unsigned int i=0;i<countof(ActorTypes);i++) if (mo->GetClass() == ActorTypes[i]) for(unsigned int i=0;i<countof(ActorTypes);i++) if (mo->GetClass() == ActorTypes[i])
{ {
@ -3851,38 +3959,38 @@ void FParser::SF_DeleteHUPic()
{ {
if (CheckArgs(1)) if (CheckArgs(1))
{ {
if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1) if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1)
script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0])); script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0]));
} }
} }
void FParser::SF_ModifyHUPic() void FParser::SF_ModifyHUPic()
{ {
if (t_argc != 4) if (t_argc != 4)
{ {
script_error("modifyhupic: invalid number of arguments\n"); script_error("modifyhupic: invalid number of arguments\n");
return; return;
} }
if (HU_ModifyFSPic(intvalue(t_argv[0]), if (HU_ModifyFSPic(intvalue(t_argv[0]),
TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny), TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny),
intvalue(t_argv[2]), intvalue(t_argv[3])) == -1) intvalue(t_argv[2]), intvalue(t_argv[3])) == -1)
{ {
script_error("modifyhypic: invalid sfpic handle %i\n", intvalue(t_argv[0])); script_error("modifyhypic: invalid sfpic handle %i\n", intvalue(t_argv[0]));
} }
return; return;
} }
void FParser::SF_SetHUPicDisplay() void FParser::SF_SetHUPicDisplay()
{ {
if (t_argc != 2) if (t_argc != 2)
{ {
script_error("sethupicdisplay: invalud number of arguments\n"); script_error("sethupicdisplay: invalud number of arguments\n");
return; return;
} }
if (HU_FSDisplay(intvalue(t_argv[0]), intvalue(t_argv[1]) > 0 ? 1 : 0) == -1) if (HU_FSDisplay(intvalue(t_argv[0]), intvalue(t_argv[1]) > 0 ? 1 : 0) == -1)
script_error("sethupicdisplay: invalid pic handle %i\n", intvalue(t_argv[0])); script_error("sethupicdisplay: invalid pic handle %i\n", intvalue(t_argv[0]));
} }
@ -3900,47 +4008,47 @@ void FParser::SF_SetCorona(void)
return; return;
} }
int num = t_argv[0].value.i; // which corona we want to modify int num = t_argv[0].value.i; // which corona we want to modify
int what = t_argv[1].value.i; // what we want to modify (type, color, offset,...) int what = t_argv[1].value.i; // what we want to modify (type, color, offset,...)
int ival = t_argv[2].value.i; // the value of what we modify int ival = t_argv[2].value.i; // the value of what we modify
double fval = ((double) t_argv[2].value.f / FRACUNIT); double fval = ((double) t_argv[2].value.f / FRACUNIT);
/* /*
switch (what) switch (what)
{ {
case 0: case 0:
lspr[num].type = ival; lspr[num].type = ival;
break; break;
case 1: case 1:
lspr[num].light_xoffset = fval; lspr[num].light_xoffset = fval;
break; break;
case 2: case 2:
lspr[num].light_yoffset = fval; lspr[num].light_yoffset = fval;
break; break;
case 3: case 3:
if (t_argv[2].type == svt_string) if (t_argv[2].type == svt_string)
lspr[num].corona_color = String2Hex(t_argv[2].value.s); lspr[num].corona_color = String2Hex(t_argv[2].value.s);
else else
memcpy(&lspr[num].corona_color, &ival, sizeof(int)); memcpy(&lspr[num].corona_color, &ival, sizeof(int));
break; break;
case 4: case 4:
lspr[num].corona_radius = fval; lspr[num].corona_radius = fval;
break; break;
case 5: case 5:
if (t_argv[2].type == svt_string) if (t_argv[2].type == svt_string)
lspr[num].dynamic_color = String2Hex(t_argv[2].value.s); lspr[num].dynamic_color = String2Hex(t_argv[2].value.s);
else else
memcpy(&lspr[num].dynamic_color, &ival, sizeof(int)); memcpy(&lspr[num].dynamic_color, &ival, sizeof(int));
break; break;
case 6: case 6:
lspr[num].dynamic_radius = fval; lspr[num].dynamic_radius = fval;
lspr[num].dynamic_sqrradius = sqrt(lspr[num].dynamic_radius); lspr[num].dynamic_sqrradius = sqrt(lspr[num].dynamic_radius);
break; break;
default: default:
CONS_Printf("Error in setcorona\n"); CONS_Printf("Error in setcorona\n");
break; break;
} }
*/ */
// no use for this! // no use for this!
t_return.type = svt_int; t_return.type = svt_int;
@ -4074,7 +4182,7 @@ again:
} }
else else
{ {
while (mo=it.Next()) while ((mo=it.Next()))
{ {
if (mo->IsA(pClass) && mo->health>0) count++; if (mo->IsA(pClass) && mo->health>0) count++;
} }
@ -4361,10 +4469,10 @@ void FParser::SF_Wait()
DRunningScript *runscr; DRunningScript *runscr;
if(t_argc != 1) if(t_argc != 1)
{ {
script_error("incorrect arguments to function\n"); script_error("incorrect arguments to function\n");
return; return;
} }
runscr = SaveCurrentScript(); runscr = SaveCurrentScript();
@ -4385,10 +4493,10 @@ void FParser::SF_TagWait()
DRunningScript *runscr; DRunningScript *runscr;
if(t_argc != 1) if(t_argc != 1)
{ {
script_error("incorrect arguments to function\n"); script_error("incorrect arguments to function\n");
return; return;
} }
runscr = SaveCurrentScript(); runscr = SaveCurrentScript();
@ -4408,10 +4516,10 @@ void FParser::SF_ScriptWait()
DRunningScript *runscr; DRunningScript *runscr;
if(t_argc != 1) if(t_argc != 1)
{ {
script_error("insufficient arguments to function\n"); script_error("insufficient arguments to function\n");
return; return;
} }
runscr = SaveCurrentScript(); runscr = SaveCurrentScript();
@ -4451,10 +4559,10 @@ void FParser::SF_ScriptWaitPre()
void FParser::SF_StartScript() void FParser::SF_StartScript()
{ {
if(t_argc != 1) if(t_argc != 1)
{ {
script_error("incorrect arguments to function\n"); script_error("incorrect arguments to function\n");
return; return;
} }
int snum = intvalue(t_argv[0]); int snum = intvalue(t_argv[0]);
@ -4493,15 +4601,15 @@ void FParser::SF_ScriptRunning()
int snum = 0; int snum = 0;
if(t_argc < 1) if(t_argc < 1)
{ {
script_error("not enough arguments to function\n"); script_error("not enough arguments to function\n");
return; return;
} }
snum = intvalue(t_argv[0]); snum = intvalue(t_argv[0]);
for(current = DFraggleThinker::ActiveThinker->RunningScripts->next; current; current=current->next) for(current = DFraggleThinker::ActiveThinker->RunningScripts->next; current; current=current->next)
{ {
if(current->script->scriptnum == snum) if(current->script->scriptnum == snum)
{ {
// script found so return // script found so return
@ -4509,7 +4617,7 @@ void FParser::SF_ScriptRunning()
t_return.value.i = 1; t_return.value.i = 1;
return; return;
} }
} }
// script not found // script not found
t_return.type = svt_int; t_return.type = svt_int;
@ -4578,27 +4686,27 @@ void init_functions(void)
new_function("playertip", &FParser::SF_PlayerTip); new_function("playertip", &FParser::SF_PlayerTip);
new_function("playeringame", &FParser::SF_PlayerInGame); new_function("playeringame", &FParser::SF_PlayerInGame);
new_function("playername", &FParser::SF_PlayerName); new_function("playername", &FParser::SF_PlayerName);
new_function("playeraddfrag", &FParser::SF_PlayerAddFrag); new_function("playeraddfrag", &FParser::SF_PlayerAddFrag);
new_function("playerobj", &FParser::SF_PlayerObj); new_function("playerobj", &FParser::SF_PlayerObj);
new_function("isplayerobj", &FParser::SF_IsPlayerObj); new_function("isplayerobj", &FParser::SF_IsPlayerObj);
new_function("isobjplayer", &FParser::SF_IsPlayerObj); new_function("isobjplayer", &FParser::SF_IsPlayerObj);
new_function("skincolor", &FParser::SF_SkinColor); new_function("skincolor", &FParser::SF_SkinColor);
new_function("playerkeys", &FParser::SF_PlayerKeys); new_function("playerkeys", &FParser::SF_PlayerKeys);
new_function("playerammo", &FParser::SF_PlayerAmmo); new_function("playerammo", &FParser::SF_PlayerAmmo);
new_function("maxplayerammo", &FParser::SF_MaxPlayerAmmo); new_function("maxplayerammo", &FParser::SF_MaxPlayerAmmo);
new_function("playerweapon", &FParser::SF_PlayerWeapon); new_function("playerweapon", &FParser::SF_PlayerWeapon);
new_function("playerselwep", &FParser::SF_PlayerSelectedWeapon); new_function("playerselwep", &FParser::SF_PlayerSelectedWeapon);
// mobj stuff // mobj stuff
new_function("spawn", &FParser::SF_Spawn); new_function("spawn", &FParser::SF_Spawn);
new_function("spawnexplosion", &FParser::SF_SpawnExplosion); new_function("spawnexplosion", &FParser::SF_SpawnExplosion);
new_function("radiusattack", &FParser::SF_RadiusAttack); new_function("radiusattack", &FParser::SF_RadiusAttack);
new_function("kill", &FParser::SF_KillObj); new_function("kill", &FParser::SF_KillObj);
new_function("removeobj", &FParser::SF_RemoveObj); new_function("removeobj", &FParser::SF_RemoveObj);
new_function("objx", &FParser::SF_ObjX); new_function("objx", &FParser::SF_ObjX);
new_function("objy", &FParser::SF_ObjY); new_function("objy", &FParser::SF_ObjY);
new_function("objz", &FParser::SF_ObjZ); new_function("objz", &FParser::SF_ObjZ);
new_function("testlocation", &FParser::SF_TestLocation); new_function("testlocation", &FParser::SF_TestLocation);
new_function("teleport", &FParser::SF_Teleport); new_function("teleport", &FParser::SF_Teleport);
new_function("silentteleport", &FParser::SF_SilentTeleport); new_function("silentteleport", &FParser::SF_SilentTeleport);
new_function("damageobj", &FParser::SF_DamageObj); new_function("damageobj", &FParser::SF_DamageObj);
@ -4618,10 +4726,10 @@ void init_functions(void)
new_function("objmomy", &FParser::SF_MobjMomy); new_function("objmomy", &FParser::SF_MobjMomy);
new_function("objmomz", &FParser::SF_MobjMomz); new_function("objmomz", &FParser::SF_MobjMomz);
new_function("spawnmissile", &FParser::SF_SpawnMissile); new_function("spawnmissile", &FParser::SF_SpawnMissile);
new_function("mapthings", &FParser::SF_MapThings); new_function("mapthings", &FParser::SF_MapThings);
new_function("objtype", &FParser::SF_ObjType); new_function("objtype", &FParser::SF_ObjType);
new_function("mapthingnumexist", &FParser::SF_MapThingNumExist); new_function("mapthingnumexist", &FParser::SF_MapThingNumExist);
new_function("objstate", &FParser::SF_ObjState); new_function("objstate", &FParser::SF_ObjState);
new_function("resurrect", &FParser::SF_Resurrect); new_function("resurrect", &FParser::SF_Resurrect);
new_function("lineattack", &FParser::SF_LineAttack); new_function("lineattack", &FParser::SF_LineAttack);
@ -4665,19 +4773,19 @@ void init_functions(void)
new_function("opendoor", &FParser::SF_OpenDoor); new_function("opendoor", &FParser::SF_OpenDoor);
new_function("closedoor", &FParser::SF_CloseDoor); new_function("closedoor", &FParser::SF_CloseDoor);
// HU Graphics // HU Graphics
new_function("newhupic", &FParser::SF_NewHUPic); new_function("newhupic", &FParser::SF_NewHUPic);
new_function("createpic", &FParser::SF_NewHUPic); new_function("createpic", &FParser::SF_NewHUPic);
new_function("deletehupic", &FParser::SF_DeleteHUPic); new_function("deletehupic", &FParser::SF_DeleteHUPic);
new_function("modifyhupic", &FParser::SF_ModifyHUPic); new_function("modifyhupic", &FParser::SF_ModifyHUPic);
new_function("modifypic", &FParser::SF_ModifyHUPic); new_function("modifypic", &FParser::SF_ModifyHUPic);
new_function("sethupicdisplay", &FParser::SF_SetHUPicDisplay); new_function("sethupicdisplay", &FParser::SF_SetHUPicDisplay);
new_function("setpicvisible", &FParser::SF_SetHUPicDisplay); new_function("setpicvisible", &FParser::SF_SetHUPicDisplay);
// //
new_function("playdemo", &FParser::SF_PlayDemo); new_function("playdemo", &FParser::SF_PlayDemo);
new_function("runcommand", &FParser::SF_RunCommand); new_function("runcommand", &FParser::SF_RunCommand);
new_function("checkcvar", &FParser::SF_CheckCVar); new_function("checkcvar", &FParser::SF_CheckCVar);
new_function("setlinetexture", &FParser::SF_SetLineTexture); new_function("setlinetexture", &FParser::SF_SetLineTexture);
new_function("linetrigger", &FParser::SF_LineTrigger); new_function("linetrigger", &FParser::SF_LineTrigger);
new_function("lineflag", &FParser::SF_LineFlag); new_function("lineflag", &FParser::SF_LineFlag);

View file

@ -328,7 +328,7 @@ void T_RegisterSpawnThing(AActor * ac)
{ {
if (DFraggleThinker::ActiveThinker) if (DFraggleThinker::ActiveThinker)
{ {
TArray<TObjPtr<AActor>> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
SpawnedThings[SpawnedThings.Size()-1] = ac; SpawnedThings[SpawnedThings.Size()-1] = ac;
} }
} }

View file

@ -655,7 +655,7 @@ public:
TObjPtr<DFsScript> LevelScript; TObjPtr<DFsScript> LevelScript;
TObjPtr<DRunningScript> RunningScripts; TObjPtr<DRunningScript> RunningScripts;
TArray<TObjPtr<AActor>> SpawnedThings; TArray<TObjPtr<AActor> > SpawnedThings;
DFraggleThinker(); DFraggleThinker();
void Destroy(); void Destroy();

View file

@ -133,7 +133,7 @@ const char *stringvalue(const svalue_t & v)
// //
//========================================================================== //==========================================================================
AActor *actorvalue(const svalue_t &svalue) AActor* actorvalue(const svalue_t &svalue)
{ {
int intval; int intval;
@ -151,7 +151,7 @@ AActor *actorvalue(const svalue_t &svalue)
} }
else else
{ {
TArray<TObjPtr<AActor>> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
// this requires some creativity. We use the intvalue // this requires some creativity. We use the intvalue
// as the thing number of a thing in the level. // as the thing number of a thing in the level.
intval = intvalue(svalue); intval = intvalue(svalue);

View file

@ -125,6 +125,11 @@ public:
OldFaceIndex = -1; OldFaceIndex = -1;
} }
void AddFaceToImageCollection (void *skn, FImageCollection *images)
{
AddFaceToImageCollectionActual (skn, images, true);
}
void MultiplayerChanged () void MultiplayerChanged ()
{ {
DBaseStatusBar::MultiplayerChanged (); DBaseStatusBar::MultiplayerChanged ();
@ -989,24 +994,6 @@ private:
NUM_DOOMSB_IMAGES NUM_DOOMSB_IMAGES
}; };
enum
{
ST_NUMPAINFACES = 5,
ST_NUMSTRAIGHTFACES = 3,
ST_NUMTURNFACES = 2,
ST_NUMSPECIALFACES = 3,
ST_NUMEXTRAFACES = 2,
ST_FACESTRIDE = ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES,
ST_NUMFACES = ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES,
ST_TURNOFFSET = ST_NUMSTRAIGHTFACES,
ST_OUCHOFFSET = ST_TURNOFFSET + ST_NUMTURNFACES,
ST_EVILGRINOFFSET = ST_OUCHOFFSET + 1,
ST_RAMPAGEOFFSET = ST_EVILGRINOFFSET + 1,
ST_GODFACE = ST_NUMPAINFACES*ST_FACESTRIDE,
ST_DEADFACE = ST_GODFACE + 1
};
FImageCollection Images; FImageCollection Images;
FImageCollection Faces; FImageCollection Faces;

View file

@ -165,6 +165,25 @@ public:
POP_Status POP_Status
}; };
// Status face stuff
enum
{
ST_NUMPAINFACES = 5,
ST_NUMSTRAIGHTFACES = 3,
ST_NUMTURNFACES = 2,
ST_NUMSPECIALFACES = 3,
ST_NUMEXTRAFACES = 2,
ST_FACESTRIDE = ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES,
ST_NUMFACES = ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES,
ST_TURNOFFSET = ST_NUMSTRAIGHTFACES,
ST_OUCHOFFSET = ST_TURNOFFSET + ST_NUMTURNFACES,
ST_EVILGRINOFFSET = ST_OUCHOFFSET + 1,
ST_RAMPAGEOFFSET = ST_EVILGRINOFFSET + 1,
ST_GODFACE = ST_NUMPAINFACES*ST_FACESTRIDE,
ST_DEADFACE = ST_GODFACE + 1
};
DBaseStatusBar (int reltop); DBaseStatusBar (int reltop);
void Destroy (); void Destroy ();
@ -190,7 +209,8 @@ public:
virtual void AttachToPlayer (player_s *player); virtual void AttachToPlayer (player_s *player);
virtual void FlashCrosshair (); virtual void FlashCrosshair ();
virtual void BlendView (float blend[4]); virtual void BlendView (float blend[4]);
virtual void SetFace (void *); // Takes a FPlayerSkin as input virtual void SetFace (void *skn); // Takes a FPlayerSkin as input
virtual void AddFaceToImageCollection (void *skn, FImageCollection *images); // Takes a FPlayerSkin as input
virtual void NewGame (); virtual void NewGame ();
virtual void ScreenSizeChanged (); virtual void ScreenSizeChanged ();
virtual void MultiplayerChanged (); virtual void MultiplayerChanged ();
@ -222,6 +242,8 @@ protected:
void GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const; void GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const;
void AddFaceToImageCollectionActual (void *skn, FImageCollection *images, bool isDoom);
public: public:
AInventory *ValidateInvFirst (int numVisible) const; AInventory *ValidateInvFirst (int numVisible) const;
void DrawCrosshair (); void DrawCrosshair ();

View file

@ -86,6 +86,7 @@ struct SBarInfo
bool interpolateHealth; bool interpolateHealth;
bool interpolateArmor; bool interpolateArmor;
bool completeBorder; bool completeBorder;
bool lowerHealthCap;
char spacingCharacter; char spacingCharacter;
int interpolationSpeed; int interpolationSpeed;
int armorInterpolationSpeed; int armorInterpolationSpeed;
@ -119,7 +120,7 @@ struct MugShotFrame
MugShotFrame(); MugShotFrame();
~MugShotFrame(); ~MugShotFrame();
FTexture *getTexture(FPlayerSkin *skn, int random, int level=0, int direction=0, bool usesLevels=false, bool health2=false, bool healthspecial=false, bool directional=false); FTexture *getTexture(FString &defaultFace, FPlayerSkin *skn, int random, int level=0, int direction=0, bool usesLevels=false, bool health2=false, bool healthspecial=false, bool directional=false);
}; };
@ -143,7 +144,7 @@ struct MugShotState
void tick(); void tick();
void reset(); void reset();
MugShotFrame getCurrentFrame() { return frames[position]; } MugShotFrame getCurrentFrame() { return frames[position]; }
FTexture *getCurrentFrameTexture(FPlayerSkin *skn, int level=0, int direction=0) { return getCurrentFrame().getTexture(skn, random, level, direction, usesLevels, health2, healthspecial, directional); } FTexture *getCurrentFrameTexture(FString &defaultFace, FPlayerSkin *skn, int level=0, int direction=0) { return getCurrentFrame().getTexture(defaultFace, skn, random, level, direction, usesLevels, health2, healthspecial, directional); }
}; };
extern TArray<MugShotState> MugShotStates; extern TArray<MugShotState> MugShotStates;
@ -197,6 +198,7 @@ enum //drawnumber flags
DRAWNUMBER_SECRETS = 4096, DRAWNUMBER_SECRETS = 4096,
DRAWNUMBER_TOTALSECRETS = 8192, DRAWNUMBER_TOTALSECRETS = 8192,
DRAWNUMBER_ARMORCLASS = 16384, DRAWNUMBER_ARMORCLASS = 16384,
DRAWNUMBER_GLOBALVAR = 32768,
}; };
enum //drawbar flags (will go into special2) enum //drawbar flags (will go into special2)
@ -253,6 +255,14 @@ enum //event flags
SBARINFOEVENT_AND = 4, SBARINFOEVENT_AND = 4,
}; };
enum //aspect ratios
{
ASPECTRATIO_4_3 = 0,
ASPECTRATIO_16_9 = 1,
ASPECTRATIO_16_10 = 2,
ASPECTRATIO_5_4 = 3,
};
enum //Key words enum //Key words
{ {
SBARINFO_BASE, SBARINFO_BASE,
@ -261,6 +271,7 @@ enum //Key words
SBARINFO_INTERPOLATEARMOR, SBARINFO_INTERPOLATEARMOR,
SBARINFO_COMPLETEBORDER, SBARINFO_COMPLETEBORDER,
SBARINFO_MONOSPACEFONTS, SBARINFO_MONOSPACEFONTS,
SBARINFO_LOWERHEALTHCAP,
SBARINFO_STATUSBAR, SBARINFO_STATUSBAR,
SBARINFO_MUGSHOT, SBARINFO_MUGSHOT,
}; };
@ -293,7 +304,9 @@ enum //Bar key words
SBARINFO_DRAWKEYBAR, SBARINFO_DRAWKEYBAR,
SBARINFO_GAMEMODE, SBARINFO_GAMEMODE,
SBARINFO_PLAYERCLASS, SBARINFO_PLAYERCLASS,
SBARINFO_ASPECTRATIO,
SBARINFO_WEAPONAMMO, SBARINFO_WEAPONAMMO,
SBARINFO_ININVENTORY,
}; };
//All this so I can change the mugshot state in ACS... //All this so I can change the mugshot state in ACS...
@ -321,13 +334,14 @@ public:
void Tick(); void Tick();
void ReceivedWeapon (AWeapon *weapon); void ReceivedWeapon (AWeapon *weapon);
void FlashItem(const PClass *itemtype); void FlashItem(const PClass *itemtype);
void ShowPop(int popnum);
void SetMugShotState(const char* stateName, bool waitTillDone=false); void SetMugShotState(const char* stateName, bool waitTillDone=false);
private: private:
void doCommands(SBarInfoBlock &block); void doCommands(SBarInfoBlock &block);
void DrawGraphic(FTexture* texture, int x, int y, int flags); void DrawGraphic(FTexture* texture, int x, int y, int flags);
void DrawString(const char* str, int x, int y, EColorRange translation, int spacing=0); void DrawString(const char* str, int x, int y, EColorRange translation, int spacing=0);
void DrawNumber(int num, int len, int x, int y, EColorRange translation, int spacing=0); void DrawNumber(int num, int len, int x, int y, EColorRange translation, int spacing=0);
void DrawFace(int accuracy, bool xdth, bool animatedgodmode, int x, int y); void DrawFace(FString &defaultFace, int accuracy, bool xdth, bool animatedgodmode, int x, int y);
int updateState(bool xdth, bool animatedgodmode); int updateState(bool xdth, bool animatedgodmode);
void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow, void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow,
int counterx, int countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter); int counterx, int countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter);
@ -349,6 +363,7 @@ private:
int mugshotHealth; int mugshotHealth;
int chainWiggle; int chainWiggle;
int artiflash; int artiflash;
int currentPopup;
unsigned int invBarOffset; unsigned int invBarOffset;
FBarShader shader_horz_normal; FBarShader shader_horz_normal;
FBarShader shader_horz_reverse; FBarShader shader_horz_reverse;

View file

@ -83,13 +83,13 @@ MugShotFrame::~MugShotFrame()
} }
//Assemble a graphic name with the specified prefix and return the FTexture. //Assemble a graphic name with the specified prefix and return the FTexture.
FTexture *MugShotFrame::getTexture(FPlayerSkin *skin, int random, int level, int direction, bool usesLevels, bool health2, bool healthspecial, bool directional) FTexture *MugShotFrame::getTexture(FString &defaultFace, FPlayerSkin *skin, int random, int level, int direction, bool usesLevels, bool health2, bool healthspecial, bool directional)
{ {
int index = !directional ? random % graphic.Size() : direction; int index = !directional ? random % graphic.Size() : direction;
if(index > (signed int) (graphic.Size()-1)) if(index > (signed int) (graphic.Size()-1))
index = graphic.Size()-1; index = graphic.Size()-1;
char* sprite = new char[9]; char* sprite = new char[9];
memcpy(sprite, skin->face[0] != 0 ? skin->face : "STF", 3); memcpy(sprite, skin->face[0] != 0 ? skin->face : defaultFace, 3);
memcpy(sprite+3, graphic[index], strlen(graphic[index])); memcpy(sprite+3, graphic[index], strlen(graphic[index]));
sprite[3+strlen(graphic[index])] = '\0'; sprite[3+strlen(graphic[index])] = '\0';
if(usesLevels) //change the last character to the level if(usesLevels) //change the last character to the level
@ -249,6 +249,10 @@ DSBarInfo::DSBarInfo () : DBaseStatusBar (SBarInfoScript->height),
{ {
patchnames[i+SBarInfoScript->Images.Size()] = InventoryBarLumps[i]; patchnames[i+SBarInfoScript->Images.Size()] = InventoryBarLumps[i];
} }
for (i = 0;i < numskins;i++)
{
AddFaceToImageCollection (&skins[i], &Images);
}
invBarOffset = SBarInfoScript->Images.Size(); invBarOffset = SBarInfoScript->Images.Size();
Images.Init(&patchnames[0], patchnames.Size()); Images.Init(&patchnames[0], patchnames.Size());
drawingFont = V_GetFont("ConFont"); drawingFont = V_GetFont("ConFont");
@ -313,6 +317,15 @@ void DSBarInfo::Draw (EHudState state)
else if(state == HUD_Fullscreen) else if(state == HUD_Fullscreen)
doCommands(SBarInfoScript->huds[STBAR_INVENTORYFULLSCREEN]); doCommands(SBarInfoScript->huds[STBAR_INVENTORYFULLSCREEN]);
} }
if(currentPopup != POP_None)
{
if(currentPopup == POP_Log)
doCommands(SBarInfoScript->huds[STBAR_POPUPLOG]);
else if(currentPopup == POP_Keys)
doCommands(SBarInfoScript->huds[STBAR_POPUPKEYS]);
else if(currentPopup == POP_Status)
doCommands(SBarInfoScript->huds[STBAR_POPUPSTATUS]);
}
} }
void DSBarInfo::NewGame () void DSBarInfo::NewGame ()
@ -409,6 +422,15 @@ void DSBarInfo::FlashItem(const PClass *itemtype)
artiflash = 4; artiflash = 4;
} }
void DSBarInfo::ShowPop(int popnum)
{
DBaseStatusBar::ShowPop(popnum);
if(popnum != currentPopup)
currentPopup = popnum;
else
currentPopup = POP_None;
}
//Public so it can be called by ACS //Public so it can be called by ACS
//Sets the mug shot state and resets it if it is not the state we are already on. //Sets the mug shot state and resets it if it is not the state we are already on.
//waitTillDone is basically a priority variable when just to true the state won't change unless the previous state is finished. //waitTillDone is basically a priority variable when just to true the state won't change unless the previous state is finished.
@ -543,25 +565,27 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
} }
break; break;
case SBARINFO_DRAWNUMBER: case SBARINFO_DRAWNUMBER:
{
int value = cmd.value;
if(drawingFont != cmd.font) if(drawingFont != cmd.font)
{ {
drawingFont = cmd.font; drawingFont = cmd.font;
} }
if(cmd.flags == DRAWNUMBER_HEALTH) if(cmd.flags == DRAWNUMBER_HEALTH)
{ {
cmd.value = health; value = health;
if(cmd.value < 0) //health shouldn't display negatives if(SBarInfoScript->lowerHealthCap && cmd.value < 0) //health shouldn't display negatives
{ {
cmd.value = 0; value = 0;
} }
} }
else if(cmd.flags == DRAWNUMBER_ARMOR) else if(cmd.flags == DRAWNUMBER_ARMOR)
{ {
cmd.value = armorAmount; value = armorAmount;
} }
else if(cmd.flags == DRAWNUMBER_AMMO1) else if(cmd.flags == DRAWNUMBER_AMMO1)
{ {
cmd.value = ammocount1; value = ammocount1;
if(ammo1 == NULL) //no ammo, do not draw if(ammo1 == NULL) //no ammo, do not draw
{ {
continue; continue;
@ -569,7 +593,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
} }
else if(cmd.flags == DRAWNUMBER_AMMO2) else if(cmd.flags == DRAWNUMBER_AMMO2)
{ {
cmd.value = ammocount2; value = ammocount2;
if(ammo2 == NULL) //no ammo, do not draw if(ammo2 == NULL) //no ammo, do not draw
{ {
continue; continue;
@ -581,11 +605,11 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
AInventory* item = CPlayer->mo->FindInventory(ammo); AInventory* item = CPlayer->mo->FindInventory(ammo);
if(item != NULL) if(item != NULL)
{ {
cmd.value = item->Amount; value = item->Amount;
} }
else else
{ {
cmd.value = 0; value = 0;
} }
} }
else if(cmd.flags == DRAWNUMBER_AMMOCAPACITY) else if(cmd.flags == DRAWNUMBER_AMMOCAPACITY)
@ -594,61 +618,64 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
AInventory* item = CPlayer->mo->FindInventory(ammo); AInventory* item = CPlayer->mo->FindInventory(ammo);
if(item != NULL) if(item != NULL)
{ {
cmd.value = item->MaxAmount; value = item->MaxAmount;
} }
else else
{ {
cmd.value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount; value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount;
} }
} }
else if(cmd.flags == DRAWNUMBER_FRAGS) else if(cmd.flags == DRAWNUMBER_FRAGS)
cmd.value = CPlayer->fragcount; value = CPlayer->fragcount;
else if(cmd.flags == DRAWNUMBER_KILLS) else if(cmd.flags == DRAWNUMBER_KILLS)
cmd.value = level.killed_monsters; value = level.killed_monsters;
else if(cmd.flags == DRAWNUMBER_MONSTERS) else if(cmd.flags == DRAWNUMBER_MONSTERS)
cmd.value = level.total_monsters; value = level.total_monsters;
else if(cmd.flags == DRAWNUMBER_ITEMS) else if(cmd.flags == DRAWNUMBER_ITEMS)
cmd.value = level.found_items; value = level.found_items;
else if(cmd.flags == DRAWNUMBER_TOTALITEMS) else if(cmd.flags == DRAWNUMBER_TOTALITEMS)
cmd.value = level.total_items; value = level.total_items;
else if(cmd.flags == DRAWNUMBER_SECRETS) else if(cmd.flags == DRAWNUMBER_SECRETS)
cmd.value = level.found_secrets; value = level.found_secrets;
else if(cmd.flags == DRAWNUMBER_TOTALSECRETS) else if(cmd.flags == DRAWNUMBER_TOTALSECRETS)
cmd.value = level.total_secrets; value = level.total_secrets;
else if(cmd.flags == DRAWNUMBER_ARMORCLASS) else if(cmd.flags == DRAWNUMBER_ARMORCLASS)
{ {
AHexenArmor *harmor = CPlayer->mo->FindInventory<AHexenArmor>(); AHexenArmor *harmor = CPlayer->mo->FindInventory<AHexenArmor>();
if(harmor != NULL) if(harmor != NULL)
{ {
cmd.value = harmor->Slots[0] + harmor->Slots[1] + value = harmor->Slots[0] + harmor->Slots[1] +
harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4];
} }
//Hexen counts basic armor also so we should too. //Hexen counts basic armor also so we should too.
if(armor != NULL) if(armor != NULL)
{ {
cmd.value += armor->SavePercent; value += armor->SavePercent;
} }
cmd.value /= (5*FRACUNIT); value /= (5*FRACUNIT);
} }
else if(cmd.flags == DRAWNUMBER_GLOBALVAR)
value = ACS_GlobalVars[cmd.value];
else if(cmd.flags == DRAWNUMBER_INVENTORY) else if(cmd.flags == DRAWNUMBER_INVENTORY)
{ {
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0]));
if(item != NULL) if(item != NULL)
{ {
cmd.value = item->Amount; value = item->Amount;
} }
else else
{ {
cmd.value = 0; value = 0;
} }
} }
if(cmd.special3 != -1 && cmd.value <= cmd.special3) //low if(cmd.special3 != -1 && cmd.value <= cmd.special3) //low
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation2, cmd.special2); DrawNumber(value, cmd.special, cmd.x, cmd.y, cmd.translation2, cmd.special2);
else if(cmd.special4 != -1 && cmd.value >= cmd.special4) //high else if(cmd.special4 != -1 && cmd.value >= cmd.special4) //high
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation3, cmd.special2); DrawNumber(value, cmd.special, cmd.x, cmd.y, cmd.translation3, cmd.special2);
else else
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation, cmd.special2); DrawNumber(value, cmd.special, cmd.x, cmd.y, cmd.translation, cmd.special2);
break; break;
}
case SBARINFO_DRAWMUGSHOT: case SBARINFO_DRAWMUGSHOT:
{ {
bool xdth = false; bool xdth = false;
@ -657,7 +684,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
xdth = true; xdth = true;
if(cmd.flags & DRAWMUGSHOT_ANIMATEDGODMODE) if(cmd.flags & DRAWMUGSHOT_ANIMATEDGODMODE)
animatedgodmode = true; animatedgodmode = true;
DrawFace(cmd.special, xdth, animatedgodmode, cmd.x, cmd.y); DrawFace(cmd.string[0], cmd.special, xdth, animatedgodmode, cmd.x, cmd.y);
break; break;
} }
case SBARINFO_DRAWSELECTEDINVENTORY: case SBARINFO_DRAWSELECTEDINVENTORY:
@ -1000,7 +1027,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
{ {
drawingFont = cmd.font; drawingFont = cmd.font;
} }
DrawString(cmd.string[0], cmd.x - drawingFont->StringWidth(cmd.string[0]), cmd.y, cmd.translation); DrawString(cmd.string[0], cmd.x - drawingFont->StringWidth(cmd.string[0]), cmd.y, cmd.translation, cmd.special);
break; break;
case SBARINFO_DRAWKEYBAR: case SBARINFO_DRAWKEYBAR:
{ {
@ -1046,6 +1073,12 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
} }
break; break;
} }
case SBARINFO_ASPECTRATIO:
if(CheckRatio(screen->GetWidth(), screen->GetHeight()) == cmd.value)
{
doCommands(cmd.subBlock);
}
break;
case SBARINFO_WEAPONAMMO: case SBARINFO_WEAPONAMMO:
if(CPlayer->ReadyWeapon != NULL) if(CPlayer->ReadyWeapon != NULL)
{ {
@ -1090,6 +1123,30 @@ void DSBarInfo::doCommands(SBarInfoBlock &block)
} }
} }
break; break;
case SBARINFO_ININVENTORY:
{
AInventory *item1 = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0]));
AInventory *item2 = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[1]));
if(cmd.flags & SBARINFOEVENT_AND)
{
if((item1 != NULL && item2 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT))
doCommands(cmd.subBlock);
else if((item1 == NULL || item2 == NULL) && (cmd.flags & SBARINFOEVENT_NOT))
doCommands(cmd.subBlock);
}
else if(cmd.flags & SBARINFOEVENT_OR)
{
if((item1 != NULL || item2 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT))
doCommands(cmd.subBlock);
else if((item1 == NULL && item2 == NULL) && (cmd.flags & SBARINFOEVENT_NOT))
doCommands(cmd.subBlock);
}
else if((item1 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT))
doCommands(cmd.subBlock);
else if((item1 == NULL) && (cmd.flags & SBARINFOEVENT_NOT))
doCommands(cmd.subBlock);
break;
}
} }
} }
} }
@ -1102,15 +1159,21 @@ void DSBarInfo::DrawGraphic(FTexture* texture, int x, int y, int flags)
x -= (texture->GetWidth()/2)-texture->LeftOffset; x -= (texture->GetWidth()/2)-texture->LeftOffset;
y -= (texture->GetHeight()/2)-texture->TopOffset; y -= (texture->GetHeight()/2)-texture->TopOffset;
} }
x += ST_X;
y += ST_Y;
int w = texture->GetScaledWidth();
int h = texture->GetScaledHeight();
screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true);
if((flags & DRAWIMAGE_TRANSLATABLE)) if((flags & DRAWIMAGE_TRANSLATABLE))
DrawImage(texture, x, y, getTranslation()); {
screen->DrawTexture(texture, x, y,
DTA_DestWidth, w,
DTA_DestHeight, h,
DTA_Translation, getTranslation(),
TAG_DONE);
}
else else
{ {
x += ST_X;
y += ST_Y;
int w = texture->GetScaledWidth();
int h = texture->GetScaledHeight();
screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true);
screen->DrawTexture(texture, x, y, screen->DrawTexture(texture, x, y,
DTA_DestWidth, w, DTA_DestWidth, w,
DTA_DestHeight, h, DTA_DestHeight, h,
@ -1142,10 +1205,20 @@ void DSBarInfo::DrawString(const char* str, int x, int y, EColorRange translatio
} }
if(SBarInfoScript->spacingCharacter == '\0') //If we are monospaced lets use the offset if(SBarInfoScript->spacingCharacter == '\0') //If we are monospaced lets use the offset
x += (character->LeftOffset+1); //ignore x offsets since we adapt to character size x += (character->LeftOffset+1); //ignore x offsets since we adapt to character size
DrawImage(character, x, y, drawingFont->GetColorTranslation(translation)); int rx = x + ST_X;
x += width + spacing; int ry = y + ST_Y;
int rw = character->GetScaledWidth();
int rh = character->GetScaledHeight();
screen->VirtualToRealCoordsInt(rx, ry, rw, rh, 320, 200, true);
screen->DrawTexture(character, rx, ry,
DTA_DestWidth, rw,
DTA_DestHeight, rh,
DTA_Translation, drawingFont->GetColorTranslation(translation),
TAG_DONE);
if(SBarInfoScript->spacingCharacter == '\0') if(SBarInfoScript->spacingCharacter == '\0')
x -= (character->LeftOffset+1); x += width + spacing - (character->LeftOffset+1);
else //width gets changed at the call to GetChar()
x += drawingFont->GetCharWidth((int) SBarInfoScript->spacingCharacter) + spacing;
str++; str++;
} }
} }
@ -1165,14 +1238,23 @@ void DSBarInfo::DrawNumber(int num, int len, int x, int y, EColorRange translati
} }
//draws the mug shot //draws the mug shot
void DSBarInfo::DrawFace(int accuracy, bool xdth, bool animatedgodmode, int x, int y) void DSBarInfo::DrawFace(FString &defaultFace, int accuracy, bool xdth, bool animatedgodmode, int x, int y)
{ {
int angle = updateState(xdth, animatedgodmode); int angle = updateState(xdth, animatedgodmode);
int level = 0; int level = 0;
for(level = 0;CPlayer->health < (accuracy-level-1)*(CPlayer->mo->GetMaxHealth()/accuracy);level++); for(level = 0;CPlayer->health < (accuracy-level-1)*(CPlayer->mo->GetMaxHealth()/accuracy);level++);
if(currentState != NULL) if(currentState != NULL)
{ {
DrawImage(currentState->getCurrentFrameTexture(&skins[CPlayer->userinfo.skin], level, angle), x, y); FTexture *face = currentState->getCurrentFrameTexture(defaultFace, &skins[CPlayer->userinfo.skin], level, angle);
x += ST_X;
y += ST_Y;
int w = face->GetScaledWidth();
int h = face->GetScaledHeight();
screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true);
screen->DrawTexture(face, x, y,
DTA_DestWidth, w,
DTA_DestHeight, h,
TAG_DONE);
} }
} }

View file

@ -57,6 +57,7 @@ static const char *SBarInfoTopLevel[] =
"interpolatearmor", "interpolatearmor",
"completeborder", "completeborder",
"monospacefonts", "monospacefonts",
"lowerhealthcap",
"statusbar", "statusbar",
"mugshot", "mugshot",
NULL NULL
@ -91,7 +92,9 @@ static const char *SBarInfoRoutineLevel[] =
"drawkeybar", "drawkeybar",
"gamemode", "gamemode",
"playerclass", "playerclass",
"aspectratio",
"weaponammo", //event "weaponammo", //event
"ininventory",
NULL NULL
}; };
@ -230,6 +233,18 @@ void SBarInfo::ParseSBarInfo(int lump)
} }
sc.MustGetToken(';'); sc.MustGetToken(';');
break; break;
case SBARINFO_LOWERHEALTHCAP:
if(sc.CheckToken(TK_False))
{
lowerHealthCap = false;
}
else
{
sc.MustGetToken(TK_True);
lowerHealthCap = true;
}
sc.MustGetToken(';');
break;
case SBARINFO_STATUSBAR: case SBARINFO_STATUSBAR:
{ {
if(!baseSet) //If the user didn't explicitly define a base, do so now. if(!baseSet) //If the user didn't explicitly define a base, do so now.
@ -470,6 +485,14 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
cmd.flags += DRAWNUMBER_TOTALSECRETS; cmd.flags += DRAWNUMBER_TOTALSECRETS;
else if(sc.Compare("armorclass")) else if(sc.Compare("armorclass"))
cmd.flags += DRAWNUMBER_ARMORCLASS; cmd.flags += DRAWNUMBER_ARMORCLASS;
else if(sc.Compare("globalvar"))
{
cmd.flags += DRAWNUMBER_GLOBALVAR;
sc.MustGetToken(TK_IntConst);
if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS)
sc.ScriptError("Global variable number out of range: %d", sc.Number);
cmd.value = sc.Number;
}
else else
{ {
cmd.flags = DRAWNUMBER_INVENTORY; cmd.flags = DRAWNUMBER_INVENTORY;
@ -738,7 +761,7 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
cmd.flags = DRAWNUMBER_INVENTORY; cmd.flags = DRAWNUMBER_INVENTORY;
cmd.setString(sc, sc.String, 0); cmd.setString(sc, sc.String, 0);
const PClass* item = PClass::FindClass(sc.String); const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item)) //must be a kind of ammo if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item))
{ {
sc.ScriptError("'%s' is not a type of inventory item.", sc.String); sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
} }
@ -843,6 +866,11 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
cmd.setString(sc, sc.String, 0, -1, false); cmd.setString(sc, sc.String, 0, -1, false);
sc.MustGetToken(','); sc.MustGetToken(',');
this->getCoordinates(sc, cmd); this->getCoordinates(sc, cmd);
if(sc.CheckToken(',')) //spacing
{
sc.MustGetToken(TK_IntConst);
cmd.special = sc.Number;
}
sc.MustGetToken(';'); sc.MustGetToken(';');
break; break;
case SBARINFO_DRAWKEYBAR: case SBARINFO_DRAWKEYBAR:
@ -908,6 +936,21 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
FinishPlayerClass: FinishPlayerClass:
this->ParseSBarInfoBlock(sc, cmd.subBlock); this->ParseSBarInfoBlock(sc, cmd.subBlock);
break; break;
case SBARINFO_ASPECTRATIO:
sc.MustGetToken(TK_StringConst);
if(sc.Compare("4:3"))
cmd.value = ASPECTRATIO_4_3;
else if(sc.Compare("16:9"))
cmd.value = ASPECTRATIO_16_9;
else if(sc.Compare("16:10"))
cmd.value = ASPECTRATIO_16_10;
else if(sc.Compare("5:4"))
cmd.value = ASPECTRATIO_5_4;
else
sc.ScriptError("Unkown aspect ratio: %s", sc.String);
sc.MustGetToken('{');
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
case SBARINFO_WEAPONAMMO: case SBARINFO_WEAPONAMMO:
sc.MustGetToken(TK_Identifier); sc.MustGetToken(TK_Identifier);
if(sc.Compare("not")) if(sc.Compare("not"))
@ -939,6 +982,39 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
sc.MustGetToken('{'); sc.MustGetToken('{');
this->ParseSBarInfoBlock(sc, cmd.subBlock); this->ParseSBarInfoBlock(sc, cmd.subBlock);
break; break;
case SBARINFO_ININVENTORY:
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("not"))
{
cmd.flags += SBARINFOEVENT_NOT;
sc.MustGetToken(TK_Identifier);
}
for(int i = 0;i < 2;i++)
{
cmd.setString(sc, sc.String, i);
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item))
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
}
if(sc.CheckToken(TK_OrOr))
{
cmd.flags += SBARINFOEVENT_OR;
sc.MustGetToken(TK_Identifier);
}
else if(sc.CheckToken(TK_AndAnd))
{
cmd.flags += SBARINFOEVENT_AND;
sc.MustGetToken(TK_Identifier);
}
else
break;
}
sc.MustGetToken('{');
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
}
} }
block.commands.Push(cmd); block.commands.Push(cmd);
} }
@ -1044,6 +1120,7 @@ void SBarInfo::Init()
interpolateHealth = false; interpolateHealth = false;
interpolateArmor = false; interpolateArmor = false;
completeBorder = false; completeBorder = false;
lowerHealthCap = true;
interpolationSpeed = 8; interpolationSpeed = 8;
armorInterpolationSpeed = 8; armorInterpolationSpeed = 8;
height = 0; height = 0;

View file

@ -1536,9 +1536,14 @@ void DBaseStatusBar::FlashItem (const PClass *itemtype)
{ {
} }
void DBaseStatusBar::SetFace (void *) void DBaseStatusBar::SetFace (void *skn)
{ {
} }
void DBaseStatusBar::AddFaceToImageCollection (void *skn, FImageCollection *images)
{
AddFaceToImageCollectionActual (skn, images, false);
}
void DBaseStatusBar::NewGame () void DBaseStatusBar::NewGame ()
{ {
@ -1575,6 +1580,72 @@ void DBaseStatusBar::ScreenSizeChanged ()
} }
} }
//---------------------------------------------------------------------------
//
// AddFaceToImageCollectionActual
//
// Adds face graphics for specified skin to the specified image collection.
// If not in DOOM statusbar and no face in current skin, do NOT default STF*
//
//---------------------------------------------------------------------------
void DBaseStatusBar::AddFaceToImageCollectionActual (void *skn, FImageCollection *images, bool isDoom)
{
const char *nameptrs[ST_NUMFACES];
char names[ST_NUMFACES][9];
char prefix[4];
int i, j;
int namespc;
int facenum;
FPlayerSkin *skin = (FPlayerSkin *)skn;
if ((skin->face[0] == 0) && !isDoom)
{
return;
}
for (i = 0; i < ST_NUMFACES; i++)
{
nameptrs[i] = names[i];
}
if (skin->face[0] != 0)
{
prefix[0] = skin->face[0];
prefix[1] = skin->face[1];
prefix[2] = skin->face[2];
prefix[3] = 0;
namespc = skin->namespc;
}
else
{
prefix[0] = 'S';
prefix[1] = 'T';
prefix[2] = 'F';
prefix[3] = 0;
namespc = ns_global;
}
facenum = 0;
for (i = 0; i < ST_NUMPAINFACES; i++)
{
for (j = 0; j < ST_NUMSTRAIGHTFACES; j++)
{
sprintf (names[facenum++], "%sST%d%d", prefix, i, j);
}
sprintf (names[facenum++], "%sTR%d0", prefix, i); // turn right
sprintf (names[facenum++], "%sTL%d0", prefix, i); // turn left
sprintf (names[facenum++], "%sOUCH%d", prefix, i); // ouch!
sprintf (names[facenum++], "%sEVL%d", prefix, i); // evil grin ;)
sprintf (names[facenum++], "%sKILL%d", prefix, i); // pissed off
}
sprintf (names[facenum++], "%sGOD0", prefix);
sprintf (names[facenum++], "%sDEAD0", prefix);
images->Add (nameptrs, ST_NUMFACES, namespc);
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// ValidateInvFirst // ValidateInvFirst

View file

@ -81,6 +81,13 @@ public:
bool TryPickup (AActor *toucher); bool TryPickup (AActor *toucher);
}; };
class ASlideshowStarter : public ADummyStrifeItem
{
DECLARE_STATELESS_ACTOR (ASlideshowStarter, ADummyStrifeItem)
public:
bool TryPickup (AActor *toucher);
};
class AStrifeWeapon : public AWeapon class AStrifeWeapon : public AWeapon
{ {
DECLARE_STATELESS_ACTOR (AStrifeWeapon, AWeapon) DECLARE_STATELESS_ACTOR (AStrifeWeapon, AWeapon)

View file

@ -514,13 +514,6 @@ bool AUpgradeAccuracy::TryPickup (AActor *toucher)
// Start a slideshow -------------------------------------------------------- // Start a slideshow --------------------------------------------------------
class ASlideshowStarter : public ADummyStrifeItem
{
DECLARE_STATELESS_ACTOR (ASlideshowStarter, ADummyStrifeItem)
public:
bool TryPickup (AActor *toucher);
};
IMPLEMENT_STATELESS_ACTOR (ASlideshowStarter, Strife, -1, 0) IMPLEMENT_STATELESS_ACTOR (ASlideshowStarter, Strife, -1, 0)
PROP_StrifeType (343) PROP_StrifeType (343)
END_DEFAULTS END_DEFAULTS

View file

@ -692,7 +692,7 @@ CCMD(listlights)
ADynamicLight * dl; ADynamicLight * dl;
TThinkerIterator<ADynamicLight> it; TThinkerIterator<ADynamicLight> it;
while (dl=it.Next()) while ((dl=it.Next()))
{ {
walls=0; walls=0;
sectors=0; sectors=0;

View file

@ -71,8 +71,7 @@ void M_OptInit (void);
// [RH] Initialize the video modes menu // [RH] Initialize the video modes menu
void M_InitVideoModesMenu (void); void M_InitVideoModesMenu (void);
struct menu_s; void M_SwitchMenu (struct menu_t *menu);
void M_SwitchMenu (struct menu_s *menu);
void M_PopMenuStack (void); void M_PopMenuStack (void);
@ -100,6 +99,7 @@ typedef enum {
discrete, discrete,
discretes, discretes,
cdiscrete, cdiscrete,
ediscrete,
discrete_guid, discrete_guid,
control, control,
screenres, screenres,
@ -145,8 +145,9 @@ typedef struct menuitem_s {
char *res3; char *res3;
} d; } d;
union { union {
struct value_s *values; struct value_t *values;
struct valuestring_t *valuestrings; struct valuestring_t *valuestrings;
struct valueenum_t *enumvalues;
GUIDName *guidvalues; GUIDName *guidvalues;
char *command; char *command;
void (*cfunc)(FBaseCVar *cvar, float newval); void (*cfunc)(FBaseCVar *cvar, float newval);
@ -157,7 +158,7 @@ typedef struct menuitem_s {
} e; } e;
} menuitem_t; } menuitem_t;
typedef struct menu_s { struct menu_t {
const char *texttitle; const char *texttitle;
int lastOn; int lastOn;
int numitems; int numitems;
@ -169,18 +170,23 @@ typedef struct menu_s {
void (*PreDraw)(void); void (*PreDraw)(void);
bool DontDim; bool DontDim;
void (*EscapeHandler)(void); void (*EscapeHandler)(void);
} menu_t; };
typedef struct value_s { struct value_t {
float value; float value;
const char *name; const char *name;
} value_t; };
struct valuestring_t { struct valuestring_t {
float value; float value;
FString name; FString name;
}; };
struct valueenum_t {
const char *value; // Value of cvar
const char *name; // Name on menu
};
typedef struct typedef struct
{ {
// -1 = no cursor here, 1 = ok, 2 = arrows ok // -1 = no cursor here, 1 = ok, 2 = arrows ok

View file

@ -1144,62 +1144,131 @@ EXTERN_CVAR (Float, snd_movievolume)
#endif #endif
EXTERN_CVAR (Bool, snd_flipstereo) EXTERN_CVAR (Bool, snd_flipstereo)
EXTERN_CVAR (Bool, snd_pitched) EXTERN_CVAR (Bool, snd_pitched)
EXTERN_CVAR (String, snd_output_format)
EXTERN_CVAR (String, snd_speakermode)
EXTERN_CVAR (String, snd_resampler)
EXTERN_CVAR (String, snd_output)
EXTERN_CVAR (Int, snd_buffersize)
EXTERN_CVAR (Int, snd_buffercount)
EXTERN_CVAR (Int, snd_samplerate)
EXTERN_CVAR (Bool, snd_hrtf)
EXTERN_CVAR (Bool, snd_waterreverb)
EXTERN_CVAR (Int, snd_mididevice)
static void MakeSoundChanges (); static void MakeSoundChanges ();
static void AdvSoundOptions (); static void AdvSoundOptions ();
static void ChooseMIDI ();
static value_t SampleRates[] = static value_t SampleRates[] =
{ {
{ 4000.f, "4000 Hz" }, { 0.f, "Default" },
{ 8000.f, "8000 Hz" }, { 4000.f, "4000 Hz" },
{ 11025.f, "11025 Hz" }, { 8000.f, "8000 Hz" },
{ 22050.f, "22050 Hz" }, { 11025.f, "11025 Hz" },
{ 32000.f, "32000 Hz" }, { 22050.f, "22050 Hz" },
{ 44100.f, "44100 Hz" }, { 32000.f, "32000 Hz" },
{ 48000.f, "48000 Hz" }, { 44100.f, "44100 Hz" },
{ 96000.f, "96000 Hz" } { 48000.f, "48000 Hz" }
}; };
static value_t BufferSizes[] = static value_t BufferSizes[] =
{ {
{ 0.f, "Default" }, { 0.f, "Default" },
{ 20.f, "20 ms" }, { 64.f, "64 samples" },
{ 40.f, "40 ms" }, { 128.f, "128 samples" },
{ 60.f, "60 ms" }, { 256.f, "256 samples" },
{ 80.f, "80 ms" }, { 512.f, "512 samples" },
{ 100.f, "100 ms" }, { 1024.f, "1024 samples" },
{ 120.f, "120 ms" }, { 2048.f, "2048 samples" },
{ 140.f, "140 ms" }, { 4096.f, "4096 samples" }
{ 160.f, "160 ms" }, };
{ 180.f, "180 ms" },
{ 200.f, "200 ms" }, static value_t BufferCounts[] =
{
{ 0.f, "Default" },
{ 2.f, "2" },
{ 3.f, "3" },
{ 4.f, "4" },
{ 5.f, "5" },
{ 6.f, "6" },
{ 7.f, "7" },
{ 8.f, "8" },
{ 9.f, "9" },
{ 10.f, "10" },
{ 11.f, "11" },
{ 12.f, "12" }
};
static valueenum_t Outputs[] =
{
{ "Default", "Default" },
#if defined(_WIN32)
{ "DirectSound", "DirectSound" },
{ "WASAPI", "Vista WASAPI" },
{ "ASIO", "ASIO" },
{ "WaveOut", "WaveOut" },
{ "OpenAL", "OpenAL (very beta)" },
#elif defined(unix)
{ "OSS", "OSS" },
{ "ALSA", "ALSA" },
{ "ESD", "ESD" },
#elif defined(__APPLE__)
{ "Sound Manager", "Sound Manager" },
{ "Core Audio", "Core Audio" },
#endif
{ "No sound", "No sound" }
};
static valueenum_t OutputFormats[] =
{
{ "PCM-8", "8-bit" },
{ "PCM-16", "16-bit" },
{ "PCM-24", "24-bit" },
{ "PCM-32", "32-bit" },
{ "PCM-Float", "32-bit float" }
};
static valueenum_t SpeakerModes[] =
{
{ "Auto", "Auto" },
{ "Mono", "Mono" },
{ "Stereo", "Stereo" },
{ "Prologic", "Dolby Prologic Decoder" },
{ "Quad", "Quad" },
{ "Surround", "5 speakers" },
{ "5.1", "5.1 speakers" },
{ "7.1", "7.1 speakers" }
};
static valueenum_t Resamplers[] =
{
{ "NoInterp", "No interpolation" },
{ "Linear", "Linear" },
{ "Cubic", "Cubic" },
{ "Spline", "Spline" }
}; };
static menuitem_t SoundItems[] = static menuitem_t SoundItems[] =
{ {
{ slider, "Sound effects volume", {&snd_sfxvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, { slider, "Sounds volume", {&snd_sfxvolume}, {0.0}, {1.0}, {0.05}, {NULL} },
#ifdef _WIN32
{ slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, { slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05}, {NULL} },
{ slider, "Movie volume", {&snd_movievolume}, {0.0}, {1.0}, {0.05}, {NULL} }, { discrete, "MIDI device", {&snd_mididevice}, {0.0}, {0.0}, {0.0}, {NULL} },
#else
{ slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05}, {NULL} },
#endif
{ redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
{ discrete, "Underwater Reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, { discrete, "Underwater reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} },
{ discrete, "Flip Stereo Channels", {&snd_flipstereo}, {2.0}, {0.0}, {0.0}, {OnOff} }, { discrete, "Randomize pitches", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} },
{ discrete, "Random Pitch Variations", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} },
{ redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
{ more, "Activate below settings", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} }, { more, "Restart sound", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} },
{ redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
{ discrete, "Sample Rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} }, { ediscrete,"Output system", {&snd_output}, {countof(Outputs)}, {0.0}, {0.0}, {(value_t *)Outputs} },
{ discrete, "Buffer Size", {&snd_buffersize}, {11.0}, {0.0}, {0.0}, {BufferSizes} }, { ediscrete,"Output format", {&snd_output_format}, {5.0}, {0.0}, {0.0}, {(value_t *)OutputFormats} },
{ ediscrete,"Speaker mode", {&snd_speakermode}, {8.0}, {0.0}, {0.0}, {(value_t *)SpeakerModes} },
{ ediscrete,"Resampler", {&snd_resampler}, {4.0}, {0.0}, {0.0}, {(value_t *)Resamplers} },
{ discrete, "HRTF filter", {&snd_hrtf}, {2.0}, {0.0}, {0.0}, {(value_t *)OnOff} },
{ discrete, "Sample rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} },
{ discrete, "Buffer size", {&snd_buffersize}, {8.0}, {0.0}, {0.0}, {BufferSizes} },
{ discrete, "Buffer count", {&snd_buffercount}, {12.0}, {0.0}, {0.0}, {BufferCounts} },
{ redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} },
{ more, "Advanced Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} }, { more, "Advanced options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} },
#ifdef _WIN32
{ more, "Select MIDI Device", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)ChooseMIDI} },
#endif
}; };
static menu_t SoundMenu = static menu_t SoundMenu =
@ -1211,29 +1280,7 @@ static menu_t SoundMenu =
SoundItems, SoundItems,
}; };
#ifdef _WIN32 #define MIDI_DEVICE_ITEM 2
/*=======================================
*
* MIDI Device Menu
*
*=======================================*/
EXTERN_CVAR (Int, snd_mididevice)
static menuitem_t MidiDeviceItems[] =
{
{ discrete, "Device", {&snd_mididevice}, {0.0}, {0.0}, {0.0}, {NULL} },
};
static menu_t MidiDeviceMenu =
{
"SELECT MIDI DEVICE",
0,
1,
0,
MidiDeviceItems,
};
#endif
/*======================================= /*=======================================
* *
@ -1521,6 +1568,42 @@ int M_FindCurGUID (const GUID &guid, GUIDName *values, int numvals)
return v; return v;
} }
const char *M_FindCurVal(const char *cur, valueenum_t *values, int numvals)
{
for (int v = 0; v < numvals; ++v)
{
if (stricmp(values[v].value, cur) == 0)
{
return values[v].name;
}
}
return cur;
}
const char *M_FindPrevVal(const char *cur, valueenum_t *values, int numvals)
{
for (int v = 0; v < numvals; ++v)
{
if (stricmp(values[v].value, cur) == 0)
{
return values[v == 0 ? numvals - 1 : v - 1].value;
}
}
return values[0].value;
}
const char *M_FindNextVal(const char *cur, valueenum_t *values, int numvals)
{
for (int v = 0; v < numvals; ++v)
{
if (stricmp(values[v].value, cur) == 0)
{
return values[v == numvals - 1 ? 0 : v + 1].value;
}
}
return values[0].value;
}
void M_OptDrawer () void M_OptDrawer ()
{ {
EColorRange color; EColorRange color;
@ -1707,6 +1790,16 @@ void M_OptDrawer ()
} }
break; break;
case ediscrete:
{
const char *v;
value = item->a.cvar->GetGenericRep (CVAR_String);
v = M_FindCurVal(value.String, item->e.enumvalues, (int)item->b.numvalues);
screen->DrawText(ValueColor, CurrentMenu->indent + 14, y, v, DTA_Clean, true, TAG_DONE);
}
break;
case discrete_guid: case discrete_guid:
{ {
int v, vals; int v, vals;
@ -2186,6 +2279,13 @@ void M_OptResponder (event_t *ev)
S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE);
break; break;
case ediscrete:
value = item->a.cvar->GetGenericRep(CVAR_String);
value.String = const_cast<char *>(M_FindPrevVal(value.String, item->e.enumvalues, (int)item->b.numvalues));
item->a.cvar->SetGenericRep(value, CVAR_String);
S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE);
break;
case bitmask: case bitmask:
{ {
int cur; int cur;
@ -2328,6 +2428,13 @@ void M_OptResponder (event_t *ev)
S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE);
break; break;
case ediscrete:
value = item->a.cvar->GetGenericRep(CVAR_String);
value.String = const_cast<char *>(M_FindNextVal(value.String, item->e.enumvalues, (int)item->b.numvalues));
item->a.cvar->SetGenericRep(value, CVAR_String);
S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE);
break;
case bitmask: case bitmask:
{ {
int cur; int cur;
@ -2893,9 +3000,19 @@ CCMD (menu_joystick)
JoystickOptions (); JoystickOptions ();
} }
static void FreeMIDIMenuList()
{
if (SoundItems[MIDI_DEVICE_ITEM].e.values != NULL)
{
delete[] SoundItems[MIDI_DEVICE_ITEM].e.values;
}
}
static void SoundOptions () static void SoundOptions ()
{ {
M_SwitchMenu (&SoundMenu); I_BuildMIDIMenuList(&SoundItems[MIDI_DEVICE_ITEM].e.values, &SoundItems[MIDI_DEVICE_ITEM].b.min);
atterm(FreeMIDIMenuList);
M_SwitchMenu(&SoundMenu);
} }
CCMD (menu_sound) CCMD (menu_sound)
@ -2917,22 +3034,6 @@ CCMD (menu_advsound)
AdvSoundOptions (); AdvSoundOptions ();
} }
#ifdef _WIN32
static void ChooseMIDI ()
{
I_BuildMIDIMenuList (&MidiDeviceItems[0].e.values,
&MidiDeviceItems[0].b.min);
M_SwitchMenu (&MidiDeviceMenu);
}
CCMD (menu_mididevice)
{
M_StartControlPanel (true);
OptionsActive = true;
ChooseMIDI ();
}
#endif
static void MakeSoundChanges (void) static void MakeSoundChanges (void)
{ {
static char snd_reset[] = "snd_reset"; static char snd_reset[] = "snd_reset";

View file

@ -93,7 +93,7 @@ static size_t ReadVarLen (const BYTE *buf, int *time_out)
return ofs; return ofs;
} }
static size_t WriteVarLen (FILE *file, int time) static size_t WriteVarLen (TArray<BYTE> &file, int time)
{ {
long buffer; long buffer;
size_t ofs; size_t ofs;
@ -105,7 +105,7 @@ static size_t WriteVarLen (FILE *file, int time)
} }
for (ofs = 0;;) for (ofs = 0;;)
{ {
fputc (buffer & 0xff, file); file.Push(BYTE(buffer & 0xff));
if (buffer & 0x80) if (buffer & 0x80)
buffer >>= 8; buffer >>= 8;
else else
@ -114,7 +114,7 @@ static size_t WriteVarLen (FILE *file, int time)
return ofs; return ofs;
} }
bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) bool ProduceMIDI (const BYTE *musBuf, TArray<BYTE> &outFile)
{ {
BYTE midStatus, midArgs, mid1, mid2; BYTE midStatus, midArgs, mid1, mid2;
size_t mus_p, maxmus_p; size_t mus_p, maxmus_p;
@ -125,7 +125,6 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile)
BYTE lastVel[16]; BYTE lastVel[16];
SBYTE chanMap[16]; SBYTE chanMap[16];
int chanCount; int chanCount;
int dupCount = 0;
long trackLen; long trackLen;
// Do some validation of the MUS file // Do some validation of the MUS file
@ -136,7 +135,9 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile)
return false; return false;
// Prep for conversion // Prep for conversion
fwrite (StaticMIDIhead, 1, sizeof(StaticMIDIhead), outFile); outFile.Clear();
outFile.Reserve(sizeof(StaticMIDIhead));
memcpy(&outFile[0], StaticMIDIhead, sizeof(StaticMIDIhead));
musBuf += LittleShort(musHead->SongStart); musBuf += LittleShort(musHead->SongStart);
maxmus_p = LittleShort(musHead->SongLen); maxmus_p = LittleShort(musHead->SongLen);
@ -167,10 +168,10 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile)
{ {
// This is the first time this channel has been used, // This is the first time this channel has been used,
// so sets its volume to 127. // so sets its volume to 127.
fputc (0, outFile); outFile.Push(0);
fputc (0xB0 | chanCount, outFile); outFile.Push(0xB0 | chanCount);
fputc (7, outFile); outFile.Push(7);
fputc (127, outFile); outFile.Push(127);
chanMap[channel] = chanCount++; chanMap[channel] = chanCount++;
if (chanCount == 9) if (chanCount == 9)
++chanCount; ++chanCount;
@ -237,20 +238,15 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile)
WriteVarLen (outFile, deltaTime); WriteVarLen (outFile, deltaTime);
if (midStatus == status) if (midStatus != status)
{
++dupCount;
fputc (mid1, outFile);
if (!midArgs)
fputc (mid2, outFile);
}
else
{ {
status = midStatus; status = midStatus;
fputc (status, outFile); outFile.Push(status);
fputc (mid1, outFile); }
if (!midArgs) outFile.Push(mid1);
fputc (mid2, outFile); if (midArgs == 0)
{
outFile.Push(mid2);
} }
if (event & 128) if (event & 128)
{ {
@ -263,12 +259,20 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile)
} }
// fill in track length // fill in track length
trackLen = ftell (outFile) - 22; trackLen = outFile.Size() - 22;
fseek (outFile, 18, SEEK_SET); outFile[18] = BYTE((trackLen >> 24) & 255);
fputc ((trackLen >> 24) & 255, outFile); outFile[19] = BYTE((trackLen >> 16) & 255);
fputc ((trackLen >> 16) & 255, outFile); outFile[20] = BYTE((trackLen >> 8) & 255);
fputc ((trackLen >> 8) & 255, outFile); outFile[21] = BYTE(trackLen & 255);
fputc (trackLen & 255, outFile);
return true; return true;
} }
bool ProduceMIDI(const BYTE *musBuf, FILE *outFile)
{
TArray<BYTE> work;
if (ProduceMIDI(musBuf, work))
{
return fwrite(&work[0], 1, work.Size(), outFile) == work.Size();
}
return false;
}

View file

@ -73,6 +73,7 @@ typedef struct
WORD Pad; WORD Pad;
} MUSHeader; } MUSHeader;
bool ProduceMIDI (const BYTE *musBuf, TArray<BYTE> &outFile);
bool ProduceMIDI (const BYTE *musBuf, FILE *outFile); bool ProduceMIDI (const BYTE *musBuf, FILE *outFile);
#endif //__MUS2MIDI_H__ #endif //__MUS2MIDI_H__

View file

@ -117,9 +117,8 @@ static void ConversationMenuEscaped ();
static FStrifeDialogueNode *CurNode, *PrevNode; static FStrifeDialogueNode *CurNode, *PrevNode;
static FBrokenLines *DialogueLines; static FBrokenLines *DialogueLines;
static AActor *ConversationNPC, *ConversationPC;
static angle_t ConversationNPCAngle; static bool Conversation_TakeStuff;
static bool ConversationFaceTalker;
#define NUM_RANDOM_LINES 10 #define NUM_RANDOM_LINES 10
#define NUM_RANDOM_GOODBYES 3 #define NUM_RANDOM_GOODBYES 3
@ -571,14 +570,14 @@ static int FindNode (const FStrifeDialogueNode *node)
// //
//============================================================================ //============================================================================
static bool CheckStrifeItem (const PClass *itemtype, int amount=-1) static bool CheckStrifeItem (player_t *player, const PClass *itemtype, int amount=-1)
{ {
AInventory *item; AInventory *item;
if (itemtype == NULL || amount == 0) if (itemtype == NULL || amount == 0)
return true; return true;
item = ConversationPC->FindInventory (itemtype); item = player->ConversationPC->FindInventory (itemtype);
if (item == NULL) if (item == NULL)
return false; return false;
@ -594,7 +593,7 @@ static bool CheckStrifeItem (const PClass *itemtype, int amount=-1)
// //
//============================================================================ //============================================================================
static void TakeStrifeItem (const PClass *itemtype, int amount) static void TakeStrifeItem (player_t *player, const PClass *itemtype, int amount)
{ {
if (itemtype == NULL || amount == 0) if (itemtype == NULL || amount == 0)
return; return;
@ -611,15 +610,10 @@ static void TakeStrifeItem (const PClass *itemtype, int amount)
if (itemtype == RUNTIME_CLASS(ASigil)) if (itemtype == RUNTIME_CLASS(ASigil))
return; return;
AInventory *item = ConversationPC->FindInventory (itemtype); Net_WriteByte (DEM_CONVERSATION);
if (item != NULL) Net_WriteByte (CONV_TAKEINVENTORY);
{ Net_WriteString (itemtype->TypeName.GetChars ());
item->Amount -= amount; Net_WriteWord (amount);
if (item->Amount <= 0)
{
item->Destroy ();
}
}
} }
CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
@ -633,7 +627,6 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
// P_StartConversation // P_StartConversation
// //
// Begins a conversation between a PC and NPC. // Begins a conversation between a PC and NPC.
// FIXME: Make this work in multiplayer.
// //
//============================================================================ //============================================================================
@ -645,14 +638,22 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
const char *toSay; const char *toSay;
int i, j; int i, j;
// [CW] If an NPC is talking to a PC already, then don't let
// anyone else talk to NPC.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || pc->player == &players[i])
continue;
if (npc == players[i].ConversationNPC)
return;
}
pc->momx = pc->momy = 0; // Stop moving pc->momx = pc->momy = 0; // Stop moving
pc->player->momx = pc->player->momy = 0; pc->player->momx = pc->player->momy = 0;
if (pc->player - players != consoleplayer) pc->player->ConversationPC = pc;
return; pc->player->ConversationNPC = npc;
ConversationPC = pc;
ConversationNPC = npc;
CurNode = npc->Conversation; CurNode = npc->Conversation;
@ -662,10 +663,10 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
} }
npc->reactiontime = 2; npc->reactiontime = 2;
ConversationFaceTalker = facetalker; pc->player->ConversationFaceTalker = facetalker;
if (saveangle) if (saveangle)
{ {
ConversationNPCAngle = npc->angle; pc->player->ConversationNPCAngle = npc->angle;
} }
oldtarget = npc->target; oldtarget = npc->target;
npc->target = pc; npc->target = pc;
@ -682,11 +683,11 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
// Check if we should jump to another node // Check if we should jump to another node
while (CurNode->ItemCheck[0] != NULL) while (CurNode->ItemCheck[0] != NULL)
{ {
if (CheckStrifeItem (CurNode->ItemCheck[0]) && if (CheckStrifeItem (pc->player, CurNode->ItemCheck[0]) &&
CheckStrifeItem (CurNode->ItemCheck[1]) && CheckStrifeItem (pc->player, CurNode->ItemCheck[1]) &&
CheckStrifeItem (CurNode->ItemCheck[2])) CheckStrifeItem (pc->player, CurNode->ItemCheck[2]))
{ {
int root = FindNode (ConversationNPC->GetDefault()->Conversation); int root = FindNode (pc->player->ConversationNPC->GetDefault()->Conversation);
CurNode = StrifeDialogues[root + CurNode->ItemCheckNode - 1]; CurNode = StrifeDialogues[root + CurNode->ItemCheckNode - 1];
} }
else else
@ -697,10 +698,13 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
if (CurNode->SpeakerVoice != 0) if (CurNode->SpeakerVoice != 0)
{ {
I_SetMusicVolume(dlg_musicvolume); I_SetMusicVolume (dlg_musicvolume);
S_SoundID (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM); S_SoundID (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM);
} }
if (pc->player != &players[consoleplayer])
return;
// Set up the menu // Set up the menu
ConversationMenu.PreDraw = DrawConversationMenu; ConversationMenu.PreDraw = DrawConversationMenu;
ConversationMenu.EscapeHandler = ConversationMenuEscaped; ConversationMenu.EscapeHandler = ConversationMenuEscaped;
@ -786,9 +790,17 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
void P_ResumeConversation () void P_ResumeConversation ()
{ {
if (ConversationPC != NULL && ConversationNPC != NULL) for (int i = 0; i < MAXPLAYERS; i++)
{ {
P_StartConversation (ConversationNPC, ConversationPC, ConversationFaceTalker, false); if (!playeringame[i])
continue;
player_t *p = &players[i];
if (p->ConversationPC != NULL && p->ConversationNPC != NULL)
{
P_StartConversation (p->ConversationNPC, p->ConversationPC, p->ConversationFaceTalker, false);
}
} }
} }
@ -803,6 +815,8 @@ static void DrawConversationMenu ()
const char *speakerName; const char *speakerName;
int i, x, y, linesize; int i, x, y, linesize;
player_t *cp = &players[consoleplayer];
assert (DialogueLines != NULL); assert (DialogueLines != NULL);
assert (CurNode != NULL); assert (CurNode != NULL);
@ -812,7 +826,8 @@ static void DrawConversationMenu ()
return; return;
} }
if (ConversationPauseTic < gametic) // [CW] Pausing the game in a multiplayer game is a bad idea.
if (ConversationPauseTic < gametic && !multiplayer)
{ {
menuactive = MENU_On; menuactive = MENU_On;
} }
@ -832,7 +847,7 @@ static void DrawConversationMenu ()
} }
else else
{ {
speakerName = ConversationNPC->GetClass()->Meta.GetMetaString (AMETA_StrifeName); speakerName = cp->ConversationNPC->GetClass()->Meta.GetMetaString (AMETA_StrifeName);
if (speakerName == NULL) if (speakerName == NULL)
{ {
speakerName = "Person"; speakerName = "Person";
@ -873,7 +888,7 @@ static void DrawConversationMenu ()
if (ShowGold) if (ShowGold)
{ {
AInventory *coin = ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin));
char goldstr[32]; char goldstr[32];
sprintf (goldstr, "%d", coin != NULL ? coin->Amount : 0); sprintf (goldstr, "%d", coin != NULL ? coin->Amount : 0);
@ -892,94 +907,89 @@ static void DrawConversationMenu ()
// //
// PickConversationReply // PickConversationReply
// //
// FIXME: Make this work in multiplayer
//
//============================================================================ //============================================================================
static void PickConversationReply () static void PickConversationReply ()
{ {
const char *replyText = NULL; const char *replyText = NULL;
FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra; FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra;
bool takestuff;
int i; int i;
player_t *cp = &players[consoleplayer];
Conversation_TakeStuff = false;
M_ClearMenus (); M_ClearMenus ();
CleanupConversationMenu (); CleanupConversationMenu ();
if (reply == NULL) if (reply == NULL)
{ {
ConversationNPC->angle = ConversationNPCAngle; Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_NPCANGLE);
return; return;
} }
// Check if you have the requisite items for this choice // Check if you have the requisite items for this choice
for (i = 0; i < 3; ++i) for (i = 0; i < 3; ++i)
{ {
if (!CheckStrifeItem (reply->ItemCheck[i], reply->ItemCheckAmount[i])) if (!CheckStrifeItem (cp, reply->ItemCheck[i], reply->ItemCheckAmount[i]))
{ {
// No, you don't. Say so and let the NPC animate negatively. // No, you don't. Say so and let the NPC animate negatively.
if (reply->QuickNo) if (reply->QuickNo)
{ {
Printf ("%s\n", reply->QuickNo); Printf ("%s\n", reply->QuickNo);
} }
ConversationNPC->ConversationAnimation (2); Net_WriteByte (DEM_CONVERSATION);
ConversationNPC->angle = ConversationNPCAngle; Net_WriteByte (CONV_ANIMATE);
Net_WriteByte (2);
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_NPCANGLE);
return; return;
} }
} }
// Yay, you do! Let the NPC animate affirmatively. // Yay, you do! Let the NPC animate affirmatively.
ConversationNPC->ConversationAnimation (1); Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_ANIMATE);
Net_WriteByte (1);
// If this reply gives you something, then try to receive it. // If this reply gives you something, then try to receive it.
takestuff = true; Conversation_TakeStuff = true;
if (reply->GiveType != NULL) if (reply->GiveType != NULL)
{ {
if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AInventory))) if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{ {
if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AWeapon))) if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{ {
if (players[consoleplayer].mo->FindInventory(reply->GiveType) != NULL) if (cp->mo->FindInventory(reply->GiveType) != NULL)
{ {
takestuff = false; Conversation_TakeStuff = false;
} }
} }
if (takestuff) if (Conversation_TakeStuff)
{ {
AInventory *item = static_cast<AInventory *> (Spawn (reply->GiveType, 0, 0, 0, NO_REPLACE)); Net_WriteByte (DEM_CONVERSATION);
// Items given here should not count as items! Net_WriteByte (CONV_GIVEINVENTORY);
if (item->flags & MF_COUNTITEM) Net_WriteString (reply->GiveType->TypeName.GetChars ());
{
level.total_items--;
item->flags &= ~MF_COUNTITEM;
}
if (item->IsA(RUNTIME_CLASS(AFlameThrower)))
{
// The flame thrower gives less ammo when given in a dialog
static_cast<AWeapon*>(item)->AmmoGive1 = 40;
}
item->flags |= MF_DROPPED;
if (!item->TryPickup (players[consoleplayer].mo))
{
item->Destroy ();
takestuff = false;
}
} }
if (reply->GiveType->IsDescendantOf (RUNTIME_CLASS (ASlideshowStarter)))
gameaction = ga_slideshow;
} }
else else
{ {
// Trying to give a non-inventory item. // Trying to give a non-inventory item.
takestuff = false; Conversation_TakeStuff = false;
Printf("Attempting to give non-inventory item %s\n", reply->GiveType->TypeName.GetChars()); Printf("Attempting to give non-inventory item %s\n", reply->GiveType->TypeName.GetChars());
} }
} }
// Take away required items if the give was successful or none was needed. // Take away required items if the give was successful or none was needed.
if (takestuff) if (Conversation_TakeStuff)
{ {
for (i = 0; i < 3; ++i) for (i = 0; i < 3; ++i)
{ {
TakeStrifeItem (reply->ItemCheck[i], reply->ItemCheckAmount[i]); TakeStrifeItem (&players[consoleplayer], reply->ItemCheck[i], reply->ItemCheckAmount[i]);
} }
replyText = reply->QuickYes; replyText = reply->QuickYes;
} }
@ -989,9 +999,9 @@ static void PickConversationReply ()
} }
// Update the quest log, if needed. // Update the quest log, if needed.
if (reply->LogNumber != 0) if (reply->LogNumber != 0)
{ {
players[consoleplayer].SetLogNumber (reply->LogNumber); cp->SetLogNumber (reply->LogNumber);
} }
if (replyText != NULL) if (replyText != NULL)
@ -1000,32 +1010,43 @@ static void PickConversationReply ()
} }
// Does this reply alter the speaker's conversation node? If NextNode is positive, // Does this reply alter the speaker's conversation node? If NextNode is positive,
// the next time they talk, the will show the new node. If it is negative, then they // the next time they talk, they will show the new node. If it is negative, then they
// will show the new node right away without terminating the dialogue. // will show the new node right away without terminating the dialogue.
if (reply->NextNode != 0) if (reply->NextNode != 0)
{ {
int rootnode = FindNode (ConversationNPC->GetDefault()->Conversation); int rootnode = FindNode (cp->ConversationNPC->GetDefault()->Conversation);
if (reply->NextNode < 0) if (reply->NextNode < 0)
{ {
ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; cp->ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1];
if (gameaction != ga_slideshow) if (gameaction != ga_slideshow)
{ {
P_StartConversation (ConversationNPC, players[consoleplayer].mo, ConversationFaceTalker, false); P_StartConversation (cp->ConversationNPC, cp->mo, cp->ConversationFaceTalker, false);
return; return;
} }
else else
{ {
S_StopSound (ConversationNPC, CHAN_VOICE); S_StopSound (cp->ConversationNPC, CHAN_VOICE);
} }
} }
else else
{ {
ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; cp->ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1];
} }
} }
ConversationNPC->angle = ConversationNPCAngle; Net_WriteByte (DEM_CONVERSATION);
I_SetMusicVolume(1.f); Net_WriteByte (CONV_NPCANGLE);
// [CW] Set these to NULL because we're not talking to them
// anymore. However, this can interfere with slideshows so
// we don't set them to NULL in that case.
if (gameaction != ga_slideshow)
{
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_SETNULL);
}
I_SetMusicVolume (1.f);
} }
//============================================================================ //============================================================================
@ -1058,19 +1079,96 @@ void CleanupConversationMenu ()
DialogueLines = NULL; DialogueLines = NULL;
} }
ConversationItems.Clear (); ConversationItems.Clear ();
I_SetMusicVolume(1.f); I_SetMusicVolume (1.f);
} }
//============================================================================ //============================================================================
// //
// ConversationMenuEscaped // ConversationMenuEscaped
// //
// Called when the user presses escape to leave tho conversation menu. // Called when the user presses escape to leave the conversation menu.
// //
//============================================================================ //============================================================================
void ConversationMenuEscaped () void ConversationMenuEscaped ()
{ {
CleanupConversationMenu (); CleanupConversationMenu ();
ConversationNPC->angle = ConversationNPCAngle;
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_NPCANGLE);
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_SETNULL);
}
//============================================================================
//
// P_ConversationCommand
//
// Complete a conversation command.
//
//============================================================================
void P_ConversationCommand (int player, BYTE **stream)
{
int type = ReadByte (stream);
switch (type)
{
case CONV_NPCANGLE:
players[player].ConversationNPC->angle = players[player].ConversationNPCAngle;
break;
case CONV_ANIMATE:
players[player].ConversationNPC->ConversationAnimation (ReadByte (stream));
break;
case CONV_GIVEINVENTORY:
{
AInventory *item = static_cast<AInventory *> (Spawn (ReadString (stream), 0, 0, 0, NO_REPLACE));
// Items given here should not count as items!
if (item->flags & MF_COUNTITEM)
{
level.total_items--;
item->flags &= ~MF_COUNTITEM;
}
if (item->IsA(RUNTIME_CLASS(AFlameThrower)))
{
// The flame thrower gives less ammo when given in a dialog
static_cast<AWeapon*>(item)->AmmoGive1 = 40;
}
item->flags |= MF_DROPPED;
if (!item->TryPickup (players[player].mo))
{
item->Destroy ();
Conversation_TakeStuff = false;
}
}
break;
case CONV_TAKEINVENTORY:
{
AInventory *item = players[player].ConversationPC->FindInventory (PClass::FindClass (ReadString (stream)));
if (item != NULL)
{
item->Amount -= ReadWord (stream);
if (item->Amount <= 0)
{
item->Destroy ();
}
}
}
break;
case CONV_SETNULL:
players[player].ConversationFaceTalker = NULL;
players[player].ConversationNPC = NULL;
players[player].ConversationPC = NULL;
players[player].ConversationNPCAngle = NULL;
break;
default:
break;
}
} }

View file

@ -5,8 +5,7 @@
// users can edit as simple text files. Particularly useful would be // users can edit as simple text files. Particularly useful would be
// the ability to call ACS functions to implement AppearsWhen properties // the ability to call ACS functions to implement AppearsWhen properties
// and ACS scripts to implement ActionTaken properties. // and ACS scripts to implement ActionTaken properties.
// TODO: Make this work in multiplayer and in demos. Multiplayer probably // TODO: Make this work in demos.
// isn't possible for Strife conversations, but demo playback should be.
struct FStrifeDialogueReply; struct FStrifeDialogueReply;
class FTexture; class FTexture;
@ -48,6 +47,16 @@ struct FStrifeDialogueReply
FBrokenLines *ReplyLines; FBrokenLines *ReplyLines;
}; };
// [CW] These are used to make conversations work.
enum
{
CONV_NPCANGLE,
CONV_ANIMATE,
CONV_GIVEINVENTORY,
CONV_TAKEINVENTORY,
CONV_SETNULL,
};
extern TArray<FStrifeDialogueNode *> StrifeDialogues; extern TArray<FStrifeDialogueNode *> StrifeDialogues;
// There were 344 types in Strife, and Strife conversations refer // There were 344 types in Strife, and Strife conversations refer
@ -62,4 +71,6 @@ void P_FreeStrifeConversations ();
void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle); void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle);
void P_ResumeConversation (); void P_ResumeConversation ();
void P_ConversationCommand (int player, BYTE **stream);
#endif #endif

View file

@ -370,7 +370,8 @@ FSwitchDef *ParseSwitchDef (FScanner &sc, bool ignoreBad)
thisframe.Time = ((max - min + 1) << 16) | min; thisframe.Time = ((max - min + 1) << 16) | min;
} }
else else
{ {
thisframe.Time = 0; // Shush, GCC.
sc.ScriptError ("Must specify a duration for switch frame"); sc.ScriptError ("Must specify a duration for switch frame");
} }
frames.Push(thisframe); frames.Push(thisframe);

View file

@ -287,7 +287,11 @@ player_s::player_s()
crouchdir(0), crouchdir(0),
crouchfactor(0), crouchfactor(0),
crouchoffset(0), crouchoffset(0),
crouchviewdelta(0) crouchviewdelta(0),
ConversationNPC(0),
ConversationPC(0),
ConversationNPCAngle(0),
ConversationFaceTalker(0)
{ {
memset (&cmd, 0, sizeof(cmd)); memset (&cmd, 0, sizeof(cmd));
memset (&userinfo, 0, sizeof(userinfo)); memset (&userinfo, 0, sizeof(userinfo));
@ -321,6 +325,8 @@ size_t player_s::FixPointers (const DObject *old, DObject *rep)
if (last_mate == old) last_mate = replacement, changed++; if (last_mate == old) last_mate = replacement, changed++;
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++;
if (ConversationNPC == old) ConversationNPC = replacement, changed++;
if (ConversationPC == old) ConversationPC = replacement, changed++;
return changed; return changed;
} }
@ -337,6 +343,8 @@ size_t player_s::PropagateMark()
GC::Mark(mate); GC::Mark(mate);
GC::Mark(last_mate); GC::Mark(last_mate);
GC::Mark(ReadyWeapon); GC::Mark(ReadyWeapon);
GC::Mark(ConversationNPC);
GC::Mark(ConversationPC);
if (PendingWeapon != WP_NOCHANGE) if (PendingWeapon != WP_NOCHANGE)
{ {
GC::Mark(PendingWeapon); GC::Mark(PendingWeapon);
@ -2426,7 +2434,11 @@ void player_s::Serialize (FArchive &arc)
<< BlendB << BlendB
<< BlendA << BlendA
<< accuracy << stamina << accuracy << stamina
<< LogText; << LogText
<< ConversationNPC
<< ConversationPC
<< ConversationNPCAngle
<< ConversationFaceTalker;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
arc << frags[i]; arc << frags[i];

View file

@ -343,7 +343,7 @@ int P_TranslateSectorSpecial (int special)
} }
} }
if (special>=0 && special<SectorTranslations.Size()) if ((unsigned)special < SectorTranslations.Size())
{ {
if (SectorTranslations[special].bitmask_allowed && mask) special = 0; if (SectorTranslations[special].bitmask_allowed && mask) special = 0;
else special = SectorTranslations[special].newtype; else special = SectorTranslations[special].newtype;

View file

@ -193,8 +193,8 @@ void R_InitPicAnims (void)
if (debuganimated) if (debuganimated)
{ {
Printf("Defining animation '%s' (texture %d, lump %d, file %d) to '%s' (texture %d, lump %d, file %d)\n", Printf("Defining animation '%s' (texture %d, lump %d, file %d) to '%s' (texture %d, lump %d, file %d)\n",
tex1->Name, tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), tex1->Name, pic1, tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()),
tex2->Name, tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); tex2->Name, pic2, tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump()));
} }
/* FIXME: doesn't work with hires texture replacements. /* FIXME: doesn't work with hires texture replacements.

View file

@ -61,4 +61,5 @@ void restoreinterpolations();
void clearinterpolations(); void clearinterpolations();
void SerializeInterpolations(FArchive &arc); void SerializeInterpolations(FArchive &arc);
#endif #endif

View file

@ -1261,10 +1261,11 @@ static void S_AddSNDINFO (int lump)
sc.MustGetString(); sc.MustGetString();
FName nm = sc.String; FName nm = sc.String;
sc.MustGetString(); sc.MustGetString();
if (sc.Compare("timidity")) MidiDevices[nm] = 1; if (sc.Compare("timidity")) MidiDevices[nm] = MDEV_TIMIDITY;
else if (sc.Compare("standard")) MidiDevices[nm] = 0; else if (sc.Compare("fmod")) MidiDevices[nm] = MDEV_FMOD;
else if (sc.Compare("opl")) MidiDevices[nm] = 2; else if (sc.Compare("standard")) MidiDevices[nm] = MDEV_MMAPI;
else if (sc.Compare("default")) MidiDevices[nm] = -1; else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL;
else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT;
else sc.ScriptError("Unknown MIDI device %s\n", sc.String); else sc.ScriptError("Unknown MIDI device %s\n", sc.String);
} }
break; break;

View file

@ -502,7 +502,7 @@ void S_ParseSndSeq (int levellump)
FScanner sc(lump, "SNDSEQ"); FScanner sc(lump, "SNDSEQ");
while (sc.GetString ()) while (sc.GetString ())
{ {
bool bDoorSound; bool bDoorSound = false;
if (*sc.String == ':' || *sc.String == '[') if (*sc.String == ':' || *sc.String == '[')
{ {

View file

@ -1307,10 +1307,10 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
{ {
int lumpnum = -1; int lumpnum = -1;
int offset, length; int offset, length;
int device = -1; int device = MDEV_DEFAULT;
int * devp = MidiDevices.CheckKey(FName(musicname)); int *devp = MidiDevices.CheckKey(FName(musicname));
if (devp != NULL) device = *devp; if (devp != NULL) device = *devp;
if (!FileExists (musicname)) if (!FileExists (musicname))

View file

@ -311,6 +311,15 @@ ReverbContainer *S_FindEnvironment (const char *name);
ReverbContainer *S_FindEnvironment (int id); ReverbContainer *S_FindEnvironment (int id);
void S_AddEnvironment (ReverbContainer *settings); void S_AddEnvironment (ReverbContainer *settings);
enum EMidiDevice
{
MDEV_DEFAULT = -1,
MDEV_MMAPI = 0,
MDEV_TIMIDITY = 1,
MDEV_OPL = 2,
MDEV_FMOD = 3,
};
typedef TMap<FName, int> MidiDeviceMap; typedef TMap<FName, int> MidiDeviceMap;
extern MidiDeviceMap MidiDevices; extern MidiDeviceMap MidiDevices;

View file

@ -152,7 +152,7 @@ void I_InitGraphics ()
} }
gl_disabled = gl_nogl; gl_disabled = gl_nogl;
#endif #endif
val.Bool = !!Args.CheckParm ("-devparm"); val.Bool = !!Args->CheckParm ("-devparm");
ticker.SetGenericRepDefault (val, CVAR_Bool); ticker.SetGenericRepDefault (val, CVAR_Bool);
#ifndef NO_GL #ifndef NO_GL

View file

@ -121,6 +121,20 @@ void STACK_ARGS call_terms ()
} }
} }
//==========================================================================
//
// FinalGC
//
// Collect garbage one last time before exiting.
//
//==========================================================================
static void FinalGC()
{
Args = NULL;
GC::FullGC();
}
static void STACK_ARGS NewFailure () static void STACK_ARGS NewFailure ()
{ {
I_FatalError ("Failed to allocate memory from system heap"); I_FatalError ("Failed to allocate memory from system heap");
@ -209,6 +223,7 @@ int main (int argc, char **argv)
try try
{ {
Args = new DArgs(argc, argv); Args = new DArgs(argc, argv);
atterm(FinalGC);
/* /*
killough 1/98: killough 1/98:

View file

@ -92,6 +92,11 @@ FMOD_RESULT SPC_CreateCodec(FMOD::System *sys);
static int Enum_NumForName(const FEnumList *list, const char *name); static int Enum_NumForName(const FEnumList *list, const char *name);
static const char *Enum_NameForNum(const FEnumList *list, int num); static const char *Enum_NameForNum(const FEnumList *list, int num);
static FMOD_RESULT F_CALLBACK Memory_Open(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata);
static FMOD_RESULT F_CALLBACK Memory_Close(void *handle, void *userdata);
static FMOD_RESULT F_CALLBACK Memory_Read(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata);
static FMOD_RESULT F_CALLBACK Memory_Seek(void *handle, unsigned int pos, void *userdata);
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR (String, snd_output) EXTERN_CVAR (String, snd_output)
@ -106,15 +111,19 @@ EXTERN_CVAR (Int, snd_channels)
ReverbContainer *ForcedEnvironment; ReverbContainer *ForcedEnvironment;
CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Int, snd_buffercount, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, snd_hrtf, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, snd_waterreverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_waterreverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, snd_resampler, "Linear", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_resampler, "Linear", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, snd_speakermode, "Auto", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_speakermode, "Auto", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, snd_output_format, "PCM-16", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_output_format, "PCM-16", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, snd_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, snd_dspnet, false, 0) CVAR (Bool, snd_dspnet, false, 0)
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
static const ReverbContainer *PrevEnvironment; static const ReverbContainer *PrevEnvironment;
static bool ShowedBanner;
// The rolloff callback is called during FMOD::Sound::play, so we need this // The rolloff callback is called during FMOD::Sound::play, so we need this
// global variable to contain the sound info during that time for the // global variable to contain the sound info during that time for the
@ -164,8 +173,6 @@ static const FEnumList SpeakerModeNames[] =
{ "1", FMOD_SPEAKERMODE_MONO }, { "1", FMOD_SPEAKERMODE_MONO },
{ "2", FMOD_SPEAKERMODE_STEREO }, { "2", FMOD_SPEAKERMODE_STEREO },
{ "4", FMOD_SPEAKERMODE_QUAD }, { "4", FMOD_SPEAKERMODE_QUAD },
{ "Headphones", 9001 },
{ "HRTF", 9001 },
{ NULL, 0 } { NULL, 0 }
}; };
@ -195,6 +202,16 @@ static const FEnumList SoundFormatNames[] =
{ NULL, 0 } { NULL, 0 }
}; };
static const char *OpenStateNames[] =
{
"Ready",
"Loading",
"Error",
"Connecting",
"Buffering",
"Seeking"
};
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
//========================================================================== //==========================================================================
@ -374,6 +391,26 @@ public:
return FMOD_OK == Channel->setPosition(pos, FMOD_TIMEUNIT_MS); return FMOD_OK == Channel->setPosition(pos, FMOD_TIMEUNIT_MS);
} }
FString GetStats()
{
FString stats;
FMOD_OPENSTATE openstate;
unsigned int percentbuffered;
unsigned int position;
bool starving;
if (FMOD_OK == Stream->getOpenState(&openstate, &percentbuffered, &starving))
{
stats = (openstate <= FMOD_OPENSTATE_SEEKING ? OpenStateNames[openstate] : "Unknown state");
stats.AppendFormat(",%3d%% buffered, %s", percentbuffered, starving ? "Starving" : "Well-fed");
}
if (Channel != NULL && FMOD_OK == Channel->getPosition(&position, FMOD_TIMEUNIT_MS))
{
stats.AppendFormat(", %d ms", position);
}
return stats;
}
static FMOD_RESULT F_CALLBACK PCMReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen) static FMOD_RESULT F_CALLBACK PCMReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen)
{ {
FMOD_RESULT result; FMOD_RESULT result;
@ -445,6 +482,8 @@ bool FMODSoundRenderer::Init()
FMOD_SOUND_FORMAT format; FMOD_SOUND_FORMAT format;
FMOD_DSP_RESAMPLER resampler; FMOD_DSP_RESAMPLER resampler;
FMOD_INITFLAGS initflags; FMOD_INITFLAGS initflags;
int samplerate;
int driver;
int eval; int eval;
@ -454,7 +493,12 @@ bool FMODSoundRenderer::Init()
PausableSfx = NULL; PausableSfx = NULL;
PrevEnvironment = DefaultEnvironments[0]; PrevEnvironment = DefaultEnvironments[0];
Printf ("I_InitSound: Initializing FMOD\n"); Printf("I_InitSound: Initializing FMOD\n");
if (!ShowedBanner)
{
Printf("FMOD Sound System, copyright © Firelight Technologies Pty, Ltd., 1994-2007.\n");
ShowedBanner = true;
}
// Create a System object and initialize. // Create a System object and initialize.
result = FMOD::System_Create(&Sys); result = FMOD::System_Create(&Sys);
@ -472,30 +516,6 @@ bool FMODSoundRenderer::Init()
return false; return false;
} }
result = Sys->getDriverCaps(0, &Driver_Caps, &Driver_MinFrequency, &Driver_MaxFrequency, &speakermode);
ERRCHECK(result);
// Set the user selected speaker mode.
eval = Enum_NumForName(SpeakerModeNames, snd_speakermode);
if (eval >= 0)
{
speakermode = FMOD_SPEAKERMODE(eval);
}
result = Sys->setSpeakerMode(speakermode < 9000 ? speakermode : FMOD_SPEAKERMODE_STEREO);
ERRCHECK(result);
// Set software format
eval = Enum_NumForName(SoundFormatNames, snd_output_format);
format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16;
eval = Enum_NumForName(ResamplerNames, snd_resampler);
resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR;
result = Sys->setSoftwareFormat(snd_samplerate, format, 0, 0, resampler);
ERRCHECK(result);
// Set software channels according to snd_channels
result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS);
ERRCHECK(result);
#ifdef _WIN32 #ifdef _WIN32
if (OSPlatform == os_WinNT4) if (OSPlatform == os_WinNT4)
{ {
@ -541,18 +561,74 @@ bool FMODSoundRenderer::Init()
ERRCHECK(result); ERRCHECK(result);
} }
result = Sys->getNumDrivers(&driver);
if (result == FMOD_OK)
{
if (snd_driver >= driver)
{
Printf (TEXTCOLOR_BLUE"Driver %d does not exist. Using 0.\n", *snd_driver);
driver = 0;
}
else
{
driver = snd_driver;
}
result = Sys->setDriver(driver);
}
result = Sys->getDriver(&driver);
result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &Driver_MaxFrequency, &speakermode);
ERRCHECK(result);
// Set the user selected speaker mode.
eval = Enum_NumForName(SpeakerModeNames, snd_speakermode);
if (eval >= 0)
{
speakermode = FMOD_SPEAKERMODE(eval);
}
result = Sys->setSpeakerMode(speakermode < 9000 ? speakermode : FMOD_SPEAKERMODE_STEREO);
ERRCHECK(result);
// Set software format
eval = Enum_NumForName(SoundFormatNames, snd_output_format);
format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16;
eval = Enum_NumForName(ResamplerNames, snd_resampler);
resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR;
samplerate = clamp<int>(snd_samplerate, Driver_MinFrequency, Driver_MaxFrequency);
if (samplerate == 0 || snd_samplerate == 0)
{ // Creative's ASIO drivers report the only supported frequency as 0!
if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL, NULL, NULL, NULL))
{
samplerate = 48000;
}
}
if (samplerate != snd_samplerate && snd_samplerate != 0)
{
Printf(TEXTCOLOR_BLUE"Sample rate %d is unsupported. Trying %d\n", *snd_samplerate, samplerate);
}
result = Sys->setSoftwareFormat(samplerate, format, 0, 0, resampler);
// Set software channels according to snd_channels
result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS);
ERRCHECK(result);
if (Driver_Caps & FMOD_CAPS_HARDWARE_EMULATED) if (Driver_Caps & FMOD_CAPS_HARDWARE_EMULATED)
{ // The user has the 'Acceleration' slider set to off! { // The user has the 'Acceleration' slider set to off!
// This is really bad for latency! // This is really bad for latency!
Printf ("Warning: The sound acceleration slider has been set to off.\n"); Printf (TEXTCOLOR_BLUE"Warning: The sound acceleration slider has been set to off.\n");
Printf ("Please turn it back on if you want decent sound.\n"); Printf (TEXTCOLOR_BLUE"Please turn it back on if you want decent sound.\n");
result = Sys->setDSPBufferSize(1024, 10); // At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms. result = Sys->setDSPBufferSize(1024, 10); // At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms.
ERRCHECK(result); ERRCHECK(result);
} }
else if (snd_buffersize != 0 || snd_buffercount != 0)
{
int buffersize = snd_buffersize ? snd_buffersize : 1024;
int buffercount = snd_buffercount ? snd_buffercount : 4;
result = Sys->setDSPBufferSize(buffersize, buffercount);
}
// Try to init // Try to init
initflags = FMOD_INIT_NORMAL; initflags = FMOD_INIT_NORMAL;
if (speakermode > 9000) if (snd_hrtf)
{ {
initflags |= FMOD_INIT_SOFTWARE_HRTF; initflags |= FMOD_INIT_SOFTWARE_HRTF;
} }
@ -560,17 +636,38 @@ bool FMODSoundRenderer::Init()
{ {
initflags |= FMOD_INIT_ENABLE_DSPNET; initflags |= FMOD_INIT_ENABLE_DSPNET;
} }
result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0); for (;;)
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) {
{ // The speaker mode selected isn't supported by this soundcard. Switch it back to stereo.
result = Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
ERRCHECK(result);
result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0); result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0);
ERRCHECK(result); if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
{ // The speaker mode selected isn't supported by this soundcard. Switch it back to stereo.
result = Sys->getSpeakerMode(&speakermode);
if (result == FMOD_OK && FMOD_OK == Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO))
{
continue;
}
}
#ifdef _WIN32
else if (result == FMOD_ERR_OUTPUT_INIT)
{
FMOD_OUTPUTTYPE output;
result = Sys->getOutput(&output);
if (result == FMOD_OK && output != FMOD_OUTPUTTYPE_DSOUND)
{
Printf(TEXTCOLOR_BLUE" Init failed for output type %s. Retrying with DirectSound.\n",
Enum_NameForNum(OutputNames, output));
if (FMOD_OK == Sys->setOutput(FMOD_OUTPUTTYPE_DSOUND))
{
continue;
}
}
}
#endif
break;
} }
if (result != FMOD_OK) if (result != FMOD_OK)
{ // Initializing FMOD failed. Cry cry. { // Initializing FMOD failed. Cry cry.
Printf (" System::init returned error code %d\n", result);
return false; return false;
} }
@ -656,6 +753,8 @@ void FMODSoundRenderer::PrintStatus()
int samplerate; int samplerate;
int numoutputchannels; int numoutputchannels;
int num2d, num3d, total; int num2d, num3d, total;
unsigned int bufferlength;
int numbuffers;
if (FMOD_OK == Sys->getOutput(&output)) if (FMOD_OK == Sys->getOutput(&output))
{ {
@ -688,6 +787,10 @@ void FMODSoundRenderer::PrintStatus()
Printf (TEXTCOLOR_LIGHTBLUE "Software mixer channels: "TEXTCOLOR_GREEN"%d\n", numoutputchannels); Printf (TEXTCOLOR_LIGHTBLUE "Software mixer channels: "TEXTCOLOR_GREEN"%d\n", numoutputchannels);
Printf (TEXTCOLOR_LIGHTBLUE "Software mixer resampler: "TEXTCOLOR_GREEN"%s\n", Enum_NameForNum(ResamplerNames, resampler)); Printf (TEXTCOLOR_LIGHTBLUE "Software mixer resampler: "TEXTCOLOR_GREEN"%s\n", Enum_NameForNum(ResamplerNames, resampler));
} }
if (FMOD_OK == Sys->getDSPBufferSize(&bufferlength, &numbuffers))
{
Printf (TEXTCOLOR_LIGHTBLUE "DSP buffers: "TEXTCOLOR_GREEN"%u samples x %d\n", bufferlength, numbuffers);
}
} }
//========================================================================== //==========================================================================
@ -839,7 +942,7 @@ SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int
capsule = new FMODStreamCapsule (userdata, callback, this); capsule = new FMODStreamCapsule (userdata, callback, this);
mode = FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE | FMOD_CREATESTREAM; mode = FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE | FMOD_CREATESTREAM | FMOD_OPENONLY;
sample_shift = (flags & SoundStream::Bits8) ? 0 : 1; sample_shift = (flags & SoundStream::Bits8) ? 0 : 1;
channel_shift = (flags & SoundStream::Mono) ? 0 : 1; channel_shift = (flags & SoundStream::Mono) ? 0 : 1;
@ -892,6 +995,7 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int fla
FMOD_MODE mode; FMOD_MODE mode;
FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), };
FMOD::Sound *stream; FMOD::Sound *stream;
FMOD_RESULT result;
mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM;
if (flags & SoundStream::Loop) if (flags & SoundStream::Loop)
@ -905,8 +1009,25 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int fla
} }
exinfo.length = length; exinfo.length = length;
exinfo.fileoffset = offset; exinfo.fileoffset = offset;
if ((*snd_midipatchset)[0] != '\0')
{
exinfo.dlsname = snd_midipatchset;
}
if (FMOD_OK == Sys->createSound(filename_or_data, mode, &exinfo, &stream)) result = Sys->createSound(filename_or_data, mode, &exinfo, &stream);
if (result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL)
{
// FMOD_ERR_FORMAT could refer to either the main sound file or
// to the DLS instrument set. Try again without special DLS
// instruments to see if that lets it succeed.
exinfo.dlsname = NULL;
result = Sys->createSound(filename_or_data, mode, &exinfo, &stream);
if (result == FMOD_OK)
{
Printf("%s is an unsupported format.\n", *snd_midipatchset);
}
}
if (result == FMOD_OK)
{ {
return new FMODStreamCapsule(stream, this); return new FMODStreamCapsule(stream, this);
} }
@ -1080,6 +1201,10 @@ FMOD_MODE FMODSoundRenderer::SetChanHeadSettings(FMOD::Channel *chan, sfxinfo_t
if (chan->get3DPanLevel(&old_level) == FMOD_OK && old_level != level) if (chan->get3DPanLevel(&old_level) == FMOD_OK && old_level != level)
{ // Only set it if it's different. { // Only set it if it's different.
chan->set3DPanLevel(level); chan->set3DPanLevel(level);
if (level < 1)
{ // Let the noise come from all speakers, not just the front ones.
chan->setSpeakerMix(1,1,1,1,1,1,1,1);
}
} }
return oldmode; return oldmode;
} }

View file

@ -69,12 +69,12 @@ extern void ChildSigHandler (int signum);
#include "i_cd.h" #include "i_cd.h"
#include "tempfiles.h" #include "tempfiles.h"
#include "templates.h" #include "templates.h"
#include "stats.h"
#include <fmod.h> #include <fmod.h>
EXTERN_CVAR (Int, snd_samplerate) EXTERN_CVAR (Int, snd_samplerate)
EXTERN_CVAR (Int, snd_mididevice) EXTERN_CVAR (Int, snd_mididevice)
CVAR(Bool, snd_modplug, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
static bool MusicDown = true; static bool MusicDown = true;
@ -83,6 +83,11 @@ int nomusic = 0;
float relative_volume = 1.f; float relative_volume = 1.f;
float saved_relative_volume = 1.0f; // this could be used to implement an ACS FadeMusic function float saved_relative_volume = 1.0f; // this could be used to implement an ACS FadeMusic function
CVAR(Bool, snd_modplug, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
StreamSong *ModPlugSong_Create(FILE *file, char *musiccache, int length);
//========================================================================== //==========================================================================
// //
// CVAR snd_musicvolume // CVAR snd_musicvolume
@ -131,6 +136,11 @@ void MusInfo::TimidityVolumeChanged()
{ {
} }
FString MusInfo::GetStats()
{
return "No stats available for this song";
}
void I_InitMusic (void) void I_InitMusic (void)
{ {
static bool setatterm = false; static bool setatterm = false;
@ -187,6 +197,9 @@ void I_PlaySong (void *handle, int _looping, float rel_vol)
currSong = info; currSong = info;
else else
currSong = NULL; currSong = NULL;
// Notify the sound system of the changed relative volume
snd_musicvolume.Callback();
} }
@ -227,7 +240,7 @@ void I_UnRegisterSong (void *handle)
} }
} }
void *I_RegisterSong (const char *filename, char * musiccache, int offset, int len, int device) void *I_RegisterSong (const char *filename, char *musiccache, int offset, int len, int device)
{ {
FILE *file; FILE *file;
MusInfo *info = NULL; MusInfo *info = NULL;
@ -238,7 +251,7 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l
return 0; return 0;
} }
if (offset!=-1) if (offset != -1)
{ {
file = fopen (filename, "rb"); file = fopen (filename, "rb");
if (file == NULL) if (file == NULL)
@ -270,105 +283,177 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l
memcpy(&id, &musiccache[0], 4); memcpy(&id, &musiccache[0], 4);
} }
#ifndef _WIN32
// non-windows platforms don't support MDEV_MIDI so map to MDEV_FMOD
if (device == MDEV_MMAPI) device = MDEV_FMOD;
#endif
// Check for MUS format // Check for MUS format
if (id == MAKE_ID('M','U','S',0x1a)) if (id == MAKE_ID('M','U','S',0x1a))
{ {
if (GSnd != NULL && device != 0 && device != 1 && (opl_enable || device == 2) ) if (GSnd != NULL)
{ {
info = new OPLMUSSong (file, musiccache, len); /* MUS are played as:
- OPL:
- if explicitly selected by $mididevice
- when opl_enable is true and no midi device is set for the song
Timidity:
- if explicitly selected by $mididevice
- when snd_mididevice is -2 and no midi device is set for the song
FMod:
- if explicitly selected by $mididevice
- when snd_mididevice is -1 and no midi device is set for the song
- as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0
MMAPI (Win32 only):
- if explicitly selected by $mididevice (non-Win32 redirects this to FMOD)
- when snd_mididevice is >= 0 and no midi device is set for the song
- as fallback when both OPL and Timidity failed and snd_mididevice is >= 0
*/
if ((opl_enable && device == MDEV_DEFAULT) || device == MDEV_OPL)
{
info = new OPLMUSSong (file, musiccache, len);
}
else if (device == MDEV_TIMIDITY || (device == MDEV_DEFAULT && snd_mididevice == -2))
{
info = new TimiditySong (file, musiccache, len);
}
if (info != NULL && !info->IsValid())
{
delete info;
info = NULL;
device = MDEV_DEFAULT;
}
if (info == NULL && (snd_mididevice == -1 || device == MDEV_FMOD) && device != MDEV_MMAPI)
{
TArray<BYTE> midi;
bool midi_made = false;
if (file == NULL)
{
midi_made = ProduceMIDI((BYTE *)musiccache, midi);
}
else
{
BYTE *mus = new BYTE[len];
size_t did_read = fread(mus, 1, len, file);
if (did_read == (size_t)len)
{
midi_made = ProduceMIDI(mus, midi);
}
fseek(file, -(long)did_read, SEEK_CUR);
delete[] mus;
}
if (midi_made)
{
info = new StreamSong((char *)&midi[0], -1, midi.Size());
if (!info->IsValid())
{
delete info;
info = NULL;
}
}
}
} }
#ifdef _WIN32
if (info == NULL) if (info == NULL)
{ {
#ifdef _WIN32 info = new MUSSong2 (file, musiccache, len);
if (device == 1 && GSnd != NULL) }
#endif // _WIN32
}
// Check for MIDI format
else
{
if (id == MAKE_ID('M','T','h','d'))
{
// This is a midi file
// MIDI can't be played with OPL so use default.
if (device == MDEV_OPL) device = MDEV_DEFAULT;
/* MIDI are played as:
Timidity:
- if explicitly selected by $mididevice
- when snd_mididevice is -2 and no midi device is set for the song
FMod:
- if explicitly selected by $mididevice
- when snd_mididevice is -1 and no midi device is set for the song
- as fallback when Timidity failed unless snd_mididevice is >= 0
MMAPI (Win32 only):
- if explicitly selected by $mididevice (non-Win32 redirects this to FMOD)
- when snd_mididevice is >= 0 and no midi device is set for the song
- as fallback when Timidity failed and snd_mididevice is >= 0
*/
if ((device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) && GSnd != NULL)
{ {
info = new TimiditySong (file, musiccache, len); info = new TimiditySong (file, musiccache, len);
if (!info->IsValid()) if (!info->IsValid())
{ {
delete info; delete info;
info = NULL; info = NULL;
device = MDEV_DEFAULT;
} }
} }
if (info == NULL && (snd_mididevice != -2 || device == 0))
{
info = new MUSSong2 (file, musiccache, len);
}
else if (info == NULL && GSnd != NULL)
#endif // _WIN32
{
info = new TimiditySong (file, musiccache, len);
}
}
}
// Check for MIDI format
else if (id == MAKE_ID('M','T','h','d'))
{
// This is a midi file
#ifdef _WIN32 #ifdef _WIN32
if (device == 1 && GSnd != NULL) if (info == NULL && device != MDEV_FMOD && (snd_mididevice >= 0 || device == MDEV_MMAPI))
{
info = new TimiditySong (file, musiccache, len);
if (!info->IsValid())
{ {
delete info; info = new MIDISong2 (file, musiccache, len);
info = NULL;
} }
}
if (info == NULL && (snd_mididevice != -2 || device == 0))
{
info = new MIDISong2 (file, musiccache, len);
}
else if (info == NULL && GSnd != NULL)
#endif // _WIN32 #endif // _WIN32
{
info = new TimiditySong (file, musiccache, len);
} }
} // Check for RDosPlay raw OPL format
// Check for RDosPlay raw OPL format else if (id == MAKE_ID('R','A','W','A') && len >= 12)
else if (id == MAKE_ID('R','A','W','A') && len >= 12)
{
DWORD fullsig[2];
if (file != NULL)
{ {
if (fread (fullsig, 4, 2, file) != 2) DWORD fullsig[2];
if (file != NULL)
{ {
fclose (file); if (fread (fullsig, 4, 2, file) != 2)
return 0; {
fclose (file);
return 0;
}
fseek (file, -8, SEEK_CUR);
} }
fseek (file, -8, SEEK_CUR); else
}
else
{
memcpy(fullsig, musiccache, 8);
}
if (fullsig[1] == MAKE_ID('D','A','T','A'))
{
info = new OPLMUSSong (file, musiccache, len);
}
}
// Check for Martin Fernandez's modified IMF format
else if (id == MAKE_ID('A','D','L','I'))
{
char fullhead[6];
if (file != NULL)
{
if (fread (fullhead, 1, 6, file) != 6)
{ {
fclose (file); memcpy(fullsig, musiccache, 8);
return 0; }
if (fullsig[1] == MAKE_ID('D','A','T','A'))
{
info = new OPLMUSSong (file, musiccache, len);
} }
fseek (file, -6, SEEK_CUR);
} }
else // Check for Martin Fernandez's modified IMF format
else if (id == MAKE_ID('A','D','L','I'))
{ {
memcpy(fullhead, musiccache, 6); char fullhead[6];
}
if (fullhead[4] == 'B' && fullhead[5] == 1) if (file != NULL)
{ {
info = new OPLMUSSong (file, musiccache, len); if (fread (fullhead, 1, 6, file) != 6)
{
fclose (file);
return 0;
}
fseek (file, -6, SEEK_CUR);
}
else
{
memcpy(fullhead, musiccache, 6);
}
if (fullhead[4] == 'B' && fullhead[5] == 1)
{
info = new OPLMUSSong (file, musiccache, len);
}
} }
} }
@ -401,11 +486,12 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l
// 1024 bytes is an arbitrary restriction. It's assumed that anything // 1024 bytes is an arbitrary restriction. It's assumed that anything
// smaller than this can't possibly be a valid music file if it hasn't // smaller than this can't possibly be a valid music file if it hasn't
// been identified already, so don't even bother trying to load it. // been identified already, so don't even bother trying to load it.
if (info == NULL && GSnd != NULL && len >= 1024) // Of course MIDIs shorter than 1024 bytes should pass.
if (info == NULL && GSnd != NULL && (len >= 1024 || id == MAKE_ID('M','T','h','d')))
{ {
if (snd_modplug) if (snd_modplug)
{ {
info = ModPlugSong::Create(file, musiccache, len); info = ModPlugSong_Create(file, musiccache, len);
} }
if (info == NULL) if (info == NULL)
{ {
@ -488,3 +574,18 @@ CCMD(testmusicvol)
else else
Printf("Current relative volume is %1.2f\n", relative_volume); Printf("Current relative volume is %1.2f\n", relative_volume);
} }
//==========================================================================
//
// STAT music
//
//==========================================================================
ADD_STAT(music)
{
if (currSong != NULL)
{
return currSong->GetStats();
}
return "No song playing";
}

View file

@ -43,7 +43,7 @@
// //
void I_InitMusic (); void I_InitMusic ();
void I_ShutdownMusic (); void I_ShutdownMusic ();
void I_BuildMIDIMenuList (struct value_s **values, float *numValues); void I_BuildMIDIMenuList (struct value_t **values, float *numValues);
void I_UpdateMusic (); void I_UpdateMusic ();
// Volume. // Volume.

View file

@ -20,7 +20,6 @@
#include "c_cvars.h" #include "c_cvars.h"
#include "mus2midi.h" #include "mus2midi.h"
#include "i_sound.h" #include "i_sound.h"
#include "modplug/modplug.h"
void I_InitMusicWin32 (); void I_InitMusicWin32 ();
void I_ShutdownMusicWin32 (); void I_ShutdownMusicWin32 ();
@ -45,6 +44,7 @@ public:
virtual bool IsValid () const = 0; virtual bool IsValid () const = 0;
virtual bool SetPosition (int order); virtual bool SetPosition (int order);
virtual void Update(); virtual void Update();
virtual FString GetStats();
enum EState enum EState
{ {
@ -243,6 +243,7 @@ public:
bool IsMIDI () const { return false; } bool IsMIDI () const { return false; }
bool IsValid () const { return m_Stream != NULL; } bool IsValid () const { return m_Stream != NULL; }
bool SetPosition (int order); bool SetPosition (int order);
FString GetStats();
protected: protected:
StreamSong () : m_Stream(NULL), m_LastPos(0) {} StreamSong () : m_Stream(NULL), m_LastPos(0) {}
@ -289,25 +290,6 @@ protected:
#endif #endif
}; };
class ModPlugSong : public StreamSong
{
public:
static ModPlugSong *Create(FILE *file, char *musiccache, int length);
~ModPlugSong ();
bool IsPlaying ();
bool SetPosition (int order);
void Play(bool);
protected:
static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata);
ModPlugSong (ModPlugFile *dat);
ModPlugFile * Data;
int order;
};
// MUS file played by a software OPL2 synth and streamed through FMOD ------- // MUS file played by a software OPL2 synth and streamed through FMOD -------
class OPLMUSSong : public StreamSong class OPLMUSSong : public StreamSong

View file

@ -68,12 +68,13 @@ extern HINSTANCE g_hInst;
#include "w_wad.h" #include "w_wad.h"
#include "i_video.h" #include "i_video.h"
#include "s_sound.h" #include "s_sound.h"
#include "v_text.h"
#include "gi.h" #include "gi.h"
#include "doomdef.h" #include "doomdef.h"
EXTERN_CVAR (Float, snd_sfxvolume) EXTERN_CVAR (Float, snd_sfxvolume)
CVAR (Int, snd_samplerate, 48000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Int, snd_buffersize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, snd_buffersize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -124,7 +125,7 @@ void I_InitSound ()
{ {
delete GSnd; delete GSnd;
GSnd = NULL; GSnd = NULL;
Printf ("Sound init failed. Using nosound.\n"); Printf (TEXTCOLOR_RED"Sound init failed. Using nosound.\n");
} }
I_InitMusic (); I_InitMusic ();
snd_sfxvolume.Callback (); snd_sfxvolume.Callback ();
@ -215,3 +216,8 @@ bool SoundStream::SetPosition(int pos)
{ {
return false; return false;
} }
FString SoundStream::GetStats()
{
return "No stream stats available.";
}

View file

@ -57,6 +57,7 @@ public:
virtual bool SetPaused (bool paused) = 0; virtual bool SetPaused (bool paused) = 0;
virtual unsigned int GetPosition () = 0; virtual unsigned int GetPosition () = 0;
virtual bool SetPosition (int pos); virtual bool SetPosition (int pos);
virtual FString GetStats();
}; };
typedef bool (*SoundStreamCallback)(SoundStream *stream, void *buff, int len, void *userdata); typedef bool (*SoundStreamCallback)(SoundStream *stream, void *buff, int len, void *userdata);

View file

@ -1,7 +1,7 @@
#ifdef _WIN32
#include "i_musicinterns.h" #include "i_musicinterns.h"
#include "c_dispatch.h" #include "c_dispatch.h"
#include "i_music.h" #include "i_music.h"
#include "i_system.h"
#include "templates.h" #include "templates.h"
#include "v_text.h" #include "v_text.h"
@ -9,6 +9,7 @@
static DWORD nummididevices; static DWORD nummididevices;
static bool nummididevicesset; static bool nummididevicesset;
#ifdef _WIN32
UINT mididevice; UINT mididevice;
CVAR (Bool, snd_midiprecache, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); CVAR (Bool, snd_midiprecache, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
@ -56,19 +57,22 @@ void I_InitMusicWin32 ()
void I_ShutdownMusicWin32 () void I_ShutdownMusicWin32 ()
{ {
// I don't know if this is an NT 4.0 bug or an FMOD bug, but if waveout // Ancient bug a saw on NT 4.0 and an old version of FMOD 3: If waveout
// is used for sound, and a MIDI is also played, then when I quit, the OS // is used for sound and a MIDI is also played, then when I quit, the OS
// tells me a free block was modified after being freed. This is // tells me a free block was modified after being freed. This is
// apparently a synchronization issue between two threads, because if I // apparently a synchronization issue between two threads, because if I
// put this Sleep here after stopping the music but before shutting down // put this Sleep here after stopping the music but before shutting down
// the entire sound system, the error does not happen. I don't think it's // the entire sound system, the error does not happen. Observed with a
// a driver problem, because it happens with both a Vortex 2 and an Audigy. // Vortex 2 (may Aureal rest in peace) and an Audigy (damn you, Creative!).
// Though if their drivers are both based off some common Microsoft sample // I no longer have a system with NT4 drivers, so I don't know if this
// code, I suppose it could be a driver issue. // workaround is still needed or not.
Sleep (50); if (OSPlatform == os_WinNT4)
{
Sleep(50);
}
} }
void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues) void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues)
{ {
if (*outValues == NULL) if (*outValues == NULL)
{ {
@ -79,13 +83,13 @@ void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues)
values[0].name = "TiMidity++"; values[0].name = "TiMidity++";
values[0].value = -2.0; values[0].value = -2.0;
values[1].name = "FMOD";
values[1].value = -1.0;
if (nummididevices > 0) if (nummididevices > 0)
{ {
UINT id; UINT id;
int p; int p;
values[1].name = "MIDI Mapper";
values[1].value = -1.0;
for (id = 0, p = 2; id < nummididevices; ++id) for (id = 0, p = 2; id < nummididevices; ++id)
{ {
MIDIOUTCAPS caps; MIDIOUTCAPS caps;
@ -107,7 +111,7 @@ void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues)
} }
else else
{ {
*numValues = 1.f; *numValues = 2.f;
} }
} }
} }
@ -157,9 +161,9 @@ CCMD (snd_listmididevices)
MMRESULT res; MMRESULT res;
PrintMidiDevice (-2, "TiMidity++", 0, 0); PrintMidiDevice (-2, "TiMidity++", 0, 0);
PrintMidiDevice (-1, "FMOD", 0, 0);
if (nummididevices != 0) if (nummididevices != 0)
{ {
PrintMidiDevice (-1, "MIDI Mapper", MOD_MAPPER, 0);
for (id = 0; id < nummididevices; ++id) for (id = 0; id < nummididevices; ++id)
{ {
res = midiOutGetDevCaps (id, &caps, sizeof(caps)); res = midiOutGetDevCaps (id, &caps, sizeof(caps));
@ -174,4 +178,39 @@ CCMD (snd_listmididevices)
} }
} }
} }
#else
// Everything but Windows uses this code.
CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self < -2)
self = -2;
else if (self > -1)
self = -1;
}
void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues)
{
if (*outValues == NULL)
{
int count = 1 + nummididevices + (nummididevices > 0);
value_t *values;
*outValues = values = new value_t[count];
values[0].name = "TiMidity++";
values[0].value = -2.0;
values[1].name = "FMOD";
values[1].value = -1.0;
*numValues = 2.f;
}
}
CCMD (snd_listmididevices)
{
Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : "");
Printf("%s-1. FMOD\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : "");
}
#endif #endif

View file

@ -244,6 +244,7 @@ TimiditySong::TimiditySong (FILE *file, char * musiccache, int len)
void TimiditySong::PrepTimidity () void TimiditySong::PrepTimidity ()
{ {
int pipeSize; int pipeSize;
#ifdef _WIN32 #ifdef _WIN32
static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, TRUE }; static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, TRUE };
@ -267,6 +268,11 @@ void TimiditySong::PrepTimidity ()
pipeSize = (timidity_pipe * timidity_frequency / 1000) pipeSize = (timidity_pipe * timidity_frequency / 1000)
<< (timidity_stereo + !timidity_8bit); << (timidity_stereo + !timidity_8bit);
if (GSnd == NULL)
{ // Can't pipe if using no sound.
pipeSize = 0;
}
if (pipeSize != 0) if (pipeSize != 0)
{ {
@ -520,11 +526,11 @@ bool TimiditySong::LaunchTimidity ()
close (WavePipe[0]); close (WavePipe[0]);
dup2 (WavePipe[1], STDOUT_FILENO); dup2 (WavePipe[1], STDOUT_FILENO);
freopen ("/dev/null", "r", stdin); freopen ("/dev/null", "r", stdin);
freopen ("/dev/null", "w", stderr); // freopen ("/dev/null", "w", stderr);
close (WavePipe[1]); close (WavePipe[1]);
printf ("exec %s\n", words.we_wordv[0]); execvp (words.we_wordv[0], words.we_wordv);
execvp (words.we_wordv[0], words.we_wordv); fprintf(stderr,"execvp failed\n");
exit (0); // if execvp succeeds, we never get here exit (0); // if execvp succeeds, we never get here
} }
else if (forkres < 0) else if (forkres < 0)
@ -536,7 +542,12 @@ bool TimiditySong::LaunchTimidity ()
// printf ("child is %d\n", forkres); // printf ("child is %d\n", forkres);
ChildProcess = forkres; ChildProcess = forkres;
close (WavePipe[1]); close (WavePipe[1]);
WavePipe[1] = -1; WavePipe[1] = -1;
/* usleep(1000000);
if (waitpid(ChildProcess, NULL, WNOHANG) == ChildProcess)
{
fprintf(stderr,"Launching timidity failed\n");
}*/
} }
wordfree (&words); wordfree (&words);
@ -575,21 +586,36 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u
} }
} }
#else #else
ssize_t got; ssize_t got;
fd_set rfds;
struct timeval tv;
if (ChildQuit == song->ChildProcess)
{
ChildQuit = 0;
fprintf (stderr, "child gone\n");
song->ChildProcess = -1;
return false;
}
FD_ZERO(&rfds);
FD_SET(song->WavePipe[0], &rfds);
tv.tv_sec = 0;
tv.tv_usec = 50;
// fprintf(stderr,"select\n");
if (select(1, &rfds, NULL, NULL, &tv) <= 0 && 0)
{ // Nothing available, so play silence.
// fprintf(stderr,"nothing\n");
// memset(buff, 0, len);
return true;
}
// fprintf(stderr,"something\n");
got = read (song->WavePipe[0], (BYTE *)buff, len); got = read (song->WavePipe[0], (BYTE *)buff, len);
if (got < len) if (got < len)
{ {
memset ((BYTE *)buff+got, 0, len-got); memset ((BYTE *)buff+got, 0, len-got);
} }
if (ChildQuit == song->ChildProcess)
{
ChildQuit = 0;
// printf ("child gone\n");
song->ChildProcess = -1;
return false;
}
#endif #endif
return true; return true;
} }

View file

@ -42,7 +42,24 @@
// 192 approximately replicates the volume of a WinAmp Wave export of the song. // 192 approximately replicates the volume of a WinAmp Wave export of the song.
CVAR(Int, snd_mod_mastervolume, 192, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, snd_mod_mastervolume, 192, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
ModPlugSong *ModPlugSong::Create (FILE *iofile, char * musiccache, int len) class ModPlugSong : public StreamSong
{
public:
~ModPlugSong ();
bool IsPlaying ();
bool SetPosition (int order);
void Play(bool);
static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata);
ModPlugSong (ModPlugFile *dat);
ModPlugFile * Data;
int order;
};
StreamSong *ModPlugSong_Create (FILE *iofile, char * musiccache, int len)
{ {
char *buffer; char *buffer;

View file

@ -50,8 +50,15 @@ StreamSong::~StreamSong ()
} }
StreamSong::StreamSong (const char *filename_or_data, int offset, int len) StreamSong::StreamSong (const char *filename_or_data, int offset, int len)
{ {
m_Stream = GSnd->OpenStream (filename_or_data, SoundStream::Loop, offset, len); if (GSnd != NULL)
{
m_Stream = GSnd->OpenStream (filename_or_data, SoundStream::Loop, offset, len);
}
else
{
m_Stream = NULL;
}
} }
bool StreamSong::IsPlaying () bool StreamSong::IsPlaying ()
@ -92,3 +99,12 @@ bool StreamSong::SetPosition(int order)
return false; return false;
} }
} }
FString StreamSong::GetStats()
{
if (m_Stream != NULL)
{
return m_Stream->GetStats();
}
return "No song loaded\n";
}

View file

@ -3,5 +3,5 @@
// This file was automatically generated by the // This file was automatically generated by the
// updaterevision tool. Do not edit by hand. // updaterevision tool. Do not edit by hand.
#define ZD_SVN_REVISION_STRING "853" #define ZD_SVN_REVISION_STRING "858"
#define ZD_SVN_REVISION_NUMBER 853 #define ZD_SVN_REVISION_NUMBER 858

View file

@ -70,6 +70,30 @@ void FImageCollection::Init (const char **patchNames, int numPatches, int namesp
} }
} }
// [MH] Mainly for mugshots with skins and SBARINFO
void FImageCollection::Add (const char **patchNames, int numPatches, int namespc)
{
int NewNumImages = NumImages + numPatches;
int *NewImageMap = new int[NewNumImages];
memcpy(NewImageMap, ImageMap, (NumImages * sizeof(int)));
for (int i = 0; i < numPatches; ++i)
{
int picnum = TexMan.AddPatch (patchNames[i], namespc, true);
if (picnum == -1 && namespc != ns_sprites)
{
picnum = TexMan.AddPatch (patchNames[i], ns_sprites);
}
NewImageMap[NumImages + i] = picnum;
}
delete[] ImageMap;
ImageMap = NewImageMap;
NumImages = NewNumImages;
}
void FImageCollection::Uninit () void FImageCollection::Uninit ()
{ {
if (ImageMap != NULL) if (ImageMap != NULL)

View file

@ -45,6 +45,7 @@ public:
~FImageCollection (); ~FImageCollection ();
void Init (const char **patchnames, int numPatches, int namespc=0); void Init (const char **patchnames, int numPatches, int namespc=0);
void Add (const char **patchnames, int numPatches, int namespc=0);
void Uninit (); void Uninit ();
FTexture *operator[] (int index) const; FTexture *operator[] (int index) const;

View file

@ -66,7 +66,7 @@
// Protocol version used in demos. // Protocol version used in demos.
// Bump it if you change existing DEM_ commands or add new ones. // Bump it if you change existing DEM_ commands or add new ones.
// Otherwise, it should be safe to leave it alone. // Otherwise, it should be safe to leave it alone.
#define DEMOGAMEVERSION 0x20B #define DEMOGAMEVERSION 0x20C
// Minimum demo version we can play. // Minimum demo version we can play.
// Bump it whenever you change or remove existing DEM_ commands. // Bump it whenever you change or remove existing DEM_ commands.
@ -77,7 +77,7 @@
// SAVESIG should match SAVEVER. // SAVESIG should match SAVEVER.
// MINSAVEVER is the minimum level snapshot version that can be loaded. // MINSAVEVER is the minimum level snapshot version that can be loaded.
#define MINSAVEVER 849 #define MINSAVEVER 854
#if ZD_SVN_REVISION_NUMBER < MINSAVEVER #if ZD_SVN_REVISION_NUMBER < MINSAVEVER
// Never write a savegame with a version lower than what we need // Never write a savegame with a version lower than what we need

View file

@ -238,6 +238,20 @@ static void UnWTS (void)
} }
} }
//==========================================================================
//
// FinalGC
//
// If this doesn't free everything, the debug CRT will let us know.
//
//==========================================================================
static void FinalGC()
{
Args = NULL;
GC::FullGC();
}
//========================================================================== //==========================================================================
// //
// LayoutErrorPane // LayoutErrorPane
@ -777,6 +791,7 @@ void DoMain (HINSTANCE hInstance)
#endif #endif
Args = new DArgs(__argc, __argv); Args = new DArgs(__argc, __argv);
atterm(FinalGC);
// Under XP, get our session ID so we can know when the user changes/locks sessions. // Under XP, get our session ID so we can know when the user changes/locks sessions.
// Since we need to remain binary compatible with older versions of Windows, we // Since we need to remain binary compatible with older versions of Windows, we