flak_m/zscript/enforcer.zsc
Marisa Kirisame c5a79e45e3 Biorifle sludge now properly follows wall, ceiling and floor movement.
Redeemer shockwaves properly respect actor mass and the DONTTHRUST flag.
All weapons and projectiles have had their knockback adjusted.
2018-06-09 22:53:39 +02:00

564 lines
15 KiB
Text

Class EClip : MiniAmmo replaces Clip
{
Default
{
Tag "Clip"; // "Large Bullets" in UT, but I think that's an oversight, since it's the same as the Minigun ammo
Inventory.PickupMessage "You picked up a Clip.";
Inventory.Amount 20;
Ammo.DropAmount 5;
}
States
{
Spawn:
ECLP A -1;
Stop;
}
}
Class EnforcerLight : DynamicLight
{
int cnt;
Default
{
DynamicLight.Type "Point";
args 255,224,64,150;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
if ( target.player ) SetOrigin(target.Vec2OffsetZ(0,0,target.player.viewz),true);
else SetOrigin(target.pos,true);
if ( cnt++ > 2 ) Destroy();
}
}
Class BulletImpact : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
Scale 0.25;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_SprayDecal("Pock",-20);
int numpt = Random[Enforcer](5,10)*scale.x*4;
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Enforcer](-.8,.8),FRandom[Enforcer](-.8,.8),FRandom[Enforcer](-.8,.8))).unit()*FRandom[Enforcer](0.1,1.2);
let s = Spawn("UTSmoke",pos+x*2);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Enforcer](128,192));
}
numpt = Random[Enforcer](3,8)*scale.x*4;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Enforcer](-1,1),FRandom[Enforcer](-1,1),FRandom[Enforcer](-1,1)).unit()*FRandom[Enforcer](2,8);
let s = Spawn("UTSpark",pos+x*2);
s.vel = pvel;
}
numpt = Random[Enforcer](2,5)*scale.x*4;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Enforcer](-1,1),FRandom[Enforcer](-1,1),FRandom[Enforcer](-1,1)).unit()*FRandom[Enforcer](2,8);
let s = Spawn("UTChip",pos+x*2);
s.vel = pvel;
}
A_PlaySound("bullet/hit",CHAN_VOICE,attenuation:3.0);
}
States
{
Spawn:
BIMP ABCDE 1 Bright;
Stop;
}
}
Class UTCasing : Actor
{
int deadtimer;
double pitchvel, anglevel;
double heat;
Default
{
Radius 2;
Height 2;
+NOBLOCKMAP;
+MISSILE;
+MOVEWITHSECTOR;
+THRUACTORS;
+USEBOUNCESTATE;
BounceType "Doom";
BounceFactor 0.65;
BounceSound "bullet/casing";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
deadtimer = 0;
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
heat = 1.0;
}
override void Tick()
{
Super.Tick();
if ( level.frozen || globalfreeze ) return;
if ( InStateSequence(CurState,ResolveState("Death")) )
{
deadtimer++;
if ( deadtimer > 300 ) A_FadeOut(0.05);
return;
}
heat -= 0.02;
if ( heat <= 0 ) return;
let s = Spawn("UTSmallSmoke",pos);
s.alpha *= heat;
}
States
{
Spawn:
PCAS A 1
{
A_SetAngle(angle+anglevel,SPF_INTERPOLATE);
A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
}
Loop;
Bounce:
PCAS A 0
{
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
vel = (vel.unit()+(FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2))).unit()*vel.length();
}
Goto Spawn;
Death:
PCAS A -1
{
A_SetPitch(0);
A_SetRoll(FRandom[Junk](0,360));
}
Stop;
}
}
Class Enforcer : UTWeapon replaces Pistol
{
int ClipCount, SlaveClipCount;
bool SlaveActive, SlaveDown, SlaveReload, SlaveAltFire;
int SlaveRefire;
property ClipCount : ClipCount;
property SlaveClipCount : SlaveClipCount;
override void PostRender( double lbottom )
{
if ( !CVar.GetCVar('flak_enforcerreload').GetBool() ) return;
if ( Amount > 1 ) Screen.DrawText(confont,Font.CR_GREEN,Screen.GetWidth()*0.01,lbottom-Screen.GetHeight()*0.01-confont.GetHeight()*2,String.Format("L Clip: %2d / 20\nR Clip: %2d / 20",slaveclipcount,clipcount));
else Screen.DrawText(confont,Font.CR_GREEN,Screen.GetWidth()*0.01,lbottom-Screen.GetHeight()*0.01-confont.GetHeight(),String.Format("Clip: %2d / 20",clipcount));
}
override bool HandlePickup( Inventory item )
{
if ( item.GetClass() == GetClass() )
{
SetTag("Dual Enforcers");
return Super.HandlePickup(item);
}
return false;
}
override Inventory CreateTossable( int amt )
{
Inventory inv = Super.CreateTossable(amt);
if ( inv )
{
SetTag("Enforcer");
inv.SetTag("Enforcer");
if ( Owner && (Owner.player.ReadyWeapon == self) )
{
// delete the slave overlay
PSprite psp;
for ( psp = Owner.player.psprites; psp; psp = psp.next )
{
if ( (psp.Caller == self) && (psp.id == 2) ) psp.Destroy();
slaveactive = false;
slavedown = false;
}
}
}
return inv;
}
action void A_EnforcerRefire( statelabel flash = null, bool slave = false )
{
Weapon weap = Weapon(invoker);
if ( !weap || !player ) return;
if ( slave )
{
if ( CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.slaveclipcount < 5) ) A_PlaySound("enforcer/click",CHAN_6);
if ( (invoker.slaveclipcount <= 0) || (weap.Ammo1.Amount <= 0) )
{
invoker.slaverefire = 0;
return;
}
bool pending = (player.PendingWeapon != WP_NOCHANGE) && (player.WeaponState & WF_REFIRESWITCHOK);
if ( (player.cmd.buttons&BT_ATTACK) && !invoker.slavealtfire && !pending && (player.health > 0) )
{
invoker.slaverefire++;
if ( player.ReadyWeapon.CheckAmmo(Weapon.PrimaryFire,true) )
player.setpsprite(2,flash?ResolveState(flash):ResolveState("LeftHold"));
}
else if ( (player.cmd.buttons&BT_ALTATTACK) && invoker.slavealtfire && !pending && (player.health > 0) )
{
invoker.slaverefire++;
if ( player.ReadyWeapon.CheckAmmo(Weapon.AltFire,true) )
player.setpsprite(2,flash?ResolveState(flash):ResolveState("LeftAltHold"));
}
else
{
invoker.slaverefire = 0;
player.ReadyWeapon.CheckAmmo(invoker.slavealtfire?Weapon.AltFire:Weapon.PrimaryFire,true);
}
}
else
{
if ( CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.clipcount < 5) ) A_PlaySound("enforcer/click",CHAN_WEAPON);
if ( (invoker.clipcount <= 0) || (weap.Ammo1.Amount <= 0) )
{
A_ClearRefire();
return;
}
A_Refire(flash);
}
}
action void A_LeftWeaponReady()
{
Weapon weap = Weapon(invoker);
if ( !weap || !player ) return;
if ( player.cmd.buttons&BT_ATTACK && !player.ReadyWeapon.bAltFire )
{
invoker.slaverefire = 0;
invoker.slavealtfire = false;
player.setpsprite(2,ResolveState("LeftFire"));
}
else if ( player.cmd.buttons&BT_ALTATTACK && player.ReadyWeapon.bAltFire )
{
invoker.slaverefire = 0;
invoker.slavealtfire = true;
player.setpsprite(2,ResolveState("LeftAltFire"));
}
}
action void A_EnforcerFire( bool alt = false, bool slave = false )
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
if ( slave )
{
invoker.slaveclipcount--;
if ( !CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.slaveclipcount <=0) ) invoker.slaveclipcount = Min(20,weap.Ammo1.Amount);
}
else
{
invoker.clipcount--;
if ( !CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.clipcount <=0) ) invoker.clipcount = Min(20,weap.Ammo1.Amount);
}
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(32,255,128,0),1);
A_PlaySound("enforcer/shoot",slave?CHAN_6:CHAN_WEAPON);
A_AlertMonsters();
A_QuakeEx(2,2,2,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.08);
if ( slave )
{
if ( alt ) A_Overlay(-3,"LeftAltMuzzleFlash");
else A_Overlay(-3,"LeftMuzzleFlash");
A_OverlayFlags(-3,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
A_OverlayRenderstyle(-3,STYLE_Add);
}
else
{
if ( alt ) A_Overlay(-2,"AltMuzzleFlash");
else A_Overlay(-2,"MuzzleFlash");
A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
A_OverlayRenderstyle(-2,STYLE_Add);
}
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
Vector3 origin = pos+(0,0,player.viewheight)+10.0*x;
int ydir = slave?-1:1;
if ( alt ) origin = origin-z*3.0+ydir*y*1.0;
else origin = origin-z*1.0+ydir*y*4.0;
double a = FRandom[Enforcer](0,360), s = FRandom[Enforcer](0,alt?0.08:0.004);
[x2, y2, z2] = Matrix4.GetAxes(BulletSlope(),angle,roll);
Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
FLineTraceData d;
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
if ( d.HitType == TRACE_HitActor )
{
int dmg = Random[Enforcer](15,20);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD )
{
let p = Spawn("BulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
p.pitch = asin(d.HitDir.z);
}
else
{
d.HitActor.TraceBleed(dmg,self);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
}
}
else if ( d.HitType != TRACE_HitNone )
{
Vector3 hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor ) hitnormal = d.HitSector.floorplane.Normal;
else if ( d.HitType == TRACE_HitCeiling ) hitnormal = d.HitSector.ceilingplane.Normal;
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
let p = Spawn("BulletImpact",d.HitLocation+hitnormal*0.01);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
}
for ( int i=0; i<8; i++ )
{
let s = Spawn("UTViewSmoke",origin);
if ( alt ) UTViewSmoke(s).ofs = (10,ydir,-3);
else UTViewSmoke(s).ofs = (10,4*ydir,-1);
s.target = self;
}
origin += x*8.0+ydir*y*6.0-z*2.0;
let c = Spawn("UTCasing",origin);
c.vel = x*FRandom[Junk](-2,2)+y*ydir*FRandom[Junk](3,6)+z*FRandom[Junk](3,5);
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( Amount > 1 ) return "%k riddled %o full of holes with the Dual Enforcers.";
return "%k riddled %o full of holes with the Enforcer.";
}
override void Travelled()
{
Super.Travelled();
slaveactive = false;
}
override void OwnerDied()
{
Super.OwnerDied();
slaverefire = 0;
}
Default
{
Tag "Enforcer";
Inventory.PickupMessage "You picked up another Enforcer!";
Inventory.MaxAmount 2;
Inventory.InterHubAmount 2;
Weapon.UpSound "enforcer/select";
Weapon.SlotNumber 2;
Weapon.SelectionOrder 8;
Weapon.AmmoType "MiniAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "MiniAmmo";
Weapon.AmmoUse2 1;
Weapon.AmmoGive 30;
Weapon.Kickback 180;
Enforcer.ClipCount 20;
Enforcer.SlaveClipCount 20;
}
States
{
Spawn:
ENFP A -1;
Stop;
ENFP B -1;
Stop;
Select:
ENFS A 1 A_Raise(int.max);
Ready:
ENFS A 0
{
invoker.slavedown = false;
if ( !invoker.slaveactive && (CountInv("Enforcer") > 1) )
A_Overlay(2,"LeftReady");
}
ENFS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOFIRE);
Idle:
ENFI A 0 A_Overlay(-9999,"Dummy");
ENFI AB 30;
ENFI A 0 A_Jump(50,"Twiddle");
Goto Idle+1;
LeftReady:
2NFS A 0
{
A_PlaySound("enforcer/select",CHAN_6);
invoker.slaveactive = true;
}
2NFS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_JumpIf(invoker.slavedown,"LeftDeselect");
LeftIdle:
2NFI A 0 A_Overlay(-9998,"LeftDummy");
2NFI AB 30;
2NFI A 0 A_Jump(50,"LeftTwiddle");
Goto LeftIdle+1;
Twiddle:
ENFT ABCDEFGHIJKLMNOPQRSTUVWXY 2;
Goto Idle+1;
LeftTwiddle:
2NFT ABCDEFGHIJKLMNOPQRSTUVWXY 2;
Goto LeftIdle+1;
Dummy:
TNT1 A 1
{
if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) A_Overlay(PSP_WEAPON,"Reload");
else if ( CVar.GetCVar('flak_enforcerreload').GetBool() && ((invoker.clipcount < min(20,invoker.Ammo1.Amount)) || (invoker.slaveclipcount < min(20,invoker.Ammo1.Amount))) ) A_WeaponReady(WRF_ALLOWRELOAD);
else A_WeaponReady();
if ( !invoker.slaveactive && (CountInv("Enforcer") > 1) ) A_Overlay(2,"LeftReady");
}
Wait;
LeftDummy:
TNT1 A 1
{
if ( health <= 0 )
{
invoker.slaveactive = false;
A_Overlay(2,"LeftDeselect");
}
else if ( CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.slavereload || (invoker.slaveclipcount < 0)) ) A_Overlay(2,"LeftReload");
else if ( invoker.slavedown ) A_Overlay(2,"LeftDeselect");
else A_LeftWeaponReady();
}
Wait;
Fire:
ENFF A 0 A_Overlay(-9999,"Null");
Hold:
ENFF A 0 A_EnforcerFire();
ENFF ABCDEFGHI 1;
ENFF J 3 A_EnforcerRefire();
Goto Idle;
LeftFire:
2NFI A 0 A_Overlay(-9998,"Null");
2NFI A 1 A_SetTics(Random[Enforcer](0,5));
LeftHold:
2NFF A 0 A_EnforcerFire(false,true);
2NFF ABCDEFGHI 1;
2NFF J 3 A_EnforcerRefire("LeftHold",true);
Goto LeftIdle;
AltFire:
ENFA A 0 A_Overlay(-9999,"Null");
ENFA ABCDEF 1;
AltHold:
ENFA G 0 A_EnforcerFire(true);
ENFA GHIJKLMN 1;
ENFA G 0 A_EnforcerRefire();
ENFA OPQRSTU 1;
Goto Idle;
LeftAltFire:
2NFI A 0 A_Overlay(-9998,"Null");
2NFI A 1 A_SetTics(Random[Enforcer](0,5));
2NFI A 0;
2NFA ABCDEF 1;
LeftAltHold:
2NFA G 0 A_EnforcerFire(true,true);
2NFA GHIJKLMN 1;
2NFA G 0 A_EnforcerRefire("LeftAltHold",true);
2NFA OPQRSTU 1;
Goto LeftIdle;
Reload:
ENFR A 0
{
invoker.slavereload = ((player.cmd.buttons&BT_RELOAD)&&(invoker.slaveclipcount < min(20,invoker.Ammo1.Amount)))||(invoker.slaveclipcount <= 0);
return A_JumpIf(invoker.clipcount>=min(20,invoker.Ammo1.Amount),"Idle");
}
ENFR A 0
{
invoker.clipcount = Min(20,invoker.Ammo1.Amount);
A_Overlay(-9999,"Null");
A_PlaySound("enforcer/click",CHAN_WEAPON);
}
ENFR ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
ENR2 AB 1;
ENR2 B 30 A_PlaySound("enforcer/reload",CHAN_WEAPON);
ENFS A 0 A_PlaySound("enforcer/select",CHAN_WEAPON);
Goto Ready;
LeftReload:
2NFR A 0
{
invoker.slaveclipcount = Min(20,invoker.Ammo1.Amount);
invoker.slavereload = false;
A_Overlay(-9998,"Null");
A_PlaySound("enforcer/click",CHAN_6);
}
2NFR ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
2NR2 AB 1;
2NR2 B 30 A_PlaySound("enforcer/reload",CHAN_6);
2NFS A 0 A_PlaySound("enforcer/select",CHAN_6);
Goto LeftReady;
Deselect:
ENFI A 1 { invoker.slavedown = true; }
ENFD A 0 A_Overlay(-9999,"Null");
ENFD A 0 A_JumpIf(invoker.slaveactive,"Deselect");
ENFD ABDEGHJK 1;
ENFD L 1 A_Lower(int.max);
Wait;
LeftDeselect:
2NFD A 0
{
A_Overlay(-9998,"Null");
invoker.slaveactive = false;
}
2NFD ABDEGHJK 1;
2NFD L 0;
Stop;
MuzzleFlash:
EMUZ A 2 Bright
{
let l = Spawn("EnforcerLight",pos);
l.target = self;
}
Stop;
AltMuzzleFlash:
EMUZ B 2 Bright
{
let l = Spawn("EnforcerLight",pos);
l.target = self;
}
Stop;
LeftMuzzleFlash:
EMUZ C 2 Bright
{
let l = Spawn("EnforcerLight",pos);
l.target = self;
}
Stop;
LeftAltMuzzleFlash:
EMUZ D 2 Bright
{
let l = Spawn("EnforcerLight",pos);
l.target = self;
}
Stop;
}
}