diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index a178ff917..70413496c 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -217,6 +217,18 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet } } +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetDisplayColor) +{ + float h, s, v, r, g, b; + PARAM_SELF_STRUCT_PROLOGUE(player_t); + D_GetPlayerColor(int(self-players), &h, &s, &v, NULL); + HSVtoRGB(&r, &g, &b, h, s, v); + int c = MAKERGB(clamp(int(r*255.f), 0, 255), + clamp(int(g*255.f), 0, 255), + clamp(int(b*255.f), 0, 255)); + ACTION_RETURN_INT(c); +} + // Find out which teams are present. If there is only one, // then another team should be chosen at random. // diff --git a/src/g_level.cpp b/src/g_level.cpp index ead3ada99..fec1050ad 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1893,6 +1893,30 @@ void FLevelLocals::AddScroller (int secnum) } } +//========================================================================== +// +// +//========================================================================== + +void FLevelLocals::SetInterMusic(const char *nextmap) +{ + auto mus = level.info->MapInterMusic.CheckKey(nextmap); + if (mus != nullptr) + S_ChangeMusic(mus->first, mus->second); + else if (level.info->InterMusic.IsNotEmpty()) + S_ChangeMusic(level.info->InterMusic, level.info->intermusicorder); + else + S_ChangeMusic(gameinfo.intermissionMusic.GetChars(), gameinfo.intermissionOrder); +} + +DEFINE_ACTION_FUNCTION(FLevelLocals, SetInterMusic) +{ + PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals); + PARAM_STRING(map); + self->SetInterMusic(map); + return 0; +} + //========================================================================== // // diff --git a/src/g_levellocals.h b/src/g_levellocals.h index b52d866f9..1574442d6 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -10,6 +10,7 @@ struct FLevelLocals { void Tick (); void AddScroller (int secnum); + void FLevelLocals::SetInterMusic(const char *nextmap); uint8_t md5[16]; // for savegame validation. If the MD5 does not match the savegame won't be loaded. int time; // time in the hub diff --git a/src/gi.cpp b/src/gi.cpp index cf28e96be..34c2fefa7 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -58,6 +58,7 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenMapNameFont) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenEnteringFont) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenFinishedFont) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gibfactor) +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, intermissioncounter) const char *GameNames[17] = diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index 2b752bda4..0c001a3ba 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -119,6 +119,16 @@ int compareteams (const void *arg1, const void *arg2) return diff; } +/* +void HU_SortPlayers +{ + if (teamplay) + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), compareteams); + else + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), comparepoints); +} +*/ + bool SB_ForceActive = false; // PRIVATE DATA DEFINITIONS ------------------------------------------------ diff --git a/src/p_user.cpp b/src/p_user.cpp index c74a89970..19de4cb18 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3471,6 +3471,7 @@ DEFINE_FIELD_X(PlayerInfo, player_t, cmd) DEFINE_FIELD_X(PlayerInfo, player_t, original_cmd) DEFINE_FIELD_X(PlayerInfo, player_t, userinfo) DEFINE_FIELD_X(PlayerInfo, player_t, weapons) +DEFINE_FIELD_NAMED_X(PlayerInfo, player_t, cmd.ucmd.buttons, buttons) DEFINE_FIELD(FPlayerClass, Type) DEFINE_FIELD(FPlayerClass, Flags) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index eab83577c..4bf8db837 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -59,6 +59,7 @@ #include "teaminfo.h" #include "r_data/sprites.h" #include "serializer.h" +#include "wi_stuff.h" static TArray properties; static TArray AFTable; @@ -874,6 +875,11 @@ void InitThingdef() } ); + auto wbplayerstruct = NewNativeStruct("WBPlayerStruct", nullptr); + wbplayerstruct->Size = sizeof(wbplayerstruct_t); + wbplayerstruct->Align = alignof(wbplayerstruct_t); + + // Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag. // It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution' // is to create a static variable from it and reference that in the script. Yuck!!! diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 432cc2419..1601660c7 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1236,6 +1236,24 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetScaledSize) // //========================================================================== +DEFINE_ACTION_FUNCTION(_TexMan, GetScaledOffset) +{ + PARAM_PROLOGUE; + PARAM_INT(texid); + auto tex = TexMan.ByIndex(texid); + if (tex != nullptr) + { + ACTION_RETURN_VEC2(DVector2(tex->GetScaledLeftOffsetDouble(), tex->GetScaledTopOffsetDouble())); + } + ACTION_RETURN_VEC2(DVector2(-1, -1)); +} + +//========================================================================== +// +// +// +//========================================================================== + DEFINE_ACTION_FUNCTION(_TexMan, CheckRealHeight) { PARAM_PROLOGUE; diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index d7f37b39e..c1ae84793 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -2084,10 +2084,7 @@ public: // Use the local level structure which can be overridden by hubs lnametexts[0] = level.LevelName; - - level_info_t *li = FindLevelInfo(wbs->next); - if (li) lnametexts[1] = li->LookupLevelName(); - else lnametexts[1] = ""; + lnametexts[1] = wbs->nextname; bg = new DInterBackground(wbs); GC::AddSoftRoot(bg); @@ -2140,14 +2137,7 @@ public: void WI_Start (wbstartstruct_t *wbstartstruct) { - auto info = FindLevelInfo(wbstartstruct->next, false); - if (info == nullptr) - { - wbstartstruct->next = ""; - } - noautostartmap = false; - V_SetBlend (0,0,0,0); WI_initVariables (wbstartstruct); WI_loadData (); if (deathmatch) @@ -2156,8 +2146,6 @@ public: WI_initNetgameStats(); else WI_initStats(); - S_StopAllChannels (); - SN_StopAllSequences (); } }; @@ -2190,11 +2178,67 @@ void WI_Drawer() void WI_Start(wbstartstruct_t *wbstartstruct) { WI_Screen = new DStatusScreen; + auto info = FindLevelInfo(wbstartstruct->next, false); + if (info == nullptr) + { + wbstartstruct->next = ""; + } + else wbstartstruct->nextname = info->LookupLevelName(); + V_SetBlend(0, 0, 0, 0); + S_StopAllChannels(); + SN_StopAllSequences(); + WI_Screen->WI_Start(wbstartstruct); GC::AddSoftRoot(WI_Screen); } +DEFINE_ACTION_FUNCTION(DStatusScreen, GetPlayerWidths) +{ + PARAM_PROLOGUE; + int maxnamewidth, maxscorewidth, maxiconheight; + HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); + if (numret > 0) ret[0].SetInt(maxnamewidth); + if (numret > 1) ret[1].SetInt(maxscorewidth); + if (numret > 2) ret[2].SetInt(maxiconheight); + return MIN(numret, 3); +} + +DEFINE_ACTION_FUNCTION(DStatusScreen, GetRowColor) +{ + PARAM_PROLOGUE; + PARAM_POINTER(p, player_t); + PARAM_BOOL(highlight); + ACTION_RETURN_INT(HU_GetRowColor(p, highlight)); +} + +DEFINE_ACTION_FUNCTION(DStatusScreen, GetSortedPlayers) +{ + PARAM_PROLOGUE; + PARAM_POINTER(array, TArray); + PARAM_BOOL(teamplay); + + player_t *sortedplayers[MAXPLAYERS]; + // Sort all players + for (int i = 0; i < MAXPLAYERS; i++) + { + sortedplayers[i] = &players[i]; + } + + if (teamplay) + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), compareteams); + else + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), comparepoints); + + array->Resize(MAXPLAYERS); + for (unsigned i = 0; i < MAXPLAYERS; i++) + { + (*array)[i] = int(sortedplayers[i] - players); + } + return 0; +} + + DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, skills); DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, sitems); DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, ssecret); @@ -2206,6 +2250,7 @@ DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, finished_ep); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, next_ep); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, current); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, next); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, nextname); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, LName0); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, LName1); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, maxkills); diff --git a/src/wi_stuff.h b/src/wi_stuff.h index 04acb1231..16e566f8d 100644 --- a/src/wi_stuff.h +++ b/src/wi_stuff.h @@ -48,6 +48,7 @@ struct wbstartstruct_t FString current; // [RH] Name of map just finished FString next; // next level, [RH] actual map name + FString nextname; // printable name for next level. FTextureID LName0; FTextureID LName1; diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 2dd8eaa8e..e892c18e9 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -28,6 +28,9 @@ version "2.4" #include "zscript/statscreen/types.txt" #include "zscript/statscreen/statscreen.txt" +#include "zscript/statscreen/statscreen_sp.txt" +#include "zscript/statscreen/statscreen_dm.txt" +#include "zscript/statscreen/statscreen_coop.txt" #include "zscript/inventory/inventory.txt" #include "zscript/inventory/inv_misc.txt" diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 664e4d994..06259c1f4 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -88,6 +88,7 @@ struct TexMan native static void ReplaceTextures(String from, String to, int flags); native static int, int GetSize(TextureID tex); native static Vector2 GetScaledSize(TextureID tex); + native static Vector2 GetScaledOffset(TextureID tex); native static int CheckRealHeight(TextureID tex); } @@ -304,6 +305,7 @@ struct GameInfoStruct native native GIFont mStatscreenEnteringFont; native GIFont mStatscreenFinishedFont; native double gibfactor; + native bool intermissioncounter; } class Object native @@ -499,6 +501,7 @@ struct LevelLocals native native static void StartSlideshow(Name whichone = 'none'); native static void WorldDone(); native static void RemoveAllBots(bool fromlist); + native void SetInterMusic(String nextmap); } struct StringTable native diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 011daae65..4848229dc 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -247,6 +247,7 @@ struct PlayerInfo native play // this is what internally is known as player_t native PlayerPawn mo; native uint8 playerstate; + native readonly uint buttons; native uint original_oldbuttons; native readonly Class cls; native float DesiredFOV; @@ -349,6 +350,7 @@ usercmd_t original_cmd; native String GetUserName() const; native Color GetColor() const; + native Color GetDisplayColor() const; native int GetColorSet() const; native int GetPlayerClassNum() const; native int GetSkin() const; diff --git a/wadsrc/static/zscript/statscreen/statscreen.txt b/wadsrc/static/zscript/statscreen/statscreen.txt index 2bfbde5be..2fba79f33 100644 --- a/wadsrc/static/zscript/statscreen/statscreen.txt +++ b/wadsrc/static/zscript/statscreen/statscreen.txt @@ -1,14 +1,14 @@ // Note that the status screen needs to run in 'play' scope! -class InterBackground native play +class InterBackground native play version("2.4") { - native InterBackground Create(wbstartstruct wbst); - native bool LoadBackground(bool isenterpic); - native void updateAnimatedBack(); - native void drawBackground(int CurState, bool drawsplat, bool snl_pointeron); + native static InterBackground Create(wbstartstruct wbst); + native virtual bool LoadBackground(bool isenterpic); + native virtual void updateAnimatedBack(); + native virtual void drawBackground(int CurState, bool drawsplat, bool snl_pointeron); } -struct PatchInfo play +struct PatchInfo play version("2.4") { Font mFont; TextureID mPatch; @@ -37,12 +37,12 @@ struct PatchInfo play // Will be made a class later, but for now needs to mirror the internal version. -class StatusScreen native play +class StatusScreen native play version("2.4") { enum EValues { // GLOBAL LOCATIONS - WI_TITLEY = 2, + TITLEY = 2, // SINGPLE-PLAYER STUFF SP_STATSX = 50, @@ -132,7 +132,7 @@ class StatusScreen native play // //==================================================================== - protected int DrawCharPatch(Font fnt, int charcode, int x, int y, int translation = Font.CR_UNTRANSLATED, bool nomove = false) + int DrawCharPatch(Font fnt, int charcode, int x, int y, int translation = Font.CR_UNTRANSLATED, bool nomove = false) { int width = fnt.GetCharWidth(charcode); screen.DrawChar(fnt, translation, x, y, charcode, nomove ? DTA_CleanNoMove : DTA_Clean, true); @@ -148,7 +148,7 @@ class StatusScreen native play // //==================================================================== - protected int DrawName(int y, TextureID tex, String levelname) + int DrawName(int y, TextureID tex, String levelname) { // draw if (tex.isValid()) @@ -188,7 +188,7 @@ class StatusScreen native play // //==================================================================== - protected int DrawPatchText(int y, PatchInfo pinfo, String stringname) + int DrawPatchText(int y, PatchInfo pinfo, String stringname) { String string = Stringtable.Localize(stringname); int midx = screen.GetWidth() / 2; @@ -216,9 +216,9 @@ class StatusScreen native play // //==================================================================== - protected int drawLF () + int drawLF () { - int y = WI_TITLEY * CleanYfac; + int y = TITLEY * CleanYfac; y = DrawName(y, wbs.LName0, lnametexts[0]); @@ -229,7 +229,7 @@ class StatusScreen native play if (y < (NG_STATSY - finished.mFont.GetHeight()*3/4) * CleanYfac) { // don't draw 'finished' if the level name is too tall - y = DrawPatchText(y, finished, "$WI_FINISHED"); + y = DrawPatchText(y, finished, "$FINISHED"); } return y; } @@ -244,11 +244,11 @@ class StatusScreen native play // //==================================================================== - protected void drawEL () + void drawEL () { - int y = WI_TITLEY * CleanYfac; + int y = TITLEY * CleanYfac; - y = DrawPatchText(y, entering, "$WI_ENTERING"); + y = DrawPatchText(y, entering, "$ENTERING"); y += entering.mFont.GetHeight() * CleanYfac / 4; DrawName(y, wbs.LName1, lnametexts[1]); } @@ -263,7 +263,7 @@ class StatusScreen native play // Returns new x position, that is, the left edge of the number. // //==================================================================== - protected int drawNum (Font fnt, int x, int y, int n, int digits, bool leadingzeros = true, int translation = Font.CR_UNTRANSLATED) + int drawNum (Font fnt, int x, int y, int n, int digits, bool leadingzeros = true, int translation = Font.CR_UNTRANSLATED) { int fntwidth = fnt.StringWidth("3"); String text; @@ -315,7 +315,7 @@ class StatusScreen native play // //==================================================================== - protected void drawPercent (Font fnt, int x, int y, int p, int b, bool show_total = true, int color = Font.CR_UNTRANSLATED) + void drawPercent (Font fnt, int x, int y, int p, int b, bool show_total = true, int color = Font.CR_UNTRANSLATED) { if (p < 0) return; @@ -355,7 +355,7 @@ class StatusScreen native play // //==================================================================== - protected void drawTime (int x, int y, int t, bool no_sucks=false) + void drawTime (int x, int y, int t, bool no_sucks=false) { bool sucky; @@ -405,7 +405,7 @@ class StatusScreen native play // //==================================================================== - protected void End () + void End () { CurState = LeavingIntermission; @@ -422,7 +422,7 @@ class StatusScreen native play // //==================================================================== - protected bool autoSkip() + bool autoSkip() { return wi_autoadvance > 0 && bcnt > (wi_autoadvance * Thinker.TICRATE); } @@ -433,7 +433,7 @@ class StatusScreen native play // //==================================================================== - protected void initNoState () + protected virtual void initNoState () { CurState = NoState; acceleratestage = 0; @@ -446,7 +446,7 @@ class StatusScreen native play // //==================================================================== - protected void updateNoState () + protected virtual void updateNoState () { if (acceleratestage) { @@ -482,7 +482,7 @@ class StatusScreen native play // //==================================================================== - protected void initShowNextLoc () + protected virtual void initShowNextLoc () { if (wbs.next == "") { @@ -504,7 +504,7 @@ class StatusScreen native play // //==================================================================== - protected void updateShowNextLoc () + protected virtual void updateShowNextLoc () { if (!--cnt || acceleratestage) initNoState(); @@ -518,7 +518,7 @@ class StatusScreen native play // //==================================================================== - protected void drawShowNextLoc(void) + protected virtual void drawShowNextLoc(void) { bg.drawBackground(CurState, true, snl_pointeron); @@ -533,10 +533,203 @@ class StatusScreen native play // //==================================================================== - void WI_drawNoState () + protected virtual void drawNoState () { snl_pointeron = true; drawShowNextLoc(); } + //==================================================================== + // + // + // + //==================================================================== + + protected int fragSum (int playernum) + { + int i; + int frags = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] + && i!=playernum) + { + frags += Plrs[playernum].frags[i]; + } + } + + // JDC hack - negative frags. + frags -= Plrs[playernum].frags[playernum]; + + return frags; + } + + //==================================================================== + // + // + // + //==================================================================== + + static void PlaySound(Sound snd) + { + S_Sound(snd, CHAN_VOICE | CHAN_UI, 1, ATTN_NONE); + } + + + // ==================================================================== + // checkForAccelerate + // Purpose: See if the player has hit either the attack or use key + // or mouse button. If so we set acceleratestage to 1 and + // all those display routines above jump right to the end. + // Args: none + // Returns: void + // + // ==================================================================== + + protected void checkForAccelerate(void) + { + int i; + + // check for button presses to skip delays + for (i = 0; i < MAXPLAYERS; i++) + { + PlayerInfo player = players[i]; + if (playeringame[i]) + { + if ((player.buttons ^ player.oldbuttons) && + ((player.buttons & player.oldbuttons) == player.oldbuttons) && player.Bot == NULL) + { + acceleratestage = 1; + playerready[i] = true; + } + player.oldbuttons = player.buttons; + } + } + } + + // ==================================================================== + // Ticker + // Purpose: Do various updates every gametic, for stats, animation, + // checking that intermission music is running, etc. + // Args: none + // Returns: void + // + // ==================================================================== + + virtual void StartMusic() + { + level.SetInterMusic(wbs.next); + } + + //==================================================================== + // + // + // + //==================================================================== + + virtual void Ticker(void) + { + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + StartMusic(); + } + + checkForAccelerate(); + bg.updateAnimatedBack(); + + switch (CurState) + { + case StatCount: + updateStats(); + break; + + case ShowNextLoc: + updateShowNextLoc(); + break; + + case NoState: + updateNoState(); + break; + + case LeavingIntermission: + // Hush, GCC. + break; + } + } + + //==================================================================== + // + // + // + //==================================================================== + + virtual void Drawer (void) + { + switch (CurState) + { + case StatCount: + drawStats(); + break; + + case ShowNextLoc: + drawShowNextLoc(); + break; + + case LeavingIntermission: + break; + + default: + drawNoState(); + break; + } + } + + //==================================================================== + // + // + // + //==================================================================== + + virtual void Start (wbstartstruct wbstartstruct) + { + noautostartmap = false; + wbs = wbstartstruct; + acceleratestage = 0; + cnt = bcnt = 0; + me = wbs.pnum; + for (int i = 0; i < MAXPLAYERS; i++) Plrs[i] = wbs.plyr[i]; + + entering.Init(gameinfo.mStatscreenEnteringFont); + finished.Init(gameinfo.mStatscreenFinishedFont); + mapname.Init(gameinfo.mStatscreenMapNameFont); + + Kills = TexMan.CheckForTexture("WIOSTK", TexMan.Type_MiscPatch); // "kills" + Secret = TexMan.CheckForTexture("WIOSTS", TexMan.Type_MiscPatch); // "scrt" + P_secret = TexMan.CheckForTexture("WISCRT2", TexMan.Type_MiscPatch); // "secret" + Items = TexMan.CheckForTexture("WIOSTI", TexMan.Type_MiscPatch); // "items" + Timepic = TexMan.CheckForTexture("WITIME", TexMan.Type_MiscPatch); // "time" + Sucks = TexMan.CheckForTexture("WISUCKS", TexMan.Type_MiscPatch); // "sucks" + Par = TexMan.CheckForTexture("WIPAR", TexMan.Type_MiscPatch); // "par" + + // Use the local level structure which can be overridden by hubs + lnametexts[0] = level.LevelName; + lnametexts[1] = wbs.nextname; + + bg = InterBackground.Create(wbs); + + initStats(); + } + + + protected virtual void initStats() {} + protected virtual void updateStats() {} + protected virtual void drawStats() {} + + native int, int, int GetPlayerWidths(); + native Color GetRowColor(PlayerInfo player, bool highlight); + native void GetSortedPlayers(in out Array sorted, bool teamplay); } diff --git a/wadsrc/static/zscript/statscreen/statscreen_coop.txt b/wadsrc/static/zscript/statscreen/statscreen_coop.txt new file mode 100644 index 000000000..1d8cae370 --- /dev/null +++ b/wadsrc/static/zscript/statscreen/statscreen_coop.txt @@ -0,0 +1,324 @@ + +class CoopStatusScreen : StatusScreen +{ + int textcolor; + + //==================================================================== + // + // + // + //==================================================================== + + override void initStats () + { + textcolor = (gameinfo.gametype & GAME_Raven) ? Font.CR_GREEN : Font.CR_UNTRANSLATED; + + CurState = StatCount; + acceleratestage = 0; + ng_state = 1; + + cnt_pause = Thinker.TICRATE; + + for (int i = 0; i < MAXPLAYERS; i++) + { + playerready[i] = false; + cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; + + if (!playeringame[i]) + continue; + + dofrags += fragSum (i); + } + + dofrags = !!dofrags; + } + + //==================================================================== + // + // + // + //==================================================================== + + override void updateStats () + { + + int i; + int fsum; + bool stillticking; + bool autoskip = autoSkip(); + + if ((acceleratestage || autoskip) && ng_state != 10) + { + acceleratestage = 0; + + for (i=0 ; i Plrs[i].skills) + cnt_kills[i] = Plrs[i].skills; + else + stillticking = true; + } + + if (!stillticking) + { + PlaySound("intermission/nextstage"); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt&3)) + PlaySound("intermission/tick"); + + stillticking = false; + + for (i=0 ; i Plrs[i].sitems) + cnt_items[i] = Plrs[i].sitems; + else + stillticking = true; + } + if (!stillticking) + { + PlaySound("intermission/nextstage"); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt&3)) + PlaySound("intermission/tick"); + + stillticking = false; + + for (i=0 ; i Plrs[i].ssecret) + cnt_secret[i] = Plrs[i].ssecret; + else + stillticking = true; + } + + if (!stillticking) + { + PlaySound("intermission/nextstage"); + ng_state += 1 + 2*!dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt&3)) + PlaySound("intermission/tick"); + + stillticking = false; + + for (i=0 ; i= (fsum = fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + PlaySound("intermission/cooptotal"); + ng_state++; + } + } + else if (ng_state == 10) + { + int i; + for (i = 0; i < MAXPLAYERS; i++) + { + // If the player is in the game and not ready, stop checking + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) + break; + } + + // All players are ready; proceed. + if ((i == MAXPLAYERS && acceleratestage) || autoskip) + { + PlaySound("intermission/pastcoopstats"); + initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = Thinker.TICRATE; + } + } + } + + //==================================================================== + // + // + // + //==================================================================== + + override void drawStats () + { + int i, x, y, ypadding, height, lineheight; + int maxnamewidth, maxscorewidth, maxiconheight; + int pwidth = IntermissionFont.GetCharWidth("%"); + int icon_x, name_x, kills_x, bonus_x, secret_x; + int bonus_len, secret_len; + int missed_kills, missed_items, missed_secrets; + float h, s, v, r, g, b; + int color; + String text_bonus, text_secret, text_kills; + TextureID readyico = TexMan.CheckForTexture("READYICO", TexMan.Type_MiscPatch); + + // draw animated background + bg.drawBackground(CurState, false, false); + + y = drawLF(); + + [maxnamewidth, maxscorewidth, maxiconheight] = GetPlayerWidths(); + // Use the readyico height if it's bigger. + Vector2 readysize = TexMan.GetScaledSize(readyico); + Vector2 readyoffset = TexMan.GetScaledOffset(readyico); + if (readysize.Y > maxiconheight) + { + maxiconheight = height; + } + height = SmallFont.GetHeight() * CleanYfac; + lineheight = MAX(height, maxiconheight * CleanYfac); + ypadding = (lineheight - height + 1) / 2; + y += CleanYfac; + + text_bonus = Stringtable.Localize((gameinfo.gametype & GAME_Raven) ? "$SCORE_BONUS" : "$SCORE_ITEMS"); + text_secret = Stringtable.Localize("$SCORE_SECRET"); + text_kills = Stringtable.Localize("$SCORE_KILLS"); + + icon_x = 8 * CleanXfac; + name_x = icon_x + maxscorewidth * CleanXfac; + kills_x = name_x + (maxnamewidth + MAX(SmallFont.StringWidth("XXXXX"), SmallFont.StringWidth(text_kills)) + 8) * CleanXfac; + bonus_x = kills_x + ((bonus_len = SmallFont.StringWidth(text_bonus)) + 8) * CleanXfac; + secret_x = bonus_x + ((secret_len = SmallFont.StringWidth(text_secret)) + 8) * CleanXfac; + + x = (screen.GetWidth() - secret_x) >> 1; + icon_x += x; + name_x += x; + kills_x += x; + bonus_x += x; + secret_x += x; + + + screen.DrawText(SmallFont, textcolor, name_x, y, Stringtable.Localize("$SCORE_NAME"), DTA_CleanNoMove, true); + screen.DrawText(SmallFont, textcolor, kills_x - SmallFont.StringWidth(text_kills)*CleanXfac, y, text_kills, DTA_CleanNoMove, true); + screen.DrawText(SmallFont, textcolor, bonus_x - bonus_len*CleanXfac, y, text_bonus, DTA_CleanNoMove, true); + screen.DrawText(SmallFont, textcolor, secret_x - secret_len*CleanXfac, y, text_secret, DTA_CleanNoMove, true); + y += height + 6 * CleanYfac; + + missed_kills = wbs.maxkills; + missed_items = wbs.maxitems; + missed_secrets = wbs.maxsecret; + + // Draw lines for each player + for (i = 0; i < MAXPLAYERS; ++i) + { + if (!playeringame[i]) + continue; + + PlayerInfo player = players[i]; + + screen.Dim(player.GetDisplayColor(), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); + + if (playerready[i] || player.Bot != NULL) // Bots are automatically assumed ready, to prevent confusion + screen.DrawTexture(readyico, true, x - (readysize.Y * CleanXfac), y, DTA_CleanNoMove, true); + + Color thiscolor = GetRowColor(player, i == consoleplayer); + if (player.mo.ScoreIcon.isValid()) + { + screen.DrawTexture(player.mo.ScoreIcon, true, icon_x, y, DTA_CleanNoMove, true); + } + screen.DrawText(SmallFont, thiscolor, name_x, y + ypadding, player.GetUserName(), DTA_CleanNoMove, true); + drawPercent(SmallFont, kills_x, y + ypadding, cnt_kills[i], wbs.maxkills, false, thiscolor); + missed_kills -= cnt_kills[i]; + if (ng_state >= 4) + { + drawPercent(SmallFont, bonus_x, y + ypadding, cnt_items[i], wbs.maxitems, false, thiscolor); + missed_items -= cnt_items[i]; + if (ng_state >= 6) + { + drawPercent(SmallFont, secret_x, y + ypadding, cnt_secret[i], wbs.maxsecret, false, thiscolor); + missed_secrets -= cnt_secret[i]; + } + } + y += lineheight + CleanYfac; + } + + // Draw "MISSED" line + y += 3 * CleanYfac; + screen.DrawText(SmallFont, Font.CR_DARKGRAY, name_x, y, Stringtable.Localize("$SCORE_MISSED"), DTA_CleanNoMove, true); + drawPercent(SmallFont, kills_x, y, missed_kills, wbs.maxkills, false, Font.CR_DARKGRAY); + if (ng_state >= 4) + { + drawPercent(SmallFont, bonus_x, y, missed_items, wbs.maxitems, false, Font.CR_DARKGRAY); + if (ng_state >= 6) + { + drawPercent(SmallFont, secret_x, y, missed_secrets, wbs.maxsecret, false, Font.CR_DARKGRAY); + } + } + + // Draw "TOTAL" line + y += height + 3 * CleanYfac; + screen.DrawText(SmallFont, textcolor, name_x, y, Stringtable.Localize("$SCORE_TOTAL"), DTA_CleanNoMove, true); + drawNum(SmallFont, kills_x, y, wbs.maxkills, 0, false, textcolor); + if (ng_state >= 4) + { + drawNum(SmallFont, bonus_x, y, wbs.maxitems, 0, false, textcolor); + if (ng_state >= 6) + { + drawNum(SmallFont, secret_x, y, wbs.maxsecret, 0, false, textcolor); + } + } + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/statscreen/statscreen_dm.txt b/wadsrc/static/zscript/statscreen/statscreen_dm.txt new file mode 100644 index 000000000..1697e959b --- /dev/null +++ b/wadsrc/static/zscript/statscreen/statscreen_dm.txt @@ -0,0 +1,244 @@ + +class DeathmatchStatusScreen : StatusScreen +{ + int textcolor; + + //==================================================================== + // + // + // + //==================================================================== + + override void initStats (void) + { + int i, j; + + textcolor = (gameinfo.gametype & GAME_Raven) ? Font.CR_GREEN : Font.CR_UNTRANSLATED; + + CurState = StatCount; + acceleratestage = 0; + for(i = 0; i < MAXPLAYERS; i++) + { + playerready[i] = false; + cnt_frags[i] = cnt_deaths[i] = player_deaths[i] = 0; + } + total_frags = 0; + total_deaths = 0; + + ng_state = 1; + cnt_pause = Thinker.TICRATE; + + for (i=0 ; i Plrs[i].fragcount) + cnt_frags[i] = Plrs[i].fragcount; + else + stillticking = true; + } + + if (!stillticking) + { + PlaySound("intermission/nextstage"); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt & 3)) + PlaySound("intermission/tick"); + + stillticking = false; + + for (i = 0; i player_deaths[i]) + cnt_deaths[i] = player_deaths[i]; + else + stillticking = true; + } + if (!stillticking) + { + PlaySound("intermission/nextstage"); + ng_state++; + } + } + else if (ng_state == 6) + { + int i; + for (i = 0; i < MAXPLAYERS; i++) + { + // If the player is in the game and not ready, stop checking + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) + break; + } + + // All players are ready; proceed. + if ((i == MAXPLAYERS && acceleratestage) || doautoskip) + { + PlaySound("intermission/pastdmstats"); + initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = Thinker.TICRATE; + } + } + } + + override void drawStats () + { + int i, pnum, x, y, ypadding, height, lineheight; + int maxnamewidth, maxscorewidth, maxiconheight; + int pwidth = IntermissionFont.GetCharWidth("%"); + int icon_x, name_x, frags_x, deaths_x; + int deaths_len; + String text_deaths, text_frags; + TextureID readyico = TexMan.CheckForTexture("READYICO", TexMan.Type_MiscPatch); + + // draw animated background + bg.drawBackground(CurState, false, false); + + y = drawLF(); + + [maxnamewidth, maxscorewidth, maxiconheight] = GetPlayerWidths(); + // Use the readyico height if it's bigger. + Vector2 readysize = TexMan.GetScaledSize(readyico); + Vector2 readyoffset = TexMan.GetScaledOffset(readyico); + height = readysize.Y - readyoffset.Y; + maxiconheight = MAX(height, maxiconheight); + height = SmallFont.GetHeight() * CleanYfac; + lineheight = MAX(height, maxiconheight * CleanYfac); + ypadding = (lineheight - height + 1) / 2; + y += CleanYfac; + + text_deaths = Stringtable.Localize("$SCORE_DEATHS"); + //text_color = Stringtable.Localize("$SCORE_COLOR"); + text_frags = Stringtable.Localize("$SCORE_FRAGS"); + + icon_x = 8 * CleanXfac; + name_x = icon_x + maxscorewidth * CleanXfac; + frags_x = name_x + (maxnamewidth + MAX(SmallFont.StringWidth("XXXXX"), SmallFont.StringWidth(text_frags)) + 8) * CleanXfac; + deaths_x = frags_x + ((deaths_len = SmallFont.StringWidth(text_deaths)) + 8) * CleanXfac; + + x = (Screen.GetWidth() - deaths_x) >> 1; + icon_x += x; + name_x += x; + frags_x += x; + deaths_x += x; + + screen.DrawText(SmallFont, textcolor, name_x, y, Stringtable.Localize("$SCORE_NAME"), DTA_CleanNoMove, true); + screen.DrawText(SmallFont, textcolor, frags_x - SmallFont.StringWidth(text_frags) * CleanXfac, y, text_frags, DTA_CleanNoMove, true); + screen.DrawText(SmallFont, textcolor, deaths_x - deaths_len * CleanXfac, y, text_deaths, DTA_CleanNoMove, true); + y += height + 6 * CleanYfac; + + // Sort all players + Array sortedplayers; + GetSortedPlayers(sortedplayers, teamplay); + + // Draw lines for each player + for (i = 0; i < sortedplayers.Size(); i++) + { + pnum = sortedplayers[i]; + PlayerInfo player = players[pnum]; + + if (!playeringame[pnum]) + continue; + + screen.Dim(player.GetDisplayColor(), 0.8, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); + + if (playerready[pnum] || player.Bot != NULL) // Bots are automatically assumed ready, to prevent confusion + screen.DrawTexture(readyico, true, x - (readysize.X * CleanXfac), y, DTA_CleanNoMove, true); + + let thiscolor = GetRowColor(player, pnum == consoleplayer); + if (player.mo.ScoreIcon.isValid()) + { + screen.DrawTexture(player.mo.ScoreIcon, true, icon_x, y, DTA_CleanNoMove, true); + } + screen.DrawText(SmallFont, thiscolor, name_x, y + ypadding, player.GetUserName(), DTA_CleanNoMove, true); + drawNum(SmallFont, frags_x, y + ypadding, cnt_frags[pnum], 0, false, thiscolor); + if (ng_state >= 2) + { + drawNum(SmallFont, deaths_x, y + ypadding, cnt_deaths[pnum], 0, false, thiscolor); + } + y += lineheight + CleanYfac; + } + + // Draw "TOTAL" line + y += height + 3 * CleanYfac; + screen.DrawText(SmallFont, textcolor, name_x, y, Stringtable.Localize("$SCORE_TOTAL"), DTA_CleanNoMove, true); + drawNum(SmallFont, frags_x, y, total_frags, 0, false, textcolor); + if (ng_state >= 4) + { + drawNum(SmallFont, deaths_x, y, total_deaths, 0, false, textcolor); + } + + // Draw game time + y += height + CleanYfac; + + int seconds = Plrs[me].stime * Thinker.TICRATE; + int hours = seconds / 3600; + int minutes = (seconds % 3600) / 60; + seconds = seconds % 60; + + String leveltime = Stringtable.Localize("$SCORE_LVLTIME") .. ": " .. String.Format("%02i:%02i:%02i", hours, minutes, seconds); + + screen.DrawText(SmallFont, textcolor, x, y, leveltime, DTA_CleanNoMove, true); + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/statscreen/statscreen_sp.txt b/wadsrc/static/zscript/statscreen/statscreen_sp.txt new file mode 100644 index 000000000..284560307 --- /dev/null +++ b/wadsrc/static/zscript/statscreen/statscreen_sp.txt @@ -0,0 +1,206 @@ + +class DoomStatusScreen : StatusScreen +{ + int intermissioncounter; + + override void initStats () + { + intermissioncounter = gameinfo.intermissioncounter; + CurState = StatCount; + acceleratestage = 0; + sp_state = 1; + cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; + cnt_time = cnt_par = -1; + cnt_pause = Thinker.TICRATE; + + cnt_total_time = -1; + } + + override void updateStats () + { + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + sp_state = 10; + PlaySound("intermission/nextstage"); + + cnt_kills[0] = Plrs[me].skills; + cnt_items[0] = Plrs[me].sitems; + cnt_secret[0] = Plrs[me].ssecret; + cnt_time = Plrs[me].stime / Thinker.TICRATE; + cnt_par = wbs.partime / Thinker.TICRATE; + cnt_total_time = wbs.totaltime / Thinker.TICRATE; + } + + if (sp_state == 2) + { + if (intermissioncounter) + { + cnt_kills[0] += 2; + + if (!(bcnt&3)) + PlaySound("intermission/tick"); + } + if (!intermissioncounter || cnt_kills[0] >= Plrs[me].skills) + { + cnt_kills[0] = Plrs[me].skills; + PlaySound("intermission/nextstage"); + sp_state++; + } + } + else if (sp_state == 4) + { + if (intermissioncounter) + { + cnt_items[0] += 2; + + if (!(bcnt&3)) + PlaySound("intermission/tick"); + } + if (!intermissioncounter || cnt_items[0] >= Plrs[me].sitems) + { + cnt_items[0] = Plrs[me].sitems; + PlaySound("intermission/nextstage"); + sp_state++; + } + } + else if (sp_state == 6) + { + if (intermissioncounter) + { + cnt_secret[0] += 2; + + if (!(bcnt&3)) + PlaySound("intermission/tick"); + } + if (!intermissioncounter || cnt_secret[0] >= Plrs[me].ssecret) + { + cnt_secret[0] = Plrs[me].ssecret; + PlaySound("intermission/nextstage"); + sp_state++; + } + } + else if (sp_state == 8) + { + if (intermissioncounter) + { + if (!(bcnt&3)) + PlaySound("intermission/tick"); + + cnt_time += 3; + cnt_par += 3; + cnt_total_time += 3; + } + + int sec = Plrs[me].stime / Thinker.TICRATE; + if (!intermissioncounter || cnt_time >= sec) + cnt_time = sec; + + int tsec = wbs.totaltime / Thinker.TICRATE; + if (!intermissioncounter || cnt_total_time >= tsec) + cnt_total_time = tsec; + + if (!intermissioncounter || cnt_par >= wbs.partime / Thinker.TICRATE) + { + cnt_par = wbs.partime / Thinker.TICRATE; + + if (cnt_time >= sec) + { + cnt_total_time = tsec; + PlaySound("intermission/nextstage"); + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + PlaySound("intermission/paststats"); + initShowNextLoc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = Thinker.TICRATE; + } + } + } + + override void drawStats (void) + { + // line height + int lh = IntermissionFont.GetHeight() * 3 / 2; + + // draw animated background + bg.drawBackground(CurState, false, false); + + drawLF(); + + screen.DrawTexture (Kills, true, SP_STATSX, SP_STATSY, DTA_Clean, true); + drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY, cnt_kills[0], wbs.maxkills); + + screen.DrawTexture (Items, true, SP_STATSX, SP_STATSY+lh, DTA_Clean, true); + drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+lh, cnt_items[0], wbs.maxitems); + + screen.DrawTexture (P_secret, true, SP_STATSX, SP_STATSY+2*lh, DTA_Clean, true); + drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0], wbs.maxsecret); + + screen.DrawTexture (Timepic, true, SP_TIMEX, SP_TIMEY, DTA_Clean, true); + drawTime (160 - SP_TIMEX, SP_TIMEY, cnt_time); + if (wi_showtotaltime) + { + drawTime (160 - SP_TIMEX, SP_TIMEY + lh, cnt_total_time, true); // no 'sucks' for total time ever! + } + + if (wbs.partime) + { + screen.DrawTexture (Par, 160 + SP_TIMEX, SP_TIMEY, DTA_Clean, true); + drawTime (320 - SP_TIMEX, SP_TIMEY, cnt_par); + } + } +} + +class RavenStatusScreen : DoomStatusScreen +{ + override void drawStats (void) + { + // line height + int lh = IntermissionFont.GetHeight() * 3 / 2; + + // draw animated background + bg.drawBackground(CurState, false, false); + + drawLF(); + + screen.DrawText (BigFont, Font.CR_UNTRANSLATED, 50, 65, Stringtable.Localize("$TXT_IMKILLS"), DTA_Clean, true, DTA_Shadow, true); + screen.DrawText (BigFont, Font.CR_UNTRANSLATED, 50, 90, Stringtable.Localize("$TXT_IMITEMS"), DTA_Clean, true, DTA_Shadow, true); + screen.DrawText (BigFont, Font.CR_UNTRANSLATED, 50, 115, Stringtable.Localize("$TXT_IMSECRETS"), DTA_Clean, true, DTA_Shadow, true); + + int countpos = gameinfo.gametype==GAME_Strife? 285:270; + if (sp_state >= 2) + { + drawPercent (IntermissionFont, countpos, 65, cnt_kills[0], wbs.maxkills); + } + if (sp_state >= 4) + { + drawPercent (IntermissionFont, countpos, 90, cnt_items[0], wbs.maxitems); + } + if (sp_state >= 6) + { + drawPercent (IntermissionFont, countpos, 115, cnt_secret[0], wbs.maxsecret); + } + if (sp_state >= 8) + { + screen.DrawText (BigFont, Font.CR_UNTRANSLATED, 85, 160, Stringtable.Localize("$TXT_IMTIME"), DTA_Clean, true, DTA_Shadow, true); + drawTime (249, 160, cnt_time); + if (wi_showtotaltime) + { + drawTime (249, 180, cnt_total_time); + } + } + } +} diff --git a/wadsrc/static/zscript/statscreen/types.txt b/wadsrc/static/zscript/statscreen/types.txt index 63485e035..c3dad0c76 100644 --- a/wadsrc/static/zscript/statscreen/types.txt +++ b/wadsrc/static/zscript/statscreen/types.txt @@ -18,9 +18,11 @@ struct WBStartStruct native version("2.4") { native int finished_ep; native int next_ep; + native @WBPlayerStruct plyr[MAXPLAYERS]; native String current; // [RH] Name of map just finished native String next; // next level, [RH] actual map name + native String nextname; // next level, printable name native TextureID LName0; native TextureID LName1;