Added Lead Ball ammo type to Spreadgun.
Partial implementation of Fuck Your Shit rounds, currently in progress. Added various shader effects to some powerups, and to player damage. Added custom view effects to player death, disabled "face attacker" because it looks weird with model-based players. Added "untouchable" spree tracking to the Stats tab. Implemented "emergency reboot system" for people who want a less shameful form of the Resurrect cheat. Cooldown for consecutive reboots can be configured. Rebalanced armors. Small language string corrections. Adjusted pickup model sizes of some weapons. Fixed missing punch sound (damn typos). Fix targetter always displaying voodoo dolls. Fix uptime breaking when loading saves, now based on total playtime rather than gametic. Readjusted Spreadgun ammo availability. Flush HUD interpolators alongside messages, fixes things such as the score VERY slowly counting up when loading a save. Spare armors now only get auto-used on pickup if there is NO armor available of that type. Added some extra visual effects to punching walls and non-bleeding actors. Slightly altered the melee range so it's not as awkward. Fixed punching not using flesh sounds for bleeding actors. Pusher primary now drags the player towards their target, like the Chainsaw. Fixed the player having no pain sounds whatsoever. Fixed stair step anchoring not working. The damage dealt when walljumping on a monster now also gets boosted by the Ragekit.
This commit is contained in:
parent
a4994ee132
commit
d1b1a0541d
33 changed files with 1112 additions and 89 deletions
|
|
@ -13,7 +13,7 @@ Class ArmorNugget : SWWMArmor
|
|||
|
||||
override int HandleDamage( int damage, Name damageType, int flags )
|
||||
{
|
||||
double factor = amount*.01;
|
||||
double factor = amount*.1;
|
||||
return int(ceil(damage*factor));
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,9 @@ Class BlastSuit : SWWMArmor
|
|||
Default
|
||||
{
|
||||
Inventory.Icon "graphics/HUD/Icons/I_BlastSuit.png";
|
||||
Inventory.Amount 200;
|
||||
Inventory.MaxAmount 200;
|
||||
Inventory.InterHubAmount 200;
|
||||
Inventory.Amount 150;
|
||||
Inventory.MaxAmount 150;
|
||||
Inventory.InterHubAmount 150;
|
||||
SWWMArmor.ArmorPriority 2;
|
||||
SWWMArmor.DrainMessage "$D_BLASTSUIT";
|
||||
SWWMArmor.GiverArmor "BlastSuitItem";
|
||||
|
|
@ -67,7 +67,7 @@ Class BlastSuit : SWWMArmor
|
|||
|
||||
override int HandleDamage( int damage, Name damageType, int flags )
|
||||
{
|
||||
double factor = .75;
|
||||
double factor = .3;
|
||||
if ( flags&DMG_EXPLOSION ) factor = 1.-(1.-factor)*.5;
|
||||
return int(ceil(damage*factor));
|
||||
}
|
||||
|
|
@ -102,9 +102,9 @@ Class WarArmor : SWWMArmor
|
|||
Default
|
||||
{
|
||||
Inventory.Icon "graphics/HUD/Icons/I_WarArmor.png";
|
||||
Inventory.Amount 600;
|
||||
Inventory.MaxAmount 600;
|
||||
Inventory.InterHubAmount 600;
|
||||
Inventory.Amount 250;
|
||||
Inventory.MaxAmount 250;
|
||||
Inventory.InterHubAmount 250;
|
||||
SWWMArmor.ArmorPriority 6;
|
||||
SWWMArmor.DrainMessage "$D_WARARMOR";
|
||||
SWWMArmor.GiverArmor "WarArmorItem";
|
||||
|
|
@ -114,8 +114,8 @@ Class WarArmor : SWWMArmor
|
|||
{
|
||||
double factor;
|
||||
// should be enough "elemental" damage types I guess
|
||||
if ( (damageType == 'Fire') || (damageType == 'Ice') || (damageType == 'Slime') || (damageType == 'Lightning') || (damageType == 'Wind') || (damageType == 'Water') ) factor = .9;
|
||||
else factor = .8;
|
||||
if ( (damageType == 'Fire') || (damageType == 'Ice') || (damageType == 'Slime') || (damageType == 'Lightning') || (damageType == 'Wind') || (damageType == 'Water') ) factor = .8;
|
||||
else factor = .5;
|
||||
if ( flags&DMG_EXPLOSION ) factor = 1.-(1.-factor)*.7;
|
||||
return int(ceil(damage*factor));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Class SWWMStats : Thinker
|
|||
PlayerInfo myplayer;
|
||||
int lastspawn, dashcount, boostcount, stompcount, airtime, kills,
|
||||
deaths, damagedealt, damagetaken, mkill, hiscore, topdealt,
|
||||
toptaken;
|
||||
toptaken, skill;
|
||||
double grounddist, airdist, fuelusage, topspeed;
|
||||
Array<WeaponUsage> wstats;
|
||||
Array<Class<Weapon> > alreadygot;
|
||||
|
|
@ -596,8 +596,11 @@ Class SWWMCombatTracker : Thinker
|
|||
if ( newhealth != lasthealth ) updated = level.maptime+35;
|
||||
if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE )
|
||||
{
|
||||
// enemies within 2000mu that have us as target
|
||||
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;
|
||||
// players (but not voodoo dolls), always visible in sp/coop
|
||||
if ( !deathmatch && mytarget.player && (mytarget.player.mo == mytarget) ) updated = level.maptime+35;
|
||||
// enemies we're directly aiming at within 600mu
|
||||
if ( players[consoleplayer].mo.CheckSight(mytarget) && ((mytarget.Vec3To(players[consoleplayer].mo).length() < 600) || (players[consoleplayer].mo.AimTarget() == mytarget)) ) updated = level.maptime;
|
||||
}
|
||||
lasthealth = newhealth;
|
||||
|
|
@ -1544,6 +1547,12 @@ Class SWWMHandler : EventHandler
|
|||
Demolitionist(players[i].mo).CheckUnderwaterAmb(true);
|
||||
}
|
||||
}
|
||||
PlayerInfo p = players[consoleplayer];
|
||||
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);
|
||||
}
|
||||
|
||||
override void PlayerDied( PlayerEvent e )
|
||||
|
|
@ -1596,7 +1605,7 @@ Class SWWMHandler : EventHandler
|
|||
s = new("SWWMStats");
|
||||
s.ChangeStatNum(Thinker.STAT_STATIC);
|
||||
s.myplayer = p;
|
||||
s.lastspawn = gametic;
|
||||
s.lastspawn = level.totaltime;
|
||||
s.favweapon = -1;
|
||||
}
|
||||
// reset some vars
|
||||
|
|
@ -1640,7 +1649,7 @@ Class SWWMHandler : EventHandler
|
|||
}
|
||||
// reset uptime since player had just died
|
||||
SWWMStats s = SWWMStats.Find(e.Thing.player);
|
||||
if ( s ) s.lastspawn = gametic;
|
||||
if ( s ) s.lastspawn = level.totaltime;
|
||||
}
|
||||
|
||||
override void WorldTick()
|
||||
|
|
@ -1865,6 +1874,8 @@ Class SWWMHandler : EventHandler
|
|||
Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),e.DamageSource.player.GetUserName(),5000);
|
||||
}
|
||||
spreecount[pnum]++;
|
||||
if ( s && spreecount[pnum] > s.skill )
|
||||
s.skill = spreecount[pnum];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2002,7 +2013,6 @@ Class SWWMHandler : EventHandler
|
|||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
e.Replacement = redpool[Random[Replacement](0,1)];
|
||||
break;
|
||||
case 4:
|
||||
|
|
@ -2023,67 +2033,65 @@ Class SWWMHandler : EventHandler
|
|||
}
|
||||
else if ( (e.Replacee == 'Shell') || (e.Replacee is 'CrossbowAmmo') )
|
||||
{
|
||||
switch( Random[Replacement](0,14) )
|
||||
switch( Random[Replacement](0,13) )
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
e.Replacement = redpool[Random[Replacement](1,2)];
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
e.Replacement = greenpool[Random[Replacement](1,2)];
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
e.Replacement = whitepool[Random[Replacement](0,1)];
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
e.Replacement = purplepool[Random[Replacement](0,1)];
|
||||
break;
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
e.Replacement = bluepool[Random[Replacement](0,2)];
|
||||
break;
|
||||
case 14:
|
||||
case 13:
|
||||
e.Replacement = blackpool[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( (e.Replacee == 'ShellBox') || (e.Replacee is 'CrossbowHefty') )
|
||||
{
|
||||
switch( Random[Replacement](0,15) )
|
||||
switch( Random[Replacement](0,14) )
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
e.Replacement = redpool[Random[Replacement](2,5)];
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
e.Replacement = greenpool[Random[Replacement](2,4)];
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
e.Replacement = whitepool[Random[Replacement](1,3)];
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
e.Replacement = purplepool[Random[Replacement](1,2)];
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
e.Replacement = bluepool[Random[Replacement](2,3)];
|
||||
break;
|
||||
case 15:
|
||||
case 14:
|
||||
e.Replacement = blackpool[Random[Replacement](0,2)];
|
||||
break;
|
||||
}
|
||||
|
|
@ -2364,6 +2372,27 @@ Class SWWMHandler : EventHandler
|
|||
Shader.SetUniform1f(p,"InvinciShader","str",str);
|
||||
}
|
||||
else Shader.SetEnabled(p,"InvinciShader",false);
|
||||
if ( pc && (mo is 'Demolitionist') )
|
||||
{
|
||||
let demo = Demolitionist(mo);
|
||||
Shader.SetEnabled(p,"Glitch",true);
|
||||
Shader.SetEnabled(p,"Grain",true);
|
||||
Shader.SetUniform1f(p,"Glitch","Timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
||||
Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/Thinker.TICRATE);
|
||||
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*.3*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.7);
|
||||
Shader.SetUniform1f(p,"Grain","ni",noiz);
|
||||
noiz = min(lastdmg*.16*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.8);
|
||||
Shader.SetUniform1f(p,"Glitch","str1",noiz);
|
||||
noiz = min(lastdmg*.12*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),3.5);
|
||||
Shader.SetUniform1f(p,"Glitch","str2",noiz);
|
||||
}
|
||||
else
|
||||
{
|
||||
Shader.SetEnabled(p,"Glitch",false);
|
||||
Shader.SetEnabled(p,"Grain",false);
|
||||
}
|
||||
}
|
||||
|
||||
static void DoFlash( Actor camera, Color c, int duration )
|
||||
|
|
|
|||
5
zscript/swwm_funstuff.zsc
Normal file
5
zscript/swwm_funstuff.zsc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// collectable items that may drop sometimes (future feature)
|
||||
|
||||
Class SWWMCollectable : Inventory
|
||||
{
|
||||
}
|
||||
|
|
@ -48,6 +48,12 @@ Class SWWMStatusBar : BaseStatusBar
|
|||
|
||||
override void FlushNotify()
|
||||
{
|
||||
// flush interpolators (useful since this virtual gets called
|
||||
// when loading saves, too)
|
||||
HealthInter.Reset(CPlayer.Health);
|
||||
ScoreInter.Reset(SWWMCredits.Get(CPlayer));
|
||||
FuelInter.Reset((CPlayer.mo is 'Demolitionist')?int(Demolitionist(CPlayer.mo).dashfuel):0);
|
||||
DashInter.Reset((CPlayer.mo is 'Demolitionist')?int((40-Demolitionist(CPlayer.mo).dashcooldown)*3.):0);
|
||||
if ( level.maptime <= 1 )
|
||||
{
|
||||
// flush ALL messages
|
||||
|
|
@ -683,12 +689,12 @@ Class SWWMStatusBar : BaseStatusBar
|
|||
}
|
||||
if ( ht > 200 )
|
||||
{
|
||||
hw = int(min(ht-100,400)*0.25);
|
||||
hw = int(min(ht-200,300)/3.);
|
||||
Screen.DrawTexture(HealthTex[2],false,margin+2,ss.y-(margin+15),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
|
||||
}
|
||||
if ( ht > 500 )
|
||||
{
|
||||
hw = int(min(ht-500,500)*0.2);
|
||||
hw = int(min(ht-500,500)/5.);
|
||||
Screen.DrawTexture(HealthTex[3],false,margin+2,ss.y-(margin+15),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
|
||||
}
|
||||
int hcolor = Font.CR_RED;
|
||||
|
|
@ -804,6 +810,50 @@ Class SWWMStatusBar : BaseStatusBar
|
|||
// don't do anything
|
||||
}
|
||||
|
||||
private void DrawDeath()
|
||||
{
|
||||
// death prompt
|
||||
let demo = Demolitionist(CPlayer.mo);
|
||||
if ( !demo || (CPlayer.Health > 0) ) return;
|
||||
String str;
|
||||
double alph;
|
||||
int len;
|
||||
double xx, yy;
|
||||
if ( demo.player.viewheight <= 6 )
|
||||
{
|
||||
Screen.Dim("Black",min(demo.deadtimer/80.,1.),0,0,Screen.GetWidth(),Screen.GetHeight());
|
||||
alph = clamp((demo.deadtimer-60)/60.,0.,1.);
|
||||
str = String.Format(StringTable.Localize("$SWWM_URDED"),CPlayer.GetUserName());
|
||||
len = mTewiFont.mFont.StringWidth(str);
|
||||
xx = (ss.x-len)/2.;
|
||||
yy = (ss.y-mTewiFont.mFont.GetHeight()*4)/2.;
|
||||
Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
|
||||
alph = clamp((demo.deadtimer-120)/60.,0.,1.);
|
||||
str = String.Format(StringTable.Localize("$SWWM_URDED2"),CPlayer.GetUserName());
|
||||
len = mTewiFont.mFont.StringWidth(str);
|
||||
xx = (ss.x-len)/2.;
|
||||
yy = ss.y/2.;
|
||||
Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
|
||||
if ( !swwm_revive )
|
||||
return;
|
||||
alph = clamp((demo.deadtimer-200)/60.,0.,1.);
|
||||
str = String.Format(StringTable.Localize("$SWWM_URDED3"),CPlayer.GetUserName());
|
||||
len = mTewiFont.mFont.StringWidth(str);
|
||||
xx = (ss.x-len)/2.;
|
||||
yy = (ss.y+mTewiFont.mFont.GetHeight()*2)/2.;
|
||||
Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
|
||||
if ( demo.revivefail > level.maptime )
|
||||
{
|
||||
str = StringTable.Localize("$SWWM_REFAIL");
|
||||
len = mTewiFont.mFont.StringWidth(str);
|
||||
xx = (ss.x-len)/2.;
|
||||
yy = ss.y-30;
|
||||
if ( (gametic%12) < 6 )
|
||||
Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void Draw( int state, double TicFrac )
|
||||
{
|
||||
Super.Draw(state,TicFrac);
|
||||
|
|
@ -823,5 +873,6 @@ Class SWWMStatusBar : BaseStatusBar
|
|||
DrawStatus();
|
||||
DrawWeapon();
|
||||
DrawMessages();
|
||||
DrawDeath();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ Class SWWMSpareArmor : Inventory abstract
|
|||
else shouldautouse = CVar.GetCVar('swwm_autousearmor',Owner.player).GetBool();
|
||||
if ( pickup && !shouldautouse ) return false;
|
||||
let cur = Owner.FindInventory(giveme);
|
||||
if ( !cur || (cur.Amount < cur.MaxAmount) )
|
||||
if ( !cur || (cur.Amount <= 0) )
|
||||
{
|
||||
Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount);
|
||||
if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6);
|
||||
|
|
@ -356,6 +356,94 @@ Class SWWMWeaponLight : DynamicLight
|
|||
}
|
||||
}
|
||||
|
||||
Class PunchImpact : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+DONTSPLASH;
|
||||
+NOTELEPORT;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3);
|
||||
A_StartSound("demolitionist/punch",CHAN_VOICE);
|
||||
A_SprayDecal("WallCrack",-20);
|
||||
int numpt = Random[Ponch](5,10);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
|
||||
}
|
||||
numpt = Random[Ponch](4,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Ponch](4,8);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class BigPunchImpact : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+DONTSPLASH;
|
||||
+NOTELEPORT;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(8,8,8,18,0,600,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.9);
|
||||
A_StartSound("pusher/althit",CHAN_VOICE);
|
||||
A_SprayDecal("BigWallCrack",-20);
|
||||
int numpt = Random[Ponch](9,16);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
|
||||
}
|
||||
numpt = Random[Ponch](9,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Ponch](9,16);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Base class for all SWWM Weapons
|
||||
Class SWWMWeapon : Weapon abstract
|
||||
{
|
||||
|
|
@ -363,7 +451,7 @@ Class SWWMWeapon : Weapon abstract
|
|||
|
||||
FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count)
|
||||
|
||||
override void AttachToOwner (Actor other)
|
||||
override void AttachToOwner( Actor other )
|
||||
{
|
||||
Inventory.AttachToOwner(other);
|
||||
Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1);
|
||||
|
|
@ -445,16 +533,17 @@ Class SWWMWeapon : Weapon abstract
|
|||
private action bool TryMelee( double angle, int dmg )
|
||||
{
|
||||
FTranslatedLineTarget t;
|
||||
double slope = AimLineAttack(angle,2*DEFMELEERANGE,t,0.,ALF_CHECK3D);
|
||||
double slope = AimLineAttack(angle,1.5*DEFMELEERANGE,t,0.,ALF_CHECK3D);
|
||||
FLineTraceData d;
|
||||
LineTrace(angle,2*DEFMELEERANGE,slope,0,player.viewheight,data:d);
|
||||
LineTrace(angle,1.5*DEFMELEERANGE,slope,0,player.viewheight,data:d);
|
||||
bool raging = CountInv("RagekitPower");
|
||||
if ( d.HitType == TRACE_HitActor )
|
||||
{
|
||||
bool bloodless = true;
|
||||
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
|
||||
self.angle += clamp(diff,-5.,5.);
|
||||
SWWMHandler.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000);
|
||||
if ( CountInv("RagekitPower") )
|
||||
if ( raging )
|
||||
{
|
||||
invoker.bEXTREMEDEATH = true;
|
||||
invoker.bNOEXTREMEDEATH = false;
|
||||
|
|
@ -468,15 +557,22 @@ Class SWWMWeapon : Weapon abstract
|
|||
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
|
||||
invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
|
||||
invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
|
||||
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25);
|
||||
int quakin = raging?8:2;
|
||||
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin);
|
||||
if ( !d.HitActor.bNOBLOOD && !d.HitActor.bINVULNERABLE )
|
||||
{
|
||||
d.HitActor.TraceBleed(dmg,invoker);
|
||||
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
|
||||
bloodless = false;
|
||||
}
|
||||
else bloodless = false;
|
||||
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12);
|
||||
A_StartSound(bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
else
|
||||
{
|
||||
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation);
|
||||
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
|
||||
}
|
||||
A_QuakeEx(quakin/2,quakin/2,quakin/2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06*quakin);
|
||||
if ( raging ) A_StartSound(bloodless?"pusher/althit":"pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
else A_StartSound(bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
A_AlertMonsters(300);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -484,7 +580,8 @@ Class SWWMWeapon : Weapon abstract
|
|||
}
|
||||
action void A_Melee( int dmg = 40 )
|
||||
{
|
||||
int maxang = CountInv("RagekitPower")?24:12;
|
||||
bool raging = CountInv("RagekitPower");
|
||||
int maxang = raging?18:12;
|
||||
for ( int i=0; i<maxang; i++ ) if ( TryMelee(angle+i*(45./16),dmg) || TryMelee(angle-i*(45./16),dmg) ) return;
|
||||
// check for walls instead
|
||||
FTranslatedLineTarget t;
|
||||
|
|
@ -492,10 +589,29 @@ Class SWWMWeapon : Weapon abstract
|
|||
FLineTraceData d;
|
||||
LineTrace(angle,DEFMELEERANGE,slope,TRF_THRUACTORS,player.viewheight,data:d);
|
||||
if ( d.HitType == TRACE_HitNone ) return;
|
||||
if ( d.HitType == TRACE_HitWall )
|
||||
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
|
||||
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12);
|
||||
A_StartSound("demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
Vector3 HitNormal = -d.HitDir;
|
||||
if ( d.HitType == TRACE_HitFloor )
|
||||
{
|
||||
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.top.Normal;
|
||||
else HitNormal = d.HitSector.floorplane.Normal;
|
||||
}
|
||||
else if ( d.HitType == TRACE_HitCeiling )
|
||||
{
|
||||
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.bottom.Normal;
|
||||
else HitNormal = d.HitSector.ceilingplane.Normal;
|
||||
}
|
||||
else if ( d.HitType == TRACE_HitWall )
|
||||
{
|
||||
HitNormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
||||
if ( !d.LineSide ) HitNormal *= -1;
|
||||
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
|
||||
}
|
||||
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation+HitNormal*4);
|
||||
p.angle = atan2(HitNormal.y,HitNormal.x);
|
||||
p.pitch = asin(-HitNormal.z);
|
||||
int quakin = raging?4:1;
|
||||
A_QuakeEx(quakin,quakin,quakin,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12*quakin);
|
||||
A_StartSound(raging?"pusher/althit":"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
A_AlertMonsters(100);
|
||||
}
|
||||
override void PlayUpSound( Actor origin )
|
||||
|
|
|
|||
|
|
@ -331,7 +331,6 @@ Class PusherWeapon : SWWMWeapon
|
|||
A_QuakeEx(3,3,3,7,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
|
||||
A_AlertMonsters(1200);
|
||||
int dmg = int(3+invoker.chargelevel*2);
|
||||
bool bloodless = false;
|
||||
if ( d.HitType == TRACE_HitActor )
|
||||
{
|
||||
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
|
||||
|
|
@ -351,6 +350,8 @@ Class PusherWeapon : SWWMWeapon
|
|||
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
|
||||
d.HitActor.A_StartSound("pusher/meat",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
}
|
||||
// move towards target
|
||||
vel.xy += Vec2To(d.HitActor).unit();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -413,7 +414,6 @@ Class PusherWeapon : SWWMWeapon
|
|||
A_QuakeEx(8,8,8,12,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
|
||||
A_AlertMonsters(1600);
|
||||
int dmg = int(80*invoker.chargelevel);
|
||||
bool bloodless = false;
|
||||
if ( d.HitType == TRACE_HitActor )
|
||||
{
|
||||
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
|
||||
|
|
|
|||
|
|
@ -430,7 +430,6 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
|
|||
if ( players[consoleplayer].Health <= 0 )
|
||||
{
|
||||
// ded
|
||||
MenuSound("menu/democlose");
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
|
@ -440,7 +439,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
|
|||
// alphabetically sorted inventory
|
||||
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv )
|
||||
{
|
||||
if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) ) continue;
|
||||
if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectable') ) continue;
|
||||
String tag = inv.GetTag();
|
||||
bool greater = false;
|
||||
for ( int i=0; i<invlist.Size(); i++ )
|
||||
|
|
@ -796,9 +795,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
|
|||
xx = 9;
|
||||
yy = 23;
|
||||
// wish I could use macros for this
|
||||
int thour = ((gametic-stats.lastspawn)/(3600*Thinker.TICRATE));
|
||||
int tmin = ((gametic-stats.lastspawn)/(60*Thinker.TICRATE))%60;
|
||||
int tsec = ((gametic-stats.lastspawn)/Thinker.TICRATE)%60;
|
||||
int thour = ((level.totaltime-stats.lastspawn)/(3600*Thinker.TICRATE));
|
||||
int tmin = ((level.totaltime-stats.lastspawn)/(60*Thinker.TICRATE))%60;
|
||||
int tsec = ((level.totaltime-stats.lastspawn)/Thinker.TICRATE)%60;
|
||||
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
|
||||
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
yy += 16;
|
||||
|
|
@ -852,6 +851,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
|
|||
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATMKILL"),stats.mkill);
|
||||
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
yy += 16;
|
||||
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSKILL"),stats.skill);
|
||||
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
yy += 16;
|
||||
str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP"));
|
||||
if ( stats.favweapon == -1 ) str = str.."N/A";
|
||||
else
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Class Demolitionist : PlayerPawn
|
|||
bool sendtoground;
|
||||
bool key_reentrant;
|
||||
|
||||
int lastdamage;
|
||||
int lastdamage, lastdamagetic;
|
||||
bool lastground;
|
||||
int lastgroundtic;
|
||||
double lastvelz, prevvelz;
|
||||
|
|
@ -33,6 +33,7 @@ Class Demolitionist : PlayerPawn
|
|||
};
|
||||
|
||||
int lastunder;
|
||||
int deadtimer, revivefail;
|
||||
|
||||
Default
|
||||
{
|
||||
|
|
@ -503,7 +504,8 @@ Class Demolitionist : PlayerPawn
|
|||
// lucky collar
|
||||
if ( Health < 25 ) damage /= 4;
|
||||
if ( source == self ) damage /= 2;
|
||||
int lastdamage = Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
||||
lastdamage = Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
||||
lastdamagetic = max(lastdamagetic,gametic+clamp(lastdamage,10,80));
|
||||
if ( (lastdamage > 0) && (PainChance == 0) && (level.maptime>lastmpain) )
|
||||
{
|
||||
lastmpain = level.maptime;
|
||||
|
|
@ -627,7 +629,6 @@ Class Demolitionist : PlayerPawn
|
|||
double spd = vel.length();
|
||||
if ( spd > 10. ) vel = (vel+accel/TICRATE).unit()*spd;
|
||||
else vel = vel+accel/TICRATE;
|
||||
player.jumptics = -2;
|
||||
}
|
||||
if ( abs(roll) > 0. ) roll += clamp(deltaangle(roll,0),-3.,3.);
|
||||
}
|
||||
|
|
@ -635,7 +636,7 @@ Class Demolitionist : PlayerPawn
|
|||
guidepitch *= .9;
|
||||
guideroll *= .9;
|
||||
// anchor to ground when going down steps
|
||||
if ( !player.onground && !bFly && !bFlyCheat && (waterlevel < 2) && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) && (vel.z < 0) )
|
||||
if ( lastground && !player.onground && !bFly && !bFlyCheat && (waterlevel < 2) && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) && (vel.z < 0) )
|
||||
{
|
||||
ssup = max(0,(pos.z-floorz));
|
||||
SetOrigin(Vec2OffsetZ(0,0,floorz),true);
|
||||
|
|
@ -801,8 +802,78 @@ Class Demolitionist : PlayerPawn
|
|||
}
|
||||
override void DeathThink()
|
||||
{
|
||||
// TODO reboot mechanic, death camera that doesn't move body
|
||||
Super.DeathThink();
|
||||
player.Uncrouch();
|
||||
TickPSprites();
|
||||
player.onground = (pos.Z<=floorz);
|
||||
// ded (demo-chan falls faster tho)
|
||||
player.deltaviewheight = 0;
|
||||
if ( player.viewheight > 6 ) player.viewheight -= 3;
|
||||
if ( player.viewheight < 6 ) player.viewheight = 6;
|
||||
// center pitch
|
||||
double dpitch = clamp(deltaangle(pitch,0),-6,6);
|
||||
if ( abs(dpitch) < 3. ) pitch = 0.;
|
||||
else pitch += dpitch;
|
||||
// add roll
|
||||
double droll = clamp(deltaangle(roll,50)*.5,-5,5);
|
||||
if ( abs(droll) < 2. ) roll = 50.;
|
||||
else roll += droll;
|
||||
player.mo.CalcHeight();
|
||||
if ( player.damagecount ) player.damagecount--;
|
||||
if ( player.poisoncount ) player.poisoncount--;
|
||||
if ( player.viewheight <= 6 )
|
||||
{
|
||||
deadtimer++;
|
||||
if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) )
|
||||
{
|
||||
// standard behaviour, respawn normally
|
||||
if ( (Level.maptime >= player.respawn_time) || ((player.cmd.buttons&BT_USE) && !player.Bot) )
|
||||
{
|
||||
player.cls = null;
|
||||
player.playerstate = PST_REBORN;
|
||||
if ( special1 > 2 ) special1 = 0;
|
||||
}
|
||||
}
|
||||
else if ( (player.cmd.buttons&BT_USE) && (deadtimer > 150) )
|
||||
{
|
||||
// reload save
|
||||
player.cls = null;
|
||||
player.playerstate = PST_ENTER;
|
||||
if ( special1 > 2 ) special1 = 0;
|
||||
}
|
||||
else if ( (player.cmd.buttons&BT_ATTACK) && (deadtimer > 150) && swwm_revive )
|
||||
{
|
||||
// reboot (if possible)
|
||||
if ( !FindInventory("ReviveCooldown") )
|
||||
{
|
||||
player.Resurrect();
|
||||
roll = 0;
|
||||
let s = Spawn("DemolitionistShockwave",pos);
|
||||
s.target = self;
|
||||
s.special1 = 30;
|
||||
ReactionTime = 17;
|
||||
A_Stop();
|
||||
A_AlertMonsters();
|
||||
if ( player == players[consoleplayer] )
|
||||
{
|
||||
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
||||
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7);
|
||||
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4);
|
||||
}
|
||||
SWWMHandler.DoFlash(self,Color(255,255,255,255),10);
|
||||
SWWMHandler.DoFlash(self,Color(255,128,192,255),30);
|
||||
if ( special1 > 2 ) special1 = 0;
|
||||
if ( swwm_revivecooldown > 0 )
|
||||
GiveInventory("ReviveCooldown",1);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_StartSound("*usefail",CHAN_ITEM,CHANF_UI|CHANF_LOCAL);
|
||||
SWWMHandler.DoFlash(self,Color(64,255,0,0),30);
|
||||
revivefail = level.maptime+20;
|
||||
}
|
||||
}
|
||||
}
|
||||
else deadtimer = 0;
|
||||
}
|
||||
override void PlayIdle()
|
||||
{
|
||||
|
|
@ -1480,3 +1551,38 @@ Class DemolitionistShockwave : Actor
|
|||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class ReviveCooldown : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Inventory.Icon "graphics/HUD/Icons/I_Revive.png";
|
||||
Powerup.Duration -30;
|
||||
}
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
if ( !Owner ) Destroy();
|
||||
if ( Owner.Health <= 0 ) return; // timer does not go down when dead
|
||||
if ( (EffectTics == 0) || ((EffectTics > 0) && (--EffectTics == 0)) )
|
||||
Destroy ();
|
||||
}
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
// adjust the duration
|
||||
EffectTics = max(0,swwm_revivecooldown)*Thinker.TICRATE;
|
||||
}
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
if ( !Owner ) return;
|
||||
Owner.A_StartSound("demolitionist/revive",CHAN_ITEMEXTRA);
|
||||
if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_REFAIL"));
|
||||
}
|
||||
override void OwnerDied()
|
||||
{
|
||||
// do nothing, this "powerup" is preserved on death
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -711,7 +711,7 @@ Class RagekitPower : Powerup
|
|||
|
||||
override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags )
|
||||
{
|
||||
if ( !passive && ((damageType == 'Melee') || (damageType == 'Dash') || (damageType == 'GroundPound')) )
|
||||
if ( !passive && ((damageType == 'Melee') || (damageType == 'Jump') || (damageType == 'Dash') || (damageType == 'GroundPound')) )
|
||||
{
|
||||
newdamage = damage*8;
|
||||
if ( level.maptime > lasteffect+5 )
|
||||
|
|
|
|||
|
|
@ -820,6 +820,600 @@ Class SaltBeam : Actor
|
|||
}
|
||||
}
|
||||
|
||||
Class OnFireLight : DynamicLight
|
||||
{
|
||||
OnFire of;
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
if ( !of || !of.victim )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
Args[0] = clamp(of.Amount*4,0,255);
|
||||
Args[1] = clamp(of.Amount*2,0,128);
|
||||
Args[3] = int(max(of.victim.radius,of.victim.height)*2.+20+clamp(of.amount/5,0,80));
|
||||
SetOrigin(of.Victim.Vec3Offset(0,0,of.Victim.Height/2),true);
|
||||
}
|
||||
}
|
||||
|
||||
Class OnFire : Actor
|
||||
{
|
||||
Actor victim, instigator, lite;
|
||||
int amount, cnt, delay;
|
||||
double oangle;
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
if ( !victim )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( victim.waterlevel > 0 )
|
||||
{
|
||||
if ( lite ) lite.Destroy();
|
||||
amount -= int(victim.waterlevel**2);
|
||||
}
|
||||
if ( (victim.Health <= 0) || ((victim is 'FlamingChunk') && !victim.bMISSILE) ) amount = min(amount,100);
|
||||
if ( !(level.maptime%3) )
|
||||
amount--;
|
||||
if ( victim.player ) amount -= int(abs(actor.deltaangle(victim.angle,oangle))/30);
|
||||
oangle = victim.angle;
|
||||
if ( amount < -30 )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( cnt > 0 ) cnt--;
|
||||
else
|
||||
{
|
||||
cnt = 10;
|
||||
if ( victim.bSHOOTABLE && (victim.Health > 0) && (amount > 0) )
|
||||
victim.DamageMobj(self,instigator,clamp(int(amount*(victim.bBOSS?0.05:0.15)),1,20),'Fire',DMG_THRUSTLESS); // the only reason why we need to use an actor
|
||||
if ( !victim )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( delay > 0 ) delay--;
|
||||
if ( level.maptime%5 ) return;
|
||||
int numpt = clamp(int(Random[FlameT](2,4)*amount*0.02),1,4);
|
||||
double mult = max(victim.radius,victim.height)/30.;
|
||||
numpt = int(clamp(numpt*mult**.5,1,5));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pos = victim.Vec3Offset(FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](victim.height*0.2,victim.height*0.8));
|
||||
double ang = FRandom[FlameT](0,360);
|
||||
double pt = FRandom[FlameT](-90,90);
|
||||
if ( amount > 0 )
|
||||
{
|
||||
let c = victim.Spawn("OnFireTrail",pos);
|
||||
c.scale *= max(.3,mult*0.5);
|
||||
c.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.5,2.)*c.scale.x;
|
||||
}
|
||||
let s = victim.Spawn("SWWMSmoke",pos);
|
||||
s.scale *= max(1.,1.6*mult);
|
||||
s.alpha *= min(amount+30,100)*0.02;
|
||||
s.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.2,.6)*s.scale.x;
|
||||
}
|
||||
if ( amount <= 0 ) return;
|
||||
// spread to nearby actors
|
||||
let bt = BlockThingsIterator.Create(victim);
|
||||
while ( bt.Next() )
|
||||
{
|
||||
let t = bt.Thing;
|
||||
if ( !t || !t.bSHOOTABLE || (t.Health <= 0) || (t == victim) || ((t == instigator) && (delay > 0)) || (victim.Distance3D(t) > victim.radius+t.radius+20) || !victim.CheckSight(t) ) continue;
|
||||
int amt = max(1,amount/10);
|
||||
if ( IsOnFire(t) ) amt = min(5,amt);
|
||||
Apply(t,instigator,amt);
|
||||
}
|
||||
}
|
||||
|
||||
static OnFire Apply( Actor victim, Actor instigator, int amount, int delay = 0 )
|
||||
{
|
||||
if ( amount <= 0 ) return null;
|
||||
let ti = ThinkerIterator.Create("OnFire");
|
||||
OnFire t;
|
||||
while ( t = OnFire(ti.Next()) )
|
||||
{
|
||||
if ( t.victim != victim ) continue;
|
||||
if ( instigator ) t.instigator = instigator;
|
||||
t.amount = min(500,t.amount+amount);
|
||||
t.cnt = min(t.cnt,5);
|
||||
return t;
|
||||
}
|
||||
t = OnFire(Spawn("OnFire"));
|
||||
t.victim = victim;
|
||||
t.instigator = instigator;
|
||||
t.amount = min(500,amount);
|
||||
t.cnt = 1;
|
||||
// for chunks
|
||||
t.delay = delay;
|
||||
t.lite = Actor.Spawn("OnFireLight",victim.pos);
|
||||
OnFireLight(t.lite).of = t;
|
||||
t.oangle = victim.angle;
|
||||
return t;
|
||||
}
|
||||
|
||||
static bool IsOnFire( Actor victim )
|
||||
{
|
||||
let ti = ThinkerIterator.Create("OnFire");
|
||||
OnFire t;
|
||||
while ( t = OnFire(ti.Next()) )
|
||||
{
|
||||
if ( t.victim != victim ) continue;
|
||||
return (t.amount>0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Default
|
||||
{
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+DONTSPLASH;
|
||||
Obituary "$O_SPREADGUN_BLACK";
|
||||
}
|
||||
}
|
||||
|
||||
Class OnFireTrailLight : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "HellExpl";
|
||||
Args 0,0,0,40;
|
||||
ReactionTime 40;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
Args[0] /= 10;
|
||||
Args[1] /= 10;
|
||||
Args[2] /= 10;
|
||||
Args[3] += 3;
|
||||
if ( !target || (target.waterlevel > 0) )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
SetOrigin(target.pos,true);
|
||||
}
|
||||
}
|
||||
|
||||
Class OnFireTrail : Actor
|
||||
{
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
Scale.x *= RandomPick[ExploS](-1,1);
|
||||
Scale.y *= RandomPick[ExploS](-1,1);
|
||||
roll = FRandom[ExploS](0,360);
|
||||
}
|
||||
action void A_Flame()
|
||||
{
|
||||
if ( waterlevel > 0 )
|
||||
vel *= 0.9;
|
||||
else
|
||||
{
|
||||
vel *= 0.98;
|
||||
vel.z += 0.2*abs(scale.x);
|
||||
}
|
||||
if ( waterlevel > 0 )
|
||||
{
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2));
|
||||
s.vel += vel*0.3;
|
||||
s.alpha *= alpha*4;
|
||||
s.scale *= 0.5+abs(scale.x)*(.5+GetAge()/6.);
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( !Random[FlameT](0,int(40*(default.alpha-alpha))) )
|
||||
{
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2));
|
||||
s.vel += vel*0.3;
|
||||
s.alpha *= alpha*4;
|
||||
s.scale *= 0.5+abs(scale.x)*(.5+GetAge()/6.);
|
||||
}
|
||||
}
|
||||
Default
|
||||
{
|
||||
RenderStyle "Add";
|
||||
Speed 2;
|
||||
Radius 4;
|
||||
Height 4;
|
||||
Alpha 0.3;
|
||||
Scale 0.6;
|
||||
+NOBLOCKMAP;
|
||||
+NOGRAVITY;
|
||||
+NOFRICTION;
|
||||
+SLIDESONWALLS;
|
||||
+ACTIVATEPCROSS;
|
||||
+ACTIVATEIMPACT;
|
||||
+NOTELEPORT;
|
||||
+FORCEXYBILLBOARD;
|
||||
+ROLLSPRITE;
|
||||
+ROLLCENTER;
|
||||
+DROPOFF;
|
||||
+NOBLOCKMONST;
|
||||
+DONTSPLASH;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XFLM ABCDEFGHIJKLMNOPQRST 1 Bright
|
||||
{
|
||||
A_Flame();
|
||||
A_SetScale(scale.x*0.98);
|
||||
A_FadeOut(0.01);
|
||||
vel.z += 0.1;
|
||||
}
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
Class FlamingChunk : Actor
|
||||
{
|
||||
double rollvel;
|
||||
OnFire myfire;
|
||||
Vector3 oldvel;
|
||||
int deadtimer;
|
||||
Actor lasthit;
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
rollvel = FRandom[FlameT](10,30)*RandomPick[FlameT](-1,1);
|
||||
Scale *= FRandom[FlameT](.8,1.2);
|
||||
if ( waterlevel <= 0 ) myfire = OnFire.Apply(self,target,int(120*scale.x),6);
|
||||
frame = Random[FlameT](0,5);
|
||||
}
|
||||
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
|
||||
{
|
||||
if ( target != lasthit )
|
||||
{
|
||||
OnFire.Apply(target,self.target,myfire?myfire.Amount:1);
|
||||
lasthit = target;
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
oldvel = vel;
|
||||
Super.Tick();
|
||||
if ( isFrozen() ) return;
|
||||
if ( InStateSequence(CurState,ResolveState("Death")) )
|
||||
{
|
||||
deadtimer++;
|
||||
if ( deadtimer > 300 ) A_FadeOut(0.05);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void A_HandleBounce()
|
||||
{
|
||||
bHITOWNER = true;
|
||||
lasthit = null;
|
||||
Vector3 HitNormal = -vel.unit();
|
||||
F3DFloor ff;
|
||||
if ( BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
ff = CurSector.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.top.Normal;
|
||||
else HitNormal = BlockingFloor.floorplane.Normal;
|
||||
}
|
||||
else if ( BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
||||
ff = CurSector.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.bottom.Normal;
|
||||
else HitNormal = BlockingCeiling.ceilingplane.Normal;
|
||||
}
|
||||
else if ( BlockingLine )
|
||||
{
|
||||
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
||||
HitNormal *= -1;
|
||||
}
|
||||
else if ( BlockingMobj )
|
||||
{
|
||||
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
|
||||
HitNormal = diff.unit();
|
||||
}
|
||||
// undo the bounce, we need to hook in our own
|
||||
vel = oldvel;
|
||||
// re-do the bounce with our formula
|
||||
vel = .8*((vel dot HitNormal)*HitNormal*-1.2+vel);
|
||||
gravity = .35;
|
||||
if ( (vel.length() < 5) && (pos.z <= floorz) )
|
||||
{
|
||||
ClearBounce();
|
||||
ExplodeMissile();
|
||||
}
|
||||
rollvel = FRandom[FlameT](10,30)*RandomPick[FlameT](-1,1);
|
||||
}
|
||||
Default
|
||||
{
|
||||
Obituary "$O_SPREADGUN_BLACK";
|
||||
DamageFunction 1;
|
||||
DamageType 'Fire';
|
||||
Radius 4;
|
||||
Height 4;
|
||||
Speed 15;
|
||||
Gravity 0.2;
|
||||
PROJECTILE;
|
||||
-NOGRAVITY;
|
||||
+ROLLSPRITE;
|
||||
+ROLLCENTER;
|
||||
+EXPLODEONWATER;
|
||||
+FORCERADIUSDMG;
|
||||
+FORCEXYBILLBOARD;
|
||||
+NODAMAGETHRUST;
|
||||
+INTERPOLATEANGLES;
|
||||
+RIPPER;
|
||||
+BLOODLESSIMPACT;
|
||||
+NOTELEPORT;
|
||||
+BOUNCEONWALLS;
|
||||
+BOUNCEONFLOORS;
|
||||
+BOUNCEONCEILINGS;
|
||||
+USEBOUNCESTATE;
|
||||
BounceFactor 1.0;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
JUNK # 1 { roll += rollvel; }
|
||||
Wait;
|
||||
Bounce:
|
||||
JUNK # 0 A_HandleBounce();
|
||||
Goto Spawn;
|
||||
Death:
|
||||
JUNK # -1 { bMOVEWITHSECTOR = true; }
|
||||
Stop;
|
||||
Dummy:
|
||||
JUNK ABCDEF -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class BallImpact : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+DONTSPLASH;
|
||||
+NOTELEPORT;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(3,3,3,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3);
|
||||
A_StartSound("spreadgun/ball",CHAN_VOICE);
|
||||
A_SprayDecal("WallCrack",-20);
|
||||
int numpt = Random[Spreadgun](5,10);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8))).unit()*FRandom[Spreadgun](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Spreadgun](128,192));
|
||||
}
|
||||
numpt = Random[Spreadgun](4,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Spreadgun](4,8);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class TheBall : Actor
|
||||
{
|
||||
double heat;
|
||||
int deadtimer;
|
||||
Vector3 oldvel;
|
||||
Actor lasthit;
|
||||
|
||||
Default
|
||||
{
|
||||
Obituary "$O_SPREADGUN_PURPLE";
|
||||
+NOBLOCKMAP;
|
||||
+BOUNCEONWALLS;
|
||||
+BOUNCEONFLOORS;
|
||||
+BOUNCEONCEILINGS;
|
||||
+USEBOUNCESTATE;
|
||||
+MISSILE;
|
||||
+NODAMAGETHRUST;
|
||||
+NOTELEPORT;
|
||||
-NOGRAVITY;
|
||||
Speed 80;
|
||||
Gravity 0.1;
|
||||
BounceFactor 1.0;
|
||||
Radius 2;
|
||||
Height 2;
|
||||
}
|
||||
override int SpecialMissileHit( Actor victim )
|
||||
{
|
||||
if ( (vel.length() <= 5) || ((victim == target) && !bHITOWNER) || (victim == lasthit) || (!victim.bSHOOTABLE && !victim.bSOLID) )
|
||||
return 1;
|
||||
// check if we should rip or bounce
|
||||
// girthitude
|
||||
double girth = (victim.radius+victim.height)/2.*max(50,victim.mass);
|
||||
// how hard this damn thing is going to slam
|
||||
double slamforce = vel.length()*350.+heat*120;
|
||||
SWWMHandler.DoKnockback(victim,vel.unit(),slamforce);
|
||||
bool bleeds = (victim && !victim.bINVULNERABLE && !victim.bNOBLOOD && victim.bSHOOTABLE);
|
||||
int dmg = int(vel.length()*4.2+heat*80);
|
||||
dmg = victim.DamageMobj(self,target,dmg,'Concussion',DMG_THRUSTLESS);
|
||||
Vector3 dir = -vel.unit();
|
||||
// slam jam
|
||||
if ( bleeds )
|
||||
{
|
||||
A_StartSound("spreadgun/ballf",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/75.)**.5);
|
||||
victim.TraceBleed(dmg,self);
|
||||
SpawnBlood(pos,atan2(dir.y,dir.x),dmg);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/75.)**.5);
|
||||
if ( vel.length() > 15 )
|
||||
{
|
||||
let s = Spawn("BallImpact",pos);
|
||||
s.angle = atan2(dir.y,dir.x);
|
||||
s.pitch = asin(-dir.z);
|
||||
}
|
||||
}
|
||||
if ( slamforce > girth )
|
||||
{
|
||||
vel *= .8;
|
||||
return 1;
|
||||
}
|
||||
// force bounce
|
||||
BlockingMobj = victim;
|
||||
A_HandleBounce();
|
||||
lasthit = victim;
|
||||
// pretend to pass through
|
||||
return 1;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_StartSound("pusher/fly",CHAN_WEAPON,CHANF_LOOPING,.6,3.,2.);
|
||||
heat = 1.;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
oldvel = vel;
|
||||
Super.Tick();
|
||||
if ( isFrozen() ) return;
|
||||
if ( InStateSequence(CurState,ResolveState("Death")) )
|
||||
{
|
||||
deadtimer++;
|
||||
if ( deadtimer > 300 ) A_FadeOut(0.05);
|
||||
return;
|
||||
}
|
||||
heat -= 0.004+0.0004*vel.length();
|
||||
A_SoundVolume(CHAN_WEAPON,vel.length()/75.);
|
||||
if ( heat <= 0 ) return;
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.alpha *= heat;
|
||||
}
|
||||
void A_HandleBounce()
|
||||
{
|
||||
bHITOWNER = true;
|
||||
lasthit = null;
|
||||
Vector3 HitNormal = -vel.unit();
|
||||
F3DFloor ff;
|
||||
if ( BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
ff = CurSector.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.top.Normal;
|
||||
else HitNormal = BlockingFloor.floorplane.Normal;
|
||||
}
|
||||
else if ( BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
||||
ff = CurSector.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.bottom.Normal;
|
||||
else HitNormal = BlockingCeiling.ceilingplane.Normal;
|
||||
}
|
||||
else if ( BlockingLine )
|
||||
{
|
||||
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
||||
HitNormal *= -1;
|
||||
}
|
||||
else if ( BlockingMobj )
|
||||
{
|
||||
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
|
||||
HitNormal = diff.unit();
|
||||
}
|
||||
// undo the bounce, we need to hook in our own
|
||||
vel = oldvel;
|
||||
// re-do the bounce with our formula
|
||||
double bcefact = .9;
|
||||
if ( BlockingMobj )
|
||||
{
|
||||
bcefact *= .7;
|
||||
if ( !BlockingMobj.bINVULNERABLE && !BlockingMobj.bNOBLOOD )
|
||||
bcefact *= .6;
|
||||
}
|
||||
vel = bcefact*((vel dot HitNormal)*HitNormal*-1.2+vel);
|
||||
// slam jam
|
||||
if ( !BlockingMobj )
|
||||
{
|
||||
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,max(0.,(vel.length()/60.-.1))**.5);
|
||||
if ( vel.length() > 15 )
|
||||
{
|
||||
let s = Spawn("BallImpact",pos);
|
||||
s.angle = atan2(HitNormal.y,HitNormal.x);
|
||||
s.pitch = asin(-HitNormal.z);
|
||||
}
|
||||
}
|
||||
gravity = .35;
|
||||
if ( (vel.length() < 5) && (pos.z <= floorz) )
|
||||
{
|
||||
ClearBounce();
|
||||
ExplodeMissile();
|
||||
}
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1;
|
||||
Stop;
|
||||
Bounce:
|
||||
XZW1 A 0 A_HandleBounce();
|
||||
Goto Spawn;
|
||||
Death:
|
||||
XZW1 A -1
|
||||
{
|
||||
bMOVEWITHSECTOR = true;
|
||||
A_StopSound(CHAN_WEAPON);
|
||||
}
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class Spreadgun : SWWMWeapon
|
||||
{
|
||||
bool fired; // shell was used
|
||||
|
|
@ -1190,7 +1784,39 @@ Class Spreadgun : SWWMWeapon
|
|||
Console.Printf("\cg// TODO Napalm Rounds\c-");
|
||||
break;
|
||||
case 5:
|
||||
Console.Printf("\cg// TODO The Ball\c-");
|
||||
a = FRandom[Spreadgun](0,360);
|
||||
s = FRandom[Spreadgun](0,.03);
|
||||
let b = Spawn("TheBall",origin);
|
||||
b.target = self;
|
||||
b.angle = atan2(x2.y,x2.x);
|
||||
b.pitch = asin(-x2.z);
|
||||
b.vel = x2*b.speed;
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
let s = Spawn("SWWMViewSmoke",origin);
|
||||
SWWMViewSmoke(s).ofs = (15,3,-3);
|
||||
s.target = self;
|
||||
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
|
||||
s.alpha *= 0.4;
|
||||
}
|
||||
for ( int i=0; i<8; i++ )
|
||||
{
|
||||
let s = Spawn("SWWMSmoke",origin);
|
||||
s.scale *= .6;
|
||||
s.alpha *= .25;
|
||||
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
|
||||
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.);
|
||||
}
|
||||
for ( int i=0; i<8; i++ )
|
||||
{
|
||||
let s = Spawn("SWWMSpark",origin);
|
||||
s.scale *= .2;
|
||||
s.alpha *= .4;
|
||||
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
|
||||
}
|
||||
SWWMHandler.DoKnockback(self,-x,2500.);
|
||||
A_Recoil(1.);
|
||||
invoker.srecoil = -.35;
|
||||
break;
|
||||
case 6:
|
||||
Console.Printf("\cg// TODO Golden Shell\c-");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue