stinger_m/zscript/stunner.zsc
Marisa Kirisame 6820c4deba 1.1.1 update (same as flak_m):
- Sound overhaul.
 - Added extended '95 theme from Unreal mac port.
2020-01-04 22:33:10 +01:00

513 lines
14 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.DoEffect();
if ( rechargespeed <= 0. ) rechargespeed = 2.;
rechargephase += 1./rechargespeed;
if ( rechargephase < 7 ) return;
rechargespeed = max(2.,.2*Amount);
rechargephase = 0;
if ( Amount >= MaxAmount ) return;
let sting = Owner.FindInventory("StingerAmmo");
if ( sting && (sting.Amount > 0) )
{
if ( !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo',true) )
sting.Amount--;
Amount++;
}
}
override bool TryPickup( in out Actor toucher )
{
if ( !sting_proto ) return false; // not allowed
return Super.TryPickup(toucher);
}
override void Tick()
{
Super.Tick();
if ( sting_proto ) return;
if ( Owner ) Owner.RemoveInventory(self);
Destroy();
}
}
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;
Array<Line> ShootThroughList;
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;
ShootThroughList.Push(Results.HitLine);
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.ShootThroughList.Clear();
t.Trace(pos,cursector,tracedir,500,0);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
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(10*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_QuakeEx(1,1,1,3,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:120,rollintensity:0.2);
A_StartSound("stun/hit",CHAN_VOICE,pitch:FRandom[Stunner](1.5,1.9)-0.08*specialf1);
A_AlertMonsters(gameinfo.gametype&GAME_Strife?100:0);
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;
}
}
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
{
if ( gameinfo.gametype&GAME_Strife )
target.DaggerAlert(self.target);
return damage;
}
Default
{
Obituary "$O_STUNNER";
RenderStyle "Add";
DamageType 'jolted';
Radius 0;
Height 0.1;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+NOTELEPORT;
+FORCEXYBILLBOARD;
+NOEXTREMEDEATH;
}
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 bool TryPickup( in out Actor toucher )
{
if ( !sting_proto ) return false; // not allowed
return Super.TryPickup(toucher);
}
override void Tick()
{
Super.Tick();
if ( sting_proto ) return;
if ( !Owner )
{
let r = Spawn("Chainsaw",pos,ALLOW_REPLACE);
r.spawnangle = spawnangle;
r.spawnpoint = spawnpoint;
r.angle = angle;
r.pitch = pitch;
r.roll = roll;
r.special = special;
r.args[0] = args[0];
r.args[1] = args[1];
r.args[2] = args[2];
r.args[3] = args[3];
r.args[4] = args[4];
r.ChangeTid(tid);
r.SpawnFlags = SpawnFlags&~MTF_SECRET;
r.HandleSpawnFlags();
r.SpawnFlags = SpawnFlags;
r.bCountSecret = SpawnFlags&MTF_SECRET;
r.vel = vel;
r.master = master;
r.target = target;
r.tracer = tracer;
r.bDropped = bDropped;
r.bNeverRespawn = bNeverRespawn;
}
else Owner.RemoveInventory(self);
Destroy();
}
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)||!deathmatch)?0:5);
StunnerAmmo(weap.Ammo1).rechargespeed = 2.;
A_StopSound(CHAN_WEAPONMISC);
A_StartSound("stun/fire",CHAN_WEAPON,CHANF_OVERLAP,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(12*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_StartSound("stun/charge",CHAN_WEAPONMISC,volume: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) && !(gameinfo.gametype&GAME_Strife) ) 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);
A_SoundVolume(CHAN_WEAPONMISC,Dampener.Active(self)?.15:1.);
if ( !Dampener.Active(self) && !(gameinfo.gametype&GAME_Strife) ) 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;
+NOEXTREMEDEATH;
}
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;
}
}