swwmgz_m/zscript/swwm_handler.zsc

328 lines
9.7 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
{
transient int lastlock;
transient int lastpickuptic[MAXPLAYERS];
SWWMScoreObj scorenums;
SWWMDamNum damnums;
SWWMInterest intpoints;
Array<String> damtypes, damcolors;
transient CVar usedamcolors;
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;
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]++;
}
override void OnRegister()
{
// oneliner RNG must be relative to consoleplayer
SetRandomSeed[DemoLines](Random[DemoLines]()+consoleplayer+int(MSTimeF()));
// "uninitialize" some vars
iwantdie = -1;
bossmap = -1;
indoomvacation = -1;
inultdoom2 = -1;
// class-checking ones can be initialized here easily
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
if ( AllActorClasses[i].GetClassName() == "RLMonster" ) hasdrlamonsters = true;
else if ( AllActorClasses[i].GetClassName() == "LOBZombieman" ) haslegionofbones = true;
}
for ( int i=0; i<AllClasses.Size(); i++ )
{
if ( AllClasses[i].GetClassName() != "CCards_Global" ) continue;
ccloaded = true;
break;
}
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;
// fucking Windows
dat.Replace("\r","");
list.Clear();
dat.Split(list,"\n");
for ( int i=0; i<list.Size(); i++ )
{
if ( (list[i].Length() == 0) || (list[i].Left(2) == "//") || (list[i].Left(1) == "") )
continue;
bludtypes.Push(list[i]);
}
}
// 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;
// fucking Windows
dat.Replace("\r","");
list.Clear();
dat.Split(list,"\n");
for ( int i=0; i<list.Size(); i++ )
{
if ( (list[i].Length() == 0) || (list[i].Left(1) == "#") || (list[i].Left(1) == "") )
continue;
int spc = list[i].IndexOf(" ");
damtypes.Push(list[i].Left(spc));
damcolors.Push(list[i].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);
// session globals
gdat = SWWMGlobals.Get();
// 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.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();
FlashTick();
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.Printf(str);
}
override void PostUiTick()
{
OnelinerUITick();
FlashUITick();
VanillaBossUITick();
MapstartUITick();
}
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 ( SWWMUtility.IsValidLockNum(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);
}
}