// additional hud things // Press F to Pay Respects Class PayRespects : HUDMessageBase { Vector2 basepos; int lifespan, initialspan, starttic; double scale; double hs; Vector2 ss; int seed, seed2; Font mSmallFont; static PayRespects PressF() { let f = new('PayRespects'); f.basepos = (FRandom[FInTheChat](0.,1.),FRandom[FInTheChat](1.02,1.05)); f.scale = FRandom[FInTheChat](.5,2.); f.lifespan = f.initialspan = Random[FInTheChat](20,80); f.starttic = level.maptime; f.seed = Random[FInTheChat](); f.seed2 = Random[FInTheChat](); f.ScreenSizeChanged(); f.mSmallFont = Font.GetFont('TewiFont'); return f; } override bool Tick() { lifespan--; return (lifespan<=0); } override void ScreenSizeChanged() { hs = CleanXFac*scale; ss = (Screen.GetWidth(),Screen.GetHeight())/hs; } override void Draw( int bottom, int visibility ) { Vector2 realpos = (basepos.x*ss.x,basepos.y*ss.y); Vector2 fo = (mSmallFont.StringWidth("F")/2.,-mSmallFont.GetHeight()); // F rise up int initspd = (128-seed); if ( (initspd >= 0) && (initspd < 32) ) initspd = 32; if ( (initspd < 0) && (initspd > -32) ) initspd = -32; int boostup = 32+(seed2/4); double fractic = System.GetTimeFrac(); fo.x += (.15*initspd)*((initialspan-(lifespan-fractic))**.6); fo.y += ((initialspan-(lifespan-fractic))**1.6)-boostup*sin((90./initialspan)*(level.maptime+fractic-starttic)); double alph = clamp((lifespan+fractic)/double(initialspan),0.,1.); Screen.DrawText(mSmallFont,Font.CR_GREEN,realpos.x-fo.x,realpos.y-fo.y,"F",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); } } // One-liners Class SWWMOneLiner : HUDMessageBase { String whichline; transient BrokenLines l; int lifespan, curtime; Font mSmallFont; static SWWMOneLiner Make( String whichline, int lifespan ) { let l = new('SWWMOneLiner'); if ( StringTable.Localize(whichline) == "" ) l.whichline = ""; else l.whichline = StringTable.Localize("$SWWM_LQUOTE")..StringTable.Localize(whichline)..StringTable.Localize("$SWWM_RQUOTE"); l.curtime = l.lifespan = lifespan; l.mSmallFont = Font.GetFont('TewiFont'); return l; } override bool Tick() { if ( players[consoleplayer].Health <= 0 ) curtime = int.min; curtime--; return (curtime<-20); } override void ScreenSizeChanged() { if ( l ) l.Destroy(); } override void Draw( int bottom, int visibility ) { int ymargin; double hs; Vector2 ss; if ( SWWMStatusBar(StatusBar) ) { ymargin = SWWMStatusBar(StatusBar).ymargin0; hs = SWWMStatusBar(StatusBar).hs0; ss = SWWMStatusBar(StatusBar).ss0; } else { ymargin = clamp(swwm_hudmargin,0,10); hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); } ymargin += 10; if ( whichline == "" ) return; // don't draw empty strings // split so it can fit if ( !l ) l = mSmallFont.BreakLines(whichline,int(min(ss.x,ss.y/.5625)*.5)); int maxlen = 0; for ( int i=0; i maxlen ) maxlen = len; } int h = mSmallFont.GetHeight(); int fh = h*l.Count(); double fractic = System.GetTimeFrac(); double fcurtime = curtime-fractic; double alph = clamp((fcurtime/20.)+1.,0.,1.); alph *= clamp((lifespan-fcurtime)/10.,0.,1.); Screen.Dim(0xFF000000,alph*.8,int((Screen.GetWidth()-(maxlen+12)*hs)/2.),int(bottom-(ymargin+2+fh)*hs),int((maxlen+12)*hs),int((fh+4)*hs)); int yy = ymargin+fh; for ( int i=0; i 0 ) alpha -= 1./duration; return (alpha<=0)||(!cam); } override void Draw( int bottom, int visibility ) { if ( (automapactive && !viewactive) || (visibility != BaseStatusBar.HUDMSGLayer_UnderHUD) ) return; if ( cam && (players[consoleplayer].camera != cam) ) return; double fractic = System.GetTimeFrac(); double falpha = alpha-fractic*(1./duration); Screen.Dim(col,(col.a/255.)*falpha*swwm_flashstrength,0,0,Screen.GetWidth(),Screen.GetHeight(),STYLE_Add); } } // Achievement notification Class SWWMAchievementNotification : HUDMessageBase { String tag, txt; transient BrokenLines l; TextureID icon, frame; double tics, holdtics, fadeintics, fadeouttics; Font mSmallFont, mTinyFont; SWWMAchievementNotification Init( String bname, TextureID icon, int num = 0 ) { tag = StringTable.Localize("$SWWM_ACHIEVEMENT_"..bname.."_TAG"); txt = StringTable.Localize("$SWWM_ACHIEVEMENT_"..bname.."_TXT"); if ( num ) txt = String.Format(txt,SWWMUtility.ThousandsNum(num)); self.icon = icon; frame = TexMan.CheckForTexture("graphics/HUD/AchievementNotification.png"); holdtics = 150; fadeintics = 20; fadeouttics = 30; tics = 0; mSmallFont = Font.GetFont('TewiFont'); mTinyFont = Font.GetFont('MiniwiFont'); return self; } override bool Tick() { return (++tics > holdtics+fadeintics+fadeouttics); } override void Draw( int bottom, int visibility ) { double ymargin; double hs; Vector2 ss; if ( SWWMStatusBar(StatusBar) ) { ymargin = SWWMStatusBar(StatusBar).ymargin; hs = SWWMStatusBar(StatusBar).hs; ss = SWWMStatusBar(StatusBar).ss; } else { ymargin = clamp(swwm_hudmargin,0,10); hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); } double fractic = System.GetTimeFrac(); double ftics = tics+fractic; double alpha = (ftics1) ) { wpna = swa.SisterWeapon.GetTag(); icoa = GetIcon(swa.SisterWeapon); duala = true; } else { wpna = swa.GetTag(); icoa = GetIcon(swa); duala = false; } wb = toweapon; // are we swapping to a dual weapon? if ( wb is 'SWWMDualWeaponGiver' ) { swb = SWWMDualWeaponGiver(wb).giveme[0]; wpnb = wb.GetTag(); icob = GetIcon(swb); dualb = true; } else if ( wb is 'SWWMWeapon' ) { swb = SWWMWeapon(wb); wpnb = swb.GetTag(); icob = GetIcon(swb); dualb = false; } else ThrowAbortException("swap to (%s) is not SWWMWeapon or SWWMDualWeaponGiver",wb?wb.GetClassName():'Null'); txt = String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),wpna,wpnb); if ( l ) l.Destroy(); } private TextureID GetIcon( Weapon w ) { let [ico, applyScale] = StatusBar.GetInventoryIcon(w,StatusBar.DI_SKIPALTICON|StatusBar.DI_SKIPREADY); return ico; } SWWMWeaponSwapTip Init( Inventory fromweapon, Inventory toweapon ) { UpdateMe(fromweapon,toweapon); holdtics = 50; fadeintics = 5; fadeouttics = 15; tics = 0; mSmallFont = Font.GetFont('TewiFont'); return self; } // used by weapons when an existing tip is already there void Poke( Inventory fromweapon, Inventory toweapon ) { // update icons and text if different weapons if ( (fromweapon != wa) || (toweapon != wb) ) UpdateMe(fromweapon,toweapon); // invert the fade out into a fade-in // otherwise just wind the tics back to right after fade-in if ( tics > (fadeintics+holdtics) ) tics = fadeintics-(tics-(fadeintics+holdtics))/(fadeouttics/fadeintics); else if ( tics > fadeintics ) tics = fadeintics; } // used by weapons when the swap has already happened void Expire( Inventory fromweapon, Inventory toweapon ) { // just forces a fade out if ( (fromweapon == wa) && (toweapon == wb) ) tics = max(tics,fadeintics+holdtics); } override bool Tick() { tics++; return (tics > holdtics+fadeintics+fadeouttics); } override void Draw( int bottom, int visibility ) { if ( tics <= 0 ) return; double ymargin; double hs; Vector2 ss; if ( SWWMStatusBar(StatusBar) ) { ymargin = SWWMStatusBar(StatusBar).ymargin0; hs = SWWMStatusBar(StatusBar).hs0; ss = SWWMStatusBar(StatusBar).ss0; } else { ymargin = clamp(swwm_hudmargin,0,10); hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); } double fractic = System.GetTimeFrac(); double ftics = tics+fractic; double alpha = (ftics txtw ) txtw = lw; } int icow = 16; int icoh = mSmallFont.GetHeight()*2; if ( icoa.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(icoa)*.125; icow += int(sz.x)+(duala?12:4); icoh = int(sz.y)+(duala?12:4); } else icow += mSmallFont.StringWidth(wpna); if ( icob.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(icob)*.125; icow += int(sz.x)+(dualb?12:4); icoh = max(icoh,int(sz.y)+(dualb?12:4)); } else { icow += mSmallFont.StringWidth(wpnb); icoh = max(icoh,mSmallFont.GetHeight()); } int w = max(txtw,icow); int h = icoh+4+mSmallFont.GetHeight()*l.Count(); Vector2 pos = (int(ss.x/2),ss.y-(ymargin+80+h)); Screen.Dim(0xFF000000,.5*alpha,int((pos.x-(w+4)/2)*hs),int((pos.y-2)*hs),int((w+4)*hs),int((h+4)*hs)); // Left Icon double x = pos.x-(icow/2); if ( icoa.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(icoa)*.125; if ( duala ) { x += sz.x/2+6; Screen.DrawTexture(icoa,false,(x-4)*hs,((pos.y+icoh/2)-4)*hs,DTA_ScaleX,.125*hs,DTA_ScaleY,.125*hs,DTA_Alpha,alpha,DTA_CenterOffset,true); Screen.DrawTexture(icoa,false,(x+4)*hs,((pos.y+icoh/2)+4)*hs,DTA_ScaleX,.125*hs,DTA_ScaleY,.125*hs,DTA_Alpha,alpha,DTA_CenterOffset,true); x += sz.x/2+6; } else { x += sz.x/2+2; Screen.DrawTexture(icoa,false,x*hs,(pos.y+icoh/2)*hs,DTA_ScaleX,.125*hs,DTA_ScaleY,.125*hs,DTA_Alpha,alpha,DTA_CenterOffset,true); x += sz.x/2+2; } } // Swap Arrow String arr = "→"; Screen.DrawText(mSmallFont,Font.CR_GREEN,x+(16-mSmallFont.StringWidth(arr))/2,pos.y+(icoh/2-mSmallFont.GetHeight()),arr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alpha); arr = "←"; Screen.DrawText(mSmallFont,Font.CR_GREEN,x+(16-mSmallFont.StringWidth(arr))/2,pos.y+(icoh/2),arr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alpha); x += 16; // Right Icon if ( icob.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(icob)*.125; if ( dualb ) { x += sz.x/2+6; Screen.DrawTexture(icob,false,(x-4)*hs,((pos.y+icoh/2)-4)*hs,DTA_ScaleX,.125*hs,DTA_ScaleY,.125*hs,DTA_Alpha,alpha,DTA_CenterOffset,true); Screen.DrawTexture(icob,false,(x+4)*hs,((pos.y+icoh/2)+4)*hs,DTA_ScaleX,.125*hs,DTA_ScaleY,.125*hs,DTA_Alpha,alpha,DTA_CenterOffset,true); } else { x += sz.x/2+2; Screen.DrawTexture(icob,false,x*hs,(pos.y+icoh/2)*hs,DTA_ScaleX,.125*hs,DTA_ScaleY,.125*hs,DTA_Alpha,alpha,DTA_CenterOffset,true); } } // Swap Message for ( int i=0; i weapon ) { let def = GetDefaultByType(weapon); wpn = def.GetTag(); txt = StringTable.Localize(def.tooltip); holdtics = 150; fadeintics = 5; fadeouttics = 15; tics = -10; mSmallFont = Font.GetFont('TewiFont'); mTinyFont = Font.GetFont('MiniwiFont'); return self; } override bool Tick() { bool clearme = (++tics > holdtics+fadeintics+fadeouttics); if ( clearme && next ) StatusBar.AttachMessage(next,-2910); return clearme; } override void Draw( int bottom, int visibility ) { if ( tics <= 0 ) return; double ymargin; double hs; Vector2 ss; if ( SWWMStatusBar(StatusBar) ) { ymargin = SWWMStatusBar(StatusBar).ymargin0; hs = SWWMStatusBar(StatusBar).hs0; ss = SWWMStatusBar(StatusBar).ss0; } else { ymargin = clamp(swwm_hudmargin,0,10); hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); } double fractic = System.GetTimeFrac(); double ftics = tics+fractic; double alpha = (ftics w ) w = lw; } int cw = int(ceil((max(w1,w)+8)/6.))*6; int h = mSmallFont.GetHeight()+8+mTinyFont.GetHeight()*l.Count(); Vector2 pos = (int(ss.x/2),ss.y-(ymargin+80+h)); Screen.Dim(0xFF000000,.5*alpha,int((pos.x-(cw+4)/2)*hs),int((pos.y-2)*hs),int((cw+4)*hs),int((h+4)*hs)); Screen.DrawText(mSmallFont,Font.CR_FIRE,pos.x-w1/2,pos.y,wpn,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alpha); for ( int i=0; i seqcnt) ) return; // replace "normal" color reset escapes with chat color escapes txt.Replace("\c-","\c*"); // some messages may have newlines in them, split them Array storemsg; txt.Split(storemsg,"\n"); foreach ( msg:storemsg ) Console.PrintfEx(PRINT_CHAT|PRINT_NONOTIFY,chrname.."\c*: "..msg.."\c*"); } private void DrawText() { if ( !l ) SetText(); int cur = charcnt; Vector2 pos = origin+(47,3); for ( int i=0; i 0) && (seqnum < (seqcnt+1)) ) { if ( blinkframe >= 0 ) Screen.DrawTexture(Blink[blinkframe],false,origin.x+2,origin.y+2,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); if ( talkframe >= 0 ) Screen.DrawTexture(Talk[talkframe],false,origin.x+2,origin.y+2,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } Screen.DrawTexture(Noiz[int(((gametic+fractic)/GameTicRate)*15)%4],false,origin.x+2,origin.y+2,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Multiply); } private int TotalLength() { if ( !l ) SetText(); int len = 0; for ( int i=0; i= TotalLength() ) { seqnum++; charcnt = 0; if ( (seqnum > seqcnt) && !nextdirect ) S_StartSound("menu/demochat",CHAN_VOICE,CHANF_UI,1.,0.); else SetText(); return; } String ch = GetChar(charcnt); // skip over color escapes if ( ch == "\c" ) { ch = GetChar(++charcnt); if ( ch == "[" ) while ( (ch = GetChar(++charcnt)) != "]" ); charcnt++; ch = GetChar(charcnt); } // speech if ( notalk.IndexOf(ch) == -1 ) { S_StartSound("misc/voice",CHAN_VOICE,CHANF_UI,.6,ATTN_NONE); talktics = 5; } // delay relative to stuff delay = chardelay; int idx = punctuation.IndexOf(ch); if ( idx >= 0 ) delay += (idx*2)+1; charcnt++; // utf-8 skipping (naive but works, as we don't have to EXPECT invalid sequences in the input) if ( ch.ByteAt(0)&0xE0 == 0xC0 ) charcnt++; else if ( ch.ByteAt(0)&0xF0 == 0xE0 ) charcnt += 2; else if ( ch.ByteAt(0)&0xF8 == 0xF0 ) charcnt += 3; if ( charcnt >= TotalLength() ) delay += (seqnum==seqcnt)?enddelay:pausedelay; } override bool Tick() { // reset when loading from a save, to avoid some wacky nonsense if ( !bDontResetMe ) { bDontResetMe = true; seqnum = -1; delay = 10; fadein = fadeout = 0; } if ( seqnum < 0 ) { // if there's a map message active, wait until it isn't if ( !hnd ) hnd = SWWMHandler(EventHandler.Find('SWWMHandler')); if ( hnd.mapmsg ) return false; delay--; if ( delay <= 0 ) { Console.Printf(StringTable.Localize("$SWWM_INCOMINGMSG"),chrfullname); S_StartSound("menu/demochat",CHAN_VOICE,CHANF_UI,1.,0.); seqnum++; } return false; } if ( blinktics <= 0 ) { blinktics--; if ( blinktics < -3 ) blinktics = (abs(GetRandom())%10)?(60+abs(GetRandom())%30):6; } else blinktics--; if ( talktics > 0 ) { if ( !(gametic%3) ) talkframe = (talkframe==-1)?(abs(GetRandom())%5):-1; talktics--; } else talkframe = -1; if ( seqnum > (seqcnt+1) ) { if ( nextmsg ) StatusBar.AttachMessage(nextmsg,-1232); return true; } if ( seqnum == 0 ) { fadein++; if ( fadein > 15 ) { delay = startdelay; seqnum++; } return false; } if ( seqnum == (seqcnt+1) ) { if ( nextmsg && nextdirect ) { nextmsg.seqnum = 1; nextmsg.bDontResetMe = true; StatusBar.AttachMessage(nextmsg,-1232); return true; } fadeout++; if ( fadeout > 30 ) seqnum++; return false; } if ( delay > 0 ) { delay--; return false; } AdvanceText(); return false; } override void Draw( int bottom, int visibility ) { if ( (seqnum < 0) || (seqnum > (seqcnt+1)) ) return; int ymargin; double alph = 1.; double fractic = System.GetTimeFrac(); if ( seqnum == 0 ) alph = (fadein+fractic)/15.; else if ( seqnum == (seqcnt+1) ) alph = 1.-(fadeout+fractic)/30.; if ( SWWMStatusBar(StatusBar) ) { ymargin = SWWMStatusBar(StatusBar).ymargin; hs = SWWMStatusBar(StatusBar).hs; ss = SWWMStatusBar(StatusBar).ss; } else { ymargin = clamp(swwm_hudmargin,0,10); hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); } origin = (int(ss.x-270)/2,ymargin+70); Screen.DrawTexture(MessageBox,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); if ( (seqnum < 1) || (seqnum > seqcnt) ) return; DrawAvatar(fractic); DrawText(); } } // used to manually trigger dialogues // the AMBUSH flag means the dialogue only shows for the activator Class SWWMDialogueTrigger : SWWMNonInteractiveActor abstract { override void Activate( Actor activator ) { // this is a huge gross hack that only exists because you can't get arg0str out of actors // (primarily, because you can convert names to ints, but not viceversa) String dlg = GetClassName(); dlg.StripLeft('SWWMDialogueTrigger'); if ( !bAMBUSH || (activator && (activator.player == players[consoleplayer])) ) EventHandler.SendInterfaceEvent(consoleplayer,"swwmsetdialogue."..dlg); Destroy(); } override void Tick() {} } Class SWWMDialogueTriggerGOTCHAEND : SWWMDialogueTrigger {} Class SWWMDialogueTriggerSpcEV2BCD : SWWMNonInteractiveActor { Actor thecaco; int spcstate; // this one is very special, yup override void PostBeginPlay() { // the one singular astral cacodemon in this map // needed for everything let cacoclass = (Class)(FindClass('AstralCacodemon','Actor')); if ( cacoclass ) thecaco = Actor(ThinkerIterator.Create(cacoclass).Next()); if ( !thecaco ) { Destroy(); return; } } override void Tick() { if ( !thecaco || (thecaco.Health <= 0) ) { EventHandler.SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2D"); Destroy(); return; } double dist; switch ( spcstate ) { case 0: // check that the player is close to the astral cacodemon dist = (thecaco.pos.xy-players[consoleplayer].mo.pos.xy).length(); if ( dist < 1024 ) { spcstate++; EventHandler.SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2B"); } break; case 1: // wait until the astral caco leaves its spawn position dist = (thecaco.pos.xy-thecaco.spawnpoint.xy).length(); if ( dist > 64 ) spcstate++; break; case 2: // check that the player is close to where the astral cacodemon originally was dist = (thecaco.spawnpoint.xy-players[consoleplayer].mo.pos.xy).length(); if ( dist < 1024 ) { spcstate++; EventHandler.SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2C"); } break; } } } Class SWWMDialogueTriggerEV2J : SWWMDialogueTrigger {} // I'm just doing this as an experiment, don't mind me Class DSMapTitle : HUDMessageBase { Struct FntGlyph { int x, y, width, height, xofs, yofs, advance; }; TextureID atlas, atlas_sub; FntGlyph glyphs[256], glyphs_sub[256]; int fheight, fheight_sub; String txt, txtsub; bool hasnewline; int tics, holdtics, fadeintics, fadeouttics; int ultics; transient bool bDontDeleteMe; // safeguard for savegames bool bTxtIsSplit; Array txt_split; Array txtwidth_split; int txtmaxwidth_split; // all the actual hard code int MyStringWidth( String str ) { int w = 0; int lw = 0; int len = str.CodePointCount(); for ( int i=0, pos=0; i 255 ) continue; lw += glyphs[ch].advance; // newline, restart line width if ( ch == 0x0a ) { w = max(w,lw); lw = 0; } } w = max(w,lw); return w; } void MyStringBreak( String str, int maxw ) { bTxtIsSplit = true; txt_split.Clear(); txtwidth_split.Clear(); txtmaxwidth_split = 0; int cur = 0; int w = 0; int len = str.CodePointCount(); bool wasspace = false; bool leftspace = false; int lastspace = 0, pastspace = 0; for ( int i=0, pos=0; i255)?0:glyphs[ch].advance; bool endof = ((i+1)>=len); if ( (ch != 0x0A) && !endof && ((w <= 0) || (w+cw <= maxw)) ) { w += cw; continue; } String sstr = str.Mid(cur,endof?int.max:(lastspace-cur)); int lw = MyStringWidth(sstr); txt_split.Push(sstr); txtwidth_split.Push(lw); if ( lw > txtmaxwidth_split ) txtmaxwidth_split = lw; w = 0; wasspace = false; cur = pastspace; lastspace = 0; pastspace = 0; leftspace = true; } } int MySubStringWidth( String str ) { int w = 0; int lw = 0; int len = str.CodePointCount(); for ( int i=0, pos=0; i 255 ) continue; lw += glyphs_sub[ch].advance; // newline, restart line width if ( ch == 0x0a ) { w = max(w,lw); lw = 0; } } w = max(w,lw); return w; } void MyDrawText( String str, double x, double y, Vector2 ss, double alpha = 1. ) { int len = str.CodePointCount(); double xx = x; double yy = y; for ( int i=0, pos=0; i 255 ) continue; if ( glyphs[ch].width && glyphs[ch].height ) { Screen.DrawTexture(atlas,false,xx-glyphs[ch].xofs,yy-glyphs[ch].yofs, DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true, DTA_SrcX,glyphs[ch].x,DTA_SrcY,glyphs[ch].y, DTA_SrcWidth,glyphs[ch].width,DTA_SrcHeight,glyphs[ch].height, DTA_DestWidth,glyphs[ch].width,DTA_DestHeight,glyphs[ch].height, DTA_Alpha,alpha); } xx += glyphs[ch].advance; // newline if ( ch == 0x0a ) { xx = x; yy += fheight; } } } void MySubDrawText( String str, double x, double y, Vector2 ss, double alpha = 1. ) { int len = str.CodePointCount(); double xx = x; double yy = y; for ( int i=0, pos=0; i 255 ) continue; if ( glyphs_sub[ch].width && glyphs_sub[ch].height ) { Screen.DrawTexture(atlas_sub,false,xx-glyphs_sub[ch].xofs,yy-glyphs_sub[ch].yofs, DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true, DTA_SrcX,glyphs_sub[ch].x,DTA_SrcY,glyphs_sub[ch].y, DTA_SrcWidth,glyphs_sub[ch].width,DTA_SrcHeight,glyphs_sub[ch].height, DTA_DestWidth,glyphs_sub[ch].width,DTA_DestHeight,glyphs_sub[ch].height, DTA_Alpha,alpha); } xx += glyphs_sub[ch].advance; // newline if ( ch == 0x0a ) { xx = x; yy += fheight_sub; } } } // stretches the "_" glyph over a specific width, center-aligned void DrawUnderline( double x, double y, Vector2 ss, double width, double alpha = 1. ) { double xx = x-width/2; // l stem Screen.DrawTexture(atlas,false,xx-4,y, DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true, DTA_SrcX,glyphs[95].x,DTA_SrcY,glyphs[95].y, DTA_SrcWidth,4,DTA_SrcHeight,glyphs[95].height, DTA_DestWidthF,4,DTA_DestHeight,glyphs[95].height, DTA_Alpha,alpha); // long Screen.DrawTexture(atlas,false,xx,y, DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true, DTA_SrcX,glyphs[95].x+4,DTA_SrcY,glyphs[95].y, DTA_SrcWidth,glyphs[95].width-8,DTA_SrcHeight,glyphs[95].height, DTA_DestWidthF,width,DTA_DestHeight,glyphs[95].height, DTA_Alpha,alpha); // r stem Screen.DrawTexture(atlas,false,xx+width,y, DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true, DTA_SrcX,glyphs[95].x+(glyphs[95].width-4),DTA_SrcY,glyphs[95].y, DTA_SrcWidth,4,DTA_SrcHeight,glyphs[95].height, DTA_DestWidthF,4,DTA_DestHeight,glyphs[95].height, DTA_Alpha,alpha); } DSMapTitle Init() { txt = level.levelname; txtsub = level.authorname; if ( txt.Left(1) == "$" ) txt = StringTable.Localize(txt); if ( txtsub.Left(1) == "$" ) txtsub = StringTable.Localize(txtsub); // may contain color escapes, which we don't support here SWWMUtility.StripColor(txt); SWWMUtility.StripColor(txtsub); // level name may contain trailing whitespace due to DEHACKED nonsense, so strip it txt.StripRight(); if ( txtsub == "" ) // if we already have an author name, don't do this stuff { int sep; if ( (sep = txt.RightIndexOf(" - by: ")) != -1 ) // 20 heretics, spooktober { txtsub = txt.Mid(sep+7); txt.Truncate(sep); } else if ( (sep = txt.RightIndexOf(" - by ")) != -1 ) // variation seen in DOOMIUM { txtsub = txt.Mid(sep+6); txt.Truncate(sep); } else if ( (sep = txt.RightIndexOf(" - ")) != -1 ) // hexmas and many others (may cause false positives?) { txtsub = txt.Mid(sep+3); txt.Truncate(sep); } } hasnewline = (txt.IndexOf("\n") != -1); bTxtIsSplit = false; tics = -10; holdtics = 140; fadeintics = 20; fadeouttics = 40; ultics = 50; atlas = TexMan.CheckForTexture("graphics/dsmapfont.png"); if ( !atlas.IsValid() || atlas.IsNull() ) ThrowAbortException("font atlas texture not found"); let lmp = Wads.CheckNumForFullname("graphics/dsmapfont.txt"); if ( lmp == -1 ) ThrowAbortException("font definition file not found"); String dat = Wads.ReadLump(lmp); Array list, ln; // Windows pls dat.Replace("\r",""); list.Clear(); dat.Split(list,"\n"); int ch = 0; for ( int i=0; i holdtics+fadeintics+fadeouttics); } override void Draw( int bottom, int visibility ) { if ( tics <= 0 ) return; double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; double fractic = System.GetTimeFrac(); double ftics = tics+fractic; double alpha = (ftics weaps; int cursel; double alph, olalph; double curY, smoothY, olsmoothY; int stage; Font fnt; private int CmpWeapon( Weapon a, Weapon b ) { let [dummya, slota, idxa] = mo.player.weapons.LocateWeapon(a.GetClass()); if ( slota == 0 ) slota = 10; let [dummyb, slotb, idxb] = mo.player.weapons.LocateWeapon(b.GetClass()); if ( slotb == 0 ) slotb = 10; if ( slota == slotb ) return idxa > idxb; return slota > slotb; } private int partition_weapons( int l, int h ) { Weapon pv = weaps[h]; int i = (l-1); for ( int j=l; j<=(h-1); j++ ) { if ( CmpWeapon(pv,weaps[j]) ) { i++; Weapon tmp = weaps[j]; weaps[j] = weaps[i]; weaps[i] = tmp; } } Weapon tmp = weaps[h]; weaps[h] = weaps[i+1]; weaps[i+1] = tmp; return i+1; } private void qsort_weapons( int l, int h ) { if ( l >= h ) return; int p = partition_weapons(l,h); qsort_weapons(l,p-1); qsort_weapons(p+1,h); } static clearscope bool PlayerHasWeapons( Actor mo ) { for ( Inventory i=mo.inv; i; i=i.inv ) { if ( !(i is 'Weapon') ) continue; let w = Weapon(i); if ( !mo.player.weapons.LocateWeapon(w.GetClass()) ) continue; if ( (w is 'SWWMWeapon') && SWWMWeapon(w).bHIDEINMENU ) continue; return true; } return false; } private TextureID GetIcon( Weapon w ) { let [ico, applyScale] = StatusBar.GetInventoryIcon(w,StatusBar.DI_SKIPALTICON|StatusBar.DI_SKIPREADY); return ico; } private double GetHeight( Weapon w ) { double h = 16.; // padding TextureID ico = GetIcon(w); if ( ico.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(ico); if ( w is 'SWWMWeapon' ) // SWWM weapon icons are larger sz *= .125; h += sz.y; } else h += fnt.GetHeight(); return h; } private double CalcHeight( bool bGetTotal = false ) { double th = 0.; double cur = -1.; for ( int i=0; i 1) && ((mo.player.ReadyWeapon == sw) || ((mo.player.ReadyWeapon != sw.SisterWeapon) && !swwm_singlefirst)) ) bar.ntagstr = sw.SisterWeapon.GetTag(); else bar.ntagstr = weaps[cursel].GetTag(); bar.ntagtic = level.totaltime; bar.ntagcol = nametagcolor; } private bool CanScroll() { double ht = 20.; // extra padding if ( weaps[cursel] ) { TextureID ico = GetIcon(weaps[cursel]); if ( ico.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(ico); if ( weaps[cursel] is 'SWWMWeapon' ) ht += sz.y*.0625; else ht += sz.y*.5; } else ht += fnt.GetHeight()*.5; } return (abs(smoothY-curY) < ht); } private bool WeaponHasAmmo( Weapon w ) { if ( SWWMWeapon(w) ) return SWWMWeapon(w).ReportHUDAmmo(); if ( (!w.Ammo1 || (w.Ammo1.Amount > 0) || w.bAMMO_OPTIONAL) || (w.Ammo2 && ((w.Ammo2.Amount > 0) || w.bALT_AMMO_OPTIONAL)) ) return true; return false; } void WeapPrev() { if ( (weaps.Size() <= 1) || !CanScroll() ) return; for ( int i=0; i= weaps.Size() ) { double fh = CalcHeight(true); smoothY -= fh; olsmoothY -= fh; cursel = 0; } if ( WeaponHasAmmo(weaps[cursel]) ) break; } curY = CalcHeight(); S_StartSound("menu/demoscroll",CHAN_AUTO,CHANF_UI); if ( !weaps[cursel] || !(displaynametags&2) ) return; ShowNameTag(); } void WeapSel() { if ( !CanScroll() ) return; stage = 2; if ( !weaps[cursel] ) S_StartSound("menu/democlose",CHAN_AUTO,CHANF_UI|CHANF_OVERLAP); else EventHandler.SendNetworkEvent(String.Format("swwmselweapon.%s",weaps[cursel].GetClassName()),mo.PlayerNumber()); } void WeapCancel() { if ( !CanScroll() ) return; stage = 2; S_StartSound("menu/democlose",CHAN_AUTO,CHANF_UI|CHANF_OVERLAP); } override bool Tick() { // automatically close if disabled or player changes weapons through other means if ( (!swwm_useweaponbar || (mo.player.PendingWeapon != WP_NOCHANGE)) && (stage != 2) ) { stage = 2; S_StartSound("menu/clear",CHAN_AUTO,CHANF_UI|CHANF_OVERLAP); } int oldsiz = weaps.Size(); bool bChanged = false; Weapon oldsel = (weaps.Size()<=0)?null:weaps[cursel]; for ( int i=0; i i ) cursel--; i--; } for ( Inventory i=mo.inv; i; i=i.inv ) { if ( !(i is 'Weapon') ) continue; let w = Weapon(i); if ( !mo.player.weapons.LocateWeapon(w.GetClass()) ) continue; if ( (w is 'SWWMWeapon') && SWWMWeapon(w).bHIDEINMENU ) continue; if ( weaps.Find(w) < weaps.Size() ) continue; weaps.Push(w); bChanged = true; } if ( cursel >= weaps.Size() ) cursel = max(0,weaps.Size()-1); if ( weaps.Size() <= 0 ) stage = 2; // force close if no weapons available else if ( bChanged ) { // re-sort and reposition qsort_weapons(0,weaps.Size()-1); let idx = weaps.Find(oldsel); if ( idx < weaps.Size() ) cursel = idx; curY = CalcHeight(); } olalph = alph; switch ( stage ) { case 0: // fade in alph += 4./GameTicRate; if ( alph >= 1. ) { alph = 1.; stage = 1; return false; } break; case 1: // do nothing break; case 2: // fade out alph -= 6./GameTicRate; if ( alph <= 0. ) return true; break; } // smooth scroll olsmoothY = smoothY; smoothY = smoothY*.8+curY*.2; double hs; if ( SWWMStatusBar(StatusBar) ) hs = SWWMStatusBar(StatusBar).hs0; else hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); if ( abs(smoothY-curY) < (1./hs) ) smoothY = curY; return false; } // simplify some code here private bool DrawWeapon( Weapon w, int i, double x, double &y, double hs, Vector2 hss, double salph, double fractic, bool bOverflow = false, bool bUpward = false ) { let sw = SWWMWeapon(w); double scl = sw?.125:1.; y += bUpward?-8.:8.; bool bHasAmmo = WeaponHasAmmo(w); TextureID ico = GetIcon(w); double fade = 0.; if ( ico.IsValid() ) { Vector2 sz = TexMan.GetScaledSize(ico); y += sz.y*scl*(bUpward?-.5:.5); fade = clamp(.8-abs(y-hss.y)/hss.y,0.,.8)/.8; if ( sw && sw.SisterWeapon && (sw.Amount > 1) && ((mo.player.ReadyWeapon == sw) || ((mo.player.ReadyWeapon != sw.SisterWeapon) && !swwm_singlefirst)) ) { // double draw Screen.DrawTexture(ico,false,x-(4.*hs),(y-4.)*hs,DTA_ScaleX,scl*hs,DTA_ScaleY,scl*hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true,DTA_Color,bHasAmmo?0xFFFFFFFF:0xFF800000,DTA_ColorOverlay,((i==cursel)&&!bOverflow)?0x00000000:0x80000000); Screen.DrawTexture(ico,false,x+(4.*hs),(y+4.)*hs,DTA_ScaleX,scl*hs,DTA_ScaleY,scl*hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true,DTA_Color,bHasAmmo?0xFFFFFFFF:0xFF800000,DTA_ColorOverlay,((i==cursel)&&!bOverflow)?0x00000000:0x80000000); } else Screen.DrawTexture(ico,false,x,y*hs,DTA_ScaleX,scl*hs,DTA_ScaleY,scl*hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true,DTA_Color,bHasAmmo?0xFFFFFFFF:0xFF800000,DTA_ColorOverlay,((i==cursel)&&!bOverflow)?0x00000000:0x80000000); if ( (i == cursel) && !bOverflow ) { Screen.DrawChar(fnt,Font.CR_FIRE,x-((sz.x*scl*.5+16.+2.*sin((gametic+fractic)*8.))*hs),y*hs,0x25BA,DTA_ScaleX,hs,DTA_ScaleY,hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true); Screen.DrawChar(fnt,Font.CR_FIRE,x+((sz.x*scl*.5+16.+2.*sin((gametic+fractic)*8.))*hs),y*hs,0x25C4,DTA_ScaleX,hs,DTA_ScaleY,hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true); } y += sz.y*scl*(bUpward?-.5:.5); } else { String label = "?WeaponName?"; if ( sw && sw.SisterWeapon && (sw.Amount > 1) && ((mo.player.ReadyWeapon == sw) || ((mo.player.ReadyWeapon != sw.SisterWeapon) && !swwm_singlefirst)) ) label = sw.SisterWeapon.GetTag(); else label = w.GetTag(); Vector2 sz = (fnt.StringWidth(label),fnt.GetHeight()); fade = clamp(.8-abs((bUpward?(y-sz.y*.5):y+sz.y*.5)-hss.y)/hss.y,0.,.8)/.8; Screen.DrawText(fnt,bHasAmmo?Font.CR_WHITE:Font.CR_RED,x-sz.x*.5*hs,y*hs,label,DTA_ScaleX,hs,DTA_ScaleY,hs,DTA_Alpha,salph*fade,DTA_ColorOverlay,((i==cursel)&&!bOverflow)?0x00000000:0x80000000); y += sz.y*(bUpward?-.5:.5); if ( (i == cursel) && !bOverflow ) { Screen.DrawChar(fnt,Font.CR_FIRE,x-((sz.x*.5+16.+2.*sin((gametic+fractic)*8.))*hs),y*hs,0x25BA,DTA_ScaleX,hs,DTA_ScaleY,hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true); Screen.DrawChar(fnt,Font.CR_FIRE,x+((sz.x*.5+16.+2.*sin((gametic+fractic)*8.))*hs),y*hs,0x25C4,DTA_ScaleX,hs,DTA_ScaleY,hs,DTA_Alpha,salph*fade,DTA_CenterOffset,true); } y += sz.y*(bUpward?-.5:.5); } y += bUpward?-8.:8.; return (fade <= 0.); } override void Draw( int bottom, int visibility ) { double fractic = System.GetTimeFrac(); double ssmoothY = SWWMUtility.Lerp(olsmoothY,smoothY,fractic); double salph = SWWMUtility.Lerp(olalph,alph,fractic); Screen.Dim(0xFF000000,.4*salph,0,0,Screen.GetWidth(),Screen.GetHeight()); double hs; Vector2 ss; if ( SWWMStatusBar(StatusBar) ) { hs = SWWMStatusBar(StatusBar).hs0; ss = SWWMStatusBar(StatusBar).ss0; } else { hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); } Vector2 hss = ss*.5; double x = Screen.GetWidth()*.5; double y = hss.y-ssmoothY; int i; for ( i=0; i= weaps.Size() ) i = 0; } // reposition and draw upwards until alpha is zero y = hss.y-ssmoothY; i = weaps.Size()-1; while ( !DrawWeapon(weaps[i%weaps.Size()],i%weaps.Size(),x,y,hs,hss,salph,fractic,true,true) ) { if ( --i < 0 ) i = weaps.Size()-1; } } }