Quadravol melee and stance swap fully implemented.

Fix quick melee not hitting world geometry (oops).
This commit is contained in:
Mari the Deer 2022-08-19 21:58:44 +02:00
commit ab462cc05d
8 changed files with 565 additions and 61 deletions

View file

@ -78,28 +78,34 @@ Class Quadravol : SWWMWeapon
override Vector3 GetTraceOffset()
{
return (10,3,-2.5);
return onehand?(10,3.5,-2):(10,3,-2.5);
}
action State A_QuadFire()
action State A_QuadFire( bool bMelee = false )
{
static const String BaseNum[] = {"one","two","three","four","five"};
static const StateLabel FireStates[] = {"FireOne","FireTwo","FireThree","FireFour","FireFive"};
static const StateLabel FireStates1H[] = {"FireOne1H","FireTwo1H","FireThree1H","FireFour1H","FireFive1H"};
int idx = clamp(invoker.chargelevel-1,0,4);
A_StartSound("quadshot/fire"..BaseNum[idx],CHAN_WEAPON,CHANF_OVERLAP);
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+3*y-2.5*z);
double offy = invoker.onehand?3.5:3;
double offz = invoker.onehand?2:2.5;
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+offy*y-offz*z);
bool pointblank = false;
int rings = 1;
Vector3 dir;
FLineTraceData d;
Actor fool = null;
for ( double i=0; i<.16; i+=.04 )
{
for ( int j=0; j<360; j+=(360/rings) )
{
dir = SWWMUtility.ConeSpread(x,y,z,j,i);
pointblank |= LineTrace(atan2(dir.y,dir.x),60,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y);
pointblank |= LineTrace(atan2(dir.y,dir.x),60,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
if ( (d.HitType == TRACE_HitActor) && !fool ) fool = d.HitActor;
}
rings += 3;
}
@ -121,13 +127,15 @@ Class Quadravol : SWWMWeapon
p.angle = atan2(x2.y,x2.x);
p.pitch = asin(-x2.z);
p.vel = x2*p.speed;
if ( pointblank )
if ( pointblank || bMelee )
{
p.tracer = self;
p.special1 = 50;
p.special2 = 40000;
p.master = fool;
p.special1 = bMelee?100:50;
p.special2 = bMelee?120000:40000;
p.bAMBUSH = bMelee;
p.ExplodeMissile(null,null);
self.DamageMobj(invoker,self,40,'Fire',DMG_EXPLOSION);
self.DamageMobj(invoker,self,bAMBUSH?20:40,'Fire',DMG_EXPLOSION);
}
for ( int i=0; i<4; i++ )
{
@ -161,7 +169,8 @@ Class Quadravol : SWWMWeapon
}
invoker.chargelevel = 0;
invoker.charged = true; // eat it up
return ResolveState(FireStates[idx]);
if ( bMelee ) return ResolveState(null);
return invoker.onehand?ResolveState(FireStates1H[idx]):ResolveState(FireStates[idx]);
}
action void A_Eject()
@ -174,7 +183,8 @@ Class Quadravol : SWWMWeapon
if ( invoker.waschambered )
{
A_ChangeModel("",2,"","",5,"models",invoker.wascharged?"QuadCell_Used.png":"QuadCell.png",CMDL_USESURFACESKIN,-1);
A_Overlay(PSP_WEAPON+1,"DropCasing");
if ( invoker.onehand ) A_Overlay(PSP_WEAPON+1,"DropCasing1H");
else A_Overlay(PSP_WEAPON+1,"DropCasing");
}
}
@ -235,6 +245,16 @@ Class Quadravol : SWWMWeapon
invoker.Ammo1.Amount++;
}
action void A_FireBayonet()
{
A_StartSound("quadshot/bayonetfire",CHAN_WEAPON,CHANF_OVERLAP);
A_Melee(50,"spreadgun/slug",1.5,.8,-.5,MELEE_Rip|MELEE_FleshSound|MELEE_NoRage|MELEE_NoUse);
Vector3 dir = SWWMUtility.Vec3FromAngles(angle,pitch);
vel += dir*4.;
A_QuakeEx(4,4,4,8,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.75);
A_BumpFOV(1.03);
}
override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon )
{
bool good = Super.PickupForAmmoSWWM(ownedWeapon);
@ -298,7 +318,11 @@ Class Quadravol : SWWMWeapon
XZW1 A -1;
Stop;
Select:
XZW2 R 1 A_FullRaise();
XZW2 R 1
{
A_FullRaise();
return A_JumpIf(invoker.onehand,"Select1H");
}
XZW2 STUVWXYZ 1;
XZW3 ABCDEFGHI 1;
XZW3 JKLMN 2;
@ -356,7 +380,11 @@ Class Quadravol : SWWMWeapon
XZW5 AB 5;
Goto Ready;
AltFire:
XZW2 A 2 A_PlayerCheckGun();
XZW2 A 2
{
A_PlayerCheckGun();
return A_JumpIf(invoker.onehand,"AltFire1H");
}
XZW5 C 2;
XZW5 D 2 A_StartSound("quadshot/leverforward",CHAN_WEAPON,CHANF_OVERLAP);
XZW5 E 2 A_Eject();
@ -376,7 +404,8 @@ Class Quadravol : SWWMWeapon
XZW2 A 2
{
if ( (invoker.clipcount >= invoker.default.clipcount) || ((invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true)) )
return ResolveState("Idle");
return invoker.onehand?ResolveState("Idle1H"):ResolveState("Idle");
if ( invoker.onehand ) return ResolveState("Reload1H");
A_StartSound("quadshot/onehand",CHAN_WEAPON,CHANF_OVERLAP);
return ResolveState(null);
}
@ -393,11 +422,12 @@ Class Quadravol : SWWMWeapon
XZW5 P 2
{
if ( (invoker.clipcount >= invoker.default.clipcount) || ((invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true)) )
return ResolveState(null);
return A_JumpIf(invoker.onehand,"ReloadEnd1H");
if ( invoker.fromfire && (player.cmd.buttons&BT_ATTACK) )
return ResolveState("ReloadHold");
invoker.fromfire = false;
return A_JumpIf(player.cmd.buttons&BT_RELOAD,"ReloadHold");
if ( player.cmd.buttons&BT_RELOAD ) return ResolveState("ReloadHold");
return A_JumpIf(invoker.onehand,"ReloadEnd1H");
}
XZW6 B 2 A_StartSound("quadshot/twohand",CHAN_WEAPON,CHANF_OVERLAP);
XZW6 CDEF 2;
@ -408,22 +438,65 @@ Class Quadravol : SWWMWeapon
XZWZ O 0;
Stop;
Idle:
// TODO idle
XZW2 A 1 A_Log("\cg// TODO - Idle Animation\c-");
XZW2 A 1 A_JumpIf(!(player.cmd.buttons&BT_RELOAD),"Ready");
Wait;
XZW2 A 2 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP,.8);
XZW6 LMNO 2;
XZW6 PQRST 3;
XZW6 U 2 A_StartSound("quadshot/twohand",CHAN_WEAPON,CHANF_OVERLAP,.6);
XZW6 VW 2;
XZW6 XYZ 3;
XZW7 A 3;
Goto Ready;
Zoom:
// TODO stance switch
XZW2 A 1 A_Log("\cg// TODO - Stance Switch\c-");
XZW2 A 1 A_JumpIf(!(player.cmd.buttons&BT_ZOOM),"Ready");
Wait;
XZW2 A 2
{
if ( invoker.onehand )
{
invoker.onehand = false;
return ResolveState("Zoom1H");
}
A_StartSound("quadshot/onehand",CHAN_WEAPON,CHANF_OVERLAP);
invoker.onehand = true;
return ResolveState(null);
}
XZW8 Z 2;
XZW9 ABCDEF 2;
Goto Ready1H;
User1:
// TODO melee
XZW2 A 1 A_Log("\cg// TODO - Quick Melee\c-");
XZW2 A 1 A_JumpIf(!(player.cmd.buttons&BT_USER1),"Ready");
Wait;
XZW2 A 2
{
A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
return A_JumpIf(invoker.onehand,"User11H");
}
XZW7 BCDEF 2;
XZW7 G 2 A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
XZW7 H 2 A_Parry(6);
XZW7 I 1;
XZW7 J 1 A_JumpIf(A_Melee(10,"demolitionist/whitm",1.5,.8,.2),"Bayonet");
XZW7 KLM 1;
XZW7 NOPQRST 2;
XZW7 UVWX 3;
Goto Ready;
Bayonet:
XZW7 J 1 A_FireBayonet();
XZW7 YZ 2 A_JumpIf((player.cmd.buttons&BT_ATTACK)&&(invoker.chargelevel>0),"BayonetFire");
XZW8 ABC 2 A_JumpIf((player.cmd.buttons&BT_ATTACK)&&(invoker.chargelevel>0),"BayonetFire");
XZW8 D 1 A_StartSound("quadshot/bayonetreturn",CHAN_WEAPON,CHANF_OVERLAP);
XZW8 EFGH 1;
XZW8 IJKL 2;
Goto Ready;
BayonetFire:
#### # 1 A_QuadFire(true);
XZW8 MNO 1;
XZW8 P 1 A_StartSound("quadshot/bayonetreturn",CHAN_WEAPON,CHANF_OVERLAP);
XZW8 QRST 1;
XZW8 UVWXY 2;
Goto Ready;
Deselect:
XZW2 A 1 A_StartSound("quadshot/deselect",CHAN_WEAPON,CHANF_OVERLAP);
XZW2 A 1
{
A_StartSound(invoker.onehand?"quadshot/deselectfast":"quadshot/deselect",CHAN_WEAPON,CHANF_OVERLAP);
return A_JumpIf(invoker.onehand,"Deselect1H");
}
XZW2 BCDEFGHIJKLMNOPQR 1;
XZW2 R -1 A_FullLower();
Stop;
@ -431,11 +504,139 @@ Class Quadravol : SWWMWeapon
XZW0 A 2 Bright
{
let psp = player.GetPSprite(PSP_FLASH);
psp.frame = Random[GunFlash](0,9);
psp.frame = Random[GunFlash](0,9)+invoker.onehand*10;
let l = Spawn("SWWMWeaponLight",pos);
l.target = self;
}
Stop;
// TODO one-handed states
// one-handed states
Select1H:
XZW9 N 2 A_FullRaise();
XZW9 OPQRSTUVW 2;
Goto Ready1H;
Ready1H:
XZW9 G 1
{
A_Fill(); // just in case
int flg = WRF_ALLOWRELOAD|WRF_ALLOWUSER1|WRF_ALLOWZOOM;
if ( (invoker.chargelevel <= 0) && (invoker.Ammo1.Amount <= 0) && (invoker.clipcount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) flg |= WRF_NOPRIMARY;
invoker.fromfire = false;
A_WeaponReady(flg);
if ( player.cmd.buttons&BT_ATTACK )
invoker.CheckAmmo(EitherFire,true);
}
Wait;
FireOne1H:
XZW9 G 1;
XZW9 XYZ 1;
XZWA A 1;
XZWA BCDE 2;
Goto Ready1H;
FireTwo1H:
XZW9 G 1;
XZWA FG 1;
XZWA HI 2;
XZWA JKLM 3;
Goto Ready1H;
FireThree1H:
XZW9 G 1;
XZWA NOPQ 2;
XZWA RSTU 3;
Goto Ready1H;
FireFour1H:
XZW9 G 1;
XZWA VWXY 2;
XZWA Z 3;
XZWB A 3;
XZWB BC 4;
Goto Ready1H;
FireFive1H:
XZW9 G 1;
XZWB DE 2;
XZWB FG 3;
XZWB HI 4;
XZWB JK 5;
Goto Ready1H;
AltFire1H:
XZW9 G 2;
XZWB L 2;
XZWB M 1
{
A_StartSound("quadshot/leverforward",CHAN_WEAPON,CHANF_OVERLAP);
A_Eject();
}
XZWB NO 1;
XZWB P 2 A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP,pitch:.6);
XZWB QR 2;
XZWB S 2 A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP,pitch:.5);
XZWB TUV 2;
XZWB W 2 A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP,pitch:.4);
XZWB XY 1;
XZWB Z 1 A_StartSound("quadshot/leverback",CHAN_WEAPON,CHANF_OVERLAP);
XZWC A 2;
XZWC B 2 A_Fill();
XZWC CDEF 3;
Goto Ready1H;
DropCasing1H:
XZWZ PQRSTUVW 1;
XZWZ X 0;
TNT1 A 1 A_DropCasing();
Stop;
Idle1H:
XZW9 G 2 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP,.8);
XZWC JKLM 2;
XZWC NOPQR 3;
XZWC STU 2;
XZWC VWX 3;
XZWC Y 3;
Goto Ready1H;
Zoom1H:
XZW9 G 2;
XZWE XYZ 2;
XZWF AB 2;
XZWF C 1 A_StartSound("quadshot/twohand",CHAN_WEAPON,CHANF_OVERLAP);
XZWF DEFG 1;
XZWF HIJKL 2;
Goto Ready;
User11H:
XZW9 G 2;
XZWC Z 2;
XZWD ABCD 2;
XZWD E 2 A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
XZWD F 2 A_Parry(6);
XZWD G 1;
XZWD H 1 A_JumpIf(A_Melee(10,"demolitionist/whitm",1.5,.8,.2),"Bayonet1H");
XZWD IJK 1;
XZWD LMNOPQR 2;
XZWD STUV 3;
Goto Ready1H;
Bayonet1H:
XZWD H 1 A_FireBayonet();
XZWD WX 2 A_JumpIf((player.cmd.buttons&BT_ATTACK)&&(invoker.chargelevel>0),"BayonetFire1H");
XZWD YZ 2 A_JumpIf((player.cmd.buttons&BT_ATTACK)&&(invoker.chargelevel>0),"BayonetFire1H");
XZWE A 2 A_JumpIf((player.cmd.buttons&BT_ATTACK)&&(invoker.chargelevel>0),"BayonetFire1H");
XZWE B 1 A_StartSound("quadshot/bayonetreturn",CHAN_WEAPON,CHANF_OVERLAP);
XZWE CDEF 1;
XZWE GHIJ 2;
Goto Ready1H;
BayonetFire1H:
#### # 1 A_QuadFire(true);
XZWE KLM 1;
XZWE N 1 A_StartSound("quadshot/bayonetreturn",CHAN_WEAPON,CHANF_OVERLAP);
XZWE OPQR 1;
XZWE STUVW 2;
Goto Ready1H;
Deselect1H:
XZW9 GHIJKLM 2;
XZW9 N -1 A_FullLower();
Stop;
Reload1H:
XZW9 G 2;
XZWC G 3;
Goto ReloadHold;
ReloadEnd1H:
XZW5 P 2;
XZWC HI 3;
Goto Ready1H;
}
}

