function InSetup() { return GetPropBool(tf_gamerules, "m_bInSetup") } function FindPlayersInRadiusAndLoS(origin, radius, team = 0) { local players = [] foreach(player in GetAlivePlayers(team)) if ((origin - player.GetOrigin()).Length() <= radius) players.push(player) return players } function FindEnemiesInRadius(origin, radius, myTeam, includeNonPlayers = 2) { local enemies = [] foreach(player in GetAlivePlayers()) if (player.GetTeam() != myTeam && (origin - player.GetOrigin()).Length() <= radius) enemies.push(player) if (includeNonPlayers > 0) for (local building = null; building = FindByClassnameWithin(building, "obj_*", origin, radius);) if (building.GetTeam() != myTeam) enemies.push(building) if (includeNonPlayers > 1) { for (local npc = null; npc = FindByClassnameWithin(npc, "base_boss", origin, radius);) if (npc.GetTeam() != myTeam) enemies.push(npc) for (local npc = null; npc = FindByClassnameWithin(npc, "tf_zombie", origin, radius);) if (npc.GetTeam() != myTeam) enemies.push(npc) } return enemies } function CreateAoE(origin, radius, applyFunc) { foreach(target in GetAlivePlayers()) { local normalVector = target.GetOrigin() - origin local distance = normalVector.Norm() if (distance <= radius) applyFunc(target, normalVector, distance) } for (local target = null; target = FindByClassname(target, "obj_*");) { local normalVector = target.GetOrigin() - origin local distance = normalVector.Norm() if (distance <= radius) applyFunc(target, normalVector, distance) } } function CreateAoEAABB(origin, min, max, applyFunc) { local min = origin + min local max = origin + max foreach(target in GetAlivePlayers()) { local targetOrigin = target.GetCenter() if (targetOrigin.x >= min.x && targetOrigin.y >= min.y && targetOrigin.z >= min.z && targetOrigin.x <= max.x && targetOrigin.y <= max.y && targetOrigin.z <= max.z) { local normalVector = targetOrigin - origin applyFunc(target, normalVector, normalVector.Norm()) } } for (local target = null; target = FindByClassname(target, "obj_*");) { local targetOrigin = target.GetCenter() if (targetOrigin.x >= min.x && targetOrigin.y >= min.y && targetOrigin.z >= min.z && targetOrigin.x <= max.x && targetOrigin.y <= max.y && targetOrigin.z <= max.z) { local normalVector = targetOrigin - origin applyFunc(target, normalVector, normalVector.Norm()) } } } function clampCeiling(valueA, valueB) { if (valueA < valueB) return valueA return valueB } min <- clampCeiling function clampFloor(valueA, valueB) { if (valueA > valueB) return valueA return valueB } max <- clampFloor function clamp(value, min, max) { if (max < min) { local tmp = min min = max max = tmp } if (value < min) return min if (value > max) return max return value } function safeget(table, field, defValue) { return table && field in table ? table[field] : defValue } function safegetfunction(table, functionName, defValue) { return table && functionName in table ? table[functionName].bindenv(table) : defValue } function IsVectorInside(vector, min, max) { return vector.x >= min.x && vector.x <= max.x && vector.y >= min.y && vector.y <= max.y && vector.z >= min.z && vector.z <= max.z } function IsValidEntity(entity) { return entity && entity.IsValid() } function ShuffleArrayCopy(arrayOriginal) { local arrayCopy = arrayOriginal.slice(0) local currentIndex = arrayCopy.len() - 1 local swap local randomIndex while (currentIndex > 0) { randomIndex = RandomInt(0, currentIndex) currentIndex -= 1 swap = arrayCopy[currentIndex] arrayCopy[currentIndex] = arrayCopy[randomIndex] arrayCopy[randomIndex] = swap } return arrayCopy } function RandomElement(array) { local len = array.len() return len > 0 ? array[RandomInt(0, len - 1)] : null } function combinetables(tableA, tableB) { if (tableB) foreach(k, v in tableB) tableA[k] <- v return tableA } function AreArraysEqual(array1, array2) { local len1 = array1.len() if (len1 != array2.len()) return false for (local i = 0; i < len1; i++) if (array1[i] != array1[i]) return false return true } function KillIfValid(entity) { if (entity && entity.IsValid()) entity.Kill() return null } function GetItemId(item) { return GetPropInt(item, "m_AttributeManager.m_Item.m_iItemDefinitionIndex") } function PrecacheModelWithGibs(model) { PrecacheEntityFromTable( { classname = "tf_generic_bomb", model = model }) } function PrecacheMaterial(material) { local tmpEnt = SpawnEntityFromTable("vgui_screen", { overlaymaterial = material width = 0 height = 0 }) local index = GetPropInt(tmpEnt, "m_nOverlayMaterial") tmpEnt.Kill() return index } function GetAttribute(item, name, defValue) { return item ? item.GetAttribute(name, defValue) : defValue } local lastTimePlayedDamageResSound = player_table() function PlayDamageResistSound(player, reductionRate) { local time = Time() if (player in lastTimePlayedDamageResSound && time - lastTimePlayedDamageResSound[player] <= 0.1) return EmitSoundEx( { sound_name = reductionRate >= 0.75 ? "Player.ResistanceLight" : reductionRate <= 0.25 ? "Player.ResistanceHeavy" : "Player.ResistanceMedium", entity = player, volume = 1 }) lastTimePlayedDamageResSound[player] <- time } function CreatePersonalModel(player, model) { local personalModel = CreateByClassname("obj_teleporter") personalModel.DispatchSpawn() personalModel.SetModel(model) personalModel.AddEFlags(EFL_NO_THINK_FUNCTION) personalModel.SetSolid(SOLID_NONE) SetPropBool(personalModel, "m_bPlacing", true) SetPropInt(personalModel, "m_fObjectFlags", 2) SetPropEntity(personalModel, "m_hBuilder", player) personalModel.AcceptInput("DisableShadow", null, null, null) return personalModel } function PlayerToString(player) { if (!player) return "null-player" return format("%s{`%s` #%d `%s` $%d}", player ? player.tostring() : "null", GetPropString(player, "m_szNetname"), GetUserID(player), GetPropString(player, "m_szNetworkIDString"), player && player.IsPlayer() ? player.GetPlayerClass() : "-") } function TableToString(table, recursive = false, padding = " ") { if (!table) return "" local string = table + "@" + table.len() + " {" foreach(k, v in table) { if (recursive && (typeof(v) == "table" || typeof(v) == "array")) v = TableToString(v, true, padding + " ") string += "\n" + padding + padding + k + " -> " + v } string += "\n" + padding + "}" return string } function InstanceToString(instance) { return "//todo " + instance } function FindKeyOfMaxValue(table) { local maxKey = null local maxVal = null foreach(key, value in table) { if (maxKey == null || value > maxVal) { maxVal = value maxKey = key } } return maxKey } function FindKeyOfMinValue(table) { local minKey = null local minVal = null foreach(key, value in table) { if (minKey == null || value < minVal) { minVal = value minKey = key } } return minKey } function GetClassnameSafe(entity) { if (!entity || !entity.IsValid()) return null return entity.GetClassname() } function IsSpaceFree(location, player, mask = MASK_VISIBLE_AND_NPCS_OR_CONTENTS_MOVEABLE) { local traceTable = { start = location, end = location, hullmin = player.GetPlayerMins(), hullmax = player.GetPlayerMaxs(), ignore = player, mask = mask } TraceHull(traceTable) return !("enthit" in traceTable) } function ExpandClass(myClass, parentClass = null) { if (parentClass == null) parentClass = OzLib foreach (key, value in parentClass) { if (key in myClass || key == "constructor") continue if (typeof(value) == "function") myClass[key] <- value.bindenv(parentClass) else myClass[key] <- value } } function EntFireArray(arrayOrTargetNames, action, value = "", delay = 0, activator = null, caller = null) { foreach(target in arrayOrTargetNames) DoEntFire(target, action, value, delay, activator, caller) } function SecondsToTicks(seconds) { return (seconds * 66).tointeger() } //========================================================= // Looping sounds //========================================================= looping_sounds <- {} function EmitLoopingSound(entity, sound_name) { entity.EmitSound(sound_name) if (!(entity in looping_sounds)) looping_sounds[entity] <- {} looping_sounds[entity][sound_name] <- true } /*function EmitLoopingSoundEx(params) { local entity = params.entity local sound_name = params.sound_name EmitSoundEx(params) if (!(entity in looping_sounds)) looping_sounds[entity] <- {} looping_sounds[entity][sound_name] <- true }*/ function StopLoopingSound(entity, sound_name) { EmitSoundEx( { sound_name = sound_name flags = SND_STOP entity = entity filter_type = RECIPIENT_FILTER_GLOBAL }) if (entity in looping_sounds && sound_name in looping_sounds[entity]) delete looping_sounds[entity][sound_name] } function StopLoopingSounds(entity) { if (!(entity in looping_sounds)) return foreach(sound_name, _ in looping_sounds[entity]) { EmitSoundEx( { sound_name = sound_name entity = entity flags = SND_STOP filter_type = RECIPIENT_FILTER_GLOBAL }) } delete looping_sounds[entity] } function LoopingSounds_OnRoundReset() { foreach(entity, i_looping_sounds in looping_sounds) foreach(sound_name in i_looping_sounds) EmitSoundEx( { sound_name = sound_name entity = entity flags = SND_STOP filter_type = RECIPIENT_FILTER_GLOBAL }) looping_sounds = {} }