- Somehow the Stinger has had randomized damage since the very beginning when it was supposed to deal 14. Guess I never EVER bothered to check that, this is a leftover from Doomreal's very early development. Back then, Doom Tournament also did randomized damage all over the place and it was bad.
746 lines
18 KiB
Text
746 lines
18 KiB
Text
Class StingerAmmo : Ammo
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_STINGERAMMO";
|
|
Inventory.Icon "I_Stingr";
|
|
Inventory.PickupMessage "";
|
|
Inventory.Amount 5;
|
|
Inventory.MaxAmount 200;
|
|
Ammo.BackpackAmount 20;
|
|
Ammo.BackpackMaxAmount 400;
|
|
Ammo.DropAmount 2;
|
|
}
|
|
override String PickupMessage()
|
|
{
|
|
return String.Format("%s%d%s",StringTable.Localize("$I_STINGERAMMOL"),Amount,StringTable.Localize("$I_STINGERAMMOR"));
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SAMO A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class StingerAmmo2 : StingerAmmo
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_STINGERAMMO2";
|
|
Inventory.Amount 40;
|
|
Ammo.DropAmount 15;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SAMO B -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class StingerBurstLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "Blue4";
|
|
Args 0,0,0,30;
|
|
ReactionTime 12;
|
|
}
|
|
}
|
|
|
|
Class StingerChunk : Actor
|
|
{
|
|
int deadtimer;
|
|
double rollvel, anglevel, pitchvel;
|
|
|
|
Default
|
|
{
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+MISSILE;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+INTERPOLATEANGLES;
|
|
BounceType "Doom";
|
|
BounceFactor 0.4;
|
|
Gravity 0.35;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
deadtimer = 0;
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
frame = Random[Junk](0,8);
|
|
scale *= Frandom[Junk](0.4,0.8);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( InStateSequence(CurState,ResolveState("Death")) )
|
|
{
|
|
deadtimer++;
|
|
if ( deadtimer > 60 ) A_FadeOut(0.05);
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
CHIP # 1
|
|
{
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
roll += rollvel;
|
|
}
|
|
Loop;
|
|
Bounce:
|
|
CHIP # 0
|
|
{
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
CHIP # -1;
|
|
Stop;
|
|
Dummy:
|
|
CHIP ABCDEFGHI -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class ViewStingerChunk : StingerChunk
|
|
{
|
|
Vector3 ofs, vvel;
|
|
Default
|
|
{
|
|
+NOCLIP;
|
|
+NOGRAVITY;
|
|
-MISSILE;
|
|
BounceType "None";
|
|
}
|
|
override void Tick()
|
|
{
|
|
Actor.Tick();
|
|
if ( !target || !target.player )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
|
Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
|
|
SetOrigin(origin,true);
|
|
bInvisible = (players[consoleplayer].camera != target);
|
|
if ( isFrozen() ) return;
|
|
ofs += vvel;
|
|
scale *= 0.75;
|
|
if ( scale.x <= 0.01 ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class TarydiumExLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "Blue2";
|
|
Args 0,0,0,60;
|
|
ReactionTime 30;
|
|
}
|
|
}
|
|
|
|
Class TarydiumExplosion : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+FORCEXYBILLBOARD;
|
|
+FORCERADIUSDMG;
|
|
+NODAMAGETHRUST;
|
|
DamageType 'TarydiumCharge';
|
|
Obituary "$O_STINGERX";
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_Explode(max(10,special1),90+special1);
|
|
UTMainHandler.DoBlast(self,90+special1,900*special1);
|
|
UTMainHandler.DoBlast(self,200+special1,200*special1); // hurtless secondary blast, so stuff that's pretty close by gets pushed away
|
|
// (just going off the novels here, it's supposed to be pretty strong)
|
|
A_QuakeEx(clamp(special1/6,2,9),clamp(special1/6,2,9),clamp(special1/6,2,9),10,0,300+special1*2,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200+special1*2,rollIntensity:0.12);
|
|
SetOrigin(Vec3Offset(0,0,16),false);
|
|
A_PlaySound("stinger/explode",CHAN_VOICE,pitch:FRandom[Stinger](0.8,1.5));
|
|
A_PlaySound("stinger/explode",CHAN_ITEM,pitch:FRandom[Stinger](0.8,1.5));
|
|
let l = Spawn("TarydiumExLight",pos);
|
|
l.args[3] = int(60*scale.x);
|
|
scale.x *= RandomPick[Stinger](-1,1);
|
|
scale.y *= RandomPick[Stinger](-1,1);
|
|
double ang, pt;
|
|
int numpt = Random[Stinger](10,15)*min(3,special1/60);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("UTSmoke",pos);
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](2,7);
|
|
c.SetShade(Color(1,3,4)*Random[Stinger](24,63));
|
|
c.scale *= 3.;
|
|
}
|
|
numpt = Random[Stinger](4,8)*min(8,special1/25);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("StingerChunk",pos);
|
|
c.angle = ang;
|
|
c.pitch = pt;
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](6,12);
|
|
}
|
|
numpt = Random[Stinger](10,20)*min(4,special1/40);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("UTChip",pos);
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](6,12);
|
|
c.scale *= FRandom[Stinger](0.9,1.5);
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay A_Jump(128,"Explo2");
|
|
Explo1:
|
|
TEX1 ABCDEFGHI 3 Bright;
|
|
Stop;
|
|
Explo2:
|
|
TEX2 ABCDEFGHIJK 3 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class TDebuffLight : PointLightAttenuated
|
|
{
|
|
TarydiumDebuff dbf;
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !dbf || !dbf.victim )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Args[0] = int(min(dbf.Amount,64));
|
|
Args[1] = int(min(dbf.Amount*3,192));
|
|
Args[2] = int(min(dbf.Amount*4,255));
|
|
Args[3] = int(max(dbf.victim.radius,dbf.victim.height)+60+min(dbf.amount/4,40));
|
|
SetOrigin(dbf.Victim.Vec3Offset(0,0,dbf.Victim.Height/2),true);
|
|
}
|
|
}
|
|
|
|
Class TarydiumDebuff : Thinker
|
|
{
|
|
Actor victim, instigator, lite;
|
|
double amount; // accumulated damage for the explosion
|
|
Vector3 oldvel;
|
|
bool wasonair, exploding;
|
|
int explodetimer;
|
|
|
|
void UpdateEffect()
|
|
{
|
|
if ( !victim ) return;
|
|
if ( !lite )
|
|
{
|
|
lite = Actor.Spawn("TDebuffLight",victim.pos);
|
|
TDebuffLight(lite).dbf = self;
|
|
lite.Args[0] = int(min(Amount,64));
|
|
lite.Args[1] = int(min(Amount*3,192));
|
|
lite.Args[2] = int(min(Amount*4,255));
|
|
lite.Args[3] = int(max(victim.radius,victim.height)+60+min(amount/4,40));
|
|
}
|
|
}
|
|
|
|
void BlowUp()
|
|
{
|
|
let b = victim.Spawn("TarydiumExplosion",victim.Vec3Offset(FRandom[Stinger](-victim.radius,victim.radius),FRandom[Stinger](-victim.radius,victim.radius),FRandom[Stinger](0,victim.height)));
|
|
b.target = instigator;
|
|
b.special1 = int(amount); // in Unreal this was capped to 10, which would make it deal barely any damage
|
|
b.scale *= .8+min(1.,amount*0.01);
|
|
amount = amount*0.9-10; // in Unreal this falls off linearly 10 points at a time, which can take very long at full charge
|
|
}
|
|
|
|
override void OnDestroy()
|
|
{
|
|
if ( lite ) lite.Destroy();
|
|
Super.OnDestroy();
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !sting_stinger || !victim || (amount <= .4) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( exploding )
|
|
{
|
|
explodetimer--;
|
|
if ( explodetimer > 0 ) return;
|
|
explodetimer = Random[Stinger](3,6);
|
|
BlowUp();
|
|
return;
|
|
}
|
|
if ( victim.Health <= 0 )
|
|
{
|
|
exploding = true;
|
|
return;
|
|
}
|
|
if ( victim.pos.z > victim.floorz ) wasonair = true;
|
|
else
|
|
{
|
|
if ( wasonair && (oldvel.z < -20) )
|
|
{
|
|
Amount += min(int(-oldvel.z),100);
|
|
exploding = true;
|
|
return;
|
|
}
|
|
wasonair = false;
|
|
}
|
|
oldvel = victim.vel;
|
|
amount *= 1.-.04/TICRATE;
|
|
UpdateEffect();
|
|
if ( amount <= .4 )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( level.maptime%3 ) return;
|
|
int numpt = clamp(int(Random[Stinger](1,3)*amount*0.02),1,8);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pos = victim.Vec3Offset(FRandom[Stinger](-victim.radius,victim.radius),FRandom[Stinger](-victim.radius,victim.radius),FRandom[Stinger](0,victim.height));
|
|
let l = victim.Spawn("StingerBurstLight",pos);
|
|
l.Args[3] /= 2;
|
|
double ang, pt;
|
|
int numpt2 = Random[Stinger](2,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = victim.Spawn("UTSmoke",pos);
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](0.3,0.8);
|
|
c.SetShade(Color(1,3,4)*Random[Stinger](24,63));
|
|
c.scale *= 3.;
|
|
c.alpha *= 0.35;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Apply( Actor victim, Actor instigator, double amount )
|
|
{
|
|
if ( !victim || (victim.Health <= 0) || !victim.bISMONSTER ) return;
|
|
if ( !sting_stinger ) return;
|
|
let ti = ThinkerIterator.Create("TarydiumDebuff",STAT_USER);
|
|
TarydiumDebuff t;
|
|
while ( t = TarydiumDebuff(ti.Next()) )
|
|
{
|
|
if ( t.victim != victim ) continue;
|
|
if ( instigator ) t.instigator = instigator;
|
|
t.amount += amount;
|
|
t.UpdateEffect();
|
|
return;
|
|
}
|
|
t = new("TarydiumDebuff");
|
|
t.ChangeStatNum(STAT_USER);
|
|
t.victim = victim;
|
|
t.instigator = instigator;
|
|
t.amount = amount;
|
|
t.oldvel = victim.vel;
|
|
t.wasonair = (victim.pos.z>victim.floorz);
|
|
t.UpdateEffect();
|
|
}
|
|
}
|
|
|
|
Class StingerProjectile : Actor
|
|
{
|
|
Vector3 oldvel;
|
|
Default
|
|
{
|
|
Obituary "$O_STINGER";
|
|
DamageType 'Stinger';
|
|
DamageFunction 14;
|
|
Speed 40;
|
|
Radius 2;
|
|
Height 2;
|
|
PROJECTILE;
|
|
+SKYEXPLODE;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
if ( sting_stingerb )
|
|
{
|
|
// set bounce stuff
|
|
bBOUNCEONWALLS = true;
|
|
bBOUNCEONFLOORS = true;
|
|
bBOUNCEONCEILINGS = true;
|
|
bALLOWBOUNCEONACTORS = true;
|
|
bCANBOUNCEWATER = true;
|
|
bUSEBOUNCESTATE = true;
|
|
BounceCount = 4;
|
|
BounceFactor = 1.;
|
|
WallBounceFactor = 1.;
|
|
}
|
|
}
|
|
override void Tick()
|
|
{
|
|
oldvel = vel;
|
|
Super.Tick();
|
|
}
|
|
void A_HandleBounce()
|
|
{
|
|
// cancel bounce based on "incidence"
|
|
if ( vel dot oldvel < 0.87 )
|
|
{
|
|
ClearBounce();
|
|
ExplodeMissile();
|
|
return;
|
|
}
|
|
if ( !Random[Stinger](0,2) ) A_PlaySound("stinger/hit2",CHAN_BODY,0.5,pitch:FRandom[Stinger](0.5,1.5));
|
|
else A_PlaySound("stinger/hit",CHAN_BODY,0.6);
|
|
A_SprayDecal("WallCrack",-20);
|
|
A_AlertMonsters();
|
|
let l = Spawn("StingerBurstLight",pos);
|
|
l.Args[3] /= 2;
|
|
double ang, pt;
|
|
int numpt = Random[Stinger](1,3);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("StingerChunk",pos);
|
|
c.scale *= 0.5;
|
|
c.angle = ang;
|
|
c.pitch = pt;
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](3,9);
|
|
}
|
|
numpt = Random[Stinger](2,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("UTSmoke",pos);
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](0.3,0.8);
|
|
c.SetShade(Color(1,3,4)*Random[Stinger](48,63));
|
|
c.alpha *= 0.35;
|
|
}
|
|
}
|
|
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
|
|
{
|
|
if ( !target.bNOBLOOD )
|
|
{
|
|
target.SpawnBlood(pos,AngleTo(target),damage);
|
|
A_PlaySound("stinger/flesh");
|
|
A_AlertMonsters();
|
|
if ( sting_stinger )
|
|
{
|
|
TarydiumDebuff.Apply(target,self.target,10);
|
|
damage = 2;
|
|
}
|
|
}
|
|
return damage;
|
|
}
|
|
action void A_StingerHit()
|
|
{
|
|
if ( !Random[Stinger](0,2) ) A_PlaySound("stinger/hit2",CHAN_BODY,0.5,pitch:FRandom[Stinger](0.5,1.5));
|
|
else A_PlaySound("stinger/hit",CHAN_BODY,0.6);
|
|
A_SprayDecal("WallCrack",20);
|
|
A_AlertMonsters();
|
|
Spawn("StingerBurstLight",pos);
|
|
double ang, pt;
|
|
int numpt = Random[Stinger](4,8);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("StingerChunk",pos);
|
|
c.angle = ang;
|
|
c.pitch = pt;
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](3,9);
|
|
}
|
|
numpt = Random[Stinger](6,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Stinger](0,360);
|
|
pt = FRandom[Stinger](-90,90);
|
|
let c = Spawn("UTSmoke",pos);
|
|
c.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[Stinger](0.3,0.8);
|
|
c.SetShade(Color(1,3,4)*Random[Stinger](48,63));
|
|
c.alpha *= 0.5;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TPRJ A -1 Bright;
|
|
Stop;
|
|
Bounce:
|
|
TPRJ A 0 Bright A_HandleBounce();
|
|
Goto Spawn;
|
|
Death:
|
|
Crash:
|
|
TNT1 A 1 A_StingerHit();
|
|
Stop;
|
|
XDeath:
|
|
TNT1 A 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class StingerLight : EnforcerLight
|
|
{
|
|
Default
|
|
{
|
|
args 64,128,255,80;
|
|
}
|
|
}
|
|
|
|
Class Stinger : UnrealWeapon
|
|
{
|
|
int flashnum;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_STINGER";
|
|
Inventory.PickupMessage "$I_STINGER";
|
|
Weapon.UpSound "stinger/select";
|
|
Weapon.SlotNumber 3;
|
|
Weapon.SelectionOrder 2500;
|
|
Weapon.SlotPriority 1;
|
|
Weapon.AmmoType "StingerAmmo";
|
|
Weapon.AmmoUse 1;
|
|
Weapon.AmmoType2 "StingerAmmo";
|
|
Weapon.AmmoUse2 1;
|
|
Weapon.AmmoGive 40;
|
|
UTWeapon.DropAmmo 20;
|
|
}
|
|
action void A_StingerFire( bool hold = false )
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
|
|
if ( hold )
|
|
{
|
|
A_SoundVolume(CHAN_WEAPON,Dampener.Active(self)?.1:1.);
|
|
A_Overlay(PSP_FLASH,"MFlashHold");
|
|
}
|
|
else
|
|
{
|
|
A_PlaySound("stinger/fire",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
|
|
A_Overlay(PSP_FLASH,"MFlash");
|
|
}
|
|
A_OverlayFlags(PSP_FLASH,PSPF_RenderStyle,true);
|
|
A_OverlayRenderstyle(PSP_FLASH,STYLE_Add);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoFlash(self,Color(16,0,64,255),1);
|
|
UTMainHandler.DoSwing(self,(FRandom[Stinger](-0.1,-0.2),FRandom[Stinger](-0.1,0.1)),4,-1.5,2,SWING_Spring,2,2);
|
|
if ( !Dampener.Active(self) ) A_AlertMonsters();
|
|
A_QuakeEx(1,1,1,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),5*x+8*y-8*z);
|
|
Actor p = Spawn("StingerProjectile",origin);
|
|
p.angle = angle;
|
|
p.pitch = BulletSlope();
|
|
p.roll = FRandom[Stinger](0,360);
|
|
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
|
p.target = self;
|
|
int numpt = Random[Stinger](5,9);
|
|
double ang;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("ViewStingerChunk",origin);
|
|
ViewStingerChunk(s).ofs = (8,2,-2);
|
|
ang = FRandom[Stinger](0,360);
|
|
ViewStingerChunk(s).vvel = (FRandom[Stinger](0.2,1.2),0,0)+(0,cos(ang),sin(ang))*FRandom[Stinger](0.3,0.9);
|
|
s.target = self;
|
|
}
|
|
for ( int i=0; i<12; i++ )
|
|
{
|
|
let s = Spawn("UTViewSmoke",origin);
|
|
UTViewSmoke(s).ofs = (16,4,-4);
|
|
ang = FRandom[Stinger](0,360);
|
|
UTViewSmoke(s).vvel = (FRandom[Stinger](0,0.4),0,0)+(0,cos(ang),sin(ang))*FRandom[Stinger](0.2,0.5);
|
|
s.target = self;
|
|
s.scale *= 1.5;
|
|
s.alpha *= 0.3;
|
|
s.SetShade(Color(1,3,4)*Random[Stinger](31,63));
|
|
}
|
|
}
|
|
action void A_StingerAltFire()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
A_PlaySound("stinger/altfire",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
|
|
A_Overlay(PSP_FLASH,"MFlash");
|
|
A_OverlayFlags(PSP_FLASH,PSPF_RenderStyle,true);
|
|
A_OverlayRenderstyle(PSP_FLASH,STYLE_Add);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoFlash(self,Color(16,0,64,255),1);
|
|
UTMainHandler.DoSwing(self,(FRandom[Stinger](-0.3,-0.8),FRandom[Stinger](-0.4,0.4)),4,-1,3,SWING_Spring,3,5);
|
|
if ( !Dampener.Active(self) ) A_AlertMonsters();
|
|
A_QuakeEx(1,1,1,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),5*x+8*y-8*z);
|
|
[x, y, z] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
Actor p;
|
|
double a, s;
|
|
Vector3 dir;
|
|
for ( int i=0; i<5; i++ )
|
|
{
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
|
|
a = FRandom[Stinger](0,360);
|
|
s = FRandom[Stinger](0,(i>0)?.08:0.); // first shot always accurate
|
|
dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
|
p = Spawn("StingerProjectile",origin);
|
|
p.angle = atan2(dir.y,dir.x);
|
|
p.pitch = asin(-dir.z);
|
|
p.roll = FRandom[Stinger](0,360);
|
|
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
|
p.target = self;
|
|
int numpt = Random[Stinger](5,9);
|
|
double ang;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("ViewStingerChunk",origin);
|
|
ViewStingerChunk(s).ofs = (8,2,-2);
|
|
ang = FRandom[Stinger](0,360);
|
|
ViewStingerChunk(s).vvel = (FRandom[Stinger](0.2,1.2),0,0)+(0,cos(ang),sin(ang))*FRandom[Stinger](0.3,0.9);
|
|
s.target = self;
|
|
}
|
|
for ( int i=0; i<12; i++ )
|
|
{
|
|
let s = Spawn("UTViewSmoke",origin);
|
|
UTViewSmoke(s).ofs = (16,4,-4);
|
|
ang = FRandom[Stinger](0,360);
|
|
UTViewSmoke(s).vvel = (FRandom[Stinger](0,0.4),0,0)+(0,cos(ang),sin(ang))*FRandom[Stinger](0.2,0.5);
|
|
s.target = self;
|
|
s.scale *= 1.5;
|
|
s.alpha *= 0.3;
|
|
s.SetShade(Color(1,3,4)*Random[Stinger](48,63));
|
|
}
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
STNP A -1;
|
|
Stop;
|
|
STNP B -1;
|
|
Stop;
|
|
Select:
|
|
STNS A 1 A_Raise(int.max);
|
|
Wait;
|
|
Ready:
|
|
STNS ABCDEFGHIJKLMNOPQRSTU 1 A_WeaponReady(WRF_NOFIRE);
|
|
Idle:
|
|
STNI A 1
|
|
{
|
|
A_CheckReload();
|
|
A_WeaponReady();
|
|
}
|
|
Wait;
|
|
Fire:
|
|
STNF A 2 A_StingerFire();
|
|
STNF BC 2;
|
|
STNI A 3;
|
|
STNI A 0 A_Refire(1);
|
|
Goto Idle;
|
|
STNI A 0
|
|
{
|
|
A_PlaySound("stinger/hold",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true);
|
|
invoker.flashnum = Random[Stinger](0,3);
|
|
}
|
|
Hold:
|
|
STNH A 1
|
|
{
|
|
// need to make sure player does the repeat fire anim
|
|
if ( self is 'UPlayer' )
|
|
UPlayer(self).PlayAttacking();
|
|
A_StingerFire(true);
|
|
invoker.flashnum = (invoker.flashnum+1)%4;
|
|
}
|
|
STNH BCDEFG 1;
|
|
STNH A 0 A_JumpIfNoAmmo(1);
|
|
STNH A 0 A_Refire();
|
|
STNH A 2 A_PlaySound("stinger/release",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
|
|
Goto Idle;
|
|
AltFire:
|
|
STNF A 2 A_StingerAltFire();
|
|
STNF BC 2;
|
|
STNI A 35; // yes, 1 second cooldown
|
|
Goto Idle;
|
|
Deselect:
|
|
STND ACEGIK 1;
|
|
STND K 1 A_Lower(int.max);
|
|
Wait;
|
|
MFlash:
|
|
STFF A 2 Bright
|
|
{
|
|
let l = Spawn("StingerLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
MFlashHold:
|
|
TNT1 A 0 A_JumpIf(invoker.flashnum==0,7);
|
|
TNT1 A 0 A_JumpIf(invoker.flashnum==1,4);
|
|
TNT1 A 0 A_JumpIf(invoker.flashnum==2,4);
|
|
TNT1 A 0 A_JumpIf(invoker.flashnum==3,1);
|
|
Stop;
|
|
STFF A 2 Bright
|
|
{
|
|
let l = Spawn("StingerLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
STFF B 2 Bright
|
|
{
|
|
let l = Spawn("StingerLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
STFF C 2 Bright
|
|
{
|
|
let l = Spawn("StingerLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
STFF D 2 Bright
|
|
{
|
|
let l = Spawn("StingerLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
/*MFlash:
|
|
STFF ABC 2 Bright;
|
|
Stop;
|
|
MFlashHold:
|
|
STFH ABCDEFG 1 Bright;
|
|
Stop;*/
|
|
}
|
|
}
|