swwmgz_m/zscript/kbase/swwm_kbasetab_mission.zsc
Marisa the Magician 2aa0ea4680 More work towards Legacy of Rust support (with caveats).
As of this commit, do not consider the experience when playing that new
expansion to be complete. I've only partially written some of the mission texts
and rudimentarily enhanced some boss fights.

Currently there is one major limitation in that the intermission texts cannot
be replaced, as they're hardcoded inside the UMAPINFO. I don't know if I can
work around that.
2025-08-20 16:47:24 +02:00

318 lines
10 KiB
Text

// mission briefing tab
Class DemolitionistMissionTab : DemolitionistMenuTab
{
Array<DemolitionistMenuTextBox> mtext;
int sel;
bool drag;
SWWMMissionLog mlog;
override DemolitionistMenuTab Init( DemolitionistMenu master )
{
title = StringTable.Localize("$SWWM_MISSTAB");
bHidden = deathmatch;
sel = 0;
mlog = SWWMMissionLog.Get();
// simplified initialization if we have a dedicated mission log
// note: mission log can't exist without one entry, so no fallback checks needed
if ( mlog )
{
for ( int i=(mlog.entries.Size()-1); i>=0; i-- )
mtext.Push(new('DemolitionistMenuTextBox').Init(master,mlog.entries[i]));
return Super.Init(master);
}
// saves time
bool nrftl = false;
bool lor = false;
bool eviternity = false;
bool eviternitwo = false;
bool hexdd = false;
String missionstr;
let stats = SWWMStats.Find(players[consoleplayer]);
if ( (gameinfo.gametype&GAME_Doom) && SWWMUtility.IsKnownMap() )
{
int clus = level.cluster;
if ( clus == 11 ) nrftl = true;
lor = SWWMUtility.IsLegacyOfRust();
eviternity = SWWMUtility.IsEviternity();
eviternitwo = SWWMUtility.IsEviternityTwo();
if ( eviternitwo )
{
// clusters in eviternity 2 have to be remapped
if ( clus == 5 ) clus = 1;
else if ( (clus == 6) || (clus == 13) ) clus = 2;
else if ( (clus == 7) || (clus == 14) ) clus = 3;
else if ( (clus == 8) || (clus == 15) ) clus = 4;
else if ( (clus == 9) || (clus == 16) ) clus = 5;
else if ( (clus == 10) || (clus == 17) ) clus = 6;
else if ( (clus == 11) || (clus == 12) || (clus == 18) || (clus == 19) ) clus = 7;
missionstr = String.Format("$SWWM_MISSION_EVITERNITYII%d",clus);
}
else if ( eviternity )
{
// we have to do some heavy lifting here because episodes don't match clusters
if ( level.levelnum <= 5 ) clus = 1;
else if ( level.levelnum <= 10 ) clus = 2;
else if ( level.levelnum <= 15 ) clus = 3;
else if ( level.levelnum <= 20 ) clus = 4;
else if ( level.levelnum <= 25 ) clus = 5;
else if ( level.levelnum <= 30 ) clus = 6;
else if ( level.levelnum <= 31 ) clus = 7;
else if ( level.levelnum <= 32 ) clus = 8;
missionstr = String.Format("$SWWM_MISSION_EVITERNITY%d",clus);
}
else if ( lor )
{
// legacy of rust has its quirks, as with umapinfo there are
// technically no clusters
if ( (level.levelnum <= 7) || (level.levelnum == 15) ) clus = 28;
else if ( (level.levelnum <= 14) || (level.levelnum == 16) ) clus = 29;
missionstr = String.Format("$SWWM_MISSION_DOOM%d",clus);
}
// naive method to guess if this is sigil
else if ( (clus == 5) && (level.mapname.Left(2) == "E5") )
missionstr = String.Format("$SWWM_MISSION_SIGIL");
else if ( (clus == 6) && (level.mapname.Left(2) == "E6") )
missionstr = String.Format("$SWWM_MISSION_SIGIL2");
else missionstr = String.Format("$SWWM_MISSION_DOOM%d",clus);
int csiz = stats.clustervisit.Size();
if ( (csiz > 0) && stats.secretdone[csiz-1] )
{
String tmpstr = missionstr.."_SECRET";
if ( !(StringTable.Localize(tmpstr) ~== tmpstr.Mid(1)) )
missionstr = tmpstr;
}
// if we came from the doom 1 episodes, use the alt mission string for Doom 2
bool fromdoomone = false;
for ( int i=0; i<csiz; i++ )
{
// also include sigil
if ( !eviternity && !eviternitwo && (stats.clustervisit[i] >= 5) && (stats.clustervisit[i] != 25) && (stats.clustervisit[i] != 26) ) continue;
fromdoomone = true;
break;
}
if ( !eviternity && !eviternitwo && (clus == 5) && fromdoomone )
missionstr = "$SWWM_MISSION_DOOM5_FROMDOOM1";
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
missionstr = "$SWWM_MISSION_NONE";
mtext.Push(new('DemolitionistMenuTextBox').Init(master,missionstr));
bool firstskip = false;
for ( int i=csiz-1; i>=0; i-- )
{
if ( (stats.clustervisit[i] == clus) && !firstskip )
continue;
firstskip = true;
String xstr = String.Format(eviternitwo?"$SWWM_MISSION_EVITERNITYII%d":eviternity?"$SWWM_MISSION_EVITERNITY%d":"$SWWM_MISSION_DOOM%d",stats.clustervisit[i]);
if ( !eviternity && !eviternitwo && (stats.clustervisit[i] == 5) && fromdoomone )
xstr = "$SWWM_MISSION_DOOM5_FROMDOOM1";
if ( stats.secretdone[i] )
{
String tmpstr = xstr.."_SECRET";
if ( !(StringTable.Localize(tmpstr) ~== tmpstr.Mid(1)) )
xstr = tmpstr;
}
if ( StringTable.Localize(xstr) ~== xstr.Mid(1) )
continue;
mtext.Push(new('DemolitionistMenuTextBox').Init(master,xstr));
}
}
else if ( (gameinfo.gametype&GAME_Heretic) && SWWMUtility.IsKnownMap() )
{
missionstr = String.Format("$SWWM_MISSION_HERETIC%d",level.cluster);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
missionstr = "$SWWM_MISSION_NONE";
mtext.Push(new('DemolitionistMenuTextBox').Init(master,missionstr));
int csiz = stats.clustervisit.Size();
bool firstskip = false;
for ( int i=csiz-1; i>=0; i-- )
{
if ( (stats.clustervisit[i] == level.cluster) && !firstskip )
continue;
firstskip = true;
String xstr = String.Format("$SWWM_MISSION_HERETIC%d",stats.clustervisit[i]);
if ( StringTable.Localize(xstr) ~== xstr.Mid(1) )
continue;
mtext.Push(new('DemolitionistMenuTextBox').Init(master,xstr));
}
}
else if ( (gameinfo.gametype&GAME_Hexen) && SWWMUtility.IsKnownMap() )
{
// detect deathkings
hexdd = SWWMUtility.IsDeathkings();
String gstr = hexdd?"HEXDD":"HEXEN";
missionstr = String.Format("$SWWM_MISSION_%s%d",gstr,level.cluster);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
missionstr = "$SWWM_MISSION_NONE";
mtext.Push(new('DemolitionistMenuTextBox').Init(master,missionstr));
int csiz = stats.clustervisit.Size();
bool firstskip = false;
for ( int i=csiz-1; i>=0; i-- )
{
if ( (stats.clustervisit[i] == level.cluster) && !firstskip )
continue;
firstskip = true;
String xstr = String.Format("$SWWM_MISSION_%s%d",gstr,stats.clustervisit[i]);
if ( StringTable.Localize(xstr) ~== xstr.Mid(1) )
continue;
mtext.Push(new('DemolitionistMenuTextBox').Init(master,xstr));
}
}
return Super.Init(master);
}
override void OnDestroy()
{
foreach ( t:mtext )
{
if ( !t ) continue;
t.Destroy();
}
}
override void MenuInput( int key )
{
if ( mtext.Size() == 0 ) return;
switch ( key )
{
case MK_DOWN:
if ( mtext[sel].Scroll(16) ) master.MenuSound("menu/demoscroll");
break;
case MK_UP:
if ( mtext[sel].Scroll(-16) ) master.MenuSound("menu/demoscroll");
break;
case MK_LEFT:
// cycle mission texts
if ( (mtext.Size() > 1) && (sel < (mtext.Size()-1)) )
{
master.MenuSound("menu/demoscroll");
sel++;
mtext[sel].smofs = 0.;
mtext[sel].ofs = 0;
drag = false; // just in case
}
break;
case MK_RIGHT:
// cycle mission texts
if ( (mtext.Size() > 1) && (sel > 0) )
{
master.MenuSound("menu/demoscroll");
sel--;
mtext[sel].smofs = 0.;
mtext[sel].ofs = 0;
drag = false; // just in case
}
break;
}
}
override void MouseInput( Vector2 pos, int btn )
{
if ( mtext.Size() == 0 ) return;
switch ( btn )
{
case MB_LEFT:
// see if we're clicking the scrollbar (if it exists)
if ( mtext[sel].scrollbar && (pos.x > (mtext[sel].x+(mtext[sel].w-8))) )
{
mtext[sel].SetOffset(pos.y);
master.MenuSound("menu/demoscroll");
drag = true;
break;
}
// cycle mission texts
if ( (mtext.Size() > 1) && (sel > 0) )
{
master.MenuSound("menu/demoscroll");
sel--;
mtext[sel].smofs = 0.;
mtext[sel].ofs = 0;
drag = false; // just in case
}
break;
case MB_RIGHT:
// cycle mission texts
if ( (mtext.Size() > 1) && (sel < (mtext.Size()-1)) )
{
master.MenuSound("menu/demoscroll");
sel++;
mtext[sel].smofs = 0.;
mtext[sel].ofs = 0;
drag = false; // just in case
}
break;
case MB_WHEELUP:
if ( mtext[sel].Scroll(-8) ) master.MenuSound("menu/demoscroll");
break;
case MB_WHEELDOWN:
if ( mtext[sel].Scroll(8) ) master.MenuSound("menu/demoscroll");
break;
case MB_DRAG:
if ( drag ) mtext[sel].SetOffset(pos.y);
break;
case MB_RELEASE:
drag = false;
break;
}
}
// stop smooth scrolling for current textbox
override void OnSelect()
{
if ( mtext.Size() == 0 ) return;
mtext[sel].smofs = mtext[sel].ofs;
}
override void OnDeselect()
{
if ( mtext.Size() == 0 ) return;
mtext[sel].smofs = mtext[sel].ofs;
}
override void Ticker()
{
// dedicated mission log can update in real time, so append new entries to the start of the array when needed and push the selector forward
if ( mlog && (mlog.entries.Size() > mtext.Size()) )
{
bool newent = false;
if ( mtext.Size() == 0 )
{
for ( int i=(mlog.entries.Size()-1); i>=0; i-- )
mtext.Push(new('DemolitionistMenuTextBox').Init(master,mlog.entries[i]));
sel = 0;
newent = true;
}
else for ( int i=(mlog.entries.Size()-1); i>=mtext.Size(); i-- )
{
mtext.Insert(0,new('DemolitionistMenuTextBox').Init(master,mlog.entries[i]));
sel++;
newent = true;
}
// notification for mission log updated
if ( newent )
{
master.tmsg = StringTable.Localize("$SWWM_NEWMISSION");
master.tmsgtic = Menu.MenuTime()+70;
}
}
// just tick the current textbox
if ( mtext.Size() == 0 ) return;
mtext[sel].Ticker();
}
override void Drawer( double fractic )
{
if ( mtext.Size() > 0 ) mtext[sel].Drawer(fractic,!!sel);
else
{
String str = StringTable.Localize("$SWWM_MISSION_NONE");
double xx = int(master.ws.x-master.mSmallFont.StringWidth(str))/2;
double yy = int(master.ws.y-master.mSmallFont.GetHeight())/2;
Screen.DrawText(master.mSmallFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
return;
}
if ( mtext.Size() <= 1 ) return;
double xx = master.ws.x-(mtext[sel].scrollbar?11:3);
double yy = master.ws.y-25;
String str = String.Format("%d \cf/\c- %d",mtext.Size()-sel,mtext.Size());
Screen.DrawText(master.mTinyFont,Font.CR_FIRE,(master.origin.x+xx)-master.mTinyFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
}
}