diff --git a/Readme.md b/Readme.md index 1bff593..fe7533f 100644 --- a/Readme.md +++ b/Readme.md @@ -59,9 +59,12 @@ This mod requires a recent GZDoom devbuild (g3.6pre-31-gd965c9aa7 or later). - Fix some oddly-oriented triangles (e.g. some parts of the flak cannon, can be easily noticed when using invisibility) - Recenter the backpack mesh (it was a complete hack job to begin with) + - UT gore system (toggleable) ## Future plans + - Change the way inventory items are dropped to be more UT-like, this will + require GZDoom changes - Add ammo counters to Pulsegun, Minigun, Flak Cannon and Rocket Launcher once scripted textures are implemented - Add player models once GZDoom gets a well deserved model animation system @@ -69,7 +72,6 @@ This mod requires a recent GZDoom devbuild (g3.6pre-31-gd965c9aa7 or later). the current state-tied system) - Add weapon attachment support to player models when that is also added in - Migrate RandomSpawners to CheckReplacement - - Perhaps come up with an add-on that imitates UT's blood/gore system - Unreal 1 weapons mod and maybe also a monsters mod - Port some of my UT weapon mods diff --git a/cvarinfo.txt b/cvarinfo.txt index 754624b..a5f75e5 100644 --- a/cvarinfo.txt +++ b/cvarinfo.txt @@ -27,3 +27,4 @@ server bool flak_doomspeed = false; // keep Doomguy run speed when using UT mov server bool flak_doomaircontrol = false; // keep Doom's limited air control when using UT movement server bool flak_nobosstelefrag = false; // disable telefragging of boss monsters (useful when translocator is enabled) server bool flak_nowalkdrop = false; // don't drop off ledges while holding walk key (glitchy) +server bool flak_corpsedamage = false; // [WIP/EXPERIMENTAL] allow corpses to take damage and be gibbed, currently just causes a jump to XDeath until gore system is implemented diff --git a/language.txt b/language.txt index 77eaa11..00e3d62 100644 --- a/language.txt +++ b/language.txt @@ -6,13 +6,12 @@ DAMNUM_TYPECOLOR_SHOT = "DamYellow"; DAMNUM_TYPECOLOR_JOLTED = "DamLightBlue"; DAMNUM_TYPECOLOR_JOLTEDX = "DamOrange"; DAMNUM_TYPECOLOR_REDEEMERDEATH = "DamBlack"; -DAMNUM_TYPECOLOR_SHREDDED = "DamGold"; +DAMNUM_TYPECOLOR_SHREDDED = "DamBrick"; DAMNUM_TYPECOLOR_FLAKDEATH = "DamGold"; DAMNUM_TYPECOLOR_SLASHED = "DamRed"; DAMNUM_TYPECOLOR_DECAPITATED = "DamDarkRed"; DAMNUM_TYPECOLOR_SLIME = "DamDarkGreen"; DAMNUM_TYPECOLOR_IMPACT = "DamGray"; -DAMNUM_TYPECOLOR_RIPPER = "DamBrick"; DAMNUM_TYPECOLOR_RIPPERALTDEATH = "DamCream"; DAMNUM_TYPECOLOR_ROCKETDEATH = "DamTan"; DAMNUM_TYPECOLOR_GRENADEDEATH = "DamTan"; diff --git a/menudef.txt b/menudef.txt index e445485..59308de 100644 --- a/menudef.txt +++ b/menudef.txt @@ -53,6 +53,7 @@ OptionMenu "UTOptionMenu" StaticText " " StaticText "Misc Options", "Gold" Option "UT Footsteps", "flak_footsteps", "YesNo" + Option "[WIP] Corpses Take Damage", "flak_corpsedamage", "YesNo" } AddOptionMenu "OptionsMenu" diff --git a/zscript/enforcer.zsc b/zscript/enforcer.zsc index 2c98d62..3d9836f 100644 --- a/zscript/enforcer.zsc +++ b/zscript/enforcer.zsc @@ -306,6 +306,7 @@ Class Enforcer : UTWeapon if ( alt ) origin = origin-z*3.0+ydir*y*1.0; else origin = origin-z*1.0+ydir*y*4.0; double a = FRandom[Enforcer](0,360), s = FRandom[Enforcer](0,alt?0.08:0.004); + if ( invoker.SlaveActive ) s *= 3; [x2, y2, z2] = Matrix4.GetAxes(BulletSlope(),angle,roll); Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); FLineTraceData d; @@ -314,7 +315,9 @@ Class Enforcer : UTWeapon { int dmg = Random[Enforcer](12,17); dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); - UTMainHandler.DoKnockback(d.HitActor,d.HitDir,3000); + double mm = 3000; + if ( FRandom[Enforcer](0,1) < 0.2 ) mm *= 5; + UTMainHandler.DoKnockback(d.HitActor,d.HitDir,mm); if ( d.HitActor.bNOBLOOD ) { let p = Spawn("BulletImpact",d.HitLocation); diff --git a/zscript/flakcannon.zsc b/zscript/flakcannon.zsc index 02e4211..c3bac4b 100644 --- a/zscript/flakcannon.zsc +++ b/zscript/flakcannon.zsc @@ -107,7 +107,6 @@ Class ChunkTrail : Actor Class FlakChunk : Actor { - Actor lasthit; ChunkTrail trail; double rollvel, pitchvel, yawvel; double lifetime, lifespeed; @@ -118,7 +117,7 @@ Class FlakChunk : Actor Radius 2; Height 2; Speed 50; - DamageFunction int(Random[Flak](15,20)*max(0.1,1.0-lifetime*4.1)); + DamageFunction Random[Flak](15,20); DamageType 'Shredded'; BounceType "Doom"; BounceFactor 0.8; @@ -130,10 +129,6 @@ Class FlakChunk : Actor +SKYEXPLODE; Scale 0.3; } - override bool CanCollideWith( Actor other, bool passive ) - { - return (vel.length()>4.0); - } override void PostBeginPlay() { Super.PostBeginPlay(); @@ -208,17 +203,17 @@ Class FlakChunk : Actor A_PlaySound("flak/bounce",volume:0.3); A_AlertMonsters(); bBOUNCEAUTOOFFFLOORONLY = true; - if ( vel.length() < 4.0 ) ExplodeMissile(); + if ( vel.length() < 5.0 ) ExplodeMissile(); } override int DoSpecialDamage( Actor target, int damage, Name damagetype ) { + if ( vel.length() <= 5.0 ) return 0; if ( !target.bNOBLOOD ) { - if ( target != lasthit ) target.SpawnBlood(pos,AngleTo(target),damage); + target.SpawnBlood(pos,AngleTo(target),damage); A_PlaySound("flak/meat",volume:0.3); A_AlertMonsters(); } - lasthit = target; return damage; } States @@ -377,7 +372,7 @@ Class FlakSlug : Actor double a, s; [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Actor p; - for ( int i=0; i<6; i++ ) + for ( int i=0; i<5; i++ ) { p = Spawn("FlakChunk",pos); p.bHITOWNER = true; @@ -475,7 +470,7 @@ Class FlakCannon : UTWeapon A_OverlayRenderstyle(-2,STYLE_Add); [x, y, z] = Matrix4.GetAxes(BulletSlope(),angle,roll); Actor p; - for ( int i=0; i<10; i++ ) + for ( int i=0; i<6; i++ ) { p = Spawn("FlakChunk",origin); a = FRandom[Flak](0,360); diff --git a/zscript/impacthammer.zsc b/zscript/impacthammer.zsc index b8de009..1456c83 100644 --- a/zscript/impacthammer.zsc +++ b/zscript/impacthammer.zsc @@ -72,7 +72,7 @@ Class ImpactHammer : UTWeapon Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+3.0*y-4.0*z; double realcharge = min(1.5,invoker.chargesize); FLineTraceData d; - LineTrace(angle,80,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); + LineTrace(angle,60,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); if ( d.HitType == TRACE_HitActor ) { int dmg = int(Random[Impact](90,120)*realcharge); @@ -133,8 +133,8 @@ Class ImpactHammer : UTWeapon [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+3.0*y-4.0*z; FLineTraceData d; - LineTrace(angle,180,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); - double dscale = d.Distance/180.; + LineTrace(angle,120,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); + double dscale = d.Distance/120.; if ( d.HitType == TRACE_HitActor ) { int dmg = int(Random[Impact](25,35)*dscale); @@ -156,10 +156,10 @@ Class ImpactHammer : UTWeapon if ( !m.bMISSILE ) continue; double rdist = level.Vec3Diff(origin,m.pos).length(); Vector3 rdir = level.Vec3Diff(origin,m.pos).unit(); - if ( LineTrace(atan2(rdir.y,rdir.x),rdist,asin(-rdir.z),TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y) || (rdist > 550) || (rdir dot x < 0.9) ) continue; + if ( LineTrace(atan2(rdir.y,rdir.x),rdist,asin(-rdir.z),TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y) || (rdist > 400) || (rdir dot x < 0.9) ) continue; m.speed = m.vel.length(); - if ( m.vel dot y > 0 ) m.vel = m.speed*(m.vel+(750-rdist)*y*0.01).unit(); - else m.vel = m.speed*(m.vel-(750-rdist)*y*0.01).unit(); + if ( m.vel dot y > 0 ) m.vel = m.speed*(m.vel+(560-rdist)*y*0.01).unit(); + else m.vel = m.speed*(m.vel-(560-rdist)*y*0.01).unit(); if ( m.target == self ) continue; if ( m.bSEEKERMISSILE ) m.tracer = m.target; m.target = self; diff --git a/zscript/minigun.zsc b/zscript/minigun.zsc index ee1ca37..d77fdb6 100644 --- a/zscript/minigun.zsc +++ b/zscript/minigun.zsc @@ -69,7 +69,7 @@ Class MinigunTracer : Actor Class Minigun : UTWeapon { - int bcnt; + int bcnt, tcnt; action void A_FireBullet( bool alt = false ) { @@ -79,7 +79,8 @@ Class Minigun : UTWeapon A_Overlay(-2,"MuzzleFlash",true); A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderstyle(-2,STYLE_Add); - if ( (alt && (invoker.bcnt++ < 2)) || (invoker.bcnt++ < 4) ) return; + invoker.bcnt++; + if ( (alt && (invoker.bcnt < 2)) || (!alt && (invoker.bcnt < 4)) ) return; invoker.bcnt = 0; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; invoker.FireEffect(); @@ -100,9 +101,11 @@ Class Minigun : UTWeapon LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); if ( d.HitType == TRACE_HitActor ) { - int dmg = Random[Minigun](16,20); // fun fact: the Minigun is one of the few weapons that has actual RNG damage in UT + int dmg = Random[Minigun](9,18); // fun fact: the Minigun is one of the few weapons that has actual RNG damage in UT dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); - UTMainHandler.DoKnockback(d.HitActor,d.HitDir,dmg*500); + double mm = 500; + if ( FRandom[Minigun](0,1) < 0.2 ) mm *= 2.5; + UTMainHandler.DoKnockback(d.HitActor,d.HitDir,dmg*mm); if ( d.HitActor.bNOBLOOD ) { let p = Spawn("BulletImpact",d.HitLocation); diff --git a/zscript/ripper.zsc b/zscript/ripper.zsc index 06620a9..19cc4ec 100644 --- a/zscript/ripper.zsc +++ b/zscript/ripper.zsc @@ -57,9 +57,9 @@ Class Razor2 : Actor { Radius 2; Height 2; - Speed 40; // should be 26 but it looks way too slow - DamageFunction (Random[Ripper](30,40)*((DamageType=='Decapitated')?3.5:1.0)); - DamageType 'Ripper'; + Speed 27; + DamageFunction (Random[Ripper](25,35)*((DamageType=='Decapitated')?3.5:1.0)); + DamageType 'Shredded'; Obituary "%k ripped a chunk of meat out of %o with the Ripper."; BounceType "Doom"; ReactionTime 7; @@ -84,7 +84,7 @@ Class Razor2 : Actor } override int SpecialMissileHit( Actor victim ) { - if ( pos.z > victim.pos.z+victim.height*0.75 ) DamageType = 'Decapitated'; + if ( pos.z > victim.pos.z+victim.height*0.81 ) DamageType = 'Decapitated'; return -1; } override int DoSpecialDamage( Actor target, int damage, Name damagetype ) @@ -135,7 +135,7 @@ Class Razor2 : Actor Bounce: RAZB A 0 { - bHITOWNER = true; + bHITOWNER = true; // technically the UT version sets this 0.2 seconds after being fired, but this works better Vector3 dir = vel.unit(); A_SetAngle(atan2(dir.y,dir.x)); A_SetPitch(asin(-dir.z)); diff --git a/zscript/sniperrifle.zsc b/zscript/sniperrifle.zsc index cc4beeb..2d8a1c2 100644 --- a/zscript/sniperrifle.zsc +++ b/zscript/sniperrifle.zsc @@ -96,7 +96,7 @@ Class SniperRifle : UTWeapon if ( d.HitType == TRACE_HitActor ) { int dmg = Random[Sniper](45,60); - if ( d.HitLocation.z >= (d.HitActor.pos.z+d.HitActor.height*0.8) ) + if ( d.HitLocation.z >= (d.HitActor.pos.z+d.HitActor.height*0.81) ) { dmg = d.HitActor.DamageMobj(invoker,self,dmg+70,'Decapitated',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); UTMainHandler.DoKnockback(d.HitActor,d.HitDir,35000); diff --git a/zscript/utcommon.zsc b/zscript/utcommon.zsc index 11042eb..d4c2788 100644 --- a/zscript/utcommon.zsc +++ b/zscript/utcommon.zsc @@ -1,6 +1,7 @@ Class UTPlayer : DoomPlayer { bool lastground; + int lastgroundtic; double lastvelz, prevvelz; transient CVar footsteps, utmovement, doomspeed, doomaircontrol, nowalkdrop; Vector2 acceleration; @@ -190,15 +191,15 @@ Class UTPlayer : DoomPlayer if ( player.onground && !bNoGravity && !lastground && (waterlevel < 3) ) { player.jumptics = 0; - if ( lastvelz < -4 ) + if ( lastvelz < -8 ) { - double vol = clamp((-lastvelz-4)*0.05,0.01,1.0); + double vol = clamp((-lastvelz-8)*0.05,0.01,1.0); if ( ((waterlevel > 0) || GetFloorTerrain().IsLiquid) && !bOnMobj ) A_PlaySound("ut/wetsplash",CHAN_AUTO,vol); else A_PlaySound("*uland",CHAN_AUTO,vol); } else forcefootstep = true; } - if ( forcefootstep || ((abs(sin(ang)) >= 1.0) && player.onground && (player.cmd.forwardmove || player.cmd.sidemove) && (waterlevel < 3)) ) + if ( forcefootstep || ((abs(sin(ang)) >= 1.0) && player.onground && lastground && (player.jumptics == 0) && (player.cmd.forwardmove || player.cmd.sidemove) && (waterlevel < 3)) ) { double vol = abs(vel.xy.length())*0.03; if ( forcefootstep ) vol = clamp(-lastvelz*0.05,0.01,1.0); @@ -237,6 +238,8 @@ Class UTPlayer : DoomPlayer } else Angle += cmd.yaw*(360./65536.); player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2); + if ( player.onground ) lastgroundtic = gametic; + if ( (abs(lastgroundtic-gametic) < 4) && (player.jumptics == 0) ) player.onground = true; double friction = FrictionToUnreal(); double fs = TweakSpeeds(1.0,0.0); if ( !doomspeed.GetBool() ) @@ -289,6 +292,7 @@ Class UTPlayer : DoomPlayer player.camera = player.mo; } player.vel *= 0; + player.jumptics = -2; } else { @@ -1102,6 +1106,54 @@ Class UTBlueKey : BlueCard } } +Class ShredCorpseHitbox : Actor +{ + int accdamage; + + Default + { + +NOGRAVITY; + -SOLID; + +DONTSPLASH; + +SHOOTABLE; + Health int.max; + } + override void PostBeginPlay() + { + if ( !target ) + { + Destroy(); + return; + } + accdamage = target.Health; + A_SetSize(target.radius,target.height); + } + override void Tick() + { + Super.Tick(); + if ( !target || (target.Health > 0) || target.InStateSequence(target.CurState,target.FindState("XDeath")) ) + { + Destroy(); + return; + } + SetOrigin(target.pos,true); + A_SetSize(target.radius,target.height); + } + override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle ) + { + accdamage -= damage; + int gibhealth = (target.GibHealth==int.min)?-target.SpawnHealth():target.GibHealth; + if ( accdamage < gibhealth ) + { + // force gib (cheap ATM) + State gib = target.FindState("XDeath"); + if ( gib ) target.SetState(gib); + Destroy(); + } + return 0; + } +} + Class GenericFlash : HUDMessageBase { Color col; @@ -1445,6 +1497,15 @@ Class UTMainHandler : StaticEventHandler Screen.DrawTexture(tex,true,0,0,DTA_VirtualWidth,1024,DTA_VirtualHeight,768); } + override void WorldThingDied( WorldEvent e ) + { + if ( e.Thing.bDONTGIB ) return; + // attach damage accumulator for corpses + if ( !CVar.GetCVar('flak_corpsedamage').GetBool() ) return; + let a = Actor.Spawn("ShredCorpseHitbox",e.Thing.pos); + a.target = e.Thing; + } + static void DoFlash( Actor camera, Color c, int duration ) { QueuedFlash qf = new("QueuedFlash");