Reduced spread of grenades. Increased size of rocket trail (looks a bit closer to UT now). Increased radius of single bullet pickup, might have been too small for collision to work properly.
694 lines
16 KiB
Text
694 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;
|
|
Radius 2;
|
|
Height 42;
|
|
}
|
|
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;
|
|
}
|
|
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);
|
|
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);
|
|
}
|
|
}
|
|
// other stuff
|
|
Screen.DrawTexture(reticle,false,320,240,DTA_VirtualWidth,640,DTA_VirtualHeight,480);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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;
|
|
Radius 24;
|
|
Height 16;
|
|
UTWeapon.DroppedHeight 16;
|
|
}
|
|
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 2;
|
|
WARD G 1 A_Lower(int.max);
|
|
Wait;
|
|
}
|
|
}
|