Removed Wallbuster reload timer debug cvar (not needed). Removed CBT easter egg. Removed PrecacheClasses from ZMAPINFO (no longer needed since 4.5 as UE1 models load much faster).
3185 lines
108 KiB
Text
3185 lines
108 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];
|
|
String mpver[MAXPLAYERS];
|
|
int checktic;
|
|
|
|
override void NewGame()
|
|
{
|
|
// set save version every new session
|
|
let svd = new("SWWMSaveVerData");
|
|
svd.ChangeStatNum(Thinker.STAT_STATIC);
|
|
svd.ver = StringTable.Localize("$SWWM_MODVER");
|
|
}
|
|
|
|
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 information)\c-";
|
|
return;
|
|
}
|
|
String cver = StringTable.Localize("$SWWM_MODVER");
|
|
if ( svd.ver != cver )
|
|
{
|
|
tainted = true;
|
|
taintver = svd.ver;
|
|
}
|
|
}
|
|
|
|
override void PlayerEntered( PlayerEvent e )
|
|
{
|
|
if ( multiplayer && (e.playernumber == consoleplayer) )
|
|
EventHandler.SendNetworkEvent("swwmversion."..StringTable.Localize("$SWWM_MODVER"));
|
|
}
|
|
|
|
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.IsManual ) return;
|
|
if ( e.Name.Left(12) ~== "swwmversion." )
|
|
{
|
|
String verstr = e.Name.Mid(12);
|
|
mpver[e.Player] = verstr;
|
|
if ( verstr != StringTable.Localize("$SWWM_MODVER") )
|
|
mptaint[e.Player] = true;
|
|
}
|
|
}
|
|
|
|
override void PostUiTick()
|
|
{
|
|
// TODO achievement update code would go in here
|
|
if ( gametic != checktic ) return;
|
|
String cver = StringTable.Localize("$SWWM_MODVER");
|
|
if ( tainted )
|
|
{
|
|
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
|
|
let svd = SWWMSaveVerData(ti.Next());
|
|
if ( !svd ) Console.Printf("\cgWARNING: \cjLoaded save contains no version data. Issues may happen.");
|
|
else
|
|
{
|
|
Console.Printf("\cgWARNING: \cjThis savegame is from a different version of SWWM GZ. Issues may happen.");
|
|
Console.Printf("\cgSaved: \cj"..svd.ver);
|
|
Console.Printf("\cgCurrent: \cj"..cver);
|
|
}
|
|
}
|
|
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: \cjSome players have different versions of SWWM GZ loaded. Desyncs will happen.\c-");
|
|
Console.Printf("\cgYou: \cj"..cver);
|
|
}
|
|
found = true;
|
|
Console.Printf("\cgPlayer %d (\c-%s\cg): \cj%s",i+1,players[i].GetUserName(),(mpver[i]=="")?"\cg(no version information)\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!");
|
|
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 == 140 )
|
|
{
|
|
Console.Printf("\cfLooks like GZDoom made a fucky wucky! owo");
|
|
S_StartSound("crash/curb",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
|
|
}
|
|
else if ( timer == 350 )
|
|
{
|
|
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.");
|
|
else Console.Printf("\cfIf you didn't trigger it manually, it's best if you take a screenshot and show it to Marisa.");
|
|
Console.Printf("\cfLoaded Version: \cj%s",StringTable.Localize("$SWWM_MODVER"));
|
|
if ( tainted ) Console.Printf("\cfSavegame Version: \cj%s",taintver);
|
|
}
|
|
timer++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handler responsible for item replacements and whatever else
|
|
Class SWWMHandler : EventHandler
|
|
{
|
|
transient String oneliner, onelinersnd;
|
|
transient int onelinertic, onelinerspan, onelinerlevel;
|
|
transient int lastlock, lastcombat;
|
|
transient Array<Actor> combatactors;
|
|
transient Array<Int> combattics;
|
|
transient int highesttic;
|
|
transient Array<QueuedFlash> flashes;
|
|
transient Array<LastLine> lastlines;
|
|
transient int lastpickuptic[MAXPLAYERS];
|
|
SWWMCombatTracker trackers;
|
|
SWWMScoreObj scorenums, damnums;
|
|
SWWMInterest intpoints;
|
|
int trackers_cnt, scorenums_cnt, damnums_cnt, intpoints_cnt;
|
|
bool tookdamage[MAXPLAYERS];
|
|
int spreecount[MAXPLAYERS];
|
|
int lastkill[MAXPLAYERS];
|
|
int multilevel[MAXPLAYERS];
|
|
int lastitemcount[MAXPLAYERS];
|
|
bool allkills, allitems, allsecrets;
|
|
bool mapclear;
|
|
bool mnotify;
|
|
// for custom cheats
|
|
transient ui int kcode;
|
|
transient ui String kstr;
|
|
|
|
// heal/armor flashes need to be handled here so they don't stack
|
|
transient int hflash[MAXPLAYERS], aflash[MAXPLAYERS];
|
|
|
|
// for menu events
|
|
transient Array<MenuTransaction> checklist;
|
|
|
|
transient CVar mutevoice, accdamage;
|
|
transient ui CVar useshaders, altrage;
|
|
transient CVar lang;
|
|
transient String curlang;
|
|
transient CVar funtags;
|
|
transient bool curfuntags;
|
|
transient int slotstrictwarn;
|
|
|
|
// optimization
|
|
OnFire fires;
|
|
int fires_cnt;
|
|
|
|
// junk
|
|
SWWMCasing casings, casings_end;
|
|
int casings_cnt, oldmaxcasings;
|
|
SWWMChip chips, chips_end;
|
|
int chips_cnt, oldmaxdebris;
|
|
// gore
|
|
mkBloodDrop blods, blods_end;
|
|
int blods_cnt, oldmaxblood;
|
|
mkFlyingGib meats, meats_end;
|
|
int meats_cnt, oldmaxgibs;
|
|
|
|
// prevents revived monsters from spawning in more golden shells
|
|
Array<Actor> alreadygold;
|
|
|
|
// attempt to optimize Ynykron singularity suction
|
|
Array<Actor> suckableactors;
|
|
|
|
// vanilla boss stuff
|
|
String bosstag;
|
|
Array<Actor> bossactors;
|
|
|
|
Actor bossbrainactor;
|
|
Actor bossviewactor;
|
|
TextureID facetex[5];
|
|
|
|
bool initialized;
|
|
ui bool ui_initialized;
|
|
ui TextureID bbar_f, bbar_r, bbar_d;
|
|
ui double bossalpha;
|
|
ui DynamicValueInterpolator ihealth, ihealthr;
|
|
ui int thealth, hmax;
|
|
ui int oldhealth[30];
|
|
ui int cummdamage, lastcummtic; // please do not misread
|
|
transient ui CVar dodrawbossbar;
|
|
|
|
enum EVanillaMap
|
|
{
|
|
MAP_NONE,
|
|
MAP_DE1M8,
|
|
MAP_DE2M8,
|
|
MAP_DE3M8,
|
|
MAP_DE4M8,
|
|
MAP_HE1M8_HE4M8,
|
|
MAP_HE2M8_HE5M8,
|
|
MAP_HE3M8,
|
|
MAP_DMAP07,
|
|
MAP_DMAP30,
|
|
MAP_HMAP12,
|
|
MAP_HMAP23_HMAP27,
|
|
MAP_HMAP36,
|
|
MAP_HMAP37,
|
|
MAP_HMAP38,
|
|
MAP_HMAP40,
|
|
MAP_EVMAP30 // eviternity
|
|
};
|
|
|
|
private int WhichVanillaBossMap()
|
|
{
|
|
String mapsum = level.GetChecksum();
|
|
if ( (mapsum ~== "94500F4B006B316FE03AC46865AEABF8")
|
|
|| (mapsum ~== "97079958C7E89C1908890730B8B9FEB7")
|
|
|| (mapsum ~== "058FB092EA1B70DA1E3CBF501C4A91A1") )
|
|
return MAP_DE1M8;
|
|
if ( mapsum ~== "EFFE91DF41AD41F6973C06F0AD67DDB9" )
|
|
return MAP_DE2M8;
|
|
if ( mapsum ~== "EF128313112110ED6C1549AF96AF26C9" )
|
|
return MAP_DE3M8;
|
|
if ( mapsum ~== "2DC939E508AB8EB68AF79D5B60568711" )
|
|
return MAP_DE4M8;
|
|
if ( (mapsum ~== "27639D04F8090D57A47D354992435893")
|
|
|| (mapsum ~== "30D1480A6D4F3A3153739D4CCF659C4E") )
|
|
return MAP_HE1M8_HE4M8;
|
|
if ( (mapsum ~== "5158C22A0F30CE5E558FD2A05D67685E")
|
|
|| (mapsum ~== "85AC7D20D18F9BC49B9696CC2E67F029") )
|
|
return MAP_HE2M8_HE5M8;
|
|
if ( mapsum ~== "4719C2C71EF28F52310B889DD5A9778B" )
|
|
return MAP_HE3M8;
|
|
if ( mapsum ~== "291F24417FB3DD411339AE82EF9B3597" )
|
|
return MAP_DMAP07;
|
|
if ( mapsum ~== "5EECD88F4491F516D590CE4BBF45F532" )
|
|
return MAP_DMAP30;
|
|
if ( (mapsum ~== "89C4CD26EF05E2577B10CAFE56226662")
|
|
|| (mapsum ~== "441BF111747671066A10A146C03EEFC4")
|
|
|| (mapsum ~== "55E321849F3699655D7E062C90682F63") )
|
|
return MAP_HMAP12;
|
|
if ( (mapsum ~== "E3B06F44DBF6F7E7754D7B1DAEF707E4")
|
|
|| (mapsum ~== "FC832437D7A2B7094A9B56C3909773D9")
|
|
|| (mapsum ~== "91AD797F95CC4C6D6AE33B21F664C60B")
|
|
|| (mapsum ~== "188B1B4244BD8DA501D8532696EC8654")
|
|
|| (mapsum ~== "5B29D0889DF09A8250D62FA09EB2B452")
|
|
|| (mapsum ~== "D3C5FA777BA52264546E6569F167AF0D") )
|
|
return MAP_HMAP23_HMAP27;
|
|
if ( (mapsum ~== "4444C95C2029DA6EECAC92DAA31CE665")
|
|
|| (mapsum ~== "33752742BCA8E539A6EE3E5D0FDA8744")
|
|
|| (mapsum ~== "3FFAF2F624C1B4BB6F581DCF7B99CBA7") )
|
|
return MAP_HMAP36;
|
|
if ( (mapsum ~== "78979A583B1E30D94C9DAE2BCFA9A18D")
|
|
|| (mapsum ~== "FDC90F44C65A71E0901C1B9FFFCF3D02")
|
|
|| (mapsum ~== "088ECE0E0F3E68448FA1D901001A0084") )
|
|
return MAP_HMAP37;
|
|
if ( (mapsum ~== "3BF62E4F9FB3CF9AF267421CE2D5F348")
|
|
|| (mapsum ~== "4799E1FDB5A3C0E3AD650B5AC215A737")
|
|
|| (mapsum ~== "5C63A02B0B04D9AE95CA51687DC3406F") )
|
|
return MAP_HMAP38;
|
|
if ( (mapsum ~== "EFAFE59092DE5E613562ACF52B86C37F")
|
|
|| (mapsum ~== "1C5DE5A921DEE405E98E7E09D9829387")
|
|
|| (mapsum ~== "2A6C4235B942467D25FD50D5B313E67A") )
|
|
return MAP_HMAP40;
|
|
if ( mapsum ~== "5C5E5C08AF3572F31CF27318679F2B4E" )
|
|
return MAP_EVMAP30;
|
|
return MAP_NONE;
|
|
}
|
|
|
|
static void QueueCasing( SWWMCasing c )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd ) return;
|
|
hnd.casings_cnt++;
|
|
if ( !hnd.casings )
|
|
{
|
|
// this is the initial one
|
|
hnd.casings = c;
|
|
hnd.casings_end = c;
|
|
}
|
|
else
|
|
{
|
|
hnd.casings_end.nextcasing = c;
|
|
c.prevcasing = hnd.casings_end;
|
|
hnd.casings_end = c;
|
|
}
|
|
while ( hnd.casings && (swwm_maxcasings >= 0) && (hnd.casings_cnt > swwm_maxcasings) )
|
|
DeQueueCasing(hnd.casings);
|
|
}
|
|
static void DeQueueCasing( SWWMCasing c )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd || !hnd.casings ) return;
|
|
if ( (hnd.casings != c) && !c.prevcasing && !c.nextcasing ) return;
|
|
hnd.casings_cnt--;
|
|
if ( !c.prevcasing ) hnd.casings = c.nextcasing;
|
|
else c.prevcasing.nextcasing = c.nextcasing;
|
|
if ( c == hnd.casings_end ) hnd.casings_end = c.prevcasing;
|
|
if ( c.nextcasing ) c.nextcasing.prevcasing = c.prevcasing;
|
|
c.killme = true;
|
|
c.prevcasing = null;
|
|
c.nextcasing = null;
|
|
}
|
|
static void QueueChip( SWWMChip c )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd ) return;
|
|
hnd.chips_cnt++;
|
|
if ( !hnd.chips )
|
|
{
|
|
// this is the initial one
|
|
hnd.chips = c;
|
|
hnd.chips_end = c;
|
|
}
|
|
else
|
|
{
|
|
hnd.chips_end.nextchip = c;
|
|
c.prevchip = hnd.chips_end;
|
|
hnd.chips_end = c;
|
|
}
|
|
while ( hnd.chips && (swwm_maxdebris >= 0) && (hnd.chips_cnt > swwm_maxdebris) )
|
|
DeQueueChip(hnd.chips);
|
|
}
|
|
static void DeQueueChip( SWWMChip c )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd || !hnd.chips ) return;
|
|
if ( (hnd.chips != c) && !c.prevchip && !c.nextchip ) return;
|
|
hnd.chips_cnt--;
|
|
if ( !c.prevchip ) hnd.chips = c.nextchip;
|
|
else c.prevchip.nextchip = c.nextchip;
|
|
if ( c == hnd.chips_end ) hnd.chips_end = c.prevchip;
|
|
if ( c.nextchip ) c.nextchip.prevchip = c.prevchip;
|
|
c.killme = true;
|
|
c.prevchip = null;
|
|
c.nextchip = null;
|
|
}
|
|
static void QueueBlod( mkBloodDrop b )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd ) return;
|
|
hnd.blods_cnt++;
|
|
if ( !hnd.blods )
|
|
{
|
|
// this is the initial one
|
|
hnd.blods = b;
|
|
hnd.blods_end = b;
|
|
}
|
|
else
|
|
{
|
|
hnd.blods_end.nextblod = b;
|
|
b.prevblod = hnd.blods_end;
|
|
hnd.blods_end = b;
|
|
}
|
|
while ( hnd.blods && (swwm_maxblood >= 0) && (hnd.blods_cnt > swwm_maxblood) )
|
|
DeQueueBlod(hnd.blods);
|
|
}
|
|
static void DeQueueBlod( mkBloodDrop b )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd || !hnd.blods ) return;
|
|
if ( (hnd.blods != b) && !b.prevblod && !b.nextblod ) return;
|
|
hnd.blods_cnt--;
|
|
if ( !b.prevblod ) hnd.blods = b.nextblod;
|
|
else b.prevblod.nextblod = b.nextblod;
|
|
if ( b == hnd.blods_end ) hnd.blods_end = b.prevblod;
|
|
if ( b.nextblod ) b.nextblod.prevblod = b.prevblod;
|
|
b.killme = true;
|
|
b.prevblod = null;
|
|
b.nextblod = null;
|
|
}
|
|
static void QueueMeat( mkFlyingGib m )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd ) return;
|
|
hnd.meats_cnt++;
|
|
if ( !hnd.meats )
|
|
{
|
|
// this is the initial one
|
|
hnd.meats = m;
|
|
hnd.meats_end = m;
|
|
}
|
|
else
|
|
{
|
|
hnd.meats_end.nextmeat = m;
|
|
m.prevmeat = hnd.meats_end;
|
|
hnd.meats_end = m;
|
|
}
|
|
while ( hnd.meats && (swwm_maxgibs >= 0) && (hnd.meats_cnt > swwm_maxgibs) )
|
|
DeQueueMeat(hnd.meats);
|
|
}
|
|
static void DeQueueMeat( mkFlyingGib m )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd || !hnd.meats ) return;
|
|
if ( (hnd.meats != m) && !m.prevmeat && !m.nextmeat ) return;
|
|
hnd.meats_cnt--;
|
|
if ( !m.prevmeat ) hnd.meats = m.nextmeat;
|
|
else m.prevmeat.nextmeat = m.nextmeat;
|
|
if ( m == hnd.meats_end ) hnd.meats_end = m.prevmeat;
|
|
if ( m.nextmeat ) m.nextmeat.prevmeat = m.prevmeat;
|
|
m.killme = true;
|
|
m.prevmeat = null;
|
|
m.nextmeat = null;
|
|
}
|
|
|
|
static void HealthFlash( int p )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd || (p == -1) ) return;
|
|
hnd.hflash[p] = gametic+5;
|
|
}
|
|
|
|
static void ArmorFlash( int p )
|
|
{
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd || (p == -1) ) return;
|
|
hnd.aflash[p] = gametic+5;
|
|
}
|
|
|
|
static int AddOneliner( String type, int level, int delay = 5 )
|
|
{
|
|
// only Demolitionist can play voice lines
|
|
if ( !(players[consoleplayer].mo is 'Demolitionist') )
|
|
return 0;
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd ) return 0;
|
|
String voicetype = CVar.GetCVar('swwm_voicetype',players[consoleplayer]).GetString();
|
|
// suppress non-rage comments when ragekit is active, only screaming allowed
|
|
if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") ) return 0;
|
|
int whichline;
|
|
String testme = String.Format("SWWM_SUBS_%s_N%s",voicetype.MakeUpper(),type.MakeUpper());
|
|
String locme = StringTable.Localize(testme,false);
|
|
int countem;
|
|
if ( testme == locme ) countem = 0;
|
|
else countem = locme.ToInt();
|
|
if ( countem == 0 ) return 0; // voicepack doesn't have this
|
|
// check last line so we don't repeat
|
|
int last = 0, ent;
|
|
for ( int i=0; i<hnd.lastlines.Size(); i++ )
|
|
{
|
|
if ( hnd.lastlines[i].type != type ) continue;
|
|
last = hnd.lastlines[i].lineno;
|
|
ent = i;
|
|
break;
|
|
}
|
|
if ( countem == 1 ) whichline = 1;
|
|
else if ( last > 0 )
|
|
{
|
|
whichline = Random[DemoLines](1,countem-1);
|
|
if ( whichline >= last ) whichline++;
|
|
hnd.lastlines[ent].lineno = whichline;
|
|
}
|
|
else
|
|
{
|
|
whichline = Random[DemoLines](1,countem);
|
|
let lst = new("LastLine");
|
|
lst.type = type;
|
|
lst.lineno = whichline;
|
|
hnd.lastlines.Push(lst);
|
|
}
|
|
hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),whichline);
|
|
hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype,type,whichline);
|
|
hnd.onelinertic = gametic+delay;
|
|
hnd.onelinerspan = int(S_GetLength(hnd.onelinersnd)*Thinker.TICRATE);
|
|
hnd.onelinerlevel = level;
|
|
return hnd.onelinertic+hnd.onelinerspan;
|
|
}
|
|
|
|
override void OnRegister()
|
|
{
|
|
// oneliner RNG must be relative to consoleplayer
|
|
SetRandomSeed[DemoLines](Random[DemoLines]()+consoleplayer+MSTime());
|
|
}
|
|
|
|
private static Vector3 UseLinePos( Line l )
|
|
{
|
|
Vector3 al, ah, bl, bh;
|
|
if ( !l.sidedef[1] )
|
|
{
|
|
// just the whole line
|
|
al = (l.v1.p,l.frontsector.floorplane.ZatPoint(l.v1.p));
|
|
ah = (l.v1.p,l.frontsector.ceilingplane.ZatPoint(l.v1.p));
|
|
bl = (l.v2.p,l.frontsector.floorplane.ZatPoint(l.v2.p));
|
|
bh = (l.v2.p,l.frontsector.ceilingplane.ZatPoint(l.v2.p));
|
|
return (al+ah+bl+bh)*.25;
|
|
}
|
|
SecPlane highestfloor, lowestfloor, lowestceiling, highestceiling;
|
|
if ( (l.frontsector.floorplane.ZatPoint(l.v1.p) > l.backsector.floorplane.ZatPoint(l.v1.p))
|
|
&& (l.frontsector.floorplane.ZatPoint(l.v2.p) > l.backsector.floorplane.ZatPoint(l.v2.p)) )
|
|
{
|
|
highestfloor = l.frontsector.floorplane;
|
|
lowestfloor = l.backsector.floorplane;
|
|
}
|
|
else
|
|
{
|
|
highestfloor = l.backsector.floorplane;
|
|
lowestfloor = l.frontsector.floorplane;
|
|
}
|
|
if ( (l.frontsector.ceilingplane.ZatPoint(l.v1.p) < l.backsector.ceilingplane.ZatPoint(l.v1.p))
|
|
&& (l.frontsector.ceilingplane.ZatPoint(l.v2.p) < l.backsector.ceilingplane.ZatPoint(l.v2.p)) )
|
|
{
|
|
lowestceiling = l.frontsector.ceilingplane;
|
|
highestceiling = l.backsector.ceilingplane;
|
|
}
|
|
else
|
|
{
|
|
lowestceiling = l.backsector.ceilingplane;
|
|
highestceiling = l.frontsector.ceilingplane;
|
|
}
|
|
// try to guess what the part that triggers this is
|
|
if ( l.Activation&SPAC_Cross )
|
|
{
|
|
// pick the "intersection"
|
|
al = (l.v1.p,highestfloor.ZatPoint(l.v1.p));
|
|
ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p));
|
|
bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p));
|
|
bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p));
|
|
return (al+ah+bl+bh)*.25;
|
|
}
|
|
// check if lower part available
|
|
al = (l.v1.p,lowestfloor.ZatPoint(l.v1.p));
|
|
ah = (l.v1.p,highestfloor.ZatPoint(l.v1.p));
|
|
bl = (l.v2.p,lowestfloor.ZatPoint(l.v2.p));
|
|
bh = (l.v2.p,highestfloor.ZatPoint(l.v2.p));
|
|
if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) )
|
|
return (al+ah+bl+bh)*.25;
|
|
// check if upper part available
|
|
al = (l.v1.p,lowestceiling.ZatPoint(l.v1.p));
|
|
ah = (l.v1.p,highestceiling.ZatPoint(l.v1.p));
|
|
bl = (l.v2.p,lowestceiling.ZatPoint(l.v2.p));
|
|
bh = (l.v2.p,highestceiling.ZatPoint(l.v2.p));
|
|
if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) )
|
|
return (al+ah+bl+bh)*.25;
|
|
// check for 3d floors
|
|
bool floorfound = false;
|
|
Vector3 fal, fah, fbl, fbh;
|
|
for ( int i=0; i<l.backsector.Get3DFloorCount(); i++ )
|
|
{
|
|
let ff = l.backsector.Get3DFloor(i);
|
|
fal = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
|
fah = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
|
fbl = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
|
fbh = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
|
// skip if higher, we'll go with the lowest 3d floor (may not be right, but whatever)
|
|
if ( floorfound && (fah.z > ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue;
|
|
al = fal;
|
|
ah = fah;
|
|
bl = fbl;
|
|
bh = fbh;
|
|
floorfound = true;
|
|
}
|
|
if ( floorfound ) return (al+ah+bl+bh)*.25;
|
|
for ( int i=0; i<l.frontsector.Get3DFloorCount(); i++ )
|
|
{
|
|
let ff = l.frontsector.Get3DFloor(i);
|
|
fal = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
|
fah = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
|
fbl = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
|
fbh = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
|
// skip if higher, we'll go with the lowest 3d floor (may not be right, but whatever)
|
|
if ( floorfound && (fah.z > ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue;
|
|
al = fal;
|
|
ah = fah;
|
|
bl = fbl;
|
|
bh = fbh;
|
|
floorfound = true;
|
|
}
|
|
if ( floorfound ) return (al+ah+bl+bh)*.25;
|
|
// check for midtex
|
|
if ( !l.sidedef[0].GetTexture(1).IsNull() )
|
|
{
|
|
double ofs = l.sidedef[0].GetTextureYOffset(1);
|
|
Vector2 siz = TexMan.GetScaledSize(l.sidedef[0].GetTexture(1));
|
|
Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[0].GetTexture(1));
|
|
ofs += tofs.y;
|
|
ofs *= l.sidedef[0].GetTextureYScale(1);
|
|
siz.y *= l.sidedef[0].GetTextureYScale(1);
|
|
if ( l.flags&Line.ML_DONTPEGBOTTOM )
|
|
{
|
|
al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs);
|
|
bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs);
|
|
ah = al+(0,0,siz.y);
|
|
bh = bl+(0,0,siz.y);
|
|
}
|
|
else
|
|
{
|
|
ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs);
|
|
bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs);
|
|
al = ah-(0,0,siz.y);
|
|
bl = bh-(0,0,siz.y);
|
|
}
|
|
return (al+ah+bl+bh)*.25;
|
|
}
|
|
if ( !l.sidedef[1].GetTexture(1).IsNull() )
|
|
{
|
|
double ofs = l.sidedef[1].GetTextureYOffset(1);
|
|
Vector2 siz = TexMan.GetScaledSize(l.sidedef[1].GetTexture(1));
|
|
Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[1].GetTexture(1));
|
|
ofs += tofs.y;
|
|
ofs *= l.sidedef[1].GetTextureYScale(1);
|
|
siz.y *= l.sidedef[1].GetTextureYScale(1);
|
|
if ( l.flags&Line.ML_DONTPEGBOTTOM )
|
|
{
|
|
al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs);
|
|
bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs);
|
|
ah = al+(0,0,siz.y);
|
|
bh = bl+(0,0,siz.y);
|
|
}
|
|
else
|
|
{
|
|
ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs);
|
|
bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs);
|
|
al = ah-(0,0,siz.y);
|
|
bl = bh-(0,0,siz.y);
|
|
}
|
|
return (al+ah+bl+bh)*.25;
|
|
}
|
|
// just use the intersection
|
|
al = (l.v1.p,highestfloor.ZatPoint(l.v1.p));
|
|
ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p));
|
|
bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p));
|
|
bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p));
|
|
return (al+ah+bl+bh)*.25;
|
|
}
|
|
|
|
static clearscope void ClearAllShaders( PlayerInfo p )
|
|
{
|
|
Shader.SetEnabled(p,"WaterWarp",false);
|
|
Shader.SetEnabled(p,"LavaWarp",false);
|
|
Shader.SetEnabled(p,"SlimeWarp",false);
|
|
Shader.SetEnabled(p,"ZoomBlur",false);
|
|
Shader.SetEnabled(p,"RagekitShader",false);
|
|
Shader.SetEnabled(p,"GhostShader",false);
|
|
Shader.SetEnabled(p,"InvinciShader",false);
|
|
Shader.SetEnabled(p,"Glitch",false);
|
|
Shader.SetEnabled(p,"Grain",false);
|
|
}
|
|
|
|
// level end stats
|
|
override void WorldUnloaded( WorldEvent e )
|
|
{
|
|
if ( !(gameinfo.gametype&GAME_STRIFE) )
|
|
{
|
|
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
|
|
SWWMStats s;
|
|
while ( s = SWWMStats(ti.Next()) )
|
|
{
|
|
int clust = 0;
|
|
bool secret = false;
|
|
if ( SWWMUtility.IsEviternity() )
|
|
{
|
|
// we have to do some heavy lifting here because episodes don't match clusters
|
|
if ( level.levelnum <= 5 ) clust = 1;
|
|
else if ( level.levelnum <= 10 ) clust = 2;
|
|
else if ( level.levelnum <= 15 ) clust = 3;
|
|
else if ( level.levelnum <= 20 ) clust = 4;
|
|
else if ( level.levelnum <= 25 ) clust = 5;
|
|
else if ( level.levelnum <= 30 ) clust = 6;
|
|
else if ( level.levelnum <= 32 )
|
|
{
|
|
secret = true;
|
|
if ( level.levelnum <= 31 ) clust = 7;
|
|
else clust = 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (gameinfo.gametype&GAME_DOOM) && ((level.cluster == 9) || (level.cluster == 10)) )
|
|
secret = true;
|
|
clust = level.cluster;
|
|
}
|
|
int csiz = s.clustervisit.Size();
|
|
if ( csiz == 0 )
|
|
{
|
|
s.clustervisit.Push(clust);
|
|
s.secretdone.Push(secret);
|
|
}
|
|
else if ( s.clustervisit[csiz-1] != clust )
|
|
{
|
|
s.clustervisit.Push(clust);
|
|
s.secretdone.Push(secret|s.secretdone[csiz-1]);
|
|
}
|
|
s.AddLevelStats();
|
|
}
|
|
}
|
|
ClearAllShaders(players[consoleplayer]);
|
|
}
|
|
|
|
override void WorldLoaded( WorldEvent e )
|
|
{
|
|
if ( level.levelname ~== "Modder Test Map" )
|
|
{
|
|
level.ReplaceTextures("-noflat-","kinstile",0);
|
|
S_ChangeMusic("music/CARDISH1.XM");
|
|
}
|
|
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
|
|
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) )
|
|
AddOneliner("mapstart",3);
|
|
if ( !e.IsSaveGame && !e.IsReopen )
|
|
{
|
|
// for skipping over merged exit lines (sharing vertices)
|
|
Array<Line> skipme;
|
|
skipme.Clear();
|
|
// find exit lines, and use lines that aren't exits
|
|
for ( int i=0; i<level.lines.Size(); i++ )
|
|
{
|
|
Line l = level.lines[i];
|
|
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;
|
|
}
|
|
// horrendous piece of shit bandaid fix because gzdoom gets on my fucking nerves with its inconsistency bullshit
|
|
let ti = ThinkerIterator.Create("SWWMLoreLibrary",Thinker.STAT_STATIC);
|
|
SWWMLoreLibrary l2;
|
|
bool bmesg = false;
|
|
while ( l2 = SWWMLoreLibrary(ti.Next()) )
|
|
{
|
|
if ( (l2 == l) || (l2.myplayer != p) ) continue;
|
|
if ( !bmesg )
|
|
{
|
|
Console.Printf("If these messages appear, something broke and should be reported.");
|
|
bmesg = true;
|
|
}
|
|
// merge excess libraries (if it ever happens)
|
|
Console.Printf("Merging existing lore library with %d entries.",l2.ent.Size());
|
|
for ( int i=0; i<l2.ent.Size(); i++ )
|
|
{
|
|
bool dontadd = false;
|
|
for ( int j=0; j<l.ent.Size(); j++ )
|
|
{
|
|
if ( l.ent[j].tag != l2.ent[i].tag ) continue;
|
|
l.ent[j].read |= l2.ent[i].read;
|
|
dontadd = true;
|
|
}
|
|
if ( dontadd ) continue;
|
|
l.ent.Push(l2.ent[i]);
|
|
}
|
|
l2.Destroy();
|
|
}
|
|
// 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");
|
|
}
|
|
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;
|
|
}
|
|
|
|
override void PlayerRespawned( PlayerEvent e )
|
|
{
|
|
PlayerEntered(e);
|
|
}
|
|
|
|
override void WorldThingRevived( WorldEvent e )
|
|
{
|
|
// reattach combat tracker
|
|
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
|
|
SWWMCombatTracker.Spawn(e.Thing);
|
|
// reattach headpats
|
|
if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing)
|
|
|| SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) )
|
|
{
|
|
// you can pet the dog, and you can also pet the caco (and friends)
|
|
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
|
|
hp.target = e.Thing;
|
|
}
|
|
if ( !(e.Thing is 'PlayerPawn') ) return;
|
|
// reset some vars
|
|
if ( e.Thing.playernumber() != -1 )
|
|
{
|
|
multilevel[e.Thing.playernumber()] = 0;
|
|
spreecount[e.Thing.playernumber()] = 0;
|
|
tookdamage[e.Thing.playernumber()] = false;
|
|
lastkill[e.Thing.playernumber()] = int.min;
|
|
}
|
|
// reset uptime since player had just died
|
|
SWWMStats s = SWWMStats.Find(e.Thing.player);
|
|
if ( s ) s.lastspawn = level.totaltime;
|
|
}
|
|
|
|
private void LangRefresh()
|
|
{
|
|
if ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]);
|
|
if ( !funtags ) funtags = CVar.GetCVar('swwm_funtags',players[consoleplayer]);
|
|
if ( (lang.GetString() != curlang) || (funtags.GetBool() != curfuntags) )
|
|
{
|
|
// manually refresh some tags if language has changed
|
|
for ( SWWMCombatTracker t=trackers; t; t=t.next )
|
|
t.UpdateTag();
|
|
for ( SWWMInterest p=intpoints; p; p=p.next )
|
|
{
|
|
if ( (p.type != INT_Key) || !p.trackedkey ) continue;
|
|
p.keytag = p.trackedkey.GetTag();
|
|
}
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !Demolitionist(players[i].mo) ) continue;
|
|
for ( SWWMItemSense s=Demolitionist(players[i].mo).itemsense; s; s=s.next )
|
|
s.UpdateTag();
|
|
}
|
|
}
|
|
curlang = lang.GetString();
|
|
curfuntags = funtags.GetBool();
|
|
}
|
|
|
|
private void QueueMaintenance()
|
|
{
|
|
if ( swwm_maxcasings != oldmaxcasings )
|
|
{
|
|
while ( casings && (swwm_maxcasings >= 0) && (casings_cnt > swwm_maxcasings) )
|
|
DeQueueCasing(casings);
|
|
}
|
|
if ( swwm_maxdebris != oldmaxdebris )
|
|
{
|
|
while ( chips && (swwm_maxdebris >= 0) && (chips_cnt > swwm_maxdebris) )
|
|
DeQueueChip(chips);
|
|
}
|
|
if ( swwm_maxblood != oldmaxblood )
|
|
{
|
|
while ( blods && (swwm_maxblood >= 0) && (blods_cnt > swwm_maxblood) )
|
|
DeQueueBlod(blods);
|
|
}
|
|
if ( swwm_maxgibs != oldmaxgibs )
|
|
{
|
|
while ( meats && (swwm_maxgibs >= 0) && (meats_cnt > swwm_maxgibs) )
|
|
DeQueueMeat(meats);
|
|
}
|
|
oldmaxcasings = swwm_maxcasings;
|
|
oldmaxdebris = swwm_maxdebris;
|
|
oldmaxblood = swwm_maxblood;
|
|
oldmaxgibs = swwm_maxgibs;
|
|
}
|
|
|
|
// countable item scoring
|
|
private void ItemCountTrack()
|
|
{
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] ) continue;
|
|
if ( players[i].itemcount > lastitemcount[i] )
|
|
{
|
|
int score = 10*(players[i].itemcount-lastitemcount[i]);
|
|
if ( (level.total_items == level.found_items) && !allitems )
|
|
{
|
|
allitems = true;
|
|
Console.Printf(StringTable.Localize("$SWWM_LASTITEM"),players[i].GetUserName(),500);
|
|
score += 490;
|
|
}
|
|
SWWMCredits.Give(players[i],score);
|
|
SWWMScoreObj.Spawn(score,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
|
|
lastitemcount[i] = players[i].itemcount;
|
|
let s = SWWMStats.Find(players[i]);
|
|
s.items++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// combat tracking
|
|
private void CombatTrack()
|
|
{
|
|
// prune old entries
|
|
for ( int i=0; i<combatactors.Size(); i++ )
|
|
{
|
|
if ( combattics[i] > highesttic )
|
|
highesttic = combattics[i];
|
|
if ( combatactors[i]
|
|
&& (combatactors[i].Health > 0)
|
|
&& !combatactors[i].bKILLED
|
|
&& !combatactors[i].bCORPSE
|
|
&& (combatactors[i].target == players[consoleplayer].mo)
|
|
&& (combattics[i]+2000 > gametic) )
|
|
continue;
|
|
combatactors.Delete(i);
|
|
combattics.Delete(i);
|
|
i--;
|
|
}
|
|
bool enteredcombat = false;
|
|
// add new entries
|
|
let ti = ThinkerIterator.Create("Actor");
|
|
Actor a;
|
|
while ( a = Actor(ti.Next()) )
|
|
{
|
|
if ( !a.player && !a.bISMONSTER ) continue;
|
|
// ignore the dead
|
|
if ( (a.Health <= 0) || a.bKILLED || a.bCORPSE ) continue;
|
|
// ignore friends
|
|
if ( a.IsFriend(players[consoleplayer].mo) ) continue;
|
|
// [Strife] ignore if not in combat
|
|
if ( (gameinfo.gametype&GAME_Strife) && !a.bINCOMBAT && !a.bJUSTATTACKED ) continue;
|
|
// [Strife] ignore certain classes
|
|
if ( (a is 'RatBuddy') || (a is 'Peasant') || (a is 'Beggar') ) continue;
|
|
// [Strife] ignore Oracle's spectre while it's inactive
|
|
if ( (a is 'AlienSpectre3') && a.InStateSequence(a.CurState,a.FindState("Spawn")) ) continue;
|
|
// ignore if not targetted or player can't see it
|
|
if ( (a.target != players[consoleplayer].mo)
|
|
|| !SWWMUtility.InPlayerFOV(players[consoleplayer],a) ) continue;
|
|
// [HDoom] ignore cute girls
|
|
if ( SWWMHDoomHandler.IsCuteGirl(a.target) ) continue;
|
|
// is it already in?
|
|
bool addme = true;
|
|
for ( int i=0; i<combatactors.Size(); i++ )
|
|
{
|
|
if ( combatactors[i] != a ) continue;
|
|
addme = false;
|
|
combattics[i] = gametic;
|
|
break;
|
|
}
|
|
// add it in
|
|
if ( addme )
|
|
{
|
|
combatactors.Push(a);
|
|
combattics.Push(gametic);
|
|
enteredcombat = true;
|
|
}
|
|
}
|
|
if ( enteredcombat && (!highesttic || (gametic > highesttic+700)) )
|
|
lastcombat = AddOneliner("fightstart",1,10);
|
|
}
|
|
|
|
private void OneHundredPercentCheck()
|
|
{
|
|
// ignore levels that have NOTHING
|
|
if ( (level.total_secrets <= 0)
|
|
&& (level.total_items <= 0)
|
|
&& (level.total_monsters <= 0) ) return;
|
|
if ( mapclear ) return;
|
|
if ( (level.found_secrets < level.total_secrets)
|
|
|| (level.found_items < level.total_items)
|
|
|| (level.killed_monsters < level.total_monsters) ) return;
|
|
mapclear = true;
|
|
Console.Printf(StringTable.Localize("$SWWM_ALLCLEAR"),5000);
|
|
S_StartSound("misc/wow",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
|
|
if ( swwm_silencemap == 1 ) S_ChangeMusic("",force:true);
|
|
else if ( swwm_silencemap > 1 ) S_ChangeMusic("music/olg.ogg",force:true);
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo ) continue;
|
|
let f = Actor.Spawn("PartyTime",players[i].mo.pos);
|
|
f.bAMBUSH = true;
|
|
SWWMCredits.Give(players[i],5000);
|
|
SWWMScoreObj.Spawn(5000,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
|
|
}
|
|
}
|
|
|
|
override void WorldTick()
|
|
{
|
|
LangRefresh();
|
|
QueueMaintenance();
|
|
if ( !(gameinfo.gametype&GAME_STRIFE) && !mnotify && (level.maptime >= 5) )
|
|
{
|
|
mnotify = true;
|
|
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
|
|
SWWMStats s;
|
|
while ( s = SWWMStats(ti.Next()) )
|
|
{
|
|
if ( !SWWMUtility.IsKnownMap() ) break;
|
|
if ( s.myplayer != players[consoleplayer] ) continue;
|
|
int clust = level.cluster;
|
|
if ( SWWMUtility.IsEviternity() )
|
|
{
|
|
// we have to do some heavy lifting here because episodes don't match clusters
|
|
if ( level.levelnum <= 5 ) clust = 1;
|
|
else if ( level.levelnum <= 10 ) clust = 2;
|
|
else if ( level.levelnum <= 15 ) clust = 3;
|
|
else if ( level.levelnum <= 20 ) clust = 4;
|
|
else if ( level.levelnum <= 25 ) clust = 5;
|
|
else if ( level.levelnum <= 30 ) clust = 6;
|
|
else if ( level.levelnum <= 31 ) clust = 7;
|
|
else if ( level.levelnum <= 32 ) clust = 8;
|
|
}
|
|
int csiz = s.clustervisit.Size();
|
|
if ( (csiz > 0) && (s.clustervisit[csiz-1] != clust) )
|
|
Console.Printf(StringTable.Localize("$SWWM_NEWMISSION"));
|
|
}
|
|
}
|
|
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
|
|
if ( onelinertic && (onelinertic < gametic) )
|
|
{
|
|
if ( players[consoleplayer].health > 0 )
|
|
{
|
|
if ( onelinerlevel > mutevoice.GetInt() )
|
|
players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE);
|
|
SendNetworkEvent("swwmremoteliner."..onelinersnd,consoleplayer,onelinerlevel);
|
|
}
|
|
onelinertic = 0;
|
|
onelinerspan = 0;
|
|
}
|
|
for ( int i=0; i<flashes.size(); i++ )
|
|
{
|
|
if ( flashes[i].tic >= gametic ) continue;
|
|
flashes.Delete(i);
|
|
i--;
|
|
}
|
|
ItemCountTrack();
|
|
CombatTrack();
|
|
OneHundredPercentCheck();
|
|
if ( initialized ) return;
|
|
// wait until bosses are active
|
|
for ( int i=0; i<bossactors.Size(); i++ )
|
|
{
|
|
if ( !bossactors[i] ) continue;
|
|
if ( (!bossactors[i].target || !bossactors[i].CheckSight(bossactors[i].target,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY))
|
|
&& (!bossviewactor || (bossviewactor && !bossviewactor.target)) ) continue;
|
|
initialized = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private bool HexenMap40()
|
|
{
|
|
if ( level.GetChecksum() ~== "2A6C4235B942467D25FD50D5B313E67A" ) return true; // 1.1
|
|
if ( level.GetChecksum() ~== "1C5DE5A921DEE405E98E7E09D9829387" ) return true; // 1.0
|
|
if ( level.GetChecksum() ~== "EFAFE59092DE5E613562ACF52B86C37F" ) return true; // beta
|
|
return false;
|
|
}
|
|
|
|
private static bool ShouldSpawnGold()
|
|
{
|
|
int totalneeded = 0;
|
|
// check "free space" in player inventories
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo ) continue;
|
|
let cg = players[i].mo.FindInventory("GoldShell");
|
|
if ( cg ) totalneeded += cg.MaxAmount-cg.Amount;
|
|
else totalneeded = GetDefaultByType("GoldShell").MaxAmount;
|
|
}
|
|
// subtract any shells already in the world
|
|
let ti = ThinkerIterator.Create("GoldShell");
|
|
GoldShell g;
|
|
while ( g = GoldShell(ti.Next()) )
|
|
{
|
|
if ( g.Owner ) continue;
|
|
totalneeded -= g.Amount;
|
|
}
|
|
return (totalneeded > 0);
|
|
}
|
|
|
|
override void WorldThingDied( WorldEvent e )
|
|
{
|
|
if ( e.Thing.default.bISMONSTER && ((e.Thing.default.bBOSS) || (e.Thing.GetSpawnHealth() >= 1000)) && (alreadygold.Find(e.Thing) == alreadygold.Size()) )
|
|
{
|
|
// make sure we can't farm drops from revivable enemies
|
|
// (or cause some things to spam-spawn gold shells)
|
|
// (*cough* Touhou Doom *cough*)
|
|
alreadygold.Push(e.Thing);
|
|
// weight drop chance based on total count of this monster type
|
|
// guarantees that maps that get a bit slaughtery won't become easy farms for drops
|
|
int dropweight = 0;
|
|
let ti = ThinkerIterator.Create(e.Thing.GetClass());
|
|
while ( ti.Next() ) dropweight++;
|
|
int minchance = max(1,6-(e.Thing.GetSpawnHealth()/1000));
|
|
dropweight = max(minchance,dropweight/4);
|
|
// make sure the gold shell is "worth spawning", too
|
|
if ( !Random[GoldDrop](0,dropweight) && ShouldSpawnGold() )
|
|
{
|
|
let g = Actor.Spawn("GoldShell",e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
|
|
double ang = FRandom[SpareShells](0,360);
|
|
g.vel.xy = (cos(ang),sin(ang))*FRandom[SpareShells](.4,.8);
|
|
g.vel.z = FRandom[SpareShells](2.,4.);
|
|
}
|
|
}
|
|
// Korax instakill
|
|
if ( (e.Thing is 'Korax') && !e.Thing.special2 && HexenMap40() )
|
|
{
|
|
e.Thing.special2 = 1;
|
|
// terminate the monster closet scripts, open all the
|
|
// doors ourselves
|
|
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,220);
|
|
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,220);
|
|
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,221);
|
|
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,255);
|
|
level.ExecuteSpecial(Door_Open,e.Thing,null,false,10,16);
|
|
level.ExecuteSpecial(Door_Open,e.Thing,null,false,11,16);
|
|
level.ExecuteSpecial(Door_Open,e.Thing,null,false,12,16);
|
|
level.ExecuteSpecial(Door_Open,e.Thing,null,false,13,16);
|
|
level.ExecuteSpecial(Door_Open,e.Thing,null,false,14,16);
|
|
level.ExecuteSpecial(Door_Open,e.Thing,null,false,10,16);
|
|
// keep the portal closed, you can't leave unless you
|
|
// kill everyone else
|
|
let t = new("UglyBoyGetsFuckedUp");
|
|
t.ChangeStatNum(Thinker.STAT_USER);
|
|
}
|
|
if ( swwm_partytime )
|
|
{
|
|
let pt = Actor.Spawn("PartyTime",e.Thing.pos);
|
|
pt.target = e.Thing;
|
|
}
|
|
// force insert gib animations on some vanilla Doom monsters
|
|
int gibhealth = e.Thing.GetGibHealth();
|
|
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
|
|
if ( !gotgibbed ) return;
|
|
if ( (e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") )
|
|
ExtraGibDeaths.GibThis(e.Thing,"DemonXDeath");
|
|
else if ( e.Thing.GetClass() == "HellKnight" )
|
|
ExtraGibDeaths.GibThis(e.Thing,"KnightXDeath");
|
|
else if ( e.Thing.GetClass() == "BaronOfHell" )
|
|
ExtraGibDeaths.GibThis(e.Thing,"BaronXDeath");
|
|
else if ( e.Thing.GetClass() == "Cacodemon" )
|
|
ExtraGibDeaths.GibThis(e.Thing,"CacoXDeath");
|
|
else if ( e.Thing.GetClass() == "Revenant" )
|
|
ExtraGibDeaths.GibThis(e.Thing,"BonerXDeath");
|
|
else if ( e.Thing.GetClass() == "Archvile" )
|
|
ExtraGibDeaths.GibThis(e.Thing,"VileXDeath");
|
|
}
|
|
|
|
// gibbing
|
|
private void DoGibThing( WorldEvent e )
|
|
{
|
|
// no gib if it was erased
|
|
if ( e.DamageType == 'Ynykron' ) return;
|
|
int gibhealth = e.Thing.GetGibHealth();
|
|
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
|
|
bool forcegibbed = false;
|
|
// force gib availability for some vanilla Doom monsters
|
|
if ( gotgibbed && ((e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "Cacodemon") || (e.Thing.GetClass() == "Revenant") || (e.Thing.GetClass() == "Archvile")) )
|
|
forcegibbed = true;
|
|
if ( !e.Thing.FindState("XDeath",true) && !e.Thing.FindState("Death.Extreme",true) && !forcegibbed )
|
|
gotgibbed = false;
|
|
// only do special handling if they use our blood
|
|
if ( (e.Thing.GetBloodType(0) != "mkBlood") || e.Thing.bNOBLOOD )
|
|
return;
|
|
CorpseFallTracker.TrackBody(e.Thing);
|
|
bool b;
|
|
Actor a;
|
|
// special handling of some monsters
|
|
if ( e.Thing.GetClass() == "Cyberdemon" )
|
|
{
|
|
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
|
|
if ( !b ) return;
|
|
mkGibber(a).gibbed = e.Thing;
|
|
mkGibber(a).delay = 40;
|
|
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
|
|
return;
|
|
}
|
|
else if ( e.Thing.GetClass() == "SpiderMastermind" )
|
|
{
|
|
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
|
|
if ( !b ) return;
|
|
mkGibber(a).gibbed = e.Thing;
|
|
mkGibber(a).delay = 60;
|
|
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
|
|
}
|
|
else if ( gotgibbed )
|
|
{
|
|
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
|
|
if ( !b ) return;
|
|
mkGibber(a).gibbed = e.Thing;
|
|
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
|
|
}
|
|
}
|
|
|
|
// damage numbers, combat tracking, etc.
|
|
private void DoDamageHandling( WorldEvent e )
|
|
{
|
|
if ( !accdamage ) accdamage = CVar.GetCVar('swwm_accdamage',players[consoleplayer]);
|
|
bool spawnme = true;
|
|
if ( accdamage.GetBool() )
|
|
{
|
|
// find existing damage number
|
|
for ( SWWMScoreObj d=damnums; d; d=d.next )
|
|
{
|
|
if ( (d.starttic < level.maptime) || (d.acc != e.Thing) ) continue;
|
|
if ( d.score-e.Damage > d.score ) d.score = int.min;
|
|
else d.score -= e.Damage;
|
|
spawnme = false;
|
|
break;
|
|
}
|
|
}
|
|
if ( spawnme ) SWWMScoreObj.Spawn(-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),Font.CR_RED,e.Thing);
|
|
// update combat tracker for it
|
|
if ( !(e.Thing is 'BossBrain') )
|
|
{
|
|
for ( SWWMCombatTracker t=trackers; t; t=t.next )
|
|
{
|
|
if ( t.mytarget != e.Thing ) continue;
|
|
t.updated = level.maptime+35;
|
|
break;
|
|
}
|
|
}
|
|
// fall dmg
|
|
SWWMWhoPushedMe.SetInstigator(e.Thing,e.DamageSource);
|
|
// stats
|
|
if ( e.Thing.player )
|
|
{
|
|
tookdamage[e.Thing.PlayerNumber()] = true;
|
|
let s = SWWMStats.Find(e.Thing.player);
|
|
s.AddDamageTaken(e.Damage);
|
|
if ( e.Damage > s.toptaken ) s.toptaken = e.Damage;
|
|
}
|
|
if ( e.DamageSource && e.DamageSource.player )
|
|
{
|
|
let s = SWWMStats.Find(e.DamageSource.player);
|
|
s.AddDamageDealt(e.Damage);
|
|
if ( e.Damage > s.topdealt ) s.topdealt = e.Damage;
|
|
}
|
|
}
|
|
|
|
// combat hit chatter
|
|
private void DoCombatHit( WorldEvent e )
|
|
{
|
|
if ( (e.DamageSource.bISMONSTER || e.DamageSource.player || (e.DamageSource is 'ScriptedMarine')) && (e.Thing == players[consoleplayer].mo) && (e.Thing.Health > 0) )
|
|
{
|
|
if ( !lastcombat || (gametic > lastcombat+40) )
|
|
{
|
|
if ( (e.Thing.IsFriend(e.DamageSource) || SWWMUtility.IsCivilian(e.DamageSource)) )
|
|
lastcombat = AddOneliner("friendhit",1,10);
|
|
else if ( (!lastcombat || (gametic > lastcombat+100)) && !Random[DemoLines](0,e.DamageSource.bBOSS?2:4) && !SWWMHDoomHandler.IsCuteGirl(e.DamageSource) ) // [HDoom] don't shout at the girls
|
|
lastcombat = AddOneliner("gethit",1,15);
|
|
}
|
|
highesttic = gametic;
|
|
}
|
|
// friendly fire lines only fire up if we didn't kill them right away (because then the teamkill line should take priority)
|
|
if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player || (e.Thing is 'ScriptedMarine')) && (e.Thing.Health > 0) )
|
|
{
|
|
// make sure it's not a moth, because otherwise they won't shut up about accidentally hurting them (it happens a lot)
|
|
if ( (e.Thing.IsFriend(e.DamageSource) || SWWMUtility.IsCivilian(e.Thing)) && !(e.Thing is 'LampMoth') )
|
|
{
|
|
if ( !lastcombat || (gametic > lastcombat+40) )
|
|
lastcombat = AddOneliner("hitfriend",1,10);
|
|
highesttic = gametic;
|
|
}
|
|
}
|
|
}
|
|
|
|
// kill scoring
|
|
private void DoKillScoring( WorldEvent e )
|
|
{
|
|
// fall damage tracking hack
|
|
let src = e.DamageSource;
|
|
if ( (e.DamageType == 'Falling') && !e.DamageSource )
|
|
src = SWWMWhoPushedMe.RecallInstigator(e.Thing);
|
|
if ( (!src || !src.player || (src == e.Thing)) ) return;
|
|
let s = SWWMStats.Find(src.player);
|
|
if ( s )
|
|
{
|
|
s.kills++;
|
|
s.AddWeaponKill(e.Inflictor,e.Thing,e.DamageType);
|
|
}
|
|
if ( src == players[consoleplayer].mo )
|
|
{
|
|
highesttic = gametic;
|
|
if ( !lastcombat || (gametic > lastcombat+40) )
|
|
{
|
|
if ( e.Thing.IsFriend(src) || SWWMUtility.IsCivilian(e.Thing) )
|
|
lastcombat = AddOneliner("friendkill",1,5);
|
|
else if ( (!lastcombat || (gametic > lastcombat+100)) && !Random[DemoLines](0,e.Thing.bBOSS?2:5) && !SWWMHDoomHandler.IsCuteGirl(e.Thing) ) // [HDoom] don't shout at the girls
|
|
lastcombat = AddOneliner("scorekill",1,15);
|
|
}
|
|
}
|
|
// no credits unless it's a counted kill or marine (that isn't friendly)
|
|
if ( e.Thing.IsFriend(src) || (!e.Thing.default.bCountKill && !(e.Thing is 'ScriptedMarine')) )
|
|
return;
|
|
int pnum = src.PlayerNumber();
|
|
if ( level.maptime < (lastkill[pnum]+5*Thinker.TICRATE) )
|
|
multilevel[pnum]++;
|
|
else multilevel[pnum] = 0;
|
|
if ( s && (multilevel[pnum]+1 > s.mkill) )
|
|
s.mkill = multilevel[pnum]+1;
|
|
lastkill[pnum] = level.maptime;
|
|
// scoring
|
|
int score = min(1000,int(ceil(e.Thing.GetSpawnHealth()*.05)*10));
|
|
SWWMScoreObj scr = null;
|
|
if ( src.player == players[consoleplayer] )
|
|
scr = SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
|
|
int ofs = 0;
|
|
if ( e.DamageType == 'Push' )
|
|
{
|
|
score += 500;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
scr.xtcolor[ofs] = Font.CR_FIRE;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_SHAMEFUL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
else if ( e.DamageType == 'Buttslam' )
|
|
{
|
|
score += 200;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
scr.xtcolor[ofs] = Font.CR_FIRE;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_BUTTSLAM");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
else if ( e.DamageType == 'Love' )
|
|
{
|
|
score += 600;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
scr.xtcolor[ofs] = Font.FindFontColor('BlushPink');
|
|
scr.xstr[ofs] = StringTable.Localize((e.Thing is 'WolfensteinSS')?"$SWWM_LOVED_ALT":"$SWWM_LOVED");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
if ( (e.Damage >= e.Thing.GetSpawnHealth()*2) || (((e.Thing.Health <= e.Thing.GetGibHealth()) || (src.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageType == 'Extreme')) && !src.bNOEXTREMEDEATH && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH)) )
|
|
{
|
|
score *= 2;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
scr.xtcolor[ofs] = Font.CR_FIRE;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_OVERKILL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
score = int(score*(1.+.5*min(multilevel[pnum],16)));
|
|
if ( (multilevel[pnum] > 0) && scr )
|
|
{
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = (multilevel[pnum]>=16)?int.max:(multilevel[pnum]+1);
|
|
scr.xtcolor[ofs] = Font.CR_FIRE;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_MULTIKILL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
spreecount[pnum]++;
|
|
if ( s && (spreecount[pnum] > s.skill) && !tookdamage[pnum] )
|
|
s.skill = spreecount[pnum];
|
|
if ( !tookdamage[pnum] )
|
|
{
|
|
int spreebonus = 10*(spreecount[pnum]);
|
|
// taper off after 10x (some people go really far with these, holy fuck)
|
|
if ( spreecount[pnum] > 10 ) spreebonus = int(spreebonus*((spreecount[pnum]/10.)**.25));
|
|
score += 100+spreebonus;
|
|
if ( (spreecount[pnum] > 0) && scr )
|
|
{
|
|
scr.xscore[ofs] = spreecount[pnum];
|
|
scr.xtcolor[ofs] = Font.CR_FIRE;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_SPREEKILL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
if ( e.Thing.bBOSS )
|
|
{
|
|
score += 2000;
|
|
if ( scr )
|
|
{
|
|
scr.xscore[ofs] = 0;
|
|
scr.xtcolor[ofs] = Font.CR_FIRE;
|
|
scr.xstr[ofs] = StringTable.Localize("$SWWM_BOSSKILL");
|
|
scr.xcnt = ++ofs;
|
|
}
|
|
}
|
|
SWWMCredits.Give(src.player,score);
|
|
if ( scr ) scr.score = score; // update final score
|
|
if ( (level.killed_monsters+1 == level.total_monsters) && !allkills )
|
|
{
|
|
allkills = true;
|
|
SWWMCredits.Give(src.player,1000);
|
|
Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),src.player.GetUserName(),1000);
|
|
SWWMScoreObj.Spawn(1000,src.Vec3Offset(0,0,src.Height/2));
|
|
}
|
|
}
|
|
|
|
override void WorldThingDamaged( WorldEvent e )
|
|
{
|
|
if ( e.Damage > 0 ) DoDamageHandling(e);
|
|
if ( e.DamageSource && (e.DamageSource != e.Thing) ) DoCombatHit(e);
|
|
if ( (e.Thing.Health > 0) || e.Thing.bKilled || e.Thing.bCorpse ) return;
|
|
DoGibThing(e);
|
|
// romero hax
|
|
if ( (e.Thing is 'BossBrain') && (e.DamageType == 'Telefrag') )
|
|
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
|
|
// voodoo doll telefragging barrel hax (eviternity death exits)
|
|
if ( (e.Thing is 'ExplosiveBarrel') && (e.DamageType == 'Telefrag') && e.DamageSource.player && (e.DamageSource.player.mo != e.DamageSource) )
|
|
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
|
|
if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill && !(e.Thing is 'ScriptedMarine') ) return;
|
|
DoKillScoring(e);
|
|
}
|
|
|
|
private void DoKeyTagFix( Actor a )
|
|
{
|
|
if ( a is 'SWWMKey' ) return; // mod's custom keys are fine
|
|
if ( a is 'RedCard' ) a.SetTag("$T_REDCARD");
|
|
else if ( a is 'BlueCard' ) a.SetTag("$T_BLUECARD");
|
|
else if ( a is 'YellowCard' ) a.SetTag("$T_YELLOWCARD");
|
|
else if ( a is 'RedSkull' ) a.SetTag("$T_REDSKULL");
|
|
else if ( a is 'BlueSkull' ) a.SetTag("$T_BLUESKULL");
|
|
else if ( a is 'YellowSkull' ) a.SetTag("$T_YELLOWSKULL");
|
|
else if ( a is 'KeyYellow' ) a.SetTag("$T_YELLOWKEY");
|
|
else if ( a is 'KeyGreen' ) a.SetTag("$T_GREENKEY");
|
|
else if ( a is 'KeyBlue' ) a.SetTag("$T_BLUEKEY");
|
|
else if ( a.GetClassName() == 'KeyRed' ) a.SetTag("$T_REDKEY");
|
|
else if ( a is 'KeySteel' ) a.SetTag("$T_KEYSTEEL");
|
|
else if ( a is 'KeyCave' ) a.SetTag("$T_KEYCAVE");
|
|
else if ( a is 'KeyAxe' ) a.SetTag("$T_KEYAXE");
|
|
else if ( a is 'KeyFire' ) a.SetTag("$T_KEYFIRE");
|
|
else if ( a is 'KeyEmerald' ) a.SetTag("$T_KEYEMERALD");
|
|
else if ( a is 'KeyDungeon' ) a.SetTag("$T_KEYDUNGEON");
|
|
else if ( a is 'KeySilver' ) a.SetTag("$T_KEYSILVER");
|
|
else if ( a is 'KeyRusted' ) a.SetTag("$T_KEYRUSTED");
|
|
else if ( a is 'KeyHorn' ) a.SetTag("$T_KEYHORN");
|
|
else if ( a is 'KeySwamp' ) a.SetTag("$T_KEYSWAMP");
|
|
else if ( a is 'KeyCastle' ) a.SetTag("$T_KEYCASTLE");
|
|
}
|
|
|
|
// tempfix keys have no tags
|
|
static void KeyTagFix( Actor a )
|
|
{
|
|
let hnd = SWWMHandler(Find("SWWMHandler"));
|
|
if ( hnd ) hnd.DoKeyTagFix(a);
|
|
}
|
|
|
|
// copies the floatbob of overlapping identical items, so it doesn't look weird
|
|
private void CopyFloatBob( Actor a )
|
|
{
|
|
let bt = BlockThingsIterator.Create(a,16);
|
|
while ( bt.Next() )
|
|
{
|
|
let t = bt.Thing;
|
|
if ( !t || (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
|
|
a.floatbobphase = t.floatbobphase;
|
|
break;
|
|
}
|
|
}
|
|
|
|
override void WorldThingSpawned( WorldEvent e )
|
|
{
|
|
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));
|
|
}
|
|
else 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";
|
|
}
|
|
else 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
|
|
if ( e.Thing.bSHOOTABLE || e.Thing.bMISSILE )
|
|
SuckableActors.Push(e.Thing);
|
|
// vanilla blood color changes
|
|
if ( (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "Bishop") || (e.Thing.GetClass() == "Korax") )
|
|
{
|
|
let gb = Actor.Spawn("GreenBloodReference");
|
|
e.Thing.CopyBloodColor(gb);
|
|
gb.Destroy();
|
|
}
|
|
else if ( e.Thing.GetClass() == "Cacodemon" )
|
|
{
|
|
let bb = Actor.Spawn("BlueBloodReference");
|
|
e.Thing.CopyBloodColor(bb);
|
|
bb.Destroy();
|
|
}
|
|
else if ( (e.Thing.GetClass() == "Wizard") || (e.Thing.GetClass() == "Heresiarch") || (e.Thing.GetClass() == "Sorcerer2") )
|
|
{
|
|
let pb = Actor.Spawn("PurpleBloodReference");
|
|
e.Thing.CopyBloodColor(pb);
|
|
pb.Destroy();
|
|
}
|
|
else if ( e.Thing.GetClass() == "LostSoul" )
|
|
e.Thing.bNOBLOOD = true;
|
|
// vanilla boss stuff
|
|
bool upgrademe = swwm_upgradebosses;
|
|
int bossmap = WhichVanillaBossMap();
|
|
if ( bossmap == MAP_DE1M8 )
|
|
{
|
|
if ( e.Thing is 'BaronOfHell' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_BRUISERS";
|
|
}
|
|
else if ( bossmap == MAP_DE2M8 )
|
|
{
|
|
if ( e.Thing is 'Cyberdemon' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_CYBIE";
|
|
}
|
|
else if ( bossmap == MAP_DE3M8 )
|
|
{
|
|
if ( e.Thing is 'Spidermastermind' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 6;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_SPIDER";
|
|
}
|
|
else if ( bossmap == MAP_DE4M8 )
|
|
{
|
|
if ( e.Thing is 'Spidermastermind' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_SPIDER2";
|
|
}
|
|
else if ( bossmap == MAP_DMAP07 )
|
|
{
|
|
if ( (e.Thing is 'Fatso') || (e.Thing is 'Arachnotron') )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_DIMPLE";
|
|
}
|
|
else if ( bossmap == MAP_DMAP30 )
|
|
{
|
|
if ( e.Thing is 'BossBrain' )
|
|
{
|
|
bossbrainactor = e.Thing;
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 40; // goodbye, instakills
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
if ( e.Thing is 'BossEye' )
|
|
bossviewactor = e.Thing;
|
|
bosstag = "$BT_IOS";
|
|
}
|
|
else if ( bossmap == MAP_HE1M8_HE4M8 )
|
|
{
|
|
if ( e.Thing is 'IronLich' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_LICHES";
|
|
}
|
|
else if ( bossmap == MAP_HE2M8_HE5M8 )
|
|
{
|
|
if ( e.Thing is 'Minotaur' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3;
|
|
if ( trk ) trk.bBOSS = true;
|
|
}
|
|
bosstag = "$BT_MINOTAUR";
|
|
}
|
|
else if ( bossmap == MAP_HE3M8 )
|
|
{
|
|
if ( e.Thing is 'Sorcerer1' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_DSPARIL";
|
|
}
|
|
else if ( e.Thing is 'Sorcerer2' )
|
|
{
|
|
// second phase
|
|
bossactors.Clear();
|
|
initialized = false;
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_DSPARIL2";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_HMAP38 )
|
|
{
|
|
if ( e.Thing is 'ClericBoss' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_CLERIC";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_HMAP36 )
|
|
{
|
|
if ( e.Thing is 'FighterBoss' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_FIGHTER";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_HMAP37 )
|
|
{
|
|
if ( e.Thing is 'MageBoss' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_MAGE";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_HMAP12 )
|
|
{
|
|
if ( e.Thing is 'Dragon' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_DRAGON";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_HMAP23_HMAP27 )
|
|
{
|
|
if ( e.Thing is 'Heresiarch' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_HERESIARCH";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_HMAP40 )
|
|
{
|
|
if ( e.Thing is 'Korax' )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 10;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_KORAX";
|
|
}
|
|
}
|
|
else if ( bossmap == MAP_EVMAP30 )
|
|
{
|
|
if ( e.Thing.GetClassName() == "ArchangelusA" )
|
|
{
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_ARCHANGELUS";
|
|
}
|
|
else if ( e.Thing.GetClassName() == "ArchangelusB" )
|
|
{
|
|
// second phase
|
|
bossactors.Clear();
|
|
initialized = false;
|
|
bossactors.Push(e.Thing);
|
|
if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5;
|
|
if ( trk ) trk.bBOSS = true;
|
|
bosstag = "$BT_ARCHANGELUS";
|
|
}
|
|
}
|
|
// inflation check
|
|
if ( trk )
|
|
{
|
|
trk.maxhealth = trk.lasthealth = e.Thing.Health;
|
|
trk.intp.Reset(trk.lasthealth);
|
|
}
|
|
}
|
|
|
|
override void PostUiTick()
|
|
{
|
|
if ( (gametic == onelinertic) && (oneliner != "") && (players[consoleplayer].health > 0) )
|
|
{
|
|
int mute;
|
|
if ( mutevoice ) mute = mutevoice.GetInt();
|
|
else mute = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt(); // we can't assign the variable here since it's play scope
|
|
if ( onelinerlevel > mute )
|
|
{
|
|
let l = SWWMOneLiner.Make(oneliner,onelinerspan);
|
|
StatusBar.AttachMessage(l,-3473);
|
|
}
|
|
SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel);
|
|
}
|
|
for ( int i=0; i<flashes.size(); i++ )
|
|
{
|
|
if ( flashes[i].tic < gametic ) continue;
|
|
GenericFlash gf = new("GenericFlash").Setup(flashes[i].cam,flashes[i].c,flashes[i].duration);
|
|
StatusBar.AttachMessage(gf,0,BaseStatusBar.HUDMSGLayer_UnderHUD);
|
|
}
|
|
if ( (!ui_initialized && initialized) || (ui_initialized && !initialized) )
|
|
{
|
|
ui_initialized = true;
|
|
thealth = 0;
|
|
for ( int i=0; i<bossactors.Size(); i++ )
|
|
{
|
|
if ( !bossactors[i] ) continue;
|
|
thealth += max(0,bossactors[i].Health);
|
|
}
|
|
hmax = thealth;
|
|
for ( int i=0; i<30; i++ ) oldhealth[i] = thealth;
|
|
cummdamage = 0;
|
|
if ( !ihealth ) ihealth = DynamicValueInterpolator.Create(thealth,.1,1,1000);
|
|
else ihealth.Reset(thealth);
|
|
if ( !ihealthr ) ihealthr = DynamicValueInterpolator.Create(thealth,.5,1,1000);
|
|
else ihealthr.Reset(thealth);
|
|
return;
|
|
}
|
|
if ( !ui_initialized ) return;
|
|
// update healthbar
|
|
int newhealth = 0;
|
|
for ( int i=0; i<bossactors.Size(); i++ )
|
|
{
|
|
if ( !bossactors[i] ) continue;
|
|
newhealth += max(0,bossactors[i].Health);
|
|
}
|
|
oldhealth[0] = newhealth;
|
|
int curcumm = max(0,thealth-newhealth);
|
|
if ( curcumm > 0 )
|
|
{
|
|
cummdamage += curcumm;
|
|
lastcummtic = gametic;
|
|
}
|
|
else if ( gametic > lastcummtic+150 ) cummdamage = 0;
|
|
thealth = newhealth;
|
|
ihealthr.Update(thealth);
|
|
if ( thealth > oldhealth[29] )
|
|
for ( int i=29; i>0; i-- )
|
|
oldhealth[i] = thealth;
|
|
ihealth.Update(oldhealth[29]);
|
|
for ( int i=29; i>0; i-- )
|
|
oldhealth[i] = oldhealth[i-1];
|
|
if ( thealth > 0 ) bossalpha = min(3.,bossalpha+1./30.);
|
|
else bossalpha = max(0,bossalpha-1./50.);
|
|
}
|
|
|
|
override bool InputProcess( InputEvent e )
|
|
{
|
|
if ( (e.Type == InputEvent.TYPE_KeyDown) && (e.KeyChar >= 0x61) && (e.KeyChar <= 0x7A) )
|
|
{
|
|
// cheat code handling
|
|
String cht[] =
|
|
{
|
|
"swwmlodsofemone", "swwmdeeplore",
|
|
// SWWM Platinum cheats
|
|
"swwmimstuck", "swwmarmojumbo", "swwmdangimhealthy",
|
|
"swwmwarriorofzaemonath", "swwmpowerparp", "swwmcannotseemyhands",
|
|
"swwmreflectonme", "swwmgunzmeneeds", "swwmbloodrainsfromheaven",
|
|
"swwmnotwannaboom", "swwmverywrappyoatmeal", "swwmflaggerybingo",
|
|
"swwmheadsball", "swwmsmarties", "swwmnocilla",
|
|
"swwmmarioisaweenie", "swwmpunish", "swwmboingball",
|
|
"swwmgassy", "swwmiamsuperman", "swwmtouchstone"
|
|
};
|
|
String cmd[] =
|
|
{
|
|
"swwmmoneycheat", "swwmlorecheat",
|
|
// SWWM Platinum cheats
|
|
"swwmsafecheat", "swwmweaponcheat", "swwmhealcheat",
|
|
"swwmynykroncheat", "swwmgravcheat", "swwminvischeat",
|
|
"swwmbarriercheat", "swwmammocheat", "swwmbloodcheat",
|
|
"swwmexplocheat", "swwmallcheat", "swwmflagcheat",
|
|
"swwmballcheat", "swwmsmartcheat", "swwmnutcheat",
|
|
"swwmweeniecheat", "swwmpunishcheat", "swwmball2cheat",
|
|
"swwmfartcheat", "swwmsupercheat", "swwmstonecheat"
|
|
};
|
|
bool matchany = false;
|
|
kstr.AppendCharacter(e.KeyChar);
|
|
if ( kstr.Length() > 0 )
|
|
{
|
|
for ( int i=0; i<cht.Size(); i++ )
|
|
{
|
|
if ( kstr != cht[i].Left(kstr.length()) ) continue;
|
|
matchany = true;
|
|
if ( kstr != cht[i] ) continue;
|
|
SendNetworkEvent(cmd[i],consoleplayer);
|
|
kcode = 0;
|
|
kstr = "";
|
|
return true;
|
|
}
|
|
if ( !matchany )
|
|
{
|
|
kcode = 0;
|
|
kstr = "";
|
|
}
|
|
else
|
|
{
|
|
kcode++;
|
|
if ( kcode > 4 ) return true; // eat keypresses from this point
|
|
}
|
|
}
|
|
// F
|
|
if ( e.KeyChar == 0x66 )
|
|
{
|
|
let demo = Demolitionist(players[consoleplayer].mo);
|
|
let gone = PlayerGone(players[consoleplayer].mo);
|
|
if ( (demo && (demo.Health <= 0) && (demo.deadtimer > 40))
|
|
|| (gone && (gone.Health <= 0) && (gone.deadtimer > 40)) )
|
|
{
|
|
// pay respects
|
|
int numf = Random[FInTheChat](1,6);
|
|
for ( int i=0; i<numf; i++ )
|
|
{
|
|
let f = PayRespects.PressF();
|
|
StatusBar.AttachMessage(f,0,layer:StatusBar.HUDMSGLayer_OverHUD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override void WorldLinePreActivated( WorldEvent e )
|
|
{
|
|
// oneliner on locked doors
|
|
if ( !e.Thing ) return;
|
|
int locknum = SWWMUtility.GetLineLock(e.ActivatedLine);
|
|
if ( (locknum < 1) || (locknum > 255) ) return;
|
|
if ( e.Thing.CheckLocalView() && !e.Thing.CheckKeys(locknum,false,true) )
|
|
{
|
|
if ( !lastlock || (gametic > lastlock+20) )
|
|
{
|
|
if ( SWWMUtility.IsValidLockNum(locknum) )
|
|
lastlock = AddOneliner("locked",2);
|
|
else lastlock = AddOneliner("jammed",2);
|
|
}
|
|
}
|
|
}
|
|
|
|
override void WorldLineActivated( WorldEvent e )
|
|
{
|
|
if ( !(e.ActivationType&SPAC_Use) ) return;
|
|
if ( !e.Thing || !e.Thing.player ) return;
|
|
let w = SWWMWeapon(e.Thing.player.ReadyWeapon);
|
|
if ( !w || !w.wallponch ) return;
|
|
let s = SWWMStats.Find(e.Thing.player);
|
|
if ( s ) s.wponch++;
|
|
}
|
|
|
|
override void CheckReplacee( ReplacedEvent e )
|
|
{
|
|
if ( e.Replacement is 'DSparilHax' )
|
|
e.Replacee = 'Sorcerer2';
|
|
}
|
|
|
|
// do any players not own dual guns yet
|
|
private bool ShouldSpawnDualExpl()
|
|
{
|
|
int np = 0, ng = 0;
|
|
// check travelling items, in case this was called mid-transition
|
|
let ti = ThinkerIterator.Create("ExplodiumGun",Thinker.STAT_TRAVELLING);
|
|
ExplodiumGun g;
|
|
while ( g = ExplodiumGun(ti.Next()) )
|
|
{
|
|
np++;
|
|
if ( g.Amount > 1 ) ng++;
|
|
}
|
|
if ( np > 0 ) return (ng < np);
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo ) continue;
|
|
np++;
|
|
if ( players[i].mo.CountInv("ExplodiumGun") > 1 ) ng++;
|
|
}
|
|
return (ng < np);
|
|
}
|
|
|
|
override void CheckReplacement( ReplaceEvent e )
|
|
{
|
|
// respect final replacements
|
|
if ( e.IsFinal ) return;
|
|
// shell types (sorted by rarity
|
|
static const Class<Actor> redpool[] = {"RedShell","RedShell2","RedShell4","RedShell8"};
|
|
static const Class<Actor> greenpool[] = {"GreenShell","GreenShell2","GreenShell4"};
|
|
static const Class<Actor> whitepool[] = {"WhiteShell","WhiteShell2"};
|
|
static const Class<Actor> purplepool[] = {"PurpleShell","PurpleShell2","PurpleShell4"};
|
|
static const Class<Actor> bluepool[] = {"BlueShell","BlueShell2","BlueShell4"};
|
|
static const Class<Actor> blackpool[] = {"BlackShell","BlackShell2"};
|
|
// only replace vanilla blood if no other gore mod is doing it
|
|
if ( (e.Replacee == "Blood") && (!e.Replacement || e.Replacement == "Blood") && swwm_blood ) e.Replacement = "mkBlood";
|
|
else if ( e.Replacee is 'ItemFog' ) e.Replacement = 'SWWMItemFog';
|
|
else if ( e.Replacee is 'TeleportFog' ) e.Replacement = 'SWWMTeleportFog';
|
|
else if ( (e.Replacee is 'CommanderKeen') && (!e.Replacement || (e.Replacement == 'CommanderKeen')) )
|
|
{
|
|
let def = GetDefaultByType(e.Replacee);
|
|
bool dehackery = false;
|
|
for ( State s=def.SpawnState; s; s=s.NextState )
|
|
{
|
|
if ( s.bDEHACKED ) dehackery = true;
|
|
if ( s.NextState == s ) 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;
|
|
if ( s.NextState == s ) break;
|
|
}
|
|
if ( dehackery ) return;
|
|
e.Replacement = 'SWWMBossBrain';
|
|
}
|
|
else if ( e.Replacee is 'RedCard' )
|
|
{
|
|
if ( level.GetChecksum() ~== "3805A661D5C4523AFF7BF86991071043" )
|
|
return; // don't replace red key in Equinox MAP13
|
|
e.Replacement = 'SWWMRedCard';
|
|
}
|
|
else if ( e.Replacee is 'BlueCard' ) e.Replacement = 'SWWMBlueCard';
|
|
else if ( e.Replacee is 'YellowCard' ) e.Replacement = 'SWWMYellowCard';
|
|
else if ( e.Replacee.GetClassName() == 'KDiZDSilverKey' ) e.Replacement = 'SWWMSilverCardKDiZD';
|
|
else if ( e.Replacee.GetClassName() == 'KDiZDGreenKey' ) e.Replacement = 'SWWMGreenCardKDiZD';
|
|
else if ( e.Replacee.GetClassName() == 'KDiZDOrangeKey' ) e.Replacement = 'SWWMOrangeCardKDiZD';
|
|
else if ( e.Replacee.GetClassName() == 'GreenCard' ) e.Replacement = 'SWWMGreenCard';
|
|
else if ( e.Replacee is 'RedSkull' ) e.Replacement = 'SWWMRedSkull';
|
|
else if ( e.Replacee is 'BlueSkull' ) e.Replacement = 'SWWMBlueSkull';
|
|
else if ( e.Replacee is 'YellowSkull' ) e.Replacement = 'SWWMYellowSkull';
|
|
else if ( e.Replacee is 'KeyGreen' ) e.Replacement = 'SWWMKeyGreen';
|
|
else if ( e.Replacee is 'KeyBlue' ) e.Replacement = 'SWWMKeyBlue';
|
|
else if ( e.Replacee is 'KeyYellow' ) e.Replacement = 'SWWMKeyYellow';
|
|
else if ( e.Replacee.GetClassName() == 'KeyRed' ) e.Replacement = 'SWWMKeyRed';
|
|
else if ( (e.Replacee is 'Chainsaw') || (e.Replacee is 'Gauntlets') || (e.Replacee is 'FWeapAxe') )
|
|
{
|
|
if ( ShouldSpawnDualExpl() ) e.Replacement = Random[Replacements](0,1)?'ExplodiumGun':'PusherWeapon';
|
|
else e.Replacement = 'PusherWeapon';
|
|
}
|
|
else if ( (e.Replacee is 'Fist') || (e.Replacee is 'Staff') ) e.Replacement = 'DeepImpact';
|
|
else if ( (e.Replacee is 'Pistol') || (e.Replacee is 'GoldWand') || (e.Replacee is 'FWeapFist') || (e.Replacee is 'CWeapMace') || (e.Replacee is 'MWeapWand') ) e.Replacement = 'ExplodiumGun';
|
|
else if ( (e.Replacee is 'Shotgun') || (e.Replacee is 'CWeapStaff') ) e.Replacement = 'Spreadgun';
|
|
else if ( (e.Replacee is 'SuperShotgun') || (e.Replacee is 'MWeapFrost') ) e.Replacement = 'Wallbuster';
|
|
else if ( e.Replacee is 'Crossbow' ) e.Replacement = Random[Replacements](0,2)?'Spreadgun':'Wallbuster';
|
|
else if ( (e.Replacee is 'Chaingun') || (e.Replacee is 'Blaster') || (e.Replacee is 'FWeaponPiece3') ) e.Replacement = 'Eviscerator';
|
|
else if ( (e.Replacee is 'RocketLauncher') || (e.Replacee is 'PhoenixRod') || (e.Replacee is 'FWeapHammer') ) e.Replacement = 'Hellblazer';
|
|
else if ( (e.Replacee is 'PlasmaRifle') || (e.Replacee is 'SkullRod') ) e.Replacement = Random[Replacements](0,2)?'Sparkster':'SilverBullet';
|
|
else if ( e.Replacee is 'CWeapFlame' ) e.Replacement = 'Sparkster';
|
|
else if ( e.Replacee is 'MWeapLightning' ) e.Replacement = 'SilverBullet';
|
|
else if ( (e.Replacee is 'BFG9000') || (e.Replacee is 'Mace') ) e.Replacement = Random[Replacements](0,2)?'CandyGun':'Ynykron';
|
|
else if ( e.Replacee is 'CWeaponPiece2' ) e.Replacement = 'CandyGun';
|
|
else if ( e.Replacee is 'MWeaponPiece1' ) e.Replacement = 'Ynykron';
|
|
else if ( (e.Replacee == 'Clip') || (e.Replacee == 'GoldWandAmmo') || (e.Replacee == 'GoldWandHefty') )
|
|
{
|
|
switch( Random[Replacements](0,14) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
e.Replacement = redpool[Random[Replacements](0,1)];
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
e.Replacement = greenpool[Random[Replacements](0,1)];
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
e.Replacement = purplepool[0];
|
|
break;
|
|
default:
|
|
e.Replacement = 'SWWMNothing';
|
|
break;
|
|
}
|
|
}
|
|
else if ( (e.Replacee == 'Shell') || (e.Replacee is 'CrossbowAmmo') )
|
|
{
|
|
switch( Random[Replacements](0,13) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
e.Replacement = redpool[Random[Replacements](0,2)];
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
e.Replacement = greenpool[Random[Replacements](0,2)];
|
|
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 == 'ShellBox') || (e.Replacee is 'CrossbowHefty') )
|
|
{
|
|
switch( Random[Replacements](0,14) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
e.Replacement = redpool[Random[Replacements](1,3)];
|
|
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 == 'ClipBox' ) e.Replacement = Random[Replacements](0,3)?'EvisceratorShell':Random[Replacements](0,4)?'EvisceratorTrioSpawn':'EvisceratorSixPack';
|
|
else if ( e.Replacee == 'BlasterAmmo' ) e.Replacement = 'EvisceratorShell';
|
|
else if ( e.Replacee == 'BlasterHefty' ) e.Replacement = Random[Replacements](0,4)?'EvisceratorTrioSpawn':'EvisceratorSixPack';
|
|
else if ( (e.Replacee == 'RocketAmmo') || (e.Replacee == 'PhoenixRodAmmo') || (e.Replacee == 'MaceAmmo') ) e.Replacement = Random[Replacements](0,2)?'HellblazerMissiles':'HellblazerCrackshots';
|
|
else if ( (e.Replacee == 'RocketBox') || (e.Replacee == 'PhoenixRodHefty') || (e.Replacee == 'MaceHefty') )
|
|
{
|
|
switch ( Random[Replacements](0,11) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
if ( Random[Replacements](0,3) ) e.Replacement = 'HellblazerMissiles';
|
|
else if ( Random[Replacements](0,2) ) e.Replacement = 'HellblazerMissileTrioSpawn';
|
|
else e.Replacement = 'HellblazerMissileMag';
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
if ( Random[Replacements](0,4) ) 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 == 'Cell') || (e.Replacee == 'SkullRodAmmo') )
|
|
{
|
|
if ( !Random[Replacements](0,2) ) e.Replacement = Random[Replacements](0,2)?'HellblazerRavagers':'HellblazerWarheads';
|
|
else if ( Random[Replacements](0,2) ) e.Replacement = 'SparkUnit';
|
|
else if ( !Random[Replacements](0,3) ) e.Replacement = 'CandyGunBullets';
|
|
else e.Replacement = Random[Replacements](0,2)?'SilverBullets':'SilverBullets2';
|
|
}
|
|
else if ( (e.Replacee == 'CellPack') || (e.Replacee == 'SkullRodHefty') )
|
|
{
|
|
if ( !Random[Replacements](0,2) )
|
|
{
|
|
if ( Random[Replacements](0,3) ) e.Replacement = Random[Replacements](0,2)?'SilverBulletsBundleSpawn':'SilverBullets2BundleSpawn';
|
|
else e.Replacement = Random[Replacements](0,2)?'SilverBulletAmmo':'SilverBulletAmmo2';
|
|
}
|
|
else if ( Random[Replacements](0,2) ) e.Replacement = 'CandyGunBulletsBundleSpawn';
|
|
else e.Replacement = 'CandyGunAmmo';
|
|
}
|
|
else if ( e.Replacee == 'Mana1' ) e.Replacement = 'FabricatorTier1';
|
|
else if ( e.Replacee == 'Mana2' ) e.Replacement = 'FabricatorTier2';
|
|
else if ( e.Replacee == 'Mana3' ) e.Replacement = 'FabricatorTier3';
|
|
else if ( e.Replacee == 'ArtiBoostMana' ) e.Replacement = 'FabricatorTier4';
|
|
else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'HammerspaceEmbiggener';
|
|
else if ( (e.Replacee == 'FWeaponPiece1') || (e.Replacee == 'FWeaponPiece2')
|
|
|| (e.Replacee == 'CWeaponPiece1') || (e.Replacee == 'CWeaponPiece3')
|
|
|| (e.Replacee == 'MWeaponPiece2') || (e.Replacee == 'MWeaponPiece3') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'SWWMNothing';
|
|
else if ( Random[Replacements](0,5) ) e.Replacement = 'HammerspaceEmbiggener';
|
|
else e.Replacement = 'GoldShell';
|
|
}
|
|
else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') || (e.Replacee == 'ArtiBlastRadius') || (e.Replacee is 'ArtiPoisonBag') ) e.Replacement = 'ArmorNuggetItem';
|
|
else if ( (e.Replacee == 'HealthBonus') || (e.Replacee == 'CrystalVial') ) e.Replacement = 'HealthNuggetItem';
|
|
else if ( e.Replacee == 'Stimpack' ) e.Replacement = 'TetraHealthItem';
|
|
else if ( e.Replacee == 'Medikit' ) e.Replacement = 'CubeHealthItem';
|
|
else if ( e.Replacee == 'ArtiHealth' ) e.Replacement = Random[Replacements](0,1)?'CubeHealthItem':'TetraHealthItem';
|
|
else if ( (e.Replacee == 'Soulsphere') || (e.Replacee == 'ArtiSuperHealth') ) e.Replacement = 'RefresherItem';
|
|
else if ( e.Replacee == 'ArtiHealingRadius' ) e.Replacement = 'SWWMNothing';
|
|
else if ( (e.Replacee == 'Megasphere') || (e.Replacee == 'ArtiEgg') || (e.Replacee == 'PlatinumHelm') ) e.Replacement = 'GrilledCheeseSandwich';
|
|
else if ( (e.Replacee == 'Blursphere') || (e.Replacee == 'ArtiInvisibility') || (e.Replacee == 'AmuletOfWarding') ) e.Replacement = 'GhostArtifact';
|
|
else if ( e.Replacee == 'Radsuit' ) e.Replacement = 'EBarrier';
|
|
else if ( (e.Replacee == 'ArtiFly') ) e.Replacement = 'GravitySuppressor';
|
|
else if ( (e.Replacee == 'InvulnerabilitySphere') || (e.Replacee == 'ArtiInvulnerability') || (e.Replacee == 'ArtiInvulnerability2') ) e.Replacement = 'FuckingInvinciball';
|
|
else if ( (e.Replacee == 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'Ragekit';
|
|
else if ( (e.Replacee == 'AllMap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'Omnisight';
|
|
else if ( (e.Replacee == 'Infrared') || (e.Replacee == 'ArtiTorch') ) e.Replacement = 'SWWMLamp';
|
|
else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'SilverShield') || (e.Replacee == 'MeshArmor') ) e.Replacement = 'BlastSuitItem';
|
|
else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'FalconShield') || (e.Replacee == 'EnchantedShield') ) e.Replacement = 'WarArmorItem';
|
|
else if ( (e.Replacee == 'ArtiPork') || (e.Replacee == 'ArtiDarkServant') || (e.Replacee == 'ArtiTeleport') || (e.Replacee == 'ArtiTeleportOther') ) e.Replacement = 'ChanceboxSpawner';
|
|
else return;
|
|
// this last part is kind of ugly, but it works
|
|
// guarantees that OUR replacements are all final
|
|
e.IsFinal = true;
|
|
}
|
|
|
|
override void 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;
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmdebugdumprng" )
|
|
{
|
|
// 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("DemoLines: %d",Random2[DemoLines]());
|
|
Console.Printf("DoBlast: %d",Random2[DoBlast]());
|
|
Console.Printf("Eviscerator: %d",Random2[Eviscerator]());
|
|
Console.Printf("Explos: %d",Random2[Explos]());
|
|
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]());
|
|
}
|
|
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]) )
|
|
{
|
|
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(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();
|
|
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",16,true);
|
|
for ( Inventory i=players[e.Args[0]].mo.inv; i; i=i.inv )
|
|
{
|
|
if ( !(i is 'Ammo') ) continue;
|
|
i.Amount = i.MaxAmount;
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmbloodcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyEdgy...\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmexplocheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyThat cheat's not needed anymore.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmallcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyStill as wrappy as it's always been.\c-");
|
|
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
|
|
S_StartSound("fabricator/use",CHAN_VOICE,CHANF_UI);
|
|
}
|
|
players[e.Args[0]].mo.CheatGive("all",0);
|
|
}
|
|
else if ( e.Name ~== "swwmflagcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyThere are no flags here.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmballcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cy\"Balls on your head\"? What was I even thinking...\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmsmartcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cySkittles are better anyway.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmnutcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyI'm way past that, it was bad for my health.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmweeniecheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyAlways has been.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmpunishcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyThis is a bulli free zone.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmball2cheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cy<insert amiga boing ball here>\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmfartcheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyI'd rather not reimplement that one.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmsupercheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyNo, you're the Demolitionist.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "swwmstonecheat" )
|
|
{
|
|
if ( SWWMUtility.CheatsDisabled(e.Args[0]) )
|
|
return;
|
|
if ( consoleplayer == e.Args[0] )
|
|
{
|
|
Console.Printf("\cyThe pinnacle of... wait, I misread that.\c-");
|
|
S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI);
|
|
}
|
|
}
|
|
}
|
|
|
|
// stuff for hud
|
|
override void RenderUnderlay( RenderEvent e )
|
|
{
|
|
// armor/health flashes
|
|
int camplayer = players[consoleplayer].Camera.PlayerNumber();
|
|
if ( camplayer != -1 )
|
|
{
|
|
if ( gametic < hflash[camplayer] )
|
|
{
|
|
double fstr = (hflash[camplayer]-(gametic+e.FracTic))/5.;
|
|
Screen.Dim(Color(64,128,255),.1875*fstr,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
}
|
|
if ( gametic < aflash[camplayer] )
|
|
{
|
|
double fstr = (aflash[camplayer]-(gametic+e.FracTic))/5.;
|
|
Screen.Dim(Color(96,255,64),.1875*fstr,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
}
|
|
}
|
|
// weapon underlays
|
|
if ( players[consoleplayer].ReadyWeapon is 'SWWMWeapon' )
|
|
SWWMWeapon(players[consoleplayer].ReadyWeapon).RenderUnderlay(e);
|
|
if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return;
|
|
SWWMStatusBar(statusbar).viewpos = e.viewpos;
|
|
SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll);
|
|
if ( slotstrictwarn && (gametic < slotstrictwarn) )
|
|
{
|
|
String str = StringTable.Localize("$SWWM_SETSLOTSTRICT");
|
|
double t = (slotstrictwarn-(gametic+e.FracTic))/20.;
|
|
double alph = clamp(t,0.,1.);
|
|
BrokenLines l = newsmallfont.BreakLines(str,300);
|
|
double yy = (200-l.Count()*newsmallfont.GetHeight())/2;
|
|
for ( int i=0; i<l.Count(); i++ )
|
|
{
|
|
double xx = (320-l.StringWidth(i))/2;
|
|
Screen.DrawText(newsmallfont,Font.CR_UNTRANSLATED,xx,yy,l.StringAt(i),DTA_Clean,true,DTA_Alpha,alph);
|
|
yy += newsmallfont.GetHeight();
|
|
}
|
|
}
|
|
}
|
|
|
|
// called by HUD (done here for the sake of cleaner code)
|
|
ui void DrawBossBar( SWWMStatusBar bar )
|
|
{
|
|
if ( !ui_initialized || (bossalpha <= 0.) ) return;
|
|
if ( !dodrawbossbar ) dodrawbossbar = CVar.GetCVar('swwm_bosshealthbars',players[consoleplayer]);
|
|
if ( !dodrawbossbar.GetBool() ) return;
|
|
if ( !bbar_f ) bbar_f = TexMan.CheckForTexture("graphics/HUD/BossHealthBarBox.png",TexMan.Type_Any);
|
|
if ( !bbar_r ) bbar_r = TexMan.CheckForTexture("graphics/HUD/BossHealthBar.png",TexMan.Type_Any);
|
|
if ( !bbar_d ) bbar_d = TexMan.CheckForTexture("graphics/HUD/BossHealthBarDecay.png",TexMan.Type_Any);
|
|
Vector2 vpos = ((bar.ss.x-300)/2.,bar.ss.y-(bar.margin+35));
|
|
Screen.DrawTexture(bbar_f,false,vpos.x-2,vpos.y-2,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha);
|
|
int rw = int(clamp((ihealthr.GetValue()*300.)/hmax,0.,300.));
|
|
int dw = int(clamp((ihealth.GetValue()*300.)/hmax,0.,300.));
|
|
Screen.DrawTexture(bbar_d,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,dw);
|
|
Screen.DrawTexture(bbar_r,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,rw);
|
|
Font barfnt = bar.LangFont(bar.mTewiFont);
|
|
Font dmgfnt = bar.mTewiFont.mFont;
|
|
if ( (cummdamage > 0) && (gametic < lastcummtic+150) )
|
|
{
|
|
double calph = clamp(((lastcummtic+150)-gametic)/50.,0.,1.);
|
|
string dnum = String.Format("%d",cummdamage);
|
|
Screen.DrawText(dmgfnt,Font.CR_RED,vpos.x+300-dmgfnt.StringWidth(dnum),vpos.y-(dmgfnt.GetHeight()+2),dnum,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha*calph);
|
|
}
|
|
Screen.DrawText(barfnt,Font.CR_WHITE,vpos.x,vpos.y-(barfnt.GetHeight()+2),StringTable.Localize((funtags&&funtags.GetBool())?(bosstag.."_FUN"):bosstag),DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha);
|
|
}
|
|
|
|
// various shaders
|
|
override void RenderOverlay( RenderEvent e )
|
|
{
|
|
PlayerInfo p = players[consoleplayer];
|
|
if ( !useshaders ) useshaders = CVar.GetCVar('swwm_shaders',p);
|
|
let mo = p.mo;
|
|
if ( !mo ) return;
|
|
bool pc = (p.camera == mo);
|
|
let rage = RagekitPower(mo.FindInventory("RagekitPower"));
|
|
if ( pc && rage && useshaders.GetBool() )
|
|
{
|
|
if ( !altrage ) altrage = CVar.GetCVar('swwm_rageshader',p);
|
|
if ( altrage.GetBool() )
|
|
{
|
|
Shader.SetEnabled(p,"RagekitShader",false);
|
|
Shader.SetEnabled(p,"RagekitAltShader",true);
|
|
Shader.SetUniform1f(p,"RagekitAltShader","timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
double xstrastr = 1.+max(0,rage.lastpulse-(gametic+e.Fractic))/35.;
|
|
Shader.SetUniform1f(p,"RagekitAltShader","xtrastr",xstrastr**2.);
|
|
}
|
|
else
|
|
{
|
|
Shader.SetEnabled(p,"RagekitAltShader",false);
|
|
Shader.SetEnabled(p,"RagekitShader",true);
|
|
Shader.SetUniform1f(p,"RagekitShader","timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
double xstrastr = 1.+max(0,rage.lastpulse-(gametic+e.Fractic))/35.;
|
|
Shader.SetUniform1f(p,"RagekitShader","xtrastr",xstrastr**2.);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Shader.SetEnabled(p,"RagekitShader",false);
|
|
Shader.SetEnabled(p,"RagekitAltShader",false);
|
|
}
|
|
let ghost = GhostPower(mo.FindInventory("GhostPower"));
|
|
if ( pc && ghost && useshaders.GetBool() ) Shader.SetEnabled(p,"GhostShader",true);
|
|
else Shader.SetEnabled(p,"GhostShader",false);
|
|
let sunny = InvinciballPower(mo.FindInventory("InvinciballPower"));
|
|
if ( pc && sunny && useshaders.GetBool() )
|
|
{
|
|
Shader.SetEnabled(p,"InvinciShader",true);
|
|
double str = max(0,sunny.lastpulse-(gametic+e.Fractic))/35.;
|
|
Shader.SetUniform1f(p,"InvinciShader","str",str);
|
|
}
|
|
else Shader.SetEnabled(p,"InvinciShader",false);
|
|
let coat = BarrierPower(mo.FindInventory("BarrierPower"));
|
|
if ( pc && coat && useshaders.GetBool() )
|
|
{
|
|
Shader.SetEnabled(p,"BarrierShader",true);
|
|
Shader.SetUniform1f(p,"BarrierShader","timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
}
|
|
else Shader.SetEnabled(p,"BarrierShader",false);
|
|
if ( pc && (mo is 'Demolitionist') && useshaders.GetBool() )
|
|
{
|
|
let demo = Demolitionist(mo);
|
|
if ( demo.lastunder == Demolitionist.UNDER_WATER )
|
|
{
|
|
Shader.SetEnabled(p,"WaterWarp",true);
|
|
Shader.SetUniform1f(p,"WaterWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"WaterWarp","dfact",coat?.25:1.);
|
|
Shader.SetUniform3f(p,"WaterWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
|
|
|
|
}
|
|
else Shader.SetEnabled(p,"WaterWarp",false);
|
|
if ( demo.lastunder == Demolitionist.UNDER_LAVA )
|
|
{
|
|
Shader.SetEnabled(p,"LavaWarp",true);
|
|
Shader.SetUniform1f(p,"LavaWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"LavaWarp","dfact",coat?.25:1.);
|
|
Shader.SetUniform3f(p,"LavaWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
|
|
}
|
|
else Shader.SetEnabled(p,"LavaWarp",false);
|
|
if ( demo.lastunder == Demolitionist.UNDER_SLIME )
|
|
{
|
|
Shader.SetEnabled(p,"SlimeWarp",true);
|
|
Shader.SetUniform1f(p,"SlimeWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"SlimeWarp","dfact",coat?.25:1.);
|
|
Shader.SetUniform3f(p,"SlimeWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
|
|
}
|
|
else Shader.SetEnabled(p,"SlimeWarp",false);
|
|
int lastdmg = (demo.Health>0)?demo.lastdamage:Random[Flicker](60,80);
|
|
int lastdmgtic = (demo.Health>0)?demo.lastdamagetic:(gametic+Random[Flicker](30,20));
|
|
double noiz = min(lastdmg*.09*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.5);
|
|
Shader.SetEnabled(p,"Glitch",noiz>0);
|
|
Shader.SetEnabled(p,"Grain",noiz>0);
|
|
if ( noiz > 0 )
|
|
{
|
|
Shader.SetUniform1f(p,"Glitch","Timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"Grain","ni",noiz);
|
|
noiz = min(lastdmg*.08*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.8);
|
|
Shader.SetUniform1f(p,"Glitch","str1",noiz);
|
|
noiz = min(lastdmg*.03*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),3.5);
|
|
Shader.SetUniform1f(p,"Glitch","str2",noiz);
|
|
}
|
|
if ( !demo.InStateSequence(demo.CurState,demo.FindState("Dash")) )
|
|
{
|
|
Shader.SetEnabled(p,"ZoomBlur",false);
|
|
return;
|
|
}
|
|
Shader.SetEnabled(p,"ZoomBlur",true);
|
|
Vector3 vel = demo.vel+demo.dashdir*demo.dashboost;
|
|
double baumpu = max(0.,(demo.bumptic-(gametic+e.Fractic))/35.);
|
|
vel += demo.dashdir*baumpu;
|
|
double spd = vel.length();
|
|
Vector3 worlddir = vel/spd;
|
|
Shader.SetUniform1f(p,"ZoomBlur","Fade",clamp((spd-20.)/60.,0.,1.));
|
|
double str = min(spd/40.,15.);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(e.ViewPitch,e.ViewAngle,e.ViewRoll);
|
|
Vector3 reldir = (worlddir dot y, worlddir dot z, worlddir dot x);
|
|
Vector2 centerspot = (.5+reldir.x*.5,.5+reldir.y*.5);
|
|
if ( reldir.z < 0 )
|
|
{
|
|
centerspot.x = 1.-centerspot.x;
|
|
centerspot.y = 1.-centerspot.y;
|
|
str *= -1;
|
|
}
|
|
Shader.SetUniform1f(p,"ZoomBlur","Str",str);
|
|
Shader.SetUniform2f(p,"ZoomBlur","CenterSpot",centerspot);
|
|
}
|
|
else
|
|
{
|
|
Shader.SetEnabled(p,"WaterWarp",false);
|
|
Shader.SetEnabled(p,"LavaWarp",false);
|
|
Shader.SetEnabled(p,"SlimeWarp",false);
|
|
Shader.SetEnabled(p,"Glitch",false);
|
|
Shader.SetEnabled(p,"Grain",false);
|
|
Shader.SetEnabled(p,"ZoomBlur",false);
|
|
}
|
|
}
|
|
|
|
static void DoFlash( Actor camera, Color c, int duration )
|
|
{
|
|
// don't flash when paused
|
|
if ( menuactive && (menuactive != Menu.OnNoPause) ) return;
|
|
QueuedFlash qf = new("QueuedFlash");
|
|
qf.duration = duration;
|
|
qf.c = c;
|
|
qf.tic = gametic;
|
|
qf.cam = camera;
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( !hnd ) return; // not supposed to happen
|
|
hnd.flashes.push(qf);
|
|
}
|
|
|
|
// can't use this until I actually figure out how to make those walls damageable
|
|
/*override void WorldLineDamaged( WorldEvent e )
|
|
{
|
|
// allow boss brain to take (reduced) damage from the facewall being shot
|
|
if ( level.mapname ~== "MAP30" )
|
|
{
|
|
if ( !SWWMUtility.IsIOSWall(e.DamageLine) ) return;
|
|
if ( bossbrainactor )
|
|
bossbrainactor.DamageMobj(e.Inflictor,e.DamageSource,e.Damage/3,e.DamageType,e.DamageFlags,e.DamageAngle);
|
|
e.NewDamage = 0;
|
|
}
|
|
}*/
|
|
}
|