Big changes (still no Ynykron altfire, sorry):

- Split off big code files for easier navigation.
- Moved DoExplosion and DoKnockback to the SWWMUtility class.
- Removed DoBlast as it is no longer used, having been replaced entirely.
- Finalized Mag Manager:
  * Implemented partial mag loading for Silver Bullet and Candygun.
  * Show spare bullets on ammo displays of Silver Bullet and Candygun.
- Rebalanced Eviscerator and Hellblazer damages.
- Adjusted Dragon's Breath damage and effects.
- Adjusted ammo spawns again.
  * Reduced shell spawns, as they seemed to max out too quickly.
  * Added "intermediate" bundle spawns for Eviscerator shells, Hellblazer missiles and Silver Bullet / Candygun rounds.
- Doubled max ammo of Biospark Carbine.
- Biospark beams no longer spawn arcs near their start point. This should reduce the likelihood of self-damage.
This commit is contained in:
Mari the Deer 2020-08-23 16:24:53 +02:00
commit f28e7e01fb
25 changed files with 4723 additions and 4545 deletions

View file

@ -5,7 +5,6 @@ Not so fundamental things during first beta:
- Extra Demolitionist animations (swimming, additional gestures)
- Dual wielding Explodium Gun
- Quick grenade function (Explodium Mag)
- Allow loading partial mags from spare bullets
- Parrying for hitscan weapons (currently parrying only handles vanilla LineAttack)
- Fun options
- Omnibusting (all weapons can bust walls)
@ -13,7 +12,7 @@ Not so fundamental things during first beta:
- Confetti gibs
- Keen replacement (need ideas)
- Achievements
- Extra Demolitionist Menu tabs (radio, pong minigame)
- Extra Demolitionist Menu tabs (radio, minigames)
- Collectables
Extra things for later:

View file

@ -8,7 +8,7 @@ SWWM_ZOOMFIRE = "Tertiary Fire / Zoom";
SWWM_MELEE = "Melee Attack";
SWWM_DASH = "Dash";
SWWM_ITEMSENSE = "Item Sense";
SWWM_EXTRAFIRE = "Quaternary Fire";
SWWM_EXTRAFIRE = "Quick Grenade";
SWWM_GESTURE1 = "Wave";
SWWM_GESTURE2 = "Thumbs Up";
SWWM_GESTURE3 = "Victory";

View file

@ -8,7 +8,7 @@ SWWM_ZOOMFIRE = "Fuego Terciario / Zoom";
SWWM_MELEE = "Ataque a Melé";
SWWM_DASH = "Esprintar";
SWWM_ITEMSENSE = "Sensor de Items";
SWWM_EXTRAFIRE = "Fuego Cuaternario";
SWWM_EXTRAFIRE = "Granada Rápida";
SWWM_GESTURE1 = "Saludar";
SWWM_GESTURE2 = "Pulgar Arriba";
SWWM_GESTURE3 = "Victoria";

View file

@ -1,2 +1,2 @@
[default]
SWWM_MODVER="\chSWWM \cwGZ\c- r503 (Sat 22 Aug 15:18:03 CEST 2020)";
SWWM_MODVER="\chSWWM \cwGZ\c- r505 (Sun 23 Aug 16:32:44 CEST 2020)";

View file

@ -14,13 +14,18 @@ version "4.4"
#include "zscript/swwm_libeye/viewport.txt"
#include "zscript/swwm_coordutil.zsc"
#include "zscript/swwm_quaternion.zsc"
#include "zscript/swwm_crimesdlg.zsc"
// base code
#include "zscript/swwm_common.zsc"
#include "zscript/swwm_utility.zsc"
#include "zscript/swwm_handler.zsc"
#include "zscript/swwm_shame.zsc"
#include "zscript/swwm_thinkers.zsc"
#include "zscript/swwm_player.zsc"
#include "zscript/swwm_inventory.zsc"
#include "zscript/swwm_hud.zsc"
#include "zscript/swwm_hudextra.zsc"
#include "zscript/swwm_menu.zsc"
#include "zscript/swwm_crimesdlg.zsc"
#include "zscript/swwm_options.zsc"
#include "zscript/swwm_title.zsc"
#include "zscript/swwm_inter.zsc"

View file

@ -230,6 +230,11 @@ Class MagAmmo : Inventory abstract
return;
}
if ( countdown-- > 0 ) return;
MagFill();
}
bool MagFill()
{
bool given = false;
while ( (pamo.Amount < pamo.MaxAmount) && (Amount >= ClipSize) )
{
@ -240,6 +245,7 @@ Class MagAmmo : Inventory abstract
pamo.PrintPickupMessage(true,pamo.PickupMessage());
}
if ( given ) pamo.PlayPickupSound(Owner);
return given;
}
override inventory CreateTossable( int amt )
@ -701,6 +707,28 @@ Class EvisceratorSixPack : EvisceratorShell
}
}
Class EvisceratorTrioSpawn : Actor
{
override void PostBeginPlay()
{
if ( bCOUNTSECRET ) level.total_secrets--;
for ( int i=0; i<3; i++ )
{
let a = Spawn("EvisceratorShell",Vec3Angle(12,i*120));
a.special = special;
a.angle = i*120;
a.FloatBobPhase = FloatBobPhase;
for ( int j=0; j<5; j++ ) a.args[j] = args[j];
if ( bCOUNTSECRET )
{
a.bCOUNTSECRET = true;
level.total_secrets++;
}
}
Destroy();
}
}
// ============================================================================
// Hellblazer ammo
// ============================================================================
@ -743,6 +771,27 @@ Class HellblazerMissileMag : HellblazerMissiles
Inventory.Amount 6;
}
}
Class HellblazerMissileTrioSpawn : Actor
{
override void PostBeginPlay()
{
if ( bCOUNTSECRET ) level.total_secrets--;
for ( int i=0; i<3; i++ )
{
let a = Spawn("HellblazerMissiles",Vec3Angle(8,i*120));
a.special = special;
a.angle = i*120;
a.FloatBobPhase = FloatBobPhase;
for ( int j=0; j<5; j++ ) a.args[j] = args[j];
if ( bCOUNTSECRET )
{
a.bCOUNTSECRET = true;
level.total_secrets++;
}
}
Destroy();
}
}
Class HellblazerCrackshots : Ammo
{
@ -876,7 +925,7 @@ Class SparkUnit : Ammo
Stamina 50000;
Inventory.Icon "graphics/HUD/Icons/A_Sparkster.png";
Inventory.Amount 1;
Inventory.MaxAmount 4;
Inventory.MaxAmount 8;
Ammo.BackpackAmount 1;
Ammo.DropAmount 1;
+FLOATBOB;
@ -991,6 +1040,51 @@ Class SilverBullets2 : MagAmmo
}
}
Class SilverBulletsBundleSpawn : Actor
{
override void PostBeginPlay()
{
if ( bCOUNTSECRET ) level.total_secrets--;
int bnd = Random[Bundle](2,3);
for ( int i=0; i<3; i++ )
{
let a = Spawn("SilverBullets",Vec3Angle(6,i*(360/bnd)));
a.special = special;
a.angle = i*(360/bnd);
a.FloatBobPhase = FloatBobPhase;
for ( int j=0; j<5; j++ ) a.args[j] = args[j];
if ( bCOUNTSECRET )
{
a.bCOUNTSECRET = true;
level.total_secrets++;
}
}
Destroy();
}
}
Class SilverBullets2BundleSpawn : Actor
{
override void PostBeginPlay()
{
if ( bCOUNTSECRET ) level.total_secrets--;
int bnd = Random[Bundle](2,3);
for ( int i=0; i<bnd; i++ )
{
let a = Spawn("SilverBullets2",Vec3Angle(6,i*(360/bnd)));
a.special = special;
a.angle = i*(360/bnd);
a.FloatBobPhase = FloatBobPhase;
for ( int j=0; j<5; j++ ) a.args[j] = args[j];
if ( bCOUNTSECRET )
{
a.bCOUNTSECRET = true;
level.total_secrets++;
}
}
Destroy();
}
}
// ============================================================================
// Candygun ammo
// ============================================================================
@ -1044,6 +1138,28 @@ Class CandyGunBullets : MagAmmo
}
}
Class CandyGunBulletsBundleSpawn : Actor
{
override void PostBeginPlay()
{
if ( bCOUNTSECRET ) level.total_secrets--;
int bnd = Random[Bundle](2,3);
for ( int i=0; i<bnd; i++ )
{
let a = Spawn("CandyGunBullets",Vec3Angle(4,i*(360/bnd)));
a.special = special;
a.angle = i*(360/bnd);
for ( int j=0; j<5; j++ ) a.args[j] = args[j];
if ( bCOUNTSECRET )
{
a.bCOUNTSECRET = true;
level.total_secrets++;
}
}
Destroy();
}
}
Class CandyGunSpares : Ammo
{
Default

View file

@ -251,7 +251,7 @@ Class HellblazerMissile : Actor
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("BigScorch",50);
A_SetScale(4.5);
SWWMHandler.DoExplosion(self,250,320000,200,90);
SWWMUtility.DoExplosion(self,350,320000,200,90);
A_NoGravity();
A_QuakeEx(5,5,5,15,0,1500,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollIntensity:.8);
A_StopSound(CHAN_BODY);
@ -347,7 +347,7 @@ Class HellblazerCrackshot : HellblazerMissile
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("BigScorch",50);
A_SetScale(6.);
SWWMHandler.DoExplosion(self,300,320000,250,100);
SWWMUtility.DoExplosion(self,300,320000,250,100);
A_NoGravity();
A_QuakeEx(6,6,6,20,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:800,rollIntensity:1.);
A_StopSound(CHAN_BODY);
@ -416,7 +416,7 @@ Class HellblazerRavager : HellblazerMissile
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("HugeScorch",50);
A_SetScale(8.);
SWWMHandler.DoExplosion(self,500,320000,300,120);
SWWMUtility.DoExplosion(self,400,320000,300,120);
A_NoGravity();
A_QuakeEx(6,6,6,30,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:800,rollIntensity:1.);
A_StopSound(CHAN_BODY);
@ -494,7 +494,7 @@ Class HellblazerWarhead : HellblazerMissile
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("WumboScorch",150);
A_SetScale(7.);
SWWMHandler.DoExplosion(self,2000,600000,400,200);
SWWMUtility.DoExplosion(self,2000,600000,400,200);
A_NoGravity();
A_QuakeEx(9,9,9,150,0,12000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:4000,rollIntensity:2.);
A_StopSound(CHAN_BODY);
@ -534,7 +534,7 @@ Class HellblazerWarhead : HellblazerMissile
void A_WarheadSub()
{
if ( special1 < 40 )
SWWMHandler.DoExplosion(self,320-special1*8,600000-special1*12000,200+special1*30);
SWWMUtility.DoExplosion(self,320-special1*8,600000-special1*12000,200+special1*30);
special1++;
if ( (special1 <= 30) && !(special1%2) )
{
@ -782,7 +782,7 @@ Class HellblazerClusterMini : HellblazerMissile2
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("BigScorch",50);
A_SetScale(2.5);
SWWMHandler.DoExplosion(self,80,200000,150,60);
SWWMUtility.DoExplosion(self,80,200000,150,60);
A_NoGravity();
A_QuakeEx(4,4,4,12,0,1000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:400,rollIntensity:.6);
A_StopSound(CHAN_BODY);
@ -1052,7 +1052,7 @@ Class HellblazerRavagerArm : Actor
l.target = p;
}
if ( !(ReactionTime%1) )
SWWMHandler.DoExplosion(self,4+reactiontime*2,1000+200*reactiontime,250-6*reactiontime);
SWWMUtility.DoExplosion(self,4+reactiontime*2,1000+200*reactiontime,250-6*reactiontime);
double spd = vel.length();
vel = (vel*.4+(FRandom[ExploS](-.2,.2),FRandom[ExploS](-.2,.2),FRandom[ExploS](-.2,.2))).unit()*spd;
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
@ -1134,7 +1134,7 @@ Class HellblazerWarheadArm : Actor
{
A_CountDown();
Spawn("HellblazerWarheadTrail",pos);
SWWMHandler.DoExplosion(self,30+reactiontime*5,3000+500*reactiontime,120+3*reactiontime);
SWWMUtility.DoExplosion(self,30+reactiontime*5,3000+500*reactiontime,120+3*reactiontime);
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = pvel+vel*.2;
@ -1257,7 +1257,7 @@ Class Hellblazer : SWWMWeapon
Vector3 x, y, z, x2, y2, z2, dir, origin;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,bAlt?22000.:32000.);
SWWMUtility.DoKnockback(self,-x,bAlt?22000.:32000.);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z);
a = FRandom[Hellblazer](0,360);

View file

@ -975,7 +975,7 @@ Class Wallbuster : SWWMWeapon
{
int realdmg = dmg?dmg:t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',!large);
SWWMHandler.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Wallbuster](0.4,1.2));
SWWMUtility.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Wallbuster](0.4,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD || t.HitList[i].HitActor.bINVULNERABLE )
{
let p = Spawn(impact,t.HitList[i].HitLocation);
@ -1187,7 +1187,7 @@ Class Wallbuster : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-1,1)+z*FRandom[Wallbuster](-1,1);
}
SWWMHandler.DoKnockback(self,-x,18000.);
SWWMUtility.DoKnockback(self,-x,18000.);
break;
case 2:
// saltshot
@ -1227,7 +1227,7 @@ Class Wallbuster : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-2,2)+z*FRandom[Wallbuster](-2,2);
}
SWWMHandler.DoKnockback(self,-x,17000.);
SWWMUtility.DoKnockback(self,-x,17000.);
break;
case 3:
// lead ball
@ -1263,7 +1263,7 @@ Class Wallbuster : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-1,1)+z*FRandom[Wallbuster](-1,1);
}
SWWMHandler.DoKnockback(self,-x,7200.);
SWWMUtility.DoKnockback(self,-x,7200.);
break;
default:
// buckshot
@ -1308,7 +1308,7 @@ Class Wallbuster : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-2,2)+z*FRandom[Wallbuster](-2,2);
}
SWWMHandler.DoKnockback(self,-x,12000.);
SWWMUtility.DoKnockback(self,-x,12000.);
break;
}
}

