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= 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_Utility.GetPlayerAxes(self); Vector3 origin = dt_Utility.GetFireOffset(self,10,0,-4); 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_Utility.GetPlayerAxes(self); Vector3 origin = dt_Utility.GetFireOffset(self,10,0,-2); 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 origin = dt_Utility.GetFireOffset(self,5,1,-3); 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; UTWeapon.BobDamping .4; +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; } }