local function removeCallbacks(player, callbacks) if not IsValid(player) then return end for _, callbackId in pairs(callbacks) do player:RemoveCallback(callbackId) end end local TELEPORT_NODE_VECTORS = { Vector(-5062, 4441.02, 800.35), Vector(-5062, 3709.02, 800.35), Vector(-108.08, 10.74, 288.98), Vector(-768.42, -1.98, 288.98), Vector(688.89, -74.24, 288.98), Vector(597.83, 358.67, 355), Vector(597.83, 748.68, 355), Vector(755.58, 1116.92, 419.2), Vector(417.68, 1138.72, 419.2), Vector(91.54, 1099.91, 419.2), Vector(177.73, 459, 419.2), Vector(-79.94, 500.18, 419.2), Vector(-370.31, 536.29, 419.2), Vector(112.28, 757.34, 419.2), Vector(-720.72, 1439.33, 328.03), Vector(-1335.29, 1166.83, 446.67), Vector(-1491.3, 1665.7, 476.28), Vector(-1491.3, 2282.4, 476.28), Vector(-1166.04, 1783.79, 467.3), Vector(-1166.04, 2152.15, 467.3), Vector(-816.08, 2392.37, 555.3), Vector(-732.82, 2392.37, 555.3), Vector(-784.83, 2816.94, 555.3), Vector(-772.71, 3333.64, 555.3), Vector(-580.46, 3668.54, 555.3), Vector(-1222.89, 3692.71, 555.3), Vector(-1132.85, 3234.52, 618.1), Vector(-1455.44, 3254.61, 618.1), Vector(-1455.44, 3675, 667.02), Vector(-2155.03, 3551.49, 476.28), Vector(-2555.87, 3356.44, 476.28), Vector(-3032.56, 3050.15, 555.3), Vector(-3028.88, 3500.47, 476.28), Vector(-2553.95, 3581.79, 476.28), Vector(-2075.97, 3887.96, 476.28), Vector(-2395.48, 3887.96, 476.28), Vector(-2395.48, 4226.32, 476.28), Vector(-1957.34, 4226.32, 476.28), Vector(-2253.49, 4541.35, 476.28), Vector(-1760.14, 4541.35, 476.28), Vector(-2532.65, 4165.79, 555.3), Vector(-2532.65, 3988.32, 555.3), Vector(-2532.65, 3795.77, 555.3), Vector(-3202.06, 3390.79, 476.28), Vector(-3202.06, 3778.9, 476.28), Vector(-3202.06, 4143.36, 476.28), Vector(-3202.06, 4419.65, 555.3), Vector(-3202.06, 4674.13, 555.3), Vector(-3587.7, 4866.06, 667.02), Vector(-3587.7, 4577.96, 667.02), Vector(-3955.48, 4577.96, 724.51), Vector(-4044.16, 4084.79, 724.51), Vector(-4044.16, 3538.81, 724.51), Vector(-4044.16, 3353.88, 789.46), Vector(-460.99, 4076.37, 535.3), Vector(-815.23, 4180.11, 535.3), Vector(-836.46, 3955.74, 535.3), Vector(-1072.72, 3985.84, 535.3), Vector(-1114.01, 4314.42, 535.3), Vector(-613.04, 4633.99, 535.3), Vector(49.79, 4486.96, 535.3), Vector(-271.41, 4501.6, 535.3), Vector(-271.41, 4289.6, 535.3), Vector(173.48, 4289.6, 535.3), Vector(-241.10, 3925.36, 535.3), Vector(375.2, 3896.01, 535.3), Vector(92.13, 3105.17, 716.11), Vector(92.13, 2523.62, 716.11), Vector(92.13, 2842.84, 716.11), Vector(-704, 482.66, 419.2), Vector(594.35, 3720.13, 716.11), Vector(110, 3542.75, 533.27), Vector(476.86, 2784.48, 581.78), Vector(-254.37, 2784.48, 581.78), Vector(-1816.27, 2558.08, 493.17), Vector(-796.36, -167.41, 269.26), Vector(-253.8, -208.54, 269.26), Vector(590.51, -256.33, 269.26), Vector(592.33, -643.10, 269.26), Vector(-292, 3631, 652.26), Vector(-292, 2918, 652.26), Vector(-384, 2063, 730.26), Vector(94.73, 2786.7, 275.57), } local TELEPORT_NODE_VECTORS_PREVIEW = { Vector(-5062, 4441.02, 587.35), Vector(-5062, 3709.02, 587.35), Vector(-108.08, 10.74, 125.37), Vector(-768.42, -1.98, 113.37), Vector(688.89, -74.24, 104.63), Vector(597.83, 358.67, 189.12), Vector(597.83, 748.68, 285.26), Vector(755.58, 1116.92, 281.49), Vector(417.68, 1138.72, 281.49), Vector(91.54, 1099.91, 281.49), Vector(177.73, 459, 281.49), Vector(-79.94, 500.18, 281.49), Vector(-370.31, 536.29, 281.49), Vector(112.28, 757.34, 281.49), Vector(-720.72, 1439.33, 295.03), Vector(-1335.29, 1166.83, 274.42), Vector(-1491.3, 1665.7, 340.87), Vector(-1491.3, 2282.4, 407.41), Vector(-1166.04, 1783.79, 406.3), Vector(-1166.04, 2152.15, 406.3), Vector(-816.08, 2392.37, 405.65), Vector(-732.82, 2392.37, 405.65), Vector(-784.83, 2816.94, 405.65), Vector(-772.71, 3333.64, 405.65), Vector(-580.46, 3668.54, 405.65), Vector(-1222.89, 3692.71, 422.3), Vector(-1132.85, 3234.52, 491.1), Vector(-1455.44, 3254.61, 511.1), Vector(-1455.44, 3675, 565.02), Vector(-2155.03, 3551.49, 275.28), Vector(-2555.87, 3356.44, 275.28), Vector(-3032.56, 3050.15, 318.3), Vector(-3028.88, 3500.47, 275.28), Vector(-2553.95, 3581.79, 275.28), Vector(-2075.97, 3887.96, 275.28), Vector(-2395.48, 3887.96, 275.28), Vector(-2395.48, 4226.32, 275.28), Vector(-1957.34, 4226.32, 275.28), Vector(-2253.49, 4541.35, 275.28), Vector(-1760.14, 4541.35, 275.28), Vector(-2532.65, 4165.79, 318.3), Vector(-2532.65, 3988.32, 318.3), Vector(-2532.65, 3795.77, 318.3), Vector(-3202.06, 3390.79, 375.28), Vector(-3202.06, 3778.9, 375.28), Vector(-3202.06, 4143.36, 375.28), Vector(-3202.06, 4419.65, 318.3), Vector(-3202.06, 4674.13, 318.3), Vector(-3587.7, 4866.06, 540.02), Vector(-3587.7, 4577.96, 540.02), Vector(-3955.48, 4577.96, 609.51), Vector(-4044.16, 4084.79, 609.51), Vector(-4044.16, 3538.81, 609.51), Vector(-4044.16, 3353.88, 609.51), Vector(-460.99, 4076.37, 365.66), Vector(-815.23, 4180.11, 365.66), Vector(-836.46, 3955.74, 365.66), Vector(-1072.72, 3985.84, 365.66), Vector(-1114.01, 4314.42, 292), Vector(-613.04, 4633.99, 254.66), Vector(49.79, 4486.96, 292), Vector(-271.41, 4501.6, 292), Vector(-271.41, 4289.6, 292), Vector(173.48, 4289.6, 292), Vector(-241.10, 3925.36, 292), Vector(375.2, 3896.01, 292), Vector(92.13, 3105.17, 488.11), Vector(92.13, 2523.62, 488.11), Vector(92.13, 2842.84, 488.11), Vector(-704, 482.66, 254.2), Vector(594.35, 3720.13, 454.11), Vector(110, 3542.75, 276.27), Vector(476.86, 2784.48, 286.78), Vector(-254.37, 2784.48, 286.78), Vector(-1816.27, 2558.08, 384.17), Vector(-796.36, -167.41, 69.52), Vector(-253.8, -208.54, 75.84), Vector(590.51, -256.33, 77.61), Vector(592.33, -643.10, 86.71), Vector(-292, 3631, 315.13), Vector(-292, 2918, 269.26), Vector(-384, 2063, 348.18), Vector(94.73, 2786.7, 275.57), } --Teleport to all vectors, announce when you have done so function teleportDiagnostics(_, activator) for index, Node in ipairs(TELEPORT_NODE_VECTORS) do --stay at each node for 2 seconds, adds the index to it because these timers will all be spawned within 1 tick. timer.Simple((0.5 + (index * 0.5)), function() activator:SetAbsOrigin(Node) end) end end local function getTargetTeamAggregatePosition(activator, targetTeamNumPrimary, targetTeamNumSecondary) local allPlayers = ents.GetAllPlayers() --Origin of the boss local activatorOrigin = activator:GetAbsOrigin() --Table to contain the players local targetTeam = {} --Closest player to the boss, will be used to determine who the outliers are local targetTeamFulcrum --Final calculation, the aggregate position of the target team. local targetTeamAggregatePosition = Vector(0,0,0) --Pick out our target players for _, player in pairs(allPlayers) do if player.m_iTeamNum == targetTeamNumPrimary or player.m_iTeamNum == targetTeamNumSecondary then --Necessary because 2 of the three possible teamnum set ups include spec, this also prevents edge-cases --where the boss will teleport around dead players. if player:IsAlive() == true then table.insert(targetTeam, player) end end end --Necessary, because it is possible to get an empty targetTeam table in the event of a teamwipe. --This is more graceful than hitting an error when it comes time to pick the fulcrum. if next(targetTeam) == nil then return nil end --PrintTable(targetTeam) --Find whoever is closest to the boss, they will be the base we use to determine outliers local lastDistance = 9999 for _, player in pairs(targetTeam) do local playerOrigin = player:GetAbsOrigin() local playerDistance = playerOrigin:Distance(activatorOrigin) if playerDistance < lastDistance then targetTeamFulcrum = player lastDistance = playerDistance end end local fulcrumOrigin = targetTeamFulcrum:GetAbsOrigin() --How many players have been added to the giga vector, if targetTeam is empty you are in deep shit local accountedPlayers = 0 for _, player in pairs(targetTeam) do --The fulcrum player themselves will also be factored into this, if they are the only non-outlier then --the aggregate will be placed on them, rather than it going to 0,0,0. local playerOrigin = player:GetAbsOrigin() if playerOrigin:Distance(fulcrumOrigin) <= 600 then accountedPlayers = accountedPlayers + 1 targetTeamAggregatePosition[1] = targetTeamAggregatePosition[1] + playerOrigin[1] targetTeamAggregatePosition[2] = targetTeamAggregatePosition[2] + playerOrigin[2] end end --Average the position of all non-outliers to get the aggregate position of red team. targetTeamAggregatePosition = Vector((targetTeamAggregatePosition[1] / accountedPlayers),(targetTeamAggregatePosition[2] / accountedPlayers),0) return targetTeamAggregatePosition end --This function will take the provided activator, get an aggregate position of red team, then teleport the activator to a node that is --minDistanceFromSelf hammer units away from where they are, minDistanceToPlayer hammer units away from the team's aggregated position, --bounded by maxDistanceToPlayer hammer units away from the aggregated position of the team. If these three conditions cannot be satisfied, --no teleportation will occur. local function snapTeleportAroundPlayers(activator, targetTeamAggregatePosition, minDistanceFromSelf, maxDistanceToPlayer, minDistanceToPlayer, targetTeamNumPrimary, targetTeamNumSecondary) local targetTeamAggregatePosition = getTargetTeamAggregatePosition(activator, targetTeamNumPrimary, targetTeamNumSecondary) local activatorOrigin = activator:GetAbsOrigin() local activatorOriginNoVertical = Vector(activatorOrigin[1], activatorOrigin[2], 0) local successfulTeleport = false for index, Node in ipairs(TELEPORT_NODE_VECTORS) do local nodeNoVertical = Vector(Node[1], Node[2], 0) local nodeToTeamDistance = nodeNoVertical:Distance(targetTeamAggregatePosition) if nodeNoVertical:Distance(activatorOriginNoVertical) >= minDistanceFromSelf and nodeToTeamDistance <= maxDistanceToPlayer and nodeToTeamDistance >= minDistanceToPlayer then --This will stop the boss from teleporting behind someone and "nothing personal kid"-ing them to the respawn screen activator:SetAttributeValue("no_attack", 1) --This does not slow down the boss at all, but it is necessary to stop the boss from teleporting behind someone with a full charge, --then instakilling them with a shield bash. activator:StunPlayer(1.5, 0.5, TF_STUNFLAG_SLOWDOWN) --Cosmetic effects begin here activator:AddCond(6, 2) local allPlayers = ents.GetAllPlayers() for _, player in pairs(allPlayers) do player:AcceptInput("$PlaySoundToSelf", "weapons/teleporter_send.wav") player:AcceptInput("$PlaySoundToSelf", "=65|misc/halloween/merasmus_spell.wav") end local teleParticle local teleParticleBeginning = ents.CreateWithKeys("info_particle_system", { effect_name = "wrenchmotron_teleport_beam", start_active = 1, flag_as_weather = 0, }, true, true) local teleParticleDestination = ents.CreateWithKeys("info_particle_system", { effect_name = "wrenchmotron_teleport_beam", start_active = 1, flag_as_weather = 0, }, true, true) --If we're not red, use a blue teleport in effect, because grey does not have one. if activator.m_iTeamNum ~= 2 then teleParticle = ents.CreateWithKeys("info_particle_system", { effect_name = "teleportedin_blue", start_active = 1, flag_as_weather = 0, }, true, true) else teleParticle = ents.CreateWithKeys("info_particle_system", { effect_name = "teleportedin_blue", start_active = 1, flag_as_weather = 0, }, true, true) end teleParticle:SetAbsOrigin(Node) teleParticle:Start() teleParticleBeginning:SetAbsOrigin(activatorOrigin) teleParticleBeginning:Start() teleParticleDestination:SetAbsOrigin(Node) teleParticleDestination:Start() --Cosmetic effects end here timer.Simple(1.5, function() activator:SetAttributeValue("no_attack", 0) end) activator:RemoveCond(17) activator:SetAbsOrigin(Node) successfulTeleport = true break end end return successfulTeleport end --Function responsible for setting the values of special attacks, returns a table --that the meta clause within zweiBossTeleLoop then sets the special values to, metaInterval --becomes a time taken to return to normal. --Takes the existing teleport parameters and metaInterval so they can be reset later. --If you were to port this set up to another boss, you could pass activator to directly change the boss' --attributes or apply conds and whatnot. local function bossSpecialAttack(specialAttackIndex, minDistanceFromSelf, maxDistanceFromPlayers, minDistanceFromPlayers, teleportInterval, metaInterval) if specialAttackIndex < 2 then return nil end --Table the main loop will be handed, goes in the same order as this function's parameters, with the first 5 entries being the changed results --and the last 5 entries being the reset results local returnTable = {} if specialAttackIndex == 2 then --very long distance teleport. returnTable = { 400, 1200, 600, 12, 1, minDistanceFromSelf, maxDistanceFromPlayers, minDistanceFromPlayers, teleportInterval, metaInterval, } elseif specialAttackIndex == 3 then --teleport panic --minimum distance from self is double that as min distance from players --to make it much more likely for him to land behind or off to the side of the team. --This has extra effects because it is significnatly more dangerous than a longer than usual snap teleport. allPlayers = ents.GetAllPlayers() for _, player in pairs(allPlayers) do player:AcceptInput("$PlaySoundToSelf", "vo/mvm/mght/demoman_mvm_m_battlecry05") player:AcceptInput("$PlaySoundToSelf", "weapons/cow_mangler_over_charge_shot.wav") player:AcceptInput("$DisplayTextChat", "{FF0000}WARNING: TELEPORT RUSH INCOMING") end local warnGameText = ents.FindByName("tele_rush_warn_text") warnGameText:AcceptInput("Display") returnTable = { 300, 600, 300, 1.65, 5, minDistanceFromSelf, maxDistanceFromPlayers, minDistanceFromPlayers, teleportInterval, metaInterval, } end return returnTable end function zweiBossTeleLoop(_, activator) local callbacks = {} local check local terminated = false --We have two teamnum vars because grey team exists, despite the naming convention, one does not take priority over the other. --Though it will be listed first in the teleport function to short circuit the AND logic and make the perf impact of its existence --slightly less significant, though it is barely existent as-is. If you are rewriting this and will be using more greys than reds or blues as enemies, --it might be wise to flip them for that reason. I would not recommend using greys as the main enemy to begin with, but that's your bad decision to make. local targetTeamNumPrimary local targetTeamNumSecondary --If we're red, our primary target is blue, and our secondary is grey if activator.m_iTeamNum == 2 then targetTeamNumPrimary = 3 targetTeamNumSecondary = 1 --If we're blue, our primary target is red, and our secondary is grey. elseif activator.m_iTeamNum == 3 then targetTeamNumPrimary = 2 targetTeamNumSecondary = 1 --If we're grey or the fucky halloween team, if that ever gets supported, blue is our primary and red is our secondary. --Blue is the primary because in most missions there will be more blues than reds in a given server. else targetTeamNumPrimary = 3 targetTeamNumSecondary = 2 end --These are variables rather than hard constants so they can be manipulated by the boss' phases and special attacks --Interval between teleports, pre teleport warning occurs 1.6 seconds before this, and never occurs if the interval is less than that local teleportInterval = 6 --Interval between special attacks local metaInterval = 6 local minDistanceFromSelf = 400 local maxDistanceFromPlayers = 700 local minDistanceFromPlayers = 200 --I had this updating once every tick, this caused a particle leak that crashes both all clients and the server. local previewRefreshInterval = 0.75 --Evaluated here so changing the boss' health is not a pain in the ass. local phaseTwoHealthBreakpoint = activator.m_iHealth * (2/3) local phaseThreeHealthBreakpoint = activator.m_iHealth * (1/3) --This variable is set to the phase number that the boss has recently changed to, in order --to prevent those clauses from spamming over potential alterations made by meta effects and what not. --Is also used to determine which special attack to use. local phaseUpdateComplianceLevel = 0 --This table is filled in by special attacks, first 5 entries are the new stats, last 5 --are the old stats. local specialAttackTable = {} --Flag used to determine which section of the special attack table to reference. local metaReturnsToNormal = false --Multiplier applied to teleportInterval if the red fulcrum is more than a certain distance away from the boss local teleportIntervalDistanceMultiplier = 1 local function terminate() if terminated then return end terminated = true timer.Stop(check) removeCallbacks(activator, callbacks) end --This is a flag to stop the early teleport warning from earraping the players local timeIntervalBucketWarningHeard = false --Used when deciding to teleport, usually every 4-5 seconds, possibly longer. local timeIntervalBucket = 0 --Used when choosing to perform special attacks that would compromise the integrity of timeIntervalBucket, like a teleport fever --that changes teleportInterval to 0.5 local timeIntervalBucketMeta = 0 --Used to determine when to preview the boss' teleports local timeIntervalBucketPreview = 0 --Multiplies maxDistanceFromPlayers, if many consecutive failed teleports occur, this will eventually force the boss --to teleport away. local failedTeleportDistanceMultiplier = 1 check = timer.Create(0.015, function() if not IsValid(activator) or not activator:IsAlive() then terminate() return end if activator:GetAttributeValue("no_attack", false) == 1 and activator:InCond(6) ~= true then activator:SetAttributeValue("no_attack", 0) end --Management if statement for phases if phaseUpdateComplianceLevel < 2 and activator.m_iHealth <= phaseTwoHealthBreakpoint then --More frequent, further teleports that are more likely to land at side and back angles minDistanceFromSelf = 600 maxDistanceFromPlayers = 800 minDistanceFromPlayers = 300 teleportInterval = 5.5 metaInterval = 8 phaseUpdateComplianceLevel = 2 elseif phaseUpdateComplianceLevel < 3 and activator.m_iHealth <= phaseThreeHealthBreakpoint then --Bold, very frequent teleports that are more likely to snap the boss a short distance away. --Special attack cooldown is longer because it is very significant. minDistanceFromSelf = 500 maxDistanceFromPlayers = 500 minDistanceFromPlayers = 200 teleportInterval = 4.5 metaInterval = 15 phaseUpdateComplianceLevel = 3 end timeIntervalBucket = timeIntervalBucket + 0.015 timeIntervalBucketMeta = timeIntervalBucketMeta + 0.015 timeIntervalBucketPreview = timeIntervalBucketPreview + 0.015 if (teleportInterval - timeIntervalBucket) <= 1.6 and timeIntervalBucketWarningHeard == false then --If we are teleporting fast enough that there isn't room for the teleporter sound, we don't need to hear it. if teleportInterval - timeIntervalBucket > 0 then local allPlayers = ents.GetAllPlayers() activator:AddCond(6, 1) --amputator ring effect, for added visual clarity activator:AddCond(20, 0.75) for _, player in pairs(allPlayers) do player:AcceptInput("$PlaySoundToSelf", "weapons/teleporter_ready.wav") timer.Simple(0.5, function() player:AcceptInput("$PlaySoundToSelf", "weapons/det_pack_timer.wav") end) timer.Simple(1, function() player:AcceptInput("$PlaySoundToSelf", "weapons/det_pack_timer.wav") end) timer.Simple(1.5, function() player:AcceptInput("$PlaySoundToSelf", "weapons/det_pack_timer.wav") end) --player:AcceptInput("$PlaySoundToSelf", "weapons/teleporter_receive.wav") end end timeIntervalBucketWarningHeard = true end if timeIntervalBucket >= (teleportInterval - 0.8) then timeIntervalBucket = 0 timeIntervalBucketWarningHeard = false --If our teleports are slower than 1.2 seconds, spend some time doing a cool sword draw and swing animation --before we teleport, otherwise, just teleport without it if teleportInterval - 0.8 > 1 then activator:SetAttributeValue("deploy time increased", 0.01) activator:WeaponSwitchSlot(2) activator:RunScriptCode("activator.PressFireButton(1)", activator) activator:SetAttributeValue("disable weapon switch", 1) timer.Simple(0.2, function() activator:RunScriptCode("activator.PressFireButton(1)", activator) activator:SetAttributeValue("gesture speed increase", 0.1) activator:SetAttributeValue("fire rate bonus", 10) end) timer.Simple(0.8, function() activator:SetAttributeValue("disable weapon switch", 0) activator:SetAttributeValue("fire rate bonus", 1) activator:SetAttributeValue("gesture speed increase", 1) activator:SetAttributeValue("deploy time increased", 1) local targetTeamAggregatePosition = getTargetTeamAggregatePosition(activator, targetTeamNumPrimary, targetTeamNumSecondary) local teleportSuccess = snapTeleportAroundPlayers(activator, targetTeamAggregatePosition, minDistanceFromSelf, (maxDistanceFromPlayers * failedTeleportDistanceMultiplier), minDistanceFromPlayers, targetTeamNumPrimary, targetTeamNumSecondary) if not teleportSuccess then failedTeleportDistanceMultiplier = failedTeleportDistanceMultiplier + 2 --Apply these penalties on a failed teleport to stop him from baiting people into melee range so he can --sodomize them with his serrated claymore. activator:SetAttributeValue("no_attack", 1) activator:StunPlayer(1.5, 0.5, TF_STUNFLAG_SLOWDOWN) timer.Simple(1.5, function() activator:SetAttributeValue("no_attack", 0) end) timer.Simple(2, function() activator:SetAttributeValue("no_attack", 0) end) else failedTeleportDistanceMultiplier = 1 end timeIntervalBucket = 0 end) else timer.Simple(0.8, function() local targetTeamAggregatePosition = getTargetTeamAggregatePosition(activator, targetTeamNumPrimary, targetTeamNumSecondary) local teleportSuccess = snapTeleportAroundPlayers(activator, targetTeamAggregatePosition, minDistanceFromSelf, (maxDistanceFromPlayers * failedTeleportDistanceMultiplier), minDistanceFromPlayers, targetTeamNumPrimary, targetTeamNumSecondary) if not teleportSuccess then --We do not apply the post teleport penalties on failed quick teleports because there --isn't an animation that could potentially fool players. failedTeleportDistanceMultiplier = failedTeleportDistanceMultiplier + 1 else failedTeleportDistanceMultiplier = 1 end timeIntervalBucket = 0 end) end end --If we've hit our meta interval, either revert to normal or call upon our special attack teleport params, if we have no special attack params for this phase, do nothing. if timeIntervalBucketMeta >= metaInterval then timeIntervalBucketMeta = 0 if metaReturnsToNormal == true then --metaReturnsToNormal will never be true if specialAttackTable is nil, --this is because it is only ever set to true if the table was valid to begin with. --No nil safety check needed here. minDistanceFromSelf = specialAttackTable[6] maxDistanceFromPlayers = specialAttackTable[7] minDistanceFromPlayers = specialAttackTable[8] teleportInterval = specialAttackTable[9] metaInterval = specialAttackTable[10] metaReturnsToNormal = false else --We need to check if metaReturnsToNormal is true before we do this so special attack values do not permanently corrupt normal values. specialAttackTable = bossSpecialAttack(phaseUpdateComplianceLevel, minDistanceFromSelf, maxDistanceFromPlayers, minDistanceFromPlayers, teleportInterval, metaInterval) if specialAttackTable ~= nil then minDistanceFromSelf = specialAttackTable[1] maxDistanceFromPlayers = specialAttackTable[2] minDistanceFromPlayers = specialAttackTable[3] teleportInterval = specialAttackTable[4] metaInterval = specialAttackTable[5] metaReturnsToNormal = true end end end if timeIntervalBucketPreview >= previewRefreshInterval then timeIntervalBucketPreview = 0 local activatorOrigin = activator:GetAbsOrigin() local activatorOriginNoVertical = Vector(activatorOrigin[1], activatorOrigin[2], 0) local targetTeamAggregatePosition = getTargetTeamAggregatePosition(activator, targetTeamNumPrimary, targetTeamNumSecondary) if targetTeamAggregatePosition:Distance(activatorOriginNoVertical) > 900 and timeIntervalBucketWarningHeard == false then if (timeIntervalBucket * 2) >= (teleportInterval - 1.6) then timeIntervalBucket = teleportInterval - 1.6 elseif timeIntervalBucketWarningHeard == false then timeIntervalBucket = timeIntervalBucket * 2 end end for index, Node in ipairs(TELEPORT_NODE_VECTORS_PREVIEW) do local nodeNoVertical = Vector(Node[1], Node[2], 0) if targetTeamAggregatePosition ~= nil then local nodeToTeamDistance = nodeNoVertical:Distance(targetTeamAggregatePosition) if nodeNoVertical:Distance(activatorOriginNoVertical) >= minDistanceFromSelf and nodeToTeamDistance <= (maxDistanceFromPlayers * failedTeleportDistanceMultiplier) and nodeToTeamDistance >= minDistanceFromPlayers then local nodeOriginRetardedSyntax = "" .. Node[1] .. " " .. Node[2] .. " " .. Node[3] .. "" local teleWarnParticle = ents.CreateWithKeys("prop_dynamic",{ ["disableshadows"] = "1", ["model"] = "models/props_mvm/indicator/indicator_circle.mdl", ["defaultanim"] = "start", --["model"] = "models/bots/scout_boss/bot_scout_boss.mdl", ["origin"] = nodeOriginRetardedSyntax, ["modelscale"] = 1.75, ["skin"] = 1, }) local teleWarnParticle1 = ents.CreateWithKeys("info_particle_system", { effect_name = "medic_healradius_blue_buffed", start_active = 1, flag_as_weather = 0, }, true, true) teleWarnParticle1:SetAbsOrigin(Node) teleWarnParticle1:Start() timer.Simple(previewRefreshInterval, function() teleWarnParticle:Remove() teleWarnParticle1:Remove() end) end end end end end, 0) callbacks.died = activator:AddCallback(ON_DEATH, function() terminate() end) callbacks.removed = activator:AddCallback(ON_REMOVE, function() terminate() end) callbacks.spawned = activator:AddCallback(ON_SPAWN, function() terminate() end) end function debugAttack() print("I attacked") end