File diff suppressed because it is too large Load diff

View file

@ -84,7 +84,7 @@ Class EvisceratorChunk : Actor
Radius 2;
Height 2;
Speed 50;
DamageFunction int(clamp((vel.length()-6)*.2+max(0,1.-lifetime)*5.,0,12));
DamageFunction int(clamp((vel.length()-6)*.15+max(0,1.-lifetime)*3.,0,10));
DamageType 'shot';
BounceFactor 1.0;
WallBounceFactor 1.0;
@ -237,7 +237,7 @@ Class EvisceratorChunk : Actor
lasthit = victim;
// don't knock back if already dead
int oldamt = SWWMDamageAccumulator.GetAmount(victim);
if ( victim.health-oldamt > 0 ) SWWMHandler.DoKnockback(victim,vel.unit(),12000);
if ( victim.health-oldamt > 0 ) SWWMUtility.DoKnockback(victim,vel.unit(),12000);
// gather damage
SWWMDamageAccumulator.Accumulate(victim,GetMissileDamage(0,0),self,target,damagetype);
int amt = SWWMDamageAccumulator.GetAmount(victim);
@ -359,7 +359,7 @@ Class EvisceratorProj : Actor
A_SprayDecal("BigScorch",50);
A_NoGravity();
A_SetScale(3.5);
SWWMHandler.DoExplosion(self,150,120000,200,80);
SWWMUtility.DoExplosion(self,150,120000,200,80);
A_QuakeEx(6,6,6,20,0,1200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.7);
A_StartSound("eviscerator/shell",CHAN_WEAPON,attenuation:.5);
A_StartSound("eviscerator/shell",CHAN_VOICE,attenuation:.3);
@ -612,7 +612,7 @@ Class Eviscerator : SWWMWeapon
Vector3 x, y, z, x2, y2, z2, dir, origin;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,25000.);
SWWMUtility.DoKnockback(self,-x,25000.);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z);
for ( int i=0; i<40; i++ )
@ -669,7 +669,7 @@ Class Eviscerator : SWWMWeapon
Vector3 x, y, z, x2, y2, z2, dir, origin;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,32000.);
SWWMUtility.DoKnockback(self,-x,32000.);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z);
a = FRandom[Eviscerator](0,360);

View file

@ -31,7 +31,7 @@ Class AirBullet : FastProjectile
if ( target == lasthit ) return 0;
lasthit = target;
}
SWWMHandler.DoKnockback(target,vel.unit(),10000);
SWWMUtility.DoKnockback(target,vel.unit(),10000);
return damage;
}
override void Effect()
@ -54,7 +54,7 @@ Class AirBullet : FastProjectile
s.alpha *= .2;
}
bAMBUSH = true;
SWWMHandler.DoExplosion(self,GetMissileDamage(0,0),0,50,ignoreme:target);
SWWMUtility.DoExplosion(self,GetMissileDamage(0,0),0,50,ignoreme:target);
bAMBUSH = false;
tcnt++;
if ( tcnt < 2 ) return;
@ -72,9 +72,9 @@ Class AirBullet : FastProjectile
dir /= dist;
double mm = 8000000./max(20.,dist);
if ( (target.pos.z > target.floorz) && target.TestMobjZ() ) mm *= 1.6;
SWWMHandler.DoKnockback(target,dir,mm);
SWWMUtility.DoKnockback(target,dir,mm);
}
SWWMHandler.DoExplosion(self,0,100000,150,ignoreme:target);
SWWMUtility.DoExplosion(self,0,100000,150,ignoreme:target);
A_QuakeEx(6,6,6,20,0,250,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
A_StartSound("deepimpact/bullethit",CHAN_VOICE,CHANF_DEFAULT,1.,.3);
A_SprayDecal("ImpactMark");
@ -282,7 +282,7 @@ Class DeepImpact : SWWMWeapon
invoker.clipcount = max(0,invoker.clipcount-3);
Vector3 x, y, z, dir;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,2000.);
SWWMUtility.DoKnockback(self,-x,2000.);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-3*z);
DeepTracer t = new("DeepTracer");
t.ignoreme = self;
@ -348,7 +348,7 @@ Class DeepImpact : SWWMWeapon
{
wnorm /= wallhits;
if ( (pos.z > floorz) && TestMobjZ() ) wnorm *= 2.4;
SWWMHandler.DoKnockback(self,wnorm.unit(),wnorm.length()*5000000.);
SWWMUtility.DoKnockback(self,wnorm.unit(),wnorm.length()*5000000.);
}
for ( int i=0; i<list.Size(); i++ )
{
@ -401,7 +401,7 @@ Class DeepImpact : SWWMWeapon
invoker.clipcount = 0;
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,42000.);
SWWMUtility.DoKnockback(self,-x,42000.);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-3*z);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.002);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);

View file

@ -330,6 +330,76 @@ Class KirinManga : SWWMCollectable
}
*/
// yay!
Class FancyConfetti : Actor
{
int deadtimer;
double anglevel, pitchvel, rollvel;
Default
{
Radius 2;
Height 2;
+NOBLOCKMAP;
+DROPOFF;
+MOVEWITHSECTOR;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
+SLIDESONWALLS;
Gravity 0.05;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
deadtimer = Random[Junk](-30,30);
anglevel = FRandom[Junk](3,12)*RandomPick[Junk](-1,1);
pitchvel = FRandom[Junk](3,12)*RandomPick[Junk](-1,1);
rollvel = FRandom[Junk](3,12)*RandomPick[Junk](-1,1);
if ( bAMBUSH ) frame = 0;
else frame = Random[Junk](0,9);
scale *= Frandom[Junk](0.8,1.2);
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
vel.xy *= 0.98;
if ( vel.z > 0 ) vel.z *= 0.98;
if ( CurState == ResolveState("Death") )
{
deadtimer++;
if ( deadtimer > 300 ) A_FadeOut(0.05);
return;
}
}
States
{
Spawn:
XZW1 # 1
{
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
return A_JumpIf(pos.z<=floorz,"Death");
}
Loop;
Death:
XZW1 # -1
{
A_Stop();
pitch = roll = 0;
}
Stop;
Dummy:
XZW1 ABCDEFGHIJ -1;
Stop;
}
}
// Collectable box (recycling of discarded "chance box" item)
Class Chancebox : Actor
{

1713
zscript/swwm_handler.zsc Normal file

File diff suppressed because it is too large Load diff

166
zscript/swwm_hudextra.zsc Normal file
View file

@ -0,0 +1,166 @@
// additional hud things
// Press F to Pay Respects
Class PayRespects : HUDMessageBase
{
Vector2 basepos;
int lifespan, initialspan, starttic;
transient Font TewiFont;
double scale;
Vector2 hs, ss;
int seed, seed2;
static PayRespects PressF()
{
let f = new("PayRespects");
f.basepos = (FRandom[FInTheChat](0.,1.),FRandom[FInTheChat](1.02,1.05));
f.scale = FRandom[FInTheChat](.5,2.);
f.lifespan = f.initialspan = Random[FInTheChat](20,80);
f.starttic = level.maptime;
f.seed = Random[FInTheChat]();
f.seed2 = Random[FInTheChat]();
f.ScreenSizeChanged();
return f;
}
override bool Tick()
{
lifespan--;
return (lifespan<=0);
}
override void ScreenSizeChanged()
{
hs = StatusBar.GetHUDScale()*scale;
ss = (Screen.GetWidth()/hs.x,Screen.GetHeight()/hs.y);
}
override void Draw( int bottom, int visibility )
{
Vector2 realpos = (basepos.x*ss.x,basepos.y*ss.y);
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
Vector2 fo = (TewiFont.StringWidth("F")/2.,-TewiFont.GetHeight());
// F rise up
int initspd = (128-seed);
if ( (initspd >= 0) && (initspd < 32) ) initspd = 32;
if ( (initspd < 0) && (initspd > -32) ) initspd = -32;
int boostup = 32+(seed2/4);
double fractic = SWWMStatusBar(statusbar)?SWWMStatusBar(statusbar).fractic:0;
fo.x += (.15*initspd)*((initialspan-(lifespan-fractic))**.6);
fo.y += ((initialspan-(lifespan-fractic))**1.6)-boostup*sin((90./initialspan)*(level.maptime+fractic-starttic));
double alph = clamp((lifespan+fractic)/double(initialspan),0.,1.);
Screen.DrawText(TewiFont,Font.CR_GREEN,realpos.x-fo.x,realpos.y-fo.y,"F",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
}
// One-liners
Class SWWMOneLiner : HUDMessageBase
{
String whichline;
int lifespan, curtime;
transient Font TewiFont, MPlusFont;
transient CVar safezone, lang, hscale;
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 ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]);
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
if ( !MPlusFont ) MPlusFont = Font.GetFont('MPlusShaded');
if ( !hscale ) hscale = CVar.GetCVar('swwm_hudscale',players[consoleplayer]);
int margin = safezone.GetInt();
Vector2 hs;
if ( hscale.GetInt() <= 0 ) hs = StatusBar.GetHUDScale();
else
{
hs.x = hscale.GetInt();
if ( Screen.GetWidth()/hs.x < 640. ) hs.x = max(1,floor(Screen.GetWidth()/640.));
hs.y = hs.x;
}
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 = StringTable.Localize("$SWWM_LQUOTE")..loc..StringTable.Localize("$SWWM_RQUOTE");
Font fnt = TewiFont;
if ( lang.GetString() ~== "jp" ) fnt = MPlusFont;
// split so it can fit
BrokenLines l = fnt.BreakLines(locs,int(ss.x*.5));
int maxlen = 0;
for ( int i=0; i<l.Count(); i++ )
{
int len = fnt.StringWidth(l.StringAt(i));
if ( len > maxlen ) maxlen = len;
}
int h = fnt.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 = fnt.StringWidth(l.StringAt(i));
Screen.DrawText(fnt,Font.CR_FIRE,int((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;
}
}
}
Class LastLine
{
String type;
int lineno;
}
// 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;
}

View file

@ -572,7 +572,7 @@ Class BigPunchSplash : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,special1,40000,120,60,ignoreme:target);
SWWMUtility.DoExplosion(self,special1,40000,120,60,ignoreme:target);
Destroy();
}
}
@ -1149,7 +1149,7 @@ Class SWWMWeapon : Weapon abstract
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);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000);
if ( raging )
{
invoker.bEXTREMEDEATH = true;

View file

@ -290,7 +290,7 @@ Class PusherProjectile : Actor
if ( target.bNOBLOOD || target.bINVULNERABLE ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP);
else A_StartSound("pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
target.A_QuakeEx(6,6,6,10,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.7);
SWWMHandler.DoKnockback(target,vel.unit(),85000);
SWWMUtility.DoKnockback(target,vel.unit(),85000);
return damage;
}
override void Touch( Actor toucher )
@ -381,7 +381,7 @@ Class PusherWeapon : SWWMWeapon
{
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
self.angle += clamp(diff,-5.,5.);
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,8500);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,8500);
d.HitActor.A_QuakeEx(4,4,4,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
@ -466,7 +466,7 @@ Class PusherWeapon : SWWMWeapon
{
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
self.angle += clamp(diff,-5.,5.);
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,85000);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,85000);
d.HitActor.A_QuakeEx(9,9,9,15,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
@ -517,7 +517,7 @@ Class PusherWeapon : SWWMWeapon
p.pitch = pitch;
p.vel = x*p.speed*invoker.chargelevel;
p.target = self;
SWWMHandler.DoKnockback(self,x,85000.);
SWWMUtility.DoKnockback(self,x,85000.);
}
if ( !gone )
{

View file

@ -1016,7 +1016,7 @@ Class Demolitionist : PlayerPawn
}
if ( jumpactor && jumpactor.bSHOOTABLE )
{
SWWMHandler.DoKnockback(jumpactor,(-walldir,0),12000);
SWWMUtility.DoKnockback(jumpactor,(-walldir,0),12000);
int dmg = jumpactor.DamageMobj(self,self,10,'Jump');
if ( FindInventory("RagekitPower") )
{
@ -2006,7 +2006,7 @@ Class DemolitionistShockwave : Actor
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.2,pitch:.7);
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.1,pitch:.4);
}
SWWMHandler.DoExplosion(self,40+min(special1,120),100000+min(special1*2000,150000),100+min(special1*2,130),80,DE_BLAST|DE_EXTRAZTHRUST,'GroundPound',target);
SWWMUtility.DoExplosion(self,40+min(special1,120),100000+min(special1*2000,150000),100+min(special1*2,130),80,DE_BLAST|DE_EXTRAZTHRUST,'GroundPound',target);
for ( int i=0; i<360; i+=5 )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3);

