Updated blood and gore code, it's less WIP now. Updated readme with some additional notes. Removed Heretic compat player classes as they are no longer needed.
312 lines
9.1 KiB
Text
312 lines
9.1 KiB
Text
Class HammerImpact : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_SprayDecal("ImpactMark",20);
|
|
int numpt = Random[Impact](20,40);
|
|
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (-x+(FRandom[Impact](-.8,.8),FRandom[Impact](-.8,.8),FRandom[Impact](-.8,.8))).unit()*FRandom[Impact](0.1,1.2);
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Impact](128,192));
|
|
}
|
|
numpt = Random[Impact](4,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Impact](-1,1),FRandom[Impact](-1,1),FRandom[Impact](-1,1)).unit()*FRandom[Impact](2,8);
|
|
let s = Spawn("UTSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Impact](4,16);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Impact](-1,1),FRandom[Impact](-1,1),FRandom[Impact](-1,1)).unit()*FRandom[Impact](2,8);
|
|
let s = Spawn("UTChip",pos);
|
|
s.vel = pvel;
|
|
}
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
Class ImpactHammer : UTWeapon
|
|
{
|
|
double chargesize, count;
|
|
|
|
action void A_ResetCharge()
|
|
{
|
|
invoker.chargesize = 0;
|
|
invoker.count = 0;
|
|
invoker.FireEffect(); // intentional UT behavior
|
|
}
|
|
action void A_ChargeUp( int amt = 1 )
|
|
{
|
|
invoker.chargesize += (0.75*amt)/TICRATE;
|
|
invoker.count += double(amt)/TICRATE;
|
|
if ( invoker.count > 0.2 )
|
|
{
|
|
invoker.count = 0;
|
|
A_AlertMonsters();
|
|
}
|
|
A_QuakeEx(clamp(int(invoker.chargesize*3),0,3),clamp(int(invoker.chargesize*3),0,3),clamp(int(invoker.chargesize*3),0,3),amt+1,0,96,"",QF_RELATIVE,rollIntensity:clamp(invoker.chargesize*0.3,0,0.3));
|
|
UTMainHandler.DoSwing(self,(FRandom[Impact](-1,1),FRandom[Impact](-1,1)),invoker.chargesize*0.1,0,1,SWING_Spring);
|
|
}
|
|
action void A_FireBlast()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
A_PlaySound("impact/release",CHAN_WEAPON);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoSwing(self,(FRandom[Impact](-0.3,-1.5),FRandom[Impact](-1.2,-0.4)),3,-0.8,3,SWING_Spring,3,2);
|
|
A_AlertMonsters();
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
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,60,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
int dmg = int(60*realcharge);
|
|
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'impact',DMG_THRUSTLESS);
|
|
d.HitActor.vel = x*(8000/d.HitActor.mass)*realcharge;
|
|
if ( d.HitActor.bNOBLOOD )
|
|
{
|
|
let p = Spawn("HammerImpact",d.HitLocation-d.HitDir*4);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x);
|
|
p.pitch = asin(-d.HitDir.z);
|
|
}
|
|
else
|
|
{
|
|
d.HitActor.TraceBleed(dmg,invoker);
|
|
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
|
|
}
|
|
}
|
|
else if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
realcharge = max(1.0,realcharge);
|
|
int dmg = int(24*realcharge); // It's a flat damage on UT, but I think it's more fair for it to scale
|
|
dmg = DamageMobj(invoker,self,dmg,'impact',DMG_THRUSTLESS);
|
|
TraceBleed(dmg,invoker);
|
|
vel -= x*(1200/mass)*realcharge;
|
|
let p = Spawn("HammerImpact",d.HitLocation-d.HitDir*4);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x);
|
|
p.pitch = asin(-d.HitDir.z);
|
|
if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
|
|
}
|
|
A_QuakeEx(int(realcharge*6),int(realcharge*6),int(realcharge*6),16,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:realcharge*0.2);
|
|
realcharge = max(1.0,realcharge);
|
|
int numpt = Random[Impact](5,10);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4))).unit()*FRandom[Impact](1,3)*realcharge;
|
|
let s = Spawn("UTSmoke",origin);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Impact](128,192));
|
|
}
|
|
numpt = Random[Impact](4,8);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5))).unit()*FRandom[Impact](2,16)*realcharge;
|
|
let s = Spawn("UTChip",origin);
|
|
s.vel = pvel;
|
|
s.scale *= 0.4;
|
|
}
|
|
invoker.chargesize = 0;
|
|
invoker.count = 0;
|
|
}
|
|
action void A_FireAltBlast()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
A_PlaySound("impact/fire",CHAN_WEAPON);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoSwing(self,(FRandom[Impact](-0.3,-1.5),FRandom[Impact](-1.2,-0.4)),2,-0.6,2,SWING_Spring,1,2);
|
|
A_AlertMonsters();
|
|
A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.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,120,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
|
double dscale = d.Distance/120.;
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
int dmg = int(20*dscale);
|
|
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'impact',DMG_THRUSTLESS);
|
|
d.HitActor.vel = x*(4000/d.HitActor.mass)*dscale;
|
|
}
|
|
else if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
int dmg = int(24*dscale);
|
|
dmg = DamageMobj(invoker,self,dmg,'impact',DMG_THRUSTLESS);
|
|
vel -= x*(600/mass)*dscale;
|
|
if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
|
|
}
|
|
// push aside projectiles
|
|
let t = ThinkerIterator.Create("Actor");
|
|
Actor m;
|
|
while ( m = Actor(t.Next()) )
|
|
{
|
|
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 > 400) || (rdir dot x < 0.9) ) continue;
|
|
m.speed = m.vel.length();
|
|
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;
|
|
}
|
|
int numpt = Random[Impact](5,10);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4))).unit()*FRandom[Impact](1,3);
|
|
let s = Spawn("UTSmoke",origin);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Impact](128,192));
|
|
}
|
|
numpt = Random[Impact](4,8);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5))).unit()*FRandom[Impact](2,16);
|
|
let s = Spawn("UTChip",origin);
|
|
s.vel = pvel;
|
|
s.scale *= 0.4;
|
|
}
|
|
}
|
|
action void A_ImpactRefire( statelabel flash = null )
|
|
{
|
|
FLineTraceData d;
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+3.0*y-4.0*z;
|
|
LineTrace(angle,40,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
|
if ( (invoker.chargesize > 1) && (d.HitType == TRACE_HitActor) )
|
|
{
|
|
A_ClearRefire();
|
|
return;
|
|
}
|
|
A_Refire(flash);
|
|
}
|
|
override void DetachFromOwner()
|
|
{
|
|
if ( Owner ) Owner.A_StopSound(CHAN_WEAPON);
|
|
Super.DetachFromOwner();
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$T_IMPACTHAMMER";
|
|
Obituary "$O_IMPACTHAMMER";
|
|
Inventory.PickupMessage "$I_IMPACTHAMMER";
|
|
Weapon.UpSound "impact/select";
|
|
Weapon.SlotNumber 1;
|
|
Weapon.SelectionOrder 9;
|
|
+WEAPON.MELEEWEAPON;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
IMPP A -1;
|
|
Stop;
|
|
IMPP B -1;
|
|
Stop;
|
|
Select:
|
|
IMPS A 1 A_Raise(int.max);
|
|
Wait;
|
|
Ready:
|
|
IMPS ABCDEFGHIJKLMNOPQ 1 A_WeaponReady(WRF_NOFIRE);
|
|
Idle:
|
|
IMPI A 1
|
|
{
|
|
invoker.chargesize = 0;
|
|
invoker.count = 0;
|
|
A_WeaponReady();
|
|
}
|
|
Wait;
|
|
Fire:
|
|
IMPL A 0
|
|
{
|
|
A_ResetCharge();
|
|
A_PlaySound("impact/pull",CHAN_WEAPON);
|
|
}
|
|
IMPL A 5 A_ChargeUp(5);
|
|
IMPL B 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPL B 5 A_ChargeUp(5);
|
|
IMPL C 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPL C 5 A_ChargeUp(5);
|
|
IMPL D 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPL D 5 A_ChargeUp(5);
|
|
IMPL E 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPL E 5 A_ChargeUp(5);
|
|
IMPL E 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPL E 0 A_PlaySound("impact/loop",CHAN_WEAPON,looping:true);
|
|
Hold:
|
|
IMPR A 1 A_ChargeUp();
|
|
IMPR B 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR B 1 A_ChargeUp();
|
|
IMPR C 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR C 1 A_ChargeUp();
|
|
IMPR D 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR D 1 A_ChargeUp();
|
|
IMPR E 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR E 1 A_ChargeUp();
|
|
IMPR F 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR F 1 A_ChargeUp();
|
|
IMPR G 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR G 1 A_ChargeUp();
|
|
IMPR H 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR H 1 A_ChargeUp();
|
|
IMPR I 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR I 1 A_ChargeUp();
|
|
IMPR J 0 A_ImpactRefire(1);
|
|
Goto Release;
|
|
IMPR J 1 A_ChargeUp();
|
|
IMPR A 0 A_ImpactRefire("Hold");
|
|
Goto Release;
|
|
Release:
|
|
IMPF A 0
|
|
{
|
|
if ( self is 'UTPlayer' )
|
|
UTPlayer(self).PlayAttacking3();
|
|
A_FireBlast();
|
|
}
|
|
IMPF AABCCDEEFGGHIIJKKLMMNOOP 1;
|
|
Goto Idle;
|
|
AltFire:
|
|
IMPF A 0 A_FireAltBlast();
|
|
IMPF ABCDEFGHIJKLMNOP 1;
|
|
Goto Idle;
|
|
Deselect:
|
|
IMPD A 0 A_StopSound(CHAN_WEAPON);
|
|
IMPD ABCDE 2;
|
|
IMPD E 1 A_Lower(int.max);
|
|
Wait;
|
|
}
|
|
}
|