View file

@ -258,12 +258,13 @@ Class QuadProj : Actor
{
Vector3 rel = level.Vec3Diff(master.pos,pos);
double hdiff = 1.-2.*clamp((rel.z-4.)/master.height,0.,.5);
SWWMUtility.DoKnockback(master,x+(0,0,hdiff*.5),200000);
SWWMUtility.DoKnockback(master,x+(0,0,hdiff*.5),200000+special2);
}
if ( tracer ) SWWMUtility.DoKnockback(tracer,-x+(0,0,.1),500000);
if ( tracer ) SWWMUtility.DoKnockback(tracer,-x+(0,0,.1),bAMBUSH?150000:500000);
SWWMUtility.DoExplosion(self,0,80000+special2,240,80,DE_BLAST|DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,100+special1,0,240,0,DE_QUADRAVOL|DE_NOSPLASH,ignoreme:tracer);
SWWMUtility.DoExplosion(self,150+special1,0,200,80,DE_HOWL|DE_NOSPLASH,ignoreme:tracer);
if ( SWWMUtility.DoExplosion(self,150+special1,0,200,80,DE_HOWL|DE_NOSPLASH,ignoreme:tracer) && bAMBUSH && target )
SWWMUtility.AchievementProgressInc("roast",1,target.player);
A_StartSound("quadshot/hit",CHAN_WEAPON,attenuation:.8);
A_StartSound("quadshot/hit",CHAN_VOICE,attenuation:.6);
A_AlertMonsters(swwm_uncapalert?0:1500);
@ -296,6 +297,13 @@ Class QuadProj : Actor
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,150,SWWMUtility.Vec3FromAngles(angle,pitch));
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
// from melee combo
if ( bAMBUSH ) return StringTable.Localize("$O_QUADRAVOL2");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
Default
{
Obituary "$O_QUADRAVOL";

View file

@ -371,7 +371,9 @@ extend Class SWWMWeapon
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
MELEE_HammerHit = 128 // is hammer melee (gibbing counts for "HAHA DAB" achievement)
MELEE_HammerHit = 128, // is hammer melee (gibbing counts for "HAHA DAB" achievement)
MELEE_NoRage = 256, // unaffected by ragekit
MELEE_NoUse = 512 // do not pass use actions to this melee
};
action void A_Parry( int duration )
@ -401,7 +403,7 @@ extend Class SWWMWeapon
int rings = 1;
double step = spread/20.;
double range = 1.5*DEFMELEERANGE*rangemul;
bool raging = CountInv("RagekitPower");
bool raging = (flags&MELEE_NoRage)?false: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 )
{
@ -499,38 +501,42 @@ extend Class SWWMWeapon
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 ( flags&MELEE_NoRage ) raging = null;
if ( raging ) rangemul += .2;
Vector3 origin = Vec3Offset(0,0,player.viewheight);
Vector3 dir = SWWMUtility.Vec3FromAngles(angle,pitch);
// check for usables
if ( !invoker.ut ) invoker.ut = new("UseLineTracer");
let ut = invoker.ut; // for convenience
ut.uses.Clear();
ut.Trace(origin,level.PointInSector(origin.xy),dir,DEFMELEERANGE*rangemul,0);
invoker.wallponch = true;
for ( int i=0; i<ut.uses.Size(); i++ )
if ( !(flags&MELEE_NoUse) )
{
if ( ut.uses[i].hitactor )
if ( !invoker.ut ) invoker.ut = new("UseLineTracer");
let ut = invoker.ut; // for convenience
ut.uses.Clear();
ut.Trace(origin,level.PointInSector(origin.xy),dir,DEFMELEERANGE*rangemul,0);
invoker.wallponch = true;
for ( int i=0; i<ut.uses.Size(); i++ )
{
// punching is not greeting/patting (that'd be weird)
if ( (ut.uses[i].hitactor == self) || (ut.uses[i].hitactor is 'Demolitionist')
|| (ut.uses[i].hitactor is 'HeadpatTracker')
|| (ut.uses[i].hitactor is 'FroggyChair') ) continue;
if ( ut.uses[i].hitactor.Used(self) ) break;
}
else if ( ut.uses[i].hitline && UseLineTracer.TangibleLine(ut.uses[i]) )
{
int locknum = SWWMUtility.GetLineLock(ut.uses[i].hitline);
if ( !locknum || CheckKeys(locknum,false,true) )
ut.uses[i].hitline.RemoteActivate(self,ut.uses[i].hitside,SPAC_Use,ut.uses[i].pos);
if ( !(ut.uses[i].hitline.activation&SPAC_UseThrough) ) break;
if ( ut.uses[i].hitactor )
{
// punching is not greeting/patting (that'd be weird)
if ( (ut.uses[i].hitactor == self) || (ut.uses[i].hitactor is 'Demolitionist')
|| (ut.uses[i].hitactor is 'HeadpatTracker')
|| (ut.uses[i].hitactor is 'FroggyChair') ) continue;
if ( ut.uses[i].hitactor.Used(self) ) break;
}
else if ( ut.uses[i].hitline && UseLineTracer.TangibleLine(ut.uses[i]) )
{
int locknum = SWWMUtility.GetLineLock(ut.uses[i].hitline);
if ( !locknum || CheckKeys(locknum,false,true) )
ut.uses[i].hitline.RemoteActivate(self,ut.uses[i].hitside,SPAC_Use,ut.uses[i].pos);
if ( !(ut.uses[i].hitline.activation&SPAC_UseThrough) ) break;
}
}
invoker.wallponch = false;
// check for shootables
SWWMBulletTrail.DoTrail(self,origin,dir,DEFMELEERANGE*rangemul,0);
}
invoker.wallponch = false;
// check for shootables
SWWMBulletTrail.DoTrail(self,origin,dir,DEFMELEERANGE*rangemul,0);
bool res = TryMelee((raging?.3:.2)*spreadmul,dmg,hitsound,rangemul,kickmul,flags);
if ( !flags&MELEE_Rip ) return res;
if ( res && !(flags&MELEE_Rip) ) return res;
// check for walls instead
FTranslatedLineTarget t;
double slope = AimLineAttack(angle,DEFMELEERANGE*rangemul,t,0.,ALF_CHECK3D);
@ -552,7 +558,7 @@ extend Class SWWMWeapon
{
HitNormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) HitNormal *= -1;
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
if ( !(flags&MELEE_NoUse) ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
}
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation+HitNormal*4);
p.angle = atan2(HitNormal.y,HitNormal.x);