1611 lines
48 KiB
Text
1611 lines
48 KiB
Text
// Plutoni Inc. Mortal Rifle (from UnSX 2)
|
|
// Slot 9, spawns shared with Candygun
|
|
|
|
Class MRHitListEntry : HitListEntry
|
|
{
|
|
// just needs this
|
|
bool bExit;
|
|
}
|
|
|
|
// used for splitting model beam between crossed portals (if any)
|
|
Class MRRailSeg
|
|
{
|
|
Vector3 enter, exit;
|
|
}
|
|
|
|
// like the silver bullet tracer except there's no penetration factor
|
|
// just a maximum possible travel distance (which is mainly used for probing through walls)
|
|
Class MisterRailTracer : LineTracer
|
|
{
|
|
Actor ignoreme;
|
|
Array<MRHitListEntry> hitlist;
|
|
Array<Line> shootthroughlist;
|
|
Array<WaterHit> waterhitlist;
|
|
Array<MRRailSeg> portalseg;
|
|
|
|
double maxdist;
|
|
|
|
bool pastwall, fullstop;
|
|
Array<WallPenetrate> WallPenetrateList;
|
|
Vector3 exitpoint;
|
|
transient Array<F3DFloor> ffloors; // needs to be done like this because HAHA SCOPE
|
|
|
|
override ETraceStatus TraceCallback()
|
|
{
|
|
// liquid splashes
|
|
if ( Results.CrossedWater )
|
|
{
|
|
let hl = new("WaterHit");
|
|
hl.sect = Results.CrossedWater;
|
|
hl.hitpos = Results.CrossedWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
else if ( Results.Crossed3DWater )
|
|
{
|
|
let hl = new("WaterHit");
|
|
hl.sect = Results.Crossed3DWater;
|
|
hl.hitpos = Results.Crossed3DWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
if ( Results.HitType == TRACE_CrossingPortal )
|
|
{
|
|
let seg = new("MRRailSeg");
|
|
seg.enter = Results.HitPos;
|
|
seg.exit = Results.SrcFromTarget;
|
|
portalseg.Push(seg);
|
|
}
|
|
else if ( Results.HitType == TRACE_HitActor )
|
|
{
|
|
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
|
|
if ( Results.HitActor.bSHOOTABLE )
|
|
{
|
|
let ent = new("MRHitListEntry");
|
|
ent.hitactor = Results.HitActor;
|
|
ent.hitlocation = Results.HitPos;
|
|
ent.x = Results.HitVector;
|
|
ent.pastwall = pastwall;
|
|
ent.bExit = false;
|
|
hitlist.Push(ent);
|
|
// also include exit point if we can reach it
|
|
Vector3 exit = SWWMUtility.TraceExit(Results.HitActor,Results.HitPos,Results.HitVector);
|
|
if ( level.Vec3Diff(Results.HitPos,exit).length() > (maxdist-Results.Distance) )
|
|
return TRACE_Skip; // exit point is beyond our max distance
|
|
ent = new("MRHitListEntry");
|
|
ent.hitactor = Results.HitActor;
|
|
ent.hitlocation = exit;
|
|
ent.x = Results.HitVector;
|
|
ent.pastwall = pastwall;
|
|
ent.bExit = true;
|
|
hitlist.Push(ent);
|
|
return TRACE_Skip;
|
|
}
|
|
return TRACE_Skip;
|
|
}
|
|
else if ( Results.HitType == TRACE_HasHitSky )
|
|
{
|
|
fullstop = true;
|
|
return TRACE_Stop;
|
|
}
|
|
else if ( Results.HitType != TRACE_HitNone )
|
|
{
|
|
if ( Results.HitType == TRACE_HitWall )
|
|
{
|
|
ShootThroughList.Push(Results.HitLine);
|
|
if ( (Results.Tier == TIER_Middle) && Results.HitLine.sidedef[1] && !(Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
|
|
return TRACE_Skip;
|
|
}
|
|
int maxstep = int(maxdist-Results.Distance);
|
|
for ( int i=1; i<=maxstep; i++ )
|
|
{
|
|
Vector3 ofs = Results.HitPos+Results.HitVector*i;
|
|
if ( level.IsPointInLevel(ofs) )
|
|
{
|
|
// double-check if we're piercing through a 3D floor (yes this is a thing that happens, oh boy)
|
|
Sector s = level.PointInSector(ofs.xy);
|
|
bool stop3d = false;
|
|
for ( int j=0; j<ffloors.Size(); j++ )
|
|
{
|
|
if ( ffloors[j].target != s ) continue;
|
|
double minz = ffloors[j].bottom.ZAtPoint(ofs.xy);
|
|
double maxz = ffloors[j].top.ZAtPoint(ofs.xy);
|
|
if ( (ofs.z < minz) || (ofs.z > maxz) ) continue;
|
|
stop3d = true;
|
|
break;
|
|
}
|
|
if ( stop3d ) continue;
|
|
let wp = new("WallPenetrate");
|
|
wp.hittype = Results.HitType;
|
|
wp.hitline = Results.HitLine;
|
|
wp.hitside = Results.Side;
|
|
wp.hittier = Results.Tier;
|
|
wp.hitsector = Results.HitSector;
|
|
wp.hitffloor = Results.ffloor;
|
|
wp.hitpos = Results.HitPos;
|
|
wp.hitdir = Results.HitVector;
|
|
wp.bustdir = Results.HitVector;
|
|
if ( Results.HitType == TRACE_HitWall )
|
|
{
|
|
wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit();
|
|
if ( !Results.Side ) wp.hitnormal *= -1;
|
|
ShootThroughList.Push(Results.HitLine);
|
|
}
|
|
else if ( Results.HitType == TRACE_HitCeiling )
|
|
wp.hitnormal = Results.HitSector.ceilingplane.Normal;
|
|
else if ( Results.HitType == TRACE_HitFloor )
|
|
wp.hitnormal = Results.HitSector.floorplane.Normal;
|
|
wp.pastwall = pastwall;
|
|
WallPenetrateList.Push(wp);
|
|
pastwall = true;
|
|
// trace backwards to find exit surface
|
|
let at = new("AuxiliarySilverBulletTracer");
|
|
at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,0);
|
|
let wp2 = new("WallPenetrate");
|
|
wp2.hittype = at.Results.HitType;
|
|
wp2.hitline = at.Results.HitLine;
|
|
wp2.hitside = at.Results.Side;
|
|
wp2.hittier = at.Results.Tier;
|
|
wp2.hitsector = at.Results.HitSector;
|
|
wp2.hitffloor = at.Results.ffloor;
|
|
wp2.hitside = at.Results.Side;
|
|
wp2.hitpos = at.Results.HitPos;
|
|
wp2.hitdir = at.Results.HitVector;
|
|
wp2.bustdir = -at.Results.HitVector;
|
|
if ( at.Results.HitType == TRACE_HitWall )
|
|
{
|
|
wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit();
|
|
if ( !at.Results.Side ) wp2.hitnormal *= -1;
|
|
if ( at.Results.HitLine.sidedef[1] )
|
|
ShootThroughList.Push(at.Results.HitLine);
|
|
}
|
|
else if ( at.Results.HitType == TRACE_HitCeiling )
|
|
wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal;
|
|
else if ( at.Results.HitType == TRACE_HitFloor )
|
|
wp2.hitnormal = at.Results.HitSector.floorplane.Normal;
|
|
else wp2.hitnormal = wp2.hitdir;
|
|
wp2.pastwall = pastwall;
|
|
WallPenetrateList.Push(wp2);
|
|
fullstop = false;
|
|
exitpoint = ofs;
|
|
return TRACE_Stop;
|
|
}
|
|
}
|
|
fullstop = true;
|
|
return TRACE_Stop;
|
|
}
|
|
return TRACE_Skip;
|
|
}
|
|
}
|
|
|
|
Class MisterRailCounter : Thinker
|
|
{
|
|
PlayerInfo player;
|
|
Array<Actor> effectors;
|
|
int nkill;
|
|
|
|
override void Tick()
|
|
{
|
|
int neff = 0;
|
|
for ( int i=0; i<effectors.Size(); i++ )
|
|
{
|
|
if ( !effectors[i] ) continue;
|
|
neff++;
|
|
}
|
|
if ( neff > 0 ) return;
|
|
SWWMUtility.AchievementProgress("rail",nkill,player);
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
Class MisterRifle : SWWMWeapon
|
|
{
|
|
int clipcount;
|
|
bool chambered, fired;
|
|
bool gchambered, gfired;
|
|
bool boltlock;
|
|
bool waschambered;
|
|
bool wasgchambered;
|
|
double prefirecnt;
|
|
transient int holdtic;
|
|
int firemode;
|
|
ui int lastfiremode;
|
|
String serialnum;
|
|
|
|
transient ui SmoothLinearValueInterpolator PreFireInter;
|
|
|
|
// for alerts in the canvas
|
|
transient int lowammotic, noammotic;
|
|
|
|
Property ClipCount : clipcount;
|
|
|
|
override void InitializeWeapon()
|
|
{
|
|
// no round in the chamber
|
|
chambered = false;
|
|
fired = false;
|
|
// no grenade loaded
|
|
gchambered = false;
|
|
gfired = false;
|
|
// bolt isn't locked
|
|
boltlock = false;
|
|
// generate a serial number
|
|
serialnum = "00000";
|
|
SWWMUtility.ObscureText(serialnum,Random[MRifle](),true);
|
|
}
|
|
|
|
override void HudTick()
|
|
{
|
|
Super.HudTick();
|
|
if ( !PreFireInter ) PreFireInter = SmoothLinearValueInterpolator.Create(prefirecnt,100.);
|
|
PreFireInter.Update(prefirecnt);
|
|
if ( lastfiremode && (lastfiremode != firemode+1) && (Owner.player == players[consoleplayer]) )
|
|
{
|
|
let bar = SWWMStatusBar(statusbar);
|
|
if ( bar )
|
|
{
|
|
bar.ntagstr = StringTable.Localize("$SWWM_MRMODE"..(firemode+1));
|
|
bar.ntagtic = level.totaltime;
|
|
bar.ntagcol = nametagcolor;
|
|
}
|
|
}
|
|
lastfiremode = firemode+1;
|
|
}
|
|
|
|
override Vector3 GetTraceOffset( int index )
|
|
{
|
|
return (10,2.8,-2.4);
|
|
}
|
|
|
|
override bool ReportHUDAmmo()
|
|
{
|
|
return (chambered&&!fired)||(clipcount>0)||(Ammo1.Amount>0)||(gchambered&&!gfired)||(Ammo2.Amount>0);
|
|
}
|
|
|
|
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
|
|
{
|
|
if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true;
|
|
if ( firemode == PrimaryFire ) return (chambered&&!fired)||(clipcount>0)||(Ammo1.Amount>0)||(Owner.CountInv("MisterRound")>0);
|
|
if ( firemode == AltFire ) return (gchambered&&!gfired)||(Ammo2.Amount>0);
|
|
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
|
|
}
|
|
|
|
override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon )
|
|
{
|
|
bool good = Super.PickupForAmmoSWWM(ownedWeapon);
|
|
let Owner = ownedWeapon.Owner;
|
|
if ( AmmoGive1 == 0 )
|
|
{
|
|
// give bullets?
|
|
if ( (clipcount > 0) || (chambered && !fired) )
|
|
{
|
|
Inventory cur = Owner.FindInventory("MisterRound");
|
|
if ( !cur )
|
|
{
|
|
cur = Inventory(Spawn("MisterRound"));
|
|
cur.Amount = 0;
|
|
cur.AttachToOwner(Owner);
|
|
}
|
|
int ingun = clipcount+(chambered&&!fired);
|
|
int maxgiveamt = min(cur.MaxAmount-cur.Amount,ingun);
|
|
int dropamt = ingun-maxgiveamt;
|
|
if ( dropamt > 0 ) cur.CreateTossable(dropamt);
|
|
cur.Amount = min(cur.MaxAmount,cur.Amount+ingun);
|
|
good = true;
|
|
}
|
|
// give grenade?
|
|
if ( gchambered && !gfired )
|
|
{
|
|
Inventory cur = Owner.FindInventory("MisterGAmmo");
|
|
if ( !cur )
|
|
{
|
|
cur = Inventory(Spawn("MisterGAmmo"));
|
|
cur.Amount = 0;
|
|
cur.AttachToOwner(Owner);
|
|
}
|
|
int ingun = (gchambered&&!gfired);
|
|
int maxgiveamt = min(cur.MaxAmount-cur.Amount,ingun);
|
|
int dropamt = ingun-maxgiveamt;
|
|
if ( dropamt > 0 ) cur.CreateTossable(dropamt);
|
|
cur.Amount = min(cur.MaxAmount,cur.Amount+ingun);
|
|
good = true;
|
|
}
|
|
}
|
|
return good;
|
|
}
|
|
|
|
override void PlayUpSound( Actor origin )
|
|
{
|
|
if ( (clipcount > 0) && (!chambered || fired) ) origin.A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
else origin.A_StartSound(UpSound,CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
|
|
action void A_AmmoAlert( bool bOnReload = false )
|
|
{
|
|
int curammo = invoker.clipcount+(invoker.chambered&&!invoker.fired);
|
|
if ( curammo == 0 )
|
|
{
|
|
// no ammo alert
|
|
if ( CheckLocalView() ) A_StartSound("mister/noammo",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.noammotic = gametic+50;
|
|
}
|
|
else if ( (curammo == 4) || (bOnReload && (curammo <= 4)) )
|
|
{
|
|
// low ammo alert
|
|
if ( CheckLocalView() ) A_StartSound("mister/lowammo",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.lowammotic = gametic+40;
|
|
}
|
|
}
|
|
|
|
action void A_BoltBack( bool bCasing = false )
|
|
{
|
|
A_StartSound("mister/boltback",CHAN_WEAPON,CHANF_OVERLAP);
|
|
if ( bCasing )
|
|
{
|
|
invoker.waschambered = invoker.chambered;
|
|
A_ChangeModel("",1,"","",10,"models","MortalRound_Used.png",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
invoker.chambered = invoker.fired = false;
|
|
A_AmmoAlert();
|
|
}
|
|
|
|
action void A_BoltForward( bool bFlick = false )
|
|
{
|
|
A_StartSound("mister/boltforward",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.boltlock = false;
|
|
if ( invoker.ClipCount > 0 )
|
|
{
|
|
invoker.clipcount--;
|
|
invoker.chambered = true;
|
|
invoker.fired = false;
|
|
}
|
|
if ( bFlick && (player == players[consoleplayer]) && swwm_beepboop )
|
|
SWWMHandler.AddOneliner("mrflick",2,0);
|
|
}
|
|
|
|
action void A_DropCasing()
|
|
{
|
|
A_ChangeModel("",1,"","",10,"models","",CMDL_USESURFACESKIN,-1);
|
|
if ( !invoker.waschambered ) return;
|
|
// brass it up (though it's not made of brass)
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),2*x+3*y-18*z);
|
|
let c = Spawn("MisterCasing",origin);
|
|
c.angle = angle;
|
|
c.pitch = pitch;
|
|
c.vel = x*FRandom[Junk](-.25,.25)+y*FRandom[Junk](-.25,.25)-(0,0,FRandom[Junk](4.,6.));
|
|
c.vel += vel*.5;
|
|
}
|
|
|
|
action void A_FireSelect()
|
|
{
|
|
if ( CheckLocalView() ) A_StartSound("mister/fireselect",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.firemode = (invoker.firemode+1)%4;
|
|
if ( (player == players[consoleplayer]) && swwm_beepboop )
|
|
SWWMHandler.AddOneliner("mrtouch",2,0);
|
|
}
|
|
|
|
action void A_MagOut()
|
|
{
|
|
A_PlayerReload();
|
|
A_StartSound("mister/magout",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_ChangeModel("",1,"","",7,"models",(invoker.clipcount>0)?"MortalMag.png":"MortalMag_Empty.png",CMDL_USESURFACESKIN,-1);
|
|
MagAmmo ma = MagAmmo(FindInventory("MisterRound"));
|
|
if ( !ma )
|
|
{
|
|
ma = MagAmmo(Spawn("MisterRound"));
|
|
ma.Amount = 0;
|
|
ma.AttachToOwner(self);
|
|
}
|
|
int maxgiveamt = min(ma.MaxAmount-ma.Amount,invoker.clipcount);
|
|
int dropamt = invoker.clipcount-maxgiveamt;
|
|
if ( dropamt > 0 ) invoker.BufferMagAmmo("MisterRound",dropamt);
|
|
ma.Amount = min(ma.MaxAmount,ma.Amount+invoker.clipcount);
|
|
ma.MagFill();
|
|
invoker.clipcount = 0;
|
|
}
|
|
|
|
action void A_MagDrop()
|
|
{
|
|
// ensure mag is invisible to avoid weird interpolation to hand
|
|
A_ChangeModel("",1,"","",7,"models","",CMDL_USESURFACESKIN,-1);
|
|
if ( swwm_nomagdrop ) return;
|
|
// drop it
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),6*x+3*y-18*z);
|
|
let c = Spawn("MisterMag",origin);
|
|
c.angle = angle;
|
|
c.pitch = pitch;
|
|
c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2.,4.));
|
|
c.vel += vel*.5;
|
|
}
|
|
|
|
action void A_MagGrab()
|
|
{
|
|
// ensure mag is full
|
|
A_ChangeModel("",1,"","",7,"models","MortalMag.png",CMDL_USESURFACESKIN,-1);
|
|
A_StartSound("mister/magget",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
|
|
action void A_MagIn()
|
|
{
|
|
MagAmmo sb = MagAmmo(FindInventory("MisterRound"));
|
|
if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) )
|
|
invoker.clipcount = invoker.default.clipcount;
|
|
else if ( (invoker.Ammo1.Amount <= 0) || (sb.Amount >= sb.ClipSize) )
|
|
{
|
|
int takeamt = min(sb.Amount,sb.ClipSize);
|
|
invoker.clipcount = takeamt;
|
|
sb.Amount -= takeamt;
|
|
int req = invoker.default.ClipCount-invoker.clipcount;
|
|
if ( req > 0 ) invoker.clipcount += invoker.FetchBufferedMagAmmo("MisterRound",req);
|
|
}
|
|
else if ( invoker.FetchBufferedMagAmmo("MisterRound",sb.ClipSize,true) )
|
|
invoker.clipcount = invoker.default.clipcount;
|
|
else
|
|
{
|
|
invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1);
|
|
invoker.clipcount = invoker.default.clipcount;
|
|
}
|
|
invoker.ClearBufferedAmmo();
|
|
A_AmmoAlert(true);
|
|
}
|
|
|
|
action State A_MisterFire()
|
|
{
|
|
if ( !invoker.chambered || invoker.fired )
|
|
{
|
|
// auto-chamber just in case (rarely ever happens)
|
|
if ( invoker.ClipCount > 0 )
|
|
return ResolveState("AutoCycle");
|
|
// auto-reload if we're out
|
|
if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || (CountInv("MisterRound") > 0) || sv_infiniteammo || FindInventory("PowerInfiniteAmmo")) )
|
|
return ResolveState("Reload");
|
|
}
|
|
// this one is handled separately
|
|
if ( invoker.firemode >= 3 ) return ResolveState("FireOverpressure");
|
|
A_PlayerFire();
|
|
A_SWWMFlash();
|
|
invoker.fired = true;
|
|
if ( invoker.firemode == 2 )
|
|
{
|
|
// stream shot
|
|
// individual sub-shots handled in the state sequence, not here
|
|
A_StartSound("mister/firestream",CHAN_WEAPON,CHANF_OVERLAP);
|
|
SWWMHandler.DoFlash(self,Color(32,64,224,255),2);
|
|
A_AlertMonsters(swwm_uncapalert?0:4000);
|
|
A_MisterFireStream(0);
|
|
return ResolveState("FireStream");
|
|
}
|
|
SWWMHandler.DoFlash(self,Color(48,64,224,255),3);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.8*y-2.4*z);
|
|
if ( invoker.firemode == 1 )
|
|
{
|
|
// cluster shot
|
|
A_StartSound("mister/firescatter",CHAN_WEAPON,CHANF_OVERLAP);
|
|
SWWMUtility.DoKnockback(self,-x,15000.);
|
|
A_QuakeEx(6,6,6,8,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.3);
|
|
A_BumpFOV(.92);
|
|
A_AlertMonsters(swwm_uncapalert?0:8000);
|
|
for ( int i=0; i<10; i++ )
|
|
{
|
|
let s = Spawn("SWWMSmoke",origin);
|
|
s.scale *= .6;
|
|
s.alpha *= .15;
|
|
s.speed *= .8;
|
|
s.vel += vel*.5+x*FRandom[Mister](1.,4.);
|
|
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
|
|
}
|
|
Vector3 x2, y2, z2;
|
|
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
double a, s;
|
|
Vector3 dir;
|
|
for ( int i=0; i<8; i++ )
|
|
{
|
|
// initial shot, triangle
|
|
if ( i < 3 )
|
|
{
|
|
a = 90+120*i+FRandom[Mister](-15,15);
|
|
s = .04+FRandom[Mister](-.01,.01);
|
|
dir = SWWMUtility.ConeSpread(x2,y2*1.5,z2,a,s);
|
|
}
|
|
// next five shots, pentagon
|
|
else
|
|
{
|
|
a = -90+72*(i-3)+FRandom[Mister](-9,9);
|
|
s = .08+FRandom[Mister](-.02,.02);
|
|
dir = SWWMUtility.ConeSpread(x2,y2*2.,z2,a,s);
|
|
}
|
|
FLineTraceData d;
|
|
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
|
|
SWWMBulletTrail.DoTrail(self,origin,dir,10000,6);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT )
|
|
{
|
|
let p = Spawn("SpreadImpact",d.HitLocation);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
|
|
p.pitch = asin(d.HitDir.z);
|
|
p.target = self;
|
|
}
|
|
let b = Spawn("MisterBuckshotImpact",d.HitLocation-d.HitDir*4.);
|
|
b.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
|
|
b.pitch = asin(d.HitDir.z);
|
|
b.target = self;
|
|
MisterBulletImpact(b).A_BulletExplode();
|
|
}
|
|
else if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
let p = Spawn("SpreadImpact",d.HitLocation+hitnormal*0.01);
|
|
p.angle = atan2(hitnormal.y,hitnormal.x);
|
|
p.pitch = asin(-hitnormal.z);
|
|
p.target = self;
|
|
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
|
|
let b = Spawn("MisterBuckshotImpact",d.HitLocation+hitnormal*4.);
|
|
b.angle = atan2(hitnormal.y,hitnormal.x);
|
|
b.pitch = asin(-hitnormal.z);
|
|
b.target = self;
|
|
MisterBulletImpact(b).A_BulletExplode();
|
|
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,444,self,d.HitDir,d.HitLocation.z);
|
|
}
|
|
}
|
|
return ResolveState("FireCluster");
|
|
}
|
|
// precision shot
|
|
A_StartSound("mister/firesemi",CHAN_WEAPON,CHANF_OVERLAP);
|
|
SWWMUtility.DoKnockback(self,-x,9000.);
|
|
A_QuakeEx(5,5,5,6,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.8);
|
|
A_BumpFOV(.95);
|
|
A_AlertMonsters(swwm_uncapalert?0:6000);
|
|
for ( int i=0; i<6; i++ )
|
|
{
|
|
let s = Spawn("SWWMSmoke",origin);
|
|
s.scale *= .4;
|
|
s.alpha *= .1;
|
|
s.speed *= .5;
|
|
s.vel += vel*.5+x*FRandom[Mister](1.,2.);
|
|
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
|
|
}
|
|
Vector3 dir;
|
|
dir = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
FLineTraceData d;
|
|
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
|
|
SWWMBulletTrail.DoTrail(self,origin,dir,10000,2);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT )
|
|
{
|
|
let p = Spawn("SWWMBulletImpact",d.HitLocation);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
|
|
p.pitch = asin(d.HitDir.z);
|
|
p.target = self;
|
|
}
|
|
let b = Spawn("MisterBulletImpact",d.HitLocation-d.HitDir*4.);
|
|
b.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
|
|
b.pitch = asin(d.HitDir.z);
|
|
b.target = self;
|
|
MisterBulletImpact(b).A_BulletExplode();
|
|
}
|
|
else if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01);
|
|
p.angle = atan2(hitnormal.y,hitnormal.x);
|
|
p.pitch = asin(-hitnormal.z);
|
|
p.target = self;
|
|
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
|
|
let b = Spawn("MisterBulletImpact",d.HitLocation+hitnormal*4.);
|
|
b.angle = atan2(hitnormal.y,hitnormal.x);
|
|
b.pitch = asin(-hitnormal.z);
|
|
b.target = self;
|
|
MisterBulletImpact(b).A_BulletExplode();
|
|
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,444,self,d.HitDir,d.HitLocation.z);
|
|
}
|
|
return ResolveState(null);
|
|
}
|
|
|
|
action void A_MisterFireStream( int index )
|
|
{
|
|
if ( index != 0 )
|
|
{
|
|
A_PlayerFire();
|
|
SWWMHandler.DoFlash(self,Color(32,64,224,255),2);
|
|
A_SWWMFlash("FastFlash");
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.8*y-2.4*z);
|
|
SWWMUtility.DoKnockback(self,-x,5000.);
|
|
A_QuakeEx(3,3,3,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.4);
|
|
A_BumpFOV(.97);
|
|
if ( index == 0 ) A_AlertMonsters(swwm_uncapalert?0:5000);
|
|
for ( int i=0; i<3; i++ )
|
|
{
|
|
let s = Spawn("SWWMSmoke",origin);
|
|
s.scale *= .4;
|
|
s.alpha *= .1;
|
|
s.speed *= .5;
|
|
s.vel += vel*.5+x*FRandom[Mister](1.,2.);
|
|
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
|
|
}
|
|
Vector3 x2, y2, z2;
|
|
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
static const double spd[] = {.00,.01,.03,.06,.10};
|
|
double a = FRandom[Mister](0,360), s = FRandom[Mister](spd[index],spd[index+1]);
|
|
Vector3 dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s);
|
|
FLineTraceData d;
|
|
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
|
|
SWWMBulletTrail.DoTrail(self,origin,dir,10000,2);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT )
|
|
{
|
|
let p = Spawn("SWWMBulletImpact",d.HitLocation);
|
|
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
|
|
p.pitch = asin(d.HitDir.z);
|
|
p.target = self;
|
|
}
|
|
let b = Spawn("MisterStreamImpact",d.HitLocation-d.HitDir*4.);
|
|
b.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
|
|
b.pitch = asin(d.HitDir.z);
|
|
b.target = self;
|
|
MisterBulletImpact(b).A_BulletExplode();
|
|
}
|
|
else if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01);
|
|
p.angle = atan2(hitnormal.y,hitnormal.x);
|
|
p.pitch = asin(-hitnormal.z);
|
|
p.target = self;
|
|
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
|
|
let b = Spawn("MisterStreamImpact",d.HitLocation+hitnormal*4.);
|
|
b.angle = atan2(hitnormal.y,hitnormal.x);
|
|
b.pitch = asin(-hitnormal.z);
|
|
b.target = self;
|
|
MisterBulletImpact(b).A_BulletExplode();
|
|
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,444,self,d.HitDir,d.HitLocation.z);
|
|
}
|
|
}
|
|
|
|
override void OwnerDied()
|
|
{
|
|
Super.OwnerDied();
|
|
prefirecnt = 0;
|
|
}
|
|
override void Travelled()
|
|
{
|
|
Super.Travelled();
|
|
prefirecnt = 0;
|
|
}
|
|
|
|
action void A_MisterStartRail()
|
|
{
|
|
// pre-heat our ovens
|
|
A_StartSound("mister/chargeover",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_StartSound("mister/holdover",CHAN_WEAPONEXTRA,CHANF_LOOPING,.01,4.,.5);
|
|
invoker.prefirecnt = 0;
|
|
}
|
|
|
|
action State A_MisterHoldRail()
|
|
{
|
|
if ( (invoker.prefirecnt < 100) && !(player.cmd.buttons&BT_ATTACK) )
|
|
{
|
|
invoker.prefirecnt = 0;
|
|
A_StopSound(CHAN_WEAPONEXTRA);
|
|
A_StartSound("mister/cancelover",CHAN_WEAPON,CHANF_OVERLAP);
|
|
return ResolveState("FireOverpressureCancel");
|
|
}
|
|
if ( invoker.prefirecnt < 100 )
|
|
{
|
|
invoker.prefirecnt += 1.5;
|
|
if ( invoker.prefirecnt >= 100 )
|
|
{
|
|
invoker.prefirecnt = 100;
|
|
invoker.holdtic = gametic;
|
|
}
|
|
}
|
|
A_SoundVolume(CHAN_WEAPONEXTRA,clamp(invoker.prefirecnt*.01,.01,1.));
|
|
A_SoundPitch(CHAN_WEAPONEXTRA,clamp(.5+invoker.prefirecnt*.005,.5,1.)**.5);
|
|
if ( (invoker.prefirecnt >= 100) && !((gametic-invoker.holdtic)%32) && CheckLocalView() )
|
|
A_StartSound("mister/beepover",CHAN_WEAPON,CHANF_OVERLAP);
|
|
return A_JumpIf((invoker.prefirecnt>=100)&&!(player.cmd.buttons&BT_ATTACK),"FireOverpressureRelease");
|
|
}
|
|
|
|
action void A_MisterFireRail()
|
|
{
|
|
invoker.prefirecnt = 0;
|
|
A_StopSound(CHAN_WEAPONEXTRA);
|
|
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+2.8*y-2.4*z);
|
|
SWWMHandler.DoFlash(self,Color(64,64,224,255),9);
|
|
A_SWWMFlash();
|
|
invoker.fired = true;
|
|
A_StartSound("mister/fireover",CHAN_WEAPON,CHANF_OVERLAP);
|
|
SWWMUtility.DoKnockback(self,-x,90000.);
|
|
A_QuakeEx(8,8,8,12,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:2.);
|
|
A_BumpFOV(.85);
|
|
A_AlertMonsters(swwm_uncapalert?0:12000);
|
|
for ( int i=0; i<12; i++ )
|
|
{
|
|
let s = Spawn("SWWMSmoke",origin);
|
|
s.scale *= .4;
|
|
s.alpha *= .1;
|
|
s.vel += vel*.5+x*FRandom[Mister](1.,12.);
|
|
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
|
|
}
|
|
Vector3 dir, startdir;
|
|
startdir = dir = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
let mrt = new("MisterRailTracer"); // I pity the fool
|
|
mrt.ignoreme = self;
|
|
mrt.hitlist.Clear();
|
|
mrt.shootthroughlist.Clear();
|
|
mrt.waterhitlist.Clear();
|
|
mrt.wallpenetratelist.Clear();
|
|
mrt.ffloors.Clear();
|
|
mrt.portalseg.Clear();
|
|
for ( int i=0; i<level.Sectors.Size(); i++ )
|
|
{
|
|
Sector s = level.Sectors[i];
|
|
for ( int j=0; j<s.Get3DFloorCount(); j++ )
|
|
{
|
|
F3DFloor ff = s.Get3DFloor(j);
|
|
if ( ff.flags&(F3DFloor.FF_EXISTS|F3DFloor.FF_SOLID) )
|
|
mrt.ffloors.Push(ff);
|
|
}
|
|
}
|
|
mrt.pastwall = false;
|
|
Vector3 norigin = origin;
|
|
mrt.maxdist = 10000.;
|
|
do
|
|
{
|
|
mrt.fullstop = true;
|
|
mrt.Trace(norigin,level.PointInSector(norigin.xy),dir,mrt.maxdist,TRACE_HitSky|TRACE_ReportPortals);
|
|
mrt.maxdist -= (mrt.exitpoint-norigin).length();
|
|
norigin = mrt.exitpoint;
|
|
dir = mrt.Results.HitVector;
|
|
}
|
|
while ( !mrt.fullstop );
|
|
let mrc = new("MisterRailCounter");
|
|
mrc.ChangeStatNum(STAT_USER); // so it can tick
|
|
mrc.player = player;
|
|
Vector3 sstart = origin;
|
|
// beam segments
|
|
if ( mrt.portalseg.Size() <= 0 )
|
|
{
|
|
Vector3 sdir = mrt.Results.HitPos-sstart;
|
|
double sdist = sdir.length();
|
|
sdir /= sdist;
|
|
Actor b;
|
|
if ( sdist > 32 )
|
|
{
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
b.frame = 1;
|
|
b.scale.y = 32;
|
|
b.angle = atan2(sdir.y,sdir.x);
|
|
b.pitch = asin(-sdir.z)+90;
|
|
MisterRailBeam(b).mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
sstart += sdir*32;
|
|
sdist -= 32;
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
}
|
|
else
|
|
{
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
b.frame = 1;
|
|
}
|
|
b.scale.y = sdist;
|
|
b.angle = atan2(sdir.y,sdir.x);
|
|
b.pitch = asin(-sdir.z)+90;
|
|
MisterRailBeam(b).mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
}
|
|
else for ( int i=0; i<mrt.portalseg.Size(); i++ )
|
|
{
|
|
Vector3 sdir = mrt.portalseg[i].enter-sstart;
|
|
double sdist = sdir.length();
|
|
sdir /= sdist;
|
|
Actor b;
|
|
if ( i == 0 )
|
|
{
|
|
if ( sdist > 32 )
|
|
{
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
b.target = self;
|
|
b.frame = 1;
|
|
b.scale.y = 32;
|
|
b.angle = atan2(sdir.y,sdir.x);
|
|
b.pitch = asin(-sdir.z)+90;
|
|
MisterRailBeam(b).mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
sstart += sdir*32;
|
|
sdist -= 32;
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
}
|
|
else
|
|
{
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
b.frame = 1;
|
|
}
|
|
}
|
|
else b = Spawn("MisterRailBeam",sstart);
|
|
b.target = self;
|
|
b.scale.y = sdist;
|
|
b.angle = atan2(sdir.y,sdir.x);
|
|
b.pitch = asin(-sdir.z)+90;
|
|
MisterRailBeam(b).mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
sstart = mrt.portalseg[i].exit;
|
|
if ( i == (mrt.portalseg.Size()-1) )
|
|
{
|
|
sdir = mrt.Results.HitPos-sstart;
|
|
sdist = sdir.length();
|
|
sdir /= sdist;
|
|
b = Spawn("MisterRailBeam",sstart);
|
|
b.target = self;
|
|
b.scale.y = sdist;
|
|
b.angle = atan2(sdir.y,sdir.x);
|
|
b.pitch = asin(-sdir.z)+90;
|
|
MisterRailBeam(b).mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
}
|
|
}
|
|
for ( int i=0; i<mrt.ShootThroughList.Size(); i++ )
|
|
{
|
|
mrt.ShootThroughList[i].Activate(self,0,SPAC_Impact);
|
|
mrt.ShootThroughList[i].Activate(self,0,SPAC_PCross);
|
|
}
|
|
for ( int i=0; i<mrt.WaterHitList.Size(); i++ )
|
|
{
|
|
let b = Spawn("InvisibleSplasher",mrt.WaterHitList[i].hitpos);
|
|
b.target = self;
|
|
b.A_CheckTerrain();
|
|
}
|
|
Array<MisterBulletImpact> bi; // so we can ignite them all at once after main contact damage
|
|
for ( int i=0; i<mrt.hitlist.Size(); i++ )
|
|
{
|
|
if ( mrt.HitList[i].bExit )
|
|
{
|
|
let b = MisterBulletImpact(Spawn("MisterRailExitImpact",mrt.hitlist[i].HitLocation-mrt.hitlist[i].x*4.));
|
|
b.angle = atan2(mrt.HitList[i].x.y,mrt.HitList[i].x.x);
|
|
b.pitch = asin(-mrt.HitList[i].x.z);
|
|
b.target = self;
|
|
b.mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
bi.Push(b);
|
|
}
|
|
else
|
|
{
|
|
let b = MisterBulletImpact(Spawn("MisterRailEntryImpact",mrt.hitlist[i].HitLocation+mrt.hitlist[i].x*4.));
|
|
b.angle = atan2(mrt.HitList[i].x.y,mrt.HitList[i].x.x)+180;
|
|
b.pitch = asin(mrt.HitList[i].x.z);
|
|
b.target = self;
|
|
b.mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
bi.Push(b);
|
|
}
|
|
if ( !mrt.HitList[i].HitActor || mrt.HitList[i].bExit ) continue;
|
|
SWWMUtility.DoKnockback(mrt.HitList[i].HitActor,mrt.HitList[i].x+(0,0,0.025),80000);
|
|
let p = SWWMPuff.Setup(mrt.HitList[i].HitLocation,mrt.HitList[i].x,invoker,self,mrt.HitList[i].HitActor);
|
|
mrt.HitList[i].HitActor.DamageMobj(p,self,4444,'Mortal',DMG_FOILINVUL|DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
|
|
if ( !mrt.HitList[i].HitActor || (mrt.HitList[i].HitActor.Health <= 0) )
|
|
mrc.nkill++;
|
|
}
|
|
LineTracer faketracer = new("LineTracer");
|
|
for ( int i=0; i<mrt.wallpenetratelist.Size(); i++ )
|
|
{
|
|
bool bExit = !!(i%2); // odd-numbered entries are exit points
|
|
Vector3 hitpos = mrt.WallPenetrateList[i].hitpos;
|
|
Vector3 hitnormal = mrt.WallPenetrateList[i].hitnormal;
|
|
let b = MisterBulletImpact(Spawn(bExit?"MisterRailExitImpact":"MisterRailEntryImpact",hitpos+hitnormal*4.));
|
|
b.angle = atan2(hitnormal.y,hitnormal.x);
|
|
b.pitch = asin(-hitnormal.z);
|
|
b.target = self;
|
|
b.mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
bi.Push(b);
|
|
if ( mrt.WallPenetrateList[i].hittype == TRACE_HitWall )
|
|
mrt.WallPenetrateList[i].hitline.Activate(self,mrt.WallPenetrateList[i].hitside,SPAC_Impact);
|
|
if ( swwm_omnibust )
|
|
{
|
|
faketracer.Results.HitType = mrt.WallPenetrateList[i].HitType;
|
|
faketracer.Results.HitSector = mrt.WallPenetrateList[i].HitSector;
|
|
faketracer.Results.HitLine = mrt.WallPenetrateList[i].HitLine;
|
|
faketracer.Results.ffloor = mrt.WallPenetrateList[i].HitFFloor;
|
|
faketracer.Results.Side = mrt.WallPenetrateList[i].HitSide;
|
|
faketracer.Results.Tier = mrt.WallPenetrateList[i].HitTier;
|
|
BusterWall.Bust(faketracer.Results,444,self,mrt.WallPenetrateList[i].BustDir,mrt.WallPenetrateList[i].HitPos.z);
|
|
}
|
|
}
|
|
if ( (mrt.Results.HitType != TRACE_HitNone) && (mrt.Results.HitType != TRACE_HasHitSky) && (mrt.Results.HitType != TRACE_HitActor) )
|
|
{
|
|
Vector3 hitnormal = -mrt.Results.HitVector;
|
|
if ( mrt.Results.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( mrt.Results.FFloor ) hitnormal = -mrt.Results.FFloor.top.Normal;
|
|
else hitnormal = mrt.Results.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( mrt.Results.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( mrt.Results.FFloor ) hitnormal = -mrt.Results.FFloor.bottom.Normal;
|
|
else hitnormal = mrt.Results.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( mrt.Results.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-mrt.Results.HitLine.delta.y,mrt.Results.HitLine.delta.x,0).unit();
|
|
if ( !mrt.Results.Side ) hitnormal *= -1;
|
|
}
|
|
let b = MisterBulletImpact(Spawn("MisterRailEntryImpact",mrt.Results.HitPos+hitnormal*4.));
|
|
b.angle = atan2(hitnormal.y,hitnormal.x);
|
|
b.pitch = asin(-hitnormal.z);
|
|
b.target = self;
|
|
b.mrc = mrc;
|
|
mrc.effectors.Push(b);
|
|
bi.Push(b);
|
|
if ( mrt.Results.HitType == TRACE_HitWall ) mrt.Results.HitLine.RemoteActivate(self,mrt.Results.Side,SPAC_Impact,mrt.Results.HitPos);
|
|
if ( swwm_omnibust ) BusterWall.Bust(mrt.Results,444,self,mrt.Results.HitVector,mrt.Results.HitPos.z);
|
|
}
|
|
for ( int i=0; i<bi.Size(); i++ ) bi[i].A_BulletExplode();
|
|
double dist = level.Vec3Diff(origin,mrt.Results.HitPos).length();
|
|
for ( double d=0.; d<=dist; d+=200. )
|
|
{
|
|
Vector3 p = level.Vec3Offset(origin,startdir*d);
|
|
if ( !level.IsPointInlevel(p) ) continue;
|
|
Spawn("MisterRailLight",p);
|
|
}
|
|
int numpt = Random[ExploS](50,100);
|
|
double dv = max(16.,dist/numpt);
|
|
for ( double d=32.; d<=dist; d+=dv )
|
|
{
|
|
Vector3 np = level.Vec3Offset(origin,startdir*d);
|
|
np = level.Vec3Offset(np,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,12));
|
|
if ( !level.IsPointInLevel(np) ) continue;
|
|
let p = Spawn("MisterPop",np);
|
|
p.target = self;
|
|
MisterPop(p).mrc = mrc;
|
|
mrc.effectors.Push(p);
|
|
}
|
|
if ( mrt.wallpenetratelist.Size() > 0 )
|
|
{
|
|
Vector3 start = origin;
|
|
Vector3 end = mrt.WallPenetrateList[0].hitpos;
|
|
Vector3 tdir = level.Vec3Diff(start,end);
|
|
double dist = tdir.length();
|
|
tdir /= dist;
|
|
for ( double d=4.; d<=dist; d+=8. )
|
|
{
|
|
if ( !Random[ExploS](0,1) ) continue;
|
|
Vector3 ofs = level.Vec3Offset(start,tdir*d);
|
|
if ( !level.IsPointInLevel(ofs) ) continue;
|
|
let b = Spawn("SWWMHalfSmoke",ofs);
|
|
b.Scale *= FRandom[ExploS](.7,1.4);
|
|
b.alpha *= .2;
|
|
b.special1 = Random[ExploS](1,3);
|
|
b.SetShade(Color(2,3,4)*Random[ExploS](48,63));
|
|
}
|
|
for ( int i=1; i<mrt.WallPenetrateList.Size(); i+=2 )
|
|
{
|
|
start = mrt.WallPenetrateList[i].hitpos;
|
|
if ( i >= mrt.WallPenetrateList.Size()-1 ) end = mrt.Results.HitPos;
|
|
else end = mrt.WallPenetrateList[i+1].hitpos;
|
|
tdir = level.Vec3Diff(start,end);
|
|
dist = tdir.length();
|
|
tdir /= dist;
|
|
for ( double d=4.; d<=dist; d+=8. )
|
|
{
|
|
if ( !Random[ExploS](0,1) ) continue;
|
|
Vector3 ofs = level.Vec3Offset(start,tdir*d);
|
|
if ( !level.IsPointInLevel(ofs) ) continue;
|
|
let b = Spawn("SWWMHalfSmoke",ofs);
|
|
b.Scale *= FRandom[ExploS](.7,1.4);
|
|
b.alpha *= .2;
|
|
b.special1 = Random[ExploS](1,3);
|
|
b.SetShade(Color(2,3,4)*Random[ExploS](48,63));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( double d=4.; d<=mrt.Results.Distance; d+=8. )
|
|
{
|
|
if ( !Random[ExploS](0,1) ) continue;
|
|
Vector3 ofs = level.Vec3Offset(origin,startdir*d);
|
|
if ( !level.IsPointInLevel(ofs) ) continue;
|
|
let b = Spawn("SWWMHalfSmoke",ofs);
|
|
b.Scale *= FRandom[ExploS](.7,1.4);
|
|
b.alpha *= .2;
|
|
b.special1 = Random[ExploS](1,3);
|
|
b.SetShade(Color(2,3,4)*Random[ExploS](48,63));
|
|
}
|
|
}
|
|
}
|
|
|
|
action void A_MisterFireGrenade()
|
|
{
|
|
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+2.8*y-4.*z);
|
|
SWWMHandler.DoFlash(self,Color(64,64,224,255),3);
|
|
A_SWWMFlash("AltFlash");
|
|
invoker.gfired = true;
|
|
A_StartSound("mister/grenade",CHAN_WEAPON,CHANF_OVERLAP);
|
|
SWWMUtility.DoKnockback(self,-x,12000.);
|
|
A_QuakeEx(4,4,4,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.6);
|
|
A_BumpFOV(.96);
|
|
A_AlertMonsters(swwm_uncapalert?0:5000);
|
|
for ( int i=0; i<9; i++ )
|
|
{
|
|
let s = Spawn("SWWMSmoke",origin);
|
|
s.scale *= .4;
|
|
s.alpha *= .1;
|
|
s.vel += vel*.5+x*FRandom[Mister](1.,4.);
|
|
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
|
|
}
|
|
Vector3 dir;
|
|
dir = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
let p = Spawn("MisterGrenade",origin);
|
|
p.target = self;
|
|
p.angle = atan2(dir.y,dir.x);
|
|
p.pitch = asin(-dir.z);
|
|
p.vel = dir*p.speed;
|
|
}
|
|
|
|
action void A_GrenadeOpen()
|
|
{
|
|
A_PlayerReload();
|
|
A_StartSound("mister/grenadeopen");
|
|
invoker.wasgchambered = invoker.gchambered;
|
|
// theoretically, this is an either-or situation
|
|
// either there's no grenade in the chamber whatsoever
|
|
// or there's the casing of a fired grenade
|
|
if ( !invoker.wasgchambered )
|
|
{
|
|
// hide both surfaces
|
|
A_ChangeModel("",1,"","",8,"models","",CMDL_USESURFACESKIN,-1);
|
|
A_ChangeModel("",1,"","",9,"models","",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
else
|
|
{
|
|
// show only a fired cartridge
|
|
A_ChangeModel("",1,"","",8,"models","MortalGrenade_Used.png",CMDL_USESURFACESKIN,-1);
|
|
A_ChangeModel("",1,"","",9,"models","",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
invoker.gfired = invoker.gchambered = false;
|
|
}
|
|
|
|
action void A_GrenadeDrop()
|
|
{
|
|
A_ChangeModel("",1,"","",8,"models","",CMDL_USESURFACESKIN,-1);
|
|
A_ChangeModel("",1,"","",9,"models","",CMDL_USESURFACESKIN,-1);
|
|
if ( !invoker.wasgchambered ) return;
|
|
// droppage
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),8*x+3*y-18*z);
|
|
let c = Spawn("MisterGCasing",origin);
|
|
c.angle = angle;
|
|
c.pitch = pitch;
|
|
c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2.,4.));
|
|
c.vel += vel*.5;
|
|
}
|
|
|
|
action void A_GrenadeGrab()
|
|
{
|
|
// the full thingy
|
|
A_ChangeModel("",1,"","",8,"models","MortalGrenade.png",CMDL_USESURFACESKIN,-1);
|
|
A_ChangeModel("",1,"","",9,"models","MortalGrenade.png",CMDL_USESURFACESKIN,-1);
|
|
A_StartSound("mister/grenadeget",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
|
|
action void A_GrenadeIn()
|
|
{
|
|
A_StartSound("mister/grenadein",CHAN_WEAPON,CHANF_OVERLAP);
|
|
if ( !sv_infiniteammo && !FindInventory("PowerInfiniteAmmo") )
|
|
invoker.Ammo2.Amount = max(0,invoker.Ammo2.Amount-1);
|
|
invoker.gchambered = true;
|
|
invoker.gfired = false;
|
|
}
|
|
|
|
action void A_GrenadeClose()
|
|
{
|
|
// we no longer need the model, hide it
|
|
A_ChangeModel("",1,"","",8,"models","",CMDL_USESURFACESKIN,-1);
|
|
A_ChangeModel("",1,"","",9,"models","",CMDL_USESURFACESKIN,-1);
|
|
A_StartSound("mister/grenadeclose",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
|
|
override void MarkPrecacheSounds()
|
|
{
|
|
Super.MarkPrecacheSounds();
|
|
MarkSound("mister/select");
|
|
MarkSound("mister/deselect");
|
|
MarkSound("mister/meleestart");
|
|
MarkSound("mister/meleeend");
|
|
MarkSound("mister/boltback");
|
|
MarkSound("mister/boltforward");
|
|
MarkSound("mister/fireselect");
|
|
MarkSound("mister/lowammo");
|
|
MarkSound("mister/noammo");
|
|
MarkSound("mister/firesemi");
|
|
MarkSound("mister/firescatter");
|
|
MarkSound("mister/firestream");
|
|
MarkSound("mister/fireover");
|
|
MarkSound("mister/chargeover");
|
|
MarkSound("mister/magout");
|
|
MarkSound("mister/magin");
|
|
MarkSound("mister/grenade");
|
|
MarkSound("mister/grenadeopen");
|
|
MarkSound("mister/grenadeget");
|
|
MarkSound("mister/grenadein");
|
|
MarkSound("mister/grenadeclose");
|
|
MarkSound("mister/hitsemi1");
|
|
MarkSound("mister/hitsemi2");
|
|
MarkSound("mister/hitsemi3");
|
|
MarkSound("mister/hitscatter1");
|
|
MarkSound("mister/hitscatter2");
|
|
MarkSound("mister/hitscatter3");
|
|
MarkSound("mister/hitstream1");
|
|
MarkSound("mister/hitstream2");
|
|
MarkSound("mister/hitstream3");
|
|
MarkSound("mister/hitover1");
|
|
MarkSound("mister/hitover2");
|
|
MarkSound("mister/hitover3");
|
|
MarkSound("mister/hitgrenade1");
|
|
MarkSound("mister/hitgrenade2");
|
|
MarkSound("mister/hitgrenade3");
|
|
MarkSound("mister/hitgrenadesub1");
|
|
MarkSound("mister/hitgrenadesub2");
|
|
MarkSound("mister/hitgrenadesub3");
|
|
MarkSound("mister/casing1");
|
|
MarkSound("mister/casing2");
|
|
MarkSound("mister/casing3");
|
|
MarkSound("mister/casing4");
|
|
MarkSound("mister/gcasing1");
|
|
MarkSound("mister/gcasing2");
|
|
MarkSound("mister/gcasing3");
|
|
MarkSound("mister/gcasing4");
|
|
MarkSound("mister/gbounce1");
|
|
MarkSound("mister/gbounce2");
|
|
MarkSound("mister/gbounce3");
|
|
MarkSound("mister/gbouncesub1");
|
|
MarkSound("mister/gbouncesub2");
|
|
MarkSound("mister/gbouncesub3");
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$T_MORTALRIFLE";
|
|
Inventory.PickupMessage "$T_MORTALRIFLE";
|
|
Obituary "$O_MORTALRIFLE";
|
|
SWWMWeapon.Tooltip "$TT_MORTALRIFLE";
|
|
SWWMWeapon.GetLine "getmortalrifle";
|
|
Weapon.SlotNumber 9;
|
|
Weapon.SlotPriority 2.;
|
|
Weapon.SelectionOrder 850;
|
|
Weapon.UpSound "mister/select";
|
|
Weapon.AmmoType1 "MisterAmmo";
|
|
Weapon.AmmoGive1 1;
|
|
Weapon.AmmoType2 "MisterGAmmo";
|
|
Weapon.AmmoGive2 0;
|
|
SWWMWeapon.DropAmmoType "SWWMCellAmmoBig";
|
|
MisterRifle.ClipCount 12;
|
|
Stamina 1600000;
|
|
+SWWMWEAPON.NOFIRSTGIVE;
|
|
+SWWMWEAPON.HASSCRTEX;
|
|
+WEAPON.BFG;
|
|
Radius 30;
|
|
Height 28;
|
|
}
|
|
States
|
|
{
|
|
Select:
|
|
XZW2 P 2
|
|
{
|
|
A_ChangeModel("",1,"","",7,"models","MortalMag.png",CMDL_USESURFACESKIN,-1);
|
|
A_FullRaise();
|
|
if ( invoker.boltlock ) return ResolveState("SelectLock");
|
|
if ( (invoker.clipcount > 0) && (!invoker.chambered || invoker.fired) )
|
|
return ResolveState("SelectCycle");
|
|
return ResolveState(null);
|
|
}
|
|
XZW2 QRSTUV 2;
|
|
XZW2 WXYZ 1;
|
|
XZW3 ABCD 2;
|
|
Goto Ready;
|
|
SelectLock:
|
|
XZWB TUVWXYZ 2;
|
|
XZWC ABCD 1;
|
|
XZWC EFGH 2;
|
|
Goto ReadyLock;
|
|
SelectCycle:
|
|
XZWI RSTUVWXYZ 2;
|
|
XZWJ A 2 A_BoltBack();
|
|
XZWJ B 2;
|
|
XZWJ C 4;
|
|
XZWJ D 2
|
|
{
|
|
A_BoltForward();
|
|
A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.15);
|
|
}
|
|
XZWJ EFG 2;
|
|
XZWJ HIJK 1;
|
|
XZWJ LMNO 2;
|
|
Goto Ready;
|
|
Ready:
|
|
XZW2 A 1
|
|
{
|
|
int flg = (WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1);
|
|
// can't alt-fire if we have no grenades left
|
|
if ( (!invoker.gchambered || invoker.gfired) && (invoker.Ammo2.Amount <= 0) && !sv_infiniteammo && !FindInventory("PowerInfiniteAmmo") )
|
|
flg |= WRF_NOSECONDARY;
|
|
A_WeaponReady(flg);
|
|
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) )
|
|
invoker.CheckAmmo(EitherFire,true);
|
|
}
|
|
Wait;
|
|
ReadyLock:
|
|
XZWB E 1
|
|
{
|
|
int flg = (WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1);
|
|
// can't alt-fire if we have no grenades left
|
|
if ( (!invoker.gchambered || invoker.gfired) && (invoker.Ammo2.Amount <= 0) && !sv_infiniteammo && !FindInventory("PowerInfiniteAmmo") )
|
|
flg |= WRF_NOSECONDARY;
|
|
// can't use primary fire if we have no ammo to reload from
|
|
if ( (invoker.Ammo1.Amount <= 0) && (CountInv("MisterRound") <= 0) && !sv_infiniteammo && !FindInventory("PowerInfiniteAmmo") )
|
|
flg |= WRF_NOPRIMARY;
|
|
A_WeaponReady(flg);
|
|
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) )
|
|
invoker.CheckAmmo(EitherFire,true);
|
|
}
|
|
Wait;
|
|
AutoCycle:
|
|
XZW2 A 1 A_JumpIf(invoker.clipcount<=0,"AutoCycleLock");
|
|
XZW6 H 2 A_BoltBack(true);
|
|
XZW6 IJ 2;
|
|
XZW6 K 2 A_BoltForward();
|
|
XZW6 LMN 2;
|
|
XZW6 O 3 A_DropCasing();
|
|
Goto Ready;
|
|
AutoCycleLock:
|
|
XZW2 A 1 { invoker.boltlock = true; }
|
|
XZWA W 2 A_BoltBack(true);
|
|
XZWA XYZ 2;
|
|
XZWB ABC 2;
|
|
XZWB D 3 A_DropCasing();
|
|
Goto ReadyLock;
|
|
Fire:
|
|
XZW2 A 1 A_MisterFire();
|
|
XZW3 EFGH 1;
|
|
XZW3 IJK 2;
|
|
Goto AutoCycle;
|
|
FireCluster:
|
|
XZW2 A 1;
|
|
XZW3 LMN 1;
|
|
XZW3 OPQRS 2;
|
|
Goto AutoCycle;
|
|
FireStream:
|
|
XZW2 A 1 A_MisterFireStream(1);
|
|
XZW3 T 1 A_MisterFireStream(2);
|
|
XZW3 U 1 A_MisterFireStream(3);
|
|
XZW3 VWXY 1;
|
|
XZW3 Z 2;
|
|
XZW4 ABC 2;
|
|
Goto AutoCycle;
|
|
FireOverpressure:
|
|
XZW2 A 2 A_MisterStartRail();
|
|
XZW4 DEF 5;
|
|
XZW4 F 1 A_MisterHoldRail();
|
|
Wait;
|
|
FireOverpressureRelease:
|
|
XZW4 F 1 A_MisterFireRail();
|
|
XZW4 GH 1;
|
|
XZW4 IJ 2;
|
|
XZW4 KLMN 3;
|
|
Goto AutoCycle;
|
|
FireOverpressureCancel:
|
|
XZW4 FED 3;
|
|
Goto Ready;
|
|
AltFire:
|
|
XZW2 A 1
|
|
{
|
|
if ( !invoker.gchambered || invoker.gfired )
|
|
return invoker.boltlock?ResolveState("AltReloadLock"):ResolveState("AltReload");
|
|
A_MisterFireGrenade();
|
|
return A_JumpIf(invoker.boltlock,"AltFireLock");
|
|
}
|
|
XZW4 OP 1;
|
|
XZW4 QR 2;
|
|
XZW4 ST 3;
|
|
XZW4 UVW 3;
|
|
Goto Ready;
|
|
AltFireLock:
|
|
XZWB E 1;
|
|
XZWC IJ 1;
|
|
XZWC KL 2;
|
|
XZWC MN 3;
|
|
XZWC OPQ 4;
|
|
Goto ReadyLock;
|
|
AltReload:
|
|
XZW2 A 3 A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW4 XYZ 3;
|
|
XZW5 A 3;
|
|
XZW5 B 2 A_GrenadeOpen();
|
|
XZW5 CD 2;
|
|
XZW5 EF 3;
|
|
XZW5 G 3 A_GrenadeDrop();
|
|
XZW5 H 3;
|
|
XZW5 I 3 A_GrenadeGrab();
|
|
XZW5 JKL 3;
|
|
XZW5 M 2 A_GrenadeIn();
|
|
XZW5 NOP 2;
|
|
XZW5 QRS 3;
|
|
XZW5 T 2 A_GrenadeClose();
|
|
XZW5 UVWXY 2;
|
|
XZW5 Z 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.15);
|
|
XZW6 ABCDEFG 2;
|
|
Goto Ready;
|
|
AltReloadLock:
|
|
XZWB E 3 A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZWC RSTU 3;
|
|
XZWC V 2 A_GrenadeOpen();
|
|
XZWC WX 2;
|
|
XZWC YZ 3;
|
|
XZWD A 3 A_GrenadeDrop();
|
|
XZWD B 3;
|
|
XZWD C 3 A_GrenadeGrab();
|
|
XZWD DEF 3;
|
|
XZWD G 2 A_GrenadeIn();
|
|
XZWD HIJ 2;
|
|
XZWD KLM 3;
|
|
XZWD N 2 A_GrenadeClose();
|
|
XZWD OPQRS 2;
|
|
XZWD T 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.15);
|
|
XZWD UVWXYZ 2;
|
|
XZWE A 2;
|
|
Goto ReadyLock;
|
|
Reload:
|
|
XZW2 A 2
|
|
{
|
|
if ( (invoker.clipcount >= invoker.default.clipcount) || ((invoker.Ammo1.Amount <= 0) && (CountInv("MisterRound") <= 0) && !sv_infiniteammo && !FindInventory("PowerInfiniteAmmo")) )
|
|
return invoker.boltlock?ResolveState("IdleLock"):ResolveState("Idle");
|
|
A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
return A_JumpIf(invoker.boltlock,"ReloadLock");
|
|
}
|
|
XZW9 KLMNOPQRST 2;
|
|
XZW9 U 2 A_MagOut();
|
|
XZW9 VWXYZ 2;
|
|
XZWA ABC 2;
|
|
XZWA D 2 A_MagDrop();
|
|
XZWA E 2;
|
|
XZWA F 2 A_MagGrab();
|
|
XZWA GHI 2;
|
|
XZWA J 2 A_StartSound("mister/magin",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZWA K 2;
|
|
XZWA L 2 A_MagIn();
|
|
XZWA MN 2;
|
|
XZWA O 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.15);
|
|
XZWA PQRSTUV 2;
|
|
Goto Ready;
|
|
ReloadLock:
|
|
XZWB E 2;
|
|
XZWG WXYZ 2;
|
|
XZWH ABCDEF 2;
|
|
XZWH G 2 A_MagOut();
|
|
XZWH HIJKLMNO 2;
|
|
XZWH P 2 A_MagDrop();
|
|
XZWH Q 2;
|
|
XZWH R 2 A_MagGrab();
|
|
XZWH STU 2;
|
|
XZWH V 2 A_StartSound("mister/magin",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZWH W 2;
|
|
XZWH X 2 A_MagIn();
|
|
XZWH YZ 2;
|
|
XZWI ABCDEFG 2;
|
|
XZWI H 2 A_BoltForward(true);
|
|
XZWI I 2;
|
|
XZWI J 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.15);
|
|
XZWI KLMN 2;
|
|
XZWI OPQ 3;
|
|
Goto Ready;
|
|
Idle:
|
|
XZW2 A 2
|
|
{
|
|
A_StartSound("mister/deselect",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_PlayerCheckGun();
|
|
}
|
|
XZW7 JKLMNO 2;
|
|
XZW7 PQR 3;
|
|
XZW7 STU 4;
|
|
XZW7 V 2 A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW7 WXY 2;
|
|
XZW7 Z 3;
|
|
XZW8 AB 3;
|
|
XZW8 CDEF 4;
|
|
XZW8 G 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.2);
|
|
XZW8 HIJK 2;
|
|
XZW8 L 3;
|
|
Goto Ready;
|
|
IdleLock:
|
|
XZWB E 2
|
|
{
|
|
A_StartSound("mister/deselect",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_PlayerCheckGun();
|
|
}
|
|
XZWE VWXYZ 2;
|
|
XZWF A 2;
|
|
XZWF BCD 3;
|
|
XZWF EFG 4;
|
|
XZWF H 2 A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZWF IJK 2;
|
|
XZWF LMN 3;
|
|
XZWF OPQR 4;
|
|
XZWF S 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.2);
|
|
XZWF TUVW 2;
|
|
XZWF X 3;
|
|
Goto ReadyLock;
|
|
Zoom:
|
|
XZW2 A 2
|
|
{
|
|
A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
return A_JumpIf(invoker.boltlock,"ZoomLock");
|
|
}
|
|
XZW6 PQRSTUVW 2;
|
|
XZW6 X 2 A_FireSelect();
|
|
XZW6 YZ 2;
|
|
XZW7 A 2;
|
|
XZW7 B 2 A_JumpIf(player.cmd.buttons&BT_ZOOM,"ReZoom");
|
|
XZW7 CD 2;
|
|
XZW7 E 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.35);
|
|
XZW7 FGHI 2;
|
|
Goto Ready;
|
|
ReZoom:
|
|
XZW7 B 4; // smoother tween
|
|
Goto Zoom+6;
|
|
ZoomLock:
|
|
XZWB E 2;
|
|
XZWE BCDEFGHI 2;
|
|
XZWE J 2 A_FireSelect();
|
|
XZWE KLM 2;
|
|
XZWE N 2 A_JumpIf(player.cmd.buttons&BT_ZOOM,"ReZoomLock");
|
|
XZWE OP 2;
|
|
XZWE Q 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP,starttime:.35);
|
|
XZWE RSTU 2;
|
|
Goto ReadyLock;
|
|
ReZoomLock:
|
|
XZWE N 4; // smoother tween
|
|
Goto ZoomLock+6;
|
|
User1:
|
|
XZW2 A 2
|
|
{
|
|
A_StartSound("mister/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
return A_JumpIf(invoker.boltlock,"User1Lock");
|
|
}
|
|
XZW8 MN 2;
|
|
XZW8 O 2
|
|
{
|
|
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_PlayerMelee();
|
|
}
|
|
XZW8 PQ 2;
|
|
XZW8 R 1;
|
|
XZW8 S 1 A_Parry(8);
|
|
XZW8 T 1;
|
|
XZW8 U 1 A_Melee(50,"demolitionist/whitm",1.25,1.2,1.2);
|
|
XZW8 VWXYZ 2;
|
|
XZW9 A 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW9 BCDEFGHIJ 2;
|
|
Goto Ready;
|
|
User1Lock:
|
|
XZWB E 2;
|
|
XZWF YZ 2;
|
|
XZWG A 2
|
|
{
|
|
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_PlayerMelee();
|
|
}
|
|
XZWG BC 2;
|
|
XZWG D 1;
|
|
XZWG E 1 A_Parry(8);
|
|
XZWG F 1;
|
|
XZWG G 1 A_Melee(50,"demolitionist/whitm",1.25,1.2,1.2);
|
|
XZWG HIJKL 2;
|
|
XZWG M 2 A_StartSound("mister/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZWG NOPQRSTUV 2;
|
|
Goto ReadyLock;
|
|
Deselect:
|
|
XZW2 A 2
|
|
{
|
|
A_StartSound("mister/deselect",CHAN_WEAPON,CHANF_OVERLAP);
|
|
return A_JumpIf(invoker.boltlock,"DeselectLock");
|
|
}
|
|
XZW2 BCDE 2;
|
|
XZW2 FGHIJKLMNOP 1;
|
|
XZW2 P -1 A_FullLower();
|
|
Stop;
|
|
DeselectLock:
|
|
XZWB EFGHI 2;
|
|
XZWB JKLMNOPQRST 1;
|
|
XZWB T -1 A_FullLower();
|
|
Stop;
|
|
Flash:
|
|
XZW0 A 2 Bright
|
|
{
|
|
let psp = player.FindPSprite(PSP_FLASH);
|
|
psp.frame = Random[GunFlash](0,3);
|
|
let l = Spawn("MisterWeaponLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
FastFlash:
|
|
XZW0 A 2 Bright
|
|
{
|
|
let psp = player.FindPSprite(PSP_FLASH);
|
|
psp.frame = Random[GunFlash](0,3);
|
|
}
|
|
Stop;
|
|
AltFlash:
|
|
XZW0 E 2 Bright
|
|
{
|
|
let psp = player.FindPSprite(PSP_FLASH);
|
|
psp.frame = Random[GunFlash](4,7);
|
|
let l = Spawn("MisterWeaponLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|