diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index 1846ecbc69..d048ffc0e4 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -1576,8 +1576,7 @@ DEFINE_ACTION_FUNCTION(DSBarInfo, GetProtrusion) DBaseStatusBar *CreateCustomStatusBar(int scriptno) { auto script = SBarInfoScript[scriptno]; - if (script == NULL) - I_FatalError("Tried to create a status bar with no script!"); + if (script == nullptr) return nullptr; auto sbar = (DBaseStatusBar*)PClass::FindClass("SBarInfoWrapper")->CreateNew(); auto core = new DSBarInfo(sbar, script); diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index 4b2b1e727b..2710c5156d 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -303,6 +303,15 @@ void ST_CreateStatusBar(bool bTitleLevel) { StatusBar = CreateCustomStatusBar(SCRIPT_DEFAULT); } + // SBARINFO failed so try the current statusbarclass again. + if (StatusBar == nullptr) + { + auto cls = PClass::FindClass(gameinfo.statusbarclass); + if (cls != nullptr) + { + StatusBar = (DBaseStatusBar *)cls->CreateNew(); + } + } } if (StatusBar == nullptr) { diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 19c605a5c0..aed9f8f609 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -933,6 +933,7 @@ void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width, { GL_IRECT bounds; + P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector. bounds.left=0; bounds.top=0; bounds.width=width; diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 629f8acb91..c491bb33e5 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -1185,10 +1185,11 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s y = particle->Pos.Y; z = particle->Pos.Z; - float scalefac=particle->size/4.0f; - // [BB] The smooth particles are smaller than the other ones. Compensate for this here. - if (gl_particles_style==2) - scalefac *= 1.7; + float factor; + if (gl_particles_style == 1) factor = 1.3f / 7.f; + else if (gl_particles_style == 2) factor = 2.5f / 7.f; + else factor = 1 / 7.f; + float scalefac=particle->size * factor; float viewvecX = GLRenderer->mViewVector.X; float viewvecY = GLRenderer->mViewVector.Y; diff --git a/src/sound/oplsynth/musicblock.cpp b/src/sound/oplsynth/musicblock.cpp index c2e23b0dc7..daea5ea2d2 100644 --- a/src/sound/oplsynth/musicblock.cpp +++ b/src/sound/oplsynth/musicblock.cpp @@ -36,6 +36,7 @@ musicBlock::musicBlock () { memset (this, 0, sizeof(*this)); + for(auto &oplchannel : oplchannels) oplchannel.Panning = 64; // default to center panning. for(auto &voice : voices) voice.index = ~0u; // mark all free. } diff --git a/src/swrenderer/drawers/r_thread.cpp b/src/swrenderer/drawers/r_thread.cpp index f6dae7d900..57d86ab9b4 100644 --- a/src/swrenderer/drawers/r_thread.cpp +++ b/src/swrenderer/drawers/r_thread.cpp @@ -33,6 +33,11 @@ #include "r_thread.h" #include "swrenderer/r_memory.h" #include "swrenderer/r_renderthread.h" +#include + +#ifdef WIN32 +void PeekThreadedErrorPane(); +#endif CVAR(Bool, r_multithreaded, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); @@ -73,10 +78,20 @@ void DrawerThreads::Execute(DrawerCommandQueuePtr commands) void DrawerThreads::WaitForWorkers() { + using namespace std::chrono_literals; + // Wait for workers to finish auto queue = Instance(); std::unique_lock end_lock(queue->end_mutex); - queue->end_condition.wait(end_lock, [&]() { return queue->tasks_left == 0; }); + if (!queue->end_condition.wait_for(end_lock, 5s, [&]() { return queue->tasks_left == 0; })) + { +#ifdef WIN32 + PeekThreadedErrorPane(); +#endif + // Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing + int *threadCrashed = nullptr; + *threadCrashed = 0xdeadbeef; + } end_lock.unlock(); // Clean up diff --git a/src/swrenderer/scene/r_scene.cpp b/src/swrenderer/scene/r_scene.cpp index 5109cb82c7..5948a5c4ab 100644 --- a/src/swrenderer/scene/r_scene.cpp +++ b/src/swrenderer/scene/r_scene.cpp @@ -57,6 +57,11 @@ #include "swrenderer/r_memory.h" #include "swrenderer/r_renderthread.h" #include "swrenderer/things/r_playersprite.h" +#include + +#ifdef WIN32 +void PeekThreadedErrorPane(); +#endif EXTERN_CVAR(Bool, r_shadercolormaps) EXTERN_CVAR(Int, r_clearbuffer) @@ -217,9 +222,18 @@ namespace swrenderer // Wait for everyone to finish: if (Threads.size() > 1) { + using namespace std::chrono_literals; std::unique_lock end_lock(end_mutex); finished_threads++; - end_condition.wait(end_lock, [&]() { return finished_threads == Threads.size(); }); + if (!end_condition.wait_for(end_lock, 5s, [&]() { return finished_threads == Threads.size(); })) + { +#ifdef WIN32 + PeekThreadedErrorPane(); +#endif + // Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing + int *threadCrashed = nullptr; + *threadCrashed = 0xdeadbeef; + } finished_threads = 0; } diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 4ae1595ef3..d6d2461857 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -821,6 +821,13 @@ void ShowErrorPane(const char *text) } } +void PeekThreadedErrorPane() +{ + // Allow SendMessage from another thread to call its message handler so that it can display the crash dialog + MSG msg; + PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE); +} + //========================================================================== // // DoMain diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 458e846c36..6dc584a586 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -39,7 +39,7 @@ IWad Autoname = "harmony" Game = "Doom" Config = "Harmony" - Mapinfo = "mapinfo/hacxharm.txt" + Mapinfo = "mapinfo/harmony.txt" MustContain = "MAP01", "0HAWK01", "0CARA3", "0NOSE1" BannerColors = "6e b4 d6", "45 4f 7e" } @@ -50,7 +50,7 @@ IWad Game = "Doom" Config = "Hacx" Autoname = "hacx.hacx2" - Mapinfo = "mapinfo/hacxharm.txt" + Mapinfo = "mapinfo/hacx.txt" MustContain = "MAP01", "HACX-E" BannerColors = "ff ff ff", "00 88 22" } @@ -61,7 +61,7 @@ IWad Game = "Doom" Config = "Hacx" Autoname = "hacx.hacx1" - Mapinfo = "mapinfo/hacxharm.txt" + Mapinfo = "mapinfo/hacx.txt" MustContain = "MAP01", "HACX-R" BannerColors = "00 00 a8", "a8 a8 a8" } diff --git a/wadsrc/static/mapinfo/hacxharm.txt b/wadsrc/static/mapinfo/hacx.txt similarity index 100% rename from wadsrc/static/mapinfo/hacxharm.txt rename to wadsrc/static/mapinfo/hacx.txt diff --git a/wadsrc/static/mapinfo/harmony.txt b/wadsrc/static/mapinfo/harmony.txt new file mode 100644 index 0000000000..b96689c9f6 --- /dev/null +++ b/wadsrc/static/mapinfo/harmony.txt @@ -0,0 +1,8 @@ +include "mapinfo/doom2.txt" + +gameinfo +{ + cursorpic = "cursor" + statusbarclass = "HarmonyStatusBar" +} + diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index d26794f6e7..1cabc49753 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -37,6 +37,7 @@ version "2.5" #include "zscript/statusbar/heretic_sbar.txt" #include "zscript/statusbar/hexen_sbar.txt" #include "zscript/statusbar/strife_sbar.txt" +#include "zscript/statusbar/harm_sbar.txt" #include "zscript/statusbar/sbarinfowrapper.txt" #include "zscript/inventory/inventory.txt" diff --git a/wadsrc/static/zscript/statusbar/harm_sbar.txt b/wadsrc/static/zscript/statusbar/harm_sbar.txt new file mode 100644 index 0000000000..42a30f2fc3 --- /dev/null +++ b/wadsrc/static/zscript/statusbar/harm_sbar.txt @@ -0,0 +1,108 @@ +class HarmonyStatusBar : DoomStatusBar +{ + int scalestate; + + override void Init() + { + Super.Init(); + + // This is for detecting the DECORATEd version of the mod which sets proper scaling for the HUD related textures. + let tex = TexMan.CheckForTexture("MEDIA0", TexMan.Type_Sprite); + if (tex.isValid()) + { + int size = TexMan.GetSize(tex); + Vector2 ssize = TexMan.GetScaledSize(tex); + if (ssize.X ~== size) scalestate = 1; + else scalestate = 0; + } + else scalestate = 1; + } + + override void Draw (int state, double TicFrac) + { + if (!scalestate) + { + Super.Draw(state, TicFrac); + return; + } + + BaseStatusBar.Draw (state, TicFrac); + + if (state == HUD_StatusBar) + { + BeginStatusBar(); + DrawMainBar (TicFrac); + } + else if (state == HUD_Fullscreen) + { + BeginHUD(); + DrawFullScreenStuff (); + } + } + + protected void DrawFullScreenStuff () + { + Vector2 iconbox = (40, 20); + // Draw health + DrawImage("MEDIA0", (20, -2), scale:(0.3, 0.3)); + DrawString(mHUDFont, FormatNumber(CPlayer.health, 3), (44, -20)); + + let armor = CPlayer.mo.FindInventory("BasicArmor"); + if (armor != null && armor.Amount > 0) + { + DrawInventoryIcon(armor, (20, -22), scale:(0.3, 0.3)); + DrawString(mHUDFont, FormatNumber(armor.Amount, 3), (44, -40)); + } + Inventory ammotype1, ammotype2; + [ammotype1, ammotype2] = GetCurrentAmmo(); + int invY = -20; + if (ammotype1 != null) + { + DrawInventoryIcon(ammotype1, (-14, -4), scale:(0.3, 0.3)); + DrawString(mHUDFont, FormatNumber(ammotype1.Amount, 3), (-30, -20), DI_TEXT_ALIGN_RIGHT); + invY -= 20; + } + if (ammotype2 != null && ammotype2 != ammotype1) + { + DrawInventoryIcon(ammotype2, (-14, invY + 17), scale:(0.3, 0.3)); + DrawString(mHUDFont, FormatNumber(ammotype2.Amount, 3), (-30, invY), DI_TEXT_ALIGN_RIGHT); + invY -= 20; + } + if (!isInventoryBarVisible() && !level.NoInventoryBar && CPlayer.mo.InvSel != null) + { + DrawInventoryIcon(CPlayer.mo.InvSel, (-14, invY + 17)); + DrawString(mHUDFont, FormatNumber(CPlayer.mo.InvSel.Amount, 3), (-30, invY), DI_TEXT_ALIGN_RIGHT); + } + if (deathmatch) + { + DrawString(mHUDFont, FormatNumber(CPlayer.FragCount, 3), (-3, 1), DI_TEXT_ALIGN_RIGHT, Font.CR_GOLD); + } + + // Draw the keys. This does not use a special draw function like SBARINFO because the specifics will be different for each mod + // so it's easier to copy or reimplement the following piece of code instead of trying to write a complicated all-encompassing solution. + Vector2 keypos = (-10, 2); + int rowc = 0; + double roww = 0; + for(let i = CPlayer.mo.Inv; i != null; i = i.Inv) + { + if (i is "Key" && i.Icon.IsValid()) + { + DrawTexture(i.Icon, keypos, DI_SCREEN_RIGHT_TOP|DI_ITEM_LEFT_TOP); + Vector2 size = TexMan.GetScaledSize(i.Icon); + keypos.Y += size.Y + 2; + roww = max(roww, size.X); + if (++rowc == 3) + { + keypos.Y = 2; + keypos.X -= roww + 2; + roww = 0; + rowc = 0; + } + } + } + if (isInventoryBarVisible()) + { + DrawInventoryBar(diparms, (0, 0), 7, DI_SCREEN_CENTER_BOTTOM, HX_SHADOW); + } + } +}