123
zscript/swwm_shame.zsc Normal file
View file

@ -0,0 +1,123 @@
// SHAMEFUL DISPLAY
// >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;
}
SetRandomSeed[bdscreen](Random[bdscreen]()+consoleplayer+MSTime());
if ( detected )
Console.Printf("\cg========\n\cgIf you have BD on your autoload, you really shouldn't.\n\cgIf you manually loaded it with this mod, why would you?\n\cgThey're not compatible and never will be.\n\cg========");
}
override void UiTick()
{
if ( !detected ) return;
if ( gamestate == GS_LEVEL )
{
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 ) scr = TexMan.CheckForTexture("graphics/bdscreen.png",TexMan.Type_Any);
Screen.Dim("Red",(timer/350.)-.2,0,0,Screen.GetWidth(),Screen.GetHeight());
double ar = Screen.GetAspectRatio();
Vector2 tsize = TexMan.GetScaledSize(scr);
Vector2 vsize = (Screen.GetWidth(),Screen.GetHeight());
if ( (tsize.x > vsize.x) || (tsize.y > vsize.y) )
{
double sar = tsize.x/tsize.y;
if ( sar > ar ) vsize = (tsize.x,tsize.x/ar);
else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y);
else vsize = tsize;
}
Screen.DrawTexture(scr,false,(vsize.x-tsize.x)/2.+FRandom[bdscreen](-1,1)*max(timer-40,0)**3*.000003,(vsize.y-tsize.y)/2.+FRandom[bdscreen](-1,1)*max(timer-40,0)**3*.000003,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true,DTA_Alpha,min(1.,timer/50.));
Screen.Dim("Red",(timer/70.)-3.5,0,0,Screen.GetWidth(),Screen.GetHeight());
}
}
// HORNY
Class SWWMHDoomHandler : StaticEventHandler
{
ui int timer;
ui TextureID scr;
bool detected;
override void OnRegister()
{
for ( int i=0; i<AllActorClasses.size(); i++ )
{
if ( AllActorClasses[i].GetClassName() != "HDoomPlayer" ) continue;
detected = true;
break;
}
SetRandomSeed[hdscreen](Random[hdscreen]()+consoleplayer+MSTime());
if ( detected )
Console.Printf("\cg========\n\cgHa ha very funny.\n\cgIf you want the Demolitionist to fuck some hot demon girls go commission a porn artist.\n\cg========");
}
override void UiTick()
{
if ( !detected ) return;
if ( gamestate == GS_LEVEL )
{
if ( timer == 50 )
{
S_StartSound("bestsound",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
S_StartSound("bestsound",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
}
else if ( timer == 70 )
{
int ngiggl = Random[hdscreen](1,14);
S_StartSound(String.Format("saya/giggle%d",ngiggl),CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
S_StartSound(String.Format("saya/giggle%d",ngiggl),CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
}
timer++;
}
else timer = 0;
}
override void RenderOverlay( RenderEvent e )
{
if ( !detected || (timer < 50) ) return;
if ( !scr ) scr = TexMan.CheckForTexture("graphics/hdscreen.png",TexMan.Type_Any);
double ar = Screen.GetAspectRatio();
Vector2 tsize = TexMan.GetScaledSize(scr);
Vector2 vsize = (Screen.GetWidth(),Screen.GetHeight());
if ( (tsize.x > vsize.x) || (tsize.y > vsize.y) )
{
double sar = tsize.x/tsize.y;
if ( sar > ar ) vsize = (tsize.x,tsize.x/ar);
else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y);
else vsize = tsize;
}
Screen.DrawTexture(scr,false,(vsize.x-tsize.x)/2.,(vsize.y-tsize.y)/2.,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
}
}

View file

@ -26,89 +26,6 @@ Class GoldShellCasing : RedShellCasing
}
}
Class SWWMDamageAccumulator : Thinker
{
Actor victim, inflictor, source;
Array<Int> amounts;
int total;
Name type;
bool dontgib;
int flags;
override void Tick()
{
Super.Tick();
// so many damn safeguards in this
if ( !victim )
{
Destroy();
return;
}
int gibhealth = victim.GetGibHealth();
// おまえはもう死んでいる
if ( (victim.health-total <= gibhealth) && !dontgib )
{
// safeguard for inflictors that have somehow ceased to exist, which apparently STILL CAN HAPPEN
if ( inflictor ) inflictor.bEXTREMEDEATH = true;
else type = 'Extreme';
}
// make sure accumulation isn't reentrant
if ( inflictor && (inflictor is 'EvisceratorChunk') ) inflictor.bAMBUSH = true;
// 何?
for ( int i=0; i<amounts.Size(); i++ )
{
if ( !victim ) break;
victim.DamageMobj(inflictor,source,amounts[i],type,DMG_THRUSTLESS|flags);
}
// clean up
if ( inflictor )
{
if ( inflictor is 'EvisceratorChunk' ) inflictor.bAMBUSH = false;
inflictor.bEXTREMEDEATH = false;
}
Destroy();
}
static void Accumulate( Actor victim, int amount, Actor inflictor, Actor source, Name type, bool dontgib = false, int flags = 0 )
{
if ( !victim ) return;
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
match = a;
break;
}
if ( !match )
{
match = new("SWWMDamageAccumulator");
match.ChangeStatNum(STAT_USER);
match.victim = victim;
match.amounts.Clear();
}
match.amounts.Push(amount);
match.total += amount;
match.inflictor = inflictor;
match.source = source;
match.type = type;
match.dontgib = dontgib;
match.flags = flags;
}
static clearscope int GetAmount( Actor victim )
{
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
return a.total;
}
return 0;
}
}
Class SpreadgunTracer : LineTracer
{
Actor ignoreme;
@ -457,7 +374,7 @@ Class DragonBreathArm : Actor
let p = Spawn("DragonBreathPuff",pos);
p.alpha *= .6+.4*(ReactionTime/20.);
p.scale *= 3.5-2.5*(ReactionTime/20.);
if ( !(ReactionTime%3) )
if ( !(ReactionTime%5) )
{
let l = Spawn("PaletteLight",pos);
l.Args[3] = int(90+50*(ReactionTime/20.));
@ -465,7 +382,7 @@ Class DragonBreathArm : Actor
l.target = p;
}
if ( !(ReactionTime%2) )
SWWMHandler.DoExplosion(self,1+(reactiontime/1.5),1000+200*reactiontime,150-6*reactiontime,ignoreme:bHITOWNER?null:target);
SWWMUtility.DoExplosion(self,1+(reactiontime*1.5),1000+200*reactiontime,150-6*reactiontime,ignoreme:bHITOWNER?null:target);
double spd = vel.length();
vel = (vel*.4+(FRandom[ExploS](-.2,.2),FRandom[ExploS](-.2,.2),FRandom[ExploS](-.2,.2))).unit()*spd;
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
@ -581,7 +498,7 @@ Class SaltImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(6000);
SWWMHandler.DoExplosion(self,25+special2*5,15000,100,40);
SWWMUtility.DoExplosion(self,25+special2*5,15000,100,40);
A_QuakeEx(3,3,3,10,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:150,rollintensity:0.2);
A_StartSound("spreadgun/salt",CHAN_VOICE,attenuation:.35);
A_SprayDecal("ShockMarkSmall",-172);
@ -691,7 +608,7 @@ Class SaltBeam : Actor
if ( args[1] ) BusterWall.Bust(t.Results,85+Accuracy*10,target,x,t.Results.HitPos.z);
if ( t.Results.HitType == TRACE_HitActor )
{
SWWMHandler.DoKnockback(t.Results.HitActor,x,25000);
SWWMUtility.DoKnockback(t.Results.HitActor,x,25000);
t.Results.HitActor.DamageMobj(self,target,60+Accuracy*5,'Salt',DMG_THRUSTLESS);
}
Vector3 norm = -x;
@ -761,7 +678,7 @@ Class SaltBeam : Actor
if ( isFrozen() ) return;
A_FadeOut(.04);
if ( Random[Spreadgun](-2,args[2]/10) == 0 )
SWWMHandler.DoExplosion(self,5+Accuracy,5000,32,ignoreme:target);
SWWMUtility.DoExplosion(self,5+Accuracy,5000,32,ignoreme:target);
if ( ((special2%5) || args[2]) && !special1 ) SpreadOut();
args[2]++;
if ( !CheckNoDelay() || (tics == -1) ) return;
@ -1423,7 +1340,7 @@ Class TheBall : Actor
}
crit = true;
}
SWWMHandler.DoKnockback(victim,vel.unit(),slamforce);
SWWMUtility.DoKnockback(victim,vel.unit(),slamforce);
bool bleeds = (victim && !victim.bINVULNERABLE && !victim.bNOBLOOD && is_schutt);
int newdmg = victim.DamageMobj(self,target,dmg,'Concussion',crit?(DMG_THRUSTLESS|DMG_FOILINVUL):DMG_THRUSTLESS); // crits ignore invulnerability
Vector3 dir = -vel.unit();
@ -1450,7 +1367,7 @@ Class TheBall : Actor
}
}
if ( crit )
SWWMHandler.DoExplosion(self,dmg/2,25000,150,80,ignoreme:target);
SWWMUtility.DoExplosion(self,dmg/2,25000,150,80,ignoreme:target);
// only rip shootables
if ( (slamforce > girth) && is_schutt )
{
@ -1656,7 +1573,7 @@ Class GoldenImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(40000);
SWWMHandler.DoExplosion(self,7777,40000,600,500,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,7777,40000,600,500,DE_EXTRAZTHRUST);
A_QuakeEx(9,9,9,40,0,5000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollintensity:1.5);
A_StartSound("spreadgun/goldexpl",CHAN_VOICE,attenuation:.3);
A_StartSound("spreadgun/goldexpl",CHAN_WEAPON,attenuation:.15);
@ -1783,7 +1700,7 @@ Class GoldenSubImpact : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,777,30000,400,300,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,777,30000,400,300,DE_EXTRAZTHRUST);
A_QuakeEx(7,7,7,20,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollintensity:.8);
A_SprayDecal("BigScorch",-172);
Scale *= FRandom[ExploS](0.8,1.1);
@ -1908,7 +1825,7 @@ Class GoldenSubSubImpact : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,77,20000,200,100,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,77,20000,200,100,DE_EXTRAZTHRUST);
A_QuakeEx(4,4,4,15,0,1000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollintensity:.4);
A_SprayDecal("Scorch",-172);
Scale *= FRandom[ExploS](0.8,1.1);
@ -2128,7 +2045,7 @@ Class Spreadgun : SWWMWeapon
{
int realdmg = dmg?dmg:t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',!large);
SWWMHandler.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Spreadgun](0.4,1.2));
SWWMUtility.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Spreadgun](0.4,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD || t.HitList[i].HitActor.bINVULNERABLE )
{
let p = Spawn(impact,t.HitList[i].HitLocation);
@ -2233,10 +2150,10 @@ Class Spreadgun : SWWMWeapon
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,25000.);
SWWMUtility.DoKnockback(self,-x,25000.);
break;
case 2:
for ( int j=0; j<9; j++ )
for ( int j=0; j<5; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.2);
@ -2270,7 +2187,7 @@ Class Spreadgun : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMHandler.DoKnockback(self,-x,13000.);
SWWMUtility.DoKnockback(self,-x,13000.);
break;
case 3:
for ( int j=0; j<8; j++ )
@ -2306,7 +2223,7 @@ Class Spreadgun : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMHandler.DoKnockback(self,-x,23000.);
SWWMUtility.DoKnockback(self,-x,23000.);
break;
case 4:
for ( int j=0; j<10; j++ )
@ -2344,7 +2261,7 @@ Class Spreadgun : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,12.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMHandler.DoKnockback(self,-x,14000.);
SWWMUtility.DoKnockback(self,-x,14000.);
break;
case 5:
a = FRandom[Spreadgun](0,360);
@ -2378,7 +2295,7 @@ Class Spreadgun : SWWMWeapon
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,9500.);
SWWMUtility.DoKnockback(self,-x,9500.);
break;
case 6:
a = FRandom[Spreadgun](0,360);
@ -2443,7 +2360,7 @@ Class Spreadgun : SWWMWeapon
s.bAMBUSH = true;
s.vel += vel*.5+x*FRandom[Spreadgun](1.,20.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMHandler.DoKnockback(self,-x,30000.);
SWWMUtility.DoKnockback(self,-x,30000.);
break;
default:
st = new("SpreadgunTracer");
@ -2483,7 +2400,7 @@ Class Spreadgun : SWWMWeapon
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMHandler.DoKnockback(self,-x,20000.);
SWWMUtility.DoKnockback(self,-x,20000.);
break;
}
break;

