Remove full message log (potential buffer overflow).

This commit is contained in:
Mari the Deer 2023-06-15 22:23:51 +02:00
commit ee1ec8c997
12 changed files with 11 additions and 422 deletions

View file

@ -661,7 +661,7 @@ Pretty simplistic so it doesn't get in the way of the action. Some things may be
### Top left corner
Message display. Can be configured to show different numbers of messages depending on whether the chat prompt is open. Chat messages take much longer to expire than others, so there's less of a chance to miss them, as they might pop back up when the less important ones expire. A full chat history can be read at any time in the **Demolitionist Menu** *(if there were any actual messages)*. Repeated messages are compressed with a multiplier suffix.
Message display. Can be configured to show different numbers of messages depending on whether the chat prompt is open. Chat messages take much longer to expire than others, so there's less of a chance to miss them, as they might pop back up when the less important ones expire. Repeated messages are compressed with a multiplier suffix.
### Top right corner

View file

@ -263,7 +263,7 @@ TOOLTIP_SWWM_NOINTERTIPS = "Hides intermission tips, in case you don't want to s
TOOLTIP_SWWM_BEEPBOOP = "The Demolitionist will make cute noises whenever pressing any usable line. This might get annoying, so it's optional.";
TOOLTIP_SWWM_VOICEAMP = "At their default volume, the Demolitionist's voice lines might be drowned out by in-game audio. This sets an additional amplification level so they're louder than other sounds.";
TOOLTIP_NETEVENT_SWWMCLEAREFFECTS = "Fades out any active blood, gore, debris and casings.";
TOOLTIP_SWWM_VOICELOG = "Subtitled player comments will be saved to the Messages tab of the Demolitionist Menu.";
TOOLTIP_SWWM_VOICELOG = "Subtitled player comments will show up in the console.";
TOOLTIP_SWWM_SKIPSKILL = "Skips confirmation of skill selection, if you get tired of seeing it every time.";
TOOLTIP_SWWM_HUDALLAMMO = "Shows all ammo types in the HUD, rather than only those for currently carried weapons.";
TOOLTIP_SWWM_NOINTROEX = "Skips the explosion sequence over the original titlepic.";
@ -415,12 +415,6 @@ SWWM_STORESWAP = "You already own a weapon in the same slot: %s.";
SWWM_NOSTORE = "(store is not available)";
SWWM_NOSTOREBUY = "(no items left to buy)";
SWWM_NOSTORESELL = "(no items to be sold)";
SWWM_CHATTAB = "Messages";
SWWM_NOCHAT = "(message history is empty)";
SWWM_CLEARCHAT0 = "Are you sure you want to clear all messages?";
SWWM_CLEARCHAT1 = "\cfEnter, Left Click\c- to confirm";
SWWM_CLEARCHAT2 = "\cfBackspace, Right Click\c- to cancel";
SWWM_CHATCLEARED = "Message history has been cleared.";
SWWM_SECRETTAB = "Secret";
SWWM_TODEMO = "\cx\"Fight for justice, Demo-chan! You can do it!\"\c- \cg♥\c- Ibuki & Saya";
SWWM_CUTIECLUB = "\cx\"Never lose hope, Demo-chan! We're all with you!\"\c- \cg♥\c- Cutie Club";
@ -511,14 +505,7 @@ SWWM_HELPTXT =
"\n"
"\cfArrow Keys:\c- Navigate\n"
"\cfEnter, Left Click:\c- Sell\n"
"\cfBackspace, Right Click:\c- Switch to buying\n"
"\n"
"\cxMessages Tab - Controls\c-\n"
"\cx———————————————————————\c-\n"
"\n"
"\cfUp, Down:\c- Scroll\n"
"\cfBackspace, Right Click:\c- Clear messages\n"
"\cfEnter, Left Click:\c- Confirm clear";
"\cfBackspace, Right Click:\c- Switch to buying";
SWWM_GAMETAB = "Games";
SWWM_PICKGAME = "Choose a game";
SWWM_GAMETITLE_MADCATGAME = "Boot Test";

View file

