diff --git a/language.version b/language.version index 58ae94113..a515a8e3f 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r690 \cu(Thu 15 Dec 13:22:40 CET 2022)\c-"; -SWWM_SHORTVER="\cw1.3pre r690 \cu(2022-12-15 13:22:40)\c-"; +SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r691 \cu(Thu 15 Dec 13:59:44 CET 2022)\c-"; +SWWM_SHORTVER="\cw1.3pre r691 \cu(2022-12-15 13:59:44)\c-"; diff --git a/zscript.txt b/zscript.txt index 6f7c6480a..c7459684d 100644 --- a/zscript.txt +++ b/zscript.txt @@ -57,6 +57,12 @@ version "4.10" // hud #include "zscript/hud/swwm_dialogue.zsc" #include "zscript/hud/swwm_hud.zsc" +#include "zscript/hud/swwm_hud_target.zsc" +#include "zscript/hud/swwm_hud_topstuff.zsc" +#include "zscript/hud/swwm_hud_inventory.zsc" +#include "zscript/hud/swwm_hud_status.zsc" +#include "zscript/hud/swwm_hud_weapons.zsc" +#include "zscript/hud/swwm_hud_messages.zsc" #include "zscript/hud/swwm_hudextra.zsc" #include "zscript/hud/swwm_hudobjects.zsc" // kbase diff --git a/zscript/hud/swwm_hud.zsc b/zscript/hud/swwm_hud.zsc index 6e98f3209..b1a8a161b 100644 --- a/zscript/hud/swwm_hud.zsc +++ b/zscript/hud/swwm_hud.zsc @@ -192,461 +192,38 @@ Class SWWMStatusBar : BaseStatusBar transient CVar tagcolor; // for mugshot - override void FlushNotify() + // called by static handler when loading a game + // forcibly flushes our interpolators + void Flush() { - // flush interpolators (useful since this virtual gets called - // when loading saves, too) - ScoreInter.Reset(SWWMCredits.Get(CPlayer)); - int hp = CPlayer.Health; - HealthInter.Reset(hp); - for ( int i=9; i>0; i-- ) - LagHealth[i] = hp; - LagHealthInter.Reset(hp); - let d = Demolitionist(CPlayer.mo); - if ( d ) - { - FuelInter.Reset(d.dashfuel/2); - DashInter.Reset((40-d.dashcooldown)*3); - } - else - { - FuelInter.Reset(0); - DashInter.Reset(0); - } - if ( level.maptime <= 1 ) - { - // flush ALL messages - MainQueue.Clear(); - PickupQueue.Clear(); - return; - } - // flush non-chat messages - for ( int i=0; i= PRINT_CHAT ) continue; - MainQueue.Delete(i); - i--; - } - // flush nametag - ntagstr = ""; - ntagtic = 0; - } - - override bool ProcessMidPrint( Font fnt, String msg, bool bold ) - { - // check for Korax lines, add them to chat (and reply to some of them) - bool koraxline = false; - if ( msg == StringTable.Localize("$TXT_ACS_MAP02_9_GREET") ) - koraxline = true; - else if ( msg == StringTable.Localize("$TXT_ACS_MAP02_11_AREYO") ) - { - EventHandler.SendNetworkEvent("swwmkoraxline",0,consoleplayer); - koraxline = true; - SWWMDialogues.StartSeq(SWWMDLG_GREET); - } - else if ( msg == StringTable.Localize("$TXT_ACS_MAP13_11_MYSER") ) - { - EventHandler.SendNetworkEvent("swwmkoraxline",1,consoleplayer); - koraxline = true; - SWWMDialogues.StartSeq(SWWMDLG_BLOOD); - } - else if ( msg == StringTable.Localize("$TXT_ACS_MAP22_27_YOUHA") ) - koraxline = true; - else if ( msg == StringTable.Localize("$TXT_ACS_MAP22_29_ITHIN") ) - { - EventHandler.SendNetworkEvent("swwmkoraxline",2,consoleplayer); - koraxline = true; - SWWMDialogues.StartSeq(SWWMDLG_GAME); - } - else if ( msg == StringTable.Localize("$TXT_ACS_MAP27_8_WORSH") ) - koraxline = true; - else if ( msg == StringTable.Localize("$TXT_ACS_MAP27_10_THENA") ) - { - EventHandler.SendNetworkEvent("swwmkoraxline",3,consoleplayer); - koraxline = true; - SWWMDialogues.StartSeq(SWWMDLG_WORSHIP); - } - else if ( msg == StringTable.Localize("$TXT_ACS_MAP35_12_AREYO") ) - koraxline = true; - else if ( msg == StringTable.Localize("$TXT_ACS_MAP35_14_TOFAC") ) - { - EventHandler.SendNetworkEvent("swwmkoraxline",4,consoleplayer); - koraxline = true; - SWWMDialogues.StartSeq(SWWMDLG_MASTERS); - } - if ( koraxline ) - { - Console.PrintfEx(PRINT_CHAT,"\cmKorax\c-: "..msg); - return true; - } - bool ispuzzle = false; - let s = SWWMStats.Find(players[consoleplayer]); - if ( s ) - { - puzzlecnt = s.puzzlecnt; - realpuzzlecnt = s.realpuzzlecnt; - } - // check for puzzle solving lines (oh god why), and increment the achievement - if ( ((level.mapname ~== "MAP04") || (level.mapname ~== "MAP05")) - && ((msg == StringTable.Localize("$TXT_ACS_MAP04_9_ONEHA")) - || (msg == StringTable.Localize("$TXT_ACS_MAP04_11_ONETH")) - || (msg == StringTable.Localize("$TXT_ACS_MAP05_6_ONETH"))) ) - { - if ( puzzlecnt >= 4 ) puzzlecnt = 0; - puzzlecnt++; - realpuzzlecnt++; - ispuzzle = true; - } - else if ( ((level.mapname ~== "MAP08") || (level.mapname ~== "MAP09") || (level.mapname ~== "MAP10")) - && ((msg == StringTable.Localize("$TXT_ACS_MAP08_6_ONESI")) - || (msg == StringTable.Localize("$TXT_ACS_MAP09_6_ONESI")) - || (msg == StringTable.Localize("$TXT_ACS_MAP10_6_ONESI"))) ) - { - if ( (puzzlecnt < 4) || (puzzlecnt >= 10) ) puzzlecnt = 4; - puzzlecnt++; - realpuzzlecnt++; - ispuzzle = true; - } - else if ( ((level.mapname ~== "MAP28") || (level.mapname ~== "MAP30") || (level.mapname ~== "MAP34")) - && ((msg == StringTable.Localize("$TXT_ACS_MAP28_6_ONENI")) - || (msg == StringTable.Localize("$TXT_ACS_MAP30_6_ONENI")) - || (msg == StringTable.Localize("$TXT_ACS_MAP34_1_ONENI"))) ) - { - if ( (puzzlecnt < 10) || (puzzlecnt >= 19) ) puzzlecnt = 10; - puzzlecnt++; - realpuzzlecnt++; - ispuzzle = true; - } - // deathkings - else if ( ((level.mapname ~== "MAP44") || (level.mapname ~== "MAP46")) - && ((msg == StringTable.Localize("$TXT_ACS_MAP44_1_THREE")) - || (msg == StringTable.Localize("$TXT_ACS_MAP44_2_TWOMO")) - || (msg == StringTable.Localize("$TXT_ACS_MAP44_3_ONEMO")) - || (msg == StringTable.Localize("$TXT_ACS_MAP44_4_THEPU")) - || (msg == StringTable.Localize("$TXT_ACS_MAP44_10_ONETH")) - || (msg == StringTable.Localize("$TXT_ACS_MAP44_11_TWOTH")) - || (msg == StringTable.Localize("$TXT_ACS_MAP46_8_ONEFO"))) ) - { - if ( (puzzlecnt < 19) || (puzzlecnt >= 30) ) puzzlecnt = 19; - puzzlecnt++; - realpuzzlecnt++; - ispuzzle = true; - } - else if ( (level.mapname ~== "MAP51") - && ((msg == StringTable.Localize("$TXT_ACS_MAP51_8_ONETH")) - || (msg == StringTable.Localize("$TXT_ACS_MAP51_9_TWOTH")) - || (msg == StringTable.Localize("$TXT_ACS_MAP51_10_THECR"))) ) - { - if ( (puzzlecnt < 30) || (puzzlecnt >= 34) ) puzzlecnt = 30; - puzzlecnt++; - realpuzzlecnt++; - ispuzzle = true; - } - if ( ispuzzle ) - { - EventHandler.SendNetworkEvent("swwmstorepuzzlecnt",consoleplayer,puzzlecnt,realpuzzlecnt); - int tpuz = SWWMUtility.IsDeathkings()?15:19; - if ( realpuzzlecnt >= tpuz ) SWWMUtility.MarkAchievement("puzzle",players[consoleplayer]); - if ( !swwm_nomapmsg ) - { - switch ( puzzlecnt ) - { - case 1: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE1); - break; - case 2: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE2); - break; - case 3: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE3); - break; - case 5: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE4); - break; - case 8: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE5); - break; - case 11: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE6); - break; - case 20: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE7); - break; - case 30: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE8); - break; - case 31: - SWWMDialogues.StartSeq(SWWMDLG_PUZZLE9); - break; - } - } - } - // rampancy fun stuff - bool mainframeline = false; - if ( (msg == StringTable.Localize("$AISPAWN_TEXT1")) - || (msg == StringTable.Localize("$AISPAWN_TEXT2")) - || (msg == StringTable.Localize("$AISEE_TEXT")) - || (msg == StringTable.Localize("$AIACTIVE_TEXT1")) - || (msg == StringTable.Localize("$AIACTIVE_TEXT2")) - || (msg == StringTable.Localize("$AIPAIN_TEXT1")) - || (msg == StringTable.Localize("$AIPAIN_TEXT2")) - || (msg == StringTable.Localize("$AIPAIN_TEXT3")) - || (msg == StringTable.Localize("$AIPAIN_TEXT4")) - || (msg == StringTable.Localize("$AIPAIN_TEXT5")) - || (msg == StringTable.Localize("$AIPAIN_TEXT6")) - || (msg == StringTable.Localize("$AIPAIN_TEXT7")) ) - mainframeline = true; - if ( mainframeline ) - { - Console.PrintfEx(PRINT_CHAT,"\cmAI Mainframe\c-: "..msg); - return true; - } - if ( !fnt || (fnt == smallfont) ) - { - midstr = msg; - midtic = level.totaltime; - midtype = bold?2:0; - return true; - } - if ( (fnt == bigfont) || (fnt == originalbigfont) ) - { - midstr = msg; - midtic = level.totaltime; - midtype = bold?3:1; - return true; - } - return false; - } - - override bool ProcessNotify( EPrintLevel printlevel, String outline ) - { - // ignore outside of levels - if ( gamestate != GS_LEVEL ) return false; - int rprintlevel = printlevel&PRINT_TYPES; - // treat any unrecognized levels as critical messages - // note that checking for PRINT_LOG isn't needed as those are skipped early in the C++ side - if ( (rprintlevel < PRINT_LOW) || (rprintlevel > PRINT_TEAMCHAT) ) rprintlevel = PRINT_HIGH; - // strip trailing newline (all Printf type messages have this) - outline.DeleteLastCharacter(); - // append chat messages to full history - if ( (rprintlevel == PRINT_CHAT) || (rprintlevel == PRINT_TEAMCHAT) ) - EventHandler.SendNetworkEvent("swwmstoremessage."..outline,level.totaltime,rprintlevel,consoleplayer); - let m = new("MsgLine"); - m.str = outline; - m.type = rprintlevel; - m.tic = level.totaltime; - m.rep = 1; - if ( rprintlevel == PRINT_LOW ) - { - // check if repeated - for ( int i=0; i a, int l, int h ) - { - PlayerInfo pv = a[h]; - int i = (l-1); - for ( int j=l; j<=(h-1); j++ ) - { - if ( pv.fragcount < a[j].fragcount ) - { - i++; - PlayerInfo tmp = a[j]; - a[j] = a[i]; - a[i] = tmp; - } - } - PlayerInfo tmp = a[h]; - a[h] = a[i+1]; - a[i+1] = tmp; - return i+1; - } - private void qsort_playerscore( Array a, int l, int h ) - { - if ( l >= h ) return; - int p = partition_playerscore(a,l,h); - qsort_playerscore(a,l,p-1); - qsort_playerscore(a,p+1,h); + FlushTopStuff(); + FlushStatus(); } // separated so they can be auto-ticked by the demolitionist menu void TickInterpolators() { - ScoreInter.Update(SWWMCredits.Get(CPlayer)); - int hp = CPlayer.Health; - HealthInter.Update(hp); - // flash 'em - if ( hp > LastHealth ) HealthFlash = gametic+25; - // lag - if ( hp > LastHealth ) - { - for ( int i=9; i>0; i-- ) - LagHealth[i] = hp; - } - LagHealth[0] = LastHealth = hp; - LagHealthInter.Update(LagHealth[9]); - for ( int i=9; i>0; i-- ) - LagHealth[i] = LagHealth[i-1]; - // ammo updates - for ( int i=0; i<18; i++ ) - { - let a = SWWMAmmo(CPlayer.mo.FindInventory(AmmoSlots[i])); - int amt = 0; - int maxamt = 0; - if ( a ) - { - amt = a.Amount; - maxamt = a.MaxAmount; - if ( a.MagAmmoType ) - { - let m = MagAmmo(CPlayer.mo.FindInventory(a.MagAmmoType)); - if ( m ) - { - amt *= m.ClipSize; - amt += m.Amount; - maxamt *= m.ClipSize; - } - } - } - else - { - let a = GetDefaultByType(AmmoSlots[i]); - maxamt = a.MaxAmount; - if ( a.MagAmmoType ) - { - let m = GetDefaultByType(a.MagAmmoType); - maxamt *= m.ClipSize; - } - } - if ( (amt > AmmoOldAmounts[i]) && (AmmoOldAmounts[i] != int.min) ) - AmmoFlash[i] = gametic+25; - AmmoOldAmounts[i] = amt; - if ( (maxamt > AmmoOldMaxAmounts[i]) && (AmmoOldMaxAmounts[i] != int.min) ) - AmmoMaxFlash[i] = gametic+25; - AmmoOldMaxAmounts[i] = maxamt; - } + // ensure money updates when using the store + TickTopStuffInterpolators(); + // ensure healthbar updates when buying healing items + TickStatusInterpolators(); + // ensure ammo flashes when buying + TickWeaponInterpolators(); } override void Tick() { Super.Tick(); + if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); pausetime = gametic; SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); - // prune old messages - for ( int i=0; i PRINT_HIGH) && (level.totaltime < (MainQueue[i].tic+GameTicRate*CHATDURATION)) ) continue; - MainQueue.Delete(i); - i--; - } - // update interpolators + // interpolators first TickInterpolators(); - let d = Demolitionist(CPlayer.mo); - if ( d ) - { - FuelInter.Update(d.dashfuel/2); - DashInter.Update((40-d.dashcooldown)*3); - } - else - { - FuelInter.Update(0); - DashInter.Update(0); - } - // stats flashing - if ( level.killed_monsters > oldkills ) - { - oldkills = level.killed_monsters; - killflash = gametic+25; - } - if ( level.found_items > olditems ) - { - olditems = level.found_items; - itemflash = gametic+25; - } - if ( level.found_secrets > oldsecrets ) - { - oldsecrets = level.found_secrets; - secretflash = gametic+25; - } - if ( level.total_monsters > oldtkills ) - { - oldtkills = level.total_monsters; - tkillflash = gametic+25; - } - if ( level.total_items > oldtitems ) - { - oldtitems = level.total_items; - titemflash = gametic+25; - } - if ( level.total_secrets > oldtsecrets ) - { - oldtsecrets = level.total_secrets; - tsecretflash = gametic+25; - } - // purge expired key flashes - for ( int i=0; i= gametic ) continue; - keyflash.Delete(i--); - } - // low health pulsing - if ( (CPlayer.health <= 0) || (CPlayer.health > 25) ) - PulsePhase = 0; - else - { - PulsePhase--; - if ( (PulsePhase < 0) || (PulsePhase > CPlayer.health*2+25) ) - PulsePhase = CPlayer.health*2+25; - } - // let weapons update their own interpolators - for ( Inventory i=CPlayer.mo.inv; i; i=i.inv ) - { - if ( !(i is 'SWWMWeapon') ) continue; - SWWMWeapon(i).HudTick(); - } - double desiredzoom = clamp(swwm_mm_zoom,.5,level.allmap?2.:1.); - if ( (minimapzoom != swwm_mm_zoom) || (oldminimapzoom != swwm_mm_zoom) ) - { - oldminimapzoom = minimapzoom; - double diff = .1*(desiredzoom-minimapzoom); - minimapzoom += diff; - if ( abs(minimapzoom-desiredzoom) <= .01 ) - minimapzoom = desiredzoom; - } - if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + // subcategory tickers + TickMessages(); + TickStatus(); + TickTopStuff(); + TickWeapons(); // part of gross hackery to override nametag display if ( CPlayer.inventorytics > 0 ) { @@ -668,64 +245,6 @@ Class SWWMStatusBar : BaseStatusBar } } lastwep = CPlayer.PendingWeapon; - // deathmatch stuff - if ( !deathmatch ) return; - if ( teamplay ) - { - if ( teamactive.Size() != Teams.Size() ) teamactive.Resize(Teams.Size()); - if ( teamscore.Size() != Teams.Size() ) teamscore.Resize(Teams.Size()); - for ( int i=0; i CPlayer.fragcount ) - rank += 1; - else if ( players[i].fragcount == CPlayer.fragcount ) - tiedscore = true; - if ( players[i].fragcount > highscore ) - highscore = players[i].fragcount; - } - if ( sortplayers.Size() != playercount ) sortplayers.Resize(playercount); - for ( int i=0, j=0; i 1000. ) return String.Format("\cj%d\cc%s",int(meters/1000.),StringTable.Localize("$SWWM_UNIT_KILOMETER")); - return String.Format("\cj%d\cc%s",int(meters),StringTable.Localize("$SWWM_UNIT_METER")); - } - - private void DrawInterest( Vector3 viewvec, out bool projinit ) - { - String tag; - SWWMInterest poi = hnd.intpoints; - if ( !poi ) return; - do - { - // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing - if ( !projinit ) - { - projinit = true; - SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); - Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); - } - Vector3 tdir = level.Vec3Diff(ViewPos,poi.pos); - if ( viewvec dot tdir < 0 ) continue; - Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); - if ( ndc.z >= 1. ) continue; - Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs2; - if ( poi.type == INT_Key ) tag = String.Format("\cf%s\c-",poi.keytag); - else if ( poi.type == INT_Exit ) - { - if ( (poi.trackedline.special == Teleport_EndGame) - || ((poi.trackedline.special == Exit_Secret) && (level.nextsecretmap.Left(6) == "enDSeQ")) - || ((poi.trackedline.special == Exit_Normal) && (level.nextmap.Left(6) == "enDSeQ")) ) - tag = String.Format("\cg%s\c-",StringTable.Localize("$SWWM_EEXIT")); - else if ( poi.trackedline.special == Exit_Secret ) - { - LevelInfo l = LevelInfo.FindLevelInfo(level.nextsecretmap); - if ( l && l.isValid() ) tag = String.Format("\cx%s:\c- %s\c-",StringTable.Localize("$SWWM_SEXIT"),l.LookupLevelName()); - else tag = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_SEXIT")); - } - else if ( (poi.trackedline.special == Exit_Normal) || ((poi.trackedline.special == ACS_Execute) && (poi.trackedline.Args[0] == -Int('E1M8_KNOCKOUT'))) ) - { - LevelInfo l = LevelInfo.FindLevelInfo(level.nextmap); - if ( l && l.isValid() ) tag = String.Format("\cy%s:\c- %s\c-",StringTable.Localize("$SWWM_NEXIT"),l.LookupLevelName()); - else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT")); - } - else if ( poi.trackedline.special == Teleport_NewMap ) - { - LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[0]); - if ( l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName()); - else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT")); - } - else if ( ((poi.trackedline.special == ACS_Execute) || (poi.trackedline.special == ACS_ExecuteAlways)) && (poi.trackedline.Args[0] == -Int('MapFadeOut')) ) - { - LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[2]); - if ( (level.levelnum != 1) && l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName()); - else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT")); - } - } - Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y-mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true); - tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length())); - Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true); - } - while ( poi = poi.next ); - } - - private int GetItemFontColor( SWWMitemSense s ) - { - let col = s.scoreitem?Font.CR_YELLOW:Font.CR_WHITE; - let i = (s.item is 'SWWMRespawnTimer')?s.item.tracer:s.item; - if ( i is 'Weapon' ) col = s.vipitem?Font.FindFontColor('VIPGold'):Font.CR_GOLD; - else if ( i is 'MagAmmo' ) col = s.vipitem?Font.FindFontColor('VIPTan'):Font.CR_TAN; - else if ( (i is 'BackpackItem') || (i is 'HammerspaceEmbiggener') ) col = Font.CR_DARKBROWN; - else if ( i is 'Ammo' ) col = s.vipitem?Font.FindFontColor('VIPBrown'):Font.CR_BROWN; - else if ( (i is 'PowerupGiver') || (i is 'AmmoFabricator') || Inventory(i).bBIGPOWERUP ) col = s.vipitem?Font.FindFontColor('VIPPurple'):Font.CR_PURPLE; - else if ( (i is 'Health') || (i is 'HealthPickup') || (i is 'SWWMHealth') ) col = Font.CR_RED; - else if ( (i is 'Armor') || (i is 'SWWMSpareArmor') ) col = Font.CR_GREEN; - else if ( i is 'PuzzleItem' ) col = Font.CR_LIGHTBLUE; - else if ( i is 'Key' ) col = Font.CR_UNTRANSLATED; - else if ( i is 'SWWMCollectible' ) col = Font.CR_PURPLE; - return col; - } - - private void DrawItemSense( Vector3 viewvec, out bool projinit ) - { - let demo = Demolitionist(CPlayer.mo); - if ( !demo ) return; - SWWMItemSense s = demo.itemsense; - if ( !s ) return; - do - { - if ( !s.item ) continue; - Vector3 tdir = level.Vec3Diff(ViewPos,s.pos); - if ( viewvec dot tdir < 0 ) continue; - Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); - if ( ndc.z >= 1. ) continue; - Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; - int mtime = level.allmap?(GameTicRate*2):GameTicRate; - double alph = clamp(((s.updated+mtime)-(level.maptime+fractic))/double(GameTicRate),0.,1.); - alph *= clamp(1.5-1.5*(tdir.length()/(level.allmap?1200.:800.)),0.,1.); - String tag = s.tag; - Screen.DrawText(mTinyFontOutline,GetItemFontColor(s),vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y-mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length())); - Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - if ( s.item is 'SWWMRespawnTimer' ) - { - tag = String.Format(StringTable.Localize("$SWWM_TRESPAWN"),s.item.special2/GameTicRate); - Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()*2,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - } - } - while ( s = s.next ); - } - - private bool IsLegendary( Actor a ) - { - for ( Inventory i=a.inv; i; i=i.inv ) - { - if ( (i.GetClassName() == "LDLegendaryMonsterToken") && swwm_ldspoil ) return true; - else if ( i.GetClassName() == "LDLegendaryMonsterTransformed" ) return true; - } - return false; - } - - private void DrawTrackers( Vector3 viewvec, out bool projinit ) - { - let cam = players[consoleplayer].camera; - if ( !cti ) cti = ThinkerIterator.Create("SWWMQuickCombatTracker",Thinker.STAT_INVENTORY); - else cti.Reinit(); - SWWMQuickCombatTracker ct; - bool onlymonsters = (swwm_targeter >= 2); - bool onlyfriends = (swwm_targeter >= 3); - int fadedist = swwm_bardist; - while ( ct = SWWMQuickCombatTracker(cti.Next()) ) - { - // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing - if ( !projinit ) - { - projinit = true; - SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); - Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); - } - // ignore unowned (can happen?) - if ( !ct.Owner ) continue; - // ignore if max health is zero (SOMEHOW can happen) - if ( ct.maxhealth <= 0 ) continue; - // ignore player trackers unless voodoo dolls - if ( ct.Owner.player && (ct.Owner.player.mo == ct.Owner) ) continue; - // ignore local player or camera - if ( (ct.Owner == CPlayer.mo) || (ct.Owner == cam) ) continue; - // ignore trackers not of this player - if ( ct.myplayer != CPlayer ) continue; - // ignore non-monsters if filtering monsters - // easy check since they have empty tags - if ( onlymonsters && (ct.mytag == "") ) continue; - // ignore enemies if filtering friends - if ( onlyfriends && (!ct.Owner.IsFriend(CPlayer.mo) || ct.Owner.player) ) continue; - // ignore trackers clearly outside of player view - Vector3 smpos = level.Vec3Offset(SWWMUtility.LerpVector3(ct.Owner.prev,ct.Owner.pos,fractic),(0,0,ct.lvheight)); - Vector3 tdir = level.Vec3Diff(viewpos,smpos); - if ( viewvec dot tdir < 0 ) continue; - // ignore trackers that are too far away - double dist = tdir.length(); - if ( (fadedist > 0) && (dist > fadedist*1.5) ) continue; - Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir); - if ( ndc.z >= 1. ) continue; - Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; - if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue; - double fin = clamp(ct.fadein+fractic,0.,5.)/5.; - double fout = clamp(ct.lifespan-fractic,0.,25.)/25.; - double alph = fin*fout; - if ( fadedist > 0 ) - { - double df = 1.-(clamp((dist-fadedist)/fadedist,0.,.5)*2.); - alph *= df; - } - Vector2 barpos = vpos-(27,7); - if ( swwm_targettags && (ct.mytag != "") ) - { - Font fnt = swwm_bigtags?mSmallFontOutline:mTinyFontOutline; - String tag = ct.mytag; - if ( IsLegendary(ct.Owner) ) - { - if ( StringTable.Localize("$SWWM_LEGPREFIX") == "R" ) tag = tag..StringTable.Localize("$SWWM_LEG"); - else tag = StringTable.Localize("$SWWM_LEG")..tag; - } - if ( ct.Owner.bBOSS || ct.Owner.FindInventory("BossMarker") ) - { - if ( swwm_bigtags ) tag = "\cx★\c- "..tag.." \cx★\c-"; - else tag = "\cx*\c- "..tag.." \cx*\c-"; // miniwi has no stars - } - // voodoo dolls aren't friends - if ( ct.Owner.IsFriend(CPlayer.mo) && !ct.Owner.player ) tag = "\cg♥\c- "..tag.." \cg♥\c-"; - Screen.DrawText(fnt,Font.CR_WHITE,vpos.x-fnt.StringWidth(tag)/2,barpos.y-(fnt.GetHeight()+2),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - } - Screen.DrawTexture(EnemyBTex,false,barpos.x,barpos.y,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - double ht = clamp(ct.intp.GetValue(fractic),0,ct.maxhealth); - double hw = (ht*50.)/ct.maxhealth; - Screen.DrawTexture(EnemyHTex[ct.Owner.bINVULNERABLE?1:0],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); - double ohw = hw; - ht = clamp(ct.intpl.GetValue(fractic),0,ct.maxhealth); - hw = (ht*50.)/ct.maxhealth; - Screen.DrawTexture(EnemyHTex[2],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowLeftF,ohw,DTA_WindowRightF,hw); - if ( ct.cummdamage <= 0 ) continue; - double calph = clamp(ct.cummspan-fractic,0.,20.)/20.; - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_RED],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,calph*alph); - if ( ct.cummflash <= 0 ) continue; - double falph = max((ct.cummflash-FracTic)/15.,0.)**1.5; - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_REDFLASH],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,falph*calph*alph,DTA_LegacyRenderStyle,STYLE_Add); - } - // player-owned trackers are drawn last - cti.Reinit(); - while ( ct = SWWMQuickCombatTracker(cti.Next()) ) - { - // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing - if ( !projinit ) - { - projinit = true; - SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); - Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); - } - // ignore unowned (can happen?) - if ( !ct.Owner ) continue; - // ignore if max health is zero (SOMEHOW can happen) - if ( ct.maxhealth <= 0 ) continue; - // ignore non-player trackers and voodoo dolls - if ( !ct.Owner.player || (ct.Owner.player.mo != ct.Owner) ) continue; - // ignore local player or camera - if ( (ct.Owner == CPlayer.mo) || (ct.Owner == cam) ) continue; - // ignore trackers not of this player - if ( ct.myplayer != CPlayer ) continue; - // ignore enemies if filtering friends - if ( onlyfriends && !ct.Owner.IsFriend(CPlayer.mo) ) continue; - // ignore trackers clearly outside of player view - Vector3 smpos = level.Vec3Offset(SWWMUtility.LerpVector3(ct.Owner.prev,ct.Owner.pos,fractic),(0,0,ct.lvheight)); - Vector3 tdir = level.Vec3Diff(viewpos,smpos); - if ( viewvec dot tdir < 0 ) continue; - Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir); - if ( ndc.z >= 1. ) continue; - Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; - if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue; - double fin = clamp(ct.fadein+fractic,0.,5.)/5.; - double fout = clamp(ct.lifespan-fractic,0.,25.)/25.; - double alph = fin*fout; - Vector2 barpos = vpos-(27,7); - Font fnt = swwm_bigtags?mSmallFontOutline:mTinyFontOutline; - int col = Font.CR_WHITE; - if ( teamplay ) - { - int team = ct.Owner.player.GetTeam(); - if ( team != -1 ) col = Font.FindFontColor(Teams[team].mName); // this works in practice because team names are the same as their text colors - if ( col == -1 ) col = Font.CR_WHITE; - } - Screen.DrawText(fnt,col,vpos.x-fnt.StringWidth(ct.mytag)/2,barpos.y-(fnt.GetHeight()+2),ct.mytag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - Screen.DrawTexture(EnemyBTex,false,barpos.x,barpos.y,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - double ht = clamp(ct.intp.GetValue(fractic),0,ct.maxhealth*100); - double hw = (ht*50.)/ct.maxhealth; - double ohw = hw; - if ( ct.Owner.bINVULNERABLE || (ct.Owner.player.cheats&(CF_GODMODE|CF_GODMODE2)) || ct.Owner.FindInventory("InvinciballPower") ) - Screen.DrawTexture(EnemyHTex[1],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); - else - { - Screen.DrawTexture(EnemyHTex[0],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); - if ( ht > ct.maxhealth ) - { - hw = (min(ht-ct.maxhealth,ct.maxhealth)*50.)/ct.maxhealth; - Screen.DrawTexture(EnemyHTex[3],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); - } - if ( ht > ct.maxhealth*2 ) - { - hw = (min(ht-ct.maxhealth*2,ct.maxhealth*3)*50.)/ct.maxhealth; - Screen.DrawTexture(EnemyHTex[4],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); - } - if ( ht > ct.maxhealth*5 ) - { - hw = (min(ht-ct.maxhealth*5,ct.maxhealth*5)*50.)/ct.maxhealth; - Screen.DrawTexture(EnemyHTex[5],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); - } - } - if ( ct.Owner.FindInventory("DivineSpriteEffect") ) - { - double falph = clamp((ht-ct.maxhealth*10)/(ct.maxhealth*60.),0.,1.); - Screen.DrawTexture(EnemyHTex[6],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph*falph,DTA_LegacyRenderStyle,STYLE_Add); - } - else - { - ht = clamp(ct.intpl.GetValue(fractic),0,ct.maxhealth); - hw = (ht*50.)/ct.maxhealth; - Screen.DrawTexture(EnemyHTex[2],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowLeftF,ohw,DTA_WindowRightF,hw); - } - if ( ct.cummdamage <= 0 ) continue; - double calph = clamp(ct.cummspan-fractic,0.,20.)/20.; - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_RED],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,calph*alph); - if ( ct.cummflash <= 0 ) continue; - double falph = max((ct.cummflash-FracTic)/15.,0.)**1.5; - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_REDFLASH],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,falph*calph*alph,DTA_LegacyRenderStyle,STYLE_Add); - } - } - - private void DrawNumbers( Vector3 viewvec, out bool projinit ) - { - SWWMDamNum snum = hnd.damnums; - if ( !snum ) return; - do - { - // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing - if ( !projinit ) - { - projinit = true; - SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); - Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); - } - Vector3 tdir = level.Vec3Diff(ViewPos,snum.pos); - if ( viewvec dot tdir < 0 ) continue; - Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); - if ( ndc.z >= 1. ) continue; - Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs; - String tag = abs(snum.damage>=Actor.TELEFRAG_DAMAGE)?(snum.damage>0)?"-∞":"+∞":String.Format("%+d",-snum.damage); - double alph = clamp((snum.lifespan+fractic)/35.,0.,1.); - Vector2 fo; - int initspd = (128-snum.seed); - int boostup = 64+snum.seed2; - fo.x = (.05*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.8); - fo.y = -((snum.initialspan-(snum.lifespan-fractic))**1.5)+boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic)); - Screen.DrawText(MiniHUDFontOutline,snum.tcolor,(vpos.x-fo.x)-(MiniHUDFontOutline.StringWidth(tag))/2,(vpos.y-fo.y)-(MiniHUDFontOutline.GetHeight())/2,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); - } - while ( snum = snum.next ); - } - - private void DrawScores( Vector3 viewvec, out bool projinit ) - { - SWWMScoreObj snum = hnd.scorenums; - if ( !snum ) return; - do - { - // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing - if ( !projinit ) - { - projinit = true; - SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); - Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); - } - Vector3 tdir = level.Vec3Diff(ViewPos,snum.pos); - if ( viewvec dot tdir < 0 ) continue; - Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); - if ( ndc.z >= 1. ) continue; - Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; - String tag = String.Format("%+d",snum.score); - double alph = clamp((snum.lifespan+fractic)/double(GameTicRate),0.,1.); - // score rises linearly - Vector2 fo = (0,snum.initialspan-(snum.lifespan-fractic)); - Screen.DrawText(mTinyFontOutline,snum.tcolor,vpos.x-(fo.x+mTinyFontOutline.StringWidth(tag)/2.),vpos.y-(fo.y+(mTinyFontOutline.GetHeight()/2.)),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - // extra strings (if available) - fo.y += mTinyFontOutline.GetHeight(); - for ( int i=0; i 0 ) tag.AppendFormat(" x%d",snum.xscore[i]); - Screen.DrawText(mTinyFontOutline,snum.xtcolor[i],vpos.x-(fo.x+mTinyFontOutline.StringWidth(tag)/2.),vpos.y-(fo.y+(mTinyFontOutline.GetHeight()/2.)),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); - fo.y += mTinyFontOutline.GetHeight(); - } - } - while ( snum = snum.next ); - } - - private void DrawTarget() - { - // don't draw when dead or with automap open - if ( (CPlayer.health <= 0) || automapactive ) return; - bool projinit = false; - Vector3 viewvec = SWWMUtility.Vec3FromAngles(viewrot.x,viewrot.y); - // points of interest - if ( level.allmap && swwm_pois ) DrawInterest(viewvec,projinit); - // sensed items - DrawItemSense(viewvec,projinit); - // targetting array - if ( swwm_targeter ) DrawTrackers(viewvec,projinit); - // floating kill scores and others - if ( swwm_damnums ) DrawNumbers(viewvec,projinit); - if ( swwm_scorenums ) DrawScores(viewvec,projinit); - Screen.ClearClipRect(); - } - override void DrawMyPos() { String str = String.Format("(%d,%d,%d)",CPlayer.mo.pos.X,CPlayer.mo.pos.Y,CPlayer.mo.pos.Z); Screen.DrawText(mTinyFontOutline,Font.CR_GREEN,(ss.x-mTinyFontOutline.StringWidth(str))/2,4,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } - override void DrawAutomapHUD( double ticFrac ) - { - // do nothing, DrawScore handles this - } - - // minimap helper code - private void GetMinimapColors() - { - mm_colorset = swwm_mm_colorset; - switch ( mm_colorset ) - { - case 1: - // gzdoom - mm_backcolor = am_backcolor; - mm_cdwallcolor = am_cdwallcolor; - mm_efwallcolor = am_efwallcolor; - mm_fdwallcolor = am_fdwallcolor; - mm_interlevelcolor = am_interlevelcolor; - mm_intralevelcolor = am_intralevelcolor; - mm_lockedcolor = am_lockedcolor; - mm_notseencolor = am_notseencolor; - mm_portalcolor = am_portalcolor; - mm_secretsectorcolor = am_secretsectorcolor; - mm_secretwallcolor = am_secretwallcolor; - mm_specialwallcolor = am_specialwallcolor; - mm_thingcolor = am_thingcolor; - mm_thingcolor_citem = am_thingcolor_citem; - mm_thingcolor_friend = am_thingcolor_friend; - mm_thingcolor_item = am_thingcolor_item; - mm_thingcolor_monster = am_thingcolor_monster; - mm_thingcolor_ncmonster = am_thingcolor_ncmonster; - mm_thingcolor_shootable = am_thingcolor; - mm_thingcolor_vipitem = am_unexploredsecretcolor; - mm_thingcolor_missile = am_specialwallcolor; - mm_tswallcolor = am_tswallcolor; - mm_unexploredsecretcolor = am_unexploredsecretcolor; - mm_wallcolor = am_wallcolor; - mm_yourcolor = am_yourcolor; - mm_displaylocks = true; - break; - case 2: - // doom - mm_backcolor = "00 00 00"; - mm_cdwallcolor = "fc fc 00"; - mm_efwallcolor = "bc 78 48"; - mm_fdwallcolor = "bc 78 48"; - mm_interlevelcolor = 0; - mm_intralevelcolor = 0; - mm_lockedcolor = "fc fc 00"; - mm_notseencolor = "6c 6c 6c"; - mm_portalcolor = "40 40 40"; - mm_secretsectorcolor = 0; - mm_secretwallcolor = 0; - mm_specialwallcolor = 0; - mm_thingcolor = "74 fc 6c"; - mm_thingcolor_citem = "74 fc 6c"; - mm_thingcolor_friend = "74 fc 6c"; - mm_thingcolor_item = "74 fc 6c"; - mm_thingcolor_monster = "74 fc 6c"; - mm_thingcolor_ncmonster = "74 fc 6c"; - mm_thingcolor_shootable = "74 fc 6c"; - mm_thingcolor_vipitem = "74 fc 6c"; - mm_thingcolor_missile = "74 fc 6c"; - mm_tswallcolor = "80 80 80"; - mm_unexploredsecretcolor = 0; - mm_wallcolor = "fc 00 00"; - mm_yourcolor = "ff ff ff"; - mm_displaylocks = false; - break; - case 3: - // strife - mm_backcolor = "00 00 00"; - mm_cdwallcolor = "77 73 73"; - mm_efwallcolor = "37 3b 5b"; - mm_fdwallcolor = "37 3b 5b"; - mm_interlevelcolor = 0; - mm_intralevelcolor = 0; - mm_lockedcolor = "77 73 73"; - mm_notseencolor = "6c 6c 6c"; - mm_portalcolor = "40 40 40"; - mm_secretsectorcolor = 0; - mm_secretwallcolor = 0; - mm_specialwallcolor = 0; - mm_thingcolor = "bb 3b 00"; - mm_thingcolor_citem = "db ab 00"; - mm_thingcolor_friend = "fc 00 00"; - mm_thingcolor_item = "db ab 00"; - mm_thingcolor_monster = "fc 00 00"; - mm_thingcolor_ncmonster = "fc 00 00"; - mm_thingcolor_shootable = "bb 3b 00"; - mm_thingcolor_vipitem = "db ab 00"; - mm_thingcolor_missile = "bb 3b 00"; - mm_tswallcolor = "77 73 73"; - mm_unexploredsecretcolor = 0; - mm_wallcolor = "c7 ce ce"; - mm_yourcolor = "ef ef ef"; - mm_displaylocks = false; - break; - case 4: - // raven - mm_backcolor = "6c 54 40"; - mm_cdwallcolor = "67 3b 1f"; - mm_efwallcolor = "d0 b0 85"; - mm_fdwallcolor = "d0 b0 85"; - mm_interlevelcolor = 0; - mm_intralevelcolor = 0; - mm_lockedcolor = "67 3b 1f"; - mm_notseencolor = "00 00 00"; - mm_portalcolor = "50 50 50"; - mm_secretsectorcolor = 0; - mm_secretwallcolor = 0; - mm_specialwallcolor = 0; - mm_thingcolor = "ec ec ec"; - mm_thingcolor_citem = "ec ec ec"; - mm_thingcolor_friend = "ec ec ec"; - mm_thingcolor_item = "ec ec ec"; - mm_thingcolor_monster = "ec ec ec"; - mm_thingcolor_ncmonster = "ec ec ec"; - mm_thingcolor_shootable = "ec ec ec"; - mm_thingcolor_vipitem = "ec ec ec"; - mm_thingcolor_missile = "ec ec ec"; - mm_tswallcolor = "58 5d 56"; - mm_unexploredsecretcolor = 0; - mm_wallcolor = "4b 32 10"; - mm_yourcolor = "ff ff ff"; - mm_displaylocks = true; - break; - default: - // swwm - mm_backcolor = "10 10 10"; - mm_cdwallcolor = "30 50 70"; - mm_efwallcolor = "80 a0 c0"; - mm_fdwallcolor = "50 70 90"; - mm_interlevelcolor = "ff 00 60"; - mm_intralevelcolor = "00 60 ff"; - mm_lockedcolor = "00 90 80"; - mm_notseencolor = "20 20 30"; - mm_portalcolor = "40 30 20"; - mm_secretsectorcolor = "80 00 ff"; - mm_secretwallcolor = "60 40 80"; - mm_specialwallcolor = "ff a0 00"; - mm_thingcolor = "ff ff ff"; - mm_thingcolor_citem = "00 ff ff"; - mm_thingcolor_friend = "80 ff a0"; - mm_thingcolor_item = "ff c0 00"; - mm_thingcolor_monster = "ff 60 40"; - mm_thingcolor_ncmonster = "a0 40 20"; - mm_thingcolor_shootable = "ff a0 a0"; - mm_thingcolor_vipitem = "80 60 ff"; - mm_thingcolor_missile = "ff a0 20"; - mm_tswallcolor = "30 20 40"; - mm_unexploredsecretcolor = "40 00 80"; - mm_wallcolor = "c0 e0 ff"; - mm_yourcolor = "80 ff 00"; - mm_displaylocks = true; - break; - } - } - private bool ShouldDisplaySpecial( int special ) - { - // thanks graf/randi/whoever - switch ( special ) - { - // the following have (max_args < 0) - // but we can't know this from zscript, so they're hardcoded here - case Polyobj_StartLine: - case Polyobj_ExplicitLine: - case Transfer_WallLight: - case Sector_Attach3dMidtex: - case ExtraFloor_LightOnly: - case Sector_CopyScroller: - case Scroll_Texture_Left: - case Scroll_Texture_Right: - case Scroll_Texture_Up: - case Scroll_Texture_Down: - case Plane_Copy: - case Line_SetIdentification: - case Line_SetPortal: - case Sector_Set3DFloor: - case Sector_SetContents: - case Plane_Align: - case Static_Init: - case Transfer_Heights: - case Transfer_FloorLight: - case Transfer_CeilingLight: - case Scroll_Texture_Model: - case Scroll_Texture_Offsets: - case PointPush_SetForce: - return false; - } - return true; - } - private bool CheckSectorAction( Sector s, out int special, bool useonly ) - { - for ( Actor act=s.SecActTarget; act; act=act.tracer ) - { - if ( (act.Health&(SectorAction.SECSPAC_Use|SectorAction.SECSPAC_UseWall) || !useonly) - && act.special && !act.bFRIENDLY ) - { - special = act.special; - return true; - } - } - return false; - } - private bool RealLineSpecial( Line l, out int special ) - { - if ( special && l.activation&SPAC_PlayerActivate ) - return true; - if ( CheckSectorAction(l.frontsector,special,!l.backsector) ) - return true; - return (l.backsector && CheckSectorAction(l.backsector,special,false)); - } - private bool ShowTriggerLine( Line l ) - { - if ( am_showtriggerlines == 0 ) return false; - int special = l.special; - if ( !RealLineSpecial(l,special) ) return false; - if ( !ShouldDisplaySpecial(special) ) return false; - if ( special && (am_showtriggerlines >= 2) ) return true; - if ( !special || (special == Door_Open) - || (special == Door_Close) - || (special == Door_CloseWaitOpen) - || (special == Door_Raise) - || (special == Door_Animated) - || (special == Generic_Door) ) - return false; - return true; - } - private bool CmpFloorPlanes( Line l ) - { - return (l.frontsector.floorplane.Normal == l.backsector.floorplane.Normal) - && (l.frontsector.floorplane.D == l.backsector.floorplane.D); - } - private bool CmpCeilingPlanes( Line l ) - { - return (l.frontsector.ceilingplane.Normal == l.backsector.ceilingplane.Normal) - && (l.frontsector.ceilingplane.D == l.backsector.ceilingplane.D); - } - - private int CheckSecret( Line l ) - { - if ( !mm_secretsectorcolor || !mm_unexploredsecretcolor ) - return 0; - if ( l.frontsector && (l.frontsector.flags&Sector.SECF_WASSECRET) ) - { - if ( am_map_secrets && !(l.frontsector.flags&Sector.SECF_SECRET) ) return 1; - if ( (am_map_secrets == 2) && !(l.flags&Line.ML_SECRET) ) return 2; - } - if ( l.backsector && (l.backsector.flags&Sector.SECF_WASSECRET) ) - { - if ( am_map_secrets && !(l.backsector.flags&Sector.SECF_SECRET) ) return 1; - if ( (am_map_secrets == 2) && !(l.flags&Line.ML_SECRET) ) return 2; - } - return 0; - } - private bool CheckFFBoundary( Line l ) - { - int fcount = l.frontsector.Get3DFloorCount(), - bcount = l.backsector.Get3DFloorCount(); - // no 3D floors, no boundary - if ( !fcount && !bcount ) return false; - int fvalid = 0, bvalid = 0; - for ( int i=0; i= -1; p-- ) - { - if ( p == thisgroup ) continue; - foreach ( l : level.lines ) - { - if ( !(l.flags&Line.ML_MAPPED) && !level.allmap && !am_cheat ) continue; - if ( (l.flags&Line.ML_DONTDRAW) && ((am_cheat == 0) || (am_cheat >= 4)) ) - continue; - Vector2 rv1 = l.v1.p-cpos, rv2 = l.v2.p-cpos; - int lgroup; - if ( l.sidedef[0].flags&Side.WALLF_POLYOBJ ) lgroup = level.PointInSector(l.v1.p+l.delta/2.).portalgroup; - else lgroup = l.frontsector.portalgroup; - bool isportal = ((numgroups>0)&&(lgroup!=thisgroup)); - if ( lgroup == p ) - { - // portal displacement - Vector2 pofs = level.GetDisplacement(lgroup,thisgroup); - rv1 += pofs; - rv2 += pofs; - } - else if ( (p != -1) || (lgroup != thisgroup) ) - continue; - Vector2 mid = (rv1+rv2)/2.; - Vector2 siz = (abs(rv1.x-rv2.x),abs(rv1.y-rv2.y))/2.; - if ( (((siz.x+zoomview)-abs(mid.x)) <= 0) || (((siz.y+zoomview)-abs(mid.y)) <= 0) ) - continue; - // flip Y - rv1.y *= -1; - rv2.y *= -1; - // rotate by view - rv1 = Actor.RotateVector(rv1,ViewRot.x-90); - rv2 = Actor.RotateVector(rv2,ViewRot.x-90); - // clip to frame - bool visible; - [visible, rv1, rv2] = SWWMUtility.LiangBarsky((-1,-1)*zoomclip,(1,1)*zoomclip,rv1,rv2); - if ( !visible ) continue; - // scale to minimap frame - rv1 *= (HALFMAPSIZE/zoomclip)*hs; - rv2 *= (HALFMAPSIZE/zoomclip)*hs; - // offset to minimap center - rv1 += basepos; - rv2 += basepos; - // get the line color - Color col = mm_wallcolor; - if ( (l.flags&Line.ML_MAPPED) || am_cheat ) - { - int secwit = CheckSecret(l); - int lock = SWWMUtility.GetLineLock(l); - if ( secwit == 1 ) col = mm_secretsectorcolor; - else if ( secwit == 2 ) col = mm_unexploredsecretcolor; - else if ( l.flags&Line.ML_SECRET ) - { - if ( am_cheat && l.backsector && mm_secretwallcolor ) - col = mm_secretwallcolor; - else col = mm_wallcolor; - } - else if ( mm_interlevelcolor - && ((l.special == Exit_Normal) - || (l.special == Exit_Secret) - || (l.special == Teleport_NewMap) - || (l.special == Teleport_EndGame)) ) - col = mm_interlevelcolor; - else if ( mm_intralevelcolor && - (l.activation&SPAC_PlayerActivate) - && ((l.special == Teleport) - || (l.special == Teleport_NoFog) - || (l.special == Teleport_ZombieChanger) - || (l.special == Teleport_Line)) ) - col = mm_intralevelcolor; - else if ( mm_displaylocks - && (lock > 0) && (lock < 256) ) - { - let lcol = SWWMUtility.GetLockColor(lock); - if ( lcol ) col = lcol; - else col = mm_lockedcolor; - } - else if ( mm_specialwallcolor && ShowTriggerLine(l) ) - col = mm_specialwallcolor; - else if ( l.frontsector && l.backsector ) - { - if ( !CmpFloorPlanes(l) ) col = mm_fdwallcolor; - else if ( !CmpCeilingPlanes(l) ) col = mm_cdwallcolor; - else if ( CheckFFBoundary(l) ) col = mm_efwallcolor; - else - { - if ( (am_cheat == 0) || (am_cheat >= 4) ) - continue; - col = mm_tswallcolor; - } - } - } - else col = mm_notseencolor; - // draw the line - if ( isportal ) - { - col = Color((col.r+mm_portalcolor.r*7)/8,(col.g+mm_portalcolor.g*7)/8,(col.b+mm_portalcolor.b*7)/8); - Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.25),col); - } - else Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.5),col); - } - } - } - private void DrawMapMarkers( Vector2 basepos ) - { - double zoomlevel = SWWMUtility.Lerp(oldminimapzoom,minimapzoom,FracTic); - double zoomview = MAPVIEWDIST*zoomlevel, zoomclip = CLIPDIST*zoomlevel; - Vector2 cpos = SWWMUtility.LerpVector2(players[consoleplayer].Camera.prev.xy,players[consoleplayer].Camera.pos.xy,FracTic); - Sector csec = players[consoleplayer].Camera.CurSector; - if ( !mi ) mi = ThinkerIterator.Create("MapMarker",Thinker.STAT_MAPMARKER); - else mi.Reinit(); - MapMarker m; - while ( m = MapMarker(mi.Next()) ) - { - if ( m.bDORMANT ) continue; - if ( m.args[1] && !(m.CurSector.moreflags&Sector.SECMF_DRAWN) ) continue; - TextureID tx; - if ( m.picnum.IsValid() ) tx = m.picnum; - else tx = m.CurState.GetSpriteTexture(1); - Vector2 sz = TexMan.GetScaledSize(tx); - Vector2 scl; - // seems to match automap scaling somewhat - if ( m.Args[2] ) scl = (m.Scale/zoomlevel)*.15; - else scl = m.Scale*.5; - sz.x *= scl.x; - sz.y *= scl.y; - double radius = max(sz.x,sz.y); // naive, I know - if ( m.args[0] ) - { - // oh bother, this will be dicks - let ai = level.CreateActorIterator(m.args[0]); - Actor a; - while ( a = ai.Next() ) - { - Vector2 rv = a.pos.xy-cpos; - bool isportal = false; - Sector sec = level.PointInSector(a.pos.xy); - if ( sec.portalgroup != csec.portalgroup ) - { - isportal = true; - // portal displacement - rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); - } - if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) - continue; - // flip Y - rv.y *= -1; - // rotate by view - rv = Actor.RotateVector(rv,ViewRot.x-90); - // scale to minimap frame - rv *= (HALFMAPSIZE/zoomclip)*hs; - // offset to minimap center - rv += basepos; - // draw - Screen.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,hs*scl.x,DTA_ScaleY,hs*scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation); - } - ai.Destroy(); - continue; - } - Vector2 rv = m.pos.xy-cpos; - bool isportal = false; - Sector sec = level.PointInSector(m.pos.xy); - if ( sec.portalgroup != csec.portalgroup ) - { - isportal = true; - // portal displacement - rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); - } - if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) - continue; - // flip Y - rv.y *= -1; - // rotate by view - rv = Actor.RotateVector(rv,ViewRot.x-90); - // scale to minimap frame - rv *= (HALFMAPSIZE/zoomclip)*hs; - // offset to minimap center - rv += basepos; - // draw - Screen.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,hs*scl.x,DTA_ScaleY,hs*scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation); - } - } - private void DrawMapThings( Vector2 basepos ) - { - double zoomlevel = SWWMUtility.Lerp(oldminimapzoom,minimapzoom,FracTic); - double zoomview = MAPVIEWDIST*zoomlevel, zoomclip = CLIPDIST*zoomlevel; - Vector2 cpos = SWWMUtility.LerpVector2(players[consoleplayer].Camera.prev.xy,players[consoleplayer].Camera.pos.xy,FracTic); - Sector csec = players[consoleplayer].Camera.CurSector; - bool drawmissiles = swwm_mm_missiles; - for ( SWWMSimpleTracker t=hnd.strackers; t; t=t.next ) - { - if ( !drawmissiles && t.ismissile ) continue; - if ( level.allmap && (t.target is 'Key') ) continue; // don't draw keys over the actual markers they have - Color col = mm_thingcolor; - bool isitem = false; - bool plainactor = false; - Vector2 pos; - double angle; - double radius; - if ( t.target ) - { - pos = SWWMUtility.LerpVector2(t.target.prev.xy,t.target.pos.xy,FracTic); - angle = t.target.angle; - radius = t.isybeam?(t.target.speed*cos(t.target.pitch-90)):t.isbeam?(t.target.speed*cos(t.target.pitch)):t.target.radius; - } - else - { - pos = t.pos.xy; - angle = t.angle; - radius = t.radius; - } - if ( t.isitem ) - { - if ( t.vipitem ) col = mm_thingcolor_vipitem; - else if ( t.countitem ) col = mm_thingcolor_citem; - else col = mm_thingcolor_item; - isitem = true; - } - else if ( t.isplayer ) col = t.playercol; - else if ( t.friendly ) col = mm_thingcolor_friend; - else if ( t.countkill ) col = mm_thingcolor_monster; - else if ( t.ismonster ) col = mm_thingcolor_ncmonster; - else if ( t.ismissile ) col = mm_thingcolor_missile; - else - { - if ( t.vipitem ) col = mm_thingcolor_vipitem; // chanceboxes - else if ( t.shootable ) col = mm_thingcolor_shootable; - plainactor = true; - } - int mtime = GameTicRate; - if ( level.allmap && !t.expired && t.target ) mtime += GameTicRate*3; - Vector2 rv = pos-cpos; - bool isportal = false; - Sector sec = level.PointInSector(pos); - if ( sec.portalgroup != csec.portalgroup ) - { - isportal = true; - // portal displacement - rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); - // and blend in the color too - col = Color((col.r+mm_portalcolor.r*7)/8,(col.g+mm_portalcolor.g*7)/8,(col.b+mm_portalcolor.b*7)/8); - } - if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) - continue; - Vector2 tv[4]; - int nidx; - if ( t.isbeam ) - { - // oriented line - nidx = 2; - tv[0] = rv; - tv[1] = rv+Actor.RotateVector((radius,0),angle); - } - else if ( isitem ) - { - // rhombus - nidx = 4; - double crad = min(radius,10); - for ( int i=0; i<4; i++ ) - tv[i] = rv+Actor.RotateVector((crad,0),i*90); - } - else if ( plainactor ) - { - // aabb box - nidx = 4; - tv[0] = rv+(-radius,-radius); - tv[1] = rv+(radius,-radius); - tv[2] = rv+(radius,radius); - tv[3] = rv+(-radius,radius); - } - else - { - // oriented triangle - nidx = 3; - tv[0] = rv+Actor.RotateVector((radius,0),angle); - tv[1] = rv+Actor.RotateVector((-radius*.5,radius*.7),angle); - tv[2] = rv+Actor.RotateVector((-radius*.5,-radius*.7),angle); - } - // flip Y - for ( int j=0; j 0) ) - { - xx = int(ss.x-(margin+2)); - String str; - if ( automapactive || (fstats > 1) ) - { - int label = am_showmaplabel; - String ln = level.levelname; - int iof = ln.IndexOf(" - by: "); - if ( iof != -1 ) ln.Truncate(iof); - if ( !label || ((level.clusterflags&level.CLUSTER_HUB) && (label == 2)) ) str = ln; - else str = String.Format("%s - %s",level.mapname.MakeUpper(),ln); - Screen.DrawText(mSmallFontOutline,tclabel,xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - yy += mSmallFontOutline.GetHeight()+4; - } - if ( (level.total_monsters > 0) && am_showmonsters && !deathmatch ) - { - str = String.Format("\c"..tclabel_s.."K \c-%d\c"..tcextra_s.."/\c-%d",level.killed_monsters,level.total_monsters); - Screen.DrawText(MiniHUDFontOutline,(level.killed_monsters>=level.total_monsters)?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( killflash && (gametic < killflash) ) - { - double alph = max((killflash-(gametic+FracTic))/25.,0.)**1.5; - str = String.Format("%d/%d",level.killed_monsters,level.total_monsters); - int slashpos = str.IndexOf("/"); - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - if ( tkillflash && (gametic < tkillflash) ) - { - double alph = max((tkillflash-(gametic+FracTic))/25.,0.)**1.5; - str = String.Format("%d",level.total_monsters); - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - yy += MiniHUDFontOutline.GetHeight()+2; - } - if ( (level.total_items > 0) && am_showitems && !deathmatch ) - { - str = String.Format("\c"..tclabel_s.."I \c-%d\c"..tcextra_s.."/\c-%d",level.found_items,level.total_items); - Screen.DrawText(MiniHUDFontOutline,(level.found_items>=level.total_items)?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( itemflash && (gametic < itemflash) ) - { - double alph = max((itemflash-(gametic+FracTic))/25.,0.)**1.5; - str = String.Format("%d/%d",level.found_items,level.total_items); - int slashpos = str.IndexOf("/"); - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - if ( titemflash && (gametic < titemflash) ) - { - double alph = max((titemflash-(gametic+FracTic))/25.,0.)**1.5; - str = String.Format("%d",level.total_items); - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - yy += MiniHUDFontOutline.GetHeight()+2; - } - if ( (level.total_secrets > 0) && am_showsecrets && !deathmatch ) - { - str = String.Format("\c"..tclabel_s.."S \c-%d\c"..tcextra_s.."/\c-%d",level.found_secrets,level.total_secrets); - Screen.DrawText(MiniHUDFontOutline,(level.found_secrets>=level.total_secrets)?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( secretflash && (gametic < secretflash) ) - { - double alph = max((secretflash-(gametic+FracTic))/25.,0.)**1.5; - str = String.Format("%d/%d",level.found_secrets,level.total_secrets); - int slashpos = str.IndexOf("/"); - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - if ( tsecretflash && (gametic < tsecretflash) ) - { - double alph = max((tsecretflash-(gametic+FracTic))/25.,0.)**1.5; - str = String.Format("%d",level.total_secrets); - Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - yy += MiniHUDFontOutline.GetHeight()+2; - } - int sec; - if ( am_showtime ) - { - sec = Thinker.Tics2Seconds(level.maptime); - str = String.Format("\c"..tclabel_s.."T \c-%02d\c"..tcextra_s..":\c-%02d\c"..tcextra_s..":\c-%02d",sec/3600,(sec%3600)/60,sec%60); - Screen.DrawText(MiniHUDFontOutline,((level.sucktime>0)&&(sec>=(level.sucktime*3600)))?tcsucks:((level.partime>0)&&(sec<=level.partime))?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - yy += MiniHUDFontOutline.GetHeight()+2; - } - // don't show total time if it's equal to map time - if ( am_showtotaltime && (level.totaltime != level.maptime) ) - { - sec = Thinker.Tics2Seconds(level.totaltime); - str = String.Format("\c"..tclabel_s.."TT \c-%02d\c"..tcextra_s..":\c-%02d\c"..tcextra_s..":\c-%02d",sec/3600,(sec%3600)/60,sec%60); - Screen.DrawText(MiniHUDFontOutline,tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - yy += MiniHUDFontOutline.GetHeight()+2; - } - yy += 3; - } - if ( deathmatch ) - { - yy += 9; - if ( playercount <= 1 ) return; - xx = int(ss.x-(margin+2)); - String str; - if ( teamplay ) - { - // draw team scores - for ( int i=0; i 0 ) str = String.Format("\c[MiniDemoBlue]%s \c-+%d",StringTable.Localize("$SWWM_DMSPREAD"),lead); - else str = String.Format("\c[MiniDemoBlue]%s \c-%d",StringTable.Localize("$SWWM_DMSPREAD"),lead); - Screen.DrawText(mSmallFontOutline,mhudfontcol[MCR_RED],xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - yy += mSmallFont.GetHeight()+3; - // draw top 3 players - for ( int i=0; i klist; - for ( int i=0; i= f.flashtime) ) continue; - double alph = max((f.flashtime-(gametic+FracTic))/25.,0.)**1.5; - Screen.DrawTexture(icon,false,keypos.x-siz.x,keypos.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_TopLeft,true,DTA_ColorOverlay,0xFFFFC040,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - break; - } - keypos.x -= siz.x+2; - colh = max(colh,siz.y); - if ( ++colc == maxcolc ) - { - keypos.x = ss.x-(margin+2); - keypos.y += colh+2; - colh = colc = 0; - } - } - } - - private bool DrawInvIcon( Inventory i, double xx, double yy, double alpha = 1., bool forceamt = false, bool selected = false, bool aspowerup = false ) - { - if ( !i || !i.Icon.IsValid() ) return false; - Vector2 scl = TexMan.GetScaledSize(i.Icon); - double mscl = min(1.,30./max(scl.x,scl.y)); - double dw = (ss.x/mscl), dh = (ss.y/mscl); - double dx = (xx+(30-scl.x*mscl)/2)/mscl, dy = (yy+(30-scl.y*mscl)/2)/mscl; - if ( i is 'Powerup' ) - { - Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,Powerup(i).IsBlinking()?alpha*.5:alpha,DTA_TopOffset,0,DTA_LeftOffset,0); - String nstr = String.Format("%ds",Powerup(i).EffectTics/GameTicRate); - int len = MiniHudFontOutline.StringWidth(nstr); - Screen.DrawText(MiniHudFontOutline,mhudfontcol[MCR_BRASS],(xx+30)-len,(yy+30)-6,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,Powerup(i).IsBlinking()?alpha*.5:alpha); - return true; - } - if ( (i is 'SWWMLamp') && aspowerup ) - { - Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,SWWMLamp(i).isBlinking()?alpha*.5:alpha,DTA_TopOffset,0,DTA_LeftOffset,0); - String nstr = String.Format("%d%%",SWWMLamp(i).Charge); - int len = MiniHudFontOutline.StringWidth(nstr); - Screen.DrawText(MiniHudFontOutline,mhudfontcol[SWWMLamp(i).bActive?MCR_BRASS:MCR_WHITE],(xx+30)-len,(yy+30)-6,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,SWWMLamp(i).isBlinking()?alpha*.5:alpha); - return true; - } - if ( (i is 'DivineSpriteEffect') && aspowerup ) - { - Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,DivineSpriteEffect(i).isBlinking()?alpha*.5:alpha,DTA_TopOffset,0,DTA_LeftOffset,0); - String nstr = String.Format("%ds",DivineSpriteEffect(i).healtim/GameTicRate); - int len = MiniHudFontOutline.StringWidth(nstr); - Screen.DrawText(MiniHudFontOutline,mhudfontcol[MCR_BRASS],(xx+30)-len,(yy+30)-6,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,DivineSpriteEffect(i).isBlinking()?alpha*.5:alpha); - return true; - } - Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,alpha,DTA_TopOffset,0,DTA_LeftOffset,0); - if ( (i.Amount > 1) || forceamt ) - { - String nstr; - if ( (i.Amount > 99999) && !forceamt ) nstr = "99999"; - else nstr = String.Format("%d",i.Amount); - int len = MiniHudFontOutline.StringWidth(nstr); - Screen.DrawText(MiniHudFontOutline,mhudfontcol[(i.Amount<=0)?MCR_RED:selected?MCR_BRASS:MCR_WHITE],(xx+30)-len,(yy+30)-6,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alpha); - } - return true; - } - - private void DrawInventory() - { - int invy = 61; - // active items (armor / powerups) - double xx = margin+2; - double yy = ss.y-(margin+invy+9); - bool drewarmor = false; - for ( Inventory i=CPlayer.mo.Inv; i; i=i.Inv ) - { - if ( (i.Amount <= 0) || (!(i is 'SWWMArmor') && !(i is 'BasicArmor')) ) continue; - if ( !DrawInvIcon(i,xx,yy,forceamt:true,selected:true) ) continue; - yy -= 34; - drewarmor = true; - } - yy = ss.y-(margin+invy+9); - if ( drewarmor ) - { - xx += 36; - if ( CPlayer.mo.InvSel && !isInventoryBarVisible() ) yy -= 34; - } - for ( Inventory i=CPlayer.mo.Inv; i; i=i.Inv ) - { - if ( (i is 'SWWMLamp') && SWWMLamp(i).bActivated ) - { - DrawInvIcon(i,xx,yy,selected:true,aspowerup:true); - yy -= 34; - continue; - } - if ( (i is 'DivineSpriteEffect') && !DivineSpriteEffect(i).bHealDone ) - { - DrawInvIcon(i,xx,yy,selected:true,aspowerup:true); - yy -= 34; - continue; - } - if ( !(i is 'Powerup') || (Powerup(i).EffectTics <= 0) || !(Powerup(i).Icon) ) continue; - if ( DrawInvIcon(i,xx,yy) ) - yy -= 34; - } - // inventory box / bar - if ( !CPlayer.mo.InvSel ) return; - if ( isInventoryBarVisible() ) - { - Array bar; - bar.Clear(); - for ( Inventory i=CPlayer.mo.FirstInv(); i; i=i.NextInv() ) bar.Push(i); - int ps = bar.Find(CPlayer.mo.InvSel); - Inventory prev[2], next[2]; - if ( bar.Size() > 1 ) - { - if ( ps+1 >= bar.Size() ) next[0] = bar[0]; - else next[0] = bar[ps+1]; - if ( ps-1 < 0 ) prev[0] = bar[bar.Size()-1]; - else prev[0] = bar[ps-1]; - } - if ( bar.Size() > 2 ) - { - if ( ps+2 >= bar.Size() ) next[1] = bar[(ps+2)-bar.Size()]; - else next[1] = bar[ps+2]; - if ( ps-2 < 0 ) prev[1] = bar[bar.Size()+(ps-2)]; - else prev[1] = bar[ps-2]; - } - xx = (ss.x-34)/2; - yy = (ss.y+64)/2; - Screen.DrawTexture(InventoryTex,false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - DrawInvIcon(CPlayer.mo.InvSel,xx+2,yy+2,selected:true); - DrawInvIcon(prev[0],xx-32,yy+2,2./3.); - DrawInvIcon(prev[1],xx-66,yy+2,1./3.); - DrawInvIcon(next[0],xx+36,yy+2,2./3.); - DrawInvIcon(next[1],xx+70,yy+2,1./3.); - return; - } - Screen.DrawTexture(InventoryTex,false,margin+36,ss.y-(margin+invy+2),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - DrawInvIcon(CPlayer.mo.InvSel,margin+38,ss.y-(margin+invy),selected:true); - } - - private void DrawWeapons() - { - Screen.DrawTexture(WeaponTex,false,ss.x-(margin+80),ss.y-(margin+10),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - double xx = ss.x-(margin+78), yy = ss.y-(margin+8); - for ( int i=1; i<=10; i++,xx+=8 ) - { - int ncolor = mhudfontcol[MCR_WHITE]; - if ( !CPlayer.HasWeaponsInSlot(i%10) ) - { - Screen.DrawText(MiniHUDFont,ncolor,xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,Color(128,0,0,0)); - continue; - } - bool selected = false; - bool dummy; - int slot; - SWWMGesture hasgesture = null; - SWWMItemGesture hasitemgesture = null; - if ( CPlayer.PendingWeapon is 'SWWMGesture' ) hasgesture = SWWMGesture(CPlayer.PendingWeapon); - else if ( CPlayer.ReadyWeapon is 'SWWMGesture' ) hasgesture = SWWMGesture(CPlayer.ReadyWeapon); - if ( CPlayer.PendingWeapon is 'SWWMItemGesture' ) hasitemgesture = SWWMItemGesture(CPlayer.PendingWeapon); - else if ( CPlayer.ReadyWeapon is 'SWWMItemGesture' ) hasitemgesture = SWWMItemGesture(CPlayer.ReadyWeapon); - if ( hasgesture && hasgesture.formerweapon ) - { - [dummy, slot] = CPlayer.weapons.LocateWeapon(hasgesture.formerweapon.GetClass()); - if ( slot == (i%10) ) selected = true; - } - else if ( hasitemgesture && hasitemgesture.gest.formerweapon ) - { - [dummy, slot] = CPlayer.weapons.LocateWeapon(hasitemgesture.gest.formerweapon.GetClass()); - if ( slot == (i%10) ) selected = true; - } - else if ( CPlayer.PendingWeapon && (CPlayer.PendingWeapon != WP_NOCHANGE) ) - { - [dummy, slot] = CPlayer.weapons.LocateWeapon(CPlayer.PendingWeapon.GetClass()); - if ( slot == (i%10) ) selected = true; - } - else if ( (!CPlayer.PendingWeapon || (CPlayer.PendingWeapon == WP_NOCHANGE)) && CPlayer.ReadyWeapon ) - { - [dummy, slot] = CPlayer.weapons.LocateWeapon(CPlayer.ReadyWeapon.GetClass()); - if ( slot == (i%10) ) selected = true; - } - if ( selected ) ncolor = mhudfontcol[MCR_BRASS]; - else - { - bool hasammo = (i==1); - for ( Inventory inv=CPlayer.mo.Inv; inv; inv=inv.Inv ) - { - if ( inv is 'Weapon' ) [dummy, slot] = CPlayer.weapons.LocateWeapon(Weapon(inv).GetClass()); - else continue; - if ( slot != (i%10) ) continue; - // CheckAmmo can't be called from ui, so we have to improvise - // for SWWM weapons I made a function for this at least - if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).ReportHUDAmmo() ) - hasammo = true; - else if ( !(inv is 'SWWMWeapon') && ((!Weapon(inv).Ammo1 || (Weapon(inv).Ammo1.Amount > 0) || Weapon(inv).bAMMO_OPTIONAL) || (Weapon(inv).Ammo2 && ((Weapon(inv).Ammo2.Amount > 0) || Weapon(inv).bALT_AMMO_OPTIONAL))) ) - hasammo = true; - } - if ( !hasammo ) ncolor = mhudfontcol[MCR_RED]; - } - Screen.DrawText(MiniHUDFont,ncolor,xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int f = hnd.WeaponFlash[i%10]; - if ( f && (gametic < f) ) - { - double alph = max((f-(gametic+FracTic))/25.,0.)**1.5; - Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_FLASH],xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_LegacyRenderStyle,STYLE_Add); - } - } - xx = ss.x-(margin+54); - yy = ss.y-(margin+14); - bool bDrewAmmo = false; - bool checkowned = !swwm_hudallammo; - Array OwnedWeapons; - if ( checkowned ) for ( Inventory i=CPlayer.mo.inv; i; i=i.inv ) - { - if ( !(i is 'SWWMWeapon') ) continue; - OwnedWeapons.Push(SWWMWeapon(i)); - } - String str; - for ( int i=17; i>=0; i-- ) - { - let a = AmmoSlots[i]; - // check if owned - if ( checkowned ) - { - bool owned = false; - foreach ( w:OwnedWeapons ) - { - if ( w.UsesAmmo(a) ) - owned = true; - } - if ( !owned ) continue; - } - if ( !bDrewAmmo ) - { - Screen.DrawTexture(AmmoTex[2],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - bDrewAmmo = true; - } - yy -= 6; - Screen.DrawTexture(AmmoTex[1],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - xx += 2; - let cur = SWWMAmmo(CPlayer.mo.FindInventory(a)); - int amt, amax; - if ( !cur ) - { - amt = 0; - amax = GetDefaultByType(a).MaxAmount; - let def = GetDefaultByType(a); - if ( def.MagAmmoType ) - { - let mag = MagAmmo(CPlayer.mo.FindInventory(def.MagAmmoType)); - if ( mag ) // theoretically can't happen, but still check for it - { - amt = mag.Amount; - amax = amax*mag.ClipSize+mag.MaxAmount; - } - else - { - let defmag = GetDefaultByType(def.MagAmmoType); - amax = amax*defmag.ClipSize+defmag.MaxAmount; - } - } - } - else - { - amt = cur.Amount; - amax = cur.MaxAmount; - if ( cur.MagAmmoType ) - { - let mag = MagAmmo(CPlayer.mo.FindInventory(cur.MagAmmoType)); - // theoretically this should never be null, but nevertheless... - if ( mag ) - { - amt = amt*mag.ClipSize+mag.Amount; - amax = amax*mag.ClipSize+mag.MaxAmount; - } - else - { - let def = GetDefaultByType(cur.MagAmmoType); - amt = amt*def.ClipSize; - amax = amax*def.ClipSize+def.MaxAmount; - } - } - } - bool used = false; - if ( CPlayer.ReadyWeapon && (CPlayer.ReadyWeapon is 'SWWMWeapon') ) - used = SWWMWeapon(CPlayer.ReadyWeapon).UsesAmmo(a); - int scol = mhudfontcol[used?MCR_BRASS:MCR_WHITE]; - int ncolor = (amt>0)?scol:mhudfontcol[MCR_RED]; - int dcnt1 = 2-int(Log10(clamp(amt,1,999))); - int dcnt2 = 2-int(Log10(clamp(amax,1,999))); - for ( int j=0; j= FS_PAIN) ) return 12; - if ( facestate == FS_OUCH ) return 10; - if ( facestate == FS_PAIN ) return (paindir==1)?8:(paindir==-1)?9:7; - if ( facestate == FS_GRIN ) return 5; - if ( facestate == FS_EVIL ) return 6; - if ( facestate == FS_SAD ) return 17; - if ( facestate == FS_WINK ) return 18; - if ( facestate == FS_BLINK ) return ((facetimer>28)||(facetimer<2))?3:4; - switch ( blinktime ) - { - case -1: - case -3: - return 3; - break; - case -2: - return 4; - break; - } - return 2; - } - - private void DrawMugshot() - { - rss = MSTime()*128; - let demo = Demolitionist(CPlayer.mo); - if ( !demo ) return; - double paintime = clamp((demo.lastdamagetimer-(gametic+Fractic))/double(GameTicRate),0.,1.); - double noiz = min(demo.lastdamage*.5*paintime,3.); - Vector2 shake = (RandomShiver(),RandomShiver())*noiz; - if ( !CPlayer.mo.FindInventory("GhostPower") ) - { - if ( !tagcolor ) tagcolor = CVar.GetCVar('swwm_tagcolor',CPlayer); - int facecol = tagcolor.GetInt(); - if ( (facecol < 0) || (facecol > 15) ) facecol = 0; - Screen.DrawTexture(FaceTex[0],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcWidth,32,DTA_SrcHeight,32,DTA_DestWidth,32,DTA_DestHeight,32,DTA_SrcX,32*(facecol%4),DTA_SrcY,32*(facecol/4)); - bool raging = CPlayer.mo.FindInventory("RagekitPower"); - bool angy = CPlayer.mo.FindInventory("AngeryPower"); - if ( raging && angy ) Screen.DrawTexture(FaceTex[16],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - else if ( raging ) Screen.DrawTexture(FaceTex[15],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - else if ( angy ) Screen.DrawTexture(FaceTex[13],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawTexture(FaceTex[1],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Color,Color(255,255,0,0),DTA_Alpha,min(1.,noiz)); - if ( (CPlayer.Health > 0) && (isInvulnerable() || CPlayer.mo.FindInventory("InvinciballPower")) ) - Screen.DrawTexture(FaceTex[1],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,.8+.1*sin(gametic+fractic)); - } - else - { - Screen.DrawTexture(FaceTex[0],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcWidth,32,DTA_SrcHeight,32,DTA_DestWidth,32,DTA_DestHeight,32,DTA_FillColor,Color(0,0,0),DTA_Alpha,.25*(1.-min(1.,noiz))); - Screen.DrawTexture(FaceTex[1],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_FillColor,Color(255,0,0),DTA_Alpha,.25*min(1.,noiz)); - } - Screen.DrawTexture(FaceTex[GetFaceTex(demo)],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( CPlayer.mo.FindInventory("BarrierPower") ) Screen.DrawTexture(FaceTex[14],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,.5,DTA_LegacyRenderStyle,STYLE_Add); - } - - private void DrawStatus() - { - DrawMugshot(); - int ox = 36; - int oy = 5; - Screen.DrawTexture(StatusTex,false,margin+ox,ss.y-(margin+22+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - String str; - double ht = clamp(HealthInter.GetValue(fractic),0,10000); - str = String.Format("%3d",clamp(round(ht),0,999)); - double hw = min(ht,100); - double bhw = hw; - int hcolor = MCR_RED; - if ( round(ht) > 500 ) hcolor = MCR_YELLOW; - else if ( round(ht) > 200 ) hcolor = MCR_PURPLE; - else if ( round(ht) > 100 ) hcolor = MCR_AQUA; - if ( isInvulnerable() || CPlayer.mo.FindInventory("InvinciballPower") ) - { - Screen.DrawTexture(HealthTex[0],false,margin+3+ox,ss.y-(margin+19+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw,DTA_ColorOverlay,Color(255,0,0,0)); - Screen.DrawTexture(HealthTex[4],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); - hcolor = MCR_WHITE; - } - else - { - Screen.DrawTexture(HealthTex[0],false,margin+3+ox,ss.y-(margin+19+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw,DTA_ColorOverlay,Color(255,0,0,0)); - Screen.DrawTexture(HealthTex[0],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); - if ( ht > 100 ) - { - hw = min(ht-100,100); - Screen.DrawTexture(HealthTex[1],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); - } - if ( ht > 200 ) - { - hw = min(ht-200,300)/3.; - Screen.DrawTexture(HealthTex[2],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); - } - if ( ht > 500 ) - { - hw = min(ht-500,500)/5.; - Screen.DrawTexture(HealthTex[3],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); - } - } - if ( CPlayer.mo.FindInventory("DivineSpriteEffect") ) - { - double falph = clamp((ht-1000)/6000.,0.,1.); - Screen.DrawTexture(HealthTex[5],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,falph,DTA_LegacyRenderStyle,STYLE_Add); - String tst; - double alph = .1; - int trl = 9; - for ( double alph = .1; alph <= .5; alph += .1 ) - { - tst = "AAA"; - SWWMUtility.ObscureText(tst,(gametic-trl)/3,true); - trl--; - Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_WHITE],margin+107+ox,ss.y-(margin+20+oy),tst,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,falph*alph,DTA_LegacyRenderStyle,STYLE_Add); - } - Screen.DrawText(MiniHUDFont,mhudfontcol[hcolor],margin+107+ox,ss.y-(margin+20+oy),String.Format("%3d",clamp(round(ht),0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,1.-falph); - } - else - { - Screen.DrawText(MiniHUDFont,mhudfontcol[hcolor],margin+107+ox,ss.y-(margin+20+oy),String.Format("%3d",clamp(round(ht),0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int f = HealthFlash; - if ( f && (gametic < f) ) - { - double alph = max((f-(gametic+FracTic))/25.,0.)**1.5; - Screen.DrawTexture(HealthTex[7],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,bhw,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_FLASH],margin+107+ox,ss.y-(margin+20+oy),str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); - } - if ( (CPlayer.health > 0) && (CPlayer.health <= 25) && (PulsePhase <= 15) && (hcolor != MCR_WHITE) ) - { - double alph = clamp(sin((PulsePhase-FracTic)*12.),0.,1.); - Screen.DrawTexture(HealthTex[6],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw,DTA_Alpha,alph); - Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_REDFLASH],margin+107+ox,ss.y-(margin+20+oy),str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); - } - ht = clamp(LagHealthInter.GetValue(fractic),0,1000); - double hwl = min(ht,100); - if ( hwl > bhw ) - { - Screen.DrawTexture(HealthTex[8],false,margin+3+ox,ss.y-(margin+19+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowLeftF,bhw,DTA_WindowRightF,hwl,DTA_ColorOverlay,Color(255,0,0,0)); - Screen.DrawTexture(HealthTex[8],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowLeftF,bhw,DTA_WindowRightF,hwl); - } - } - double ft = clamp(FuelInter.GetValue(fractic),0,120); - Screen.DrawTexture(FuelTex[swwm_superfuel],false,margin+3+ox,ss.y-(margin+7+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,ft,DTA_ColorOverlay,Color(255,0,0,0)); - Screen.DrawTexture(FuelTex[swwm_superfuel],false,margin+2+ox,ss.y-(margin+8+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,ft); - let d = Demolitionist(CPlayer.mo); - bool blink = (!d || (d.dashfuel > 20) || ((gametic%10) < 5)); - double dt = clamp(DashInter.GetValue(fractic),0,120); - Screen.DrawTexture(DashTex,false,margin+3+ox,ss.y-(margin+4+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,dt,DTA_ColorOverlay,Color(255,0,0,0)); - Screen.DrawTexture(DashTex,false,margin+2+ox,ss.y-(margin+5+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,dt,DTA_ColorOverlay,Color(blink?0:96,0,0,0)); - } - - private void DrawPickups() - { - int h = mSmallFont.GetHeight(); - // draw nametags below them - double yy; - double nalph = 0.; - double tagtime = (ntagtic+70)-(level.totaltime+fractic); - if ( (ntagstr != "") && (tagtime > 0) ) - { - nalph = clamp(tagtime/20.,0.,1.); - yy = ss.y-(margin+50); - // shift up if boss healthbar is present - if ( hnd && (hnd.bossalpha > 0.) ) yy -= int(40*clamp(hnd.bossalpha*2.,0.,1.)); - int len = mSmallFont.StringWidth(ntagstr); - double xx = (ss.x-len)/2.; - Screen.Dim("Black",.8*nalph,int((xx-6)*hs),int(yy*hs),int((len+12)*hs),int((h+4)*hs)); - Screen.DrawText(mSmallFont,ntagcol,int(xx),yy+2,ntagstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,nalph); - } - if ( PickupQueue.Size() <= 0 ) return; - // reverse order since they're drawn bottom to top - int mend = max(0,PickupQueue.Size()-MAXPICKUP); - yy = ss.y-(margin+50); - // shift up if boss healthbar is present - if ( hnd && (hnd.bossalpha > 0.) ) yy -= int(40*clamp(hnd.bossalpha*2.,0.,1.)); - // shift up again if nametag is present - if ( nalph > 0. ) yy -= int((mSmallFont.GetHeight()+6)*clamp(nalph*2.,0.,1.)); - for ( int i=PickupQueue.Size()-1; i>=mend; i-- ) - { - PickupQueue[i].UpdateText(int(ss.x*.75)); - double curtime = (PickupQueue[i].tic+GameTicRate*PICKDURATION)-(level.totaltime+fractic); - double alph = clamp(curtime/20.,0.,1.); - let l = PickupQueue[i].l; - int maxlen = 0; - for ( int j=0; j maxlen ) maxlen = len; - } - double xx = (ss.x-maxlen)/2.; - Screen.Dim("Black",.8*alph,int((xx-6)*hs),int((yy-h*(l.Count()-1))*hs),int((maxlen+12)*hs),int((h*l.Count()+4)*hs)); - for ( int j=l.Count()-1; j>=0; j-- ) - { - int len = mSmallFont.StringWidth(l.StringAt(j)); - xx = int((ss.x-len)/2.); - Screen.DrawText(mSmallFont,msg0color,xx,yy+2,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); - yy -= h; - } - yy -= 6; - } - } - - private void DrawMessages( double boxalph = 1. ) - { - double xx, yy; - if ( midstr != "" ) - { - double ssp = (midtype&1)?.5:1.; - double hsp = (midtype&1)?2.:1.; - int col = (midtype&2)?msgmidcolor2:msgmidcolor; - double curtime = (midtic+int(GameTicRate*con_midtime))-(level.totaltime+fractic); - double alph = clamp(curtime/20.,0.,1.); - if ( !midl || (midsz != int(ss.x*ssp)) ) - { - if ( midl ) midl.Destroy(); - midl = mSmallFont.BreakLines(midstr,int(ss.x*ssp)); - } - int h = mSmallFont.GetHeight(); - int maxlen = 0; - for ( int i=0; i=gametic)?MAXSHOWNBIG:MAXSHOWN)); - xx = margin; - yy = margin; - bool smol = (ss.x<640); - Screen.DrawTexture(ChatTex[smol?3:0],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,boxalph); - yy += 2; - for ( int i=mstart; i 1) || forceamt ) + { + String nstr; + if ( (i.Amount > 99999) && !forceamt ) nstr = "99999"; + else nstr = String.Format("%d",i.Amount); + int len = MiniHudFontOutline.StringWidth(nstr); + Screen.DrawText(MiniHudFontOutline,mhudfontcol[(i.Amount<=0)?MCR_RED:selected?MCR_BRASS:MCR_WHITE],(xx+30)-len,(yy+30)-6,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alpha); + } + return true; + } + + private void DrawInventory() + { + int invy = 61; + // active items (armor / powerups) + double xx = margin+2; + double yy = ss.y-(margin+invy+9); + bool drewarmor = false; + for ( Inventory i=CPlayer.mo.Inv; i; i=i.Inv ) + { + if ( (i.Amount <= 0) || (!(i is 'SWWMArmor') && !(i is 'BasicArmor')) ) continue; + if ( !DrawInvIcon(i,xx,yy,forceamt:true,selected:true) ) continue; + yy -= 34; + drewarmor = true; + } + yy = ss.y-(margin+invy+9); + if ( drewarmor ) + { + xx += 36; + if ( CPlayer.mo.InvSel && !isInventoryBarVisible() ) yy -= 34; + } + for ( Inventory i=CPlayer.mo.Inv; i; i=i.Inv ) + { + if ( (i is 'SWWMLamp') && SWWMLamp(i).bActivated ) + { + DrawInvIcon(i,xx,yy,selected:true,aspowerup:true); + yy -= 34; + continue; + } + if ( (i is 'DivineSpriteEffect') && !DivineSpriteEffect(i).bHealDone ) + { + DrawInvIcon(i,xx,yy,selected:true,aspowerup:true); + yy -= 34; + continue; + } + if ( !(i is 'Powerup') || (Powerup(i).EffectTics <= 0) || !(Powerup(i).Icon) ) continue; + if ( DrawInvIcon(i,xx,yy) ) + yy -= 34; + } + // inventory box / bar + if ( !CPlayer.mo.InvSel ) return; + if ( isInventoryBarVisible() ) + { + Array bar; + bar.Clear(); + for ( Inventory i=CPlayer.mo.FirstInv(); i; i=i.NextInv() ) bar.Push(i); + int ps = bar.Find(CPlayer.mo.InvSel); + Inventory prev[2], next[2]; + if ( bar.Size() > 1 ) + { + if ( ps+1 >= bar.Size() ) next[0] = bar[0]; + else next[0] = bar[ps+1]; + if ( ps-1 < 0 ) prev[0] = bar[bar.Size()-1]; + else prev[0] = bar[ps-1]; + } + if ( bar.Size() > 2 ) + { + if ( ps+2 >= bar.Size() ) next[1] = bar[(ps+2)-bar.Size()]; + else next[1] = bar[ps+2]; + if ( ps-2 < 0 ) prev[1] = bar[bar.Size()+(ps-2)]; + else prev[1] = bar[ps-2]; + } + xx = (ss.x-34)/2; + yy = (ss.y+64)/2; + Screen.DrawTexture(InventoryTex,false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + DrawInvIcon(CPlayer.mo.InvSel,xx+2,yy+2,selected:true); + DrawInvIcon(prev[0],xx-32,yy+2,2./3.); + DrawInvIcon(prev[1],xx-66,yy+2,1./3.); + DrawInvIcon(next[0],xx+36,yy+2,2./3.); + DrawInvIcon(next[1],xx+70,yy+2,1./3.); + return; + } + Screen.DrawTexture(InventoryTex,false,margin+36,ss.y-(margin+invy+2),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + DrawInvIcon(CPlayer.mo.InvSel,margin+38,ss.y-(margin+invy),selected:true); + } + + override void DrawPowerups() + { + // don't do anything, handled by inventory drawer + } +} diff --git a/zscript/hud/swwm_hud_messages.zsc b/zscript/hud/swwm_hud_messages.zsc new file mode 100644 index 000000000..cacbaec74 --- /dev/null +++ b/zscript/hud/swwm_hud_messages.zsc @@ -0,0 +1,391 @@ +// messages (notifications, chat, pickups, midprint...) +extend Class SWWMStatusBar +{ + override void FlushNotify() + { + if ( level.maptime <= 1 ) + { + // flush ALL messages + MainQueue.Clear(); + PickupQueue.Clear(); + midstr = ""; + midtic = 0; + ntagstr = ""; + ntagtic = 0; + return; + } + // flush non-chat messages and nametag + for ( int i=0; i= PRINT_CHAT ) continue; + MainQueue.Delete(i); + i--; + } + ntagstr = ""; + ntagtic = 0; + } + + override bool ProcessMidPrint( Font fnt, String msg, bool bold ) + { + // check for Korax lines, add them to chat (and reply to some of them) + bool koraxline = false; + if ( msg == StringTable.Localize("$TXT_ACS_MAP02_9_GREET") ) + koraxline = true; + else if ( msg == StringTable.Localize("$TXT_ACS_MAP02_11_AREYO") ) + { + EventHandler.SendNetworkEvent("swwmkoraxline",0,consoleplayer); + koraxline = true; + SWWMDialogues.StartSeq(SWWMDLG_GREET); + } + else if ( msg == StringTable.Localize("$TXT_ACS_MAP13_11_MYSER") ) + { + EventHandler.SendNetworkEvent("swwmkoraxline",1,consoleplayer); + koraxline = true; + SWWMDialogues.StartSeq(SWWMDLG_BLOOD); + } + else if ( msg == StringTable.Localize("$TXT_ACS_MAP22_27_YOUHA") ) + koraxline = true; + else if ( msg == StringTable.Localize("$TXT_ACS_MAP22_29_ITHIN") ) + { + EventHandler.SendNetworkEvent("swwmkoraxline",2,consoleplayer); + koraxline = true; + SWWMDialogues.StartSeq(SWWMDLG_GAME); + } + else if ( msg == StringTable.Localize("$TXT_ACS_MAP27_8_WORSH") ) + koraxline = true; + else if ( msg == StringTable.Localize("$TXT_ACS_MAP27_10_THENA") ) + { + EventHandler.SendNetworkEvent("swwmkoraxline",3,consoleplayer); + koraxline = true; + SWWMDialogues.StartSeq(SWWMDLG_WORSHIP); + } + else if ( msg == StringTable.Localize("$TXT_ACS_MAP35_12_AREYO") ) + koraxline = true; + else if ( msg == StringTable.Localize("$TXT_ACS_MAP35_14_TOFAC") ) + { + EventHandler.SendNetworkEvent("swwmkoraxline",4,consoleplayer); + koraxline = true; + SWWMDialogues.StartSeq(SWWMDLG_MASTERS); + } + if ( koraxline ) + { + Console.PrintfEx(PRINT_CHAT,"\cmKorax\c-: "..msg); + return true; + } + bool ispuzzle = false; + let s = SWWMStats.Find(players[consoleplayer]); + if ( s ) + { + puzzlecnt = s.puzzlecnt; + realpuzzlecnt = s.realpuzzlecnt; + } + // check for puzzle solving lines (oh god why), and increment the achievement + if ( ((level.mapname ~== "MAP04") || (level.mapname ~== "MAP05")) + && ((msg == StringTable.Localize("$TXT_ACS_MAP04_9_ONEHA")) + || (msg == StringTable.Localize("$TXT_ACS_MAP04_11_ONETH")) + || (msg == StringTable.Localize("$TXT_ACS_MAP05_6_ONETH"))) ) + { + if ( puzzlecnt >= 4 ) puzzlecnt = 0; + puzzlecnt++; + realpuzzlecnt++; + ispuzzle = true; + } + else if ( ((level.mapname ~== "MAP08") || (level.mapname ~== "MAP09") || (level.mapname ~== "MAP10")) + && ((msg == StringTable.Localize("$TXT_ACS_MAP08_6_ONESI")) + || (msg == StringTable.Localize("$TXT_ACS_MAP09_6_ONESI")) + || (msg == StringTable.Localize("$TXT_ACS_MAP10_6_ONESI"))) ) + { + if ( (puzzlecnt < 4) || (puzzlecnt >= 10) ) puzzlecnt = 4; + puzzlecnt++; + realpuzzlecnt++; + ispuzzle = true; + } + else if ( ((level.mapname ~== "MAP28") || (level.mapname ~== "MAP30") || (level.mapname ~== "MAP34")) + && ((msg == StringTable.Localize("$TXT_ACS_MAP28_6_ONENI")) + || (msg == StringTable.Localize("$TXT_ACS_MAP30_6_ONENI")) + || (msg == StringTable.Localize("$TXT_ACS_MAP34_1_ONENI"))) ) + { + if ( (puzzlecnt < 10) || (puzzlecnt >= 19) ) puzzlecnt = 10; + puzzlecnt++; + realpuzzlecnt++; + ispuzzle = true; + } + // deathkings + else if ( ((level.mapname ~== "MAP44") || (level.mapname ~== "MAP46")) + && ((msg == StringTable.Localize("$TXT_ACS_MAP44_1_THREE")) + || (msg == StringTable.Localize("$TXT_ACS_MAP44_2_TWOMO")) + || (msg == StringTable.Localize("$TXT_ACS_MAP44_3_ONEMO")) + || (msg == StringTable.Localize("$TXT_ACS_MAP44_4_THEPU")) + || (msg == StringTable.Localize("$TXT_ACS_MAP44_10_ONETH")) + || (msg == StringTable.Localize("$TXT_ACS_MAP44_11_TWOTH")) + || (msg == StringTable.Localize("$TXT_ACS_MAP46_8_ONEFO"))) ) + { + if ( (puzzlecnt < 19) || (puzzlecnt >= 30) ) puzzlecnt = 19; + puzzlecnt++; + realpuzzlecnt++; + ispuzzle = true; + } + else if ( (level.mapname ~== "MAP51") + && ((msg == StringTable.Localize("$TXT_ACS_MAP51_8_ONETH")) + || (msg == StringTable.Localize("$TXT_ACS_MAP51_9_TWOTH")) + || (msg == StringTable.Localize("$TXT_ACS_MAP51_10_THECR"))) ) + { + if ( (puzzlecnt < 30) || (puzzlecnt >= 34) ) puzzlecnt = 30; + puzzlecnt++; + realpuzzlecnt++; + ispuzzle = true; + } + if ( ispuzzle ) + { + EventHandler.SendNetworkEvent("swwmstorepuzzlecnt",consoleplayer,puzzlecnt,realpuzzlecnt); + int tpuz = SWWMUtility.IsDeathkings()?15:19; + if ( realpuzzlecnt >= tpuz ) SWWMUtility.MarkAchievement("puzzle",players[consoleplayer]); + if ( !swwm_nomapmsg ) + { + switch ( puzzlecnt ) + { + case 1: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE1); + break; + case 2: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE2); + break; + case 3: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE3); + break; + case 5: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE4); + break; + case 8: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE5); + break; + case 11: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE6); + break; + case 20: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE7); + break; + case 30: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE8); + break; + case 31: + SWWMDialogues.StartSeq(SWWMDLG_PUZZLE9); + break; + } + } + } + // rampancy fun stuff + bool mainframeline = false; + if ( (msg == StringTable.Localize("$AISPAWN_TEXT1")) + || (msg == StringTable.Localize("$AISPAWN_TEXT2")) + || (msg == StringTable.Localize("$AISEE_TEXT")) + || (msg == StringTable.Localize("$AIACTIVE_TEXT1")) + || (msg == StringTable.Localize("$AIACTIVE_TEXT2")) + || (msg == StringTable.Localize("$AIPAIN_TEXT1")) + || (msg == StringTable.Localize("$AIPAIN_TEXT2")) + || (msg == StringTable.Localize("$AIPAIN_TEXT3")) + || (msg == StringTable.Localize("$AIPAIN_TEXT4")) + || (msg == StringTable.Localize("$AIPAIN_TEXT5")) + || (msg == StringTable.Localize("$AIPAIN_TEXT6")) + || (msg == StringTable.Localize("$AIPAIN_TEXT7")) ) + mainframeline = true; + if ( mainframeline ) + { + Console.PrintfEx(PRINT_CHAT,"\cmAI Mainframe\c-: "..msg); + return true; + } + if ( !fnt || (fnt == smallfont) ) + { + midstr = msg; + midtic = level.totaltime; + midtype = bold?2:0; + return true; + } + if ( (fnt == bigfont) || (fnt == originalbigfont) ) + { + midstr = msg; + midtic = level.totaltime; + midtype = bold?3:1; + return true; + } + return false; + } + + override bool ProcessNotify( EPrintLevel printlevel, String outline ) + { + // ignore outside of levels + if ( gamestate != GS_LEVEL ) return false; + int rprintlevel = printlevel&PRINT_TYPES; + // treat any unrecognized levels as critical messages + // note that checking for PRINT_LOG isn't needed as those are skipped early in the C++ side + if ( (rprintlevel < PRINT_LOW) || (rprintlevel > PRINT_TEAMCHAT) ) rprintlevel = PRINT_HIGH; + // strip trailing newline (all Printf type messages have this) + outline.DeleteLastCharacter(); + // append chat messages to full history + if ( (rprintlevel == PRINT_CHAT) || (rprintlevel == PRINT_TEAMCHAT) ) + EventHandler.SendNetworkEvent("swwmstoremessage."..outline,level.totaltime,rprintlevel,consoleplayer); + let m = new("MsgLine"); + m.str = outline; + m.type = rprintlevel; + m.tic = level.totaltime; + m.rep = 1; + if ( rprintlevel == PRINT_LOW ) + { + // check if repeated + for ( int i=0; i PRINT_HIGH) && (level.totaltime < (MainQueue[i].tic+GameTicRate*CHATDURATION)) ) continue; + MainQueue.Delete(i); + i--; + } + } + + private void DrawPickups() + { + int h = mSmallFont.GetHeight(); + // draw nametags below them + double yy; + double nalph = 0.; + double tagtime = (ntagtic+70)-(level.totaltime+fractic); + if ( (ntagstr != "") && (tagtime > 0) ) + { + nalph = clamp(tagtime/20.,0.,1.); + yy = ss.y-(margin+50); + // shift up if boss healthbar is present + if ( hnd && (hnd.bossalpha > 0.) ) yy -= int(40*clamp(hnd.bossalpha*2.,0.,1.)); + int len = mSmallFont.StringWidth(ntagstr); + double xx = (ss.x-len)/2.; + Screen.Dim("Black",.8*nalph,int((xx-6)*hs),int(yy*hs),int((len+12)*hs),int((h+4)*hs)); + Screen.DrawText(mSmallFont,ntagcol,int(xx),yy+2,ntagstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,nalph); + } + if ( PickupQueue.Size() <= 0 ) return; + // reverse order since they're drawn bottom to top + int mend = max(0,PickupQueue.Size()-MAXPICKUP); + yy = ss.y-(margin+50); + // shift up if boss healthbar is present + if ( hnd && (hnd.bossalpha > 0.) ) yy -= int(40*clamp(hnd.bossalpha*2.,0.,1.)); + // shift up again if nametag is present + if ( nalph > 0. ) yy -= int((mSmallFont.GetHeight()+6)*clamp(nalph*2.,0.,1.)); + for ( int i=PickupQueue.Size()-1; i>=mend; i-- ) + { + PickupQueue[i].UpdateText(int(ss.x*.75)); + double curtime = (PickupQueue[i].tic+GameTicRate*PICKDURATION)-(level.totaltime+fractic); + double alph = clamp(curtime/20.,0.,1.); + let l = PickupQueue[i].l; + int maxlen = 0; + for ( int j=0; j maxlen ) maxlen = len; + } + double xx = (ss.x-maxlen)/2.; + Screen.Dim("Black",.8*alph,int((xx-6)*hs),int((yy-h*(l.Count()-1))*hs),int((maxlen+12)*hs),int((h*l.Count()+4)*hs)); + for ( int j=l.Count()-1; j>=0; j-- ) + { + int len = mSmallFont.StringWidth(l.StringAt(j)); + xx = int((ss.x-len)/2.); + Screen.DrawText(mSmallFont,msg0color,xx,yy+2,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + yy -= h; + } + yy -= 6; + } + } + + private void DrawMessages( double boxalph = 1. ) + { + double xx, yy; + if ( midstr != "" ) + { + double ssp = (midtype&1)?.5:1.; + double hsp = (midtype&1)?2.:1.; + int col = (midtype&2)?msgmidcolor2:msgmidcolor; + double curtime = (midtic+int(GameTicRate*con_midtime))-(level.totaltime+fractic); + double alph = clamp(curtime/20.,0.,1.); + if ( !midl || (midsz != int(ss.x*ssp)) ) + { + if ( midl ) midl.Destroy(); + midl = mSmallFont.BreakLines(midstr,int(ss.x*ssp)); + } + int h = mSmallFont.GetHeight(); + int maxlen = 0; + for ( int i=0; i=gametic)?MAXSHOWNBIG:MAXSHOWN)); + xx = margin; + yy = margin; + bool smol = (ss.x<640); + Screen.DrawTexture(ChatTex[smol?3:0],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,boxalph); + yy += 2; + for ( int i=mstart; i0; i-- ) + LagHealth[i] = hp; + LagHealthInter.Reset(hp); + let d = Demolitionist(CPlayer.mo); + if ( d ) + { + FuelInter.Reset(d.dashfuel/2); + DashInter.Reset((40-d.dashcooldown)*3); + } + else + { + FuelInter.Reset(0); + DashInter.Reset(0); + } + } + + private void TickStatusInterpolators() + { + int hp = CPlayer.Health; + HealthInter.Update(hp); + // flash 'em + if ( hp > LastHealth ) HealthFlash = gametic+25; + // lag + if ( hp > LastHealth ) + { + for ( int i=9; i>0; i-- ) + LagHealth[i] = hp; + } + LagHealth[0] = LastHealth = hp; + LagHealthInter.Update(LagHealth[9]); + for ( int i=9; i>0; i-- ) + LagHealth[i] = LagHealth[i-1]; + // fuel/dash + let d = Demolitionist(CPlayer.mo); + if ( d ) + { + FuelInter.Update(d.dashfuel/2); + DashInter.Update((40-d.dashcooldown)*3); + } + else + { + FuelInter.Update(0); + DashInter.Update(0); + } + } + + private void TickStatus() + { + // low health pulsing + if ( (CPlayer.health <= 0) || (CPlayer.health > 25) ) + PulsePhase = 0; + else + { + PulsePhase--; + if ( (PulsePhase < 0) || (PulsePhase > CPlayer.health*2+25) ) + PulsePhase = CPlayer.health*2+25; + } + } + + private int GetRandom() + { + return (rss = (rss<<1)*35447+(rss/87)); + } + + private double RandomShiver() + { + int sd = GetRandom(); + return ((abs(sd)%11)-5)*.1; + } + + private int GetFaceTex( Demolitionist demo ) + { + let facestate = demo.facestate; + let paindir = demo.paindir; + let facetimer = demo.facetimer; + let blinktime = demo.blinktime; + if ( CPlayer.Health <= 0 ) return 11; + if ( (isInvulnerable() || demo.FindInventory("InvinciballPower")) && (facestate >= FS_PAIN) ) return 12; + if ( facestate == FS_OUCH ) return 10; + if ( facestate == FS_PAIN ) return (paindir==1)?8:(paindir==-1)?9:7; + if ( facestate == FS_GRIN ) return 5; + if ( facestate == FS_EVIL ) return 6; + if ( facestate == FS_SAD ) return 17; + if ( facestate == FS_WINK ) return 18; + if ( facestate == FS_BLINK ) return ((facetimer>28)||(facetimer<2))?3:4; + switch ( blinktime ) + { + case -1: + case -3: + return 3; + break; + case -2: + return 4; + break; + } + return 2; + } + + private void DrawMugshot() + { + rss = MSTime()*128; + let demo = Demolitionist(CPlayer.mo); + if ( !demo ) return; + double paintime = clamp((demo.lastdamagetimer-(gametic+Fractic))/double(GameTicRate),0.,1.); + double noiz = min(demo.lastdamage*.5*paintime,3.); + Vector2 shake = (RandomShiver(),RandomShiver())*noiz; + if ( !CPlayer.mo.FindInventory("GhostPower") ) + { + if ( !tagcolor ) tagcolor = CVar.GetCVar('swwm_tagcolor',CPlayer); + int facecol = tagcolor.GetInt(); + if ( (facecol < 0) || (facecol > 15) ) facecol = 0; + Screen.DrawTexture(FaceTex[0],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcWidth,32,DTA_SrcHeight,32,DTA_DestWidth,32,DTA_DestHeight,32,DTA_SrcX,32*(facecol%4),DTA_SrcY,32*(facecol/4)); + bool raging = CPlayer.mo.FindInventory("RagekitPower"); + bool angy = CPlayer.mo.FindInventory("AngeryPower"); + if ( raging && angy ) Screen.DrawTexture(FaceTex[16],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + else if ( raging ) Screen.DrawTexture(FaceTex[15],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + else if ( angy ) Screen.DrawTexture(FaceTex[13],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(FaceTex[1],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Color,Color(255,255,0,0),DTA_Alpha,min(1.,noiz)); + if ( (CPlayer.Health > 0) && (isInvulnerable() || CPlayer.mo.FindInventory("InvinciballPower")) ) + Screen.DrawTexture(FaceTex[1],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,.8+.1*sin(gametic+fractic)); + } + else + { + Screen.DrawTexture(FaceTex[0],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcWidth,32,DTA_SrcHeight,32,DTA_DestWidth,32,DTA_DestHeight,32,DTA_FillColor,Color(0,0,0),DTA_Alpha,.25*(1.-min(1.,noiz))); + Screen.DrawTexture(FaceTex[1],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_FillColor,Color(255,0,0),DTA_Alpha,.25*min(1.,noiz)); + } + Screen.DrawTexture(FaceTex[GetFaceTex(demo)],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( CPlayer.mo.FindInventory("BarrierPower") ) Screen.DrawTexture(FaceTex[14],false,margin+shake.x,ss.y-(margin+32)+shake.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,.5,DTA_LegacyRenderStyle,STYLE_Add); + } + + private void DrawStatus() + { + DrawMugshot(); + int ox = 36; + int oy = 5; + Screen.DrawTexture(StatusTex,false,margin+ox,ss.y-(margin+22+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + String str; + double ht = clamp(HealthInter.GetValue(fractic),0,10000); + str = String.Format("%3d",clamp(round(ht),0,999)); + double hw = min(ht,100); + double bhw = hw; + int hcolor = MCR_RED; + if ( round(ht) > 500 ) hcolor = MCR_YELLOW; + else if ( round(ht) > 200 ) hcolor = MCR_PURPLE; + else if ( round(ht) > 100 ) hcolor = MCR_AQUA; + if ( isInvulnerable() || CPlayer.mo.FindInventory("InvinciballPower") ) + { + Screen.DrawTexture(HealthTex[0],false,margin+3+ox,ss.y-(margin+19+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw,DTA_ColorOverlay,Color(255,0,0,0)); + Screen.DrawTexture(HealthTex[4],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); + hcolor = MCR_WHITE; + } + else + { + Screen.DrawTexture(HealthTex[0],false,margin+3+ox,ss.y-(margin+19+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw,DTA_ColorOverlay,Color(255,0,0,0)); + Screen.DrawTexture(HealthTex[0],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); + if ( ht > 100 ) + { + hw = min(ht-100,100); + Screen.DrawTexture(HealthTex[1],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); + } + if ( ht > 200 ) + { + hw = min(ht-200,300)/3.; + Screen.DrawTexture(HealthTex[2],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); + } + if ( ht > 500 ) + { + hw = min(ht-500,500)/5.; + Screen.DrawTexture(HealthTex[3],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw); + } + } + if ( CPlayer.mo.FindInventory("DivineSpriteEffect") ) + { + double falph = clamp((ht-1000)/6000.,0.,1.); + Screen.DrawTexture(HealthTex[5],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,falph,DTA_LegacyRenderStyle,STYLE_Add); + String tst; + double alph = .1; + int trl = 9; + for ( double alph = .1; alph <= .5; alph += .1 ) + { + tst = "AAA"; + SWWMUtility.ObscureText(tst,(gametic-trl)/3,true); + trl--; + Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_WHITE],margin+107+ox,ss.y-(margin+20+oy),tst,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,falph*alph,DTA_LegacyRenderStyle,STYLE_Add); + } + Screen.DrawText(MiniHUDFont,mhudfontcol[hcolor],margin+107+ox,ss.y-(margin+20+oy),String.Format("%3d",clamp(round(ht),0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,1.-falph); + } + else + { + Screen.DrawText(MiniHUDFont,mhudfontcol[hcolor],margin+107+ox,ss.y-(margin+20+oy),String.Format("%3d",clamp(round(ht),0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int f = HealthFlash; + if ( f && (gametic < f) ) + { + double alph = max((f-(gametic+FracTic))/25.,0.)**1.5; + Screen.DrawTexture(HealthTex[7],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,bhw,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_FLASH],margin+107+ox,ss.y-(margin+20+oy),str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + if ( (CPlayer.health > 0) && (CPlayer.health <= 25) && (PulsePhase <= 15) && (hcolor != MCR_WHITE) ) + { + double alph = clamp(sin((PulsePhase-FracTic)*12.),0.,1.); + Screen.DrawTexture(HealthTex[6],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,hw,DTA_Alpha,alph); + Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_REDFLASH],margin+107+ox,ss.y-(margin+20+oy),str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + } + ht = clamp(LagHealthInter.GetValue(fractic),0,1000); + double hwl = min(ht,100); + if ( hwl > bhw ) + { + Screen.DrawTexture(HealthTex[8],false,margin+3+ox,ss.y-(margin+19+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowLeftF,bhw,DTA_WindowRightF,hwl,DTA_ColorOverlay,Color(255,0,0,0)); + Screen.DrawTexture(HealthTex[8],false,margin+2+ox,ss.y-(margin+20+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowLeftF,bhw,DTA_WindowRightF,hwl); + } + } + double ft = clamp(FuelInter.GetValue(fractic),0,120); + Screen.DrawTexture(FuelTex[swwm_superfuel],false,margin+3+ox,ss.y-(margin+7+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,ft,DTA_ColorOverlay,Color(255,0,0,0)); + Screen.DrawTexture(FuelTex[swwm_superfuel],false,margin+2+ox,ss.y-(margin+8+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,ft); + let d = Demolitionist(CPlayer.mo); + bool blink = (!d || (d.dashfuel > 20) || ((gametic%10) < 5)); + double dt = clamp(DashInter.GetValue(fractic),0,120); + Screen.DrawTexture(DashTex,false,margin+3+ox,ss.y-(margin+4+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,dt,DTA_ColorOverlay,Color(255,0,0,0)); + Screen.DrawTexture(DashTex,false,margin+2+ox,ss.y-(margin+5+oy),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,dt,DTA_ColorOverlay,Color(blink?0:96,0,0,0)); + } +} diff --git a/zscript/hud/swwm_hud_target.zsc b/zscript/hud/swwm_hud_target.zsc new file mode 100644 index 000000000..0fc51bee4 --- /dev/null +++ b/zscript/hud/swwm_hud_target.zsc @@ -0,0 +1,386 @@ +// Targeter +extend Class SWWMStatusBar +{ + + static private string FormatDist( double dist ) + { + double meters = dist/32.; + if ( meters > 1000. ) return String.Format("\cj%d\cc%s",int(meters/1000.),StringTable.Localize("$SWWM_UNIT_KILOMETER")); + return String.Format("\cj%d\cc%s",int(meters),StringTable.Localize("$SWWM_UNIT_METER")); + } + + private void DrawInterest( Vector3 viewvec, out bool projinit ) + { + String tag; + SWWMInterest poi = hnd.intpoints; + if ( !poi ) return; + do + { + // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing + if ( !projinit ) + { + projinit = true; + SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); + Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); + } + Vector3 tdir = level.Vec3Diff(ViewPos,poi.pos); + if ( viewvec dot tdir < 0 ) continue; + Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); + if ( ndc.z >= 1. ) continue; + Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs2; + if ( poi.type == INT_Key ) tag = String.Format("\cf%s\c-",poi.keytag); + else if ( poi.type == INT_Exit ) + { + if ( (poi.trackedline.special == Teleport_EndGame) + || ((poi.trackedline.special == Exit_Secret) && (level.nextsecretmap.Left(6) == "enDSeQ")) + || ((poi.trackedline.special == Exit_Normal) && (level.nextmap.Left(6) == "enDSeQ")) ) + tag = String.Format("\cg%s\c-",StringTable.Localize("$SWWM_EEXIT")); + else if ( poi.trackedline.special == Exit_Secret ) + { + LevelInfo l = LevelInfo.FindLevelInfo(level.nextsecretmap); + if ( l && l.isValid() ) tag = String.Format("\cx%s:\c- %s\c-",StringTable.Localize("$SWWM_SEXIT"),l.LookupLevelName()); + else tag = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_SEXIT")); + } + else if ( (poi.trackedline.special == Exit_Normal) || ((poi.trackedline.special == ACS_Execute) && (poi.trackedline.Args[0] == -Int('E1M8_KNOCKOUT'))) ) + { + LevelInfo l = LevelInfo.FindLevelInfo(level.nextmap); + if ( l && l.isValid() ) tag = String.Format("\cy%s:\c- %s\c-",StringTable.Localize("$SWWM_NEXIT"),l.LookupLevelName()); + else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT")); + } + else if ( poi.trackedline.special == Teleport_NewMap ) + { + LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[0]); + if ( l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName()); + else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT")); + } + else if ( ((poi.trackedline.special == ACS_Execute) || (poi.trackedline.special == ACS_ExecuteAlways)) && (poi.trackedline.Args[0] == -Int('MapFadeOut')) ) + { + LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[2]); + if ( (level.levelnum != 1) && l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName()); + else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT")); + } + } + Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y-mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true); + tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length())); + Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true); + } + while ( poi = poi.next ); + } + + private int GetItemFontColor( SWWMitemSense s ) + { + let col = s.scoreitem?Font.CR_YELLOW:Font.CR_WHITE; + let i = (s.item is 'SWWMRespawnTimer')?s.item.tracer:s.item; + if ( i is 'Weapon' ) col = s.vipitem?Font.FindFontColor('VIPGold'):Font.CR_GOLD; + else if ( i is 'MagAmmo' ) col = s.vipitem?Font.FindFontColor('VIPTan'):Font.CR_TAN; + else if ( (i is 'BackpackItem') || (i is 'HammerspaceEmbiggener') ) col = Font.CR_DARKBROWN; + else if ( i is 'Ammo' ) col = s.vipitem?Font.FindFontColor('VIPBrown'):Font.CR_BROWN; + else if ( (i is 'PowerupGiver') || (i is 'AmmoFabricator') || Inventory(i).bBIGPOWERUP ) col = s.vipitem?Font.FindFontColor('VIPPurple'):Font.CR_PURPLE; + else if ( (i is 'Health') || (i is 'HealthPickup') || (i is 'SWWMHealth') ) col = Font.CR_RED; + else if ( (i is 'Armor') || (i is 'SWWMSpareArmor') ) col = Font.CR_GREEN; + else if ( i is 'PuzzleItem' ) col = Font.CR_LIGHTBLUE; + else if ( i is 'Key' ) col = Font.CR_UNTRANSLATED; + else if ( i is 'SWWMCollectible' ) col = Font.CR_PURPLE; + return col; + } + + private void DrawItemSense( Vector3 viewvec, out bool projinit ) + { + let demo = Demolitionist(CPlayer.mo); + if ( !demo ) return; + SWWMItemSense s = demo.itemsense; + if ( !s ) return; + do + { + if ( !s.item ) continue; + Vector3 tdir = level.Vec3Diff(ViewPos,s.pos); + if ( viewvec dot tdir < 0 ) continue; + Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); + if ( ndc.z >= 1. ) continue; + Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; + int mtime = level.allmap?(GameTicRate*2):GameTicRate; + double alph = clamp(((s.updated+mtime)-(level.maptime+fractic))/double(GameTicRate),0.,1.); + alph *= clamp(1.5-1.5*(tdir.length()/(level.allmap?1200.:800.)),0.,1.); + String tag = s.tag; + Screen.DrawText(mTinyFontOutline,GetItemFontColor(s),vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y-mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length())); + Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + if ( s.item is 'SWWMRespawnTimer' ) + { + tag = String.Format(StringTable.Localize("$SWWM_TRESPAWN"),s.item.special2/GameTicRate); + Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()*2,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + } + } + while ( s = s.next ); + } + + private bool IsLegendary( Actor a ) + { + for ( Inventory i=a.inv; i; i=i.inv ) + { + if ( (i.GetClassName() == "LDLegendaryMonsterToken") && swwm_ldspoil ) return true; + else if ( i.GetClassName() == "LDLegendaryMonsterTransformed" ) return true; + } + return false; + } + + private void DrawTrackers( Vector3 viewvec, out bool projinit ) + { + let cam = players[consoleplayer].camera; + if ( !cti ) cti = ThinkerIterator.Create("SWWMQuickCombatTracker",Thinker.STAT_INVENTORY); + else cti.Reinit(); + SWWMQuickCombatTracker ct; + bool onlymonsters = (swwm_targeter >= 2); + bool onlyfriends = (swwm_targeter >= 3); + int fadedist = swwm_bardist; + while ( ct = SWWMQuickCombatTracker(cti.Next()) ) + { + // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing + if ( !projinit ) + { + projinit = true; + SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); + Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); + } + // ignore unowned (can happen?) + if ( !ct.Owner ) continue; + // ignore if max health is zero (SOMEHOW can happen) + if ( ct.maxhealth <= 0 ) continue; + // ignore player trackers unless voodoo dolls + if ( ct.Owner.player && (ct.Owner.player.mo == ct.Owner) ) continue; + // ignore local player or camera + if ( (ct.Owner == CPlayer.mo) || (ct.Owner == cam) ) continue; + // ignore trackers not of this player + if ( ct.myplayer != CPlayer ) continue; + // ignore non-monsters if filtering monsters + // easy check since they have empty tags + if ( onlymonsters && (ct.mytag == "") ) continue; + // ignore enemies if filtering friends + if ( onlyfriends && (!ct.Owner.IsFriend(CPlayer.mo) || ct.Owner.player) ) continue; + // ignore trackers clearly outside of player view + Vector3 smpos = level.Vec3Offset(SWWMUtility.LerpVector3(ct.Owner.prev,ct.Owner.pos,fractic),(0,0,ct.lvheight)); + Vector3 tdir = level.Vec3Diff(viewpos,smpos); + if ( viewvec dot tdir < 0 ) continue; + // ignore trackers that are too far away + double dist = tdir.length(); + if ( (fadedist > 0) && (dist > fadedist*1.5) ) continue; + Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir); + if ( ndc.z >= 1. ) continue; + Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; + if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue; + double fin = clamp(ct.fadein+fractic,0.,5.)/5.; + double fout = clamp(ct.lifespan-fractic,0.,25.)/25.; + double alph = fin*fout; + if ( fadedist > 0 ) + { + double df = 1.-(clamp((dist-fadedist)/fadedist,0.,.5)*2.); + alph *= df; + } + Vector2 barpos = vpos-(27,7); + if ( swwm_targettags && (ct.mytag != "") ) + { + Font fnt = swwm_bigtags?mSmallFontOutline:mTinyFontOutline; + String tag = ct.mytag; + if ( IsLegendary(ct.Owner) ) + { + if ( StringTable.Localize("$SWWM_LEGPREFIX") == "R" ) tag = tag..StringTable.Localize("$SWWM_LEG"); + else tag = StringTable.Localize("$SWWM_LEG")..tag; + } + if ( ct.Owner.bBOSS || ct.Owner.FindInventory("BossMarker") ) + { + if ( swwm_bigtags ) tag = "\cx★\c- "..tag.." \cx★\c-"; + else tag = "\cx*\c- "..tag.." \cx*\c-"; // miniwi has no stars + } + // voodoo dolls aren't friends + if ( ct.Owner.IsFriend(CPlayer.mo) && !ct.Owner.player ) tag = "\cg♥\c- "..tag.." \cg♥\c-"; + Screen.DrawText(fnt,Font.CR_WHITE,vpos.x-fnt.StringWidth(tag)/2,barpos.y-(fnt.GetHeight()+2),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + } + Screen.DrawTexture(EnemyBTex,false,barpos.x,barpos.y,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + double ht = clamp(ct.intp.GetValue(fractic),0,ct.maxhealth); + double hw = (ht*50.)/ct.maxhealth; + Screen.DrawTexture(EnemyHTex[ct.Owner.bINVULNERABLE?1:0],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); + double ohw = hw; + ht = clamp(ct.intpl.GetValue(fractic),0,ct.maxhealth); + hw = (ht*50.)/ct.maxhealth; + Screen.DrawTexture(EnemyHTex[2],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowLeftF,ohw,DTA_WindowRightF,hw); + if ( ct.cummdamage <= 0 ) continue; + double calph = clamp(ct.cummspan-fractic,0.,20.)/20.; + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_RED],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,calph*alph); + if ( ct.cummflash <= 0 ) continue; + double falph = max((ct.cummflash-FracTic)/15.,0.)**1.5; + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_REDFLASH],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,falph*calph*alph,DTA_LegacyRenderStyle,STYLE_Add); + } + // player-owned trackers are drawn last + cti.Reinit(); + while ( ct = SWWMQuickCombatTracker(cti.Next()) ) + { + // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing + if ( !projinit ) + { + projinit = true; + SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); + Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); + } + // ignore unowned (can happen?) + if ( !ct.Owner ) continue; + // ignore if max health is zero (SOMEHOW can happen) + if ( ct.maxhealth <= 0 ) continue; + // ignore non-player trackers and voodoo dolls + if ( !ct.Owner.player || (ct.Owner.player.mo != ct.Owner) ) continue; + // ignore local player or camera + if ( (ct.Owner == CPlayer.mo) || (ct.Owner == cam) ) continue; + // ignore trackers not of this player + if ( ct.myplayer != CPlayer ) continue; + // ignore enemies if filtering friends + if ( onlyfriends && !ct.Owner.IsFriend(CPlayer.mo) ) continue; + // ignore trackers clearly outside of player view + Vector3 smpos = level.Vec3Offset(SWWMUtility.LerpVector3(ct.Owner.prev,ct.Owner.pos,fractic),(0,0,ct.lvheight)); + Vector3 tdir = level.Vec3Diff(viewpos,smpos); + if ( viewvec dot tdir < 0 ) continue; + Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir); + if ( ndc.z >= 1. ) continue; + Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; + if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue; + double fin = clamp(ct.fadein+fractic,0.,5.)/5.; + double fout = clamp(ct.lifespan-fractic,0.,25.)/25.; + double alph = fin*fout; + Vector2 barpos = vpos-(27,7); + Font fnt = swwm_bigtags?mSmallFontOutline:mTinyFontOutline; + int col = Font.CR_WHITE; + if ( teamplay ) + { + int team = ct.Owner.player.GetTeam(); + if ( team != -1 ) col = Font.FindFontColor(Teams[team].mName); // this works in practice because team names are the same as their text colors + if ( col == -1 ) col = Font.CR_WHITE; + } + Screen.DrawText(fnt,col,vpos.x-fnt.StringWidth(ct.mytag)/2,barpos.y-(fnt.GetHeight()+2),ct.mytag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + Screen.DrawTexture(EnemyBTex,false,barpos.x,barpos.y,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + double ht = clamp(ct.intp.GetValue(fractic),0,ct.maxhealth*100); + double hw = (ht*50.)/ct.maxhealth; + double ohw = hw; + if ( ct.Owner.bINVULNERABLE || (ct.Owner.player.cheats&(CF_GODMODE|CF_GODMODE2)) || ct.Owner.FindInventory("InvinciballPower") ) + Screen.DrawTexture(EnemyHTex[1],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); + else + { + Screen.DrawTexture(EnemyHTex[0],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); + if ( ht > ct.maxhealth ) + { + hw = (min(ht-ct.maxhealth,ct.maxhealth)*50.)/ct.maxhealth; + Screen.DrawTexture(EnemyHTex[3],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); + } + if ( ht > ct.maxhealth*2 ) + { + hw = (min(ht-ct.maxhealth*2,ct.maxhealth*3)*50.)/ct.maxhealth; + Screen.DrawTexture(EnemyHTex[4],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); + } + if ( ht > ct.maxhealth*5 ) + { + hw = (min(ht-ct.maxhealth*5,ct.maxhealth*5)*50.)/ct.maxhealth; + Screen.DrawTexture(EnemyHTex[5],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw); + } + } + if ( ct.Owner.FindInventory("DivineSpriteEffect") ) + { + double falph = clamp((ht-ct.maxhealth*10)/(ct.maxhealth*60.),0.,1.); + Screen.DrawTexture(EnemyHTex[6],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph*falph,DTA_LegacyRenderStyle,STYLE_Add); + } + else + { + ht = clamp(ct.intpl.GetValue(fractic),0,ct.maxhealth); + hw = (ht*50.)/ct.maxhealth; + Screen.DrawTexture(EnemyHTex[2],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowLeftF,ohw,DTA_WindowRightF,hw); + } + if ( ct.cummdamage <= 0 ) continue; + double calph = clamp(ct.cummspan-fractic,0.,20.)/20.; + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_RED],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,calph*alph); + if ( ct.cummflash <= 0 ) continue; + double falph = max((ct.cummflash-FracTic)/15.,0.)**1.5; + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_REDFLASH],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,falph*calph*alph,DTA_LegacyRenderStyle,STYLE_Add); + } + } + + private void DrawNumbers( Vector3 viewvec, out bool projinit ) + { + SWWMDamNum snum = hnd.damnums; + if ( !snum ) return; + do + { + // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing + if ( !projinit ) + { + projinit = true; + SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); + Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); + } + Vector3 tdir = level.Vec3Diff(ViewPos,snum.pos); + if ( viewvec dot tdir < 0 ) continue; + Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); + if ( ndc.z >= 1. ) continue; + Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs; + String tag = abs(snum.damage>=Actor.TELEFRAG_DAMAGE)?(snum.damage>0)?"-∞":"+∞":String.Format("%+d",-snum.damage); + double alph = clamp((snum.lifespan+fractic)/35.,0.,1.); + Vector2 fo; + int initspd = (128-snum.seed); + int boostup = 64+snum.seed2; + fo.x = (.05*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.8); + fo.y = -((snum.initialspan-(snum.lifespan-fractic))**1.5)+boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic)); + Screen.DrawText(MiniHUDFontOutline,snum.tcolor,(vpos.x-fo.x)-(MiniHUDFontOutline.StringWidth(tag))/2,(vpos.y-fo.y)-(MiniHUDFontOutline.GetHeight())/2,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + } + while ( snum = snum.next ); + } + + private void DrawScores( Vector3 viewvec, out bool projinit ) + { + SWWMScoreObj snum = hnd.scorenums; + if ( !snum ) return; + do + { + // this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing + if ( !projinit ) + { + projinit = true; + SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov); + Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh); + } + Vector3 tdir = level.Vec3Diff(ViewPos,snum.pos); + if ( viewvec dot tdir < 0 ) continue; + Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir); + if ( ndc.z >= 1. ) continue; + Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1; + String tag = String.Format("%+d",snum.score); + double alph = clamp((snum.lifespan+fractic)/double(GameTicRate),0.,1.); + // score rises linearly + Vector2 fo = (0,snum.initialspan-(snum.lifespan-fractic)); + Screen.DrawText(mTinyFontOutline,snum.tcolor,vpos.x-(fo.x+mTinyFontOutline.StringWidth(tag)/2.),vpos.y-(fo.y+(mTinyFontOutline.GetHeight()/2.)),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + // extra strings (if available) + fo.y += mTinyFontOutline.GetHeight(); + for ( int i=0; i 0 ) tag.AppendFormat(" x%d",snum.xscore[i]); + Screen.DrawText(mTinyFontOutline,snum.xtcolor[i],vpos.x-(fo.x+mTinyFontOutline.StringWidth(tag)/2.),vpos.y-(fo.y+(mTinyFontOutline.GetHeight()/2.)),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph); + fo.y += mTinyFontOutline.GetHeight(); + } + } + while ( snum = snum.next ); + } + + private void DrawTarget() + { + // don't draw when dead or with automap open + if ( (CPlayer.health <= 0) || automapactive ) return; + bool projinit = false; + Vector3 viewvec = SWWMUtility.Vec3FromAngles(viewrot.x,viewrot.y); + // points of interest + if ( level.allmap && swwm_pois ) DrawInterest(viewvec,projinit); + // sensed items + DrawItemSense(viewvec,projinit); + // targetting array + if ( swwm_targeter ) DrawTrackers(viewvec,projinit); + // floating kill scores and others + if ( swwm_damnums ) DrawNumbers(viewvec,projinit); + if ( swwm_scorenums ) DrawScores(viewvec,projinit); + Screen.ClearClipRect(); + } +} diff --git a/zscript/hud/swwm_hud_topstuff.zsc b/zscript/hud/swwm_hud_topstuff.zsc new file mode 100644 index 000000000..b4aa154df --- /dev/null +++ b/zscript/hud/swwm_hud_topstuff.zsc @@ -0,0 +1,967 @@ +// Minimap and stats +extend Class SWWMStatusBar +{ + // quicksort (player scores) + private int partition_playerscore( Array a, int l, int h ) + { + PlayerInfo pv = a[h]; + int i = (l-1); + for ( int j=l; j<=(h-1); j++ ) + { + if ( pv.fragcount < a[j].fragcount ) + { + i++; + PlayerInfo tmp = a[j]; + a[j] = a[i]; + a[i] = tmp; + } + } + PlayerInfo tmp = a[h]; + a[h] = a[i+1]; + a[i+1] = tmp; + return i+1; + } + private void qsort_playerscore( Array a, int l, int h ) + { + if ( l >= h ) return; + int p = partition_playerscore(a,l,h); + qsort_playerscore(a,l,p-1); + qsort_playerscore(a,p+1,h); + } + + private void FlushTopStuff() + { + ScoreInter.Reset(SWWMCredits.Get(CPlayer)); + } + + private void TickTopStuffInterpolators() + { + ScoreInter.Update(SWWMCredits.Get(CPlayer)); + // stats flashing + if ( level.killed_monsters > oldkills ) + { + oldkills = level.killed_monsters; + killflash = gametic+25; + } + if ( level.found_items > olditems ) + { + olditems = level.found_items; + itemflash = gametic+25; + } + if ( level.found_secrets > oldsecrets ) + { + oldsecrets = level.found_secrets; + secretflash = gametic+25; + } + if ( level.total_monsters > oldtkills ) + { + oldtkills = level.total_monsters; + tkillflash = gametic+25; + } + if ( level.total_items > oldtitems ) + { + oldtitems = level.total_items; + titemflash = gametic+25; + } + if ( level.total_secrets > oldtsecrets ) + { + oldtsecrets = level.total_secrets; + tsecretflash = gametic+25; + } + // purge expired key flashes + for ( int i=0; i= gametic ) continue; + keyflash.Delete(i--); + } + // minimap zoom interpolation + double desiredzoom = clamp(swwm_mm_zoom,.5,level.allmap?2.:1.); + if ( (minimapzoom != swwm_mm_zoom) || (oldminimapzoom != swwm_mm_zoom) ) + { + oldminimapzoom = minimapzoom; + double diff = .1*(desiredzoom-minimapzoom); + minimapzoom += diff; + if ( abs(minimapzoom-desiredzoom) <= .01 ) + minimapzoom = desiredzoom; + } + } + + private void TickTopStuff() + { + // deathmatch stuff + if ( !deathmatch ) return; + if ( teamplay ) + { + if ( teamactive.Size() != Teams.Size() ) teamactive.Resize(Teams.Size()); + if ( teamscore.Size() != Teams.Size() ) teamscore.Resize(Teams.Size()); + for ( int i=0; i CPlayer.fragcount ) + rank += 1; + else if ( players[i].fragcount == CPlayer.fragcount ) + tiedscore = true; + if ( players[i].fragcount > highscore ) + highscore = players[i].fragcount; + } + if ( sortplayers.Size() != playercount ) sortplayers.Resize(playercount); + for ( int i=0, j=0; i= 2) ) return true; + if ( !special || (special == Door_Open) + || (special == Door_Close) + || (special == Door_CloseWaitOpen) + || (special == Door_Raise) + || (special == Door_Animated) + || (special == Generic_Door) ) + return false; + return true; + } + private bool CmpFloorPlanes( Line l ) + { + return (l.frontsector.floorplane.Normal == l.backsector.floorplane.Normal) + && (l.frontsector.floorplane.D == l.backsector.floorplane.D); + } + private bool CmpCeilingPlanes( Line l ) + { + return (l.frontsector.ceilingplane.Normal == l.backsector.ceilingplane.Normal) + && (l.frontsector.ceilingplane.D == l.backsector.ceilingplane.D); + } + + private int CheckSecret( Line l ) + { + if ( !mm_secretsectorcolor || !mm_unexploredsecretcolor ) + return 0; + if ( l.frontsector && (l.frontsector.flags&Sector.SECF_WASSECRET) ) + { + if ( am_map_secrets && !(l.frontsector.flags&Sector.SECF_SECRET) ) return 1; + if ( (am_map_secrets == 2) && !(l.flags&Line.ML_SECRET) ) return 2; + } + if ( l.backsector && (l.backsector.flags&Sector.SECF_WASSECRET) ) + { + if ( am_map_secrets && !(l.backsector.flags&Sector.SECF_SECRET) ) return 1; + if ( (am_map_secrets == 2) && !(l.flags&Line.ML_SECRET) ) return 2; + } + return 0; + } + private bool CheckFFBoundary( Line l ) + { + int fcount = l.frontsector.Get3DFloorCount(), + bcount = l.backsector.Get3DFloorCount(); + // no 3D floors, no boundary + if ( !fcount && !bcount ) return false; + int fvalid = 0, bvalid = 0; + for ( int i=0; i= -1; p-- ) + { + if ( p == thisgroup ) continue; + foreach ( l : level.lines ) + { + if ( !(l.flags&Line.ML_MAPPED) && !level.allmap && !am_cheat ) continue; + if ( (l.flags&Line.ML_DONTDRAW) && ((am_cheat == 0) || (am_cheat >= 4)) ) + continue; + Vector2 rv1 = l.v1.p-cpos, rv2 = l.v2.p-cpos; + int lgroup; + if ( l.sidedef[0].flags&Side.WALLF_POLYOBJ ) lgroup = level.PointInSector(l.v1.p+l.delta/2.).portalgroup; + else lgroup = l.frontsector.portalgroup; + bool isportal = ((numgroups>0)&&(lgroup!=thisgroup)); + if ( lgroup == p ) + { + // portal displacement + Vector2 pofs = level.GetDisplacement(lgroup,thisgroup); + rv1 += pofs; + rv2 += pofs; + } + else if ( (p != -1) || (lgroup != thisgroup) ) + continue; + Vector2 mid = (rv1+rv2)/2.; + Vector2 siz = (abs(rv1.x-rv2.x),abs(rv1.y-rv2.y))/2.; + if ( (((siz.x+zoomview)-abs(mid.x)) <= 0) || (((siz.y+zoomview)-abs(mid.y)) <= 0) ) + continue; + // flip Y + rv1.y *= -1; + rv2.y *= -1; + // rotate by view + rv1 = Actor.RotateVector(rv1,ViewRot.x-90); + rv2 = Actor.RotateVector(rv2,ViewRot.x-90); + // clip to frame + bool visible; + [visible, rv1, rv2] = SWWMUtility.LiangBarsky((-1,-1)*zoomclip,(1,1)*zoomclip,rv1,rv2); + if ( !visible ) continue; + // scale to minimap frame + rv1 *= (HALFMAPSIZE/zoomclip)*hs; + rv2 *= (HALFMAPSIZE/zoomclip)*hs; + // offset to minimap center + rv1 += basepos; + rv2 += basepos; + // get the line color + Color col = mm_wallcolor; + if ( (l.flags&Line.ML_MAPPED) || am_cheat ) + { + int secwit = CheckSecret(l); + int lock = SWWMUtility.GetLineLock(l); + if ( secwit == 1 ) col = mm_secretsectorcolor; + else if ( secwit == 2 ) col = mm_unexploredsecretcolor; + else if ( l.flags&Line.ML_SECRET ) + { + if ( am_cheat && l.backsector && mm_secretwallcolor ) + col = mm_secretwallcolor; + else col = mm_wallcolor; + } + else if ( mm_interlevelcolor + && ((l.special == Exit_Normal) + || (l.special == Exit_Secret) + || (l.special == Teleport_NewMap) + || (l.special == Teleport_EndGame)) ) + col = mm_interlevelcolor; + else if ( mm_intralevelcolor && + (l.activation&SPAC_PlayerActivate) + && ((l.special == Teleport) + || (l.special == Teleport_NoFog) + || (l.special == Teleport_ZombieChanger) + || (l.special == Teleport_Line)) ) + col = mm_intralevelcolor; + else if ( mm_displaylocks + && (lock > 0) && (lock < 256) ) + { + let lcol = SWWMUtility.GetLockColor(lock); + if ( lcol ) col = lcol; + else col = mm_lockedcolor; + } + else if ( mm_specialwallcolor && ShowTriggerLine(l) ) + col = mm_specialwallcolor; + else if ( l.frontsector && l.backsector ) + { + if ( !CmpFloorPlanes(l) ) col = mm_fdwallcolor; + else if ( !CmpCeilingPlanes(l) ) col = mm_cdwallcolor; + else if ( CheckFFBoundary(l) ) col = mm_efwallcolor; + else + { + if ( (am_cheat == 0) || (am_cheat >= 4) ) + continue; + col = mm_tswallcolor; + } + } + } + else col = mm_notseencolor; + // draw the line + if ( isportal ) + { + col = Color((col.r+mm_portalcolor.r*7)/8,(col.g+mm_portalcolor.g*7)/8,(col.b+mm_portalcolor.b*7)/8); + Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.25),col); + } + else Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.5),col); + } + } + } + private void DrawMapMarkers( Vector2 basepos ) + { + double zoomlevel = SWWMUtility.Lerp(oldminimapzoom,minimapzoom,FracTic); + double zoomview = MAPVIEWDIST*zoomlevel, zoomclip = CLIPDIST*zoomlevel; + Vector2 cpos = SWWMUtility.LerpVector2(players[consoleplayer].Camera.prev.xy,players[consoleplayer].Camera.pos.xy,FracTic); + Sector csec = players[consoleplayer].Camera.CurSector; + if ( !mi ) mi = ThinkerIterator.Create("MapMarker",Thinker.STAT_MAPMARKER); + else mi.Reinit(); + MapMarker m; + while ( m = MapMarker(mi.Next()) ) + { + if ( m.bDORMANT ) continue; + if ( m.args[1] && !(m.CurSector.moreflags&Sector.SECMF_DRAWN) ) continue; + TextureID tx; + if ( m.picnum.IsValid() ) tx = m.picnum; + else tx = m.CurState.GetSpriteTexture(1); + Vector2 sz = TexMan.GetScaledSize(tx); + Vector2 scl; + // seems to match automap scaling somewhat + if ( m.Args[2] ) scl = (m.Scale/zoomlevel)*.15; + else scl = m.Scale*.5; + sz.x *= scl.x; + sz.y *= scl.y; + double radius = max(sz.x,sz.y); // naive, I know + if ( m.args[0] ) + { + // oh bother, this will be dicks + let ai = level.CreateActorIterator(m.args[0]); + Actor a; + while ( a = ai.Next() ) + { + Vector2 rv = a.pos.xy-cpos; + bool isportal = false; + Sector sec = level.PointInSector(a.pos.xy); + if ( sec.portalgroup != csec.portalgroup ) + { + isportal = true; + // portal displacement + rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); + } + if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) + continue; + // flip Y + rv.y *= -1; + // rotate by view + rv = Actor.RotateVector(rv,ViewRot.x-90); + // scale to minimap frame + rv *= (HALFMAPSIZE/zoomclip)*hs; + // offset to minimap center + rv += basepos; + // draw + Screen.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,hs*scl.x,DTA_ScaleY,hs*scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation); + } + ai.Destroy(); + continue; + } + Vector2 rv = m.pos.xy-cpos; + bool isportal = false; + Sector sec = level.PointInSector(m.pos.xy); + if ( sec.portalgroup != csec.portalgroup ) + { + isportal = true; + // portal displacement + rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); + } + if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) + continue; + // flip Y + rv.y *= -1; + // rotate by view + rv = Actor.RotateVector(rv,ViewRot.x-90); + // scale to minimap frame + rv *= (HALFMAPSIZE/zoomclip)*hs; + // offset to minimap center + rv += basepos; + // draw + Screen.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,hs*scl.x,DTA_ScaleY,hs*scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation); + } + } + private void DrawMapThings( Vector2 basepos ) + { + double zoomlevel = SWWMUtility.Lerp(oldminimapzoom,minimapzoom,FracTic); + double zoomview = MAPVIEWDIST*zoomlevel, zoomclip = CLIPDIST*zoomlevel; + Vector2 cpos = SWWMUtility.LerpVector2(players[consoleplayer].Camera.prev.xy,players[consoleplayer].Camera.pos.xy,FracTic); + Sector csec = players[consoleplayer].Camera.CurSector; + bool drawmissiles = swwm_mm_missiles; + for ( SWWMSimpleTracker t=hnd.strackers; t; t=t.next ) + { + if ( !drawmissiles && t.ismissile ) continue; + if ( level.allmap && (t.target is 'Key') ) continue; // don't draw keys over the actual markers they have + Color col = mm_thingcolor; + bool isitem = false; + bool plainactor = false; + Vector2 pos; + double angle; + double radius; + if ( t.target ) + { + pos = SWWMUtility.LerpVector2(t.target.prev.xy,t.target.pos.xy,FracTic); + angle = t.target.angle; + radius = t.isybeam?(t.target.speed*cos(t.target.pitch-90)):t.isbeam?(t.target.speed*cos(t.target.pitch)):t.target.radius; + } + else + { + pos = t.pos.xy; + angle = t.angle; + radius = t.radius; + } + if ( t.isitem ) + { + if ( t.vipitem ) col = mm_thingcolor_vipitem; + else if ( t.countitem ) col = mm_thingcolor_citem; + else col = mm_thingcolor_item; + isitem = true; + } + else if ( t.isplayer ) col = t.playercol; + else if ( t.friendly ) col = mm_thingcolor_friend; + else if ( t.countkill ) col = mm_thingcolor_monster; + else if ( t.ismonster ) col = mm_thingcolor_ncmonster; + else if ( t.ismissile ) col = mm_thingcolor_missile; + else + { + if ( t.vipitem ) col = mm_thingcolor_vipitem; // chanceboxes + else if ( t.shootable ) col = mm_thingcolor_shootable; + plainactor = true; + } + int mtime = GameTicRate; + if ( level.allmap && !t.expired && t.target ) mtime += GameTicRate*3; + Vector2 rv = pos-cpos; + bool isportal = false; + Sector sec = level.PointInSector(pos); + if ( sec.portalgroup != csec.portalgroup ) + { + isportal = true; + // portal displacement + rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); + // and blend in the color too + col = Color((col.r+mm_portalcolor.r*7)/8,(col.g+mm_portalcolor.g*7)/8,(col.b+mm_portalcolor.b*7)/8); + } + if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) + continue; + Vector2 tv[4]; + int nidx; + if ( t.isbeam ) + { + // oriented line + nidx = 2; + tv[0] = rv; + tv[1] = rv+Actor.RotateVector((radius,0),angle); + } + else if ( isitem ) + { + // rhombus + nidx = 4; + double crad = min(radius,10); + for ( int i=0; i<4; i++ ) + tv[i] = rv+Actor.RotateVector((crad,0),i*90); + } + else if ( plainactor ) + { + // aabb box + nidx = 4; + tv[0] = rv+(-radius,-radius); + tv[1] = rv+(radius,-radius); + tv[2] = rv+(radius,radius); + tv[3] = rv+(-radius,radius); + } + else + { + // oriented triangle + nidx = 3; + tv[0] = rv+Actor.RotateVector((radius,0),angle); + tv[1] = rv+Actor.RotateVector((-radius*.5,radius*.7),angle); + tv[2] = rv+Actor.RotateVector((-radius*.5,-radius*.7),angle); + } + // flip Y + for ( int j=0; j 0) ) + { + xx = int(ss.x-(margin+2)); + String str; + if ( automapactive || (fstats > 1) ) + { + int label = am_showmaplabel; + String ln = level.levelname; + int iof = ln.IndexOf(" - by: "); + if ( iof != -1 ) ln.Truncate(iof); + if ( !label || ((level.clusterflags&level.CLUSTER_HUB) && (label == 2)) ) str = ln; + else str = String.Format("%s - %s",level.mapname.MakeUpper(),ln); + Screen.DrawText(mSmallFontOutline,tclabel,xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + yy += mSmallFontOutline.GetHeight()+4; + } + if ( (level.total_monsters > 0) && am_showmonsters && !deathmatch ) + { + str = String.Format("\c"..tclabel_s.."K \c-%d\c"..tcextra_s.."/\c-%d",level.killed_monsters,level.total_monsters); + Screen.DrawText(MiniHUDFontOutline,(level.killed_monsters>=level.total_monsters)?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( killflash && (gametic < killflash) ) + { + double alph = max((killflash-(gametic+FracTic))/25.,0.)**1.5; + str = String.Format("%d/%d",level.killed_monsters,level.total_monsters); + int slashpos = str.IndexOf("/"); + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + if ( tkillflash && (gametic < tkillflash) ) + { + double alph = max((tkillflash-(gametic+FracTic))/25.,0.)**1.5; + str = String.Format("%d",level.total_monsters); + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + yy += MiniHUDFontOutline.GetHeight()+2; + } + if ( (level.total_items > 0) && am_showitems && !deathmatch ) + { + str = String.Format("\c"..tclabel_s.."I \c-%d\c"..tcextra_s.."/\c-%d",level.found_items,level.total_items); + Screen.DrawText(MiniHUDFontOutline,(level.found_items>=level.total_items)?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( itemflash && (gametic < itemflash) ) + { + double alph = max((itemflash-(gametic+FracTic))/25.,0.)**1.5; + str = String.Format("%d/%d",level.found_items,level.total_items); + int slashpos = str.IndexOf("/"); + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + if ( titemflash && (gametic < titemflash) ) + { + double alph = max((titemflash-(gametic+FracTic))/25.,0.)**1.5; + str = String.Format("%d",level.total_items); + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + yy += MiniHUDFontOutline.GetHeight()+2; + } + if ( (level.total_secrets > 0) && am_showsecrets && !deathmatch ) + { + str = String.Format("\c"..tclabel_s.."S \c-%d\c"..tcextra_s.."/\c-%d",level.found_secrets,level.total_secrets); + Screen.DrawText(MiniHUDFontOutline,(level.found_secrets>=level.total_secrets)?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( secretflash && (gametic < secretflash) ) + { + double alph = max((secretflash-(gametic+FracTic))/25.,0.)**1.5; + str = String.Format("%d/%d",level.found_secrets,level.total_secrets); + int slashpos = str.IndexOf("/"); + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + if ( tsecretflash && (gametic < tsecretflash) ) + { + double alph = max((tsecretflash-(gametic+FracTic))/25.,0.)**1.5; + str = String.Format("%d",level.total_secrets); + Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + } + yy += MiniHUDFontOutline.GetHeight()+2; + } + int sec; + if ( am_showtime ) + { + sec = Thinker.Tics2Seconds(level.maptime); + str = String.Format("\c"..tclabel_s.."T \c-%02d\c"..tcextra_s..":\c-%02d\c"..tcextra_s..":\c-%02d",sec/3600,(sec%3600)/60,sec%60); + Screen.DrawText(MiniHUDFontOutline,((level.sucktime>0)&&(sec>=(level.sucktime*3600)))?tcsucks:((level.partime>0)&&(sec<=level.partime))?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + yy += MiniHUDFontOutline.GetHeight()+2; + } + // don't show total time if it's equal to map time + if ( am_showtotaltime && (level.totaltime != level.maptime) ) + { + sec = Thinker.Tics2Seconds(level.totaltime); + str = String.Format("\c"..tclabel_s.."TT \c-%02d\c"..tcextra_s..":\c-%02d\c"..tcextra_s..":\c-%02d",sec/3600,(sec%3600)/60,sec%60); + Screen.DrawText(MiniHUDFontOutline,tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + yy += MiniHUDFontOutline.GetHeight()+2; + } + yy += 3; + } + if ( deathmatch ) + { + yy += 9; + if ( playercount <= 1 ) return; + xx = int(ss.x-(margin+2)); + String str; + if ( teamplay ) + { + // draw team scores + for ( int i=0; i 0 ) str = String.Format("\c[MiniDemoBlue]%s \c-+%d",StringTable.Localize("$SWWM_DMSPREAD"),lead); + else str = String.Format("\c[MiniDemoBlue]%s \c-%d",StringTable.Localize("$SWWM_DMSPREAD"),lead); + Screen.DrawText(mSmallFontOutline,mhudfontcol[MCR_RED],xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + yy += mSmallFont.GetHeight()+3; + // draw top 3 players + for ( int i=0; i klist; + for ( int i=0; i= f.flashtime) ) continue; + double alph = max((f.flashtime-(gametic+FracTic))/25.,0.)**1.5; + Screen.DrawTexture(icon,false,keypos.x-siz.x,keypos.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_TopLeft,true,DTA_ColorOverlay,0xFFFFC040,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); + break; + } + keypos.x -= siz.x+2; + colh = max(colh,siz.y); + if ( ++colc == maxcolc ) + { + keypos.x = ss.x-(margin+2); + keypos.y += colh+2; + colh = colc = 0; + } + } + } + + override void DrawAutomapHUD( double ticFrac ) + { + // do nothing, DrawTopStuff handles this + } +} diff --git a/zscript/hud/swwm_hud_weapons.zsc b/zscript/hud/swwm_hud_weapons.zsc new file mode 100644 index 000000000..200a5c070 --- /dev/null +++ b/zscript/hud/swwm_hud_weapons.zsc @@ -0,0 +1,295 @@ +// Weapons and ammo +extend Class SWWMStatusBar +{ + private void TickWeaponInterpolators() + { + // ammo updates + for ( int i=0; i<18; i++ ) + { + let a = SWWMAmmo(CPlayer.mo.FindInventory(AmmoSlots[i])); + int amt = 0; + int maxamt = 0; + if ( a ) + { + amt = a.Amount; + maxamt = a.MaxAmount; + if ( a.MagAmmoType ) + { + let m = MagAmmo(CPlayer.mo.FindInventory(a.MagAmmoType)); + if ( m ) + { + amt *= m.ClipSize; + amt += m.Amount; + maxamt *= m.ClipSize; + } + } + } + else + { + let a = GetDefaultByType(AmmoSlots[i]); + maxamt = a.MaxAmount; + if ( a.MagAmmoType ) + { + let m = GetDefaultByType(a.MagAmmoType); + maxamt *= m.ClipSize; + } + } + if ( (amt > AmmoOldAmounts[i]) && (AmmoOldAmounts[i] != int.min) ) + AmmoFlash[i] = gametic+25; + AmmoOldAmounts[i] = amt; + if ( (maxamt > AmmoOldMaxAmounts[i]) && (AmmoOldMaxAmounts[i] != int.min) ) + AmmoMaxFlash[i] = gametic+25; + AmmoOldMaxAmounts[i] = maxamt; + } + } + + private void TickWeapons() + { + // let weapons update their own interpolators + for ( Inventory i=CPlayer.mo.inv; i; i=i.inv ) + { + if ( !(i is 'SWWMWeapon') ) continue; + SWWMWeapon(i).HudTick(); + } + } + + // hello??? why is this function clearscope??? + override void ReceivedWeapon( Weapon weapn ) + { + Super.ReceivedWeapon(weapn); + int dummy, slot; + [dummy, slot] = players[consoleplayer].weapons.LocateWeapon(weapn.GetClass()); + EventHandler.SendNetworkEvent("swwmweaponreceive",slot,consoleplayer); + } + + private void DrawWeapons() + { + Screen.DrawTexture(WeaponTex,false,ss.x-(margin+80),ss.y-(margin+10),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + double xx = ss.x-(margin+78), yy = ss.y-(margin+8); + for ( int i=1; i<=10; i++,xx+=8 ) + { + int ncolor = mhudfontcol[MCR_WHITE]; + if ( !CPlayer.HasWeaponsInSlot(i%10) ) + { + Screen.DrawText(MiniHUDFont,ncolor,xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,Color(128,0,0,0)); + continue; + } + bool selected = false; + bool dummy; + int slot; + SWWMGesture hasgesture = null; + SWWMItemGesture hasitemgesture = null; + if ( CPlayer.PendingWeapon is 'SWWMGesture' ) hasgesture = SWWMGesture(CPlayer.PendingWeapon); + else if ( CPlayer.ReadyWeapon is 'SWWMGesture' ) hasgesture = SWWMGesture(CPlayer.ReadyWeapon); + if ( CPlayer.PendingWeapon is 'SWWMItemGesture' ) hasitemgesture = SWWMItemGesture(CPlayer.PendingWeapon); + else if ( CPlayer.ReadyWeapon is 'SWWMItemGesture' ) hasitemgesture = SWWMItemGesture(CPlayer.ReadyWeapon); + if ( hasgesture && hasgesture.formerweapon ) + { + [dummy, slot] = CPlayer.weapons.LocateWeapon(hasgesture.formerweapon.GetClass()); + if ( slot == (i%10) ) selected = true; + } + else if ( hasitemgesture && hasitemgesture.gest.formerweapon ) + { + [dummy, slot] = CPlayer.weapons.LocateWeapon(hasitemgesture.gest.formerweapon.GetClass()); + if ( slot == (i%10) ) selected = true; + } + else if ( CPlayer.PendingWeapon && (CPlayer.PendingWeapon != WP_NOCHANGE) ) + { + [dummy, slot] = CPlayer.weapons.LocateWeapon(CPlayer.PendingWeapon.GetClass()); + if ( slot == (i%10) ) selected = true; + } + else if ( (!CPlayer.PendingWeapon || (CPlayer.PendingWeapon == WP_NOCHANGE)) && CPlayer.ReadyWeapon ) + { + [dummy, slot] = CPlayer.weapons.LocateWeapon(CPlayer.ReadyWeapon.GetClass()); + if ( slot == (i%10) ) selected = true; + } + if ( selected ) ncolor = mhudfontcol[MCR_BRASS]; + else + { + bool hasammo = (i==1); + for ( Inventory inv=CPlayer.mo.Inv; inv; inv=inv.Inv ) + { + if ( inv is 'Weapon' ) [dummy, slot] = CPlayer.weapons.LocateWeapon(Weapon(inv).GetClass()); + else continue; + if ( slot != (i%10) ) continue; + // CheckAmmo can't be called from ui, so we have to improvise + // for SWWM weapons I made a function for this at least + if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).ReportHUDAmmo() ) + hasammo = true; + else if ( !(inv is 'SWWMWeapon') && ((!Weapon(inv).Ammo1 || (Weapon(inv).Ammo1.Amount > 0) || Weapon(inv).bAMMO_OPTIONAL) || (Weapon(inv).Ammo2 && ((Weapon(inv).Ammo2.Amount > 0) || Weapon(inv).bALT_AMMO_OPTIONAL))) ) + hasammo = true; + } + if ( !hasammo ) ncolor = mhudfontcol[MCR_RED]; + } + Screen.DrawText(MiniHUDFont,ncolor,xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int f = hnd.WeaponFlash[i%10]; + if ( f && (gametic < f) ) + { + double alph = max((f-(gametic+FracTic))/25.,0.)**1.5; + Screen.DrawText(MiniHUDFont,mhudfontcol[MCR_FLASH],xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_LegacyRenderStyle,STYLE_Add); + } + } + xx = ss.x-(margin+54); + yy = ss.y-(margin+14); + bool bDrewAmmo = false; + bool checkowned = !swwm_hudallammo; + Array OwnedWeapons; + if ( checkowned ) for ( Inventory i=CPlayer.mo.inv; i; i=i.inv ) + { + if ( !(i is 'SWWMWeapon') ) continue; + OwnedWeapons.Push(SWWMWeapon(i)); + } + String str; + for ( int i=17; i>=0; i-- ) + { + let a = AmmoSlots[i]; + // check if owned + if ( checkowned ) + { + bool owned = false; + foreach ( w:OwnedWeapons ) + { + if ( w.UsesAmmo(a) ) + owned = true; + } + if ( !owned ) continue; + } + if ( !bDrewAmmo ) + { + Screen.DrawTexture(AmmoTex[2],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + bDrewAmmo = true; + } + yy -= 6; + Screen.DrawTexture(AmmoTex[1],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + xx += 2; + let cur = SWWMAmmo(CPlayer.mo.FindInventory(a)); + int amt, amax; + if ( !cur ) + { + amt = 0; + amax = GetDefaultByType(a).MaxAmount; + let def = GetDefaultByType(a); + if ( def.MagAmmoType ) + { + let mag = MagAmmo(CPlayer.mo.FindInventory(def.MagAmmoType)); + if ( mag ) // theoretically can't happen, but still check for it + { + amt = mag.Amount; + amax = amax*mag.ClipSize+mag.MaxAmount; + } + else + { + let defmag = GetDefaultByType(def.MagAmmoType); + amax = amax*defmag.ClipSize+defmag.MaxAmount; + } + } + } + else + { + amt = cur.Amount; + amax = cur.MaxAmount; + if ( cur.MagAmmoType ) + { + let mag = MagAmmo(CPlayer.mo.FindInventory(cur.MagAmmoType)); + // theoretically this should never be null, but nevertheless... + if ( mag ) + { + amt = amt*mag.ClipSize+mag.Amount; + amax = amax*mag.ClipSize+mag.MaxAmount; + } + else + { + let def = GetDefaultByType(cur.MagAmmoType); + amt = amt*def.ClipSize; + amax = amax*def.ClipSize+def.MaxAmount; + } + } + } + bool used = false; + if ( CPlayer.ReadyWeapon && (CPlayer.ReadyWeapon is 'SWWMWeapon') ) + used = SWWMWeapon(CPlayer.ReadyWeapon).UsesAmmo(a); + int scol = mhudfontcol[used?MCR_BRASS:MCR_WHITE]; + int ncolor = (amt>0)?scol:mhudfontcol[MCR_RED]; + int dcnt1 = 2-int(Log10(clamp(amt,1,999))); + int dcnt2 = 2-int(Log10(clamp(amax,1,999))); + for ( int j=0; j= 2.) ) mmz = 2.; @@ -209,6 +210,11 @@ Class SWWMStaticHandler : StaticEventHandler if ( (gamestate != GS_LEVEL) || !swwm_showmaptitle ) return; StatusBar.AttachMessage(new("DSMapTitle").Init(),-7777); } + else if ( e.Name ~== "swwmflushud" ) + { + if ( !(StatusBar is 'SWWMStatusBar') ) return; + SWWMStatusBar(StatusBar).Flush(); + } else if ( e.Name ~== "swwmaprcheck" ) { if ( gamestate != GS_LEVEL ) return;