swwmgz_m/zscript/swwm_common.zsc
Marisa Kirisame 7a01fdc4e8 Spreadgun buckshot and slug implemented.
Tweaks here and there to other stuff.
Refined wall jumping/climbing, also works on actors (including other players).
Refined how boosting/dashing handles falling speeds.
Added improved air control. It was very much needed.
Added "kick" sounds to wall jumps.
Add option to hear other player's voices in mp.
Fix some broken localization.
Fix invulnerable monsters bleeding from some attacks.
Fix desync when jumping on top of another player with prediction enabled.
Make moths immune to your damage, so you can stop accidentally killing them.
Make normal ammo buyable in Hexen again.
2020-02-27 02:00:17 +01:00

2279 lines
62 KiB
Text

// common code goes here
enum ESWWMGZChannels
{
CHAN_YOUDONEFUCKEDUP = 63200,
CHAN_DEMOVOICE = 63201,
CHAN_FOOTSTEP = 63202,
CHAN_WEAPONEXTRA = 63203,
CHAN_POWERUP = 63204,
CHAN_POWERUPEXTRA = 63205,
CHAN_JETPACK = 63206,
CHAN_ITEMEXTRA = 63207
};
// Misc. Utility code
Class SWWMUtility
{
static void StripColor( out String str )
{
int len = str.CodePointCount();
for ( int i=0; i<len; i++ )
{
int remlen = 0;
if ( str.GetNextCodePoint(i) != 0x1C )
continue;
remlen++;
if ( str.GetNextCodePoint(i+remlen) == 0x5B )
while ( str.GetNextCodePoint(i+remlen) != 0x5D )
remlen++;
remlen++;
str.Remove(i,remlen);
len -= remlen;
}
}
}
// Stats
Class WeaponUsage
{
Class<Weapon> w;
int kills;
}
Class SWWMStats : Thinker
{
PlayerInfo myplayer;
int lastspawn, dashcount, boostcount, stompcount, airtime, kills,
deaths, damagedealt, damagetaken, mkill, hiscore, topdealt,
toptaken;
double grounddist, airdist, fuelusage, topspeed;
Array<WeaponUsage> wstats;
Array<Class<Weapon> > alreadygot;
int favweapon;
bool GotWeapon( Class<Weapon> which )
{
for ( int i=0; i<alreadygot.Size(); i++ )
{
if ( alreadygot[i] == which ) return true;
}
alreadygot.Push(which);
return false;
}
void AddWeaponKill( Class<Weapon> which )
{
for ( int i=0; i<wstats.Size(); i++ )
{
if ( wstats[i].w != which ) continue;
wstats[i].kills++;
if ( (favweapon == -1) || (wstats[favweapon].kills < wstats[i].kills) ) favweapon = i;
return;
}
let ws = new("WeaponUsage");
ws.w = which;
ws.kills = 1;
wstats.Push(ws);
if ( (favweapon == -1) || (wstats[favweapon].kills < ws.kills) )
favweapon = wstats.Size()-1;
}
static clearscope SWWMStats Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMStats",STAT_STATIC);
SWWMStats t;
while ( t = SWWMStats(ti.Next()) )
{
if ( t.myplayer != p ) continue;
return t;
}
return null;
}
}
// Scoring
Class SWWMCredits : Thinker
{
PlayerInfo myplayer;
int credits;
static void Give( PlayerInfo p, int amount )
{
let c = Find(p);
if ( !c ) return;
if ( (c.credits+amount < c.credits) || (c.credits+amount > 999999999) ) c.credits = 999999999;
else c.credits += amount;
let s = SWWMStats.Find(p);
if ( s && (c.credits > s.hiscore) ) s.hiscore = c.credits;
}
static clearscope bool CanTake( PlayerInfo p, int amount )
{
let c = Find(p);
if ( !c ) return false;
// too much!
if ( (c.credits-amount < 0) || (c.credits-amount > c.credits) ) return false;
return true;
}
static bool Take( PlayerInfo p, int amount )
{
let c = Find(p);
if ( !c ) return false;
// too much!
if ( (c.credits-amount < 0) || (c.credits-amount > c.credits) ) return false;
c.credits -= amount;
return true;
}
static clearscope int Get( PlayerInfo p )
{
let c = Find(p);
if ( !c ) return 0;
return c.credits;
}
static clearscope SWWMCredits Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMCredits",STAT_STATIC);
SWWMCredits t;
while ( t = SWWMCredits(ti.Next()) )
{
if ( t.myplayer != p ) continue;
return t;
}
return null;
}
}
// Trading history between players
Class SWWMTrade
{
int timestamp, type;
String other, what;
}
Class SWWMTradeHistory : Thinker
{
PlayerInfo myplayer;
Array<SWWMTrade> ent;
static void RegisterSend( PlayerInfo p, PlayerInfo other, String what )
{
let th = Find(p);
if ( !th ) return;
SWWMTrade t = new("SWWMTrade");
t.timestamp = gametic;
t.type = 0;
t.other = other.GetUserName();
t.what = what;
th.ent.Push(t);
}
static void RegisterReceive( PlayerInfo p, PlayerInfo other, String what )
{
let th = Find(p);
if ( !th ) return;
SWWMTrade t = new("SWWMTrade");
t.timestamp = gametic;
t.type = 1;
t.other = other.GetUserName();
t.what = what;
th.ent.Push(t);
}
static clearscope SWWMTradeHistory Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMTradeHistory",STAT_STATIC);
SWWMTradeHistory th;
while ( th = SWWMTradeHistory(ti.Next()) )
{
if ( th.myplayer != p ) continue;
return th;
}
return Null;
}
}
// Lore holder
enum ELoreTab
{
LORE_ITEM,
LORE_PEOPLE,
LORE_LORE // lol
};
Class SWWMLore
{
String tag, text, assoc;
int tab;
bool read;
}
Class SWWMLoreLibrary : Thinker
{
PlayerInfo myplayer;
Array<SWWMLore> ent;
int lastaddtic;
bool DirectAdd( String ref )
{
// restrictions
if ( !(gameinfo.gametype&GAME_Raven) )
{
if ( ref ~== "Doomslayer" ) return true; // not witnessed
}
if ( !(gameinfo.gametype&GAME_Hexen) )
{
if ( ref ~== "Kirin" ) return true; // not met
}
ref = ref.MakeUpper();
String tag = String.Format("SWWM_LORETAG_%s",ref);
String tab = String.Format("SWWM_LORETAB_%s",ref);
String text = String.Format("SWWM_LORETXT_%s",ref);
String assoc = String.Format("SWWM_LOREREL_%s",ref);
// redirects
if ( gameinfo.gametype&GAME_Raven )
{
if ( text ~== "SWWM_LORETXT_UAC" )
text = "SWWM_LORETXT_UAC2"; // uac ded
else if ( text ~== "SWWM_LORETXT_HELL" )
text = "SWWM_LORETXT_HELL2"; // invasion was a thing of the past
else if ( text ~== "SWWM_LORETXT_NANA" )
text = "SWWM_LORETXT_NANA2"; // demo met nana
}
if ( gameinfo.gametype&GAME_Hexen )
{
if ( text ~== "SWWM_LORETXT_SAYA" )
text = "SWWM_LORETXT_SAYA2"; // married kirin
else if ( text ~== "SWWM_LORETXT_ANARUKON" )
text = "SWWM_LORETXT_ANARUKON2"; // comments from miyamoto-xanai wedding
else if ( text ~== "SWWM_LORETXT_HELL2" )
text = "SWWM_LORETXT_HELL3"; // mention the return of manakei
}
// check that it's valid
if ( StringTable.Localize(tag,false) == tag ) return false;
if ( StringTable.Localize(tab,false) == tab ) return false;
if ( StringTable.Localize(text,false) == text ) return false;
// check if existing
for ( int i=0; i<ent.Size(); i++ )
{
if ( ent[i].tag != "$"..tag ) continue;
return true;
}
SWWMLore e = new("SWWMLore");
e.tag = "$"..tag;
if ( StringTable.Localize(e.tag) == "" )
{
Console.Printf("Entry \"%s\" has an empty tag.",ref);
return true;
}
String ttab = StringTable.Localize(tab,false);
if ( ttab ~== "People" ) e.tab = LORE_PEOPLE;
else if ( ttab ~== "Lore" ) e.tab = LORE_LORE;
else if ( ttab ~== "Item" ) e.tab = LORE_ITEM;
else
{
Console.Printf("Entry \"%s\" has an incorrect tab setting of \"%s\".",ref,ttab);
return true;
}
e.text = "$"..text;
if ( StringTable.Localize(e.text) == "" )
{
Console.Printf("Entry \"%s\" has empty text.",ref);
return true;
}
e.assoc = "$"..assoc;
e.read = false;
// "new lore" message
if ( (level.maptime > 0) && (gametic > lastaddtic) && (myplayer == players[consoleplayer]) && (!menuactive || (menuactive == Menu.OnNoPause)) )
Console.Printf(StringTable.Localize("$SWWM_NEWLORE"));
lastaddtic = gametic;
// sorted add
String loca = StringTable.Localize(e.tag), locb;
int cpa, cpb;
for ( int i=0; i<ent.Size(); i++ )
{
locb = StringTable.Localize(ent[i].tag);
if ( locb < loca ) continue;
ent.Insert(i,e);
return true;
}
// append otherwise
ent.Push(e);
return true;
}
static void Add( PlayerInfo p, String ref )
{
SWWMLoreLibrary ll = Find(p);
if ( !ll )
{
ll = new("SWWMLoreLibrary");
ll.ChangeStatNum(STAT_STATIC);
ll.myplayer = p;
}
ll.DirectAdd(ref);
}
void MarkRead( int idx )
{
if ( (idx < 0) || (idx >= ent.Size()) ) return;
if ( !ent[idx].read )
{
ent[idx].read = true;
// add associated entries
Array<String> rel;
rel.Clear();
String assocstr = StringTable.Localize(ent[idx].assoc);
assocstr.Split(rel,";",0);
for ( int i=0; i<rel.Size(); i++ )
{
if ( (rel[i] != "") && !DirectAdd(rel[i]) )
Console.Printf("Related entry \"%s\" not found, please update LANGUAGE.txt",rel[i]);
}
}
}
clearscope int FindEntry( String tag )
{
for ( int i=0; i<ent.Size(); i++ )
{
if ( ent[i].tag ~== tag )
return i;
}
return -1;
}
static clearscope SWWMLoreLibrary Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMLoreLibrary",STAT_STATIC);
SWWMLoreLibrary ll;
while ( ll = SWWMLoreLibrary(ti.Next()) )
{
if ( ll.myplayer != p ) continue;
return ll;
}
return Null;
}
}
// floating scores
Class SWWMScoreObj : Thinker
{
int tcolor;
int score;
String str;
Vector3 pos;
int lifespan, initialspan;
int starttic, seed, seed2;
int ofs;
static SWWMScoreObj Spawn( int score, Vector3 pos, int tcolor = Font.CR_GOLD, String str = "", int ofs = 0 )
{
let o = new("SWWMScoreObj");
o.ChangeStatNum(STAT_USER);
o.score = score;
o.pos = pos;
o.lifespan = o.initialspan = 60;
o.tcolor = tcolor;
o.starttic = level.maptime;
o.str = str;
o.seed = Random[ScoreBits]();
o.seed2 = Random[ScoreBits]();
o.ofs = ofs;
return o;
}
override void Tick()
{
lifespan--;
if ( lifespan <= 0 ) Destroy();
}
}
enum EInterestType
{
INT_Key,
INT_Exit
};
Class SWWMInterest : Thinker
{
int type;
Key trackedkey;
Line trackedline;
Vector3 pos;
static SWWMInterest Spawn( Vector3 pos = (0,0,0), Key thekey = null, Line theline = null )
{
if ( (!thekey && !theline) || (thekey && theline) ) return null;
let i = new("SWWMInterest");
i.ChangeStatNum(STAT_USER);
i.trackedkey = thekey;
i.trackedline = theline;
if ( thekey ) i.type = INT_Key;
else if ( theline ) i.type = INT_Exit;
else
{
i.Destroy();
return null;
}
i.pos = thekey?thekey.Vec3Offset(0,0,thekey.height/2):pos;
return i;
}
override void Tick()
{
// update
if ( (type == INT_Key) && (!trackedkey || trackedkey.Owner) ) Destroy();
else if ( trackedkey ) pos = trackedkey.Vec3Offset(0,0,trackedkey.height/2);
}
}
// enemy combat tracker
Class SWWMCombatTracker : Thinker
{
Actor mytarget;
String mytag;
int updated, lasthealth, maxhealth;
DynamicValueInterpolator intp;
Vector3 pos, prevpos;
PlayerInfo myplayer;
static SWWMCombatTracker Spawn( Actor target )
{
let ti = ThinkerIterator.Create("SWWMCombatTracker",STAT_USER);
SWWMCombatTracker t;
while ( t = SWWMCombatTracker(ti.Next()) )
{
if ( t.mytarget != target ) continue;
return t;
}
t = new("SWWMCombatTracker");
t.ChangeStatNum(STAT_USER);
t.mytarget = target;
if ( target.player || target.bISMONSTER )
t.mytag = target.player?target.player.GetUserName():target.GetTag();
else t.mytag = "";
t.lasthealth = t.maxhealth = target.health;
t.updated = int.min;
t.pos = level.Vec3Offset(target.pos,(0,0,target.default.height));
t.prevpos = level.Vec3Offset(target.prev,(0,0,target.default.height));
t.intp = DynamicValueInterpolator.Create(t.lasthealth,.5,1,100);
t.myplayer = target.player;
return t;
}
override void Tick()
{
// update
if ( mytarget )
{
pos = level.Vec3Offset(mytarget.pos,(0,0,mytarget.default.height));
prevpos = level.Vec3Offset(mytarget.prev,(0,0,mytarget.default.height));
}
if ( !mytarget || (mytarget.Health <= 0) )
{
// we're done
if ( updated > level.maptime ) updated = level.maptime;
lasthealth = 0;
intp.Update(lasthealth);
if ( level.maptime > updated+35 ) Destroy();
return;
}
if ( mytarget.player || mytarget.bISMONSTER )
mytag = mytarget.player?mytarget.player.GetUserName():mytarget.GetTag();
else mytag = "";
int newhealth = mytarget.Health;
if ( newhealth != lasthealth ) updated = level.maptime+35;
if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE )
{
if ( mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && mytarget.CheckSight(mytarget.target) && (mytarget.Vec3To(mytarget.target).length() < 2000) ) updated = level.maptime+70;
if ( mytarget.player ) updated = level.maptime+35;
if ( players[consoleplayer].mo.CheckSight(mytarget) && ((mytarget.Vec3To(players[consoleplayer].mo).length() < 600) || (players[consoleplayer].mo.AimTarget() == mytarget)) ) updated = level.maptime;
}
lasthealth = newhealth;
intp.Update(lasthealth);
}
}
// One-liners
Class SWWMOneLiner : HUDMessageBase
{
String whichline;
int lifespan, curtime;
transient Font TewiFont;
transient CVar safezone;
static SWWMOneLiner Make( String whichline, int lifespan )
{
let l = new("SWWMOneLiner");
l.whichline = whichline;
l.curtime = l.lifespan = lifespan;
return l;
}
override bool Tick()
{
if ( players[consoleplayer].Health <= 0 ) curtime = int.min;
curtime--;
return (curtime<-20);
}
override void Draw( int bottom, int visibility )
{
if ( !safezone ) safezone = CVar.GetCVar('swwm_hudmargin',players[consoleplayer]);
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
int margin = safezone.GetInt();
Vector2 hs = StatusBar.GetHUDScale();
Vector2 ss = (Screen.GetWidth()/hs.x,Screen.GetHeight()/hs.y);
String loc = StringTable.Localize(whichline);
if ( loc.Length() <= 0 ) return; // don't draw empty strings
String locs = "\""..loc.."\"";
// split so it can fit
BrokenLines l = TewiFont.BreakLines(locs,int(ss.x*.5));
int maxlen = 0;
for ( int i=0; i<l.Count(); i++ )
{
int len = TewiFont.StringWidth(l.StringAt(i));
if ( len > maxlen ) maxlen = len;
}
int h = TewiFont.GetHeight();
int fh = h*l.Count();
double alph = clamp((curtime/20.)+1.,0.,1.);
alph *= clamp((lifespan-curtime)/10.,0.,1.);
Screen.Dim("Black",alph*.8,int((Screen.GetWidth()-(maxlen+12)*hs.x)/2.),int(bottom-(margin+2+fh)*hs.y),int((maxlen+12)*hs.x),int((fh+4)*hs.y));
int yy = margin+fh;
for ( int i=0; i<l.Count(); i++ )
{
int len = TewiFont.StringWidth(l.StringAt(i));
Screen.DrawText(TewiFont,Font.CR_FIRE,(ss.x-len)/2.,(bottom/hs.y)-yy,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
yy -= h;
}
}
}
// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop
Class PaletteLight : DynamicLight
{
Color pal[256];
bool IsLooping;
Default
{
Tag "Explosion";
DynamicLight.Type "Point";
Args 0,0,0,80;
ReactionTime 15;
}
private void UpdateLight()
{
int index = 255-((255*ReactionTime)/abs(default.ReactionTime));
args[LIGHT_RED] = pal[index].r;
args[LIGHT_GREEN] = pal[index].g;
args[LIGHT_BLUE] = pal[index].b;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",GetTag()));
String paldat = Wads.ReadLump(lump);
for ( int i=0; i<256; i++ )
{
pal[i].r = paldat.ByteAt(i*3);
pal[i].g = paldat.ByteAt(i*3+1);
pal[i].b = paldat.ByteAt(i*3+2);
}
if ( ReactionTime < 0 )
{
ReactionTime = -ReactionTime;
IsLooping = true;
}
UpdateLight();
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
ReactionTime--;
if ( ReactionTime < 0 )
{
if ( !IsLooping )
{
Destroy();
return;
}
else ReactionTime = abs(default.ReactionTime);
}
UpdateLight();
}
}
// Generic particles
Class SWWMSmoke : Actor
{
Default
{
RenderStyle "Shaded";
StencilColor "FFFFFF";
Radius 2;
Height 2;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
+THRUACTORS;
+NOTELEPORT;
+CANBOUNCEWATER;
-BOUNCEAUTOOFF;
BounceType "Hexen";
BounceFactor 1.0;
WallBounceFactor 1.0;
Scale 0.3;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
roll = Frandom[Puff](0,360);
scale.x *= RandomPick[Puff](-1,1);
scale.y *= RandomPick[Puff](-1,1);
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
vel *= 0.96;
vel.z += 0.01;
if ( waterlevel > 0 )
{
let b = Spawn("SWWMBubble",pos);
b.scale *= abs(scale.x);
b.vel = vel;
Destroy();
}
}
States
{
Spawn:
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
Stop;
}
}
Class SWWMSmallSmoke : SWWMSmoke
{
override void PostBeginPlay()
{
Actor.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.1,0.3);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
}
}
Class SWWMViewSmoke : SWWMSmoke
{
Vector3 ofs, vvel;
override void PostBeginPlay()
{
Actor.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.1,0.3);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vvel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
}
override void Tick()
{
Actor.Tick();
if ( !target || !target.player )
{
Destroy();
return;
}
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
SetOrigin(origin,true);
bInvisible = (players[consoleplayer].camera != target);
if ( isFrozen() ) return;
ofs += vvel;
vvel *= 0.96;
vvel.z += 0.01;
if ( waterlevel > 0 ) Destroy();
}
}
Class SWWMBubble : Actor
{
Default
{
RenderStyle "Add";
Radius 2;
Height 2;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.5;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
if ( waterlevel <= 0 ) Destroy();
SetState(ResolveState("Spawn")+Random[Puff](0,19));
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
vel *= 0.96;
vel.z += 0.05;
if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy();
}
States
{
Spawn:
XBUB ABCDEFGHIJKLMNOPQRST 1;
Loop;
}
}
Class SWWMSpark : Actor
{
Default
{
RenderStyle "Add";
Radius 2;
Height 2;
+NOBLOCKMAP;
+FORCEXYBILLBOARD;
+MISSILE;
+MOVEWITHSECTOR;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
BounceType "Doom";
BounceFactor 0.4;
Gravity 0.2;
Scale 0.05;
}
override void Tick()
{
Super.Tick();
if ( waterlevel > 0 )
{
let b = Spawn("SWWMBubble",pos);
b.vel = vel;
b.scale *= 0.3;
Destroy();
}
}
States
{
Spawn:
BLPF A 1 Bright A_FadeOut(0.01);
Wait;
Death:
BLPF A 1 Bright A_FadeOut(0.05);
Wait;
}
}
Class SWWMChip : Actor
{
int deadtimer;
double rollvel;
Default
{
Radius 2;
Height 2;
+NOBLOCKMAP;
+MISSILE;
+MOVEWITHSECTOR;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
BounceType "Doom";
BounceFactor 0.3;
Gravity 0.35;
Scale 0.2;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
deadtimer = 0;
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
frame = Random[Junk](0,5);
scale *= Frandom[Junk](0.8,1.2);
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
if ( InStateSequence(CurState,ResolveState("Death")) )
{
deadtimer++;
if ( deadtimer > 300 ) A_FadeOut(0.05);
return;
}
}
States
{
Spawn:
JUNK # 1
{
roll += rollvel;
}
Loop;
Bounce:
JUNK # 0
{
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
}
Goto Spawn;
Death:
JUNK # -1;
Stop;
Dummy:
JUNK ABCDEF -1;
Stop;
}
}
Class SWWMNothing : Actor
{
States
{
Spawn:
TNT1 A 1;
Stop;
}
}
Class PoofLight : PaletteLight
{
Default
{
Tag "Yellow";
ReactionTime 5;
Args 0,0,0,60;
}
}
Class SWWMItemFog : Actor
{
Default
{
RenderStyle "Add";
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+ROLLSPRITE;
+ROLLCENTER;
}
States
{
Spawn:
BLPF A 2 Bright NoDelay
{
// offset up
SetOrigin(Vec3Offset(0,0,16),false);
roll = FRandom[ExploS](0,360);
scale *= FRandom[ExploS](0.9,1.1);
scale.x *= RandomPick[ExploS](-1,1);
scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](16,32);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8);
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(3,2,1)*Random[ExploS](64,85));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 3.;
s.alpha *= .2;
}
Spawn("PoofLight",pos);
}
BLPF A 1 Bright A_FadeOut(.3);
Wait;
}
}
Class TeleLight : PaletteLight
{
Default
{
Tag "ImpactWav";
ReactionTime 10;
Args 0,0,0,150;
}
}
Class SWWMTeleportFog : Actor
{
Default
{
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
SetOrigin(Vec3Offset(0,0,28),false);
A_StartSound("misc/teleport",CHAN_VOICE);
Spawn("TeleLight",pos);
}
States
{
Spawn:
TNT1 A 1
{
int numpt = int(Random[ExploS](16,32)*alpha);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8)*alpha;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,2,3)*int(Random[ExploS](64,85)*alpha));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 3.*alpha;
s.alpha *= alpha;
}
A_FadeOut();
}
Wait;
}
}
// Bullet trails from DT
Class WaterHit
{
Sector sect;
Vector3 hitpos;
}
Class InvisibleSplasher : Actor
{
Default
{
Mass 100;
VSpeed -2;
}
States
{
Spawn:
TNT1 A 2;
Stop;
}
}
Class SmolInvisibleSplasher : InvisibleSplasher
{
Default
{
Mass 25;
}
}
Class SWWMBulletTrail : LineTracer
{
Array<WaterHit> WaterHitList;
Array<Line> ShootThroughList;
Actor ignoreme;
static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, int dist, int bubblechance, bool smoky = false )
{
let t = new("SWWMBulletTrail");
t.ignoreme = target;
t.WaterHitList.Clear();
t.ShootThroughList.Clear();
t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bubblechance) ) continue;
let b = Actor.Spawn(smoky?"SWWMSmallSmoke":"SWWMBubble",level.Vec3Offset(pos,dir*i));
b.Scale *= FRandom[Boolet](.4,.6);
}
t.Destroy();
}
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
// Elastic recoil from DT
Enum ESwingMode
{
SWING_Straight, // constant increment
SWING_Spring, // bounces back after a delay
};
Class Swinger : Thinker
{
Actor target;
Vector2 dir;
double inc, rmul;
int steps, mode, delay;
double str, tstr;
int cnt, cstate;
Enum ESwingerState
{
STATE_Initial,
STATE_Wait,
STATE_Return,
};
override void Tick()
{
if ( !target ) cstate = -1;
switch ( cstate )
{
case STATE_Initial:
target.A_SetAngle(target.angle+dir.x*str,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch+dir.y*str,SPF_INTERPOLATE);
str += inc;
if ( ++cnt >= steps )
{
cnt = 0;
str = tstr/steps;
cstate = (mode==SWING_Straight)?(-1):(delay>0)?STATE_Wait:STATE_Return;
}
else tstr += str;
break;
case STATE_Wait:
if ( ++cnt >= delay )
{
cnt = 0;
cstate = STATE_Return;
}
break;
case STATE_Return:
target.A_SetAngle(target.angle-dir.x*(str/rmul),SPF_INTERPOLATE);
target.A_SetPitch(target.pitch-dir.y*(str/rmul),SPF_INTERPOLATE);
if ( ++cnt >= steps*rmul )
{
cnt = 0;
cstate = -1;
}
break;
default:
Destroy();
return;
}
}
}
// Screen flashes from DT
Class GenericFlash : HUDMessageBase
{
Color col;
int duration;
double alpha;
Actor cam;
transient CVar str;
GenericFlash Setup( Actor camera, Color c, int d )
{
alpha = 1.0;
col = c;
duration = d;
cam = camera;
return self;
}
override bool Tick()
{
if ( duration > 0 ) alpha -= 1./duration;
return (alpha<=0)||(!cam);
}
override void Draw( int bottom, int visibility )
{
if ( automapactive || (visibility != BaseStatusBar.HUDMSGLayer_UnderHUD) ) return;
if ( cam && (players[consoleplayer].camera != cam) ) return;
if ( !str ) str = CVar.GetCVar('swwm_flashstrength',players[consoleplayer]);
Screen.Dim(col,(col.a/255.)*alpha*str.GetFloat(),0,0,Screen.GetWidth(),Screen.GetHeight());
}
}
Class QueuedFlash
{
Color c;
int duration;
int tic;
Actor cam;
}
Class LastLine
{
String type;
int lineno;
}
// 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;
bool tookdamage[MAXPLAYERS];
int spreecount[MAXPLAYERS];
int lastkill[MAXPLAYERS];
int multilevel[MAXPLAYERS];
int lastitemcount[MAXPLAYERS];
transient CVar mutevoice;
static int AddOneliner( String type, int level, int delay = 5 )
{
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;
int countem = 0, i = 1;
String testme, locme;
do
{
testme = String.Format("SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),i);
locme = StringTable.Localize(testme,false);
if ( testme != locme ) countem++;
i++;
}
while ( (testme != locme) && (i < 100) ); // gotta prevent infinite loops
if ( countem == 0 )
{
if ( voicetype ~== "default" ) return 0;
// retry with the default voicetype
voicetype = "default";
i = 1;
do
{
testme = String.Format("SWWM_SUBS_DEFAULT_%s%d",type.MakeUpper(),i);
locme = StringTable.Localize(testme,false);
if ( testme != locme ) countem++;
i++;
}
while ( (testme != locme) && (i < 100) ); // gotta prevent infinite loops
if ( countem == 0 ) return 0;
}
// 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 ( 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;
// TODO check for any 3d floors
for ( int i=0; i<l.frontsector.Get3DFloorCount(); i++ )
{
}
for ( int i=0; i<l.backsector.Get3DFloorCount(); i++ )
{
}
// TODO check for midtex
// 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;
}
override void WorldLoaded( WorldEvent e )
{
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<int> 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 ( (l.special != Exit_Normal) && (l.special != Exit_Secret) && (l.special != Teleport_EndGame) && (l.special != Teleport_NewMap) )
continue;
if ( skipme.Find(l.Index()) < 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;
skipme.Push(l2.Index());
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;
skipme.Push(l2.Index());
xcnt++;
lpos += UseLinePos(l2);
}
}
lpos /= xcnt;
SWWMInterest.Spawn(lpos,theline:l);
}
}
else if ( e.IsSaveGame || e.IsReopen )
{
// clear all floating numbers
let ti = ThinkerIterator.Create("SWWMScoreObj",Thinker.STAT_USER);
Thinker t;
while ( t = ti.Next() )
t.Destroy();
}
}
override void PlayerDied( PlayerEvent e )
{
let s = SWWMStats.Find(players[e.playernumber]);
if ( s ) s.deaths++;
}
override void PlayerEntered( PlayerEvent e )
{
// create some static thinkers for this player
PlayerInfo p = players[e.playernumber];
SWWMTradeHistory th = SWWMTradeHistory.Find(p);
if ( !th )
{
th = new("SWWMTradeHistory");
th.ChangeStatNum(Thinker.STAT_STATIC);
th.myplayer = p;
}
SWWMCredits c = SWWMCredits.Find(p);
if ( !c )
{
c = new("SWWMCredits");
c.ChangeStatNum(Thinker.STAT_STATIC);
c.myplayer = p;
}
SWWMLoreLibrary l = SWWMLoreLibrary.Find(p);
if ( !l )
{
l = new("SWWMLoreLibrary");
l.ChangeStatNum(Thinker.STAT_STATIC);
l.myplayer = p;
}
// pre-add some entries to start with
l.DirectAdd("Demolitionist");
l.DirectAdd("KnowledgeBase");
l.DirectAdd("Saya");
l.DirectAdd("UAC");
if ( gameinfo.gametype&GAME_Raven )
{
l.DirectAdd("Parthoris");
l.DirectAdd("SerpentRiders");
l.DirectAdd("Sidhe");
}
if ( gameinfo.gametype&GAME_Hexen )
l.DirectAdd("Cronos");
SWWMStats s = SWWMStats.Find(p);
if ( !s )
{
s = new("SWWMStats");
s.ChangeStatNum(Thinker.STAT_STATIC);
s.myplayer = p;
s.lastspawn = gametic;
s.favweapon = -1;
}
// reset some vars
multilevel[e.playernumber] = 0;
spreecount[e.playernumber] = 0;
tookdamage[e.playernumber] = false;
lastkill[e.playernumber] = int.min;
// reset combat tracker
SWWMCombatTracker.Spawn(players[e.playernumber].mo);
}
override void PlayerRespawned( PlayerEvent e )
{
PlayerEntered(e);
}
override void WorldThingRevived( WorldEvent e )
{
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 = gametic;
}
override void WorldTick()
{
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 = 25*(players[i].itemcount-lastitemcount[i]);
if ( level.total_items == level.found_items )
{
Console.Printf(StringTable.Localize("$SWWM_LASTITEM"),players[i].GetUserName(),2000);
score += 2475;
}
SWWMCredits.Give(players[i],score);
lastitemcount[i] = players[i].itemcount;
}
}
// 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 && !a.bCountKill ) continue;
// ignore the dead
if ( (a.Health <= 0) || a.bKILLED || a.bCORPSE ) continue;
// ignore if not targetted or either actor can't see the other
if ( (a.target != players[consoleplayer].mo)
|| !a.CheckSight(players[consoleplayer].mo)
|| !players[consoleplayer].mo.CheckSight(a) ) 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+100)) )
lastcombat = AddOneliner("fightstart",1,10);
}
override void WorldThingDied( WorldEvent e )
{
if ( (e.Thing.default.bBOSS && !Random[GoldDrop](0,2)) || (e.Thing.default.bBOSSDEATH && !Random[GoldDrop](0,6)) )
{
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.);
}
}
override void WorldThingDamaged( WorldEvent e )
{
if ( e.Damage > 0 )
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);
if ( e.Thing.player )
{
tookdamage[e.Thing.PlayerNumber()] = true;
let s = SWWMStats.Find(e.Thing.player);
s.damagetaken += 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.damagedealt += e.Damage;
if ( e.Damage > s.topdealt ) s.topdealt = e.Damage;
}
if ( e.DamageSource && (e.DamageSource != e.Thing) )
{
if ( (e.DamageSource.bISMONSTER || e.DamageSource.player) && (e.Thing == players[consoleplayer].mo) && (e.Thing.Health > 0) )
{
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner(e.Thing.IsFriend(e.DamageSource)?"friendhit":"gethit",1,15);
highesttic = gametic;
}
if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player) )
{
// 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) && !(e.Thing is 'LampMoth') )
{
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner("hitfriend",1,15);
highesttic = gametic;
}
}
}
if ( (e.Thing.Health > 0) || e.Thing.bKilled || e.Thing.bCorpse ) return;
if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill ) return;
if ( (e.DamageSource && e.DamageSource.player && (e.DamageSource != e.Thing)) )
{
let s = SWWMStats.Find(e.DamageSource.player);
if ( s )
{
s.kills++;
if ( e.DamageSource.player.ReadyWeapon )
s.AddWeaponKill(e.DamageSource.player.ReadyWeapon.GetClass());
}
if ( e.DamageSource == players[consoleplayer].mo )
{
highesttic = gametic;
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner("scorekill",1,15);
}
if ( !e.Thing.default.bCountKill ) // no credits
return;
int pnum = e.DamageSource.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(2000,int(ceil(e.Thing.SpawnHealth()*.5)*10));
int ofs = 0;
if ( ((e.Thing.Health <= e.Thing.GetGibHealth()) || (e.DamageSource.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH)) && !e.DamageSource.bNOEXTREMEDEATH && !(e.Inflictor && e.Inflictor.bNOEXTREMEDEATH) )
{
score = int(score*1.25);
if ( e.DamageSource.player == players[consoleplayer] )
SWWMScoreObj.Spawn(0,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_OVERKILL",++ofs);
}
score = int(score*(1.+.5*min(multilevel[pnum],16)));
if ( (multilevel[pnum] > 0) && (e.DamageSource.player == players[consoleplayer]) )
SWWMScoreObj.Spawn((multilevel[pnum]>=16)?int.max:(multilevel[pnum]+1),e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_MULTIKILL",++ofs);
if ( !tookdamage[pnum] )
{
score += 100+50*spreecount[pnum];
if ( (spreecount[pnum] > 0) && (e.DamageSource.player == players[consoleplayer]) )
SWWMScoreObj.Spawn(spreecount[pnum]+1,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_SPREEKILL",++ofs);
}
if ( e.Thing.bBOSS )
{
score += 10000;
if ( e.DamageSource.player == players[consoleplayer] )
SWWMScoreObj.Spawn(0,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_BOSSKILL",++ofs);
}
SWWMCredits.Give(e.DamageSource.player,score);
if ( e.DamageSource.player == players[consoleplayer] )
SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
if ( level.killed_monsters+1 == level.total_monsters )
{
SWWMCredits.Give(e.DamageSource.player,5000);
Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),e.DamageSource.player.GetUserName(),5000);
}
spreecount[pnum]++;
}
}
private void DoKeyTagFix( Actor a )
{
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);
}
override void WorldThingSpawned( WorldEvent e )
{
if ( e.Thing is 'Key' )
{
DoKeyTagFix(e.Thing);
SWWMInterest.Spawn(thekey:Key(e.Thing));
}
if ( (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') ) SWWMCombatTracker.Spawn(e.Thing);
}
override void PostUiTick()
{
if ( (gametic == onelinertic) && (oneliner != "") && (players[consoleplayer].health > 0) )
{
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);
}
}
override void WorldLinePreActivated( WorldEvent e )
{
// oneliner on locked doors
if ( !e.Thing ) return;
int locknum = e.ActivatedLine.locknumber;
if ( !locknum )
{
// check the special
switch ( e.ActivatedLine.special )
{
case FS_Execute:
locknum = e.ActivatedLine.Args[2];
break;
case Door_LockedRaise:
case Door_Animated:
locknum = e.ActivatedLine.Args[3];
break;
case ACS_LockedExecute:
case ACS_LockedExecuteDoor:
case Generic_Door:
locknum = e.ActivatedLine.Args[4];
break;
}
}
if ( !locknum ) return;
if ( e.Thing.CheckLocalView() && !e.Thing.CheckKeys(locknum,false,true) )
{
if ( !lastlock || (gametic > lastlock+20) )
lastlock = AddOneliner("locked",2);
}
}
override void CheckReplacement( ReplaceEvent e )
{
// shell types (sorted by rarity
static const Class<Actor> redpool[] = {"RedShell","RedShell2","RedShell4","RedShell8","RedShell12","RedShell16"};
static const Class<Actor> greenpool[] = {"GreenShell","GreenShell2","GreenShell4","GreenShell8","GreenShell12"};
static const Class<Actor> whitepool[] = {"WhiteShell","WhiteShell2","WhiteShell4","WhiteShell8"};
static const Class<Actor> purplepool[] = {"PurpleShell","PurpleShell2","PurpleShell4"};
static const Class<Actor> bluepool[] = {"BlueShell","BlueShell2","BlueShell4","BlueShell8"};
static const Class<Actor> blackpool[] = {"BlackShell","BlackShell2","BlackShell4"};
if ( e.Replacee is 'ItemFog' ) e.Replacement = 'SWWMItemFog';
if ( e.Replacee is 'TeleportFog' ) e.Replacement = 'SWWMTeleportFog';
else if ( (e.Replacee is 'Chainsaw') || (e.Replacee is 'Gauntlets') || (e.Replacee is 'FWeapAxe') ) 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' )
{
if ( Random[Replacements](0,2) ) e.Replacement = 'Spreadgun';
else e.Replacement = 'Wallbuster';
}
else if ( (e.Replacee is 'Chaingun') || (e.Replacee is 'Blaster') || (e.Replacee is 'FWeapHammer') ) e.Replacement = 'Eviscerator';
else if ( (e.Replacee is 'RocketLauncher') || (e.Replacee is 'PhoenixRod') || (e.Replacee is 'CWeapFlame') ) e.Replacement = 'Hellblazer';
else if ( (e.Replacee is 'PlasmaRifle') || (e.Replacee is 'SkullRod') )
{
if ( Random[Replacements](0,2) ) e.Replacement = 'Sparkster';
else e.Replacement = 'SilverBullet';
}
else if ( e.Replacee is 'MWeapLightning' ) e.Replacement = 'Sparkster';
else if ( e.Replacee is 'FWeaponPiece3' ) e.Replacement = 'SilverBullet';
else if ( (e.Replacee is 'BFG9000') || (e.Replacee is 'Mace') )
{
if ( !Random[Replacements](0,2) ) e.Replacement = 'Ynykron';
else e.Replacement = 'CandyGun';
}
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') || (e.Replacee is 'ArtiPoisonBag') )
{
switch( Random[Replacement](0,6) )
{
case 0:
case 1:
case 2:
e.Replacement = redpool[Random[Replacement](0,2)];
break;
case 3:
case 4:
e.Replacement = greenpool[Random[Replacement](0,2)];
break;
case 5:
e.Replacement = whitepool[Random[Replacement](0,1)];
break;
case 6:
e.Replacement = purplepool[0];
break;
}
}
else if ( (e.Replacee == 'Shell') || (e.Replacee is 'CrossbowAmmo') )
{
switch( Random[Replacement](0,10) )
{
case 0:
case 1:
case 2:
e.Replacement = redpool[Random[Replacement](2,3)];
break;
case 3:
case 4:
e.Replacement = greenpool[Random[Replacement](2,3)];
break;
case 5:
case 6:
e.Replacement = whitepool[Random[Replacement](1,2)];
break;
case 7:
case 8:
e.Replacement = purplepool[Random[Replacement](0,2)];
break;
case 9:
e.Replacement = bluepool[Random[Replacement](0,2)];
break;
case 10:
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:
case 3:
e.Replacement = redpool[Random[Replacement](3,5)];
break;
case 4:
case 5:
case 6:
e.Replacement = greenpool[Random[Replacement](3,4)];
break;
case 7:
case 8:
case 9:
e.Replacement = whitepool[Random[Replacement](2,3)];
break;
case 10:
case 11:
e.Replacement = purplepool[Random[Replacement](1,2)];
break;
case 12:
case 13:
e.Replacement = bluepool[Random[Replacement](2,3)];
break;
case 14:
e.Replacement = blackpool[Random[Replacement](0,2)];
break;
}
}
else if ( e.Replacee == 'ClipBox' )
{
if ( Random[Replacements](0,2) ) e.Replacement = 'EvisceratorShell';
else e.Replacement = 'EvisceratorSixPack';
}
else if ( e.Replacee == 'BlasterAmmo' ) e.Replacement = 'EvisceratorShell';
else if ( e.Replacee == 'BlasterHefty' ) e.Replacement = 'EvisceratorSixPack';
else if ( (e.Replacee == 'RocketAmmo') || (e.Replacee == 'PhoenixRodAmmo') || (e.Replacee == 'MaceAmmo') )
{
switch ( Random[Replacements](0,9) )
{
case 0:
case 1:
case 2:
case 3:
e.Replacement = 'HellblazerMissiles';
break;
case 4:
case 5:
case 6:
e.Replacement = 'HellblazerCrackshots';
break;
case 7:
case 8:
e.Replacement = 'HellblazerRavagers';
break;
case 9:
e.Replacement = 'HellblazerWarheads';
break;
}
}
else if ( (e.Replacee == 'RocketBox') || (e.Replacee == 'PhoenixRodHefty') || (e.Replacee == 'MaceHefty') )
{
switch ( Random[Replacements](0,9) )
{
case 0:
case 1:
case 2:
case 3:
e.Replacement = 'HellblazerMissileMag';
break;
case 4:
case 5:
case 6:
e.Replacement = 'HellblazerCrackshotMag';
break;
case 7:
case 8:
e.Replacement = 'HellblazerRavagerMag';
break;
case 9:
e.Replacement = 'HellblazerWarheadMag';
break;
}
}
else if ( (e.Replacee == 'Cell') || (e.Replacee == 'SkullRodAmmo') )
{
if ( Random[Replacements](0,3) ) e.Replacement = 'SparkUnit';
else e.Replacement = 'SilverBulletAmmo';
}
else if ( (e.Replacee == 'ArtiTeleport') || (e.Replacee == 'ArtiTeleportOther') )
{
if ( Random[Replacements](0,2) ) e.Replacement = 'SWWMNothing';
else e.Replacement = 'GoldShell';
}
else if ( (e.Replacee == 'CellPack') || (e.Replacee == 'SkullRodHefty') )
{
if ( Random[Replacements](0,2) ) e.Replacement = 'SilverBulletAmmo';
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 == 'ArtiPork') ) e.Replacement = 'HammerspaceEmbiggener';
else if ( (e.Replacee == 'FWeaponPiece1') || (e.Replacee == 'FWeaponPiece2')
|| (e.Replacee == 'CWeaponPiece1') || (e.Replacee == 'CWeaponPiece3')
|| (e.Replacee == 'MWeaponPiece2') || (e.Replacee == 'MWeaponPiece3') ) e.Replacement = 'SWWMNothing';
else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') || (e.Replacee == 'ArtiBlastRadius') ) 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' )
{
if ( gameinfo.gametype&GAME_Heretic && Random[Replacements](0,1) ) e.Replacement = 'CubeHealthItem';
else e.Replacement = 'TetraHealthItem';
}
else if ( (e.Replacee == 'Soulsphere') || (e.Replacee == 'ArtiSuperHealth') )
{
if ( gameinfo.gametype&GAME_Hexen ) e.Replacement = 'CubeHealthItem';
else e.Replacement = 'RefresherItem';
}
else if ( e.Replacee == 'ArtiHealingRadius' ) e.Replacement = 'RefresherItem';
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.Replacee == 'ArtiFly') || (e.Replacee == 'ArtiSpeedBoots') ) 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 == 'ArtiDarkServant') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'Ragekit';
else if ( (e.Replacee == 'AllMap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'Omnisight';
else if ( (e.Replacee == 'Infrared') || (e.Replacee == 'ArtiTorch') || (e.Replacee == 'ArtiDarkServant') || (e.Replacee == 'ArtiBoostArmor') ) 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';
}
override void NetworkProcess( ConsoleEvent e )
{
if ( e.Name ~== "swwmgesture" )
{
if ( (e.player == -1) || !playeringame[e.player] || !players[e.player].mo ) return;
let mo = players[e.player].mo;
if ( (mo.Health <= 0) || !(mo is 'Demolitionist') ) return;
switch ( e.Args[0] )
{
case 1:
if ( mo.FindState("Approve") ) mo.SetStateLabel("Approve");
break;
case 2:
if ( mo.FindState("Victory") ) mo.SetStateLabel("Victory");
break;
default:
if ( mo.FindState("Taunt") ) mo.SetStateLabel("Taunt");
break;
}
}
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]) )
{
players[e.Args[0]].mo.GiveInventory(item,e.Args[2]);
if ( item is 'Weapon' ) 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;
}
}
int ramt = players[e.Args[1]].mo.CountInv(item);
if ( amt > ramt ) amt = ramt;
if ( players[e.Args[1]].mo.GiveInventory(item,amt) )
{
players[e.Args[0]].mo.TakeInventory(item,amt);
// add to history
SWWMTradeHistory.RegisterSend(players[e.Args[0]],players[e.Args[1]],def.GetTag());
SWWMTradeHistory.RegisterReceive(players[e.Args[1]],players[e.Args[0]],def.GetTag());
// 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());
}
}
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;
players[e.Args[0]].mo.UseInventory(i);
}
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.));
}
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)));
}
}
// stuff for hud
override void RenderUnderlay( RenderEvent e )
{
if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return;
SWWMStatusBar(statusbar).viewpos = e.viewpos;
SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll);
}
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);
}
// Doom's explosions aren't fully 3D
static void DoBlast( Actor Source, double ExplosionRadius, double MomentumTransfer )
{
BlockThingsIterator bi = BlockThingsIterator.Create(Source,ExplosionRadius);
while ( bi.Next() )
{
Actor a = bi.Thing;
if ( !a || !a.bSHOOTABLE || !Source.CheckSight(a,0xf) || (a == Source) || (Source.Distance3D(a) > ExplosionRadius) || a.bCANNOTPUSH || (a.Mass >= 10000000) )
continue;
Vector3 midpoint = a.Vec3Offset(0,0,a.height*0.5);
Vector3 dir = Level.Vec3Diff(Source.pos,midpoint);
double dist = max(1,dir.length());
double damagescale = 1-max(0,(dist-a.radius)/ExplosionRadius);
dir = dir/dist;
a.vel += dir*damagescale*(MomentumTransfer/(Thinker.TICRATE*max(50,a.mass)));
}
}
// Same for this
static void DoKnockback( Actor Victim, Vector3 HitDirection, double MomentumTransfer )
{
if ( !Victim || !Victim.bSHOOTABLE || Victim.bCANNOTPUSH || (Victim.Mass >= 10000000) ) return;
Victim.vel += HitDirection*(MomentumTransfer/(Thinker.TICRATE*max(50,Victim.Mass)));
}
static void DoSwing( Actor target, Vector2 dir, double initial, double inc, int steps, int mode = 0, int delay = 0, double rmul = 1.0 )
{
let s = new("Swinger");
s.ChangeStatNum(Thinker.STAT_USER);
s.target = target;
s.dir = dir;
s.inc = inc;
s.rmul = rmul;
s.steps = steps;
s.mode = mode;
s.delay = delay;
s.cnt = 0;
s.cstate = 0;
s.str = initial;
s.tstr = initial;
}
}
// Fancy crash effect
Class SWWMCrashHandler : StaticEventHandler
{
ui bool wasinmap;
ui int timer;
override void UiTick()
{
if ( (gamestate == GS_LEVEL) || (gamestate == GS_TITLELEVEL) )
{
wasinmap = true;
timer = 0;
}
else if ( (gamestate == GS_FULLCONSOLE) && (wasinmap || (timer > 0)) )
{
wasinmap = false;
if ( timer == 1 )
{
Console.Printf(TEXTCOLOR_GOLD.."Oopsie Woopsie!"..TEXTCOLOR_NORMAL);
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 if ( timer == 140 )
{
Console.Printf(TEXTCOLOR_GOLD.."Looks like GZDoom made a fucky wucky! owo"..TEXTCOLOR_NORMAL);
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(TEXTCOLOR_GOLD.."Don't blame me. Shouldn't have tried running this with Brutal Doom."..TEXTCOLOR_NORMAL);
else Console.Printf(TEXTCOLOR_GOLD.."If you didn't trigger it manually, it's best if you take a screenshot and show it to Marisa."..TEXTCOLOR_NORMAL);
}
timer++;
}
}
}
// >loading brutal doom with this
Class SWWMBrutalHandler : StaticEventHandler
{
ui int timer;
ui TextureID scr;
bool detected;
override void OnRegister()
{
for ( int i=0; i<AllActorClasses.size(); i++ )
{
if ( (AllActorClasses[i].GetClassName() != "BrutalWeapon")
&& (AllActorClasses[i].GetClassName() != "BrutalDoomer") ) continue;
detected = true;
break;
}
}
override void UiTick()
{
if ( !detected ) return;
if ( (gamestate == GS_LEVEL) || (gamestate == GS_TITLELEVEL) )
{
if ( timer == 1 )
{
S_StartSound("brutal/ezmodo",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
S_StartSound("brutal/ezmodo",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
}
else if ( timer == 350 ) ThrowAbortException(">Brutal Doom");
timer++;
}
else timer = 0;
}
override void WorldTick()
{
if ( !detected ) return;
for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] ) players[i].cheats |= CF_TOTALLYFROZEN;
}
override void RenderOverlay( RenderEvent e )
{
if ( !detected ) return;
if ( scr.IsNull() ) scr = TexMan.CheckForTexture("graphics/bdscreen.png",TexMan.Type_Any);
Screen.Dim("Red",(timer/350.)-.2,0,0,Screen.GetWidth(),Screen.GetHeight());
Screen.DrawTexture(scr,false,FRandom[bdscreen](-1,1)*max(timer-40,0)**3*.000003,FRandom[bdscreen](-1,1)*max(timer-40,0)**3*.000003,DTA_VirtualWidth,1280,DTA_VirtualHeight,960,DTA_Alpha,min(1.,timer/50.));
Screen.Dim("Red",(timer/70.)-3.5,0,0,Screen.GetWidth(),Screen.GetHeight());
}
}