swwmgz_m/zscript/swwm_handler.zsc
Marisa Kirisame 67483877c0 Implemented selling at the store (right click / backspace to toggle).
Implemented excess ammo dropping for weapon pickups (previously it was just sold).
Implemented dropped weapon loaded ammo handling (previously this made ammo simply disappear).
Implemented mag splitting for excess ammo pickups (split 'em into individual rounds).
Dropping a Candygun will drop spares first (if any, they will be empty, too).
2021-01-25 17:32:44 +01:00

3484 lines
118 KiB
Text

// event handlers and whatnot
// save version holder
Class SWWMSaveVerData : Thinker
{
String ver;
}
// Static handler responsible for some special stuff
Class SWWMStaticHandler : StaticEventHandler
{
// crash handler
ui bool wasinmap;
ui int timer;
// versioning
bool tainted;
String taintver;
bool mptaint[MAXPLAYERS];
bool mprecv[MAXPLAYERS];
String mpver[MAXPLAYERS];
int checktic;
ui bool mpsent, checked;
override void NewGame()
{
// set save version every new session
let svd = new("SWWMSaveVerData");
svd.ChangeStatNum(Thinker.STAT_STATIC);
svd.ver = StringTable.Localize("$SWWM_SHORTVER");
}
override void WorldLoaded( WorldEvent e )
{
// save version checker
if ( !e.IsSaveGame ) return;
checktic = gametic+5;
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
let svd = SWWMSaveVerData(ti.Next());
if ( !svd )
{
tainted = true;
taintver = "\cg(no version info)\c-";
return;
}
String cver = StringTable.Localize("$SWWM_SHORTVER");
if ( svd.ver != cver )
{
tainted = true;
taintver = svd.ver;
}
}
override void OnRegister()
{
// preload various fonts
Font.GetFont('k6x8');
Font.GetFont('k6x8Shaded');
Font.GetFont('k6x8ShadedInverse');
Font.GetFont('Miniwi');
Font.GetFont('MiniwiShaded');
Font.GetFont('MiniwiShadedInverse');
Font.GetFont('MPlus');
Font.GetFont('MPlusShaded');
Font.GetFont('MPlusShadedInverse');
Font.GetFont('Tewi');
Font.GetFont('TewiShaded');
Font.GetFont('TewiShadedInverse');
Font.GetFont('SWWMBigFont');
}
override void NetworkProcess( ConsoleEvent e )
{
if ( e.Name ~== "swwmgetversion" )
{
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
let svd = SWWMSaveVerData(ti.Next());
if ( svd ) Console.Printf("\cj%s\c-",svd.ver);
else Console.Printf("\cg(no version data)\c-");
if ( tainted ) Console.Printf("\cgversion mismatched\c-");
else Console.Printf("\cdversion not mismatched\c-");
return;
}
if ( e.IsManual ) return;
if ( e.Name.Left(12) ~== "swwmversion." )
{
String verstr = e.Name.Mid(12);
mprecv[e.Player] = true;
mpver[e.Player] = verstr;
if ( verstr != StringTable.Localize("$SWWM_SHORTVER") )
mptaint[e.Player] = true;
}
}
override void PostUiTick()
{
// TODO achievement update code would go in here
if ( gamestate != GS_LEVEL ) return;
if ( !mpsent )
{
EventHandler.SendNetworkEvent("swwmversion."..StringTable.Localize("$SWWM_SHORTVER"));
mpsent = true;
return;
}
if ( checked || (gametic < checktic) ) return;
if ( multiplayer )
{
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || mprecv[i] ) continue;
// waiting for version info from all players
return;
}
}
checked = true;
String cver = StringTable.Localize("$SWWM_SHORTVER");
if ( tainted )
{
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
let svd = SWWMSaveVerData(ti.Next());
if ( !svd ) Console.Printf("\cgWARNING:\n \cjSave contains no version data. Issues may happen.\c-");
else
{
Console.Printf("\cgWARNING:\n \cjVersion mismatch with save data. Issues may happen.\c-");
Console.Printf("\cgSaved:\n \cj"..svd.ver.."\c-");
Console.Printf("\cgCurrent:\n \cj"..cver.."\c-");
}
}
if ( multiplayer )
{
bool found = false;
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) || (!mptaint[i] && (mpver[i] != "")) ) continue;
if ( !found )
{
Console.Printf("\cfWARNING:\n \cjVersion mismatch between players. Desyncs will happen.\c-");
Console.Printf("\cgYou:\n \cj"..cver.."\c-");
}
found = true;
Console.Printf("\cgPlayer %d (\c-%s\cg):\n \cj%s\c-",i+1,players[i].GetUserName(),(mpver[i]=="")?"\cg(no version data)\c-":mpver[i]);
}
}
}
override void UiTick()
{
// HACK! Graf, please let us change this in a cleaner way
if ( menuDelegate.GetClass() == 'DoomMenuDelegate' )
{
menuDelegate.Destroy();
menuDelegate = new("SWWMMenuDelegate");
}
// Hey Graf how about you let us replace the class used for the
// "Read This!" menu in mapinfo/gameinfo or something so I
// don't have to do this hack here?
Menu cur = Menu.GetCurrentMenu();
if ( cur is 'ReadThisMenu' )
{
cur.Close();
Menu.SetMenu('SWWMHelpMenu');
}
// Fancy crash effect
if ( (gamestate == GS_LEVEL) || (gamestate == GS_TITLELEVEL) )
{
wasinmap = true;
timer = 0;
}
else if ( (gamestate == GS_FULLCONSOLE) && ((wasinmap && !players[consoleplayer].viewheight) || (timer > 0)) )
{
wasinmap = false;
if ( timer == 1 )
{
Console.Printf("\cfOopsie Woopsie!\c-");
let hnd = SWWMBrutalHandler(StaticEventHandler.Find("SWWMBrutalHandler"));
if ( hnd && hnd.detected )
{
S_StartSound("crash/glass",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
S_StartSound("crash/glass",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
}
else S_StartSound("crash/crash",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
}
else if ( timer == 70 )
{
Console.Printf("\cfLooks like GZDoom made a fucky wucky! owo\c-");
S_StartSound("crash/curb",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
}
else if ( timer == 140 )
{
let hnd = SWWMBrutalHandler(StaticEventHandler.Find("SWWMBrutalHandler"));
if ( hnd && hnd.detected ) Console.Printf("\cfDon't blame me. Shouldn't have tried running this with Brutal Doom.\c-");
else Console.Printf("\cfIf you didn't trigger it manually, it's best if you take a screenshot and show it to Marisa.\c-");
Console.Printf("\cfLoaded Version:\n \cj%s\c-",StringTable.Localize("$SWWM_SHORTVER"));
if ( tainted ) Console.Printf("\cfSavegame Version:\n \cj%s\c-",taintver);
}
timer++;
}
}
}
// fuck
Class DontDuplicate : Inventory {}
Class DontDuplicate2 : Inventory {}
Class HOLYCOWIMTOTALLYGOINGSOFASTOHFUCK : Inventory
{
override void DoEffect()
{
Super.DoEffect();
if ( !Owner || (Owner.Health <= 0) ) return;
if ( (Owner.tics > 1) && (Owner.tics > max(1,Owner.CurState.tics/2)) )
Owner.tics = max(1,Owner.CurState.tics/2);
}
}
// Handler responsible for item replacements and whatever else
Class SWWMHandler : EventHandler
{
transient String oneliner, onelinersnd;
transient int onelinertic, onelinerspan, onelinerlevel;
transient int lastlock, lastcombat;
transient Array<Actor> combatactors;
transient Array<Int> combattics;
transient int highesttic;
transient Array<QueuedFlash> flashes;
transient Array<LastLine> lastlines;
transient int lastpickuptic[MAXPLAYERS];
SWWMCombatTracker trackers;
SWWMScoreObj scorenums, damnums;
SWWMInterest intpoints;
int trackers_cnt, scorenums_cnt, damnums_cnt, intpoints_cnt;
bool tookdamage[MAXPLAYERS];
int spreecount[MAXPLAYERS];
int lastkill[MAXPLAYERS];
int multilevel[MAXPLAYERS];
int lastitemcount[MAXPLAYERS];
bool allkills, allitems, allsecrets;
bool mapclear;
bool mnotify;
// for custom cheats
transient ui int kcode;
transient ui String kstr;
// heal/armor flashes need to be handled here so they don't stack
transient int hflash[MAXPLAYERS], aflash[MAXPLAYERS];
// for menu events
transient Array<MenuTransaction> checklist;
transient CVar mutevoice, accdamage;
transient ui CVar useshaders, altrage;
transient CVar lang;
transient String curlang;
transient CVar funtags;
transient bool curfuntags;
transient int slotstrictwarn;
// optimization
OnFire fires;
int fires_cnt;
// junk
SWWMCasing casings, casings_end;
int casings_cnt, oldmaxcasings;
SWWMChip chips, chips_end;
int chips_cnt, oldmaxdebris;
// gore
mkBloodDrop blods, blods_end;
int blods_cnt, oldmaxblood;
mkFlyingGib meats, meats_end;
int meats_cnt, oldmaxgibs;
// prevents revived monsters from spawning in more golden shells
Array<Actor> alreadygold;
// attempt to optimize Ynykron singularity suction
Array<Actor> suckableactors;
// vanilla boss stuff
String bosstag;
Array<Actor> bossactors;
Actor bossbrainactor;
Actor bossviewactor;
TextureID facetex[5];
bool initialized;
ui bool ui_initialized;
ui TextureID bbar_f, bbar_r, bbar_d;
ui double bossalpha;
ui DynamicValueInterpolator ihealth, ihealthr;
ui int thealth, hmax;
ui int oldhealth[30];
ui int cummdamage, lastcummtic; // please do not misread
transient ui CVar dodrawbossbar;
enum EVanillaMap
{
MAP_NONE,
MAP_DE1M8,
MAP_DE2M8,
MAP_DE3M8,
MAP_DE4M8,
MAP_HE1M8_HE4M8,
MAP_HE2M8_HE5M8,
MAP_HE3M8,
MAP_DMAP07,
MAP_DMAP30,
MAP_HMAP12,
MAP_HMAP23_HMAP27,
MAP_HMAP36,
MAP_HMAP37,
MAP_HMAP38,
MAP_HMAP40,
MAP_EVMAP30 // eviternity
};
private int WhichVanillaBossMap()
{
String mapsum = level.GetChecksum();
if ( (mapsum ~== "94500F4B006B316FE03AC46865AEABF8")
|| (mapsum ~== "97079958C7E89C1908890730B8B9FEB7")
|| (mapsum ~== "058FB092EA1B70DA1E3CBF501C4A91A1") )
return MAP_DE1M8;
if ( mapsum ~== "EFFE91DF41AD41F6973C06F0AD67DDB9" )
return MAP_DE2M8;
if ( mapsum ~== "EF128313112110ED6C1549AF96AF26C9" )
return MAP_DE3M8;
if ( mapsum ~== "2DC939E508AB8EB68AF79D5B60568711" )
return MAP_DE4M8;
if ( (mapsum ~== "27639D04F8090D57A47D354992435893")
|| (mapsum ~== "30D1480A6D4F3A3153739D4CCF659C4E") )
return MAP_HE1M8_HE4M8;
if ( (mapsum ~== "5158C22A0F30CE5E558FD2A05D67685E")
|| (mapsum ~== "85AC7D20D18F9BC49B9696CC2E67F029") )
return MAP_HE2M8_HE5M8;
if ( mapsum ~== "4719C2C71EF28F52310B889DD5A9778B" )
return MAP_HE3M8;
if ( mapsum ~== "291F24417FB3DD411339AE82EF9B3597" )
return MAP_DMAP07;
if ( mapsum ~== "5EECD88F4491F516D590CE4BBF45F532" )
return MAP_DMAP30;
if ( (mapsum ~== "89C4CD26EF05E2577B10CAFE56226662")
|| (mapsum ~== "441BF111747671066A10A146C03EEFC4")
|| (mapsum ~== "55E321849F3699655D7E062C90682F63") )
return MAP_HMAP12;
if ( (mapsum ~== "E3B06F44DBF6F7E7754D7B1DAEF707E4")
|| (mapsum ~== "FC832437D7A2B7094A9B56C3909773D9")
|| (mapsum ~== "91AD797F95CC4C6D6AE33B21F664C60B")
|| (mapsum ~== "188B1B4244BD8DA501D8532696EC8654")
|| (mapsum ~== "5B29D0889DF09A8250D62FA09EB2B452")
|| (mapsum ~== "D3C5FA777BA52264546E6569F167AF0D") )
return MAP_HMAP23_HMAP27;
if ( (mapsum ~== "4444C95C2029DA6EECAC92DAA31CE665")
|| (mapsum ~== "33752742BCA8E539A6EE3E5D0FDA8744")
|| (mapsum ~== "3FFAF2F624C1B4BB6F581DCF7B99CBA7") )
return MAP_HMAP36;
if ( (mapsum ~== "78979A583B1E30D94C9DAE2BCFA9A18D")
|| (mapsum ~== "FDC90F44C65A71E0901C1B9FFFCF3D02")
|| (mapsum ~== "088ECE0E0F3E68448FA1D901001A0084") )
return MAP_HMAP37;
if ( (mapsum ~== "3BF62E4F9FB3CF9AF267421CE2D5F348")
|| (mapsum ~== "4799E1FDB5A3C0E3AD650B5AC215A737")
|| (mapsum ~== "5C63A02B0B04D9AE95CA51687DC3406F") )
return MAP_HMAP38;
if ( (mapsum ~== "EFAFE59092DE5E613562ACF52B86C37F")
|| (mapsum ~== "1C5DE5A921DEE405E98E7E09D9829387")
|| (mapsum ~== "2A6C4235B942467D25FD50D5B313E67A") )
return MAP_HMAP40;
if ( mapsum ~== "5C5E5C08AF3572F31CF27318679F2B4E" )
return MAP_EVMAP30;
return MAP_NONE;
}
static void QueueCasing( SWWMCasing c )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return;
hnd.casings_cnt++;
if ( !hnd.casings )
{
// this is the initial one
hnd.casings = c;
hnd.casings_end = c;
}
else
{
hnd.casings_end.nextcasing = c;
c.prevcasing = hnd.casings_end;
hnd.casings_end = c;
}
while ( hnd.casings && (swwm_maxcasings >= 0) && (hnd.casings_cnt > swwm_maxcasings) )
DeQueueCasing(hnd.casings);
}
static void DeQueueCasing( SWWMCasing c )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd || !hnd.casings ) return;
if ( (hnd.casings != c) && !c.prevcasing && !c.nextcasing ) return;
hnd.casings_cnt--;
if ( !c.prevcasing ) hnd.casings = c.nextcasing;
else c.prevcasing.nextcasing = c.nextcasing;
if ( c == hnd.casings_end ) hnd.casings_end = c.prevcasing;
if ( c.nextcasing ) c.nextcasing.prevcasing = c.prevcasing;
c.killme = true;
c.prevcasing = null;
c.nextcasing = null;
}
static void QueueChip( SWWMChip c )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return;
hnd.chips_cnt++;
if ( !hnd.chips )
{
// this is the initial one
hnd.chips = c;
hnd.chips_end = c;
}
else
{
hnd.chips_end.nextchip = c;
c.prevchip = hnd.chips_end;
hnd.chips_end = c;
}
while ( hnd.chips && (swwm_maxdebris >= 0) && (hnd.chips_cnt > swwm_maxdebris) )
DeQueueChip(hnd.chips);
}
static void DeQueueChip( SWWMChip c )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd || !hnd.chips ) return;
if ( (hnd.chips != c) && !c.prevchip && !c.nextchip ) return;
hnd.chips_cnt--;
if ( !c.prevchip ) hnd.chips = c.nextchip;
else c.prevchip.nextchip = c.nextchip;
if ( c == hnd.chips_end ) hnd.chips_end = c.prevchip;
if ( c.nextchip ) c.nextchip.prevchip = c.prevchip;
c.killme = true;
c.prevchip = null;
c.nextchip = null;
}
static void QueueBlod( mkBloodDrop b )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return;
hnd.blods_cnt++;
if ( !hnd.blods )
{
// this is the initial one
hnd.blods = b;
hnd.blods_end = b;
}
else
{
hnd.blods_end.nextblod = b;
b.prevblod = hnd.blods_end;
hnd.blods_end = b;
}
while ( hnd.blods && (swwm_maxblood >= 0) && (hnd.blods_cnt > swwm_maxblood) )
DeQueueBlod(hnd.blods);
}
static void DeQueueBlod( mkBloodDrop b )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd || !hnd.blods ) return;
if ( (hnd.blods != b) && !b.prevblod && !b.nextblod ) return;
hnd.blods_cnt--;
if ( !b.prevblod ) hnd.blods = b.nextblod;
else b.prevblod.nextblod = b.nextblod;
if ( b == hnd.blods_end ) hnd.blods_end = b.prevblod;
if ( b.nextblod ) b.nextblod.prevblod = b.prevblod;
b.killme = true;
b.prevblod = null;
b.nextblod = null;
}
static void QueueMeat( mkFlyingGib m )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return;
hnd.meats_cnt++;
if ( !hnd.meats )
{
// this is the initial one
hnd.meats = m;
hnd.meats_end = m;
}
else
{
hnd.meats_end.nextmeat = m;
m.prevmeat = hnd.meats_end;
hnd.meats_end = m;
}
while ( hnd.meats && (swwm_maxgibs >= 0) && (hnd.meats_cnt > swwm_maxgibs) )
DeQueueMeat(hnd.meats);
}
static void DeQueueMeat( mkFlyingGib m )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd || !hnd.meats ) return;
if ( (hnd.meats != m) && !m.prevmeat && !m.nextmeat ) return;
hnd.meats_cnt--;
if ( !m.prevmeat ) hnd.meats = m.nextmeat;
else m.prevmeat.nextmeat = m.nextmeat;
if ( m == hnd.meats_end ) hnd.meats_end = m.prevmeat;
if ( m.nextmeat ) m.nextmeat.prevmeat = m.prevmeat;
m.killme = true;
m.prevmeat = null;
m.nextmeat = null;
}
static void HealthFlash( int p )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd || (p == -1) ) return;
hnd.hflash[p] = gametic+5;
}
static void ArmorFlash( int p )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd || (p == -1) ) return;
hnd.aflash[p] = gametic+5;
}
static int AddOneliner( String type, int level, int delay = 5 )
{
// only Demolitionist can play voice lines
if ( !(players[consoleplayer].mo is 'Demolitionist') )
return 0;
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return 0;
String voicetype = CVar.GetCVar('swwm_voicetype',players[consoleplayer]).GetString();
// suppress non-rage comments when ragekit is active, only screaming allowed
if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") ) return 0;
int whichline;
String testme = String.Format("SWWM_SUBS_%s_N%s",voicetype.MakeUpper(),type.MakeUpper());
String locme = StringTable.Localize(testme,false);
int countem;
if ( testme == locme ) countem = 0;
else countem = locme.ToInt();
if ( countem == 0 ) return 0; // voicepack doesn't have this
// check last line so we don't repeat
int last = 0, ent;
for ( int i=0; i<hnd.lastlines.Size(); i++ )
{
if ( hnd.lastlines[i].type != type ) continue;
last = hnd.lastlines[i].lineno;
ent = i;
break;
}
if ( countem == 1 ) whichline = 1;
else if ( last > 0 )
{
whichline = Random[DemoLines](1,countem-1);
if ( whichline >= last ) whichline++;
hnd.lastlines[ent].lineno = whichline;
}
else
{
whichline = Random[DemoLines](1,countem);
let lst = new("LastLine");
lst.type = type;
lst.lineno = whichline;
hnd.lastlines.Push(lst);
}
hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),whichline);
hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype,type,whichline);
hnd.onelinertic = gametic+delay;
hnd.onelinerspan = int(S_GetLength(hnd.onelinersnd)*Thinker.TICRATE);
hnd.onelinerlevel = level;
return hnd.onelinertic+hnd.onelinerspan;
}
override void OnRegister()
{
// oneliner RNG must be relative to consoleplayer
SetRandomSeed[DemoLines](Random[DemoLines]()+consoleplayer+MSTime());
}
private static Vector3 UseLinePos( Line l )
{
Vector3 al, ah, bl, bh;
if ( !l.sidedef[1] )
{
// just the whole line
al = (l.v1.p,l.frontsector.floorplane.ZatPoint(l.v1.p));
ah = (l.v1.p,l.frontsector.ceilingplane.ZatPoint(l.v1.p));
bl = (l.v2.p,l.frontsector.floorplane.ZatPoint(l.v2.p));
bh = (l.v2.p,l.frontsector.ceilingplane.ZatPoint(l.v2.p));
return (al+ah+bl+bh)*.25;
}
SecPlane highestfloor, lowestfloor, lowestceiling, highestceiling;
if ( (l.frontsector.floorplane.ZatPoint(l.v1.p) > l.backsector.floorplane.ZatPoint(l.v1.p))
&& (l.frontsector.floorplane.ZatPoint(l.v2.p) > l.backsector.floorplane.ZatPoint(l.v2.p)) )
{
highestfloor = l.frontsector.floorplane;
lowestfloor = l.backsector.floorplane;
}
else
{
highestfloor = l.backsector.floorplane;
lowestfloor = l.frontsector.floorplane;
}
if ( (l.frontsector.ceilingplane.ZatPoint(l.v1.p) < l.backsector.ceilingplane.ZatPoint(l.v1.p))
&& (l.frontsector.ceilingplane.ZatPoint(l.v2.p) < l.backsector.ceilingplane.ZatPoint(l.v2.p)) )
{
lowestceiling = l.frontsector.ceilingplane;
highestceiling = l.backsector.ceilingplane;
}
else
{
lowestceiling = l.backsector.ceilingplane;
highestceiling = l.frontsector.ceilingplane;
}
// try to guess what the part that triggers this is
if ( l.Activation&SPAC_Cross )
{
// pick the "intersection"
al = (l.v1.p,highestfloor.ZatPoint(l.v1.p));
ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p));
bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p));
bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p));
return (al+ah+bl+bh)*.25;
}
// check if lower part available
al = (l.v1.p,lowestfloor.ZatPoint(l.v1.p));
ah = (l.v1.p,highestfloor.ZatPoint(l.v1.p));
bl = (l.v2.p,lowestfloor.ZatPoint(l.v2.p));
bh = (l.v2.p,highestfloor.ZatPoint(l.v2.p));
if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) )
return (al+ah+bl+bh)*.25;
// check if upper part available
al = (l.v1.p,lowestceiling.ZatPoint(l.v1.p));
ah = (l.v1.p,highestceiling.ZatPoint(l.v1.p));
bl = (l.v2.p,lowestceiling.ZatPoint(l.v2.p));
bh = (l.v2.p,highestceiling.ZatPoint(l.v2.p));
if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) )
return (al+ah+bl+bh)*.25;
// check for 3d floors
bool floorfound = false;
Vector3 fal, fah, fbl, fbh;
for ( int i=0; i<l.backsector.Get3DFloorCount(); i++ )
{
let ff = l.backsector.Get3DFloor(i);
fal = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
fah = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
fbl = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
fbh = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
// skip if higher, we'll go with the lowest 3d floor (may not be right, but whatever)
if ( floorfound && (fah.z > ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue;
al = fal;
ah = fah;
bl = fbl;
bh = fbh;
floorfound = true;
}
if ( floorfound ) return (al+ah+bl+bh)*.25;
for ( int i=0; i<l.frontsector.Get3DFloorCount(); i++ )
{
let ff = l.frontsector.Get3DFloor(i);
fal = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
fah = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
fbl = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
fbh = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
// skip if higher, we'll go with the lowest 3d floor (may not be right, but whatever)
if ( floorfound && (fah.z > ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue;
al = fal;
ah = fah;
bl = fbl;
bh = fbh;
floorfound = true;
}
if ( floorfound ) return (al+ah+bl+bh)*.25;
// check for midtex
if ( !l.sidedef[0].GetTexture(1).IsNull() )
{
double ofs = l.sidedef[0].GetTextureYOffset(1);
Vector2 siz = TexMan.GetScaledSize(l.sidedef[0].GetTexture(1));
Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[0].GetTexture(1));
ofs += tofs.y;
ofs *= l.sidedef[0].GetTextureYScale(1);
siz.y *= l.sidedef[0].GetTextureYScale(1);
if ( l.flags&Line.ML_DONTPEGBOTTOM )
{
al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs);
bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs);
ah = al+(0,0,siz.y);
bh = bl+(0,0,siz.y);
}
else
{
ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs);
bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs);
al = ah-(0,0,siz.y);
bl = bh-(0,0,siz.y);
}
return (al+ah+bl+bh)*.25;
}
if ( !l.sidedef[1].GetTexture(1).IsNull() )
{
double ofs = l.sidedef[1].GetTextureYOffset(1);
Vector2 siz = TexMan.GetScaledSize(l.sidedef[1].GetTexture(1));
Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[1].GetTexture(1));
ofs += tofs.y;
ofs *= l.sidedef[1].GetTextureYScale(1);
siz.y *= l.sidedef[1].GetTextureYScale(1);
if ( l.flags&Line.ML_DONTPEGBOTTOM )
{
al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs);
bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs);
ah = al+(0,0,siz.y);
bh = bl+(0,0,siz.y);
}
else
{
ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs);
bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs);
al = ah-(0,0,siz.y);
bl = bh-(0,0,siz.y);
}
return (al+ah+bl+bh)*.25;
}
// just use the intersection
al = (l.v1.p,highestfloor.ZatPoint(l.v1.p));
ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p));
bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p));
bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p));
return (al+ah+bl+bh)*.25;
}
static clearscope void ClearAllShaders( PlayerInfo p )
{
Shader.SetEnabled(p,"WaterWarp",false);
Shader.SetEnabled(p,"LavaWarp",false);
Shader.SetEnabled(p,"SlimeWarp",false);
Shader.SetEnabled(p,"ZoomBlur",false);
Shader.SetEnabled(p,"RagekitShader",false);
Shader.SetEnabled(p,"GhostShader",false);
Shader.SetEnabled(p,"InvinciShader",false);
Shader.SetEnabled(p,"Glitch",false);
Shader.SetEnabled(p,"Grain",false);
}
// level end stats
override void WorldUnloaded( WorldEvent e )
{
if ( !(gameinfo.gametype&GAME_STRIFE) )
{
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
SWWMStats s;
while ( s = SWWMStats(ti.Next()) )
{
int clust = 0;
bool secret = false;
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 <= 32 )
{
secret = true;
if ( level.levelnum <= 31 ) clust = 7;
else clust = 8;
}
}
else
{
if ( (gameinfo.gametype&GAME_DOOM) && ((level.cluster == 9) || (level.cluster == 10)) )
secret = true;
clust = level.cluster;
}
int csiz = s.clustervisit.Size();
if ( csiz == 0 )
{
s.clustervisit.Push(clust);
s.secretdone.Push(secret);
}
else if ( s.clustervisit[csiz-1] != clust )
{
s.clustervisit.Push(clust);
s.secretdone.Push(secret|s.secretdone[csiz-1]);
}
s.AddLevelStats();
}
}
ClearAllShaders(players[consoleplayer]);
// reset score on dead players (death exit™)
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (players[i].Health > 0) ) continue;
let c = SWWMCredits.Find(players[i]);
if ( c ) c.credits = c.hcredits = 0;
}
}
override void WorldLoaded( WorldEvent e )
{
if ( level.levelname ~== "Modder Test Map" )
{
level.ReplaceTextures("-noflat-","kinstile",0);
S_ChangeMusic("music/CARDISH1.XM");
}
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) )
AddOneliner("mapstart",3);
if ( !e.IsSaveGame && !e.IsReopen )
{
// for skipping over merged exit lines (sharing vertices)
Array<Line> skipme;
skipme.Clear();
// find exit lines, and use lines that aren't exits
for ( int i=0; i<level.lines.Size(); i++ )
{
Line l = level.lines[i];
// while we're at it, add teleporter sparks
if ( SWWMUtility.IsTeleportLine(l) )
{
let a = SWWMTeleportLine(Actor.Spawn("SWWMTeleportLine"));
a.tline = l;
}
if ( !SWWMUtility.IsExitLine(l) )
continue;
if ( skipme.Find(l) < skipme.Size() ) continue;
Vector3 lpos = UseLinePos(l);
// look for connected lines
int xcnt = 1;
if ( l.frontsector )
{
for ( int j=0; j<l.frontsector.Lines.Size(); j++ )
{
let l2 = l.frontsector.Lines[j];
if ( (l2 == l) || (l2.special != l.special) ) continue;
// needs to have a point in common with this one or any of the added lines
if ( (l2.v1 != l.v1) && (l2.v2 != l.v2) && (l2.v1 != l.v2) && (l2.v2 != l.v1) )
{
bool nomatches = true;
for ( int k=0; k<skipme.Size(); k++ )
{
if ( (l2.v1 != skipme[k].v1) && (l2.v2 != skipme[k].v2) && (l2.v1 != skipme[k].v2) && (l2.v2 != skipme[k].v1) )
continue;
nomatches = false;
break;
}
if ( nomatches ) continue;
}
skipme.Push(l2);
xcnt++;
lpos += UseLinePos(l2);
}
}
if ( l.backsector )
{
for ( int j=0; j<l.backsector.Lines.Size(); j++ )
{
let l2 = l.backsector.Lines[j];
if ( (l2 == l) || (l2.special != l.special) ) continue;
// needs to have a point in common with this one or any of the added lines
if ( (l2.v1 != l.v1) && (l2.v2 != l.v2) && (l2.v1 != l.v2) && (l2.v2 != l.v1) )
{
bool nomatches = true;
for ( int k=0; k<skipme.Size(); k++ )
{
if ( (l2.v1 != skipme[k].v1) && (l2.v2 != skipme[k].v2) && (l2.v1 != skipme[k].v2) && (l2.v2 != skipme[k].v1) )
continue;
nomatches = false;
break;
}
if ( nomatches ) continue;
}
skipme.Push(l2);
xcnt++;
lpos += UseLinePos(l2);
}
}
lpos /= xcnt;
SWWMInterest.Spawn(lpos,theline:l);
}
// spawn loot
Chancebox.SpawnChanceboxes();
}
else if ( e.IsSaveGame || e.IsReopen )
{
// clear all floating numbers
for ( SWWMScoreObj sc=scorenums; sc; sc=sc.Next )
sc.lifespan = 0;
for ( SWWMScoreObj sc=damnums; sc; sc=sc.Next )
sc.lifespan = 0;
// restore underwater sounds for players
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !(players[i].mo is 'Demolitionist') ) continue;
Demolitionist(players[i].mo).CheckUnderwaterAmb(true);
}
}
ClearAllShaders(players[consoleplayer]);
// recheck queues in case limits changed
while ( casings && (casings_cnt > swwm_maxcasings) )
DeQueueCasing(casings);
while ( chips && (chips_cnt > swwm_maxdebris) )
DeQueueChip(chips);
while ( blods && (blods_cnt > swwm_maxblood) )
DeQueueBlod(blods);
while ( meats && (meats_cnt > swwm_maxgibs) )
DeQueueMeat(meats);
}
override void PlayerDied( PlayerEvent e )
{
let s = SWWMStats.Find(players[e.playernumber]);
if ( s ) s.deaths++;
}
override void PlayerEntered( PlayerEvent e )
{
PlayerInfo p = players[e.playernumber];
// override KEYCONF-forced player classes when run with other gameplay mods (wish this was easier)
if ( !(p.mo is 'Demolitionist') )
{
// make sure it's defined here, so special purpose classes (player chunks, scripted overrides) are respected
for ( int i=0; i<PlayerClasses.Size(); i++ )
{
if ( !(p.mo is PlayerClasses[i].Type) ) continue;
// perform a hotswap, code adapted from my .flow player morph in spooktober
let n = PlayerPawn(Actor.Spawn("Demolitionist",p.mo.pos));
n.player = p;
n.angle = p.mo.angle;
n.pitch = p.mo.pitch;
p.camera = n;
p.mo.Destroy();
p.mo = n;
n.GiveDefaultInventory();
let e = Weapon(n.FindInventory("ExplodiumGun"));
if ( e )
{
p.ReadyWeapon = null;
p.PendingWeapon = e;
n.BringUpWeapon();
}
// warn if strict KEYCONF is enabled
if ( setslotstrict && (p == players[consoleplayer]) )
slotstrictwarn = gametic+300;
break;
}
}
// create some static thinkers for this player if needed
SWWMTradeHistory th = SWWMTradeHistory.Find(p);
if ( !th )
{
th = new("SWWMTradeHistory");
th.ChangeStatNum(Thinker.STAT_STATIC);
th.myplayer = p;
}
SWWMCredits c = SWWMCredits.Find(p);
if ( !c )
{
c = new("SWWMCredits");
c.ChangeStatNum(Thinker.STAT_STATIC);
c.myplayer = p;
}
SWWMLoreLibrary l = SWWMLoreLibrary.Find(p);
if ( !l )
{
l = new("SWWMLoreLibrary");
l.ChangeStatNum(Thinker.STAT_STATIC);
l.myplayer = p;
}
// pre-add some entries to start with
l.DirectAdd("Demolitionist");
l.DirectAdd("KnowledgeBase");
l.DirectAdd("Saya");
l.DirectAdd("UAC");
if ( SWWMUtility.IsEviternity() )
{
l.DirectAdd("Gods");
l.DirectAdd("SUSAN");
}
if ( gameinfo.gametype&(GAME_Raven|GAME_Strife) )
{
l.DirectAdd("Parthoris");
l.DirectAdd("SerpentRiders");
l.DirectAdd("Sidhe");
}
if ( gameinfo.gametype&(GAME_Hexen|GAME_Strife) )
l.DirectAdd("Cronos");
if ( gameinfo.gametype&GAME_Strife )
{
l.DirectAdd("TheOrder");
l.DirectAdd("TheFront");
}
// starting weapons (if owned)
if ( p.mo.FindInventory('DeepImpact') )
l.DirectAdd("DeepImpact");
if ( p.mo.FindInventory('ExplodiumGun') )
l.DirectAdd("ExplodiumGun");
SWWMStats s = SWWMStats.Find(p);
if ( !s )
{
s = new("SWWMStats");
s.ChangeStatNum(Thinker.STAT_STATIC);
s.myplayer = p;
s.lastspawn = level.totaltime;
s.favweapon = -1;
for ( Inventory i=p.mo.Inv; i; i=i.inv )
{
if ( i is 'Weapon' )
s.GotWeapon(Weapon(i).GetClass());
}
}
// reset some vars
multilevel[e.playernumber] = 0;
spreecount[e.playernumber] = 0;
tookdamage[e.playernumber] = false;
lastkill[e.playernumber] = int.min;
// reset combat tracker
if ( !swwm_notrack )
SWWMCombatTracker.Spawn(players[e.playernumber].mo);
// reset score (optional) if inventory should be cleared
if ( swwm_resetscore && level.removeitems && !e.IsReturn )
c.credits = c.hcredits = 0;
// re-add any missing collectibles after a death exit (yes, this happens)
for ( int i=0; i<s.ownedcollectibles.Size(); i++ )
{
if ( p.mo.FindInventory(s.ownedcollectibles[i]) ) continue;
let c = SWWMCollectible(Actor.Spawn(s.ownedcollectibles[i],p.mo.pos));
c.propagated = true;
if ( !c.CallTryPickup(p.mo) )
c.Destroy();
}
}
override void PlayerRespawned( PlayerEvent e )
{
// reset some vars
multilevel[e.playernumber] = 0;
spreecount[e.playernumber] = 0;
tookdamage[e.playernumber] = false;
lastkill[e.playernumber] = int.min;
// reset combat tracker
if ( !swwm_notrack )
SWWMCombatTracker.Spawn(players[e.playernumber].mo);
}
override void WorldThingRevived( WorldEvent e )
{
// reattach combat tracker
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
SWWMCombatTracker.Spawn(e.Thing);
// 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') ) 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;
}
private void LangRefresh()
{
if ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]);
if ( !funtags ) funtags = CVar.GetCVar('swwm_funtags',players[consoleplayer]);
if ( (lang.GetString() != curlang) || (funtags.GetBool() != curfuntags) )
{
// manually refresh some tags if language has changed
for ( SWWMCombatTracker t=trackers; t; t=t.next )
t.UpdateTag();
for ( SWWMInterest p=intpoints; p; p=p.next )
{
if ( (p.type != INT_Key) || !p.trackedkey ) continue;
p.keytag = p.trackedkey.GetTag();
}
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !Demolitionist(players[i].mo) ) continue;
for ( SWWMItemSense s=Demolitionist(players[i].mo).itemsense; s; s=s.next )
s.UpdateTag();
}
}
curlang = lang.GetString();
curfuntags = funtags.GetBool();
}
private void QueueMaintenance()
{
if ( swwm_maxcasings != oldmaxcasings )
{
while ( casings && (swwm_maxcasings >= 0) && (casings_cnt > swwm_maxcasings) )
DeQueueCasing(casings);
}
if ( swwm_maxdebris != oldmaxdebris )
{
while ( chips && (swwm_maxdebris >= 0) && (chips_cnt > swwm_maxdebris) )
DeQueueChip(chips);
}
if ( swwm_maxblood != oldmaxblood )
{
while ( blods && (swwm_maxblood >= 0) && (blods_cnt > swwm_maxblood) )
DeQueueBlod(blods);
}
if ( swwm_maxgibs != oldmaxgibs )
{
while ( meats && (swwm_maxgibs >= 0) && (meats_cnt > swwm_maxgibs) )
DeQueueMeat(meats);
}
oldmaxcasings = swwm_maxcasings;
oldmaxdebris = swwm_maxdebris;
oldmaxblood = swwm_maxblood;
oldmaxgibs = swwm_maxgibs;
}
// countable item scoring
private void ItemCountTrack()
{
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] ) continue;
if ( players[i].itemcount > lastitemcount[i] )
{
int score = 10*(players[i].itemcount-lastitemcount[i]);
if ( (level.total_items == level.found_items) && !allitems )
{
allitems = true;
Console.Printf(StringTable.Localize("$SWWM_LASTITEM"),players[i].GetUserName(),500);
score += 490;
}
SWWMCredits.Give(players[i],score);
SWWMScoreObj.Spawn(score,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
lastitemcount[i] = players[i].itemcount;
let s = SWWMStats.Find(players[i]);
s.items++;
}
}
}
// combat tracking
private void CombatTrack()
{
// prune old entries
for ( int i=0; i<combatactors.Size(); i++ )
{
if ( combattics[i] > highesttic )
highesttic = combattics[i];
if ( combatactors[i]
&& (combatactors[i].Health > 0)
&& !combatactors[i].bKILLED
&& !combatactors[i].bCORPSE
&& (combatactors[i].target == players[consoleplayer].mo)
&& (combattics[i]+2000 > gametic) )
continue;
combatactors.Delete(i);
combattics.Delete(i);
i--;
}
bool enteredcombat = false;
// add new entries
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( !a.player && !a.bISMONSTER ) continue;
// ignore the dead
if ( (a.Health <= 0) || a.bKILLED || a.bCORPSE ) continue;
// ignore friends
if ( a.IsFriend(players[consoleplayer].mo) ) continue;
// [Strife] ignore if not in combat
if ( (gameinfo.gametype&GAME_Strife) && !a.bINCOMBAT && !a.bJUSTATTACKED ) continue;
// [Strife] ignore certain classes
if ( (a is 'RatBuddy') || (a is 'Peasant') || (a is 'Beggar') ) continue;
// [Strife] ignore Oracle's spectre while it's inactive
if ( (a is 'AlienSpectre3') && a.InStateSequence(a.CurState,a.FindState("Spawn")) ) continue;
// ignore if not targetted or player can't see it
if ( (a.target != players[consoleplayer].mo)
|| !SWWMUtility.InPlayerFOV(players[consoleplayer],a) ) continue;
// [HDoom] ignore cute girls
if ( SWWMHDoomHandler.IsCuteGirl(a.target) ) continue;
// is it already in?
bool addme = true;
for ( int i=0; i<combatactors.Size(); i++ )
{
if ( combatactors[i] != a ) continue;
addme = false;
combattics[i] = gametic;
break;
}
// add it in
if ( addme )
{
combatactors.Push(a);
combattics.Push(gametic);
enteredcombat = true;
}
}
if ( enteredcombat && (!highesttic || (gametic > highesttic+700)) )
lastcombat = AddOneliner("fightstart",1,10);
}
private void OneHundredPercentCheck()
{
// ignore levels that have NOTHING
if ( (level.total_secrets <= 0)
&& (level.total_items <= 0)
&& (level.total_monsters <= 0) ) return;
if ( mapclear ) return;
if ( (level.found_secrets < level.total_secrets)
|| (level.found_items < level.total_items)
|| (level.killed_monsters < level.total_monsters) ) return;
mapclear = true;
Console.Printf(StringTable.Localize("$SWWM_ALLCLEAR"),5000);
S_StartSound("misc/wow",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
if ( swwm_silencemap == 1 ) S_ChangeMusic("",force:true);
else if ( swwm_silencemap > 1 ) S_ChangeMusic("music/olg.ogg",force:true);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
let f = Actor.Spawn("PartyTime",players[i].mo.pos);
f.bAMBUSH = true;
SWWMCredits.Give(players[i],5000);
SWWMScoreObj.Spawn(5000,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
}
}
override void WorldTick()
{
LangRefresh();
QueueMaintenance();
if ( !(gameinfo.gametype&GAME_STRIFE) && !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"));
}
}
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
if ( onelinertic && (onelinertic < gametic) )
{
if ( players[consoleplayer].health > 0 )
{
if ( onelinerlevel > mutevoice.GetInt() )
players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE);
SendNetworkEvent("swwmremoteliner."..onelinersnd,consoleplayer,onelinerlevel);
}
onelinertic = 0;
onelinerspan = 0;
}
for ( int i=0; i<flashes.size(); i++ )
{
if ( flashes[i].tic >= gametic ) continue;
flashes.Delete(i);
i--;
}
ItemCountTrack();
CombatTrack();
OneHundredPercentCheck();
if ( initialized ) return;
// wait until bosses are active
for ( int i=0; i<bossactors.Size(); i++ )
{
if ( !bossactors[i] ) continue;
if ( (!bossactors[i].target || !bossactors[i].CheckSight(bossactors[i].target,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY))
&& (!bossviewactor || (bossviewactor && !bossviewactor.target)) ) continue;
initialized = true;
break;
}
}
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;
}
private static bool ShouldSpawnGold()
{
int totalneeded = 0;
// check "free space" in player inventories
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
let cg = players[i].mo.FindInventory("GoldShell");
if ( cg ) totalneeded += cg.MaxAmount-cg.Amount;
else totalneeded = GetDefaultByType("GoldShell").MaxAmount;
}
// subtract any shells already in the world
let ti = ThinkerIterator.Create("GoldShell");
GoldShell g;
while ( g = GoldShell(ti.Next()) )
{
if ( g.Owner ) continue;
totalneeded -= g.Amount;
}
return (totalneeded > 0);
}
override void WorldThingDied( WorldEvent e )
{
if ( e.Thing.default.bISMONSTER && ((e.Thing.default.bBOSS) || (e.Thing.GetSpawnHealth() >= 1000)) && (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/4);
// make sure the gold shell is "worth spawning", too
if ( !Random[GoldDrop](0,dropweight) && ShouldSpawnGold() )
{
let g = Actor.Spawn("GoldShell",e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
double ang = FRandom[SpareShells](0,360);
g.vel.xy = (cos(ang),sin(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("UglyBoyGetsFuckedUp");
t.ChangeStatNum(Thinker.STAT_USER);
}
if ( swwm_partytime )
{
let pt = Actor.Spawn("PartyTime",e.Thing.pos);
pt.target = e.Thing;
}
// force insert gib animations on some vanilla Doom monsters
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
if ( !gotgibbed ) return;
if ( (e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") )
ExtraGibDeaths.GibThis(e.Thing,"DemonXDeath");
else if ( e.Thing.GetClass() == "HellKnight" )
ExtraGibDeaths.GibThis(e.Thing,"KnightXDeath");
else if ( e.Thing.GetClass() == "BaronOfHell" )
ExtraGibDeaths.GibThis(e.Thing,"BaronXDeath");
else if ( e.Thing.GetClass() == "Cacodemon" )
ExtraGibDeaths.GibThis(e.Thing,"CacoXDeath");
else if ( e.Thing.GetClass() == "Revenant" )
ExtraGibDeaths.GibThis(e.Thing,"BonerXDeath");
else if ( e.Thing.GetClass() == "Archvile" )
ExtraGibDeaths.GibThis(e.Thing,"VileXDeath");
}
// gibbing
private void DoGibThing( WorldEvent e )
{
// no gib if it was erased
if ( e.DamageType == 'Ynykron' ) return;
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
bool forcegibbed = false;
// force gib availability for some vanilla Doom monsters
if ( gotgibbed && ((e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "Cacodemon") || (e.Thing.GetClass() == "Revenant") || (e.Thing.GetClass() == "Archvile")) )
forcegibbed = true;
if ( !e.Thing.FindState("XDeath",true) && !e.Thing.FindState("Death.Extreme",true) && !forcegibbed )
gotgibbed = false;
// only do special handling if they use our blood
if ( (e.Thing.GetBloodType(0) != "mkBlood") || e.Thing.bNOBLOOD )
return;
CorpseFallTracker.TrackBody(e.Thing);
bool b;
Actor a;
// special handling of some monsters
if ( e.Thing.GetClass() == "Cyberdemon" )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
mkGibber(a).delay = 40;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
return;
}
else if ( e.Thing.GetClass() == "SpiderMastermind" )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
mkGibber(a).delay = 60;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
}
else if ( gotgibbed )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
}
}
// damage numbers, combat tracking, etc.
private void DoDamageHandling( WorldEvent e )
{
if ( !accdamage ) accdamage = CVar.GetCVar('swwm_accdamage',players[consoleplayer]);
bool spawnme = true;
if ( accdamage.GetBool() )
{
// find existing damage number
for ( SWWMScoreObj d=damnums; d; d=d.next )
{
if ( (d.starttic < level.maptime) || (d.acc != e.Thing) ) continue;
if ( d.score-e.Damage > d.score ) d.score = int.min;
else d.score -= e.Damage;
spawnme = false;
break;
}
}
if ( spawnme ) SWWMScoreObj.Spawn(-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),Font.CR_RED,e.Thing);
// update combat tracker for it
if ( !(e.Thing is 'BossBrain') )
{
for ( SWWMCombatTracker t=trackers; t; t=t.next )
{
if ( t.mytarget != e.Thing ) continue;
t.updated = level.maptime+35;
break;
}
}
// fall dmg
SWWMWhoPushedMe.SetInstigator(e.Thing,e.DamageSource);
// stats
if ( e.Thing.player )
{
tookdamage[e.Thing.PlayerNumber()] = true;
let s = SWWMStats.Find(e.Thing.player);
s.AddDamageTaken(e.Damage);
if ( e.Damage > s.toptaken ) s.toptaken = e.Damage;
}
if ( e.DamageSource && e.DamageSource.player )
{
let s = SWWMStats.Find(e.DamageSource.player);
s.AddDamageDealt(e.Damage);
if ( e.Damage > s.topdealt ) s.topdealt = e.Damage;
}
}
// combat hit chatter
private void DoCombatHit( WorldEvent e )
{
if ( (e.DamageSource.bISMONSTER || e.DamageSource.player || (e.DamageSource is 'ScriptedMarine')) && (e.Thing == players[consoleplayer].mo) && (e.Thing.Health > 0) )
{
if ( !lastcombat || (gametic > lastcombat+40) )
{
if ( (e.Thing.IsFriend(e.DamageSource) || SWWMUtility.IsCivilian(e.DamageSource)) )
lastcombat = AddOneliner("friendhit",1,10);
else if ( (!lastcombat || (gametic > lastcombat+100)) && !Random[DemoLines](0,e.DamageSource.bBOSS?2:4) && !SWWMHDoomHandler.IsCuteGirl(e.DamageSource) ) // [HDoom] don't shout at the girls
lastcombat = AddOneliner("gethit",1,15);
}
highesttic = gametic;
}
// friendly fire lines only fire up if we didn't kill them right away (because then the teamkill line should take priority)
if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player || (e.Thing is 'ScriptedMarine')) && (e.Thing.Health > 0) )
{
// make sure it's not a moth, because otherwise they won't shut up about accidentally hurting them (it happens a lot)
if ( (e.Thing.IsFriend(e.DamageSource) || SWWMUtility.IsCivilian(e.Thing)) && !(e.Thing is 'LampMoth') )
{
if ( !lastcombat || (gametic > lastcombat+40) )
lastcombat = AddOneliner("hitfriend",1,10);
highesttic = gametic;
}
}
}
// kill scoring
private void DoKillScoring( WorldEvent e )
{
// fall damage tracking hack
let src = e.DamageSource;
if ( (e.DamageType == 'Falling') && !e.DamageSource )
src = SWWMWhoPushedMe.RecallInstigator(e.Thing);
if ( (!src || !src.player || (src == e.Thing)) ) return;
let s = SWWMStats.Find(src.player);
if ( s )
{
s.kills++;
s.AddWeaponKill(e.Inflictor,e.Thing,e.DamageType);
}
if ( src == players[consoleplayer].mo )
{
highesttic = gametic;
if ( !lastcombat || (gametic > lastcombat+40) )
{
if ( e.Thing.IsFriend(src) || SWWMUtility.IsCivilian(e.Thing) )
lastcombat = AddOneliner("friendkill",1,5);
else if ( (!lastcombat || (gametic > lastcombat+100)) && !Random[DemoLines](0,e.Thing.bBOSS?2:5) && !SWWMHDoomHandler.IsCuteGirl(e.Thing) ) // [HDoom] don't shout at the girls
lastcombat = AddOneliner("scorekill",1,15);
}
}
// no credits unless it's a counted kill or marine (that isn't friendly)
if ( e.Thing.IsFriend(src) || (!e.Thing.default.bCountKill && !(e.Thing is 'ScriptedMarine')) )
return;
int pnum = src.PlayerNumber();
if ( level.maptime < (lastkill[pnum]+5*Thinker.TICRATE) )
multilevel[pnum]++;
else multilevel[pnum] = 0;
if ( s && (multilevel[pnum]+1 > s.mkill) )
s.mkill = multilevel[pnum]+1;
lastkill[pnum] = level.maptime;
// scoring
int score = min(1000,int(ceil(e.Thing.GetSpawnHealth()*.05)*10));
SWWMScoreObj scr = null;
if ( src.player == players[consoleplayer] )
scr = SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
int ofs = 0;
if ( e.DamageType == 'Push' )
{
score += 500;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_SHAMEFUL");
scr.xcnt = ++ofs;
}
}
else if ( e.DamageType == 'Buttslam' )
{
score += 200;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_BUTTSLAM");
scr.xcnt = ++ofs;
}
}
else if ( e.DamageType == 'Love' )
{
score += 600;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.FindFontColor('BlushPink');
scr.xstr[ofs] = StringTable.Localize((e.Thing is 'WolfensteinSS')?"$SWWM_LOVED_ALT":"$SWWM_LOVED");
scr.xcnt = ++ofs;
}
}
if ( (e.Damage >= e.Thing.GetSpawnHealth()*2) || (((e.Thing.Health <= e.Thing.GetGibHealth()) || (src.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageType == 'Extreme')) && !src.bNOEXTREMEDEATH && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH)) )
{
score *= 2;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_OVERKILL");
scr.xcnt = ++ofs;
}
}
score = int(score*(1.+.5*min(multilevel[pnum],16)));
if ( (multilevel[pnum] > 0) && scr )
{
if ( scr )
{
scr.xscore[ofs] = (multilevel[pnum]>=16)?int.max:(multilevel[pnum]+1);
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_MULTIKILL");
scr.xcnt = ++ofs;
}
}
spreecount[pnum]++;
if ( s && (spreecount[pnum] > s.skill) && !tookdamage[pnum] )
s.skill = spreecount[pnum];
if ( !tookdamage[pnum] )
{
int spreebonus = 10*(spreecount[pnum]);
// taper off after 10x (some people go really far with these, holy fuck)
if ( spreecount[pnum] > 10 ) spreebonus = int(spreebonus*((spreecount[pnum]/10.)**.25));
score += 100+spreebonus;
if ( (spreecount[pnum] > 0) && scr )
{
scr.xscore[ofs] = spreecount[pnum];
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_SPREEKILL");
scr.xcnt = ++ofs;
}
}
if ( e.Thing.bBOSS )
{
score += 2000;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_BOSSKILL");
scr.xcnt = ++ofs;
}
}
SWWMCredits.Give(src.player,score);
if ( scr ) scr.score = score; // update final score
if ( (level.killed_monsters+1 == level.total_monsters) && !allkills )
{
allkills = true;
SWWMCredits.Give(src.player,1000);
Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),src.player.GetUserName(),1000);
SWWMScoreObj.Spawn(1000,src.Vec3Offset(0,0,src.Height/2));
}
}
override void WorldThingDamaged( WorldEvent e )
{
if ( e.Damage > 0 ) DoDamageHandling(e);
if ( e.DamageSource && (e.DamageSource != e.Thing) ) DoCombatHit(e);
if ( (e.Thing.Health > 0) || e.Thing.bKilled || e.Thing.bCorpse ) return;
DoGibThing(e);
// romero hax
if ( (e.Thing is 'BossBrain') && (e.DamageType == 'Telefrag') )
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
// voodoo doll telefragging barrel hax (eviternity death exits)
if ( (e.Thing is 'ExplosiveBarrel') && (e.DamageType == 'Telefrag') && e.DamageSource.player && (e.DamageSource.player.mo != e.DamageSource) )
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill && !(e.Thing is 'ScriptedMarine') ) return;
DoKillScoring(e);
}
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 is 'KeyYellow' ) a.SetTag("$T_YELLOWKEY");
else if ( a is 'KeyGreen' ) a.SetTag("$T_GREENKEY");
else if ( a is 'KeyBlue' ) a.SetTag("$T_BLUEKEY");
else if ( a.GetClassName() == 'KeyRed' ) a.SetTag("$T_REDKEY");
else if ( a is 'KeySteel' ) a.SetTag("$T_KEYSTEEL");
else if ( a is 'KeyCave' ) a.SetTag("$T_KEYCAVE");
else if ( a is 'KeyAxe' ) a.SetTag("$T_KEYAXE");
else if ( a is 'KeyFire' ) a.SetTag("$T_KEYFIRE");
else if ( a is 'KeyEmerald' ) a.SetTag("$T_KEYEMERALD");
else if ( a is 'KeyDungeon' ) a.SetTag("$T_KEYDUNGEON");
else if ( a is 'KeySilver' ) a.SetTag("$T_KEYSILVER");
else if ( a is 'KeyRusted' ) a.SetTag("$T_KEYRUSTED");
else if ( a is 'KeyHorn' ) a.SetTag("$T_KEYHORN");
else if ( a is 'KeySwamp' ) a.SetTag("$T_KEYSWAMP");
else if ( a is 'KeyCastle' ) a.SetTag("$T_KEYCASTLE");
}
// 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 )
{
let bt = BlockThingsIterator.Create(a,16);
while ( bt.Next() )
{
let t = bt.Thing;
if ( !t || (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
a.floatbobphase = t.floatbobphase;
a.angle = t.angle; // also copy angle
break;
}
}
override void WorldThingDestroyed( WorldEvent e )
{
if ( !e.Thing.default.bSHOOTABLE || !e.Thing.default.bMISSILE )
return;
// remove from suckables
int pos = suckableactors.Find(e.Thing);
if ( pos >= suckableactors.Size() ) return;
suckableactors.Delete(pos);
}
override void WorldThingSpawned( WorldEvent e )
{
// I WANT DIE
if ( G_SkillName() == StringTable.Localize("$SWWM_SKLUNATIC") )
{
if ( e.Thing.bMISSILE && !e.Thing.FindInventory("DontDuplicate") && !e.Thing.IsZeroDamage() && (e.Thing.target && e.Thing.target.bISMONSTER && !e.Thing.target.player) )
{
e.Thing.speed *= 2;
e.Thing.vel *= 2;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(e.Thing.pitch,e.Thing.angle,e.Thing.roll);
int numpt = Random[ExtraMissiles](1,2);
for ( int i=0; i<numpt; i++ )
{
double a = FRandom[ExtraMissiles](0,360);
double s = FRandom[ExtraMissiles](0,.1);
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
let p = Actor.Spawn(e.Thing.GetClass(),e.Thing.pos);
p.GiveInventory("DontDuplicate",1);
p.target = e.Thing.target;
p.tracer = e.Thing.tracer;
p.master = e.Thing.master;
p.speed *= FRandom[ExtraMissiles](1.,3.);
p.vel = dir*p.speed;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.roll = e.Thing.roll;
}
}
if ( e.Thing.bISMONSTER && !(e.Thing is 'PlayerPawn') )
{
e.Thing.GiveInventory("HOLYCOWIMTOTALLYGOINGSOFASTOHFUCK",1);
// random chance to spawn doubles
if ( !e.Thing.FindInventory("DontDuplicate") && !Random[ExtraMissiles](0,2) )
{
int numpt = Random[ExtraMissiles](1,2);
for ( int i=0; i<numpt; i++ )
{
let x = Actor.Spawn(e.Thing.GetClass(),e.Thing.Vec3Angle(e.Thing.Radius*FRandom[ExtraMissiles](1.5,4.),FRandom[ExtraMissiles](0,360)));
if ( !x.TestMobjLocation() || !level.IsPointInLevel(x.pos) )
{
x.ClearCounters();
x.Destroy();
}
else
{
x.angle = e.Thing.angle;
x.GiveInventory("DontDuplicate",1);
}
}
}
}
}
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 ( swwm_doomfall && e.Thing.bISMONSTER )
e.Thing.bFALLDAMAGE = true;
if ( e.Thing is 'Key' )
{
DoKeyTagFix(e.Thing);
SWWMInterest.Spawn(thekey:Key(e.Thing));
}
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");
// OH BOY, THESE AREN'T CHANGEABLE
//e.Thing.Obituary = "$OB_ANNIHIL";
}
else if ( e.Thing.GetClassName() == "FormerCaptain" )
{
e.Thing.SetTag("$FN_FCAPTAIN");
//e.Thing.Obituary = "$OB_FCAPTAIN";
}
else if ( e.Thing.GetClassName() == "NightmareDemon" )
{
e.Thing.SetTag("$FN_NDEMON");
//e.Thing.Obituary = "$OB_NDEMON";
}
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;
}
SWWMCombatTracker trk;
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
trk = SWWMCombatTracker.Spawn(e.Thing);
if ( !(e.Thing is 'LampMoth') && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER || e.Thing.bCORPSE || (e.Thing is 'Inventory') || (e.Thing is 'CompanionLamp')) )
{
if ( (swwm_shadows == 2) || ((swwm_shadows == 1) && ((e.Thing is 'Demolitionist') || (e.Thing.SpawnState.sprite == e.Thing.GetSpriteIndex('XZW1')))) )
SWWMShadow.Track(e.Thing);
}
// Ynykron vortex optimization (faster than a thinker iterator)
if ( e.Thing.bSHOOTABLE || e.Thing.bMISSILE )
SuckableActors.Push(e.Thing);
// vanilla blood color changes
if ( (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "Bishop") || (e.Thing.GetClass() == "Korax") )
{
let gb = Actor.Spawn("GreenBloodReference");
e.Thing.CopyBloodColor(gb);
gb.Destroy();
}
else if ( e.Thing.GetClass() == "Cacodemon" )
{
let bb = Actor.Spawn("BlueBloodReference");
e.Thing.CopyBloodColor(bb);
bb.Destroy();
}
else if ( (e.Thing.GetClass() == "Wizard") || (e.Thing.GetClass() == "Heresiarch") || (e.Thing.GetClass() == "Sorcerer2") )
{
let pb = Actor.Spawn("PurpleBloodReference");
e.Thing.CopyBloodColor(pb);
pb.Destroy();
}
else if ( e.Thing.GetClass() == "LostSoul" )
e.Thing.bNOBLOOD = true;
// vanilla boss stuff
bool upgrademe = swwm_upgradebosses;
int bossmap = WhichVanillaBossMap();
if ( bossmap == MAP_DE1M8 )
{
if ( e.Thing is 'BaronOfHell' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_BRUISERS";
}
else if ( bossmap == MAP_DE2M8 )
{
if ( e.Thing is 'Cyberdemon' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_CYBIE";
}
else if ( bossmap == MAP_DE3M8 )
{
if ( e.Thing is 'Spidermastermind' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 6;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_SPIDER";
}
else if ( bossmap == MAP_DE4M8 )
{
if ( e.Thing is 'Spidermastermind' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_SPIDER2";
}
else if ( bossmap == MAP_DMAP07 )
{
if ( (e.Thing is 'Fatso') || (e.Thing is 'Arachnotron') )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_DIMPLE";
}
else if ( bossmap == MAP_DMAP30 )
{
if ( e.Thing is 'BossBrain' )
{
bossbrainactor = e.Thing;
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 40; // goodbye, instakills
if ( trk ) trk.bBOSS = true;
}
if ( e.Thing is 'BossEye' )
bossviewactor = e.Thing;
bosstag = "$BT_IOS";
}
else if ( bossmap == MAP_HE1M8_HE4M8 )
{
if ( e.Thing is 'IronLich' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_LICHES";
}
else if ( bossmap == MAP_HE2M8_HE5M8 )
{
if ( e.Thing is 'Minotaur' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3;
if ( trk ) trk.bBOSS = true;
}
bosstag = "$BT_MINOTAUR";
}
else if ( bossmap == MAP_HE3M8 )
{
if ( e.Thing is 'Sorcerer1' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DSPARIL";
}
else if ( e.Thing is 'Sorcerer2' )
{
// second phase
bossactors.Clear();
initialized = false;
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DSPARIL2";
}
}
else if ( bossmap == MAP_HMAP38 )
{
if ( e.Thing is 'ClericBoss' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_CLERIC";
}
}
else if ( bossmap == MAP_HMAP36 )
{
if ( e.Thing is 'FighterBoss' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_FIGHTER";
}
}
else if ( bossmap == MAP_HMAP37 )
{
if ( e.Thing is 'MageBoss' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_MAGE";
}
}
else if ( bossmap == MAP_HMAP12 )
{
if ( e.Thing is 'Dragon' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DRAGON";
}
}
else if ( bossmap == MAP_HMAP23_HMAP27 )
{
if ( e.Thing is 'Heresiarch' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_HERESIARCH";
}
}
else if ( bossmap == MAP_HMAP40 )
{
if ( e.Thing is 'Korax' )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 10;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_KORAX";
}
}
else if ( bossmap == MAP_EVMAP30 )
{
if ( e.Thing.GetClassName() == "ArchangelusA" )
{
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_ARCHANGELUS";
}
else if ( e.Thing.GetClassName() == "ArchangelusB" )
{
// second phase
bossactors.Clear();
initialized = false;
bossactors.Push(e.Thing);
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_ARCHANGELUS";
}
}
// inflation check
if ( trk )
{
trk.maxhealth = trk.lasthealth = e.Thing.Health;
trk.intp.Reset(trk.lasthealth);
}
}
override void PostUiTick()
{
if ( (gametic == onelinertic) && (oneliner != "") && (players[consoleplayer].health > 0) )
{
int mute;
if ( mutevoice ) mute = mutevoice.GetInt();
else mute = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt(); // we can't assign the variable here since it's play scope
if ( onelinerlevel > mute )
{
let l = SWWMOneLiner.Make(oneliner,onelinerspan);
StatusBar.AttachMessage(l,-3473);
}
SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel);
}
for ( int i=0; i<flashes.size(); i++ )
{
if ( flashes[i].tic < gametic ) continue;
GenericFlash gf = new("GenericFlash").Setup(flashes[i].cam,flashes[i].c,flashes[i].duration);
StatusBar.AttachMessage(gf,0,BaseStatusBar.HUDMSGLayer_UnderHUD);
}
if ( (!ui_initialized && initialized) || (ui_initialized && !initialized) )
{
ui_initialized = true;
thealth = 0;
for ( int i=0; i<bossactors.Size(); i++ )
{
if ( !bossactors[i] ) continue;
thealth += max(0,bossactors[i].Health);
}
hmax = thealth;
for ( int i=0; i<30; i++ ) oldhealth[i] = thealth;
cummdamage = 0;
if ( !ihealth ) ihealth = DynamicValueInterpolator.Create(thealth,.1,1,1000);
else ihealth.Reset(thealth);
if ( !ihealthr ) ihealthr = DynamicValueInterpolator.Create(thealth,.5,1,1000);
else ihealthr.Reset(thealth);
return;
}
if ( !ui_initialized ) return;
// update healthbar
int newhealth = 0;
for ( int i=0; i<bossactors.Size(); i++ )
{
if ( !bossactors[i] ) continue;
newhealth += max(0,bossactors[i].Health);
}
oldhealth[0] = newhealth;
int curcumm = max(0,thealth-newhealth);
if ( curcumm > 0 )
{
cummdamage += curcumm;
lastcummtic = gametic;
}
else if ( gametic > lastcummtic+150 ) cummdamage = 0;
thealth = newhealth;
ihealthr.Update(thealth);
if ( thealth > oldhealth[29] )
for ( int i=29; i>0; i-- )
oldhealth[i] = thealth;
ihealth.Update(oldhealth[29]);
for ( int i=29; i>0; i-- )
oldhealth[i] = oldhealth[i-1];
if ( thealth > 0 ) bossalpha = min(3.,bossalpha+1./30.);
else bossalpha = max(0,bossalpha-1./50.);
}
override bool InputProcess( InputEvent e )
{
if ( (e.Type == InputEvent.TYPE_KeyDown) && (e.KeyChar >= 0x61) && (e.KeyChar <= 0x7A) )
{
// cheat code handling
String cht[] =
{
"swwmlodsofemone", "swwmdeeplore",
// SWWM Platinum cheats
"swwmimstuck", "swwmarmojumbo", "swwmdangimhealthy",
"swwmwarriorofzaemonath", "swwmpowerparp", "swwmcannotseemyhands",
"swwmreflectonme", "swwmgunzmeneeds", "swwmbloodrainsfromheaven",
"swwmnotwannaboom", "swwmverywrappyoatmeal", "swwmflaggerybingo",
"swwmheadsball", "swwmsmarties", "swwmnocilla",
"swwmmarioisaweenie", "swwmpunish", "swwmboingball",
"swwmgassy", "swwmiamsuperman", "swwmtouchstone"
};
String cmd[] =
{
"swwmmoneycheat", "swwmlorecheat",
// SWWM Platinum cheats
"swwmsafecheat", "swwmweaponcheat", "swwmhealcheat",
"swwmynykroncheat", "swwmgravcheat", "swwminvischeat",
"swwmbarriercheat", "swwmammocheat", "swwmbloodcheat",
"swwmexplocheat", "swwmallcheat", "swwmflagcheat",
"swwmballcheat", "swwmsmartcheat", "swwmnutcheat",
"swwmweeniecheat", "swwmpunishcheat", "swwmball2cheat",
"swwmfartcheat", "swwmsupercheat", "swwmstonecheat"
};
bool matchany = false;
kstr.AppendCharacter(e.KeyChar);
if ( kstr.Length() > 0 )
{
for ( int i=0; i<cht.Size(); i++ )
{
if ( kstr != cht[i].Left(kstr.length()) ) continue;
matchany = true;
if ( kstr != cht[i] ) continue;
SendNetworkEvent(cmd[i],consoleplayer);
kcode = 0;
kstr = "";
return true;
}
if ( !matchany )
{
kcode = 0;
kstr = "";
}
else
{
kcode++;
if ( kcode > 4 ) return true; // eat keypresses from this point
}
}
// F
if ( e.KeyChar == 0x66 )
{
let demo = Demolitionist(players[consoleplayer].mo);
let gone = PlayerGone(players[consoleplayer].mo);
if ( (demo && (demo.Health <= 0) && (demo.deadtimer > 40))
|| (gone && (gone.Health <= 0) && (gone.deadtimer > 40)) )
{
// pay respects
int numf = Random[FInTheChat](1,6);
for ( int i=0; i<numf; i++ )
{
let f = PayRespects.PressF();
StatusBar.AttachMessage(f,0,layer:StatusBar.HUDMSGLayer_OverHUD);
}
}
}
}
return false;
}
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;
let w = SWWMWeapon(e.Thing.player.ReadyWeapon);
if ( !w || !w.wallponch ) return;
let s = SWWMStats.Find(e.Thing.player);
if ( s ) s.wponch++;
}
override void CheckReplacee( ReplacedEvent e )
{
if ( e.Replacement is 'DSparilHax' )
e.Replacee = 'Sorcerer2';
}
// do any players not own dual guns yet
private bool ShouldSpawnDualExpl()
{
int np = 0, ng = 0;
// check travelling items, in case this was called mid-transition
let ti = ThinkerIterator.Create("ExplodiumGun",Thinker.STAT_TRAVELLING);
ExplodiumGun g;
while ( g = ExplodiumGun(ti.Next()) )
{
np++;
if ( g.Amount > 1 ) ng++;
}
if ( np > 0 ) return (ng < np);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
np++;
if ( players[i].mo.CountInv("ExplodiumGun") > 1 ) ng++;
}
return (ng < np);
}
override void CheckReplacement( ReplaceEvent e )
{
// respect final replacements
if ( e.IsFinal ) return;
// shell types (sorted by rarity
static const Class<Actor> redpool[] = {"RedShell","RedShell2","RedShell4","RedShell8"};
static const Class<Actor> greenpool[] = {"GreenShell","GreenShell2","GreenShell4"};
static const Class<Actor> whitepool[] = {"WhiteShell","WhiteShell2"};
static const Class<Actor> purplepool[] = {"PurpleShell","PurpleShell2","PurpleShell4"};
static const Class<Actor> bluepool[] = {"BlueShell","BlueShell2","BlueShell4"};
static const Class<Actor> blackpool[] = {"BlackShell","BlackShell2"};
// only replace vanilla blood if no other gore mod is doing it
if ( (e.Replacee == "Blood") && (!e.Replacement || e.Replacement == "Blood") && swwm_blood ) e.Replacement = "mkBlood";
else if ( e.Replacee is 'ItemFog' ) e.Replacement = 'SWWMItemFog';
else if ( e.Replacee is 'TeleportFog' ) e.Replacement = 'SWWMTeleportFog';
else if ( (e.Replacee is 'CommanderKeen') && (!e.Replacement || (e.Replacement == 'CommanderKeen')) )
{
let def = GetDefaultByType(e.Replacee);
bool dehackery = false;
for ( State s=def.SpawnState; s; s=s.NextState )
{
if ( s.bDEHACKED ) dehackery = true;
// keep checking until we hit a loop, just in case
if ( s.NextState && (s.DistanceTo(s.NextState) <= 0) ) break;
}
if ( dehackery ) return;
e.Replacement = 'SWWMHangingKeen';
}
else if ( (e.Replacee is 'BossBrain') && (!e.Replacement || (e.Replacement == 'BossBrain')) )
{
let def = GetDefaultByType(e.Replacee);
bool dehackery = false;
for ( State s=def.SpawnState; s; s=s.NextState )
{
if ( s.bDEHACKED ) dehackery = true;
// keep checking until we hit a loop, just in case
if ( s.NextState && (s.DistanceTo(s.NextState) <= 0) ) break;
}
if ( dehackery ) return;
e.Replacement = 'SWWMBossBrain';
}
else if ( e.Replacee is 'RedCard' )
{
if ( level.GetChecksum() ~== "3805A661D5C4523AFF7BF86991071043" )
return; // don't replace red key in Equinox MAP13
e.Replacement = 'SWWMRedCard';
}
else if ( e.Replacee is 'BlueCard' ) e.Replacement = 'SWWMBlueCard';
else if ( e.Replacee is 'YellowCard' ) e.Replacement = 'SWWMYellowCard';
else if ( e.Replacee.GetClassName() == 'KDiZDSilverKey' ) e.Replacement = 'SWWMSilverCardKDiZD';
else if ( e.Replacee.GetClassName() == 'KDiZDGreenKey' ) e.Replacement = 'SWWMGreenCardKDiZD';
else if ( e.Replacee.GetClassName() == 'KDiZDOrangeKey' ) e.Replacement = 'SWWMOrangeCardKDiZD';
else if ( e.Replacee.GetClassName() == 'GreenCard' ) e.Replacement = 'SWWMGreenCard';
else if ( e.Replacee is 'RedSkull' ) e.Replacement = 'SWWMRedSkull';
else if ( e.Replacee is 'BlueSkull' ) e.Replacement = 'SWWMBlueSkull';
else if ( e.Replacee is 'YellowSkull' ) e.Replacement = 'SWWMYellowSkull';
else if ( e.Replacee is 'KeyGreen' ) e.Replacement = 'SWWMKeyGreen';
else if ( e.Replacee is 'KeyBlue' ) e.Replacement = 'SWWMKeyBlue';
else if ( e.Replacee is 'KeyYellow' ) e.Replacement = 'SWWMKeyYellow';
else if ( e.Replacee.GetClassName() == 'KeyRed' ) e.Replacement = 'SWWMKeyRed';
else if ( (e.Replacee is 'Chainsaw') || (e.Replacee is 'Gauntlets') || (e.Replacee is 'FWeapAxe') )
{
if ( ShouldSpawnDualExpl() && Random[Replacements](0,1) ) e.Replacement = 'ExplodiumGun';
else e.Replacement = 'PusherWeapon';
}
else if ( (e.Replacee is 'Fist') || (e.Replacee is 'Staff') ) e.Replacement = 'DeepImpact';
else if ( (e.Replacee is 'Pistol') || (e.Replacee is 'GoldWand') || (e.Replacee is 'FWeapFist') || (e.Replacee is 'CWeapMace') || (e.Replacee is 'MWeapWand') || (e.Replacee.GetClassName() == 'TangoPistol') ) e.Replacement = 'ExplodiumGun';
else if ( (e.Replacee is 'Shotgun') || (e.Replacee is 'CWeapStaff') || (e.Replacee.GetClassName() == 'TangoShotgun') ) e.Replacement = 'Spreadgun';
else if ( (e.Replacee is 'SuperShotgun') || (e.Replacee is 'MWeapFrost') || (e.Replacee.GetClassName() == 'TangoSuperShotgun') ) e.Replacement = 'Wallbuster';
else if ( (e.Replacee is 'Crossbow') || (e.Replacee.GetClassName() == 'TangoScrapGun') ) e.Replacement = Random[Replacements](0,2)?'Spreadgun':'Wallbuster';
else if ( (e.Replacee is 'Chaingun') || (e.Replacee is 'Blaster') || (e.Replacee is 'FWeaponPiece3') || (e.Replacee.GetClassName() == 'TangoAssaultRifle') || (e.Replacee.GetClassName() == 'TangoChaingun') ) e.Replacement = 'Eviscerator';
else if ( (e.Replacee is 'RocketLauncher') || (e.Replacee is 'PhoenixRod') || (e.Replacee is 'FWeapHammer') || (e.Replacee.GetClassName() == 'TangoStomper') ) e.Replacement = 'Hellblazer';
else if ( (e.Replacee is 'PlasmaRifle') || (e.Replacee is 'SkullRod') ) e.Replacement = Random[Replacements](0,2)?'Sparkster':'SilverBullet';
else if ( e.Replacee is 'CWeapFlame' ) e.Replacement = 'Sparkster';
else if ( e.Replacee is 'MWeapLightning' ) e.Replacement = 'SilverBullet';
else if ( (e.Replacee is 'BFG9000') || (e.Replacee is 'Mace') ) e.Replacement = Random[Replacements](0,2)?'CandyGun':'Ynykron';
else if ( e.Replacee is 'CWeaponPiece2' ) e.Replacement = 'CandyGun';
else if ( e.Replacee is 'MWeaponPiece1' ) e.Replacement = 'Ynykron';
else if ( (e.Replacee is 'ShellBox') || (e.Replacee is 'CrossbowHefty') || (e.Replacee.GetClassName() == 'TangoShellBox') || (e.Replacee.GetClassName() == 'TangoScrapDrumPack') )
{
switch( Random[Replacements](0,14) )
{
case 0:
case 1:
case 2:
e.Replacement = redpool[Random[Replacements](1,2)];
break;
case 3:
case 4:
case 5:
e.Replacement = greenpool[Random[Replacements](1,2)];
break;
case 6:
case 7:
case 8:
e.Replacement = whitepool[Random[Replacements](0,1)];
break;
case 9:
case 10:
case 11:
e.Replacement = purplepool[Random[Replacements](0,2)];
break;
case 12:
case 13:
e.Replacement = bluepool[Random[Replacements](0,2)];
break;
case 14:
e.Replacement = blackpool[Random[Replacements](0,1)];
break;
}
}
else if ( (e.Replacee is 'Shell') || (e.Replacee is 'CrossbowAmmo') || (e.Replacee.GetClassName() == 'TangoShell') || (e.Replacee.GetClassName() == 'TangoScrapDrum') )
{
switch( Random[Replacements](0,13) )
{
case 0:
case 1:
case 2:
e.Replacement = redpool[Random[Replacements](0,1)];
break;
case 3:
case 4:
case 5:
e.Replacement = greenpool[Random[Replacements](0,1)];
break;
case 6:
case 7:
e.Replacement = whitepool[0];
break;
case 8:
case 9:
case 10:
e.Replacement = purplepool[Random[Replacements](0,1)];
break;
case 11:
case 12:
e.Replacement = bluepool[Random[Replacements](0,1)];
break;
case 13:
e.Replacement = blackpool[0];
break;
}
}
else if ( e.Replacee is 'ClipBox' ) e.Replacement = Random[Replacements](0,4)?'EvisceratorShell':Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack';
else if ( (e.Replacee is 'Clip') || (e.Replacee is 'GoldWandAmmo') ) e.Replacement = 'SWWMNothing';
else if ( e.Replacee is 'BlasterHefty' ) e.Replacement = Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack';
else if ( (e.Replacee is 'BlasterAmmo') || (e.Replacee.GetClassName() == 'TangoBulletClipHalf') ) e.Replacement = 'EvisceratorShell';
else if ( (e.Replacee is 'RocketBox') || (e.Replacee is 'PhoenixRodHefty') || (e.Replacee is 'MaceHefty')|| (e.Replacee.GetClassName() == 'TangoStomperBox') )
{
switch ( Random[Replacements](0,11) )
{
case 0:
case 1:
case 2:
case 3:
case 4:
if ( Random[Replacements](0,5) ) e.Replacement = 'HellblazerMissiles';
else if ( Random[Replacements](0,4) ) e.Replacement = 'HellblazerMissileTrioSpawn';
else e.Replacement = 'HellblazerMissileMag';
break;
case 5:
case 6:
case 7:
case 8:
if ( Random[Replacements](0,6) ) e.Replacement = 'HellblazerCrackshots';
else e.Replacement = 'HellblazerCrackshotMag';
break;
case 9:
case 10:
if ( Random[Replacements](0,8) ) e.Replacement = 'HellblazerRavagers';
else e.Replacement = 'HellblazerRavagerMag';
break;
case 11:
if ( Random[Replacements](0,10) ) e.Replacement = 'HellblazerWarheads';
else e.Replacement = 'HellblazerWarheadMag';
break;
}
}
else if ( (e.Replacee is 'RocketAmmo') || (e.Replacee is 'PhoenixRodAmmo') || (e.Replacee is 'MaceAmmo') || (e.Replacee.GetClassName() == 'TangoStomperAmmo') ) e.Replacement = Random[Replacements](0,2)?'HellblazerMissiles':'HellblazerCrackshots';
else if ( (e.Replacee is 'CellPack') || (e.Replacee is 'SkullRodHefty') )
{
if ( !Random[Replacements](0,2) )
{
if ( Random[Replacements](0,3) ) e.Replacement = Random[Replacements](0,2)?'SilverBulletsBundleSpawn':'SilverBullets2BundleSpawn';
else e.Replacement = Random[Replacements](0,2)?'SilverBulletAmmo':'SilverBulletAmmo2';
}
else if ( Random[Replacements](0,2) ) e.Replacement = 'CandyGunBulletsBundleSpawn';
else e.Replacement = 'CandyGunAmmo';
}
else if ( (e.Replacee is 'Cell') || (e.Replacee is 'SkullRodAmmo') )
{
if ( !Random[Replacements](0,2) ) e.Replacement = Random[Replacements](0,2)?'HellblazerRavagers':'HellblazerWarheads';
else if ( Random[Replacements](0,2) ) e.Replacement = 'SparkUnit';
else if ( !Random[Replacements](0,3) ) e.Replacement = 'CandyGunBullets';
else e.Replacement = Random[Replacements](0,2)?'SilverBullets':'SilverBullets2';
}
else if ( e.Replacee is 'Mana1' ) e.Replacement = 'FabricatorTier1';
else if ( e.Replacee is 'Mana2' ) e.Replacement = 'FabricatorTier2';
else if ( e.Replacee is 'Mana3' ) e.Replacement = 'FabricatorTier3';
else if ( e.Replacee is 'ArtiBoostMana' ) e.Replacement = 'FabricatorTier4';
else if ( (e.Replacee is 'Backpack') || (e.Replacee is 'BagOfHolding') || (e.Replacee is 'ArtiBoostArmor') ) e.Replacement = 'HammerspaceEmbiggener';
else if ( (e.Replacee is 'FWeaponPiece1') || (e.Replacee is 'FWeaponPiece2')
|| (e.Replacee is 'CWeaponPiece1') || (e.Replacee is 'CWeaponPiece3')
|| (e.Replacee is 'MWeaponPiece2') || (e.Replacee is 'MWeaponPiece3') )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'SWWMNothing';
else if ( Random[Replacements](0,5) ) e.Replacement = 'HammerspaceEmbiggener';
else e.Replacement = 'GoldShell';
}
else if ( (e.Replacee is 'ArmorBonus') || (e.Replacee is 'ArtiTimeBomb') || (e.Replacee is 'ArtiBlastRadius') || (e.Replacee is 'ArtiPoisonBag') ) e.Replacement = 'ArmorNuggetItem';
else if ( (e.Replacee is 'HealthBonus') || (e.Replacee is 'CrystalVial') ) e.Replacement = 'HealthNuggetItem';
else if ( e.Replacee is 'Stimpack' ) e.Replacement = 'TetraHealthItem';
else if ( e.Replacee is 'Medikit' ) e.Replacement = 'CubeHealthItem';
else if ( e.Replacee is 'ArtiHealth' ) e.Replacement = Random[Replacements](0,1)?'CubeHealthItem':'TetraHealthItem';
else if ( (e.Replacee is 'Soulsphere') || (e.Replacee is 'ArtiSuperHealth') ) e.Replacement = 'RefresherItem';
else if ( e.Replacee is 'ArtiHealingRadius' ) e.Replacement = 'SWWMNothing';
else if ( (e.Replacee is 'Megasphere') || (e.Replacee is 'ArtiEgg') || (e.Replacee is 'PlatinumHelm') ) e.Replacement = 'GrilledCheeseSandwich';
else if ( (e.Replacee is 'Blursphere') || (e.Replacee is 'ArtiInvisibility') || (e.Replacee is 'AmuletOfWarding') ) e.Replacement = 'GhostArtifact';
else if ( e.Replacee is 'Radsuit' ) e.Replacement = 'EBarrier';
else if ( (e.Replacee is 'ArtiFly') ) e.Replacement = 'GravitySuppressor';
else if ( (e.Replacee is 'InvulnerabilitySphere') || (e.Replacee is 'ArtiInvulnerability') || (e.Replacee is 'ArtiInvulnerability2') ) e.Replacement = 'FuckingInvinciball';
else if ( (e.Replacee is 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'Ragekit';
else if ( (e.Replacee is 'AllMap') || (e.Replacee is 'SuperMap') ) e.Replacement = 'Omnisight';
else if ( (e.Replacee is 'Infrared') || (e.Replacee is 'ArtiTorch') ) e.Replacement = 'SWWMLamp';
else if ( (e.Replacee is 'GreenArmor') || (e.Replacee is 'SilverShield') || (e.Replacee is 'MeshArmor') ) e.Replacement = 'BlastSuitItem';
else if ( (e.Replacee is 'BlueArmor') || (e.Replacee is 'FalconShield') || (e.Replacee is 'EnchantedShield') ) e.Replacement = 'WarArmorItem';
else if ( (e.Replacee is 'ArtiPork') || (e.Replacee is 'ArtiDarkServant') || (e.Replacee is 'ArtiTeleport') || (e.Replacee == 'ArtiTeleportOther') ) e.Replacement = 'ChanceboxSpawner';
else return;
// this last part is kind of ugly, but it works
// guarantees that OUR replacements are all final
e.IsFinal = true;
}
override void ConsoleProcess( ConsoleEvent e )
{
// doing it with an event because this way we can control WHEN it should be openable
if ( e.Name ~== "swwmdemomenu" )
{
if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') )
return;
Menu.SetMenu('DemolitionistMenu');
}
}
override void NetworkProcess( ConsoleEvent e )
{
static const Class<Ammo> cbttypes[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
if ( e.Name ~== "swwmgesture" )
{
if ( (e.player == -1) || !playeringame[e.player] || !players[e.player].mo ) return;
let mo = players[e.player].mo;
switch( e.Args[0] )
{
case 0:
SWWMGesture.SetGesture(mo,GS_Wave);
break;
case 1:
SWWMGesture.SetGesture(mo,GS_ThumbsUp);
break;
case 2:
SWWMGesture.SetGesture(mo,GS_Victory);
break;
case 3:
SWWMGesture.SetGesture(mo,GS_BlowKiss);
break;
}
return;
}
else if ( e.Name ~== "swwmfixitemcaps" )
{
// this command is only really needed when I update item max amounts mid-playthrough
if ( multiplayer && (e.player != Net_Arbitrator) )
{
if ( e.player == consoleplayer )
Console.Printf("Only the net arbitrator can call this event.");
return;
}
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
let mo = players[i].mo;
Inventory hams = mo.FindInventory("HammerspaceEmbiggener");
if ( hams )
{
if ( hams.MaxAmount != hams.default.MaxAmount )
Console.Printf("Adjust %s capacity (%d -> %d)",hams.GetTag(),hams.MaxAmount,hams.default.MaxAmount);
hams.MaxAmount = hams.default.MaxAmount;
hams.Amount = min(hams.Amount,hams.MaxAmount);
}
for ( Inventory inv=mo.inv; inv; inv=inv.inv )
{
if ( inv is 'Ammo' )
{
Ammo(inv).BackpackMaxAmount = Ammo(inv).default.BackpackMaxAmount;
int newmax = inv.default.MaxAmount;
if ( (hams.Amount > 0) && (Ammo(inv).BackpackMaxAmount > 0) )
{
double factor = (Ammo(inv).BackpackMaxAmount-inv.default.MaxAmount)/double(hams.MaxAmount);
newmax = int(inv.default.MaxAmount+hams.Amount*factor);
}
if ( inv.MaxAmount != newmax )
Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,newmax);
int dropme = max(0,inv.Amount-newmax);
if ( dropme )
{
Console.Printf("Dropped %dx %s.",dropme,inv.GetTag());
// non-SWWM ammos won't subdivide, but whatever, this is a debug command
inv.CreateTossable(dropme);
}
inv.MaxAmount = newmax;
}
else if ( inv is 'MagAmmo' )
{
if ( inv.MaxAmount != inv.default.MaxAmount )
Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,inv.default.MaxAmount);
// just drop the extras
int dropme = max(0,inv.Amount-inv.default.MaxAmount);
if ( dropme )
{
Console.Printf("Dropped %dx %s.",dropme,inv.GetTag());
inv.CreateTossable(dropme);
}
inv.MaxAmount = inv.default.MaxAmount;
}
else
{
if ( inv.MaxAmount != inv.default.MaxAmount )
Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,inv.default.MaxAmount);
// only drop droppables (obviously)
if ( !inv.bUNDROPPABLE && !inv.bUNTOSSABLE )
{
int dropme = max(0,inv.Amount-inv.default.MaxAmount);
if ( dropme )
{
Console.Printf("Dropped %dx %s.",dropme,inv.GetTag());
for ( int j=0; j<dropme; j++ ) inv.CreateTossable(j);
}
}
inv.MaxAmount = inv.default.MaxAmount;
inv.Amount = min(inv.Amount,inv.MaxAmount);
}
}
}
}
else if ( e.Name ~== "swwmupdatetrackers" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )
{
if ( e.player == consoleplayer )
Console.Printf("Only the net arbitrator can call this event.");
return;
}
if ( swwm_notrack )
{
int n = trackers_cnt;
while ( trackers ) trackers.Destroy(); // wow that's simple, all in one line
Console.Printf("%d trackers removed.",n);
}
else
{
int n = trackers_cnt;
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( (!a.bSHOOTABLE && !a.bISMONSTER) || (a is 'LampMoth') || (a is 'CompanionLamp') ) continue;
let trk = SWWMCombatTracker.Spawn(a);
if ( !a.player ) trk.maxhealth = max(a.health,a.GetSpawnHealth());
}
n = (trackers_cnt-n);
Console.Printf("%d trackers added.",n);
}
return;
}
else if ( e.Name ~== "swwmtrimsuckables" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )
{
if ( e.player == consoleplayer )
Console.Printf("Only the net arbitrator can call this event.");
return;
}
int n = 0;
for ( int i=0; i<suckableactors.Size(); i++ )
{
if ( suckableactors[i] && (suckableactors[i].bSHOOTABLE || suckableactors[i].bMISSILE) ) continue;
suckableactors.Delete(i);
i--;
n++;
}
Console.Printf("%d suckable actors trimmed.",n);
return;
}
else if ( e.Name ~== "swwmdebugdumprng" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )
{
if ( e.player == consoleplayer )
Console.Printf("Only the net arbitrator can call this event.");
return;
}
// dump the values of all mod RNGs (might help someday to track down what desyncs)
Console.Printf("\cxSWWM GZ RNG dump for player %d (\c-%s\cx):\c-",consoleplayer,players[consoleplayer].GetUserName());
Console.Printf("bdscreen: %d",Random2[bdscreen]());
Console.Printf("Blood: %d",Random2[Blood]());
Console.Printf("Boolet: %d",Random2[Boolet]());
Console.Printf("BrainExplode: %d",Random2[BrainExplode]());
Console.Printf("Bundle: %d",Random2[Bundle]());
Console.Printf("Candy: %d",Random2[Candy]());
Console.Printf("Chancebox: %d",Random2[Chancebox]());
Console.Printf("Corrode: %d",Random2[Corrode]());
Console.Printf("DemoLines: %d",Random2[DemoLines]());
Console.Printf("DoBlast: %d",Random2[DoBlast]());
Console.Printf("Eviscerator: %d",Random2[Eviscerator]());
Console.Printf("Explodium: %d",Random2[Explodium]());
Console.Printf("Explos: %d",Random2[Explos]());
Console.Printf("ExtraMissiles: %d",Random2[ExtraMissiles]());
Console.Printf("FInTheChat: %d",Random2[FInTheChat]());
Console.Printf("FlameT: %d",Random2[FlameT]());
Console.Printf("Flicker: %d",Random2[Flicker]());
Console.Printf("FunTags: %d",Random2[FunTags]());
Console.Printf("Gibs: %d",Random2[Gibs]());
Console.Printf("GoldDrop: %d",Random2[GoldDrop]());
Console.Printf("Goldy: %d",Random2[Goldy]());
Console.Printf("GunFlash: %d",Random2[GunFlash]());
Console.Printf("hdscreen: %d",Random2[hdscreen]());
Console.Printf("Hellblazer: %d",Random2[Hellblazer]());
Console.Printf("HudStuff: %d",Random2[HudStuff]());
Console.Printf("Impact: %d",Random2[Impact]());
Console.Printf("InterArt: %d",Random2[InterArt]());
Console.Printf("Invinciball: %d",Random2[Invinciball]());
Console.Printf("Junk: %d",Random2[Junk]());
Console.Printf("Moth: %d",Random2[Moth]());
Console.Printf("Nugget: %d",Random2[Nugget]());
Console.Printf("Parry: %d",Random2[Parry]());
Console.Printf("Ponch: %d",Random2[Ponch]());
Console.Printf("Puff: %d",Random2[Puff]());
Console.Printf("Pusher: %d",Random2[Pusher]());
Console.Printf("Rage: %d",Random2[Rage]());
Console.Printf("Replacements: %d",Random2[Replacements]());
Console.Printf("ScoreBits: %d",Random2[ScoreBits]());
Console.Printf("ShellDrop: %d",Random2[ShellDrop]());
Console.Printf("Shivers: %d",Random2[Shivers]());
Console.Printf("Silverbullet: %d",Random2[Silverbullet]());
Console.Printf("SpareShells: %d",Random2[SpareShells]());
Console.Printf("Sparkster: %d",Random2[Sparkster]());
Console.Printf("Spread: %d",Random2[Spread]());
Console.Printf("Spreadgun: %d",Random2[Spreadgun]());
Console.Printf("TUID: %d",Random2[TUID]());
Console.Printf("Wallbuster: %d",Random2[Wallbuster]());
Console.Printf("WallbusterMenu: %d",Random2[WallbusterMenu]());
Console.Printf("Ynykron: %d",Random2[Ynykron]());
return;
}
if ( e.IsManual ) return;
if ( e.Name.Left(14) ~== "swwmstoregive." )
{
Class<Inventory> item = e.Name.Mid(14);
if ( !item ) return;
if ( SWWMCredits.Take(players[e.Args[0]],e.Args[1]) )
{
let def = GetDefaultByType(item);
SWWMWeapon sw;
// drop the swapweapon if we own it first
if ( swwm_swapweapons && (item is 'SWWMWeapon') && (sw = SWWMWeapon(def).HasSwapWeapon(players[e.Args[0]].mo)) )
{
bool swapto = (sw == players[e.Args[0]].ReadyWeapon) || (sw.SisterWeapon && (sw.Sisterweapon == players[e.Args[0]].ReadyWeapon));
int ngun = sw.Amount;
double ang = -15*(ngun-1);
for ( int i=0; i<ngun; i++ )
{
let d = players[e.Args[0]].mo.DropInventory(sw);
if ( !d || (ngun <= 1) ) continue;
// adjust angle for multi-drops
d.angle = players[e.Args[0]].mo.angle+ang;
d.vel.xy = Actor.RotateVector((5,0),d.angle);
d.vel.z = 1;
d.vel += players[e.Args[0]].mo.vel;
ang += 30;
}
// don't autoswitch just yet (hacky)
if ( swapto )
{
players[e.Args[0]].ReadyWeapon = null;
players[e.Args[0]].PendingWeapon = WP_NOCHANGE;
}
}
if ( (item is 'ArmorNuggetItem') || (item is 'HealthNuggetItem') )
{
// these have to be given in a loop because fun reasons
for ( int i=0; i<e.Args[2]; i++ )
players[e.Args[0]].mo.GiveInventory(item,1,true);
}
else players[e.Args[0]].mo.GiveInventory(item,e.Args[2],true);
// fucky workaround
let inv = players[e.Args[0]].mo.FindInventory(item);
if ( inv && (inv.Amount <= 0) && !inv.bKEEPDEPLETED ) inv.Destroy();
if ( item is 'Weapon' )
{
// special case, select dual guns if we bought a second one
if ( (item is 'ExplodiumGun') && (players[e.Args[0]].mo.CountInv("ExplodiumGun") > 1) )
players[e.Args[0]].mo.A_SelectWeapon("DualExplodiumGun");
else players[e.Args[0]].mo.A_SelectWeapon((Class<Weapon>)(item));
}
}
}
else if ( e.Name.Left(14) ~== "swwmstoretake." )
{
Class<Inventory> item = e.Name.Mid(14);
if ( !item ) return;
int amt = e.Args[2];
if ( item is 'CandyGun' )
{
// check if we can sell a spare instead, for the same price
int n = players[e.Args[0]].mo.CountInv('CandyGunSpares');
if ( n >= amt )
{
players[e.Args[0]].mo.TakeInventory('CandyGunSpares',amt);
SWWMCredits.Give(players[e.Args[0]],e.Args[1]);
return;
}
}
// if player currently has the dual wield weapon selected, switch over
if ( item is 'SWWMWeapon' )
{
let c = Weapon(players[e.Args[0]].mo.FindInventory(item));
if ( c.SisterWeapon && (players[e.Args[0]].ReadyWeapon == c.SisterWeapon) )
{
players[e.Args[0]].ReadyWeapon = c;
players[e.Args[0]].SetPSprite(PSP_WEAPON,c.FindState("Ready"));
players[e.Args[0]].SetPSprite(PSP_WEAPON+1,null); // delete left weapon psprite
}
}
players[e.Args[0]].mo.TakeInventory(item,amt);
SWWMCredits.Give(players[e.Args[0]],e.Args[1]);
}
else if ( e.Name.Left(10) ~== "swwmtrade." )
{
Class<Inventory> item = e.Name.Mid(10);
if ( !item ) return;
let def = GetDefaultByType(item);
int amt = def.Amount;
// if it's an ammo, check the largest unit givable
if ( item is 'Ammo' )
{
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let a = (Class<Ammo>)(AllActorClasses[i]);
if ( !a || (a.GetParentClass() != item) || (GetDefaultByType(a).Amount < amt) ) continue;
amt = GetDefaultByType(a).Amount;
}
}
Inventory ritm = players[e.Args[1]].mo.FindInventory(item);
if ( ritm )
{
int maxgive = ritm.MaxAmount-ritm.Amount;
if ( amt > maxgive ) amt = maxgive;
}
else if ( amt > def.MaxAmount ) amt = def.MaxAmount;
bool rslt = false;
if ( (amt > 0) && players[e.Args[1]].mo.GiveInventory(item,amt,true) )
{
players[e.Args[0]].mo.TakeInventory(item,amt);
// add to history
SWWMTradeHistory.RegisterSend(players[e.Args[0]],players[e.Args[1]],item,amt);
SWWMTradeHistory.RegisterReceive(players[e.Args[1]],players[e.Args[0]],item,amt);
// add messages
if ( e.Args[0] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGSENT"),amt,def.GetTag(),players[e.Args[1]].GetUserName());
if ( e.Args[1] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGRECV"),players[e.Args[0]].GetUserName(),amt,def.GetTag());
rslt = true;
}
if ( e.Args[0] == consoleplayer )
{
let t = new("MenuTransaction");
t.uid = e.Args[2];
t.type = MenuTransaction.TT_ITEMSEND;
t.result = rslt;
t.used = item;
t.usedup = (players[e.Args[1]].mo.CountInv(item)<=0);
checklist.Push(t);
}
}
else if ( e.Name.Left(17) ~== "swwmmarkloreread." )
{
let l = SWWMLoreLibrary.Find(players[e.Args[0]]);
let idx = l.FindEntry(e.Name.Mid(17));
l.MarkRead(idx);
}
else if ( e.Name.Left(12) ~== "swwmuseitem." )
{
Class<Inventory> item = e.Name.Mid(12);
if ( !item ) return;
let i = players[e.Args[0]].mo.FindInventory(item);
if ( !i ) return;
bool rslt = players[e.Args[0]].mo.UseInventory(i);
if ( e.Args[0] == consoleplayer )
{
let t = new("MenuTransaction");
t.uid = e.Args[1];
t.type = MenuTransaction.TT_ITEMUSE;
let w = (Class<Weapon>)(item);
if ( w )
{
t.result = (players[e.Args[0]].PendingWeapon==Weapon(i));
// dual wield gun support
if ( (i is 'ExplodiumGun') && (players[e.Args[0]].PendingWeapon==Weapon(i).SisterWeapon) )
t.result = true;
}
else t.result = rslt;
t.used = item;
t.usedup = (!i||(i.Amount<=0));
checklist.Push(t);
}
}
else if ( e.Name.Left(13) ~== "swwmdropitem." )
{
Class<Inventory> item = e.Name.Mid(13);
if ( !item ) return;
let i = players[e.Args[0]].mo.FindInventory(item);
if ( !i ) return;
int amt = i.default.Amount;
// if it's an ammo, check the largest unit givable
if ( i is 'Ammo' )
{
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let a = (Class<Ammo>)(AllActorClasses[i]);
if ( !a || (a.GetParentClass() != item) || (GetDefaultByType(a).Amount < amt) ) continue;
amt = GetDefaultByType(a).Amount;
}
}
if ( amt > i.Amount ) amt = i.Amount;
let drop = players[e.Args[0]].mo.DropInventory(i,amt);
// add some random velocity so multiple drops don't get bunched together
if ( drop ) drop.vel += (Actor.RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),players[e.Args[0]].mo.angle),FRandom[Junk](2.,5.));
if ( e.Args[0] == consoleplayer )
{
let t = new("MenuTransaction");
t.uid = e.Args[1];
t.type = MenuTransaction.TT_ITEMDROP;
t.used = item;
t.result = drop;
t.usedup = (!i||(i.Amount<=0));
checklist.Push(t);
}
}
else if ( e.Name ~== "swwmkoraxline" )
{
if ( consoleplayer != e.Args[1] ) return;
switch ( e.Args[0] )
{
case 0:
AddOneliner("koraxgreet",3,60);
break;
case 1:
AddOneliner("koraxblood",3,150);
break;
case 2:
AddOneliner("koraxgame",3,120);
break;
case 3:
AddOneliner("koraxworship",3,80);
break;
case 4:
AddOneliner("koraxmasters",3,90);
break;
}
}
else if ( e.Name.Left(16) ~== "swwmremoteliner." )
{
if ( consoleplayer == e.Args[0] ) return;
if ( !CVar.GetCVar('swwm_othervoice',players[consoleplayer]).GetBool() ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= e.Args[1] ) return;
players[e.Args[0]].mo.A_StartSound(e.Name.Mid(16),CHAN_DEMOVOICE,attenuation:.5);
}
else if ( e.Name.Left(19) ~== "swwmremotelinertxt." )
{
if ( consoleplayer == e.Args[0] ) return;
if ( !CVar.GetCVar('swwm_othervoice',players[consoleplayer]).GetBool() ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= e.Args[1] ) return;
double dist = players[consoleplayer].Camera.Distance3D(players[e.Args[0]].mo);
if ( dist < 2000 )
Console.Printf("\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19)));
}
else if ( e.Name.Left(8) ~== "swwmcbt." )
{
// from wikipedia, the free encyclopedia
if ( !playeringame[e.Args[0]] || !players[e.Args[0]].mo ) return;
let cbt = Wallbuster(players[e.Args[0]].mo.FindInventory("Wallbuster"));
if ( !cbt ) return;
cbt.reloadqueue.Clear();
if ( e.Name.Mid(8) ~== "EMPTY" ) cbt.clearout = true;
else
{
cbt.clearout = false;
Array<String> qs;
qs.Clear();
String rite = e.Name.Mid(8);
rite.Split(qs,",",TOK_SKIPEMPTY);
for ( int i=0; i<qs.Size(); i++ )
{
int qi = qs[i].ToInt();
if ( (qi < 0) || (qi > 3) ) continue;
cbt.reloadqueue.Push(cbttypes[qi]);
}
}
cbt.waitreload = false;
}
else if ( e.Name ~== "swwmcleartransaction" )
{
if ( e.Args[1] != consoleplayer ) return;
for ( int i=0; i<checklist.Size(); i++ )
{
if ( checklist[i].uid != e.Args[0] ) continue;
checklist.Delete(i);
i--;
}
}
else if ( e.Name ~== "swwmclearalltransactions" )
{
if ( e.Args[0] != consoleplayer ) return;
checklist.Clear();
}
// CHEATS
else if ( e.Name ~== "swwmmoneycheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
// what's that spell?
// loadsamoney! ... probably
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyLOADSAMONEY!\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/emone",CHAN_VOICE,CHANF_UI);
}
SWWMCredits.Give(players[e.Args[0]],1000000000);
SWWMScoreObj.Spawn(1000000000,players[e.Args[0]].mo.Vec3Offset(0,0,players[e.Args[0]].mo.Height/2));
}
else if ( e.Name ~== "swwmlorecheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyKNOWLEDGE!\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/lamborghini",CHAN_VOICE,CHANF_UI);
}
// look up all lore files
for ( int l=0; l<Wads.GetNumLumps(); l++ )
{
String fn = Wads.GetLumpFullName(l);
if ( fn.Left(13) != "lore/default/" ) continue;
int ext = fn.IndexOf(".txt");
if ( ext != fn.Length()-4 ) continue;
SWWMLoreLibrary.Add(players[e.Args[0]],fn.Mid(13,ext-13));
}
}
else if ( e.Name ~== "swwmsafecheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyStay out of trouble.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
}
Vector3 safepos;
double safeangle;
[safepos, safeangle] = level.PickPlayerStart(e.Args[0]);
players[e.Args[0]].mo.Teleport(safepos,safeangle,TF_TELEFRAG|TF_FORCED|TF_USESPOTZ);
}
else if ( e.Name ~== "swwmweaponcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyYou better be happy now\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/w_pkup",CHAN_VOICE,CHANF_UI);
}
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let w = (Class<SWWMWeapon>)(AllActorClasses[i]);
if ( !w || (w == 'SWWMWeapon') ) continue;
let def = GetDefaultByType(w);
if ( def.bCHEATNOTWEAPON ) continue;
let ow = players[e.Args[0]].mo.FindInventory(w);
if ( ow && (ow.Amount >= ow.MaxAmount) ) continue;
if ( ow ) ow.Amount = ow.MaxAmount;
else players[e.Args[0]].mo.GiveInventory(w,def.MaxAmount);
}
}
else if ( e.Name ~== "swwmhealcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyRemember to stay fit.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/health_pkup",CHAN_VOICE,CHANF_UI);
}
players[e.Args[0]].health = players[e.Args[0]].mo.health = 1000;
}
else if ( e.Name ~== "swwmynykroncheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyYou're still crazy.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/w_pkup",CHAN_VOICE,CHANF_UI);
}
players[e.Args[0]].mo.GiveInventory("Ynykron",1);
}
else if ( e.Name ~== "swwmgravcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyGot something floatier.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI);
}
let g = GravityPower(players[e.Args[0]].mo.FindInventory("GravityPower"));
if ( g ) g.EffectTics += g.default.EffectTics;
else players[e.Args[0]].mo.GiveInventory("GravityPower",1);
}
else if ( e.Name ~== "swwminvischeat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyProbably because you're invisible.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI);
}
let g = GhostPower(players[e.Args[0]].mo.FindInventory("GhostPower"));
if ( g ) g.EffectTics += g.default.EffectTics;
else players[e.Args[0]].mo.GiveInventory("GhostPower",1);
}
else if ( e.Name ~== "swwmbarriercheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cySafe from those pesky elements.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI);
}
let b = BarrierPower(players[e.Args[0]].mo.FindInventory("BarrierPower"));
if ( b ) b.EffectTics += b.default.EffectTics;
else players[e.Args[0]].mo.GiveInventory("BarrierPower",1);
}
else if ( e.Name ~== "swwmammocheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyDon't squander it.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("misc/ammo_pkup",CHAN_VOICE,CHANF_UI);
}
players[e.Args[0]].mo.GiveInventory("HammerspaceEmbiggener",8,true);
for ( Inventory i=players[e.Args[0]].mo.inv; i; i=i.inv )
{
if ( !(i is 'Ammo') ) continue;
i.Amount = i.MaxAmount;
}
}
else if ( e.Name ~== "swwmbloodcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyEdgy...\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmexplocheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyThat cheat's not needed anymore.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmallcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyStill as wrappy as it's always been.\c-");
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
S_StartSound("fabricator/use",CHAN_VOICE,CHANF_UI);
}
players[e.Args[0]].mo.CheatGive("all",0);
}
else if ( e.Name ~== "swwmflagcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyThere are no flags here.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmballcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cy\"Balls on your head\"? What was I even thinking...\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmsmartcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cySkittles are better anyway.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmnutcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyI'm way past that, it was bad for my health.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmweeniecheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyAlways has been.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmpunishcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyThis is a bulli free zone.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmball2cheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cy<insert amiga boing ball here>\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmfartcheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyI'd rather not reimplement that one.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmsupercheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyNo, you're the Demolitionist.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
else if ( e.Name ~== "swwmstonecheat" )
{
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
return;
if ( consoleplayer == e.Args[0] )
{
Console.Printf("\cyThe pinnacle of... wait, I misread that.\c-");
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
}
}
}
// stuff for hud
override void RenderUnderlay( RenderEvent e )
{
// armor/health flashes
int camplayer = players[consoleplayer].Camera.PlayerNumber();
if ( camplayer != -1 )
{
if ( gametic < hflash[camplayer] )
{
double fstr = (hflash[camplayer]-(gametic+e.FracTic))/5.;
Screen.Dim(Color(64,128,255),.1875*fstr,0,0,Screen.GetWidth(),Screen.GetHeight());
}
if ( gametic < aflash[camplayer] )
{
double fstr = (aflash[camplayer]-(gametic+e.FracTic))/5.;
Screen.Dim(Color(96,255,64),.1875*fstr,0,0,Screen.GetWidth(),Screen.GetHeight());
}
}
// weapon underlays
if ( players[consoleplayer].ReadyWeapon is 'SWWMWeapon' )
SWWMWeapon(players[consoleplayer].ReadyWeapon).RenderUnderlay(e);
if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return;
SWWMStatusBar(statusbar).viewpos = e.viewpos;
SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll);
if ( slotstrictwarn && (gametic < slotstrictwarn) )
{
String str = StringTable.Localize("$SWWM_SETSLOTSTRICT");
double t = (slotstrictwarn-(gametic+e.FracTic))/20.;
double alph = clamp(t,0.,1.);
BrokenLines l = newsmallfont.BreakLines(str,300);
double yy = (200-l.Count()*newsmallfont.GetHeight())/2;
for ( int i=0; i<l.Count(); i++ )
{
double xx = (320-l.StringWidth(i))/2;
Screen.DrawText(newsmallfont,Font.CR_UNTRANSLATED,xx,yy,l.StringAt(i),DTA_Clean,true,DTA_Alpha,alph);
yy += newsmallfont.GetHeight();
}
}
}
// called by HUD (done here for the sake of cleaner code)
ui void DrawBossBar( SWWMStatusBar bar )
{
if ( !ui_initialized || (bossalpha <= 0.) ) return;
if ( !dodrawbossbar ) dodrawbossbar = CVar.GetCVar('swwm_bosshealthbars',players[consoleplayer]);
if ( !dodrawbossbar.GetBool() ) return;
if ( !bbar_f ) bbar_f = TexMan.CheckForTexture("graphics/HUD/BossHealthBarBox.png",TexMan.Type_Any);
if ( !bbar_r ) bbar_r = TexMan.CheckForTexture("graphics/HUD/BossHealthBar.png",TexMan.Type_Any);
if ( !bbar_d ) bbar_d = TexMan.CheckForTexture("graphics/HUD/BossHealthBarDecay.png",TexMan.Type_Any);
Vector2 vpos = ((bar.ss.x-300)/2.,bar.ss.y-(bar.margin+35));
Screen.DrawTexture(bbar_f,false,vpos.x-2,vpos.y-2,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha);
int rw = int(clamp((ihealthr.GetValue()*300.)/hmax,0.,300.));
int dw = int(clamp((ihealth.GetValue()*300.)/hmax,0.,300.));
Screen.DrawTexture(bbar_d,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,dw);
Screen.DrawTexture(bbar_r,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,rw);
Font barfnt = bar.LangFont(bar.mTewiFont);
Font dmgfnt = bar.mTewiFont.mFont;
if ( (cummdamage > 0) && (gametic < lastcummtic+150) )
{
double calph = clamp(((lastcummtic+150)-gametic)/50.,0.,1.);
string dnum = String.Format("%d",cummdamage);
Screen.DrawText(dmgfnt,Font.CR_RED,vpos.x+300-dmgfnt.StringWidth(dnum),vpos.y-(dmgfnt.GetHeight()+2),dnum,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha*calph);
}
Screen.DrawText(barfnt,Font.CR_WHITE,vpos.x,vpos.y-(barfnt.GetHeight()+2),StringTable.Localize((funtags&&funtags.GetBool())?(bosstag.."_FUN"):bosstag),DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha);
}
// various shaders
override void RenderOverlay( RenderEvent e )
{
PlayerInfo p = players[consoleplayer];
if ( !useshaders ) useshaders = CVar.GetCVar('swwm_shaders',p);
let mo = p.mo;
if ( !mo ) return;
bool pc = (p.camera == mo);
let rage = RagekitPower(mo.FindInventory("RagekitPower"));
if ( pc && rage && useshaders.GetBool() )
{
if ( !altrage ) altrage = CVar.GetCVar('swwm_rageshader',p);
if ( altrage.GetBool() )
{
Shader.SetEnabled(p,"RagekitShader",false);
Shader.SetEnabled(p,"RagekitAltShader",true);
Shader.SetUniform1f(p,"RagekitAltShader","timer",(gametic+e.FracTic)/Thinker.TICRATE);
double xstrastr = 1.+max(0,rage.lastpulse-(gametic+e.Fractic))/35.;
Shader.SetUniform1f(p,"RagekitAltShader","xtrastr",xstrastr**2.);
}
else
{
Shader.SetEnabled(p,"RagekitAltShader",false);
Shader.SetEnabled(p,"RagekitShader",true);
Shader.SetUniform1f(p,"RagekitShader","timer",(gametic+e.FracTic)/Thinker.TICRATE);
double xstrastr = 1.+max(0,rage.lastpulse-(gametic+e.Fractic))/35.;
Shader.SetUniform1f(p,"RagekitShader","xtrastr",xstrastr**2.);
}
}
else
{
Shader.SetEnabled(p,"RagekitShader",false);
Shader.SetEnabled(p,"RagekitAltShader",false);
}
let ghost = GhostPower(mo.FindInventory("GhostPower"));
if ( pc && ghost && useshaders.GetBool() ) Shader.SetEnabled(p,"GhostShader",true);
else Shader.SetEnabled(p,"GhostShader",false);
let sunny = InvinciballPower(mo.FindInventory("InvinciballPower"));
if ( pc && sunny && useshaders.GetBool() )
{
Shader.SetEnabled(p,"InvinciShader",true);
double str = max(0,sunny.lastpulse-(gametic+e.Fractic))/35.;
Shader.SetUniform1f(p,"InvinciShader","str",str);
}
else Shader.SetEnabled(p,"InvinciShader",false);
let coat = BarrierPower(mo.FindInventory("BarrierPower"));
if ( pc && coat && useshaders.GetBool() )
{
Shader.SetEnabled(p,"BarrierShader",true);
Shader.SetUniform1f(p,"BarrierShader","timer",(gametic+e.FracTic)/Thinker.TICRATE);
}
else Shader.SetEnabled(p,"BarrierShader",false);
if ( pc && (mo is 'Demolitionist') && useshaders.GetBool() )
{
let demo = Demolitionist(mo);
if ( demo.lastunder == Demolitionist.UNDER_WATER )
{
Shader.SetEnabled(p,"WaterWarp",true);
Shader.SetUniform1f(p,"WaterWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"WaterWarp","dfact",coat?.25:1.);
Shader.SetUniform3f(p,"WaterWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
}
else Shader.SetEnabled(p,"WaterWarp",false);
if ( demo.lastunder == Demolitionist.UNDER_LAVA )
{
Shader.SetEnabled(p,"LavaWarp",true);
Shader.SetUniform1f(p,"LavaWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"LavaWarp","dfact",coat?.25:1.);
Shader.SetUniform3f(p,"LavaWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
}
else Shader.SetEnabled(p,"LavaWarp",false);
if ( demo.lastunder == Demolitionist.UNDER_SLIME )
{
Shader.SetEnabled(p,"SlimeWarp",true);
Shader.SetUniform1f(p,"SlimeWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"SlimeWarp","dfact",coat?.25:1.);
Shader.SetUniform3f(p,"SlimeWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
}
else Shader.SetEnabled(p,"SlimeWarp",false);
int lastdmg = (demo.Health>0)?demo.lastdamage:Random[Flicker](60,80);
int lastdmgtic = (demo.Health>0)?demo.lastdamagetic:(gametic+Random[Flicker](30,20));
double noiz = min(lastdmg*.09*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.5);
Shader.SetEnabled(p,"Glitch",noiz>0);
Shader.SetEnabled(p,"Grain",noiz>0);
if ( noiz > 0 )
{
Shader.SetUniform1f(p,"Glitch","Timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"Grain","ni",noiz);
noiz = min(lastdmg*.08*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.8);
Shader.SetUniform1f(p,"Glitch","str1",noiz);
noiz = min(lastdmg*.03*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),3.5);
Shader.SetUniform1f(p,"Glitch","str2",noiz);
}
if ( !demo.InStateSequence(demo.CurState,demo.FindState("Dash")) )
{
Shader.SetEnabled(p,"ZoomBlur",false);
return;
}
Shader.SetEnabled(p,"ZoomBlur",true);
Vector3 vel = demo.vel+demo.dashdir*demo.dashboost;
double baumpu = max(0.,(demo.bumptic-(gametic+e.Fractic))/35.);
vel += demo.dashdir*baumpu;
double spd = vel.length();
Vector3 worlddir = vel/spd;
Shader.SetUniform1f(p,"ZoomBlur","Fade",clamp((spd-20.)/60.,0.,1.));
double str = min(spd/40.,15.);
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(e.ViewPitch,e.ViewAngle,e.ViewRoll);
Vector3 reldir = (worlddir dot y, worlddir dot z, worlddir dot x);
Vector2 centerspot = (.5+reldir.x*.5,.5+reldir.y*.5);
if ( reldir.z < 0 )
{
centerspot.x = 1.-centerspot.x;
centerspot.y = 1.-centerspot.y;
str *= -1;
}
Shader.SetUniform1f(p,"ZoomBlur","Str",str);
Shader.SetUniform2f(p,"ZoomBlur","CenterSpot",centerspot);
}
else
{
Shader.SetEnabled(p,"WaterWarp",false);
Shader.SetEnabled(p,"LavaWarp",false);
Shader.SetEnabled(p,"SlimeWarp",false);
Shader.SetEnabled(p,"Glitch",false);
Shader.SetEnabled(p,"Grain",false);
Shader.SetEnabled(p,"ZoomBlur",false);
}
}
static void DoFlash( Actor camera, Color c, int duration )
{
// don't flash when paused
if ( menuactive && (menuactive != Menu.OnNoPause) ) return;
QueuedFlash qf = new("QueuedFlash");
qf.duration = duration;
qf.c = c;
qf.tic = gametic;
qf.cam = camera;
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return; // not supposed to happen
hnd.flashes.push(qf);
}
// can't use this until I actually figure out how to make those walls damageable
/*override void WorldLineDamaged( WorldEvent e )
{
// allow boss brain to take (reduced) damage from the facewall being shot
if ( level.mapname ~== "MAP30" )
{
if ( !SWWMUtility.IsIOSWall(e.DamageLine) ) return;
if ( bossbrainactor )
bossbrainactor.DamageMobj(e.Inflictor,e.DamageSource,e.Damage/3,e.DamageType,e.DamageFlags,e.DamageAngle);
e.NewDamage = 0;
}
}*/
}