// 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]; // these two are mostly used transient int lastnuggettic[MAXPLAYERS]; // to avoid deafening players SWWMScoreObj scorenums; SWWMDamNum damnums; SWWMInterest intpoints; Array 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 funtagsv, mergemonstersv; // for checkreplacement bool hasdrlamonsters, haslegionofbones; int iskdizd; Array 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; // for bloody footsteps mkBloodPool bloodpools; 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 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 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 0) ) SWWMQuickCombatTracker.Update(self,players[i],t); // keep healthbars updated for all friends of this player for ( int j=0; j 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() { OnelinerUITick(); 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