// Imanaki Corp Hellfire Cannon Mk3, aka "Hellblazer" (from SWWM series, originally inspired by the Hellraiser from OMGWEAPONS) // Slot 6, replaces Rocket Launcher, Phoenix Rod, Firestorm Class HellblazerX : SWWMItemOverlay { States { Spawn: XZW1 A -1; // not fullbright Stop; } } Class Hellblazer : SWWMWeapon { int clipcount; bool magstate[6]; // true: rocket was spent int magpos; // current rotation int spinskipped; int preload; // additional loaded missiles (up to 2) Actor seektarget[3]; int seekcnt; int preloadcnt; transient ui SWWMProjectionData projdata; ui TextureID LockIcon; override void RenderUnderlay( RenderEvent e ) { Super.RenderUnderlay(e); if ( !LockIcon ) LockIcon = TexMan.CheckForTexture("graphics/HUD/HellblazerMissileLock.png"); SWWMUtility.PrepareProjData(projdata,e.ViewPos,e.ViewAngle,e.ViewPitch,e.ViewRoll,players[consoleplayer].fov); int cliptop = projdata.viewy, clipbottom = projdata.viewy+projdata.viewh, clipleft = projdata.viewx, clipright = projdata.viewx+projdata.vieww; double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/360.)),1.); Vector2 ss = (Screen.GetWidth()/hs,Screen.GetHeight()/hs); for ( int i=0; i<3; i++ ) { if ( !seektarget[i] ) continue; Vector3 tpos = SWWMUtility.LerpVector3(seektarget[i].prev,seektarget[i].pos,e.FracTic); Vector3 ndc = SWWMUtility.ProjectPoint(projdata,e.viewpos+level.Vec3Diff(e.viewpos,tpos+(0,0,seektarget[i].Height/2))); if ( ndc.z > 1. ) continue; Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc); Screen.DrawTexture(LockIcon,false,vpos.x/hs,vpos.y/hs,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_CenterOffset,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright); } } override Vector3 GetTraceOffset( int index ) { return (10.,3.5,-5.); } action void A_HellblazerFire( bool bAlt = false ) { A_StartSound(bAlt?"hellblazer/altfire":"hellblazer/fire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:(bAlt?1.7:.8)); A_AlertMonsters(swwm_uncapalert?0:bAlt?400:1200); int qstr = bAlt?4:5; A_QuakeEx(qstr,qstr,qstr,bAlt?4:12,0,8,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.12*qstr); A_BumpFOV(bAlt?.96:.93); A_PlayerFire(); invoker.clipcount = max(0,invoker.clipcount-1); invoker.magstate[invoker.magpos-invoker.preload] = true; invoker.spinskipped++; Vector3 x, y, z, x2, y2, z2, dir, origin; double a, s; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); SWWMUtility.DoKnockback(self,-x,bAlt?22000.:32000.); [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3.5*y-5*z); a = FRandom[Hellblazer](0,360); s = FRandom[Hellblazer](0,bAlt?.02:.005); dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s); let p = Spawn(bAlt?"HellblazerMissile2":"HellblazerMissile",origin); p.target = self; int tidx = max(0,invoker.seekcnt-(invoker.preload+1)); if ( invoker.seektarget[tidx] ) HellblazerMissile(p).seektarget = invoker.seektarget[tidx]; if ( invoker.preload <= 0 ) for ( int i=0; i<3; i++ ) invoker.seektarget[i] = null; p.angle = atan2(dir.y,dir.x); p.pitch = asin(-dir.z); p.vel = dir*p.speed; if ( bAlt ) { if ( p.waterlevel <= 0 ) p.vel.z += 3.5; return; } for ( int i=0; i<5; i++ ) { let s = Spawn("SWWMSmoke",origin); s.special1 = 1; s.scale *= 2.4; s.alpha *= .4; s.SetShade(Color(4,3,2)*Random[Hellblazer](48,63)); s.vel += vel*.5+x*FRandom[Hellblazer](6.,10.)+y*FRandom[Hellblazer](-2,2)+z*FRandom[Hellblazer](-2,2); } for ( int i=0; i<6; i++ ) { let s = Spawn("SWWMSpark",origin); s.scale *= .7; s.alpha *= .4; s.vel += vel*.5+x*FRandom[Hellblazer](4.,8.)+y*FRandom[Hellblazer](-2,2)+z*FRandom[Hellblazer](-2,2); } } override bool ReportHUDAmmo() { return (clipcount>0)||(Ammo1.Amount>0); } override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) { if ( (firemode == PrimaryFire) || (firemode == AltFire) ) return (clipcount>0)||(Ammo1.Amount>0); return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); } action void A_GlassOverlay( StateLabel g ) { player.SetPSprite(PSP_WEAPON+1,invoker.FindState(g)); // we have to still use A_Overlay* functions for these A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); } action state A_JumpX( StateLabel a, StateLabel g ) { A_GlassOverlay(g); return invoker.FindState(a); } override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) { bool good = Super.PickupForAmmoSWWM(ownedWeapon); let Owner = ownedWeapon.Owner; if ( (AmmoGive1 == 0) && (clipcount > 0) ) { // let's get this bread Inventory cur = Owner.FindInventory(AmmoType1); if ( !cur ) { cur = Inventory(Spawn(AmmoType1)); cur.Amount = 0; cur.AttachToOwner(Owner); } int maxgiveamt = min(cur.MaxAmount-cur.Amount,clipcount); int dropamt = clipcount-maxgiveamt; if ( dropamt > 0 ) cur.CreateTossable(dropamt); cur.Amount = min(cur.MaxAmount,cur.Amount+clipcount); good = true; } return good; } override void InitializeWeapon() { // 3 hellblazer missiles loaded clipcount = 3; magpos = 0; for ( int i=0; i<6; i++ ) magstate[i] = (i>2); } clearscope int LoadedCapacity() const { return min(Ammo1.Amount+clipcount,6); } action void A_SwapAmmo() { int takeamt = invoker.LoadedCapacity()-invoker.clipcount; invoker.clipcount = invoker.LoadedCapacity(); if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-takeamt); invoker.magpos = 0; for ( int i=0; i<6; i++ ) invoker.magstate[i] = !(invoker.clipcount > i); return; } action void A_HellblazerReady() { int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; // can we fire? bool canfire = (invoker.clipcount > 0) || (invoker.Ammo1.Amount > 0); if ( !canfire ) flg |= WRF_NOPRIMARY|WRF_NOSECONDARY; A_WeaponReady(flg); if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) invoker.CheckAmmo(EitherFire,true); } // check if weapon was dropped or interrupted in some way before the mag spin could be done action void A_CheckSpinSkip() { for ( int i=0; i<3; i++ ) invoker.seektarget[i] = null; // check preloaded rockets first (note, this theoretically would never go below zero) invoker.magpos = max(0,invoker.magpos-invoker.preload); invoker.preload = 0; // do the spin invoker.magpos = (invoker.magpos+invoker.spinskipped)%6; invoker.spinskipped = 0; } action void A_UpdatePickup() { for ( int i=0; i<6; i++ ) { if ( !invoker.bInitialized ) { // not initialized, default to 3 loaded rockets if ( i < 3 ) A_ChangeModel("",0,"","",6+i,"models","HellblazerMissile.png",CMDL_USESURFACESKIN,-1); else A_ChangeModel("",0,"","",6+i,"models","",CMDL_USESURFACESKIN,-1); continue; } int idx = i-invoker.magpos; if ( idx > 5 ) idx -= 6; else if ( idx < 0 ) idx += 6; if ( invoker.magstate[i] ) A_ChangeModel("",0,"","",6+idx,"models","",CMDL_USESURFACESKIN,-1); else A_ChangeModel("",0,"","",6+idx,"models","HellblazerMissile.png",CMDL_USESURFACESKIN,-1); } } action void A_CheckLockOn() { if ( invoker.seekcnt > invoker.preload ) return; Vector3 x, y, z, x2, y2, z2, dir; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x); // try to catch target in cone of vision [x2, y2, z2] = swwm_CoordUtil.GetAxes(pitch,angle,roll); Array hits; hits.Clear(); int rings = 1; FLineTraceData d; for ( double i=0; i<.1; i+=.02 ) { for ( int j=0; j<360; j+=(360/rings) ) { dir = SWWMUtility.ConeSpread(x2,y2,z2,j,i); LineTrace(atan2(dir.y,dir.x),8000.,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); if ( d.HitType != TRACE_HitActor ) continue; if ( (d.HitActor == invoker.seektarget[0]) || (d.HitActor == invoker.seektarget[1]) || (d.HitActor == invoker.seektarget[2]) ) continue; if ( !d.HitActor.bISMONSTER && !(d.HitActor is 'BossBrain') && !d.HitActor.player ) continue; if ( d.HitActor.IsFriend(self) ) continue; bool addme = true; for ( int k=0; k= 10)) ) { invoker.preloadcnt = 0; return A_JumpX("PreFire","Ready_G"); } if ( invoker.preloadcnt >= 10 ) { invoker.preloadcnt = 0; return A_JumpX("PreLoad","Ready_G"); } return ResolveState(null); } return A_JumpX("DoFire","DoFire_G"); } Wait; DoFire: XZW2 A 1 A_HellblazerFire(); XZW2 QRSTUVW 2; XZW2 A 0 { // chain-fire if ( invoker.preload > 0 ) { invoker.preload--; invoker.spinskipped--; return A_JumpX("DoFire","DoFire_G"); } return ResolveState(null); } Goto Cycle; DoFire_G: XZW7 A 1; XZW7 QRSTUVW 2; Goto Ready_G; // state jump to cycling is done elsewhere AltFire: #### # 0 { invoker.preloadcnt = 0; invoker.preload = 0; } #### # 1 { if ( invoker.clipcount <= 0 ) return A_JumpX("Unload","Unload_G"); if ( player.cmd.buttons&BT_ALTATTACK ) { invoker.preloadcnt++; if ( (player.cmd.buttons&BT_ATTACK) || ((invoker.clipcount < 2) && (invoker.preloadcnt >= 10)) ) { invoker.preloadcnt = 0; return A_JumpX("PreAltFire","Ready_G"); } if ( invoker.preloadcnt >= 10 ) { invoker.preloadcnt = 0; return A_JumpX("PreAltLoad","Ready_G"); } return ResolveState(null); } return A_JumpX("DoAltFire","DoAltFire_G"); } Wait; DoAltFire: XZW2 A 1 A_HellblazerFire(true); XZW2 XYZ 2; XZW3 ABCD 2; XZW2 A 0 { // chain-fire if ( invoker.preload > 0 ) { invoker.preload--; invoker.spinskipped--; return A_JumpX("DoAltFire","DoAltFire_G"); } return ResolveState(null); } Goto Cycle; DoAltFire_G: XZW7 A 1; XZW7 XYZ 2; XZW8 ABCD 2; Goto Ready_G; // state jump to cycling is done elsewhere PreLoad: XZW2 A 10 A_StartSound("hellblazer/preload",CHAN_WEAPON,CHANF_OVERLAP); XZW2 A 2 { invoker.magpos = (invoker.magpos+1)%6; invoker.preload++; A_GlassOverlay("Cycle_G"); } XZW3 E 2; XZW3 FGHI 2; XZW3 I 0; XZW3 FE 2; XZW2 A 1 { if ( !(player.cmd.buttons&BT_ATTACK) ) return A_JumpX("DoFire","DoFire_G"); if ( (player.cmd.buttons&BT_ALTATTACK) || (invoker.preload >= min(invoker.clipcount-1,2)) ) return A_JumpX("PreFire","Ready_G"); return A_JumpX("PreLoad","Ready_G"); } Wait; PreAltLoad: XZW2 A 10 A_StartSound("hellblazer/preload",CHAN_WEAPON,CHANF_OVERLAP); XZW2 A 2 { invoker.magpos = (invoker.magpos+1)%6; invoker.preload++; A_GlassOverlay("Cycle_G"); } XZW3 E 2; XZW3 FGHI 2; XZW3 I 0; XZW3 FE 2; XZW2 A 1 { if ( !(player.cmd.buttons&BT_ALTATTACK) ) return A_JumpX("DoAltFire","DoAltFire_G"); if ( (player.cmd.buttons&BT_ATTACK) || (invoker.preload >= min(invoker.clipcount-1,2)) ) return A_JumpX("PreAltFire","Ready_G"); return A_JumpX("PreAltLoad","Ready_G"); } Wait; PreFire: XZW2 A 0 { invoker.seekcnt = 0; A_StartSound("hellblazer/hold",CHAN_WEAPON,CHANF_OVERLAP); } XZW2 A 1 { if ( !(player.cmd.buttons&BT_ATTACK) ) return A_JumpX("DoFire","DoFire_G"); A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); if ( (player.cmd.buttons&BT_ZOOM) && !(player.oldbuttons&BT_ZOOM) ) A_CheckLockOn(); if ( (player.cmd.buttons&BT_RELOAD) && !(player.oldbuttons&BT_RELOAD) ) { for ( int i=0; i<3; i++ ) invoker.seektarget[i] = null; if ( invoker.seekcnt > 0 ) A_StartSound("hellblazer/clear",CHAN_WEAPON,CHANF_OVERLAP); invoker.seekcnt = 0; } return ResolveState(null); } Wait; PreAltFire: XZW2 A 0 { invoker.seekcnt = 0; A_StartSound("hellblazer/hold",CHAN_WEAPON,CHANF_OVERLAP); } XZW2 A 1 { if ( !(player.cmd.buttons&BT_ALTATTACK) ) return A_JumpX("DoAltFire","DoAltFire_G"); A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); if ( (player.cmd.buttons&BT_ZOOM) && !(player.oldbuttons&BT_ZOOM) ) A_CheckLockOn(); if ( (player.cmd.buttons&BT_RELOAD) && !(player.oldbuttons&BT_RELOAD) ) { for ( int i=0; i<3; i++ ) invoker.seektarget[i] = null; if ( invoker.seekcnt > 0 ) A_StartSound("hellblazer/clear",CHAN_WEAPON,CHANF_OVERLAP); invoker.seekcnt = 0; } return ResolveState(null); } Wait; Cycle: XZW2 A 2 { invoker.spinskipped--; invoker.magpos = (invoker.magpos+1)%6; A_GlassOverlay("Cycle_G"); } XZW3 E 2; XZW3 FGHI 2; XZW3 I 0; XZW3 FE 2; Goto Ready; Cycle_G: XZW7 A 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); XZW8 E 2; XZW8 F 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); XZW8 GH 2; XZW8 I 2; XZW8 I 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); XZW8 FE 2; Goto Ready_G; Reload: XZW2 A 2 { if ( invoker.clipcount >= invoker.LoadedCapacity() ) return A_JumpX("Zoom","Zoom_G"); return A_JumpX("Unload","Unload_G"); } Goto Ready; Unload: XZW2 A 2; XZW3 JKLMNOPQRSTUVWXYZ 2; XZW4 ABCDEFGHIJ 2; XZW6 VWXYZ 3; XZW4 J 0 A_SwapAmmo(); Goto Load; Unload_G: XZW7 A 2 A_StartSound("hellblazer/meleestart",CHAN_WEAPON,CHANF_OVERLAP); XZW8 JKL 2; XZW8 M 2 A_StartSound("hellblazer/open",CHAN_WEAPON,CHANF_OVERLAP); XZW8 NOPQ 2; XZW8 RSTUVWXY 2; XZW8 Z 2 A_StartSound("hellblazer/magout",CHAN_WEAPON,CHANF_OVERLAP); XZW9 ABCDEFGHIJ 2; XZWB VWXYZ 3; Goto Load_G; Load: XZW4 JKLMNOPQRSTUVWXYZ 2; XZW5 ABCDEFGHIJ 2; Goto Ready; Load_G: XZW9 J 2 A_PlayerReload(); XZW9 KLMNOP 2; XZW9 Q 2 A_StartSound("hellblazer/magin",CHAN_WEAPON,CHANF_OVERLAP); XZW9 RSTUVWX 2; XZW9 Y 2 A_StartSound("hellblazer/close",CHAN_WEAPON,CHANF_OVERLAP); XZW9 Z 2; XZWA ABC 2; XZWA D 2 A_StartSound("hellblazer/meleestop",CHAN_WEAPON,CHANF_OVERLAP); XZWA EFGHIJ 2; Goto Ready_G; Zoom: XZW2 A 2 A_GlassOverlay("Zoom_G"); XZW5 KLMNOPQRSTUVWXYZ 2; XZW6 A 2; Goto Ready; Zoom_G: XZW7 A 2 { A_StartSound("hellblazer/idle",CHAN_WEAPON,CHANF_OVERLAP); A_PlayerCheckGun(); } XZWA KLMNO 2; XZWA P 2 A_StartSound("hellblazer/dustoff",CHAN_WEAPON,CHANF_OVERLAP); XZWA Q 2; XZWA RSTUVWXYZ 2; XZWB A 2; Goto Ready_G; User1: XZW2 A 2 A_GlassOverlay("User1_G"); XZW6 BCDE 2; XZW6 FGH 1; XZW6 IJK 2; XZW6 LMNOPQRSTU 2; Goto Ready; User1_G: XZW7 A 2 { A_StartSound("hellblazer/meleestart",CHAN_WEAPON,CHANF_OVERLAP); A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); A_PlayerMelee(); } XZWB BCD 2; XZWB E 2 A_Parry(9); XZWB FG 1; XZWB H 1 A_Melee(75,"demolitionist/whitl",1.05,1.2,1.2); XZWB IJKLM 2; XZWB N 2 A_StartSound("hellblazer/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZWB OPQRSTU 2; Goto Ready_G; } }