Melee tweaks.

This commit is contained in:
Mari the Deer 2022-04-26 18:52:56 +02:00
commit 9ea1ce6210
8 changed files with 118 additions and 62 deletions

View file

@ -303,10 +303,75 @@ Class MHitList
Vector3 dir, pos;
}
Class MeleeTracer : LineTracer
{
Actor ignoreme;
Array<MHitList> hits;
Vector3 x; // used to get closest hit to center
bool dorip;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( !Results.HitActor.bSHOOTABLE
|| (Results.HitActor == ignoreme)
|| Results.HitActor.FindInventory("ParriedBuff") ) return TRACE_Skip;
bool addme = true;
for ( int k=0; k<hits.Size(); k++ )
{
if ( hits[k].a != Results.HitActor ) continue;
if ( (hits[k].dir dot x) < (Results.HitVector dot x) )
{
// closer to centerpoint
hits[k].dir = Results.HitVector;
hits[k].pos = Results.HitPos;
}
addme = false;
break;
}
if ( addme )
{
let h = new("MHitList");
h.a = Results.HitActor;
h.dir = Results.HitVector;
h.pos = Results.HitPos;
hits.Push(h);
}
return dorip?TRACE_Skip:TRACE_Stop;
}
if ( Results.HitType == TRACE_HitWall )
{
if ( Results.Tier == TIER_Middle )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
return TRACE_Skip;
}
}
return TRACE_Stop;
}
}
extend Class SWWMWeapon
{
Actor pfield; // instance of parry field for current melee attack
bool wallponch; // is punching a wall (for activation checks)
transient MeleeTracer mt;
transient UseLineTracer ut;
// melee attack flags
enum EMeleeFlags
{
MELEE_Rip = 1, // trace will continue instead of stopping at the first hit (useful for swords)
MELEE_ForceSound = 2, // force the use of the provided hit sound while raging
MELEE_FleshSound = 4, // hit sound has a "flesh" variant (will append "f" suffix to hitsound string)
MELEE_ForceBust = 8, // bust walls even without Omnibusting enabled
MELEE_Vertical = 16, // ring is widened vertically, rather than horizontally
MELEE_Wider = 32, // ring is widened by 2.5x rather than 1.5x
MELEE_ExtraWide = 64 // if Wider is also specified, widen by 5x, otherwise widen by 3x
};
action void A_Parry( int duration )
{
@ -322,91 +387,75 @@ extend Class SWWMWeapon
GiveInventory("ParryDamageChecker",1); // need this so parried projectiles deal extra damage
}
// multi-hit cone rather than the usual one-hit arc, more fun
private action bool TryMelee( double spread, int dmg, String hitsound = "", double rangemul = 1., double kickmul = 1. )
private action bool TryMelee( double spread, int dmg, String hitsound, double rangemul, double kickmul, int flags )
{
Vector3 x, y, z, dir;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = Vec2OffsetZ(0,0,player.viewz);
Array<MHitList> hits;
hits.Clear();
FLineTraceData d;
if ( !invoker.mt ) invoker.mt = new("MeleeTracer");
let mt = invoker.mt; // for convenience
mt.ignoreme = self;
mt.hits.Clear();
mt.dorip = !!(flags&MELEE_Rip);
int rings = 1;
double step = spread/20.;
double range = 1.5*DEFMELEERANGE*rangemul;
bool raging = CountInv("RagekitPower");
double widemul = (flags&MELEE_ExtraWide)?(flags&MELEE_Wider)?5:3:(flags&MELEE_Wider)?2.5:1.5;
for ( double i=0; i<spread; i+=step )
{
for ( int j=0; j<360; j+=(360/rings) )
{
dir = (x+y*cos(j)*1.5*i+z*sin(j)*i).unit(); // wide ring
LineTrace(atan2(dir.y,dir.x),range,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
if ( d.HitType != TRACE_HitActor ) continue;
if ( d.HitActor.FindInventory("ParriedBuff") ) continue;
bool addme = true;
for ( int k=0; k<hits.Size(); k++ )
{
if ( hits[k].a != d.HitActor ) continue;
if ( (hits[k].dir dot x) < (dir dot x) )
{
// closer to centerpoint
hits[k].dir = dir;
hits[k].pos = d.HitLocation;
}
addme = false;
break;
}
if ( !addme ) continue;
let nhit = new("MHitList");
nhit.a = d.HitActor;
nhit.dir = dir;
nhit.pos = d.HitLocation;
hits.Push(nhit);
// wide ring
if ( flags&MELEE_Vertical ) dir = (x+y*cos(j)*i+z*sin(j)*widemul*i).unit();
else dir = (x+y*cos(j)*widemul*i+z*sin(j)*i).unit();
mt.Trace(origin,level.PointInSector(origin.xy),dir,range,0);
}
rings += 5;
}
// no targets
if ( hits.Size() <= 0 ) return false;
if ( mt.hits.Size() <= 0 ) return false;
bool blooded = false;
bool bloodless = false;
int flg = DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF;
if ( raging ) flg |= DMG_FOILINVUL;
int quakin = raging?8:2;
double diff = 0.;
for ( int i=0; i<hits.Size(); i++ )
for ( int i=0; i<mt.hits.Size(); i++ )
{
diff += deltaangle(self.angle,AngleTo(hits[i].a));
SWWMUtility.DoKnockback(hits[i].a,hits[i].dir,dmg*2000*kickmul);
diff += deltaangle(self.angle,AngleTo(mt.hits[i].a));
SWWMUtility.DoKnockback(mt.hits[i].a,mt.hits[i].dir,dmg*2000*kickmul);
// lol oops
if ( !hits[i].a.bDORMANT ) hits[i].a.DaggerAlert(self);
if ( !hits[i].a.bNOBLOOD && !hits[i].a.bDORMANT && (raging || !hits[i].a.bINVULNERABLE) ) blooded = true;
if ( !mt.hits[i].a.bDORMANT ) mt.hits[i].a.DaggerAlert(self);
if ( !mt.hits[i].a.bNOBLOOD && !mt.hits[i].a.bDORMANT && (raging || !mt.hits[i].a.bINVULNERABLE) ) blooded = true;
else bloodless = true;
let p = SWWMPuff.Setup(hits[i].pos,hits[i].dir,invoker,self,hits[i].a);
int newdmg = hits[i].a.DamageMobj(p,self,dmg,'Melee',flg);
let p = SWWMPuff.Setup(mt.hits[i].pos,mt.hits[i].dir,invoker,self,mt.hits[i].a);
int newdmg = mt.hits[i].a.DamageMobj(p,self,dmg,'Melee',flg);
// things can instantly cease to exist after taking damage (wow)
if ( hits[i].a )
if ( mt.hits[i].a )
{
if ( hits[i].a.player ) hits[i].a.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin);
if ( !hits[i].a.bNOBLOOD && !hits[i].a.bDORMANT && (raging || !hits[i].a.bINVULNERABLE) )
if ( mt.hits[i].a.player ) mt.hits[i].a.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin);
if ( !mt.hits[i].a.bNOBLOOD && !mt.hits[i].a.bDORMANT && (raging || !mt.hits[i].a.bINVULNERABLE) )
{
hits[i].a.TraceBleed(newdmg,invoker);
hits[i].a.SpawnBlood(hits[i].pos,atan2(hits[i].dir.y,hits[i].dir.x)+180,newdmg);
mt.hits[i].a.TraceBleed(newdmg,invoker);
mt.hits[i].a.SpawnBlood(mt.hits[i].pos,atan2(mt.hits[i].dir.y,mt.hits[i].dir.x)+180,newdmg);
}
else
{
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",hits[i].pos);
p.angle = atan2(hits[i].dir.y,hits[i].dir.x);
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",mt.hits[i].pos);
p.angle = atan2(mt.hits[i].dir.y,mt.hits[i].dir.x);
}
}
if ( raging )
{
let ps = Spawn("BigPunchSplash",hits[i].pos);
let ps = Spawn("BigPunchSplash",mt.hits[i].pos);
ps.target = self;
ps.special1 = dmg;
}
}
self.angle += clamp(diff/hits.Size(),-5.,5.); // averaged reorient
self.angle += clamp(diff/mt.hits.Size(),-5.,5.); // averaged reorient
A_QuakeEx(quakin/2,quakin/2,quakin/2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06*quakin);
if ( raging )
if ( raging && !(flags&MELEE_ForceSound) )
{
if ( blooded ) A_StartSound("pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
if ( bloodless ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP);
@ -416,21 +465,27 @@ extend Class SWWMWeapon
if ( blooded ) A_StartSound("demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP);
if ( bloodless ) A_StartSound("demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
}
else if ( flags&MELEE_FleshSound )
{
if ( blooded ) A_StartSound(hitsound.."f",CHAN_WEAPON,CHANF_OVERLAP);
if ( bloodless ) A_StartSound(hitsound,CHAN_WEAPON,CHANF_OVERLAP);
}
else A_StartSound(hitsound,CHAN_WEAPON,CHANF_OVERLAP);
A_AlertMonsters(swwm_uncapalert?0:300);
A_BumpFOV(.96);
return true;
}
action void A_Melee( int dmg = 40, String hitsound = "", double rangemul = 1., double spreadmul = 1., double kickmul = 1. )
action bool A_Melee( int dmg = 40, String hitsound = "", double rangemul = 1., double spreadmul = 1., double kickmul = 1., int flags = 0 )
{
let raging = RagekitPower(FindInventory("RagekitPower"));
if ( raging ) rangemul += .2;
Vector3 origin = Vec3Offset(0,0,player.viewheight);
Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch));
// check for usables
let ut = new("UseLineTracer");
if ( !invoker.ut ) invoker.ut = new("UseLineTracer");
let ut = invoker.ut; // for convenience
ut.uses.Clear();
ut.Trace(origin,CurSector,dir,DEFMELEERANGE*rangemul,0);
ut.Trace(origin,level.PointInSector(origin.xy),dir,DEFMELEERANGE*rangemul,0);
invoker.wallponch = true;
for ( int i=0; i<ut.uses.Size(); i++ )
{
@ -453,14 +508,14 @@ extend Class SWWMWeapon
invoker.wallponch = false;
// check for shootables
SWWMBulletTrail.DoTrail(self,origin,dir,DEFMELEERANGE*rangemul,0);
if ( TryMelee((raging?.3:.2)*spreadmul,dmg,hitsound,rangemul,kickmul) )
return;
if ( TryMelee((raging?.3:.2)*spreadmul,dmg,hitsound,rangemul,kickmul,flags) )
return true;
// check for walls instead
FTranslatedLineTarget t;
double slope = AimLineAttack(angle,DEFMELEERANGE*rangemul,t,0.,ALF_CHECK3D);
FLineTraceData d;
LineTrace(angle,DEFMELEERANGE*rangemul,slope,TRF_THRUACTORS,player.viewheight,data:d);
if ( d.HitType == TRACE_HitNone ) return;
if ( d.HitType == TRACE_HitNone ) return false;
Vector3 HitNormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
@ -491,9 +546,10 @@ extend Class SWWMWeapon
int quakin = raging?4:1;
A_QuakeEx(quakin,quakin,quakin,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12*quakin);
A_BumpFOV(.98);
A_StartSound(raging?"pusher/althit":(hitsound!="")?hitsound:"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound((raging&&!(flags&MELEE_ForceSound))?"pusher/althit":(hitsound!="")?hitsound:"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
A_AlertMonsters(swwm_uncapalert?0:100);
if ( raging ) raging.DoHitFX();
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,raging?(dmg*8):dmg,self,d.HitDir,d.HitLocation.z);
if ( (flags&MELEE_ForceBust) || swwm_omnibust ) BusterWall.BustLinetrace(d,raging?(dmg*8):dmg,self,d.HitDir,d.HitLocation.z);
return true;
}
}

View file

@ -554,7 +554,7 @@ Class Ynykron : SWWMWeapon
XZW9 AB 2;
XZW9 C 1 A_Parry(9);
XZW9 DE 1;
XZW9 F 1 A_Melee(100,"demolitionist/whitl",1.5,1.6,1.7);
XZW9 F 1 A_Melee(100,"demolitionist/whitl",1.5,1.6,1.7,MELEE_Wider);
XZW9 GHIJK 1;
XZW9 LMNO 2;
XZW9 P 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP);

View file

@ -1496,7 +1496,7 @@ Class SilverBullet : SWWMWeapon
XZW6 IJK 3;
XZW6 L 1 A_Parry(9);
XZW6 MN 1;
XZW6 O 1 A_Melee(90,"demolitionist/whitl",1.6,2.,2.);
XZW6 O 1 A_Melee(90,"demolitionist/whitl",1.6,2.,2.,MELEE_Wider);
XZW6 PQ 1;
XZW6 RST 2;
XZW6 U 3 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
@ -1513,7 +1513,7 @@ Class SilverBullet : SWWMWeapon
XZWC IJK 3;
XZWC L 1 A_Parry(9);
XZWC MN 1;
XZWC O 1 A_Melee(90,"demolitionist/whitl",1.6,2.,2.);
XZWC O 1 A_Melee(90,"demolitionist/whitl",1.6,2.,2.,MELEE_Wider);
XZWC PQ 1;
XZWC RST 2;
XZWC U 3 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP);