diff --git a/language.version b/language.version index 74783aecb..3b6f4f610 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r446 \cu(Tue 13 Sep 18:12:15 CEST 2022)\c-"; -SWWM_SHORTVER="\cw1.3pre r446 \cu(2022-09-13 18:12:15)\c-"; +SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r447 \cu(Tue 13 Sep 21:25:06 CEST 2022)\c-"; +SWWM_SHORTVER="\cw1.3pre r447 \cu(2022-09-13 21:25:06)\c-"; diff --git a/modeldef.mortalrifle b/modeldef.mortalrifle index 8a75c242f..26405b681 100644 --- a/modeldef.mortalrifle +++ b/modeldef.mortalrifle @@ -1,3 +1,60 @@ +Model "MisterCasing" +{ + Path "models" + + Model 0 "MortalCasing_d.3d" + Skin 0 "MortalRound_Used.png" + Scale 0.03 0.03 0.03 + AngleOffset 90 + USEACTORPITCH + USEACTORROLL + + FrameIndex XZW1 A 0 0 + ZOffset 0.35 + FrameIndex XZW1 B 0 0 + PitchOffset 90 + FrameIndex XZW1 C 0 0 + PitchOffset 180 + FrameIndex XZW1 D 0 0 + PitchOffset 270 + FrameIndex XZW1 E 0 0 +} + +Model "MisterGCasing" +{ + Path "models" + + Model 0 "MortalGrenadeCasing_d.3d" + Skin 0 "MortalGrenade_Used.png" + Scale 0.03 0.03 0.03 + AngleOffset 90 + USEACTORPITCH + USEACTORROLL + + FrameIndex XZW1 A 0 0 + ZOffset 1.1 + FrameIndex XZW1 B 0 0 +} + +Model "MisterMag" +{ + Path "models" + + Model 0 "MortalMag_d.3d" + Scale 0.03 0.03 0.03 + AngleOffset 90 + USEACTORPITCH + USEACTORROLL + + Skin 0 "MortalMag_Empty.png" + FrameIndex XZW1 A 0 0 + ZOffset 0.8 + PitchOffset 90 + FrameIndex XZW1 B 0 0 + PitchOffset -90 + FrameIndex XZW1 C 0 0 +} + Model "MisterRifle" { Path "models" @@ -16,6 +73,38 @@ Model "MisterRifle" FrameIndex XZW1 A 0 0 } +Model "MisterRifle" +{ + Path "models/extra" + + Model 2 "Flat_d.3d" + Scale 0.15 0.15 0.15 + Offset 15 -90 -15 + RollOffset 90 + AngleOffset 90 + + Skin 2 "MRMuz0.png" + FrameIndex XZW0 A 2 0 + Skin 2 "MRMuz1.png" + FrameIndex XZW0 B 2 0 + Skin 2 "MRMuz2.png" + FrameIndex XZW0 C 2 0 + Skin 2 "MRMuz3.png" + FrameIndex XZW0 D 2 0 + + Scale 0.1 0.1 0.1 + Offset 15 -80 -26 + + Skin 2 "MRMuz0.png" + FrameIndex XZW0 E 2 0 + Skin 2 "MRMuz1.png" + FrameIndex XZW0 F 2 0 + Skin 2 "MRMuz2.png" + FrameIndex XZW0 G 2 0 + Skin 2 "MRMuz3.png" + FrameIndex XZW0 H 2 0 +} + Model "MisterRifle" { Path "models" diff --git a/models/extra/MuzTest.png b/models/extra/MuzTest.png new file mode 100644 index 000000000..c93a9b860 Binary files /dev/null and b/models/extra/MuzTest.png differ diff --git a/palettes/Cyanblu.pal b/palettes/Cyanblu.pal new file mode 100644 index 000000000..725b1e350 Binary files /dev/null and b/palettes/Cyanblu.pal differ diff --git a/zscript/dlc1/swwm_mister.zsc b/zscript/dlc1/swwm_mister.zsc index 92b8c29df..e98717bc4 100644 --- a/zscript/dlc1/swwm_mister.zsc +++ b/zscript/dlc1/swwm_mister.zsc @@ -1,14 +1,157 @@ // Plutoni Inc. Mortal Rifle (from UnSX 2) // Slot 9, spawns shared with Candygun +// like the silver bullet tracer except there's no penetration factor +// just a maximum possible travel distance (which is mainly used for probing through walls) +Class MisterRailTracer : LineTracer +{ + Actor ignoreme; + Array hitlist; + Array shootthroughlist; + Array waterhitlist; + + double RealMaxDist; + + bool pastwall, fullstop; + Array WallPenetrateList; + Vector3 exitpoint; + transient Array ffloors; // needs to be done like this because HAHA SCOPE + + override ETraceStatus TraceCallback() + { + // liquid splashes + if ( Results.CrossedWater ) + { + let hl = new("WaterHit"); + hl.sect = Results.CrossedWater; + hl.hitpos = Results.CrossedWaterPos; + WaterHitList.Push(hl); + } + else if ( Results.Crossed3DWater ) + { + let hl = new("WaterHit"); + hl.sect = Results.Crossed3DWater; + hl.hitpos = Results.Crossed3DWaterPos; + WaterHitList.Push(hl); + } + if ( Results.HitType == TRACE_HitActor ) + { + if ( Results.HitActor == ignoreme ) return TRACE_Skip; + if ( Results.HitActor.bSHOOTABLE ) + { + let ent = new("HitListEntry"); + ent.hitactor = Results.HitActor; + ent.hitlocation = Results.HitPos; + ent.x = Results.HitVector; + ent.pastwall = pastwall; + hitlist.Push(ent); + return TRACE_Skip; + } + return TRACE_Skip; + } + else if ( Results.HitType == TRACE_HasHitSky ) + { + fullstop = true; + return TRACE_Stop; + } + else if ( Results.HitType != TRACE_HitNone ) + { + if ( Results.HitType == TRACE_HitWall ) + { + ShootThroughList.Push(Results.HitLine); + if ( (Results.Tier == TIER_Middle) && Results.HitLine.sidedef[1] && !(Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) + return TRACE_Skip; + } + int maxstep = int(RealMaxDist-Results.Distance); + for ( int i=1; i<=maxstep; i++ ) + { + Vector3 ofs = Results.HitPos+Results.HitVector*i; + if ( level.IsPointInLevel(ofs) ) + { + // double-check if we're piercing through a 3D floor (yes this is a thing that happens, oh boy) + Sector s = level.PointInSector(ofs.xy); + bool stop3d = false; + for ( int j=0; j maxz) ) continue; + stop3d = true; + break; + } + if ( stop3d ) continue; + let wp = new("WallPenetrate"); + wp.hittype = Results.HitType; + wp.hitline = Results.HitLine; + wp.hitside = Results.Side; + wp.hittier = Results.Tier; + wp.hitsector = Results.HitSector; + wp.hitffloor = Results.ffloor; + wp.hitpos = Results.HitPos; + wp.hitdir = Results.HitVector; + wp.bustdir = Results.HitVector; + if ( Results.HitType == TRACE_HitWall ) + { + wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit(); + if ( !Results.Side ) wp.hitnormal *= -1; + ShootThroughList.Push(Results.HitLine); + } + else if ( Results.HitType == TRACE_HitCeiling ) + wp.hitnormal = Results.HitSector.ceilingplane.Normal; + else if ( Results.HitType == TRACE_HitFloor ) + wp.hitnormal = Results.HitSector.floorplane.Normal; + wp.pastwall = pastwall; + WallPenetrateList.Push(wp); + pastwall = true; + // trace backwards to find exit surface + let at = new("AuxiliarySilverBulletTracer"); + at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,0); + let wp2 = new("WallPenetrate"); + wp2.hittype = at.Results.HitType; + wp2.hitline = at.Results.HitLine; + wp2.hitside = at.Results.Side; + wp2.hittier = at.Results.Tier; + wp2.hitsector = at.Results.HitSector; + wp2.hitffloor = at.Results.ffloor; + wp2.hitside = at.Results.Side; + wp2.hitpos = at.Results.HitPos; + wp2.hitdir = at.Results.HitVector; + wp2.bustdir = -at.Results.HitVector; + if ( at.Results.HitType == TRACE_HitWall ) + { + wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit(); + if ( !at.Results.Side ) wp2.hitnormal *= -1; + if ( at.Results.HitLine.sidedef[1] ) + ShootThroughList.Push(at.Results.HitLine); + } + else if ( at.Results.HitType == TRACE_HitCeiling ) + wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal; + else if ( at.Results.HitType == TRACE_HitFloor ) + wp2.hitnormal = at.Results.HitSector.floorplane.Normal; + else wp2.hitnormal = wp2.hitdir; + wp2.pastwall = pastwall; + WallPenetrateList.Push(wp2); + fullstop = false; + exitpoint = ofs; + return TRACE_Stop; + } + } + fullstop = true; + return TRACE_Stop; + } + return TRACE_Skip; + } +} + Class MisterRifle : SWWMWeapon { int clipcount; bool chambered, fired; bool gchambered, gfired; bool boltlock, initialized; - bool waschambered, wasfired; - bool wasgchambered, wasgfired; + bool waschambered; + bool wasgchambered; int prefirecnt; int firemode; ui int lastfiremode; @@ -19,6 +162,8 @@ Class MisterRifle : SWWMWeapon Property ClipCount : clipcount; + transient MisterRailTracer mrt; // I pity the fool + override void AttachToOwner( Actor other ) { if ( !initialized ) @@ -55,6 +200,11 @@ Class MisterRifle : SWWMWeapon lastfiremode = firemode+1; } + override Vector3 GetTraceOffset() + { + return (10,2.8,-2.4); + } + override bool ReportHUDAmmo() { return (chambered&&!fired)||(clipcount>0)||(Ammo1.Amount>0)||(gchambered&&!gfired)||(Ammo2.Amount>0); @@ -141,8 +291,7 @@ Class MisterRifle : SWWMWeapon if ( bCasing ) { invoker.waschambered = invoker.chambered; - invoker.wasfired = invoker.fired; - A_ChangeModel("",1,"","",10,"models",invoker.wasfired?"MortalRound_Used.png":"MortalRound.png",CMDL_USESURFACESKIN,-1); + A_ChangeModel("",1,"","",10,"models","MortalRound_Used.png",CMDL_USESURFACESKIN,-1); } invoker.chambered = invoker.fired = false; A_AmmoAlert(); @@ -164,8 +313,17 @@ Class MisterRifle : SWWMWeapon action void A_DropCasing() { - // TODO A_ChangeModel("",1,"","",10,"models","",CMDL_USESURFACESKIN,-1); + if ( !invoker.waschambered ) return; + // brass it up (though it's not made of brass) + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),2*x+3*y-18*z); + let c = Spawn("MisterCasing",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.25,.25)+y*FRandom[Junk](-.25,.25)-(0,0,FRandom[Junk](4.,6.)); + c.vel += vel*.5; } action void A_FireSelect() @@ -200,7 +358,16 @@ Class MisterRifle : SWWMWeapon { // ensure mag is invisible to avoid weird interpolation to hand A_ChangeModel("",1,"","",7,"models","",CMDL_USESURFACESKIN,-1); - // TODO drop physical empty mag if enabled + if ( swwm_nomagdrop ) return; + // drop it + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),6*x+3*y-18*z); + let c = Spawn("MisterMag",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2.,4.)); + c.vel += vel*.5; } action void A_MagGrab() @@ -248,29 +415,121 @@ Class MisterRifle : SWWMWeapon // this one is handled separately if ( invoker.firemode >= 3 ) return ResolveState("FireOverpressure"); A_PlayerFire(); - invoker.fired = true; // TODO, mark this round as fired (will do the rest later) + A_SWWMFlash(); + invoker.fired = true; if ( invoker.firemode == 2 ) { // stream shot // individual sub-shots handled in the state sequence, not here A_StartSound("mister/firestream",CHAN_WEAPON,CHANF_OVERLAP); + SWWMHandler.DoFlash(self,Color(32,64,224,255),2); + A_AlertMonsters(swwm_uncapalert?0:4000); + A_MisterFireStream(true); return ResolveState("FireStream"); } + SWWMHandler.DoFlash(self,Color(48,64,224,255),3); + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.8*y-2.4*z); if ( invoker.firemode == 1 ) { // cluster shot A_StartSound("mister/firescatter",CHAN_WEAPON,CHANF_OVERLAP); + SWWMUtility.DoKnockback(self,-x,15000.); + A_QuakeEx(6,6,6,8,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.3); + A_BumpFOV(.92); + A_AlertMonsters(swwm_uncapalert?0:8000); return ResolveState("FireCluster"); } // precision shot A_StartSound("mister/firesemi",CHAN_WEAPON,CHANF_OVERLAP); + SWWMUtility.DoKnockback(self,-x,9000.); + A_QuakeEx(5,5,5,6,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.8); + A_BumpFOV(.95); + A_AlertMonsters(swwm_uncapalert?0:6000); + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .4; + s.alpha *= .1; + s.speed *= .5; + s.vel += vel*.5+x*FRandom[Mister](1.,2.); + s.SetShade(Color(0,3,4)*Random[ExploS](48,63)); + } + Vector3 dir; + dir = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + FLineTraceData d; + LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d); + SWWMBulletTrail.DoTrail(self,origin,dir,10000,2); + if ( d.HitType == TRACE_HitActor ) + { + int dmg = 48; + // might as well apply explosion on top + if ( dmg >= d.HitActor.Health ) dmg += 200; + SWWMUtility.DoKnockback(d.HitActor,d.HitDir,20000); + let p = SWWMPuff.Setup(d.HitLocation,d.HitDir,invoker,self,d.HitActor); + dmg = d.HitActor.DamageMobj(p,self,dmg,'Mortal',DMG_THRUSTLESS|DMG_FOILINVUL|DMG_INFLICTOR_IS_PUFF); + if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT ) + { + let p = Spawn("SWWMBulletImpact",d.HitLocation); + p.angle = atan2(d.HitDir.y,d.HitDir.x)+180; + p.pitch = asin(d.HitDir.z); + } + else + { + d.HitActor.TraceBleed(dmg,self); + d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); + } + let b = Spawn("MisterBulletImpact",d.HitLocation-d.HitDir*4.); + b.angle = atan2(d.HitDir.y,d.HitDir.x)+180; + b.pitch = asin(d.HitDir.z); + b.target = self; + } + else if ( d.HitType != TRACE_HitNone ) + { + Vector3 hitnormal = -d.HitDir; + if ( d.HitType == TRACE_HitFloor ) + { + if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal; + else hitnormal = d.HitSector.floorplane.Normal; + } + else if ( d.HitType == TRACE_HitCeiling ) + { + if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal; + else hitnormal = d.HitSector.ceilingplane.Normal; + } + else if ( d.HitType == TRACE_HitWall ) + { + hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit(); + if ( !d.LineSide ) hitnormal *= -1; + } + let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01); + p.angle = atan2(hitnormal.y,hitnormal.x); + p.pitch = asin(-hitnormal.z); + if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); + let b = Spawn("MisterBulletImpact",d.HitLocation+hitnormal*4.); + b.angle = atan2(hitnormal.y,hitnormal.x); + b.pitch = asin(-hitnormal.z); + b.target = self; + if ( swwm_omnibust ) BusterWall.BustLinetrace(d,2150,self,d.HitDir,d.HitLocation.z); + } return ResolveState(null); } - action void A_MisterFireStream() + action void A_MisterFireStream( bool bFromMain = false ) { - // TODO - A_PlayerFire(); + if ( !bFromMain ) + { + A_PlayerFire(); + SWWMHandler.DoFlash(self,Color(32,64,224,255),2); + A_SWWMFlash("FastFlash"); + } + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.8*y-2.4*z); + SWWMUtility.DoKnockback(self,-x,5000.); + A_QuakeEx(3,3,3,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.4); + A_BumpFOV(.97); } action void A_MisterStartRail() @@ -281,18 +540,34 @@ Class MisterRifle : SWWMWeapon action void A_MisterFireRail() { - // TODO, just mark round as fired for now A_PlayerFire(); + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.8*y-2.4*z); + SWWMHandler.DoFlash(self,Color(64,64,224,255),9); + A_SWWMFlash(); invoker.fired = true; A_StartSound("mister/fireover",CHAN_WEAPON,CHANF_OVERLAP); + SWWMUtility.DoKnockback(self,-x,90000.); + A_QuakeEx(8,8,8,12,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:2.); + A_BumpFOV(.85); + A_AlertMonsters(swwm_uncapalert?0:12000); } action void A_MisterFireGrenade() { - // TODO, just mark round as fired for now A_PlayerFire(); + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.8*y-4.*z); + SWWMHandler.DoFlash(self,Color(64,64,224,255),3); + A_SWWMFlash("AltFlash"); invoker.gfired = true; A_StartSound("mister/grenade",CHAN_WEAPON,CHANF_OVERLAP); + SWWMUtility.DoKnockback(self,-x,12000.); + A_QuakeEx(4,4,4,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.6); + A_BumpFOV(.96); + A_AlertMonsters(swwm_uncapalert?0:5000); } action void A_GrenadeOpen() @@ -300,7 +575,6 @@ Class MisterRifle : SWWMWeapon A_PlayerReload(); A_StartSound("mister/grenadeopen"); invoker.wasgchambered = invoker.gchambered; - invoker.wasgfired = invoker.gfired; // theoretically, this is an either-or situation // either there's no grenade in the chamber whatsoever // or there's the casing of a fired grenade @@ -310,7 +584,7 @@ Class MisterRifle : SWWMWeapon A_ChangeModel("",1,"","",8,"models","",CMDL_USESURFACESKIN,-1); A_ChangeModel("",1,"","",9,"models","",CMDL_USESURFACESKIN,-1); } - else // invoker.wasgfired + else { // show only a fired cartridge A_ChangeModel("",1,"","",8,"models","MortalGrenade_Used.png",CMDL_USESURFACESKIN,-1); @@ -321,9 +595,18 @@ Class MisterRifle : SWWMWeapon action void A_GrenadeDrop() { - // TODO A_ChangeModel("",1,"","",8,"models","",CMDL_USESURFACESKIN,-1); A_ChangeModel("",1,"","",9,"models","",CMDL_USESURFACESKIN,-1); + if ( !invoker.wasgchambered ) return; + // droppage + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),8*x+3*y-18*z); + let c = Spawn("MisterGCasing",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2.,4.)); + c.vel += vel*.5; } action void A_GrenadeGrab() @@ -706,6 +989,31 @@ Class MisterRifle : SWWMWeapon XZWB JKLMNOPQRST 1; XZWB T -1 A_FullLower(); Stop; + Flash: + XZW0 A 2 Bright + { + let psp = player.FindPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](0,3); + let l = Spawn("MisterWeaponLight",pos); + l.target = self; + } + Stop; + FastFlash: + XZW0 A 2 Bright + { + let psp = player.FindPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](0,3); + } + Stop; + AltFlash: + XZW0 E 2 Bright + { + let psp = player.FindPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](4,7); + let l = Spawn("MisterWeaponLight",pos); + l.target = self; + } + Stop; Spawn: XZW1 A -1; Stop; diff --git a/zscript/dlc1/swwm_mister_fx.zsc b/zscript/dlc1/swwm_mister_fx.zsc index 910626fc3..cfabd46ad 100644 --- a/zscript/dlc1/swwm_mister_fx.zsc +++ b/zscript/dlc1/swwm_mister_fx.zsc @@ -1 +1,217 @@ // Mortal Rifle projectiles and effects + +Class MisterWeaponLight : SWWMWeaponLight +{ + Default + { + args 64,224,255,150; + } +} + +Class MisterCasing : SWWMCasing +{ + Default + { + BounceSound "mister/casing"; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + heat = 0; + } + States + { + Death: + XZW1 BCDE -1 + { + bINTERPOLATEANGLES = false; + pitch = roll = 0; + angle = FRandom[Junk](0,360); + frame = RandomPick[Junk](1,4); + } + Stop; + } +} + +Class MisterGCasing : SWWMCasing +{ + Default + { + Mass 8; + BounceFactor 0.5; + WallBounceFactor 0.5; + BounceSound "mister/gcasing"; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + heat = 0; + } +} + +Class MisterMag : SWWMCasing +{ + Default + { + Mass 10; + BounceFactor 0.4; + WallBounceFactor 0.4; + BounceSound "explodium/mag"; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + heat = 0; + } + States + { + Death: + XZW1 BC -1 + { + bINTERPOLATEANGLES = false; + pitch = roll = 0; + angle = FRandom[Junk](0,360); + frame = RandomPick[Junk](1,2); + } + Stop; + } +} + +Class MisterExLight : PaletteLight +{ + Default + { + Tag "Cyanblu"; + ReactionTime 30; + Args 0,0,0,200; + } +} + +Class MisterBulletImpactPop : Actor +{ + Default + { + RenderStyle "Add"; + Scale 6.; + Radius .1; + Height 0.; + +NOGRAVITY; + +NOBLOCKMAP; + +NODAMAGETHRUST; + +FORCERADIUSDMG; + +FORCEXYBILLBOARD; + +ROLLSPRITE; + +ROLLCENTER; + +NOTELEPORT; + +NOINTERACTION; + } + override void Tick() + { + if ( isFrozen() ) return; + A_SetScale(scale.x*.9,scale.y*.9); + A_FadeOut(.15); + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + BLPF C 5 NoDelay Bright + { + Scale *= FRandom[ExploS](0.6,1.8); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + roll = FRandom[Explos](0,360); + } + Stop; + } +} + +Class MisterBulletImpact : Actor +{ + Default + { + Obituary "$O_MORTALRIFLE"; + DamageType "Mortal"; + RenderStyle "Add"; + Radius .1; + Height 0.; + Scale 1.5; + +NOGRAVITY; + +NOBLOCKMAP; + +NODAMAGETHRUST; + +FORCERADIUSDMG; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +FOILINVUL; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_AlertMonsters(swwm_uncapalert?0:4000); + SWWMUtility.DoExplosion(self,200,20000,100,40,DE_EXTRAZTHRUST); + A_QuakeEx(6,6,6,10,0,400,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:150,rollintensity:.6); + A_StartSound("mister/hitsemi",CHAN_VOICE,attenuation:.3); + A_StartSound("mister/hitsemi",CHAN_WEAPON,attenuation:.2); + A_SprayDecal("RocketBlast",-172); + Scale *= FRandom[ExploS](0.8,1.1); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + int numpt = Random[ExploS](20,30); + for ( int i=0; i 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XEX7 ACEGIKMOQSUWY[] 1 Bright + { + if ( special1 && (special1 < 10) ) SWWMUtility.DoExplosion(self,200-special1*20,20000+special1*1000,100+special1*5,40+special1*2,DE_EXTRAZTHRUST); + special1++; + } + Stop; + } +} + diff --git a/zscript/weapons/swwm_baseweapon_fx.zsc b/zscript/weapons/swwm_baseweapon_fx.zsc index ba0b36fcf..2a054f5e0 100644 --- a/zscript/weapons/swwm_baseweapon_fx.zsc +++ b/zscript/weapons/swwm_baseweapon_fx.zsc @@ -85,6 +85,7 @@ Class SWWMCasing : Actor abstract Death: XZW1 B -1 { + bINTERPOLATEANGLES = false; pitch = roll = 0; angle = FRandom[Junk](0,360); } diff --git a/zscript/weapons/swwm_danmaku_fx.zsc b/zscript/weapons/swwm_danmaku_fx.zsc index 5fd9925bf..d791fbf88 100644 --- a/zscript/weapons/swwm_danmaku_fx.zsc +++ b/zscript/weapons/swwm_danmaku_fx.zsc @@ -701,6 +701,7 @@ Class EvisceratorCasing : SWWMCasing Death: XZW1 BC -1 { + bINTERPOLATEANGLES = false; pitch = roll = 0; angle = FRandom[Junk](0,360); frame = RandomPick[Junk](1,2); diff --git a/zscript/weapons/swwm_sparkyboi_fx.zsc b/zscript/weapons/swwm_sparkyboi_fx.zsc index d81833f89..f388c9f7f 100644 --- a/zscript/weapons/swwm_sparkyboi_fx.zsc +++ b/zscript/weapons/swwm_sparkyboi_fx.zsc @@ -1945,6 +1945,7 @@ Class SparksterMag : SWWMCasing Death: XZW1 BC -1 { + bINTERPOLATEANGLES = false; pitch = 0; angle = FRandom[Junk](0,360); roll = 0; diff --git a/zscript/weapons/swwm_splode_fx.zsc b/zscript/weapons/swwm_splode_fx.zsc index 09d85bf5a..6e6de8732 100644 --- a/zscript/weapons/swwm_splode_fx.zsc +++ b/zscript/weapons/swwm_splode_fx.zsc @@ -21,6 +21,7 @@ Class ExplodiumMag : SWWMCasing Death: XZW1 BC -1 { + bINTERPOLATEANGLES = false; pitch = roll = 0; angle = FRandom[Junk](0,360); frame = RandomPick[Junk](1,2); diff --git a/zscript/weapons/swwm_tastytreat.zsc b/zscript/weapons/swwm_tastytreat.zsc index 49165b231..0530a63f1 100644 --- a/zscript/weapons/swwm_tastytreat.zsc +++ b/zscript/weapons/swwm_tastytreat.zsc @@ -14,6 +14,9 @@ Class CandyGun : SWWMWeapon // re-edit to allow picking up spares in coop override bool HandlePickup( Inventory item ) { + // can't hold both weapons at once + if ( swwm_swapweapons && IsSwapWeapon(item) ) + return true; if ( (item.GetClass() == GetClass()) && !item.ShouldStay() ) { bool ammoget, spareget; diff --git a/zscript/weapons/swwm_tastytreat_fx.zsc b/zscript/weapons/swwm_tastytreat_fx.zsc index 8cf104263..201bf90ea 100644 --- a/zscript/weapons/swwm_tastytreat_fx.zsc +++ b/zscript/weapons/swwm_tastytreat_fx.zsc @@ -254,7 +254,7 @@ Class CandyPop : Actor States { Spawn: - BLPF B 3 NoDelay + BLPF B 3 NoDelay Bright { SWWMUtility.DoExplosion(self,500,60000,180,60); Scale *= FRandom[ExploS](0.6,1.8); @@ -319,7 +319,7 @@ Class TinyCandyPop : CandyPop States { Spawn: - BLPF B 3 NoDelay + BLPF B 3 NoDelay Bright { SWWMUtility.DoExplosion(self,200,32000,60,20); Scale *= FRandom[ExploS](0.6,1.8); diff --git a/zscript/weapons/swwm_thiccboolet_fx.zsc b/zscript/weapons/swwm_thiccboolet_fx.zsc index 77f26ccbd..936164245 100644 --- a/zscript/weapons/swwm_thiccboolet_fx.zsc +++ b/zscript/weapons/swwm_thiccboolet_fx.zsc @@ -28,6 +28,7 @@ Class SilverBulletMag : SWWMCasing Death: XZW1 BC -1 { + bINTERPOLATEANGLES = false; pitch = roll = 0; angle = FRandom[Junk](0,360); frame = RandomPick[Junk](1,2);