// The Demolitionist Class Demolitionist : PlayerPawn { int last_jump_held, last_boost, last_kick; Vector3 dashdir; double dashfuel, dashboost; int dashcooldown, boostcooldown, fuelcooldown; bool fullfuel; bool sendtoground; bool key_reentrant; bool bInDefaultInventory; bool oldsinglefirst; int lastdamage; transient int lastdamagetic; bool lastground; int lastgroundtic, lastairtic; double lastvelz, prevvelz, landvelz; double ssup; transient CVar myvoice; SWWMStats mystats; int cairtime; bool hasteleported; bool hasrevived; int lastmpain; double guideangle, guidepitch, guideroll; // for weapon bobbing stuff Array bumpvelz, bumppitch; double oldangle, oldpitch; double oldlagangle, oldlagpitch, oldlagready; Vector3 oldlagvel; double lagangle, lagpitch, lagready; Vector3 lagvel; enum EUnderType { UNDER_NONE, UNDER_WATER, UNDER_SLIME, UNDER_LAVA }; int lastunder; Color undercol; int deadtimer; transient int revivefail; transient int bumptic; transient double lastbump; Actor selflight; Actor oldencroached; Vector3 oldencroachedpos; int encroachtics; Vector3 pretelepos; SWWMItemSense itemsense; int itemsense_cnt; int healcooldown, healtimer, oldhealth; bool scriptedinvul; bool hitactivate; Actor froggy; transient int lastuse, failcounter, failcooldown; transient SWWMItemTracer itrace; bool meleeuse; transient bool bWalking; Property DashFuel : dashfuel; Default { Tag "$T_DEMOLITIONIST"; Speed 1; Radius 16; // should be 9 in theory, but it'd be too thin Height 56; Mass 500; PainChance 255; MaxSlopeSteepness 0; // mountain goat mode, all slopes are walkable Player.DisplayName "$T_DEMOLITIONIST"; // StartItem array is defined but not used directly // just declared here for mod compat Player.StartItem "ExplodiumGun"; Player.StartItem "DeepImpact"; Player.StartItem "AlmasteelPlating"; Player.StartItem "SayaCollar"; Player.ViewHeight 52; Player.AirCapacity 0; Player.GruntSpeed 20; Player.ForwardMove 1., 1.; Player.SideMove 1., 1.; Player.SoundClass "demolitionist"; DamageFactor "Drowning", 0.; DamageFactor "Poison", 0.; DamageFactor "PoisonCloud", 0.; DamageFactor "Falling", 0.; Demolitionist.DashFuel 240.; +NOBLOOD; +DONTGIB; +NOICEDEATH; +DONTMORPH; +DONTDRAIN; +DONTCORPSE; } // oof Vector2 NormalizedMove() { if ( !(player.cmd.forwardmove|player.cmd.sidemove) ) return (0,0); Vector2 mvec = (player.cmd.forwardmove,-player.cmd.sidemove/.96); double maxval = max(abs(mvec.x),abs(mvec.y)); // sorry, we don't use that here specifically, that's for tweakspeed to handle if ( !(player.cmd.buttons&BT_RUN) ) maxval *= 2.; return mvec.unit()*maxval; } double TweakSpeed() { double fact = bWalking?.08:(player.cmd.buttons&BT_RUN)?1.25:.5; for ( Inventory i=Inv; i; i=i.Inv ) fact *= i.GetSpeedFactor(); return fact; } // oh yay, more cheat modification override void CheatGive( String name, int amount ) { if ( !player.mo || (player.health <= 0) ) return; int giveall = ALL_NO; if ( name ~== "all" ) giveall = ALL_YES; else if ( name ~== "everything" ) giveall = ALL_YESYES; if ( giveall || (name ~== "health") ) { if ( amount > 0 ) { health = min(health+amount,1000); player.health = health; } else player.health = health = 200; } if ( giveall || (name ~== "backpack") ) { let def = GetDefaultByType('HammerspaceEmbiggener'); GiveInventory('TradedHammerspaceEmbiggener',def.MaxAmount,true); if ( !giveall ) return; } if ( giveall || (name ~== "ammo") ) { // first we must build an array of all valid weapons, this saves time instead of doing recursive loops Array > validweapons; for ( int i=0; i)(AllActorClasses[i]); if ( !type2 ) continue; let rep = GetReplacement(type2); if ( (rep != type2) && !(rep is "DehackedPickup") ) continue; readonly weap = GetDefaultByType(type2); if ( !player || !player.weapons.LocateWeapon(type2) || weap.bCheatNotWeapon ) continue; let ready = weap.FindState("Ready"); if ( !ready || !ready.ValidateSpriteFrame() ) continue; validweapons.Push(type2); } // Find every unique type of ammo. Give it to the player if // he doesn't have it already, and set each to its maximum. for ( int i=0; i)(AllActorClasses[i]); if ( !type || (type.GetParentClass() != "Ammo") ) continue; // Only give if it's for a valid weapon, unless using "give everything" bool isvalid = false; for ( int j=0; j weap = GetDefaultByType(validweapons[j]); if ( (validweapons[j] is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(type) ) { isvalid = true; break; } if ( (weap.AmmoType1 == type) || (weap.AmmoType2 == type) ) { isvalid = true; break; } } if ( !isvalid && (giveall != ALL_YESYES) ) continue; let ammoitem = FindInventory(type); if ( !ammoitem ) { ammoitem = Inventory(Spawn(type)); ammoitem.AttachToOwner(self); ammoitem.Amount = ammoitem.MaxAmount; } else if ( ammoitem.Amount < ammoitem.MaxAmount ) ammoitem.Amount = ammoitem.MaxAmount; } // also give mag ammo for ( int i=0; i)(AllActorClasses[i]); if ( !type || (type.GetParentClass() != "MagAmmo") ) continue; let pamo = GetDefaultByType(type).ParentAmmo; // Only give if it's for a valid weapon, unless using "give everything" bool isvalid = false; for ( int j=0; j)(AllActorClasses[j]); if ( !type2 ) continue; let rep = GetReplacement(type2); if ( (rep != type2) && !(rep is "DehackedPickup") ) continue; readonly weap = GetDefaultByType(type2); if ( !player.weapons.LocateWeapon(type2) || (weap.bCheatNotWeapon && (giveall != ALL_YESYES)) ) continue; let ready = weap.FindState("Ready"); if ( !ready || !ready.ValidateSpriteFrame() ) continue; if ( (type2 is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(pamo) ) { isvalid = true; break; } if ( (weap.AmmoType1 == pamo) || (weap.AmmoType2 == pamo) ) { isvalid = true; break; } } if ( !isvalid && (giveall != ALL_YESYES) ) continue; let magitem = FindInventory(type); if ( !magitem ) { magitem = Inventory(Spawn(type)); magitem.AttachToOwner(self); magitem.Amount = magitem.MaxAmount; } else if ( magitem.Amount < magitem.MaxAmount ) magitem.Amount = magitem.MaxAmount; } if ( !giveall ) return; } if ( giveall || (name ~== "armor") ) { // Give only subclasses of SWWMArmor for ( int i=0; i)(AllActorClasses[i]); if ( !type || (type == 'SWWMArmor') ) continue; if ( GetReplacement(type) == type ) { let owned = FindInventory(type); if ( owned ) { owned.Amount = owned.MaxAmount; continue; } let item = Inventory(Spawn(type)); item.ClearCounters(); // don't increase item counts item.Amount = item.MaxAmount; if ( !item.CallTryPickup(self) ) item.Destroy(); } } // Also give spares for ( int i=0; i)(AllActorClasses[i]); if ( !type || (type == 'SWWMSpareArmor') ) continue; if ( GetReplacement(type) == type ) { let owned = FindInventory(type); if ( owned ) { owned.Amount = owned.MaxAmount; continue; } let item = Inventory(Spawn(type)); item.ClearCounters(); // don't increase item counts item.Amount = item.MaxAmount; if ( !item.CallTryPickup(self) ) item.Destroy(); } } if ( !giveall ) return; } if ( giveall || (name ~== "keys") ) { for ( int i=0; i)(AllActorClasses[i]); if ( !type ) continue; let keyitem = GetDefaultByType(type); if ( keyitem.special1 ) { let rep = GetReplacement(type); // handle replaced keys if ( !(rep is 'Key') ) continue; // iwad restrictions (vanilla doesn't care, but here they'll show in the inventory) if ( !(gameinfo.gametype&GAME_HERETIC) && ((rep is 'SWWMKeyGreen') || (rep is 'SWWMKeyBlue') || (rep is 'SWWMKeyYellow')) ) continue; if ( !(gameinfo.gametype&GAME_DOOMCHEX) && ((rep is 'SWWMRedCard') || (rep is 'SWWMBlueCard') || (rep is 'SWWMYellowCard')) ) continue; let item = Inventory(Spawn(rep)); SWWMHandler.KeyTagFix(item); if ( item is 'SWWMKey' ) SWWMKey(item).propagated = true; // no anim key_reentrant = true; if ( !item.CallTryPickup(self) ) item.Destroy(); key_reentrant = false; } } if ( !giveall ) return; } if ( giveall || (name ~== "weapons") ) { let savedpending = player.PendingWeapon; for ( int i=0; i)(AllActorClasses[i]); if ( !type || (type == "Weapon") ) continue; // Don't give already owned weapons let owned = FindInventory(type); if ( owned && (owned.Amount >= owned.MaxAmount) ) continue; // Don't give replaced weapons unless the replacement was done by Dehacked. let rep = GetReplacement(type); if ( (rep == type) || (rep is "DehackedPickup") ) { // Give the weapon only if it is set in a weapon slot. if ( !player.weapons.LocateWeapon(type) ) continue; readonly def = GetDefaultByType(type); if ( (giveall == ALL_YESYES) || !def.bCheatNotWeapon ) GiveInventory(type,1,true); } } player.PendingWeapon = savedpending; if ( !giveall ) return; } if ( giveall || (name ~== "artifacts") ) { for ( int i=0; i)(AllActorClasses[i]); if ( !type ) continue; // no fabricators before hexen if ( !(gameinfo.gametype&GAME_Hexen) && (type is 'AmmoFabricator') ) continue; // no barriers outside doom if ( !(gameinfo.gametype&GAME_DOOM) && (type is 'EBarrier') ) continue; // no gravity outside raven if ( !(gameinfo.gametype&GAME_RAVEN) && (type is 'GravitySuppressor') ) continue; // Don't give maxed items let owned = FindInventory(type); if ( owned && (owned.Amount >= owned.MaxAmount) ) continue; let def = GetDefaultByType (type); if ( def.Icon.isValid() && !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "MagAmmo") && !(type is "Armor") && !(type is "Key") ) { // Do not give replaced items unless using "give everything" if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) ) GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true); } } if ( !giveall ) return; } if ( giveall || (name ~== "puzzlepieces") ) { for ( int i=0; i)(AllActorClasses[i]); if ( !type ) continue; let def = GetDefaultByType(type); if ( !def.Icon.isValid() ) continue; // Do not give replaced items unless using "give everything" if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) ) GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true); } if ( !giveall ) return; } if ( (giveall == ALL_YESYES) || (name ~== "collectibles") ) { for ( int i=0; i)(AllActorClasses[i]); if ( !type || (type == 'SWWMCollectible') ) continue; let def = GetDefaultByType(type); // check that we can collect it in this IWAD if ( !def.ValidGame() ) continue; let item = Inventory(Spawn(AllActorClasses[i])); SWWMCollectible(item).propagated = true; // no score or anims if ( !item.CallTryPickup(self) ) item.Destroy(); } } if ( giveall ) return; let type = name; if ( !type ) { if ( PlayerNumber() == consoleplayer ) A_Log(String.Format("Unknown item \"%s\"\n",name)); } else GiveInventory(type,amount,true); } override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) { if ( inflictor && inflictor.FindInventory("ParriedBuff") ) return StringTable.Localize("$O_PARRY"); if ( mod == 'Jump' ) return StringTable.Localize("$O_JUMP"); if ( mod == 'Dash' ) return StringTable.Localize("$O_DASH"); if ( mod == 'Buttslam' ) return StringTable.Localize("$O_BUTT"); if ( mod == 'GroundPound' ) return StringTable.Localize("$O_POUND"); return Super.GetObituary(victim,inflictor,mod,playerattack); } override void GiveDefaultInventory() { if ( !player ) return; bInDefaultInventory = true; // prevent default inventory from adding lore entries (fixes lore library disappearing when loading saves WHILE DEAD) // we don't need hexen or basic armors // we can simplify the code a lot here since no handling of many special conditions is needed let sc = Inventory(Spawn('SayaCollar')); sc.AttachToOwner(self); let ap = Inventory(Spawn('AlmasteelPlating')); ap.AttachToOwner(self); let dp = Inventory(Spawn('DeepImpact')); dp.AttachToOwner(self); let eg = ExplodiumGun(Spawn('ExplodiumGun')); eg.AttachToOwner(self); eg.chambered = true; DualExplodiumGun(eg.SisterWeapon).chambered = true; player.ReadyWeapon = player.PendingWeapon = eg; // in hexdd, we start with the chaos sphere in our grasp if ( SWWMUtility.IsDeathkings() && SWWMUtility.CheckMD5List("vanillahexen.list") ) { let cs = Inventory(Spawn('SWWMChaosSphere')); cs.AttachToOwner(self); } // in deathmatch, we start with 8 embiggeners if ( deathmatch ) { let em = Inventory(Spawn('TradedHammerspaceEmbiggener')); em.Amount = em.MaxAmount; if ( !em.CallTryPickup(self) ) em.Destroy(); } bInDefaultInventory = false; } override void ClearInventory() { Super.ClearInventory(); // remove some specific "undroppable" items Actor last = self; while ( last.inv ) { let i = last.inv; if ( (i is 'SWWMArmor') || (i is 'HammerspaceEmbiggener') ) { i.DepleteOrDestroy(); if ( !i.bDestroyed ) last = i; } else last = i; } } override void PostBeginPlay() { Super.PostBeginPlay(); oldsinglefirst = swwm_singlefirst; // super already sets up the slots, so save the cvar value now mystats = SWWMStats.Find(player); lastground = true; } void A_Dash() { vel += dashdir*dashboost*clamp(dashfuel/20.,0.,1.); player.vel *= 0.; if ( dashboost < .2 ) dashboost = 0.; else { if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:800); if ( !(player.cmd.buttons&BT_USER2) ) dashboost *= .1; } double fueluse = (dashfuel-max(0.,dashfuel-dashboost))/60.; SWWMUtility.AchievementProgressIncDouble('swwm_progress_fuel',fueluse,player); mystats.fuelusage += fueluse; if ( !swwm_superfuel ) dashfuel = max(0.,dashfuel-dashboost); dashcooldown = min(40,max(10,int(dashcooldown*1.4))); fuelcooldown = max(30,fuelcooldown); if ( (dashfuel <= 0.) && fullfuel ) SWWMUtility.AchievementProgressInc('swwm_progress_brake',1,player); if ( (dashfuel <= 0.) || (dashboost <= 0.) ) SetStateLabel("DashEnd"); } void A_BoostUp( bool initial = false ) { vel += (0,0,1)*dashboost*clamp(dashfuel/10.,0,1.); player.vel *= 0.; if ( dashboost < .2 ) dashboost = 0.; else { if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:800); dashboost *= (player.cmd.buttons&BT_JUMP)?.95:.4; last_boost = level.maptime+1; } double fueluse = (dashfuel-max(0.,dashfuel-dashboost))/60.; SWWMUtility.AchievementProgressIncDouble('swwm_progress_fuel',fueluse,player); mystats.fuelusage += fueluse; if ( !swwm_superfuel ) dashfuel = max(0.,dashfuel-dashboost); if ( ((dashfuel <= 0.) || (dashboost <= 0.)) ) { if ( !initial ) { if ( player.onground ) SetStateLabel("JumpEnd"); else SetStateLabel("Fall"); } return; } fuelcooldown = max(20,fuelcooldown); } void SenseItems() { let thesight = FindInventory("Omnisight"); let bt = BlockThingsIterator.Create(self,800); while ( bt.Next() ) { let i = bt.Thing; if ( !i || (!(i is 'Inventory') && !(i is 'Chancebox') && !(i is 'SWWMRespawnTimer')) ) continue; if ( (i is 'Inventory') && (i.bINVISIBLE || !i.bSPECIAL || Inventory(i).Owner) ) continue; if ( (i is 'Chancebox') && (i.CurState != i.SpawnState) ) continue; if ( !SWWMUtility.SphereIntersect(i,pos,800) ) continue; if ( !thesight && !CheckSight(i,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue; SWWMItemSense.Spawn(self,i); } } void CheckDefaceTexture() { if ( player.usedown ) return; FLineTraceData d; LineTrace(angle,DEFMELEERANGE*2,pitch,TRF_THRUACTORS,player.viewheight,data:d); if ( d.HitType == TRACE_HitNone ) return; bool remove; TextureID replacewith; [remove, replacewith] = SWWMUtility.DefaceTexture(d.HitTexture); if ( !remove ) return; if ( (d.HitType != TRACE_HitWall) || !d.HitLine.special || !(d.HitLine.activation&SPAC_Use) ) player.usedown = true; A_StartSound("bestsound",CHAN_ITEMEXTRA,CHANF_OVERLAP); lastbump *= .97; int scr = (TexMan.GetName(d.HitTexture).Left(6)~=="ZZWOLF")?200:20; SWWMCredits.Give(player,scr); if ( player == players[consoleplayer] ) SWWMScoreObj.Spawn(scr,d.HitLocation); if ( d.HitType == TRACE_HitWall ) { if ( d.Hit3DFloor ) { if ( d.Hit3DFloor.flags&F3DFloor.FF_UPPERTEXTURE ) d.HitLine.sidedef[d.LineSide].SetTexture(0,replacewith); else if ( d.Hit3DFloor.flags&F3DFloor.FF_LOWERTEXTURE ) d.HitLine.sidedef[d.LineSide].SetTexture(2,replacewith); else d.Hit3DFloor.master.sidedef[0].SetTexture(1,replacewith); } else d.HitLine.sidedef[d.LineSide].SetTexture(d.LinePart,replacewith); } else if ( d.HitType == TRACE_HitCeiling ) { if ( d.Hit3DFloor ) { if ( d.Hit3DFloor.flags&F3DFloor.FF_INVERTSECTOR ) d.Hit3DFloor.model.SetTexture(1,replacewith); else d.Hit3DFloor.model.SetTexture(0,replacewith); } else d.HitSector.SetTexture(1,replacewith); } else if ( d.HitType == TRACE_HitFloor ) { if ( d.Hit3DFloor ) { if ( d.Hit3DFloor.flags&F3DFloor.FF_INVERTSECTOR ) d.Hit3DFloor.model.SetTexture(0,replacewith); else d.Hit3DFloor.model.SetTexture(1,replacewith); } else d.HitSector.SetTexture(0,replacewith); } } void CheckItemUsePickup() { if ( player.usedown ) return; if ( !itrace ) itrace = new("SWWMItemTracer"); Vector3 x, y, z, dir; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); Vector3 origin = Vec2OffsetZ(0,0,player.viewz); Sector os = level.PointInSector(origin.xy); int rings = 1; Array ignoreme; ignoreme.Clear(); for ( double i=0; i<.2; i+=.02 ) { for ( int j=0; j<360; j+=(360/rings) ) { dir = (x+y*cos(j)*i+z*sin(j)*i).unit(); itrace.Trace(origin,os,dir,UseRange,0); if ( itrace.Results.HitType != TRACE_HitActor ) continue; if ( ignoreme.Find(itrace.Results.HitActor) < ignoreme.Size() ) continue; player.usedown = true; // we found an item, ignore further uses if ( itrace.Results.HitActor.Used(self) ) return; ignoreme.Push(itrace.Results.HitActor); } rings += 2; } } void CheckUnderwaterAmb( bool restore = false ) { Vector3 headpos = Vec3Offset(0,0,player.viewheight); Vector3 centerpos = Vec3Offset(0,0,height/2); Sector headregion = null; if ( CurSector.moreflags&Sector.SECMF_UNDERWATER ) // check underwater sector headregion = CurSector; else if ( CurSector.heightsec && (Cursector.heightsec.moreflags&Sector.SECMF_UNDERWATER) ) // check height transfer { let hsec = CurSector.heightsec; double fh = hsec.floorplane.ZAtPoint(pos.xy); if ( pos.z < fh ) { if ( headpos.z <= fh ) headregion = hsec; } else if ( !(hsec.moreflags&Sector.SECMF_FAKEFLOORONLY) && (headpos.z > hsec.ceilingplane.ZAtPoint(pos.xy)) ) headregion = hsec; } else // check 3D floors { for ( int i=0; i centerpos.z) ) continue; if ( headpos.z <= ff_top ) headregion = ff.model; break; } } int curunder = UNDER_NONE; if ( headregion ) { switch ( headregion.damagetype ) { case 'Fire': case 'Lava': curunder = UNDER_LAVA; break; case 'Slime': case 'Poison': case 'PoisonCloud': curunder = UNDER_SLIME; break; case 'Ice': case 'Drowning': default: curunder = UNDER_WATER; break; } undercol = headregion.ColorMap.LightColor; } if ( (curunder != lastunder) || restore ) { static const string undersnd[] = {"","misc/underwater","misc/underslime","misc/underlava"}; static const string entersnd[] = {"","misc/waterenter","misc/slimeenter","misc/lavaenter"}; static const string exitsnd[] = {"","misc/waterexit","misc/slimeexit","misc/lavaexit"}; A_StopSound(CHAN_AMBEXTRA); if ( curunder > UNDER_NONE ) { A_StartSound(undersnd[curunder],CHAN_AMBEXTRA,CHANF_LOOP|CHANF_UI); if ( !restore && (players[consoleplayer].Camera == self) ) A_StartSound(entersnd[curunder],CHAN_FOOTSTEP,CHANF_OVERLAP|CHANF_UI); } if ( !restore && (lastunder > UNDER_NONE) && (players[consoleplayer].Camera == self) ) A_StartSound(exitsnd[lastunder],CHAN_FOOTSTEP,CHANF_OVERLAP|CHANF_UI); } if ( curunder > UNDER_NONE ) A_SoundVolume(CHAN_AMBEXTRA,(players[consoleplayer].Camera==self)?1.:0.); lastunder = curunder; } override void CheckFOV() { if ( !player ) return; float desired = player.desiredfov; // adjust fov from weapon (abs due to special use of negative // to prevent it from scaling look sensitivity) if ( (player.playerstate != PST_DEAD) && player.readyweapon && player.readyweapon.fovscale ) desired *= abs(player.readyweapon.fovscale); // additional fov bump from various effects // akin to the old A_ZoomFactor trick, but not limited to weapons and can stack if ( lastbump <= 0. ) lastbump = 1.; if ( lastbump != 1. ) { player.fov *= lastbump; lastbump = 1.; } // adjust fov from dashing double spd = vel.length(); if ( InStateSequence(CurState,FindState("Dash")) && (spd > 10.) ) { Vector3 facedir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)); if ( spd > 0. ) { double rel = max(0,vel.unit() dot facedir); desired *= 1.+clamp(rel*(spd-10.),-80.,80.)*.002; } } if ( player.fov == desired ) return; // interpolate towards desired fov if ( abs(player.fov-desired) < .1 ) player.fov = desired; else { float zoom = max(.1,abs(player.fov-desired)*.35); if ( player.fov > desired ) player.fov -= zoom; else player.fov += zoom; } } override Vector2 BobWeapon( double ticfrac ) { // non-mod weapons bob normally if ( !(player.ReadyWeapon is 'SWWMWeapon') ) return Super.BobWeapon(ticfrac); bool oldbob = !!(player.WeaponState&WF_WEAPONBOBBING); player.WeaponState |= WF_WEAPONBOBBING; // always bob Vector2 cur = Super.BobWeapon(ticfrac); if ( !oldbob ) player.WeaponState &= ~WF_WEAPONBOBBING; double fangle = oldangle*(1.-ticfrac)+angle*ticfrac; double fpitch = (oldpitch*(1.-ticfrac)+pitch*ticfrac); double flagangle = (oldlagangle*(1.-ticfrac)+lagangle*ticfrac); double flagpitch = (oldlagpitch*(1.-ticfrac)+lagpitch*ticfrac); double diffang = fangle-flagangle; double diffpitch = fpitch-flagpitch; if ( abs(diffang) > 1. ) { int sgn = (diffang>0)?1:-1; diffang = abs(diffang)**.7*sgn; } if ( abs(diffpitch) > 1. ) { int sgn = (diffpitch>0)?1:-1; diffpitch = abs(diffpitch)**.7*sgn; } Vector3 flagvel = oldlagvel*(1.-ticfrac)+lagvel*ticfrac; double diffx = flagvel dot (cos(flagangle+90),sin(flagangle+90),0); double diffy = flagvel dot (0,0,1); if ( abs(diffx) > 1. ) { int sgn = (diffx>0)?1:-1; diffx = abs(diffx)**.5*sgn; } if ( abs(diffy) > 1. ) { int sgn = (diffy>0)?1:-1; diffy = abs(diffy)**.5*sgn; } // don't do inertial sway when in 6dof mode, causes issues if ( !swwm_fly6dof || !((waterlevel < 2) && bFly && !bFlyCheat && !(player.cheats&CF_NOCLIP2)) ) { cur.x += diffang; cur.y -= diffpitch; cur.x += diffx*4.; cur.y += diffy*4.; } return cur*(oldlagready*(1.-ticfrac)+lagready*ticfrac); } override void PlayerThink() { oldangle = angle; oldpitch = pitch; if ( player && (player.mo == self) && (player.playerstate != PST_DEAD) && (player.cmd.buttons&BT_USE) ) { if ( !player.usedown ) lastuse = gametic; CheckDefaceTexture(); if ( !player.usedown && froggy ) player.usedown = froggy.Used(self); // try to "use" the item closest to the crosshair CheckItemUsePickup(); } Super.PlayerThink(); if ( (gametic == lastuse) && IsActorPlayingSound(CHAN_VOICE,"*usefail") ) { failcounter++; if ( (failcounter > 8) && !Random[DemoLines](0,max(0,12-failcounter/3)) && (gametic > failcooldown) ) { failcooldown = SWWMHandler.AddOneliner("puzzfail",2,20); failcounter = max(4,failcounter-10); } } else if ( gametic > lastuse+50 ) failcounter = 0; oldlagangle = lagangle; oldlagpitch = lagpitch; lagangle = lagangle*.8+angle*.2; lagpitch = lagpitch*.8+pitch*.2; if ( !player || (player.mo != self) ) return; if ( (player.playerstate != PST_DEAD) && (player.jumptics != 0) ) { // faster falloff player.jumptics -= 5; if ( player.onground && (player.jumptics < -18) ) player.jumptics = 0; } if ( player.playerstate != PST_DEAD ) { // quick grenade if ( player.cmd.buttons&BT_USER4 ) SWWMGesture.SetGesture(self,GS_Grenade); // emergency melee with no weapon else if ( !player.ReadyWeapon && (player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1)) ) SWWMGesture.SetGesture(self,GS_EmptyMelee); } } void NearbyItemSparkles() { if ( (player != players[consoleplayer]) || !swwm_itemsparkles ) return; let bt = BlockThingsIterator.Create(self,500); while ( bt.Next() ) { let t = bt.Thing; if ( !t || !(t is 'Inventory') || !t.bSPECIAL || t.bINVISIBLE || Inventory(t).Owner || !SWWMUtility.SphereIntersect(t,pos,500) ) continue; Vector3 bpos = (0,0,16); if ( t.bFLOATBOB ) bpos.z += t.GetBobOffset(); double alph = clamp((500.-Distance3D(t))/500.,0.,1.); Color pcol = "Gold"; if ( Inventory(t).PickupFlash is 'SWWMPickupFlash' ) { let def = GetDefaultByType(Inventory(t).PickupFlash); pcol = Color(def.Args[1]*85,def.Args[2]*85,def.Args[3]*85); } int numpt = clamp(int(max(t.radius,t.height-16)/4),1,8); for ( int i=0; i oldhealth) ) { healtimer = 0; healcooldown = 80; } else if ( health > 200 ) { if ( healcooldown > 0 ) healcooldown--; else { if ( health > 500 ) { if ( !FindInventory("GrilledCheeseSafeguard") && !(healtimer%5) ) A_SetHealth(health-1); } else if ( health > 200 ) { if ( !FindInventory("RefresherRegen") && !(healtimer%15) ) A_SetHealth(health-1); } healtimer++; } } } oldhealth = health; oldlagvel = lagvel; oldlagready = lagready; if ( player.weaponstate&WF_WEAPONBOBBING ) lagready = lagready*.9+.1; else lagready = lagready*.4; lagvel = lagvel*.8+vel*.2; double traveldist = level.Vec3Diff(oldpos,pos).length(); if ( waterlevel < 2 ) { if ( !player.onground || bNoGravity ) { cairtime++; if ( cairtime > mystats.airtime ) mystats.airtime = cairtime; mystats.airdist += traveldist; } else { cairtime = 0; mystats.grounddist += traveldist; } } else mystats.swimdist += traveldist; CheckUnderwaterAmb(); if ( player.cmd.buttons&BT_USER3 ) SenseItems(); if ( vel.length() > mystats.topspeed ) mystats.topspeed = vel.length(); if ( vel.length() > ((3600*GameTicRate)/32000.) ) SWWMUtility.AchievementProgress('swwm_progress_sanic',int((vel.length()*3600*GameTicRate)/32000.),player); if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player); if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) ) { // bump down weapon bumpvelz.Push(-lastvelz); double bpitch = min(-lastvelz/10.,20); if ( lastvelz < -25 ) { let s = Spawn("DemolitionistShockwave",pos); s.target = self; s.special1 = int(-lastvelz); A_AlertMonsters(swwm_uncapalert?0:2500); bpitch = min(bpitch+30,60); lastbump *= 1.3; if ( FindInventory("RagekitPower") ) { // stop for just a split second UNLESS bunnyhopping if ( !(player.cmd.buttons&BT_RUN) || (level.maptime >= (lastairtic+10)) ) ReactionTime = 6; } else { A_Stop(); ReactionTime = 17; } if ( player == players[consoleplayer] ) { A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP); A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7); A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4); } mystats.stompcount++; } double newp = min(90,pitch+bpitch); bumppitch.Push(newp-pitch); A_SetPitch(newp,SPF_INTERPOLATE); if ( lastvelz < -10 ) { A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP); if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:200); } if ( (lastvelz < -gruntspeed) && (swwm_mutevoice < 4) && (health > 0) ) A_StartSound(String.Format("voice/%s/grunt",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP); if ( lastvelz < -1 ) A_Footstep(0,1,clamp(-lastvelz*0.05,0.0,1.0)); // bounce off slopes if ( pos.z <= floorz ) { F3DFloor ff; for ( int i=0; i= -25) && (!oldencroached.bISMONSTER || (oldencroached.Health > 0)) ) vel += oldencroached.vel+level.Vec3Diff(oldencroachedpos,oldencroached.pos); oldencroached = null; encroachtics = 0; } if ( encroached && encroached.bSHOOTABLE && !encroached.bNODAMAGE && (lastvelz <= 0) && !(encroached is 'Demolitionist') ) { if ( (lastvelz < -5) || !(level.maptime%5) ) { int realdmg = encroached.DamageMobj(self,self,int((2+max(0,-lastvelz*3))*max(1.,mass/encroached.mass)),'Jump',DMG_THRUSTLESS); if ( FindInventory("RagekitPower") ) { let ps = Spawn("BigPunchSplash",pos); ps.damagetype = 'Jump'; ps.target = self; ps.special1 = realdmg; } if ( encroached && !encroached.bNOBLOOD && !encroached.bINVULNERABLE ) { encroached.TraceBleed(realdmg,self); encroached.SpawnBlood(pos,angle,realdmg); } } } for ( int i=0; i double.epsilon ) continue; bumpvelz.Delete(i--); } for ( int i=0; i double.epsilon ) continue; bumppitch.Delete(i--); } if ( player.onground && !lastground ) landvelz = lastvelz; else if ( !player.onground && lastground ) landvelz = 0; else if ( player.onground && lastground ) landvelz *= .9; lastground = player.onground; lastvelz = prevvelz; prevvelz = vel.z; bool isdashing = InStateSequence(CurState,FindState("Dash")); bool isboosting = InStateSequence(CurState,FindState("Boost")); bNOFRICTION = (((waterlevel<2)&&(bFly&&!bFlyCheat&&!(player.cheats&CF_NOCLIP2)))||isdashing); fuelcooldown = max(0,fuelcooldown-1); dashcooldown = max(0,dashcooldown-1); boostcooldown = max(0,boostcooldown-1); if ( fuelcooldown <= 0 ) dashfuel = min(default.dashfuel,dashfuel+clamp(dashfuel*.025,.1,3.)); if ( ((dashboost <= 0) || !(isdashing || (isboosting && player.cmd.buttons&BT_JUMP))) && IsActorPlayingSound(CHAN_JETPACK,"demolitionist/jet") ) A_StartSound("demolitionist/jetstop",CHAN_JETPACK); PainChance = isdashing?0:255; if ( isdashing || (vel.length() > 30) ) { bool oldpush = bCANPUSHWALLS; bool olduse = bCANUSEWALLS; bool oldmcross = bACTIVATEMCROSS; bool oldtele = bNOTELEPORT; // needed to prevent the many TryMove calls from activating unwanted lines bCANPUSHWALLS = false; bCANUSEWALLS = false; bACTIVATEMCROSS = false; bNOTELEPORT = true; Actor a; if ( isdashing && (dashboost > 0.) ) { for ( int i=-1; i<=1; i+=2 ) for ( int j=1; j<4; j++ ) { a = Spawn("DashTrail",Vec3Angle(15,angle+i*140,35)); a.target = self; a.vel = (RotateVector((j,0),angle+i*160),0)-(0,0,1)*j; a.vel -= vel*.5; } } Vector3 dir = vel; double spd = dir.length(); dir = dir/spd; Vector3 viewdir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); // look for things we could potentially bump into bool bumped = false; let bi = BlockThingsIterator.Create(self,500); let raging = RagekitPower(FindInventory("RagekitPower")); double maxmass = max(mass*spd/40.,200); if ( raging ) maxmass *= 2; while ( (spd > 0) && bi.Next() ) { a = bi.Thing; if ( !a || (a == self) || (!a.bSOLID && !a.bSHOOTABLE) || a.bTHRUACTORS || a.bCORPSE ) continue; if ( !SWWMUtility.ExtrudeIntersect(self,a,dir*(spd+radius),8) ) continue; if ( (a.pos.z <= a.floorz) && (a.height <= MaxStepHeight) ) continue; Vector3 diff = level.Vec3Diff(pos,a.pos); Vector3 dirto = diff.unit(); if ( dir dot dirto < .1 ) continue; if ( (diff.z <= -a.height) && (lastvelz < -25) ) continue; if ( (diff.z <= -a.height) && !isdashing ) continue; // don't bump bridges if hit at a specific angle if ( a.bACTLIKEBRIDGE ) { Vector3 bnorm = -dirto; if ( diff.z <= -a.height ) continue; // no bump from above else if ( diff.z >= Height ) bnorm = (0,0,-1); else if ( diff.x > a.Radius+Radius ) bnorm = (-1,0,0); else if ( diff.x < -(a.Radius+Radius) ) bnorm = (1,0,0); else if ( diff.y > a.Radius+Radius ) bnorm = (0,-1,0); else if ( diff.y < -(a.Radius+Radius) ) bnorm = (0,1,0); if ( dir dot bnorm > -.6 ) continue; } if ( !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue; // large monsters will stop the player (unless hit from above if we're going at ground pound speed) A_QuakeEx(4,4,4,10,0,128,"",QF_RELATIVE|QF_SCALEDOWN); A_AlertMonsters(swwm_uncapalert?0:800); A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); a.A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); bumptic = gametic+int(20+spd/4.); lastbump *= .8; if ( (a.bDONTTHRUST || a.bACTLIKEBRIDGE || (a.Mass >= maxmass) || (!a.bSHOOTABLE && !a.bPUSHABLE && (a.Health > 0))) && a.bSOLID && (dir dot dirto > .65) ) { if ( bumped ) continue; bumped = true; SWWMUtility.AchievementProgressInc('swwm_progress_bonk',1,player); A_QuakeEx(8,8,8,16,0,128,"",QF_RELATIVE|QF_SCALEDOWN); vel *= .2; vel -= dir*(10+(spd*30/mass)); vel -= dirto*(10+(spd*50/mass)); vel.z += 5+(spd*(10/mass)); dashboost *= 0.; } Vector3 pushdir = dirto*.1+dir*.9; if ( !a.bDONTTHRUST && (a.Mass < maxmass) && (a.bSHOOTABLE || a.bPUSHABLE) ) { a.vel += pushdir*(25+(spd*20/max(50,a.mass))); if ( (a.pos.z <= a.floorz) || !a.TestMobjZ() ) a.vel.z += 5+(spd*(5/max(50,a.mass))); } int flg = DMG_THRUSTLESS; if ( raging ) flg |= DMG_FOILINVUL; if ( !a.player && !a.bDONTBLAST ) a.bBLASTED = true; int dmg = int(10+spd*3.); bool buttslam = false; // BUTTSLAM if ( dir dot viewdir < -.3 ) { dmg *= 3; buttslam = true; } if ( a.bSHOOTABLE ) { dmg = a.DamageMobj(self,self,dmg,buttslam?'Buttslam':'Dash',flg); if ( a && !a.bNOBLOOD && (raging || !a.bINVULNERABLE) ) { a.TraceBleed(dmg,self); a.SpawnBlood(level.Vec3Offset(pos,diff/2),atan2(dir.y,dir.x)+180,dmg); } if ( buttslam && (!a || (a.Health <= 0)) ) { A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHANF_OVERLAP,1.,.4); if ( swwm_buttsfx ) A_StartSound("demolitionist/buttslamx",CHAN_DAMAGE,CHAN_OVERLAP,1.,.2); Spawn("SWWMItemFog",level.Vec3Offset(pos,diff/2)); A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.); mystats.buttslams++; lastbump *= .8; } } if ( raging ) { let ps = Spawn("BigPunchSplash",level.Vec3Offset(pos,diff/2)); ps.damagetype = buttslam?'Buttslam':'Dash'; ps.target = self; ps.special1 = dmg; raging.DoHitFX(); } } // check for ceiling collision if ( (spd > 0) && !bumped && ((pos.z+Height+dir.z*spd) >= ceilingz) ) { F3DFloor ff; for ( int i=0; i 0) && !bumped && !TryMove(Vec2Offset(steppy.x,steppy.y),1,false,tm) && SWWMUtility.BlockingLineIsBlocking(self,Line.ML_BLOCKEVERYTHING|Line.ML_BLOCKING|Line.ML_BLOCK_PLAYERS) ) { Vector3 wallnorm = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit(); int lside = 1; if ( !BlockingLine.sidedef[1] || !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) ) { lside = 0; wallnorm *= -1; } // don't bump if we're only grazing it if ( dir dot wallnorm > -.6 ) continue; bool buttslam = false; // BUTTSLAM if ( dir dot viewdir < -.3 ) { buttslam = true; // leave buttmark A_SprayDecal("ButtMark",172,(cos(angle+90)*2,sin(angle+90)*2,Height*.54),dir); A_SprayDecal("ButtMark",172,(cos(angle-90)*2,sin(angle-90)*2,Height*.54),dir); } if ( raging || swwm_omnibust ) { // see if we can bust it let tempme = new("LineTracer"); // gross hack to pass needed data int dmg = int(10+spd*3.); if ( raging ) dmg *= 8; if ( buttslam ) dmg *= 3; tempme.Results.HitLine = BlockingLine; tempme.Results.HitType = TRACE_HitWall; tempme.Results.Side = lside; tempme.Results.Tier = TIER_MIDDLE; if ( BlockingLine.sidedef[1] ) { double ceilz = BlockingLine.sidedef[!lside].sector.ceilingplane.ZAtPoint(pos.xy); double florz = BlockingLine.sidedef[!lside].sector.floorplane.ZAtPoint(pos.xy); if ( pos.z+Height >= ceilz ) tempme.Results.Tier = TIER_UPPER; else if ( pos.z <= florz ) tempme.Results.Tier = TIER_LOWER; } if ( BusterWall.Bust(tempme.Results,dmg,self,dir,pos.z+Height) ) { // busted through if ( raging ) { let ps = Spawn("BigPunchSplash",Vec3Offset(dir.x*radius,dir.y*radius,(tempme.Results.Tier==TIER_UPPER)?Height:(tempme.Results.Tier==TIER_LOWER)?0:(Height/2))); ps.damagetype = buttslam?'Buttslam':'Dash'; ps.target = self; ps.special1 = int(10+spd*3.); raging.DoHitFX(); } A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); if ( buttslam ) { A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHANF_OVERLAP,1.,.4); if ( swwm_buttsfx ) A_StartSound("demolitionist/buttslamx",CHAN_DAMAGE,CHAN_OVERLAP,1.,.2); Spawn("SWWMItemFog",Vec3Offset(dir.x*radius,dir.y*radius,(tempme.Results.Tier==TIER_UPPER)?Height:(tempme.Results.Tier==TIER_LOWER)?0:(Height/2))); A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.); mystats.buttslams++; lastbump *= .8; } if ( raging ) continue; // don't stop } } // wallbump bumped = true; SWWMUtility.AchievementProgressInc('swwm_progress_bonk',1,player); A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); bumptic = gametic+int(25+spd/4.); lastbump *= .8; A_QuakeEx(8,8,8,16,0,128,"",QF_RELATIVE|QF_SCALEDOWN); A_AlertMonsters(swwm_uncapalert?0:800); vel *= .2; vel -= dir*(10+(spd*30/mass)); vel += wallnorm*(10+(spd*50/mass)); vel.z += 5+(spd*(10/mass)); dashboost *= 0.; if ( raging ) { let ps = Spawn("BigPunchSplash",Vec3Offset(dir.x*radius,dir.y*radius,Height/2.)); ps.damagetype = (dir dot viewdir < -3.)?'Buttslam':'Dash'; ps.target = self; ps.special1 = int(10+spd*3.); raging.DoHitFX(); } // activate it int locknum = SWWMUtility.GetLineLock(BlockingLine); if ( !locknum || CheckKeys(locknum,false,true) ) { hitactivate = true; BlockingLine.Activate(self,lside,SPAC_Use); hitactivate = false; } BlockingLine.Activate(self,lside,SPAC_Impact); break; } } // check for slope boosting (only if dashing) if ( (pos.z <= floorz) && (spd > 0) && isdashing ) { F3DFloor ff; for ( int i=0; i 0.) ) { Actor a; for ( int i=-1; i<=1; i+=2 ) for ( int j=1; j<4; j++ ) { a = Spawn("DashTrail",Vec3Angle(10,angle+i*140,40)); a.target = self; a.vel = .5*(RotateVector((j,0),angle+i*160),0)-(0,0,1)*j; a.vel -= vel*.5; } } } private void CheckBreakCrusher() { double gaph = (ceilingz-floorz); if ( gaph > height*.6 ) return; // the smaller the gap, the more likely the crusher will snap if ( Random[Demolitionist](0,3) && (FRandom[Demolitionist](0,gaph/height) > .1) ) return; double diffh = 8.+(default.height-gaph); // how much the crusher will have to "snap" after breaking let ceil = ceilingsector; let flor = floorsector; let ceilse = ceil.ceilingdata; let florse = flor.floordata; if ( ceilse && florse ) { // snap both planes let q = Spawn("BustedQuake",(ceil.centerspot.x,ceil.centerspot.y,ceil.ceilingplane.ZAtPoint(ceil.centerspot))); q.special1 = 6; q = Spawn("BustedQuake",(flor.centerspot.x,flor.centerspot.y,flor.floorplane.ZAtPoint(flor.centerspot))); q.special1 = 6; SWWMCrusherBroken.Create(flor,ceil,diffh/2.); } else if ( ceilse ) { // snap ceiling let q = Spawn("BustedQuake",(ceil.centerspot.x,ceil.centerspot.y,ceil.ceilingplane.ZAtPoint(ceil.centerspot))); q.special1 = 10; SWWMCrusherBroken.Create(null,ceil,diffh); } else if ( florse ) { // snap floor let q = Spawn("BustedQuake",(flor.centerspot.x,flor.centerspot.y,flor.floorplane.ZAtPoint(flor.centerspot))); q.special1 = 10; SWWMCrusherBroken.Create(flor,null,diffh); } } override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle ) { // we still have to ENSURE ENTIRELY that this gets nullified (TELEFRAG_DAMAGE overrides damage factors somehow) if ( (mod == 'Falling') | (mod == 'Drowning') || (mod == 'Poison') || (mod == 'PoisonCloud') ) damage = 0; if ( (swwm_strictuntouchable >= 2) && (damage > 0) && player ) { let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); if ( hnd ) hnd.tookdamage[PlayerNumber()] = true; } // check if we can break any active crushers if ( (mod == 'Crush') && !inflictor && !source ) CheckBreakCrusher(); // no damage whatsoever if ( scriptedinvul ) return 0; if ( damage <= 0 ) return Super.DamageMobj(inflictor,source,damage,mod,flags,angle); if ( !inflictor && !source && (FloorSector.flags&Sector.SECF_ENDLEVEL) ) { // end level hax damage = max(50,health-100); flags |= DMG_FORCED|DMG_NO_ARMOR; mod = 'EndLevel'; } int oldpchance = PainChance; if ( damage < 5 ) PainChance = 0; int realdmg = Super.DamageMobj(inflictor,source,damage,mod,flags,angle); // hotfix damagecount overflow until proper fix is in stable gzdoom if ( player && (player.damagecount < 0) ) player.damagecount = 100; lastdamage = max(lastdamage,realdmg); lastdamagetic = max(lastdamagetic,gametic+clamp(lastdamage/2,10,40)); if ( (lastdamage > 0) && (PainChance == 0) && (level.maptime>lastmpain) ) { lastmpain = level.maptime; A_DemoPain(); } PainChance = oldpchance; if ( (Health <= 0) && (source == self) && (flags&DMG_EXPLOSION) ) SWWMUtility.MarkAchievement('swwm_achievement_dime',player); return realdmg; } override void CalcHeight() { Super.CalcHeight(); // handle smooth step down (hacky but looks ok) player.viewz += ssup; ssup = max(0,(ssup*.7)-.25); } override void CheckPitch() { if ( (waterlevel < 2) && bFly && !bFlyCheat && !(player.cheats&CF_NOCLIP2) ) return; // handled in moveplayer Super.CheckPitch(); } override void CheckMoveUpDown() { if ( InStateSequence(CurState,FindState("Dash")) ) player.cmd.upmove = 0; if ( (waterlevel < 2) && bFly && !bFlyCheat && !(player.cheats&CF_NOCLIP2) ) { double fs = TweakSpeed(); Vector3 x, y, z; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); Vector3 accel; if ( (player.cmd.upmove == -32768) || sendtoground ) { sendtoground = true; player.centering = true; accel = (0,0,-4096); } else accel = z*player.cmd.upmove*8.; accel *= fs/128.; vel = vel+accel/GameTicRate; if ( sendtoground ) vel.xy *= .6; if ( (pos.z <= floorz) || bOnMobj ) sendtoground = false; if ( vel.length() > 50. ) vel = vel.unit()*50.; return; } else sendtoground = false; Super.CheckMoveUpDown(); } override void MovePlayer() { if ( !player || (player.mo != self) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) ) { dashboost = 0.; player.vel *= 0.; if ( IsActorPlayingSound(CHAN_JETPACK,"demolitionist/jet") ) A_StartSound("demolitionist/jetstop",CHAN_JETPACK); Super.MovePlayer(); return; } bool isdashing = InStateSequence(CurState,FindState("Dash")); if ( isdashing ) player.cmd.forwardmove = player.cmd.sidemove = 0; if ( (waterlevel < 2) && bFly && !bFlyCheat && !(player.cheats&CF_NOCLIP2) ) { player.onground = false; if ( player.turnticks ) { player.turnticks--; guideangle = (180./TURN180_TICKS); } else guideangle += .2*player.cmd.yaw*(360./65536.); guidepitch -= .2*player.cmd.pitch*(360./65536.); if ( player.centering ) guidepitch = clamp(deltaangle(pitch,0),-3.,3.); if ( player.centering || !swwm_fly6dof ) guideroll = clamp(deltaangle(roll,0),-3.,3.); if ( swwm_fly6dof ) { swwm_Quaternion orient = swwm_Quaternion.createFromAngles(angle,pitch,roll); swwm_Quaternion angles = swwm_Quaternion.createFromAngles(guideangle,guidepitch,guideroll); orient = orient.multiplyQuat(angles); double npitch, nangle, nroll; [nangle, npitch, nroll] = orient.toAngles(); A_SetAngle(nangle,SPF_INTERPOLATE); A_SetPitch(npitch,SPF_INTERPOLATE); A_SetRoll(nroll,SPF_INTERPOLATE); } else { A_SetAngle(angle+guideangle,SPF_INTERPOLATE); A_SetPitch(clamp(pitch+guidepitch,player.MinPitch,player.MaxPitch),SPF_INTERPOLATE); A_SetRoll(roll+guideroll,SPF_INTERPOLATE); } if ( (abs(roll) <= 1./65536.) && (abs(pitch) <= 1./65536.) ) { guideroll = guidepitch = roll = pitch = 0.; player.centering = false; } double fs = TweakSpeed(); double jcmove = 0.; if ( player.cmd.buttons&BT_JUMP ) jcmove += 4096.; if ( player.cmd.buttons&BT_CROUCH ) jcmove -= 4096.; if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor; Vector3 x, y, z; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); Vector2 nmove = NormalizedMove(); Vector3 accel = x*nmove.x-y*nmove.y+z*jcmove; accel *= fs/320.; double spd = vel.length(); if ( spd > 40. ) vel = (vel+accel/GameTicRate).unit()*spd; else vel = vel+accel/GameTicRate; vel *= .97; player.vel = (1,1)*vel.length(); player.jumptics = -2; if ( !(player.cheats & CF_PREDICTING) && (player.cmd.forwardmove|player.cmd.sidemove) ) PlayRunning(); if ( player.cheats&CF_REVERTPLEASE ) { player.cheats &= ~CF_REVERTPLEASE; player.camera = player.mo; } } else { if ( player.turnticks ) { player.turnticks--; angle = (180./TURN180_TICKS); } else angle += player.cmd.yaw*(360./65536.); player.onground = (pos.z<=floorz)||bOnMobj||bMBFBouncer||(player.cheats&CF_NOCLIP2); if ( player.cmd.forwardmove|player.cmd.sidemove ) { double bobfactor; double friction, movefactor; [friction, movefactor] = GetFriction(); bobfactor = (friction maxspd ) vel = (vel+accel/GameTicRate).unit()*spd; else vel = vel+accel/GameTicRate; } if ( !(player.cheats&CF_PREDICTING) && (nmove.length() > 0.) ) PlayRunning(); if ( player.cheats&CF_REVERTPLEASE ) { player.cheats &= ~CF_REVERTPLEASE; player.camera = player.mo; } } else if ( player.onground ) vel *= .95; // quickly decelerate if we're not holding movement keys if ( abs(roll) > 0. ) A_SetRoll(roll+clamp(deltaangle(roll,0),-3.,3.),SPF_INTERPOLATE); } guideangle *= .9; guidepitch *= .9; guideroll *= .9; // anchor to ground when going down steps if ( lastground && !player.onground && !bFly && !bFlyCheat && (waterlevel < 2) && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) && (vel.z < 0) && !isdashing ) { // test for gap crossing (i.e: climbing up platforms with holes between them) Vector3 storepos = pos; double storefloorz = floorz; bool crossgap = false; for ( int i=1; i<=4; i++ ) // test up to 4 steps ahead, should be enough for most cases { SetOrigin(Vec3Offset(vel.x,vel.y,vel.z),true); if ( floorz < storepos.z ) continue; crossgap = true; break; } SetOrigin(storepos,true); floorz = storefloorz; if ( !crossgap ) { ssup = max(0,(pos.z-floorz)); SetZ(floorz); lastground = player.onground = true; } } if ( player.onground ) lastgroundtic = level.maptime; else lastairtic = level.maptime; if ( !(player.cheats & CF_PREDICTING) && !(player.cmd.forwardmove|player.cmd.sidemove) ) PlayIdle(); Vector3 dodge = (0,0,0), x, y, z; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); int fm = player.cmd.forwardmove; int sm = player.cmd.sidemove; if ( !(fm|sm) ) fm = 1; if ( fm ) dodge += (fm>0)?X:-X; if ( sm ) dodge += (sm>0)?Y:-Y; if ( player.cmd.buttons&BT_CROUCH ) dodge = (0,0,-1); // death from above if ( dodge == (0,0,0) ) { if ( !player.onground || bNOGRAVITY || (waterlevel > 2) || (player.cheats&CF_NOCLIP2) ) dodge = X; else dodge.xy = RotateVector((1,0),angle); } if ( player.onground && !bNOGRAVITY && (waterlevel < 2) && !(player.cheats&CF_NOCLIP2) ) { dodge.z = max(0,dodge.z); if ( !level.IsJumpingAllowed() ) dodge.z = min(0,dodge.z); } if ( (dodge.length() > 0) && (dashcooldown <= 0) && (dashfuel > 20.) && player.cmd.buttons&BT_USER2 && (player.onground || level.IsJumpingAllowed() || (player.cmd.buttons&BT_CROUCH)) && (gamestate == GS_LEVEL) ) { fullfuel = (dashfuel >= default.dashfuel); dashdir = dodge.unit(); dashcooldown = 10; dashboost = 20.; bOnMobj = false; if ( player.cheats & CF_REVERTPLEASE ) { player.cheats &= ~CF_REVERTPLEASE; player.camera = player.mo; } vel += dashdir*dashboost; vel.z += clamp(-vel.z*.4,0.,30.); player.jumptics = -1; SetStateLabel("Dash"); A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP); lastbump *= .95; mystats.dashcount++; double newp = min(90.,pitch+5.); bumppitch.Push(newp-pitch); A_SetPitch(newp,SPF_INTERPOLATE); } } override void CheckJump() { if ( InStateSequence(CurState,FindState("Dash")) ) return; // do not if ( !(player.cmd.buttons&BT_JUMP) || (gamestate != GS_LEVEL) ) return; Vector2 walldir = (cos(angle),sin(angle)); bool walljump = false, wallclimb = false; double climbvelz = 0.; FLineTraceData d; Actor jumpactor = null; for ( int i=-4; i= 2 ) vel.z = 4*Speed; else if ( (waterlevel < 2) && bFly && !bFlyCheat && !(player.cheats&CF_NOCLIP2) ) return; else if ( bNoGravity ) vel.z = 3; else if ( level.IsJumpingAllowed() && ((player.onground && (player.jumptics == 0)) || (!player.onground && (level.maptime > last_jump_held) && (((dashfuel > 10.) && (boostcooldown <= 0)) || walljump || wallclimb))) ) { if ( !player.onground && (((walljump || wallclimb) && (level.maptime < last_kick+8)) || (!(walljump || wallclimb) && (level.maptime < last_boost+8))) ) return; double jumpvelz = JumpZ*35./GameTicRate; double jumpfac = 0; for ( let p=Inv; p; p=p.Inv ) { let pp = PowerHighJump(p); if ( pp ) { double f = pp.Strength; if ( f > jumpfac ) jumpfac = f; } } if ( jumpfac > 0 ) jumpvelz *= jumpfac; bool raging = FindInventory("RagekitPower"); if ( raging ) jumpvelz *= 2.; double pvelz = vel.z; if ( !player.onground && !(player.cheats&CF_PREDICTING) ) { // check for wall stuff if ( walljump ) { if ( vel.z < 10. ) vel.z += 2.*jumpvelz+clamp(-pvelz*.6,0.,30.); vel.xy += walldir*20*Speed; lastbump *= .95; } else if ( wallclimb ) { if ( vel.z < 10. ) vel.z += climbvelz+clamp(-pvelz*.95,0.,30.); vel.xy += walldir*10*Speed; lastbump *= .97; } if ( walljump && jumpactor && jumpactor.bSHOOTABLE ) { SWWMUtility.DoKnockback(jumpactor,(-walldir,0),12000); int dmg = jumpactor.DamageMobj(self,self,10,'Jump'); if ( raging ) { let ps = Spawn("BigPunchSplash",pos); ps.damagetype = 'Jump'; ps.target = self; ps.special1 = dmg; } } if ( walljump || wallclimb ) { A_StartSound(walljump?"demolitionist/kick":"demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP); if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:100); last_kick = level.maptime+1; SWWMUtility.AchievementProgressInc('swwm_progress_jump',1,player); } } bOnMobj = false; player.jumpTics = -1; if ( !(player.cheats&CF_PREDICTING) ) { A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP); if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:100); } if ( (dashfuel > 10.) && !player.onground && !walljump && !wallclimb ) { dashboost = 3.; boostcooldown = 20; if ( vel.z < 10. ) vel.z += jumpvelz+clamp(-pvelz*.4,0.,30.); A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP); lastbump *= .95; mystats.boostcount++; last_boost = level.maptime+1; double newp = min(90.,pitch+3.); bumppitch.Push(newp-pitch); A_SetPitch(newp,SPF_INTERPOLATE); SetStateLabel("Boost"); } else { dashboost = 0.; double bpitch = clamp((vel.length()-10)/5.,0.,20.); double newp = min(90.,pitch+bpitch); bumppitch.Push(newp-pitch); A_SetPitch(newp,SPF_INTERPOLATE); // bunnyhop time if ( !walljump && !wallclimb ) { if ( !bWalking && (level.maptime < (lastairtic+10)) ) { SWWMUtility.AchievementProgressInc('swwm_progress_bune',1,player); // bhop, z vel relative to vel size if ( vel.z < 25. ) // don't ramp up too hard { vel.z += jumpvelz*(((player.cmd.buttons&BT_RUN)?1.2:.65)+vel.length()*.01); // add part of last landing z velocity too vel.z += max(0,-landvelz*(raging?.45:.35)); } // accelerate vel.xy += (RotateVector(NormalizedMove(),angle)/2400.)*(1.+vel.length()*.025)*TweakSpeed(); } else { // first jump if ( vel.z < 10. ) // don't ramp up too hard { vel.z += jumpvelz*(bWalking?.75:(player.cmd.buttons&BT_RUN)?1.25:1.); // add part of last landing z velocity too vel.z += max(0,-landvelz*(raging?.35:.25)); } // long jump if running/sprinting if ( !walljump && !wallclimb && !bWalking ) vel.xy += (RotateVector(NormalizedMove(),angle)/1500.)*(raging?2.:1.)*TweakSpeed(); } } SetStateLabel("Jump"); } } last_jump_held = level.maptime+1; } override void DeathThink() { player.Uncrouch(); TickPSprites(); player.onground = (pos.Z<=floorz); // ded (demo-chan falls faster tho) player.deltaviewheight = 0; if ( player.viewheight > 6 ) player.viewheight -= 3; if ( player.viewheight < 6 ) player.viewheight = 6; // center pitch double dpitch = clamp(deltaangle(pitch,0),-6,6); if ( abs(dpitch) < 3. ) pitch = 0.; else A_SetPitch(pitch+dpitch,SPF_INTERPOLATE); // add roll double droll = clamp(deltaangle(roll,50)*.5,-5,5); if ( abs(droll) < 2. ) roll = 50.; else A_SetRoll(roll+droll,SPF_INTERPOLATE); player.mo.CalcHeight(); if ( player.damagecount ) player.damagecount--; if ( player.poisoncount ) player.poisoncount--; // solid unless we can respawn, for safety if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) ) bSolid = false; else bSolid = true; if ( player.viewheight <= 6 ) { deadtimer++; if ( (deadtimer == 60) && (player == players[consoleplayer]) ) A_StartSound("demolitionist/youdied",CHAN_DEMOVOICE,CHANF_OVERLAP|CHANF_UI); if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) ) { // standard behaviour, respawn normally if ( (((player.cmd.buttons&BT_USE) || ((deathmatch || alwaysapplydmflags) && sv_forcerespawn)) && !sv_norespawn) && ((Level.maptime >= player.respawn_time) || ((player.cmd.buttons&BT_USE) && !player.Bot)) ) { player.cls = null; player.playerstate = PST_REBORN; if ( special1 > 2 ) special1 = 0; } } else if ( (player.cmd.buttons&BT_USE) && (deadtimer > 120) ) { // reload save player.cls = null; player.playerstate = PST_ENTER; if ( special1 > 2 ) special1 = 0; } else if ( (player.cmd.buttons&BT_ATTACK) && (deadtimer > 120) && swwm_revive ) { // reboot (if possible) if ( !FindInventory("ReviveCooldown") && (((swwm_revivecooldown >= 0) && (G_SkillPropertyInt(SKILLP_ACSReturn) < 4)) || !hasrevived) ) { if ( hasrevived ) SWWMUtility.MarkAchievement('swwm_achievement_sekiro',player); hasrevived = true; player.Resurrect(); player.damagecount = 0; player.bonuscount = 0; player.poisoncount = 0; SetState(FindState("Spawn")+1); // skip tweening roll = 0; let s = Spawn("DemolitionistShockwave",pos); s.target = self; s.special1 = 30; ReactionTime = 17; A_Stop(); A_AlertMonsters(swwm_uncapalert?0:2500); if ( player == players[consoleplayer] ) { A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP); A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7); A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4); } lastbump *= 1.5; SWWMHandler.DoFlash(self,Color(255,255,255,255),10); SWWMHandler.DoFlash(self,Color(255,128,192,255),30); SWWMScoreObj.Spawn(default.health,Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Height/2),ST_Health); if ( special1 > 2 ) special1 = 0; if ( (swwm_revivecooldown > 0) && (G_SkillPropertyInt(SKILLP_ACSReturn) < 4) ) GiveInventory("ReviveCooldown",1); } else if ( level.maptime > revivefail ) { if ( player == players[consoleplayer] ) A_StartSound("menu/fail",CHAN_ITEM,CHANF_UI); revivefail = level.maptime+120; } } } else deadtimer = 0; } override void PlayIdle() { if ( !player ) { if ( !InStateSequence(CurState,FindState("Spawn")) ) SetStateLabel("Spawn"); return; } if ( player.health <= 0 ) return; if ( !bNoGravity && player.onground && (waterlevel < 3) ) { // Ground if ( player.crouchdir == -1 ) { // Crouching if ( InStateSequence(CurState,FindState("CrouchMove")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("Crouch"); else if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Float")) || InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("StartCrouch"); } else { if ( InStateSequence(CurState,FindState("Crouch")) || InStateSequence(CurState,FindState("CrouchMove")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("EndCrouch"); else if ( InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("Float")) ) { SetStateLabel("Spawn"); A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2); } else if ( InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) ) SetStateLabel("SeeFastEnd"); else if ( InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("SwimEnd"); } } else if ( !bNoGravity && (waterlevel < 1) ) { // Falling if ( player.crouchdir == -1 ) { if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Jump")) || InStateSequence(CurState,FindState("Float")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) || InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("StartCrouch"); } else { if ( (InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Float"))) && (abs(pos.z-floorz) > maxstepheight) ) SetStateLabel("Fall"); else if ( InStateSequence(CurState,FindState("Crouch")) || InStateSequence(CurState,FindState("CrouchMove")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("EndCrouch"); else if ( InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("SwimEnd"); } } else { // Swimming if ( player.crouchdir == -1 ) { // Crouching if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Jump")) || InStateSequence(CurState,FindState("Float")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) || InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("StartCrouch"); } else { if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Jump")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) ) SetStateLabel("Float"); else if ( InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("SwimEnd"); else if ( InStateSequence(CurState,FindState("Crouch")) || InStateSequence(CurState,FindState("CrouchMove")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("EndCrouch"); } } } override void PlayRunning() { if ( !player ) { if ( !InStateSequence(CurState,FindState("SeeRun")) ) SetStateLabel("SeeRun"); return; } if ( player.health <= 0 ) return; if ( !bNoGravity && player.onground && (waterlevel < 3) ) { // Ground if ( player.crouchdir == -1 ) { // Crouching if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) || InStateSequence(CurState,FindState("Float")) || InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("StartCrouch"); else if ( InStateSequence(CurState,FindState("Crouch")) ) { switch( FastCheck() ) { case 2: SetStateLabel("CrouchMoveFast"); break; case 1: SetStateLabel("CrouchMoveRun"); break; default: SetStateLabel("CrouchMove"); break; } } } else { if ( InStateSequence(CurState,FindState("Crouch")) || InStateSequence(CurState,FindState("CrouchMove")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("EndCrouch"); else if ( InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("SwimEnd"); else if ( (FastCheck() == 2) && (InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun"))) ) SetStateLabel("SeeFast"); else if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Float")) || InStateSequence(CurState,FindState("Turn")) ) { if ( FastCheck() == 1 ) SetStateLabel("SeeRun"); else SetStateLabel("See"); A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2); } } } else if ( !bNoGravity && (waterlevel < 1) ) { // Falling PlayIdle(); } else { // Swimming if ( player.crouchdir == -1 ) { // Crouching if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Jump")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) || InStateSequence(CurState,FindState("Float")) || InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("StartCrouch"); else if ( InStateSequence(CurState,FindState("Crouch")) ) { switch( FastCheck() ) { case 2: SetStateLabel("CrouchMoveFast"); break; case 1: SetStateLabel("CrouchMoveRun"); break; default: SetStateLabel("CrouchMove"); break; } } } else if ( bFlyCheat || (player.cheats&CF_NOCLIP2) ) { // Special case, fly cheats don't play a swim animation, only float // (this fixes Demo "swimming" on the library ladder in Spooktober, for example) if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Jump")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) || InStateSequence(CurState,FindState("Swim")) || InStateSequence(CurState,FindState("SwimLoop")) || InStateSequence(CurState,FindState("SwimLoopRun")) || InStateSequence(CurState,FindState("SwimLoopFast")) ) SetStateLabel("Float"); else if ( InStateSequence(CurState,FindState("Crouch")) || InStateSequence(CurState,FindState("CrouchMove")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("EndCrouch"); } else { if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("Turn")) || InStateSequence(CurState,FindState("See")) || InStateSequence(CurState,FindState("SeeRun")) || InStateSequence(CurState,FindState("SeeFast")) || InStateSequence(CurState,FindState("SeeFastLoop")) || InStateSequence(CurState,FindState("SeeFastEnd")) || InStateSequence(CurState,FindState("Jump")) || InStateSequence(CurState,FindState("Fall")) || InStateSequence(CurState,FindState("FallLoop")) || InStateSequence(CurState,FindState("Float")) ) SetStateLabel("Swim"); else if ( InStateSequence(CurState,FindState("Crouch")) || InStateSequence(CurState,FindState("CrouchMoveRun")) || InStateSequence(CurState,FindState("CrouchMoveFast")) ) SetStateLabel("EndCrouch"); } } } override void PlayAttacking() { // Do nothing if it's a SWWM weapon, since those do things themselves if ( player && (player.ReadyWeapon is 'SWWMWeapon') ) return; if ( InStateSequence(CurState,FindState("Dash")) || InStateSequence(CurState,FindState("Boost")) ) return; // don't cancel dash/boost if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchMissile"); else SetStateLabel("Missile"); } override void PlayAttacking2() { PlayAttacking(); } void PlayFire() { if ( InStateSequence(CurState,FindState("Dash")) || InStateSequence(CurState,FindState("Boost")) ) return; // don't cancel dash/boost if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchMissile"); else SetStateLabel("Missile"); } void PlayMelee() { if ( InStateSequence(CurState,FindState("Dash")) || InStateSequence(CurState,FindState("Boost")) ) return; // don't cancel dash/boost if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchMelee"); else SetStateLabel("Melee"); } void PlayFastMelee() { if ( InStateSequence(CurState,FindState("Dash")) || InStateSequence(CurState,FindState("Boost")) ) return; // don't cancel dash/boost if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchFastMelee"); else SetStateLabel("FastMelee"); } void PlayReload() { if ( InStateSequence(CurState,FindState("Dash")) || InStateSequence(CurState,FindState("Boost")) ) return; // don't cancel dash/boost if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchReload"); else SetStateLabel("Reload"); } void PlayCheckGun() { if ( InStateSequence(CurState,FindState("Dash")) || InStateSequence(CurState,FindState("Boost")) ) return; // don't cancel dash/boost if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchCheckGun"); else SetStateLabel("CheckGun"); } void A_DMFade() { if ( player ) return; Spawn("TeleportFog",pos,ALLOW_REPLACE); Destroy(); } void A_DemoPain() { if ( IsActorPlayingSound(CHAN_JETPACK,"demolitionist/jet") ) A_StartSound("demolitionist/jetstop",CHAN_JETPACK); if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player); if ( lastdamage > 90 ) { if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:1200); A_QuakeEx(3,3,3,9,0,8,"",QF_RELATIVE|QF_SCALEDOWN); A_StartSound("demolitionist/hipain",CHAN_VOICE); lastbump *= 1.04; if ( !player || (player.mo != self) ) return; // voodoo dolls have no voice if ( swwm_mutevoice < 4 ) A_StartSound(String.Format("voice/%s/hipain",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP); } else if ( lastdamage > 30 ) { if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:600); A_QuakeEx(2,2,2,6,0,8,"",QF_RELATIVE|QF_SCALEDOWN); A_StartSound("demolitionist/pain",CHAN_VOICE); lastbump *= 1.02; if ( !player || (player.mo != self) ) return; // voodoo dolls have no voice if ( swwm_mutevoice < 4 ) A_StartSound(String.Format("voice/%s/pain",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP); } else if ( lastdamage > 0 ) { if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:200); A_QuakeEx(1,1,1,3,0,8,"",QF_RELATIVE|QF_SCALEDOWN); A_StartSound("demolitionist/lopain",CHAN_VOICE); lastbump *= 1.01; if ( !player || (player.mo != self) || (lastdamage < 5) ) return; // voodoo dolls have no voice if ( swwm_mutevoice < 4 ) A_StartSound(String.Format("voice/%s/lopain",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP); } lastdamage = 0; } void A_DemoScream() { if ( IsActorPlayingSound(CHAN_JETPACK,"demolitionist/jet") ) A_StartSound("demolitionist/jetstop",CHAN_JETPACK); A_StopSound(CHAN_DEMOVOICE); if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player); Sound snd = "demolitionist/death"; if ( special1 < 10 ) snd = "demolitionist/wdeath"; if ( health < -50 ) snd = "demolitionist/xdeath"; A_StartSound(snd,CHAN_VOICE); if ( !player || (player.mo != self) ) return; // voodoo dolls have no voice if ( swwm_mutevoice < 4 ) A_StartSound(String.Format("voice/%s/death",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP); } override bool OnGiveSecret( bool printmsg, bool playsound ) { if ( !player ) return false; int score = 100; // last secret (this is called before counting it up, so have to subtract) let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); if ( !deathmatch && (level.found_secrets == level.total_secrets-1) && (!hnd || !hnd.allsecrets) ) { if ( hnd ) hnd.allsecrets = true; score = 1000; Console.Printf(StringTable.Localize("$SWWM_LASTSECRET"),player.GetUserName(),score); SWWMUtility.AchievementProgressInc('swwm_progress_allsecrets',1,player); } else Console.Printf(StringTable.Localize("$SWWM_FINDSECRET"),player.GetUserName(),score); if ( CheckLocalView() ) SWWMHandler.AddOneliner("findsecret",2,40); SWWMCredits.Give(player,score); SWWMScoreObj.Spawn(score,Vec3Offset(0,0,Height/2)); // somehow ongivesecret can be called BEFORE PostBeginPlay (what the fuck) if ( !mystats ) mystats = SWWMStats.Find(player); mystats.secrets++; return true; } override void AddInventory( Inventory item ) { // hackaround for replaced keys if ( (item is 'Key') && !key_reentrant ) { let rep = (Class)(GetReplacement(item.GetClass())); if ( rep && (item.GetClass() != rep) ) { // also add the new key Actor whom = player?player.mo:PlayerPawn(self); if ( !whom.FindInventory(rep) ) { let nk = Inventory(Spawn(rep)); nk.AttachToOwner(whom); nk.Use(true); } return; } } Super.AddInventory(item); if ( !player ) return; if ( !bInDefaultInventory ) { // add lore if any SWWMLoreLibrary.Add(player,item.GetClassName()); // weapon get oneliner if ( (item is 'Weapon') && !(item is 'SWWMGesture') && !(item is 'SWWMItemGesture') && mystats && !mystats.GotWeapon(Weapon(item).GetClass()) && CheckLocalView() ) SWWMHandler.AddOneliner("getweapon",2); } if ( (item is 'Key') && !key_reentrant && !deathmatch && !bInDefaultInventory ) { // score int score = 100; Console.Printf(StringTable.Localize("$SWWM_FINDKEY"),player.GetUserName(),item.GetTag(),score); SWWMCredits.Give(player,score); SWWMScoreObj.Spawn(100,player.mo.Vec3Offset(0,0,Height/2)); if ( !swwm_sharekeys ) return; // share all keys in mp for ( int i=0; i 0) && (mystats.ownedcollectibles.Find(cls) < mystats.ownedcollectibles.Size()) ) return; mystats.ownedcollectibles.Push(cls); } } override bool UseInventory( Inventory item ) { let itemtype = item.GetClass(); if ( (player.cheats&CF_TOTALLYFROZEN) || isFrozen() ) return false; if ( !Actor.UseInventory(item) ) { if ( player == players[consoleplayer] ) { if ( !(item is 'Weapon') ) A_StartSound("menu/noinvuse",CHAN_ITEMEXTRA); if ( item is 'PuzzleItem' ) SWWMHandler.AddOneliner("puzzfail",2,20); } return false; } // use sounds of big powerups are heard by other players if ( (player == players[consoleplayer]) || item.bBIGPOWERUP ) A_StartSound(item.UseSound,CHAN_ITEMEXTRA); if ( (player == players[consoleplayer]) && (item is 'PuzzleItem') ) SWWMHandler.AddOneliner("puzzsucc",2,10); return true; } void A_Footstep( double yofs, int run = 0, double vol = .3 ) { if ( run == 2 ) { A_StartSound("demolitionist/run",CHAN_FOOTSTEP,CHANF_OVERLAP,vol); let b = Spawn("InvisibleSplasher",level.Vec3Offset(pos,(RotateVector((0,yofs*.25*radius),angle),0))); b.A_CheckTerrain(); vel.xy += (RotateVector(NormalizedMove(),angle)/3600.)*TweakSpeed(); if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:(200*vol)); } else if ( run == 1 ) { A_StartSound("demolitionist/walk",CHAN_FOOTSTEP,CHANF_OVERLAP,vol*.5); let b = Spawn("InvisibleSplasher",level.Vec3Offset(pos,(RotateVector((0,yofs*.25*radius),angle),0))); b.A_CheckTerrain(); vel.xy += (RotateVector(NormalizedMove(),angle)/4800.)*TweakSpeed(); if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:(100*vol)); } else { A_StartSound("demolitionist/walk",CHAN_FOOTSTEP,CHANF_OVERLAP,vol*.2); let b = Spawn("SmolInvisibleSplasher",level.Vec3Offset(pos,(RotateVector((0,yofs*.25*radius),angle),0))); b.A_CheckTerrain(); vel.xy += (RotateVector(NormalizedMove(),angle)/6400.)*TweakSpeed(); if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:(50*vol)); } } override bool Used( Actor user ) { if ( !(user is 'Demolitionist') || !player || (player.mo != self) ) return false; if ( (user.player == players[consoleplayer]) && (health > 0) ) { SWWMHandler.AddOneliner("greet",2); return true; } return false; } int FastCheck() { if ( !player ) return 1; if ( bWalking || (NormalizedMove().length() <= 8000.) ) return 0; // walk if ( player.cmd.buttons&BT_RUN ) return 2; // sprint return 1; // walk } State A_FastJump( StateLabel walk = null, StateLabel run = null, StateLabel sprint = null ) { int fc = FastCheck(); if ( (fc == 2) && sprint ) return ResolveState(sprint); if ( (fc == 1) && run ) return ResolveState(run); if ( (fc == 0) && walk ) return ResolveState(walk); return ResolveState(null); } bool AllowCrouch() { if ( player.cmd.buttons&BT_JUMP ) return false; if ( InStateSequence(CurState,FindState("Dash")) ) return false; // no crouch during dash return true; } // Imagine having to duplicate two functions only to change a couple values in both // I sure love constants override void CrouchMove( int direction ) { double defaultheight = FullHeight; double savedheight = Height; double crouchspeed = direction*CROUCHSPEED*.8; // slow down slightly so it matches the animation double oldheight = player.viewheight; player.crouchdir = direction; player.crouchfactor += crouchspeed; // check whether the move is ok Height = defaultheight; // actually test the full height, or it'll look weird if ( !TryMove(pos.xy,false) ) { Height = savedheight; if ( direction > 0 ) { // doesn't fit player.crouchfactor -= crouchspeed; player.crouchdir = -1; // force crouch return; } } Height = savedheight; player.crouchfactor = clamp(player.crouchfactor,.3,1.); player.viewheight = ViewHeight*player.crouchfactor; player.crouchviewdelta = player.viewheight-ViewHeight; // Check for eyes going above/below fake floor due to crouching motion. CheckFakeFloorTriggers(pos.z+oldheight,true); } override void CheckCrouch( bool totallyfrozen ) { // crouch to swim/float down if ( !totallyfrozen && (player.cmd.buttons&BT_CROUCH) ) { if ( waterlevel >= 2 ) vel.z = -4*Speed; else if ( bNOGRAVITY ) vel.z = -3; } bool wascrouching = !!(player.cmd.buttons&BT_CROUCH); if ( !AllowCrouch() ) player.cmd.buttons &= ~BT_CROUCH; if ( CanCrouch() && (player.health > 0) && level.IsCrouchingAllowed() ) { if ( !totallyfrozen ) { int crouchdir = player.crouching; if ( !crouchdir ) crouchdir = (player.cmd.buttons&BT_CROUCH)?-1:1; else if ( player.cmd.buttons&BT_CROUCH ) player.crouching = 0; if ( (crouchdir == 1) && (player.crouchfactor < 1) && (pos.z+height < ceilingz) ) CrouchMove(1); else if ( (crouchdir == -1) && (player.crouchfactor > .3) ) CrouchMove(-1); } } else player.Uncrouch(); player.crouchoffset = -(ViewHeight)*(1-player.crouchfactor); // we need the crouch button state to be preserved for other functions if ( wascrouching ) player.cmd.buttons |= BT_CROUCH; } override void Travelled() { // reinitialize dashfuel = default.dashfuel; last_boost = 0; last_kick = 0; hasrevived = false; // cancel dash/boost A_StopSound(CHAN_JETPACK); fuelcooldown = 0.; dashcooldown = 0.; dashboost = 0.; // prevent sudden stomping if we were previously falling lastvelz = vel.z; // early cancel gestures if ( player ) { if ( player.ReadyWeapon is 'SWWMItemGesture' ) player.ReadyWeapon = SWWMItemGesture(player.ReadyWeapon).gest; if ( player.ReadyWeapon is 'SWWMGesture' ) { player.PendingWeapon = SWWMGesture(player.ReadyWeapon).formerweapon; player.SetPSprite(PSP_WEAPON,player.ReadyWeapon.ResolveState("Deselect")); } } // re-add ourselves to the "suckable list" (otherwise the Ynykron Singularity won't hurt us) let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); if ( hnd ) hnd.SuckableActors.Push(self); // re-attach shadow if ( swwm_shadows <= 0 ) return; let ti = ThinkerIterator.Create("SWWMShadow"); Actor a; while ( a = Actor(ti.Next()) ) { if ( a.target == self ) return; // shadow already attached } SWWMShadow.Track(self); } override bool PreTeleport( Vector3 destpos, double destangle, int flags ) { // store old pos pretelepos = pos; return true; } override void PostTeleport( Vector3 destpos, double destangle, int flags ) { hasteleported = true; // notify tick that we teleported, so it ignores the travel distance mystats.teledist += level.Vec3Diff(pretelepos,pos).length(); // reset all smooth bob variables if angles/velocity aren't carried over if ( !(flags&TELF_KEEPORIENTATION) ) { oldlagangle = lagangle = oldangle = angle; oldlagpitch = lagpitch = oldpitch = pitch; } if ( !(flags&TELF_KEEPVELOCITY) ) { oldlagvel = lagvel = vel; lastvelz = vel.z; } // notify carried lamp that we just moved let l = SWWMLamp(FindInventory("SWWMLamp")); if ( l && l.thelamp ) CompanionLamp(l.thelamp).justteleport = true; } override void MarkPrecacheSounds() { Super.MarkPrecacheSounds(); MarkSound("demolitionist/walk1"); MarkSound("demolitionist/walk2"); MarkSound("demolitionist/walk3"); MarkSound("demolitionist/walk4"); MarkSound("demolitionist/runstart1"); MarkSound("demolitionist/runstart2"); MarkSound("demolitionist/runstart3"); MarkSound("demolitionist/runstart4"); MarkSound("demolitionist/run1"); MarkSound("demolitionist/run2"); MarkSound("demolitionist/run3"); MarkSound("demolitionist/run4"); MarkSound("demolitionist/runstop1"); MarkSound("demolitionist/runstop2"); MarkSound("demolitionist/runstop3"); MarkSound("demolitionist/runstop4"); MarkSound("demolitionist/jet"); MarkSound("demolitionist/jetstop"); MarkSound("demolitionist/death1"); MarkSound("demolitionist/death2"); MarkSound("demolitionist/death3"); MarkSound("demolitionist/xdeath1"); MarkSound("demolitionist/xdeath2"); MarkSound("demolitionist/xdeath3"); MarkSound("demolitionist/wdeath1"); MarkSound("demolitionist/wdeath2"); MarkSound("demolitionist/wdeath3"); MarkSound("demolitionist/pain1"); MarkSound("demolitionist/pain2"); MarkSound("demolitionist/pain3"); MarkSound("demolitionist/hipain1"); MarkSound("demolitionist/hipain2"); MarkSound("demolitionist/hipain3"); MarkSound("demolitionist/lopain1"); MarkSound("demolitionist/lopain2"); MarkSound("demolitionist/lopain3"); MarkSound("demolitionist/hardland1"); MarkSound("demolitionist/hardland2"); MarkSound("demolitionist/hardland3"); MarkSound("demolitionist/swing1"); MarkSound("demolitionist/swing2"); MarkSound("demolitionist/swing3"); MarkSound("demolitionist/wswing1"); MarkSound("demolitionist/wswing2"); MarkSound("demolitionist/punch1"); MarkSound("demolitionist/punch2"); MarkSound("demolitionist/punch3"); MarkSound("demolitionist/punchf1"); MarkSound("demolitionist/punchf2"); MarkSound("demolitionist/punchf3"); MarkSound("demolitionist/bump1"); MarkSound("demolitionist/bump2"); MarkSound("demolitionist/bump3"); MarkSound("demolitionist/kick1"); MarkSound("demolitionist/kick2"); MarkSound("demolitionist/kick3"); MarkSound("demolitionist/revive"); MarkSound("demolitionist/youdied"); MarkSound("demolitionist/parry"); MarkSound("demolitionist/handsup"); MarkSound("demolitionist/handsdown"); MarkSound("demolitionist/whits1"); MarkSound("demolitionist/whits2"); MarkSound("demolitionist/whits3"); MarkSound("demolitionist/whitm1"); MarkSound("demolitionist/whitm2"); MarkSound("demolitionist/whitm3"); MarkSound("demolitionist/whitl1"); MarkSound("demolitionist/whitl2"); MarkSound("demolitionist/buttslam"); MarkSound("demolitionist/buttslamx"); MarkSound("demolitionist/smooch"); MarkSound("demolitionist/blowkiss"); MarkSound("demolitionist/petting"); MarkSound("demolitionist/knockout"); } States { Spawn: // normal idle #### # 2; XZW1 A 1 { if ( !player || (player.mo != self) ) return ResolveState("VoodooSpawn"); return A_JumpIf(player&&(player.mo==self)&&(abs(player.cmd.yaw|player.cmd.pitch)>128),"Turn"); } Wait; See: // normal walking #### # 2; XZW1 BCD 2 A_FastJump(null,"SeeRun","SeeFast"); XZW1 E 0 A_Footstep(1); XZW1 EFGHIJKL 2 A_FastJump(null,"SeeRun","SeeFast"); XZW1 M 0 A_Footstep(-1); XZW1 MNOPA 2 A_FastJump(null,"SeeRun","SeeFast"); Goto See+1; Turn: #### # 8 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.1); XZW1 C 1 A_JumpIf(!player||!(player.cmd.yaw|player.cmd.pitch),1); Wait; XZW1 C 3 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.1); Goto Spawn+1; SeeRun: #### # 2; XZWI PQR 2 A_FastJump("See",null,"SeeFast"); XZWI S 0 A_Footstep(1,1); XZWI STUVWX 2 A_FastJump("See",null,"SeeFast"); XZWI Y 0 A_Footstep(-1,1); XZWI YZ 2 A_FastJump("See",null,"SeeFast"); XZW1 A 2 A_FastJump("See",null,"SeeFast"); Goto SeeRun+1; SeeFast: // sprinting #### # 2 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.3); XZW1 QRS 2; Goto SeeFastLoop; SeeFastLoop: // keep sprinting XZW1 T 1 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW1 U 1 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW1 V 0 A_Footstep(1,2); XZW1 V 1 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW1 W 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW1 X 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW1 Y 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW1 Z 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 A 1 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 B 1 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 C 0 A_Footstep(1,2); XZW2 C 1 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 D 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 E 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 F 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); XZW2 G 2 A_JumpIf(FastCheck()!=2,"SeeFastEnd"); Goto SeeFastLoop; SeeFastEnd: // brake #### # 2 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.3); XZW2 HIJKL 2; Goto Spawn+1; Pain: // ouchy XZW1 A 1 { if ( !player || (player.mo != self) ) return ResolveState("VoodooPain"); return A_JumpIf(player&&(player.mo==self)&&(player.crouchdir==-1),"CrouchPain"); } XZW2 M 1 A_DemoPain(); XZW2 NOPQ 1; Goto Spawn+1; Death: XDeath: // ded XZW1 A 0 { if ( !player || (player.mo != self) ) return ResolveState("VoodooDeath"); return A_JumpIf(player&&(player.mo==self)&&(player.crouchdir==-1),"CrouchDeath"); } XZW1 A 2 { A_DemoScream(); bSOLID = true; // we need this to avoid clipping into things } XZW2 RSTUVWXYZ 2; XZW3 ABCDEFG 2; XZW3 H 1 A_DMFade(); Wait; Boost: // start boost #### # 2; XZW3 IJKLMNO 2 { if ( player.onground||bNoGravity||(waterlevel>=3) ) return ResolveState("BoostEnd"); A_BoostUp(true); return ResolveState(null); } // keep boost XZW3 P 1 { if ( player.onground||bNoGravity||(waterlevel>=3) ) return ResolveState("BoostEnd"); A_BoostUp(false); return ResolveState(null); } XZW3 P 1 A_JumpIf((vel.z<-10)&&(pos.z>(floorz+80)),"Fall"); Goto Boost+8; BoostEnd: // stop boost #### # 2; XZW3 PQRSTUVW 2; Goto Spawn+1; Jump: #### # 2; XZWJ ABCDEF 2 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd"); Goto FallLoop; Fall: // start fall #### # 4; XZW3 XYZ 2 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd"); XZW4 AB 2 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd"); Goto FallLoop; FallLoop: // falling XZW4 CDEFGH 3 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd"); Goto FallLoop; FallEnd: // landing XZW4 CIJKLMN 2; Goto Spawn+1; Dash: #### # 2; XZW4 O 2 A_Dash(); XZW4 PQRS 2 A_Dash(); Goto Dash+2; DashEnd: XZW4 TUVWX 2; Goto Spawn+1; Wave: #### # 3; XZW4 YZ 3; XZW5 ABCDEFGHIJKLM 3; Goto Spawn+1; Approve: #### # 3; XZW5 NOPQRSTUVWXYZ 3; XZW6 ABCD 3; Goto Spawn+1; Victory: #### # 3; XZW6 EFGHIJKLMNOPQRSTUVW 3; Goto Spawn+1; BlowKiss: #### # 3; XZWD EFGHIJKLMNOPQRSTUVW 3; Goto Spawn+1; Headpat: #### # 3; XZWH ST 3; XZWH UVW 2; XZWH XYZ 1; XZWI A 1; XZWI BCDE 2; XZWI FG 1; HeadpatLoop: XZWI H 1; XZWH XYZ 1; XZWI A 1; XZWI BCDE 2; XZWI FGH 1; XZWI IJK 2; XZWI LMNO 3; Goto Spawn+1; Ragepat: #### # 3; XZWH ST 2; XZWH UVW 1; XZWH XZ 1; XZWI BCDE 1; XZWI FH 1; XZWH XZ 1; XZWI BCDE 1; XZWI FH 1; XZWH XZ 1; XZWI BCDE 1; XZWI F 1; RagepatLoop: XZWI H 1; XZWH XZ 1; XZWI BCDE 1; XZWI FH 1; XZWI IJK 1; XZWI LMNO 2; Goto Spawn+1; Missile: // attacking #### # 2; XZW6 XYZ 2; XZW7 ABC 2; Goto Spawn+1; Melee: // ponch #### # 2; XZW8 TUVWXYZ 2; XZW9 ABCDEF 2; Goto Spawn+1; FastMelee: // ponch (fast) #### # 2; XZW8 TUVWXYZ 1; XZW9 ABCDE 1; XZW9 F 2; Goto Spawn+1; Reload: // reload #### # 2; XZW9 GHIJKLMNOPQRSTUVWXYZ 2; XZWA ABCDE 2; Goto Spawn+1; CheckGun: // speen #### # 2; XZWA FGHIJKLMNOPQRSTUVWXY 2; Goto Spawn+1; StartCrouch: // go crouching #### # 2 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.45); XZW7 DEFGH 1; XZW7 IJKL 2; Goto Crouch+1; Crouch: #### # 4; XZW7 M -1; Stop; CrouchMove: #### # 4; XZW7 MN 4 A_FastJump(null,"CrouchMoveRun","CrouchMoveFast"); XZW7 O 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.1); XZW7 OPQRS 4 A_FastJump(null,"CrouchMoveRun","CrouchMoveFast"); XZW7 T 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.1); XZW7 TUV 4 A_FastJump(null,"CrouchMoveRun","CrouchMoveFast"); Goto CrouchMove+1; CrouchMoveRun: #### # 3; XZW7 MN 3 A_FastJump("CrouchMove",null,"CrouchMoveFast"); XZW7 O 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2); XZW7 OPQRS 3 A_FastJump("CrouchMove",null,"CrouchMoveFast"); XZW7 T 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2); XZW7 TUV 3 A_FastJump("CrouchMove",null,"CrouchMoveFast"); Goto CrouchMoveRun+1; CrouchMoveFast: #### # 2; XZW7 MN 2 A_FastJump("CrouchMove","CrouchMoveRun",null); XZW7 O 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.4); XZW7 OPQRS 2 A_FastJump("CrouchMove","CrouchMoveRun",null); XZW7 T 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.4); XZW7 TUV 2 A_FastJump("CrouchMove","CrouchMoveRun",null); Goto CrouchMoveFast+2; CrouchWave: #### # 3; XZWF ABCDEFGHIJKLMNO 3; Goto Crouch+1; CrouchApprove: #### # 3; XZWF PQRSTUVWXYZ 3; XZWG ABCDEF 3; Goto Crouch+1; CrouchVictory: #### # 3; XZWG GHIJKLMNOPQRSTUVWXY 3; Goto Crouch+1; CrouchBlowKiss: #### # 3; XZWG Z 3; XZWH ABCDEFGHIJKLMNOPQR 3; Goto Crouch+1; CrouchMissile: XZW7 M 2; XZW7 WXYZ 2; XZW8 AB 2; Goto Crouch+1; CrouchMelee: XZW7 M 2; XZWA Z 2; XZWB ABCDEFGHIJKL 2; Goto Crouch+1; CrouchFastMelee: XZW7 M 2; XZWA Z 1; XZWB ABCDEFGHIJK 1; XZWB L 2; Goto Crouch+1; CrouchReload: XZW7 M 2; XZWB MNOPQRSTUVWXYZ 2; XZWC ABCDEFGHIJ 2; Goto Crouch+1; CrouchCheckGun: XZW7 M 2; XZWC LMNOPQRSTUVWXYZ 2; XZWD ABCD 2; Goto Crouch+1; CrouchPain: XZW7 M 1; XZW8 C 1 A_DemoPain(); XZW8 DEF 1; Goto Crouch+1; CrouchDeath: XZW7 M 2 { A_DemoScream(); A_NoBlocking(); } XZW8 GHIJK 2; XZW8 L 1 A_DMFade(); Wait; EndCrouch: #### # 2 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.45); XZW8 MNOPQRS 2; Goto Spawn+1; Float: #### # 3; XZWD XYZ 3; XZWE ABCDEFGHI 3; Goto Float+1; Swim: #### # 2; XZWE JKL 2; Goto SwimLoop; SwimLoop: #### # 5; XZWE MNO 5 A_FastJump(null,"SwimLoopRun","SwimLoopFast"); XZWE P 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.1); XZWE PQRSTU 5 A_FastJump(null,"SwimLoopRun","SwimLoopFast"); XZWE V 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.1); XZWE VWX 5 A_FastJump(null,"SwimLoopRun","SwimLoopFast"); Goto SwimLoop+1; SwimLoopRun: #### # 3; XZWE MNO 3 A_FastJump("SwimLoop",null,"SwimLoopFast"); XZWE P 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2); XZWE PQRSTU 3 A_FastJump("SwimLoop",null,"SwimLoopFast"); XZWE V 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2); XZWE VWX 3 A_FastJump("SwimLoop",null,"SwimLoopFast"); Goto SwimLoopRun+1; SwimLoopFast: #### # 2; XZWE MNO 2 A_FastJump("SwimLoop","SwimLoopRun",null); XZWE P 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.4); XZWE PQRSTU 2 A_FastJump("SwimLoop","SwimLoopRun",null); XZWE V 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.4); XZWE VWX 2 A_FastJump("SwimLoop","SwimLoopRun",null); Goto SwimLoopFast+1; SwimEnd: #### # 2; XZWE MYZ 2; Goto Float+1; VoodooSpawn: XZWZ A -1; Loop; VoodooPain: XZWZ A 1; XZWZ B 1 A_DemoPain(); XZWZ CDEF 1; Goto VoodooSpawn; VoodooDeath: XZWZ A 2 { A_DemoScream(); A_NoBlocking(); } XZWZ GHIJKLMNO 2; XZWZ PQR 2; XZWZ S -1; Stop; } } // for the doom 2 cast Class CastDemolitionist : Actor { Default { DeathSound "demolitionist/death"; } States { Spawn: See: ZYX1 ABCDEFGHIJKLMNOP 2; Loop; Missile: ZYX1 A 2; ZYX2 ABCDEF 2; Goto See; Death: ZYX1 A 2; ZYX3 ABCDEFGHIJKLMNOP 2; ZYX3 Q -1; Stop; } }