Add 50% crusher damage reduction + chance to break crushers. Don't spawn gibs when using 'kill monsters'. Allow player to "fake pick up" unsupported vanilla armor, so specials and item counters can still work.
3996 lines
134 KiB
Text
3996 lines
134 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 mnotify;
|
|
bool allkills, allitems, allsecrets;
|
|
bool mapclear;
|
|
int mapclearagain, restartmus;
|
|
String lastmus;
|
|
int lastorder;
|
|
bool lastloop;
|
|
// 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;
|
|
transient CVar numcolor_scr, numcolor_bonus, numcolor_dmg, numcolor_hp, numcolor_ap;
|
|
|
|
// 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;
|
|
|
|
bool nugflip; // h/a nugget flip-flop spawn counter
|
|
|
|
// stuff to reduce worldthingspawned overhead
|
|
int bossmap;
|
|
int iwantdie;
|
|
int indoomvacation;
|
|
|
|
bool hasdrlamonsters;
|
|
|
|
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_HMAP48_HMAP55,
|
|
MAP_HMAP36,
|
|
MAP_HMAP37,
|
|
MAP_HMAP38,
|
|
MAP_HMAP40,
|
|
MAP_HMAP60,
|
|
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")
|
|
|| (mapsum ~== "15FC0991D975325556EFF71F241A4458")
|
|
|| (mapsum ~== "2FAD54B58487884F06EAFA507B553921") )
|
|
return MAP_HMAP23_HMAP27_HMAP48_HMAP55;
|
|
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 ~== "B0ADDB295A3ACCE43978AAC91FB8C58A" )
|
|
return MAP_HMAP60;
|
|
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)*GameTicRate);
|
|
hnd.onelinerlevel = level;
|
|
return hnd.onelinertic+hnd.onelinerspan;
|
|
}
|
|
|
|
override void OnRegister()
|
|
{
|
|
// oneliner RNG must be relative to consoleplayer
|
|
SetRandomSeed[DemoLines](Random[DemoLines]()+consoleplayer+MSTime());
|
|
// "uninitialize" some vars
|
|
iwantdie = -1;
|
|
bossmap = -1;
|
|
indoomvacation = -1;
|
|
// this one can be initialized here easily
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
if ( AllActorClasses[i].GetClassName() != "RLMonster" ) continue;
|
|
hasdrlamonsters = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 )
|
|
{
|
|
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
|
|
SWWMStats s;
|
|
while ( s = SWWMStats(ti.Next()) )
|
|
{
|
|
if ( !(gameinfo.gametype&GAME_STRIFE) )
|
|
{
|
|
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();
|
|
s.lastcluster = level.cluster;
|
|
}
|
|
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 )
|
|
{
|
|
// doom vacation map01 hackaround for OPEN script not letting us
|
|
// change certain line specials in levelpostprocessor because
|
|
// HOLY FUCK IS EVERYTHING SHIT SOMETIMES
|
|
if ( (level.GetChecksum() ~== "F286BABF0D152259CD6B996E8920CA70")
|
|
|| (level.GetChecksum() ~== "A52BD2038CF814101AAB7D9C78F9ACE2") )
|
|
level.ExecuteSpecial(ACS_Execute,null,null,false,-Int('DVACATION_UNFUCK'));
|
|
// 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();
|
|
}
|
|
// reset inventory (including unclearables) on forced pistol starts (must have visited at least one map, though)
|
|
// known bug: the previous weapon will play its select sound regardless, this is ENTIRELY IMPOSSIBLE to fix
|
|
if ( swwm_pistolstart && (s.lstats.Size() > 0) && ((s.lastcluster != level.cluster) || !(level.clusterflags&LevelLocals.CLUSTER_HUB)) )
|
|
SWWMUtility.WipeInventory(p.mo,swwm_resetscore);
|
|
}
|
|
|
|
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()
|
|
{
|
|
if ( !mapclear && (restartmus > 0) )
|
|
{
|
|
restartmus--;
|
|
if ( restartmus == 0 ) S_ChangeMusic(lastmus,lastorder,lastloop,true);
|
|
return;
|
|
}
|
|
// ignore levels that have NOTHING
|
|
if ( (level.total_secrets <= 0) && (level.total_items <= 0) && (level.total_monsters <= 0) ) return;
|
|
if ( mapclear )
|
|
{
|
|
if ( (swwm_silencemap == 1) && (musplaying.name != "") )
|
|
{
|
|
lastmus = musplaying.name;
|
|
lastorder = musplaying.baseorder;
|
|
lastloop = musplaying.loop;
|
|
S_ChangeMusic("",force:true);
|
|
}
|
|
else if ( (swwm_silencemap > 1) && (musplaying.name != "music/olg.ogg") )
|
|
{
|
|
lastmus = musplaying.name;
|
|
lastorder = musplaying.baseorder;
|
|
lastloop = musplaying.loop;
|
|
S_ChangeMusic("music/olg.ogg",force:true);
|
|
}
|
|
if ( (level.found_secrets < level.total_secrets) || (level.found_items < level.total_items) || (level.killed_monsters < level.total_monsters) )
|
|
{
|
|
if ( swwm_silencemap > 0 )
|
|
{
|
|
restartmus = 25;
|
|
S_ChangeMusic("",force:true);
|
|
}
|
|
S_StartSound("recordscratch",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
|
|
mapclear = false;
|
|
if ( mapclearagain > 1 ) Console.Printf(StringTable.Localize("$SWWM_NOTCLEARAGAIN"));
|
|
else Console.Printf(StringTable.Localize("$SWWM_NOTCLEAR"));
|
|
}
|
|
return;
|
|
}
|
|
if ( (level.found_secrets < level.total_secrets) || (level.found_items < level.total_items) || (level.killed_monsters < level.total_monsters) ) return;
|
|
restartmus = 0;
|
|
mapclear = true;
|
|
if ( mapclearagain ) Console.Printf(StringTable.Localize("$SWWM_ALLCLEARAGAIN"),500);
|
|
else Console.Printf(StringTable.Localize("$SWWM_ALLCLEAR"),5000);
|
|
S_StartSound("misc/wow",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
|
|
lastmus = musplaying.name;
|
|
lastorder = musplaying.baseorder;
|
|
lastloop = musplaying.loop;
|
|
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;
|
|
if ( mapclearagain )
|
|
{
|
|
SWWMCredits.Give(players[i],500);
|
|
SWWMScoreObj.Spawn(500,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
|
|
}
|
|
else
|
|
{
|
|
SWWMCredits.Give(players[i],5000);
|
|
SWWMScoreObj.Spawn(5000,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
|
|
}
|
|
}
|
|
mapclearagain++;
|
|
}
|
|
|
|
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 or used the kill monsters cheat
|
|
if ( (e.DamageType == 'Ynykron') || (e.DamageType == 'Massacre') ) 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),ST_Damage,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);
|
|
if ( s ) // deathmatch telefrag-on-spawn may cause this to be null
|
|
{
|
|
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);
|
|
if ( s ) // deathmatch telefrag-on-spawn may cause this to be null
|
|
{
|
|
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*GameTicRate) )
|
|
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.xstr[ofs] = StringTable.Localize("$SWWM_SHAMEFUL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
else if ( e.DamageType == 'Buttslam' )
|
|
{
|
|
score += 300;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
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;
|
|
}
|
|
}
|
|
else if ( e.Inflictor is 'FroggyChair' )
|
|
{
|
|
score += 1440;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
scr.xtcolor[ofs] = Font.CR_GREEN;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_FROGGED");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
Inventory pb;
|
|
if ( e.Inflictor && (pb = e.Inflictor.FindInventory('ParriedBuff')) )
|
|
{
|
|
score += 200;
|
|
if ( pb.special1&1 ) score += 200;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
if ( pb.special1&1 ) scr.xstr[ofs] = StringTable.Localize("$SWWM_PPARRY");
|
|
else scr.xstr[ofs] = StringTable.Localize("$SWWM_PARRY");
|
|
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.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.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.xstr[ofs] = StringTable.Localize("$SWWM_SPREEKILL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
if ( e.Thing.bBOSS )
|
|
{
|
|
score += 2000;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
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 ( iwantdie == -1 ) iwantdie = (G_SkillName() == StringTable.Localize("$SWWM_SKLUNATIC"));
|
|
if ( iwantdie )
|
|
{
|
|
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;
|
|
double ang = e.Thing.target.target?e.Thing.AngleTo(e.Thing.target.target):e.Thing.angle;
|
|
double pt = e.Thing.target.target?SWWMUtility.PitchTo(e.Thing,e.Thing.target.target,.5):e.Thing.pitch;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pt,ang,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++ )
|
|
{
|
|
// three attempts for each
|
|
for ( int j=0; j<3; j++ )
|
|
{
|
|
let x = Actor.Spawn(e.Thing.GetClass(),e.Thing.Vec3Angle(e.Thing.Radius*FRandom[ExtraMissiles](1.5,4.),FRandom[ExtraMissiles](0,360)));
|
|
if ( x.pos.z+x.height > x.ceilingz ) x.SetZ(x.ceilingz-x.height);
|
|
if ( x.pos.z < x.floorz ) x.SetZ(x.floorz);
|
|
if ( !x.TestMobjLocation() || !x.TestMobjZ() || !level.IsPointInLevel(x.pos) )
|
|
{
|
|
x.ClearCounters();
|
|
x.Destroy();
|
|
}
|
|
else
|
|
{
|
|
x.angle = e.Thing.angle;
|
|
x.GiveInventory("DontDuplicate",1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 ( indoomvacation == -1 ) indoomvacation = SWWMUtility.InDoomVacation();
|
|
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";
|
|
}
|
|
// doom vacation stuff
|
|
else if ( indoomvacation )
|
|
{
|
|
if ( e.Thing.GetClassName() == "Babe" )
|
|
{
|
|
e.Thing.bSHOOTABLE = false; // no hurt
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .2;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "CommanderKeen" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .4;
|
|
HeadpatTracker(hp).hdoomangfix = 5;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "BBChair" )
|
|
{
|
|
e.Thing.bUSESPECIAL = false;
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .2;
|
|
HeadpatTracker(hp).hdoomangfix = 15;
|
|
HeadpatTracker(hp).patstate = e.Thing.MeleeState;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "EvilEye" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .1;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "HeadCandles" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomangfix = 20;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "HeartColumn" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = -.3;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "Meat2" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .6;
|
|
HeadpatTracker(hp).hdoomangfix = -15;
|
|
HeadpatTracker(hp).dvacationarghack = true;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "Meat3" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .6;
|
|
HeadpatTracker(hp).hdoomangfix = 20;
|
|
HeadpatTracker(hp).dvacationarghack = true;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "LegsBabe" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = -1.5;
|
|
HeadpatTracker(hp).hdoomangfix = 20;
|
|
HeadpatTracker(hp).dvacationarghack = true;
|
|
}
|
|
else if ( e.Thing.GetClassName() == "Meat4" )
|
|
{
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
HeadpatTracker(hp).hdoomheightfix = .6;
|
|
HeadpatTracker(hp).hdoomangfix = 15;
|
|
HeadpatTracker(hp).dvacationarghack = true;
|
|
}
|
|
}
|
|
if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing)
|
|
|| SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) )
|
|
{
|
|
// you can pet the dog, and you can also pet the caco (and friends)
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
}
|
|
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;
|
|
if ( bossmap == -1 ) 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_HMAP48_HMAP55 )
|
|
{
|
|
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_HMAP60 )
|
|
{
|
|
if ( (e.Thing is 'FighterBoss') || (e.Thing is 'ClericBoss') || (e.Thing is 'MageBoss') )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_DEATHKINGS";
|
|
initialized = true; // healthbar shows from the start
|
|
}
|
|
}
|
|
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", "swwmfroggygang",
|
|
// 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", "swwmfroggycheat",
|
|
// 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';
|
|
}
|
|
|
|
private Class<Actor> GetDRLAReplacement( Class<Actor> a )
|
|
{
|
|
static const String refpool[] =
|
|
{
|
|
"Zombieman",
|
|
"Shotgunguy",
|
|
"Chaingunguy",
|
|
"DoomImp",
|
|
"Demon",
|
|
"Spectre",
|
|
"LostSoul",
|
|
"Cacodemon",
|
|
"HellKnight",
|
|
"BaronOfHell",
|
|
"Arachnotron",
|
|
"PainElemental",
|
|
"Revenant",
|
|
"Fatso",
|
|
"Archvile",
|
|
"Cyberdemon",
|
|
"SpiderMastermind",
|
|
"BossEye",
|
|
"BossBrain"
|
|
};
|
|
static const String babypool[] =
|
|
{
|
|
"RLFormerHumanPistol",
|
|
"RLFormerSergeantShotgun",
|
|
"RLFormerCommandoChaingun",
|
|
"RLImp",
|
|
"RLDemon",
|
|
"RLSpectre",
|
|
"RLLostSoul",
|
|
"RLCacodemon",
|
|
"RLHellKnight",
|
|
"RLBaronOfHell",
|
|
"RLArachnotron",
|
|
"RLPainElemental",
|
|
"RLRevenant",
|
|
"RLMancubus",
|
|
"RLArchvile",
|
|
"RLCyberdemon",
|
|
"RLSpiderMastermindVariantSpawner",
|
|
"RLEasyBossEye",
|
|
"RLBossBrain"
|
|
};
|
|
static const String easypool[] =
|
|
{
|
|
"RLFormerHumanNoArmageddonSpawner",
|
|
"RLFormerSergeantNoArmageddonSpawner",
|
|
"RLFormerCommandoNoArmageddonSpawner",
|
|
"RLImpNoArmageddonSpawner",
|
|
"RLDemonNoArmageddonSpawner",
|
|
"RLSpectreNoArmageddonSpawner",
|
|
"RLLostSoulNoArmageddonSpawner",
|
|
"RLCacodemonNoArmageddonSpawner",
|
|
"RLHellKnightNoArmageddonSpawner",
|
|
"RLBaronOfHellNoArmageddonSpawner",
|
|
"RLArachnotronNoArmageddonSpawner",
|
|
"RLPainElementalNoArmageddonSpawner",
|
|
"RLRevenantNoArmageddonSpawner",
|
|
"RLMancubusNoArmageddonSpawner",
|
|
"RLArchvileNoArmageddonSpawner",
|
|
"RLCyberdemonNoArmageddonSpawner",
|
|
"RLSpiderMastermindNoArmageddonSpawner",
|
|
"RLBossEye",
|
|
"RLBossBrain"
|
|
};
|
|
static const String normalpool[] =
|
|
{
|
|
"RLFormerHumanNoArmageddonSpawner",
|
|
"RLFormerSergeantNoArmageddonSpawner",
|
|
"RLFormerCommandoNoArmageddonSpawner",
|
|
"RLImpNoArmageddonSpawner",
|
|
"RLDemonNoArmageddonSpawner",
|
|
"RLSpectreNoArmageddonSpawner",
|
|
"RLLostSoulNoArmageddonSpawner",
|
|
"RLCacodemonNoArmageddonSpawner",
|
|
"RLHellKnightNoArmageddonSpawner",
|
|
"RLBaronOfHellNoArmageddonSpawner",
|
|
"RLArachnotronNoArmageddonSpawner",
|
|
"RLPainElementalNoArmageddonSpawner",
|
|
"RLRevenantNoArmageddonSpawner",
|
|
"RLMancubusNoArmageddonSpawner",
|
|
"RLArchvileNoArmageddonSpawner",
|
|
"RLCyberdemonNoArmageddonSpawner",
|
|
"RLSpiderMastermindNoArmageddonSpawner",
|
|
"RLBossEye",
|
|
"RLBossBrain"
|
|
};
|
|
static const String hardpool[] =
|
|
{
|
|
"RLFormerHumanSpawner",
|
|
"RLFormerSergeantSpawner",
|
|
"RLFormerCommandoSpawner",
|
|
"RLImpSpawner",
|
|
"RLDemonSpawner",
|
|
"RLSpectreSpawner",
|
|
"RLLostSoulSpawner",
|
|
"RLCacodemonSpawner",
|
|
"RLHellKnightSpawner",
|
|
"RLBaronOfHellSpawner",
|
|
"RLArachnotronSpawner",
|
|
"RLPainElementalSpawner",
|
|
"RLRevenantSpawner",
|
|
"RLMancubusSpawner",
|
|
"RLArchvileSpawner",
|
|
"RLCyberdemonSpawner",
|
|
"RLSpiderMastermindSpawner",
|
|
"RLUVBossEye",
|
|
"RLBossBrain"
|
|
};
|
|
static const String nightmarepool[] =
|
|
{
|
|
"RLEliteFormerHumanSpawner",
|
|
"RLEliteFormerSergeantSpawner",
|
|
"RLEliteFormerCommandoSpawner",
|
|
"RLNightmareImp",
|
|
"RLNightmareDemon",
|
|
"RLNightmareSpectre",
|
|
"RLNightmareLostSoul",
|
|
"RLNightmareCacodemon",
|
|
"RLNightmareHellKnight",
|
|
"RLNightmareBaronOfHell",
|
|
"RLNightmareArachnotron",
|
|
"RLNightmarePainElemental",
|
|
"RLNightmareRevenant",
|
|
"RLNightmareMancubus",
|
|
"RLNightmareArchvile",
|
|
"RLNightmareCyberdemonSpawner",
|
|
"RLNightmareSpiderMastermindSpawner",
|
|
"RLNightmareBossEye",
|
|
"RLNightmareBossBrain"
|
|
};
|
|
static const String technophobiapool[] =
|
|
{
|
|
"RLFormerCyborgBattleRifle",
|
|
"RLFormerCyborgBattleRifle",
|
|
"RLFormerCyborgBattleRifle",
|
|
"RLCyberneticImp",
|
|
"RLCyberneticDemon",
|
|
"RLCyberneticSpectre",
|
|
"RLCyberneticLostSoul",
|
|
"RLCacodemon",
|
|
"RLCyberneticHellKnight",
|
|
"RLCyberneticBaronOfHell",
|
|
"RLCyberneticArachnotron",
|
|
"RLCyberneticPainElemental",
|
|
"RLCyberneticRevenant",
|
|
"RLCyberneticMancubus",
|
|
"RLCyberneticArchvile",
|
|
"RLCyberneticCyberdemonSpawner",
|
|
"RLCyberneticSpiderMastermindSpawner",
|
|
"RLTechnophobiaBossEye",
|
|
"RLTechnophobiaBossBrain"
|
|
};
|
|
static const String armageddonpool[] =
|
|
{
|
|
"RLFormerAssaultTrooper",
|
|
"RLFormerOverwatch",
|
|
"RLFormerShocktrooper",
|
|
"RLArmageddonImp",
|
|
"RLArmageddonDemon",
|
|
"RLArmageddonSpectreSpawner",
|
|
"RLTheHungrySpawner",
|
|
"RLArmageddonCacodemon",
|
|
"RLArmageddonHellKnightSpawner",
|
|
"RLArmageddonBaronOfHell",
|
|
"RLArmageddonArachnotron",
|
|
"RLArmageddonPainElemental",
|
|
"RLArmageddonRevenant",
|
|
"RLArmageddonMancubus",
|
|
"RLArmageddonArchvileSpawner",
|
|
"RLArmageddonCyberdemonSpawner",
|
|
"RLArmageddonSpiderMastermindSpawner",
|
|
"RLArmageddonBossEye",
|
|
"RLArmageddonBossBrain"
|
|
};
|
|
static const String adaptivepool[] =
|
|
{
|
|
"RLAdaptiveFormerHuman",
|
|
"RLAdaptiveFormerSergeant",
|
|
"RLAdaptiveFormerCommando",
|
|
"RLAdaptiveImp",
|
|
"RLAdaptiveDemon",
|
|
"RLAdaptiveSpectre",
|
|
"RLAdaptiveLostSoul",
|
|
"RLAdaptiveCacodemon",
|
|
"RLAdaptiveHellKnight",
|
|
"RLAdaptiveBaronOfHell",
|
|
"RLAdaptiveArachnotron",
|
|
"RLAdaptivePainElemental",
|
|
"RLAdaptiveRevenant",
|
|
"RLAdaptiveMancubus",
|
|
"RLAdaptiveArchvile",
|
|
"RLAdaptiveCyberdemon",
|
|
"RLAdaptiveSpiderMastermind",
|
|
"RLUVBossEye",
|
|
"RLBossBrain"
|
|
};
|
|
switch ( swwm_drlaskill )
|
|
{
|
|
case 0:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return babypool[i];
|
|
}
|
|
break;
|
|
case 1:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return easypool[i];
|
|
}
|
|
break;
|
|
case 2:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return normalpool[i];
|
|
}
|
|
break;
|
|
case 3:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return hardpool[i];
|
|
}
|
|
break;
|
|
case 4:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return nightmarepool[i];
|
|
}
|
|
break;
|
|
case 5:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return technophobiapool[i];
|
|
}
|
|
break;
|
|
case 6:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return armageddonpool[i];
|
|
}
|
|
break;
|
|
case 7:
|
|
default:
|
|
for ( int i=0; i<18; i++ )
|
|
{
|
|
if ( !(a is refpool[i]) ) continue;
|
|
return adaptivepool[i];
|
|
}
|
|
break;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
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"};
|
|
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"};
|
|
// DRLA Monsters stuff
|
|
if ( hasdrlamonsters )
|
|
{
|
|
let rep = GetDRLAReplacement(e.Replacee);
|
|
if ( rep )
|
|
{
|
|
e.Replacement = rep;
|
|
e.IsFinal = true;
|
|
return;
|
|
}
|
|
}
|
|
// 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') ) e.Replacement = SWWMUtility.PickSWWMSlot1();
|
|
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 = SWWMUtility.PickSWWMSlot2();
|
|
else if ( (e.Replacee is 'Shotgun') || (e.Replacee is 'CWeapStaff') || (e.Replacee.GetClassName() == 'TangoShotgun') ) e.Replacement = SWWMUtility.IsDoomOne()?SWWMUtility.PickHereticSlot3():SWWMUtility.PickSWWMSlot3();
|
|
else if ( (e.Replacee is 'SuperShotgun') || (e.Replacee is 'MWeapFrost') || (e.Replacee.GetClassName() == 'TangoSuperShotgun') ) e.Replacement = SWWMUtility.PickSWWMSlot4();
|
|
else if ( (e.Replacee is 'Crossbow') || (e.Replacee.GetClassName() == 'TangoScrapGun') ) e.Replacement = SWWMUtility.PickHereticSlot3();
|
|
else if ( (e.Replacee is 'Chaingun') || (e.Replacee is 'Blaster') || (e.Replacee is 'FWeaponPiece3') || (e.Replacee.GetClassName() == 'TangoAssaultRifle') || (e.Replacee.GetClassName() == 'TangoChaingun') ) e.Replacement = SWWMUtility.PickSWWMSlot5();
|
|
else if ( (e.Replacee is 'RocketLauncher') || (e.Replacee is 'PhoenixRod') || (e.Replacee is 'FWeapHammer') || (e.Replacee.GetClassName() == 'TangoStomper') ) e.Replacement = SWWMUtility.PickSWWMSlot6();
|
|
else if ( (e.Replacee is 'PlasmaRifle') || (e.Replacee is 'SkullRod') ) e.Replacement = SWWMUtility.PickDoomSlot6();
|
|
else if ( e.Replacee is 'CWeapFlame' ) e.Replacement = SWWMUtility.PickSWWMSlot7();
|
|
else if ( e.Replacee is 'MWeapLightning' ) e.Replacement = SWWMUtility.PickSWWMSlot8();
|
|
else if ( (e.Replacee is 'BFG9000') || (e.Replacee is 'Mace') ) e.Replacement = SWWMUtility.PickDoomSlot7();
|
|
else if ( e.Replacee is 'CWeaponPiece2' ) e.Replacement = SWWMUtility.PickSWWMSlot9();
|
|
else if ( e.Replacee is 'MWeaponPiece1' ) e.Replacement = SWWMUtility.PickSWWMSlot0();
|
|
else if ( (e.Replacee is 'ShellBox') || (e.Replacee is 'CrossbowHefty') || (e.Replacee.GetClassName() == 'TangoShellBox') || (e.Replacee.GetClassName() == 'TangoScrapDrumPack') )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SMW05SmallAmmo':'SMW05BigAmmo';
|
|
else */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') )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SMW05SmallAmmo':'SMW05BundleSpawn';
|
|
else */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' )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenSmallAmmo':'SheenBigAmmo';
|
|
else */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 = /*(e.Replacee is 'GoldWandHefty')?'SheenSmallAmmo':'SheenTinyAmmo'*/'SWWMNothing';
|
|
else if ( e.Replacee is 'BlasterHefty' )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenBigAmmo':'SheenSmallAmmo';
|
|
else */e.Replacement = Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack';
|
|
}
|
|
else if ( (e.Replacee is 'BlasterAmmo') || (e.Replacee.GetClassName() == 'TangoBulletClipHalf') )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenSmallAmmo':'SheenTinyAmmo';
|
|
else */e.Replacement = 'EvisceratorShell';
|
|
}
|
|
else if ( (e.Replacee is 'RocketBox') || (e.Replacee is 'PhoenixRodHefty') || (e.Replacee is 'MaceHefty')|| (e.Replacee.GetClassName() == 'TangoStomperBox') )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'QuadravolAmmo':'QuadravolAmmoBundleSpawn';
|
|
else */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') )
|
|
{
|
|
/*if ( Random[Replacements](0,1) ) e.Replacement = 'QuadravolAmmo';
|
|
else */e.Replacement = Random[Replacements](0,2)?'HellblazerMissiles':'HellblazerCrackshots';
|
|
}
|
|
else if ( (e.Replacee is 'CellPack') || (e.Replacee is 'SkullRodHefty') )
|
|
{
|
|
/*if ( Random[Replacements](0,1) )
|
|
{
|
|
if ( !Random[Replacements](0,2) ) e.Replacement = Random[Replacements](0,3)?'EMPCoreBundleSpawn':'EMPCore';
|
|
else if ( Random[Replacements](0,2) ) e.Replacement = 'RayBoltBundleSpawn';
|
|
else e.Replacement = 'RayAmmo';
|
|
}
|
|
else */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)?'RayBattery':*/Random[Replacements](0,2)?'HellblazerRavagers':'HellblazerWarheads';
|
|
else if ( Random[Replacements](0,2) ) e.Replacement = /*Random[Replacements](0,1)?'DarkCanister':*/'SparkUnit';
|
|
else if ( !Random[Replacements](0,3) ) e.Replacement = /*Random[Replacements](0,1)?'RayBolt':*/'CandyGunBullets';
|
|
else e.Replacement = /*Random[Replacements](0,1)?'EMPCore':*/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 'ArtiPork') ) 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') ) e.Replacement = 'SWWMNothing';
|
|
else if ( e.Replacee is 'ArmorBonus' ) e.Replacement = 'ArmorNuggetItem';
|
|
else if ( e.Replacee is 'HealthBonus' ) e.Replacement = 'HealthNuggetItem';
|
|
else if ( (e.Replacee is 'ArtiTimeBomb') || (e.Replacee is 'ArtiBlastRadius') || (e.Replacee is 'ArtiPoisonBag') || (e.Replacee is 'ArtiHealingRadius') ) e.Replacement = (nugflip=!nugflip)?'HealthNuggetBundleSpawn':'ArmorNuggetBundleSpawn';
|
|
else if ( (e.Replacee is 'HealthBonus') ) e.Replacement = 'HealthNuggetItem';
|
|
else if ( (e.Replacee is 'Stimpack') || (e.Replacee is 'CrystalVial') ) e.Replacement = 'TetraHealthItem';
|
|
else if ( (e.Replacee is 'Medikit') || (e.Replacee is 'ArtiHealth') ) e.Replacement = 'CubeHealthItem';
|
|
else if ( (e.Replacee is 'Soulsphere') || (e.Replacee is 'ArtiSuperHealth') ) e.Replacement = 'RefresherItem';
|
|
else if ( (e.Replacee is 'Megasphere') || (e.Replacee is 'ArtiEgg') || (e.Replacee is 'ArtiBoostArmor') ) e.Replacement = 'GrilledCheeseSandwich';
|
|
else if ( (e.Replacee is 'Blursphere') || (e.Replacee is 'ArtiInvisibility') ) 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 'PlatinumHelm') || (e.Replacee is 'AmuletOfWarding') ) e.Replacement = 'BlastSuitItem';
|
|
else if ( (e.Replacee is 'BlueArmor') || (e.Replacee is 'EnchantedShield') || (e.Replacee is 'MeshArmor') || (e.Replacee is 'FalconShield') ) e.Replacement = 'WarArmorItem';
|
|
else if ( (e.Replacee is 'ArtiDarkServant') || (e.Replacee == 'ArtiTeleportOther') ) e.Replacement = 'ChanceboxSpawner';
|
|
else if ( e.Replacee is 'ArtiTeleport' ) e.Replacement = (gameinfo.GameType&GAME_Hexen)?'ChanceboxSpawner':'SWWMNothing';
|
|
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
|
|
}
|
|
}
|
|
// if we're selling an embiggener, we need to readjust ammo
|
|
if ( item is 'HammerspaceEmbiggener' )
|
|
{
|
|
let ritm = players[e.Args[0]].mo.FindInventory(item);
|
|
for ( Inventory i=players[e.Args[0]].mo.Inv; i; i=i.Inv )
|
|
{
|
|
if ( !(i is 'Ammo') ) continue;
|
|
if ( Ammo(i).BackpackMaxAmount > 0 )
|
|
{
|
|
double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(ritm.MaxAmount);
|
|
i.MaxAmount = int(i.default.MaxAmount+(ritm.Amount-amt)*factor);
|
|
}
|
|
// drop excess ammo
|
|
int excess = i.Amount-i.MaxAmount;
|
|
if ( excess > 0 ) i.CreateTossable(excess);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmfroggycheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cdHop!\c-");
|
|
S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
let mo = players[e.Args[0]].mo;
|
|
Actor f = Actor(ThinkerIterator.Create("FroggyChair").Next());
|
|
if ( !f ) f = mo.Spawn("FroggyChair");
|
|
f.SetOrigin(mo.Vec2OffsetZ(cos(mo.angle)*40.,sin(mo.angle)*40.,mo.player.viewz-32.),false);
|
|
f.A_SetAngle(f.AngleTo(mo));
|
|
f.Spawn("SWWMItemFog",f.pos);
|
|
f.A_StartSound("bestsound",CHAN_ITEMEXTRA);
|
|
}
|
|
}
|
|
|
|
// 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)/GameTicRate);
|
|
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)/GameTicRate);
|
|
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)/GameTicRate);
|
|
}
|
|
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)/GameTicRate);
|
|
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)/GameTicRate);
|
|
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)/GameTicRate);
|
|
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)/GameTicRate);
|
|
Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/GameTicRate);
|
|
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;
|
|
}
|
|
}*/
|
|
}
|