- Changeable player skins. - Ammo LEDs are now 1:1 with UT by using canvas textures. - Integrate some add-ons, including reskins. - Various fixes (some backported from Demolitionist). - Migrated from libeye to Gutamatics.
367 lines
9.5 KiB
Text
367 lines
9.5 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 = dt_Utility.Vec3FromAngle(angle,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 10;
|
|
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);
|
|
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_WEAPONMISC);
|
|
A_StartSound("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);
|
|
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_WEAPONMISC);
|
|
if ( !Ammo1 || (Ammo1.Amount > 0) )
|
|
A_StartSound("chainsaw/lower",CHAN_WEAPONMISC);
|
|
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);
|
|
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_StartSound("chainsaw/idle",CHAN_WEAPONMISC,CHANF_LOOPING);
|
|
else
|
|
return ResolveState("DryIdle");
|
|
return ResolveState(null);
|
|
}
|
|
CSWI ABCDEFGHIJ 1
|
|
{
|
|
A_Vibrate();
|
|
A_WeaponReady();
|
|
if ( invoker.Ammo1 && (invoker.Ammo1.Amount <= 0) )
|
|
{
|
|
A_StopSound(CHAN_WEAPONMISC);
|
|
A_StartSound("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_StartSound("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_StartSound("chainsaw/fire",CHAN_WEAPONMISC,CHANF_LOOPING);
|
|
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_StopSound(CHAN_WEAPONMISC);
|
|
A_StartSound("chainsaw/fire",CHAN_WEAPON);
|
|
}
|
|
CSWA ABCDE 2 A_Vibrate(true);
|
|
CSWA F 2 A_Overlay(PSP_WEAPON+1,"AltFireSwipes");
|
|
CSWA GHIJ 2;
|
|
CSWA K 2 A_Vibrate(true);
|
|
CSWA K 0
|
|
{
|
|
if ( invoker.Ammo1 && (invoker.Ammo1.Amount <= 0) )
|
|
{
|
|
A_StopSound(CHAN_WEAPONMISC);
|
|
A_StartSound("chainsaw/lower",CHAN_WEAPON);
|
|
}
|
|
else
|
|
{
|
|
A_StopSound(CHAN_WEAPON);
|
|
A_StartSound("chainsaw/idle",CHAN_WEAPONMISC,CHANF_LOOPING);
|
|
}
|
|
}
|
|
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_StartSound("chainsaw/lower",CHAN_WEAPONMISC);
|
|
}
|
|
CSWD ABCDEF 1;
|
|
CSWD F 1 A_Lower(int.max);
|
|
Wait;
|
|
}
|
|
}
|