diff --git a/FuturePlans.md b/FuturePlans.md index 6ea26fe1c..5f74bcb17 100644 --- a/FuturePlans.md +++ b/FuturePlans.md @@ -22,8 +22,8 @@ More weapons, because we need 'em. In addition, all the "easy to implement" mini - [8] Ray-Khom *(UnSX)* - The Most Silent Takedown *(Kill 40 enemies with the Ray-Khom primary without alerting them)* - John Romero's Curse *(Fry yourself by discharging an electric weapon in water)* - - [9] Mortal Rifle *(UnSX 2)* - - Railed Hard *(Shoot through 44 enemies with a single Mortal Rifle overpressure shot)* + - ✓ [9] Mortal Rifle *(UnSX 2)* + - ✓ Railed Hard *(Kill 44 enemies with a single Mortal Rifle overpressure shot)* - [0] Rafan-Kos *(UnSX 4)* - Blasting Ropes *(Melt a grand total of 5000 enemies with the Rafan-Kos)* * ☑ **Additional Voice Acting:** diff --git a/language.def_base b/language.def_base index 2fca323ef..e345e7c45 100644 --- a/language.def_base +++ b/language.def_base @@ -1304,7 +1304,7 @@ SWWM_ACHIEVEMENT_PUZZLE_TXT = "Solve all fractions of \"puzzles\" in the same sa SWWM_ACHIEVEMENT_RAGE_TAG = "No Talk me Angy"; SWWM_ACHIEVEMENT_RAGE_TXT = "Use %s Ragekits"; SWWM_ACHIEVEMENT_RAIL_TAG = "Railed Hard"; -SWWM_ACHIEVEMENT_RAIL_TXT = "Shoot through %s enemies with a single Mortal Rifle overpressure shot"; +SWWM_ACHIEVEMENT_RAIL_TXT = "Kill %s enemies with a single Mortal Rifle overpressure shot"; SWWM_ACHIEVEMENT_REFLECT_TAG = "Return to Sender"; SWWM_ACHIEVEMENT_REFLECT_TXT = "Kill %s enemies with parried projectiles"; SWWM_ACHIEVEMENT_REFRESH_TAG = "Stay Fresh"; diff --git a/language.es_base b/language.es_base index 995b0cc5e..31a635381 100644 --- a/language.es_base +++ b/language.es_base @@ -1151,7 +1151,7 @@ SWWM_ACHIEVEMENT_PUZZLE_TXT = "Resuelve todas las partes de \"puzzles\" en la mi SWWM_ACHIEVEMENT_RAGE_TAG = "No Hablo me Enfado"; SWWM_ACHIEVEMENT_RAGE_TXT = "Usa %s Ragekits"; SWWM_ACHIEVEMENT_RAIL_TAG = "Metida de Través"; -SWWM_ACHIEVEMENT_RAIL_TXT = "Atraviesa %s enemigos con un solo disparo a presión del Rifle Mortal"; +SWWM_ACHIEVEMENT_RAIL_TXT = "Mata %s enemigos con un solo disparo a presión del Rifle Mortal"; SWWM_ACHIEVEMENT_REFLECT_TAG = "Devuelto a Remitente"; SWWM_ACHIEVEMENT_REFLECT_TXT = "Mata %s enemigos con proyectiles desviados"; SWWM_ACHIEVEMENT_REFRESH_TAG = "Siempre Fresco"; diff --git a/language.version b/language.version index d49f6a3f2..4b2fca2a6 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r523 \cu(Wed 28 Sep 17:58:30 CEST 2022)\c-"; -SWWM_SHORTVER="\cw1.3pre r523 \cu(2022-09-28 17:58:30)\c-"; +SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r524 \cu(Wed 28 Sep 21:00:24 CEST 2022)\c-"; +SWWM_SHORTVER="\cw1.3pre r524 \cu(2022-09-28 21:00:24)\c-"; diff --git a/modeldef.mortalrifle b/modeldef.mortalrifle index 7680ef7a4..71e659836 100644 --- a/modeldef.mortalrifle +++ b/modeldef.mortalrifle @@ -80,6 +80,37 @@ Model "MisterSubGrenade" FrameIndex XZW1 A 0 0 } +Model "MisterRailBeam" +{ + Path "models/extra" + + Model 0 "YBeam.obj" + Skin 0 "MRBolt.png" + Scale 1 1 0.6 + USEACTORPITCH + USEACTORROLL + DONTCULLBACKFACES + + FrameIndex XZW1 A 0 0 + Skin 0 "MRBoltS.png" + FrameIndex XZW1 B 0 0 +} +Model "MisterRailChildBeam" +{ + Path "models/extra" + + Model 0 "YBeam.obj" + Skin 0 "MRBolt.png" + Scale 1 1 0.6 + USEACTORPITCH + USEACTORROLL + DONTCULLBACKFACES + + FrameIndex XZW1 A 0 0 + Skin 0 "MRBoltS.png" + FrameIndex XZW1 B 0 0 +} + Model "MisterRifle" { Path "models" diff --git a/models/extra/MRBoltS.png b/models/extra/MRBoltS.png index d90a9b295..bbc833d14 100644 Binary files a/models/extra/MRBoltS.png and b/models/extra/MRBoltS.png differ diff --git a/palettes/Cyanblu.pal b/palettes/Cyanblu.pal index 725b1e350..95128d917 100644 Binary files a/palettes/Cyanblu.pal and b/palettes/Cyanblu.pal differ diff --git a/zscript/dlc1/swwm_dlcweapons_hud.zsc b/zscript/dlc1/swwm_dlcweapons_hud.zsc index 05e66c795..99ecd94f1 100644 --- a/zscript/dlc1/swwm_dlcweapons_hud.zsc +++ b/zscript/dlc1/swwm_dlcweapons_hud.zsc @@ -163,8 +163,6 @@ extend Class MisterRifle for ( int i=0; i hitlist; + Array hitlist; Array shootthroughlist; Array waterhitlist; + Array portalseg; - double RealMaxDist; + double maxdist; bool pastwall, fullstop; Array WallPenetrateList; @@ -34,16 +47,35 @@ Class MisterRailTracer : LineTracer hl.hitpos = Results.Crossed3DWaterPos; WaterHitList.Push(hl); } - if ( Results.HitType == TRACE_HitActor ) + if ( Results.HitType == TRACE_CrossingPortal ) + { + let seg = new("MRRailSeg"); + seg.enter = Results.HitPos; + seg.exit = Results.SrcFromTarget; + portalseg.Push(seg); + } + else if ( Results.HitType == TRACE_HitActor ) { if ( Results.HitActor == ignoreme ) return TRACE_Skip; if ( Results.HitActor.bSHOOTABLE ) { - let ent = new("HitListEntry"); + let ent = new("MRHitListEntry"); ent.hitactor = Results.HitActor; ent.hitlocation = Results.HitPos; ent.x = Results.HitVector; ent.pastwall = pastwall; + ent.bExit = false; + hitlist.Push(ent); + // also include exit point if we can reach it + Vector3 exit = SWWMUtility.TraceExit(Results.HitActor,Results.HitPos,Results.HitVector); + if ( level.Vec3Diff(Results.HitPos,exit).length() > (maxdist-Results.Distance) ) + return TRACE_Skip; // exit point is beyond our max distance + ent = new("MRHitListEntry"); + ent.hitactor = Results.HitActor; + ent.hitlocation = exit; + ent.x = Results.HitVector; + ent.pastwall = pastwall; + ent.bExit = true; hitlist.Push(ent); return TRACE_Skip; } @@ -62,7 +94,7 @@ Class MisterRailTracer : LineTracer 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); + int maxstep = int(maxdist-Results.Distance); for ( int i=1; i<=maxstep; i++ ) { Vector3 ofs = Results.HitPos+Results.HitVector*i; @@ -144,6 +176,26 @@ Class MisterRailTracer : LineTracer } } +Class MisterRailCounter : Thinker +{ + PlayerInfo player; + Array effectors; + int nkill; + + override void Tick() + { + int neff = 0; + for ( int i=0; i 0 ) return; + SWWMUtility.AchievementProgress("rail",nkill,player); + Destroy(); + } +} + Class MisterRifle : SWWMWeapon { int clipcount; @@ -152,7 +204,7 @@ Class MisterRifle : SWWMWeapon bool boltlock; bool waschambered; bool wasgchambered; - int prefirecnt; + double prefirecnt; transient int holdtic; int firemode; ui int lastfiremode; @@ -165,8 +217,6 @@ Class MisterRifle : SWWMWeapon Property ClipCount : clipcount; - transient MisterRailTracer mrt; // I pity the fool - override void InitializeWeapon() { // no round in the chamber @@ -694,8 +744,15 @@ Class MisterRifle : SWWMWeapon A_StartSound("mister/cancelover",CHAN_WEAPON,CHANF_OVERLAP); return ResolveState("FireOverpressureCancel"); } - if ( invoker.prefirecnt == 99 ) invoker.holdtic = gametic; - invoker.prefirecnt = min(100,int(invoker.prefirecnt+1)); + if ( invoker.prefirecnt < 100 ) + { + invoker.prefirecnt += 1.5; + if ( invoker.prefirecnt >= 100 ) + { + invoker.prefirecnt = 100; + invoker.holdtic = gametic; + } + } A_SoundVolume(CHAN_WEAPONEXTRA,clamp(invoker.prefirecnt*.01,.01,1.)); A_SoundPitch(CHAN_WEAPONEXTRA,clamp(.5+invoker.prefirecnt*.005,.5,1.)**.5); if ( (invoker.prefirecnt >= 100) && !((gametic-invoker.holdtic)%32) && CheckLocalView() ) @@ -719,6 +776,299 @@ Class MisterRifle : SWWMWeapon 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); + for ( int i=0; i<12; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .4; + s.alpha *= .1; + s.vel += vel*.5+x*FRandom[Mister](1.,12.); + s.SetShade(Color(0,3,4)*Random[ExploS](48,63)); + } + Vector3 dir, startdir; + startdir = dir = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + let mrt = new("MisterRailTracer"); // I pity the fool + mrt.ignoreme = self; + mrt.hitlist.Clear(); + mrt.shootthroughlist.Clear(); + mrt.waterhitlist.Clear(); + mrt.wallpenetratelist.Clear(); + mrt.ffloors.Clear(); + mrt.portalseg.Clear(); + for ( int i=0; i 32 ) + { + b = Spawn("MisterRailBeam",sstart); + b.frame = 1; + b.scale.y = 32; + b.angle = atan2(sdir.y,sdir.x); + b.pitch = asin(-sdir.z)+90; + MisterRailBeam(b).mrc = mrc; + mrc.effectors.Push(b); + sstart += sdir*32; + sdist -= 32; + b = Spawn("MisterRailBeam",sstart); + } + else + { + b = Spawn("MisterRailBeam",sstart); + b.frame = 1; + } + b.scale.y = sdist; + b.angle = atan2(sdir.y,sdir.x); + b.pitch = asin(-sdir.z)+90; + MisterRailBeam(b).mrc = mrc; + mrc.effectors.Push(b); + } + else for ( int i=0; i 32 ) + { + b = Spawn("MisterRailBeam",sstart); + b.target = self; + b.frame = 1; + b.scale.y = 32; + b.angle = atan2(sdir.y,sdir.x); + b.pitch = asin(-sdir.z)+90; + MisterRailBeam(b).mrc = mrc; + mrc.effectors.Push(b); + sstart += sdir*32; + sdist -= 32; + b = Spawn("MisterRailBeam",sstart); + } + else + { + b = Spawn("MisterRailBeam",sstart); + b.frame = 1; + } + } + else b = Spawn("MisterRailBeam",sstart); + b.target = self; + b.scale.y = sdist; + b.angle = atan2(sdir.y,sdir.x); + b.pitch = asin(-sdir.z)+90; + MisterRailBeam(b).mrc = mrc; + mrc.effectors.Push(b); + sstart = mrt.portalseg[i].exit; + if ( i == (mrt.portalseg.Size()-1) ) + { + sdir = mrt.Results.HitPos-sstart; + sdist = sdir.length(); + sdir /= sdist; + b = Spawn("MisterRailBeam",sstart); + b.target = self; + b.scale.y = sdist; + b.angle = atan2(sdir.y,sdir.x); + b.pitch = asin(-sdir.z)+90; + MisterRailBeam(b).mrc = mrc; + mrc.effectors.Push(b); + } + } + for ( int i=0; i bi; // so we can ignite them all at once after main contact damage + for ( int i=0; i 0 ) + { + Vector3 start = origin; + Vector3 end = mrt.WallPenetrateList[0].hitpos; + Vector3 tdir = level.Vec3Diff(start,end); + double dist = tdir.length(); + tdir /= dist; + for ( double d=4.; d<=dist; d+=8. ) + { + if ( !Random[ExploS](0,1) ) continue; + Vector3 ofs = level.Vec3Offset(start,tdir*d); + if ( !level.IsPointInLevel(ofs) ) continue; + let b = Spawn("SWWMHalfSmoke",ofs); + b.Scale *= FRandom[ExploS](.7,1.4); + b.alpha *= .2; + b.special1 = Random[ExploS](1,3); + b.SetShade(Color(2,3,4)*Random[ExploS](48,63)); + } + for ( int i=1; i= mrt.WallPenetrateList.Size()-1 ) end = mrt.Results.HitPos; + else end = mrt.WallPenetrateList[i+1].hitpos; + tdir = level.Vec3Diff(start,end); + dist = tdir.length(); + tdir /= dist; + for ( double d=4.; d<=dist; d+=8. ) + { + if ( !Random[ExploS](0,1) ) continue; + Vector3 ofs = level.Vec3Offset(start,tdir*d); + if ( !level.IsPointInLevel(ofs) ) continue; + let b = Spawn("SWWMHalfSmoke",ofs); + b.Scale *= FRandom[ExploS](.7,1.4); + b.alpha *= .2; + b.special1 = Random[ExploS](1,3); + b.SetShade(Color(2,3,4)*Random[ExploS](48,63)); + } + } + } + else + { + for ( double d=4.; d<=mrt.Results.Distance; d+=8. ) + { + if ( !Random[ExploS](0,1) ) continue; + Vector3 ofs = level.Vec3Offset(origin,startdir*d); + if ( !level.IsPointInLevel(ofs) ) continue; + let b = Spawn("SWWMHalfSmoke",ofs); + b.Scale *= FRandom[ExploS](.7,1.4); + b.alpha *= .2; + b.special1 = Random[ExploS](1,3); + b.SetShade(Color(2,3,4)*Random[ExploS](48,63)); + } + } } action void A_MisterFireGrenade() diff --git a/zscript/dlc1/swwm_mister_fx.zsc b/zscript/dlc1/swwm_mister_fx.zsc index 037b4ca6f..32684b350 100644 --- a/zscript/dlc1/swwm_mister_fx.zsc +++ b/zscript/dlc1/swwm_mister_fx.zsc @@ -191,6 +191,7 @@ Class MisterBulletImpactPop : Actor Class MisterFuzzy : Actor { + MisterRailCounter mrc; Default { Obituary "$O_MORTALRIFLE"; @@ -215,7 +216,9 @@ Class MisterFuzzy : Actor override void Tick() { if ( isFrozen() ) return; - SWWMUtility.DoExplosion(self,(special2<0)?4:44,3000,80,80,DE_EXTRAZTHRUST); + int nhit, nkill; + [nhit, nkill] = SWWMUtility.DoExplosion(self,(special2<0)?4:44,3000,80,80,DE_EXTRAZTHRUST|DE_COUNTENEMIES); + if ( mrc ) mrc.nkill += nkill; special1--; if ( special1 <= 0 ) { @@ -332,6 +335,7 @@ Class MisterFuzzyTrail : Actor Class MisterPop : Actor { + MisterRailCounter mrc; Default { Obituary "$O_MORTALRIFLE"; @@ -368,13 +372,18 @@ Class MisterPop : Actor Spawn: TNT1 A 1 NoDelay { - A_SetTics(Random[ExploS](1,5)); + A_SetTics(Random[ExploS](1,15)); Scale *= FRandom[ExploS](.5,1.5); Scale.x *= RandomPick[ExploS](-1,1); Scale.y *= RandomPick[ExploS](-1,1); roll = FRandom[ExploS](0,360); } - BLPF C 2 Bright { SWWMUtility.DoExplosion(self,4,2000,50,50,DE_EXTRAZTHRUST); } + BLPF C 2 Bright + { + int nhit, nkill; + [nhit, nkill] = SWWMUtility.DoExplosion(self,4,2000,50,50,DE_EXTRAZTHRUST|DE_COUNTENEMIES); + if ( mrc ) mrc.nkill += nkill; + } TNT1 A 1 { let p = Spawn("MisterFuzzyTrail",pos); @@ -387,6 +396,7 @@ Class MisterPop : Actor Class MisterBulletImpact : Actor { + MisterRailCounter mrc; // simplify code by putting this here Default { Obituary "$O_MORTALRIFLE"; @@ -648,6 +658,280 @@ Class MisterStreamImpact : MisterBulletImpact } } +Class MisterRailEntryImpact : MisterBulletImpact +{ + Default + { + Scale 1.2; + } + override void A_BulletExplode() + { + A_AlertMonsters(swwm_uncapalert?0:4000); + int nhit, nkill; + [nhit, nkill] = SWWMUtility.DoExplosion(self,444,200000,100,100,DE_EXTRAZTHRUST|DE_COUNTENEMIES); + if ( mrc ) mrc.nkill += nkill; + A_QuakeEx(6,6,6,10,0,400,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:150,rollintensity:.8); + A_StartSound("mister/hitover",CHAN_VOICE,attenuation:.35); + A_SprayDecal("SmallRocketBlast",-172); + Scale *= FRandom[ExploS](0.8,1.1); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + int numpt = Random[ExploS](8,12); + for ( int i=0; i viewdist ) continue; if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) @@ -380,7 +380,7 @@ extend Class SWWMHandler let a = beams[i]; if ( !a ) continue; Vector2 rv = a.pos.xy-relpos; - double rad = a.speed*cos(a.pitch); + double rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch)); if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist ) continue; if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) diff --git a/zscript/hud/swwm_hud.zsc b/zscript/hud/swwm_hud.zsc index 41d8e7137..c212b25cb 100644 --- a/zscript/hud/swwm_hud.zsc +++ b/zscript/hud/swwm_hud.zsc @@ -2198,7 +2198,7 @@ Class SWWMStatusBar : BaseStatusBar { pos = SWWMUtility.LerpVector2(t.target.prev.xy,t.target.pos.xy,FracTic); angle = t.target.angle; - radius = t.isbeam?(t.target.speed*cos(t.target.pitch)):t.target.radius; + radius = t.isybeam?(t.target.scale.y*cos(t.target.pitch-90)):t.isbeam?(t.target.speed*cos(t.target.pitch)):t.target.radius; } else { diff --git a/zscript/hud/swwm_hudobjects.zsc b/zscript/hud/swwm_hudobjects.zsc index 4b73c04e7..e11beffa6 100644 --- a/zscript/hud/swwm_hudobjects.zsc +++ b/zscript/hud/swwm_hudobjects.zsc @@ -265,7 +265,7 @@ Class SWWMSimpleTracker play bool vipitem; bool expired; bool ismissile; - bool isbeam; + bool isbeam, isybeam; int lastupdate; ui double smoothalpha; // smoothened alpha, for ui SWWMSimpleTracker next; @@ -274,7 +274,8 @@ Class SWWMSimpleTracker play { if ( !target ) return; isbeam = SWWMUtility.IsBeamProj(target); - radius = isbeam?(target.speed*cos(target.pitch)):target.radius; + isybeam = isbeam&&SWWMUtility.IsYBeam(target); + radius = isybeam?(target.scale.y*cos(target.pitch+90)):isbeam?(target.speed*cos(target.pitch)):target.radius; angle = target.angle; pos = target.pos; isplayer = target.player; diff --git a/zscript/swwm_common_fx.zsc b/zscript/swwm_common_fx.zsc index 1bcad9df0..ae64b8117 100644 --- a/zscript/swwm_common_fx.zsc +++ b/zscript/swwm_common_fx.zsc @@ -939,7 +939,7 @@ Class SWWMTeleportLine : Actor override void Tick() { - if ( !tline ) + if ( !tline || !tline.special ) { Destroy(); return; diff --git a/zscript/utility/swwm_utility.zsc b/zscript/utility/swwm_utility.zsc index de7d7424c..104d627da 100644 --- a/zscript/utility/swwm_utility.zsc +++ b/zscript/utility/swwm_utility.zsc @@ -1466,6 +1466,13 @@ Class SWWMUtility if ( a is 'YnykronLightningArc' ) return true; if ( a is 'YnykronAltBeam' ) return true; if ( a is 'MykradvoTendril' ) return true; + if ( a is 'MisterRailBeam' ) return true; + return false; + } + // is this a YBeam type? (real pitch is pitch-90, scale.y == length) + static play bool IsYBeam( Actor a ) + { + if ( a is 'MisterRailBeam' ) return true; return false; } diff --git a/zscript/weapons/swwm_thiccboolet.zsc b/zscript/weapons/swwm_thiccboolet.zsc index 79c60a78a..f5bb5df49 100644 --- a/zscript/weapons/swwm_thiccboolet.zsc +++ b/zscript/weapons/swwm_thiccboolet.zsc @@ -674,6 +674,7 @@ Class SilverBullet : SWWMWeapon sst.Trace(norigin,level.PointInSector(norigin.xy),x2,maxdist,TRACE_HitSky); maxdist -= (sst.exitpoint-norigin).length(); norigin = sst.exitpoint; + x2 = sst.Results.HitVector; } while ( !sst.fullstop ); invoker.nkills = 0;