// Inventory stuff Mixin Class SWWMAutoUseFix { override bool HandlePickup( Inventory item ) { bool res = Super.HandlePickup(item); if ( res && (item.GetClass() == item.GetClass()) ) { if ( Use(true) ) Amount--; if ( Amount <= 0 ) DepleteOrDestroy(); } return res; } } // Base class for all SWWM Armors Class SWWMArmor : Armor abstract { int priority; String drainmsg; Class parent; Property ArmorPriority : priority; Property DrainMessage : drainmsg; Property GiverArmor : parent; Default { +INVENTORY.AUTOACTIVATE; +INVENTORY.UNTOSSABLE; +INVENTORY.UNDROPPABLE; +INVENTORY.KEEPDEPLETED; +INVENTORY.ALWAYSPICKUP; } override void AttachToOwner( Actor other ) { Super.AttachToOwner(other); // find last armor that's better than us Inventory found = null; for ( Inventory i=other.Inv; i; i=i.Inv ) { if ( !(i is 'SWWMArmor') || (i == self) || (SWWMArmor(i).priority < priority) ) continue; found = i; } if ( !found ) return; // place ourselves right after it Inventory saved = found.Inv; found.Inv = self; other.Inv = Inv; Inv = saved; } // for subclasses virtual int HandleDamage( int damage, Name damageType, int flags ) { return damage; } override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) { if ( !passive ) return; int saved; if ( (amount > 0) && !DamageTypeDefinition.IgnoreArmor(damageType) && (damage > 0) ) { SWWMHandler.DoFlash(Owner,Color(int(clamp(damage*.15,1,16)),255,224,192),3); Owner.A_StartSound("armor/hit",CHAN_BODY,CHANF_DEFAULT,clamp(damage*.03,0.,1.),2.5); saved = HandleDamage(damage,damageType,flags); if ( amount <= saved ) saved = amount; newdamage -= saved; if ( newdamage < 0 ) Owner.GiveBody(abs(newdamage)); amount -= saved; damage = newdamage; if ( amount <= 0 ) { if ( damage > 0 ) newdamage = ApplyDamageFactors(GetClass(),damageType,damage,damage); if ( Owner.CountInv(parent) > 0 ) { Amount = default.Amount; if ( GetDefaultByType(parent).UseSound ) Owner.A_StartSound(GetDefaultByType(parent).UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); Owner.TakeInventory(parent,1); } else { if ( Owner.CheckLocalView() && (drainmsg != "") ) Console.Printf(StringTable.Localize(drainmsg)); DepleteOrDestroy(); } return; } } if ( damage > 0 ) newdamage = ApplyDamageFactors(GetClass(),damageType,damage,damage); } } // gives armor when used Class SWWMSpareArmor : Inventory abstract { Mixin SWWMAutoUseFix; Class giveme; Property GiveArmor : giveme; override bool Use( bool pickup ) { bool shouldautouse = false; if ( swwm_enforceautousearmor == 1 ) shouldautouse = true; else if ( swwm_enforceautousearmor == -1 ) shouldautouse = false; else shouldautouse = CVar.GetCVar('swwm_autousearmor',Owner.player).GetBool(); if ( pickup && !shouldautouse ) return false; let cur = Owner.FindInventory(giveme); if ( !cur || (cur.Amount < cur.MaxAmount) ) { Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); SWWMHandler.DoFlash(Owner,Color(48,96,255,64),5); return true; } return false; } Default { +INVENTORY.INVBAR; +INVENTORY.ISARMOR; +INVENTORY.AUTOACTIVATE; Inventory.MaxAmount 5; Inventory.InterHubAmount 5; +FLOATBOB; FloatBobStrength 0.25; } } Class SWWMHealth : Inventory abstract { Mixin SWWMAutoUseFix; // can't use the Health class for whatever reason // nice parser you got there I guess? Class giveme; Property GiveHealth : giveme; override bool Use( bool pickup ) { bool shouldautouse = false; if ( swwm_enforceautousehealth == 1 ) shouldautouse = true; else if ( swwm_enforceautousehealth == -1 ) shouldautouse = false; else shouldautouse = CVar.GetCVar('swwm_autousehealth',Owner.player).GetBool(); if ( pickup && !shouldautouse ) return false; if ( Owner.Health >= GetDefaultByType(giveme).MaxAmount ) return false; if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); SWWMHandler.DoFlash(Owner,Color(48,64,128,255),5); Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_GREEN); return true; } virtual void AutoUseExtra() { } override void DoEffect() { Super.DoEffect(); if ( Amount <= 0 ) DepleteOrDestroy(); } override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) { if ( passive && (Owner.Health-damage <= 0) ) { if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); while ( (Amount > 0) && (newdamage > 0) ) { newdamage = damage-GetDefaultByType(giveme).Amount; if ( newdamage < 0 ) Owner.GiveBody(-newdamage,GetDefaultByType(giveme).MaxAmount); newdamage = max(0,newdamage); AutoUseExtra(); Amount--; } } else newdamage = damage; } Default { +INVENTORY.INVBAR; +INVENTORY.ISHEALTH; +INVENTORY.AUTOACTIVATE; Inventory.MaxAmount 5; Inventory.InterHubAmount 5; Inventory.UseSound "misc/health_pkup"; +FLOATBOB; FloatBobStrength 0.25; } } // Base casing classes Class SWWMCasing : Actor abstract { int deadtimer, numbounces; double pitchvel, anglevel; double heat; Default { Radius 2; Height 2; +NOBLOCKMAP; +MISSILE; +MOVEWITHSECTOR; +THRUACTORS; +USEBOUNCESTATE; +INTERPOLATEANGLES; +NOTELEPORT; +ROLLSPRITE; +ROLLCENTER; Mass 1; Gravity 0.35; BounceType "Hexen"; WallBounceFactor 0.65; BounceFactor 0.65; BounceSound "explodium/casing"; } override void PostBeginPlay() { Super.PostBeginPlay(); deadtimer = 0; pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); heat = 1.0; } override void Tick() { Super.Tick(); if ( isFrozen() ) return; if ( InStateSequence(CurState,ResolveState("Death")) ) { deadtimer++; if ( deadtimer > 300 ) A_FadeOut(0.05); return; } heat -= 0.05; if ( heat <= 0 ) return; let s = Spawn("SWWMSmallSmoke",pos); s.alpha *= heat; } States { Spawn: XZW1 A 1 { angle += anglevel; pitch += pitchvel; } Loop; Bounce: #### # 0 { pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); vel = (vel.unit()+(FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2))).unit()*vel.length(); if ( numbounces && ((numbounces > 3) || (Random[Junk](1,20) < 17) || (vel.z > -1.4)) ) { ClearBounce(); ExplodeMissile(); } numbounces++; } Goto Spawn; Death: #### # -1 { pitch = roll = 0; angle = FRandom[Junk](0,360); } Stop; } } Class SWWMBulletImpact : Actor { Default { RenderStyle "Add"; Radius 0.1; Height 0; +NOGRAVITY; +NOBLOCKMAP; +DONTSPLASH; +NOTELEPORT; Scale 0.25; } override void PostBeginPlay() { Super.PostBeginPlay(); A_SprayDecal("Pock",-20); int numpt = int(Random[Junk](5,10)*scale.x*4); Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); for ( int i=0; i 2 ) Destroy(); } } // Base class for all SWWM Weapons Class SWWMWeapon : Weapon abstract { private int SWeaponFlags; FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count) override void AttachToOwner (Actor other) { Inventory.AttachToOwner(other); Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1); Ammo2 = AddAmmo(Owner,AmmoType2,bNoFirstGive?0:AmmoGive2); SisterWeapon = AddWeapon(SisterWeaponType); if ( Owner.player ) { if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch ) Owner.player.PendingWeapon = self; if ( Owner.player.mo == players[consoleplayer].camera ) StatusBar.ReceivedWeapon(self); } GivenAsMorphWeapon = false; } override void DetachFromOwner() { Owner.A_StopSound(CHAN_WEAPON); Owner.A_StopSound(CHAN_WEAPONEXTRA); Super.DetachFromOwner(); } override void OwnerDied() { if ( Owner.player && (Owner.player.ReadyWeapon == self) ) { Owner.A_StopSound(CHAN_WEAPON); Owner.A_StopSound(CHAN_WEAPONEXTRA); } A_ClearRefire(); Super.OwnerDied(); } override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) { if ( mod == 'Melee' ) return StringTable.Localize("$O_MELEE"); return Super.GetObituary(victim,inflictor,mod,playerattack); } // draw ammo on hud above weapon box virtual ui void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) { } // HUD-side ticking virtual ui void HudTick() { } // instant raise/lower action void A_FullRaise() { if ( !player ) return; if ( player.PendingWeapon != WP_NOCHANGE ) { player.mo.DropWeapon(); return; } if ( !player.ReadyWeapon ) return; let psp = player.GetPSprite(PSP_WEAPON); psp.y = WEAPONTOP; } action void A_FullLower() { if ( !player ) return; if ( !player.ReadyWeapon ) { player.mo.BringUpWeapon(); return; } let psp = player.GetPSprite(PSP_WEAPON); psp.y = WEAPONBOTTOM; if ( player.playerstate == PST_DEAD ) { // Player is dead, so don't bring up a pending weapon // Player is dead, so keep the weapon off screen player.SetPSprite(PSP_FLASH,null); psp.SetState(player.ReadyWeapon.FindState('DeadLowered')); return; } // [RH] Clear the flash state. Only needed for Strife. player.SetPSprite(PSP_FLASH,null); player.mo.BringUpWeapon(); } private action bool TryMelee( double angle, int dmg ) { FTranslatedLineTarget t; double slope = AimLineAttack(angle,DEFMELEERANGE,t,0.,ALF_CHECK3D); FLineTraceData d; LineTrace(angle,DEFMELEERANGE,slope,0,player.viewheight,data:d); if ( d.HitType == TRACE_HitActor ) { bool bloodless = true; double diff = deltaangle(self.angle,AngleTo(d.HitActor)); self.angle += clamp(diff,-5.,5.); SWWMHandler.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000); if ( CountInv("RagekitPower") ) { invoker.bEXTREMEDEATH = true; invoker.bNOEXTREMEDEATH = false; } else { invoker.bEXTREMEDEATH = false; invoker.bNOEXTREMEDEATH = true; } d.HitActor.DaggerAlert(self); dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH; invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH; if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25); if ( !d.HitActor.bNOBLOOD && !d.HitActor.bINVULNERABLE ) { d.HitActor.TraceBleed(dmg,invoker); d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); } else bloodless = false; A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); A_StartSound(bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP); A_AlertMonsters(300); return true; } return false; } action void A_Melee( int dmg = 40 ) { for ( int i=0; i<16; i++ ) if ( TryMelee(angle+i*(45./16),dmg) || TryMelee(angle-i*(45./16),dmg) ) return; // check for walls instead FTranslatedLineTarget t; double slope = AimLineAttack(angle,DEFMELEERANGE,t,0.,ALF_CHECK3D); FLineTraceData d; LineTrace(angle,DEFMELEERANGE,slope,TRF_THRUACTORS,player.viewheight,data:d); if ( d.HitType == TRACE_HitNone ) return; if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4); A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); A_StartSound("demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP); A_AlertMonsters(100); } override void PlayUpSound( Actor origin ) { if ( UpSound ) origin.A_StartSound(UpSound,CHAN_WEAPON,CHANF_OVERLAP); } action void A_SWWMFlash( StateLabel flashlabel = null ) { if ( !player || !player.ReadyWeapon ) return; Weapon weap = player.ReadyWeapon; State flashstate = null; if ( !flashlabel ) { if ( weap.bAltFire ) flashstate = weap.FindState('AltFlash'); if ( !flashstate ) flashstate = weap.FindState('Flash'); } else flashstate = weap.FindState(flashlabel); player.SetPSprite(PSP_FLASH,flashstate); A_OverlayFlags(PSP_FLASH,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderStyle(PSP_FLASH,STYLE_Add); } // tells the SWWM HUD that this weapon has ammo available virtual clearscope bool ReportHUDAmmo() { return (!Ammo1||(Ammo1.Amount>0)||(Ammo2&&(Ammo2.Amount>0))); } // tells the Embiggener that this weapon uses the specified ammo type // even if it is not its primary one virtual clearscope bool UsesAmmo( Class kind ) { return (AmmoType1&&(kind is AmmoType1))||(AmmoType2&&(kind is AmmoType2)); } override void ModifyDropAmount( int dropamount ) { Super.ModifyDropAmount(dropamount); if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) ) AmmoGive1 = 1; if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) ) AmmoGive2 = 1; } Default { Weapon.BobStyle "Alpha"; Weapon.BobSpeed 3.0; Weapon.BobRangeX 0.5; Weapon.BobRangeY 0.2; Weapon.YAdjust 0; +WEAPON.NOALERT; +WEAPON.NODEATHINPUT; +FLOATBOB; FloatBobStrength 0.25; } }