View file

@ -265,7 +265,7 @@ Class BigBiospark : Actor
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2));
double dist = dirto.length();
dirto /= dist;
if ( dist > 4. ) SWWMHandler.DoKnockback(t,-dirto,clamp(120.-dist,0.,120.)*100);
if ( dist > 4. ) SWWMUtility.DoKnockback(t,-dirto,clamp(120.-dist,0.,120.)*100);
}
}
Health -= 2;
@ -276,7 +276,7 @@ Class BigBiospark : Actor
{
A_StopSound(CHAN_VOICE);
A_AlertMonsters(15000);
SWWMHandler.DoExplosion(self,450,200000,300,100);
SWWMUtility.DoExplosion(self,450,200000,300,100);
A_QuakeEx(9,9,9,30,0,1400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollIntensity:1.5);
A_StartSound("biospark/bighit",CHAN_ITEM,attenuation:.4);
A_StartSound("biospark/bighit",CHAN_WEAPON,attenuation:.3);
@ -330,8 +330,8 @@ Class BigBiospark : Actor
if ( special1 > 40 ) return;
double factor = (40-special1)/40.;
double invfct = 1.-factor;
SWWMHandler.DoExplosion(self,150*factor,0.,250*invfct);
SWWMHandler.DoExplosion(self,0,-8000*factor,500*invfct);
SWWMUtility.DoExplosion(self,150*factor,0.,250*invfct);
SWWMUtility.DoExplosion(self,0,-8000*factor,500*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
{
@ -536,7 +536,7 @@ Class BiosparkBall : Actor
{
A_StopSound(CHAN_VOICE);
A_AlertMonsters(5000);
SWWMHandler.DoExplosion(self,50,120000,150,80);
SWWMUtility.DoExplosion(self,50,120000,150,80);
A_QuakeEx(6,6,6,16,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.8);
A_StartSound("biospark/hit",CHAN_ITEM,attenuation:.8);
A_StartSound("biospark/hit",CHAN_WEAPON,attenuation:.6);
@ -588,8 +588,8 @@ Class BiosparkBall : Actor
if ( special1 > 30 ) return;
double factor = (30-special1)/30.;
double invfct = 1.-factor;
SWWMHandler.DoExplosion(self,30*factor,0.,150*invfct);
SWWMHandler.DoExplosion(self,0,-5000*factor,300*invfct);
SWWMUtility.DoExplosion(self,30*factor,0.,150*invfct);
SWWMUtility.DoExplosion(self,0,-5000*factor,300*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
{
@ -699,7 +699,7 @@ Class BiosparkBeamImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(2000);
SWWMHandler.DoExplosion(self,30,120000,100,40);
SWWMUtility.DoExplosion(self,30,120000,100,40);
A_QuakeEx(3,3,3,12,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.4);
A_StartSound("biospark/beamhit",CHAN_ITEM,attenuation:1.1);
A_StartSound("biospark/beamhit",CHAN_WEAPON,attenuation:.8);
@ -752,8 +752,8 @@ Class BiosparkBeamImpact : Actor
if ( special1 > 30 ) return;
double factor = (30-special1)/30.;
double invfct = 1.-factor;
SWWMHandler.DoExplosion(self,10*factor,0.,50*invfct);
SWWMHandler.DoExplosion(self,0.,-5000*factor,100*invfct);
SWWMUtility.DoExplosion(self,10*factor,0.,50*invfct);
SWWMUtility.DoExplosion(self,0.,-5000*factor,100*invfct);
int numpt = int(Random[ExploS](8,16)*factor);
for ( int i=0; i<numpt; i++ )
{
@ -834,7 +834,7 @@ Class BiosparkComboImpactSub : Actor
{
Super.PostBeginPlay();
if ( !bAMBUSH ) return;
SWWMHandler.DoExplosion(self,200,10000,300,120);
SWWMUtility.DoExplosion(self,200,10000,300,120);
}
States
{
@ -887,7 +887,7 @@ Class BiosparkComboImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(6000);
SWWMHandler.DoExplosion(self,bAMBUSH?(700+Args[0]*100):300,bAMBUSH?480000:200000,bAMBUSH?800:400,bAMBUSH?400:200);
SWWMUtility.DoExplosion(self,bAMBUSH?(700+Args[0]*100):300,bAMBUSH?480000:200000,bAMBUSH?800:400,bAMBUSH?400:200);
A_QuakeEx(9,9,9,25,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:600,rollIntensity:1.5);
A_StartSound("biospark/bighit",CHAN_ITEM,attenuation:.4);
A_StartSound("biospark/bighit",CHAN_WEAPON,attenuation:.3);
@ -955,8 +955,8 @@ Class BiosparkComboImpact : Actor
if ( special1 > mx ) return;
double factor = (mx-special1)/mx;
double invfct = 1.-factor;
SWWMHandler.DoExplosion(self,(bAMBUSH?(500+Args[0]*50):200)*factor,0.,(bAMBUSH?900.:300.)*invfct);
SWWMHandler.DoExplosion(self,0.,-12000*factor,(bAMBUSH?1500:500)*invfct);
SWWMUtility.DoExplosion(self,(bAMBUSH?(500+Args[0]*50):200)*factor,0.,(bAMBUSH?900.:300.)*invfct);
SWWMUtility.DoExplosion(self,0.,-12000*factor,(bAMBUSH?1500:500)*invfct);
int numpt = int(Random[ExploS](8,16)*factor);
for ( int i=0; i<numpt; i++ )
{
@ -1096,7 +1096,7 @@ Class BiosparkBeam : Actor
}
else
{
SWWMHandler.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,15000);
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,15000);
t.hitlist[i].hitactor.DamageMobj(self,target,GetMissileDamage(0,0),'Electric',DMG_THRUSTLESS);
}
}
@ -1177,7 +1177,7 @@ Class BiosparkBeam : Actor
int numpt = Random[Sparkster](-1,6);
for ( int i=0; i<numpt; i++ )
{
let a = Spawn("BiosparkArc",level.Vec3Offset(pos,tdir*FRandom[Sparkster](0,1)));
let a = Spawn("BiosparkArc",level.Vec3Offset(pos,tdir*FRandom[Sparkster](frame?0:.3,1)));
a.angle = angle;
a.pitch = pitch;
a.target = target;
@ -1186,7 +1186,7 @@ Class BiosparkBeam : Actor
numpt = Random[Sparkster](3,6);
for ( int i=0; i<numpt; i++ )
{
let a = Spawn("BiosparkArcSmall",level.Vec3Offset(pos,tdir*FRandom[Sparkster](0,1)));
let a = Spawn("BiosparkArcSmall",level.Vec3Offset(pos,tdir*FRandom[Sparkster](frame?0:.3,1)));
a.angle = angle;
a.pitch = pitch;
a.target = target;
@ -1239,7 +1239,7 @@ Class BiosparkBeam : Actor
}
else
{
SWWMHandler.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,15000);
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,15000);
t.hitlist[i].hitactor.DamageMobj(self,target,int(GetMissileDamage(0,0)*alpha),'Electric',DMG_THRUSTLESS);
}
}
@ -1428,7 +1428,7 @@ Class BiosparkArc : Actor
}
for ( int i=0; i<t.hitlist.Size(); i++ )
{
SWWMHandler.DoKnockback(t.hitlist[i].hitactor,-t.hitlist[i].x,GetMissileDamage(0,0)*1000);
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,-t.hitlist[i].x,GetMissileDamage(0,0)*1000);
t.hitlist[i].hitactor.DamageMobj(self,target,GetMissileDamage(0,0),'Electric',DMG_THRUSTLESS);
}
Vector3 normal = -t.Results.HitVector, dir = t.Results.HitVector;
@ -1761,7 +1761,7 @@ Class BiosparkCore : Actor
bNOGRAVITY = true;
A_SetRenderStyle(1.,STYLE_Add);
A_AlertMonsters(5000);
SWWMHandler.DoExplosion(self,50,120000,150,60);
SWWMUtility.DoExplosion(self,50,120000,150,60);
A_QuakeEx(6,6,6,16,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.8);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
@ -1810,8 +1810,8 @@ Class BiosparkCore : Actor
if ( special2 > 30 ) return;
double factor = (30-special2)/30.;
double invfct = 1.-factor;
SWWMHandler.DoExplosion(self,0.,-5000*factor,300*invfct);
SWWMHandler.DoExplosion(self,30*factor,0.,150*invfct);
SWWMUtility.DoExplosion(self,0.,-5000*factor,300*invfct);
SWWMUtility.DoExplosion(self,30*factor,0.,150*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
{
@ -2022,7 +2022,7 @@ Class Sparkster : SWWMWeapon
A_ZoomFactor(1.);
A_AlertMonsters(1200);
A_PlayerFire();
SWWMHandler.DoKnockback(self,-x,2500.);
SWWMUtility.DoKnockback(self,-x,2500.);
a = FRandom[Spread](0,360);
s = FRandom[Spread](0,.012);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);

View file

@ -64,7 +64,7 @@ Class ExplodiumMagArm : Actor
{
A_CountDown();
Spawn("ExplodiumMagTrail",pos);
SWWMHandler.DoExplosion(self,2+reactiontime/3,3000+500*reactiontime,40+3*reactiontime,20);
SWWMUtility.DoExplosion(self,2+reactiontime/3,3000+500*reactiontime,40+3*reactiontime,20);
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = pvel+vel*.2;
@ -133,7 +133,7 @@ Class ExplodiumMagProj : Actor
A_SetRenderStyle(1.,STYLE_Add);
Scale *= 2.+.2*special1;
A_AlertMonsters(6000);
SWWMHandler.DoExplosion(self,20+15*special1,80000+8000*special1,90+10*special1,60,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,20+15*special1,80000+8000*special1,90+10*special1,60,DE_EXTRAZTHRUST);
A_QuakeEx(9,9,9,30,0,400+80*special1,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollintensity:2.);
A_StartSound("explodium/maghit",CHAN_VOICE,attenuation:.35);
A_StartSound("explodium/maghit",CHAN_WEAPON,attenuation:.2);
@ -227,7 +227,7 @@ Class ExplodiumBulletImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(3000);
SWWMHandler.DoExplosion(self,25,80000,90,40,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,25,80000,90,40,DE_EXTRAZTHRUST);
A_QuakeEx(4,4,4,10,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:150,rollintensity:0.2);
A_StartSound("explodium/hit",CHAN_VOICE,attenuation:.6);
A_StartSound("explodium/hit",CHAN_WEAPON,attenuation:.3);
@ -304,7 +304,7 @@ Class ExplodiumGun : SWWMWeapon
A_PlayerFire();
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,12000.);
SWWMUtility.DoKnockback(self,-x,4000.);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-2*z);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.002);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
@ -315,7 +315,7 @@ Class ExplodiumGun : SWWMWeapon
if ( d.HitType == TRACE_HitActor )
{
int dmg = 25;
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,48000);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,48000);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{

View file

@ -111,7 +111,7 @@ Class CandyBeam : Actor
}
for ( int i=0; i<t.hitlist.Size(); i++ )
{
SWWMHandler.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,12000);
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,12000);
t.hitlist[i].hitactor.DamageMobj(self,target,GetMissileDamage(0,0),'Explodium',DMG_THRUSTLESS);
}
Vector3 normal = -t.Results.HitVector, dir = t.Results.HitVector;
@ -250,7 +250,7 @@ Class CandyPop : Actor
Spawn:
BLPF B 3 NoDelay
{
SWWMHandler.DoExplosion(self,200,60000,120,40);
SWWMUtility.DoExplosion(self,200,60000,120,40);
Scale *= FRandom[ExploS](0.6,1.8);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
@ -314,7 +314,7 @@ Class TinyCandyPop : CandyPop
Spawn:
BLPF B 3 NoDelay
{
SWWMHandler.DoExplosion(self,60,32000,80,20);
SWWMUtility.DoExplosion(self,60,32000,80,20);
Scale *= FRandom[ExploS](0.6,1.8);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
@ -372,7 +372,7 @@ Class CandyMagArm : Actor
{
A_CountDown();
Spawn("CandyMagTrail",pos);
SWWMHandler.DoExplosion(self,80+reactiontime*7,3000+800*reactiontime,50+6*reactiontime);
SWWMUtility.DoExplosion(self,80+reactiontime*7,3000+800*reactiontime,50+6*reactiontime);
double spd = vel.length();
vel = (vel*.1+(FRandom[ExploS](-.7,.7),FRandom[ExploS](-.7,.7),FRandom[ExploS](-.7,.7))).unit()*spd;
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
@ -448,7 +448,7 @@ Class CandyMagArmBig : CandyMagArm
{
ReactionTime--;
Spawn("CandyMagTrailBig",pos);
SWWMHandler.DoExplosion(self,120+reactiontime*10,3000+900*reactiontime,100+8*reactiontime);
SWWMUtility.DoExplosion(self,120+reactiontime*10,3000+900*reactiontime,100+8*reactiontime);
double spd = vel.length();
vel = (vel*.1+(FRandom[ExploS](-.5,.5),FRandom[ExploS](-.5,.5),FRandom[ExploS](-.5,.5))).unit()*spd;
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
@ -527,7 +527,7 @@ Class CandyGunProj : Actor
A_SetRenderStyle(1.,STYLE_Add);
Scale *= 6.+.2*special1;
A_AlertMonsters(40000);
SWWMHandler.DoExplosion(self,4000+700*special1,80000+15000*special1,400+20*special1,250,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,4000+700*special1,80000+15000*special1,400+20*special1,250,DE_EXTRAZTHRUST);
A_QuakeEx(9,9,9,70,0,1500+100*special1,"",QF_RELATIVE|QF_SCALEDOWN,falloff:1200,rollintensity:2.);
A_StartSound("candygun/gunhit",CHAN_VOICE,attenuation:.24);
A_StartSound("candygun/gunhit",CHAN_WEAPON,attenuation:.12);
@ -637,7 +637,7 @@ Class CandyMagProj : Actor
A_SetRenderStyle(1.,STYLE_Add);
Scale *= 3.+.2*special1;
A_AlertMonsters(20000);
SWWMHandler.DoExplosion(self,800+700*special1,60000+15000*special1,200+15*special1,100,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,800+700*special1,60000+15000*special1,200+15*special1,100,DE_EXTRAZTHRUST);
A_QuakeEx(9,9,9,30,0,500+80*special1,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollintensity:2.);
A_StartSound("candygun/maghit",CHAN_VOICE,attenuation:.24);
A_StartSound("candygun/maghit",CHAN_WEAPON,attenuation:.12);
@ -731,7 +731,7 @@ Class CandyBulletImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(9000);
SWWMHandler.DoExplosion(self,700,48000,200,80,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,700,48000,200,80,DE_EXTRAZTHRUST);
A_QuakeEx(6,6,6,15,0,300,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollintensity:0.2);
A_StartSound("candygun/hit",CHAN_VOICE,attenuation:.25);
A_StartSound("candygun/hit",CHAN_WEAPON,attenuation:.5);
@ -900,7 +900,14 @@ Class CandyGun : SWWMWeapon
if ( chambered ) Screen.DrawText(TewiFont,Font.CR_FIRE,bx-22,by-21,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1);
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-19,by-14,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int cx = (Ammo1.Amount>9)?48:45;
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-18,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int sb = Owner.CountInv("CandyGunBullets");
if ( sb > 0 )
{
int cbx = (sb>9)?50:47;
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cbx,by-21,String.Format("⁺%s",SWWMUtility.SuperscriptNum(sb)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1);
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-14,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
else Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-18,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-37,by-40,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
@ -920,7 +927,7 @@ Class CandyGun : SWWMWeapon
A_PlayerFire();
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMHandler.DoKnockback(self,-x,18000.);
SWWMUtility.DoKnockback(self,-x,18000.);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-2*z);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.005);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
@ -931,7 +938,7 @@ Class CandyGun : SWWMWeapon
if ( d.HitType == TRACE_HitActor )
{
int dmg = 900;
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,72000);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,72000);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS|DMG_FOILINVUL,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD )
{
@ -1054,8 +1061,9 @@ Class CandyGun : SWWMWeapon
int dropamt = invoker.clipcount-maxgiveamt;
if ( dropamt > 0 ) ma.CreateTossable(dropamt);
ma.Amount = min(ma.MaxAmount,ma.Amount+invoker.clipcount);
if ( CheckLocalView() ) for ( int i=0; i<(invoker.clipcount-dropamt); i++ ) ma.PrintPickupMessage(true,ma.PickupMessage());
if ( (invoker.clipcount-dropamt) > 0 ) ma.PlayPickupSound(self);
ma.MagFill();
if ( CheckLocalView() ) for ( int i=0; i<min(ma.Amount,invoker.clipcount-dropamt); i++ ) ma.PrintPickupMessage(true,ma.PickupMessage());
if ( min(ma.Amount,invoker.clipcount-dropamt) > 0 ) ma.PlayPickupSound(self);
invoker.clipcount = 0;
}
@ -1075,14 +1083,14 @@ Class CandyGun : SWWMWeapon
override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount )
{
if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true;
if ( fireMode == PrimaryFire ) return (chambered || (clipcount > 0) || (Ammo1.Amount > 0));
if ( fireMode == AltFire ) return (Ammo1.Amount > 0);
if ( fireMode == PrimaryFire ) return (chambered || (clipcount > 0) || (Ammo1.Amount > 0) || (Owner.CountInv("CandyGunBullets") > 0));
if ( fireMode == AltFire ) return ((Ammo1.Amount > 0) || (Owner.CountInv("CandyGunBullets") > 0));
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
}
override bool ReportHUDAmmo()
{
if ( chambered || (clipcount > 0) ) return true;
if ( chambered || (clipcount > 0) || (Owner.CountInv("CandyGunBullets") > 0) ) return true;
if ( Ammo1.Amount <= 0 ) return false;
return Super.ReportHUDAmmo();
}
@ -1119,13 +1127,13 @@ Class CandyGun : SWWMWeapon
Ready:
XZW2 A 1
{
if ( (invoker.clipcount <= 0) && !invoker.chambered && (invoker.Ammo1.Amount > 0) ) player.SetPSprite(PSP_WEAPON,ResolveState("Reload"));
if ( (invoker.clipcount <= 0) && !invoker.chambered && ((invoker.Ammo1.Amount > 0) || (CountInv("CandyGunBullets") > 0)) ) player.SetPSprite(PSP_WEAPON,ResolveState("Reload"));
else if ( (invoker.clipcount > 0) && !invoker.chambered ) player.SetPSprite(PSP_WEAPON,ResolveState("Slide"));
else
{
int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1;
if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) || (invoker.Ammo1.Amount > 0) || invoker.chambered ) flg |= WRF_ALLOWRELOAD;
if ( (invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) flg |= WRF_NOSECONDARY;
if ( (invoker.Ammo1.Amount <= 0) && (CountInv("CandyGunBullets") <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) flg |= WRF_NOSECONDARY;
A_WeaponReady(flg);
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) )
invoker.CheckAmmo(EitherFire,true);
@ -1148,7 +1156,7 @@ Class CandyGun : SWWMWeapon
XZW5 Q 1;
XZW5 R 1
{
if ( player.cmd.buttons&BT_ATTACK && (((invoker.Ammo1.Amount > 0) && (invoker.Ammo2.Amount > 0)) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) )
if ( player.cmd.buttons&BT_ATTACK && ((((invoker.Ammo1.Amount > 0) || (CountInv("CandyGunBullets") > 0)) && (invoker.Ammo2.Amount > 0)) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) )
player.SetPSprite(PSP_WEAPON,ResolveState("SpecialFire"));
}
XZW5 STUVWXYZ 1;
@ -1187,15 +1195,26 @@ Class CandyGun : SWWMWeapon
XZW1 B 0
{
invoker.PlayUpSound(self);
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) )
invoker.clipcount = invoker.default.clipcount;
else if ( invoker.Ammo1.Amount <= 0 )
{
MagAmmo sb = MagAmmo(FindInventory("CandyGunBullets"));
int takeamt = min(sb.Amount,sb.ClipSize);
invoker.clipcount = takeamt;
sb.Amount -= takeamt;
}
else
{
invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1);
invoker.clipcount = invoker.default.clipcount;
invoker.clipcount = invoker.default.clipcount;
}
}
Goto Select;
Reload:
XZW2 A 1
{
if ( (invoker.Ammo1.Amount <= 0) || (invoker.clipcount >= invoker.default.clipcount) ) return ResolveState("CheckBullet");
if ( ((invoker.Ammo1.Amount <= 0) && (CountInv("CandyGunBullets") <= 0)) || (invoker.clipcount >= invoker.default.clipcount) ) return ResolveState("CheckBullet");
A_PlayerReload();
if ( invoker.clipcount <= 0 ) return ResolveState("ReloadEmpty");
return ResolveState(null);
@ -1225,9 +1244,20 @@ Class CandyGun : SWWMWeapon
XZW4 R 1
{
A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP);
invoker.clipcount = invoker.default.clipcount;
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) )
invoker.clipcount = invoker.default.clipcount;
else if ( invoker.Ammo1.Amount <= 0 )
{
MagAmmo sb = MagAmmo(FindInventory("CandyGunBullets"));
int takeamt = min(sb.Amount,sb.ClipSize);
invoker.clipcount = takeamt;
sb.Amount -= takeamt;
}
else
{
invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1);
invoker.clipcount = invoker.default.clipcount;
}
}
XZW4 STUV 1;
XZW2 A 1 A_JumpIf(!invoker.chambered,"Slide");

