Due to some arbitrary change in GZDoom, which was later merged into VKDoom as well, PostUiTick no longer consistently gets called after WorldTick. This is kind of funny and also disappointing and sad because I intentionally designed that feature this way, on purpose, and now it no longer works as intended. Who knows how many other mods are affected by this now.
363 lines
11 KiB
Text
363 lines
11 KiB
Text
// Handler responsible for item replacements and whatever else
|
|
// most of the code is split up to make it easier to navigate
|
|
|
|
Class SWWMHandler : EventHandler
|
|
{
|
|
Mixin SWWMUIRandom;
|
|
|
|
transient int lastlock;
|
|
transient int lastpickuptic[MAXPLAYERS]; // these two are mostly used
|
|
transient int lastnuggettic[MAXPLAYERS]; // to avoid deafening players
|
|
SWWMScoreObj scorenums;
|
|
SWWMDamNum damnums;
|
|
SWWMInterest intpoints;
|
|
Array<String> damtypes, damcolors;
|
|
|
|
transient int slotstrictwarn;
|
|
transient ui String sswstr;
|
|
transient ui BrokenLines sswl;
|
|
|
|
// stuff to reduce worldthingspawned overhead
|
|
int bossmap;
|
|
int iwantdie;
|
|
int indoomvacation;
|
|
int inultdoom2;
|
|
Array<Service> funtagsv, mergemonstersv;
|
|
|
|
// for checkreplacement
|
|
bool hasdrlamonsters, haslegionofbones;
|
|
int iskdizd;
|
|
Array<String> bludtypes;
|
|
|
|
// mod inter-compat stuff
|
|
bool ccloaded;
|
|
|
|
// session globals
|
|
SWWMGlobals gdat;
|
|
|
|
// profiling data
|
|
bool profiling;
|
|
int bprofiletics, profiletics; // how many tics to aggregate data for
|
|
double prof_ms[8], prof_avg[8];
|
|
int prof_calls[8];
|
|
double curms;
|
|
|
|
// to avoid some overlaps
|
|
ui DSMapTitle mapmsg;
|
|
|
|
// corruption cards stuff
|
|
ui bool incardmenu, cardmessaged;
|
|
|
|
// ring buffer for player path tracing in minimap
|
|
const MAX_TRACED_BUFSZ = 8192;
|
|
transient ui Vector2 traced_steps[MAX_TRACED_BUFSZ];
|
|
transient ui int traced_steppos, traced_stepnum;
|
|
transient ui Vector2 oldplayerpos;
|
|
transient ui bool do_trace_steps;
|
|
|
|
enum EProfileTimer
|
|
{
|
|
PT_WORLDTICK,
|
|
PT_WORLDTHINGSPAWNED,
|
|
PT_WORLDTHINGDESTROYED,
|
|
PT_WORLDTHINGDIED,
|
|
PT_WORLDTHINGDAMAGED,
|
|
PT_WORLDTHINGREVIVED,
|
|
PT_CHECKREPLACEMENT,
|
|
PT_CHECKREPLACEE
|
|
}
|
|
|
|
private void ProfileTick()
|
|
{
|
|
curms = MSTimeF();
|
|
}
|
|
private void ProfileTock( int idx )
|
|
{
|
|
double diff = (MSTimeF()-curms);
|
|
prof_ms[idx] += diff;
|
|
prof_avg[idx] = (prof_calls[idx]>0)?(prof_avg[idx]+diff)/2.:diff;
|
|
prof_calls[idx]++;
|
|
}
|
|
|
|
static play void ToggleStore( bool val )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find('SWWMHandler'));
|
|
if ( !hnd || !hnd.gdat ) return; // shouldn't happen, but doesn't hurt to check
|
|
hnd.gdat.disablestore = !val;
|
|
}
|
|
static play void ToggleRevive( bool val )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find('SWWMHandler'));
|
|
if ( !hnd || !hnd.gdat ) return; // shouldn't happen, but doesn't hurt to check
|
|
hnd.gdat.disablerevive = !val;
|
|
}
|
|
|
|
override void OnRegister()
|
|
{
|
|
// oneliner RNG must be relative to consoleplayer
|
|
SetRandomSeed[DemoLines](Random[DemoLines]()+consoleplayer+MSTime());
|
|
// "uninitialize" some vars
|
|
iwantdie = -1;
|
|
bossmap = -1;
|
|
indoomvacation = -1;
|
|
inultdoom2 = -1;
|
|
// class-checking ones can be initialized here easily
|
|
if ( FindClass('RLMonster','Actor') ) hasdrlamonsters = true;
|
|
if ( FindClass('LOBZombieman','Actor') ) haslegionofbones = true;
|
|
if ( FindClass('CCards_Global','Thinker') ) ccloaded = true;
|
|
if ( LevelInfo.MapExists("Z1M1") && (LevelInfo.MapChecksum("Z1M1") ~== "2B7744234ED2C162AD08A3255E979F65") )
|
|
iskdizd = true;
|
|
// read bludtype files if they can be found
|
|
for ( int lmp = Wads.FindLump("BLUDTYPE"); lmp != -1; lmp = Wads.FindLump("BLUDTYPE",lmp+1) )
|
|
{
|
|
String dat = Wads.ReadLump(lmp);
|
|
Array<String> list;
|
|
// Windows pls
|
|
dat.Replace("\r","");
|
|
list.Clear();
|
|
dat.Split(list,"\n");
|
|
foreach ( l:list )
|
|
{
|
|
if ( (l.Length() == 0) || (l.Left(2) == "//") || (l.Left(1) == "") )
|
|
continue;
|
|
bludtypes.Push(l);
|
|
}
|
|
}
|
|
// read damnum colors
|
|
damtypes.Clear();
|
|
damcolors.Clear();
|
|
for ( int lmp = Wads.FindLump("DAMTYPES"); lmp != -1; lmp = Wads.FindLump("DAMTYPES",lmp+1) )
|
|
{
|
|
String dat = Wads.ReadLump(lmp);
|
|
Array<String> list;
|
|
// Windows pls
|
|
dat.Replace("\r","");
|
|
list.Clear();
|
|
dat.Split(list,"\n");
|
|
foreach ( l:list )
|
|
{
|
|
if ( (l.Length() == 0) || (l.Left(1) == "#") || (l.Left(1) == "") )
|
|
continue;
|
|
int spc = l.IndexOf(" ");
|
|
damtypes.Push(l.Left(spc));
|
|
damcolors.Push(l.Mid(spc+1));
|
|
}
|
|
}
|
|
// cache various services into the handler on register
|
|
// this dramatically reduces overhead by not having to use an iterator every time they're needed
|
|
// especially noticeable for fun tags, as they're looked up for every monster on map load
|
|
let si = ServiceIterator.Find('FunTagService');
|
|
Service sv;
|
|
while ( sv = si.Next() ) funtagsv.Push(sv);
|
|
si = ServiceIterator.Find('MergeMonsterService');
|
|
while ( sv = si.Next() ) mergemonstersv.Push(sv);
|
|
// start profiling
|
|
if ( swwm_profstart <= 0 ) return;
|
|
bprofiletics = profiletics = swwm_profstart;
|
|
profiling = true;
|
|
for ( int i=0; i<8; i++ )
|
|
{
|
|
prof_ms[i] = 0;
|
|
prof_avg[i] = 0;
|
|
prof_calls[i] = 0;
|
|
}
|
|
Console.Printf("Gathering data for %d tic%s...",bprofiletics,(bprofiletics>1)?"s":"");
|
|
}
|
|
|
|
override void WorldTick()
|
|
{
|
|
if ( profiling ) ProfileTick();
|
|
LangRefresh();
|
|
QueueMaintenance();
|
|
if ( !mnotify && (level.maptime >= 5) )
|
|
{
|
|
mnotify = true;
|
|
let ti = ThinkerIterator.Create('SWWMStats',Thinker.STAT_STATIC);
|
|
SWWMStats s;
|
|
while ( s = SWWMStats(ti.Next()) )
|
|
{
|
|
if ( !SWWMUtility.IsKnownMap() ) break;
|
|
if ( s.myplayer != players[consoleplayer] ) continue;
|
|
int clust = level.cluster;
|
|
if ( SWWMUtility.IsEviternityTwo() )
|
|
{
|
|
// clusters have to be remapped here
|
|
if ( clust == 5 ) clust = 1;
|
|
else if ( (clust == 6) || (clust == 13) ) clust = 2;
|
|
else if ( (clust == 7) || (clust == 14) ) clust = 3;
|
|
else if ( (clust == 8) || (clust == 15) ) clust = 4;
|
|
else if ( (clust == 9) || (clust == 16) ) clust = 5;
|
|
else if ( (clust == 10) || (clust == 17) ) clust = 6;
|
|
else if ( (clust == 11) || (clust == 12) || (clust == 18) || (clust == 19) ) clust = 7;
|
|
}
|
|
else if ( SWWMUtility.IsEviternity() )
|
|
{
|
|
// we have to do some heavy lifting here because episodes don't match clusters
|
|
if ( level.levelnum <= 5 ) clust = 1;
|
|
else if ( level.levelnum <= 10 ) clust = 2;
|
|
else if ( level.levelnum <= 15 ) clust = 3;
|
|
else if ( level.levelnum <= 20 ) clust = 4;
|
|
else if ( level.levelnum <= 25 ) clust = 5;
|
|
else if ( level.levelnum <= 30 ) clust = 6;
|
|
else if ( level.levelnum <= 31 ) clust = 7;
|
|
else if ( level.levelnum <= 32 ) clust = 8;
|
|
}
|
|
int csiz = s.clustervisit.Size();
|
|
if ( (csiz > 0) && (s.clustervisit[csiz-1] != clust) )
|
|
Console.Printf(StringTable.Localize("$SWWM_NEWMISSION"));
|
|
}
|
|
}
|
|
for ( int i=0; i<legtrack.Size(); i++ )
|
|
{
|
|
// when a monster mutates, update its healthbar for all players
|
|
for ( int j=0; j<MAXPLAYERS; j++ )
|
|
{
|
|
if ( !playeringame[j] ) continue;
|
|
let t = SWWMQuickCombatTracker.Update(self,players[j],legtrack[i].Owner);
|
|
if ( t && (t.myplayer == players[consoleplayer]) ) Console.Printf(StringTable.Localize("$SWWM_LTFORM"),t.mytag);
|
|
}
|
|
legtrack.Delete(i--);
|
|
}
|
|
// healthbar on whatever the player is aiming at
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || (players[i].Health <= 0) || !players[i].mo ) continue;
|
|
let t = players[i].mo.GetPointer(AAPTR_PLAYER_GETTARGET);
|
|
if ( t && t.bSHOOTABLE && (t.Health > 0) ) SWWMQuickCombatTracker.Update(self,players[i],t);
|
|
// keep healthbars updated for all friends of this player
|
|
for ( int j=0; j<MAXPLAYERS; j++ )
|
|
{
|
|
if ( !playeringame[j] || (players[j].Health <= 0) || !players[j].mo || !players[j].mo.IsFriend(players[i].mo) ) continue;
|
|
SWWMQuickCombatTracker.Update(self,players[j],players[i].mo);
|
|
}
|
|
}
|
|
OnelinerTick();
|
|
ItemCountTrack();
|
|
CombatTrack();
|
|
OneHundredPercentCheck();
|
|
UpdateHUDObjects();
|
|
SimpleTracking();
|
|
VanillaBossTick();
|
|
if ( !profiling ) return;
|
|
ProfileTock(PT_WorldTick);
|
|
Console.Printf("%d...",profiletics);
|
|
profiletics--;
|
|
if ( profiletics > 0 ) return;
|
|
profiling = false;
|
|
static const String prof_name[] =
|
|
{
|
|
"WorldTick ",
|
|
"WorldThingSpawned ",
|
|
"WorldThingDestroyed",
|
|
"WorldThingDied ",
|
|
"WorldThingDamaged ",
|
|
"WorldThingRevived ",
|
|
"CheckReplacement ",
|
|
"CheckReplacee "
|
|
};
|
|
Console.Printf("Done!");
|
|
String str = String.Format(
|
|
"SWWMHandler profiling info for %d tic%s:\n"
|
|
" event name | calls | total ms | avg ms\n"
|
|
"---------------------|--------|-------------|-------------\n",
|
|
bprofiletics,(bprofiletics>1)?"s":"");
|
|
for ( int i=0; i<8; i++ )
|
|
str.AppendFormat(" %s | %6d | %11.6f | %11.6f\n",prof_name[i],prof_calls[i],prof_ms[i],prof_avg[i]);
|
|
Console.PrintfEx(PRINT_HIGH|PRINT_NONOTIFY,str);
|
|
}
|
|
|
|
override void PostUiTick()
|
|
{
|
|
VanillaBossUITick();
|
|
// corruption cards dialogue
|
|
if ( ccloaded && !gdat.ccstartonce && !cardmessaged && (gamestate == GS_LEVEL) )
|
|
{
|
|
let m = Menu.GetCurrentMenu();
|
|
if ( m && (m.GetClassName() == 'CorruptionCardsSelector') ) incardmenu = true;
|
|
else if ( incardmenu )
|
|
{
|
|
if ( !swwm_ccmessage ) SWWMDialogues.StartSeq("CC");
|
|
CVar.GetCVar('swwm_ccmessage').SetBool(true);
|
|
cardmessaged = true;
|
|
SendNetworkEvent("swwmccstart");
|
|
}
|
|
}
|
|
}
|
|
|
|
override void WorldLinePreActivated( WorldEvent e )
|
|
{
|
|
// oneliner on locked doors
|
|
if ( !e.Thing ) return;
|
|
int locknum = SWWMUtility.GetLineLock(e.ActivatedLine);
|
|
if ( (locknum < 1) || (locknum > 255) ) return;
|
|
if ( e.Thing.CheckLocalView() && !e.Thing.CheckKeys(locknum,false,true) )
|
|
{
|
|
if ( !lastlock || (gametic > lastlock+20) )
|
|
{
|
|
if ( Key.IsLockDefined(locknum) )
|
|
lastlock = AddOneliner("locked",2);
|
|
else lastlock = AddOneliner("jammed",2);
|
|
}
|
|
}
|
|
}
|
|
|
|
override void WorldLineActivated( WorldEvent e )
|
|
{
|
|
if ( !(e.ActivationType&SPAC_Use) ) return;
|
|
if ( !e.Thing || !e.Thing.player ) return;
|
|
if ( (e.Thing.player == players[consoleplayer]) && swwm_beepboop )
|
|
SWWMHandler.AddOneliner("buttonpush",2,0);
|
|
let w = SWWMWeapon(e.Thing.player.ReadyWeapon);
|
|
if ( (!w || !w.wallponch) && (!(e.Thing is 'Demolitionist') || !Demolitionist(e.Thing).hitactivate) ) return;
|
|
let s = SWWMStats.Find(e.Thing.player);
|
|
if ( s ) s.wponch++;
|
|
SWWMUtility.AchievementProgressInc("slemg",1,e.Thing.player);
|
|
}
|
|
|
|
// stuff for hud
|
|
override void RenderUnderlay( RenderEvent e )
|
|
{
|
|
// armor/health flashes
|
|
FlashRender(e);
|
|
if ( slotstrictwarn && (gametic < slotstrictwarn) )
|
|
{
|
|
String str = StringTable.Localize("$SWWM_SETSLOTSTRICT");
|
|
if ( sswstr != str )
|
|
{
|
|
sswstr = str;
|
|
if ( sswl ) sswl.Destroy();
|
|
}
|
|
double t = (slotstrictwarn-(gametic+e.FracTic))/20.;
|
|
double alph = clamp(t,0.,1.);
|
|
if ( !sswl ) sswl = newsmallfont.BreakLines(sswstr,300);
|
|
double yy = (200-sswl.Count()*newsmallfont.GetHeight())/2;
|
|
for ( int i=0; i<sswl.Count(); i++ )
|
|
{
|
|
double xx = (320-sswl.StringWidth(i))/2;
|
|
Screen.DrawText(newsmallfont,Font.CR_UNTRANSLATED,xx,yy,sswl.StringAt(i),DTA_Clean,true,DTA_Alpha,alph);
|
|
yy += newsmallfont.GetHeight();
|
|
}
|
|
sswl.Destroy();
|
|
}
|
|
// weapon underlays
|
|
let sw = SWWMWeapon(players[consoleplayer].ReadyWeapon);
|
|
if ( sw )
|
|
{
|
|
sw.RenderUnderlay(e);
|
|
if ( sw.bHASSCRTEX ) sw.RenderTexture(e);
|
|
}
|
|
TraceCrosshairs(e);
|
|
RenderCrosshairs(e);
|
|
if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return;
|
|
SWWMStatusBar(statusbar).viewpos = e.viewpos;
|
|
SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll);
|
|
}
|
|
|
|
// various shaders
|
|
override void RenderOverlay( RenderEvent e )
|
|
{
|
|
CheatOverlay(e);
|
|
RenderShaders(e);
|
|
DrawDebug(e);
|
|
}
|
|
}
|