flak_m/zscript/warheadlauncher.zsc
Marisa Kirisame 72eb6ca8d1 Impact Hammer fully implemented. Began working on Chainsaw.
Tweaked the shake effect on all weapons.
Covered up some seams on redeemer texture. Moved the model slightly to cover up a gap.
Corrected the chainsaw and translocator HUD icons.
2018-05-26 00:10:31 +02:00

634 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');
}
}
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 8;
Height 8;
+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 WarShell : Actor
{
double destangle, destpitch;
Actor l, b;
Default
{
Obituary "%o was vaporized by %k's Redeemer!!";
Radius 4;
Height 4;
Speed 2;
DamageType 'RedeemerDeath';
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<8; i++ )
A_SpawnParticle("6060FF",0,Random[Warhead](10,30),FRandom[Warhead](2,4),0,taildir.x*32,taildir.y*32,taildir.z*32,taildir.x*2+FRandom[Warhead](-.5,.5),taildir.y*2+FRandom[Warhead](-.5,.5),taildir.z*2+FRandom[Warhead](-.5,.5),accelz:0.2,fadestepf:0);
return;
}
for ( int i=0; i<8; i++ )
A_SpawnParticle("404040",0,20,2,0,taildir.x*32,taildir.y*32,taildir.z*32,taildir.x*2+FRandom[Warhead](-.5,.5),taildir.y*2+FRandom[Warhead](-.5,.5),taildir.z*2+FRandom[Warhead](-.5,.5),accelz:0.1,sizestep:1);
for ( int i=0; i<8; i++ )
A_SpawnParticle("FFA020",SPF_FULLBRIGHT,10,6,0,taildir.x*35+FRandom[Warhead](-1,1),taildir.y*35+FRandom[Warhead](-1,1),taildir.z*35+FRandom[Warhead](-1,1),taildir.x*4+FRandom[Warhead](-.25,.25),taildir.y*4+FRandom[Warhead](-.25,.25),taildir.z*4+FRandom[Warhead](-.25,.25));
}
action void A_Vaporize()
{
if ( invoker.l ) invoker.l.Destroy();
if ( invoker.b ) invoker.b.Destroy();
A_SetScale(2.0);
A_Explode(1000,300);
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,350);
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);
A_SpawnParticle(Color(1,1,1)*Random[Warhead](32,128),0,Random[Warhead](40,50),FRandom[Warhead](4,12),0,origin.x-pos.x,origin.y-pos.y,origin.z-pos.z,pvel.x,pvel.y,pvel.z,0,0,0.03,0.5,-1,0.5);
}
}
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;
}
States
{
Spawn:
RDMP A -1;
Stop;
RDMP B -1;
Stop;
Ready:
WARS ABCDEFGHIJKLMNO 1;
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;
}
}