flak_m/zscript/warheadlauncher.zsc
Marisa Kirisame fb96c7523e Portal awareness adjustments to various vector operations.
Got rid of the deprecated Matrix4.GetAxes method. Next step is to get rid of more stuff by migrating to libeye.
Mirrored Translocator model so it shows the actually detailed side. At some point in UT's development it got flipped around for some reason.
Weapon code cleanup (most noticeable on states).
Backported scope shader from Doomreal.
Added optional "dummied out" Sniper zoom sounds from a dubious source.
2019-09-28 20:06:51 +02:00

773 lines
20 KiB
Text

// wasn't placeable in UT99
Class WarheadAmmo : Ammo
{
Default
{
Tag "$T_WARHEADAMMO";
Inventory.PickupMessage "$I_WARHEADAMMO";
Inventory.Amount 1;
Inventory.MaxAmount 2;
Ammo.BackpackAmount 0;
Ammo.BackpackMaxAmount 3;
Ammo.DropAmount 1;
Inventory.RespawnTics 2100;
+INVENTORY.IGNORESKILL;
}
States
{
Spawn:
WMIS A -1;
Stop;
}
}
Class ShockWave : Actor
{
double shocksize, olddmgradius;
double lifespan;
int icount;
transient ThinkerIterator t;
Default
{
Obituary "$O_REDEEMER";
RenderStyle "Add";
Radius 0.1;
Height 0;
Scale 1.0;
ReactionTime 60;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
lifespan = ReactionTime;
A_PlaySound("warhead/explode",CHAN_VOICE,attenuation:ATTN_NONE);
A_QuakeEx(9,9,9,100,0,12000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:1200,rollIntensity:0.5);
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
if ( alpha <= 0 ) return;
if ( !t ) t = ThinkerIterator.Create("Actor");
icount++;
if ( icount == 4 ) Spawn("WarheadSubExplosion",pos);
lifespan--;
alpha -= 1./ReactionTime;
shocksize = 13*(ReactionTime-lifespan)+3.5/(lifespan/ReactionTime+0.05);
A_SetScale(shocksize*0.25);
double dmgradius = shocksize*1.5;
Actor a;
t.Reinit();
while ( a = Actor(t.Next()) )
{
if ( !a.bShootable || !CheckSight(a,0xf) || (Distance3D(a) > dmgradius) ) continue;
Vector3 dir = Vec3To(a);
double dist = max(1,dir.length());
dir = dir/dist+(0,0,0.3);
double moscale = max(0,1100-0.22*dist);
if ( (dist > olddmgradius-a.radius) || (dir dot a.vel <= 0) )
{
if ( !a.bDONTTHRUST ) a.vel += dir*((moscale+20)/a.mass);
a.DamageMobj(self,target,int(moscale),'RedeemerDeath',DMG_THRUSTLESS);
}
if ( a.player ) UTMainHandler.DoFlash(a,Color(32,255,255,255),80);
}
olddmgradius = dmgradius;
}
States
{
Spawn:
RWAV A 100 Bright;
Stop;
}
}
Class WarheadSubExplosion : Actor
{
Default
{
Renderstyle "Add";
Scale 2.8;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
Spawn("WarheadExplodLight",pos);
}
States
{
Spawn:
WE__ ABCDEFGHIJKLMNOPR 3 Bright;
Stop;
}
}
Class WarheadHitbox : Actor
{
Default
{
Radius 2;
Height 4;
+SHOOTABLE;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOBLOOD;
+NOTELEPORT;
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
if ( (damage > 5) && target )
{
target.bAMBUSH = true;
target.ExplodeMissile(null,inflictor);
}
return 0;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
SetOrigin(target.Vec3Offset(0,0,-height*0.5),true);
}
States
{
Spawn:
TNT1 A 10 A_AlertMonsters(0,AMF_TARGETEMITTER);
Wait;
}
}
Class WarheadExplodLight : PaletteLight
{
Default
{
ReactionTime 50;
Args 0,0,0,300;
}
}
Class WarheadLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
Args 255,224,192,70;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
Vector3 taildir = -(cos(target.angle)*cos(target.pitch),sin(target.angle)*cos(target.pitch),-sin(target.pitch));
SetOrigin(level.Vec3Offset(pos,taildir*20),true);
args[LIGHT_INTENSITY] = Random[Warhead](6,8)*10;
}
}
Class WarheadTrail : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
+NOTELEPORT;
Scale 0.2;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
roll = FRandom[Puff](0,360);
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
vel *= 0.99;
A_FadeOut(0.1);
}
States
{
Spawn:
RTRL A -1 Bright;
Stop;
}
}
Class WarShell : Actor
{
double destangle, destpitch;
Actor l, b;
Default
{
Obituary "$O_REDEEMER";
Radius 2;
Height 2;
Speed 6;
DamageType 'RedeemerDeath';
DamageFactor 1000;
PROJECTILE;
+FORCEXYBILLBOARD;
+SKYEXPLODE;
+FORCERADIUSDMG;
+EXPLODEONWATER;
+INTERPOLATEANGLES;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
l = Spawn("Warheadlight",pos);
l.target = self;
b = Spawn("WarheadHitbox",pos);
b.target = self;
A_PlaySound("warhead/fly",CHAN_VOICE,1.0,true);
destangle = angle;
destpitch = pitch;
}
override int SpecialMissileHit( Actor victim )
{
if ( victim == b ) return 1;
return -1;
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
if ( !bMISSILE ) return;
if ( vel.length() > 0 )
{
if ( waterlevel > 0 )
{
vel *= 0.98;
if ( vel.length() < 6 ) vel += vel.unit()*0.35;
}
else if ( vel.length() < 20 ) vel += vel.unit()*0.35;
}
}
action void A_Trail()
{
Vector3 taildir = -(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
if ( waterlevel > 0 )
{
for ( int i=0; i<4; i++ )
{
let s = Spawn("UTBubble",pos+taildir*32+(FRandom[Warhead](-.5,.5),FRandom[Warhead](-.5,.5),FRandom[Warhead](-.5,.5)));
s.vel = taildir*2;
}
return;
}
for ( int i=0; i<4; i++ )
{
let s = Spawn("UTSmoke",pos+taildir*32+(FRandom[Warhead](-.5,.5),FRandom[Warhead](-.5,.5),FRandom[Warhead](-.5,.5)));
s.vel = taildir*2;
s.SetShade("404040");
}
for ( int i=0; i<4; i++ )
{
let s = Spawn("WarheadTrail",pos+taildir*35+(FRandom[Warhead](-1,1),FRandom[Warhead](-1,1),FRandom[Warhead](-1,1)));
s.vel = taildir*4;
}
}
action void A_Vaporize()
{
if ( invoker.l ) invoker.l.Destroy();
if ( invoker.b ) invoker.b.Destroy();
A_SetScale(2.0);
A_Explode(1000,180);
A_SprayDecal("BigBlast");
A_QuakeEx(8,8,8,20,0,300,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.35);
A_PlaySound("shock/hit",CHAN_VOICE,attenuation:0.5);
A_AlertMonsters();
A_SetRenderStyle(1.0,STYLE_Add);
Spawn("WarheadExplodLight",pos);
let s = Spawn("ShockWave",pos);
s.target = target;
}
action void A_Intercepted()
{
if ( invoker.l ) invoker.l.Destroy();
if ( invoker.b ) invoker.b.Destroy();
A_Explode(1000,220);
A_SprayDecal("BigBlast");
A_QuakeEx(8,8,8,20,0,300,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.35);
A_PlaySound("shock/hit",CHAN_VOICE,attenuation:0.5);
A_AlertMonsters();
A_SetRenderStyle(1.0,STYLE_Add);
Spawn("WarheadExplodLight",pos);
}
States
{
Spawn:
WMIS A 1 A_Trail();
Wait;
Death:
TNT1 A 0 A_JumpIf(bAMBUSH,"Death.Intercept");
TNT1 A 0 A_Vaporize();
NE__ ABCDEFGHIJKLMNOPR 3 Bright;
Stop;
Death.Intercept:
TNT1 A 0 A_Intercepted();
WE__ ABCDEFGHIJKLMNOPR 3 Bright;
Stop;
}
}
Class GuidedWarShell : WarShell
{
double lagangle, lagpitch, lagroll, lagangle2, lagpitch2, lagroll2,
guideangle, guidepitch, guideroll;
bool justleft;
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( target && target.player ) target.player.camera = self;
justleft = true;
}
override void Tick()
{
Actor.Tick();
if ( justleft && (Distance3D(target) > (2*max(target.radius,target.height))) )
{
justleft = false;
bHITOWNER = true;
}
if ( isFrozen() ) return;
if ( !bMISSILE ) return;
if ( !target || !target.player || (target.Health <= 0) )
{
bAMBUSH = true;
ExplodeMissile();
return;
}
if ( target.player.cmd.buttons&BT_ATTACK )
{
ExplodeMissile();
return;
}
if ( vel.length() > 0 )
{
lagangle = target.player.cmd.yaw/128.;
lagpitch = -target.player.cmd.pitch/128.;
if ( target.player.cmd.buttons&BT_RELOAD ) lagroll = 5;
else if ( target.player.cmd.buttons&BT_ZOOM ) lagroll = -5;
else lagroll = 0;
guideangle = lagangle2*0.95+lagangle*0.05;
guidepitch = lagpitch2*0.95+lagpitch*0.05;
guideroll = lagroll2*0.95+lagroll*0.05;
dt_Quat orient = dt_Quat.create_euler(pitch,angle,roll);
dt_Quat angles = dt_Quat.create_euler(guidepitch,guideangle,guideroll);
orient = orient.qmul(angles);
double npitch, nangle, nroll;
[npitch, nangle, nroll] = orient.to_euler();
angle = nangle;
pitch = npitch;
roll = nroll;
vel = (vel+(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*0.8).unit()*11;
}
lagangle2 = lagangle2*0.95+lagangle*0.05;
lagpitch2 = lagpitch2*0.95+lagpitch*0.05;
lagroll2 = lagroll2*0.95+lagroll*0.05;
}
States
{
Death:
TNT1 A 0
{
if ( WarheadLauncher(master) )
WarheadLauncher(master).Guided = null;
if ( target && target.player && target.player.camera == self ) target.player.camera = target;
}
Goto Super::Death;
}
}
Class MidTracer : LineTracer
{
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor ) return TRACE_Skip;
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] ) return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class TargetActor
{
Vector2 vpos;
String diststr;
}
Class RedeemerHUD : HUDMessageBase
{
Actor Camera;
Vector3 ViewPos;
double ViewAngle, ViewPitch, ViewRoll, LagRoll, LagRoll2;
TextureID reticle1, reticle2, arrow, mark, readout;
Font whfont;
transient ThinkerIterator t;
transient MidTracer tr;
Array<TargetActor> ta;
Shape2D sshape, darrow;
bool dodim;
RedeemerHUD Init()
{
reticle1 = TexMan.CheckForTexture("GuidedX1",TexMan.Type_Any);
reticle2 = TexMan.CheckForTexture("GuidedX2",TexMan.Type_Any);
arrow = TexMan.CheckForTexture("AroDup",TexMan.Type_Any);
mark = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any);
whfont = Font.GetFont('WHFONT');
sshape = new("Shape2D");
sshape.PushCoord((0,0));
sshape.PushCoord((1,0));
sshape.PushCoord((0,1));
sshape.PushCoord((1,1));
sshape.PushTriangle(0,3,1);
sshape.PushTriangle(0,2,3);
return self;
}
override bool Tick()
{
LagRoll = dt_Quat.Normalize180(ViewRoll-LagRoll2);
LagRoll2 += dt_Quat.Normalize180(LagRoll-LagRoll2)*0.1;
// shootable targetting
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() && !CVar.GetCVar('flak_redeemerreadout_perframe',players[consoleplayer]).GetBool() )
{
if ( !t ) t = ThinkerIterator.Create("Actor");
if ( !tr ) tr = new("MidTracer");
t.Reinit();
ta.Clear();
Actor a;
Vector3 vdir = (cos(ViewAngle)*cos(ViewPitch),sin(ViewAngle)*cos(ViewPitch),-sin(ViewPitch));
while ( a = Actor(t.Next()) )
{
Vector3 tdir = Level.Vec3Diff(ViewPos,a.Pos+(0,0,a.Height*0.5));
if ( !a.bSHOOTABLE || (a.Health <= 0) || ((Camera is 'GuidedWarShell') && (a == GuidedWarShell(Camera).b)) || (tdir.length() > 2000) || (acos(tdir.unit() dot vdir) > players[consoleplayer].FOV) || tr.Trace(ViewPos,Camera.CurSector,tdir.unit(),tdir.length(),0) ) continue;
Vector3 wpos = ViewPos+tdir;
Vector3 spos = dt_CoordUtil.WorldToScreen(wpos,ViewPos,ViewPitch,ViewAngle,ViewRoll,players[consoleplayer].FOV);
if ( spos.z > 1.0 ) continue;
TargetActor te = new("TargetActor");
te.vpos = dt_CoordUtil.ToViewport(spos);
te.diststr = String.Format("%f",tdir.length());
te.diststr.Replace(".","");
ta.Push(te);
}
}
return !Camera;
}
override void Draw( int bottom, int visibility )
{
if ( visibility != StatusBar.HUDMSGLayer_UnderHUD ) return;
if ( dodim ) Screen.Dim("C8 00 00",0.2,0,0,Screen.GetWidth(),Screen.GetHeight());
// shootable targetting
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() )
{
if ( CVar.GetCVar('flak_redeemerreadout_perframe',players[consoleplayer]).GetBool() )
{
if ( !t ) t = ThinkerIterator.Create("Actor");
if ( !tr ) tr = new("MidTracer");
t.Reinit();
ta.Clear();
Actor a;
Vector3 vdir = (cos(ViewAngle)*cos(ViewPitch),sin(ViewAngle)*cos(ViewPitch),-sin(ViewPitch));
while ( a = Actor(t.Next()) )
{
Vector3 tdir = Level.Vec3Diff(ViewPos,a.Pos+(0,0,a.Height*0.5));
if ( !a.bSHOOTABLE || (a.Health <= 0) || ((Camera is 'GuidedWarShell') && (a == GuidedWarShell(Camera).b)) || (tdir.length() > 2000) || (acos(tdir.unit() dot vdir) > players[consoleplayer].FOV) || tr.Trace(ViewPos,Camera.CurSector,tdir.unit(),tdir.length(),0) ) continue;
Vector3 wpos = ViewPos+tdir;
Vector3 spos = dt_CoordUtil.WorldToScreen(wpos,ViewPos,ViewPitch,ViewAngle,ViewRoll,players[consoleplayer].FOV);
if ( spos.z > 1.0 ) continue;
TargetActor te = new("TargetActor");
te.vpos = dt_CoordUtil.ToViewport(spos);
te.diststr = String.Format("%f",tdir.length());
te.diststr.Replace(".","");
ta.Push(te);
}
}
for ( int i=0; i<ta.Size(); i++ )
{
Screen.DrawTexture(mark,false,ta[i].vpos.x,ta[i].vpos.y,DTA_LegacyRenderStyle,STYLE_Add);
Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(ta[i].vpos.x-whfont.StringWidth(ta[i].diststr)/2)-12,ta[i].vpos.y+8,ta[i].diststr,DTA_LegacyRenderStyle,STYLE_Add);
}
}
// reticle
Vector2 vs = (640,640/Screen.GetAspectRatio());
Vector2 mid, siz, siz2;
[mid, siz] = Screen.VirtualToRealCoords(vs*0.5,(128,128),vs,false,false);
[mid, siz2] = Screen.VirtualToRealCoords(vs*0.5,(8,4),vs,false,false);
Vector2 verts[4];
verts[0] = (-siz.x,-siz.y);
verts[1] = (siz.x,-siz.y);
verts[2] = (-siz.x,siz.y);
verts[3] = (siz.x,siz.y);
sshape.Clear(Shape2D.C_Verts);
double rrot = -LagRoll2*2;
for ( int i=0; i<4; i++ )
sshape.PushVertex(mid+(verts[i].x*cos(rrot)-verts[i].y*sin(rrot),verts[i].x*sin(rrot)+verts[i].y*cos(rrot)));
Screen.DrawShape(reticle1,false,sshape,DTA_LegacyRenderStyle,STYLE_Add);
sshape.Clear(Shape2D.C_Verts);
verts[0] = (-siz2.x,siz.y+siz2.y);
verts[1] = (siz2.x,siz.y+siz2.y);
verts[2] = (-siz2.x,siz.y+3*siz2.y);
verts[3] = (siz2.x,siz.y+3*siz2.y);
for ( int i=0; i<4; i++ )
sshape.PushVertex(mid+(verts[i].x*cos(rrot)-verts[i].y*sin(rrot),verts[i].x*sin(rrot)+verts[i].y*cos(rrot)));
Screen.DrawShape(arrow,false,sshape,DTA_LegacyRenderStyle,STYLE_Add);
Screen.DrawTexture(reticle2,false,vs.x*0.5,vs.y*0.5,DTA_VirtualWidthF,vs.x,DTA_VirtualHeightF,vs.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add);
// faux assembly readout
int numreadouts = int(vs.y/128+2);
for ( int i=0; i<numreadouts; i++ )
{
int scroll = (gametic*5)%128;
Screen.DrawTexture(readout,false,0,i*128-scroll,DTA_VirtualWidthF,vs.x,DTA_VirtualHeightF,vs.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add);
}
}
}
Class RedeemerHUDStatic : HUDMessageBase
{
int tickcnt;
TextureID tx;
RedeemerHUDStatic Init()
{
tx = TexMan.CheckForTexture("static1",TexMan.Type_Any);
tickcnt = 0;
return self;
}
override bool Tick()
{
return (++tickcnt>15);
}
override void Draw( int bottom, int visibility )
{
if ( visibility != StatusBar.HUDMSGLayer_UnderHUD ) return;
double sw, sh;
sw = 256.;
sh = sw*(Screen.GetHeight()/double(Screen.GetWidth()));
Screen.DrawTexture(tx,true,0,0,DTA_VirtualWidthF,sw,DTA_VirtualHeightF,sh,DTA_KeepRatio,true);
}
}
Class RedeemerHUDHandler : EventHandler
{
ui RedeemerHUD rhud;
transient ui CVar deemershader;
override void RenderOverlay( RenderEvent e )
{
if ( !deemershader ) deemershader = CVar.GetCVar('flak_deemershader',players[consoleplayer]);
if ( e.Camera is 'GuidedWarShell' )
{
if ( !rhud )
{
rhud = new("RedeemerHUD").Init();
StatusBar.AttachMessage(rhud,0,StatusBar.HUDMSGLayer_UnderHUD);
}
rhud.Camera = e.Camera;
rhud.ViewPos = e.ViewPos;
rhud.ViewAngle = e.ViewAngle;
rhud.ViewPitch = e.ViewPitch;
rhud.ViewRoll = e.ViewRoll;
rhud.dodim = !deemershader.GetBool();
Shader.SetEnabled(players[consoleplayer],"RedeemerView",deemershader.GetBool());
Shader.SetUniform1f(players[consoleplayer],"RedeemerView","Timer",gametic+e.fractic);
}
else if ( rhud )
{
Shader.SetEnabled(players[consoleplayer],"RedeemerView",false);
StatusBar.DetachMessage(rhud);
rhud.Destroy();
StatusBar.AttachMessage(new("RedeemerHUDStatic").Init(),0,StatusBar.HUDMSGLayer_UnderHUD);
}
}
}
Class WarheadLauncher : UTWeapon
{
transient CVar noswitchdeemer;
Actor guided;
override void Tick()
{
Super.Tick();
if ( !Owner || !Owner.player ) return;
if ( guided ) crosshair = 99;
else crosshair = 0;
if ( !noswitchdeemer ) noswitchdeemer = CVar.GetCVar('flak_noswitchdeemer',Owner.player);
if ( noswitchdeemer.GetBool() ) SelectionOrder = int.max;
else SelectionOrder = 0;
}
action void A_WarheadFire()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
A_PlaySound("warhead/fire",CHAN_WEAPON);
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(128,255,128,128),1);
UTMainHandler.DoSwing(self,(FRandom[Warhead](0.6,1.2),FRandom[Warhead](0.2,0.5)),4,-1,3,SWING_Spring,2,5);
UTMainHandler.DoSwing(self,(FRandom[Warhead](0.2,0.5),FRandom[Warhead](-0.9,-1.5)),4,-0.6,5,SWING_Spring,3,3);
A_AlertMonsters();
A_QuakeEx(6,6,6,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.2);
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
vel -= x*10;
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z);
Actor p = Spawn("WarShell",origin);
p.angle = angle;
p.pitch = BulletSlope();
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
p.target = self;
}
action void A_WarheadSmoke()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
vel -= x*0.2;
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z);
int numpt = Random[Warhead](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Warhead](-.8,.8),FRandom[Warhead](-.8,.8),FRandom[Warhead](-.8,.8))).unit()*FRandom[Warhead](1,2);
let s = Spawn("UTSmoke",origin);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Warhead](32,128));
}
}
action void A_WarheadAlt()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
A_PlaySound("warhead/fire",CHAN_WEAPON);
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(128,255,128,128),1);
A_AlertMonsters();
A_QuakeEx(6,6,6,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.2);
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
vel -= x*10;
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z);
Actor p = Spawn("GuidedWarShell",origin);
p.angle = angle;
p.pitch = BulletSlope();
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
p.target = self;
p.master = invoker;
invoker.guided = p;
}
// disallow dropping while guiding
override Inventory CreateTossable( int amt )
{
if ( guided ) return null;
return Super.CreateTossable();
}
override void OwnerDied()
{
Super.OwnerDied();
if ( guided ) guided.ExplodeMissile();
}
override void OnDestroy()
{
Super.OnDestroy();
if ( guided ) guided.ExplodeMissile();
}
Default
{
Tag "$T_REDEEMER";
Inventory.PickupMessage "$I_REDEEMER";
Weapon.UpSound "warhead/select";
Weapon.SlotNumber 0;
Weapon.SelectionOrder 0;
Weapon.AmmoType "WarheadAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "WarheadAmmo";
Weapon.AmmoUse2 1;
Weapon.AmmoGive 1;
Inventory.RespawnTics 2100;
+INVENTORY.ALWAYSPICKUP;
+INVENTORY.IGNORESKILL;
+WEAPON.NOAUTOFIRE;
UTWeapon.DropAmmo 1;
}
States
{
Spawn:
RDMP A -1;
Stop;
RDMP B -1;
Stop;
Ready:
WARS ABCDEFGHIJKLMNO 1 A_WeaponReady(WRF_NOFIRE);
Idle:
WARI A 1
{
A_CheckReload();
A_WeaponReady();
}
Wait;
Fire:
WARF A 4 A_WarheadFire();
WARF BCDEFG 4 A_WarheadSmoke();
WARI A 5;
Goto Idle;
AltFire:
WARF A 4 A_WarheadAlt();
WARF BCDEFG 4 A_WarheadSmoke();
WARI A 1 A_JumpIf(!invoker.Guided,1);
Wait;
WARI A 30;
Goto Idle;
Select:
WARS A 1 A_Raise(int.max);
Wait;
Deselect:
WARD ABCDEFG 1;
WARD G 1 A_Lower(int.max);
Wait;
}
}