// 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; 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+MSTime()); // "uninitialize" some vars iwantdie = -1; bossmap = -1; indoomvacation = -1; inultdoom2 = -1; // class-checking ones can be initialized here easily foreach ( cls:AllActorClasses ) { if ( cls.GetClassName() == "RLMonster" ) hasdrlamonsters = true; else if ( cls.GetClassName() == "LOBZombieman" ) haslegionofbones = true; } foreach ( cls:AllClasses ) { if ( cls.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 list; // fucking Windows 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; // fucking Windows 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); // 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 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.Printf(str); } override void PostUiTick() { OnelinerUITick(); FlashUITick(); 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(SWWMDLG_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 ( 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