@ -261,7 +261,7 @@ TOOLTIP_SWWM_NOINTERTIPS = "Oculta los consejos de intermisión, por si no quier
TOOLTIP_SWWM_BEEPBOOP = "La Demolicionista hará ruiditos adorables cuando pulse cualquier línea utilizable. Esto puede que resulte cansino, así que es opcional.";
TOOLTIP_SWWM_VOICEAMP = "A su volumen normal, las voces de la Demolicionista pueden ser ahogadas por el audio del juego. Con esto se le dá un nivel de amplificación extra para que sean más estridentes que otros sonidos.";
TOOLTIP_NETEVENT_SWWMCLEAREFFECTS = "Desvanece cualquier sangre, vísceras, escombros y casquillos activos.";
TOOLTIP_SWWM_VOICELOG = "Los comentarios de jugador subtitulados se guardarán en la pestaña de Mensajes del Menú de Demolicionista.";
TOOLTIP_SWWM_VOICELOG = "Los comentarios de jugador subtitulados aparecerán en la consola.";
TOOLTIP_SWWM_SKIPSKILL = "Salta la confirmación de selección de dificultad, si te has cansado de verla todo el tiempo.";
TOOLTIP_SWWM_HUDALLAMMO = "Muestra todos los tipos de munición en el HUD, en lugar de solo los de las armas obtenidas.";
TOOLTIP_SWWM_NOINTROEX = "Salta la secuencia de explosiones sobre la imagen de título original.";
@ -374,12 +374,6 @@ SWWM_STORESWAP = "Ya posees un arma en la misma ranura: %s.";
SWWM_NOSTORE = "(tienda no disponible)";
SWWM_NOSTOREBUY = "(no queda nada que comprar)";
SWWM_NOSTORESELL = "(no tienes ítems que vender)";
SWWM_CHATTAB = "Mensajes";
SWWM_NOCHAT = "(historial de mensajes vacío)";
SWWM_CLEARCHAT0 = "¿Seguro que quieres borrar todos los mensajes?";
SWWM_CLEARCHAT1 = "\cfEnter/Click Izdo.\c- para confirmar";
SWWM_CLEARCHAT2 = "\cfRetroceso/Click Dcho.\c- para cancelar";
SWWM_CHATCLEARED = "El historial de mensajes ha sido borrado.";
SWWM_SECRETTAB = "Secreto";
SWWM_TODEMO = "\cx\"¡Lucha por la justicia, Demo-chan! ¡Tú puedes!\"\c- \cg♥\c- Ibuki y Saya";
SWWM_CUTIECLUB = "\cx\"¡Jamás pierdas la esperanza, Demo-chan! ¡Estamos todas contigo!\"\c- \cg♥\c- Club Cuqui";
@ -470,30 +464,10 @@ SWWM_HELPTXT =
"\n"
"\cfFlechas:\c- Navegar\n"
"\cfEnter/Click Izdo.:\c- Vender\n"
"\cfRetroceso/Click Dcho.:\c- Cambiar a compra\n"
"\n"
"\cxPestaña de Mensajes - Controles\c-\n"
"\cx———————————————————————————————\c-\n"
"\n"
"\cfArriba/Abajo:\c- Scroll\n"
"\cfRetroceso/Click Dcho.:\c- Borrar mensajes\n"
"\cfEnter/Click Izdo.:\c- Confirmar borrado";
"\cfRetroceso/Click Dcho.:\c- Cambiar a compra";
SWWM_GAMETAB = "Juegos";
SWWM_PICKGAME = "Elige un juego";
SWWM_GAMETITLE_MADCATGAME = "Test de Arranque";
// Wallbuster menu
SWWM_BUSTERTITLE = "Wallbuster - Menú de Recarga Fácil";
SWWM_BUSTERKEYS =
"\cfIzq/Dcha:\c- Seleccionar Munición\n"
"\cfAbajo:\c- Agregar Munición\n"
"\cfArriba:\c- Quitar Munición\n"
"\cfAv Pág:\c- Agregar 5 Munición\n"
"\cfRe Pág:\c- Quitar 5 Munición\n"
"\cfRetroceso:\c- Limpiar Munición\n"
"\cfEnter:\c- Iniciar Recarga\n"
"\cfEnter (si vacío):\c- Recarga Automática\n"
"\cfSuprimir:\c- Vaciado Completo\n"
"\cfEsc:\c- Cancelar recarga\n";
// Help menu
SWWM_HELP_STORY = "La Historia Hasta Ahora";
SWWM_HELP_STORYDOOM =

