Added water step/land sounds. Made footsteps toggleable (in case the player is using another mod that adds them). Added handling of liquid terrain on all actors that need it. Fixed dual enforcers breaking after level transition. Fixed some refire issues caused by the use of the Resurrect cheat. Made guided redeemer missiles explode on player death. Replaced pure particles on pulse gun effects with actor particles.
702 lines
16 KiB
Text
702 lines
16 KiB
Text
// shouldn't be placed in the world (it wasn't in UT99)
|
|
Class WarheadAmmo : Ammo
|
|
{
|
|
Default
|
|
{
|
|
Tag "Redeemer Missile";
|
|
Inventory.PickupMessage "You picked up a Redeemer Missile.";
|
|
Inventory.Amount 1;
|
|
Inventory.MaxAmount 2;
|
|
Ammo.BackpackAmount 0;
|
|
Ammo.BackpackMaxAmount 2;
|
|
Ammo.DropAmount 1;
|
|
Inventory.RespawnTics 2100;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
WMIS A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class ShockWave : Actor
|
|
{
|
|
double shocksize, olddmgradius;
|
|
double lifespan;
|
|
int icount;
|
|
ThinkerIterator t;
|
|
Default
|
|
{
|
|
Obituary "%o was vaporized by %k's Redeemer!!";
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
Scale 1.0;
|
|
ReactionTime 60;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
}
|
|
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,rollIntensity:0.5);
|
|
t = ThinkerIterator.Create("Actor");
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( globalfreeze || level.frozen ) return;
|
|
if ( alpha <= 0 ) return;
|
|
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) || (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) || (dir dot a.vel < 0) )
|
|
{
|
|
a.vel += dir*(moscale/a.mass+20);
|
|
a.DamageMobj(self,target,moscale,'RedeemerDeath',DMG_THRUSTLESS);
|
|
}
|
|
}
|
|
olddmgradius = dmgradius;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
RWAV A 100 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class WarheadSubExplosion : Actor
|
|
{
|
|
Default
|
|
{
|
|
Renderstyle "Add";
|
|
Scale 2.8;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
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;
|
|
}
|
|
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.pos-(0,0,height*0.5),true);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 10 A_AlertMonsters(0,AMF_TARGETEMITTER);
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class WarheadExplodLight : DynamicLight
|
|
{
|
|
double lifetime;
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
ReactionTime 50;
|
|
Args 255,192,128,300;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
lifetime = 1.0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( globalfreeze || level.frozen ) return;
|
|
args[LIGHT_RED] = 255*lifetime;
|
|
args[LIGHT_GREEN] = 192*lifetime;
|
|
args[LIGHT_BLUE] = 128*lifetime;
|
|
lifetime -= 1./ReactionTime;
|
|
if ( lifetime <= 0 ) Destroy();
|
|
}
|
|
}
|
|
|
|
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(target.Vec3Offset(taildir.x*20,taildir.y*20,taildir.z*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;
|
|
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 ( level.frozen || globalfreeze ) 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 was vaporized by %k's Redeemer!!";
|
|
Radius 2;
|
|
Height 2;
|
|
Speed 2;
|
|
DamageType 'RedeemerDeath';
|
|
DamageFactor 1000;
|
|
PROJECTILE;
|
|
+FORCEXYBILLBOARD;
|
|
+SKYEXPLODE;
|
|
+FORCERADIUSDMG;
|
|
+EXPLODEONWATER;
|
|
}
|
|
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 ( globalfreeze || level.frozen ) return;
|
|
if ( !bMISSILE ) return;
|
|
if ( vel.length() > 0 )
|
|
{
|
|
if ( waterlevel > 0 )
|
|
{
|
|
vel *= 0.98;
|
|
if ( vel.length() < 5 ) vel += vel.unit()*0.5;
|
|
}
|
|
else if ( vel.length() < 10 ) vel += vel.unit()*0.5;
|
|
}
|
|
}
|
|
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, lagangle2, lagpitch2;
|
|
double guideangle, guidepitch, lastguideroll;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
if ( target && target.player ) target.player.camera = self;
|
|
guideangle = angle;
|
|
guidepitch = pitch;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Actor.Tick();
|
|
if ( globalfreeze || level.frozen ) 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.;
|
|
guideangle += lagangle2*0.95+lagangle*0.05;
|
|
guidepitch += lagpitch2*0.95+lagpitch*0.05;
|
|
guidepitch = Clamp(guidepitch,-89,89);
|
|
double guideroll = -lagangle2*15;
|
|
Vector3 dir = (cos(guideangle)*cos(guidepitch),sin(guideangle)*cos(guidepitch),-sin(guidepitch));
|
|
destangle = atan2(dir.y,dir.x);
|
|
destpitch = asin(-dir.z);
|
|
double destroll = lastguideroll*0.9+guideroll*0.1;
|
|
A_SetAngle(destangle,SPF_INTERPOLATE);
|
|
A_SetPitch(destpitch,SPF_INTERPOLATE);
|
|
A_SetRoll(destroll,SPF_INTERPOLATE);
|
|
vel = vel.length()*(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
if ( waterlevel > 0 )
|
|
{
|
|
vel *= 0.98;
|
|
if ( vel.length() < 5 ) vel += vel.unit()*0.5;
|
|
}
|
|
else if ( vel.length() < 10 ) vel += vel.unit()*0.5;
|
|
}
|
|
lagangle2 = lagangle2*0.95+lagangle*0.05;
|
|
lagpitch2 = lagpitch2*0.95+lagpitch*0.05;
|
|
lastguideroll = roll*0.98;
|
|
}
|
|
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;
|
|
TextureID reticle, mark, readout;
|
|
Font whfont;
|
|
ThinkerIterator t;
|
|
MidTracer tr;
|
|
Array<TargetActor> ta;
|
|
|
|
RedeemerHUD Init()
|
|
{
|
|
reticle = TexMan.CheckForTexture("GuidedX",TexMan.Type_Any);
|
|
mark = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
|
|
readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any);
|
|
whfont = Font.GetFont('WHFONT');
|
|
t = ThinkerIterator.Create("Actor");
|
|
tr = new("MidTracer");
|
|
return self;
|
|
}
|
|
override bool Tick()
|
|
{
|
|
// shootable targetting
|
|
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() )
|
|
{
|
|
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 = mkCoordUtil.WorldToScreen(wpos,ViewPos,ViewPitch,ViewAngle,ViewRoll,players[consoleplayer].FOV);
|
|
if ( spos.z > 1.0 ) continue;
|
|
TargetActor te = new("TargetActor");
|
|
te.vpos = mkCoordUtil.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;
|
|
Screen.Dim("Red",0.5,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
// shootable targetting
|
|
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() )
|
|
{
|
|
for ( int i=0; i<ta.Size(); i++ )
|
|
{
|
|
Screen.DrawTexture(mark,false,ta[i].vpos.x,ta[i].vpos.y,DTA_RenderStyle,(1|2<<8|1<<16));
|
|
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_RenderStyle,(1|2<<8|1<<16));
|
|
}
|
|
}
|
|
// other stuff
|
|
Screen.DrawTexture(reticle,false,320,240,DTA_VirtualWidth,640,DTA_VirtualHeight,480,DTA_RenderStyle,(1|2<<8|1<<16));
|
|
int numreadouts = Screen.GetHeight()/128+2;
|
|
for ( int i=0; i<numreadouts; i++ )
|
|
{
|
|
int scroll = (gametic*5)%128;
|
|
Screen.DrawTexture(readout,false,0,i*128-scroll,DTA_RenderStyle,(1|2<<8|1<<16));
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
override void RenderOverlay( RenderEvent e )
|
|
{
|
|
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;
|
|
}
|
|
else if ( rhud )
|
|
{
|
|
StatusBar.DetachMessage(rhud);
|
|
rhud.Destroy();
|
|
StatusBar.AttachMessage(new("RedeemerHUDStatic").Init(),0,StatusBar.HUDMSGLayer_UnderHUD);
|
|
}
|
|
}
|
|
}
|
|
|
|
Class WarheadLauncher : UTWeapon replaces BFG9000
|
|
{
|
|
Actor guided;
|
|
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);
|
|
A_AlertMonsters();
|
|
A_QuakeEx(6,6,6,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.2);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
|
|
vel -= x*10;
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+2.0*y-2.0*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] = Matrix4.GetAxes(pitch,angle,roll);
|
|
vel -= x*0.2;
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+2.0*y-2.0*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] = Matrix4.GetAxes(pitch,angle,roll);
|
|
vel -= x*10;
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+2.0*y-2.0*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 "Redeemer";
|
|
Inventory.PickupMessage "You got the 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;
|
|
+WEAPON.NOAUTOFIRE;
|
|
}
|
|
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;
|
|
}
|
|
}
|