/* ** hudmessages.cpp ** Neato messages for the HUD ** **--------------------------------------------------------------------------- ** Copyright 1998-2006 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "templates.h" #include "doomdef.h" #include "sbar.h" #include "c_cvars.h" #include "v_video.h" #include "cmdlib.h" #include "doomstat.h" #include "farchive.h" EXTERN_CVAR (Int, con_scaletext) IMPLEMENT_POINTY_CLASS (DHUDMessage) DECLARE_POINTER(Next) END_POINTERS IMPLEMENT_CLASS (DHUDMessageFadeOut) IMPLEMENT_CLASS (DHUDMessageFadeInOut) IMPLEMENT_CLASS (DHUDMessageTypeOnFadeOut) /************************************************************************* * Basic HUD message. Appears and disappears without any special effects * *************************************************************************/ inline FArchive &operator<< (FArchive &arc, EColorRange &i) { BYTE val = (BYTE)i; arc << val; i = (EColorRange)val; return arc; } //============================================================================ // // DHUDMessage Constructor // //============================================================================ DHUDMessage::DHUDMessage (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime) { if (hudwidth == 0 || hudheight == 0) { // for y range [-1.0, 0.0]: Positions top edge of box // for y range [0.0, 1.0]: Positions center of box // for x range [-2.0, -1.0]: Positions left edge of box, and centers text inside it // for x range [-1.0, 0.0]: Positions left edge of box // for x range [0.0, 1.0]: Positions center of box // for x range [1.0, 2.0]: Positions center of box, and centers text inside it HUDWidth = HUDHeight = 0; if (fabs (x) > 2.f) { CenterX = true; Left = 0.5f; } else { Left = x < -1.f ? x + 1.f : x > 1.f ? x - 1.f : x; if (fabs(x) > 1.f) { CenterX = true; } else { CenterX = false; } } } else { // HUD size is specified, so coordinates are in pixels, but you can add // some fractions to affect positioning: // For y: .1 = positions top edge of box // .2 = positions bottom edge of box // For x: .1 = positions left edge of box // .2 = positions right edge of box // .4 = centers text inside box // .4 can also be added to either .1 or .2 Top = y; HUDWidth = hudwidth; HUDHeight = hudheight; float intpart; int fracpart = (int)(fabsf (modff (x, &intpart)) * 10.f + 0.5f); if (fracpart & 4) { CenterX = true; } else { CenterX = false; } if (x > 0) { Left = intpart + (float)(fracpart & 3) / 10.f; } else { Left = intpart - (float)(fracpart & 3) / 10.f; } } NoWrap = false; ClipX = ClipY = ClipWidth = ClipHeight = 0; WrapWidth = 0; Top = y; Next = NULL; Lines = NULL; HoldTics = (int)(holdTime * TICRATE); Tics = 0; TextColor = textColor; State = 0; SourceText = copystring (text); Font = font; VisibilityFlags = 0; Style = STYLE_Translucent; Alpha = FRACUNIT; ResetText (SourceText); } //============================================================================ // // DHUDMessage Destructor // //============================================================================ DHUDMessage::~DHUDMessage () { if (Lines) { V_FreeBrokenLines (Lines); Lines = NULL; if (screen != NULL) { BorderNeedRefresh = screen->GetPageCount (); } } if (SourceText != NULL) { delete[] SourceText; } } //============================================================================ // // DHUDMessage :: Serialize // //============================================================================ void DHUDMessage::Serialize (FArchive &arc) { Super::Serialize (arc); arc << Left << Top << CenterX << HoldTics << Tics << State << TextColor << SBarID << SourceText << Font << Next << HUDWidth << HUDHeight; if (SaveVersion >= 3960) { arc << NoWrap; arc << ClipX << ClipY << ClipWidth << ClipHeight; arc << WrapWidth; } else { NoWrap = false; ClipX = ClipY = ClipWidth = ClipHeight = WrapWidth = 0; } if (arc.IsLoading ()) { Lines = NULL; ResetText (SourceText); } if (SaveVersion < 3821) { VisibilityFlags = 0; } else { arc << VisibilityFlags; } if (SaveVersion < 3824) { Style = STYLE_Translucent; Alpha = FRACUNIT; } else { arc << Style << Alpha; } } //============================================================================ // // DHUDMessage :: ScreenSizeChanged // //============================================================================ void DHUDMessage::ScreenSizeChanged () { if (HUDWidth == 0) { ResetText (SourceText); } } //============================================================================ // // DHUDMessage :: CalcClipCoords // // Take the clip rectangle in HUD coordinates (set via SetHudSize in ACS) // and convert them to screen coordinates. // //============================================================================ void DHUDMessage::CalcClipCoords(int hudheight) { int x = ClipX, y = ClipY, w = ClipWidth, h = ClipHeight; if ((x | y | w | h) == 0) { // No clipping rectangle set; use the full screen. ClipLeft = 0; ClipTop = 0; ClipRight = screen->GetWidth(); ClipBot = screen->GetHeight(); } else { screen->VirtualToRealCoordsInt(x, y, w, h, HUDWidth, hudheight, false, true); ClipLeft = x; ClipTop = y; ClipRight = x + w; ClipBot = y + h; } } //============================================================================ // // DHUDMessage :: ResetText // //============================================================================ void DHUDMessage::ResetText (const char *text) { int width; if (HUDWidth != 0) { width = WrapWidth == 0 ? HUDWidth : WrapWidth; } else { width = con_scaletext >= 2 ? SCREENWIDTH/2 : (con_scaletext ? SCREENWIDTH / CleanXfac : SCREENWIDTH); } if (Lines != NULL) { V_FreeBrokenLines (Lines); } Lines = V_BreakLines (Font, NoWrap ? INT_MAX : width, (BYTE *)text); NumLines = 0; Width = 0; Height = 0; if (Lines) { for (; Lines[NumLines].Width >= 0; NumLines++) { Height += Font->GetHeight (); Width = MAX (Width, Lines[NumLines].Width); } } } //============================================================================ // // DHUDMessage :: Tick // //============================================================================ bool DHUDMessage::Tick () { Tics++; if (HoldTics != 0 && HoldTics <= Tics) { // This message has expired return true; } return false; } //============================================================================ // // DHUDMessage :: Draw // //============================================================================ void DHUDMessage::Draw (int bottom, int visibility) { int xscale, yscale; int x, y; int ystep; int i; bool clean = false; int hudheight; // If any of the visibility flags match, do NOT draw this message. if (VisibilityFlags & visibility) { return; } DrawSetup (); int screen_width = SCREENWIDTH; int screen_height = SCREENHEIGHT; if (HUDWidth == 0 && con_scaletext==1) { clean = true; xscale = CleanXfac; yscale = CleanYfac; } else { xscale = yscale = 1; if (HUDWidth==0 && con_scaletext>1) { screen_width/=2; screen_height/=2; bottom/=2; } } if (HUDWidth == 0) { if (Left > 0.f) { // Position center x = (int)((float)(screen_width - Width * xscale) * Left); } else { // Position edge x = (int)((float)screen_width * -Left); } if (Top > 0.f) { // Position center y = (int)((float)(bottom - Height * yscale) * Top); } else { // Position edge y = (int)((float)bottom * -Top); } } else { float intpart; int fracpart; fracpart = (int)(fabsf (modff (Left, &intpart)) * 10.f + 0.5f); x = (int)intpart; switch (fracpart & 3) { case 0: // Position center x -= Width / 2; break; case 2: // Position right x -= Width; break; } fracpart = (int)(fabsf (modff (Top, &intpart)) * 10.f + 0.5f); y = (int)intpart; switch (fracpart & 3) { case 0: // Position center y -= Height / 2; break; case 2: // Position bottom y -= Height; break; } } if (CenterX) { x += Width * xscale / 2; } ystep = Font->GetHeight() * yscale; if (HUDHeight < 0) { // A negative height means the HUD size covers the status bar hudheight = -HUDHeight; } else { // A positive height means the HUD size does not cover the status bar hudheight = Scale (HUDHeight, screen_height, bottom); } CalcClipCoords(hudheight); for (i = 0; i < NumLines; i++) { int drawx; drawx = CenterX ? x - Lines[i].Width*xscale/2 : x; DoDraw (i, drawx, y, clean, hudheight); y += ystep; } } //============================================================================ // // DHUDMessage :: DrawSetup // //============================================================================ void DHUDMessage::DrawSetup () { } //============================================================================ // // DHUDMessage :: DoDraw // //============================================================================ void DHUDMessage::DoDraw (int linenum, int x, int y, bool clean, int hudheight) { if (hudheight == 0) { if (con_scaletext <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, DTA_Alpha, Alpha, DTA_RenderStyle, Style, TAG_DONE); } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, SCREENWIDTH/2, DTA_VirtualHeight, SCREENHEIGHT/2, DTA_Alpha, Alpha, DTA_RenderStyle, Style, DTA_KeepRatio, true, TAG_DONE); } } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, HUDWidth, DTA_VirtualHeight, hudheight, DTA_ClipLeft, ClipLeft, DTA_ClipRight, ClipRight, DTA_ClipTop, ClipTop, DTA_ClipBottom, ClipBot, DTA_Alpha, Alpha, DTA_RenderStyle, Style, TAG_DONE); } } /****************************** * HUD message that fades out * ******************************/ //============================================================================ // // DHUDMessageFadeOut Constructor // //============================================================================ DHUDMessageFadeOut::DHUDMessageFadeOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime, float fadeOutTime) : DHUDMessage (font, text, x, y, hudwidth, hudheight, textColor, holdTime) { FadeOutTics = (int)(fadeOutTime * TICRATE); State = 1; } //============================================================================ // // DHUDMessageFadeOut :: Serialize // //============================================================================ void DHUDMessageFadeOut::Serialize (FArchive &arc) { Super::Serialize (arc); arc << FadeOutTics; } //============================================================================ // // DHUDMessageFadeOut :: Tick // //============================================================================ bool DHUDMessageFadeOut::Tick () { Tics++; if (State == 1 && HoldTics <= Tics) { State++; Tics -= HoldTics; } if (State == 2 && FadeOutTics <= Tics) { return true; } return false; } //============================================================================ // // DHUDMessageFadeOut :: DoDraw // //============================================================================ void DHUDMessageFadeOut::DoDraw (int linenum, int x, int y, bool clean, int hudheight) { if (State == 1) { DHUDMessage::DoDraw (linenum, x, y, clean, hudheight); } else { fixed_t trans = -(Tics - FadeOutTics) * FRACUNIT / FadeOutTics; trans = FixedMul(trans, Alpha); if (hudheight == 0) { if (con_scaletext <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, DTA_Alpha, trans, DTA_RenderStyle, Style, TAG_DONE); } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, SCREENWIDTH/2, DTA_VirtualHeight, SCREENHEIGHT/2, DTA_Alpha, trans, DTA_RenderStyle, Style, DTA_KeepRatio, true, TAG_DONE); } } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, HUDWidth, DTA_VirtualHeight, hudheight, DTA_ClipLeft, ClipLeft, DTA_ClipRight, ClipRight, DTA_ClipTop, ClipTop, DTA_ClipBottom, ClipBot, DTA_Alpha, trans, DTA_RenderStyle, Style, TAG_DONE); } BorderNeedRefresh = screen->GetPageCount (); } } /*************************************** * HUD message that fades in, then out * ***************************************/ //============================================================================ // // DHUDMessageFadeInOut Constructor // //============================================================================ DHUDMessageFadeInOut::DHUDMessageFadeInOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime, float fadeInTime, float fadeOutTime) : DHUDMessageFadeOut (font, text, x, y, hudwidth, hudheight, textColor, holdTime, fadeOutTime) { FadeInTics = (int)(fadeInTime * TICRATE); State = FadeInTics == 0; } //============================================================================ // // DHUDMessageFadeInOut :: Serialize // //============================================================================ void DHUDMessageFadeInOut::Serialize (FArchive &arc) { Super::Serialize (arc); arc << FadeInTics; } //============================================================================ // // DHUDMessageFadeInOut :: Tick // //============================================================================ bool DHUDMessageFadeInOut::Tick () { if (!Super::Tick ()) { if (State == 0 && FadeInTics <= Tics) { State++; Tics -= FadeInTics; } return false; } return true; } //============================================================================ // // DHUDMessageFadeInOut :: DoDraw // //============================================================================ void DHUDMessageFadeInOut::DoDraw (int linenum, int x, int y, bool clean, int hudheight) { if (State == 0) { fixed_t trans = Tics * FRACUNIT / FadeInTics; trans = FixedMul(trans, Alpha); if (hudheight == 0) { if (con_scaletext <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, DTA_Alpha, trans, DTA_RenderStyle, Style, TAG_DONE); } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, SCREENWIDTH/2, DTA_VirtualHeight, SCREENHEIGHT/2, DTA_Alpha, trans, DTA_RenderStyle, Style, DTA_KeepRatio, true, TAG_DONE); } } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, HUDWidth, DTA_VirtualHeight, hudheight, DTA_ClipLeft, ClipLeft, DTA_ClipRight, ClipRight, DTA_ClipTop, ClipTop, DTA_ClipBottom, ClipBot, DTA_Alpha, trans, DTA_RenderStyle, Style, TAG_DONE); } BorderNeedRefresh = screen->GetPageCount (); } else { Super::DoDraw (linenum, x, y, clean, hudheight); } } /**************************************************** * HUD message that gets "typed" on, then fades out * ****************************************************/ //============================================================================ // // DHUDMessageTypeOnFadeOut Constructor // //============================================================================ DHUDMessageTypeOnFadeOut::DHUDMessageTypeOnFadeOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float typeTime, float holdTime, float fadeOutTime) : DHUDMessageFadeOut (font, text, x, y, hudwidth, hudheight, textColor, holdTime, fadeOutTime) { TypeOnTime = typeTime * TICRATE; if (TypeOnTime == 0.f) TypeOnTime = 0.1f; CurrLine = 0; LineLen = (int)Lines[0].Text.Len(); LineVisible = 0; State = 3; } //============================================================================ // // DHUDMessageTypeOnFadeOut :: Serialize // //============================================================================ void DHUDMessageTypeOnFadeOut::Serialize (FArchive &arc) { Super::Serialize (arc); arc << TypeOnTime << CurrLine << LineVisible << LineLen; } //============================================================================ // // DHUDMessageTypeOnFadeOut :: Tick // //============================================================================ bool DHUDMessageTypeOnFadeOut::Tick () { if (!Super::Tick ()) { if (State == 3) { int step = Tics == 0 ? 0 : int(Tics / TypeOnTime) - int((Tics - 1) / TypeOnTime); int linevis = LineVisible; int linedrawcount = linevis; FString text = Lines[CurrLine].Text; // Advance LineVisible by 'step' *visible* characters while (step > 0 && State == 3) { if (linevis > LineLen) { linevis = 0; linedrawcount = 0; CurrLine++; if (CurrLine >= NumLines) { Tics = 0; State = 1; } else { text = Lines[CurrLine].Text; LineLen = (int)text.Len(); } } if (State == 3 && --step >= 0) { linedrawcount++; if (text[linevis++] == TEXTCOLOR_ESCAPE) { if (text[linevis] == '[') { // named color while (text[linevis] != ']' && text[linevis] != '\0') { linevis++; } } linevis += 2; } } } LineVisible = linevis; } return false; } return true; } //============================================================================ // // DHUDMessageTypeOnFadeOut :: ScreenSizeChanged // //============================================================================ void DHUDMessageTypeOnFadeOut::ScreenSizeChanged () { int charCount = 0, i; for (i = 0; i < CurrLine; ++i) { charCount += (int)Lines[i].Text.Len(); } charCount += LineVisible; Super::ScreenSizeChanged (); if (State == 3) { CurrLine = 0; LineLen = (int)Lines[0].Text.Len(); Tics = (int)(charCount * TypeOnTime) - 1; Tick (); } } //============================================================================ // // DHUDMessageTypeOnFadeOut :: DoDraw // //============================================================================ void DHUDMessageTypeOnFadeOut::DoDraw (int linenum, int x, int y, bool clean, int hudheight) { if (State == 3) { if (linenum < CurrLine) { DHUDMessage::DoDraw (linenum, x, y, clean, hudheight); } else if (linenum == CurrLine) { if (hudheight == 0) { if (con_scaletext <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, DTA_TextLen, LineVisible, DTA_Alpha, Alpha, DTA_RenderStyle, Style, TAG_DONE); } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, SCREENWIDTH/2, DTA_VirtualHeight, SCREENHEIGHT/2, DTA_KeepRatio, true, DTA_TextLen, LineVisible, DTA_Alpha, Alpha, DTA_RenderStyle, Style, TAG_DONE); } } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_VirtualWidth, HUDWidth, DTA_VirtualHeight, hudheight, DTA_ClipLeft, ClipLeft, DTA_ClipRight, ClipRight, DTA_ClipTop, ClipTop, DTA_ClipBottom, ClipBot, DTA_Alpha, Alpha, DTA_TextLen, LineVisible, DTA_RenderStyle, Style, TAG_DONE); } } } else { DHUDMessageFadeOut::DoDraw (linenum, x, y, clean, hudheight); } }