From 79ff46d1a47f5416b66444d3bbfa3f525bcf6866 Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Thu, 12 Mar 2020 22:55:45 +0100 Subject: [PATCH] Eviscerator now finally implemented. Deep Impact alt can now be used at any moment, but its potential damage will be lower. In addition, the Deep Impact now deals rip damage only once per target, and on top deals slightly reduced damage to bosses. --- graphics/HUD/EvisceratorDisplay.png | Bin 0 -> 154 bytes graphics/HUD/EvisceratorShell.png | Bin 0 -> 447 bytes graphics/InterBG.png | Bin 145404 -> 145200 bytes language.def | 2 +- language.es | 2 +- modeldef.eviscerator | 54 +++ trnslate.txt | 8 + zscript/swwm_danmaku.zsc | 720 +++++++++++++++++++++++++++- zscript/swwm_deepdarkimpact.zsc | 21 +- zscript/swwm_jackhammer.zsc | 6 +- zscript/swwm_shot.zsc | 8 +- zscript/swwm_splode.zsc | 1 - 12 files changed, 798 insertions(+), 24 deletions(-) create mode 100644 graphics/HUD/EvisceratorDisplay.png create mode 100644 graphics/HUD/EvisceratorShell.png create mode 100644 trnslate.txt diff --git a/graphics/HUD/EvisceratorDisplay.png b/graphics/HUD/EvisceratorDisplay.png new file mode 100644 index 0000000000000000000000000000000000000000..c8713bf816f07ceda6a82f1516169e91911845d9 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0y~yV9;k^U=UmaM+Z<7e8B;beu`+x!GGrIKVYv!qho`Hb%Q~loCIFstCK3Pu literal 0 HcmV?d00001 diff --git a/graphics/HUD/EvisceratorShell.png b/graphics/HUD/EvisceratorShell.png new file mode 100644 index 0000000000000000000000000000000000000000..7b6f93e1fe00e10b3e48497c738fb3f45cf06966 GIT binary patch literal 447 zcmeAS@N?(olHy`uVBq!ia0y~yVBi5^4mJh`hOp9@=L`&tZJsWUAsXjfC+^L^?7(oe zK7IaM3twMPk4tG8)10hACa%)a`EXsy@Wb>HhL!+TEpY**3vCuF{F3g^-QTxxi%@LE zT9e(aR?{svHlzC^%7K#eDn1C#Oe0CYi5~zx!LJ zA$@YE?`089+gCwKa~S3{l&%xF^z>)<^ly3!jMGlH?2Y4p=~);1|H+yADQJ}?qUdgCvTHzX1(nGdm|~{k4xRxUswGQ{laN;gihz<^)>V3KPTI{ zdtFz}Y47>g&cn{U=&mf=UpuDuTTRW~VXVhOwl96XENymbPI2q(H7iz4+8}!AW^YE; z2b=kR>bs;xB1J@JmmD_o4Dd?yakS2v{G4qmx81z?#l@S|BLD8=jj8#iFVdQ&MBb@ E0Fdv=Sz&SEVui)EhZPogK2}&h zm6=gt`9ftzh2?9V85NfAOlDMAezciUVfn?yj0(%|9%fWn{_-)S!t$@oObXi>RhX=W QSQMXc4B75r#JDoJRni5*{u5J_fK;F0*1DTWYfW>nyLs?Ma05LwKq zz_U{8Z74$IFrxy`L)S|S5h9Nn6?hgbd2=5j!px+=^WE8ZGXsN=H7H=Vn<_I|3$fgf N?|8Aj#FS|dBLMyxU8Vp4 diff --git a/language.def b/language.def index 4a5cb0e35..3efb6aaed 100644 --- a/language.def +++ b/language.def @@ -462,7 +462,7 @@ SWWM_LORETXT_DEEPIMPACT = "\n" "Primary Fire: Quick puffs of compressed air. Pushes away enemies and projectiles, while also dealing some damage.\n" "\n" -"Secondary Fire: Compresses all the air into a single highly destructive shot. This \"air bullet\" is capable of penetrating multiple targets, digging a hole staight through their bodies. Requires a full air tank.\n" +"Secondary Fire: Compresses all the air into a single highly destructive shot. This \"air bullet\" is capable of penetrating multiple targets, digging a hole staight through their bodies. The potential damage depends on how much air there was in the tank.\n" "\n" "Reloading: The lever on the side must be pulled several times in order to refill the internal air tank. Once at full capacity, a smart mechanism will lock the lever to avoid accidental overfilling.\n" "\n" diff --git a/language.es b/language.es index 0727d10ea..61e84391f 100644 --- a/language.es +++ b/language.es @@ -425,7 +425,7 @@ SWWM_LORETXT_DEEPIMPACT = "\n" "Fuego Primario: Rápidos soplos de aire comprimido. Empujan enemigos y proyectiles, al mismo tiempo que causan algo de daño.\n" "\n" -"Fuego Secundario: Comprime todo el aire en un único disparo áltamente destructivo. Esta \"bala de aire\" es capaz de atravesar múltiples objetivos, abriendo una brecha directamente a través de sus cuerpos. Requiere un tanque de aire completamente lleno.\n" +"Fuego Secundario: Comprime todo el aire en un único disparo áltamente destructivo. Esta \"bala de aire\" es capaz de atravesar múltiples objetivos, abriendo una brecha directamente a través de sus cuerpos. El potencial de daño depende de la cantidad de aire que había en el tanque.\n" "\n" "Recarga: Se debe tirar de la palanca en el lateral para rellenar el tanque de aire interno. Ya en su capacidad máxima, un mecanismo inteligente bloqueará la palanca para evitar la sobrecarga accidental.\n" "\n" diff --git a/modeldef.eviscerator b/modeldef.eviscerator index c67c9f786..5ccbc1735 100644 --- a/modeldef.eviscerator +++ b/modeldef.eviscerator @@ -1,3 +1,29 @@ +Model "EvisceratorCasing" +{ + Path "models" + + Model 0 "EvisceratorShell_d.3d" + Skin 0 "EvisceratorShell.png" + Scale 0.08 0.08 0.08 + USEACTORPITCH + USEACTORROLL + + FrameIndex XZW1 A 0 0 +} + +Model "EvisceratorProj" +{ + Path "models" + + Model 0 "EvisceratorProj_d.3d" + Skin 0 "EvisceratorShell.png" + Scale 0.08 0.08 0.08 + USEACTORPITCH + USEACTORROLL + + FrameIndex XZW1 A 0 0 +} + Model "Eviscerator" { Path "models" @@ -12,6 +38,34 @@ Model "Eviscerator" FrameIndex XZW1 A 0 0 } +Model "Eviscerator" +{ + Path "models/extra" + + Model 2 "Flat_d.3d" + Offset 18 -60 -22 + PitchOffset 90 + + Scale 0.3 0.3 0.3 + Skin 2 "EvisceratorMuz0.png" + FrameIndex XZWZ A 2 0 + Skin 2 "EvisceratorMuz1.png" + FrameIndex XZWZ B 2 0 + Skin 2 "EvisceratorMuz2.png" + FrameIndex XZWZ C 2 0 + Skin 2 "EvisceratorMuz3.png" + FrameIndex XZWZ D 2 0 + Scale 0.12 0.12 0.12 + Skin 2 "EvisceratorMuz0.png" + FrameIndex XZWZ E 2 0 + Skin 2 "EvisceratorMuz1.png" + FrameIndex XZWZ F 2 0 + Skin 2 "EvisceratorMuz2.png" + FrameIndex XZWZ G 2 0 + Skin 2 "EvisceratorMuz3.png" + FrameIndex XZWZ H 2 0 +} + Model "Eviscerator" { Path "models" diff --git a/trnslate.txt b/trnslate.txt new file mode 100644 index 000000000..53510f6c6 --- /dev/null +++ b/trnslate.txt @@ -0,0 +1,8 @@ +HotMetal0 = "0:255=%[0.45,0.4,0.25]:[2.0,1.8,0.5]" +HotMetal1 = "0:255=%[0.4,0.35,0.22]:[1.8,1.5,0.45]" +HotMetal2 = "0:255=%[0.36,0.3,0.2]:[1.6,1.3,0.42]" +HotMetal3 = "0:255=%[0.32,0.25,0.18]:[1.4,1.1,0.38]" +HotMetal4 = "0:255=%[0.28,0.22,0.15]:[1.1,0.8,0.35]" +HotMetal5 = "0:255=%[0.23,0.18,0.12]:[0.7,0.6,0.32]" +HotMetal6 = "0:255=%[0.2,0.12,0.1]:[0.5,0.4,0.28]" +HotMetal7 = "0:255=%[0.0,0.0,0.0]:[0.25,0.25,0.25]" diff --git a/zscript/swwm_danmaku.zsc b/zscript/swwm_danmaku.zsc index 89d2ade18..c1afa8565 100644 --- a/zscript/swwm_danmaku.zsc +++ b/zscript/swwm_danmaku.zsc @@ -1,8 +1,500 @@ // Mr. BIG SHOT Industries "Eviscerator" High Load Flak Cannon (from SWWM series) // Slot 5, replaces Chaingun, Dragon Claw, Hammer of Retribution +Class EvisceratorChunkLight : DynamicLight +{ + Default + { + DynamicLight.Type "Point"; + Args 255,224,128,8; + } + override void Tick() + { + Super.Tick(); + if ( !target ) + { + Destroy(); + return; + } + SetOrigin(target.pos,true); + if ( isFrozen() ) return; + double intst = clamp((7-EvisceratorChunk(target).lifetime*10)/7.,0,1); + args[LIGHT_RED] = int(255*intst); + args[LIGHT_GREEN] = int(224*intst); + args[LIGHT_BLUE] = int(128*intst); + } +} + +Class ChunkImpact : Actor +{ + Default + { + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_SprayDecal("WallCrack",-20); + int numpt = Random[Eviscerator](-1,2); + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i 20) && !Random[Eviscerator](0,2) ) + { + let l = Spawn("ChunkImpact",pos); + l.angle = atan2(HitNormal.y,HitNormal.x); + l.pitch = asin(-HitNormal.z); + } + A_Gravity(); + gravity = .35; + rollvel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1)*(vel.length()/speed); + A_StartSound("eviscerator/hit",CHAN_WEAPON,CHANF_OVERLAP,.4); + A_AlertMonsters(300); + if ( vel.length() < 3 ) + { + A_Stop(); + ClearBounce(); + ExplodeMissile(); + } + } + + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + if ( bAMBUSH || (vel.length() <= 5) ) return damage; + if ( !target.bNOBLOOD ) + { + target.SpawnBlood(pos,AngleTo(target),damage); + A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.1); + A_AlertMonsters(900); + } + return -1; + } + + override int SpecialMissileHit( Actor victim ) + { + if ( bAMBUSH || (vel.length() <= 5) || ((victim == target) && !bHITOWNER) || (victim == lasthit) ) + return 1; + // with this we can guarantee that the chunk won't just keep on dealing damage + // this is something I wish Unreal's boulders did + lasthit = victim; + // gather damage + SWWMDamageAccumulator.Accumulate(victim,GetMissileDamage(0,0),self,target,damagetype); + int amt = SWWMDamageAccumulator.GetAmount(victim); + // pass through if it's already dead + if ( victim.health-amt <= 0 ) + { + // bleed if not gibbed (reduces effect spam) + if ( !victim.bNOBLOOD && (victim.health-amt > victim.GetGibHealth()) ) + { + victim.SpawnBlood(pos,AngleTo(victim),damage); + A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.1); + A_AlertMonsters(900); + } + return 1; + } + SWWMHandler.DoKnockback(victim,vel.unit(),8000); + return -1; + } + States + { + Spawn: + JUNK # -1; + Stop; + Bounce: + JUNK # 0 A_HandleBounce(); + Goto Spawn; + Death: + JUNK # 0 + { + bMOVEWITHSECTOR = true; + A_SetTics(Random[Eviscerator](30,50)); + } + JUNK # 1 A_FadeOut(); + Wait; + XDeath: + TNT1 A 1; + Stop; + } +} + +Class EvisceratorProjSmoke : Actor +{ + double lifetime, lifespeed; + Default + { + Radius 0.1; + Height 0; + +NOBLOCKMAP; + +NOGRAVITY; + +DONTSPLASH; + +NOTELEPORT; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + lifetime = 0; + lifespeed = FRandom[Eviscerator](0.004,0.008); + } + override void Tick() + { + Super.Tick(); + if ( isFrozen() ) return; + lifetime += lifespeed; + let s = Spawn("SWWMSmoke",pos); + s.vel = (FRandom[Eviscerator](-0.5,0.5),FRandom[Eviscerator](-0.5,0.5),FRandom[Eviscerator](-0.5,0.5)); + s.vel.z += 2.; + s.alpha = scale.x; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + scale.x = max(0,1-lifetime); + if ( scale.x <= 0 ) Destroy(); + } + States + { + Spawn: + TNT1 A -1; + Stop; + } +} + +Class EvisceratorProjLight : PaletteLight +{ + Default + { + Args 0,0,0,140; + ReactionTime 20; + } +} + +Class EvisceratorProj : Actor +{ + double heat; + Default + { + Obituary "$O_EVISCERATOR"; + DamageType 'Exploded'; + Radius 4; + Height 4; + Gravity 0.35; + Speed 50; + PROJECTILE; + -NOGRAVITY; + +EXPLODEONWATER; + +HITTRACER; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + vel.z += 3; + heat = 1.5; + } + action void A_EvisExplode() + { + bForceXYBillboard = true; + A_SetRenderStyle(1.0,STYLE_Add); + A_SprayDecal("BigScorch",50); + A_NoGravity(); + A_SetScale(3.5); + SWWMHandler.DoBlast(self,240,120000); + A_Explode(150,240); + A_QuakeEx(6,6,6,20,0,1200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.7); + A_StartSound("eviscerator/shell",CHAN_WEAPON,attenuation:.5); + A_StartSound("eviscerator/shell",CHAN_VOICE,attenuation:.3); + A_AlertMonsters(3000); + if ( !Tracer ) Spawn("EvisceratorProjSmoke",pos); + Spawn("EvisceratorProjLight",pos); + Vector3 x, y, z; + double a, s; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Actor p; + Vector3 spawnofs; + if ( BlockingMobj ) spawnofs = level.Vec3Diff(pos,BlockingMobj.Vec3Offset(0,0,BlockingMobj.height/2)).unit()*4; + else if ( BlockingFloor ) spawnofs = BlockingFloor.floorplane.Normal*4; + else if ( BlockingCeiling ) spawnofs = BlockingCeiling.ceilingplane.Normal*4; + else if ( BlockingLine ) + { + spawnofs = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit()*4; + if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) ) + spawnofs *= -1; + } + for ( int i=0; i<40; i++ ) + { + p = Spawn("EvisceratorChunk",level.Vec3Offset(pos,spawnofs)); + p.bHITOWNER = true; + a = FRandom[Eviscerator](0,360); + s = FRandom[Eviscerator](0,.4); + Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit(); + p.angle = atan2(dir.y,dir.x); + p.pitch = -asin(dir.z); + p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*(p.speed+FRandom[Eviscerator](-5,15)); + p.target = target; + } + int numpt = Random[Eviscerator](8,12); + for ( int i=0; i 8 ) return; + int numpt = Random[Eviscerator](0,8-special1); + double ang, pt; + for ( int i=0; i 0 ) + { + let s = Spawn("SWWMSmoke",pos); + s.alpha *= heat; + } + } + Wait; + Death: + TNT1 A 0 A_EvisExplode(); + XSEX ABCDEFGHIJKLMNOPQRS 2 Bright A_Subexpl(); + Stop; + } +} + +Class EvisceratorSubExpl : Actor +{ + Default + { + RenderStyle "Add"; + Scale 2.; + Alpha .6; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOBLOCKMAP; + +FORCEXYBILLBOARD; + +NOTELEPORT; + } + States + { + Spawn: + XSEX ABCDEFGHIJKLMNOPQRS 1 Bright; + Stop; + } +} + +Class EvisceratorCasing : SWWMCasing +{ + Default + { + Mass 10; + BounceFactor 0.4; + WallBounceFactor 0.4; + BounceSound "eviscerator/casing"; + } + States + { + Death: + #### # -1 + { + pitch = RandomPick[Junk](-90,90); + angle = FRandom[Junk](0,360); + roll = 0; + } + Stop; + } +} + Class Eviscerator : SWWMWeapon { + bool isfiring; // barrel is extended bool extended; // pending shell load @@ -10,6 +502,38 @@ Class Eviscerator : SWWMWeapon // countdown to loading new shell int loadtics; + transient ui TextureID WeaponBox, AmmoIcon; + transient ui Font TewiFont; + + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/EvisceratorDisplay.png",TexMan.Type_Any); + if ( !AmmoIcon ) AmmoIcon = TexMan.CheckForTexture("graphics/HUD/EvisceratorShell.png",TexMan.Type_Any); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + Screen.DrawTexture(WeaponBox,false,bx-47,by-17,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + String astr = String.Format("%d",Ammo1.Amount); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-15-(TewiFont.StringWidth(astr)+1),by-16,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + bool noshell = pendingload||(isfiring&&!pendingload); + Screen.DrawTexture(AmmoIcon,false,bx-15,by-15,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,noshell?Color(128,0,0,0):Color(0,0,0,0)); + Screen.DrawText(TewiFont,Font.CR_WHITE,bx-45,by-16,extended?"►":"",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + + override bool ReportHUDAmmo() + { + if ( (Ammo1.Amount > 0) || !pendingload ) return true; + return false; + } + + override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) + { + if ( (firemode == PrimaryFire) || (firemode == AltFire) ) + { + if ( (Ammo1.Amount > 0) || !pendingload ) return true; + return false; + } + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + override void DoEffect() { Super.DoEffect(); @@ -20,18 +544,145 @@ Class Eviscerator : SWWMWeapon } loadtics++; if ( (loadtics == 9) && Owner && Owner.player && (Owner.player.ReadyWeapon == self) ) + { Owner.A_StartSound("eviscerator/load",CHAN_WEAPON,CHANF_OVERLAP); + if ( !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo',true) ) + Ammo1.Amount = max(0,Ammo1.Amount-1); + } if ( loadtics > 20 ) pendingload = false; } action void A_EvisceratorFire() { + let weap = Weapon(invoker); + if ( !weap ) return; + invoker.isfiring = true; A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP); + A_QuakeEx(6,6,6,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.5); + A_ZoomFactor(.94,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + SWWMHandler.DoFlash(self,Color(64,255,224,96),3); + A_AlertMonsters(); + A_Recoil(2.); + Vector3 x, y, z, x2, y2, z2, dir, origin; + double a, s; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + SWWMHandler.DoKnockback(self,-x,15000.); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z); + for ( int i=0; i<40; i++ ) + { + a = FRandom[Eviscerator](0,360); + s = FRandom[Eviscerator](0,invoker.extended?.06:.3); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("EvisceratorChunk",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed*FRandom[Eviscerator](.9,1.1); + } + for ( int i=0; i<4; i++ ) + { + let s = Spawn("SWWMViewSmoke",origin); + SWWMViewSmoke(s).ofs = (10,3,-5); + s.target = self; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + s.alpha *= .2; + } + for ( int i=0; i<8; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-1,1)+z*FRandom[Eviscerator](-1,1); + } + for ( int i=0; i<9; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); + } } action void A_EvisceratorAltFire() { + let weap = Weapon(invoker); + if ( !weap ) return; + invoker.isfiring = true; A_StartSound("eviscerator/altfire",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP); + A_QuakeEx(4,4,4,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.9); + A_ZoomFactor(.91,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + SWWMHandler.DoFlash(self,Color(16,255,224,96),3); + A_AlertMonsters(); + A_Recoil(2.2); + Vector3 x, y, z, x2, y2, z2, dir, origin; + double a, s; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + SWWMHandler.DoKnockback(self,-x,22000.); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z); + a = FRandom[Eviscerator](0,360); + s = FRandom[Eviscerator](0,invoker.extended?.003:.02); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("EvisceratorProj",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + for ( int i=0; i<4; i++ ) + { + let s = Spawn("SWWMViewSmoke",origin); + SWWMViewSmoke(s).ofs = (10,3,-5); + s.target = self; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + s.alpha *= .1; + } + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.alpha *= .2; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); + } + for ( int i=0; i<5; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); + } + } + + action void A_EvisceratorEject() + { + Vector3 x, y, z, origin; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*10-y*10-z*10); + let c = Spawn("EvisceratorCasing",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.5,.5)-y*FRandom[Junk](3,6)-(0,0,FRandom[Junk](4,6)); + c.vel += vel*.5; + } + + action void A_EvisceratorCasingSmoke( Vector3 ofs ) + { + Vector3 x, y, z, origin; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); + let s = Spawn("SWWMViewSmoke",origin); + SWWMViewSmoke(s).ofs = (ofs.x,ofs.y,ofs.z); + s.target = self; + s.alpha *= .4; } Default @@ -68,6 +719,7 @@ Class Eviscerator : SWWMWeapon Select: XZW2 H 2 { + invoker.isfiring = false; A_FullRaise(); return A_JumpIf(invoker.extended,"SelectExt"); } @@ -79,16 +731,20 @@ Class Eviscerator : SWWMWeapon Ready: XZW2 A 1 { + invoker.isfiring = false; int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; if ( invoker.pendingload ) flg |= WRF_NOFIRE; + A_CheckReload(); A_WeaponReady(flg); } Wait; ReadyExt: XZW4 Z 1 { + invoker.isfiring = false; int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; if ( invoker.pendingload ) flg |= WRF_NOFIRE; + A_CheckReload(); A_WeaponReady(flg); } Wait; @@ -98,34 +754,57 @@ Class Eviscerator : SWWMWeapon A_EvisceratorFire(); return A_JumpIf(invoker.extended,"FireExt"); } - XZW3 EFGHIJKLMNOPQR 1; + XZW3 EFGH 1; + XZW3 I 1 A_Recoil(-.8); + XZW3 JKLMNOPQR 1; Goto Eject; FireExt: XZW4 Z 1; - XZW6 DEFGHIJKLMNOPQ 1; + XZW6 DEFG 1; + XZW6 H 1 A_Recoil(-.8); + XZW6 IJKLMNOPQ 1; Goto EjectExt; Eject: XZW2 A 4; XZW3 STUV 2; XZW3 W 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 XYZ 1; + XZW3 X 1 A_Overlay(-9999,"EjectSmoke"); + XZW3 YZ 1; XZW4 AB 1; - XZW4 C 1 A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); + XZW4 C 1 + { + A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); + invoker.pendingload = true; + } XZW4 DEF 1; XZW4 GHI 2; - XZW2 A 1 { invoker.pendingload = true; } Goto Ready; EjectExt: XZW4 Z 4; XZW6 RSTU 2; XZW6 V 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP); - XZW6 WXYZ 1; + XZW6 W 1 A_Overlay(-9999,"EjectSmoke"); + XZW6 XYZ 1; XZW7 A 1; - XZW7 B 1 A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); + XZW7 B 1 + { + A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); + invoker.pendingload = true; + } XZW7 CDE 1; XZW7 FGH 2; - XZW4 Z 1 { invoker.pendingload = true; } Goto ReadyExt; + EjectSmoke: + TNT1 A 1 A_EvisceratorCasingSmoke((10,2,-3)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-1,-2)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-3,-1.5)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-5,-2)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-7,-3.5)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-8.5,-5)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-10,-9)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-11,-14)); + TNT1 A 1 A_EvisceratorEject(); + Stop; AltFire: XZW2 A 2 { @@ -135,8 +814,7 @@ Class Eviscerator : SWWMWeapon XZW2 STUVW 1; XZW2 XYZ 2; XZW3 ABCD 2; - XZW2 A 6; - XZW2 A 2 { invoker.pendingload = true; } + XZW2 A 1 { invoker.pendingload = true; } Goto Ready; AltFireExt: XZW4 Z 2; @@ -144,8 +822,7 @@ Class Eviscerator : SWWMWeapon XZW5 WXY 2; XZW5 Z 2; XZW6 ABC 2; - XZW4 Z 6; - XZW4 Z 2 { invoker.pendingload = true; } + XZW4 Z 1 { invoker.pendingload = true; } Goto Ready; Reload: XZW2 A 2 @@ -221,5 +898,24 @@ Class Eviscerator : SWWMWeapon XZWA Z 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZWB ABC 2; Goto ReadyExt; + Flash: + XZWZ A 2 + { + let psp = player.GetPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](0,3); + let l = Spawn("SWWMWeaponLight",pos); + l.target = self; + } + Stop; + AltFlash: + XZWZ A 2 + { + let psp = player.GetPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](4,7); + let l = Spawn("SWWMWeaponLight",pos); + l.target = self; + l.args[3] -= 20; + } + Stop; } } diff --git a/zscript/swwm_deepdarkimpact.zsc b/zscript/swwm_deepdarkimpact.zsc index fa78e24bf..ff681f120 100644 --- a/zscript/swwm_deepdarkimpact.zsc +++ b/zscript/swwm_deepdarkimpact.zsc @@ -4,6 +4,7 @@ Class AirBullet : FastProjectile { int tcnt; + Actor lasthit; Default { @@ -22,8 +23,14 @@ Class AirBullet : FastProjectile } override int DoSpecialDamage( Actor target, int damage, Name damagetype ) { - SWWMHandler.DoKnockback(target,vel.unit(),10000); - return damage; + if ( target.bBOSS ) damage = int(damage*.4); + if ( !bAMBUSH ) + { + if ( target == lasthit ) return 0; + lasthit = target; + } + SWWMHandler.DoKnockback(target,vel.unit(),10000*specialf1); + return int(damage*specialf1); } override void Effect() { @@ -44,7 +51,9 @@ Class AirBullet : FastProjectile s.scale *= 1.8; s.alpha *= .2; } + bAMBUSH = true; A_Explode(GetMissileDamage(0,0),80,0); + bAMBUSH = false; A_AlertMonsters(400); tcnt++; if ( tcnt < 2 ) return; @@ -221,7 +230,7 @@ Class DeepImpact : SWWMWeapon { invoker.charge += 1./TICRATE; A_WeaponOffset(FRandom[Impact](-1,1)*invoker.charge,32+FRandom[Impact](-1,1)*invoker.charge); - if ( invoker.charge >= 1. ) + if ( invoker.charge >= (invoker.clipcount*.01) ) { A_WeaponOffset(0,32); player.SetPSprite(PSP_WEAPON,ResolveState("AltRelease")); @@ -342,7 +351,6 @@ Class DeepImpact : SWWMWeapon A_Recoil(4.); A_StartSound("deepimpact/altfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.5); A_AlertMonsters(); - invoker.charge = 0; invoker.clipcount = 0; Vector3 x, y, z, x2, y2, z2; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); @@ -355,6 +363,8 @@ Class DeepImpact : SWWMWeapon p.angle = atan2(dir.y,dir.x); p.pitch = asin(-dir.z); p.vel = dir*p.speed; + p.specialf1 = invoker.charge; + invoker.charge = 0; int numpt = Random[Impact](14,18); for ( int i=0; i