--[[----------------+ MISC VARS +------------------]]-- local Global = ents.FindByClass( "tf_gamerules" ) if Global.NewSpawns ~= nil then if ARENA_SPAWNS ~= Global.NewSpawns then ARENA_SPAWNS = Global.NewSpawns end Global.NewSpawns = nil end --[[----------------+ WAVE VARS +------------------]]-- local WAVE = 0 local TotalCount = 0 local SpawnTimer = nil local InWave = false local InWave = false local LOADOUT_POSITION_BODY = -1 local LOADOUT_POSITION_PRIMARY_AND_SECONDARY = -2 local GLOBAL_CASH_MULT = 1 local VARIANT_CHANCE = 0.05 local SHOP_WAVE = false local ARENA = 1 local ARENA_SPAWNS = { [1] = { { pos = Vector(1400, -311, 0), teamnum = 3 }, { pos = Vector(1200, -311, 0), teamnum = 3 }, { pos = Vector(1000, -311, 0), teamnum = 3 }, { pos = Vector(800, -311, 0), teamnum = 3 }, { pos = Vector(600, -311, 0), teamnum = 3 }, { pos = Vector(1695, -338, 320), teamnum = 3 }, { pos = Vector(1895, -338, 320), teamnum = 3 }, { pos = Vector(345, -353, 128), teamnum = 3 }, { pos = Vector(2280, -889, 0), teamnum = 3 }, { pos = Vector(1116, -2333, 64), teamnum = 2 }, { pos = Vector(1034, -2333, 64), teamnum = 2 }, { pos = Vector(952, -2333, 64), teamnum = 2 }, }, [2] = { { pos = Vector(3422, -1688, -629), teamnum = 3 }, { pos = Vector(3436, -1523, -634), teamnum = 3 }, { pos = Vector(3453, -1306, -641), teamnum = 3 }, { pos = Vector(3681, -1324, -644), teamnum = 3 }, { pos = Vector(3664, -1543, -634), teamnum = 3 }, { pos = Vector(3649, -1731, -618), teamnum = 3 }, { pos = Vector(1934, -1404, -634), teamnum = 3 }, { pos = Vector(1939, -1523, -639), teamnum = 3 }, { pos = Vector(1790, -1529, -632), teamnum = 3 }, { pos = Vector(1784, -1387, -633), teamnum = 3 }, { pos = Vector(2272, 1141, -128), teamnum = 2 }, { pos = Vector(2276, 1065, -128), teamnum = 2 }, { pos = Vector(2281, 957, -128), teamnum = 2 }, }, [3] = { { pos = Vector(-3966, -2765, -206), teamnum = 3 }, { pos = Vector(-3696, 712, -393), teamnum = 3 }, { pos = Vector(-600, -1369, -824), teamnum = 2 }, { pos = Vector(-603, -1256, -831), teamnum = 2 }, { pos = Vector(-606, -1135, -831), teamnum = 2 }, }, } local WAVE_TYPE_STANDARD = 1 local WAVE_TYPE_HORDE = 2 local WAVE_TYPE_CURRENT = WAVE_TYPE_STANDARD local WAVE_TYPE_CHANCE_TABLE = { [WAVE_TYPE_STANDARD] = 1, [WAVE_TYPE_HORDE] = 0.1, } local WAVE_HORDE_DURATION = 30 local WAVE_DURATION = 0 -- DEV FUNCTIONS {{ function AddPosToSpawns( _, activator ) table.insert( ARENA_SPAWNS[ARENA], { pos = activator:GetAbsOrigin(), teamnum = activator.m_iTeamNum } ) end function GetSpawns() PrintTable( ARENA_SPAWNS ) end function CopySpawns() Global.NewSpawns = ARENA_SPAWNS end -- }} local MODIFY_VARIANT_CHANCE = 1 local MODIFY_VARIANT_VOTE_OPTIONS = 2 local MODIFY_VARIANT_GLOBALCASH = 3 local MODIFY_VARIANT_ENEMYMULT = 4 local ENCOUNTER_MODIFY_STATS = { [MODIFY_VARIANT_CHANCE] = 1, [MODIFY_VARIANT_VOTE_OPTIONS] = 0, [MODIFY_VARIANT_GLOBALCASH] = 1, [MODIFY_VARIANT_ENEMYMULT] = 1, } local VOTING = false local VOTE_OPTIONS = 3 local VOTE_RESULTS = {} local VOTE_MENU = nil --[[----------------+ UPGRADES +------------------]]-- local UPGRADE_COMMON_CHANCE = 0.75 local UPGRADE_UNCOMMON_CHANCE = 0.15 local UPGRADE_RARE_CHANCE = 0.07 local UPGRADE_EPIC_CHANCE = 0.02 local UPGRADE_LEGENDARY_CHANCE = 0.01 local UPGRADE_GREED_CHANCE = 0.78 local UPGRADE_GREED_RARE_CHANCE = 0.13 local UPGRADE_GREED_EPIC_CHANCE = 0.07 local UPGRADE_GREED_LEGENDARY_CHANCE = 0.02 local UPGRADE_SHOP_COMMON_CHANCE = 0.6 local UPGRADE_SHOP_UNCOMMON_CHANCE = 0.15 local UPGRADE_SHOP_RARE_CHANCE = 0.1 local UPGRADE_SHOP_GREED_CHANCE = 0.09 local UPGRADE_SHOP_EPIC_CHANCE = 0.04 local UPGRADE_SHOP_LEGENDARY_CHANCE = 0.02 local UPGRADE_TYPE_COMMON = 0 local UPGRADE_TYPE_UNCOMMON = 1 local UPGRADE_TYPE_RARE = 2 local UPGRADE_TYPE_EPIC = 3 local UPGRADE_TYPE_LEGENDARY = 4 local UPGRADE_TYPE_GREED = 5 local UPGRADE_COST_COMMON_BASE = 100 local UPGRADE_COST_UNCOMMON_BASE = 125 local UPGRADE_COST_RARE_BASE = 180 local UPGRADE_COST_EPIC_BASE = 275 local UPGRADE_COST_LEGENDARY_BASE = 650 local UPGRADE_COST_GREED_BASE = 500 local UPGRADE_COST_RAMP_COEF = 1.05 -- math.floor( COST * ( ( WAVE - 1 )^UPGRADE_COST_RAMP_COEF ) ) local UpgradeChances = { [UPGRADE_TYPE_COMMON] = UPGRADE_COMMON_CHANCE, [UPGRADE_TYPE_UNCOMMON] = UPGRADE_UNCOMMON_CHANCE, [UPGRADE_TYPE_RARE] = UPGRADE_RARE_CHANCE, [UPGRADE_TYPE_EPIC] = UPGRADE_EPIC_CHANCE, [UPGRADE_TYPE_LEGENDARY] = UPGRADE_LEGENDARY_CHANCE } local UpgradeChancesEPIC = { [UPGRADE_TYPE_EPIC] = 1 - UPGRADE_LEGENDARY_CHANCE, [UPGRADE_TYPE_LEGENDARY] = UPGRADE_LEGENDARY_CHANCE } local UpgradeChancesGREED = { [UPGRADE_TYPE_GREED] = UPGRADE_GREED_CHANCE, [UPGRADE_TYPE_RARE] = UPGRADE_GREED_RARE_CHANCE, [UPGRADE_TYPE_EPIC] = UPGRADE_GREED_EPIC_CHANCE, [UPGRADE_TYPE_LEGENDARY] = UPGRADE_GREED_LEGENDARY_CHANCE } local UpgradeChancesSHOP = { [UPGRADE_TYPE_COMMON] = UPGRADE_SHOP_COMMON_CHANCE, [UPGRADE_TYPE_UNCOMMON] = UPGRADE_SHOP_UNCOMMON_CHANCE, [UPGRADE_TYPE_RARE] = UPGRADE_SHOP_RARE_CHANCE, [UPGRADE_TYPE_EPIC] = UPGRADE_SHOP_EPIC_CHANCE, [UPGRADE_TYPE_LEGENDARY] = UPGRADE_SHOP_LEGENDARY_CHANCE, [UPGRADE_TYPE_GREED] = UPGRADE_SHOP_GREED_CHANCE, } local UPGRADE_LOOT_COMMON = 0 local UPGRADE_LOOT_GREED = 1 local UPGRADE_LOOT_EPIC = 2 local UPGRADE_LOOT_SHOP = 3 local UPGRADE_TYPE_TABLE = { [UPGRADE_TYPE_COMMON] = "Common", [UPGRADE_TYPE_UNCOMMON] = "Uncommon", [UPGRADE_TYPE_RARE] = "Rare", [UPGRADE_TYPE_EPIC] = "Epic", [UPGRADE_TYPE_LEGENDARY] = "Legendary", [UPGRADE_TYPE_GREED] = "GREED", } local UPGRADE_LOOT_TABLE = { [UPGRADE_LOOT_COMMON] = UpgradeChances, [UPGRADE_LOOT_GREED] = UpgradeChancesGREED, [UPGRADE_LOOT_EPIC] = UpgradeChancesEPIC, [UPGRADE_LOOT_SHOP] = UpgradeChancesSHOP, } local UPGRADE_COST_TABLE = { [UPGRADE_TYPE_COMMON] = UPGRADE_COST_COMMON_BASE, [UPGRADE_TYPE_UNCOMMON] = UPGRADE_COST_UNCOMMON_BASE, [UPGRADE_TYPE_RARE] = UPGRADE_COST_RARE_BASE, [UPGRADE_TYPE_EPIC] = UPGRADE_COST_EPIC_BASE, [UPGRADE_TYPE_LEGENDARY] = UPGRADE_COST_LEGENDARY_BASE, [UPGRADE_TYPE_GREED] = UPGRADE_COST_GREED_BASE, } local COMMANDS_TABLE = { "!Upgrades", "/Upgrades" } local UPGRADE_DEFAULT_COUNT = 3 local UPGRADE_DEFAULT_MAX_REROLLS = 1 local UPGRADE_SKIP_BONUS = 250 local UPGRADE_REROLL = -1 local UPGRADE_REROLL_COST = 10 local UPGRADE_REROLL_COST_RAMP_COEF = 1.15 local UPGRADE_CATEGORY_ALL = 0 local UPGRADE_CATEGORY_DAMAGE = 1 local UPGRADE_CATEGORY_SPEED = 2 local UPGRADE_CATEGORY_HEALTH = 3 local UPGRADE_CATEGORY_MONEY = 4 local UPGRADE_CATEGORY_GREED = 5 local UPGRADE_CATEGORY_EPIC = 6 local UPGRADE_CATEGORY_ELITE_BOSS = 7 local UPGRADE_CATEGORY_SHOP = 8 local UPGRADE_LOOT_CATEGORY = UPGRADE_CATEGORY_ALL local VOTE_CATEGORIES = { [UPGRADE_CATEGORY_ALL] = { name = "Random Loot", Weight = 0.7 }, [UPGRADE_CATEGORY_DAMAGE] = { name = "Damage", Weight = 1 }, [UPGRADE_CATEGORY_SPEED] = { name = "Speed", Weight = 1 }, [UPGRADE_CATEGORY_HEALTH] = { name = "Health", Weight = 1 }, [UPGRADE_CATEGORY_MONEY] = { name = "Money", Weight = 0.85 }, [UPGRADE_CATEGORY_GREED] = { name = "GREED", Weight = 0.4 }, [UPGRADE_CATEGORY_EPIC] = { name = "Random Epic", Weight = 0.3 }, [UPGRADE_CATEGORY_ELITE_BOSS] = { name = "Elite Fight", Weight = 0 }, [UPGRADE_CATEGORY_SHOP] = { name = "Shop!", Weight = 0 }, } --Common local UPGRADE_COMMON_DMG = 0 local UPGRADE_COMMON_FIRERATE = 1 --Uncommon local UPGRADE_UNCOMMON_DMG = 1000 local UPGRADE_UNCOMMON_FIRERATE = 1001 --Rare local UPGRADE_RARE_DMG = 2000 local UPGRADE_RARE_FIRERATE = 2001 --Epic local UPGRADE_EPIC_DMG = 3000 local UPGRADE_EPIC_FIRERATE = 3001 --Legendary local UPGRADE_LEGENDARY_DMG = 4000 local UPGRADE_LEGENDARY_FIRERATE = 4001 --Greed local UPGRADE_GREED_DMG = 5000 local UPGRADE_GREED_FIRERATE = 5001 local Upgrades = { --ApplyFunction params (In order): player, upgrade --Common [UPGRADE_COMMON_DMG] = { Name = "Damage Boost", Description = "+ 10% Damage", Attributes = { { attr = "damage bonus", increment = 0.1, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "add", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_COMMON, Category = UPGRADE_CATEGORY_DAMAGE, WeaponList = WEAPONS_LIST_DAMAGE, ApplyFunction = nil, SalvagedFunction = nil }, [UPGRADE_COMMON_FIRERATE] = { Name = "Rapid Fire", Description = "+ 2% Fire rate", Attributes = { { attr = "fire rate bonus", increment = 0.98, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "multiply", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_COMMON, Category = UPGRADE_CATEGORY_SPEED, WeaponList = WEAPONS_LIST_FIRERATE, ApplyFunction = nil, SalvagedFunction = nil }, --Uncommon: 50% More than common [UPGRADE_UNCOMMON_DMG] = { Name = "Damage Boost", Description = "+ 15% Damage", Attributes = { { attr = "damage bonus", increment = 0.15, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "add", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_UNCOMMON, Category = UPGRADE_CATEGORY_DAMAGE, WeaponList = WEAPONS_LIST_DAMAGE, ApplyFunction = nil, SalvagedFunction = nil }, [UPGRADE_UNCOMMON_FIRERATE] = { Name = "Rapid Fire", Description = "+ 3% Fire rate", Attributes = { { attr = "fire rate bonus", increment = 0.97, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "multiply", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_UNCOMMON, Category = UPGRADE_CATEGORY_SPEED, WeaponList = WEAPONS_LIST_FIRERATE, ApplyFunction = nil, SalvagedFunction = nil }, --Rare: Common + Uncommon [UPGRADE_RARE_DMG] = { Name = "Damage Shot", Description = "+ 25% Damage", Attributes = { { attr = "damage bonus", increment = 0.25, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "add", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_RARE, Category = UPGRADE_CATEGORY_DAMAGE, WeaponList = WEAPONS_LIST_DAMAGE, ApplyFunction = nil, SalvagedFunction = nil }, [UPGRADE_RARE_FIRERATE] = { Name = "Supercharged", Description = "+ 5% Fire rate", Attributes = { { attr = "fire rate bonus", increment = 0.95, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "multiply", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_RARE, Category = UPGRADE_CATEGORY_SPEED, WeaponList = WEAPONS_LIST_FIRERATE, ApplyFunction = nil, SalvagedFunction = nil }, --Epic: Common + Uncommon + Rare [UPGRADE_EPIC_DMG] = { Name = "Damage Shot", Description = "+ 40% Damage", Attributes = { { attr = "damage bonus", increment = 0.4, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "add", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_EPIC, Category = UPGRADE_CATEGORY_DAMAGE, WeaponList = WEAPONS_LIST_DAMAGE, ApplyFunction = nil, SalvagedFunction = nil }, [UPGRADE_EPIC_FIRERATE] = { Name = "Supercharged", Description = "+ 8% Fire rate", Attributes = { { attr = "fire rate bonus", increment = 0.92, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "multiply", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_EPIC, Category = UPGRADE_CATEGORY_SPEED, WeaponList = WEAPONS_LIST_FIRERATE, ApplyFunction = nil, SalvagedFunction = nil }, --Legendary: 2Common + Uncommon + Rare + Epic [UPGRADE_LEGENDARY_DMG] = { Name = "Pure Damage", Description = "+ 100% Damage", Attributes = { { attr = "damage bonus", increment = 1, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "add", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_LEGENDARY, Category = UPGRADE_CATEGORY_DAMAGE, WeaponList = WEAPONS_LIST_DAMAGE, ApplyFunction = nil, SalvagedFunction = nil }, [UPGRADE_LEGENDARY_FIRERATE] = { Name = "Hail Storm", Description = "+ 20% Fire rate", Attributes = { { attr = "fire rate bonus", increment = 0.8, slot = LOADOUT_POSITION_PRIMARY_AND_SECONDARY, type = "multiply", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_LEGENDARY, Category = UPGRADE_CATEGORY_SPEED, WeaponList = WEAPONS_LIST_FIRERATE, ApplyFunction = nil, SalvagedFunction = nil }, --Greed [UPGRADE_GREED_DMG] = { Name = "Double Or Nothing", Description = "+ 250% Global damage mult \n- 100% Damage vuln", cap = nil, Attributes = { { attr = "damage bonus", increment = 2.5, slot = LOADOUT_POSITION_BODY, type = "add", cap = nil, default = 1 }, { attr = "dmg taken increased", increment = 1, slot = LOADOUT_POSITION_BODY, type = "add", cap = nil, default = 1 } }, Type = UPGRADE_TYPE_GREED, Category = UPGRADE_CATEGORY_GREED, WeaponList = nil, ApplyFunction = nil, SalvagedFunction = nil, default = 1 }, } local UPGRADE_DEFAULT_MENU = { timeout = 0, title = "Choose an Upgrade!", itemsPerPage = nil, flags = MENUFLAG_BUTTON_EXIT | MENUFLAG_NO_SOUND, onSelect = UpgradeBrought, onCancel = UpgradeSkipped, } local UPGRADE_VIEW_MENU = { timeout = 0, title = "Your Upgrades (Press to salvage)", itemsPerPage = nil, flags = MENUFLAG_BUTTON_EXIT, onSelect = UpgradeSalvaged, onCancel = nil, } local UPGRADE_WARNING_MENU = { timeout = 0, title = "Are you sure?", itemsPerPage = nil, flags = MENUFLAG_BUTTON_EXITBACK, onSelect = UpgradeSalvaged, onCancel = nil, } local UPGRADE_VOTING_MENU = { timeout = 0, title = "Vote on Next Victory Loot!", itemsPerPage = nil, flags = 0, onSelect = Voted, onCancel = nil, } --[[----------------+ ENEMIES +------------------]]-- local ENEMY_BASE_COUNT = 15 local ENEMY_BASE_MAXACTIVE_COUNT = 60 local ENEMY_BASE_HEALTH_SCALER = 1.33 local ENEMY_BASE_COUNT_SCALER = 1.15 local ENEMY_BASE_HEALTH = 25 local ENEMY_MAX_COUNT_MULT = 1 local ENEMY_BASE_CASH = 35 local ATTR_REMOVE_ON_DEATH = 1 << 0 local ATTR_AGGRESSIVE = 1 << 1 -- Push Behavior local ATTR_IS_NPC = 1 << 2 local ATTR_SUPPRESS_FIRE = 1 << 3 -- Don't shoot local ATTR_DISABLE_DODGE = 1 << 4 -- Disable Dodge local ATTR_BECOME_SPECTATOR_ON_DEATH = 1 << 5 -- Send to Spec instantly local ATTR_QUOTA_MANANGED = 1 << 6 local ATTR_RETAIN_BUILDINGS = 1 << 7 -- Keep buildings local ATTR_SPAWN_WITH_FULL_CHARGE = 1 << 8 -- Spawn with full charge on meter items local ATTR_ALWAYS_CRIT = 1 << 9 -- Always crit local ATTR_IGNORE_ENEMIES = 1 << 10 local ATTR_HOLD_FIRE_UNTIL_FULL_RELOAD = 1 << 11 -- Fully reload first local ATTR_PRIORITIZE_DEFENSE = 1 << 12 local ATTR_ALWAYS_FIRE_WEAPON = 1 << 13 -- Always shoot local ATTR_TELEPORT_TO_HINT = 1 << 14 -- Teleport to engie hint (dependent on bomb pos) local ATTR_MINIBOSS = 1 << 15 -- Giant local ATTR_USE_BOSS_HEALTH_BAR = 1 << 16 -- Healthbar local ATTR_IGNORE_FLAG = 1 << 17 -- Ignore bomb local ATTR_AUTO_JUMP = 1 << 18 -- Auto jump (Doesn't work on it's own) local ATTR_AIR_CHARGE_ONLY = 1 << 19 -- Only charge in the air local ATTR_PREFER_VACCINATOR_BULLETS = 1 << 20 -- Sets vacc to bullet local ATTR_PREFER_VACCINATOR_BLAST = 1 << 21 -- Sets vacc to blast local ATTR_PREFER_VACCINATOR_FIRE = 1 << 22 -- Sets vacc to fire local ATTR_BULLET_IMMUNE = 1 << 23 -- Fully immune to bullets local ATTR_BLAST_IMMUNE = 1 << 24 -- Fully immune to blast local ATTR_FIRE_IMMUNE = 1 << 25 -- Fully immune to fire local ATTR_PARACHUTE = 1 << 26 -- Use parachute when falling for a while local ATTR_PROJECTILE_SHIELD = 1 << 27 -- Use proj shield local WEPRST_ANY_WEAPON = 0 local WEPRST_MELEE_ONLY = 1 local WEPRST_PRIMARY_ONLY = 2 local WEPRST_SECONDARY_ONLY = 4 local AI_EASY = 0 local AI_NORMAL = 1 local AI_HARD = 2 local AI_EXPERT = 3 local ENEMY_TYPE_COMMON = 1 local ENEMY_TYPE_MINIGIANT = 2 local ENEMY_TYPE_GIANT = 3 local ENEMY_TYPE_BOSS = 4 local ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, } local ENEMY_TYPE_CHANCE_SCALE_MULT = { [1] = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 0, [ENEMY_TYPE_GIANT] = 0, [ENEMY_TYPE_BOSS] = 0, }, [2] = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 0.25, [ENEMY_TYPE_GIANT] = 0, [ENEMY_TYPE_BOSS] = 0, }, [3] = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 0.5, [ENEMY_TYPE_GIANT] = 0.33, [ENEMY_TYPE_BOSS] = 0, }, [4] = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 0.75, [ENEMY_TYPE_GIANT] = 0.67, [ENEMY_TYPE_BOSS] = 0, }, [5] = { [ENEMY_TYPE_COMMON] = 0.9, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, }, [6] = { [ENEMY_TYPE_COMMON] = 0, [ENEMY_TYPE_MINIGIANT] = 0, [ENEMY_TYPE_GIANT] = 0, [ENEMY_TYPE_BOSS] = 0, }, [7] = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 1, }, [0] = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 1, }, } local ENEMY_MODEL_TABLE = { [0] = { -- COMMONS [TF_CLASS_DEMOMAN] = "models/bots/demo/bot_demo.mdl", [TF_CLASS_ENGINEER] = "models/bots/engineer/bot_engineer.mdl", [TF_CLASS_HEAVYWEAPONS] = "models/bots/heavy/bot_heavy.mdl", [TF_CLASS_MEDIC] = "models/bots/medic/bot_medic.mdl", [TF_CLASS_PYRO] = "models/bots/pyro/bot_pyro.mdl", [TF_CLASS_SCOUT] = "models/bots/scout/bot_scout.mdl", [TF_CLASS_SNIPER] = "models/bots/sniper/bot_sniper.mdl", [TF_CLASS_SOLDIER] = "models/bots/soldier/bot_soldier.mdl", [TF_CLASS_SPY] = "models/bots/spy/bot_spy.mdl", }, [1] = { -- GIANTS [TF_CLASS_DEMOMAN] = "models/bots/demo_boss/bot_demo_boss.mdl", [TF_CLASS_ENGINEER] = "models/bots/engineer/bot_engineer.mdl", [TF_CLASS_HEAVYWEAPONS] = "models/bots/heavy_boss/bot_heavy_boss.mdl", [TF_CLASS_MEDIC] = "models/bots/medic/bot_medic.mdl", [TF_CLASS_PYRO] = "models/bots/pyroy_boss/bot_pyro.mdl", [TF_CLASS_SCOUT] = "models/bots/scouty_boss/bot_scouty_boss.mdl", [TF_CLASS_SNIPER] = "models/bots/sniper/bot_sniper.mdl", [TF_CLASS_SOLDIER] = "models/bots/soldiery_boss/bot_soldiery_boss.mdl", [TF_CLASS_SPY] = "models/bots/spy/bot_spy.mdl", } } local ENEMY_TEMPLATES = { ["T_TFBot_Enemy_Basic"] = { Weight = 1, Name = "Basic", Type = ENEMY_TYPE_COMMON, WeaponRestrictions = WEPRST_MELEE_ONLY, Skill = AI_EXPERT, Class = "Scout", Action = "Mobber", Health = ENEMY_BASE_HEALTH, DefaultSlot = LOADOUT_POSITION_MELEE, Attributes = ATTR_DISABLE_DODGE | ATTR_IGNORE_FLAG, Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed penalty"] = 0.75 }, Scale = 1, BaseCashMult = 1, AddConds = {}, VisionRange = -1, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen } }, }, ["T_TFBot_Enemy_Blocker"] = { Weight = 0.65, Name = "Blocker", Type = ENEMY_TYPE_COMMON, WeaponRestrictions = WEPRST_MELEE_ONLY, Skill = AI_EXPERT, Class = "Heavyweapons", Action = "Mobber", Health = ENEMY_BASE_HEALTH * 2, DefaultSlot = LOADOUT_POSITION_MELEE, Attributes = ATTR_IGNORE_FLAG, Items = {}, ItemAttributes = {}, CharacterAttributes = {}, Scale = 1, BaseCashMult = 1, AddConds = {}, VisionRange = -1, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen }, }, }, ["T_TFBot_Enemy_Knight"] = { Weight = 0.25, Name = "Knight", Type = ENEMY_TYPE_COMMON, WeaponRestrictions = WEPRST_MELEE_ONLY, Skill = AI_EXPERT, Class = "Demoman", Action = "Mobber", Health = ENEMY_BASE_HEALTH, DefaultSlot = LOADOUT_POSITION_MELEE, Attributes = ATTR_IGNORE_FLAG | ATTR_DISABLE_DODGE, Items = { "Ali Baba's Wee Booties", "The Chargin' Targe", "The Half-Zatoichi" }, ItemAttributes = {}, CharacterAttributes = {}, Scale = 1, BaseCashMult = 1, AddConds = {}, VisionRange = -1, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5, ["mult charging move speed"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen }, { Append_Name = "Charging ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["charge recharge rate increased"] = 2, ["charge time increased"] = 0.5 }, AddConds = { TF_COND_SHIELD_CHARGE }, Weight = 0.5, }, { Append_Name = "Enraged ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["fire rate bonus"] = 0.8 }, AddConds = { TF_COND_CRITBOOSTED }, Weight = 0.75, }, }, }, ["T_TFBot_Enemy_Medic"] = { Weight = 0.1 * 5, Name = "Uber Medic", Type = ENEMY_TYPE_COMMON, WeaponRestrictions = WEPRST_SECONDARY_ONLY, Skill = AI_EXPERT, Class = "Medic", Action = "Medic", Health = ENEMY_BASE_HEALTH * 2.4, DefaultSlot = LOADOUT_POSITION_SECONDARY, Attributes = ATTR_IGNORE_FLAG | ATTR_DISABLE_DODGE | ATTR_SPAWN_WITH_FULL_CHARGE, Items = { "TF_WEAPON_SYRINGEGUN_MEDIC", "TF_WEAPON_MEDIGUN" }, ItemAttributes = {}, CharacterAttributes = { ["ubercharge rate bonus"] = 5, ["heal rate bonus"] = 0.1}, Scale = 1, BaseCashMult = 1, AddConds = {}, VisionRange = -1, LogicFunc = function( botSpawn, data ) -- Passes botSpawn, data local alivebots = #ents.GetAllAliveNonMedicBots() local switched if switched == nil then switched = false end if alivebots > 0 then botSpawn:SetActivePlayerWeapon( botSpawn:GetPlayerItemBySlot( 1 ) ) -- botSpawn:BotCommand( "switch_action Medic" ) else if switched == false then botSpawn:BotCommand( "switch_action Mobber" ) botSpawn:AcceptInput("$BotCommand","switch_action Mobber") botSpawn:RunScriptCode( "activator.ClearAllWeaponRestrictions()", botSpawn, botSpawn ) timer.Simple( 0.5, function() switched = false end) end botSpawn:WeaponStripSlot( 1 ) botSpawn:SetActivePlayerWeapon( botSpawn:GetPlayerItemBySlot( 0 ) ) end end, SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 0.5, }, { Append_Name = "Bigheal ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["ubercharge rate bonus"] = 0.1, ["heal rate bonus"] = 10 }, AddConds = {}, Weight = 1, }, }, }, ["T_TFBot_Enemy_Shotgunner"] = { Weight = 0.075, Name = "Shotgunner", Type = ENEMY_TYPE_MINIGIANT, WeaponRestrictions = WEPRST_SECONDARY_ONLY, Skill = AI_EXPERT, Class = "Heavyweapons", Action = "Mobber", Health = ENEMY_BASE_HEALTH * 5, DefaultSlot = LOADOUT_POSITION_SECONDARY, Attributes = ATTR_IGNORE_FLAG | ATTR_HOLD_FIRE_UNTIL_FULL_RELOAD | ATTR_DISABLE_DODGE, Items = { "TF_WEAPON_SHOTGUN_HWG" }, ItemAttributes = { [1] = { ["Fire rate penalty"] = 2, ["Damage bonus"] = 1.25, ["Spread Penalty"] = 1.5, } }, CharacterAttributes = {}, Scale = 1.5, BaseCashMult = 1.5, AddConds = {}, VisionRange = 500, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen }, { Append_Name = "Frenzied ", Items = {}, ItemAttributes = { [1] = { ["Fire rate bonus"] = 0.5, ["reload time increased"] = 0.5, } }, CharacterAttributes = {}, AddConds = {}, Weight = 1, }, }, }, ["T_TFBot_Enemy_Grenadier"] = { Weight = 0.05, Name = "Grenader", Type = ENEMY_TYPE_MINIGIANT, WeaponRestrictions = WEPRST_PRIMARY_ONLY, Skill = AI_EXPERT, Class = "Demoman", Action = "Mobber", Health = ENEMY_BASE_HEALTH * 4, DefaultSlot = LOADOUT_POSITION_PRIMARY, Attributes = ATTR_IGNORE_FLAG | ATTR_DISABLE_DODGE, Items = {"Magnum Opus"}, ItemAttributes = {}, CharacterAttributes = { ["fire rate penalty"] = 2, ["reload time increased"] = 1.5, ["projectile speed increased hidden"] = 0.67 }, Scale = 1.5, BaseCashMult = 1.5, AddConds = {}, VisionRange = -1, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen }, { Append_Name = "Frenzied ", Items = {}, ItemAttributes = { [0] = { ["Fire rate bonus"] = 0.5, ["reload time increased"] = 0.5, } }, CharacterAttributes = {}, AddConds = {}, Weight = 1, }, }, }, ["T_TFBot_Enemy_Baseballer"] = { Weight = 0.075, Name = "Baseballer", Type = ENEMY_TYPE_MINIGIANT, WeaponRestrictions = WEPRST_MELEE_ONLY, DefaultSlot = LOADOUT_POSITION_MELEE, Skill = AI_EXPERT, Class = "Scout", Action = "Mobber", Health = ENEMY_BASE_HEALTH * 4, Attributes = ATTR_IGNORE_FLAG | ATTR_DISABLE_DODGE, Items = {"The Sandman", "Batter's Helmet", "Batter's Bracers"}, ItemAttributes = { [2] = { ["effect bar recharge rate increased"] = 0.15, } }, CharacterAttributes = {}, Scale = 1.5, BaseCashMult = 1.5, AddConds = {}, VisionRange = -1, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen }, { Append_Name = "Major League ", Items = {}, ItemAttributes = { [2] = { ["effect bar recharge rate increased"] = 0.05, } }, CharacterAttributes = {}, AddConds = {}, Weight = 1, }, { Append_Name = "Ornament ", Items = { "The Wrap Assassin" }, ItemAttributes = { [2] = { ["effect bar recharge rate increased"] = 0.25, } }, CharacterAttributes = {}, AddConds = {}, Weight = 0.85, }, }, }, ["T_TFBot_Enemy_Launcher"] = { Weight = 0.05, Name = "Launcher", Type = ENEMY_TYPE_MINIGIANT, WeaponRestrictions = WEPRST_PRIMARY_ONLY, DefaultSlot = LOADOUT_POSITION_PRIMARY, Skill = AI_EXPERT, Class = "Soldier", Action = "Mobber", Health = ENEMY_BASE_HEALTH * 6, Attributes = ATTR_IGNORE_FLAG | ATTR_HOLD_FIRE_UNTIL_FULL_RELOAD | ATTR_DISABLE_DODGE, Items = {"Tyrantium Helmet"}, ItemAttributes = { [0] = { ["fire rate penalty"] = 3, ["damage bonus"] = 1.25, ["ignores other projectiles"] = 1, ["mult projectile count"] = 3, ["projectile speed decreased"] = 0.65, ["blast radius increased"] = 1.2, ["spread angle pattern"] = "0 10 0|0 0 0|0 -10 0", } }, CharacterAttributes = { ["blast dmg to self increased"] = 0 }, Scale = 1.5, BaseCashMult = 1.5, AddConds = {}, VisionRange = -1, LogicFunc = nil, -- Passes botSpawn, data SpawnFunc = nil, -- Passes botSpawn, data DeathFunc = nil, -- Passes botSpawn Variants = { { Append_Name = "Speedy ", Items = {}, ItemAttributes = {}, CharacterAttributes = { ["move speed bonus"] = 1.5 }, AddConds = {}, Weight = 1, -- If we roll the chance for variant, weight of this one to be chosen }, { Append_Name = "Frenzied ", Items = {}, ItemAttributes = { [0] = { ["Fire rate bonus"] = 0.5, ["reload time increased"] = 0.5, } }, CharacterAttributes = {}, AddConds = {}, Weight = 1, }, { Append_Name = "Homing ", Items = {}, ItemAttributes = { [0] = { ["projectile speed decreased"] = 0.25, ["mod projectile heat seek power"] = 90, ["mod projectile heat aim error"] = 90, ["mod projectile heat aim time"] = 6, ["mod projectile heat no predict target speed"] = 1, ["mod projectile heat aim start time"] = 1, } }, CharacterAttributes = {}, AddConds = {}, Weight = 0.75, }, { Append_Name = "Accelerating ", Items = {}, ItemAttributes = { [0] = { ["projectile speed decreased"] = 0.1, ["projectile acceleration"] = 2200, ["projectile acceleration time"] = 0.75, ["projectile acceleration start time"] = 0.5, } }, CharacterAttributes = {}, AddConds = {}, Weight = 0.75, }, }, }, } --[[----------------+ FUNCTIONS +------------------]]-- function removeCallbacks(player, callbacks) if not IsValid(player) then return end for _, callbackId in pairs(callbacks) do player:RemoveCallback(callbackId) end end math.WeightedRandom = function( tbl ) local total = 0 for i, d in pairs( tbl ) do if tostring( type( d ) ) == "table" then total = total + d.Weight else total = total + d end end local random = math.randomfloat( 0, total ) local cumulativeProbability = 0 for key, item in pairs(tbl) do if tostring( type( tbl[key] ) ) == "table" then cumulativeProbability = cumulativeProbability + tbl[key].Weight else cumulativeProbability = cumulativeProbability + tbl[key] end if (cumulativeProbability >= random) then return key; end end end math.WeightedRandomEnemy = function( tbl ) local total = 0 for i, d in pairs( tbl ) do total = total + ( d.Weight * ENEMY_TYPE_CHANCE_MULT[ d.Type ] ) end local random = math.randomfloat( 0, total ) local cumulativeProbability = 0 for key, item in pairs(tbl) do cumulativeProbability = cumulativeProbability + ( tbl[key].Weight * ENEMY_TYPE_CHANCE_MULT[ tbl[key].Type ] ) if (cumulativeProbability >= random) then return key; end end end CEntity.GetAttributeValueClean = function(self, attribute, default, slot) if (IsValid(self)) then --print( attribute, default, slot, slot == LOADOUT_POSITION_BODY, ( self:GetAttributeValue( tostring( attribute ), true ) or default ) ) if slot == LOADOUT_POSITION_BODY then return ( self:GetAttributeValue( tostring( attribute ), true ) or default ) else if IsValid( self:GetPlayerItemBySlot(slot) ) then return ( self:GetPlayerItemBySlot(slot):GetAttributeValue( tostring( attribute ), true ) or default ) end end end end CEntity.GetItemBySlotClean = function(self, slot) if (IsValid(self)) then --print( attribute, default, slot, slot == LOADOUT_POSITION_BODY, ( self:GetAttributeValue( tostring( attribute ), true ) or default ) ) --print( slot ) if slot == LOADOUT_POSITION_BODY then return nil elseif slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then return { self:GetPlayerItemBySlot( 0 ), self:GetPlayerItemBySlot( 1 ) } else return self:GetPlayerItemBySlot( tonumber(slot) ) end end end ents.GetAllAliveBots = function() local ply = {} for _, player in pairs( ents.GetAllPlayers() ) do if player:IsBot() and player:IsAlive() then table.insert( ply, player ) end end return ply end ents.GetAllAliveNonMedicBots = function() local ply = {} for _, player in pairs( ents.GetAllPlayers() ) do if player:IsBot() and player:IsAlive() and ( player.m_iClass ~= TF_CLASS_MEDIC ) then table.insert( ply, player ) end end return ply end function GetAllVotes() local Votes = 0 for i = 0, #VOTE_RESULTS do if VOTE_RESULTS[i] ~= nil then Votes = Votes + #VOTE_RESULTS[i] end end return Votes end function GetVoteWinner() local HighestVote = -100 local Winners = {} for i = 0, #VOTE_RESULTS do if VOTE_RESULTS[i] ~= nil then if #VOTE_RESULTS[i] > HighestVote then HighestVote = #VOTE_RESULTS[i] Winners = { i } elseif #VOTE_RESULTS[i] == HighestVote then table.insert( Winners, i ) end end end if #Winners == 1 then return Winners[1] else return Winners[ math.random( 1, #Winners ) ] end end function HasTrue( tbl ) if tostring(type( tbl )) == "table" then for i, v in pairs( tbl ) do if v == true then return true end end else return tbl end return false end function WeaponAllowed( weapon, list ) if not weapon then return false end if not list then return true end --print( type( list ), type( weapon ) ) if tostring(type(list)) == "table" then if tostring(type(weapon)) == "table" then local result = {} for i, wep in pairs( weapon ) do if list[wep:GetClassname()] ~= nil then result[i] = list[wep:GetClassname()] elseif list[ wep:GetItemName() ] ~= nil then result[i] = list[wep:GetItemName()] else --failsafe result[i] = ( false ) end end --PrintTable( result ) return result else --print( list[weapon:GetClassname()] ) if list[weapon:GetClassname()] ~= nil then return ( list[weapon:GetClassname()] ) elseif list[ weapon:GetItemName() ] ~= nil then return ( list[weapon:GetItemName()] ) else --failsafe print( "Weapon was not found on provided list", weapon:GetItemName(), weapon:GetClassname() ) return false end end else if weapon:GetClassname() == list or ( weapon:GetItemName() == list ) then return true else return false end end return false end function GetTypeCost( type ) return math.floor( UPGRADE_COST_TABLE[type] * math.max( ( ( WAVE - 1 )^UPGRADE_COST_RAMP_COEF ), 1 ) ) end function GetEnemyCount() return math.floor( ENEMY_BASE_COUNT + ( ( WAVE - 1 )^ENEMY_BASE_COUNT_SCALER ) ) end function GetEnemyHealthMult() return WAVE^ENEMY_BASE_HEALTH_SCALER end function BuildVoteMenu( player, dev ) player = player or dev --print( math.floor( ( WAVE + 1 ) / ( 6 + ( math.floor( ( WAVE + 1.5 ) / 7 ) * 7 ) ) ), ( WAVE + 1 ) / ( 6 + ( math.floor( ( WAVE + 1.5 ) / 7 ) * 7 ) ) ) local AboutToBeShop = tobool( math.floor( ( WAVE + 1 ) / ( 6 + ( math.floor( ( WAVE + 1.5 ) / 7 ) * 7 ) ) ) ) -- print( SHOP_WAVE, AboutToBeShop ) if InWave then return end local function GetVotes( index ) if VOTE_RESULTS[index] ~= nil then return #VOTE_RESULTS[index] else return 0 end end if not VOTE_MENU then VOTE_MENU = {} local Categories = {} for key, val in pairs(UPGRADE_VOTING_MENU) do VOTE_MENU[key] = val end local function CategorySelected( cate ) for i, _ in pairs( Categories ) do if Categories[i] == cate then return true end end return false end VOTE_MENU["onSelect"] = function( ply, index, value ) Voted( ply,index,value ) end if SHOP_WAVE == false and AboutToBeShop == false then for i=1, tonumber( VOTE_OPTIONS + ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_VOTE_OPTIONS] ) do local random = math.WeightedRandom( VOTE_CATEGORIES ) local category = VOTE_CATEGORIES[ random ] local name = category.name if i~=1 then while CategorySelected( random ) do random = math.WeightedRandom( VOTE_CATEGORIES ) category = VOTE_CATEGORIES[ random ] name = category.name end end table.insert( Categories, random ) VOTE_MENU[i] = { text = name, value = random, disabled = false } end elseif AboutToBeShop and ( not SHOP_WAVE ) then VOTE_MENU[1] = { text = VOTE_CATEGORIES[ UPGRADE_CATEGORY_SHOP ].name, value = UPGRADE_CATEGORY_SHOP, disabled = false } elseif SHOP_WAVE and ( not AboutToBeShop ) then VOTE_MENU[1] = { text = VOTE_CATEGORIES[ UPGRADE_CATEGORY_ELITE_BOSS ].name, value = UPGRADE_CATEGORY_ELITE_BOSS, disabled = false } end else if SHOP_WAVE == false and AboutToBeShop == false then for i=1, tonumber( VOTE_OPTIONS + ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_VOTE_OPTIONS] ) do local str = tostring( VOTE_CATEGORIES[tonumber( VOTE_MENU[i].value )].name ) .. " " for votes=1, GetVotes( tonumber( VOTE_MENU[i].value ) ) do str = str .. "☑" end VOTE_MENU[i] = { text = str, value = tonumber( VOTE_MENU[i].value ), disabled = false } end elseif AboutToBeShop and ( not SHOP_WAVE ) then local str = tostring( VOTE_CATEGORIES[ UPGRADE_CATEGORY_SHOP ].name ) .. " " for votes=1, GetVotes( tonumber( UPGRADE_CATEGORY_SHOP ) ) do str = str .. "☑" end VOTE_MENU[1] = { text = str, value = UPGRADE_CATEGORY_SHOP, disabled = false } elseif SHOP_WAVE and ( not AboutToBeShop ) then local str = tostring( VOTE_CATEGORIES[ UPGRADE_CATEGORY_ELITE_BOSS ].name ) .. " " for votes=1, GetVotes( tonumber( UPGRADE_CATEGORY_ELITE_BOSS ) ) do str = str .. "☑" end VOTE_MENU[1] = { text = str, value = UPGRADE_CATEGORY_ELITE_BOSS, disabled = false } end end player:DisplayMenu( VOTE_MENU ) end function Voted( player, index, value ) value = tonumber( value ) for i = 0, #VOTE_RESULTS do for index, id in pairs( VOTE_RESULTS[i] ) do if id == player:GetSteamId() then table.remove( VOTE_RESULTS[i], index ) end end end table.insert( VOTE_RESULTS[value], player:GetSteamId() ) end function BuildUpgradeMenu( player, dev, loot_table ) player = player or dev loot_table = loot_table or UPGRADE_LOOT_COMMON if player.InMenu == true then return end local new_menu = {} for key, val in pairs(UPGRADE_DEFAULT_MENU) do new_menu[key] = val end --new_menu[itemsPerPage] = player.DisplayedUpgrades new_menu["onSelect"] = function( ply, index, value ) UpgradeBrought( ply,index,value ) end new_menu["onCancel"] = function( ply, reason ) UpgradeSkipped( ply,reason ) end for i=1, tonumber( player.DisplayedUpgrades ) do local type = table.RandomChance( UPGRADE_LOOT_TABLE[ loot_table ] ) -- type 0-4 local upgrades_list = GetUpgradesTableFiltered(type) local rnd = math.random( 1, #upgrades_list ) local selected_upgrade = Upgrades[ upgrades_list[rnd] ] local loops = 0 while IsUpgradeCapped( player, selected_upgrade ) or ( not HasTrue( WeaponAllowed( player:GetItemBySlotClean( selected_upgrade.Attributes[1].slot ), selected_upgrade.WeaponList ) ) ) or ( ( UPGRADE_LOOT_CATEGORY ~= UPGRADE_CATEGORY_ALL ) and ( selected_upgrade.Category ~= UPGRADE_LOOT_CATEGORY ) ) do rnd = math.random( 1, #upgrades_list ) selected_upgrade = Upgrades[ upgrades_list[rnd] ] loops = loops + 1 -- failsafe to avoid stackoverflow if loops > 1200 then print( "breaking" ) break end end local cost = "" if player.InShop then cost = "$" .. tostring( GetTypeCost(type) ) -- new_menu["flags"] = MENUFLAG_NO_SOUND -- new_menu["onCancel"] = nil end --local str = tostring( UPGRADE_TYPE_TABLE[type] ) .. ": " .. selected_upgrade.Name .. "\n" .. "Description: " .. "\n" ..selected_upgrade.Description local str = tostring( UPGRADE_TYPE_TABLE[type] ) .. ": " .. selected_upgrade.Name .. " " .. cost .. "\n" .. selected_upgrade.Description new_menu[i] = { text = str, value = upgrades_list[rnd], disabled = IsUpgradeCapped( player, selected_upgrade ) } end local reroll_cost = math.floor( math.max( ( WAVE - 1 ), 1 ) * ( UPGRADE_REROLL_COST * UPGRADE_REROLL_COST_RAMP_COEF ) * ( player.Rerolls + 1 )^UPGRADE_REROLL_COST_RAMP_COEF ) new_menu[player.DisplayedUpgrades + 1] = { text = "Reroll Shop $" .. tostring(reroll_cost) .. " " .. tostring(player.Rerolls) .. "/" .. tostring(player.MaxRerolls), value = UPGRADE_REROLL - loot_table, disabled = tobool( player.Rerolls >= player.MaxRerolls ) } local total = 0 for i=1, player.DisplayedUpgrades do total = total + GetTypeCost(Upgrades[tonumber(new_menu[i].value)].Type) end total = math.floor( total / player.DisplayedUpgrades ) if not player.InShop then new_menu["title"] = new_menu["title"] .. "\nSkip for $" .. tostring( total ) end --PrintTable( new_menu ) player.Menu = new_menu player.InMenu = true player:DisplayMenu( new_menu ) end function BuildViewUpgradeMenu( player, dev ) player = player or dev --if player.InMenu == true then return end player.InRoll = false local new_menu = {} for key, val in pairs(UPGRADE_VIEW_MENU) do new_menu[key] = val end for index, count in pairs( player.Upgrades ) do if count > 0 then local greed = false if Upgrades[index].Type == UPGRADE_TYPE_GREED then greed = true end local extra_text = "" if greed then extra_text = "\n**You cannot drop Greed Loot!**" end table.insert( new_menu, { text = tostring( UPGRADE_TYPE_TABLE[Upgrades[index].Type] ) .. ": " .. Upgrades[index].Name .. " x" .. tostring(count) .. "\n" .. Upgrades[index].Description .. " each" .. extra_text, value = index, disabled = greed } ) end end new_menu["onCancel"] = function( ply, reason ) player.InRoll = false if player.InMenu then player:DisplayMenu( player.Menu ) end end new_menu["onSelect"] = function( ply, index, value ) local warning_menu = {} value = tonumber( value ) for k, v in pairs(UPGRADE_WARNING_MENU) do warning_menu[k] = v end warning_menu["onSelect"] = function( ply, i, v ) player.InRoll = false if player.InMenu then player:DisplayMenu( player.Menu ) end UpgradeSalvaged( ply, i, v ) end warning_menu["onCancel"] = function( ply, reason ) BuildViewUpgradeMenu( ply, ply ) end warning_menu["title"] = "Salvage " .. tostring( UPGRADE_TYPE_TABLE[Upgrades[value].Type] ) .. ": " .. Upgrades[value].Name .. " x" .. tostring(player.Upgrades[value]) warning_menu[1] = { text = "Yes. \n" .. Upgrades[value].Description .. "\nYou'll get $" .. tostring( math.floor( GetTypeCost( Upgrades[value].Type ) * 0.5 * player.Upgrades[value] ) ) .. "\nPress back to cancel", value = value, disabled = false } player:DisplayMenu( warning_menu ) end player:DisplayMenu( new_menu ) end function UpgradeSalvaged( player, index, value ) value = tonumber(value) local upgrade = Upgrades[value] player:AddCurrency( math.floor(GetTypeCost( Upgrades[value].Type ) * 0.5 * player.Upgrades[value] ) ) player:Print( PRINT_TARGET_CENTER, "Salvaged " .. tostring( UPGRADE_TYPE_TABLE[Upgrades[value].Type] ) .. ": " .. Upgrades[value].Name .. " x" .. tostring(player.Upgrades[value]) .. "\nFor $" .. tostring( math.floor(GetTypeCost( Upgrades[value].Type ) * 0.5 * player.Upgrades[value] ) ) ) for i = 1, player.Upgrades[value] do for j = 1, #upgrade.Attributes do --print( upgrade.ApplyFunction ) pcall( upgrade.SalvagedFunction, player, tonumber( value ) ) --if not ( HasTrue( WeaponAllowed( player:GetItemBySlotClean( upgrade.slot ), upgrade.WeaponList ) ) ) then goto skip2 end if upgrade.Attributes[j].slot == LOADOUT_POSITION_BODY then local cap = ( upgrade.Attributes[j].cap or ( ( 2^32 ) / 2 ) - 1 ) if tostring( upgrade.Attributes[j].type ) == "add" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) - upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "multiply" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) / upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "subtract" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) + upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "divide" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) * upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "set" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].default ) end elseif upgrade.Attributes[j].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then if tostring( upgrade.Attributes[j].type ) == "add" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) - upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) - upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "multiply" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) / upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) / upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "subtract" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) + upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) + upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "divide" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) * upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) * upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "set" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].default ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].default ) end end else if ( WeaponAllowed( player:GetPlayerItemBySlot( upgrade.Attributes[j].slot ), upgrade.WeaponList ) ) then if tostring( upgrade.Attributes[j].type ) == "add" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) - upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "multiply" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) / upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "subtract" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) + upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "divide" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) * upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "set" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].default ) end end end ::skip2:: end end player.Upgrades[value] = 0 end function ApplyMissingUpgrades( player ) for i, v in pairs( player.Upgrades ) do local upgrade = Upgrades[i] for j = 1, ( #upgrade.Attributes ) do if v == 0 then goto skip end for k = 1, v do pcall( upgrade.ApplyFunction, player, tonumber( value ) ) if upgrade.Attributes[j].slot == LOADOUT_POSITION_BODY then local cap = ( upgrade.Attributes[j].cap or ( ( 2^32 ) / 2 ) - 1 ) if tostring( upgrade.Attributes[j].type ) == "add" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) + upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "multiply" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) * upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "subtract" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) - upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "divide" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) / upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "set" then player:SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end elseif upgrade.Attributes[j].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then if tostring( upgrade.Attributes[j].type ) == "add" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) + upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) + upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "multiply" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) * upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) * upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "subtract" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) - upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) - upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "divide" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) / upgrade.Attributes[j].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) / upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "set" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end end else if ( WeaponAllowed( player:GetPlayerItemBySlot( upgrade.Attributes[j].slot ), upgrade.WeaponList ) ) then if tostring( upgrade.Attributes[j].type ) == "add" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) + upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "multiply" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) * upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "subtract" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) - upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "divide" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) / upgrade.Attributes[j].increment ), cap ) elseif tostring( upgrade.Attributes[j].type ) == "set" then player:GetPlayerItemBySlot(upgrade.Attributes[j].slot):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end end end end ::skip:: end end end function UpgradeBrought( player, index, value ) player:PlaySoundToSelf( "ui/buttonrollover.wav" ) if tonumber( value ) >= 0 then local upgrade = Upgrades[ tonumber( value ) ] if player.InShop then if ( player.m_nCurrency < GetTypeCost(upgrade.Type) ) then player:PlaySoundToSelf( "mvm/mvm_money_vanish.wav" ) player:PlaySoundToSelf( "mvm/mvm_money_vanish.wav" ) player:Print( PRINT_TARGET_CENTER, "Not enough money" ) timer.Simple( 0.2, function() if player.InShop then player.Menu["flags"] = MENUFLAG_NO_SOUND | MENUFLAG_BUTTON_EXIT end player:DisplayMenu( player.Menu ) end) return end player:RemoveCurrency( GetTypeCost(upgrade.Type) ) player:Print( PRINT_TARGET_CENTER, "Brought: " .. tostring( UPGRADE_TYPE_TABLE[upgrade.Type] ) .. ": " .. upgrade.Name .. " $" .. tostring(GetTypeCost(upgrade.Type)) .. "\n" .. upgrade.Description ) player:PlaySoundToSelf( "MVM.PlayerUpgraded" ) player:PlaySoundToSelf( "MVM.PlayerUpgraded" ) else player:Print( PRINT_TARGET_CENTER, "Picked up: " .. tostring( UPGRADE_TYPE_TABLE[upgrade.Type] ) .. ": " .. upgrade.Name .. "\n" .. upgrade.Description ) player.Rerolls = 0 --player.InShop = false player.InMenu = false player.InRoll = false player:PlaySoundToSelf( "ui/item_metal_tiny_pickup.wav" ) player:PlaySoundToSelf( "ui/item_metal_tiny_pickup.wav" ) player:PlaySoundToSelf( "ui/item_metal_tiny_pickup.wav" ) end --print( player, index, value ) for i = 1, #upgrade.Attributes do --print( upgrade.ApplyFunction ) pcall( upgrade.ApplyFunction, player, tonumber( value ) ) if upgrade.Attributes[i].slot == LOADOUT_POSITION_BODY then local cap = ( upgrade.Attributes[i].cap or ( ( 2^32 ) / 2 ) - 1 ) if tostring( upgrade.Attributes[i].type ) == "add" then player:SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "multiply" then player:SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) * upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "subtract" then player:SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) - upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "divide" then player:SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) / upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "set" then player:SetAttributeValue( tostring( upgrade.Attributes[i].attr ), upgrade.Attributes[i].increment ) end elseif upgrade.Attributes[i].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then if tostring( upgrade.Attributes[i].type ) == "add" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) + upgrade.Attributes[i].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_SECONDARY ) + upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "multiply" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) * upgrade.Attributes[i].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_SECONDARY ) * upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "subtract" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) - upgrade.Attributes[i].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_SECONDARY ) - upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "divide" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) / upgrade.Attributes[i].increment ), cap ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_SECONDARY ) / upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "set" then if ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), upgrade.Attributes[i].increment ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), upgrade.Attributes[i].increment ) end end else if ( WeaponAllowed( player:GetPlayerItemBySlot( upgrade.Attributes[i].slot ), upgrade.WeaponList ) ) then if tostring( upgrade.Attributes[i].type ) == "add" then player:GetPlayerItemBySlot(upgrade.Attributes[i].slot):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) + upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "multiply" then player:GetPlayerItemBySlot(upgrade.Attributes[i].slot):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) * upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "subtract" then player:GetPlayerItemBySlot(upgrade.Attributes[i].slot):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) - upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "divide" then player:GetPlayerItemBySlot(upgrade.Attributes[i].slot):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) / upgrade.Attributes[i].increment ), cap ) elseif tostring( upgrade.Attributes[i].type ) == "set" then player:GetPlayerItemBySlot(upgrade.Attributes[i].slot):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), upgrade.Attributes[i].increment ) end end end ::skip3:: end player.Upgrades[ tonumber( value ) ] = player.Upgrades[ tonumber( value ) ] + 1 player.Menu[ tonumber( index) ]["disabled"] = true timer.Simple( 0.2, function() if player.InShop then player.Menu["flags"] = MENUFLAG_NO_SOUND | MENUFLAG_BUTTON_EXIT player:DisplayMenu( player.Menu ) end end) elseif tonumber( value ) <= UPGRADE_REROLL then if ( player.m_nCurrency < math.floor( ( math.max( ( WAVE - 1 ), 1 ) * ( UPGRADE_REROLL_COST * UPGRADE_REROLL_COST_RAMP_COEF * ( player.Rerolls + 1 )^UPGRADE_REROLL_COST_RAMP_COEF ) ) ) ) then player:PlaySoundToSelf( "mvm/mvm_money_vanish.wav" ) player:PlaySoundToSelf( "mvm/mvm_money_vanish.wav" ) player:Print( PRINT_TARGET_CENTER, "Not enough money" ) timer.Simple( 0.2, function() if player.InShop then player.Menu["flags"] = MENUFLAG_NO_SOUND | MENUFLAG_BUTTON_EXIT end player:DisplayMenu( player.Menu ) end) return end player:PlaySoundToSelf( "ui/trade_up_apply_sticker.wav" ) player:PlaySoundToSelf( "ui/trade_up_apply_sticker.wav" ) player:RemoveCurrency( math.floor( ( math.max( ( WAVE - 1 ), 1 ) * ( UPGRADE_REROLL_COST * UPGRADE_REROLL_COST_RAMP_COEF * ( player.Rerolls + 1 )^UPGRADE_REROLL_COST_RAMP_COEF ) ) ) ) player.Rerolls = player.Rerolls + 1 player.InMenu = false player.InRoll = true timer.Simple( 0.2, function() BuildUpgradeMenu( player, player, math.abs( tonumber( value ) + 1 ) ) player.InRoll = false end) end end function UpgradeSkipped( player, reason ) if reason == MENU_CANCEL_NODISPLAY or reason == MENU_CANCEL_TIMEOUT or reason == MENU_CANCEL_INTERRUPTED then --player:DisplayMenu( player.Menu ) else player.InMenu = false player.InRoll = false --player.InShop = false player.Rerolls = 0 end if reason == MENU_CANCEL_EXIT then if not player.InShop then local total = 0 for i=1, player.DisplayedUpgrades do total = total + GetTypeCost(Upgrades[tonumber(player.Menu[i].value)].Type) end total = math.floor( total / player.DisplayedUpgrades ) player:AddCurrency( total ) player.InMenu = false player.InRoll = false --player.InShop = false player.Rerolls = 0 player:PlaySoundToSelf( "MVM.MoneyPickup" ) player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) else player.InMenu = false player.InRoll = false player.InShop = false player.Rerolls = 0 player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) end end end function GetUpgradesTableFiltered( type ) local new_tbl = {} local cate = UPGRADE_LOOT_CATEGORY if cate == UPGRADE_CATEGORY_ELITE_BOSS then cate = UPGRADE_CATEGORY_EPIC end print( type, UPGRADE_LOOT_CATEGORY ) for index, upgrade in pairs( Upgrades ) do if ( cate ~= UPGRADE_CATEGORY_ALL ) and ( cate ~= UPGRADE_CATEGORY_EPIC ) and ( cate ~= UPGRADE_CATEGORY_GREED ) and ( cate ~= UPGRADE_CATEGORY_SHOP ) then print( upgrade.Type, type, upgrade.Type == type, upgrade.Category, cate, upgrade.Category == cate ) if ( upgrade.Type == type ) and ( upgrade.Category == cate ) then table.insert( new_tbl, index ) end else if upgrade.Type == type then table.insert( new_tbl, index ) end end end if new_tbl == nil then for index, upgrade in pairs( Upgrades ) do if upgrade.Type == type then table.insert( new_tbl, index ) end end end return new_tbl end function IsUpgradeCapped( player, upgrade ) local capped = false for i = 1, #upgrade.Attributes do --print( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default ) ) if upgrade.Attributes[i].cap == nil then goto skip end if upgrade.Attributes[i].slot == LOADOUT_POSITION_BODY then if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) >= upgrade.Attributes[i].cap elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) <= upgrade.Attributes[i].cap elseif upgrade.Attributes[i].type == "set" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) == upgrade.Attributes[i].cap end elseif upgrade.Attributes[i].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) >= upgrade.Attributes[i].cap elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) <= upgrade.Attributes[i].cap elseif upgrade.Attributes[i].type == "set" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_PRIMARY ) == upgrade.Attributes[i].cap end else if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) >= upgrade.Attributes[i].cap elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) <= upgrade.Attributes[i].cap elseif upgrade.Attributes[i].type == "set" then capped = player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) == upgrade.Attributes[i].cap end end ::skip:: end return capped end function SetUpPlayerInfo( player ) if not player.Upgrades then player.DisplayedUpgrades = UPGRADE_DEFAULT_COUNT player.Rerolls = 0 player.MaxRerolls = UPGRADE_DEFAULT_MAX_REROLLS player.Upgrades = {} player.CostMult = 1 player.CashMult = 1 player.VoteMenu = nil for index, upgrade in pairs( Upgrades ) do player.Upgrades[index] = 0 end end --PrintTable( player.Upgrades ) player.InMenu = false player.InRoll = false player.InShop = false player.WithinAShop = false end function OnPlayerChat( player, text ) --print( text ) for s,_ in pairs(COMMANDS_TABLE) do --print( s, string.lower(COMMANDS_TABLE[s]), string.lower(text), string.match(string.lower(text), string.lower(COMMANDS_TABLE[s])) ) if ( string.match(string.lower(text), string.lower(COMMANDS_TABLE[s])) ) then BuildViewUpgradeMenu( player, player ) player.InRoll = true end end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ function findFreeBot() local chosen for _, bot in pairs(ents.GetAllPlayers()) do if not bot:IsRealPlayer() and not bot:IsAlive() and (bot.m_iTeamNum == 1 or bot.m_iTeamNum == 0) and bot:GetPlayerName() ~= "Demo-Bot" then chosen = bot break end end return chosen end function ApplyName( bot, data ) local displayName = data.Name bot.m_szNetname = displayName bot:SetFakeClientConVar("name", displayName) end function SpawnEnemy( data ) local callbacks = {} local botSpawn = findFreeBot() if not botSpawn then print( "GLOBAL BOT LIMIT REACHED" ) return end local Possible_Spawns = {} for index, data in pairs( ARENA_SPAWNS[ ARENA ] ) do if data.teamnum == 3 then table.insert( Possible_Spawns, data ) end end local pos = Possible_Spawns[ math.random( 1, #Possible_Spawns ) ].pos + Vector( 0, 0, 16 ) ApplyName( botSpawn, data ) botSpawn:SetAbsOrigin( pos ) botSpawn:SetAbsOrigin( pos ) botSpawn:SwitchClassInPlace( data.Class ) botSpawn:SwitchClassInPlace( data.Class ) botSpawn.m_iTeamNum = 3 botSpawn.m_iszClassIcon = "" botSpawn.CashMult = data.BaseCashMult botSpawn:SetCustomModelWithClassAnimations( ENEMY_MODEL_TABLE[ 0 ][botSpawn.m_iClass] ) botSpawn.DeathFunction = data.DeathFunc callbacks.died = botSpawn:AddCallback(ON_DEATH, function() for name, _ in pairs(botSpawn:GetAllAttributeValues()) do botSpawn:SetAttributeValue(name, nil) end botSpawn:ResetFakeSendProp("m_iTeamNum") botSpawn.m_iTeamNum = 1 removeCallbacks(botSpawn, callbacks) pcall( timer.Stop, botSpawn.logicLoop ) pcall( botSpawn.DeathFunction, botSpawn ) -- print( botSpawn.CashMult ) for _, player in pairs( ents.GetAllRealPlayers() ) do player:AddCurrency( ENEMY_BASE_CASH * botSpawn.CashMult * player.CashMult * ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_GLOBALCASH] ) end end) timer.Simple(0, function() -- if not ( botSpawn:IsAlive() and IsValid( botSpawn ) and botSpawn:OnTeam( 3 ) ) then -- return -- end botSpawn:SetAbsOrigin( pos ) botSpawn:SetAbsOrigin( pos ) botSpawn:SwitchClassInPlace( data.Class ) botSpawn:SwitchClassInPlace( data.Class ) botSpawn:SetAttributeValue("hidden maxhealth non buffed", ( data.Health - botSpawn.m_iHealth ) ) botSpawn:SetAttributeValue("SET BONUS: max health additive bonus", ( data.Health * GetEnemyHealthMult() ) - data.Health ) for i=0, 2 do -- Remove unused items, save on edicts/networked entities if i ~= data.DefaultSlot and ( data.Items[i] == nil ) then botSpawn:WeaponStripSlot( i ) end end botSpawn:SetActivePlayerWeapon( botSpawn:GetPlayerItemBySlot( data.DefaultSlot ) ) for i, name in pairs( data.Items ) do -- print( tostring( name ), botSpawn:GiveItem( tostring( name ), nil, false, true ) ) botSpawn:GiveItem( tostring( name ), nil, false, true ) end for name, value in pairs(data.CharacterAttributes) do botSpawn:SetAttributeValue(name, value) end for i = 0, 10 do if IsValid( botSpawn:GetPlayerItemBySlot( i ) ) and ( data.ItemAttributes[i] ~= nil ) then for name, value in pairs(data.ItemAttributes[i]) do botSpawn:SetAttributeValue(name, value) end end end for cond, _ in pairs( data.AddConds ) do botSpawn:AddCond( cond ) end botSpawn:RunScriptCode( "activator.ClearBehaviorFlag(1023)", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ClearAllWeaponRestrictions()", botSpawn, botSpawn ) if ( data.Variants ) and ( data.Variants[1] ) then if math.random() <= ( VARIANT_CHANCE * ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] ) then local variant = data.Variants[ math.WeightedRandom( data.Variants ) ] local displayName = variant.Append_Name .. data.Name botSpawn.m_szNetname = displayName botSpawn:SetFakeClientConVar("name", displayName) for name, value in pairs(variant.CharacterAttributes) do botSpawn:SetAttributeValue(name, value) end for i = 0, 10 do if IsValid( botSpawn:GetPlayerItemBySlot( i ) ) and ( variant.ItemAttributes[i] ) then for name, value in pairs(variant.ItemAttributes[i]) do botSpawn:SetAttributeValue(name, value) end end end for i, name in pairs( variant.Items ) do -- print( tostring( name ), botSpawn:GiveItem( tostring( name ), nil, false, true ) ) botSpawn:GiveItem( tostring( name ), nil, false, true ) end for cond, _ in pairs( variant.AddConds ) do botSpawn:AddCond( cond ) end end end --print( botSpawn.m_nBotSkill ) botSpawn:RunScriptCode( "activator.AddBotAttribute(" .. tostring( data.Attributes ) .. ")", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.AddWeaponRestriction(" .. tostring( data.WeaponRestrictions ) .. ")", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.SetDifficulty(" .. tostring( data.Skill ) .. ")", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.SetMaxVisionRangeOverride(" .. tostring( data.VisionRange ) .. ")", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.SetScaleOverride(" .. tostring( data.Scale ) .. ")", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ForceChangeTeam(3, true)", botSpawn, botSpawn ) -- print( botSpawn.m_bIsMiniBoss, botSpawn.m_iClass, ENEMY_MODEL_TABLE ) botSpawn:SetCustomModelWithClassAnimations( ENEMY_MODEL_TABLE[ botSpawn.m_bIsMiniBoss ][botSpawn.m_iClass] ) --botSpawn:RunScriptCode( "printl( activator.HasBotAttribute( 16 ) )", botSpawn, botSpawn ) botSpawn:BotCommand( "switch_action Mobber" ) botSpawn:BotCommand( "switch_action " .. tostring( data.Action ) ) util.ParticleEffect( "teleportedin_blue", botSpawn:GetAbsOrigin() ) timer.Create( 0.1, function() botSpawn:BotCommand( "switch_action " .. tostring( data.Action ) ) botSpawn:SetAbsOrigin( pos ) botSpawn:SetActivePlayerWeapon( botSpawn:GetPlayerItemBySlot( data.DefaultSlot ) ) end, 3 ) botSpawn.logicLoop = timer.Create(0.1, function() if not ( botSpawn:IsAlive() and IsValid( botSpawn ) and botSpawn:OnTeam( 3 ) ) then pcall( timer.stop, botSpawn.logicLoop ) return end --print( botSpawn ) pcall( data.LogicFunc, botSpawn, data ) end, 0 ) pcall( data.SpawnFunc, botSpawn, data ) end) return botSpawn end --[[----------------+ WAVES +------------------]]-- function SetWaveCount( count ) WAVE = tonumber(count) end function ResetEncounterModifiers() ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] = 1 ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_VOTE_OPTIONS] = 0 ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, } end function HardResetEncounterModifiers() ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] = 1 ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_VOTE_OPTIONS] = 0 ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_GLOBALCASH] = 1 ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_ENEMYMULT] = 1 ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, } end function InitWave() pcall( timer.Stop, SpawnTimer ) ResetEncounterModifiers() WAVE = 0 TotalCount = 0 SpawnTimer = nil InWave = false SHOP_WAVE = false ARENA = 1 ents.FindByClass('tf_objective_resource').m_nMannVsMachineMaxWaveCount = 0 ents.FindByClass('tf_objective_resource').m_nMannVsMachineWaveCount = 0 VOTING = false VOTE_OPTIONS = 3 VOTE_RESULTS = {} VOTE_MENU = nil for _, player in pairs( ents.GetAllRealPlayers() ) do for index, upgrade in pairs( Upgrades ) do player.DisplayedUpgrades = UPGRADE_DEFAULT_COUNT player.Rerolls = 0 player.MaxRerolls = UPGRADE_DEFAULT_MAX_REROLLS player.Upgrades = {} player.CostMult = 1 player.CashMult = 1 player.VoteMenu = nil for index, upgrade in pairs( Upgrades ) do player.Upgrades[index] = 0 end player.InMenu = false player.InRoll = false player.InShop = false player.WithinAShop = false player:HideMenu() end end end function StartWave() VOTE_MENU = nil for i = 0, #VOTE_CATEGORIES do VOTE_RESULTS[i] = {} end WAVE = WAVE + 1 WAVE_DURATION = CurTime() + WAVE_HORDE_DURATION + 5 ents.FindByClass('tf_objective_resource').m_nMannVsMachineWaveCount = WAVE InWave = true WAVE_TYPE_CURRENT = math.WeightedRandom( WAVE_TYPE_CHANCE_TABLE ) SHOP_WAVE = tobool( math.floor( WAVE / ( 6 + ( math.floor( ( WAVE + 0.5 ) / 7 ) * 7 ) ) ) ) TotalCount = math.floor( GetEnemyCount() ) ARENA = math.random( 1, #ARENA_SPAWNS ) StartDelay = nil ResetEncounterModifiers() local Modif = math.floor( WAVE/7 ) * 7 if UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_EPIC then ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 0.8, [ENEMY_TYPE_MINIGIANT] = 1.2, [ENEMY_TYPE_GIANT] = 1.2, [ENEMY_TYPE_BOSS] = 0, } elseif UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_ELITE_BOSS then ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 0, [ENEMY_TYPE_MINIGIANT] = 0, [ENEMY_TYPE_GIANT] = 0, [ENEMY_TYPE_BOSS] = 1, } TotalCount = 1 else ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, } end if WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE then ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 0.25, [ENEMY_TYPE_GIANT] = 0.1, [ENEMY_TYPE_BOSS] = 0, } end print( "Before Chance", ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_COMMON], ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_MINIGIANT] ) for i=1, #ENEMY_TYPE_CHANCE_MULT do ENEMY_TYPE_CHANCE_MULT[i] = ENEMY_TYPE_CHANCE_MULT[i] * ENEMY_TYPE_CHANCE_SCALE_MULT[ WAVE - Modif ][i] end print( WAVE - Modif, "After Chance", ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_COMMON], ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_MINIGIANT] ) if SHOP_WAVE == false then local Possible_Spawns = {} for index, data in pairs( ARENA_SPAWNS[ ARENA ] ) do if data.teamnum == 2 then table.insert( Possible_Spawns, data ) end end for _, player in pairs( ents.GetAllRealPlayers() ) do player:HideMenu() player.InShop = false player:AcceptInput( "RunScriptCode", "ScreenFade( !activator, 255, 255, 255, 255, 1, 0.5, 2 )", player, player ) player:AddCond( 87, 2 ) timer.Simple( 1, function() player:SetAbsOrigin( Possible_Spawns[ math.random( 1, #Possible_Spawns ) ].pos + Vector( 0, 0, 16 ) ) player:AcceptInput( "RunScriptCode", "ScreenFade( !activator, 255, 255, 255, 255, 1, 0, 1 )", player, player ) player:Print( 2, "Loot on Victory: " .. VOTE_CATEGORIES[ UPGRADE_LOOT_CATEGORY ].name .. "!" ) end) timer.Simple( 2, function() player:RemoveCond( 87 ) end) end local BaseEnemies = TotalCount local SpawnedCount = 0 local SpawnDelay = 0 local StillSpawning = true TotalCount = math.floor( TotalCount * ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_ENEMYMULT] ) -- print( math.WeightedRandom( ENEMY_TEMPLATES ), GetEnemyCount(), GetEnemyHealthMult() ) timer.Simple( 1, function() SpawnTimer = timer.Create( 0.1, function() if WAVE_TYPE_CURRENT == WAVE_TYPE_STANDARD then if CurTime() >= SpawnDelay and ( #ents.GetAllAliveBots() < ENEMY_BASE_MAXACTIVE_COUNT ) then SpawnDelay = CurTime() + math.randomfloat( 1, 3 ) for i=1, math.random( math.min(math.floor( TotalCount / 10 ), 3), math.min(math.ceil( TotalCount / 7 ), 5) ) do if ( #ents.GetAllAliveBots() >= ENEMY_BASE_MAXACTIVE_COUNT ) then break end if SpawnedCount >= TotalCount then StillSpawning = false break end SpawnedCount = SpawnedCount + 1 local bot = SpawnEnemy( ENEMY_TEMPLATES[math.WeightedRandomEnemy( ENEMY_TEMPLATES )] ) bot.CashMult = bot.CashMult * ( TotalCount / BaseEnemies ) end end -- print( StillSpawning, TotalCount, SpawnedCount, SpawnedCount >= TotalCount, #ents.GetAllAliveBots() ) if StillSpawning == false and ( #ents.GetAllAliveBots() <= 0 ) then pcall( timer.Stop, SpawnTimer ) timer.Simple( 1, function() EndWave() end) return end elseif WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE then if CurTime() >= SpawnDelay and ( #ents.GetAllAliveBots() < ENEMY_BASE_MAXACTIVE_COUNT ) then SpawnDelay = CurTime() + 1 for i=1, math.random( 1, 3 ) do if ( #ents.GetAllAliveBots() >= ENEMY_BASE_MAXACTIVE_COUNT ) then break end SpawnedCount = SpawnedCount + 1 local bot = SpawnEnemy( ENEMY_TEMPLATES[math.WeightedRandomEnemy( ENEMY_TEMPLATES )] ) if BaseEnemies <= SpawnedCount then bot.CashMult = 0 end end end if CurTime() > WAVE_DURATION then pcall( timer.Stop, SpawnTimer ) timer.Simple( 1, function() EndWave() end) return end end end, 0) end) else for _, player in pairs( ents.GetAllRealPlayers() ) do player:HideMenu() player.InShop = true player:AcceptInput( "RunScriptCode", "ScreenFade( !activator, 255, 255, 255, 255, 1, 0.5, 2 )", player, player ) player:AddCond( 87, 2 ) timer.Simple( 1, function() player:SetAbsOrigin( Vector( 1088, -3623, 64 ) ) player:AcceptInput( "RunScriptCode", "ScreenFade( !activator, 255, 255, 255, 255, 1, 0, 1 )", player, player ) player:Print( 2, "Shop Time" ) end) timer.Simple( 2, function() InWave = false player:RemoveCond( 87 ) BuildUpgradeMenu( player, player, UPGRADE_LOOT_SHOP ) end) end end end function TestToggleShop() for _, player in pairs( ents.GetAllPlayers() ) do if player.InShop == false then player.InShop = true else player.InShop = false end end end function EndWave() pcall( timer.Stop, SpawnTimer ) SpawnTimer = nil TotalCount = 0 for _, bot in pairs( ents.GetAllAliveBots() ) do bot:Suicide() end for _, player in pairs( ents.GetAllRealPlayers() ) do local loot = UPGRADE_LOOT_COMMON if UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_GREED then loot = UPGRADE_LOOT_GREED elseif UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_EPIC or UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_ELITE_BOSS then loot = UPGRADE_LOOT_EPIC end BuildUpgradeMenu( player, player, loot ) end InWave = false end function WaveThink() if not StartDelay then StartDelay = 0 end --print( ( not InWave ), ( WAVE > 0 ) ) if ( not InWave and ( WAVE > 0 ) ) or ( SHOP_WAVE == true ) then for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not player.InMenu ) and ( not player.InRoll ) then BuildVoteMenu( player ) if StartDelay and ( StartDelay > CurTime() ) then player:Print( 2, "Next Wave Starts In: " .. tostring( math.ceil( StartDelay - CurTime(), 0 ) ) ) end end end if GetAllVotes() >= #ents.GetAllRealPlayers() then if StartDelay == 0 then StartDelay = CurTime() + 10 end if CurTime() >= StartDelay then UPGRADE_LOOT_CATEGORY = GetVoteWinner() StartWave() end end end -- if InWave and ( WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE ) then -- for _, player in pairs( ents.GetAllRealPlayers() ) do -- player:Print( 2, "¡HORDE! Time Left: " .. tostring( math.ceil( WAVE_DURATION - CurTime(), 0 ) ) .. " Seconds" ) -- end -- end timer.Simple( 0.1, function() WaveThink() end) end --[[----------------+ HOOKS +------------------]]-- local hooks = {} hooks.Add = function( name, event, callback ) --print( name, event, callback ) hooks[ tostring( name ) ] = AddEventCallback( tostring( event ), callback ) --PrintTable( hooks ) end hooks.Add( "ON_SPAWN", "player_spawn", function( event ) local userid = event.userid local class = event.class local player = ents.GetPlayerByUserId( tonumber( userid ) ) --print(class, player.OldClass) if player:IsRealPlayer() then SetUpPlayerInfo( player ) timer.Simple( 0.015, function() -- local items = {} -- table.insert( items, player:GetPlayerItemBySlot(0):GetItemName() ) -- table.insert( items, player:GetPlayerItemBySlot(1):GetItemName() ) -- table.insert( items, player:GetPlayerItemBySlot(2):GetItemName() ) player:WeaponStripSlot( 0 ) player:WeaponStripSlot( 1 ) player:WeaponStripSlot( 2 ) -- for _, weapon in pairs( items ) do -- player:GiveItem( weapon ) -- end for attr, val in pairs( player:GetAllAttributeValues() ) do player:SetAttributeValue( attr, nil ) end timer.Simple( 0.015, function() player:Regenerate() -- if ( ( player.OldClass ~= nil ) and ( class ~= player.OldClass ) ) then ApplyMissingUpgrades( player ) -- end -- player.OldClass = class end) end) end -- if player:IsValid() then -- player.ArmorPlates = 0 -- player.MaxArmorPlates = 6 -- end end) --[[----------------+ SETUP +------------------]]-- WaveThink() for i = 0, #VOTE_CATEGORIES do VOTE_RESULTS[i] = {} end for _, shop in pairs( ents.FindAllByClass( "func_upgradestation" ) ) do shop:AddCallback( ON_START_TOUCH, function( ent, other, hitPos, hitNormal ) -- if other.InShop == false and ( other.InMenu == false ) then -- other.InShop = true other.WithinAShop = true -- BuildUpgradeMenu( other, other, UPGRADE_LOOT_COMMON ) -- end end) shop:AddCallback( ON_END_TOUCH, function( ent, other, hitPos, hitNormal ) other.WithinAShop = false end) end print( "hippopotamus-fuck" )