PrecacheScriptSound("Marauder.VoiceAttack") PrecacheScriptSound("Marauder.SmallAttack") PrecacheScriptSound("Marauder.BigAttack") PrecacheScriptSound("PlayerDash") PrecacheScriptSound("MeteorExplode") PrecacheEntityFromTable({ classname = "info_particle_system", effect_name = "marauder_axe_charged_glow"}) class AttackSpec { key = null call = null min2 = 0.0 max2 = 0.0 weight = 1.0 cd = 0.0 readyAt = 0.0 constructor(k, fnRef, minHU, maxHU, w, cooldown) { key = k call = fnRef min2 = minHU.tofloat() * minHU.tofloat() max2 = maxHU.tofloat() * maxHU.tofloat() weight = w.tofloat() cd = cooldown.tofloat() readyAt = 0.0 } } ::Marauder <- { Debug = { function Log(t = null) { for (local player; player = Entities.FindByClassname(player, "player");) { if (t) { foreach (k,v in t) { printl(k + ": " + v) } } local Chat = @(m) (printl(m), ClientPrint(player, 2, m)) ClientPrint(player, 3, format("\x07FF3232DEBUG [%s].\nCheck console for details", e)) Chat(format("\n====== TIMESTAMP: %g ======\nDEBUG [%s]", Time(), e)) Chat("CALLSTACK") local s, l = 2 while (s = getstackinfos(l++)) Chat(format("*FUNCTION [%s()] %s line [%d]", s.func, s.src, s.line)) Chat("LOCALS") if (s = getstackinfos(2)) { foreach (n, v in s.locals) { local t = type(v) t == "null" ? Chat(format("[%s] NULL" , n)) : t == "integer" ? Chat(format("[%s] %d" , n, v)) : t == "float" ? Chat(format("[%s] %.14g" , n, v)) : t == "string" ? Chat(format("[%s] \"%s\"", n, v)) : Chat(format("[%s] %s %s" , n, t, v.tostring())) } } return } } } Combat = { BIG_SLAM_AXE_DMG = 150 BIG_SLAM_AXE_RANGE = 512 BIG_SLAM_MAX_FOV = 180 MELEE_PUSH_DMG = 50 MELEE_PUSH_RANGE = 210 MELEE_PUSH_MAX_FOV = 150 MELEE_PUSH_FORCE = 750 MELEE_PUSH_FORCE_VERTICAL = 500 AXE_SHORT_DMG = 150 AXE_SHORT_RANGE = 175 AXE_SHORT_MAX_FOV = 120 AXE_SHORT_SHIELD_DMG = 150 AXE_SHORT_SHIELD_RANGE = 192 AXE_SHORT_SHIELD_MAX_FOV = 110 AXE_LONG_DMG = 150 AXE_LONG_RANGE = 312 AXE_LONG_MAX_FOV = 140 SPINSLICE_DMG = 150 SPINSLICE_RANGE = 312 SPINSLICE_MAX_FOV = 180 BIGCHOP_DMG = 300 BIGCHOP_RANGE = 192 BIGCHOP_MAX_FOV = 120 GROUND_SLAM_TAUNT_DMG = 25 GROUND_SLAM_RADIUS = 500 GROUND_SLAM_FORCE = 1000 AXE_THROW_BLAST_DMG = 300 AXE_THROW_BLAST_RADIUS = 112 } AttackCatalog = [ { key = "SwiftChop_Close", fn = "SwiftChop_Close", min = 0.0, max = 450.0, weight = 3, cd = 2.5 }, { key = "GroundSlam", fn = "GroundSlam", min = 0.0, max = 500.0, weight = 1, cd = 15.0 }, { key = "Stab", fn = "Stab", min = 120, max = 500, weight = 2, cd = 5 }, { key = "SwiftChop_Long", fn = "SwiftChop_Long", min = 100.0, max = 500.0, weight = 3, cd = 5 }, { key = "SpinSlice", fn = "SpinSlice", min = 400.0, max = 600.0, weight = 0.75, cd = 12.0 }, { key = "ComboUppercut", fn = "ComboUppercut", min = 60.0, max = 460.0, weight = 1.5, cd = 10.0 }, { key = "BigChop", fn = "BigChop", min = 200.0, max = 550.0, weight = 0.5, cd = 10.0 }, { key = "BigSlam_AOE", fn = "BigSlam_AOE", min = 0, max = 250.0, weight = 0.7, cd = 20.0 }, { key = "Hammer_AOE", fn = "Hammer_AOE", min = 120.0, max = 400.0, weight = 1.50, cd = 45.0 }, { key = "BigSlam_Jump", fn = "BigSlam_Jump", min = 600.0, max = 1000.0, weight = 0.5, cd = 25.0 }, { key = "ShotgunQuickdraw", fn = "ShotgunQuickdraw", min = 220.0, max = 520.0, weight = 2, cd = 3.5 }, // --- Shield variants --- { key = "AxeChop_Shield", fn = "AxeChop_Shield", min = 110.0, max = 300.0, weight = 5, cd = 2.5 }, { key = "AxeThrow_Shield", fn = "AxeThrow_Shield", min = 300.0, max = 1200.0, weight = 4, cd = 2.5 }, { key = "Summon_Atk", fn = "Summon_Atk", min = 0.0, max = 600.0, weight = 0.4, cd = 60.0 } ] MarauderAttackSequences = { ALL = [ {sName = "bigchop", flDuration = 1.8, flTimeToEffect = 0.633}, {sName = "bigslam_01", flDuration = 2.33, flTimeToEffect = 0.9}, {sName = "jump_bigslam_01", flDuration = 1.933, flTimeToEffect = 0.8}, {sName = "spinslice", flDuration = 2.1, flTimeToEffect = 0.733}, {sName = "swiftchop_01", flDuration = 1.833, flTimeToEffect = 0.6}, {sName = "swiftchop_02", flDuration = 1.866, flTimeToEffect = 0.633}, {sName = "swiftchop_03", flDuration = 2.166, flTimeToEffect = 0.866}, {sName = "swiftchop_04", flDuration = 2.6, flTimeToEffect = 1}, {sName = "swiftchop_close_01", flDuration = 1.366, flTimeToEffect = 0.533}, {sName = "swiftchop_close_02", flDuration = 1.366, flTimeToEffect = 0.5}, {sName = "swiftchop_close_03", flDuration = 1.266, flTimeToEffect = 0.6}, {sName = "swiftchop_close_04", flDuration = 1.366, flTimeToEffect = 0.533}, {sName = "swiftchop_close_05", flDuration = 1.366, flTimeToEffect = 0.433}, {sName = "swiftchop_close_06", flDuration = 1.366, flTimeToEffect = 0.4}, {sName = "taunt_01", flDuration = 1.5333, flTimeToEffect = 1}, {sName = "quickdraw", flDuration = 1.533, flTimeToEffect = 0.544}, {sName = "hammer", flDuration = 2.366, flTimeToEffect = 1.1}, {sName = "stab_01", flDuration = 2.5, flTimeToEffect = 1.166} //TODO ], SHIELD_UP = [ {sName = "block_shoulder_axe_throw_01", flDuration = 1.03, flTimeToEffect = 0.4}, {sName = "block_shoulder_axe_throw_axe_disappears", flDuration = 1.03, flTimeToEffect = 0.4}, {sName = "block_shoulder_wolf_summon", flDuration = 1.7, flTimeToEffect = 0.7}, ], SPECIAL = [ {sName = "combo_uppercut_gunblast", flDuration = 2.5, flTimeToEffect = [0.333, 0.866]}, {sName = "combo_stab_stab_bigslam_01", flDuration = 4.1, flTimeToEffect = [0.8, 1.333, 2.9]} //too much trouble to get this one to work ] } SequenceLookup = {} function BuildLookup() { foreach (category, attacks in Marauder.MarauderAttackSequences) { foreach (seq in attacks) { Marauder.SequenceLookup[seq.sName] <- seq } } } // AI table lookups function BuildAttackState(hPlayer) { hPlayer.ValidateScriptScope() local s = hPlayer.GetScriptScope() local arr = [] foreach (a in Marauder.AttackCatalog) { local fnRef = Marauder[a.fn] arr.append(AttackSpec(a.key, fnRef, a.min, a.max, a.weight, a.cd)) } s.Attacks <- arr s.LastDecision <- 0.0 s.DecisionInterval <- 0.15 s.ScoreEpsilon <- 0.001 } function ShieldState(hPlayer, enabled) { if (!hPlayer || !hPlayer.IsValid()) return; hPlayer.ValidateScriptScope() local s = hPlayer.GetScriptScope() if (enabled && !s.SHIELD_UP) { s.SHIELD_UP <- true; Marauder.WeaponSwitchCustom(hPlayer, 1) hPlayer.AddCustomAttribute("major move speed bonus", 0.5, -1) hPlayer.ExtinguishPlayerBurning() return; } if (!enabled && s.SHIELD_UP) { s.SHIELD_UP <- false; Marauder.WeaponSwitchCustom(hPlayer, 0) hPlayer.RemoveCustomAttribute("major move speed bonus") return; } } function IsAttackAllowed(hPlayer, spec) { local s = hPlayer.GetScriptScope() if (spec.key == "AxeThrow_Shield") return true local isShield = s.SHIELD_ONLY.rawin(spec.key) return s.SHIELD_UP ? isShield : !isShield } function SelectAttack(hPlayer, d2, visible) { local s = hPlayer.GetScriptScope() if (!s.USE_SELECTOR || s.IS_VURNEABLE) return null local now = Time() if (now < s.NextAttackAllowedAt) return null local cand = [] foreach (a in s.Attacks) { if (now < a.readyAt) continue if (d2 < a.min2 || d2 > a.max2) continue if (!IsAttackAllowed(hPlayer, a)) continue if (a.weight <= 0.0) continue cand.append(a) } if (cand.len() == 0) return null local total = 0.0 foreach (a in cand) total += a.weight.tofloat() local r = RandomFloat(0.0, total) local acc = 0.0 foreach (a in cand) { acc += a.weight.tofloat() if (r <= acc) return a } return cand.top() } function ExecuteAttack(hPlayer, spec) { local now = Time() spec.call(hPlayer) local s = hPlayer.GetScriptScope() local dur = 0.0 if ("tCurrentSeq" in s && s.tCurrentSeq && "flDuration" in s.tCurrentSeq) dur = s.tCurrentSeq.flDuration.tofloat() spec.readyAt = now + spec.cd + dur s.LastAttackKey = spec.key s.NextAttackAllowedAt = now + s.GlobalAttackGap } function WeaponSwitchCustom(hPlayer, slot) { hPlayer.RemoveCustomAttribute("disable weapon switch") hPlayer.Weapon_Switch(GetPropEntityArray(hPlayer, "m_hMyWeapons", slot)) hPlayer.AddCustomAttribute("disable weapon switch", 1, -1) } // base functions function PushAttack(hPlayer, flPush, iRange, iMaxDegreeFOV, flUp) { local flCosFOV = cos((iMaxDegreeFOV / 2.0) * (PI / 180.0)) local vLookDir = hPlayer.IsPlayer() ? hPlayer.EyeAngles().Forward() : hPlayer.GetAbsAngles().Forward() local vPlayerCenter = hPlayer.GetCenter() for (local hVictim = null; hVictim = Entities.FindByClassnameWithin(hVictim, "player", vPlayerCenter, iRange);) { if (!hVictim || !hVictim.IsValid()) continue if (hVictim == hPlayer) continue if (hVictim.GetTeam() != Constants.ETFTeam.TF_TEAM_BLUE) continue local vToEnemy = hVictim.GetCenter() - vPlayerCenter vToEnemy.Norm() if (vLookDir.Dot(vToEnemy) < flCosFOV) continue local vImpulse = vToEnemy * flPush vImpulse.z += flUp hVictim.ApplyAbsVelocityImpulse(vImpulse) } } function ShotgunAttack(hPlayer) { local hWeapon = CreateByClassname("tf_point_weapon_mimic") hWeapon.KeyValueFromString("$weaponname", "MarauderShotgunWep") hWeapon.DispatchSpawn() hWeapon.SetOwner(hPlayer) hWeapon.SetTeam(hPlayer.GetTeam()) local iAttachment = hPlayer.LookupAttachment("shotgun_hand2") local q = hPlayer.EyeAngles() local o = hPlayer.GetAttachmentOrigin(iAttachment) local v = Vector(0,0,15) + o - q.Forward() * 46.0 DispatchParticleEffect("muzzle_scattergun_flash", o + q.Forward() * 8, q.Forward()) hWeapon.SetAbsOrigin(v) hWeapon.SetAbsAngles(q) hWeapon.AcceptInput("FireOnce", null, null, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/weapons/shotgun_fire_02.wav") EntFireByHandle(hWeapon, "Kill", null, -1, null, null) } function AxeBaseAttack(hPlayer, iDamage, iRange, iMaxDegreeFOV, iDamageType) { local start = hPlayer.EyePosition(); local dir = hPlayer.IsPlayer() ? hPlayer.EyeAngles().Forward() : hPlayer.GetAbsAngles().Forward(); dir.Norm(); local cosThresh = cos((iMaxDegreeFOV * 0.5) * (PI / 180.0)); local best = null; local bestDot = -1.0; local bestDist2 = 1e30; local ent = null; while ((ent = Entities.FindByClassnameWithin(ent, "player", start, iRange)) != null) { if (ent == hPlayer) continue; if (ent.GetTeam() != Constants.ETFTeam.TF_TEAM_BLUE) continue; local toHit = ent.GetCenter() - start; local dist2 = toHit.LengthSqr(); toHit.Norm(); local d = dir.Dot(toHit); if (d < cosThresh) continue; local tr = { start = start, end = ent.GetCenter(), ignore = hPlayer }; TraceLineEx(tr); if (!("enthit" in tr) || tr.enthit != ent) continue; if (d > bestDot || (d == bestDot && dist2 < bestDist2)) { best = ent; bestDot = d; bestDist2 = dist2; } } if (!best) return; local wep = hPlayer.GetActiveWeapon(); best.TakeDamageEx(hPlayer, hPlayer, wep, Vector(0, 0, 0), best.GetCenter(), iDamage, iDamageType); while ((ent = Entities.FindByClassnameWithin(ent, "obj_*", start, iRange)) != null) { if (ent == hPlayer) continue; if (ent.GetTeam() != Constants.ETFTeam.TF_TEAM_BLUE) continue; local toHit = ent.GetCenter() - start; local dist2 = toHit.LengthSqr(); toHit.Norm(); local d = dir.Dot(toHit); if (d < cosThresh) continue; local tr = { start = start, end = ent.GetCenter(), ignore = hPlayer }; TraceLineEx(tr); if (!("enthit" in tr) || tr.enthit != ent) continue; if (d > bestDot || (d == bestDot && dist2 < bestDist2)) { best = ent; bestDot = d; bestDist2 = dist2; } } if (!best) return; local wep = hPlayer.GetActiveWeapon(); best.TakeDamageEx(hPlayer, hPlayer, wep, Vector(0, 0, 0), best.GetCenter(), iDamage * 5, iDamageType); } function OppositeTeam(team) { switch (team) { case Constants.ETFTeam.TF_TEAM_RED: return Constants.ETFTeam.TF_TEAM_BLUE; case Constants.ETFTeam.TF_TEAM_BLUE: return Constants.ETFTeam.TF_TEAM_RED; case Constants.ETFTeam.TF_TEAM_PVE_DEFENDERS: return Constants.ETFTeam.TF_TEAM_PVE_INVADERS; case Constants.ETFTeam.TF_TEAM_PVE_INVADERS: return Constants.ETFTeam.TF_TEAM_PVE_DEFENDERS; } return -1 } function IsValidEnemy(hBot, p, opp) { if (!p) return false if (!p.IsAlive()) return false if (p.GetTeam() != opp) return false return true } function DotTo(hBot, p) { local fwd = hBot.EyeAngles().Forward() local d = p.GetCenter() - hBot.EyePosition() d.Norm() return fwd.Dot(d) } function HasLOS(hBot, p) { local tr = { start = hBot.EyePosition(), end = p.EyePosition(), mask = 16513, ignore = hBot } TraceLineEx(tr) return !tr.hit } function GetThreat(hBot) { local s = hBot.GetScriptScope() local now = Time() local opp = Marauder.OppositeTeam(hBot.GetTeam()) local origin = hBot.GetCenter() local retained = ("THREAT_TARGET" in s) ? s.THREAT_TARGET : null local retainedValid = retained && retained.IsValid() && retained.IsAlive() && retained.GetTeam() == opp local retainedPos = retainedValid ? retained.GetCenter() : null local retainedD2 = retainedValid ? (origin - retainedPos).LengthSqr() : 0.0 local retainedDot = retainedValid ? Marauder.DotTo(hBot, retained) : -1.0 local retainedLOS = retainedValid ? Marauder.HasLOS(hBot, retained) : false local retainedInFOV = retainedValid ? (retainedDot >= s.THREAT_FOV_COS) : false local retainActive = retainedValid && (retainedLOS || (now <= s.THREAT_RETAIN_UNTIL) || ((now - s.THREAT_LAST_SEEN_TIME) <= s.THREAT_LOS_GRACE)) local bestVis = null local bestVisD2 = 1e18 local bestVisDot = -1.0 local bestWide = null local bestWideD2 = 1e18 local closest = null local closestD2 = 1e18 for (local i = 1; i <= MAX_CLIENTS; i++) { local p = PlayerInstanceFromIndex(i) if (!Marauder.IsValidEnemy(hBot, p, opp)) continue local pC = p.GetCenter() local d2 = (origin - pC).LengthSqr() if (d2 > s.THREAT_RADIUS_SQR) continue if (d2 < closestD2) { closest = p; closestD2 = d2 } local dot = Marauder.DotTo(hBot, p) if (dot >= s.THREAT_FOV_COS && Marauder.HasLOS(hBot, p)) { if (!bestVis || dot > bestVisDot || (dot == bestVisDot && d2 < bestVisD2)) { bestVis = p; bestVisD2 = d2; bestVisDot = dot } } else if (dot >= s.THREAT_WIDE_FOV_COS && Marauder.HasLOS(hBot, p)) { if (!bestWide || d2 < bestWideD2) { bestWide = p; bestWideD2 = d2 } } } local switchD2Factor = ("THREAT_SWITCH_D2_FACTOR" in s) ? s.THREAT_SWITCH_D2_FACTOR : 0.64 local switchDotDelta = ("THREAT_SWITCH_DOT_DELTA" in s) ? s.THREAT_SWITCH_DOT_DELTA : 0.1 if (bestVis) { local takeVis = !retainActive || (retainedValid && (!retainedLOS || !retainedInFOV)) || (retainedValid && (bestVisD2 < retainedD2 * switchD2Factor || bestVisDot > retainedDot + switchDotDelta)) if (takeVis) { s.THREAT_TARGET = bestVis s.THREAT_VISIBLE = true s.THREAT_LAST_SEEN_POS = bestVis.GetCenter() s.THREAT_LAST_SEEN_TIME = now s.THREAT_RETAIN_UNTIL = now + s.THREAT_RETAIN_TIME return [bestVis, bestVisD2, true] } } if (bestWide) { local takeWide = !retainActive || (retainedValid && (!retainedLOS || bestWideD2 < retainedD2 * switchD2Factor)) if (takeWide) { s.THREAT_TARGET = bestWide s.THREAT_VISIBLE = true s.THREAT_LAST_SEEN_POS = bestWide.GetCenter() s.THREAT_LAST_SEEN_TIME = now s.THREAT_RETAIN_UNTIL = now + s.THREAT_RETAIN_TIME return [bestWide, bestWideD2, true] } } if (retainActive) { s.THREAT_TARGET = retained s.THREAT_VISIBLE = retainedLOS s.THREAT_LAST_SEEN_POS = retainedPos ? retainedPos : s.THREAT_LAST_SEEN_POS if (retainedLOS) { s.THREAT_LAST_SEEN_TIME = now s.THREAT_RETAIN_UNTIL = now + s.THREAT_RETAIN_TIME return [retained, retainedD2, true] } else { local d2r = (origin - s.THREAT_LAST_SEEN_POS).LengthSqr() return [retained, d2r, false] } } if (closest) { local dotOverall = Marauder.DotTo(hBot, closest) if (dotOverall >= s.THREAT_WIDE_FOV_COS && Marauder.HasLOS(hBot, closest)) { s.THREAT_TARGET = closest s.THREAT_VISIBLE = true s.THREAT_LAST_SEEN_POS = closest.GetCenter() s.THREAT_LAST_SEEN_TIME = now s.THREAT_RETAIN_UNTIL = now + s.THREAT_RETAIN_TIME return [closest, closestD2, true] } } s.THREAT_TARGET = null s.THREAT_VISIBLE = false return [null, null, false] } function GetMarauderSequenceByName(name) {return Marauder.SequenceLookup.rawin(name) ? Marauder.SequenceLookup[name] : null} function OffsetNextAttack(hPlayer, flOffset) { for (local i = 1; i < 3; i++) { SetPropFloat(GetPropEntityArray(hPlayer, "m_hMyWeapons", i), "m_flNextPrimaryAttack", Time() + flOffset) } } function SetAttackFX(hPlayer, flDuration) { DispatchParticleAttached(hPlayer, null, "axeM") DispatchParticleAttached(hPlayer, "marauder_axe_charged_large", "axeM") EntFireByHandle(hPlayer, "runscriptcode", @" DispatchParticleAttached(self, null, `axeM`) DispatchParticleAttached(self, `marauder_axe_charged_glow`, `axeM`) ", flDuration, null, null) } function LeapTo(hEnt, vDest, duration = null, arcScale = 1.0, apexHU = null) { if (!hEnt || !hEnt.IsValid()) return hEnt.ValidateScriptScope() local s = hEnt.GetScriptScope() local p0 = hEnt.GetOrigin() local down = { start = vDest, end = vDest - Vector(0,0,1000), mask = (MASK_VISIBLE_AND_NPCS & ~CONTENTS_MONSTER), ignore = hEnt } TraceLineEx(down) local p3 = down.hit ? down.pos : vDest local mins = hEnt.GetBoundingMins() local maxs = hEnt.GetBoundingMaxs() local hull = { start = p3, end = p3, hullmin = mins, hullmax = maxs, mask = MASK_PLAYERSOLID, ignore = hEnt } TraceHull(hull) if ("startsolid" in hull) { local dirs = [Vector(1,0,0), Vector(-1,0,0), Vector(0,1,0), Vector(0,-1,0), Vector(0,0,1), Vector(0,0,-1)] for (local i = 16; i <= 96; i += 16) { foreach (d in dirs) { hull.start = p3 + d * i hull.end = hull.start delete hull.startsolid TraceHull(hull) if (!("startsolid" in hull)) { p3 = hull.end; i = 999; break } } } } local apexMain = ((apexHU != null ? apexHU : 150.0) * arcScale).tofloat() local apexMid = ((apexHU != null ? apexHU * 0.5 : 75.0) * arcScale).tofloat() local p1 = p0 + (p3 - p0) * 0.5; p1.z = (p0.z > p3.z ? p0.z : p3.z) + apexMain local p2 = p0 + (p3 - p0) * 0.75; p2.z = ((p0.z + p3.z) * 0.5) + apexMid local steps = 32 local prev = p0 local arcLen = 0.0 for (local i = 1; i <= steps; i++) { local t = i.tofloat() / steps.tofloat() local inv = 1.0 - t local A = p0 * (inv*inv*inv) local B = p1 * (3.0*inv*inv*t) local C = p2 * (3.0*inv*t*t) local D = p3 * (t*t*t) local pos = A + B + C + D arcLen += (pos - prev).Length() prev = pos } local spd = 0.0 if (duration != null && duration > 0.0) spd = arcLen / duration else { local ms = 0.0 try { ms = NetProps.GetPropFloat(hEnt, "m_flMaxspeed") } catch(e) {} local cur = hEnt.GetAbsVelocity().Length() if (ms > 0.0) spd = ms else if (cur > 0.0) spd = cur else spd = arcLen } local thinkName = "MR_LeapThink" if ("ThinkTable" in s && (thinkName in s.ThinkTable)) s.RemoveThink(thinkName) s.MR_LEAP_T <- 0.0 s.MR_LEAP_SPD <- spd s.MR_LEAP_P0 <- p0 s.MR_LEAP_P1 <- p1 s.MR_LEAP_P2 <- p2 s.MR_LEAP_P3 <- p3 s.AddThink(thinkName, function() { if (!self.IsAlive()) { RemoveThink(thinkName); return -1 } NetProps.SetPropInt(self, "m_fFlags", NetProps.GetPropInt(self, "m_fFlags") | FL_ONGROUND) NetProps.SetPropFloat(self, "m_flFallVelocity", 0.0) if (MR_LEAP_T >= 1.0) { self.SetAbsOrigin(MR_LEAP_P3) self.SetAbsVelocity(Vector(0,0,0)) NetProps.SetPropFloat(self, "m_flFallVelocity", 0.0) NetProps.SetPropInt(self, "m_fFlags", NetProps.GetPropInt(self, "m_fFlags") | FL_ONGROUND) RemoveThink(thinkName) return -1 } local t = MR_LEAP_T local inv = 1.0 - t local d1 = (MR_LEAP_P1 - MR_LEAP_P0) * (3.0 * inv * inv) local d2 = (MR_LEAP_P2 - MR_LEAP_P1) * (6.0 * inv * t) local d3 = (MR_LEAP_P3 - MR_LEAP_P2) * (3.0 * t * t) local deriv = d1 + d2 + d3 local mag = deriv.Length() if (mag < 1e-6) { self.SetAbsOrigin(MR_LEAP_P3) self.SetAbsVelocity(Vector(0,0,0)) NetProps.SetPropFloat(self, "m_flFallVelocity", 0.0) NetProps.SetPropInt(self, "m_fFlags", NetProps.GetPropInt(self, "m_fFlags") | FL_ONGROUND) RemoveThink(thinkName) return -1 } local v = deriv * (MR_LEAP_SPD / mag) self.SetAbsVelocity(v) MR_LEAP_T = t + (MR_LEAP_SPD * FrameTime()) / mag if (MR_LEAP_T > 1.0) MR_LEAP_T = 1.0 return -1 }) } function ThrowAxe(hPlayer) { if (!hPlayer || !hPlayer.IsValid() || !hPlayer.IsAlive()) return null local sModel = "models/doom_eternal/demons/axe_projectile.mdl" local flSpeed = 1200.0 local flArcUp = 120.0 local flG = 800.0 local flSpin = 1440.0 local flScale = 1.3 local iSkin = 2 local iBombFF = 0 local iAttach = hPlayer.LookupAttachment("handR") if (iAttach == 0) iAttach = hPlayer.LookupAttachment("axeR") local vFwd = hPlayer.IsPlayer() ? hPlayer.EyeAngles().Forward() : hPlayer.GetAbsAngles().Forward() vFwd.Norm() local vSpawn = iAttach != 0 ? hPlayer.GetAttachmentOrigin(iAttach) : hPlayer.EyePosition() vSpawn += vFwd * 48.0 + Vector(0,0,12.0) local vVel = vFwd * flSpeed + Vector(0,0,flArcUp) + hPlayer.GetAbsVelocity() local yaw0 = atan2(vVel.y, vVel.x) * 180.0 / PI local pitch0 = -atan2(vVel.z, sqrt(vVel.x*vVel.x + vVel.y*vVel.y)) * 180.0 / PI PrecacheModel(sModel) local hAxe = Entities.CreateByClassname("tf_projectile_rocket") hAxe.SetTeam(hPlayer.GetTeam()) hAxe.SetOwner(hPlayer) hAxe.SetAbsOrigin(vSpawn) hAxe.SetAbsAngles(QAngle(pitch0, yaw0, 0)) hAxe.DispatchSpawn() NetProps.SetPropInt(hAxe, "m_nModelIndex", PrecacheModel(sModel)) NetProps.SetPropFloat(hAxe, "m_flModelScale", flScale) NetProps.SetPropInt(hAxe, "m_nSkin", iSkin) hAxe.AcceptInput("DispatchEffect", "ParticleEffectStop", hAxe, hAxe) hAxe.SetAbsVelocity(vVel) DispatchParticleAttached(hAxe, "marauder_axe_charged_glow", "trail") try { NetProps.SetPropVector(hAxe, "m_vInitialVelocity", vVel) } catch(e) {} HellOnEarth.SetDestroyCallback(hAxe, function() { local pos = self.GetOrigin() local owner = self.GetOwner() local team = self.GetTeam() local hBomb = Entities.CreateByClassname("tf_generic_bomb") SetPropBool(hBomb, "m_bForcePurgeFixedupStrings", true) hBomb.KeyValueFromInt("damage", Marauder.Combat.AXE_THROW_BLAST_DMG) hBomb.KeyValueFromInt("radius", Marauder.Combat.AXE_THROW_BLAST_RADIUS) hBomb.KeyValueFromInt("friendlyfire", iBombFF) hBomb.KeyValueFromString("classname", "lava_axe") hBomb.DispatchSpawn() hBomb.SetAbsOrigin(pos) hBomb.SetTeam(team) if (owner && owner.IsValid()) hBomb.SetOwner(owner) hBomb.AcceptInput("Detonate", null, owner, owner) DispatchParticleEffect("argent_hit", pos, Vector()) ScreenShake(pos, 10, 4, 0.6, Marauder.Combat.AXE_THROW_BLAST_RADIUS * 4, 0, true) HellOnEarth.PlaySoundFromEntity(owner, "ptx/sfx/demons/marauder/axe_explode.wav") }) hAxe.ValidateScriptScope() local s = hAxe.GetScriptScope() s.vVel <- vVel s.flG <- flG s.flSpin <- flSpin s.Tick <- function() { if (!self || !self.IsValid()) return -1.0 local dt = FrameTime(); if (dt <= 0.0) dt = 0.015 vVel += Vector(0,0,-flG) * dt self.SetAbsVelocity(vVel) local yaw = atan2(vVel.y, vVel.x) * 180.0 / PI local ang = self.GetAbsAngles() ang.y = yaw ang.x += flSpin * dt ang.z = 0 self.SetAbsAngles(ang) return 0.0 } AddThinkToEnt(hAxe, "Tick") return hAxe } function Dash(hPlayer, vDirection, flForce = 1200.0) { if (!hPlayer || !hPlayer.IsValid()) return; local dir = vDirection; dir.z = 0.0; if (dir.LengthSqr() < 1e-6) return; dir.Norm(); local s = hPlayer.GetScriptScope(); s._dashPrevVel <- hPlayer.GetAbsVelocity(); local v = dir * flForce; v.z = 0.1; hPlayer.SetAbsVelocity(v); hPlayer.SetGravity(50); hPlayer.AddCondEx(TF_COND_SPEED_BOOST, 0.35, null) hPlayer.AddCondEx(TF_COND_LOST_FOOTING, 0.35, null); EntFireByHandle(hPlayer, "runscriptcode", @" local s = self.GetScriptScope() local v = s._dashPrevVel self.SetAbsVelocity(v) self.SetGravity(1) s._dashPrevVel = null ", 0.35, null, null); } function NoStrafeLock(hPlayer) { hPlayer.ValidateScriptScope(); local s = hPlayer.GetScriptScope(); s.NoStrafeEye <- hPlayer.EyeAngles(); s.AddThink("NoStrafe", function() { if (!self.IsAlive()) return; local ang = NoStrafeEye; self.SnapEyeAngles(ang); local v = self.GetAbsVelocity(); local spd2 = v.x*v.x + v.y*v.y; if (spd2 < 25.0) return; local f = ang.Forward(); f.z = 0.0; local f2 = f.x*f.x + f.y*f.y; if (f2 < 1e-6) return; local spd = sqrt(spd2); local inv = 1.0 / sqrt(f2); f.x *= inv; f.y *= inv; v.x = f.x * spd; v.y = f.y * spd; self.SetAbsVelocity(v); }); } function NoStrafeUnlock(hPlayer) { hPlayer.ValidateScriptScope() hPlayer.GetScriptScope().RemoveThink("NoStrafe") } function AxeThrow_Wrapper(hPlayer) { EntFireByHandle(hPlayer, "runscriptcode", "Marauder.ThrowAxe(self)", 0.466, null, null) } function RingBeamAttack(hAttacker, flExpandHU, iDamage = 300, flDuration = 2.5, vOrigin = null) { if (!hAttacker || !hAttacker.IsValid()) return null PrecacheModel("models/empty.mdl") local vCenter = hAttacker.GetOrigin() if (vOrigin) vCenter = vOrigin local qAngles = hAttacker.IsPlayer() ? hAttacker.EyeAngles() : hAttacker.GetAbsAngles() local vRight = qAngles.Left() * -1 vRight.z = 0.0 vRight.Norm() local idString = hAttacker.entindex().tointeger().tostring() + "_" + ((Time() * 1000).tointeger()).tostring() local markerOneName = "ringmarkerone_" + idString local markerTwoName = "ringmarkertwo_" + idString local beamName = "roundbeam_" + idString local hMarkerOne = Entities.CreateByClassname("prop_dynamic") hMarkerOne.KeyValueFromString("targetname", markerOneName) hMarkerOne.KeyValueFromString("model", "models/empty.mdl") hMarkerOne.KeyValueFromInt("solid", 0) hMarkerOne.DispatchSpawn() hMarkerOne.SetAbsOrigin(vCenter + Vector(0, 0, 30)) local hMarkerTwo = Entities.CreateByClassname("prop_dynamic") hMarkerTwo.KeyValueFromString("targetname", markerTwoName) hMarkerTwo.KeyValueFromString("model", "models/empty.mdl") hMarkerTwo.KeyValueFromInt("solid", 0) hMarkerTwo.DispatchSpawn() hMarkerTwo.SetAbsOrigin(vCenter + Vector(0, 0, 30)) hMarkerTwo.SetAbsAngles(QAngle(0, 180, 0)) local hBeam = Entities.CreateByClassname("env_beam") hBeam.DispatchSpawn() hBeam.SetAbsOrigin(vCenter + Vector(0, 0, 30)) hBeam.ValidateScriptScope() local data = hBeam.GetScriptScope() data.vCenter <- vCenter data.vRight <- vRight data.flRingZ <- vCenter.z + 30.0 data.flRadius <- 0.0 data.flMaxRadius <- flExpandHU.tofloat() data.flDuration <- flDuration.tofloat() <= 0.0 ? 0.01 : flDuration.tofloat() data.flSpeed <- data.flMaxRadius / data.flDuration data.flElapsed <- 0.0 data.hMarkerOne <- hMarkerOne data.hMarkerTwo <- hMarkerTwo data.flLastTime <- Time() data.flStartTime <- data.flLastTime data.hAttacker <- hAttacker data.iDamagePerSec <- iDamage data.flThickness <- 8.0 data.bStruck <- false data.UpdateMarkers <- function() { local p1 = vCenter + vRight * flRadius local p2 = vCenter - vRight * flRadius p1.z = flRingZ p2.z = flRingZ hMarkerOne.SetAbsOrigin(p1) hMarkerTwo.SetAbsOrigin(p2) } data.ApplyDpsToPlayers <- function(dt) { for (local hPlayer = null; hPlayer = Entities.FindByClassname(hPlayer, "player");) { if (!hPlayer || !hPlayer.IsValid() || !hPlayer.IsAlive()) continue if (hAttacker.IsValid() && hPlayer.GetTeam() == hAttacker.GetTeam()) continue local z0 = hPlayer.GetOrigin().z local z1 = hPlayer.EyePosition().z if (!(flRingZ > z0 - 10 && flRingZ < z1 + 40)) continue local dist = ((hPlayer.GetOrigin() + Vector(0, 0, 10)) - vCenter).Length() if (fabs(dist - flRadius) > flThickness) continue hPlayer.ValidateScriptScope() local playerData = hPlayer.GetScriptScope() if (!(playerData.rawin("flLastRingDmg")) || Time() > playerData.flLastRingDmg + 0.09) { hPlayer.TakeDamageCustom(hPlayer, hAttacker, null, Vector(0, 0, 0), Vector(0, 0, 0), iDamage, 8, 0) playerData.flLastRingDmg <- Time() } } } data.ApplyDpsToBuildings <- function(dt) { local function tryHit(classname) { for (local hObj = null; hObj = Entities.FindByClassname(hObj, classname);) { if (!hObj || !hObj.IsValid()) continue if (hAttacker.IsValid() && hObj.GetTeam() == hAttacker.GetTeam()) continue local z0 = hObj.GetOrigin().z local z1 = z0 + 60 if (!(flRingZ > z0 - 10 && flRingZ < z1 + 10)) continue local dist = ((hObj.GetOrigin() + Vector(0, 0, 10)) - vCenter).Length() if (fabs(dist - flRadius) > flThickness) continue hObj.ValidateScriptScope() local objData = hObj.GetScriptScope() if (!(objData.rawin("flLastRingDmg")) || Time() > objData.flLastRingDmg + 0.18) { hObj.TakeDamageCustom(hObj, hAttacker, null, Vector(0, 0, 0), Vector(0, 0, 0), iDamage, 8, 0) objData.flLastRingDmg <- Time() } } } tryHit("obj_sentrygun") tryHit("obj_dispenser") tryHit("obj_teleporter") } data.Cleanup <- function() { if (self && self.IsValid()) { AddThinkToEnt(self, null) self.AcceptInput("TurnOff", null, null, null) EntFireByHandle(self, "Kill", "", 0.01, null, null) } if (hMarkerOne && hMarkerOne.IsValid()) EntFireByHandle(hMarkerOne, "Kill", "", 0.0, null, null) if (hMarkerTwo && hMarkerTwo.IsValid()) EntFireByHandle(hMarkerTwo, "Kill", "", 0.0, null, null) } data.RingThink <- function() { local d = self.GetScriptScope() local now = Time() local dt = now - d.flLastTime if (dt <= 0.0) dt = 0.01 if (dt > 0.1) dt = 0.1 d.flLastTime = now d.flElapsed += dt if (d.flElapsed >= d.flDuration || now - d.flStartTime > d.flDuration + 0.25) { d.Cleanup(); return } d.flRadius = d.flSpeed * d.flElapsed if (d.flRadius >= d.flMaxRadius) { d.Cleanup(); return } d.UpdateMarkers() if (!d.bStruck) { self.AcceptInput("StrikeOnce", null, null, null); d.bStruck <- true } d.ApplyDpsToPlayers(dt) d.ApplyDpsToBuildings(dt) return -1 } AddThinkToEnt(hBeam, "RingThink") return hBeam } function RingBeamAttack_Wrapper(hPlayer, vHitPos) { local hBeam = Marauder.RingBeamAttack(hPlayer, 1000, 50, 1.5, vHitPos) hBeam.ValidateScriptScope() local hScope = hBeam.GetScriptScope() hScope.vRingPos <- vHitPos hScope.hOwner <- hPlayer EntFireByHandle(hBeam, "runscriptcode", @"Marauder.RingBeamAttack(self.GetScriptScope().hOwner, 1000, 50, 1.5, self.GetScriptScope().vRingPos)", 0.15, hPlayer, null) } function Summon_Strike(hPlayer, flRadius, iCount, flTotalSetupTime = 2.0, iMaxStrikes = 16) { if (!hPlayer || !hPlayer.IsValid()) return null local iMinAreaSize = 1500 local vCenter = hPlayer.GetOrigin() local aBots = [] for (local i = 1; i <= MAX_CLIENTS; i++) { local h = PlayerInstanceFromIndex(i) if (h && h.IsValid() && h.IsAlive() && h.IsBotOfType(1337) && h.HasBotTag("marauder_summon")) aBots.append(h) } local tNav = {} NavMesh.GetNavAreasInRadius(vCenter, flRadius, tNav) local aSpots = [] foreach (id, nav in tNav) { if (nav.GetSizeX() * nav.GetSizeY() > iMinAreaSize && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_RED) && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_BLUE) && nav.IsReachableByTeam(TF_TEAM_BLUE)) aSpots.append(nav) } local iStrikes = (iCount <= iMaxStrikes) ? iCount : iMaxStrikes if (iStrikes <= 0) return 0 local aTimes = HellOnEarth.meteorTimes(iStrikes, flTotalSetupTime) local aLocs = [] if (aSpots.len() > 0) { for (local i = 0; i < iStrikes; i++) { local nav = aSpots[RandomInt(0, aSpots.len() - 1)] aLocs.append(nav.FindRandomSpot()) } } else { for (local i = 0; i < iStrikes; i++) aLocs.append(vCenter) } if (aBots.len() > 1) { for (local i = aBots.len() - 1; i > 0; i--) { local j = RandomInt(0, i) local t = aBots[i]; aBots[i] = aBots[j]; aBots[j] = t } } local aTargets = [] if (aBots.len() > 0) { for (local k = 0; k < iStrikes; k++) aTargets.append(aBots[k % aBots.len()]) } else { for (local k = 0; k < iStrikes; k++) aTargets.append(null) } local aIdx = [] for (local i = 0; i < iStrikes; i++) aIdx.append(i) for (local i = aIdx.len() - 1; i > 0; i--) { local j = RandomInt(0, i) local t = aIdx[i]; aIdx[i] = aIdx[j]; aIdx[j] = t } for (local n = 0; n < iStrikes; n++) { local iSel = aIdx[n] local hBot = aTargets[iSel] local vPos = aLocs[iSel] local flT = aTimes[iSel] local trace = { start = vPos, end = vPos - Vector(0, 0, 1000), mask = MASK_PLAYERSOLID & ~(CONTENTS_MONSTER) } TraceLineEx(trace) if (!trace.hit) return vPos = trace.pos local hIndicator = SpawnEntityFromTable("prop_dynamic", { model = "models/props_mvm/indicator/indicator_circle.mdl", DisableBoneFollowers = "1", skin = "5", DefaultAnim = "start", disableshadows = "1", StartDisabled = "1" }); hIndicator.SetAbsOrigin(vPos + Vector(0,0,7)) hIndicator.SetModelScale(1.7, 0) EntFireByHandle(hIndicator, "Enable", "", flT, null, null) EntFireByHandle(hIndicator, "Kill", "", 3 + flT, null, null) local sBotExpr = (hBot && hBot.IsValid()) ? format("EntIndexToHScript(%d)", hBot.entindex()) : "null" local sCmd = format("Marauder.LightningSummon(%s, Vector(%d,%d,%d))", sBotExpr, vPos.x, vPos.y, vPos.z) EntFire("bignet", "runscriptcode", sCmd, flT + 3.0) } return iStrikes } function LightningSummon(hPlayer, vPos) { if (hPlayer) hPlayer.SetAbsOrigin(vPos) DispatchParticleEffect("thunder_strike_r", vPos, Vector(0,90,0)) EmitSoundEx({ sound_name = "MeteorExplode" origin = vPos volume = 1 sound_level = 21 // evaluate from 5000 filter_type = RECIPIENT_FILTER_GLOBAL }) local bomb = SpawnEntityFromTable("tf_generic_bomb", { damage = 300, radius = 150, friendlyfire = 0, classname = "megaton" }) ScreenShake(vPos, 10, 16, 1.5, 1500, 0, true) bomb.SetTeam(2) bomb.SetAbsOrigin(vPos) DispatchParticleEffect("hell_ground", vPos + Vector(0,0,5), Vector(90)) EntFireByHandle(bomb, "RunScriptCode", "self.SetHealth(1); self.TakeDamage(1, 0, activator)", 0.1, bomb, bomb) } // MOVESET ADDITIONS ------- SPECIAL ATTACKS ------- SPECIAL ATTACKS ------- SPECIAL ATTACKS // MOVESET ADDITIONS ------- SPECIAL ATTACKS ------- SPECIAL ATTACKS ------- SPECIAL ATTACKS function Summon_Atk(hPlayer) { // ThrowAxe EntFire("spawnbot_marauder_support", "Enable") EntFire("spawnbot_marauder_support", "Disable", null, 3) local tSeqInfo = Marauder.GetMarauderSequenceByName("block_shoulder_wolf_summon") Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.001, tSeqInfo.flDuration) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.SmallAttack") EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq HellOnEarth.PlaySoundFromEntity(self, `ptx/cinematic/gameover.wav`) HellOnEarth.PlaySoundFromEntity(self, `ptx/cinematic/bfg_explode.wav`) Marauder.Summon_Strike(self, 1650, 32, 7, 32) local hChargeParticle = SpawnEntityFromTable(`info_particle_system`, { effect_name = `marauder_portal_wind`, start_active = 1, }) local hChargeParticle2 = SpawnEntityFromTable(`info_particle_system`, { effect_name = `alien_mothership_laser_charge_sep3_firing2_large`, start_active = 1, }) local hChargeParticle3 = SpawnEntityFromTable(`info_particle_system`, { effect_name = `hell_crack_200_teamcolor_red`, start_active = 1, }) ScreenShake(self.GetOrigin(), 10, 15, 5, 9999, 0, true) DispatchParticleEffect(`fl_spin`, self.GetCenter(), Vector()) hChargeParticle.SetAbsOrigin(self.GetCenter() + Vector(0,0,500)) hChargeParticle.SetAbsAngles(QAngle(90,0,0)) hChargeParticle2.SetAbsOrigin(self.GetCenter() + Vector(0,0,2500)) hChargeParticle3.SetAbsOrigin(self.GetCenter() - Vector(0,0,50)) local hRedCorrection = SpawnEntityFromTable(`color_correction`, { StartDisabled = 0, fadeInDuration = 1.0, fadeOutDuration = 3.0, maxweight = 0.5, maxfalloff = -1, minfalloff = 0, filename = `materials/colorcorrection/hell_on_earth.raw` }) EntFireByHandle(hRedCorrection, `Enable`, null, -1, null, null) EntFireByHandle(hRedCorrection, `Disable`, null, 10, null, null) EntFireByHandle(hRedCorrection, `Kill`, null, 15, null, null) EntFireByHandle(hChargeParticle, `Kill`, null, 10, null, null) EntFireByHandle(hChargeParticle2, `Kill`, null, 10, null, null) EntFireByHandle(hChargeParticle3, `Kill`, null, 10, null, null) ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null) } // --------------------------------------- MOVESET BEGIN --------------------------------------- // --------------------------------------- MOVESET BEGIN --------------------------------------- function Evade(hPlayer) // needs an indicator { if (hPlayer.GetScriptScope().STUN_END_TIME > Time()) return local vFwd = hPlayer.EyeAngles().Forward(); vFwd.z = 0.0 if (vFwd.LengthSqr() < 1e-6) return 0 vFwd.Norm() local vRight = Vector(vFwd.y, -vFwd.x, 0.0) local vLeft = Vector(-vFwd.y, vFwd.x, 0.0) local vBack = vFwd * -1.0 local vStart = hPlayer.GetCenter() local vMins = hPlayer.GetBoundingMins() local vMaxs = hPlayer.GetBoundingMaxs() local flMaxCheck = 750.0; local tRH = { start = vStart, end = vStart + vRight * flMaxCheck, hullmin = vMins, hullmax = vMaxs, mask = MASK_PLAYERSOLID, ignore = hPlayer }; TraceHull(tRH); local vRPos = ("pos" in tRH) ? tRH.pos : (vStart + vRight * flMaxCheck) local flRDist = ("startsolid" in tRH) ? 0.0 : (vRPos - vStart).Length() local tLH = { start = vStart, end = vStart + vLeft * flMaxCheck, hullmin = vMins, hullmax = vMaxs, mask = MASK_PLAYERSOLID, ignore = hPlayer }; TraceHull(tLH); local vLPos = ("pos" in tLH) ? tLH.pos : (vStart + vLeft * flMaxCheck) local flLDist = ("startsolid" in tLH) ? 0.0 : (vLPos - vStart).Length() local tBH = { start = vStart, end = vStart + vBack * flMaxCheck, hullmin = vMins, hullmax = vMaxs, mask = MASK_PLAYERSOLID, ignore = hPlayer }; TraceHull(tBH); local vBPos = ("pos" in tBH) ? tBH.pos : (vStart + vBack * flMaxCheck) local flBDist = ("startsolid" in tBH) ? 0.0 : (vBPos - vStart).Length() local vBestDir = vRight; local flBestDist = flRDist; if (flLDist > flBestDist) { flBestDist = flLDist; vBestDir = vLeft; } if (flBDist > flBestDist) { flBestDist = flBDist; vBestDir = vBack; } if (flBestDist < 200.0) return 0; // placeholder: not null local hScope = hPlayer.GetScriptScope() if (!hScope) hPlayer.GetScriptScope().tCurrentSeq = 1 hPlayer.AcceptInput("$PlaySequence", (vBestDir == vRight) ? "evade_right" : "evade_left", hPlayer, null) EntFireByHandle(hPlayer, "runscriptcode", @" if (self.GetScriptScope().tCurrentSeq == 1) self.GetScriptScope().tCurrentSeq = null ", 1, null, null); local vDashDir = vBestDir; vDashDir.z = 0.1; hPlayer.SetAbsVelocity(vDashDir * 1200); hPlayer.SetGravity(0.00001); hPlayer.AddCondEx(TF_COND_LOST_FOOTING, 0.35, null) hPlayer.AddCondEx(TF_COND_SPEED_BOOST, 0.35, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "PlayerDash") EntFireByHandle(hPlayer, "runscriptcode", @"self.SetGravity(1)", 0.35, null, null); return 1; } function AxeThrow_Shield(hPlayer) { // ThrowAxe local tSeqInfo = Marauder.GetMarauderSequenceByName("block_shoulder_axe_throw_01") Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.001, tSeqInfo.flDuration) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.SmallAttack", 4000) EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`, 4000) ", tSeqInfo.flTimeToEffect * 0.65, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq Marauder.ThrowAxe(self) ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function AxeChop_Shield(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("block_shoulder_axe_throw_01") Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.001, tSeqInfo.flDuration) Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.SmallAttack") EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`) ", tSeqInfo.flTimeToEffect * 0.65, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq Marauder.AxeBaseAttack(self, Marauder.Combat.AXE_SHORT_SHIELD_DMG, Marauder.Combat.AXE_SHORT_SHIELD_RANGE, Marauder.Combat.AXE_SHORT_SHIELD_MAX_FOV, 64) ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function ComboUppercut(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("combo_uppercut_gunblast"); local dir = hPlayer.EyeAngles().Forward(); local hScope = hPlayer.GetScriptScope() local iAttackTimeFirst = tSeqInfo.flTimeToEffect[0] local iAttackTimeSecond = tSeqInfo.flTimeToEffect[1] Marauder.OffsetNextAttack(hPlayer, iAttackTimeFirst + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.001, tSeqInfo.flDuration + 0.25) Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.VoiceAttack") EntFireByHandle(hScope.hShotgun, "SetParentAttachment", "shotgun_hand2", 0.475, hPlayer, hPlayer) EntFireByHandle(hScope.hShotgun, "runscriptcode", "self.SetLocalOrigin(Vector(0,3,0))", 0.475, hPlayer, hPlayer) EntFireByHandle(hScope.hShotgun, "SetParentAttachment", "shotgun_holster2", 2.2, hPlayer, hPlayer) EntFireByHandle(hScope.hShotgun, "runscriptcode", "self.SetLocalOrigin(Vector())", 2.2, hPlayer, hPlayer) EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.SmallAttack`) ", iAttackTimeFirst * 0.65, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq Marauder.AxeBaseAttack(self, Marauder.Combat.AXE_SHORT_DMG, Marauder.Combat.AXE_SHORT_RANGE, Marauder.Combat.AXE_SHORT_MAX_FOV, 64) Marauder.PushAttack(self, 40, Marauder.Combat.AXE_SHORT_RANGE, Marauder.Combat.AXE_SHORT_MAX_FOV, 500) ", iAttackTimeFirst, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" Marauder.ShotgunAttack(self) ", iAttackTimeSecond, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function ShotgunQuickdraw(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("quickdraw") local dir = hPlayer.EyeAngles().Forward() Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) local hScope = hPlayer.GetScriptScope() hScope.tCurrentSeq = tSeqInfo hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/sfx/demons/marauder/shotgun_warning.wav") hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.001, tSeqInfo.flDuration) EntFireByHandle(hScope.hShotgun, "SetParentAttachment", "shotgun_hand2", 0.467, hPlayer, hPlayer) EntFireByHandle(hScope.hShotgun, "runscriptcode", "self.SetLocalOrigin(Vector(0,3,0))", 0.467, hPlayer, hPlayer) EntFireByHandle(hScope.hShotgun, "SetParentAttachment", "shotgun_holster2", 1.233, hPlayer, hPlayer) EntFireByHandle(hScope.hShotgun, "runscriptcode", "self.SetLocalOrigin(Vector())", 1.233, hPlayer, hPlayer) EntFireByHandle(hPlayer, "runscriptcode", @" Marauder.ShotgunAttack(self) ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null) } function GroundSlam(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("taunt_01"); Marauder.NoStrafeLock(hPlayer); local dir = hPlayer.EyeAngles().Forward(); Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.0001, tSeqInfo.flDuration) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.NoStrafeUnlock(self) local vHitPos = self.GetAttachmentOrigin(self.LookupAttachment(`axeR`)) local Trace = { start = vHitPos, end = vHitPos - Vector(0, 0, 1000), mask = 33579137 } TraceLineEx(Trace) if (!Trace.hit) return vHitPos = Trace.pos ScreenShake(vHitPos, 48, 15, 1, -1, 0, true) self.ValidateScriptScope() local s = self.GetScriptScope() if (!(`PullPushes` in s)) s.PullPushes <- [] local o = self.GetOrigin() + Vector(0.0, 0.0, 10.0) local pull = SpawnEntityFromTable(`point_push`, { origin = o, radius = 600.0, magnitude = -1600.0, innerradius = 200.0, spawnflags = 8, enabled = 1 }) s.PullPushes.append(pull) local core = SpawnEntityFromTable(`point_push`, { origin = o, radius = 24.0, magnitude = 800.0, innerradius = 24.0, spawnflags = 8, enabled = 1 }) s.PullPushes.append(core) HellOnEarth.PlaySoundFromEntity(self, `misc/doomsday_missile_explosion.wav`) DispatchParticleEffect(`hell_ground`, vHitPos + Vector(0,0,10), Vector(90)) DispatchParticleEffect(`hammer_impact_button`, vHitPos + Vector(0,0,10), Vector(0, 0, 0)) DispatchParticleEffect(`hammer_bones_kickup`, vHitPos + Vector(0,0,10), Vector(0, 0, 0)) ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" local s = self.GetScriptScope(); if (`PullVortexFX` in s && s.PullVortexFX && s.PullVortexFX.IsValid()) { s.PullVortexFX.Destroy(); s.PullVortexFX = null; } if (`PullPushes` in s) { foreach(p in s.PullPushes) if (p && p.IsValid()) p.Destroy(); s.PullPushes.clear(); } ", tSeqInfo.flTimeToEffect + 0.2, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function BigChop(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("bigchop"); Marauder.NoStrafeLock(hPlayer); local dir = hPlayer.EyeAngles().Forward(); Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999); hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo; hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect); hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null); Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`) ", tSeqInfo.flTimeToEffect * 0.65, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, Marauder.Combat.SPINSLICE_DMG, Marauder.Combat.SPINSLICE_RANGE, Marauder.Combat.SPINSLICE_MAX_FOV, 64) Marauder.NoStrafeUnlock(self) ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function Stab(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("stab_01"); Marauder.NoStrafeLock(hPlayer); hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo; hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect); hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null); Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`) local dir = self.EyeAngles().Forward(); Marauder.Dash(self, dir, 800.0) ", tSeqInfo.flTimeToEffect * 0.65, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, 120, 220, 100, 64) ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" Marauder.NoStrafeUnlock(self) self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function SpinSlice(hPlayer) { local tSeqInfo = Marauder.GetMarauderSequenceByName("spinslice"); Marauder.NoStrafeLock(hPlayer); local dir = hPlayer.EyeAngles().Forward(); Marauder.Dash(hPlayer, dir, 1200.0) Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999); hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.GetScriptScope().IS_VURNEABLE = true DispatchParticleEffect("FX_green_eyeflash_flash", hPlayer.EyePosition() + hPlayer.EyeAngles().Forward() * 25, Vector()) HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/sfx/demons/marauder/marauder_vurneable.mp3") hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect); hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null); Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`) ", tSeqInfo.flTimeToEffect * 0.65, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" if (self.InCond(71)) return local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, Marauder.Combat.SPINSLICE_DMG, Marauder.Combat.SPINSLICE_RANGE, Marauder.Combat.SPINSLICE_MAX_FOV, 64) Marauder.NoStrafeUnlock(self) self.GetScriptScope().IS_VURNEABLE = false self.GetScriptScope().STUN_DMG_ACCUM = 0 ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function SwiftChop_Long(hPlayer) { local s = hPlayer.GetScriptScope() local tSeqInfo = s.swiftChopLongSeqs[RandomInt(0, 3)] Marauder.NoStrafeLock(hPlayer) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect) Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) s.tCurrentSeq = tSeqInfo; EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`) local dir = self.EyeAngles().Forward(); Marauder.Dash(self, self.EyeAngles().Forward(), 1000.0) Marauder.NoStrafeUnlock(self) ", tSeqInfo.flTimeToEffect * 0.65, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq Marauder.NoStrafeUnlock(self) self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, Marauder.Combat.AXE_LONG_DMG, Marauder.Combat.AXE_LONG_RANGE, Marauder.Combat.AXE_LONG_MAX_FOV, 64) ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function SwiftChop_Close(hPlayer) { local s = hPlayer.GetScriptScope(); local tSeqInfo = s.swiftChopCloseSeqs[RandomInt(0, 3)]; Marauder.NoStrafeLock(hPlayer); HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.SmallAttack"); hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null); hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect); Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999); Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) s.tCurrentSeq = tSeqInfo; EntFireByHandle(hPlayer, "runscriptcode", @" HellOnEarth.PlaySoundFromEntity(self, `Marauder.BigAttack`) local dir = self.EyeAngles().Forward(); Marauder.Dash(self, self.EyeAngles().Forward(), 700.0) Marauder.NoStrafeUnlock(self) ", tSeqInfo.flTimeToEffect * 0.25, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq Marauder.NoStrafeUnlock(self) self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, Marauder.Combat.AXE_SHORT_DMG, Marauder.Combat.AXE_SHORT_RANGE, Marauder.Combat.AXE_SHORT_MAX_FOV, 64) ", tSeqInfo.flTimeToEffect, null, null); EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null); } function SwiftChop_Close_Push(hPlayer) { local sName = RandomInt(0,1) == 0 ? "swiftchop_close_03" : "swiftchop_close_04" local tSeqInfo = Marauder.GetMarauderSequenceByName(sName) Marauder.NoStrafeLock(hPlayer) HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.SmallAttack") hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect) Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" local hSeqInfo = self.GetScriptScope().tCurrentSeq Marauder.NoStrafeUnlock(self) self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.PushAttack(self, Marauder.Combat.MELEE_PUSH_FORCE, Marauder.Combat.MELEE_PUSH_RANGE, Marauder.Combat.MELEE_PUSH_MAX_FOV, Marauder.Combat.MELEE_PUSH_FORCE_VERTICAL) Marauder.AxeBaseAttack(self, Marauder.Combat.MELEE_PUSH_DMG, Marauder.Combat.MELEE_PUSH_RANGE, Marauder.Combat.MELEE_PUSH_MAX_FOV, 64) ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null ", tSeqInfo.flDuration + 0.2, null, null) } function BigSlam_Jump(hPlayer) { HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/sfx/demons/marauder/big_slam.wav") HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.VoiceAttack") local tSeqInfo = Marauder.GetMarauderSequenceByName("jump_bigslam_01") local vDest local s = hPlayer.GetScriptScope() local tgt = ("THREAT_TARGET" in s) ? s.THREAT_TARGET : null if (tgt && tgt.IsValid() && tgt.IsAlive()) { local tr = { start = tgt.GetOrigin(), end = tgt.GetOrigin() - Vector(0,0,1000), mask = (MASK_VISIBLE_AND_NPCS & ~CONTENTS_MONSTER), ignore = hPlayer } TraceLineEx(tr) vDest = tr.hit ? tr.pos : tgt.GetOrigin() } else { local d = hPlayer.EyeAngles().Forward(); d.z = 0.0 if (d.LengthSqr() < 1e-6) return d.Norm() vDest = hPlayer.GetOrigin() + d * 500.0 } Marauder.LeapTo(hPlayer, vDest, 0.6, 1, null) // Note: hardcoded 0.6 jump-leap duration; his legs touch the ground before the axe is swung into the ground Marauder.NoStrafeLock(hPlayer) Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.001, tSeqInfo.flDuration) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) hPlayer.GetScriptScope().IS_VURNEABLE = true DispatchParticleEffect("FX_green_eyeflash_flash", hPlayer.EyePosition() + hPlayer.EyeAngles().Forward() * 25, Vector()) HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/sfx/demons/marauder/marauder_vurneable.mp3") EntFireByHandle(hPlayer, "runscriptcode", @" if (self.InCond(71)) return local hScope = self.GetScriptScope() local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, Marauder.Combat.BIG_SLAM_AXE_DMG, Marauder.Combat.BIG_SLAM_AXE_RANGE, Marauder.Combat.BIG_SLAM_MAX_FOV, 128) local vHitPos = self.GetAttachmentOrigin(self.LookupAttachment(`axeR`)) local Trace = { start = vHitPos + Vector(0,0,100), end = vHitPos - Vector(0, 0, 1000), mask = 33579137 } TraceLineEx(Trace) if (!Trace.hit) return vHitPos = Trace.pos ScreenShake(vHitPos, 32, 15, 1, 600, 0, true) local hBomb = Entities.CreateByClassname(`tf_generic_bomb`) SetPropBool(hBomb, `m_bForcePurgeFixedupStrings`, true) hBomb.KeyValueFromInt(`damage`, 500) hBomb.KeyValueFromInt(`radius`, 95) hBomb.KeyValueFromInt(`friendlyfire`, 0) hBomb.KeyValueFromString(`classname`, `lava_axe`) hBomb.DispatchSpawn() hBomb.SetAbsOrigin(vHitPos) hBomb.SetTeam(self.GetTeam()) hBomb.SetOwner(self) hBomb.AcceptInput(`Detonate`, null, self, self) DispatchParticleEffect(`argent_spawn`, vHitPos + Vector(0,0,10), Vector()) DispatchParticleEffect(`FX_9_big_exp_1_air`, vHitPos + Vector(0,0,10), Vector()) self.GetScriptScope().IS_VURNEABLE = false self.GetScriptScope().STUN_DMG_ACCUM = 0 ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null Marauder.NoStrafeUnlock(self) ", tSeqInfo.flDuration + 0.2, null, null) } function BigSlam_AOE(hPlayer) // MIN_COOLDOWN -> 8 { HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/sfx/demons/marauder/big_slam.wav") HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.VoiceAttack") local tSeqInfo = Marauder.GetMarauderSequenceByName("bigslam_01") Marauder.NoStrafeLock(hPlayer) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect) Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" local hScope = self.GetScriptScope() local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) Marauder.AxeBaseAttack(self, Marauder.Combat.BIG_SLAM_AXE_DMG, Marauder.Combat.BIG_SLAM_AXE_RANGE, Marauder.Combat.BIG_SLAM_MAX_FOV, 128) local vHitPos = self.GetAttachmentOrigin(self.LookupAttachment(`axeR`)) local Trace = { start = vHitPos, end = vHitPos - Vector(0, 0, 1000), mask = 33579137 } TraceLineEx(Trace) if (!Trace.hit) return vHitPos = Trace.pos ScreenShake(vHitPos, 32, 15, 1, 600, 0, true) hScope.hCrack <- HellOnEarth.DispatchParticleEffectObj(`hell_finish`, vHitPos + Vector(0,0,2.5)) local hCrack = hScope.hCrack if (hCrack && hCrack.IsValid()) { hCrack.ValidateScriptScope() local s = hCrack.GetScriptScope() s.radius <- 200.0 s.Ignite <- function(player, duration = 3.0, damage = 15) { local utilignite = FindByName(null, `__utilignite`) if (utilignite == null) { utilignite = SpawnEntityFromTable(`trigger_ignite`, { targetname = `__utilignite`, burn_duration = duration, damage = damage, spawnflags = SF_TRIGGER_ALLOW_CLIENTS }) } EntFireByHandle(utilignite, `StartTouch`, ``, -1, player, player) EntFireByHandle(utilignite, `EndTouch`, ``, -1, player, player) } s.Think <- function() { local origin = self.GetOrigin() for (local p = null; p = Entities.FindByClassnameWithin(p, `player`, origin, radius);) { if (!p || !p.IsValid()) continue if (NetProps.GetPropInt(p, `m_lifeState`) != 0 || p.GetTeam() == TEAM_SPECTATOR) continue if (p.GetTeam() == TF_TEAM_PVE_DEFENDERS) { DispatchParticleEffect(`healthgained_red_large`, p.EyePosition() + Vector(0, 0, p.GetModelScale() * 20), Vector(0, 0, 0)) p.AddCondEx(32, 1, null) p.SetHealth(p.GetHealth() + 300) } else { Ignite(p) } } return 0.5 } AddThinkToEnt(hCrack, `Think`) } local hBomb = Entities.CreateByClassname(`tf_generic_bomb`) SetPropBool(hBomb, `m_bForcePurgeFixedupStrings`, true) hBomb.KeyValueFromInt(`damage`, 250) hBomb.KeyValueFromInt(`radius`, 125) hBomb.KeyValueFromInt(`friendlyfire`, 0) hBomb.KeyValueFromString(`classname`, `lava_axe`) hBomb.DispatchSpawn() hBomb.SetAbsOrigin(vHitPos) hBomb.SetTeam(self.GetTeam()) hBomb.SetOwner(self) hBomb.AcceptInput(`Detonate`, null, self, self) DispatchParticleEffect(`argent_hit`, vHitPos + Vector(0,0,10), Vector()) ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().hCrack.Kill() EntFire(`__utilignite`,`Kill`) ", tSeqInfo.flDuration + 5, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null Marauder.NoStrafeUnlock(self) ", tSeqInfo.flDuration + 0.2, null, null) } function Hammer_AOE(hPlayer) // MIN_COOLDOWN -> 8 { HellOnEarth.PlaySoundFromEntity(hPlayer, "ptx/sfx/demons/marauder/big_slam.wav") HellOnEarth.PlaySoundFromEntity(hPlayer, "Marauder.VoiceAttack") local tSeqInfo = Marauder.GetMarauderSequenceByName("hammer") Marauder.NoStrafeLock(hPlayer) hPlayer.AcceptInput("$PlaySequence", tSeqInfo.sName, hPlayer, null) hPlayer.AddCustomAttribute("CARD: move speed bonus", 0.5, tSeqInfo.flTimeToEffect) Marauder.OffsetNextAttack(hPlayer, tSeqInfo.flTimeToEffect + 999) hPlayer.GetScriptScope().tCurrentSeq = tSeqInfo Marauder.SetAttackFX(hPlayer, tSeqInfo.flDuration) EntFireByHandle(hPlayer, "runscriptcode", @" local hScope = self.GetScriptScope() local hSeqInfo = self.GetScriptScope().tCurrentSeq self.AddCustomAttribute(`CARD: move speed bonus`, 0.001, hSeqInfo.flDuration - hSeqInfo.flTimeToEffect) local vHitPos = self.GetAttachmentOrigin(self.LookupAttachment(`axeR`)) local Trace = { start = vHitPos, end = vHitPos - Vector(0, 0, 1000), mask = 33579137 } TraceLineEx(Trace) if (!Trace.hit) return vHitPos = Trace.pos ScreenShake(vHitPos, 32, 15, 1, 1500, 0, true) local hBomb = Entities.CreateByClassname(`tf_generic_bomb`) SetPropBool(hBomb, `m_bForcePurgeFixedupStrings`, true) hBomb.KeyValueFromInt(`damage`, 300) hBomb.KeyValueFromInt(`radius`, 70) hBomb.KeyValueFromInt(`friendlyfire`, 0) hBomb.KeyValueFromString(`classname`, `lava_axe`) hBomb.DispatchSpawn() hBomb.SetAbsOrigin(vHitPos) hBomb.SetTeam(self.GetTeam()) hBomb.SetOwner(self) hBomb.AcceptInput(`Detonate`, null, self, self) DispatchParticleEffect(`hell_judgmentcut_sphere`, vHitPos + Vector(0,0,10), Vector(0, 0, 0)) self.GetScriptScope().__vCurrHitPos <- vHitPos Marauder.RingBeamAttack_Wrapper(self, vHitPos) EntFireByHandle(self, `runscriptcode`, @`Marauder.RingBeamAttack_Wrapper(self, self.GetScriptScope().__vCurrHitPos)`, 1, null, null) EntFireByHandle(self, `runscriptcode`, @`Marauder.RingBeamAttack_Wrapper(self, self.GetScriptScope().__vCurrHitPos)`, 2, null, null) local hCrack = HellOnEarth.DispatchParticleEffectObj(`hell_crack_200_teamcolor_red`, vHitPos + Vector(0,0,2.5)) if (hCrack && hCrack.IsValid()) { hCrack.ValidateScriptScope() local s = hCrack.GetScriptScope() s.radius <- 150 s.Ignite <- function(player, duration = 3, damage = 25) { local utilignite = FindByName(null, `__utilignite`) if (utilignite == null) { utilignite = SpawnEntityFromTable(`trigger_ignite`, { targetname = `__utilignite`, burn_duration = duration, damage = damage, spawnflags = SF_TRIGGER_ALLOW_CLIENTS }) } EntFireByHandle(utilignite, `StartTouch`, ``, -1, player, player) EntFireByHandle(utilignite, `EndTouch`, ``, -1, player, player) } s.Think <- function() { local origin = self.GetOrigin() for (local p = null; p = Entities.FindByClassnameWithin(p, `player`, origin, radius);) { if (!p || !p.IsValid()) continue if (NetProps.GetPropInt(p, `m_lifeState`) != 0 || p.GetTeam() == TEAM_SPECTATOR) continue if (p.GetTeam() == TF_TEAM_PVE_DEFENDERS) { DispatchParticleEffect(`healthgained_red_large`, p.EyePosition() + Vector(0, 0, p.GetModelScale() * 20), Vector(0, 0, 0)) p.AddCondEx(32, 1, null) p.SetHealth(p.GetHealth() + 300) } else { Ignite(p) } } return 0.5 } AddThinkToEnt(hCrack, `Think`) } EntFireByHandle(hCrack, `Kill`, ``, 5, null, null) ", tSeqInfo.flTimeToEffect, null, null) EntFireByHandle(hPlayer, "runscriptcode", @" self.GetScriptScope().tCurrentSeq = null Marauder.NoStrafeUnlock(self) ", tSeqInfo.flDuration + 0.2, null, null) } // --------------------------------------- MOVESET END --------------------------------------- // --------------------------------------- MOVESET END --------------------------------------- // logic function Setup(hPlayer) { Marauder.BuildLookup() Marauder.BuildAttackState(hPlayer) local c = ("ThreatConfig" in Marauder) ? Marauder.ThreatConfig : { DETECT_RADIUS = 3000.0, ALT_PROXIMITY = 500.0, RETAIN_TIME = 0.5, LOS_GRACE = 0.25, FOV_DEG = 60.0, WIDE_FOV_DEG = 120.0 } PrecacheModel("models/doom_eternal/demons/marauder_new.mdl") hPlayer.SetCustomModelWithClassAnimations("models/doom_eternal/demons/marauder_new.mdl") if ("HellOnEarth" in getroottable()) HellOnEarth.CURRENT_BOSS_HANDLE = hPlayer hPlayer.ValidateScriptScope() local hScope = hPlayer.GetScriptScope() if (!("ThinkTable" in hScope)) hScope.ThinkTable <- {} hScope.AddThink <- function(name, fn) { ThinkTable[name] <- fn } hScope.RemoveThink <- function(name) { if (name in ThinkTable) delete ThinkTable[name] } hScope.tCurrentSeq <- null hScope.LOCK_EN <- false hScope.THREAT_FOV_COS <- cos((c.FOV_DEG * 0.5) * (PI / 180.0)) hScope.THREAT_WIDE_FOV_COS <- cos((c.WIDE_FOV_DEG * 0.5) * (PI / 180.0)) hScope.THREAT_RADIUS_SQR <- c.DETECT_RADIUS * c.DETECT_RADIUS hScope.THREAT_ALT_PROX_SQR <- c.ALT_PROXIMITY * c.ALT_PROXIMITY hScope.THREAT_RETAIN_TIME <- c.RETAIN_TIME hScope.THREAT_LOS_GRACE <- c.LOS_GRACE hScope.THREAT_TARGET <- null hScope.THREAT_VISIBLE <- false hScope.THREAT_LAST_SEEN_POS <- hPlayer.GetCenter() hScope.THREAT_LAST_SEEN_TIME <- 0.0 hScope.THREAT_RETAIN_UNTIL <- 0.0 hScope.CURRENTLY_PERFORMING_ACTION <- false hScope.USE_WEIGHTED_RANDOM <- true hScope.SHIELD_UP <- false hScope.SHIELD_GRACE_PERIOD <- 2.0 hScope.GRACE_PERIOD_END <- -1 hScope.SHIELD_IDLE_DROP_DELAY <- 1.0 hScope.LAST_SHIELD_HIT_TIME <- 0.0 hScope.USE_SELECTOR <- true hScope.SHIELD_ONLY <- { AxeChop_Shield=true, Summon_Atk=true } hScope.IS_VURNEABLE <- false hScope.SHIELD_UP_TIME <- 0.0 hScope.SHIELD_UP_TIME_SECONDS <- 0.0 hScope.EVADE_DAMAGE_ACCUM <- 0.0 hScope.EVADE_THRESHOLD <- 500.0 hScope.EVADE_LAST_TIME <- -99999.0 hScope.EVADE_COOLDOWN <- 6.0 hScope.tPlayerDamage <- {} hScope.PUNISH_THRESHOLD <- 250 hScope.PD_VAL <- array(2049, 0.0) hScope.PD_TS <- array(2049, 0.0) hScope.DECAY_PER_SEC <- 10.0 hScope.LAST_PARTICLE_TIME <- Time() hScope.STUN_END_TIME <- Time() hScope.STUN_DMG_ACCUM <- 0 hScope.LastAttackKey <- null hScope.GlobalAttackGap <- 2.0 hScope.NextAttackAllowedAt <- 0.0 for (local i = 0; i <= MAX_CLIENTS; i++) { local hPlayer = PlayerInstanceFromIndex(i) if (!hPlayer || !hPlayer.IsValid() || hPlayer.IsBotOfType(1337)) continue hScope.tPlayerDamage[hPlayer.entindex()] <- 0 } local aClose = ["swiftchop_close_01","swiftchop_close_02","swiftchop_close_05","swiftchop_close_06"] local aLong = ["swiftchop_01","swiftchop_02","swiftchop_03","swiftchop_04"] local closeCache = [] local longCache = [] for (local i = 0; i < aClose.len(); ++i) closeCache.append(Marauder.GetMarauderSequenceByName(aClose[i])) for (local i = 0; i < aLong.len(); ++i) longCache.append(Marauder.GetMarauderSequenceByName(aLong[i])) hScope.swiftChopCloseSeqs <- closeCache hScope.swiftChopLongSeqs <- longCache hScope.hShotgun <- SpawnEntityFromTable("tf_wearable", {}) NetProps.SetPropInt(hScope.hShotgun , "m_nModelIndex", PrecacheModel("models/doom_eternal/demons/marauder_shotgun_prop.mdl")) NetProps.SetPropBool(hScope.hShotgun , "m_bValidatedAttachedEntity", true) hScope.hShotgun.SetTeam(hPlayer.GetTeam()) hScope.hShotgun.SetOwner(hPlayer) NetProps.SetPropEntity(hScope.hShotgun , "m_hOwnerEntity", hPlayer) NetProps.SetPropInt(hScope.hShotgun , "m_fEffects", NetProps.GetPropInt(hScope.hShotgun , "m_fEffects") & ~129) hScope.hShotgun.AcceptInput("SetParent", "!activator", hPlayer, null) hScope.hShotgun.AcceptInput("SetParentAttachment", "shotgun_holster2", hPlayer, null) HellOnEarth.DispatchParticleAttached(hPlayer, "marauder_axe_charged_glow", "axeM") hScope.AddThink("ShieldThink", function() { if (!self.IsAlive()) return if (!SHIELD_UP) return local now = Time() if (now - LAST_SHIELD_HIT_TIME >= SHIELD_IDLE_DROP_DELAY) { Marauder.ShieldState(self, false) GRACE_PERIOD_END = -1 } }) hScope.DecayAt <- function(idx, now) { local v = PD_VAL[idx] local dt = now - PD_TS[idx] if (dt > 0.0) { v -= DECAY_PER_SEC * dt if (v < 0.0) v = 0.0 PD_VAL[idx] = v PD_TS[idx] = now } return v } hScope.AddDamage <- function(idx, amt) { local now = Time() local v = DecayAt(idx, now) PD_VAL[idx] = v + amt PD_TS[idx] = now } hScope.GetDamage <- function(idx) { return DecayAt(idx, Time()) } hScope.ResetDamage <- function(idx) { PD_VAL[idx] = 0.0 PD_TS[idx] = Time() } hScope.MarauderTableThink <- function() { foreach (name, func in ThinkTable) { func.call(this) } return -1 } hScope.AddThink("MarauderAI", MarauderAI) AddThinkToEnt(hPlayer, "MarauderTableThink") } // EVENTS function ProcessEnt(hEnt) { if (!hEnt.IsBotOfType(1337)) return if (!hEnt.HasBotTag("bot_marauder")) return Marauder.Setup(hEnt) } function OnGameEvent_player_spawn(params) { local hEntity = GetPlayerFromUserID(params.userid) if (!hEntity || !hEntity.IsValid()) return EntFireByHandle(hEntity, "RunScriptCode", "Marauder.ProcessEnt(self)", -1, null, null) } function OnScriptHook_OnTakeDamage(params) { if (params.const_entity == null || params.attacker == null || params.attacker == params.const_entity) return local hVictim = params.const_entity if (!hVictim.IsPlayer() || !hVictim.IsBotOfType(TF_BOT_TYPE)) return if (hVictim.HasBotTag("bot_marauder")) { hVictim.ValidateScriptScope() local s = hVictim.GetScriptScope() local now = Time() s.LAST_SHIELD_HIT_TIME = now if (s.IS_VURNEABLE && params.attacker.GetTeam() == 3) { s.STUN_DNG_ACCUM += params.damage if (s.STUN_DMG_ACCUM <= 200) return HellOnEarth.PlaySoundFromEntity(hVictim, "ptx/sfx/demons/marauder/marauder_dazed.wav") hVictim.AddCondEx(71, 3, hVictim) s.SHIELD_UP = false s.IS_VURNEABLE = false s.STUN_DMG_ACCUM = 0 s.STUN_END_TIME = Time() + 4 hVictim.GetScriptScope().USE_SELECTOR = false DispatchParticleEffect("merasmus_dazed_bits", hVictim.EyePosition(), Vector(0, 0, 0)) EntFireByHandle(hVictim, "runscriptcode", "self.GetScriptScope().USE_SELECTOR = true", 5, null, null) } if (s.GRACE_PERIOD_END == -1) s.GRACE_PERIOD_END = Time() if (s.SHIELD_UP && hVictim.GetScriptScope().STUN_END_TIME < Time()) { local hAttacker = params.attacker local idx = hAttacker.GetEntityIndex() s.AddDamage(idx, params.damage) if (s.GetDamage(idx) > s.PUNISH_THRESHOLD) { s.ResetDamage(idx) local hAttacker = params.attacker local a = hVictim.GetCenter() + hVictim.EyeAngles().Forward() * 30 local b = hAttacker.GetCenter() local startAnchor = SpawnEntityFromTable("info_particle_system", { start_active = 0, origin = a }) startAnchor.SetAbsOrigin(a) local endAnchor = SpawnEntityFromTable("info_particle_system", { start_active = 0, origin = b }) endAnchor.SetAbsOrigin(b) local beam = SpawnEntityFromTable("info_particle_system", { effect_name = "marauder_lightning", start_active = 0, origin = a }) beam.AcceptInput("SetParent", "!activator", startAnchor, startAnchor) NetProps.SetPropEntityArray(beam, "m_hControlPointEnts", endAnchor, 0) NetProps.SetPropEntityArray(beam, "m_hControlPointEnts", endAnchor, 1) beam.AcceptInput("Start", null, null, null) local life = 0.25 EntFireByHandle(beam, "Kill", null, life, null, null) EntFireByHandle(startAnchor, "Kill", null, life, null, null) EntFireByHandle(endAnchor, "Kill", null, life, null, null) local hKillIcon = CreateByClassname("tf_weapon_bat") hKillIcon.DispatchSpawn() hKillIcon.KeyValueFromString("classname", "deflect_huntsman_flyingburn") printl((hAttacker.GetMaxHealth()*0.75).tointeger()) hAttacker.TakeDamageEx(hKillIcon, hVictim, null, Vector(), hVictim.GetCenter(), (hAttacker.GetMaxHealth()/2).tointeger(), DMG_PLASMA) EntFireByHandle(hKillIcon, "Kill", null, -1, null, null) HellOnEarth.PlaySoundFromEntity(hAttacker, "ptx/weapons/unmaykry_fire_2.wav", 2500) DispatchParticleEffect("argent_hit", hAttacker.GetCenter(), Vector()) } if (s.LAST_PARTICLE_TIME + 0.25 < Time()) { DispatchParticleEffect("miss_text", hVictim.EyePosition() + Vector(0,0,30), Vector()) s.LAST_PARTICLE_TIME = Time() } params.damage = 0 return } if (params.attacker.GetTeam() == Constants.ETFTeam.TF_TEAM_BLUE && params.damage > 0) { s.EVADE_DAMAGE_ACCUM += params.damage.tofloat() if ((now - s.EVADE_LAST_TIME) >= s.EVADE_COOLDOWN && s.EVADE_DAMAGE_ACCUM >= s.EVADE_THRESHOLD && !hVictim.InCond(71)) { if (s.tCurrentSeq) return Marauder.NoStrafeUnlock(hVictim) if (Marauder.Evade(hVictim) == 1) { s.EVADE_LAST_TIME = now s.EVADE_DAMAGE_ACCUM = 0.0 s.NextAttackAllowedAt = now + 0.5 return } } } if (s.tCurrentSeq) return if (!s.SHIELD_UP && (s.GRACE_PERIOD_END != -1 && now >= s.GRACE_PERIOD_END + s.SHIELD_GRACE_PERIOD) && s.STUN_END_TIME < Time()) { Marauder.ShieldState(hVictim, true) params.damage = 0 } return } } } ::MarauderAI <- function() { if (self.GetTeam() == Constants.ETFTeam.TEAM_SPECTATOR) { NetProps.SetPropString(self, "m_iszScriptThinkFunction", "") local s0 = self.GetScriptScope() if ("hShotgun" in s0 && s0.hShotgun && s0.hShotgun.IsValid()) s0.hShotgun.Kill() self.TerminateScriptScope() return } local info = Marauder.GetThreat(self) local target = info[0] local d2 = info[1] local vis = info[2] if (!target) return local s = self.GetScriptScope() if (s.tCurrentSeq) return -1 local now = Time() if (now < s.LastDecision + s.DecisionInterval) return s.LastDecision = now local spec = Marauder.SelectAttack(self, d2.tofloat(), vis) if (!spec) return -1 Marauder.ExecuteAttack(self, spec) } // TESTING PURPOSE FUNCTION //::MarauderAI <- function() { if (self.GetTeam() == Constants.ETFTeam.TEAM_SPECTATOR) { NetProps.SetPropString(self, "m_iszScriptThinkFunction", ""); hShotgun.Kill(); self.TerminateScriptScope() } local info = Marauder.GetThreat(self); local target = info[0]; local d2 = info[1]; local vis = info[2]; if (!target) return; if (!tCurrentSeq && target && vis) { local slamRangeSqr = Marauder.Combat.BIG_SLAM_AXE_RANGE * Marauder.Combat.BIG_SLAM_AXE_RANGE; if (d2 <= slamRangeSqr) { Marauder.Summon_Atk(self) } } } __CollectGameEventCallbacks(::Marauder)