328 lines
9.7 KiB
Text
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);
|
|
}
|
|
}
|