flak_m/zscript/warheadlauncher.zsc
Marisa Kirisame 5248ac8fd6 A couple more things, including an ambient glow shader effect, some more item replacements, and menu options.
Damage Amplifier has been implemented. Armor items are being worked on. Powerups will come later, then I'll continue doing more weapons.
2018-05-20 02:10:30 +02:00

612 lines
15 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;
}
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 RedeemerHUD : HUDMessageBase
{
Actor Camera;
Vector3 ViewPos;
double ViewAngle, ViewPitch, ViewRoll;
TextureID reticle, mark, readout;
Font whfont;
ThinkerIterator t;
MidTracer tr;
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()
{
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() )
{
t.Reinit();
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;
Vector2 vpos = mkCoordUtil.ToViewport(spos);
Screen.DrawTexture(mark,false,vpos.x,vpos.y);
String diststr = String.Format("%f",tdir.length());
diststr.Replace(".","");
Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(vpos.x-whfont.StringWidth(diststr)/2)-12,vpos.y+8,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();
A_AlertMonsters();
A_QuakeEx(6,6,6,20,0,100,"",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();
A_AlertMonsters();
A_QuakeEx(6,6,6,20,0,100,"",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.AmmoType "WarheadAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "WarheadAmmo";
Weapon.AmmoUse2 1;
Weapon.AmmoGive 1;
}
States
{
Spawn:
RDMP A -1;
Stop;
RDMP B -1;
Stop;
Ready:
WARS ABCDEFGHIJKLMNO 1;
Idle:
WARI A 1
{
A_CheckReload();
A_WeaponReady();
}
Wait;
Fire:
WARI A 3;
WARF A 3 A_WarheadFire();
WARF BCDEFG 3 A_WarheadSmoke();
WARI A 5;
Goto Idle;
AltFire:
WARI A 3;
WARF A 3 A_WarheadAlt();
WARF BCDEFG 3 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;
}
}