View file

@ -56,7 +56,7 @@ Class SilverAirRip : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,40,2000,40,ignoreme:target);
SWWMUtility.DoExplosion(self,40,2000,40,ignoreme:target);
Destroy();
}
}
@ -78,7 +78,7 @@ Class SilverAirRip2 : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,20,2000,30,ignoreme:target);
SWWMUtility.DoExplosion(self,20,2000,30,ignoreme:target);
Destroy();
}
}
@ -101,7 +101,7 @@ Class SilverImpact : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,100,8000,100,20);
SWWMUtility.DoExplosion(self,100,8000,100,20);
A_AlertMonsters(2500);
A_QuakeEx(4,4,4,20,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.9);
if ( special1 )
@ -394,7 +394,7 @@ Class FatChodeImpact : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMHandler.DoExplosion(self,1500,40000,250,120,DE_THRUWALLS|DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,1500,40000,250,120,DE_THRUWALLS|DE_EXTRAZTHRUST);
A_AlertMonsters(8000);
A_QuakeEx(7,7,7,50,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:800,rollIntensity:1.);
A_StartSound("silverbullet/chode",CHAN_VOICE,CHANF_DEFAULT,1.,.35);
@ -528,7 +528,7 @@ Class FatChodeExplosionArm : Actor
TNT1 A 1
{
A_CountDown();
SWWMHandler.DoExplosion(self,20+reactiontime,8000+1500*reactiontime,80+5*reactiontime,50,DE_THRUWALLS);
SWWMUtility.DoExplosion(self,20+reactiontime,8000+1500*reactiontime,80+5*reactiontime,50,DE_THRUWALLS);
if ( level.IsPointInLevel(pos) )
{
A_SprayDecal("HugeScorch",-32);
@ -596,9 +596,23 @@ Class SilverBullet : SWWMWeapon
Screen.DrawTexture(AmmoIcon[0],false,bx-18,by-19,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(128,0,0,0):Color(0,0,0,0));
Screen.DrawTexture(AmmoIcon[1],false,bx-18,by-41,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(0,0,0,0):Color(128,0,0,0));
int cx = (Ammo1.Amount>9)?32:29;
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-18,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(128,0,0,0):Color(0,0,0,0));
int sb = Owner.CountInv("SilverBullets");
if ( sb > 0 )
{
int cbx = (sb>9)?34:30;
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cbx,by-21,String.Format("⁺%s",SWWMUtility.SuperscriptNum(sb)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1,DTA_ColorOverlay,fcbselected?Color(128,0,0,0):Color(0,0,0,0));
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-14,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(128,0,0,0):Color(0,0,0,0));
}
else Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-18,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(128,0,0,0):Color(0,0,0,0));
cx = (Ammo2.Amount>9)?32:29;
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-40,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(0,0,0,0):Color(128,0,0,0));
sb = Owner.CountInv("SilverBullets2");
if ( sb > 0 )
{
int cbx = (sb>9)?34:30;
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cbx,by-43,String.Format("⁺%s",SWWMUtility.SuperscriptNum(sb)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1,DTA_ColorOverlay,fcbselected?Color(0,0,0,0):Color(128,0,0,0));
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-36,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(0,0,0,0):Color(128,0,0,0));
}
else Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-40,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fcbselected?Color(0,0,0,0):Color(128,0,0,0));
}
override void HudTick()
{
@ -609,7 +623,7 @@ Class SilverBullet : SWWMWeapon
override bool ReportHUDAmmo()
{
if ( (chambered && !fired) || (clipcount > 0) ) return true;
if ( (Ammo1.Amount <= 0) && (Ammo2.Amount <= 0) ) return false;
if ( (Ammo1.Amount <= 0) && (Ammo2.Amount <= 0) && (CountInv("SilverBullets") <= 0) && (CountInv("SilverBullets2") <= 0) ) return false;
return Super.ReportHUDAmmo();
}
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
@ -620,7 +634,7 @@ Class SilverBullet : SWWMWeapon
// allow player to still use the zoom even if there's no ammo left
// (should work fine, assuming I've correctly interpreted the execution chain of all this stuff)
if ( autoswitch && (fireMode == AltFire) ) return true;
return ((chambered && !fired) || (clipcount > 0) || (Ammo1.Amount > 0) || (Ammo2.Amount > 0));
return ((chambered && !fired) || (clipcount > 0) || (Ammo1.Amount > 0) || (Ammo2.Amount > 0) || (CountInv("SilverBullets") > 0) || (CountInv("SilverBullets2") > 0));
}
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
}
@ -692,7 +706,7 @@ Class SilverBullet : SWWMWeapon
{
int realdmg = t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',false,DMG_FOILINVUL);
SWWMHandler.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),realdmg*20.*FRandom[SilverBullet](.8,1.2));
SWWMUtility.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),realdmg*20.*FRandom[SilverBullet](.8,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD )
{
let p = Spawn("SilverImpact",t.HitList[i].HitLocation);
@ -797,7 +811,7 @@ Class SilverBullet : SWWMWeapon
if ( t.Results.HitType == TRACE_HitActor )
{
int dmg = t.Results.HitActor.DamageMobj(invoker,self,1000,'shot',DMG_FOILINVUL|DMG_USEANGLE|DMG_THRUSTLESS,atan2(t.Results.HitVector.y,t.Results.HitVector.x));
SWWMHandler.DoKnockback(t.Results.HitActor,t.Results.HitVector+(0,0,.025),dmg*20.*FRandom[SilverBullet](.8,1.2));
SWWMUtility.DoKnockback(t.Results.HitActor,t.Results.HitVector+(0,0,.025),dmg*20.*FRandom[SilverBullet](.8,1.2));
if ( !t.Results.HitActor.bNOBLOOD )
{
t.Results.HitActor.TraceBleed(dmg,self);
@ -899,7 +913,7 @@ Class SilverBullet : SWWMWeapon
}
double fact = invoker.fcbchambered?270000.:320000.;
if ( player.mo.CanCrouch() && (player.crouchfactor != 1) ) fact /= 10.;
SWWMHandler.DoKnockback(self,-x,fact);
SWWMUtility.DoKnockback(self,-x,fact);
}
action void A_DropCasing( bool fcb = false )
{
@ -945,8 +959,9 @@ Class SilverBullet : SWWMWeapon
int dropamt = invoker.clipcount-maxgiveamt;
if ( dropamt > 0 ) ma.CreateTossable(dropamt);
ma.Amount = min(ma.MaxAmount,ma.Amount+invoker.clipcount);
if ( CheckLocalView() ) for ( int i=0; i<(invoker.clipcount-dropamt); i++ ) ma.PrintPickupMessage(true,ma.PickupMessage());
if ( (invoker.clipcount-dropamt) > 0 ) ma.PlayPickupSound(self);
ma.MagFill();
if ( CheckLocalView() ) for ( int i=0; i<min(ma.Amount,invoker.clipcount-dropamt); i++ ) ma.PrintPickupMessage(true,ma.PickupMessage());
if ( min(ma.Amount,invoker.clipcount-dropamt) > 0 ) ma.PlayPickupSound(self);
}
invoker.ClipCount = 0;
Vector3 x, y, z;
@ -983,8 +998,10 @@ Class SilverBullet : SWWMWeapon
action void A_SwitchAmmoType( bool ifempty = false )
{
bool oldsel = invoker.fcbselected;
if ( invoker.fcbselected && (invoker.Ammo1.Amount > 0) ) invoker.fcbselected = (ifempty&&(invoker.Ammo2.Amount>0))?true:false;
else if ( !invoker.fcbselected && (invoker.Ammo2.Amount > 0) ) invoker.fcbselected = (ifempty&&(invoker.Ammo1.Amount>0))?false:true;
if ( invoker.fcbselected && ((invoker.Ammo1.Amount > 0) || (CountInv("SilverBullets") > 0)) )
invoker.fcbselected = (ifempty&&((invoker.Ammo2.Amount>0)||(CountInv("SilverBullets2")>0)))?true:false;
else if ( !invoker.fcbselected && ((invoker.Ammo2.Amount > 0) || (CountInv("SilverBullets2") > 0)) )
invoker.fcbselected = (ifempty&&((invoker.Ammo1.Amount>0)||(CountInv("SilverBullets")>0)))?false:true;
if ( oldsel != invoker.fcbselected ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL);
A_WeaponReady(WRF_NOFIRE);
}
@ -1067,7 +1084,7 @@ Class SilverBullet : SWWMWeapon
{
if ( !invoker.chambered || invoker.fired )
{
if ( !invoker.fired && (invoker.clipcount <= 0) && (sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) || (invoker.Ammo1.Amount > 0) || (invoker.Ammo2.Amount > 0)) )
if ( !invoker.fired && (invoker.clipcount <= 0) && (sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) || (invoker.Ammo1.Amount > 0) || (invoker.Ammo2.Amount > 0) || (CountInv("SilverBullets") > 0) || (CountInv("SilverBullets2") > 0)) )
{
A_SwitchAmmoType(true);
return ResolveState("Reload");
@ -1275,7 +1292,7 @@ Class SilverBullet : SWWMWeapon
Reload:
XZW2 A 0
{
if ( ((invoker.clipcount >= invoker.default.clipcount) && (invoker.fcbselected == invoker.fcbloaded)) || (!sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) && (invoker.Ammo1.Amount <= 0) && (invoker.Ammo2.Amount <= 0)) )
if ( ((invoker.clipcount >= invoker.default.clipcount) && (invoker.fcbselected == invoker.fcbloaded)) || (!sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) && (invoker.Ammo1.Amount <= 0) && (invoker.Ammo2.Amount <= 0) && (CountInv("SilverBullets") <= 0) && (CountInv("SilverBullets2") <= 0)) )
return ResolveState("Idle");
if ( invoker.zoomed )
{
@ -1328,9 +1345,20 @@ Class SilverBullet : SWWMWeapon
XZW5 D 2
{
invoker.fcbloaded = false;
invoker.clipcount = invoker.default.clipcount;
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) )
invoker.clipcount = invoker.default.clipcount;
else if ( invoker.Ammo1.Amount <= 0 )
{
MagAmmo sb = MagAmmo(FindInventory("SilverBullets"));
int takeamt = min(sb.Amount,sb.ClipSize);
invoker.clipcount = takeamt;
sb.Amount -= takeamt;
}
else
{
invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1);
invoker.clipcount = invoker.default.clipcount;
}
A_SwitchAmmoType(true);
A_StartSound("silverbullet/magin",CHAN_WEAPON,CHANF_OVERLAP);
}
@ -1343,9 +1371,20 @@ Class SilverBullet : SWWMWeapon
XZWB D 2
{
invoker.fcbloaded = true;
invoker.clipcount = invoker.default.clipcount;
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) )
invoker.clipcount = invoker.default.clipcount;
else if ( invoker.Ammo2.Amount <= 0 )
{
MagAmmo sb = MagAmmo(FindInventory("SilverBullets2"));
int takeamt = min(sb.Amount,sb.ClipSize);
invoker.clipcount = takeamt;
sb.Amount -= takeamt;
}
else
{
invoker.Ammo2.Amount = max(0,invoker.Ammo2.Amount-1);
invoker.clipcount = invoker.default.clipcount;
}
A_SwitchAmmoType(true);
A_StartSound("silverbullet/magin",CHAN_WEAPON,CHANF_OVERLAP);
}

