2963 lines
100 KiB
Text
2963 lines
100 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;
|
|
// CBT easter egg
|
|
bool initme;
|
|
double cbtvol;
|
|
transient CVar cbtme;
|
|
|
|
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;
|
|
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
|
|
let svd = SWWMSaveVerData(ti.Next());
|
|
if ( !svd )
|
|
{
|
|
tainted = true;
|
|
taintver = "\cg(no version information)\c-";
|
|
Console.Printf("\cgWARNING: \cjLoaded save contains no version data. Issues may happen.");
|
|
return;
|
|
}
|
|
String cver = StringTable.Localize("$SWWM_MODVER");
|
|
if ( svd.ver != cver )
|
|
{
|
|
tainted = true;
|
|
taintver = svd.ver;
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 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++;
|
|
}
|
|
}
|
|
|
|
// from Wikipedia, the free encyclopedia
|
|
override void WorldTick()
|
|
{
|
|
if ( !cbtme ) cbtme = CVar.GetCVar('swwm_cbtmeme',players[consoleplayer]);
|
|
if ( !players[consoleplayer].mo ) return;
|
|
let cbt = players[consoleplayer].mo.FindInventory("Wallbuster");
|
|
if ( !cbt ) return;
|
|
if ( cbt == players[consoleplayer].ReadyWeapon )
|
|
{
|
|
if ( !initme && (cbtme.GetInt() > 0) )
|
|
{
|
|
initme = true;
|
|
cbtvol = .6;
|
|
}
|
|
cbtvol = min(.6,cbtvol+.1);
|
|
if ( (cbtme.GetInt() >= 1) && !cbt.IsActorPlayingSound(CHAN_AMBEXTRA+1) )
|
|
cbt.A_StartSound("wallbuster/olddays",CHAN_AMBEXTRA+1,CHANF_UI|CHANF_LOOP,cbtvol,0.);
|
|
else if ( (cbtme.GetInt() < 1) && cbt.IsActorPlayingSound(CHAN_AMBEXTRA+1) )
|
|
cbt.A_StopSound(CHAN_AMBEXTRA+1);
|
|
if ( (cbtme.GetInt() >= 2) && !cbt.IsActorPlayingSound(CHAN_AMBEXTRA+2) )
|
|
{
|
|
// restart the music
|
|
if ( cbt.IsActorPlayingSound(CHAN_AMBEXTRA+1) )
|
|
{
|
|
cbt.A_StopSound(CHAN_AMBEXTRA+1);
|
|
cbt.A_StartSound("wallbuster/olddays",CHAN_AMBEXTRA+1,CHANF_UI|CHANF_LOOP,cbtvol,0.);
|
|
}
|
|
cbt.A_StartSound("wallbuster/cbt",CHAN_AMBEXTRA+2,CHANF_UI,cbtvol,0.);
|
|
}
|
|
else if ( (cbtme.GetInt() < 2) && cbt.IsActorPlayingSound(CHAN_AMBEXTRA+2) )
|
|
cbt.A_StopSound(CHAN_AMBEXTRA+2);
|
|
}
|
|
else cbtvol = max(0.,cbtvol-.1);
|
|
cbt.A_SoundVolume(CHAN_AMBEXTRA+1,cbtvol*((cbtme.GetInt()>=2)?.8:1.));
|
|
cbt.A_SoundVolume(CHAN_AMBEXTRA+2,cbtvol);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
// 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 )
|
|
{
|
|
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
|
|
SWWMStats s;
|
|
while ( s = SWWMStats(ti.Next()) )
|
|
s.AddLevelStats();
|
|
ClearAllShaders(players[consoleplayer]);
|
|
}
|
|
|
|
override void WorldLoaded( WorldEvent e )
|
|
{
|
|
if ( level.levelname ~== "Modder Test Map" ) level.ReplaceTextures("-noflat-","kinstile",0);
|
|
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 ( 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;
|
|
}
|
|
|
|
override void WorldTick()
|
|
{
|
|
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();
|
|
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;
|
|
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--;
|
|
}
|
|
// countable item scoring
|
|
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
|
|
// 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);
|
|
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;
|
|
}
|
|
|
|
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);
|
|
if ( !Random[GoldDrop](0,dropweight) )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
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]+1;
|
|
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));
|
|
}
|
|
spreecount[pnum]++;
|
|
if ( s && (spreecount[pnum] > s.skill) && !tookdamage[pnum] )
|
|
s.skill = spreecount[pnum];
|
|
}
|
|
|
|
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[Replacement](0,14) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
e.Replacement = redpool[Random[Replacement](0,1)];
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
e.Replacement = greenpool[Random[Replacement](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[Replacement](0,13) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
e.Replacement = redpool[Random[Replacement](0,2)];
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
e.Replacement = greenpool[Random[Replacement](0,2)];
|
|
break;
|
|
case 6:
|
|
case 7:
|
|
e.Replacement = whitepool[0];
|
|
break;
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
e.Replacement = purplepool[Random[Replacement](0,1)];
|
|
break;
|
|
case 11:
|
|
case 12:
|
|
e.Replacement = bluepool[Random[Replacement](0,1)];
|
|
break;
|
|
case 13:
|
|
e.Replacement = blackpool[0];
|
|
break;
|
|
}
|
|
}
|
|
else if ( (e.Replacee == 'ShellBox') || (e.Replacee is 'CrossbowHefty') )
|
|
{
|
|
switch( Random[Replacement](0,14) )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
e.Replacement = redpool[Random[Replacement](1,3)];
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
e.Replacement = greenpool[Random[Replacement](1,2)];
|
|
break;
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
e.Replacement = whitepool[Random[Replacement](0,1)];
|
|
break;
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
e.Replacement = purplepool[Random[Replacement](0,2)];
|
|
break;
|
|
case 12:
|
|
case 13:
|
|
e.Replacement = bluepool[Random[Replacement](0,2)];
|
|
break;
|
|
case 14:
|
|
e.Replacement = blackpool[Random[Replacement](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;
|
|
if ( e.Args[0] < 0 ) return; // special gestures can't be manually started
|
|
SWWMGesture.SetGesture(mo,e.Args[0]);
|
|
}
|
|
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;
|
|
}
|
|
}*/
|
|
}
|