Class ShockAmmo : Ammo { Default { Tag "ShockCore"; Inventory.PickupMessage "You picked up a Shock Core."; Inventory.PickupSound "ut/ammo"; Inventory.Amount 10; Inventory.MaxAmount 50; Ammo.BackpackAmount 50; Ammo.BackpackMaxAmount 100; Ammo.DropAmount 10; } States { Spawn: SHOA A -1; Stop; } } Class ShockBeamTracer : LineTracer { Actor owner, ignore; override ETraceStatus TraceCallback() { if ( Results.HitType == TRACE_HitActor ) { if ( (Results.HitActor == owner) || (Results.HitActor == ignore) ) return TRACE_Skip; if ( Results.HitActor.bSHOOTABLE || Results.HitActor is 'ShockHitbox' ) return TRACE_Stop; return TRACE_Skip; } else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) { if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) ) return TRACE_Stop; return TRACE_Skip; } return TRACE_Stop; } } Class ShockRifleWave : Actor { Default { RenderStyle "Add"; Radius 0.1; Height 0; Scale 0.7; +NOBLOCKMAP; +NOGRAVITY; +DONTSPLASH; } override void Tick() { Super.Tick(); alpha -= 1/50.; } States { Spawn: SWAV A 50 Bright; SWAV B 0 Bright; Stop; } } Class ShockBeamRing : Actor { Default { RenderStyle "Add"; Radius 0.1; Height 0; Scale 0.5; ReactionTime 18; +NOBLOCKMAP; +NOGRAVITY; +DONTSPLASH; } override void Tick() { Super.Tick(); alpha -= 1./ReactionTime; } States { Spawn: SRNG ABCDEFGHI 2 Bright; Stop; } } Class ShockBlastRing : ShockBeamRing { Default { Scale 3.0; ReactionTime 45; } States { Spawn: SRNG ABCDEFGHI 5 Bright; Stop; } } Class ShockBeam : Actor { ShockBeamTracer t; Vector3 tracedir; bool moving; double totaldist; Default { Obituary "%k inflicted mortal damage upon %o with the Shock Rifle"; RenderStyle "Add"; Radius 0.1; Height 0; Scale 0.4; DamageFunction Random[ASMD](30,45); +NOGRAVITY; +NOCLIP; +DONTSPLASH; +FORCEXYBILLBOARD; } override void PostBeginPlay() { Super.PostBeginPlay(); t = new("ShockBeamTracer"); t.owner = target; t.ignore = self; moving = true; } override void Tick() { Super.Tick(); if ( !moving ) return; // step trace tracedir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); t.Trace(pos,cursector,tracedir,1000,0); totaldist += t.Results.Distance; // spawn particles for ( int i=0; i= 10000.0 ) { // reposition and explode on air SetOrigin(t.Results.HitPos-t.Results.HitVector*4,true); A_SprayDecal("ShockMark",16); ExplodeMissile(t.Results.HitLine,null); moving = false; let r = Spawn("ShockBeamRing",pos); r.angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x); r.pitch = asin(-t.Results.HitVector.z); } else if ( t.Results.HitType == TRACE_HitNone ) { // reposition SetOrigin(t.Results.HitPos+t.Results.HitVector,true); angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x); pitch = asin(-t.Results.HitVector.z); } else if ( t.Results.HitType == TRACE_HitActor ) { // reposition and explode on actor SetOrigin(t.Results.HitPos-t.Results.HitVector*4,true); ExplodeMissile(null,t.Results.HitActor); if ( t.Results.HitActor is 'ShockHitbox' ) { if ( target ) target.TakeInventory('ShockAmmo',2); let b = t.Results.HitActor.target; b.ExplodeMissile(null,self); b.A_Explode(Random[ASMD](150,160),250); b.A_QuakeEx(6,6,6,60,0,1200,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.2); b.A_SprayDecal("BigShockMark1",100); b.A_SprayDecal("BigShockMark2",100); Spawn("ShockRifleWave",b.pos); Spawn("ShockBlastLight",b.pos); let r = Spawn("ShockBlastRing",b.pos); r.angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x); r.pitch = asin(-t.Results.HitVector.z); A_PlaySound("shock/blast",CHAN_WEAPON,attenuation:0.5); } else { t.Results.HitActor.DamageMobj(self,target,Random[ASMD](35,50),'jolted'); let r = Spawn("ShockBeamRing",pos); r.angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x); r.pitch = asin(-t.Results.HitVector.z); } moving = false; } else { // reposition and explode on wall SetOrigin(t.Results.HitPos-t.Results.HitVector*4,true); A_SprayDecal("ShockMark",16); ExplodeMissile(t.Results.HitLine,null); moving = false; Vector3 HitNormal = t.Results.HitVector; if ( t.Results.HitType == TRACE_HitWall ) { t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,pos); // calculate normal HitNormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit(); if ( t.Results.Side == 1 ) HitNormal *= -1; } else if ( t.Results.HitType == TRACE_HitFloor ) HitNormal = t.Results.HitSector.floorplane.Normal; else if ( t.Results.HitType == TRACE_HitCeiling ) HitNormal = t.Results.HitSector.ceilingplane.Normal; let r = Spawn("ShockBeamRing",pos); r.angle = atan2(HitNormal.y,HitNormal.x); r.pitch = asin(-HitNormal.z); } } action void A_BeamExplode() { Spawn("ShockBeamLight",pos); A_QuakeEx(2,2,2,5,0,120,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1); A_PlaySound("shock/hit",CHAN_VOICE); } States { Spawn: TNT1 A 1; Wait; Death: TNT1 A 0 A_BeamExplode(); AEXP ABCDEFGHIJKL 1 Bright; TNT1 A 100; Stop; } } Class ShockBallLight : DynamicLight { Default { DynamicLight.Type "Point"; Args 160,128,255,120; } override void PostBeginPlay() { Super.PostBeginPlay(); } override void Tick() { Super.Tick(); if ( !target || target.InStateSequence(target.CurState,target.ResolveState("Death")) ) { Destroy(); return; } SetOrigin(target.pos,true); args[LIGHT_INTENSITY] = Random[ASMD](10,14)*10; } } Class ShockBeamLight : ShockExplLight { Default { ReactionTime 15; Args 0,0,0,80; } } Class ShockBeamHitLight : ShockExplLight { Default { ReactionTime 24; Args 0,0,0,120; } } Class ShockBlastLight : ShockExplLight { Default { ReactionTime 50; Args 0,0,0,300; } } Class ShockExplLight : DynamicLight { double lifetime; Default { DynamicLight.Type "Point"; ReactionTime 30; Args 160,128,255,150; } override void PostBeginPlay() { Super.PostBeginPlay(); lifetime = 1.0; } override void Tick() { Super.Tick(); args[LIGHT_RED] = 160*lifetime; args[LIGHT_GREEN] = 128*lifetime; args[LIGHT_BLUE] = 255*lifetime; lifetime -= 1./ReactionTime; if ( lifetime <= 0 ) Destroy(); } } Class ShockHitbox : Actor { Default { Radius 16; Height 16; +NOGRAVITY; +NOCLIP; +DONTSPLASH; } override void Tick() { Super.Tick(); if ( !target || target.InStateSequence(target.CurState,target.ResolveState("Death")) ) { Destroy(); return; } SetOrigin(target.pos-(0,0,height*0.5),true); } } Class ShockBall : Actor { Actor l, b; override void PostBeginPlay() { Super.PostBeginPlay(); l = Spawn("ShockBallLight",pos); l.target = self; b = Spawn("ShockHitbox",pos); b.target = self; } action void A_BallExplode() { A_Explode(Random[ASMD](50,60),70); A_SprayDecal("ShockMarkBig",16); Spawn("ShockExplLight",pos); A_SetScale(1.0); let r = Spawn("ShockBeamRing",pos); r.angle = angle; r.pitch = pitch; r.scale *= 1.5; A_PlaySound("shock/hit",CHAN_VOICE); A_PlaySound("shock/ball",CHAN_WEAPON); A_QuakeEx(4,4,4,30,0,200,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.15); } Default { Obituary "%k inflicted mortal damage upon %o with the Shock Rifle"; RenderStyle "Add"; DamageType 'jolted'; Radius 4; Height 4; Scale 0.4; Speed 20; PROJECTILE; +FORCEXYBILLBOARD; +SKYEXPLODE; +FORCERADIUSDMG; } States { Spawn: ABAL ABCD 2 Bright; Loop; Death: TNT1 A 0 A_BallExplode(); AEXP ABCDEFGHIJKL 2 Bright; TNT1 A 300; Stop; } } Class ShockRifle : UTWeapon { action void A_Selecting() { A_PlaySound("shock/select",CHAN_WEAPON); } action void A_ShockFire() { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; A_PlaySound("shock/fire",CHAN_WEAPON); A_QuakeEx(3,3,3,4,0,64,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1); Vector3 x, y, z; double a, s; [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Vector3 origin = pos+(0,0,player.viewheight)+10.0*x+3.0*y-4.0*z; Actor p = Spawn("ShockBeam",origin); p.angle = angle; p.pitch = BulletSlope(); p.target = self; } action void A_ShockAlt() { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; A_PlaySound("shock/altfire",CHAN_WEAPON); A_QuakeEx(3,3,3,8,0,64,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1); Vector3 x, y, z; double a, s; [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Vector3 origin = pos+(0,0,player.viewheight)+10.0*x+3.0*y-4.0*z; Actor p = Spawn("ShockBall",origin); p.angle = angle; p.pitch = BulletSlope(); p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed; p.target = self; } Default { Tag "Shock Rifle"; Inventory.PickupMessage "You got the ASMD Shock Rifle."; Weapon.SlotNumber 4; Weapon.AmmoType "ShockAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "ShockAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 20; } States { Spawn: ASMP A -1; Stop; ASMP B -1; Stop; Ready: ASMS A 1 A_Selecting(); ASMS BCDEFGHIJKLMNO 1; Idle: ASMI A 1 { A_CheckReload(); A_WeaponReady(); } Wait; Fire: ASMF A 1 A_ShockFire(); ASMF BCDEFGHIJ 2; Goto Idle; AltFire: ASMA A 1 A_ShockAlt(); ASMA BCDEFGHIJ 2; Goto Idle; Deselect: ASMD ABCDEFG 1; ASMD G 1 A_Lower(int.max); Wait; Select: ASMS A 1 A_Raise(int.max); Wait; } }