View file

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r809 \cu(Sun 4 Jun 11:52:38 CEST 2023)\c-";
SWWM_SHORTVER="\cw1.3pre r809 \cu(2023-06-04 11:52:38)\c-";
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r811 \cu(Thu 15 Jun 22:25:44 CEST 2023)\c-";
SWWM_SHORTVER="\cw1.3pre r811 \cu(2023-06-15 22:25:44)\c-";

View file

@ -99,7 +99,6 @@ version "4.11"
#include "zscript/kbase/swwm_kbasetab_keychain.zsc"
#include "zscript/kbase/swwm_kbasetab_library.zsc"
#include "zscript/kbase/swwm_kbasetab_store.zsc"
#include "zscript/kbase/swwm_kbasetab_chat.zsc"
#include "zscript/kbase/swwm_kbasetab_help.zsc"
#include "zscript/kbase/swwm_kbasetab_secret.zsc"
#include "zscript/kbase/swwm_kbasetab_games.zsc"

View file

@ -150,7 +150,7 @@ extend Class SWWMHandler
{
let l = SWWMOneLiner.Make(oneliner,onelinerspan);
StatusBar.AttachMessage(l,-3473);
SendNetworkEvent("swwmstoremessage.\cd"..(multiplayer?players[consoleplayer].GetUserName():"Demo").."\c*: "..StringTable.Localize(oneliner).."\c*",level.totaltime,PRINT_CHAT,consoleplayer);
if ( swwm_voicelog ) Console.PrintfEx(PRINT_CHAT|PRINT_NONOTIFY,"\cd"..(multiplayer?players[consoleplayer].GetUserName():"Demo").."\c*: "..StringTable.Localize(oneliner).."\c*");
}
SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel);
}

View file

