diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index ab8255cdf..fa5b8a030 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -54,6 +54,7 @@ #include "r_utility.h" #include "cmdlib.h" #include "g_levellocals.h" +#include "virtual.h" #include @@ -1079,6 +1080,48 @@ bool ST_IsLatencyVisible() && (hud_showlag <= 2); } +//--------------------------------------------------------------------------- +// +// draw the overlay +// +//--------------------------------------------------------------------------- + +static void DrawPowerups(player_t *CPlayer) +{ + // Each icon gets a 32x32 block to draw itself in. + int x, y; + AInventory *item; + const int yshift = SmallFont->GetHeight(); + const int POWERUPICONSIZE = 32; + + x = hudwidth -20; + y = POWERUPICONSIZE * 5/4 + + (ST_IsTimeVisible() ? yshift : 0) + + (ST_IsLatencyVisible() ? yshift : 0); + + for (item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory) + { + IFVIRTUALPTR(item, AInventory, GetPowerupIcon) + { + VMValue param[] = { item }; + int rv; + VMReturn ret(&rv); + GlobalVMStack.Call(func, param, 1, &ret, 1); + auto tex = FSetTextureID(rv); + if (!tex.isValid()) continue; + auto texture = TexMan(tex); + + screen->DrawTexture(texture, x, y, DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_CenterBottomOffset, true, TAG_DONE); + + x -= POWERUPICONSIZE; + if (x < -hudwidth / 2) + { + x = -20; + y += POWERUPICONSIZE * 3 / 2; + } + } + } +} //--------------------------------------------------------------------------- // @@ -1158,6 +1201,7 @@ void DrawHUD() DrawTime(); DrawLatency(); + DrawPowerups(CPlayer); } else { diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h index 0b3f676ca..0079d5b71 100644 --- a/src/g_statusbar/sbar.h +++ b/src/g_statusbar/sbar.h @@ -443,6 +443,7 @@ public: double drawClip[4] = { 0,0,0,0 }; // defines a clipping rectangle (not used yet) bool fullscreenOffsets = false; // current screen is displayed with fullscreen behavior. DVector2 cleanScale; // factor for scaled fullscreen display. + FMugShot mugshot; private: bool RepositionCoords (int &x, int &y, int xo, int yo, const int w, const int h) const; diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index 6548822fa..e747717cc 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -1317,7 +1317,6 @@ public: adjustRelCenter(x.RelCenter(), y.RelCenter(), dx, dy, rx, ry, xScale, yScale); - // We can't use DTA_HUDRules since it forces a width and height. // Translation: No high res. bool xright = *x < 0 && !x.RelCenter(); bool ybot = *y < 0 && !y.RelCenter(); diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index dfbd22ffb..51106914e 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -431,6 +431,7 @@ void DBaseStatusBar::CallTick() GlobalVMStack.Call(func, params, countof(params), nullptr, 0); } else Tick(); + mugshot.Tick(CPlayer); } //--------------------------------------------------------------------------- @@ -1023,6 +1024,7 @@ void DBaseStatusBar::SetMugShotState(const char *stateName, bool waitTillDone, b VMValue params[] = { (DObject*)this, &statestring, waitTillDone, reset }; GlobalVMStack.Call(func, params, countof(params), nullptr, 0); } + mugshot.SetState(stateName, waitTillDone, reset); } //--------------------------------------------------------------------------- @@ -1052,7 +1054,18 @@ void DBaseStatusBar::DrawTopStuff (EHudState state) DTA_CleanNoMove, true, TAG_DONE); } - DrawPowerups (); + if (state != HUD_AltHud) + { + auto saved = fullscreenOffsets; + fullscreenOffsets = true; + IFVIRTUAL(DBaseStatusBar, DrawPowerups) + { + VMValue params[] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0); + } + fullscreenOffsets = saved; + } + if (automapactive && !viewactive) { DrawMessages (HUDMSGLayer_OverMap, (state == HUD_StatusBar) ? gST_Y : SCREENHEIGHT); @@ -1068,46 +1081,6 @@ void DBaseStatusBar::DrawTopStuff (EHudState state) } } -//--------------------------------------------------------------------------- -// -// DrawPowerups -// -//--------------------------------------------------------------------------- - -void DBaseStatusBar::DrawPowerups () -{ - // Each icon gets a 32x32 block to draw itself in. - int x, y; - AInventory *item; - const int yshift = SmallFont->GetHeight(); - - x = -20; - y = 17 - + (ST_IsTimeVisible() ? yshift : 0) - + (ST_IsLatencyVisible() ? yshift : 0); - for (item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory) - { - IFVIRTUALPTR(item, AInventory, DrawPowerup) - { - VMValue params[3] = { item, x, y }; - VMReturn ret; - int retv; - - ret.IntAt(&retv); - GlobalVMStack.Call(func, params, 3, &ret, 1); - if (retv) - { - x -= POWERUPICONSIZE; - if (x < -POWERUPICONSIZE * 5) - { - x = -20; - y += POWERUPICONSIZE * 2; - } - } - } - } -} - //--------------------------------------------------------------------------- // // BlendView @@ -1225,6 +1198,7 @@ void DBaseStatusBar::NewGame () VMValue params[] = { (DObject*)this }; GlobalVMStack.Call(func, params, countof(params), nullptr, 0); } + mugshot.Reset(); } void DBaseStatusBar::ShowPop(int pop) @@ -1426,7 +1400,6 @@ void DBaseStatusBar::DrawGraphic(FTextureID texture, bool animate, double x, dou // Todo: Allow other scaling values, too. if (Scaled) { - y += RelTop - VerticalResolution; screen->VirtualToRealCoords(x, y, width, height, HorizontalResolution, VerticalResolution, true, true); } } @@ -1517,8 +1490,135 @@ void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, d x -= static_cast ((spacing)* cstring.Len()) / 2; break; } + + const uint8_t* str = (const uint8_t*)cstring.GetChars(); + const EColorRange boldTranslation = EColorRange(translation ? translation - 1 : NumTextColors - 1); + int fontcolor = translation; + double orgx = 0, orgy = 0; + + if (fullscreenOffsets) + { + if (hud_scale) + { + shadowX *= (int)cleanScale.X; + shadowY *= (int)cleanScale.Y; + } + + switch (screenalign & HMASK) + { + default: orgx = 0; break; + case HCENTER: orgx = screen->GetWidth() / 2; break; + case RIGHT: orgx = screen->GetWidth(); break; + } + + switch (screenalign & VMASK) + { + default: orgy = 0; break; + case VCENTER: orgy = screen->GetHeight() / 2; break; + case BOTTOM: orgy = screen->GetHeight(); break; + } + + if (screenalign == (RIGHT | TOP) && vid_fps) orgy += 10; + } + int ch; + while (ch = *str++, ch != '\0') + { + if (ch == ' ') + { + x += monospaced ? spacing : font->GetSpaceWidth() + spacing; + continue; + } + else if (ch == TEXTCOLOR_ESCAPE) + { + EColorRange newColor = V_ParseFontColor(str, translation, boldTranslation); + if (newColor != CR_UNDEFINED) + fontcolor = newColor; + continue; + } + + int width; + FTexture* c = font->GetChar((unsigned char)ch, &width); + if (c == NULL) //missing character. + { + continue; + } + + if (!monospaced) //If we are monospaced lets use the offset + x += (c->LeftOffset + 1); //ignore x offsets since we adapt to character size + + double rx, ry, rw, rh; + rx = x + drawOffset.X; + ry = y + drawOffset.Y; + rw = c->GetScaledWidthDouble(); + rh = c->GetScaledHeightDouble(); + + if (monospaced) + { + // align the character in the monospaced cell according to the general alignment to ensure that it gets positioned properly + // (i.e. right aligned text aligns to the right edge of the character and not the empty part of the cell.) + switch (align) + { + default: + break; + case ALIGN_CENTER: + rx -= (spacing) / 2; + break; + case ALIGN_RIGHT: + rx -= spacing; + break; + } + } + + if (!fullscreenOffsets) + { + rx += ST_X; + ry += ST_Y; + + // Todo: Allow other scaling values, too. + if (Scaled) + { + screen->VirtualToRealCoords(rx, ry, rw, rh, HorizontalResolution, VerticalResolution, true); + } + } + else + { + if (hud_scale) + { + rx *= cleanScale.X; + ry *= cleanScale.Y; + rw *= cleanScale.X; + rh *= cleanScale.Y; + } + rx += orgx; + ry += orgy; + } + // This is not really such a great way to draw shadows because they can overlap with previously drawn characters. + // This may have to be changed to draw the shadow text up front separately. + if (shadowX != 0 || shadowY != 0) + { + screen->DrawChar(font, CR_UNTRANSLATED, rx + shadowX, ry + shadowY, ch, + DTA_DestWidthF, rw, + DTA_DestHeightF, rh, + DTA_Alpha, (Alpha * HR_SHADOW), + DTA_FillColor, 0, + TAG_DONE); + } + screen->DrawChar(font, fontcolor, rx, ry, ch, + DTA_DestWidthF, rw, + DTA_DestHeightF, rh, + DTA_Alpha, Alpha, + TAG_DONE); + + if (!monospaced) + x += width + spacing - (c->LeftOffset + 1); + else + x += spacing; + } + } + + DEFINE_ACTION_FUNCTION(DBaseStatusBar, DrawString) { PARAM_SELF_PROLOGUE(DBaseStatusBar); @@ -1527,7 +1627,7 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, DrawString) PARAM_FLOAT(x); PARAM_FLOAT(y); PARAM_FLOAT(alpha); - PARAM_BOOL(trans); + PARAM_INT(trans); PARAM_INT(ialign); PARAM_INT(salign); PARAM_INT_DEF(spacing); @@ -1636,3 +1736,64 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSArrayString) PARAM_INT(index); ACTION_RETURN_STRING(FBehavior::StaticLookupString(ACS_GlobalArrays[arrayno][index])); } + +DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSValue) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + ACTION_RETURN_INT(ACS_GlobalVars[index]); +} + +DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSArrayValue) +{ + PARAM_PROLOGUE; + PARAM_INT(arrayno); + PARAM_INT(index); + ACTION_RETURN_INT(ACS_GlobalArrays[arrayno][index]); +} + +enum ENumFlags +{ + FNF_FILLZEROS, + FNF_WHENNOTZERO, +}; + +DEFINE_ACTION_FUNCTION(DBaseStatusBar, FormatNumber) +{ + PARAM_PROLOGUE; + PARAM_INT(number); + PARAM_INT(minsize); + PARAM_INT(maxsize); + PARAM_INT(flags); + PARAM_STRING_DEF(prefix); + static int maxvals[] = { 1, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999 }; + + if (number == 0 && (flags & FNF_WHENNOTZERO)) ACTION_RETURN_STRING(""); + if (maxsize > 0 && maxsize < 10) + { + number = clamp(number, -maxvals[maxsize - 1], maxvals[maxsize]); + } + FString fmt; + if (minsize <= 1) fmt.Format("%s%d", prefix.GetChars(), number); + else if (flags & FNF_FILLZEROS) fmt.Format("%s%0*d", prefix.GetChars(), minsize, number); + else fmt.Format("%s%*d", prefix.GetChars(), minsize, number); + ACTION_RETURN_STRING(fmt); +} + +DEFINE_ACTION_FUNCTION(DBaseStatusBar, ReceivedWeapon) +{ + PARAM_SELF_PROLOGUE(DBaseStatusBar); + self->mugshot.Grin(); + return 0; +} + +DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetMugshot) +{ + PARAM_SELF_PROLOGUE(DBaseStatusBar); + PARAM_POINTER(player, player_t); + PARAM_STRING(def_face); + PARAM_INT(accuracy); + PARAM_INT_DEF(stateflags); + auto tex = self->mugshot.GetFace(player, def_face, accuracy, (FMugShot::StateFlags)stateflags); + ACTION_RETURN_INT(tex ? tex->id.GetIndex() : -1); +} diff --git a/src/gl/textures/gl_material.cpp b/src/gl/textures/gl_material.cpp index b4a13af7c..b981b4ecf 100644 --- a/src/gl/textures/gl_material.cpp +++ b/src/gl/textures/gl_material.cpp @@ -490,7 +490,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) mSpriteU[1] = mSpriteV[1] = 1.f; FTexture *basetex = (tx->bWarped && gl.legacyMode)? tx : tx->GetRedirect(false); - // allow the redirect only if the textute is not expanded or the scale matches. + // allow the redirect only if the texture is not expanded or the scale matches. if (!expanded || (tx->Scale.X == basetex->Scale.X && tx->Scale.Y == basetex->Scale.Y)) { mBaseLayer = ValidateSysTexture(basetex, expanded); diff --git a/src/polyrenderer/scene/poly_cull.cpp b/src/polyrenderer/scene/poly_cull.cpp index 6fe591527..01f157a93 100644 --- a/src/polyrenderer/scene/poly_cull.cpp +++ b/src/polyrenderer/scene/poly_cull.cpp @@ -287,6 +287,23 @@ bool PolyCull::GetAnglesForLine(double x1, double y1, double x2, double y2, angl return !IsSegmentCulled(angle1, angle2); } +void PolyCull::MarkViewFrustum() +{ + // Clips things outside the viewing frustum. + auto &viewpoint = PolyRenderer::Instance()->Viewpoint; + auto &viewwindow = PolyRenderer::Instance()->Viewwindow; + double tilt = fabs(viewpoint.Angles.Pitch.Degrees); + if (tilt > 46.0) // If the pitch is larger than this you can look all around + return; + + double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(viewwindow.WidescreenRatio) / 90.0; + angle_t a1 = DAngle(floatangle).BAMs(); + if (a1 < ANGLE_180) + { + MarkSegmentCulled(AngleToPseudo(viewpoint.Angles.Yaw.BAMs() + a1), AngleToPseudo(viewpoint.Angles.Yaw.BAMs() - a1)); + } +} + //----------------------------------------------------------------------------- // // ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the @@ -320,3 +337,16 @@ angle_t PolyCull::PointToPseudoAngle(double x, double y) return xs_Fix<30>::ToFix(result); } } + +angle_t PolyCull::AngleToPseudo(angle_t ang) +{ + double vecx = cos(ang * M_PI / ANGLE_180); + double vecy = sin(ang * M_PI / ANGLE_180); + + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2.f - result; + } + return xs_Fix<30>::ToFix(result); +} diff --git a/src/polyrenderer/scene/poly_cull.h b/src/polyrenderer/scene/poly_cull.h index d217de7ef..d0430501d 100644 --- a/src/polyrenderer/scene/poly_cull.h +++ b/src/polyrenderer/scene/poly_cull.h @@ -35,6 +35,7 @@ public: void MarkSegmentCulled(angle_t angle1, angle_t angle2); bool IsSegmentCulled(angle_t angle1, angle_t angle2) const; void InvertSegments(); + void MarkViewFrustum(); std::vector PvsSectors; double MaxCeilingHeight = 0.0; @@ -64,4 +65,5 @@ private: Vec4f PortalClipPlane; static angle_t PointToPseudoAngle(double x, double y); + static angle_t AngleToPseudo(angle_t ang); }; diff --git a/src/polyrenderer/scene/poly_scene.cpp b/src/polyrenderer/scene/poly_scene.cpp index 176341e97..f3a0b2887 100644 --- a/src/polyrenderer/scene/poly_scene.cpp +++ b/src/polyrenderer/scene/poly_scene.cpp @@ -73,6 +73,7 @@ void RenderPolyScene::Render(int portalDepth) ClearBuffers(); if (!PortalSegmentsAdded) Cull.ClearSolidSegments(); + Cull.MarkViewFrustum(); Cull.CullScene(WorldToClip, PortalPlane); Cull.ClearSolidSegments(); RenderSectors(); @@ -124,6 +125,8 @@ void RenderPolyScene::RenderSubsector(subsector_t *sub) } } + RenderMemory &memory = PolyRenderer::Instance()->FrameMemory; + bool mainBSP = ((unsigned int)(sub->Index()) < level.subsectors.Size()); if (mainBSP) { @@ -131,7 +134,7 @@ void RenderPolyScene::RenderSubsector(subsector_t *sub) for (int i = ParticlesInSubsec[subsectorIndex]; i != NO_PARTICLE; i = Particles[i].snext) { particle_t *particle = Particles + i; - TranslucentObjects.push_back({ particle, sub, subsectorDepth }); + TranslucentObjects.push_back(memory.NewObject(particle, sub, subsectorDepth)); } } @@ -146,7 +149,7 @@ void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, const DVe subsector_t *sub = &level.subsectors[0]; auto it = SubsectorDepths.find(sub); if (it != SubsectorDepths.end()) - TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, 0.0f, 1.0f }); + TranslucentObjects.push_back(PolyRenderer::Instance()->FrameMemory.NewObject(thing, sub, it->second, sortDistance, 0.0f, 1.0f)); } else { @@ -187,7 +190,7 @@ void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, DVector2 auto it = SubsectorDepths.find(sub); if (it != SubsectorDepths.end()) - TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, (float)t1, (float)t2 }); + TranslucentObjects.push_back(PolyRenderer::Instance()->FrameMemory.NewObject(thing, sub, it->second, sortDistance, (float)t1, (float)t2)); } void RenderPolyScene::RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth) @@ -347,29 +350,29 @@ void RenderPolyScene::RenderTranslucent(int portalDepth) } } - std::stable_sort(TranslucentObjects.begin(), TranslucentObjects.end()); + std::stable_sort(TranslucentObjects.begin(), TranslucentObjects.end(), [](auto a, auto b) { return *a < *b; }); for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it) { - auto &obj = *it; - if (obj.particle) + PolyTranslucentObject *obj = *it; + if (obj->particle) { RenderPolyParticle spr; - spr.Render(WorldToClip, PortalPlane, obj.particle, obj.sub, obj.subsectorDepth, StencilValue + 1); + spr.Render(WorldToClip, PortalPlane, obj->particle, obj->sub, obj->subsectorDepth, StencilValue + 1); } - else if (!obj.thing) + else if (!obj->thing) { - obj.wall.Render(WorldToClip, PortalPlane, Cull); + obj->wall.Render(WorldToClip, PortalPlane, Cull); } - else if ((obj.thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + else if ((obj->thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) { RenderPolyWallSprite wallspr; - wallspr.Render(WorldToClip, PortalPlane, obj.thing, obj.sub, obj.subsectorDepth, StencilValue + 1); + wallspr.Render(WorldToClip, PortalPlane, obj->thing, obj->sub, obj->subsectorDepth, StencilValue + 1); } else { RenderPolySprite spr; - spr.Render(WorldToClip, PortalPlane, obj.thing, obj.sub, obj.subsectorDepth, StencilValue + 1, obj.SpriteLeft, obj.SpriteRight); + spr.Render(WorldToClip, PortalPlane, obj->thing, obj->sub, obj->subsectorDepth, StencilValue + 1, obj->SpriteLeft, obj->SpriteRight); } } } diff --git a/src/polyrenderer/scene/poly_scene.h b/src/polyrenderer/scene/poly_scene.h index ba6fd5eea..77c7925c0 100644 --- a/src/polyrenderer/scene/poly_scene.h +++ b/src/polyrenderer/scene/poly_scene.h @@ -97,7 +97,7 @@ private: uint32_t NextSubsectorDepth = 0; std::set SeenSectors; std::unordered_map SubsectorDepths; - std::vector TranslucentObjects; + std::vector TranslucentObjects; std::vector> SectorPortals; std::vector> LinePortals; diff --git a/src/polyrenderer/scene/poly_wall.cpp b/src/polyrenderer/scene/poly_wall.cpp index 078b10e0d..abbfd15f8 100644 --- a/src/polyrenderer/scene/poly_wall.cpp +++ b/src/polyrenderer/scene/poly_wall.cpp @@ -37,7 +37,7 @@ EXTERN_CVAR(Bool, r_drawmirrors) -bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals) +bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals) { PolyDrawLinePortal *polyportal = nullptr; if (line->backsector == nullptr && line->linedef && line->sidedef == line->linedef->sidedef[0] && (line->linedef->special == Line_Mirror && r_drawmirrors)) @@ -153,7 +153,7 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const Vec4f &clipP FTexture *midtex = TexMan(line->sidedef->GetTexture(side_t::mid), true); if (midtex && midtex->UseType != FTexture::TEX_Null) - translucentWallsOutput.push_back({ wall }); + translucentWallsOutput.push_back(PolyRenderer::Instance()->FrameMemory.NewObject(wall)); if (polyportal) { @@ -165,7 +165,7 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const Vec4f &clipP return polyportal != nullptr; } -void RenderPolyWall::Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput) +void RenderPolyWall::Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput) { double frontceilz1 = fakeFloor->top.plane->ZatPoint(line->v1); double frontfloorz1 = fakeFloor->bottom.plane->ZatPoint(line->v1); diff --git a/src/polyrenderer/scene/poly_wall.h b/src/polyrenderer/scene/poly_wall.h index 014110a40..a435201b2 100644 --- a/src/polyrenderer/scene/poly_wall.h +++ b/src/polyrenderer/scene/poly_wall.h @@ -32,8 +32,8 @@ class Vec4f; class RenderPolyWall { public: - static bool RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals); - static void Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput); + static bool RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals); + static void Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput); void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2); void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull); diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 65aa1b7f6..f7e318472 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -144,16 +144,6 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawTexture) return 0; } -DEFINE_ACTION_FUNCTION(_Screen, DrawHUDTexture) -{ - PARAM_PROLOGUE; - PARAM_INT(texid); - PARAM_FLOAT(x); - PARAM_FLOAT(y); - screen->DrawTexture(TexMan(FSetTextureID(texid)), x, y, DTA_HUDRules, HUD_Normal, TAG_END); - return 0; -} - void DCanvas::DrawTextureParms(FTexture *img, DrawParms &parms) { #ifndef NO_SWRENDER @@ -220,6 +210,8 @@ bool DCanvas::SetTextureParms(DrawParms *parms, FTexture *img, double xx, double case DTA_HUDRules: case DTA_HUDRulesC: { + // Note that this has been deprecated because it cannot intelligently decide what scale + // actually needs to be used in conjunction with the active status bar. bool xright = parms->x < 0; bool ybot = parms->y < 0; diff --git a/wadsrc/static/fontdefs.txt b/wadsrc/static/fontdefs.txt index 3858ef742..c70c8024d 100644 --- a/wadsrc/static/fontdefs.txt +++ b/wadsrc/static/fontdefs.txt @@ -61,6 +61,35 @@ INDEXFONT_DOOM 9 STYSNUM9 } +INDEXFONT_STRIFE_YELLOW +{ + % INVFONY% + 0 INVFONY0 + 1 INVFONY1 + 2 INVFONY2 + 3 INVFONY3 + 4 INVFONY4 + 5 INVFONY5 + 6 INVFONY6 + 7 INVFONY7 + 8 INVFONY8 + 9 INVFONY9 +} + +INDEXFONT_STRIFE_GREEN +{ + % INVFONG% + 0 INVFONG0 + 1 INVFONG1 + 2 INVFONG2 + 3 INVFONG3 + 4 INVFONG4 + 5 INVFONG5 + 6 INVFONG6 + 7 INVFONG7 + 8 INVFONG8 + 9 INVFONG9 +} // Doom and Chex intermissions use special text glyphs. The Raven and Strife // games just use the standard big font. diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 282310bac..6b6922ad3 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -159,13 +159,18 @@ struct Screen native native static void Clear(int left, int top, int right, int bottom, Color color, int palcolor = -1); native static void Dim(Color col, double amount, int x, int y, int w, int h); - native static void DrawHUDTexture(TextureID tex, double x, double y); native static vararg void DrawTexture(TextureID tex, bool animate, double x, double y, ...); native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...); native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...); native static void DrawFrame(int x, int y, int w, int h); native static Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true); native static double GetAspectRatio(); + + // This is a leftover of the abandoned Inventory.DrawPowerup method. + deprecated("2.5") static ui void DrawHUDTexture(TextureID tex, double x, double y) + { + statusBar.DrawTexture(tex, (x, y), true, 1., BaseStatusBar.ALIGN_TOP|BaseStatusBar.ALIGN_RIGHT, (32, 32), BaseStatusBar.ALIGN_CENTER_BOTTOM); + } } struct Font native diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index d5c15e0b2..c526939a1 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -820,13 +820,28 @@ class Inventory : Actor native // // AInventory :: DrawPowerup // - // Gives self item a chance to draw a special status indicator on the screen. - // Returns false if it didn't draw anything. + // This has been deprecated because it is not how this should be done + // Use GetPowerupIcon instead! // //=========================================================================== virtual ui version("2.4") bool DrawPowerup(int x, int y) { return false; } + //=========================================================================== + // + // AInventory :: GetPowerupIcon + // + // Returns the icon that should be drawn for an active powerup. + // + //=========================================================================== + + virtual clearscope version("2.5") TextureID GetPowerupIcon() const + { + TextureID id; + id.SetInvalid(); + return id; + } + //=========================================================================== // // AInventory :: AbsorbDamage diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 3861f12a8..045331672 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -273,17 +273,9 @@ class Powerup : Inventory // //=========================================================================== - override bool DrawPowerup (int x, int y) + override TextureID GetPowerupIcon() { - if (!Icon.isValid()) - { - return false; - } - if (!isBlinking()) - { - screen.DrawHUDTexture(Icon, x, y); - } - return true; + return Icon; } //=========================================================================== @@ -292,7 +284,7 @@ class Powerup : Inventory // //=========================================================================== - virtual bool isBlinking() const + virtual clearscope bool isBlinking() const { return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink); } @@ -941,7 +933,7 @@ class PowerFlight : Powerup +INVENTORY.HUBPOWER } - ui bool HitCenterFrame; + clearscope bool HitCenterFrame; //=========================================================================== // @@ -1011,50 +1003,46 @@ class PowerFlight : Powerup // //=========================================================================== - override bool DrawPowerup (int x, int y) + override TextureID GetPowerupIcon () { // If this item got a valid icon use that instead of the default spinning wings. if (Icon.isValid()) { - return Super.DrawPowerup(x, y); + return Icon; } - if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16)) - { - TextureID picnum = TexMan.CheckForTexture ("SPFLY0", TexMan.Type_MiscPatch); - int frame = (level.time/3) & 15; + TextureID picnum = TexMan.CheckForTexture ("SPFLY0", TexMan.Type_MiscPatch); + int frame = (level.time/3) & 15; - if (!picnum.isValid()) + if (!picnum.isValid()) + { + return picnum; + } + if (Owner.bNoGravity) + { + if (HitCenterFrame && (frame != 15 && frame != 0)) { - return false; - } - if (Owner.bNoGravity) - { - if (HitCenterFrame && (frame != 15 && frame != 0)) - { - screen.DrawHUDTexture (picnum + 15, x, y); - } - else - { - screen.DrawHUDTexture (picnum + frame, x, y); - HitCenterFrame = false; - } + return picnum + 15; } else { - if (!HitCenterFrame && (frame != 15 && frame != 0)) - { - screen.DrawHUDTexture (picnum + frame, x, y); - HitCenterFrame = false; - } - else - { - screen.DrawHUDTexture (picnum+15, x, y); - HitCenterFrame = true; - } + HitCenterFrame = false; + return picnum + frame; + } + } + else + { + if (!HitCenterFrame && (frame != 15 && frame != 0)) + { + HitCenterFrame = false; + return picnum + frame; + } + else + { + HitCenterFrame = true; + return picnum+15; } } - return true; } diff --git a/wadsrc/static/zscript/statusbar/statusbar.txt b/wadsrc/static/zscript/statusbar/statusbar.txt index 742d0995b..2a1b92492 100644 --- a/wadsrc/static/zscript/statusbar/statusbar.txt +++ b/wadsrc/static/zscript/statusbar/statusbar.txt @@ -1,4 +1,21 @@ +struct MugShot +{ + enum StateFlags + { + STANDARD = 0x0, + + XDEATHFACE = 0x1, + ANIMATEDGODMODE = 0x2, + DISABLEGRIN = 0x4, + DISABLEOUCH = 0x8, + DISABLEPAIN = 0x10, + DISABLERAMPAGE = 0x20, + CUSTOM = 0x40, + } +} + + class BaseStatusBar native ui { enum EPop @@ -46,19 +63,24 @@ class BaseStatusBar native ui DI_SKIPREADY = 0x8, DI_ALTICONFIRST = 0x10, DI_TRANSLATABLE = 0x20, - DI_FORCESCALE = 0x40 + DI_FORCESCALE = 0x40, + DI_DIM = 0x80, }; enum IconType { - PLAYERICON = 1000, - AMMO1, - AMMO2, - ARMOR, - WEAPONICON, - SIGIL, - WEAPONSLOT, - SELECTEDINVENTORYICON, + ITYPE_PLAYERICON = 1000, + ITYPE_AMMO1, + ITYPE_AMMO2, + ITYPE_ARMOR, + ITYPE_WEAPON, + ITYPE_SIGIL, + ITYPE_WEAPONSLOT, + ITYPE_SELECTEDINVENTORY, + } + + enum HexArmorType + { HEXENARMOR_ARMOR, HEXENARMOR_SHIELD, HEXENARMOR_HELM, @@ -67,35 +89,36 @@ class BaseStatusBar native ui enum EAlign { - TOP = 0, - VCENTER = 1, - BOTTOM = 2, - VOFFSET = 3, - VMASK = 3, + ALIGN_TOP = 0, + ALIGN_VCENTER = 1, + ALIGN_BOTTOM = 2, + ALIGN_VOFFSET = 3, + ALIGN_VMASK = 3, - LEFT = 0, - HCENTER = 4, - RIGHT = 8, - HOFFSET = 12, - HMASK = 12, + ALIGN_LEFT = 0, + ALIGN_HCENTER = 4, + ALIGN_RIGHT = 8, + ALIGN_HOFFSET = 12, + ALIGN_HMASK = 12, - CENTER = VCENTER|HCENTER, - CENTER_BOTTOM = BOTTOM|HCENTER + ALIGN_CENTER = ALIGN_VCENTER|ALIGN_HCENTER, + ALIGN_CENTER_BOTTOM = ALIGN_BOTTOM|ALIGN_HCENTER, + ALIGN_OFFSETS = ALIGN_HOFFSET|ALIGN_VOFFSET }; enum ETextAlign { - ALIGN_LEFT = 0, - ALIGN_CENTER = 1, - ALIGN_RIGHT = 2 + TEXT_LEFT = 0, + TEXT_CENTER = 1, + TEXT_RIGHT = 2 }; enum SBGameModes { - SINGLEPLAYER = 0x1, - COOPERATIVE = 0x2, - DEATHMATCH = 0x4, - TEAMGAME = 0x8 + GAMEMODE_SINGLEPLAYER = 0x1, + GAMEMODE_COOPERATIVE = 0x2, + GAMEMODE_DEATHMATCH = 0x4, + GAMEMODE_TEAMGAME = 0x8 }; enum AmmoModes @@ -112,8 +135,15 @@ class BaseStatusBar native ui HUD_HorizCenter } + enum ENumFlags + { + FNF_FILLZEROS, + FNF_WHENNOTZERO, + } + const XHAIRSHRINKSIZE =(1./18); const XHAIRPICKUPSIZE = (2+XHAIRSHRINKSIZE); + const POWERUPICONSIZE = 32; native int ST_X, ST_Y; @@ -143,22 +173,25 @@ class BaseStatusBar native ui native virtual void Tick (); native virtual void Draw (int state, double TicFrac); native virtual void ScreenSizeChanged (); + native virtual clearscope void ReceivedWeapon (Weapon weapn); virtual void FlashItem (class itemtype) {} virtual void AttachToPlayer (PlayerInfo player) { CPlayer = player; } virtual void FlashCrosshair () { CrosshairSize = XHAIRPICKUPSIZE; } virtual void NewGame () {} virtual void ShowPop (int popnum) { ShowLog = (popnum == POP_Log && !ShowLog); } - virtual clearscope void ReceivedWeapon (Weapon weapn) {} virtual bool MustDrawLog(int state) { return true; } virtual void SetMugShotState (String state_name, bool wait_till_done=false, bool reset=false) {} native void RefreshBackground () const; + native TextureID GetMugshot(PlayerInfo player, String default_face, int accuracy, int stateflags=MugShot.STANDARD); + + // These functions are kept native solely for performance reasons. They get called repeatedly and can drag down performance easily if they get too slow. native Inventory ValidateInvFirst (int numVisible) const; native static TextureID, bool GetInventoryIcon(Inventory item, int flags); - native void DrawGraphic(TextureID texture, bool animate, Vector2 pos, double Alpha, bool translatable, bool dim, int imgAlign, int screenalign, bool alphamap, Vector2 box); native void DrawString(Font font, String string, Vector2 pos , double Alpha, int translation, int align, int screenalign, int spacing=0, bool monospaced = false, int shadowX=0, int shadowY=0, int wrapwidth = -1, int linespacing = 4); + native static String FormatNumber(int number, int minsize, int maxsize, int format, String prefix = ""); //============================================================================ @@ -243,10 +276,74 @@ class BaseStatusBar native ui if (w == null) return ""; return w.GetTag(); } - + // These cannot be done in ZScript. native String GetGlobalACSString(int index); native String GetGlobalACSArrayString(int arrayno, int index); + native int GetGlobalACSValue(int index); + native int GetGlobalACSArrayValue(int arrayno, int index); + + //============================================================================ + // + // Convenience functions to retrieve some numbers + // + //============================================================================ + + int GetArmorAmount() + { + let armor = CPlayer.mo.FindInventory("BasicArmor"); + return armor? armor.Amount : 0; + } + + int GetMaxAmount(class item) + { + let it = CPlayer.mo.FindInventory(item); + return it? it.MaxAmount : GetDefaultByType(item).MaxAmount; + } + + int GetArmorSavePercent() + { + double add = 0; + let harmor = HexenArmor(CPlayer.mo.FindInventory("HexenArmor")); + if(harmor != NULL) + { + add = harmor.Slots[0] + harmor.Slots[1] + harmor.Slots[2] + harmor.Slots[3] + harmor.Slots[4]; + } + //Hexen counts basic armor also so we should too. + let armor = BasicArmor(CPlayer.mo.FindInventory("BasicArmor")); + if(armor != NULL) + { + add += armor.SavePercent * 100; + } + return int(add); + } + + // Note that this retrieves the value in tics, not seconds like the equivalent SBARINFO function. + // The idea is to let the caller decide what to do with it instead of destroying accuracy here. + int GetAirTime() + { + if(CPlayer.mo.waterlevel < 3) + return level.airsupply; + else + return max(CPlayer.air_finished - level.time, 0); + } + + int GetSelectedInventoryAmount() + { + if(CPlayer.mo.InvSel != NULL) return CPlayer.mo.InvSel.Amount; + return 0; + } + + int GetKeyCount() + { + int num = 0; + for(Inventory item = CPlayer.mo.Inv;item != NULL;item = item.Inv) + { + if(item is "Key") num++; + } + return num; + } + //============================================================================ // // various checker functions, based on SBARINFOs condition nodes. @@ -261,10 +358,10 @@ class BaseStatusBar native ui bool CheckGameMode(int ValidModes) { - return (!multiplayer && (ValidModes & SINGLEPLAYER)) || - (deathmatch && (ValidModes & DEATHMATCH)) || - (multiplayer && !deathmatch && (ValidModes & COOPERATIVE)) || - (teamplay && (ValidModes & TEAMGAME)); + return (!multiplayer && (ValidModes & GAMEMODE_SINGLEPLAYER)) || + (deathmatch && (ValidModes & GAMEMODE_DEATHMATCH)) || + (multiplayer && !deathmatch && (ValidModes & GAMEMODE_COOPERATIVE)) || + (teamplay && (ValidModes & GAMEMODE_TEAMGAME)); } //============================================================================ @@ -416,6 +513,35 @@ class BaseStatusBar native ui } + //--------------------------------------------------------------------------- + // + // DrawPowerups + // + //--------------------------------------------------------------------------- + + virtual void DrawPowerups () + { + // The AltHUD specific adjustments have been removed here, because the AltHUD uses its own variant of this function + // that can obey AltHUD rules - which this cannot. + Vector2 pos = (-20, POWERUPICONSIZE * 5 / 4); + double maxpos = screen.GetWidth() / 2; + for (let item = CPlayer.mo.Inv; item != NULL; item = item.Inv) + { + let icon = item.GetPowerupIcon(); + if (icon.IsValid()) + { + // Each icon gets a 32x32 block. + DrawTexture(icon, pos, true, 1.0, ALIGN_TOP|ALIGN_RIGHT, (POWERUPICONSIZE, POWERUPICONSIZE), ALIGN_CENTER_BOTTOM); + pos.x -= POWERUPICONSIZE; + if (pos.x < -maxpos) + { + pos.x = -20; + pos.y += POWERUPICONSIZE * 3 / 2; + } + } + } + } + //============================================================================ // // draw stuff @@ -436,7 +562,7 @@ class BaseStatusBar native ui // //============================================================================ - void DrawTexture(TextureID texture, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0, Vector2 scale = (1., 1.) ) + void DrawTexture(TextureID texture, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0, Vector2 scale = (1., 1.) ) { if (!texture.IsValid()) return; // nothing to draw @@ -467,13 +593,12 @@ class BaseStatusBar native ui else scale1 = min(scale1, scale2); boxsize = texsize * scale1; - screenAlign = TOP|LEFT; // anything else makes no sense here. } else { boxsize = texsize; } - DrawGraphic(texture, animated, pos, Alpha, !!(flags & DI_TRANSLATABLE), false, itemAlign, screenAlign, false, boxsize); + DrawGraphic(texture, animated, pos, Alpha, !!(flags & DI_TRANSLATABLE), !!(flags & DI_DIM), itemAlign, screenAlign, false, boxsize); } //============================================================================ @@ -482,7 +607,7 @@ class BaseStatusBar native ui // //============================================================================ - void DrawImage(String imagename, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0, Vector2 scale = (1., 1.) ) + void DrawImage(String imagename, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0, Vector2 scale = (1., 1.) ) { let tex = TexMan.CheckForTexture(imagename, TexMan.TYPE_MiscPatch); DrawTexture(tex, pos, animated, screenalign, alpha, boxsize, itemAlign, flags, scale); @@ -494,36 +619,36 @@ class BaseStatusBar native ui // //============================================================================ - void DrawIcon(int icontype, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0) + void DrawIcon(int icontype, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0) { TextureID texture; Vector2 applyscale = (1, 1); Inventory atype1, atype2; switch (icontype) { - case PLAYERICON: + case ITYPE_PLAYERICON: texture = CPlayer.mo.ScoreIcon; break; - case AMMO1: - case AMMO2: + case ITYPE_AMMO1: + case ITYPE_AMMO2: [atype1, atype2] = GetCurrentAmmo(); - [texture, applyscale] = GetIcon(icontype == AMMO1? atype1 : atype2, flags, true); + [texture, applyscale] = GetIcon(icontype == ITYPE_AMMO1? atype1 : atype2, flags, true); break; - case ARMOR: + case ITYPE_ARMOR: [texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("BasicArmor"), flags, false); break; - case WEAPONICON: + case ITYPE_WEAPON: [texture, applyscale] = GetIcon(CPlayer.ReadyWeapon, flags, false); break; - case SIGIL: + case ITYPE_SIGIL: [texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("Sigil"), flags, false); break; - case SELECTEDINVENTORYICON: + case ITYPE_SELECTEDINVENTORY: if (CPlayer.mo.InvSel != NULL) texture = CPlayer.mo.InvSel.Icon; break; @@ -537,7 +662,7 @@ class BaseStatusBar native ui // //============================================================================ - void DrawHexenArmor(int armortype, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0) + void DrawHexenArmor(int armortype, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0) { let harmor = HexenArmor(statusBar.CPlayer.mo.FindInventory("HexenArmor")); if (harmor != NULL) @@ -561,7 +686,7 @@ class BaseStatusBar native ui // //============================================================================ - void DrawInventoryIcon(class item, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0) + void DrawInventoryIcon(class item, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0) { let texture = GetDefaultByType(item).Icon; if (texture.IsValid()) @@ -569,4 +694,85 @@ class BaseStatusBar native ui DrawTexture(texture, pos, animated, screenalign, alpha, boxsize, itemAlign, flags); } } + } + +//============================================================================ +// +// a generic value interpolator for status bar elements that can change +// gradually to their new value. +// +//============================================================================ + +class LinearValueInterpolator : Object +{ + int mCurrentValue; + int mMaxChange; + + static LinearValueInterpolator Create(int startval, int maxchange) + { + let v = new("LinearValueInterpolator"); + v.mCurrentValue = startval; + v.mMaxChange = maxchange; + return v; + } + + // This must be called peroiodically in the status bar's Tick function. + // Do not call this in the Draw function because that may skip some frames! + void Update(int destvalue) + { + if (mCurrentValue > destvalue) + { + mCurrentValue = max(destvalue, mCurrentValue - mMaxChange); + } + else + { + mCurrentValue = min(destvalue, mCurrentValue + mMaxChange); + } + } + + // This must be called in the draw function to retrieve the value for output. + int GetValue() + { + return mCurrentValue; + } +} + +class DynamicValueInterpolator : Object +{ + int mCurrentValue; + int mMinChange; + double mChangeFactor; + + + static DynamicValueInterpolator Create(int startval, double changefactor, int minchange) + { + let v = new("DynamicValueInterpolator"); + v.mCurrentValue = startval; + v.mMinChange = minchange; + v.mChangeFactor = changefactor; + return v; + } + + // This must be called peroiodically in the status bar's Tick function. + // Do not call this in the Draw function because that may skip some frames! + void Update(int destvalue) + { + int diff = int(max(abs(destvalue - mCurrentValue) * mChangeFactor, mMinChange)); + if (mCurrentValue > destvalue) + { + mCurrentValue = max(destvalue, mCurrentValue - diff); + } + else + { + mCurrentValue = min(destvalue, mCurrentValue + diff); + } + } + + // This must be called in the draw function to retrieve the value for output. + int GetValue() + { + return mCurrentValue; + } +} + diff --git a/wadsrc/static/zscript/statusbar/strife_sbar.txt b/wadsrc/static/zscript/statusbar/strife_sbar.txt index f75bfffb5..cb2ef8a5c 100644 --- a/wadsrc/static/zscript/statusbar/strife_sbar.txt +++ b/wadsrc/static/zscript/statusbar/strife_sbar.txt @@ -48,7 +48,6 @@ class StrifeStatusBar : BaseStatusBar imgARM1, imgARM2, imgNEGATIVE, - imgINumbers = imgFONG0, }; TextureID Images[imgNEGATIVE + 1]; @@ -79,18 +78,21 @@ class StrifeStatusBar : BaseStatusBar if (state == HUD_StatusBar) { + fullscreenoffsets = false; DrawMainBar (TicFrac); } else { if (state == HUD_Fullscreen) { + fullscreenoffsets = true; DrawFullScreenStuff (); } // Draw pop screen (log, keys, and status) if (CurrentPop != POP_None && PopHeight < 0) { + fullscreenoffsets = false; DrawPopScreen (screen.GetHeight(), TicFrac); } } @@ -302,11 +304,11 @@ class StrifeStatusBar : BaseStatusBar DrawPopScreen (Scaled ? (ST_Y - 8) * screen.GetHeight() / 200 : ST_Y - 8, TicFrac); } - DrawImage (Images[imgINVBACK], 0, 0); - DrawImage (Images[imgINVTOP], 0, -8); + DrawTexture(Images[imgINVBACK], (0, 0), true, 1.0, itemAlign:ALIGN_OFFSETS); + DrawTexture(Images[imgINVTOP], (0, -8), true, 1.0, itemAlign:ALIGN_OFFSETS); // Health - DrINumber (CPlayer.health, 79, -6, imgFONG0); + DrawString("Indexfont_Strife_Green", FormatNumber(CPlayer.health, 3, 5, 0), (86, -6), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, 0, 7, true, 1, 1); int points; if (CPlayer.cheats & CF_GODMODE) { @@ -323,8 +325,8 @@ class StrifeStatusBar : BaseStatusBar item = CPlayer.mo.FindInventory('BasicArmor'); if (item != NULL && item.Amount > 0) { - DrawImage (item.Icon, 2, 9); - DrINumber (item.Amount, 27, 23, imgFONY0); + DrawTexture(item.Icon, (2, 9), true, 1.0, itemAlign:ALIGN_OFFSETS); + DrawString("Indexfont_Strife_Yellow", FormatNumber(item.Amount, 3, 5, 0), (34, 23), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, 0, 7, true, 1, 1); } // Ammo @@ -334,15 +336,15 @@ class StrifeStatusBar : BaseStatusBar [ammo1, ammo2, ammocount1, ammocount2] = GetCurrentAmmo (); if (ammo1 != NULL) { - DrINumber (ammo1.Amount, 311, -6, imgFONG0); - DrawImage (ammo1.Icon, 290, 13); + DrawString("Indexfont_Strife_Green", FormatNumber(ammo1.Amount, 3, 5, 0), (318, -6), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, 0, 7, true, 1, 1); + DrawTexture (ammo1.Icon, (290, 13), true, 1.0, itemAlign:ALIGN_OFFSETS); } // Sigil item = CPlayer.mo.FindInventory('Sigil'); if (item != NULL) { - DrawImage (item.Icon, 253, 7); + DrawTexture (item.Icon, (253, 7), true, 1.0, itemAlign:ALIGN_OFFSETS); } // Inventory @@ -353,33 +355,30 @@ class StrifeStatusBar : BaseStatusBar { if (item == CPlayer.mo.InvSel) { - screen.DrawTexture (Images[CursorImage], true, - 42 + 35*i + ST_X, 12 + ST_Y, - DTA_Bottom320x200, Scaled, - DTA_Alpha, 1. - ItemFlash); + DrawTexture (item.Icon, (42 + 35*i, 12), true, 1. - ItemFlash, itemAlign:ALIGN_OFFSETS, item.Amount <= 0? DI_DIM : 0); } if (item.Icon.isValid()) { - DrawDimImage (item.Icon, 48 + 35*i, 14, item.Amount <= 0); + DrawTexture (item.Icon, (48 + 35*i, 14), true, 1.0, itemAlign:ALIGN_OFFSETS, item.Amount <= 0? DI_DIM : 0); } - DrINumber (item.Amount, 74 + 35*i, 23, imgFONY0); + DrawString("Indexfont_Strife_Yellow", FormatNumber(item.Amount, 3, 5, 0), (81 + 35*i, 23), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, 0, 7, true, 1, 1); i++; } } protected void DrawFullScreenStuff () { - fullscreenoffsets = true; // Draw health - DrINumberOuter (CPlayer.health, 4, -10, false, 7); - DrawTexture(Images[imgMEDI], (14, -17), false, 1.0, BOTTOM|LEFT, itemalign: BOTTOM|HCENTER); + + DrawString("Indexfont_Strife_Green", FormatNumber(CPlayer.health, 3, 0, 0), (4, -10), 1., (CPlayer.health < CPlayer.mo.RunHealth)? Font.CR_BRICK : Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_LEFT|ALIGN_BOTTOM, 7, true, 1, 1); + DrawTexture(Images[imgMEDI], (14, -17), false, 1.0, ALIGN_BOTTOM|ALIGN_LEFT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER); // Draw armor let armor = CPlayer.mo.FindInventory('BasicArmor'); if (armor != NULL && armor.Amount != 0) { - DrINumberOuter (armor.Amount, 35, -10, false, 7); - DrawTexture(armor.Icon, (45, -17), false, 1.0, BOTTOM|LEFT, itemalign: BOTTOM|HCENTER); + DrawString("Indexfont_Strife_Yellow", FormatNumber(armor.Amount, 3, 0, 0), (35, -10), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_LEFT|ALIGN_BOTTOM, 7, true, 1, 1); + DrawTexture(armor.Icon, (45, -17), false, 1.0, ALIGN_BOTTOM|ALIGN_LEFT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER); } // Draw ammo @@ -390,19 +389,19 @@ class StrifeStatusBar : BaseStatusBar if (ammo1 != NULL) { // Draw primary ammo in the bottom-right corner - DrINumberOuter (ammo1.Amount, -23, -10, false, 7); - DrawTexture(ammo1.Icon, (-14, -17), false, 1.0, BOTTOM|RIGHT, itemalign: BOTTOM|HCENTER); + DrawString("Indexfont_Strife_Green", FormatNumber(ammo1.Amount, 3, 0, 0), (-23, -10), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_RIGHT|ALIGN_BOTTOM, 7, true, 1, 1); + DrawTexture(ammo1.Icon, (-14, -17), false, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER); if (ammo2 != NULL && ammo1!=ammo2) { // Draw secondary ammo just above the primary ammo - DrINumberOuter (ammo2.Amount, -23, -48, false, 7); - DrawTexture(ammo1.Icon, (-14, -55), false, 1.0, BOTTOM|RIGHT, itemalign: BOTTOM|HCENTER); + DrawString("Indexfont_Strife_Green", FormatNumber(ammo1.Amount, 3, 0, 0), (-23, -48), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_RIGHT|ALIGN_BOTTOM, 7, true, 1, 1); + DrawTexture(ammo1.Icon, (-14, -55), false, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER); } } if (deathmatch) { // Draw frags (in DM) - DrBNumberOuterFont (CPlayer.fragcount, -44, 1); + DrawString("BigFont", FormatNumber(CPlayer.FragCount, 3, 0, 0), (-44, 1), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_RIGHT|ALIGN_TOP, 0, false, 2, 2); } // Draw inventory @@ -412,18 +411,10 @@ class StrifeStatusBar : BaseStatusBar { if (ItemFlash > 0) { - vector2 size = TexMan.GetScaledSize(Images[CursorImage]); - screen.DrawTexture (Images[CursorImage], true, -28, -15, - DTA_HUDRules, HUD_Normal, - DTA_LeftOffsetF, size.X, - DTA_TopOffsetF, size.Y, - DTA_Alpha, ItemFlash); + DrawTexture(Images[CursorImage], (-28, -15), true, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, ItemAlign:ALIGN_BOTTOM|ALIGN_RIGHT); } - DrINumberOuter (CPlayer.mo.InvSel.Amount, -51, -10, false, 7); - screen.DrawTexture (CPlayer.mo.InvSel.Icon, true, -42, -17, - DTA_HUDRules, HUD_Normal, - DTA_CenterBottomOffset, true, - DTA_ColorOverlay, CPlayer.mo.InvSel.Amount > 0 ? 0 : Color(170, 0, 0, 0)); + DrawString("Indexfont_Strife_Yellow", FormatNumber(CPlayer.mo.InvSel.Amount, 3, 5, 0), (-23, -10), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, ALIGN_BOTTOM|ALIGN_RIGHT, 7, true, 1, 1); + DrawTexture(CPlayer.mo.InvSel.Icon, (-42, -17), true, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, ItemAlign:ALIGN_BOTTOM|ALIGN_HCENTER, CPlayer.mo.InvSel.Amount > 0 ? 0 : DI_DIM); } } else @@ -431,23 +422,20 @@ class StrifeStatusBar : BaseStatusBar CPlayer.mo.InvFirst = ValidateInvFirst (6); int i = 0; Inventory item; + Vector2 box = TexMan.GetScaledSize(Images[CursorImage]) - (4, 4); // Fit oversized icons into the box. if (CPlayer.mo.InvFirst != NULL) { for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv()) { if (item == CPlayer.mo.InvSel) { - screen.DrawTexture (Images[CursorImage], true, -100+i*35, -21, - DTA_HUDRules, HUD_HorizCenter, - DTA_Alpha, 0.75); + DrawTexture(Images[CursorImage], (-90+i*35, -3), true, 0.75, ALIGN_CENTER_BOTTOM, ItemAlign:ALIGN_CENTER_BOTTOM); } if (item.Icon.isValid()) { - screen.DrawTexture (item.Icon, true, -94 + i*35, -19, - DTA_HUDRules, HUD_HorizCenter, - DTA_ColorOverlay, CPlayer.mo.InvSel.Amount > 0 ? 0 : Color(170, 0, 0, 0)); + DrawTexture(item.Icon, (-90+i*35, -5), true, 0.75, ALIGN_CENTER_BOTTOM, box, ALIGN_CENTER_BOTTOM, CPlayer.mo.InvSel.Amount > 0 ? 0 : DI_DIM); } - DrINumberOuter (item.Amount, -89 + i*35, -10, true, 7); + DrawString("Indexfont_Strife_Yellow", FormatNumber(item.Amount, 3, 5, 0), (-65 + i*35, -8), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, ALIGN_CENTER_BOTTOM, 7, true, 1, 1); ++i; } } @@ -480,14 +468,8 @@ class StrifeStatusBar : BaseStatusBar { case POP_Log: { - int seconds = Thinker.Tics2Seconds(level.time); // Draw the latest log message. - buff = String.Format("%02d:%02d:%02d", - seconds / 3600, - (seconds % 3600) / 60, - (seconds) % 60); - - screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff, + screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, Level.TimeFormatted(), DTA_CleanNoMove, true); if (CPlayer.LogText.Length() > 0) @@ -640,25 +622,6 @@ class StrifeStatusBar : BaseStatusBar } } - void DrINumber (int val, int x, int y, int imgBase) const - { - x -= 7; - - if (val == 0) - { - DrawImage (Images[imgBase], x, y); - } - else - { - while (val != 0) - { - DrawImage (Images[imgBase+val%10], x, y); - val /= 10; - x -= 7; - } - } - } - void DrINumber2 (int val, int x, int y, int width, int imgBase) const { x -= width; @@ -677,183 +640,5 @@ class StrifeStatusBar : BaseStatusBar } } } - - - //--------------------------------------------------------------------------- - // - // PROC DrINumberOuter - // - // Draws a number outside the status bar, possibly scaled. - // - //--------------------------------------------------------------------------- - - void DrINumberOuter(int val, int x, int y, bool center, int w) const - { - bool negative = false; - - x += w * 2; - if (val < 0) - { - negative = true; - val = -val; - } - else if (val == 0) - { - screen.DrawTexture(Images[imgINumbers], true, x + 1, y + 1, - DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, - DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); - screen.DrawTexture(Images[imgINumbers], true, x, y, - DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); - return; - } - - int oval = val; - int ox = x; - - // First the shadow - while (val != 0) - { - screen.DrawTexture(Images[imgINumbers + val % 10], true, x + 1, y + 1, - DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, - DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); - x -= w; - val /= 10; - } - if (negative) - { - screen.DrawTexture(Images[imgNEGATIVE], true, x + 1, y + 1, - DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, - DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); - } - - // Then the real deal - val = oval; - x = ox; - while (val != 0) - { - screen.DrawTexture(Images[imgINumbers + val % 10], true, x, y, - DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); - x -= w; - val /= 10; - } - if (negative) - { - screen.DrawTexture(Images[imgNEGATIVE], true, x, y, - DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); - } - } - - - //--------------------------------------------------------------------------- - // - // PROC DrBNumberOuter - // - // Draws a three digit number using the real big font outside the status bar. - // - //--------------------------------------------------------------------------- - - void DrBNumberOuterFont(int val, int x, int y, int size = 3) const - { - int v; - bool negative = false; - TextureID pic; - - int w = BigFont.GetCharWidth("0"); - int ww = w; - - if (w > 1) - { - w--; - } - int xpos = x + w / 2 + (size - 1)*w; - - if (val == 0) - { - screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, "0", - DTA_HUDRules, HUD_Normal, - DTA_Alpha, HR_SHADOW, - DTA_FillColor, 0); - screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "0", - DTA_HUDRules, HUD_Normal); - return; - } - else if (val < 0) - { - negative = true; - val = -val; - } - - int oval = val; - int oxpos = xpos; - - // First the shadow - while (val != 0) - { - v = BigFont.GetCharWidth(int("0") + val % 10); - screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, - DTA_HUDRules, HUD_Normal, - DTA_Alpha, HR_SHADOW, - DTA_FillColor, 0); - val /= 10; - xpos -= w; - } - if (negative) - { - v = BigFont.GetCharWidth("-"); - screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, "-", - DTA_HUDRules, HUD_Normal, - DTA_Alpha, HR_SHADOW, - DTA_FillColor, 0); - } - - // Then the foreground number - val = oval; - xpos = oxpos; - while (val != 0) - { - v = BigFont.GetCharWidth(int("0") + val % 10); - screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "0", DTA_HUDRules, HUD_Normal); - val /= 10; - xpos -= w; - } - if (negative) - { - v = BigFont.GetCharWidth("-"); - screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "-", DTA_HUDRules, HUD_Normal); - } - } - - //--------------------------------------------------------------------------- - // - // PROC DrawImage - // - // Draws an image with the status bar's upper-left corner as the origin. - // - //--------------------------------------------------------------------------- - - void DrawImage(TextureID img, int x, int y) const - { - if (img.IsValid()) - { - screen.DrawTexture(img, true, x + ST_X, y + ST_Y, DTA_Bottom320x200, Scaled); - } - } - - //--------------------------------------------------------------------------- - // - // PROC DrawImage - // - // Draws an optionally dimmed image with the status bar's upper-left corner - // as the origin. - // - //--------------------------------------------------------------------------- - - void DrawDimImage(TextureID img, int x, int y, bool dimmed) const - { - if (img.IsValid()) - { - screen.DrawTexture(img, true, x + ST_X, y + ST_Y, DTA_ColorOverlay, dimmed ? Color(170, 0, 0, 0) : 0, DTA_Bottom320x200, Scaled); - } - } }