// WorldThing* events (excluding damage) extend Class SWWMHandler { // prevents revived monsters from spawning in more golden shells Array alreadygold; // legendary monster markers (for the "has mutated" message) Array legtrack; override void WorldThingRevived( WorldEvent e ) { if ( profiling ) ProfileTick(); // reattach headpats if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing) || SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) ) { // you can pet the dog, and you can also pet the caco (and friends) let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; } if ( !(e.Thing is 'PlayerPawn') ) { if ( profiling ) ProfileTock(PT_WORLDTHINGREVIVED); return; } // reset some vars if ( e.Thing.playernumber() != -1 ) { multilevel[e.Thing.playernumber()] = 0; spreecount[e.Thing.playernumber()] = 0; tookdamage[e.Thing.playernumber()] = false; lastkill[e.Thing.playernumber()] = int.min; } // reset uptime since player had just died SWWMStats s = SWWMStats.Find(e.Thing.player); if ( s ) s.lastspawn = level.totaltime; if ( profiling ) ProfileTock(PT_WORLDTHINGREVIVED); } private bool HexenMap40() { if ( level.GetChecksum() ~== "2A6C4235B942467D25FD50D5B313E67A" ) return true; // 1.1 if ( level.GetChecksum() ~== "1C5DE5A921DEE405E98E7E09D9829387" ) return true; // 1.0 if ( level.GetChecksum() ~== "EFAFE59092DE5E613562ACF52B86C37F" ) return true; // beta return false; } static int ShouldSpawnGold() { int totalneeded = 0; // check "free space" in player inventories for ( int i=0; i= 1000) || e.Thing.FindInventory('BossMarker')) && (alreadygold.Find(e.Thing) == alreadygold.Size()) ) { // make sure we can't farm drops from revivable enemies // (or cause some things to spam-spawn gold shells) // (*cough* Touhou Doom *cough*) alreadygold.Push(e.Thing); // weight drop chance based on total count of this monster type // guarantees that maps that get a bit slaughtery won't become easy farms for drops int dropweight = 0; let ti = ThinkerIterator.Create(e.Thing.GetClass()); while ( ti.Next() ) dropweight++; int minchance = max(1,6-(e.Thing.GetSpawnHealth()/1000)); dropweight = max(minchance,dropweight/2); // make sure the gold shell is "worth spawning", too // (also, chance should be reduced as you acquire more shells) int gchance = int(ceil(ShouldSpawnGold()/2.)); if ( !Random[GoldDrop](0,dropweight) && Random[GoldDrop](0,gchance) ) { let g = Actor.Spawn('GoldShell',e.Thing.Vec3Offset(0,0,e.Thing.Height/2.)); double ang = FRandom[SpareShells](0.,360.); g.vel.xy = Actor.AngleToVector(ang,FRandom[SpareShells](.4,.8)); g.vel.z = FRandom[SpareShells](2.,4.); } } // Korax instakill if ( (e.Thing is 'Korax') && !e.Thing.special2 && HexenMap40() ) { e.Thing.special2 = 1; // terminate the monster closet scripts, open all the // doors ourselves level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,220); level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,220); level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,221); level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,255); level.ExecuteSpecial(Door_Open,e.Thing,null,false,10,16); level.ExecuteSpecial(Door_Open,e.Thing,null,false,11,16); level.ExecuteSpecial(Door_Open,e.Thing,null,false,12,16); level.ExecuteSpecial(Door_Open,e.Thing,null,false,13,16); level.ExecuteSpecial(Door_Open,e.Thing,null,false,14,16); level.ExecuteSpecial(Door_Open,e.Thing,null,false,10,16); // keep the portal closed, you can't leave unless you // kill everyone else let t = new('KoraxYeeted'); t.ChangeStatNum(Thinker.STAT_USER); } // Archangelus instakill if ( (e.Thing.GetClassName() == 'ArchangelusA') && (e.Thing.DamageType == 'Ynykron') ) { // this will skip the second phase dialogue // (note that you can only have an Ynykron in this fight by cheating, tho) archangelus_zapped = true; } // Archangelus death if ( e.Thing.GetClassName() == 'ArchangelusB' ) { // kill all other monsters let ti = ThinkerIterator.Create('Actor'); Actor a; while ( a = Actor(ti.Next()) ) { if ( (a != e.Thing) && (a.Health > 0) && (a.bBossSpawned || a.bCOUNTKILL) ) a.DamageMobj(e.Thing,e.Thing,a.Health,'EndMii',DMG_FORCED|DMG_THRUSTLESS); } } // Origin instakill if ( ((e.Thing.GetClassName() == 'The_Origin_Phase_1') || (e.Thing.GetClassName() == 'The_Origin_Phase_2') || (e.Thing.GetClassName() == 'The_Absolute_Origin_Phase_1') || (e.Thing.GetClassName() == 'The_Absolute_Origin_Phase_2')) && (e.Thing.DamageType == 'Ynykron') ) { // this will trigger special dialogue on the next phase origin_zapped = true; } if ( swwm_partytime ) { let pt = Actor.Spawn('PartyTime',e.Thing.pos); pt.target = e.Thing; } if ( profiling ) ProfileTock(PT_WORLDTHINGDIED); } private void DoKeyTagFix( Actor a ) { if ( a is 'SWWMKey' ) return; // mod's custom keys are fine if ( a is 'RedCard' ) a.SetTag("$T_REDCARD"); else if ( a is 'BlueCard' ) a.SetTag("$T_BLUECARD"); else if ( a is 'YellowCard' ) a.SetTag("$T_YELLOWCARD"); else if ( a is 'RedSkull' ) a.SetTag("$T_REDSKULL"); else if ( a is 'BlueSkull' ) a.SetTag("$T_BLUESKULL"); else if ( a is 'YellowSkull' ) a.SetTag("$T_YELLOWSKULL"); else if ( a.GetClassName() == 'KeyRed' ) a.SetTag("$T_KEYRED"); } // tempfix keys have no tags static void KeyTagFix( Actor a ) { let hnd = SWWMHandler(Find('SWWMHandler')); if ( hnd ) hnd.DoKeyTagFix(a); } // copies the floatbob of overlapping identical items, so it doesn't look weird private void CopyFloatBob( Actor a ) { for ( Actor t=a.CurSector.thinglist; t; t=t.snext ) { if ( (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue; a.floatbobphase = t.floatbobphase; a.angle = t.angle; // also copy angle break; } } private bool SuppressMultiItem( WorldEvent e ) { // quick checks if ( !G_SkillPropertyInt(SKILLP_SpawnMulti) || multiplayer || sv_alwaysspawnmulti ) return false; // is it coop inventory? suppress it if ( (e.Thing.spawnflags&(MTF_COOPERATIVE|MTF_DEATHMATCH)) && !(e.Thing.spawnflags&MTF_SINGLE) && ((e.Thing is 'Inventory') || (e.Thing is 'MaceSpawner')) ) { e.Thing.ClearCounters(); e.Thing.Destroy(); return true; } return false; } override void WorldThingDestroyed( WorldEvent e ) { if ( profiling ) ProfileTick(); // for gibber throttling if ( e.Thing is 'mkBloodDrop' ) blods_realcnt--; else if ( e.Thing is 'mkFlyingGib' ) meats_realcnt--; if ( profiling ) ProfileTock(PT_WORLDTHINGDESTROYED); } override void WorldThingSpawned( WorldEvent e ) { if ( profiling ) ProfileTick(); // for gibber throttling if ( e.Thing is 'mkBloodDrop' ) blods_realcnt++; else if ( e.Thing is 'mkFlyingGib' ) meats_realcnt++; if ( !e.Thing || SuppressMultiItem(e) ) { if ( profiling ) ProfileTock(PT_WORLDTHINGSPAWNED); return; } IWantDieSpawn(e); if ( (e.Thing is 'TeleportDest') || (e.Thing is 'BossTarget') ) { let d = Actor.Spawn('SWWMTeleportDest',e.Thing.pos); d.bNOGRAVITY = e.Thing.bNOGRAVITY; } if ( e.Thing is 'Inventory' ) { CopyFloatBob(e.Thing); if ( Inventory(e.Thing).PickupFlash is 'SWWMPickupFlash' ) { let p = Actor.Spawn(Inventory(e.Thing).PickupFlash,e.Thing.pos); p.target = e.Thing; p.SetStateLabel('Pickup'); } // for notification if ( e.Thing.GetClassName() == 'LDLegendaryMonsterTransformed' ) legtrack.Push(Inventory(e.Thing)); } if ( swwm_doomfall && e.Thing.bISMONSTER && !e.Thing.bBOSS ) e.Thing.bFALLDAMAGE = true; if ( e.Thing is 'Key' ) { DoKeyTagFix(e.Thing); SWWMInterest.Spawn(self,thekey:Key(e.Thing)); } if ( indoomvacation == -1 ) indoomvacation = SWWMUtility.InDoomVacation(); if ( inultdoom2 == -1 ) inultdoom2 = SWWMUtility.IsUltDoom2(); if ( e.Thing.GetClass() == 'Pig' ) e.Thing.SetTag("$FN_PIG"); // missing in gzdoom // eviternity stuff else if ( (e.Thing.GetClassName() == 'Archangelus') || (e.Thing.GetClassName() == 'ArchangelusA') || (e.Thing.GetClassName() == 'ArchangelusB') ) e.Thing.SetTag("$FN_ANGEL"); else if ( e.Thing.GetClassName() == 'AstralCaco' ) e.Thing.SetTag("$FN_ASTRAL"); else if ( e.Thing.GetClassName() == 'Annihilator' ) e.Thing.SetTag("$FN_ANNIHIL"); else if ( e.Thing.GetClassName() == 'FormerCaptain' ) e.Thing.SetTag("$FN_FCAPTAIN"); else if ( e.Thing.GetClassName() == 'NightmareDemon' ) e.Thing.SetTag("$FN_NDEMON"); // eviternity 2 stuff else if ( e.Thing.GetClassName() == 'FormerCorporal' ) e.Thing.SetTag("$FN_FCORPORAL"); else if ( e.Thing.GetClassName() == 'AstralArachnotron' ) e.Thing.SetTag("$FN_ASTRALARACH"); else if ( e.Thing.GetClassName() == 'AstralCacodemon' ) e.Thing.SetTag("$FN_ASTRAL"); else if ( e.Thing.GetClassName() == 'Veilimp' ) e.Thing.SetTag("$FN_VEILIMP"); else if ( (e.Thing.GetClassName() == 'GoldenAstralCaco') || (e.Thing.GetClassName() == 'GoldenAstralCacoBoss') ) e.Thing.SetTag("$FN_ASTRALGOLD"); else if ( e.Thing.GetClassName() == 'DukeOfHell' ) e.Thing.SetTag("$FN_DUKE"); else if ( e.Thing.GetClassName() == 'AstralBabycaco' ) e.Thing.SetTag("$FN_ASTRALBABY"); else if ( e.Thing.GetClassName() == 'NightmareCacodemon' ) e.Thing.SetTag("$FN_NAC"); else if ( e.Thing.GetClassName() == 'AstralMancubus' ) e.Thing.SetTag("$FN_ASTRALFATSO"); else if ( (e.Thing.GetClassName() == 'NecromenaceA') || (e.Thing.GetClassName() == 'NecromenaceB') || (e.Thing.GetClassName() == 'NecromenaceC') || (e.Thing.GetClassName() == 'NecromenaceD') ) e.Thing.SetTag("$FN_NECROMENACE"); else if ( (e.Thing.GetClassName() == 'The_Origin_Phase_1') || (e.Thing.GetClassName() == 'The_Origin_Phase_2') || (e.Thing.GetClassName() == 'The_Origin_Phase_3') || (e.Thing.GetClassName() == 'The_Absolute_Origin_Phase_1') || (e.Thing.GetClassName() == 'The_Absolute_Origin_Phase_2') || (e.Thing.GetClassName() == 'The_Absolute_Origin_Phase_3') ) e.Thing.SetTag("$FN_ORIGIN"); else if ( e.Thing.GetClassName() == 'SpectralAstralCacodemon' ) e.Thing.SetTag("$FN_SAC"); else if ( e.Thing.GetClassName() == 'GrandDukeofHell' ) e.Thing.SetTag("$FN_GDUKE"); // doom vacation stuff else if ( indoomvacation ) { if ( e.Thing.GetClassName() == 'Babe' ) { e.Thing.bSHOOTABLE = false; // no hurt let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .2; } else if ( e.Thing.GetClassName() == 'CommanderKeen' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .4; HeadpatTracker(hp).angfix = 5; } else if ( e.Thing.GetClassName() == 'BBChair' ) { e.Thing.bUSESPECIAL = false; let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .2; HeadpatTracker(hp).angfix = 15; HeadpatTracker(hp).patstate = e.Thing.MeleeState; } else if ( e.Thing.GetClassName() == 'EvilEye' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .1; } else if ( e.Thing.GetClassName() == 'HeadCandles' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).angfix = 20; } else if ( e.Thing.GetClassName() == 'HeartColumn' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = -.3; } else if ( e.Thing.GetClassName() == 'Meat2' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .6; HeadpatTracker(hp).angfix = -15; HeadpatTracker(hp).dvacationarghack = true; } else if ( e.Thing.GetClassName() == 'Meat3' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .6; HeadpatTracker(hp).angfix = 20; HeadpatTracker(hp).dvacationarghack = true; } else if ( e.Thing.GetClassName() == 'LegsBabe' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = -1.5; HeadpatTracker(hp).angfix = 20; HeadpatTracker(hp).dvacationarghack = true; } else if ( e.Thing.GetClassName() == 'Meat4' ) { let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; HeadpatTracker(hp).heightfix = .6; HeadpatTracker(hp).angfix = 15; HeadpatTracker(hp).dvacationarghack = true; } } else if ( inultdoom2 && (e.Thing.GetClassName() == 'WolfensteinSS') ) { e.Thing.SetTag("$FN_ELITEZOMBIE"); //e.Thing.Obituary = "$OB_ELITEZOMBIE"; e.Thing.SeeSound = "grunt/sight"; e.Thing.AttackSound = "grunt/attack"; e.Thing.PainSound = "grunt/pain"; e.Thing.DeathSound = "grunt/death"; e.Thing.ActiveSound = "grunt/active"; } else if ( ccloaded && (e.Thing.GetClassName() == 'CCards_Token_Glitched') ) { if ( !gdat.cclilithonce ) SendInterfaceEvent(consoleplayer,"swwmsetdialogue.LILITH"); gdat.cclilithonce = true; } if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing) || SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) ) { // you can pet the dog, and you can also pet the caco (and friends) let hp = Actor.Spawn('HeadpatTracker',e.Thing.pos); hp.target = e.Thing; } // vanilla blood color changes if ( (e.Thing.GetClassName() == 'BaronOfHell') || (e.Thing.GetClassName() == 'HellKnight') || (e.Thing.GetClassName() == 'Bishop') || (e.Thing.GetClassName() == 'Korax') ) { let gb = Actor.Spawn('GreenBloodReference'); e.Thing.CopyBloodColor(gb); gb.Destroy(); } else if ( e.Thing.GetClassName() == 'Cacodemon' ) { let bb = Actor.Spawn('BlueBloodReference'); e.Thing.CopyBloodColor(bb); bb.Destroy(); } else if ( (e.Thing.GetClassName() == 'Wizard') || (e.Thing.GetClassName() == 'Heresiarch') || (e.Thing.GetClassName() == 'Sorcerer2') ) { let pb = Actor.Spawn('PurpleBloodReference'); e.Thing.CopyBloodColor(pb); pb.Destroy(); } else if ( e.Thing.GetClassName() == 'LostSoul' ) e.Thing.bNOBLOOD = true; VanillaBossSpawn(e); if ( profiling ) ProfileTock(PT_WORLDTHINGSPAWNED); } }