Class Bonesaw : UnrealWeapon { override bool TryPickup( in out Actor toucher ) { if ( !sting_proto || !sting_dubious ) return false; // not allowed return Super.TryPickup(toucher); } override void Tick() { Super.Tick(); if ( sting_proto && sting_dubious ) return; if ( !Owner ) { let r = Spawn("Chainsaw",pos,ALLOW_REPLACE); r.spawnangle = spawnangle; r.spawnpoint = spawnpoint; r.angle = angle; r.pitch = pitch; r.roll = roll; r.special = special; r.args[0] = args[0]; r.args[1] = args[1]; r.args[2] = args[2]; r.args[3] = args[3]; r.args[4] = args[4]; r.ChangeTid(tid); r.SpawnFlags = SpawnFlags&~MTF_SECRET; r.HandleSpawnFlags(); r.SpawnFlags = SpawnFlags; r.bCountSecret = SpawnFlags&MTF_SECRET; r.vel = vel; r.master = master; r.target = target; r.tracer = tracer; r.bDropped = bDropped; Destroy(); } else { Owner.RemoveInventory(self); Destroy(); } } override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) { if ( !bAltFire ) return StringTable.Localize((mod=='Decapitated')?"$O_RAZORCLAWDECAP2":"$O_RAZORCLAW2"); return StringTable.Localize((mod=='Decapitated')?"$O_RAZORCLAWDECAP1":"$O_RAZORCLAW1"); } private action bool TryHit( double angle, int dmg ) { FTranslatedLineTarget t; double slope = AimLineAttack(angle,DEFMELEERANGE,t,0.,ALF_CHECK3D); FLineTraceData d; Vector3 x, y, z, origin; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),y*4-z*4); LineTrace(angle,DEFMELEERANGE,slope,TRF_ABSPOSITION,origin.z,origin.x,origin.y,data:d); if ( d.HitType != TRACE_HitNone ) { if ( d.HitType == TRACE_HitActor ) { if ( d.HitLocation.z >= (d.HitActor.pos.z+d.HitActor.height*0.8) ) dmg = d.HitActor.DamageMobj(invoker,self,dmg*2,'Decapitated',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); else dmg = d.HitActor.DamageMobj(invoker,self,dmg,'slashed',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); UTMainHandler.DoKnockback(d.HitActor,d.HitDir,-7000); UTMainHandler.DoKnockback(self,-d.HitDir,-2000); if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25); if ( !d.HitActor.bNOBLOOD ) { d.HitActor.TraceBleed(dmg,invoker); d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); } } else if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4); A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); if ( !d.HitActor || d.HitActor.bNOBLOOD ) { A_PlaySound("ripper/hit",CHAN_6); 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 A_PlaySound("ripper/flesh",CHAN_6); A_AlertMonsters(); if ( invoker.bAltFire ) A_QuakeEx(3,3,3,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.13); else A_QuakeEx(1,1,1,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06); return true; } return false; } action void A_Slice() { UTMainHandler.DoSwing(self,(FRandom[Bonesaw](-1,1),FRandom[Bonesaw](-1,1)),0.3,-0.2,2,SWING_Spring,0,2); invoker.special1++; if ( invoker.special1 < 5 ) return; invoker.special1 = 0; invoker.FireEffect(); for ( int i=0; i<8; i++ ) if ( TryHit(angle+i*(45./16),6) || TryHit(angle-i*(45./16),6) ) return; } action void A_Clamp() { invoker.FireEffect(); for ( int i=0; i<8; i++ ) if ( TryHit(angle+i*(45./16),30) || TryHit(angle-i*(45./16),30) ) return; } Default { Tag "$T_RAZORCLAW"; Inventory.PickupMessage "$I_RAZORCLAW"; Weapon.UpSound "bonesaw/select"; Weapon.SlotNumber 1; Weapon.SelectionOrder 5500; Weapon.SlotPriority 0.9; +WEAPON.MELEEWEAPON; } States { Spawn: CSWP A -1; Stop; CSWP B -1; Stop; Select: CSWS A 1 A_Raise(int.max); Wait; Ready: CSWS ABCDEFGHI 2 A_WeaponReady(WRF_NOFIRE); Goto Idle; Dummy: TNT1 A 1 A_WeaponReady(); Wait; Idle: #### # 2 A_Overlay(-9999,"Dummy"); CSWI ABCDE 15; CSWI A 0 A_Jump(32,"Twiddle"); Goto Idle+1; Twiddle: #### # 2; CSWT ABCDEFGHIJKLM 3; CSWI A 3; Goto Idle; Fire: #### # 2 { A_Overlay(-9999,"Null"); A_PlaySound("bonesaw/spin",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true); invoker.special2 = 0; } CSWF ABCDEFGHIJKLMNO 1 { invoker.special2++; A_WeaponOffset(0+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,32+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,WOF_INTERPOLATE); } CSWF PQRSTUVWX 1 { invoker.special2++; A_WeaponOffset(0+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,32+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,WOF_INTERPOLATE); A_Slice(); } Goto Hold; Hold: CSWH A 0 A_SoundVolume(CHAN_WEAPON,Dampener.Active(self)?.1:1.); CSWH ABCDEFGHIJKLMNOPQR 1 { A_WeaponOffset(0+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,32+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,WOF_INTERPOLATE); A_Slice(); } CSWH A 0 A_Refire("Hold"); Goto Release; Release: CSWR A 0 A_PlaySound("bonesaw/spinend",CHAN_WEAPON,Dampener.Active(self)?.1:1.); CSWR ABCDEFGHI 1 { A_WeaponOffset(0+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,32+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,WOF_INTERPOLATE); invoker.special2--; A_Slice(); } CSWR JKLMNOPQRSTUVWX 1 { A_WeaponOffset(0+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,32+FRandom[Bonesaw](-0.1,0.1)*invoker.special2,WOF_INTERPOLATE); invoker.special2--; } Goto Idle; AltFire: #### # 1 { A_Overlay(-9999,"Null"); A_PlaySound("bonesaw/claw",CHAN_WEAPON,Dampener.Active(self)?.1:1.); UTMainHandler.DoSwing(self,(FRandom[Bonesaw](-0.1,0.1),FRandom[Bonesaw](-0.2,-0.3)),4,0,8,SWING_Spring,5,0.7); } CSWA ABC 2; CSWA D 0 { UTMainHandler.DoSwing(self,(FRandom[Bonesaw](-0.1,0.1),FRandom[Bonesaw](0.8,0.5)),2,0,8,SWING_Spring,2,1.2); } CSWA DEF 1; CSWA G 0 A_Clamp(); CSWA GH 1; CSWA IJLMN 3; CSWI A 3; Goto Idle; Deselect: CSWD A 1 A_Overlay(-9999,"Null"); CSWD ABCDEFGHI 1; CSWD I 1 A_Lower(int.max); Wait; } }