flak_m/zscript/chainsaw.zsc
Marisa Kirisame fb96c7523e Portal awareness adjustments to various vector operations.
Got rid of the deprecated Matrix4.GetAxes method. Next step is to get rid of more stuff by migrating to libeye.
Mirrored Translocator model so it shows the actually detailed side. At some point in UT's development it got flipped around for some reason.
Weapon code cleanup (most noticeable on states).
Backported scope shader from Doomreal.
Added optional "dummied out" Sniper zoom sounds from a dubious source.
2019-09-28 20:06:51 +02:00

369 lines
9.8 KiB
Text

Class SawImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_SprayDecal("WallCrack",20);
int numpt = Random[Chainsaw](5,10);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (-x+(FRandom[Chainsaw](-.8,.8),FRandom[Chainsaw](-.8,.8),FRandom[Chainsaw](-.8,.8))).unit()*FRandom[Chainsaw](0.1,1.2);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Chainsaw](128,192));
}
numpt = Random[Chainsaw](4,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1)).unit()*FRandom[Chainsaw](2,8);
let s = Spawn("UTSpark",pos);
s.vel = pvel;
}
numpt = Random[Chainsaw](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1)).unit()*FRandom[Chainsaw](2,8);
let s = Spawn("UTChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class ChainsawAmmo : Ammo
{
Default
{
Tag "$T_CHAINSAWAMMO";
Inventory.PickupMessage "$I_CHAINSAWAMMO";
Inventory.Amount 20;
Inventory.MaxAmount 100;
Ammo.BackpackAmount 50;
Ammo.BackpackMaxAmount 100;
Ammo.DropAmount 20;
}
States
{
Spawn:
CSAM A -1;
Stop;
}
}
Class UTChainsaw : UTWeapon
{
double sawcnt;
double ammocharge;
override void DoEffect()
{
Super.Tick();
if ( flak_sawammo )
{
AmmoType1 = "ChainsawAmmo";
if ( !Ammo1 ) Ammo1 = NonIdioticAddAmmo(Owner,AmmoType1,GetDefaultByType(AmmoType1).MaxAmount);
if ( (Owner.player.ReadyWeapon != self) || (Ammo1.Amount <= 0) )
return;
if ( ammocharge >= 1. )
{
Ammo1.Amount = max(Ammo1.Amount-int(ammocharge),0);
ammocharge = 0.;
}
else ammocharge = ammocharge+1./TICRATE;
return;
}
if ( Ammo1 ) Ammo1.Destroy();
if ( AmmoType1 ) AmmoType1 = null;
}
protected bool PickupForSawAmmo( Weapon ownedWeapon )
{
bool gotstuff = false;
// Don't take ammo if the weapon sticks around.
if ( !ShouldStay() )
{
int oldamount = 0;
if ( ownedWeapon.Ammo1 )
{
oldamount = ownedWeapon.Ammo1.Amount;
gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,ownedWeapon.Ammo1.default.MaxAmount);
}
let Owner = ownedWeapon.Owner;
if ( gotstuff && Owner && Owner.player )
{
if ( ownedWeapon.Ammo1 && !oldamount )
PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass());
}
}
return gotstuff;
}
override bool HandlePickup (Inventory item)
{
if ( item.GetClass() == GetClass() )
{
if ( UTChainsaw(item).PickupForSawAmmo(self) )
item.bPickupGood = true;
if ( MaxAmount > 1 ) return Inventory.HandlePickup(item);
return true;
}
return false;
}
action void A_SawHit()
{
invoker.ammocharge += 10./TICRATE;
A_QuakeEx(2,2,2,2,0,1,"",QF_RELATIVE,rollIntensity:0.15);
UTMainHandler.DoSwing(self,(FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1)),0.6,-0.2,2,SWING_Spring);
invoker.sawcnt += 1./TICRATE;
if ( invoker.sawcnt > 0.15 )
{
invoker.sawcnt = 0;
invoker.FireEffect();
A_AlertMonsters();
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-4*z);
FLineTraceData d;
LineTrace(angle,90,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
if ( d.HitType == TRACE_HitActor )
{
int dmg = 20;
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'slashed',DMG_USEANGLE,atan2(d.HitDir.y,d.HitDir.x));
d.HitActor.vel -= x*(500/d.HitActor.mass);
vel += x*(100/mass);
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(5,5,5,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25);
if ( d.HitActor.bNOBLOOD )
{
let p = Spawn("SawImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(d.HitDir.y,d.HitDir.x);
p.pitch = asin(-d.HitDir.z);
}
else
{
d.HitActor.TraceBleed(dmg,invoker);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
}
}
else if ( d.HitType != TRACE_HitNone )
{
let p = Spawn("SawImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(d.HitDir.y,d.HitDir.x);
p.pitch = asin(-d.HitDir.z);
if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
}
}
bool quitout = false;
if ( invoker.Ammo1 && (invoker.Ammo1.Amount <= 0) )
{
A_StopSound(CHAN_6);
A_PlaySound("chainsaw/lower",CHAN_WEAPON);
quitout = true;
}
else if ( !(player.cmd.buttons&BT_ATTACK) ) quitout = true;
if ( quitout ) player.SetPSprite(PSP_WEAPON,ResolveState("Release"));
}
action void A_SawSwipe( bool initial = false )
{
invoker.ammocharge += 20./TICRATE;
A_QuakeEx(2,2,2,3,0,1,"",QF_RELATIVE,rollIntensity:0.15);
UTMainHandler.DoSwing(self,(FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1)),0.6,-0.2,2,SWING_Spring);
if ( initial ) invoker.FireEffect();
A_AlertMonsters();
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*z);
FLineTraceData d;
double ang = (angle-60)+120*invoker.sawcnt;
LineTrace(ang,90,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
if ( d.HitType == TRACE_HitActor )
{
int dmg = 20;
if ( d.HitLocation.z >= (d.HitActor.pos.z+d.HitActor.height*0.8) )
dmg = d.HitActor.DamageMobj(invoker,self,dmg*3,'Decapitated',DMG_USEANGLE,atan2(d.HitDir.y,d.HitDir.x));
else dmg = d.HitActor.DamageMobj(invoker,self,dmg,'slashed',DMG_USEANGLE,atan2(d.HitDir.y,d.HitDir.x));
d.HitActor.vel = -y*(1200/d.HitActor.mass);
vel += x*(100/mass);
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(5,5,5,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25);
if ( d.HitActor.bNOBLOOD )
{
let p = Spawn("SawImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(d.HitDir.y,d.HitDir.x);
p.pitch = asin(-d.HitDir.z);
}
else
{
d.HitActor.TraceBleed(dmg,invoker);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
}
}
else if ( d.HitType != TRACE_HitNone )
{
let p = Spawn("SawImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(d.HitDir.y,d.HitDir.x);
p.pitch = asin(-d.HitDir.z);
if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
}
invoker.sawcnt += 0.1;
}
override void DetachFromOwner()
{
if ( Owner ) Owner.A_StopSound(CHAN_6);
A_PlaySound("chainsaw/lower",CHAN_6);
Super.DetachFromOwner();
}
action void A_Vibrate( bool bAlt = false )
{
invoker.sawcnt = 0;
A_AlertMonsters();
if ( bAlt )
{
invoker.ammocharge += 40./TICRATE;
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE,rollIntensity:0.4);
}
else A_QuakeEx(0,0,0,2,0,1,"",QF_RELATIVE,rollIntensity:0.2);
UTMainHandler.DoSwing(self,(FRandom[Chainsaw](-1,1),FRandom[Chainsaw](-1,1)),0.25,-0.1,2,SWING_Spring);
if ( bAlt || Random[Chainsaw](0,2) ) return;
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),5*x+y-3*z);
for ( int i=0; i<5; i++ )
{
let s = Spawn("UTViewSmoke",origin);
UTViewSmoke(s).ofs = (5,1,-3);
s.scale *= 1.2;
s.alpha *= 0.2;
s.SetShade("202020");
s.target = self;
UTViewSmoke(s).vvel += (0,-0.2,0);
}
}
Default
{
Tag "$T_CHAINSAW";
Obituary "$O_CHAINSAW";
Inventory.PickupMessage "$I_CHAINSAW";
Weapon.UpSound "chainsaw/select";
Weapon.SlotNumber 1;
Weapon.SelectionOrder 9;
+WEAPON.MELEEWEAPON;
+FORCEPAIN;
+NOEXTREMEDEATH;
}
States
{
Spawn:
CSWP A -1;
Stop;
CSWP B -1;
Stop;
Select:
CSWS A 1 A_Raise(int.max);
Wait;
Ready:
CSWS A 0
{
if ( invoker.Ammo1 && (invoker.Ammo1.Amount <= 0) )
A_StopSound(CHAN_WEAPON);
}
CSWS ABCDEFGHIJLMNO 1
{
if ( !invoker.Ammo1 || (invoker.Ammo1.Amount > 0) )
A_Vibrate();
A_WeaponReady(WRF_NOFIRE);
}
Idle:
CSWI A 0
{
if ( !invoker.Ammo1 || (invoker.Ammo1.Amount > 0) )
A_PlaySound("chainsaw/idle",CHAN_6,looping:true);
else
return ResolveState("DryIdle");
return ResolveState(null);
}
CSWI ABCDEFGHIJ 1
{
A_Vibrate();
A_WeaponReady();
if ( invoker.Ammo1 && (invoker.Ammo1.Amount <= 0) )
{
A_StopSound(CHAN_6);
A_PlaySound("chainsaw/lower",CHAN_WEAPON);
return ResolveState("DryIdle");
}
return ResolveState(null);
}
Goto Idle+1;
DryIdle:
CSWI C 1
{
A_WeaponReady(WRF_NOFIRE);
if ( !invoker.Ammo1 || (invoker.Ammo1.Amount > 0) )
{
A_PlaySound("chainsaw/select",CHAN_WEAPON);
return ResolveState("Idle");
}
else if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) )
PlayerPawn(invoker.Owner).PickNewWeapon(null);
return ResolveState(null);
}
Wait;
Fire:
CSWJ A 1 A_PlaySound("chainsaw/fire",CHAN_6,looping:true);
CSWJ BCDEF 1 A_Vibrate();
Goto Hold;
Hold:
CSWJ GHIJKLMNOPQRS 1 A_SawHit();
Loop;
Release:
CSWJ FEDCBA 1 A_Vibrate();
Goto Idle;
AltFire:
CSWA A 0
{
A_PlaySound("chainsaw/fire",CHAN_6);
UTMainHandler.DoSwing(self,(FRandom[Chainsaw](-0.8,-1.2),FRandom[Chainsaw](-0.4,-0.7)),0,1,5,SWING_Spring,6,3);
}
CSWA ABCDE 2 A_Vibrate(true);
CSWA F 2
{
A_Overlay(PSP_WEAPON+1,"AltFireSwipes");
UTMainHandler.DoSwing(self,(FRandom[Chainsaw](0.8,1.2),0),0,2,6,SWING_Spring,1,3);
}
CSWA GHIJ 2;
CSWA K 2 A_Vibrate(true);
CSWA K 0
{
if ( invoker.Ammo1 && (invoker.Ammo1.Amount <= 0) )
{
A_StopSound(CHAN_6);
A_PlaySound("chainsaw/lower",CHAN_WEAPON);
}
else A_PlaySound("chainsaw/idle",CHAN_6,looping:true);
}
Goto Ready;
AltFireSwipes:
TNT1 A 1 A_SawSwipe(true);
TNT1 AAAAAAAAA 1 A_SawSwipe();
Stop;
Deselect:
CSWD A 0
{
if ( !invoker.Ammo1 || (invoker.Ammo1.Amount > 0) )
A_PlaySound("chainsaw/lower",CHAN_6);
}
CSWD ABCDEF 1;
CSWD F 1 A_Lower(int.max);
Wait;
}
}