1066
zscript/swwm_thinkers.zsc Normal file

File diff suppressed because it is too large Load diff

688
zscript/swwm_utility.zsc Normal file
View file

@ -0,0 +1,688 @@
// Misc. Utility code
enum EDoExplosionFlags
{
DE_BLAST = 1, // sets BLASTED flag on pushed actors
DE_NOBLEED = 2, // does not spawn blood decals on hit
DE_NOSPLASH = 4, // like XF_NOSPLASH
DE_THRUWALLS = 8, // damages through geometry (no sight check)
DE_NOTMISSILE = 16, // instigator is the source itself (normally it'd be its target pointer)
DE_EXTRAZTHRUST = 32, // applies a higher Z thrust to enemies on ground
};
Class SWWMUtility
{
static clearscope 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;
}
}
static clearscope String SuperscriptNum( int val )
{
// unicode is fun
static const int digs[] = {0x2070,0x00B9,0x00B2,0x00B3,0x2074,0x2075,0x2076,0x2077,0x2078,0x2079};
String str = "";
int digits = int(Log10(val));
for ( int i=digits; i>=0; i-- )
{
int d = int(val/(10**i))%10;
str.AppendCharacter(digs[d]);
}
return str;
}
static clearscope void BeautifyClassName( out String str )
{
String workstr = str;
str.Truncate(0);
workstr.Replace("_"," ");
int len = workstr.CodePointCount();
for ( int i=0; i<len; i++ )
{
int cp1 = workstr.GetNextCodePoint(i);
str.AppendCharacter(cp1);
if ( i < len-1 )
{
int cp2 = workstr.GetNextCodePoint(i+1);
if ( (String.CharLower(cp1) == cp1) && (String.CharUpper(cp2) == cp2) )
str.AppendCharacter(0x20);
}
}
}
static clearscope int GetLineLock( Line l )
{
int locknum = l.locknumber;
if ( !locknum )
{
// check the special
switch ( l.special )
{
case FS_Execute:
locknum = l.Args[2];
break;
case Door_LockedRaise:
case Door_Animated:
locknum = l.Args[3];
break;
case ACS_LockedExecute:
case ACS_LockedExecuteDoor:
case Generic_Door:
locknum = l.Args[4];
break;
}
}
return locknum;
}
static clearscope bool IsExitLine( Line l )
{
if ( (l.special == Exit_Normal) || (l.special == Exit_Secret) || (l.special == Teleport_EndGame) || (l.special == Teleport_NewMap) )
return true;
return false;
}
// how the fuck is this not available to ZScript?
// copied from P_PointOnLineSidePrecise()
static clearscope int PointOnLineSide( Vector2 p, Line l )
{
if ( !l ) return 0;
return (((p.y-l.v1.p.y)*l.delta.x+(l.v1.p.x-p.x)*l.delta.y) > double.epsilon);
}
// box intersection check, for collision detection
static clearscope bool BoxIntersect( Actor a, Actor b, Vector3 ofs = (0,0,0), int pad = 0 )
{
Vector3 diff = level.Vec3Diff(level.Vec3Offset(a.pos,ofs),b.pos);
if ( (abs(diff.x) > (a.radius+b.radius+pad)) || (abs(diff.y) > (a.radius+b.radius+pad)) ) return false;
if ( (diff.z > a.height+pad) || (diff.z < -(b.height+pad)) ) return false;
return true;
}
// extruded box intersection check, useful when checking things that might be hit along a path
static clearscope bool ExtrudeIntersect( Actor a, Actor b, Vector3 range, int steps, int pad = 0 )
{
if ( steps <= 0 ) return BoxIntersect(a,b,pad:pad);
double step = 1./steps;
for ( double i=step; i<=1.; i+=step )
{
if ( BoxIntersect(a,b,range*i,pad) )
return true;
}
return false;
}
// sphere intersection check, useful for proximity detection
static clearscope bool SphereIntersect( Actor a, Vector3 p, double radius )
{
Vector3 ap = p+level.Vec3Diff(p,a.pos); // portal-relative actor position
Vector3 amin = ap+(-a.radius,-a.radius,0),
amax = ap+(a.radius,a.radius,a.height);
double distsq = 0.;
if ( p.x < amin.x ) distsq += (amin.x-p.x)**2;
if ( p.x > amax.x ) distsq += (p.x-amax.x)**2;
if ( p.y < amin.y ) distsq += (amin.y-p.y)**2;
if ( p.y > amax.y ) distsq += (p.y-amax.y)**2;
if ( p.z < amin.z ) distsq += (amin.z-p.z)**2;
if ( p.z > amax.z ) distsq += (p.z-amax.z)**2;
return (distsq <= (radius**2));
}
// THANKS FOR NOT GIVING US ANY OTHER WAY TO CHECK IF A LOCK NUMBER IS VALID
static clearscope bool IsValidLockNum( int l )
{
if ( (l < 1) || (l > 255) ) return true;
Array<Int> valid;
valid.Clear();
for ( int i=0; i<Wads.GetNumLumps(); i++ )
{
String lname = Wads.GetLumpName(i);
if ( !(lname ~== "LOCKDEFS") ) continue;
String data = Wads.ReadLump(i);
Array<String> lines;
lines.Clear();
data.Split(lines,"\n");
for ( int j=0; j<lines.Size(); j++ )
{
if ( lines[j].Left(10) ~== "CLEARLOCKS" ) valid.Clear();
else if ( Lines[j].Left(5) ~== "LOCK " )
{
Array<String> spl;
spl.Clear();
lines[j].Split(spl," ",TOK_SKIPEMPTY);
// check game string (if any)
if ( spl.Size() > 2 )
{
if ( (spl[2] ~== "DOOM") && !(gameinfo.gametype&GAME_Doom) ) continue;
else if ( (spl[2] ~== "HERETIC") && !(gameinfo.gametype&GAME_Heretic) ) continue;
else if ( (spl[2] ~== "HEXEN") && !(gameinfo.gametype&GAME_Hexen) ) continue;
else if ( (spl[2] ~== "STRIFE") && !(gameinfo.gametype&GAME_Strife) ) continue;
else if ( (spl[2] ~== "CHEX") && !(gameinfo.gametype&GAME_Chex) ) continue;
}
valid.Push(spl[1].ToInt());
}
}
}
for ( int i=0; i<valid.Size(); i++ )
{
if ( valid[i] == l ) return true;
}
return false;
}
// wheeeeeeee, let's play a game of "who is who"
static clearscope bool IsCivilian( Actor a )
{
if ( a is 'Beggar' )
{
if ( (a.level.mapname ~== "MAP32") && (a is 'Beggar1') )
return false; // Prisoner (sorry but we have to)
return true;
}
if ( a is 'Peasant' )
{
// exclude certain key NPCs
if ( (a.level.mapname ~== "MAP01") && (a is 'Peasant9') )
return false; // Beldin (sorry but we have to)
if ( (a.level.mapname ~== "MAP02") && (a is 'Peasant22') )
return false; // Mourel (fuck that guy)
if ( (a.level.mapname ~== "MAP02") && (a is 'Peasant4') )
return false; // Harris (also fuck that guy)
if ( (a.level.mapname ~== "MAP04") && (a is 'Peasant5') )
return false; // Derwin (fat bastard)
if ( (a.level.mapname ~== "MAP04") && (a is 'Peasant7') )
return false; // Ketrick (THIS IS GARBAGE)
if ( (a.level.mapname ~== "MAP05") && (a is 'Peasant7') )
return false; // Montag (gimme the damn key)
if ( (a.level.mapname ~== "MAP05") && (a is 'Peasant8') )
return false; // Wolenick (gimme a hand)
if ( (a.level.mapname ~== "MAP33") && (a is 'Peasant5') )
return false; // Harris (also fuck that guy)
return true;
}
return false;
}
// Thanks to ZZYZX and Nash
static play void SetToSlope( Actor a, double dang )
{
vector3 fnormal = a.CurSector.floorplane.Normal;
// find closest 3d floor for its normal
F3DFloor ff;
for ( int i=0; i<a.CurSector.Get3DFloorCount(); i++ )
{
if ( !(a.CurSector.Get3DFloor(i).top.ZAtPoint(a.pos.xy) ~== a.floorz) ) continue;
ff = a.CurSector.Get3DFloor(i);
break;
}
if ( ff ) fnormal = -ff.top.Normal;
vector2 fnormalp1 = ((fnormal.x != 0) || (fnormal.y != 0))?(fnormal.x,fnormal.y).Unit():(0,0);
vector2 fnormalp2 = ((fnormal.x,fnormal.y).Length(),fnormal.z);
double fang = atan2(fnormalp1.y,fnormalp1.x); // floor angle (not pitch!)
double fpitch = atan2(fnormalp2.x,fnormalp2.y); // floor pitch
double ddiff1 = sin(fang-dang);
double ddiff2 = cos(fang-dang);
a.pitch = fpitch*ddiff2;
a.roll = -fpitch*ddiff1;
a.angle = dang;
}
static clearscope int Round100( double x )
{
return int(ceil(x/100.)*100.);
}
// can't make clearscope because of SectorEffect.GetSector(), isn't that amazing?
static play bool IsDoorSector( Sector s, int part )
{
// super-easy mode: check for boss special sectors
if ( (level.mapname ~== "E1M8") || (level.mapname ~== "E2M8") || (level.mapname ~== "E3M8")
|| (level.mapname ~== "E4M6") || (level.mapname ~== "E4M8") || (level.mapname ~== "E5M8")
|| (level.mapname ~== "MAP07") )
{
let si = level.CreateSectorTagIterator(666);
int idx;
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
if ( level.mapname ~== "MAP07" )
{
let si2 = level.CreateSectorTagIterator(667);
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
}
}
// easy mode: check if it's already moving
if ( part )
{
let ti = ThinkerIterator.Create("MovingCeiling",Thinker.STAT_SECTOREFFECT);
MovingCeiling mc;
while ( mc = MovingCeiling(ti.Next()) )
if ( mc.GetSector() == s )
return true;
}
else
{
let ti = ThinkerIterator.Create("MovingFloor",Thinker.STAT_SECTOREFFECT);
MovingFloor mf;
while ( mf = MovingFloor(ti.Next()) )
if ( mf.GetSector() == s )
return true;
}
// hard mode: look for all lines/actors with movement specials referencing us
for ( int i=0; i<level.Lines.Size(); i++ )
{
Line l = level.Lines[i];
if ( !l.special ) continue;
if ( (part && (l.special >= 10) && (l.special <= 13))
|| (!part && (l.special >= 20) && (l.special <= 25))
|| (!part && (l.special == 28))
|| ((l.special >= 29) && (l.special <= 30))
|| (!part && (l.special >= 35) && (l.special <= 37))
|| (part && (l.special >= 40) && (l.special <= 45))
|| (!part && (l.special == 46))
|| (part && (l.special == 47))
|| (!part && (l.special >= 60) && (l.special <= 68))
|| (part && (l.special == 69))
|| ((l.special >= 94) && (l.special <= 96))
|| (part && (l.special == 97))
|| (!part && (l.special == 99))
|| (part && (l.special == 104))
|| (part && (l.special >= 105) && (l.special <= 106))
|| (part && (l.special >= 168) && (l.special <= 169))
|| (!part && (l.special == 172))
|| (part && (l.special >= 192) && (l.special <= 199))
|| (!part && (l.special == 200))
|| (part && (l.special >= 201) && (l.special <= 202))
|| (!part && (l.special == 203))
|| (part && (l.special == 205))
|| (!part && (l.special >= 206) && (l.special <= 207))
|| (!part && (l.special == 228))
|| (!part && (l.special >= 230) && (l.special <= 231))
|| (!part && (l.special >= 238) && (l.special <= 242))
|| ((l.special >= 245) && (l.special <= 247))
|| (part && (l.special == 249))
|| (!part && (l.special >= 250) && (l.special <= 251))
|| (part && (l.special >= 251) && (l.special <= 255))
|| (!part && (l.special >= 256) && (l.special <= 261))
|| (part && (l.special >= 262) && (l.special <= 269))
|| (!part && (l.special == 275))
|| (part && (l.special == 276))
|| (!part && (l.special == 279))
|| (part && (l.special == 280)) )
{
let si = level.CreateSectorTagIterator(l.Args[0],l);
int idx;
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
}
}
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( !a.special || !a.Args[0] ) continue;
if ( (part && (a.special >= 10) && (a.special <= 13))
|| (!part && (a.special >= 20) && (a.special <= 25))
|| (!part && (a.special == 28))
|| ((a.special >= 29) && (a.special <= 30))
|| (!part && (a.special >= 35) && (a.special <= 37))
|| (part && (a.special >= 40) && (a.special <= 45))
|| (!part && (a.special == 46))
|| (part && (a.special == 47))
|| (!part && (a.special >= 60) && (a.special <= 68))
|| (part && (a.special == 69))
|| ((a.special >= 94) && (a.special <= 96))
|| (part && (a.special == 97))
|| (!part && (a.special == 99))
|| (part && (a.special == 104))
|| (part && (a.special >= 105) && (a.special <= 106))
|| (part && (a.special >= 168) && (a.special <= 169))
|| (!part && (a.special == 172))
|| (part && (a.special >= 192) && (a.special <= 199))
|| (!part && (a.special == 200))
|| (part && (a.special >= 201) && (a.special <= 202))
|| (!part && (a.special == 203))
|| (part && (a.special == 205))
|| (!part && (a.special >= 206) && (a.special <= 207))
|| (!part && (a.special == 228))
|| (!part && (a.special >= 230) && (a.special <= 231))
|| (!part && (a.special >= 238) && (a.special <= 242))
|| ((a.special >= 245) && (a.special <= 247))
|| (part && (a.special == 249))
|| (!part && (a.special >= 250) && (a.special <= 251))
|| (part && (a.special >= 251) && (a.special <= 255))
|| (!part && (a.special >= 256) && (a.special <= 261))
|| (part && (a.special >= 262) && (a.special <= 269))
|| (!part && (a.special == 275))
|| (part && (a.special == 276))
|| (!part && (a.special == 279))
|| (part && (a.special == 280)) )
{
let si = level.CreateSectorTagIterator(a.Args[0]);
int idx;
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
}
}
return false;
}
// because GetTag() returns the localized string, we need to do things the hard way
static clearscope String GetFunTag( Actor a, String defstr = "" )
{
if ( a is 'SWWMMonster' ) return SWWMMonster(a).GetFunTag(defstr);
int ntags = 1;
String basetag = "";
switch ( a.GetClassName() )
{
// Doom
case 'ZombieMan':
case 'StealthZombieMan':
basetag = "ZOMBIE";
break;
case 'ShotgunGuy':
case 'StealthShotgunGuy':
basetag = "SHOTGUN";
break;
case 'ChaingunGuy':
case 'StealthChaingunGuy':
basetag = "HEAVY";
break;
case 'DoomImp':
case 'StealthDoomImp':
basetag = "IMP";
break;
case 'Demon':
case 'StealthDemon':
basetag = "DEMON";
break;
case 'Spectre':
basetag = "SPECTRE";
break;
case 'LostSoul':
basetag = "LOST";
break;
case 'Cacodemon':
case 'StealthCacodemon':
basetag = "CACO";
break;
case 'HellKnight':
case 'StealthHellKnight':
basetag = "HELL";
break;
case 'BaronOfHell':
case 'StealthBaron':
basetag = "BARON";
break;
case 'Arachnotron':
case 'StealthArachnotron':
basetag = "ARACH";
break;
case 'PainElemental':
basetag = "PAIN";
break;
case 'Revenant':
case 'StealthRevenant':
basetag = "REVEN";
break;
case 'Fatso':
case 'StealthFatso':
basetag = "MANCU";
break;
case 'Archvile':
case 'StealthArchvile':
basetag = "ARCH";
break;
case 'SpiderMastermind':
basetag = "SPIDER";
break;
case 'Cyberdemon':
basetag = "CYBER";
break;
case 'BossBrain':
basetag = "BOSSBRAIN";
break;
case 'WolfensteinSS':
basetag = "WOLFSS";
break;
case 'CommanderKeen':
basetag = "KEEN";
break;
case 'MBFHelperDog':
basetag = "DOG";
break;
// Heretic
case 'Chicken':
basetag = "CHICKEN";
break;
case 'Beast':
basetag = "BEAST";
break;
case 'Clink':
basetag = "CLINK";
break;
case 'Sorcerer1':
case 'Sorcerer2':
basetag = "DSPARIL";
break;
case 'HereticImp':
case 'HereticImpLeader':
basetag = "HERETICIMP";
break;
case 'Ironlich':
basetag = "IRONLICH";
break;
case 'Knight':
case 'KnightGhost':
basetag = "BONEKNIGHT";
break;
case 'Minotaur':
case 'MinotaurFriend':
basetag = "MINOTAUR";
break;
case 'Mummy':
case 'MummyGhost':
basetag = "MUMMY";
break;
case 'MummyLeader':
case 'MummyLeaderGhost':
basetag = "MUMMYLEADER";
break;
case 'Snake':
basetag = "SNAKE";
break;
case 'Wizard':
basetag = "WIZARD";
break;
// Hexen
case 'FireDemon':
basetag = "FIREDEMON";
break;
case 'Demon1':
case 'Demon1Mash':
case 'Demon2':
case 'Demon2Mash':
basetag = "DEMON1";
break;
case 'Ettin':
case 'EttinMash':
basetag = "ETTIN";
break;
case 'Centaur':
case 'CentaurMash':
basetag = "CENTAUR";
break;
case 'CentaurLeader':
basetag = "SLAUGHTAUR";
break;
case 'Bishop':
basetag = "BISHOP";
break;
case 'IceGuy':
basetag = "ICEGUY";
break;
case 'Serpent':
case 'SerpentLeader':
basetag = "SERPENT";
break;
case 'Wraith':
case 'WraithBuried':
basetag = "WRAITH";
break;
case 'Dragon':
basetag = "DRAGON";
break;
case 'Korax':
basetag = "KORAX";
break;
case 'FighterBoss':
basetag = "FBOSS";
break;
case 'MageBoss':
basetag = "MBOSS";
break;
case 'ClericBoss':
basetag = "CBOSS";
break;
case 'Heresiarch':
basetag = "HERESIARCH";
break;
}
if ( basetag == "" ) return a.GetTag(defstr);
String funtag = "FN_"..basetag.."_FUN";
String lfuntag = StringTable.Localize(funtag,false);
if ( lfuntag != funtag ) return lfuntag;
String nfuntag = "FN_"..basetag.."_FUNN";
String lnfuntag = StringTable.Localize(nfuntag,false);
if ( lnfuntag == nfuntag ) return a.GetTag(defstr);
ntags = lnfuntag.ToInt();
return StringTable.Localize(String.Format("$FN_%s_FUN%d",basetag,Random[FunTags](1,ntags)));
}
// Apply full 3D knockback in a specific direction, useful for hitscan
static play void DoKnockback( Actor Victim, Vector3 HitDirection, double MomentumTransfer )
{
if ( !Victim )
return;
if ( !Victim.bSHOOTABLE && !Victim.bVULNERABLE )
return;
if ( Victim.bDONTTHRUST || (Victim.Mass >= Actor.LARGE_MASS) )
return;
Vector3 Momentum = HitDirection*MomentumTransfer;
if ( (Victim.pos.z <= Victim.floorz) || !Victim.TestMobjZ() )
Momentum.z = max(Momentum.z,.1*Momentum.length());
Momentum /= Thinker.TICRATE*max(50,Victim.Mass);
Victim.vel += Momentum;
}
// complete spherical and more accurate replacement of A_Explode
// 100% free of the buggery GZDoom's own splash damage has
static play void DoExplosion( Actor Source, double Damage, double MomentumTransfer, double ExplosionRadius, double FullDamageRadius = 0., int flags = 0, Name DamageType = '', Actor ignoreme = null )
{
// debug, display radius sphere
if ( swwm_explosiondebug )
{
let s = Actor.Spawn("RadiusDebugSphere",Source.pos);
s.Scale *= ExplosionRadius;
s.SetShade((Damage>0)?"Green":"Blue");
if ( FullDamageRadius > 0. )
{
let s = Actor.Spawn("RadiusDebugSphere",Source.pos);
s.Scale *= FullDamageRadius;
s.SetShade("Red");
}
}
if ( !(flags&DE_NOSPLASH) ) Source.CheckSplash(ExplosionRadius);
double brange = 1./(ExplosionRadius-FullDamageRadius);
Actor Instigator = (flags&DE_NOTMISSILE)?Source:Source.target;
BlockThingsIterator bi = BlockThingsIterator.Create(Source,ExplosionRadius*2); // test with doubled radius, just to be sure
while ( bi.Next() )
{
Actor a = bi.Thing;
// early checks for self and ignored actor (usually the instigator)
if ( !a || (a == ignoreme) || (a == Source) )
continue;
// can't be affected
if ( !a.bSHOOTABLE && !a.bVULNERABLE )
continue;
// no blasting if no radius dmg (unless forced)
if ( a.bNORADIUSDMG && !Source.bFORCERADIUSDMG )
continue;
// check the DONTHARMCLASS/DONTHARMSPECIES flags
if ( !a.player && ((Source.bDONTHARMCLASS && (a.GetClass() == Source.GetClass())) || (Source.bDONTHARMSPECIES && (a.GetSpecies() == Source.GetSpecies()))) )
continue;
// can we see it
if ( !(flags&DE_THRUWALLS) && !Source.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
// intersecting?
if ( !SWWMUtility.SphereIntersect(a,Source.pos,ExplosionRadius) )
continue;
// calculate factor
Vector3 dir = level.Vec3Diff(Source.pos,a.Vec3Offset(0,0,a.Height/2));
double dist = dir.length();
// intersecting, randomize direction
if ( dir.length() <= double.epsilon )
{
double ang = FRandom[DoBlast](0,360);
double pt = FRandom[DoBlast](-90,90);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
}
dir /= dist;
dist = clamp(dist-FullDamageRadius,0,min(dist,ExplosionRadius));
double damagescale = 1.-clamp((dist-a.Radius)*brange,0.,1.);
double mm = MomentumTransfer*damagescale;
// no knockback if massive/unpushable
if ( (abs(mm) > 0.) && !a.bDORMANT && !a.bDONTTHRUST && (a.Mass < Actor.LARGE_MASS) )
{
Vector3 Momentum = dir*mm;
if ( (a.pos.z <= a.floorz) || !a.TestMobjZ() )
Momentum.z = max(Momentum.z,(flags&DE_EXTRAZTHRUST?.4:.1)*Momentum.length());
Momentum /= Thinker.TICRATE*max(50,a.Mass); // prevent tiny things from getting yeeted at warp speed
a.vel += Momentum;
if ( (flags&DE_BLAST) && a.bCANBLAST && !a.bDONTBLAST ) a.bBLASTED = true;
}
// hit it
int dmg = int(Damage*damagescale);
if ( dmg <= 0 ) continue; // no harm
int ndmg = a.DamageMobj(Instigator,Source,dmg,(DamageType!='')?Source.DamageType:DamageType,DMG_EXPLOSION,atan2(-dir.y,-dir.x));
if ( !(flags&DE_NOBLEED) ) a.TraceBleed((ndmg>0)?ndmg:dmg,Source);
}
// TODO destructible geometry support
}
}
Class RadiusDebugSphere : Actor
{
Default
{
RenderStyle "AddStencil";
StencilColor "White";
Radius .1;
Height 0.;
+NOGRAVITY;
+NOINTERACTION;
}
States
{
Spawn:
XZW1 A 1 BRIGHT A_FadeOut();
Wait;
}
}