@ -88,20 +88,6 @@ extend Class SWWMHandler
bar.ntagtic = level.totaltime;
bar.ntagcol = nametagcolor;
}
else if ( e.Name ~== "swwmrefreshmessagelog" )
{
let kbase = DemolitionistMenu(Menu.GetCurrentMenu());
if ( !kbase ) return;
foreach ( t:kbase.tabs )
{
if ( !(t is 'DemolitionistChatTab') ) continue;
let ct = DemolitionistChatTab(t);
if ( ct.msglist ) ct.msglist.Destroy();
break;
}
kbase.tmsg = StringTable.Localize("$SWWM_CHATCLEARED");
kbase.tmsgtic = Menu.MenuTime()+70;
}
}
override void NetworkProcess( ConsoleEvent e )
@ -463,17 +449,6 @@ extend Class SWWMHandler
break;
}
}
else if ( e.Name.Left(17) ~== "swwmstoremessage." )
{
if ( consoleplayer != e.Args[2] ) return;
SWWMFullHistory.PushMessage(e.Name.Mid(17),e.Args[0],e.Args[1]);
}
else if ( e.Name ~== "swwmclearmessages" )
{
if ( consoleplayer != e.Args[0] ) return;
SWWMFullHistory.ClearMessages();
SendInterfaceEvent(consoleplayer,"swwmrefreshmessagelog");
}
else if ( e.Name.Left(16) ~== "swwmremoteliner." )
{
if ( consoleplayer == e.Args[0] ) return;
@ -486,10 +461,7 @@ extend Class SWWMHandler
if ( swwm_mutevoice >= e.Args[1] ) return;
double dist = players[consoleplayer].Camera.Distance3D(players[e.Args[0]].mo);
if ( dist < 2000 )
{
Console.Printf("\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19)));
if ( swwm_voicelog ) SWWMFullHistory.PushMessage("\cd"..players[e.Args[0]].GetUserName().."\c*: "..StringTable.Localize(e.Name.Mid(19)).."\c*",level.totaltime,PRINT_CHAT);
}
Console.PrintfEx(PRINT_CHAT,"\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19)));
}
else if ( e.Name ~== "swwmcleartransaction" )
{

View file

@ -221,9 +221,6 @@ extend Class SWWMStatusBar
if ( (rprintlevel < PRINT_LOW) || (rprintlevel > PRINT_TEAMCHAT) ) rprintlevel = PRINT_HIGH;
// strip trailing newline (all Printf type messages have this)
outline.DeleteLastCharacter();
// append main queue messages to full history
if ( rprintlevel != PRINT_LOW )
EventHandler.SendNetworkEvent("swwmstoremessage."..outline,level.totaltime,rprintlevel,consoleplayer);
let m = new("MsgLine");
m.str = outline;
m.type = rprintlevel;

View file

@ -335,17 +335,12 @@ Class SWWMDirectMessage : HUDMessageBase
else txt = StringTable.Localize("$SWWM_"..seqname..seqnum);
if ( l ) l.Destroy();
l = mSmallFont.BreakLines(txt,220);
// append to the player's chat log (if it's valid)
// if valid, append to the console so the player may re-read this if they want
if ( (seqnum < 1) || (seqnum > seqcnt) ) return;
// some messages may have newlines in them, split them
Array<String> storemsg;
txt.Split(storemsg,"\n");
foreach ( msg:storemsg )
{
EventHandler.SendNetworkEvent("swwmstoremessage."..chrname.."\c*: "..msg.."\c*",level.totaltime,PRINT_CHAT,consoleplayer);
// stick it into the console as well
Console.PrintfEx(PRINT_CHAT|PRINT_NONOTIFY,chrname.."\c*: "..msg.."\c*");
}
foreach ( msg:storemsg ) Console.PrintfEx(PRINT_CHAT|PRINT_NONOTIFY,chrname.."\c*: "..msg.."\c*");
}
private void DrawText()

View file

@ -185,7 +185,6 @@ Class DemolitionistMenu : GenericMenu
'DemolitionistKeychainTab',
'DemolitionistLibraryTab',
'DemolitionistStoreTab',
'DemolitionistChatTab',
//'DemolitionistGameTab', // disabled until 1.5
'DemolitionistHelpTab',
'DemolitionistSecretTab'

View file

@ -1,273 +0,0 @@
// all them messages
Class DemolitionistChatTab : DemolitionistMenuTab
{
DemolitionistMenuList msglist;
int ofs, maxofs;
double smofs;
bool drag;
bool justopened;
bool bDel;
SWWMFullHistory hist;
override DemolitionistMenuTab Init( DemolitionistMenu master )
{
title = StringTable.Localize("$SWWM_CHATTAB");
hist = SWWMFullHistory.Get();
justopened = true;
return Super.Init(master);
}
override void OnDestroy()
{
Super.OnDestroy();
if ( msglist ) msglist.Destroy();
}
override void OnSelect()
{
smofs = ofs;
justopened = true;
}
override void OnDeselect()
{
smofs = ofs;
}
override void Ticker()
{
if ( !hist ) return;
int ypos = 0;
bool recenter = true;
if ( !msglist )
{
msglist = new("DemolitionistMenuList");
msglist.master = master;
msglist.selected = -1;
msglist.items.Resize(hist.msg.Size());
for ( int i=0; i<hist.msg.Size(); i++ )
{
msglist.items[i] = new("DemolitionistMenuChatItem").Init(master,hist.msg[i]);
msglist.items[i].ypos = ypos;
ypos += msglist.items[i].GetHeight();
}
}
else if ( msglist.items.Size() < hist.msg.Size() )
{
// theoretically we can assume that the message list will never shrink in the middle of gameplay, only grow
// also the array only ever has elements pushed, never inserted in-between
// if this somehow breaks, I don't know what the hell can do that
msglist.items.Resize(hist.msg.Size());
for ( int i=0; i<hist.msg.Size(); i++ )
{
if ( !msglist.items[i] )
{
msglist.items[i] = new("DemolitionistMenuChatItem").Init(master,hist.msg[i]);
msglist.items[i].ypos = ypos;
}
ypos += msglist.items[i].GetHeight();
}
if ( ofs < maxofs ) recenter = false; // don't recenter if we weren't at the bottom
}
else
{
foreach ( i:msglist.items )
ypos += i.GetHeight();
recenter = false;
}
maxofs = max(ypos-int(master.ws.y-46),0);
// push it to the bottom
if ( recenter ) ofs = maxofs;
// update smooth scroll
smofs = (smofs*.6)+(ofs*.4);
if ( (abs(smofs-ofs) < (1./master.hs)) || justopened ) smofs = ofs;
justopened = false;
// tick the list (not really needed, as elements don't dynamically update anything)
msglist.Ticker();
}
// called when sending a scroll input
// returns true if the position actually changed
// speed: how many pixels to move (either back or forward)
bool Scroll( int speed )
{
if ( maxofs <= 0 ) return false;
int oldofs = ofs;
ofs = clamp(ofs+speed,0,maxofs);
return (ofs != oldofs);
}
// called when clicking on our scrollbar
// returns true if the position actually changed
// y: relative click position
bool SetOffset( double y )
{
if ( maxofs <= 0 ) return false;
int oldofs = ofs;
ofs = clamp(int(round((y-20.5)/((master.ws.y-41.)/maxofs))),0,maxofs);
return (ofs != oldofs);
}
override void MenuInput( int key )
{
if ( maxofs <= 0 ) return;
switch ( key )
{
case MK_DOWN:
if ( Scroll(16) ) master.MenuSound("menu/demoscroll");
break;
case MK_UP:
if ( Scroll(-16) ) master.MenuSound("menu/demoscroll");
break;
case MK_ENTER:
if ( bDel )
{
master.MenuSound("menu/democlose");
bDel = false;
EventHandler.SendNetworkEvent("swwmclearmessages",consoleplayer);
}
break;
case MK_BACK:
bDel = !bDel;
master.MenuSound(bDel?"menu/demosel":"menu/democlose");
break;
}
}
override void MouseInput( Vector2 pos, int btn )
{
if ( maxofs <= 0 ) return;
switch ( btn )
{
case MB_LEFT:
// see if we're clicking the scrollbar (if it exists)
if ( pos.x > (master.ws.x-8) )
{
SetOffset(pos.y);
master.MenuSound("menu/demoscroll");
drag = true;
}
MenuInput(MK_ENTER);
break;
case MB_RIGHT:
MenuInput(MK_BACK);
break;
case MB_WHEELUP:
if ( Scroll(-8) ) master.MenuSound("menu/demoscroll");
break;
case MB_WHEELDOWN:
if ( Scroll(8) ) master.MenuSound("menu/demoscroll");
break;
case MB_DRAG:
if ( drag ) SetOffset(pos.y);
break;
case MB_RELEASE:
drag = false;
break;
}
}
override void Drawer( double fractic )
{
if ( bDel )
{
double fh = master.mSmallFont.GetHeight();
double yy = int(master.ws.y-fh*4)/2;
String str = StringTable.Localize("$SWWM_CLEARCHAT0");
double xx = int(master.ws.x-master.mSmallFont.StringWidth(str))/2;
Screen.DrawText(master.mSmallFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
yy += fh*2;
str = StringTable.Localize("$SWWM_CLEARCHAT1");
xx = int(master.ws.x-master.mSmallFont.StringWidth(str))/2;
Screen.DrawText(master.mSmallFont,Font.CR_WHITE,master.origin.x+xx,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
yy += fh;
str = StringTable.Localize("$SWWM_CLEARCHAT2");
xx = int(master.ws.x-master.mSmallFont.StringWidth(str))/2;
Screen.DrawText(master.mSmallFont,Font.CR_WHITE,master.origin.x+xx,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
return;
}
if ( !hist || !msglist || (msglist.items.Size() <= 0) )
{
String str = StringTable.Localize("$SWWM_NOCHAT");
double xx = int(master.ws.x-master.mSmallFont.StringWidth(str))/2;
double yy = int(master.ws.y-master.mSmallFont.GetHeight())/2;
Screen.DrawText(master.mSmallFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
return;
}
double ssmofs = (smofs~==ofs)?smofs:(smofs*(1.-fractic)+((smofs*.6)+(ofs*.4))*fractic);
double xx = 3;
double yy = 23;
Screen.SetClipRect(int((master.origin.x+3)*master.hs),int((master.origin.y+23)*master.hs),int((master.ws.x-12)*master.hs),int((master.ws.y-46)*master.hs));
msglist.Drawer((xx,yy-ssmofs));
Screen.ClearClipRect();
if ( maxofs <= 0 ) return;
xx = master.ws.x-8;
master.DrawVSeparator(xx,14,master.ws.y-28);
xx += 2;
yy = floor(ssmofs*((master.ws.y-39)/maxofs))+14;
Screen.DrawText(master.mSmallFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,"▮",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
}
}
// chat message item
Class DemolitionistMenuChatItem : DemolitionistMenuListItem
{
BrokenLines l;
int msgtype;
int margin;
String tstamp;
DemolitionistMenuChatItem Init( DemolitionistMenu master, MsgLine m )
{
Super.Init(master,"");
msgtype = m.type;
if ( m.type == -1 )
{
// map change marker
label = String.Format("\cx[\c-%s\cx]\c-",m.str);
return self;
}
// actually use crimetime for the timestamps, for immersion
int rtime = (m.tic/GameTicRate)+(master.c_minute*60)+(master.c_hour*3600);
int thour = (rtime/3600)%24;
int tmin = (rtime/60)%60;
int tsec = rtime%60;
tstamp = String.Format("\cm[\c-%02d\cm:\c-%02d\cm:\c-%02d\cm]\c- ",thour,tmin,tsec);
margin = master.mSmallFont.StringWidth(tstamp);
String nstr = m.str;
if ( m.rep > 1 ) nstr.AppendFormat(" (x%d)",m.rep);
l = master.mSmallFont.BreakLines(nstr,int(master.ws.x-14)-margin);
return self;
}
override void OnDestroy()
{
Super.OnDestroy();
if ( l ) l.Destroy();
}
override int GetWidth()
{
return int(master.ws.x-14);
}
override int GetHeight()
{
if ( l ) return l.Count()*14;
return 14;
}
override void Drawer( Vector2 pos, bool selected )
{
if ( msgtype == -1 )
{
// level change label
double xx = int((master.ws.x-14)-master.mSmallFont.StringWidth(label))/2;
Screen.DrawText(master.mSmallFont,Font.CR_GOLD,master.origin.x+pos.x+xx,master.origin.y+pos.y,label,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
return;
}
Screen.DrawText(master.mSmallFont,Font.CR_DARKGRAY,master.origin.x+pos.x,master.origin.y+pos.y,tstamp,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
int col = msg2color;
if ( msgtype == PRINT_MEDIUM ) col = msg1color;
else if ( msgtype == PRINT_CHAT ) col = msg3color;
else if ( msgtype == PRINT_TEAMCHAT ) col = msg4color;
for ( int i=0; i<l.Count(); i++ )
Screen.DrawText(master.mSmallFont,col,master.origin.x+pos.x+margin,master.origin.y+pos.y+i*14,l.StringAt(i),DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
}
}

View file

@ -7,67 +7,6 @@ Class SWWMStaticThinker : Thinker abstract
// if they don't, something is very wrong
}
// "Full History" contains all messages since session start, nothing is flushed
// this can be accessed from a section of the knowledge base
Class SWWMFullHistory : SWWMStaticThinker
{
String lastmap;
Array<MsgLine> msg;
static clearscope SWWMFullHistory Get()
{
let fh = SWWMFullHistory(ThinkerIterator.Create("SWWMFullHistory",STAT_STATIC).Next());
return fh;
}
static play void PushMessage( String str, int tic, int type )
{
let fh = SWWMFullHistory(ThinkerIterator.Create("SWWMFullHistory",STAT_STATIC).Next());
if ( !fh )
{
fh = new("SWWMFullHistory");
fh.ChangeStatNum(STAT_STATIC);
}
MsgLine m;
if ( level.mapname != fh.lastmap )
{
// push a map change label
m = new("MsgLine");
m.str = level.levelname;
m.type = -1; // hopefully this won't be used by actual messages
fh.lastmap = level.mapname;
fh.msg.Push(m);
}
// if the last added line is identical to this one, update it with a repetition marker
int last = fh.msg.Size()-1;
if ( (fh.msg[last].str == str) && (fh.msg[last].type == type) )
{
fh.msg[last].tic = tic;
fh.msg[last].rep++;
return;
}
m = new("MsgLine");
m.str = str;
m.tic = tic;
m.type = type;
m.rep = 1;
fh.msg.Push(m);
}
static play void ClearMessages()
{
let fh = SWWMFullHistory(ThinkerIterator.Create("SWWMFullHistory",STAT_STATIC).Next());
if ( !fh )
{
fh = new("SWWMFullHistory");
fh.ChangeStatNum(STAT_STATIC);
return;
}
fh.lastmap = "";
fh.msg.Clear();
}
}
// Dedicated mission log (for custom maps)
Class SWWMMissionLog : SWWMStaticThinker
{