From 9d846395bc0e78a3881ea8530502125191a5a4c9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 May 2014 21:05:00 +0200 Subject: [PATCH 01/16] - replaced console buffer with a significantly more efficient new version that also can hold a lot more data. --- src/CMakeLists.txt | 1 + src/c_console.cpp | 402 +++++----------------------------------- src/c_console.h | 3 + src/c_consolebuffer.cpp | 317 +++++++++++++++++++++++++++++++ src/c_consolebuffer.h | 85 +++++++++ src/nodebuild.cpp | 6 - src/v_text.cpp | 4 +- src/v_text.h | 6 +- 8 files changed, 458 insertions(+), 366 deletions(-) create mode 100644 src/c_consolebuffer.cpp create mode 100644 src/c_consolebuffer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5203b294cf..517b6bdbe8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -789,6 +789,7 @@ add_executable( zdoom WIN32 c_bind.cpp c_cmds.cpp c_console.cpp + c_consolebuffer.cpp c_cvars.cpp c_dispatch.cpp c_expr.cpp diff --git a/src/c_console.cpp b/src/c_console.cpp index 396b2dfcec..4dabdb9e11 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -65,22 +65,29 @@ #include "g_level.h" #include "d_event.h" #include "d_player.h" +#include "c_consolebuffer.h" #include "gi.h" -#define CONSOLESIZE 16384 // Number of characters to store in console -#define CONSOLELINES 256 // Max number of lines of console text -#define LINEMASK (CONSOLELINES-1) - #define LEFTMARGIN 8 #define RIGHTMARGIN 8 #define BOTTOMARGIN 12 + +CUSTOM_CVAR(Int, con_buffersize, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + // ensure a minimum size + if (self >= 0 && self < 128) self = 128; +} + +FConsoleBuffer *conbuffer; + static void C_TabComplete (bool goForward); static bool C_TabCompleteList (); static bool TabbedLast; // True if last key pressed was tab static bool TabbedList; // True if tab list was shown -CVAR (Bool, con_notablist, false, CVAR_ARCHIVE) +CVAR(Bool, con_notablist, false, CVAR_ARCHIVE) + static FTextureID conback; static DWORD conshade; @@ -94,18 +101,15 @@ extern FBaseCVar *CVars; extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; int ConCols, PhysRows; +int ConWidth; bool vidactive = false; bool cursoron = false; int ConBottom, ConScroll, RowAdjust; int CursorTicker; constate_e ConsoleState = c_up; -static char ConsoleBuffer[CONSOLESIZE]; -static char *Lines[CONSOLELINES]; -static bool LineJoins[CONSOLELINES]; static int TopLine, InsertLine; -static char *BufferRover = ConsoleBuffer; static void ClearConsole (); static void C_PasteText(FString clip, BYTE *buffer, int len); @@ -182,7 +186,6 @@ static struct NotifyText static int NotifyTop, NotifyTopGoal; -#define PRINTLEVELS 5 int PrintColors[PRINTLEVELS+2] = { CR_RED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_GOLD }; static void setmsgcolor (int index, int color); @@ -328,78 +331,11 @@ void C_InitConsole (int width, int height, bool ingame) { cwidth = cheight = 8; } - ConCols = (width - LEFTMARGIN - RIGHTMARGIN) / cwidth; + ConWidth = (width - LEFTMARGIN - RIGHTMARGIN); + ConCols = ConWidth / cwidth; PhysRows = height / cheight; - // If there is some text in the console buffer, reformat it - // for the new resolution. - if (TopLine != InsertLine) - { - // Note: Don't use new here, because we attach a handler to new in - // i_main.cpp that calls I_FatalError if the allocation fails, - // but we can gracefully handle such a condition here by just - // clearing the console buffer. (OTOH, what are the chances that - // any other memory allocations would succeed if we can't get - // these mallocs here?) - - char *fmtBuff = (char *)malloc (CONSOLESIZE); - char **fmtLines = (char **)malloc (CONSOLELINES*sizeof(char*)*4); - int out = 0; - - if (fmtBuff && fmtLines) - { - int in; - char *fmtpos; - bool newline = true; - - fmtpos = fmtBuff; - memset (fmtBuff, 0, CONSOLESIZE); - - for (in = TopLine; in != InsertLine; in = (in + 1) & LINEMASK) - { - size_t len = strlen (Lines[in]); - - if (fmtpos + len + 2 - fmtBuff > CONSOLESIZE) - { - break; - } - if (newline) - { - newline = false; - fmtLines[out++] = fmtpos; - } - strcpy (fmtpos, Lines[in]); - fmtpos += len; - if (!LineJoins[in]) - { - *fmtpos++ = '\n'; - fmtpos++; - if (out == CONSOLELINES*4) - { - break; - } - newline = true; - } - } - } - - ClearConsole (); - - if (fmtBuff && fmtLines) - { - int i; - - for (i = 0; i < out; i++) - { - AddToConsole (-1, fmtLines[i]); - } - } - - if (fmtBuff) - free (fmtBuff); - if (fmtLines) - free (fmtLines); - } + if (conbuffer == NULL) conbuffer = new FConsoleBuffer; } //========================================================================== @@ -507,16 +443,21 @@ void C_DeinitConsole () work = NULL; worklen = 0; } + + if (conbuffer != NULL) + { + delete conbuffer; + conbuffer = NULL; + } } static void ClearConsole () { - RowAdjust = 0; + if (conbuffer != NULL) + { + conbuffer->Clear(); + } TopLine = InsertLine = 0; - BufferRover = ConsoleBuffer; - memset (ConsoleBuffer, 0, CONSOLESIZE); - memset (Lines, 0, sizeof(Lines)); - memset (LineJoins, 0, sizeof(LineJoins)); } static void setmsgcolor (int index, int color) @@ -596,223 +537,9 @@ void C_AddNotifyString (int printlevel, const char *source) NotifyTopGoal = 0; } -static int FlushLines (const char *start, const char *stop) -{ - int i; - - for (i = TopLine; i != InsertLine; i = (i + 1) & LINEMASK) - { - if (Lines[i] < stop && Lines[i] + strlen (Lines[i]) > start) - { - Lines[i] = NULL; - } - else - { - break; - } - } - return i; -} - -static void AddLine (const char *text, bool more, size_t len) -{ - if (BufferRover + len + 1 - ConsoleBuffer > CONSOLESIZE) - { - TopLine = FlushLines (BufferRover, ConsoleBuffer + CONSOLESIZE); - BufferRover = ConsoleBuffer; - } - if (len >= CONSOLESIZE - 1) - { - text = text + len - CONSOLESIZE + 1; - len = CONSOLESIZE - 1; - } - TopLine = FlushLines (BufferRover, BufferRover + len + 1); - memcpy (BufferRover, text, len); - BufferRover[len] = 0; - Lines[InsertLine] = BufferRover; - BufferRover += len + 1; - LineJoins[InsertLine] = more; - InsertLine = (InsertLine + 1) & LINEMASK; - if (InsertLine == TopLine) - { - TopLine = (TopLine + 1) & LINEMASK; - } -} - void AddToConsole (int printlevel, const char *text) { - static enum - { - NEWLINE, - APPENDLINE, - REPLACELINE - } addtype = NEWLINE; - - char *work_p; - char *linestart; - FString cc('A' + char(CR_TAN)); - int size, len; - int x; - int maxwidth; - - if (ConsoleDrawing) - { - EnqueueConsoleText (false, printlevel, text); - return; - } - - len = (int)strlen (text); - size = len + 20; - - if (addtype != NEWLINE) - { - InsertLine = (InsertLine - 1) & LINEMASK; - if (Lines[InsertLine] == NULL) - { - InsertLine = (InsertLine + 1) & LINEMASK; - addtype = NEWLINE; - } - else - { - BufferRover = Lines[InsertLine]; - } - } - if (addtype == APPENDLINE) - { - size += (int)strlen (Lines[InsertLine]); - } - if (size > worklen) - { - work = (char *)M_Realloc (work, size); - worklen = size; - } - if (work == NULL) - { - static char oom[] = TEXTCOLOR_RED "*** OUT OF MEMORY ***"; - work = oom; - worklen = 0; - } - else - { - if (addtype == APPENDLINE) - { - strcpy (work, Lines[InsertLine]); - strcat (work, text); - } - else if (printlevel >= 0) - { - work[0] = TEXTCOLOR_ESCAPE; - work[1] = 'A' + (printlevel == PRINT_HIGH ? CR_TAN : - printlevel == 200 ? CR_GREEN : - printlevel < PRINTLEVELS ? PrintColors[printlevel] : - CR_TAN); - cc = work[1]; - strcpy (work + 2, text); - } - else - { - strcpy (work, text); - } - } - - work_p = linestart = work; - - if (ConFont != NULL && screen != NULL) - { - x = 0; - maxwidth = screen->GetWidth() - LEFTMARGIN - RIGHTMARGIN; - - while (*work_p) - { - if (*work_p == TEXTCOLOR_ESCAPE) - { - work_p++; - if (*work_p == '[') - { - char *start = work_p; - while (*work_p != ']' && *work_p != '\0') - { - work_p++; - } - if (*work_p != '\0') - { - work_p++; - } - cc = FString(start, work_p - start); - } - else if (*work_p != '\0') - { - cc = *work_p++; - } - continue; - } - int w = ConFont->GetCharWidth (*work_p); - if (*work_p == '\n' || x + w > maxwidth) - { - AddLine (linestart, *work_p != '\n', work_p - linestart); - if (*work_p == '\n') - { - x = 0; - work_p++; - } - else - { - x = w; - } - if (*work_p) - { - linestart = work_p - 1 - cc.Len(); - if (linestart < work) - { - // The line start is outside the buffer. - // Make space for the newly inserted stuff. - size_t movesize = work-linestart; - memmove(work + movesize, work, strlen(work)+1); - work_p += movesize; - linestart = work; - } - linestart[0] = TEXTCOLOR_ESCAPE; - strncpy (linestart + 1, cc, cc.Len()); - } - else - { - linestart = work_p; - } - } - else - { - x += w; - work_p++; - } - } - - if (*linestart) - { - AddLine (linestart, true, work_p - linestart); - } - } - else - { - while (*work_p) - { - if (*work_p++ == '\n') - { - AddLine (linestart, false, work_p - linestart - 1); - linestart = work_p; - } - } - if (*linestart) - { - AddLine (linestart, true, work_p - linestart); - } - } - - switch (text[len-1]) - { - case '\r': addtype = REPLACELINE; break; - case '\n': addtype = NEWLINE; break; - default: addtype = APPENDLINE; break; - } + conbuffer->AddText(printlevel, text, Logfile); } /* Adds a string to the console and also to the notify buffer */ @@ -823,34 +550,6 @@ int PrintString (int printlevel, const char *outline) return 0; } - if (Logfile) - { - // Strip out any color escape sequences before writing to the log file - char * copy = new char[strlen(outline)+1]; - const char * srcp = outline; - char * dstp = copy; - - while (*srcp != 0) - { - if (*srcp!=0x1c) - { - *dstp++=*srcp++; - } - else - { - if (srcp[1]!=0) srcp+=2; - else break; - } - } - *dstp=0; - - fputs (copy, Logfile); - delete [] copy; -//#ifdef _DEBUG - fflush (Logfile); -//#endif - } - if (printlevel != PRINT_LOG) { I_PrintStr (outline); @@ -949,6 +648,11 @@ void C_Ticker () if (lasttic == 0) lasttic = gametic - 1; + if (con_buffersize > 0) + { + conbuffer->ResizeBuffer(con_buffersize); + } + if (ConsoleState != c_up) { if (ConsoleState == c_falling) @@ -1236,38 +940,22 @@ void C_DrawConsole (bool hw2d) if (lines > 0) { + // No more enqueuing because adding new text to the console won't touch the actual print data. + conbuffer->FormatText(ConFont, ConWidth); + unsigned int consolelines = conbuffer->GetFormattedLineCount(); + FBrokenLines **blines = conbuffer->GetLines(); + FBrokenLines **printline = blines + consolelines - 1 - RowAdjust; + int bottomline = ConBottom - ConFont->GetHeight()*2 - 4; - int pos = (InsertLine - 1) & LINEMASK; - int i; ConsoleDrawing = true; - for (i = RowAdjust; i; i--) + for(FBrokenLines **p = printline; p >= blines && lines > 0; p--, lines--) { - if (pos == TopLine) - { - RowAdjust = RowAdjust - i; - break; - } - else - { - pos = (pos - 1) & LINEMASK; - } + screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE); } - pos++; - do - { - pos = (pos - 1) & LINEMASK; - if (Lines[pos] != NULL) - { - screen->DrawText (ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), - Lines[pos], TAG_DONE); - } - lines--; - } while (pos != TopLine && lines > 0); ConsoleDrawing = false; - DequeueConsoleText (); if (ConBottom >= 20) { @@ -1293,7 +981,7 @@ void C_DrawConsole (bool hw2d) { // Indicate that the view has been scrolled up (10) // and if we can scroll no further (12) - screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, pos == TopLine ? 12 : 10, TAG_DONE); + screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE); } } } @@ -1445,7 +1133,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) RowAdjust += (SCREENHEIGHT-4) / ((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3; } - else if (RowAdjust < CONSOLELINES) + else if (RowAdjust < conbuffer->GetFormattedLineCount()) { // Scroll console buffer up if (ev->subtype == EV_GUI_WheelUp) { @@ -1455,6 +1143,10 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) { RowAdjust++; } + if (RowAdjust > conbuffer->GetFormattedLineCount()) + { + RowAdjust = conbuffer->GetFormattedLineCount(); + } } break; @@ -1488,7 +1180,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case GK_HOME: if (ev->data3 & GKM_CTRL) { // Move to top of console buffer - RowAdjust = CONSOLELINES; + RowAdjust = conbuffer->GetFormattedLineCount(); } else { // Move cursor to start of line diff --git a/src/c_console.h b/src/c_console.h index 8560d6779e..2c2e553f96 100644 --- a/src/c_console.h +++ b/src/c_console.h @@ -47,6 +47,9 @@ typedef enum cstate_t } constate_e; +#define PRINTLEVELS 5 +extern int PrintColors[PRINTLEVELS + 2]; + extern constate_e ConsoleState; extern int ConBottom; diff --git a/src/c_consolebuffer.cpp b/src/c_consolebuffer.cpp new file mode 100644 index 0000000000..3f64f34e4b --- /dev/null +++ b/src/c_consolebuffer.cpp @@ -0,0 +1,317 @@ +/* +** consolebuffer.cpp +** +** manages the text for the console +** +**--------------------------------------------------------------------------- +** Copyright 2014 Christoph Oelckers +** 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 "c_console.h" +#include "c_consolebuffer.h" + + +//========================================================================== +// +// +// +//========================================================================== + +FConsoleBuffer::FConsoleBuffer() +{ + mLogFile = NULL; + mAddType = NEWLINE; + mLastFont = NULL; + mLastDisplayWidth = -1; + mLastLineNeedsUpdate = false; + mTextLines = 0; + mBufferWasCleared = true; + mBrokenStart.Push(0); +} + +//========================================================================== +// +// +// +//========================================================================== + +FConsoleBuffer::~FConsoleBuffer() +{ + FreeBrokenText(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FConsoleBuffer::FreeBrokenText(unsigned start, unsigned end) +{ + if (end > mBrokenConsoleText.Size()) end = mBrokenConsoleText.Size(); + for (unsigned i = start; i < end; i++) + { + if (mBrokenConsoleText[i] != NULL) V_FreeBrokenLines(mBrokenConsoleText[i]); + mBrokenConsoleText[i] = NULL; + } +} + +//========================================================================== +// +// Adds a new line of text to the console +// This is kept as simple as possible. This function does not: +// - remove old text if the buffer gets larger than the specified size +// - format the text for the current screen layout +// +// These tasks will only be be performed once per frame because they are +// relatively expensive. The old console did them each time text was added +// resulting in extremely bad performance with a high output rate. +// +//========================================================================== + +void FConsoleBuffer::AddText(int printlevel, const char *text, FILE *logfile) +{ + FString build = TEXTCOLOR_TAN; + + if (mAddType == REPLACELINE) + { + // Just wondering: Do we actually need this case? If so, it may need some work. + mConsoleText.Pop(); // remove the line to be replaced + mLastLineNeedsUpdate = true; + } + else if (mAddType == APPENDLINE) + { + mConsoleText.Pop(build); + printlevel = -1; + mLastLineNeedsUpdate = true; + } + + if (printlevel >= 0 && printlevel != PRINT_HIGH) + { + if (printlevel == 200) build = TEXTCOLOR_GREEN; + else if (printlevel < PRINTLEVELS) build.Format("%c%c", TEXTCOLOR_ESCAPE, PrintColors[printlevel]); + } + + size_t textsize = strlen(text); + + if (text[textsize-1] == '\r') + { + textsize--; + mAddType = REPLACELINE; + } + else if (text[textsize-1] == '\n') + { + textsize--; + mAddType = NEWLINE; + } + else + { + mAddType = APPENDLINE; + } + + // don't bother about linefeeds etc. inside the text, we'll let the formatter sort this out later. + build.AppendCStrPart(text, textsize); + mConsoleText.Push(build); + if (logfile != NULL) WriteLineToLog(logfile, text); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FConsoleBuffer::WriteLineToLog(FILE *LogFile, const char *outline) +{ + // Strip out any color escape sequences before writing to the log file + char * copy = new char[strlen(outline)+1]; + const char * srcp = outline; + char * dstp = copy; + + while (*srcp != 0) + { + + if (*srcp != TEXTCOLOR_ESCAPE) + { + switch (*srcp) + { + case '\35': + *dstp++ = '<'; + break; + + case '\36': + *dstp++ = '-'; + break; + + case '\37': + *dstp++ = '>'; + break; + + default: + *dstp++=*srcp; + break; + } + srcp++; + } + else if (srcp[1] == '[') + { + srcp+=2; + while (*srcp != ']' && *srcp != 0) srcp++; + if (*srcp == ']') srcp++; + } + else + { + if (srcp[1]!=0) srcp+=2; + else break; + } + } + *dstp=0; + + fputs (copy, LogFile); + delete [] copy; + fflush (LogFile); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FConsoleBuffer::WriteContentToLog(FILE *LogFile) +{ + if (LogFile != NULL) + { + for (unsigned i = 0; i < mConsoleText.Size(); i++) + { + WriteLineToLog(LogFile, mConsoleText[i]); + } + } +} + +//========================================================================== +// +// ensures that the following text is not appended to the current line. +// +//========================================================================== + +void FConsoleBuffer::Linefeed(FILE *Logfile) +{ + if (mAddType != NEWLINE && Logfile != NULL) fputc('\n', Logfile); + mAddType = NEWLINE; +} + +//========================================================================== +// +// +// +//========================================================================== + +static const char bar1[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" + "\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_TAN "\n"; +static const char bar2[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" + "\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_GREEN "\n"; +static const char bar3[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" + "\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_NORMAL "\n"; +static const char logbar[] = "\n<------------------------------->\n"; + +void FConsoleBuffer::AddMidText(const char *string, bool bold, FILE *Logfile) +{ + Linefeed(Logfile); + AddText (-1, bold? bar2 : bar1, Logfile); + AddText (-1, string, Logfile); + Linefeed(Logfile); + AddText(-1, bar3, Logfile); +} + +//========================================================================== +// +// Format the text for output +// +//========================================================================== + +void FConsoleBuffer::FormatText(FFont *formatfont, int displaywidth) +{ + if (formatfont != mLastFont || displaywidth != mLastDisplayWidth || mBufferWasCleared) + { + FreeBrokenText(); + mBrokenConsoleText.Clear(); + mBrokenStart.Clear(); + mBrokenStart.Push(0); + mBrokenLines.Clear(); + mLastFont = formatfont; + mLastDisplayWidth = displaywidth; + mBufferWasCleared = false; + } + unsigned brokensize = mBrokenConsoleText.Size(); + if (brokensize == mConsoleText.Size()) + { + // The last line got text appended. We have to wait until here to format it because + // it is possible that during display new text will be added from the NetUpdate calls in the software version of DrawTextureV. + if (mLastLineNeedsUpdate) + { + brokensize--; + V_FreeBrokenLines(mBrokenConsoleText[brokensize]); + mBrokenConsoleText.Resize(brokensize); + } + } + mBrokenLines.Resize(mBrokenStart[brokensize]); + mBrokenStart.Resize(brokensize); + for (unsigned i = brokensize; i < mConsoleText.Size(); i++) + { + FBrokenLines *bl = V_BreakLines(formatfont, displaywidth, mConsoleText[i], true); + mBrokenConsoleText.Push(bl); + mBrokenStart.Push(mBrokenLines.Size()); + while (bl->Width != -1) + { + mBrokenLines.Push(bl); + bl++; + } + } + mTextLines = mBrokenLines.Size(); + mBrokenStart.Push(mTextLines); + mLastLineNeedsUpdate = false; +} + +//========================================================================== +// +// Delete old content if number of lines gets too large +// +//========================================================================== + +void FConsoleBuffer::ResizeBuffer(unsigned newsize) +{ + if (mConsoleText.Size() > newsize) + { + unsigned todelete = mConsoleText.Size() - newsize; + mConsoleText.Delete(0, todelete); + mBufferWasCleared = true; + } +} + diff --git a/src/c_consolebuffer.h b/src/c_consolebuffer.h new file mode 100644 index 0000000000..710423012b --- /dev/null +++ b/src/c_consolebuffer.h @@ -0,0 +1,85 @@ +/* +** consolebuffer.h +** +** manages the text for the console +** +**--------------------------------------------------------------------------- +** Copyright 2014 Christoph Oelckers +** 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 +#include +#include "zstring.h" +#include "tarray.h" +#include "v_text.h" + +enum EAddType +{ + NEWLINE, + APPENDLINE, + REPLACELINE +}; + +class FConsoleBuffer +{ + TArray mConsoleText; + TArray mBrokenConsoleText; // This holds the structures returned by V_BreakLines and is used for memory management. + TArray mBrokenStart; + TArray mBrokenLines; // This holds the single lines, indexed by mBrokenStart and is used for printing. + FILE * mLogFile; + EAddType mAddType; + int mTextLines; + bool mBufferWasCleared; + + FFont *mLastFont; + int mLastDisplayWidth; + bool mLastLineNeedsUpdate; + + void WriteLineToLog(FILE *LogFile, const char *outline); + void FreeBrokenText(unsigned int start = 0, unsigned int end = INT_MAX); + + void Linefeed(FILE *Logfile); + +public: + FConsoleBuffer(); + ~FConsoleBuffer(); + void AddText(int printlevel, const char *string, FILE *logfile = NULL); + void AddMidText(const char *string, bool bold, FILE *logfile); + void FormatText(FFont *formatfont, int displaywidth); + void ResizeBuffer(unsigned newsize); + void WriteContentToLog(FILE *logfile); + void Clear() + { + mBufferWasCleared = true; + mConsoleText.Clear(); + } + int GetFormattedLineCount() { return mTextLines; } + FBrokenLines **GetLines() { return &mBrokenLines[0]; } +}; + diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index f3ceec17d9..210f0f3dea 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -125,12 +125,10 @@ void FNodeBuilder::BuildTree () { fixed_t bbox[4]; - C_InitTicker ("Building BSP", FRACUNIT); HackSeg = DWORD_MAX; HackMate = DWORD_MAX; CreateNode (0, Segs.Size(), bbox); CreateSubsectorsForReal (); - C_InitTicker (NULL, 0); } int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]) @@ -199,10 +197,6 @@ int FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4]) } SegsStuffed += count; - if ((SegsStuffed & ~127) != ((SegsStuffed - count) & ~127)) - { - C_SetTicker (MulScale16 (SegsStuffed, (SDWORD)Segs.Size())); - } D(Printf (PRINT_LOG, "bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); diff --git a/src/v_text.cpp b/src/v_text.cpp index c5208be36b..64b95c041f 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -336,7 +336,7 @@ static void breakit (FBrokenLines *line, FFont *font, const BYTE *start, const B line->Width = font->StringWidth (line->Text); } -FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string) +FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string, bool preservecolor) { FBrokenLines lines[128]; // Support up to 128 lines (should be plenty) @@ -397,7 +397,7 @@ FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string) space = string - 1; breakit (&lines[i], font, start, space, linecolor); - if (c == '\n') + if (c == '\n' && !preservecolor) { lastcolor = ""; // Why, oh why, did I do it like this? } diff --git a/src/v_text.h b/src/v_text.h index 29dfa627b5..f1426b30ad 100644 --- a/src/v_text.h +++ b/src/v_text.h @@ -75,9 +75,9 @@ struct FBrokenLines #define TEXTCOLOR_CHAT "\034*" #define TEXTCOLOR_TEAMCHAT "\034!" -FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str); +FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str, bool preservecolor = false); void V_FreeBrokenLines (FBrokenLines *lines); -inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str) - { return V_BreakLines (font, maxwidth, (const BYTE *)str); } +inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str, bool preservecolor = false) + { return V_BreakLines (font, maxwidth, (const BYTE *)str, preservecolor); } #endif //__V_TEXT_H__ From b285cbebe4fb7f3a84240eabeb4276761565567b Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sun, 25 May 2014 01:12:16 +0200 Subject: [PATCH 02/16] - Fixed compiler errors in latest TEXTURES code. --- src/r_data/sprites.cpp | 4 ++-- src/r_utility.cpp | 2 +- src/textures/animations.cpp | 4 ++-- src/textures/jpegtexture.cpp | 4 ++-- src/textures/multipatchtexture.cpp | 14 +++++++------- src/textures/texturemanager.cpp | 2 +- src/w_wad.h | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index 6046194035..c1e59c415f 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -63,7 +63,7 @@ static bool R_InstallSpriteLump (FTextureID lump, unsigned frame, char rot, bool if (frame >= MAX_SPRITE_FRAMES || rotation > 16) { - Printf (TEXTCOLOR_RED"R_InstallSpriteLump: Bad frame characters in lump %s\n", TexMan[lump]->Name); + Printf (TEXTCOLOR_RED"R_InstallSpriteLump: Bad frame characters in lump %s\n", TexMan[lump]->Name.GetChars()); return false; } @@ -998,4 +998,4 @@ void R_DeinitSpriteData() delete[] skins; skins = NULL; } -} \ No newline at end of file +} diff --git a/src/r_utility.cpp b/src/r_utility.cpp index e11631700e..5bf38ad26e 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -972,7 +972,7 @@ void FCanvasTextureInfo::Add (AActor *viewpoint, FTextureID picnum, int fov) texture = static_cast(TexMan[picnum]); if (!texture->bHasCanvas) { - Printf ("%s is not a valid target for a camera\n", texture->Name); + Printf ("%s is not a valid target for a camera\n", texture->Name.GetChars()); return; } diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index 3601646a05..58ef7a5ca7 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -231,8 +231,8 @@ void FTextureManager::InitAnimated (void) if (debuganimated) { Printf("Defining animation '%s' (texture %d, lump %d, file %d) to '%s' (texture %d, lump %d, file %d)\n", - tex1->Name, pic1.GetIndex(), tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), - tex2->Name, pic2.GetIndex(), tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); + tex1->Name.GetChars(), pic1.GetIndex(), tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), + tex2->Name.GetChars(), pic2.GetIndex(), tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); } if (pic1 == pic2) diff --git a/src/textures/jpegtexture.cpp b/src/textures/jpegtexture.cpp index 3a87f70af8..849cec6458 100644 --- a/src/textures/jpegtexture.cpp +++ b/src/textures/jpegtexture.cpp @@ -448,7 +448,7 @@ void FJPEGTexture::MakeTexture () } catch (int) { - Printf (TEXTCOLOR_ORANGE " in texture %s\n", Name); + Printf (TEXTCOLOR_ORANGE " in texture %s\n", Name.GetChars()); jpeg_destroy_decompress(&cinfo); } if (buff != NULL) @@ -532,7 +532,7 @@ int FJPEGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FC } catch(int) { - Printf (TEXTCOLOR_ORANGE " in JPEG texture %s\n", Name); + Printf (TEXTCOLOR_ORANGE " in JPEG texture %s\n", Name.GetChars()); } jpeg_destroy_decompress(&cinfo); if (buff != NULL) delete [] buff; diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 84e7385dbb..348ad3a989 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -268,14 +268,14 @@ FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchl if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) { I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", - maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); + maxpatchnum, Name.GetChars(), LittleShort(mpatch.d->patch)+1); } Parts[i].OriginX = LittleShort(mpatch.d->originx); Parts[i].OriginY = LittleShort(mpatch.d->originy); Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; if (Parts[i].Texture == NULL) { - Printf(TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); + Printf(TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name.GetChars(), Name.GetChars()); NumParts--; i--; } @@ -290,7 +290,7 @@ FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchl } if (NumParts == 0) { - Printf ("Texture %s is left without any patches\n", Name); + Printf ("Texture %s is left without any patches\n", Name.GetChars()); } CheckForHacks (); @@ -1023,7 +1023,7 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, i } if (part.Texture == NULL) { - if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); + if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name.GetChars()); } sc.MustGetStringName(","); sc.MustGetNumber(); @@ -1244,13 +1244,13 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) { sc.MustGetFloat(); xScale = FLOAT2FIXED(sc.Float); - if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name); + if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name.GetChars()); } else if (sc.Compare("YScale")) { sc.MustGetFloat(); yScale = FLOAT2FIXED(sc.Float); - if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name); + if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name.GetChars()); } else if (sc.Compare("WorldPanning")) { @@ -1318,7 +1318,7 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) if (Width <= 0 || Height <= 0) { UseType = FTexture::TEX_Null; - Printf("Texture %s has invalid dimensions (%d, %d)\n", Name, Width, Height); + Printf("Texture %s has invalid dimensions (%d, %d)\n", Name.GetChars(), Width, Height); Width = Height = 1; } CalcBitSize (); diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 2a10ce0df9..a79769c3d9 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -949,7 +949,7 @@ void FTextureManager::SortTexturesByType(int start, int end) { if (newtextures[j] != NULL) { - Printf("Texture %s has unknown type!\n", newtextures[j]->Name); + Printf("Texture %s has unknown type!\n", newtextures[j]->Name.GetChars()); AddTexture(newtextures[j]); } } diff --git a/src/w_wad.h b/src/w_wad.h index 0a95699f62..4dfe3434d1 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -196,8 +196,8 @@ public: int GetLumpOffset (int lump); // [RH] Returns offset of lump in the wadfile int GetLumpFlags (int lump); // Return the flags for this lump void GetLumpName (char *to, int lump) const; // [RH] Copies the lump name to to using uppercopy - void FWadCollection::GetLumpName(FString &to, int lump) const; - const char *GetLumpFullName(int lump) const; // [RH] Returns the lump's full name + void GetLumpName (FString &to, int lump) const; + const char *GetLumpFullName (int lump) const; // [RH] Returns the lump's full name FString GetLumpFullPath (int lump) const; // [RH] Returns wad's name + lump's full name int GetLumpFile (int lump) const; // [RH] Returns wadnum for a specified lump int GetLumpNamespace (int lump) const; // [RH] Returns the namespace a lump belongs to From 75cde0b2219fdd798eaa30e1d41ce33dd696b9f6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 29 May 2014 17:30:01 +0200 Subject: [PATCH 03/16] - allow locks to check for a key's species so that newly defined keys can open previously defined locks without the need to redefine them. --- src/g_shared/a_keys.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/g_shared/a_keys.cpp b/src/g_shared/a_keys.cpp index e7f1b286b0..67d0ffb101 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_shared/a_keys.cpp @@ -19,12 +19,29 @@ struct OneKey bool check(AActor * owner) { - // P_GetMapColorForKey() checks the key directly - if (owner->IsKindOf (RUNTIME_CLASS(AKey))) + if (owner->IsKindOf(RUNTIME_CLASS(AKey))) + { + // P_GetMapColorForKey() checks the key directly return owner->IsA(key); - // Other calls check an actor that may have a key in its inventory. - else - return !!owner->FindInventory(key); + } + else + { + // Other calls check an actor that may have a key in its inventory. + AInventory *item; + + for (item = owner->Inventory; item != NULL; item = item->Inventory) + { + if (item->IsA(key)) + { + return true; + } + else if (item->GetSpecies() == key->TypeName) + { + return true; + } + } + return false; + } } }; From 8f5683e23d61825e77b2c7b68312a01cf4354d61 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 29 May 2014 17:50:14 +0200 Subject: [PATCH 04/16] - moved secret found message to string table and removed the CVAR crutch that dates from a time when modifying string table content wasn't as easy as it is now. - added 'showsecretsector' CVAR to show the sector number with the secret found message. --- src/g_shared/a_pickups.cpp | 2 +- src/g_shared/a_secrettrigger.cpp | 4 +--- src/p_spec.cpp | 21 +++++++++++++++------ src/p_spec.h | 2 +- wadsrc/static/language.enu | 2 ++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index cc559ea94b..f14e7f4c70 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1018,7 +1018,7 @@ void AInventory::Touch (AActor *toucher) if (flags5 & MF5_COUNTSECRET) { - P_GiveSecret(toucher, true, true); + P_GiveSecret(toucher, true, true, -1); } //Added by MC: Check if item taken was the roam destination of any bot diff --git a/src/g_shared/a_secrettrigger.cpp b/src/g_shared/a_secrettrigger.cpp index 4484340db6..24c6f0db4d 100644 --- a/src/g_shared/a_secrettrigger.cpp +++ b/src/g_shared/a_secrettrigger.cpp @@ -42,8 +42,6 @@ #include "v_font.h" #include "p_spec.h" -EXTERN_CVAR(String, secretmessage) - class ASecretTrigger : public AActor { DECLARE_CLASS (ASecretTrigger, AActor) @@ -62,7 +60,7 @@ void ASecretTrigger::PostBeginPlay () void ASecretTrigger::Activate (AActor *activator) { - P_GiveSecret(activator, args[0] <= 1, (args[0] == 0 || args[0] == 2)); + P_GiveSecret(activator, args[0] <= 1, (args[0] == 0 || args[0] == 2), -1); Destroy (); } diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 63f7c1c89e..e66dc2ab46 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -61,6 +61,7 @@ #include "a_sharedglobal.h" #include "farchive.h" #include "a_keys.h" +#include "c_dispatch.h" // State. #include "r_state.h" @@ -73,9 +74,6 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); void P_SetupPortals(); -// [GrafZahl] Make this message changable by the user! ;) -CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE) - IMPLEMENT_POINTY_CLASS (DScroller) DECLARE_POINTER (m_Interpolations[0]) DECLARE_POINTER (m_Interpolations[1]) @@ -581,7 +579,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) if (sector->special & SECRET_MASK) { sector->special &= ~SECRET_MASK; - P_GiveSecret(player->mo, true, true); + P_GiveSecret(player->mo, true, true, int(sector - sectors)); } } @@ -672,7 +670,9 @@ void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass, // //============================================================================ -void P_GiveSecret(AActor *actor, bool printmessage, bool playsound) +CVAR(Bool, showsecretsector, false, 0) + +void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornum) { if (actor != NULL) { @@ -682,7 +682,16 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound) } if (actor->CheckLocalView (consoleplayer)) { - if (printmessage) C_MidPrint (SmallFont, secretmessage); + if (printmessage) + { + if (!showsecretsector || sectornum < 0) C_MidPrint(SmallFont, GStrings["SECRETMESSAGE"]); + else + { + FString s = GStrings["SECRETMESSAGE"]; + s.AppendFormat(" (Sector %d)", sectornum); + C_MidPrint(SmallFont, s); + } + } if (playsound) S_Sound (CHAN_AUTO | CHAN_UI, "misc/secret", 1, ATTN_NORM); } } diff --git a/src/p_spec.h b/src/p_spec.h index f8dad701b1..245c699d7b 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -172,7 +172,7 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType); void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass, int flags); void P_SetSectorFriction (int tag, int amount, bool alterFlag); -void P_GiveSecret(AActor *actor, bool printmessage, bool playsound); +void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornum); // // getSide() diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 5695ad502c..76ba7c0d71 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2,6 +2,8 @@ [enu default] +SECRETMESSAGE = "A secret is revealed!"; + D_DEVSTR = "Useless mode ON.\n"; D_CDROM = "CD-ROM Version: zdoom.ini from c:\\zdoomdat\n"; PRESSKEY = "press a key."; From 98c25deec19039f61d3ea11e510e4628334c7da5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 31 May 2014 16:29:28 +0300 Subject: [PATCH 05/16] Fix LZMA compilation on GCC with position-independent code (PIC) generation enabled EBX register is used Global Offset Table in PIC http://www.greyhat.ch/lab/downloads/pic.html --- lzma/C/CpuArch.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lzma/C/CpuArch.c b/lzma/C/CpuArch.c index 4b319fa880..925edbeb88 100644 --- a/lzma/C/CpuArch.c +++ b/lzma/C/CpuArch.c @@ -74,8 +74,24 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) *c = c2; *d = d2; + #elif __PIC__ + + /* GCC or Clang WITH position-independent code generation */ + + __asm__ __volatile__ ( + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + : "=a" (*a) , + "=r" (*b) , + "=c" (*c) , + "=d" (*d) + : "0" (function)) ; + #else + /* GCC or Clang WITHOUT position-independent code generation */ + __asm__ __volatile__ ( "cpuid" : "=a" (*a) , From e8513a64edf92bb767560b2239c8f91a80ba802e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 1 Jun 2014 15:29:22 +1200 Subject: [PATCH 06/16] Rebuild nodes for hellfact map04 --- wadsrc/static/compatibility.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index e2516a30d6..1c2307ec05 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -332,7 +332,7 @@ F481922F4881F74760F3C0437FD5EDD0 // map03 setactivation 455 16 // SPAC_Push } - +8B2AC8D4DB4A49A5DCCBB067E04434D6 // The Hell Factory Hub One, map04 65A1EB4C87386F290816660A52932FF1 // Master Levels, garrison.wad { rebuildnodes From 3817bed0b3dcbc1dd85f6c1c745e5de9ba0b0759 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 1 Jun 2014 18:11:50 +1200 Subject: [PATCH 07/16] Weap scroll could sometimes miss sameslot weapons In rear cases, when using next/prevweap, defined weapons in the same slot couldn't cycle when looping to another when you only had weapons in 1 slot. --- src/g_shared/a_weapons.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 6caf42b7da..fae232557b 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -1143,7 +1143,7 @@ AWeapon *FWeaponSlots::PickNextWeapon(player_t *player) return weap; } } - while ((slot != startslot || index != startindex) && slotschecked < NUM_WEAPON_SLOTS); + while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); } return player->ReadyWeapon; } @@ -1198,7 +1198,7 @@ AWeapon *FWeaponSlots::PickPrevWeapon (player_t *player) return weap; } } - while ((slot != startslot || index != startindex) && slotschecked < NUM_WEAPON_SLOTS); + while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS); } return player->ReadyWeapon; } From 96e4cb90b7e48d40ae07353a34db60318b87b19c Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 1 Jun 2014 15:12:41 +0300 Subject: [PATCH 08/16] Fix crash on attempt to save cached OpenGL nodes on OS X Root permissions are required to be able to create directories inside /Library/Application Support So user's ~/Library/Application Support is used to store cached nodes --- src/m_specialpaths.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_specialpaths.cpp b/src/m_specialpaths.cpp index b4fcba3c38..e6f74eda61 100644 --- a/src/m_specialpaths.cpp +++ b/src/m_specialpaths.cpp @@ -335,7 +335,7 @@ FString M_GetCachePath(bool create) char pathstr[PATH_MAX]; FSRef folder; - if (noErr == FSFindFolder(kLocalDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && + if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) { path = pathstr; From 9cd074ddf3cf65fd2192320d34b8e5a05357deb6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 2 Jun 2014 10:51:17 +0200 Subject: [PATCH 09/16] - fixed: plane equation vectors must be normalized when being loaded from UDMF. --- src/p_udmf.cpp | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 19dfdf08cd..7588199b4d 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1246,6 +1246,7 @@ public: int fadecolor = -1; int desaturation = -1; int fplaneflags = 0, cplaneflags = 0; + double fp[4] = { 0 }, cp[4] = { 0 }; memset(sec, 0, sizeof(*sec)); sec->lightlevel = 160; @@ -1449,44 +1450,42 @@ public: case NAME_floorplane_a: fplaneflags |= 1; - sec->floorplane.a = CheckFixed(key); + fp[0] = CheckFloat(key); break; case NAME_floorplane_b: fplaneflags |= 2; - sec->floorplane.b = CheckFixed(key); + fp[1] = CheckFloat(key); break; case NAME_floorplane_c: fplaneflags |= 4; - sec->floorplane.c = CheckFixed(key); - sec->floorplane.ic = FixedDiv(FRACUNIT, sec->floorplane.c); + fp[2] = CheckFloat(key); break; case NAME_floorplane_d: fplaneflags |= 8; - sec->floorplane.d = CheckFixed(key); + fp[3] = CheckFloat(key); break; case NAME_ceilingplane_a: cplaneflags |= 1; - sec->ceilingplane.a = CheckFixed(key); + cp[0] = CheckFloat(key); break; case NAME_ceilingplane_b: cplaneflags |= 2; - sec->ceilingplane.b = CheckFixed(key); + cp[1] = CheckFloat(key); break; case NAME_ceilingplane_c: cplaneflags |= 4; - sec->ceilingplane.c = CheckFixed(key); - sec->ceilingplane.ic = FixedDiv(FRACUNIT, sec->ceilingplane.c); + cp[2] = CheckFloat(key); break; case NAME_ceilingplane_d: cplaneflags |= 8; - sec->ceilingplane.d = CheckFixed(key); + cp[3] = CheckFloat(key); break; default: @@ -1509,6 +1508,17 @@ public: sec->floorplane.c = FRACUNIT; sec->floorplane.ic = FRACUNIT; } + else + { + double ulen = TVector3(fp[0], fp[1], fp[2]).Length(); + + // normalize the vector, it must have a length of 1 + sec->floorplane.a = FLOAT2FIXED(fp[0] / ulen); + sec->floorplane.b = FLOAT2FIXED(fp[1] / ulen); + sec->floorplane.c = FLOAT2FIXED(fp[2] / ulen); + sec->floorplane.d = FLOAT2FIXED(fp[3] / ulen); + sec->floorplane.ic = FLOAT2FIXED(ulen / fp[2]); + } if (cplaneflags != 15) { sec->ceilingplane.a = sec->ceilingplane.b = 0; @@ -1516,6 +1526,17 @@ public: sec->ceilingplane.c = -FRACUNIT; sec->ceilingplane.ic = -FRACUNIT; } + else + { + double ulen = TVector3(cp[0], cp[1], cp[2]).Length(); + + // normalize the vector, it must have a length of 1 + sec->floorplane.a = FLOAT2FIXED(cp[0] / ulen); + sec->floorplane.b = FLOAT2FIXED(cp[1] / ulen); + sec->floorplane.c = FLOAT2FIXED(cp[2] / ulen); + sec->floorplane.d = FLOAT2FIXED(cp[3] / ulen); + sec->floorplane.ic = FLOAT2FIXED(ulen / cp[2]); + } if (lightcolor == -1 && fadecolor == -1 && desaturation == -1) { From 3e7b0c29165114d543669bb00793920412f37c08 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 2 Jun 2014 10:44:29 +0300 Subject: [PATCH 10/16] Fix crash when GL nodes file cannot be opened for writing Report errors to console if nodes file cannot be opened or written --- src/p_glnodes.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index a8fa04c345..e59f3859af 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -1168,8 +1168,21 @@ static void CreateCachedNodes(MapData *map) FString path = CreateCacheName(map, true); FILE *f = fopen(path, "wb"); - fwrite(compressed, 1, outlen+offset, f); - fclose(f); + + if (f != NULL) + { + if (fwrite(compressed, outlen+offset, 1, f) != 1) + { + Printf("Error saving nodes to file %s\n", path.GetChars()); + } + + fclose(f); + } + else + { + Printf("Cannot open nodes file %s for writing\n", path.GetChars()); + } + delete [] compressed; } From 20adcecb1d480724ebccc9448d2bef562c3ba59f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 2 Jun 2014 11:37:40 +0300 Subject: [PATCH 11/16] Remove redundant saving of GL nodes if they were loaded from cache --- src/p_glnodes.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index e59f3859af..899005ffa3 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -959,6 +959,7 @@ bool P_LoadGLNodes(MapData * map) bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) { bool ret = false; + bool loaded = false; // If the map loading code has performed a node rebuild we don't need to check for it again. if (!rebuilt && !P_CheckForGLNodes()) @@ -978,7 +979,8 @@ bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) numsegs = 0; // Try to load GL nodes (cached or GWA) - if (!P_LoadGLNodes(map)) + loaded = P_LoadGLNodes(map); + if (!loaded) { // none found - we have to build new ones! unsigned int startTime, endTime; @@ -1006,20 +1008,22 @@ bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) } } + if (!loaded) + { #ifdef DEBUG - // Building nodes in debug is much slower so let's cache them only if cachetime is 0 - buildtime = 0; + // Building nodes in debug is much slower so let's cache them only if cachetime is 0 + buildtime = 0; #endif - if (gl_cachenodes && buildtime/1000.f >= gl_cachetime) - { - DPrintf("Caching nodes\n"); - CreateCachedNodes(map); + if (gl_cachenodes && buildtime/1000.f >= gl_cachetime) + { + DPrintf("Caching nodes\n"); + CreateCachedNodes(map); + } + else + { + DPrintf("Not caching nodes (time = %f)\n", buildtime/1000.f); + } } - else - { - DPrintf("Not caching nodes (time = %f)\n", buildtime/1000.f); - } - if (!gamenodes) { From 842ef86e73efc3819b61289db0cba5e6c66ff364 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 9 Jun 2014 19:51:32 +1200 Subject: [PATCH 12/16] Don't reset the inventory of dead players --- src/g_game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 65fa5733d4..b84ac890d9 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1307,7 +1307,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) } // Clears the entire inventory and gives back the defaults for starting a game - if (flags & CHANGELEVEL_RESETINVENTORY) + if ((flags & CHANGELEVEL_RESETINVENTORY) && p->playerstate != PST_DEAD) { p->mo->ClearInventory(); p->mo->GiveDefaultInventory(); From 67c6690689b98b36bffecf557dffe9dca875a810 Mon Sep 17 00:00:00 2001 From: WChrisK Date: Wed, 11 Jun 2014 23:30:25 -0400 Subject: [PATCH 13/16] Added a check that doesn't print empty obituary strings, as wad's that hide obituary strings in multiplayer games end up spamming a lot of empty lines. --- src/p_interaction.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 4be8ed6278..0eae786324 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -320,6 +320,10 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf message = GStrings("OB_DEFAULT"); } + // [CK] Don't display empty strings + if (message == NULL || strlen(message) <= 0) + return; + SexMessage (message, gendermessage, gender, self->player->userinfo.GetName(), attacker->player->userinfo.GetName()); Printf (PRINT_MEDIUM, "%s\n", gendermessage); From 2838c4b25bab4ee375e282e0f7e6aa137f1008bd Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 16 Jun 2014 03:19:05 +1200 Subject: [PATCH 14/16] Prediction was rebuilding too much thinglist data - Stopped player prediction from rebuilding more sector list data then the player originally had. --- src/p_local.h | 1 + src/p_user.cpp | 57 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index e7fb2cb7d5..58d9d02e50 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -500,6 +500,7 @@ void P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, void P_DelSector_List(); void P_DelSeclist(msecnode_t *); // phares 3/16/98 +msecnode_t* P_DelSecnode(msecnode_t *); void P_CreateSecNodeList(AActor*,fixed_t,fixed_t); // phares 3/14/98 int P_GetMoveFactor(const AActor *mo, int *frictionp); // phares 3/6/98 int P_GetFriction(const AActor *mo, int *frictionfactor); diff --git a/src/p_user.cpp b/src/p_user.cpp index ea7b315d80..cd111838d5 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2742,25 +2742,11 @@ void P_UnPredictPlayer () act->UnlinkFromWorld(); memcpy(&act->x, PredictionActorBackup, sizeof(AActor)-((BYTE *)&act->x - (BYTE *)act)); - // Make the sector_list match the player's touching_sectorlist before it got predicted. - P_DelSeclist(sector_list); - sector_list = NULL; - for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;) - { - sector_list = P_AddSecnode(PredictionTouchingSectorsBackup[i], act, sector_list); - } - - // The blockmap ordering needs to remain unchanged, too. Right now, act has the right - // pointers, so temporarily set its MF_NOBLOCKMAP flag so that LinkToWorld() does not - // mess with them. - { - DWORD keepflags = act->flags; - act->flags |= MF_NOBLOCKMAP; - act->LinkToWorld(); - act->flags = keepflags; - } - - // Restore sector links. + // The blockmap ordering needs to remain unchanged, too. + // Restore sector links and refrences. + // [ED850] This is somewhat of a duplicate of LinkToWorld(), but we need to keep every thing the same, + // otherwise we end up fixing bugs in blockmap logic (i.e undefined behaviour with polyobject collisions), + // which we really don't want to do here. if (!(act->flags & MF_NOSECTOR)) { sector_t *sec = act->Sector; @@ -2781,6 +2767,39 @@ void P_UnPredictPlayer () *link = me; } + // Destroy old refrences + msecnode_t *node = sector_list; + while (node) + { + node->m_thing = NULL; + node = node->m_tnext; + } + + // Make the sector_list match the player's touching_sectorlist before it got predicted. + P_DelSeclist(sector_list); + sector_list = NULL; + for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;) + { + sector_list = P_AddSecnode(PredictionTouchingSectorsBackup[i], act, sector_list); + } + act->touching_sectorlist = sector_list; // Attach to thing + sector_list = NULL; // clear for next time + + node = sector_list; + while (node) + { + if (node->m_thing == NULL) + { + if (node == sector_list) + sector_list = node->m_tnext; + node = P_DelSecnode(node); + } + else + { + node = node->m_tnext; + } + } + msecnode_t *snode; // Restore sector thinglist order From a3a7ee569f97499df524ba17ad33552daf4cb432 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 17 Jun 2014 19:23:34 +1200 Subject: [PATCH 15/16] Multi-intermission waits for all players + changes - Added a segment of code that now makes the intermission wait for all players before advancing, instead of continuing on any player. A "ready icon" shows to reflect this. - The Deathmatch intermisson couldn't show the ready icon (because it just used the ingame scoreboard), so a proper intermission was added, which reflects the same design as the coop scoreboard. - The colour column wasted more space then it should have needed, so it was replaced with player colour backgrounds. - Slight y offset adjustments to make everything fit in 320x200 properly. --- src/hu_scores.cpp | 14 +- src/hu_stuff.h | 5 + src/wi_stuff.cpp | 375 ++++++++++++++++------------ wadsrc/static/graphics/readyico.png | Bin 0 -> 259 bytes wadsrc/static/language.enu | 2 + 5 files changed, 234 insertions(+), 162 deletions(-) create mode 100644 wadsrc/static/graphics/readyico.png diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index afbb7a35d5..af5d1bbaa6 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -82,9 +82,7 @@ CVAR (Int, sb_deathmatch_otherplayercolor, CR_GREY, CVAR_ARCHIVE) CVAR (Bool, sb_teamdeathmatch_enable, true, CVAR_ARCHIVE) CVAR (Int, sb_teamdeathmatch_headingcolor, CR_RED, CVAR_ARCHIVE) -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static int STACK_ARGS comparepoints (const void *arg1, const void *arg2) +int STACK_ARGS comparepoints (const void *arg1, const void *arg2) { // Compare first be frags/kills, then by name. player_t *p1 = *(player_t **)arg1; @@ -99,7 +97,7 @@ static int STACK_ARGS comparepoints (const void *arg1, const void *arg2) return diff; } -static int STACK_ARGS compareteams (const void *arg1, const void *arg2) +int STACK_ARGS compareteams (const void *arg1, const void *arg2) { // Compare first by teams, then by frags, then by name. player_t *p1 = *(player_t **)arg1; @@ -118,6 +116,8 @@ static int STACK_ARGS compareteams (const void *arg1, const void *arg2) return diff; } +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + // CODE -------------------------------------------------------------------- //========================================================================== @@ -247,7 +247,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER lineheight = MAX(height, maxiconheight * CleanYfac); ypadding = (lineheight - height + 1) / 2; - bottom = gamestate != GS_INTERMISSION ? ST_Y : SCREENHEIGHT; + bottom = ST_Y; y = MAX(48*CleanYfac, (bottom - MAXPLAYERS * (height + CleanYfac + 1)) / 2); HU_DrawTimeRemaining (bottom - height); @@ -255,10 +255,6 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER if (teamplay && deathmatch) { y -= (BigFont->GetHeight() + 8) * CleanYfac; - if (gamestate == GS_INTERMISSION) - { - y = MAX(BigFont->GetHeight() * 4, y); - } for (i = 0; i < Teams.Size (); i++) { diff --git a/src/hu_stuff.h b/src/hu_stuff.h index c8e76fee0f..e2cc75918a 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -50,4 +50,9 @@ void HU_GetPlayerWidths(int &maxnamewidth, int &maxscorewidth, int &maxiconheigh void HU_DrawColorBar(int x, int y, int height, int playernum); int HU_GetRowColor(player_t *player, bool hightlight); +// Sorting routines + +int comparepoints(const void *arg1, const void *arg2); +int compareteams(const void *arg1, const void *arg2); + #endif diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 982668566c..477de8f3ea 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -45,6 +45,7 @@ #include "v_text.h" #include "gi.h" #include "d_player.h" +#include "d_netinf.h" #include "b_bot.h" #include "textures/textures.h" #include "r_data/r_translate.h" @@ -190,6 +191,7 @@ static TArray anims; #define SHOWNEXTLOCDELAY 4 // in seconds static int acceleratestage; // used to accelerate or skip a stage +static bool playerready[MAXPLAYERS]; static int me; // wbs->pnum static stateenum_t state; // specifies current state static wbstartstruct_t *wbs; // contains information passed into intermission @@ -199,11 +201,17 @@ static int bcnt; // used for timing of background animation static int cnt_kills[MAXPLAYERS]; static int cnt_items[MAXPLAYERS]; static int cnt_secret[MAXPLAYERS]; +static int cnt_frags[MAXPLAYERS]; +static int cnt_deaths[MAXPLAYERS]; static int cnt_time; static int cnt_total_time; static int cnt_par; static int cnt_pause; +static int total_frags; +static int total_deaths; static bool noautostartmap; +static int dofrags; +static int ng_state; // // GRAPHICS @@ -1208,128 +1216,133 @@ int WI_fragSum (int playernum) return frags; } -static int dm_state; -static int dm_frags[MAXPLAYERS][MAXPLAYERS]; -static int dm_totals[MAXPLAYERS]; +static int player_deaths[MAXPLAYERS]; void WI_initDeathmatchStats (void) { - int i, j; state = StatCount; acceleratestage = 0; - dm_state = 1; + memset(playerready, 0, sizeof(playerready)); + memset(cnt_frags, 0, sizeof(cnt_frags)); + memset(cnt_deaths, 0, sizeof(cnt_frags)); + memset(player_deaths, 0, sizeof(player_deaths)); + total_frags = 0; + total_deaths = 0; + ng_state = 1; cnt_pause = TICRATE; for (i=0 ; i 99) - dm_frags[i][j] = 99; + cnt_frags[i] += 2; - if (dm_frags[i][j] < -99) - dm_frags[i][j] = -99; - - stillticking = true; - } - } - dm_totals[i] = WI_fragSum(i); + if (cnt_frags[i] > plrs[i].fragcount) + cnt_frags[i] = plrs[i].fragcount; + else + stillticking = true; + } - if (dm_totals[i] > 99) - dm_totals[i] = 99; - - if (dm_totals[i] < -99) - dm_totals[i] = -99; - } - + if (!stillticking) + { + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt & 3)) + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + stillticking = false; + + for (i = 0; i player_deaths[i]) + cnt_deaths[i] = player_deaths[i]; + else + stillticking = true; } if (!stillticking) { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - dm_state++; + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state++; } - */ - dm_state = 3; } - else if (dm_state == 4) + else if (ng_state == 6) { - if (acceleratestage) + int i; + for (i = 0; i < MAXPLAYERS; i++) { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/pastdmstats", 1, ATTN_NONE); + // If the player is in the game and not ready, stop checking + if (playeringame[i] && !players[i].isbot && !playerready[i]) + break; + } + + // All players are ready; proceed. + if (i == MAXPLAYERS && acceleratestage) + { + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/pastdmstats", 1, ATTN_NONE); WI_initShowNextLoc(); } } - else if (dm_state & 1) + else if (ng_state & 1) { if (!--cnt_pause) { - dm_state++; + ng_state++; cnt_pause = TICRATE; } } @@ -1339,97 +1352,126 @@ void WI_updateDeathmatchStats () void WI_drawDeathmatchStats () { + 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; + float h, s, v, r, g, b; + EColorRange color; + const char *text_deaths, *text_frags; + FTexture *readyico = TexMan.FindTexture("READYICO"); + player_t *sortedplayers[MAXPLAYERS]; // draw animated background - WI_drawBackground(); - WI_drawLF(); + WI_drawBackground(); - // [RH] Draw heads-up scores display - HU_DrawScores (&players[me]); + y = WI_drawLF(); -/* - int i; - int j; - int x; - int y; - int w; - - int lh; // line height + HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); + // Use the readyico height if it's bigger. + height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset(); + maxiconheight = MAX(height, maxiconheight); + height = SmallFont->GetHeight() * CleanYfac; + lineheight = MAX(height, maxiconheight * CleanYfac); + ypadding = (lineheight - height + 1) / 2; + y += CleanYfac; - lh = WI_SPACINGY; + text_deaths = GStrings("SCORE_DEATHS"); + //text_color = GStrings("SCORE_COLOR"); + text_frags = GStrings("SCORE_FRAGS"); - // draw stat titles (top line) - V_DrawPatchClean(DM_TOTALSX-LittleShort(total->width)/2, - DM_MATRIXY-WI_SPACINGY+10, - &FB, - total); - - V_DrawPatchClean(DM_KILLERSX, DM_KILLERSY, &FB, killers); - V_DrawPatchClean(DM_VICTIMSX, DM_VICTIMSY, &FB, victims); + 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; - // draw P? - x = DM_MATRIXX + DM_SPACINGX; - y = DM_MATRIXY; + x = (SCREENWIDTH - deaths_x) >> 1; + icon_x += x; + name_x += x; + frags_x += x; + deaths_x += x; - for (i=0 ; iDrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, frags_x - SmallFont->StringWidth(text_frags)*CleanXfac, y, text_frags, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, deaths_x - deaths_len*CleanXfac, y, text_deaths, DTA_CleanNoMove, true, TAG_DONE); + y += height + 6 * CleanYfac; + + // Sort all players + for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i]) - { - V_DrawPatchClean(x-LittleShort(p[i]->width)/2, - DM_MATRIXY - WI_SPACINGY, - &FB, - p[i]); - - V_DrawPatchClean(DM_MATRIXX-LittleShort(p[i]->width)/2, - y, - &FB, - p[i]); - - if (i == me) - { - V_DrawPatchClean(x-LittleShort(p[i]->width)/2, - DM_MATRIXY - WI_SPACINGY, - &FB, - bstar); - - V_DrawPatchClean(DM_MATRIXX-LittleShort(p[i]->width)/2, - y, - &FB, - star); - } - } - x += DM_SPACINGX; - y += WI_SPACINGY; + sortedplayers[i] = &players[i]; } - // draw stats - y = DM_MATRIXY+10; - w = LittleShort(num[0]->width); + if (teamplay) + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), compareteams); + else + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), comparepoints); - for (i=0 ; iDim(MAKERGB(clamp(int(r*255.f), 0, 255), + clamp(int(g*255.f), 0, 255), + clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); + + if (playerready[pnum] || player->isbot) // Bots are automatically assumed ready, to prevent confusion + screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); + + color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); + if (player->mo->ScoreIcon.isValid()) { - for (j=0 ; jmo->ScoreIcon]; + screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE); } - y += WI_SPACINGY; + screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); + WI_drawNum(SmallFont, frags_x, y + ypadding, cnt_frags[pnum], 0, false, color); + if (ng_state >= 2) + { + WI_drawNum(SmallFont, deaths_x, y + ypadding, cnt_deaths[pnum], 0, false, color); + } + y += lineheight + CleanYfac; } -*/ + + // Draw "TOTAL" line + y += height + 3 * CleanYfac; + color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; + screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE); + WI_drawNum(SmallFont, frags_x, y, total_frags, 0, false, color); + if (ng_state >= 4) + { + WI_drawNum(SmallFont, deaths_x, y, total_deaths, 0, false, color); + } + + // Draw game time + y += height + CleanYfac; + + int seconds = plrs[me].stime / TICRATE; + int hours = seconds / 3600; + int minutes = (seconds % 3600) / 60; + seconds = seconds % 60; + + FString leveltime = GStrings("SCORE_LVLTIME"); + leveltime += ": "; + + char timer[sizeof "HH:MM:SS"]; + mysnprintf(timer, sizeof(timer), "%02i:%02i:%02i", hours, minutes, seconds); + leveltime += timer; + + screen->DrawText(SmallFont, color, x, y, leveltime, DTA_CleanNoMove, true, TAG_DONE); } -static int cnt_frags[MAXPLAYERS]; -static int dofrags; -static int ng_state; - void WI_initNetgameStats () { @@ -1437,6 +1479,7 @@ void WI_initNetgameStats () state = StatCount; acceleratestage = 0; + memset(playerready, 0, sizeof(playerready)); ng_state = 1; cnt_pause = TICRATE; @@ -1587,7 +1630,16 @@ void WI_updateNetgameStats () } else if (ng_state == 10) { - if (acceleratestage) + 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].isbot && !playerready[i]) + break; + } + + // All players are ready; proceed. + if (i == MAXPLAYERS && acceleratestage) { S_Sound (CHAN_VOICE | CHAN_UI, "intermission/pastcoopstats", 1, ATTN_NONE); WI_initShowNextLoc(); @@ -1611,8 +1663,10 @@ void WI_drawNetgameStats () 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; EColorRange color; - const char *text_bonus, *text_color, *text_secret, *text_kills; + const char *text_bonus, *text_secret, *text_kills; + FTexture *readyico = TexMan.FindTexture("READYICO"); // draw animated background WI_drawBackground(); @@ -1620,17 +1674,22 @@ void WI_drawNetgameStats () y = WI_drawLF(); HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); + // Use the readyico height if it's bigger. + height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset(); + if (height > maxiconheight) + { + maxiconheight = height; + } height = SmallFont->GetHeight() * CleanYfac; lineheight = MAX(height, maxiconheight * CleanYfac); ypadding = (lineheight - height + 1) / 2; - y += 16*CleanYfac; + y += CleanYfac; text_bonus = GStrings((gameinfo.gametype & GAME_Raven) ? "SCORE_BONUS" : "SCORE_ITEMS"); - text_color = GStrings("SCORE_COLOR"); text_secret = GStrings("SCORE_SECRET"); text_kills = GStrings("SCORE_KILLS"); - icon_x = (SmallFont->StringWidth(text_color) + 8) * CleanXfac; + 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; @@ -1645,7 +1704,6 @@ void WI_drawNetgameStats () color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; - screen->DrawText(SmallFont, color, x, y, text_color, DTA_CleanNoMove, true, TAG_DONE); screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE); screen->DrawText(SmallFont, color, kills_x - SmallFont->StringWidth(text_kills)*CleanXfac, y, text_kills, DTA_CleanNoMove, true, TAG_DONE); screen->DrawText(SmallFont, color, bonus_x - bonus_len*CleanXfac, y, text_bonus, DTA_CleanNoMove, true, TAG_DONE); @@ -1665,7 +1723,17 @@ void WI_drawNetgameStats () continue; player = &players[i]; - HU_DrawColorBar(x, y, lineheight, i); + + D_GetPlayerColor(i, &h, &s, &v, NULL); + HSVtoRGB(&r, &g, &b, h, s, v); + + screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255), + clamp(int(g*255.f), 0, 255), + clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); + + if (playerready[i] || player->isbot) // Bots are automatically assumed ready, to prevent confusion + screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); + color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); if (player->mo->ScoreIcon.isValid()) { @@ -1689,7 +1757,7 @@ void WI_drawNetgameStats () } // Draw "MISSED" line - y += 5 * CleanYfac; + y += 3 * CleanYfac; screen->DrawText(SmallFont, CR_DARKGRAY, name_x, y, GStrings("SCORE_MISSED"), DTA_CleanNoMove, true, TAG_DONE); WI_drawPercent(SmallFont, kills_x, y, missed_kills, wbs->maxkills, false, CR_DARKGRAY); if (ng_state >= 4) @@ -1702,7 +1770,7 @@ void WI_drawNetgameStats () } // Draw "TOTAL" line - y += height + 5 * CleanYfac; + y += height + 3 * CleanYfac; color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE); WI_drawNum(SmallFont, kills_x, y, wbs->maxkills, 0, false, color); @@ -1939,6 +2007,7 @@ void WI_checkForAccelerate(void) == players[i].oldbuttons) && !player->isbot) { acceleratestage = 1; + playerready[i] = true; } player->oldbuttons = player->cmd.ucmd.buttons; } diff --git a/wadsrc/static/graphics/readyico.png b/wadsrc/static/graphics/readyico.png new file mode 100644 index 0000000000000000000000000000000000000000..5a9b20804bdec28c4ba6ad078b556e53c5e3d543 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^96-#)!3HEdkIOdzDVB6cUq=Rpjs4tz5?O(K&H|6f zVg?4jLm Date: Tue, 17 Jun 2014 20:25:53 +1200 Subject: [PATCH 16/16] Added wi_autoadvance - Prevents an absent player from stopping the intermission --- src/wi_stuff.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 477de8f3ea..e60fee3033 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -64,6 +64,7 @@ typedef enum CVAR (Bool, wi_percents, true, CVAR_ARCHIVE) CVAR (Bool, wi_showtotaltime, true, CVAR_ARCHIVE) CVAR (Bool, wi_noautostartmap, false, CVAR_USERINFO|CVAR_ARCHIVE) +CVAR (Int, wi_autoadvance, 0, CVAR_SERVERINFO) void WI_loadData (); @@ -1113,6 +1114,7 @@ void WI_updateNoState () else { bool noauto = noautostartmap; + bool autoskip = (wi_autoadvance > 0 && bcnt > (wi_autoadvance * TICRATE)); for (int i = 0; !noauto && i < MAXPLAYERS; ++i) { @@ -1121,7 +1123,7 @@ void WI_updateNoState () noauto |= players[i].userinfo.GetNoAutostartMap(); } } - if (!noauto) + if (!noauto || autoskip) { cnt--; } @@ -1252,10 +1254,11 @@ void WI_updateDeathmatchStats () int i; bool stillticking; + bool autoskip = (wi_autoadvance > 0 && bcnt > (wi_autoadvance * TICRATE)); WI_updateAnimatedBack(); - if (acceleratestage && ng_state != 6) + if ((acceleratestage || autoskip) && ng_state != 6) { acceleratestage = 0; @@ -1332,7 +1335,7 @@ void WI_updateDeathmatchStats () } // All players are ready; proceed. - if (i == MAXPLAYERS && acceleratestage) + if ((i == MAXPLAYERS && acceleratestage) || autoskip) { S_Sound(CHAN_VOICE | CHAN_UI, "intermission/pastdmstats", 1, ATTN_NONE); WI_initShowNextLoc(); @@ -1503,10 +1506,11 @@ void WI_updateNetgameStats () int i; int fsum; bool stillticking; + bool autoskip = (wi_autoadvance > 0 && bcnt > (wi_autoadvance * TICRATE)); WI_updateAnimatedBack (); - if (acceleratestage && ng_state != 10) + if ((acceleratestage || autoskip) && ng_state != 10) { acceleratestage = 0; @@ -1639,7 +1643,7 @@ void WI_updateNetgameStats () } // All players are ready; proceed. - if (i == MAXPLAYERS && acceleratestage) + if ((i == MAXPLAYERS && acceleratestage) || autoskip) { S_Sound (CHAN_VOICE | CHAN_UI, "intermission/pastcoopstats", 1, ATTN_NONE); WI_initShowNextLoc();