diff --git a/Makefile.linux b/Makefile.linux index 59560e8d..583ec701 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -12,19 +12,20 @@ CFLAGS += `pkg-config gtk+-2.0 --cflags` CFLAGS += -DHAVE_FILELENGTH -D__forceinline=inline `sdl-config --cflags` CFLAGS += -DNEED_STRUPR 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 += -lGL -lGLU 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 += -Isnes_spc/snes_spc/ -I/usr/include/fmodex/ CFLAGS += $(INCLUDES) RELEASEOBJ ?= releaseobj DEBUGOBJ ?= debugobj CPPSRCS = $(wildcard $(addsuffix *.cpp,$(SRCDIRS))) -CSRCS = $(wildcard $(addsuffix *.c,$(SRCDIRS))) +CSRCS = $(filter-out src/xlat/xlat_parser.c, $(wildcard $(addsuffix *.c,$(SRCDIRS)))) ifndef NOASM ASRCS = $(wildcard src/*.nas) CFLAGS += -DUSEASM=1 @@ -78,11 +79,11 @@ endef 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 $(CCDV) $(CXX) $(LDFLAGS) $(OBJDIR)/autostart.o \ $(filter-out %/autostart.o %/autozend.o,$(OBJS)) \ - $(OBJDIR)/autozend.o -o $(ZDOOMBIN) + snes_spc/libsnes_spc.a $(OBJDIR)/autozend.o -o $(ZDOOMBIN) endif # include any of the dep files that already exist @@ -119,23 +120,36 @@ ifdef RESTART @make -f $(firstword $(MAKEFILE_LIST)) RESTART= 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): mkdir $(OBJDIR) -toolsandpk3: tools/makewad/makewad tools/dehsupp/dehsupp tools/xlatcc/xlatcc +toolsandpk3: tools/makewad/makewad src/svnrevision_gz.h make -C wadsrc/ zdoom.pk3: toolsandpk3 ln -sf wadsrc/gzdoom.pk3 ./ +snes_spc/libsnes_spc.a: + $(MAKE) -C snes_spc/ + tools/makewad/makewad: ccdv make -C tools/makewad/ -tools/dehsupp/dehsupp: ccdv - make -C tools/dehsupp/ +tools/lemon/lemon: + $(MAKE) -C tools/lemon/ -tools/xlatcc/xlatcc: ccdv - make -C tools/xlatcc/ +tools/updaterevision/updaterevision: ccdv + make -C tools/updaterevision/ + +src/svnrevision_gz.h: tools/updaterevision/updaterevision + tools/updaterevision/updaterevision src src/svnrevision_gz.h ccdv: ccdv-posix.c @gcc -Os -s ccdv-posix.c -o ccdv diff --git a/ccdv-posix.c b/ccdv-posix.c index 98672544..191678f8 100644 --- a/ccdv-posix.c +++ b/ccdv-posix.c @@ -304,11 +304,7 @@ int main(int argc, char **argv) snprintf(gAction, sizeof(gAction), "Running %s", Basename(argv[1])); memset(gArgsStr, 0, sizeof(gArgsStr)); - if(strcmp(gAction+8, "ar") == 0) - { - snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i + 1])); - } - else if(strcmp(gAction+8, "cc") == 0 || + if(strcmp(gAction+8, "cc") == 0 || strcmp(gAction+8, "ld") == 0 || strcmp(gAction+8, "gcc") == 0 || strcmp(gAction+8, "g++") == 0 || diff --git a/src/actor.h b/src/actor.h index cf246826..6fdcf862 100644 --- a/src/actor.h +++ b/src/actor.h @@ -769,7 +769,7 @@ public: enum { S_NULL = 2, S_GENERICFREEZEDEATH = 3 }; - TArray> dynamiclights; + TArray > dynamiclights; void * lightassociations; bool hasmodel; subsector_s * subsector; diff --git a/src/b_game.cpp b/src/b_game.cpp index cdb2e52e..602814d1 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -88,7 +88,6 @@ enum static bool waitingforspawn[MAXPLAYERS]; - DCajunMaster::~DCajunMaster() { ForgetBots(); diff --git a/src/c_console.cpp b/src/c_console.cpp index 9ea68023..7b0a9d5e 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -865,7 +865,7 @@ int VPrintf (int printlevel, const char *format, va_list parms) FString outline; outline.VFormat (format, parms); - return PrintString (printlevel, outline); + return PrintString (printlevel, outline.GetChars()); } int STACK_ARGS Printf (int printlevel, const char *format, ...) diff --git a/src/d_main.cpp b/src/d_main.cpp index f6417657..7841fa5b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -242,7 +242,20 @@ static const char *IWADNames[] = "hexen.wad", "hexdd.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 }; diff --git a/src/d_net.cpp b/src/d_net.cpp index f58c6a2e..83db8f21 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -53,6 +53,7 @@ #include "a_sharedglobal.h" #include "st_start.h" #include "teaminfo.h" +#include "p_conversation.h" 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); @@ -2361,6 +2362,10 @@ void Net_DoCommand (int type, BYTE **stream, int player) } break; + case DEM_CONVERSATION: + P_ConversationCommand (player, stream); + break; + default: I_Error ("Unknown net command: %d", type); break; @@ -2451,6 +2456,31 @@ void Net_SkipCommand (int type, BYTE **stream) skip = 3 + *(*stream + 2) * 4; 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: return; } diff --git a/src/d_player.h b/src/d_player.h index 8c0d117a..594e7f9e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -320,6 +320,11 @@ public: fixed_t crouchoffset; fixed_t crouchviewdelta; + // [CW] I moved these here for multiplayer conversation support. + AActor *ConversationNPC, *ConversationPC; + angle_t ConversationNPCAngle; + bool ConversationFaceTalker; + fixed_t GetDeltaViewHeight() const { return (mo->ViewHeight + crouchviewdelta - viewheight) >> 3; diff --git a/src/d_protocol.h b/src/d_protocol.h index e28e1d3d..eedf353c 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -150,6 +150,7 @@ enum EDemoCommand DEM_ADDCONTROLLER, // 48 Player to add to the controller list. DEM_DELCONTROLLER, // 49 Player to remove from the controller list. DEM_KILLCLASSCHEAT, // 50 String: Class to kill. + DEM_CONVERSATION, // 51 Make conversations work. }; // The following are implemented by cht_DoCheat in m_cheat.cpp diff --git a/src/dobject.cpp b/src/dobject.cpp index 304d1c87..790147e9 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -418,16 +418,19 @@ void DObject::Destroy () size_t DObject::PropagateMark() { const PClass *info = GetClass(); - const size_t *offsets = info->FlatPointers; - if (offsets == NULL) + if (!PClass::bShutdown) { - const_cast(info)->BuildFlatPointers(); - offsets = info->FlatPointers; - } - while (*offsets != ~(size_t)0) - { - GC::Mark((DObject **)((BYTE *)this + *offsets)); - offsets++; + const size_t *offsets = info->FlatPointers; + if (offsets == NULL) + { + const_cast(info)->BuildFlatPointers(); + offsets = info->FlatPointers; + } + while (*offsets != ~(size_t)0) + { + GC::Mark((DObject **)((BYTE *)this + *offsets)); + offsets++; + } } return info->Size; } diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 1bf3d483..840c4e94 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -298,10 +298,14 @@ static void MarkRoot() if (playeringame[i]) players[i].PropagateMark(); } - if (SectorMarker == NULL) + if (SectorMarker == NULL && sectors != NULL) { SectorMarker = new DSectorMarker; } + else if (sectors == NULL) + { + SectorMarker = NULL; + } else { SectorMarker->SecNum = 0; diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 53c8fa75..dcd2f697 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -41,6 +41,7 @@ TArray PClass::m_RuntimeActors; TArray PClass::m_Types; PClass *PClass::TypeHash[PClass::HASH_SIZE]; +bool PClass::bShutdown; // A harmless non_NULL FlatPointer for classes without pointers. static const size_t TheEnd = ~0u; @@ -110,6 +111,7 @@ void PClass::StaticShutdown () { delete[] uniqueFPs[i]; } + bShutdown = true; } void PClass::StaticFreeData (PClass *type) diff --git a/src/dobjtype.h b/src/dobjtype.h index 88f832f9..aae53970 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -138,6 +138,8 @@ struct PClass enum { HASH_SIZE = 256 }; static PClass *TypeHash[HASH_SIZE]; + + static bool bShutdown; }; #endif diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 6f8b7c62..4ed42b02 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -476,7 +476,7 @@ DFsSection *FParser::looping_section() SDWORD rover_index = Script->MakeIndex(Rover); for(n=0; nsections[n]; // check all the sections in this hashchain @@ -494,7 +494,7 @@ DFsSection *FParser::looping_section() } current = current->next; } - } + } return best; // return the best one found } @@ -510,10 +510,10 @@ void FParser::SF_Continue(void) DFsSection *section; if(!(section = looping_section()) ) // no loop found - { + { script_error("continue() not in loop\n"); return; - } + } Rover = Script->SectionEnd(section); // jump to the closing brace } @@ -529,10 +529,10 @@ void FParser::SF_Break(void) DFsSection *section; if(!(section = looping_section()) ) - { + { script_error("break() not in loop\n"); return; - } + } Rover = Script->SectionEnd(section) + 1; // jump out of the loop } @@ -761,12 +761,12 @@ void FParser::SF_PlayerName(void) int plnum; if(!t_argc) - { + { player_t *pl=NULL; if (Script->trigger) pl = Script->trigger->player; if(pl) plnum = pl - players; else plnum=-1; - } + } else plnum = T_GetPlayerNum(t_argv[0]); @@ -820,8 +820,17 @@ void FParser::SF_PlayerObj(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; if(mo && mo->player) // haleyjd: added mo->player @@ -924,11 +933,17 @@ void FParser::SF_RemoveObj(void) void FParser::SF_KillObj(void) { + // use trigger object if not specified AActor *mo; - - if(t_argc) mo = actorvalue(t_argv[0]); - else mo = Script->trigger; // default to trigger object - + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; // default to trigger object + } + if(mo) { // ensure the thing can be killed @@ -949,8 +964,17 @@ void FParser::SF_KillObj(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.value.f = mo ? mo->x : 0; // null ptr check } @@ -963,8 +987,17 @@ void FParser::SF_ObjX(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.value.f = mo ? mo->y : 0; // null ptr check } @@ -977,8 +1010,17 @@ void FParser::SF_ObjY(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.value.f = mo ? mo->z : 0; // null ptr check } @@ -992,8 +1034,17 @@ void FParser::SF_ObjZ(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.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) { // 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.value.i = mo ? mo->Sector->tag : 0; // nullptr check } @@ -1113,8 +1172,16 @@ void FParser::SF_ObjSector(void) void FParser::SF_ObjHealth(void) { // 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.value.i = mo ? mo->health : 0; } @@ -1155,7 +1222,7 @@ void FParser::SF_ObjFlag(void) // make the new flag mo->flags |= (!!intvalue(t_argv[2])) << flagnum; - } + } } t_return.type = svt_int; if (mo && flagnum<26) @@ -1352,7 +1419,7 @@ void FParser::SF_PointToDist(void) // Doing this in floating point is actually faster with modern computers! float x = floatvalue(t_argv[2]) - floatvalue(t_argv[0]); float y = floatvalue(t_argv[3]) - floatvalue(t_argv[1]); - + t_return.type = svt_fixed; t_return.value.f = (fixed_t)(sqrtf(x*x+y*y)*65536.f); } @@ -1875,7 +1942,7 @@ void DLightLevel::Tick() } else { - // decrease lightlevel + // decrease lightlevel if(m_Sector->lightlevel - speed <= destlevel) { // stop changing light level @@ -1987,7 +2054,7 @@ void FParser::SF_SectorColormap(void) int i = -1; 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]); @@ -1995,7 +2062,7 @@ void FParser::SF_SectorColormap(void) secnum = T_FindSectorFromTag(tagnum, -1); 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 = §ors[secnum]; @@ -2463,15 +2530,17 @@ void FParser::SF_Gamemode(void) void FParser::SF_IsPlayerObj(void) { + // use trigger object if not specified AActor *mo; - - if(!t_argc) + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else { mo = Script->trigger; } - else - mo = actorvalue(t_argv[0]); - + t_return.type = svt_int; t_return.value.i = (mo && mo->player) ? 1 : 0; } @@ -2770,9 +2839,9 @@ void FParser::SF_PlayerWeapon() "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" }; - int playernum; - int weaponnum; - int newweapon; + int playernum; + int weaponnum; + int newweapon; if (CheckArgs(2)) { @@ -3014,7 +3083,7 @@ void FParser::SF_MoveCamera(void) // I have to use floats for the math where angles are divided // by fixed values. double fangledist, fanglestep, fmovestep; - int angledir; + int angledir; AActor* target; int moved; int quad1, quad2; @@ -3177,17 +3246,21 @@ void FParser::SF_MoveCamera(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) - mo = Script->trigger; - else - mo = actorvalue(t_argv[0]); - - if(mo) - { - mo->Activate(Script->trigger); - } + if(mo) + { + mo->Activate(Script->trigger); + } } //========================================================================== @@ -3316,9 +3389,9 @@ void FParser::SF_SpawnExplosion() void FParser::SF_RadiusAttack() { - AActor *spot; - AActor *source; - int damage; + AActor *spot; + AActor *source; + int damage; if (CheckArgs(3)) { @@ -3367,13 +3440,22 @@ void FParser::SF_SetObjPosition() void FParser::SF_TestLocation() { - AActor *mo = t_argc ? actorvalue(t_argv[0]) : Script->trigger; - - if (mo) + // use trigger object if not specified + AActor *mo; + if(t_argc) { - t_return.type = svt_int; - t_return.value.f = !!P_TestMobjLocation(mo); - } + mo = actorvalue(t_argv[0]); + } + 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 { - 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) { @@ -3411,8 +3502,17 @@ void FParser::SF_HealObj() //no pain sound 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; if(mo && (mo->health <= 0 || mo->flags&MF_CORPSE)) t_return.value.i = 1; @@ -3428,8 +3528,8 @@ void FParser::SF_ObjDead() void FParser::SF_SpawnMissile() { - AActor *mobj; - AActor *target; + AActor *mobj; + AActor *target; const PClass * PClass; if (CheckArgs(3)) @@ -3450,9 +3550,9 @@ void FParser::SF_SpawnMissile() void FParser::SF_MapThingNumExist() { - TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; - int intval; + int intval; if (CheckArgs(1)) { @@ -3479,10 +3579,10 @@ void FParser::SF_MapThingNumExist() void FParser::SF_MapThings() { - TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; 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() { int state; - AActor *mo; + AActor *mo = NULL; if (CheckArgs(1)) { @@ -3691,7 +3791,15 @@ void FParser::SF_LineAttack() void FParser::SF_ObjType() { // 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;iGetClass() == ActorTypes[i]) { @@ -3851,38 +3959,38 @@ void FParser::SF_DeleteHUPic() { if (CheckArgs(1)) { - if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1) - script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0])); + if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1) + script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0])); } } void FParser::SF_ModifyHUPic() { - if (t_argc != 4) - { - script_error("modifyhupic: invalid number of arguments\n"); - return; - } + if (t_argc != 4) + { + script_error("modifyhupic: invalid number of arguments\n"); + 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), 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() { - if (t_argc != 2) - { - script_error("sethupicdisplay: invalud number of arguments\n"); - return; - } + if (t_argc != 2) + { + script_error("sethupicdisplay: invalud number of arguments\n"); + return; + } - 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])); + 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])); } @@ -3900,47 +4008,47 @@ void FParser::SF_SetCorona(void) return; } - 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 ival = t_argv[2].value.i; // the value of what we modify - double fval = ((double) t_argv[2].value.f / FRACUNIT); + 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 ival = t_argv[2].value.i; // the value of what we modify + double fval = ((double) t_argv[2].value.f / FRACUNIT); /* - switch (what) - { - case 0: - lspr[num].type = ival; - break; - case 1: - lspr[num].light_xoffset = fval; - break; - case 2: - lspr[num].light_yoffset = fval; - break; - case 3: - if (t_argv[2].type == svt_string) - lspr[num].corona_color = String2Hex(t_argv[2].value.s); - else - memcpy(&lspr[num].corona_color, &ival, sizeof(int)); - break; - case 4: - lspr[num].corona_radius = fval; - break; - case 5: - if (t_argv[2].type == svt_string) - lspr[num].dynamic_color = String2Hex(t_argv[2].value.s); - else - memcpy(&lspr[num].dynamic_color, &ival, sizeof(int)); - break; - case 6: - lspr[num].dynamic_radius = fval; - lspr[num].dynamic_sqrradius = sqrt(lspr[num].dynamic_radius); - break; - default: - CONS_Printf("Error in setcorona\n"); - break; - } - */ + switch (what) + { + case 0: + lspr[num].type = ival; + break; + case 1: + lspr[num].light_xoffset = fval; + break; + case 2: + lspr[num].light_yoffset = fval; + break; + case 3: + if (t_argv[2].type == svt_string) + lspr[num].corona_color = String2Hex(t_argv[2].value.s); + else + memcpy(&lspr[num].corona_color, &ival, sizeof(int)); + break; + case 4: + lspr[num].corona_radius = fval; + break; + case 5: + if (t_argv[2].type == svt_string) + lspr[num].dynamic_color = String2Hex(t_argv[2].value.s); + else + memcpy(&lspr[num].dynamic_color, &ival, sizeof(int)); + break; + case 6: + lspr[num].dynamic_radius = fval; + lspr[num].dynamic_sqrradius = sqrt(lspr[num].dynamic_radius); + break; + default: + CONS_Printf("Error in setcorona\n"); + break; + } + */ // no use for this! t_return.type = svt_int; @@ -4074,7 +4182,7 @@ again: } else { - while (mo=it.Next()) + while ((mo=it.Next())) { if (mo->IsA(pClass) && mo->health>0) count++; } @@ -4361,10 +4469,10 @@ void FParser::SF_Wait() DRunningScript *runscr; if(t_argc != 1) - { + { script_error("incorrect arguments to function\n"); return; - } + } runscr = SaveCurrentScript(); @@ -4385,10 +4493,10 @@ void FParser::SF_TagWait() DRunningScript *runscr; if(t_argc != 1) - { + { script_error("incorrect arguments to function\n"); return; - } + } runscr = SaveCurrentScript(); @@ -4408,10 +4516,10 @@ void FParser::SF_ScriptWait() DRunningScript *runscr; if(t_argc != 1) - { + { script_error("insufficient arguments to function\n"); return; - } + } runscr = SaveCurrentScript(); @@ -4451,10 +4559,10 @@ void FParser::SF_ScriptWaitPre() void FParser::SF_StartScript() { if(t_argc != 1) - { + { script_error("incorrect arguments to function\n"); return; - } + } int snum = intvalue(t_argv[0]); @@ -4493,15 +4601,15 @@ void FParser::SF_ScriptRunning() int snum = 0; if(t_argc < 1) - { + { script_error("not enough arguments to function\n"); return; - } + } snum = intvalue(t_argv[0]); for(current = DFraggleThinker::ActiveThinker->RunningScripts->next; current; current=current->next) - { + { if(current->script->scriptnum == snum) { // script found so return @@ -4509,7 +4617,7 @@ void FParser::SF_ScriptRunning() t_return.value.i = 1; return; } - } + } // script not found t_return.type = svt_int; @@ -4578,27 +4686,27 @@ void init_functions(void) new_function("playertip", &FParser::SF_PlayerTip); new_function("playeringame", &FParser::SF_PlayerInGame); 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("isplayerobj", &FParser::SF_IsPlayerObj); new_function("isobjplayer", &FParser::SF_IsPlayerObj); new_function("skincolor", &FParser::SF_SkinColor); new_function("playerkeys", &FParser::SF_PlayerKeys); 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("playerselwep", &FParser::SF_PlayerSelectedWeapon); // mobj stuff new_function("spawn", &FParser::SF_Spawn); 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("removeobj", &FParser::SF_RemoveObj); new_function("objx", &FParser::SF_ObjX); new_function("objy", &FParser::SF_ObjY); 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("silentteleport", &FParser::SF_SilentTeleport); new_function("damageobj", &FParser::SF_DamageObj); @@ -4618,10 +4726,10 @@ void init_functions(void) new_function("objmomy", &FParser::SF_MobjMomy); new_function("objmomz", &FParser::SF_MobjMomz); - new_function("spawnmissile", &FParser::SF_SpawnMissile); - new_function("mapthings", &FParser::SF_MapThings); - new_function("objtype", &FParser::SF_ObjType); - new_function("mapthingnumexist", &FParser::SF_MapThingNumExist); + new_function("spawnmissile", &FParser::SF_SpawnMissile); + new_function("mapthings", &FParser::SF_MapThings); + new_function("objtype", &FParser::SF_ObjType); + new_function("mapthingnumexist", &FParser::SF_MapThingNumExist); new_function("objstate", &FParser::SF_ObjState); new_function("resurrect", &FParser::SF_Resurrect); new_function("lineattack", &FParser::SF_LineAttack); @@ -4665,19 +4773,19 @@ void init_functions(void) new_function("opendoor", &FParser::SF_OpenDoor); new_function("closedoor", &FParser::SF_CloseDoor); - // HU Graphics - new_function("newhupic", &FParser::SF_NewHUPic); - new_function("createpic", &FParser::SF_NewHUPic); - new_function("deletehupic", &FParser::SF_DeleteHUPic); - new_function("modifyhupic", &FParser::SF_ModifyHUPic); - new_function("modifypic", &FParser::SF_ModifyHUPic); - new_function("sethupicdisplay", &FParser::SF_SetHUPicDisplay); - new_function("setpicvisible", &FParser::SF_SetHUPicDisplay); + // HU Graphics + new_function("newhupic", &FParser::SF_NewHUPic); + new_function("createpic", &FParser::SF_NewHUPic); + new_function("deletehupic", &FParser::SF_DeleteHUPic); + new_function("modifyhupic", &FParser::SF_ModifyHUPic); + new_function("modifypic", &FParser::SF_ModifyHUPic); + new_function("sethupicdisplay", &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("checkcvar", &FParser::SF_CheckCVar); + new_function("checkcvar", &FParser::SF_CheckCVar); new_function("setlinetexture", &FParser::SF_SetLineTexture); new_function("linetrigger", &FParser::SF_LineTrigger); new_function("lineflag", &FParser::SF_LineFlag); diff --git a/src/fragglescript/t_load.cpp b/src/fragglescript/t_load.cpp index e0d7189d..81a10371 100644 --- a/src/fragglescript/t_load.cpp +++ b/src/fragglescript/t_load.cpp @@ -328,7 +328,7 @@ void T_RegisterSpawnThing(AActor * ac) { if (DFraggleThinker::ActiveThinker) { - TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; SpawnedThings[SpawnedThings.Size()-1] = ac; } } diff --git a/src/fragglescript/t_script.h b/src/fragglescript/t_script.h index b48b5fde..30c72740 100644 --- a/src/fragglescript/t_script.h +++ b/src/fragglescript/t_script.h @@ -655,7 +655,7 @@ public: TObjPtr LevelScript; TObjPtr RunningScripts; - TArray> SpawnedThings; + TArray > SpawnedThings; DFraggleThinker(); void Destroy(); diff --git a/src/fragglescript/t_variable.cpp b/src/fragglescript/t_variable.cpp index 3988e92c..95e8613d 100644 --- a/src/fragglescript/t_variable.cpp +++ b/src/fragglescript/t_variable.cpp @@ -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; @@ -151,7 +151,7 @@ AActor *actorvalue(const svalue_t &svalue) } else { - TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; // this requires some creativity. We use the intvalue // as the thing number of a thing in the level. intval = intvalue(svalue); diff --git a/src/g_doom/doom_sbar.cpp b/src/g_doom/doom_sbar.cpp index c97b3961..d5c852e7 100644 --- a/src/g_doom/doom_sbar.cpp +++ b/src/g_doom/doom_sbar.cpp @@ -125,6 +125,11 @@ public: OldFaceIndex = -1; } + void AddFaceToImageCollection (void *skn, FImageCollection *images) + { + AddFaceToImageCollectionActual (skn, images, true); + } + void MultiplayerChanged () { DBaseStatusBar::MultiplayerChanged (); @@ -989,24 +994,6 @@ private: 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 Faces; diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index f7101d97..58d18189 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -165,6 +165,25 @@ public: 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); void Destroy (); @@ -190,7 +209,8 @@ public: virtual void AttachToPlayer (player_s *player); virtual void FlashCrosshair (); 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 ScreenSizeChanged (); virtual void MultiplayerChanged (); @@ -222,6 +242,8 @@ protected: void GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const; + void AddFaceToImageCollectionActual (void *skn, FImageCollection *images, bool isDoom); + public: AInventory *ValidateInvFirst (int numVisible) const; void DrawCrosshair (); diff --git a/src/g_shared/sbarinfo.h b/src/g_shared/sbarinfo.h index 548961a9..217f89dd 100644 --- a/src/g_shared/sbarinfo.h +++ b/src/g_shared/sbarinfo.h @@ -86,6 +86,7 @@ struct SBarInfo bool interpolateHealth; bool interpolateArmor; bool completeBorder; + bool lowerHealthCap; char spacingCharacter; int interpolationSpeed; int armorInterpolationSpeed; @@ -119,7 +120,7 @@ struct 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 reset(); 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 MugShotStates; @@ -197,6 +198,7 @@ enum //drawnumber flags DRAWNUMBER_SECRETS = 4096, DRAWNUMBER_TOTALSECRETS = 8192, DRAWNUMBER_ARMORCLASS = 16384, + DRAWNUMBER_GLOBALVAR = 32768, }; enum //drawbar flags (will go into special2) @@ -253,6 +255,14 @@ enum //event flags 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 { SBARINFO_BASE, @@ -261,6 +271,7 @@ enum //Key words SBARINFO_INTERPOLATEARMOR, SBARINFO_COMPLETEBORDER, SBARINFO_MONOSPACEFONTS, + SBARINFO_LOWERHEALTHCAP, SBARINFO_STATUSBAR, SBARINFO_MUGSHOT, }; @@ -293,7 +304,9 @@ enum //Bar key words SBARINFO_DRAWKEYBAR, SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, + SBARINFO_ASPECTRATIO, SBARINFO_WEAPONAMMO, + SBARINFO_ININVENTORY, }; //All this so I can change the mugshot state in ACS... @@ -321,13 +334,14 @@ public: void Tick(); void ReceivedWeapon (AWeapon *weapon); void FlashItem(const PClass *itemtype); + void ShowPop(int popnum); void SetMugShotState(const char* stateName, bool waitTillDone=false); private: void doCommands(SBarInfoBlock &block); 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 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); void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow, int counterx, int countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter); @@ -349,6 +363,7 @@ private: int mugshotHealth; int chainWiggle; int artiflash; + int currentPopup; unsigned int invBarOffset; FBarShader shader_horz_normal; FBarShader shader_horz_reverse; diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp index b65d0251..5b2fe7c2 100644 --- a/src/g_shared/sbarinfo_display.cpp +++ b/src/g_shared/sbarinfo_display.cpp @@ -83,13 +83,13 @@ MugShotFrame::~MugShotFrame() } //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; if(index > (signed int) (graphic.Size()-1)) index = graphic.Size()-1; 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])); sprite[3+strlen(graphic[index])] = '\0'; 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]; } + for (i = 0;i < numskins;i++) + { + AddFaceToImageCollection (&skins[i], &Images); + } invBarOffset = SBarInfoScript->Images.Size(); Images.Init(&patchnames[0], patchnames.Size()); drawingFont = V_GetFont("ConFont"); @@ -313,6 +317,15 @@ void DSBarInfo::Draw (EHudState state) else if(state == HUD_Fullscreen) 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 () @@ -409,6 +422,15 @@ void DSBarInfo::FlashItem(const PClass *itemtype) 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 //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. @@ -543,25 +565,27 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) } break; case SBARINFO_DRAWNUMBER: + { + int value = cmd.value; if(drawingFont != cmd.font) { drawingFont = cmd.font; } if(cmd.flags == DRAWNUMBER_HEALTH) { - cmd.value = health; - if(cmd.value < 0) //health shouldn't display negatives + value = health; + if(SBarInfoScript->lowerHealthCap && cmd.value < 0) //health shouldn't display negatives { - cmd.value = 0; + value = 0; } } else if(cmd.flags == DRAWNUMBER_ARMOR) { - cmd.value = armorAmount; + value = armorAmount; } else if(cmd.flags == DRAWNUMBER_AMMO1) { - cmd.value = ammocount1; + value = ammocount1; if(ammo1 == NULL) //no ammo, do not draw { continue; @@ -569,7 +593,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) } else if(cmd.flags == DRAWNUMBER_AMMO2) { - cmd.value = ammocount2; + value = ammocount2; if(ammo2 == NULL) //no ammo, do not draw { continue; @@ -581,11 +605,11 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) AInventory* item = CPlayer->mo->FindInventory(ammo); if(item != NULL) { - cmd.value = item->Amount; + value = item->Amount; } else { - cmd.value = 0; + value = 0; } } else if(cmd.flags == DRAWNUMBER_AMMOCAPACITY) @@ -594,61 +618,64 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) AInventory* item = CPlayer->mo->FindInventory(ammo); if(item != NULL) { - cmd.value = item->MaxAmount; + value = item->MaxAmount; } else { - cmd.value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount; + value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount; } } else if(cmd.flags == DRAWNUMBER_FRAGS) - cmd.value = CPlayer->fragcount; + value = CPlayer->fragcount; else if(cmd.flags == DRAWNUMBER_KILLS) - cmd.value = level.killed_monsters; + value = level.killed_monsters; else if(cmd.flags == DRAWNUMBER_MONSTERS) - cmd.value = level.total_monsters; + value = level.total_monsters; else if(cmd.flags == DRAWNUMBER_ITEMS) - cmd.value = level.found_items; + value = level.found_items; else if(cmd.flags == DRAWNUMBER_TOTALITEMS) - cmd.value = level.total_items; + value = level.total_items; else if(cmd.flags == DRAWNUMBER_SECRETS) - cmd.value = level.found_secrets; + value = level.found_secrets; else if(cmd.flags == DRAWNUMBER_TOTALSECRETS) - cmd.value = level.total_secrets; + value = level.total_secrets; else if(cmd.flags == DRAWNUMBER_ARMORCLASS) { AHexenArmor *harmor = CPlayer->mo->FindInventory(); 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]; } //Hexen counts basic armor also so we should too. 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) { AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); if(item != NULL) { - cmd.value = item->Amount; + value = item->Amount; } else { - cmd.value = 0; + value = 0; } } 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 - 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 - 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; + } case SBARINFO_DRAWMUGSHOT: { bool xdth = false; @@ -657,7 +684,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) xdth = true; if(cmd.flags & DRAWMUGSHOT_ANIMATEDGODMODE) animatedgodmode = true; - DrawFace(cmd.special, xdth, animatedgodmode, cmd.x, cmd.y); + DrawFace(cmd.string[0], cmd.special, xdth, animatedgodmode, cmd.x, cmd.y); break; } case SBARINFO_DRAWSELECTEDINVENTORY: @@ -1000,7 +1027,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) { 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; case SBARINFO_DRAWKEYBAR: { @@ -1046,6 +1073,12 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) } break; } + case SBARINFO_ASPECTRATIO: + if(CheckRatio(screen->GetWidth(), screen->GetHeight()) == cmd.value) + { + doCommands(cmd.subBlock); + } + break; case SBARINFO_WEAPONAMMO: if(CPlayer->ReadyWeapon != NULL) { @@ -1090,6 +1123,30 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) } } 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; 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)) - DrawImage(texture, x, y, getTranslation()); + { + screen->DrawTexture(texture, x, y, + DTA_DestWidth, w, + DTA_DestHeight, h, + DTA_Translation, getTranslation(), + TAG_DONE); + } 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, DTA_DestWidth, w, 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 x += (character->LeftOffset+1); //ignore x offsets since we adapt to character size - DrawImage(character, x, y, drawingFont->GetColorTranslation(translation)); - x += width + spacing; + int rx = x + ST_X; + 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') - 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++; } } @@ -1165,14 +1238,23 @@ void DSBarInfo::DrawNumber(int num, int len, int x, int y, EColorRange translati } //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 level = 0; for(level = 0;CPlayer->health < (accuracy-level-1)*(CPlayer->mo->GetMaxHealth()/accuracy);level++); 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); } } diff --git a/src/g_shared/sbarinfo_parser.cpp b/src/g_shared/sbarinfo_parser.cpp index 51be9a51..6d628c48 100644 --- a/src/g_shared/sbarinfo_parser.cpp +++ b/src/g_shared/sbarinfo_parser.cpp @@ -57,6 +57,7 @@ static const char *SBarInfoTopLevel[] = "interpolatearmor", "completeborder", "monospacefonts", + "lowerhealthcap", "statusbar", "mugshot", NULL @@ -91,7 +92,9 @@ static const char *SBarInfoRoutineLevel[] = "drawkeybar", "gamemode", "playerclass", + "aspectratio", "weaponammo", //event + "ininventory", NULL }; @@ -230,6 +233,18 @@ void SBarInfo::ParseSBarInfo(int lump) } sc.MustGetToken(';'); break; + case SBARINFO_LOWERHEALTHCAP: + if(sc.CheckToken(TK_False)) + { + lowerHealthCap = false; + } + else + { + sc.MustGetToken(TK_True); + lowerHealthCap = true; + } + sc.MustGetToken(';'); + break; case SBARINFO_STATUSBAR: { 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; else if(sc.Compare("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 { cmd.flags = DRAWNUMBER_INVENTORY; @@ -738,7 +761,7 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) cmd.flags = DRAWNUMBER_INVENTORY; cmd.setString(sc, sc.String, 0); 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); } @@ -843,6 +866,11 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) cmd.setString(sc, sc.String, 0, -1, false); sc.MustGetToken(','); this->getCoordinates(sc, cmd); + if(sc.CheckToken(',')) //spacing + { + sc.MustGetToken(TK_IntConst); + cmd.special = sc.Number; + } sc.MustGetToken(';'); break; case SBARINFO_DRAWKEYBAR: @@ -908,6 +936,21 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) FinishPlayerClass: this->ParseSBarInfoBlock(sc, cmd.subBlock); 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: sc.MustGetToken(TK_Identifier); if(sc.Compare("not")) @@ -939,6 +982,39 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) sc.MustGetToken('{'); this->ParseSBarInfoBlock(sc, cmd.subBlock); 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); } @@ -1044,6 +1120,7 @@ void SBarInfo::Init() interpolateHealth = false; interpolateArmor = false; completeBorder = false; + lowerHealthCap = true; interpolationSpeed = 8; armorInterpolationSpeed = 8; height = 0; diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index c8209eb4..74e5a0e0 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -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 () { @@ -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 diff --git a/src/g_strife/a_strifeglobal.h b/src/g_strife/a_strifeglobal.h index 3474bcd5..bc485614 100644 --- a/src/g_strife/a_strifeglobal.h +++ b/src/g_strife/a_strifeglobal.h @@ -81,6 +81,13 @@ public: bool TryPickup (AActor *toucher); }; +class ASlideshowStarter : public ADummyStrifeItem +{ + DECLARE_STATELESS_ACTOR (ASlideshowStarter, ADummyStrifeItem) +public: + bool TryPickup (AActor *toucher); +}; + class AStrifeWeapon : public AWeapon { DECLARE_STATELESS_ACTOR (AStrifeWeapon, AWeapon) diff --git a/src/g_strife/a_strifeitems.cpp b/src/g_strife/a_strifeitems.cpp index 6335b823..4014ed03 100644 --- a/src/g_strife/a_strifeitems.cpp +++ b/src/g_strife/a_strifeitems.cpp @@ -514,13 +514,6 @@ bool AUpgradeAccuracy::TryPickup (AActor *toucher) // Start a slideshow -------------------------------------------------------- -class ASlideshowStarter : public ADummyStrifeItem -{ - DECLARE_STATELESS_ACTOR (ASlideshowStarter, ADummyStrifeItem) -public: - bool TryPickup (AActor *toucher); -}; - IMPLEMENT_STATELESS_ACTOR (ASlideshowStarter, Strife, -1, 0) PROP_StrifeType (343) END_DEFAULTS diff --git a/src/gl/a_dynlight.cpp b/src/gl/a_dynlight.cpp index 5f3a5e02..f8f4ee5e 100644 --- a/src/gl/a_dynlight.cpp +++ b/src/gl/a_dynlight.cpp @@ -692,7 +692,7 @@ CCMD(listlights) ADynamicLight * dl; TThinkerIterator it; - while (dl=it.Next()) + while ((dl=it.Next())) { walls=0; sectors=0; diff --git a/src/m_menu.h b/src/m_menu.h index 4419828c..679ee604 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -71,8 +71,7 @@ void M_OptInit (void); // [RH] Initialize the video modes menu void M_InitVideoModesMenu (void); -struct menu_s; -void M_SwitchMenu (struct menu_s *menu); +void M_SwitchMenu (struct menu_t *menu); void M_PopMenuStack (void); @@ -100,6 +99,7 @@ typedef enum { discrete, discretes, cdiscrete, + ediscrete, discrete_guid, control, screenres, @@ -145,8 +145,9 @@ typedef struct menuitem_s { char *res3; } d; union { - struct value_s *values; + struct value_t *values; struct valuestring_t *valuestrings; + struct valueenum_t *enumvalues; GUIDName *guidvalues; char *command; void (*cfunc)(FBaseCVar *cvar, float newval); @@ -157,7 +158,7 @@ typedef struct menuitem_s { } e; } menuitem_t; -typedef struct menu_s { +struct menu_t { const char *texttitle; int lastOn; int numitems; @@ -169,18 +170,23 @@ typedef struct menu_s { void (*PreDraw)(void); bool DontDim; void (*EscapeHandler)(void); -} menu_t; +}; -typedef struct value_s { +struct value_t { float value; const char *name; -} value_t; +}; struct valuestring_t { float value; FString name; }; +struct valueenum_t { + const char *value; // Value of cvar + const char *name; // Name on menu +}; + typedef struct { // -1 = no cursor here, 1 = ok, 2 = arrows ok diff --git a/src/m_options.cpp b/src/m_options.cpp index e37f9aa1..fe0ac1d0 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -1144,62 +1144,131 @@ EXTERN_CVAR (Float, snd_movievolume) #endif EXTERN_CVAR (Bool, snd_flipstereo) 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 AdvSoundOptions (); -static void ChooseMIDI (); static value_t SampleRates[] = { - { 4000.f, "4000 Hz" }, - { 8000.f, "8000 Hz" }, - { 11025.f, "11025 Hz" }, - { 22050.f, "22050 Hz" }, - { 32000.f, "32000 Hz" }, - { 44100.f, "44100 Hz" }, - { 48000.f, "48000 Hz" }, - { 96000.f, "96000 Hz" } + { 0.f, "Default" }, + { 4000.f, "4000 Hz" }, + { 8000.f, "8000 Hz" }, + { 11025.f, "11025 Hz" }, + { 22050.f, "22050 Hz" }, + { 32000.f, "32000 Hz" }, + { 44100.f, "44100 Hz" }, + { 48000.f, "48000 Hz" } }; static value_t BufferSizes[] = { - { 0.f, "Default" }, - { 20.f, "20 ms" }, - { 40.f, "40 ms" }, - { 60.f, "60 ms" }, - { 80.f, "80 ms" }, - { 100.f, "100 ms" }, - { 120.f, "120 ms" }, - { 140.f, "140 ms" }, - { 160.f, "160 ms" }, - { 180.f, "180 ms" }, - { 200.f, "200 ms" }, + { 0.f, "Default" }, + { 64.f, "64 samples" }, + { 128.f, "128 samples" }, + { 256.f, "256 samples" }, + { 512.f, "512 samples" }, + { 1024.f, "1024 samples" }, + { 2048.f, "2048 samples" }, + { 4096.f, "4096 samples" } +}; + +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[] = { - { slider, "Sound effects volume", {&snd_sfxvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, -#ifdef _WIN32 + { slider, "Sounds volume", {&snd_sfxvolume}, {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} }, -#else - { slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, -#endif + { discrete, "MIDI device", {&snd_mididevice}, {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, "Flip Stereo Channels", {&snd_flipstereo}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Random Pitch Variations", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, + { discrete, "Underwater reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, + { discrete, "Randomize pitches", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, { 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} }, - { discrete, "Sample Rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} }, - { discrete, "Buffer Size", {&snd_buffersize}, {11.0}, {0.0}, {0.0}, {BufferSizes} }, + { ediscrete,"Output system", {&snd_output}, {countof(Outputs)}, {0.0}, {0.0}, {(value_t *)Outputs} }, + { 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} }, - { 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 + { more, "Advanced options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} }, }; static menu_t SoundMenu = @@ -1211,29 +1280,7 @@ static menu_t SoundMenu = SoundItems, }; -#ifdef _WIN32 -/*======================================= - * - * 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 +#define MIDI_DEVICE_ITEM 2 /*======================================= * @@ -1521,6 +1568,42 @@ int M_FindCurGUID (const GUID &guid, GUIDName *values, int numvals) 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 () { EColorRange color; @@ -1707,6 +1790,16 @@ void M_OptDrawer () } 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: { int v, vals; @@ -2186,6 +2279,13 @@ void M_OptResponder (event_t *ev) S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); break; + case ediscrete: + value = item->a.cvar->GetGenericRep(CVAR_String); + value.String = const_cast(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: { int cur; @@ -2328,6 +2428,13 @@ void M_OptResponder (event_t *ev) S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); break; + case ediscrete: + value = item->a.cvar->GetGenericRep(CVAR_String); + value.String = const_cast(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: { int cur; @@ -2893,9 +3000,19 @@ CCMD (menu_joystick) JoystickOptions (); } +static void FreeMIDIMenuList() +{ + if (SoundItems[MIDI_DEVICE_ITEM].e.values != NULL) + { + delete[] SoundItems[MIDI_DEVICE_ITEM].e.values; + } +} + 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) @@ -2917,22 +3034,6 @@ CCMD (menu_advsound) 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 char snd_reset[] = "snd_reset"; diff --git a/src/mus2midi.cpp b/src/mus2midi.cpp index db2c5e73..804adc11 100644 --- a/src/mus2midi.cpp +++ b/src/mus2midi.cpp @@ -93,7 +93,7 @@ static size_t ReadVarLen (const BYTE *buf, int *time_out) return ofs; } -static size_t WriteVarLen (FILE *file, int time) +static size_t WriteVarLen (TArray &file, int time) { long buffer; size_t ofs; @@ -105,7 +105,7 @@ static size_t WriteVarLen (FILE *file, int time) } for (ofs = 0;;) { - fputc (buffer & 0xff, file); + file.Push(BYTE(buffer & 0xff)); if (buffer & 0x80) buffer >>= 8; else @@ -114,7 +114,7 @@ static size_t WriteVarLen (FILE *file, int time) return ofs; } -bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) +bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) { BYTE midStatus, midArgs, mid1, mid2; size_t mus_p, maxmus_p; @@ -125,7 +125,6 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) BYTE lastVel[16]; SBYTE chanMap[16]; int chanCount; - int dupCount = 0; long trackLen; // Do some validation of the MUS file @@ -136,7 +135,9 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) return false; // 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); 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, // so sets its volume to 127. - fputc (0, outFile); - fputc (0xB0 | chanCount, outFile); - fputc (7, outFile); - fputc (127, outFile); + outFile.Push(0); + outFile.Push(0xB0 | chanCount); + outFile.Push(7); + outFile.Push(127); chanMap[channel] = chanCount++; if (chanCount == 9) ++chanCount; @@ -237,20 +238,15 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) WriteVarLen (outFile, deltaTime); - if (midStatus == status) - { - ++dupCount; - fputc (mid1, outFile); - if (!midArgs) - fputc (mid2, outFile); - } - else + if (midStatus != status) { status = midStatus; - fputc (status, outFile); - fputc (mid1, outFile); - if (!midArgs) - fputc (mid2, outFile); + outFile.Push(status); + } + outFile.Push(mid1); + if (midArgs == 0) + { + outFile.Push(mid2); } if (event & 128) { @@ -263,12 +259,20 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) } // fill in track length - trackLen = ftell (outFile) - 22; - fseek (outFile, 18, SEEK_SET); - fputc ((trackLen >> 24) & 255, outFile); - fputc ((trackLen >> 16) & 255, outFile); - fputc ((trackLen >> 8) & 255, outFile); - fputc (trackLen & 255, outFile); - + trackLen = outFile.Size() - 22; + outFile[18] = BYTE((trackLen >> 24) & 255); + outFile[19] = BYTE((trackLen >> 16) & 255); + outFile[20] = BYTE((trackLen >> 8) & 255); + outFile[21] = BYTE(trackLen & 255); return true; } + +bool ProduceMIDI(const BYTE *musBuf, FILE *outFile) +{ + TArray work; + if (ProduceMIDI(musBuf, work)) + { + return fwrite(&work[0], 1, work.Size(), outFile) == work.Size(); + } + return false; +} diff --git a/src/mus2midi.h b/src/mus2midi.h index 61e32070..e36da678 100644 --- a/src/mus2midi.h +++ b/src/mus2midi.h @@ -73,6 +73,7 @@ typedef struct WORD Pad; } MUSHeader; +bool ProduceMIDI (const BYTE *musBuf, TArray &outFile); bool ProduceMIDI (const BYTE *musBuf, FILE *outFile); #endif //__MUS2MIDI_H__ diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index da2a5f02..e7aaedd2 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -117,9 +117,8 @@ static void ConversationMenuEscaped (); static FStrifeDialogueNode *CurNode, *PrevNode; static FBrokenLines *DialogueLines; -static AActor *ConversationNPC, *ConversationPC; -static angle_t ConversationNPCAngle; -static bool ConversationFaceTalker; + +static bool Conversation_TakeStuff; #define NUM_RANDOM_LINES 10 #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; if (itemtype == NULL || amount == 0) return true; - item = ConversationPC->FindInventory (itemtype); + item = player->ConversationPC->FindInventory (itemtype); if (item == NULL) 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) return; @@ -611,15 +610,10 @@ static void TakeStrifeItem (const PClass *itemtype, int amount) if (itemtype == RUNTIME_CLASS(ASigil)) return; - AInventory *item = ConversationPC->FindInventory (itemtype); - if (item != NULL) - { - item->Amount -= amount; - if (item->Amount <= 0) - { - item->Destroy (); - } - } + Net_WriteByte (DEM_CONVERSATION); + Net_WriteByte (CONV_TAKEINVENTORY); + Net_WriteString (itemtype->TypeName.GetChars ()); + Net_WriteWord (amount); } CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) @@ -633,7 +627,6 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) // P_StartConversation // // 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; 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->player->momx = pc->player->momy = 0; - if (pc->player - players != consoleplayer) - return; - - ConversationPC = pc; - ConversationNPC = npc; + pc->player->ConversationPC = pc; + pc->player->ConversationNPC = npc; CurNode = npc->Conversation; @@ -662,10 +663,10 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang } npc->reactiontime = 2; - ConversationFaceTalker = facetalker; + pc->player->ConversationFaceTalker = facetalker; if (saveangle) { - ConversationNPCAngle = npc->angle; + pc->player->ConversationNPCAngle = npc->angle; } oldtarget = npc->target; 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 while (CurNode->ItemCheck[0] != NULL) { - if (CheckStrifeItem (CurNode->ItemCheck[0]) && - CheckStrifeItem (CurNode->ItemCheck[1]) && - CheckStrifeItem (CurNode->ItemCheck[2])) + if (CheckStrifeItem (pc->player, CurNode->ItemCheck[0]) && + CheckStrifeItem (pc->player, CurNode->ItemCheck[1]) && + 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]; } else @@ -697,10 +698,13 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang if (CurNode->SpeakerVoice != 0) { - I_SetMusicVolume(dlg_musicvolume); + I_SetMusicVolume (dlg_musicvolume); S_SoundID (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM); } + if (pc->player != &players[consoleplayer]) + return; + // Set up the menu ConversationMenu.PreDraw = DrawConversationMenu; ConversationMenu.EscapeHandler = ConversationMenuEscaped; @@ -786,9 +790,17 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang 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; int i, x, y, linesize; + player_t *cp = &players[consoleplayer]; + assert (DialogueLines != NULL); assert (CurNode != NULL); @@ -812,7 +826,8 @@ static void DrawConversationMenu () return; } - if (ConversationPauseTic < gametic) + // [CW] Pausing the game in a multiplayer game is a bad idea. + if (ConversationPauseTic < gametic && !multiplayer) { menuactive = MENU_On; } @@ -832,7 +847,7 @@ static void DrawConversationMenu () } else { - speakerName = ConversationNPC->GetClass()->Meta.GetMetaString (AMETA_StrifeName); + speakerName = cp->ConversationNPC->GetClass()->Meta.GetMetaString (AMETA_StrifeName); if (speakerName == NULL) { speakerName = "Person"; @@ -873,7 +888,7 @@ static void DrawConversationMenu () if (ShowGold) { - AInventory *coin = ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); + AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); char goldstr[32]; sprintf (goldstr, "%d", coin != NULL ? coin->Amount : 0); @@ -892,94 +907,89 @@ static void DrawConversationMenu () // // PickConversationReply // -// FIXME: Make this work in multiplayer -// //============================================================================ static void PickConversationReply () { const char *replyText = NULL; FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra; - bool takestuff; int i; + player_t *cp = &players[consoleplayer]; + + Conversation_TakeStuff = false; M_ClearMenus (); CleanupConversationMenu (); if (reply == NULL) { - ConversationNPC->angle = ConversationNPCAngle; + Net_WriteByte (DEM_CONVERSATION); + Net_WriteByte (CONV_NPCANGLE); return; } // Check if you have the requisite items for this choice 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. if (reply->QuickNo) { Printf ("%s\n", reply->QuickNo); } - ConversationNPC->ConversationAnimation (2); - ConversationNPC->angle = ConversationNPCAngle; + Net_WriteByte (DEM_CONVERSATION); + Net_WriteByte (CONV_ANIMATE); + Net_WriteByte (2); + + Net_WriteByte (DEM_CONVERSATION); + Net_WriteByte (CONV_NPCANGLE); return; } } // 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. - takestuff = true; + Conversation_TakeStuff = true; if (reply->GiveType != NULL) { if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AInventory))) { 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 (Spawn (reply->GiveType, 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(item)->AmmoGive1 = 40; - } - item->flags |= MF_DROPPED; - if (!item->TryPickup (players[consoleplayer].mo)) - { - item->Destroy (); - takestuff = false; - } + Net_WriteByte (DEM_CONVERSATION); + Net_WriteByte (CONV_GIVEINVENTORY); + Net_WriteString (reply->GiveType->TypeName.GetChars ()); } + + if (reply->GiveType->IsDescendantOf (RUNTIME_CLASS (ASlideshowStarter))) + gameaction = ga_slideshow; } else { // 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()); } } // Take away required items if the give was successful or none was needed. - if (takestuff) + if (Conversation_TakeStuff) { 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; } @@ -989,9 +999,9 @@ static void PickConversationReply () } // 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) @@ -1000,32 +1010,43 @@ static void PickConversationReply () } // 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. if (reply->NextNode != 0) { - int rootnode = FindNode (ConversationNPC->GetDefault()->Conversation); + int rootnode = FindNode (cp->ConversationNPC->GetDefault()->Conversation); if (reply->NextNode < 0) { - ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; + cp->ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; if (gameaction != ga_slideshow) { - P_StartConversation (ConversationNPC, players[consoleplayer].mo, ConversationFaceTalker, false); + P_StartConversation (cp->ConversationNPC, cp->mo, cp->ConversationFaceTalker, false); return; } else { - S_StopSound (ConversationNPC, CHAN_VOICE); + S_StopSound (cp->ConversationNPC, CHAN_VOICE); } } else { - ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; + cp->ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; } } - ConversationNPC->angle = ConversationNPCAngle; - I_SetMusicVolume(1.f); + Net_WriteByte (DEM_CONVERSATION); + 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; } ConversationItems.Clear (); - I_SetMusicVolume(1.f); + I_SetMusicVolume (1.f); } //============================================================================ // // 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 () { 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 (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(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; + } } diff --git a/src/p_conversation.h b/src/p_conversation.h index 8ee35c27..bf270efb 100644 --- a/src/p_conversation.h +++ b/src/p_conversation.h @@ -5,8 +5,7 @@ // users can edit as simple text files. Particularly useful would be // the ability to call ACS functions to implement AppearsWhen properties // and ACS scripts to implement ActionTaken properties. -// TODO: Make this work in multiplayer and in demos. Multiplayer probably -// isn't possible for Strife conversations, but demo playback should be. +// TODO: Make this work in demos. struct FStrifeDialogueReply; class FTexture; @@ -48,6 +47,16 @@ struct FStrifeDialogueReply FBrokenLines *ReplyLines; }; +// [CW] These are used to make conversations work. +enum +{ + CONV_NPCANGLE, + CONV_ANIMATE, + CONV_GIVEINVENTORY, + CONV_TAKEINVENTORY, + CONV_SETNULL, +}; + extern TArray StrifeDialogues; // 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_ResumeConversation (); +void P_ConversationCommand (int player, BYTE **stream); + #endif diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 91f1efe2..f94b5530 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -370,7 +370,8 @@ FSwitchDef *ParseSwitchDef (FScanner &sc, bool ignoreBad) thisframe.Time = ((max - min + 1) << 16) | min; } else - { + { + thisframe.Time = 0; // Shush, GCC. sc.ScriptError ("Must specify a duration for switch frame"); } frames.Push(thisframe); diff --git a/src/p_user.cpp b/src/p_user.cpp index cb707310..c722007d 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -287,7 +287,11 @@ player_s::player_s() crouchdir(0), crouchfactor(0), crouchoffset(0), - crouchviewdelta(0) + crouchviewdelta(0), + ConversationNPC(0), + ConversationPC(0), + ConversationNPCAngle(0), + ConversationFaceTalker(0) { memset (&cmd, 0, sizeof(cmd)); 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 (ReadyWeapon == old) ReadyWeapon = static_cast(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast(rep), changed++; + if (ConversationNPC == old) ConversationNPC = replacement, changed++; + if (ConversationPC == old) ConversationPC = replacement, changed++; return changed; } @@ -337,6 +343,8 @@ size_t player_s::PropagateMark() GC::Mark(mate); GC::Mark(last_mate); GC::Mark(ReadyWeapon); + GC::Mark(ConversationNPC); + GC::Mark(ConversationPC); if (PendingWeapon != WP_NOCHANGE) { GC::Mark(PendingWeapon); @@ -2426,7 +2434,11 @@ void player_s::Serialize (FArchive &arc) << BlendB << BlendA << accuracy << stamina - << LogText; + << LogText + << ConversationNPC + << ConversationPC + << ConversationNPCAngle + << ConversationFaceTalker; for (i = 0; i < MAXPLAYERS; i++) arc << frags[i]; diff --git a/src/p_xlat.cpp b/src/p_xlat.cpp index 7eafaa06..5a5b4810 100644 --- a/src/p_xlat.cpp +++ b/src/p_xlat.cpp @@ -343,7 +343,7 @@ int P_TranslateSectorSpecial (int special) } } - if (special>=0 && specialName, tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), - tex2->Name, tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); + tex1->Name, pic1, tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), + tex2->Name, pic2, tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); } /* FIXME: doesn't work with hires texture replacements. diff --git a/src/r_interpolate.h b/src/r_interpolate.h index 3820cb68..c708d90c 100644 --- a/src/r_interpolate.h +++ b/src/r_interpolate.h @@ -61,4 +61,5 @@ void restoreinterpolations(); void clearinterpolations(); void SerializeInterpolations(FArchive &arc); -#endif \ No newline at end of file +#endif + diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 64647ca1..055f318a 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1261,10 +1261,11 @@ static void S_AddSNDINFO (int lump) sc.MustGetString(); FName nm = sc.String; sc.MustGetString(); - if (sc.Compare("timidity")) MidiDevices[nm] = 1; - else if (sc.Compare("standard")) MidiDevices[nm] = 0; - else if (sc.Compare("opl")) MidiDevices[nm] = 2; - else if (sc.Compare("default")) MidiDevices[nm] = -1; + if (sc.Compare("timidity")) MidiDevices[nm] = MDEV_TIMIDITY; + else if (sc.Compare("fmod")) MidiDevices[nm] = MDEV_FMOD; + else if (sc.Compare("standard")) MidiDevices[nm] = MDEV_MMAPI; + else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL; + else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); } break; diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 53d335a0..2af2d4bb 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -502,7 +502,7 @@ void S_ParseSndSeq (int levellump) FScanner sc(lump, "SNDSEQ"); while (sc.GetString ()) { - bool bDoorSound; + bool bDoorSound = false; if (*sc.String == ':' || *sc.String == '[') { diff --git a/src/s_sound.cpp b/src/s_sound.cpp index c47b1b8c..b54f589e 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1307,10 +1307,10 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) { int lumpnum = -1; 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 (!FileExists (musicname)) diff --git a/src/s_sound.h b/src/s_sound.h index f05498eb..f5ab57e3 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -311,6 +311,15 @@ ReverbContainer *S_FindEnvironment (const char *name); ReverbContainer *S_FindEnvironment (int id); void S_AddEnvironment (ReverbContainer *settings); +enum EMidiDevice +{ + MDEV_DEFAULT = -1, + MDEV_MMAPI = 0, + MDEV_TIMIDITY = 1, + MDEV_OPL = 2, + MDEV_FMOD = 3, +}; + typedef TMap MidiDeviceMap; extern MidiDeviceMap MidiDevices; diff --git a/src/sdl/hardware.cpp b/src/sdl/hardware.cpp index 59b4ad6d..b39fb50e 100644 --- a/src/sdl/hardware.cpp +++ b/src/sdl/hardware.cpp @@ -152,7 +152,7 @@ void I_InitGraphics () } gl_disabled = gl_nogl; #endif - val.Bool = !!Args.CheckParm ("-devparm"); + val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); #ifndef NO_GL diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index e208bae0..336b303c 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -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 () { I_FatalError ("Failed to allocate memory from system heap"); @@ -209,6 +223,7 @@ int main (int argc, char **argv) try { Args = new DArgs(argc, argv); + atterm(FinalGC); /* killough 1/98: diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index f6c243f9..d0625340 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -92,6 +92,11 @@ FMOD_RESULT SPC_CreateCodec(FMOD::System *sys); static int Enum_NumForName(const FEnumList *list, const char *name); 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 ---------------------------------------------- EXTERN_CVAR (String, snd_output) @@ -106,15 +111,19 @@ EXTERN_CVAR (Int, snd_channels) ReverbContainer *ForcedEnvironment; 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 (String, snd_resampler, "Linear", 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_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_dspnet, false, 0) // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const ReverbContainer *PrevEnvironment; +static bool ShowedBanner; // 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 @@ -164,8 +173,6 @@ static const FEnumList SpeakerModeNames[] = { "1", FMOD_SPEAKERMODE_MONO }, { "2", FMOD_SPEAKERMODE_STEREO }, { "4", FMOD_SPEAKERMODE_QUAD }, - { "Headphones", 9001 }, - { "HRTF", 9001 }, { NULL, 0 } }; @@ -195,6 +202,16 @@ static const FEnumList SoundFormatNames[] = { NULL, 0 } }; +static const char *OpenStateNames[] = +{ + "Ready", + "Loading", + "Error", + "Connecting", + "Buffering", + "Seeking" +}; + // CODE -------------------------------------------------------------------- //========================================================================== @@ -374,6 +391,26 @@ public: 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) { FMOD_RESULT result; @@ -445,6 +482,8 @@ bool FMODSoundRenderer::Init() FMOD_SOUND_FORMAT format; FMOD_DSP_RESAMPLER resampler; FMOD_INITFLAGS initflags; + int samplerate; + int driver; int eval; @@ -454,7 +493,12 @@ bool FMODSoundRenderer::Init() PausableSfx = NULL; 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. result = FMOD::System_Create(&Sys); @@ -472,30 +516,6 @@ bool FMODSoundRenderer::Init() 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 if (OSPlatform == os_WinNT4) { @@ -541,18 +561,74 @@ bool FMODSoundRenderer::Init() 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(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) { // The user has the 'Acceleration' slider set to off! // This is really bad for latency! - Printf ("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"Warning: The sound acceleration slider has been set to off.\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. 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 initflags = FMOD_INIT_NORMAL; - if (speakermode > 9000) + if (snd_hrtf) { initflags |= FMOD_INIT_SOFTWARE_HRTF; } @@ -560,17 +636,38 @@ bool FMODSoundRenderer::Init() { initflags |= FMOD_INIT_ENABLE_DSPNET; } - result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0); - 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); - + for (;;) + { 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) { // Initializing FMOD failed. Cry cry. + Printf (" System::init returned error code %d\n", result); return false; } @@ -656,6 +753,8 @@ void FMODSoundRenderer::PrintStatus() int samplerate; int numoutputchannels; int num2d, num3d, total; + unsigned int bufferlength; + int numbuffers; 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 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); - 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; 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_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; FMOD::Sound *stream; + FMOD_RESULT result; mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; if (flags & SoundStream::Loop) @@ -905,8 +1009,25 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int fla } exinfo.length = length; 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); } @@ -1080,6 +1201,10 @@ FMOD_MODE FMODSoundRenderer::SetChanHeadSettings(FMOD::Channel *chan, sfxinfo_t if (chan->get3DPanLevel(&old_level) == FMOD_OK && old_level != level) { // Only set it if it's different. 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; } diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 9c980a28..8e309846 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -69,12 +69,12 @@ extern void ChildSigHandler (int signum); #include "i_cd.h" #include "tempfiles.h" #include "templates.h" +#include "stats.h" #include EXTERN_CVAR (Int, snd_samplerate) EXTERN_CVAR (Int, snd_mididevice) -CVAR(Bool, snd_modplug, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) static bool MusicDown = true; @@ -83,6 +83,11 @@ int nomusic = 0; float relative_volume = 1.f; 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 @@ -131,6 +136,11 @@ void MusInfo::TimidityVolumeChanged() { } +FString MusInfo::GetStats() +{ + return "No stats available for this song"; +} + void I_InitMusic (void) { static bool setatterm = false; @@ -187,6 +197,9 @@ void I_PlaySong (void *handle, int _looping, float rel_vol) currSong = info; else 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; MusInfo *info = NULL; @@ -238,7 +251,7 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l return 0; } - if (offset!=-1) + if (offset != -1) { file = fopen (filename, "rb"); if (file == NULL) @@ -270,105 +283,177 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l 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 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 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) { -#ifdef _WIN32 - if (device == 1 && GSnd != NULL) + info = new MUSSong2 (file, musiccache, len); + } +#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); if (!info->IsValid()) { delete info; 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 - if (device == 1 && GSnd != NULL) - { - info = new TimiditySong (file, musiccache, len); - if (!info->IsValid()) + if (info == NULL && device != MDEV_FMOD && (snd_mididevice >= 0 || device == MDEV_MMAPI)) { - delete info; - info = NULL; + info = new MIDISong2 (file, musiccache, len); } - } - if (info == NULL && (snd_mididevice != -2 || device == 0)) - { - info = new MIDISong2 (file, musiccache, len); - } - else if (info == NULL && GSnd != NULL) #endif // _WIN32 - { - info = new TimiditySong (file, musiccache, len); } - } - // Check for RDosPlay raw OPL format - else if (id == MAKE_ID('R','A','W','A') && len >= 12) - { - DWORD fullsig[2]; - - if (file != NULL) + // Check for RDosPlay raw OPL format + else if (id == MAKE_ID('R','A','W','A') && len >= 12) { - if (fread (fullsig, 4, 2, file) != 2) + DWORD fullsig[2]; + + if (file != NULL) { - fclose (file); - return 0; + if (fread (fullsig, 4, 2, file) != 2) + { + fclose (file); + return 0; + } + fseek (file, -8, SEEK_CUR); } - fseek (file, -8, SEEK_CUR); - } - 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) + else { - fclose (file); - return 0; + memcpy(fullsig, musiccache, 8); + } + + 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); - } - if (fullhead[4] == 'B' && fullhead[5] == 1) - { - info = new OPLMUSSong (file, musiccache, len); + char fullhead[6]; + + if (file != NULL) + { + 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 // 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. - 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) { - info = ModPlugSong::Create(file, musiccache, len); + info = ModPlugSong_Create(file, musiccache, len); } if (info == NULL) { @@ -488,3 +574,18 @@ CCMD(testmusicvol) else 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"; +} diff --git a/src/sound/i_music.h b/src/sound/i_music.h index cf808252..039e1cf2 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -43,7 +43,7 @@ // void I_InitMusic (); void I_ShutdownMusic (); -void I_BuildMIDIMenuList (struct value_s **values, float *numValues); +void I_BuildMIDIMenuList (struct value_t **values, float *numValues); void I_UpdateMusic (); // Volume. diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 664bd551..42f896ab 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -20,7 +20,6 @@ #include "c_cvars.h" #include "mus2midi.h" #include "i_sound.h" -#include "modplug/modplug.h" void I_InitMusicWin32 (); void I_ShutdownMusicWin32 (); @@ -45,6 +44,7 @@ public: virtual bool IsValid () const = 0; virtual bool SetPosition (int order); virtual void Update(); + virtual FString GetStats(); enum EState { @@ -243,6 +243,7 @@ public: bool IsMIDI () const { return false; } bool IsValid () const { return m_Stream != NULL; } bool SetPosition (int order); + FString GetStats(); protected: StreamSong () : m_Stream(NULL), m_LastPos(0) {} @@ -289,25 +290,6 @@ protected: #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 ------- class OPLMUSSong : public StreamSong diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 03e2ee3c..b98337ac 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -68,12 +68,13 @@ extern HINSTANCE g_hInst; #include "w_wad.h" #include "i_video.h" #include "s_sound.h" +#include "v_text.h" #include "gi.h" #include "doomdef.h" 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 (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -124,7 +125,7 @@ void I_InitSound () { delete GSnd; GSnd = NULL; - Printf ("Sound init failed. Using nosound.\n"); + Printf (TEXTCOLOR_RED"Sound init failed. Using nosound.\n"); } I_InitMusic (); snd_sfxvolume.Callback (); @@ -215,3 +216,8 @@ bool SoundStream::SetPosition(int pos) { return false; } + +FString SoundStream::GetStats() +{ + return "No stream stats available."; +} diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index 35acba41..ad36ce5b 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -57,6 +57,7 @@ public: virtual bool SetPaused (bool paused) = 0; virtual unsigned int GetPosition () = 0; virtual bool SetPosition (int pos); + virtual FString GetStats(); }; typedef bool (*SoundStreamCallback)(SoundStream *stream, void *buff, int len, void *userdata); diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index ea9bc871..f168e53c 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -1,7 +1,7 @@ -#ifdef _WIN32 #include "i_musicinterns.h" #include "c_dispatch.h" #include "i_music.h" +#include "i_system.h" #include "templates.h" #include "v_text.h" @@ -9,6 +9,7 @@ static DWORD nummididevices; static bool nummididevicesset; +#ifdef _WIN32 UINT mididevice; CVAR (Bool, snd_midiprecache, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); @@ -56,19 +57,22 @@ void I_InitMusicWin32 () void I_ShutdownMusicWin32 () { - // I don't know if this is an NT 4.0 bug or an FMOD bug, but if waveout - // is used for sound, and a MIDI is also played, then when I quit, the OS + // 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 // tells me a free block was modified after being freed. This is // apparently a synchronization issue between two threads, because if I // 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 - // a driver problem, because it happens with both a Vortex 2 and an Audigy. - // Though if their drivers are both based off some common Microsoft sample - // code, I suppose it could be a driver issue. - Sleep (50); + // the entire sound system, the error does not happen. Observed with a + // Vortex 2 (may Aureal rest in peace) and an Audigy (damn you, Creative!). + // I no longer have a system with NT4 drivers, so I don't know if this + // workaround is still needed or not. + 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) { @@ -79,13 +83,13 @@ void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues) values[0].name = "TiMidity++"; values[0].value = -2.0; + values[1].name = "FMOD"; + values[1].value = -1.0; if (nummididevices > 0) { UINT id; int p; - values[1].name = "MIDI Mapper"; - values[1].value = -1.0; for (id = 0, p = 2; id < nummididevices; ++id) { MIDIOUTCAPS caps; @@ -107,7 +111,7 @@ void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues) } else { - *numValues = 1.f; + *numValues = 2.f; } } } @@ -157,9 +161,9 @@ CCMD (snd_listmididevices) MMRESULT res; PrintMidiDevice (-2, "TiMidity++", 0, 0); + PrintMidiDevice (-1, "FMOD", 0, 0); if (nummididevices != 0) { - PrintMidiDevice (-1, "MIDI Mapper", MOD_MAPPER, 0); for (id = 0; id < nummididevices; ++id) { 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 diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index 3f45d165..c8ab55e0 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -244,6 +244,7 @@ TimiditySong::TimiditySong (FILE *file, char * musiccache, int len) void TimiditySong::PrepTimidity () { int pipeSize; + #ifdef _WIN32 static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, TRUE }; @@ -267,6 +268,11 @@ void TimiditySong::PrepTimidity () pipeSize = (timidity_pipe * timidity_frequency / 1000) << (timidity_stereo + !timidity_8bit); + + if (GSnd == NULL) + { // Can't pipe if using no sound. + pipeSize = 0; + } if (pipeSize != 0) { @@ -520,11 +526,11 @@ bool TimiditySong::LaunchTimidity () close (WavePipe[0]); dup2 (WavePipe[1], STDOUT_FILENO); freopen ("/dev/null", "r", stdin); - freopen ("/dev/null", "w", stderr); +// freopen ("/dev/null", "w", stderr); 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 } else if (forkres < 0) @@ -536,7 +542,12 @@ bool TimiditySong::LaunchTimidity () // printf ("child is %d\n", forkres); ChildProcess = forkres; 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); @@ -575,21 +586,36 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u } } #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); if (got < len) { memset ((BYTE *)buff+got, 0, len-got); } - - if (ChildQuit == song->ChildProcess) - { - ChildQuit = 0; -// printf ("child gone\n"); - song->ChildProcess = -1; - return false; - } #endif return true; } diff --git a/src/sound/music_modplug.cpp b/src/sound/music_modplug.cpp index 594d4228..b7f6a0ba 100644 --- a/src/sound/music_modplug.cpp +++ b/src/sound/music_modplug.cpp @@ -42,7 +42,24 @@ // 192 approximately replicates the volume of a WinAmp Wave export of the song. 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; diff --git a/src/sound/music_stream.cpp b/src/sound/music_stream.cpp index 12fc8e60..19ff431f 100644 --- a/src/sound/music_stream.cpp +++ b/src/sound/music_stream.cpp @@ -50,8 +50,15 @@ StreamSong::~StreamSong () } 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 () @@ -92,3 +99,12 @@ bool StreamSong::SetPosition(int order) return false; } } + +FString StreamSong::GetStats() +{ + if (m_Stream != NULL) + { + return m_Stream->GetStats(); + } + return "No song loaded\n"; +} diff --git a/src/svnrevision.h b/src/svnrevision.h index 34577504..dddd0313 100644 --- a/src/svnrevision.h +++ b/src/svnrevision.h @@ -3,5 +3,5 @@ // This file was automatically generated by the // updaterevision tool. Do not edit by hand. -#define ZD_SVN_REVISION_STRING "853" -#define ZD_SVN_REVISION_NUMBER 853 +#define ZD_SVN_REVISION_STRING "858" +#define ZD_SVN_REVISION_NUMBER 858 diff --git a/src/v_collection.cpp b/src/v_collection.cpp index 55bac609..bc61cd86 100644 --- a/src/v_collection.cpp +++ b/src/v_collection.cpp @@ -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 () { if (ImageMap != NULL) diff --git a/src/v_collection.h b/src/v_collection.h index be6eed11..1a7898eb 100644 --- a/src/v_collection.h +++ b/src/v_collection.h @@ -45,6 +45,7 @@ public: ~FImageCollection (); void Init (const char **patchnames, int numPatches, int namespc=0); + void Add (const char **patchnames, int numPatches, int namespc=0); void Uninit (); FTexture *operator[] (int index) const; diff --git a/src/version.h b/src/version.h index 7fb5f838..4594f234 100644 --- a/src/version.h +++ b/src/version.h @@ -66,7 +66,7 @@ // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. // Otherwise, it should be safe to leave it alone. -#define DEMOGAMEVERSION 0x20B +#define DEMOGAMEVERSION 0x20C // Minimum demo version we can play. // Bump it whenever you change or remove existing DEM_ commands. @@ -77,7 +77,7 @@ // SAVESIG should match SAVEVER. // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 849 +#define MINSAVEVER 854 #if ZD_SVN_REVISION_NUMBER < MINSAVEVER // Never write a savegame with a version lower than what we need diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index f29ab2c1..4119debf 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -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 @@ -777,6 +791,7 @@ void DoMain (HINSTANCE hInstance) #endif Args = new DArgs(__argc, __argv); + atterm(FinalGC); // 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