- Corrected selection order of all weapons. - Rewrote flaky reloading code. Should be more robust now. - [flak_m] manually triggered player animations no longer happen while dead. - Protomag whip damage changed to 15, instead of same damage as Doom fist. This matches the Unreal 0.83 default melee damage. - Adjusted dual-wielded weapons to try and prevent spin/reload from triggering right when the second gun is brought up. - Razorclaw no longer stunlocks and can gib. - Razorclaw damages increased (5/20 → 6/30). - Dispersion Pistol alt splash damage radius reduced (120 → 80); - Fixed Dispersion Pistol switching out instead of releasing when out of ammo. - Dispersion Pistol will only autoselect if it has at least 10 ammo. - Increased Fireblaster alt projectile damage (50 → 60). - Fireblaster no longer forces an autoswitch when trying to altfire with less than 30 ammo. - Increased Autocannon ammo capacity (30/50 → 32/64) and pickup ammo (20 → 32). - Increased Autocannon hit damage (150 → 300) and explosion radius (50 → 120). - Adjusted Impaler bolt damage formula (2*amplifiermult**3 → 10*amplifiermult**1.5). - Impaler bolt seek range now affected by Amplifier. - Reduced Impaler base explosion radius (120 → 80). - Impaler altfire drain also affected by Amplifier too. - Removed the ability to put out fire by moving very fast. This was a dumb idea as it would make fast monsters harder to burn. - Increased Quadshot per-trace damage to fixed value (Random(4,8) → 11). This matches the 0.83 damage value for it. - Doubled the damage boost of Razorjack charging. - Fix Stinger altfire only firing 4 shoots. Was supposed to fire 5 with the first being always accurate. - Adjusted Stunner damage and charge range so it matches Unreal Bible values (0-6/1-6 → 0-5/1-20). - Reduced Stunner ammo consumption for a full charge (20 → 10). - Stunner now also flagged as a "wimpy weapon". - Reduced Eightball splash damage radius (200/200 → 120/140). - Dispersion Pistol and Stunner always have at least 1 ammo after each shot. - Added option to disable Impaler beam self-damage.
439 lines
12 KiB
Text
439 lines
12 KiB
Text
Class StunnerAmmo : Ammo
|
|
{
|
|
double rechargephase, rechargespeed;
|
|
|
|
Default
|
|
{
|
|
Inventory.Icon "I_Stun";
|
|
Inventory.Amount 10;
|
|
Inventory.MaxAmount 50;
|
|
Ammo.BackpackAmount 0;
|
|
Ammo.BackpackMaxAmount 50;
|
|
}
|
|
override void DoEffect()
|
|
{
|
|
Super.Tick();
|
|
rechargespeed = max(2.,.2*Amount);
|
|
rechargephase += 1./rechargespeed;
|
|
if ( rechargephase < 7 ) return;
|
|
rechargephase = 0;
|
|
Amount = min(Amount+1,MaxAmount);
|
|
}
|
|
}
|
|
|
|
Class StunTrail : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
A_FadeOut(1./11.,0);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FATR ABCDE 3 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class StunLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "DYellow";
|
|
}
|
|
}
|
|
|
|
Class StunTracer : LineTracer
|
|
{
|
|
Actor owner, ignore;
|
|
|
|
override ETraceStatus TraceCallback()
|
|
{
|
|
if ( Results.HitType == TRACE_HitActor )
|
|
{
|
|
if ( (Results.HitActor == owner) || (Results.HitActor == ignore) ) return TRACE_Skip;
|
|
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
|
|
return TRACE_Skip;
|
|
}
|
|
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
|
|
{
|
|
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
|
|
return TRACE_Stop;
|
|
return TRACE_Skip;
|
|
}
|
|
return TRACE_Stop;
|
|
}
|
|
}
|
|
|
|
Class StunProj : Actor
|
|
{
|
|
StunTracer t;
|
|
Vector3 tracedir;
|
|
bool moving;
|
|
double totaldist;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
t = new("StunTracer");
|
|
t.owner = target;
|
|
t.ignore = self;
|
|
moving = true;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( !moving )
|
|
{
|
|
A_FadeOut(1/19.,0);
|
|
return;
|
|
}
|
|
// step trace
|
|
tracedir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
t.Trace(pos,cursector,tracedir,500,0);
|
|
totaldist += t.Results.Distance;
|
|
// spawn particles
|
|
for ( int i=10; i<t.Results.Distance; i+=80 )
|
|
Spawn("StunLight",Vec3Offset(tracedir.x*i,tracedir.y*i,tracedir.z*i));
|
|
for ( int i=0; i<t.Results.Distance; i+=4 )
|
|
{
|
|
Vector3 pofs = tracedir*FRandom[Stunner](0,2)+(FRandom[Stunner](-.5,.5),FRandom[Stunner](-.5,.5),FRandom[Stunner](-.5,.5));
|
|
let s = Spawn("UTSpark",Vec3Offset(tracedir.x*i+pofs.x,tracedir.y*i+pofs.y,tracedir.z*i+pofs.z));
|
|
s.scale *= 0.2;
|
|
s.bNOGRAVITY = true;
|
|
s.vel = (FRandom[Stunner](-.3,.3),FRandom[Stunner](-.3,.3),FRandom[Stunner](-.3,.3));
|
|
}
|
|
for ( int i=10; i<t.Results.Distance; i+=20 )
|
|
{
|
|
let s = Spawn("StunTrail",Vec3Offset(tracedir.x*i,tracedir.y*i,tracedir.z*i));
|
|
s.angle = angle;
|
|
s.pitch = pitch;
|
|
}
|
|
if ( totaldist >= 10000.0 )
|
|
{
|
|
// reposition and explode on air
|
|
SetOrigin(t.Results.HitPos-t.Results.HitVector*4,false);
|
|
StunExplode();
|
|
}
|
|
else if ( t.Results.HitType == TRACE_HitNone )
|
|
{
|
|
// reposition
|
|
SetOrigin(t.Results.HitPos+t.Results.HitVector,false);
|
|
angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x);
|
|
pitch = asin(-t.Results.HitVector.z);
|
|
}
|
|
else if ( t.Results.HitType == TRACE_HitActor )
|
|
{
|
|
// reposition and explode on actor
|
|
SetOrigin(t.Results.HitPos-t.Results.HitVector*4,false);
|
|
StunExplode();
|
|
Actor a = t.Results.HitActor;
|
|
a.DamageMobj(self,target,max(1,int(4*specialf1)),'jolted',DMG_USEANGLE,atan2(t.Results.HitVector.y,t.Results.HitVector.x));
|
|
if ( !a.bDONTTHRUST )
|
|
{
|
|
UTMainHandler.DoKnockback(a,t.Results.HitVector,(bAMBUSH?-22000:26000)*specialf1);
|
|
a.vel.z += 2.*specialf1;
|
|
}
|
|
angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x);
|
|
pitch = asin(-t.Results.HitVector.z);
|
|
}
|
|
else
|
|
{
|
|
// reposition and explode on wall
|
|
SetOrigin(t.Results.HitPos-t.Results.HitVector*4,false);
|
|
A_SprayDecal("ShockMark",16);
|
|
Vector3 HitNormal = t.Results.HitVector;
|
|
if ( t.Results.HitType == TRACE_HitWall )
|
|
{
|
|
t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,pos);
|
|
// calculate normal
|
|
HitNormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
|
|
if ( t.Results.Side == 0 ) HitNormal *= -1;
|
|
}
|
|
else if ( t.Results.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( t.Results.ffloor ) HitNormal = -t.Results.ffloor.top.Normal;
|
|
else HitNormal = t.Results.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( t.Results.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( t.Results.ffloor ) HitNormal = -t.Results.ffloor.bottom.Normal;
|
|
else HitNormal = t.Results.HitSector.ceilingplane.Normal;
|
|
}
|
|
StunExplode();
|
|
angle = atan2(HitNormal.y,HitNormal.x);
|
|
pitch = asin(-HitNormal.z);
|
|
}
|
|
}
|
|
void StunExplode()
|
|
{
|
|
moving = false;
|
|
SetStateLabel("Death");
|
|
A_Explode(int(0.6*specialf1),50);
|
|
A_QuakeEx(1,1,1,3,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:120,rollintensity:0.2);
|
|
A_PlaySound("stun/hit",CHAN_VOICE,pitch:FRandom[Stunner](1.5,1.9)-0.08*specialf1);
|
|
UTMainHandler.DoBlast(self,50,(bAMBUSH?-7000:11000)*specialf1);
|
|
Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
int numpt = Random[ExploS](10,15);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
|
let s = Spawn("UTSpark",pos);
|
|
s.scale *= 0.2;
|
|
s.bNOGRAVITY = true;
|
|
s.vel = pvel;
|
|
}
|
|
}
|
|
Default
|
|
{
|
|
Obituary "$O_STUNNER";
|
|
RenderStyle "Add";
|
|
DamageType 'jolted';
|
|
Radius 0;
|
|
Height 0.1;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+FORCERADIUSDMG;
|
|
+NODAMAGETHRUST;
|
|
+NOTELEPORT;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A -1;
|
|
Stop;
|
|
Death:
|
|
FATR ABCDEFGHIJK 2 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class StunnerImpact : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_SprayDecal("WallCrack",20);
|
|
int numpt = Random[Stunner](10,20);
|
|
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (-x+(FRandom[Stunner](-.8,.8),FRandom[Stunner](-.8,.8),FRandom[Stunner](-.8,.8))).unit()*FRandom[Stunner](0.1,1.2);
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Stunner](128,192));
|
|
}
|
|
numpt = Random[Stunner](4,10);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Stunner](-1,1),FRandom[Stunner](-1,1),FRandom[Stunner](-1,1)).unit()*FRandom[Stunner](2,8);
|
|
let s = Spawn("UTSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Stunner](4,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Stunner](-1,1),FRandom[Stunner](-1,1),FRandom[Stunner](-1,1)).unit()*FRandom[Stunner](2,8);
|
|
let s = Spawn("UTChip",pos);
|
|
s.vel = pvel;
|
|
}
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
Class Stunner : UnrealWeapon
|
|
{
|
|
double chargesize, count;
|
|
bool bCharging;
|
|
|
|
override int, int, bool, bool GetClipAmount()
|
|
{
|
|
return bCharging?min(5,int(chargesize)):-1, -1, false, false;
|
|
}
|
|
action void A_StunnerFire()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
A_WeaponOffset(0,32);
|
|
invoker.bCharging = false;
|
|
if ( self is 'UTPlayer' )
|
|
UTPlayer(self).PlayAttacking3();
|
|
StunnerAmmo(weap.Ammo1).rechargephase = ((weap.Ammo1.Amount>0)?0:5);
|
|
A_PlaySound("stun/fire",CHAN_WEAPON,Dampener.Active(self)?.4:1.,pitch:1.2-invoker.chargesize*0.06);
|
|
double mult = Amplifier.GetMult(self,int(invoker.ChargeSize*50)+50);
|
|
invoker.FireEffect();
|
|
A_QuakeEx(1+int(0.5*invoker.chargesize),1+int(0.5*invoker.chargesize),1+int(0.5*invoker.chargesize),5+int(1.2*invoker.chargesize),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05+0.01*invoker.chargesize);
|
|
UTMainHandler.DoSwing(self,(FRandom[Stunner](-0.04,0.04),FRandom[Stunner](-0.9,-0.4)),1+invoker.chargesize*0.3,-0.1,3,SWING_Spring,3,2.5);
|
|
A_Overlay(-2,"Null");
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*10-z*5);
|
|
let p = Spawn("StunProj",origin);
|
|
p.angle = angle;
|
|
p.pitch = BulletSlope();
|
|
p.target = self;
|
|
p.specialf1 = invoker.chargesize*mult;
|
|
p.bAMBUSH = weap.bAltFire;
|
|
FLineTraceData d;
|
|
LineTrace(angle,60,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
int dmg = int(5*invoker.chargesize);
|
|
if ( d.HitLocation.z >= (d.HitActor.pos.z+d.HitActor.height*0.81) )
|
|
dmg = d.HitActor.DamageMobj(invoker,self,dmg*2,'Decapitated',DMG_THRUSTLESS);
|
|
else dmg = d.HitActor.DamageMobj(invoker,self,dmg,'impact',DMG_THRUSTLESS);
|
|
UTMainHandler.DoKnockback(d.HitActor,x,4000*invoker.chargesize);
|
|
if ( d.HitActor.bNOBLOOD )
|
|
{
|
|
let p = Spawn("StunnerImpact",d.HitLocation-d.HitDir*4);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x);
|
|
p.pitch = asin(-d.HitDir.z);
|
|
}
|
|
else
|
|
{
|
|
d.HitActor.TraceBleed(dmg,invoker);
|
|
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
|
|
}
|
|
}
|
|
else if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
let p = Spawn("StunnerImpact",d.HitLocation-d.HitDir*4);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x);
|
|
p.pitch = asin(-d.HitDir.z);
|
|
if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
|
|
}
|
|
}
|
|
action void A_BeginCharge()
|
|
{
|
|
let weap = Weapon(invoker);
|
|
invoker.bCharging = true;
|
|
weap.DepleteAmmo(weap.bAltFire,true,1);
|
|
invoker.count = 0;
|
|
invoker.chargesize = .6;
|
|
A_PlaySound("stun/charge",CHAN_WEAPON,Dampener.Active(self)?.15:1.);
|
|
A_Overlay(-2,"Sparks");
|
|
A_OverlayFlags(-2,PSPF_RenderStyle,true);
|
|
A_OverlayRenderStyle(-2,STYLE_Add);
|
|
StunnerAmmo(weap.Ammo1).rechargephase = 0;
|
|
if ( !Dampener.Active(self) ) A_AlertMonsters();
|
|
}
|
|
action State A_ChargeUp()
|
|
{
|
|
if ( !(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)) )
|
|
return ResolveState("Release");
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return ResolveState(null);
|
|
StunnerAmmo(weap.Ammo1).rechargephase = 0;
|
|
UTMainHandler.DoSwing(self,(FRandom[Stunner](-1,1),FRandom[Stunner](-1,1)),0.02*invoker.chargesize,0,2,SWING_Spring);
|
|
A_WeaponOffset(FRandom[Stunner](-1,1)*1.2*invoker.chargesize,32+FRandom[Stunner](-1,1)*1.2*invoker.chargesize);
|
|
if ( !Dampener.Active(self) ) A_AlertMonsters();
|
|
if ( invoker.chargesize >= 5. )
|
|
{
|
|
invoker.count += 1./35.;
|
|
if ( invoker.count > 1.5 ) return ResolveState("Release");
|
|
return ResolveState(null);
|
|
}
|
|
invoker.chargesize += 2.4/35.;
|
|
invoker.count += 1.2/35.;
|
|
if ( invoker.count < 0.24 ) return ResolveState(null);
|
|
invoker.count = 0;
|
|
if ( !(sv_infiniteammo || (FindInventory('PowerInfiniteAmmo',true))) )
|
|
{
|
|
if ( weap.Ammo1.Amount < 1 )
|
|
return ResolveState("Release");
|
|
weap.Ammo1.Amount--;
|
|
}
|
|
return ResolveState(null);
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$T_STUNNER";
|
|
Inventory.PickupMessage "$I_STUNNER";
|
|
Weapon.UpSound "stun/select";
|
|
Weapon.SlotNumber 4;
|
|
Weapon.SelectionOrder 8000;
|
|
Weapon.SlotPriority 0.9;
|
|
Weapon.AmmoType "StunnerAmmo";
|
|
Weapon.AmmoUse 1;
|
|
Weapon.AmmoType2 "StunnerAmmo";
|
|
Weapon.AmmoUse2 1;
|
|
Weapon.AmmoGive 50;
|
|
UTWeapon.DropAmmo 50;
|
|
+WEAPON.WIMPY_WEAPON;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
STNP A -1;
|
|
Stop;
|
|
STNP B -1;
|
|
Stop;
|
|
Select:
|
|
STNS A 1 A_Raise(int.max);
|
|
Wait;
|
|
Ready:
|
|
STNS ABCDEFGHIJKLMNOPQRSTUVWX 2 A_WeaponReady(WRF_NOFIRE);
|
|
Goto Idle;
|
|
Idle:
|
|
STNI A 1
|
|
{
|
|
A_CheckReload();
|
|
let weap = Weapon(invoker);
|
|
if ( weap && weap.Ammo1.Amount > 0 ) A_WeaponReady();
|
|
else A_WeaponReady(WRF_NOFIRE);
|
|
}
|
|
Wait;
|
|
Fire:
|
|
AltFire:
|
|
#### # 2 A_BeginCharge();
|
|
STNF ABCDEFGHIJKLMNOPQRST 2 A_ChargeUp();
|
|
Goto Hold;
|
|
Hold:
|
|
STNH R 1 A_ChargeUp();
|
|
Wait;
|
|
Release:
|
|
#### # 2;
|
|
STNR A 0 A_StunnerFire();
|
|
STNR ABC 1;
|
|
STR2 ABCDE 2;
|
|
STNF TRPNLJHFDB 1 A_WeaponReady(WRF_NOSWITCH|WRF_DISABLESWITCH);
|
|
Goto Idle;
|
|
Deselect:
|
|
STND ABCDEFGHIJKL 1;
|
|
STND L 1 A_Lower(int.max);
|
|
Wait;
|
|
Sparks:
|
|
TNT1 A 8;
|
|
STFF ABCDEFGHIJKLMNOPQRS 2 Bright;
|
|
STFF J 0 A_JumpIf(invoker.chargesize>=5.,1);
|
|
Goto Sparks+10;
|
|
STFF JIHGFEDCBA 2 Bright;
|
|
Stop;
|
|
}
|
|
}
|