--Made by Wacev --I used the build-a-bot script as a *template* for the enemy spawning logic --Hi Royal :hiwave: -- no AI was used in the making of this project -- Credits List (of the ones I know): -- Royal for making the code the enemy spawn logic is based off of (and just being super cool) -- 200 for hexagon forcefields -- Entropic for the ammo pickup prefabs -- The people behind the construction tf2maps asset pack -- Guymouser for providing a lot of ideas and feedback (especially early on) -- And to everyone who has played and given feedback on this mission :) -- Todo List: -- Low Priority: -- Overclocks -- Make group of enemies spawn together -- disease spreader: upon being hit, your max health rapidly drains for several seconds to a point where it remains low for a while before restoring back to max, slower projectile speed (giant ranged repair variant) -- Redo Double Dip, cap of 3, for each tick: re-roll the rarity greeds, epics, and legendaries (high roller re-rolls after each DD re-roll) [maybe] -- Rework "Tank" greed -- Blood loss rework, max hp drains to your current hp (when below max), more hp = faster drain -- Buff gemstone to 7.5% (maybe) -- Medium Priority: -- CTF -- Add MP40 -- Heal rate ring -- SMGS are LOUD -- High Priority: -- Gauntlet -- Add /stats command -- Prerequisite upgrades -- Feedback: -- Increase Sellout's effect on Paycheck/Passive Income so you don't gain more money passively than you spent buying a billion upgrades last shop, (Passive Income combined with Sellout lets you gain more money than you spent buying tons of upgrades, meaning you just scale to the point that you one-shot everything and it isn't fun. For example, buying 15 rings of power in a single shop, or getting 15 paychecks from an epic shop;) -- Lower the health-pools of the Artillery and Master Builder, since they outright control the battlefield but don't die to focus fire. I've often focused an artillery down and killed everything else with splash damage before the artillery died, so their health seems bloated as hell, (The Artillery doesn't die to focus fire, which is annoying because he fully dictates the battle and makes it so you can't hide from line-of-sight elites, so he has to die first, but then he doesn't die;) -- Ok latest addition: figure out why the harpooners can just drag you horizontally into the lava instead of towards them like what the fuck (The harpooners can just sometimes drag you straight into the lava/void instead of towards them, and since you can't move, you're just dead with no recourse like what the fuck bro just command-grabbed me what the FUCK) --[[----------------+ MISC VARS +------------------]]-- GLOBAL_PLAYER_UPGRADES = {} -- big 'ol table of player upgrades, should save upgrades between rejoins LEAVERS = {} -- anti retry cheese UPGRADES_CATES = {} -- list of all upgrade categories that were chosen for late joiners GLOBAL_PLAYER_DATA = {} -- used to store important info, like last join times for data wiping PLAYER_WIPE_GRACE = 7 -- number of waves to wait before wiping absent players' data Global = ents.FindByClass( "tf_gamerules" ) PlayerTable = {} PlayerCount = 0 COMMANDS_TABLE = { { name = "!Upgrades", type = "viewupgrades" } , { name = "/Upgrades", type = "viewupgrades" } , { name = "!Flex", type = "flexoptions" } , { name = "/Flex", type = "flexoptions" } , { name = "!Info", type = "infomenu" } , { name = "/Info", type = "infomenu" } , { name = "/Changelog", type = "changelog" }, { name = "!ChangeLog", type = "changelog" } } local ChangeLog = { "+ Added \"Fresh Bullets\" damage perk", "+ Added \"Fire Arms Training\" and \"BOGO Card\" Bourgeoi$ie perks", "- Nerfed Corrupted Sniper's projectile speed to be the same as normal sniper" } -- MINIGIANTS_USED = {} GIANTS_USED = {} buildings = { obj_sentrygun = 1, obj_dispenser = 2, obj_teleporter = 3 } local CHEATERS = { ["[U:1:1770405241]"] = true, ["[U:1:1704340995]"] = true, ["[U:1:1922018405]"] = true, ["[U:1:1904913778]"] = true, ["[U:1:1062396597]"] = true, ["[U:1:1916202565]"] = true, ["[U:1:1723121518]"] = true, ["[U:1:1877411735]"] = true, ["[U:1:221152966]"] = true, ["[U:1:1883157641]"] = true, ["[U:1:1885514529]"] = true, ["[U:1:1644350202]"] = true, ["[U:1:1841779628]"] = true, ["[U:1:349826851]"] = true, ["[U:1:386700108]"] = true, ["[U:1:97203339]"] = true, ["[U:1:74057001]"] = true, ["[U:1:1712924683]"] = true, ["[U:1:1786692235]"] = true, ["[U:1:1314698454]"] = true, ["[U:1:1268276814]"] = true, ["[U:1:1007530715]"] = true, ["[U:1:432526137]"] = true, ["[U:1:1017755058]"] = true, ["[U:1:1735802732]"] = true, ["[U:1:172744906]"] = true, ["[U:1:1719864092]"] = true, ["[U:1:1415441535]"] = true, ["[U:1:418343582]"] = true, ["[U:1:399239913]"] = true, ["[U:1:145288356]"] = true, ["[U:1:1267741971]"] = true, ["[U:1:41024556]"] = true, ["[U:1:213021739]"] = true, ["[U:1:425291587]"] = true, ["[U:1:164500106]"] = true, ["[U:1:837368705]"] = true, ["[U:1:130490845]"] = true, ["[U:1:114550444]"] = true, ["[U:1:89784922]"] = true, ["[U:1:874295988]"] = true, ["[U:1:1427498087]"] = true, ["[U:1:400236800]"] = true, ["[U:1:1302787525]"] = true, ["[U:1:1183809195]"] = true, ["[U:1:1572558078]"] = true, ["[U:1:1251129637]"] = true, ["[U:1:427894071]"] = true, ["[U:1:1226042555]"] = true, ["[U:1:1018883373]"] = true, ["[U:1:67368924]"] = true, ["[U:1:34513941]"] = true, ["[U:1:92521691]"] = true, ["[U:1:143642940]"] = true, ["[U:1:1049365161]"] = true, ["[U:1:86699889]"] = true, ["[U:1:103643619]"] = true, ["[U:1:412199207]"] = true, ["[U:1:134142630]"] = true, ["[U:1:1008509390]"] = true, ["[U:1:237981327]"] = true, ["[U:1:840925169]"] = true, ["[U:1:477029644]"] = true, ["[U:1:1924480034]"] = true, ["[U:1:1101504870]"] = true, ["[U:1:1547208917]"] = true, ["[U:1:1365131859]"] = true, ["[U:1:89784922]"] = true, ["[U:1:1226799266]"] = true, ["[U:1:1173812518]"] = true, ["[U:1:998943685]"] = true, ["[U:1:1433062718]"] = true, ["[U:1:1173714004]"] = true, ["[U:1:819776027]"] = true, ["[U:1:1145096557]"] = true, ["[U:1:823667499]"] = true, ["[U:1:830605347]"] = true, ["[U:1:1307419174]"] = true, ["[U:1:1256195134]"] = true, ["[U:1:1202421555]"] = true, ["[U:1:1301528028]"] = true, ["[U:1:322547794]"] = true, ["[U:1:1665468033]"] = true, ["[U:1:259701995]"] = true, ["[U:1:1033125380]"] = true, ["[U:1:882994776]"] = true, ["[U:1:1683068248]"] = true, ["[U:1:891985556]"] = true, ["[U:1:1400389325]"] = true, ["[U:1:74094238]"] = true, ["[U:1:1278691361]"] = true, ["[U:1:1520275434]"] = true, ["[U:1:1862471601]"] = true, ["[U:1:1296772921]"] = true, ["[U:1:95255355]"] = true, ["[U:1:1478524000]"] = true, ["[U:1:1870229588]"] = true, ["[U:1:1458906148]"] = true, ["[U:1:1623515566]"] = true, ["[U:1:1156944372]"] = true, ["[U:1:1930300425]"] = true, ["[U:1:791838941]"] = true, ["[U:1:1910463882]"] = true, ["[U:1:788649579]"] = true, ["[U:1:1130902302]"] = true, ["[U:1:427719259]"] = true, ["[U:1:785381766]"] = true, ["[U:1:781284899]"] = true, ["[U:1:67538603]"] = true, ["[U:1:782905859]"] = true, ["[U:1:818164532]"] = true, ["[U:1:33568711]"] = true, ["[U:1:382989309]"] = true, ["[U:1:1315783641]"] = true, } DISGUISE_AUTO_DROP_DELAY = 30 DISGUISE_AUTO_DROP_DELAY_HORDE = 20 local IsOtherUpload PlayerDefaultHealth = { [TF_CLASS_SCOUT] = 125, [TF_CLASS_SNIPER] = 125, [TF_CLASS_ENGINEER] = 125, [TF_CLASS_SPY] = 125, [TF_CLASS_MEDIC] = 150, [TF_CLASS_DEMOMAN] = 175, [TF_CLASS_PYRO] = 175, [TF_CLASS_SOLDIER] = 200, [TF_CLASS_CIVILIAN] = 200, [TF_CLASS_HEAVYWEAPONS] = 300, } PlayerDefaultMovespeed = { [TF_CLASS_SCOUT] = 400, [TF_CLASS_SNIPER] = 300, [TF_CLASS_ENGINEER] = 300, [TF_CLASS_SPY] = 320, [TF_CLASS_MEDIC] = 320, [TF_CLASS_DEMOMAN] = 280, [TF_CLASS_PYRO] = 300, [TF_CLASS_SOLDIER] = 240, [TF_CLASS_HEAVYWEAPONS] = 230, } --[[----------------+ WAVE VARS +------------------]]-- WEAPON_FUEL_LEAK_PARTICLES = 0 WEAPON_FUEL_LEAK_PARTICLES_MAX = 100 WAVE = 0 TotalCount = 0 SpawnTimer = nil InWave = false InWave = false WaveStarted = false StillSpawning = false GLOBAL_CASH_MULT = 1 VARIANT_CHANCE = 0.05 SHOP_WAVE = false ARENA = 1 ARENA_MAX = 1 local capture_area_size_small = 256 local capture_area_size_medium = 320 local capture_area_size_large = 512 local capture_area_size_xl = 768 WAVE_FORCEDFLEXES = {} WAVE_CAPTUREPOINTS_CAPTURED = 0 WAVE_CAPTUREPOINTS_AMOUNT = 2 WAVE_CAPTUREPOINTS_AMOUNT_XLADD = 2 WAVE_CAPTUREPOINTS_DURATION = 65 WAVE_CAPTUREPOINTS_WAVE_BREAKDURATION = 10 WAVE_CAPTUREPOINTS_INFOTABLE = { [1] = { { pos = Vector( 0, 0, -56 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( 0, 0, -312 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( 0, 640, -56 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( 0, -640, -56 ), cap_duration = 15, size = capture_area_size_small }, }, [2] = { { pos = Vector( 96, 2816, -184 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( -544, 4288, -120 ), cap_duration = 15, size = capture_area_size_medium }, { pos = Vector( 928, 4472, -184 ), cap_duration = 15, size = capture_area_size_large }, { pos = Vector( 224, 3552, -184 ), cap_duration = 15, size = capture_area_size_medium }, }, [3] = { { pos = Vector( -2496, -3968, 16 ), cap_duration = 15, size = capture_area_size_medium }, { pos = Vector( 2496, -3968, 16 ), cap_duration = 15, size = capture_area_size_medium }, { pos = Vector( 0, -4000, 56 ), cap_duration = 22, size = capture_area_size_large }, -- no bots disrupt it, 50% longer capture time { pos = Vector( -32, -6432, 16 ), cap_duration = 7, size = capture_area_size_large }, -- very dangerous, 100% faster cap time }, [4] = { { pos = Vector( 8128, 456, -120 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( 8128, -456, -120 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( 8128, 896, -120 ), cap_duration = 15, size = capture_area_size_small }, { pos = Vector( 8128, -896, -120 ), cap_duration = 15, size = capture_area_size_small }, }, } WAVE_CAPTUREPOINTS_TRACKED = {} WAVE_REVIVES = 0 WAVE_REVIVE_PENALTY = 1.2 WAVE_AUTO_START_TIMER = 30 WAVE_AUTO_START_TIMER_SHOP = 60 WAVE_AUTO_START_MIN_JUMP = 5 WAVE_TIMELIMIT_MIN = 0.25 WAVE_TIMELIMIT_START = 1.25 WAVE_TIMELIMIT_DECAY = 0.2 WAVE_TIMELIMIT_TIME = 0 WAVE_TIMELIMIT_MAX = 0 -- 1 + math.max( WAVE_TIMELIMIT_START - ( WAVE_TIMELIMIT_START * ( math.floor( WAVE / ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) ) * WAVE_TIMELIMIT_DECAY ) ) + WAVE_TIMELIMIT_MIN, WAVE_TIMELIMIT_MIN ) INFO_DISPLAY_TABLE = { { Name = "Commands", ViewedInfo = "Chat Commands that open specific menus \n Available Commands: \n /Upgrades \n ^ Brings up a menu of ever upgrade you have \n You can salvage upgrades for some money \n /Flex \n ^ Brings up a menu of optional difficulty modifiers" }, { Name = "Credits", ViewedInfo = "Every few encounters you will be bought to a shop \n You spend credits at the shop \n Killing enemies earns credits for the whote team \n Other sources of credits are not shared \n Spent credits are not shared \n Credits are also used for rerolling perks" }, { Name = "Shops", ViewedInfo = "You spend credits at a shop \n For shops only: Voting is replaced with ready up, you now choose the shop you go to \n Standard random loot shop will always be there \n All Bourgeoisie items are rare price or high, and most are one-off (not stackable) \n Bourgeoisie items CANNOT be discounted" }, { Name = "Respawns", ViewedInfo = "You will respawn 30 seconds after you die \n You CANNOT buy back in this mode \n You will be auto respawned on the end of an encounter \n Medics incur a global revive rate penalty based on the number of revives per encount" }, { Name = "Encounters", ViewedInfo = "Every ''wave'' will either be an encounter, shop, or elite fight \n There are 5 encounters, followed by a shop and an elite fight \n You will be able to pick one perk after every encounter \n After 28 total waves: you can either end the game or continue with a random flex added" }, { Name = "Waves", ViewedInfo = "Every encounter will have it's own wave type \n Wave Types: Invasion (kill x number of enemies) \n Horde (survive for x number of seconds) \n Control Points (capture x number of points with the allotted time) \n Wave type is displayed next to it's name \n Waves have a 5% chance to be XL \n XL Waves have a lot more enemies but give you 2 loot rewards \n ^ (Two separate drops in a queue, unlike Double Dip)" }, { Name = "Scaling", ViewedInfo = "The amount of enemies, their hp, damage, kb resistance, and move speed increases each ''wave'' \n Every 7 encounters, enemies receive a boost in their hp, increases with player count \n Enemies also gain a boost in damage after each elite fight \n Health scaling is exponential, rest are linear" }, { Name = "Upgrade Tiers", ViewedInfo = "There are 6 different tiers of upgrades: \n Common, Uncommon, Rare, Epic, Legendary \n The 6th rarity is GREED \n GREED Upgrades have a strong upside and downside \n GREED Upgrades can be salvaged but for $0 \n You can close out of non-shop loot menus to skip \n Skipping gives you money related to the average cost of the perks \n You can reroll all perks at the cost of credits" }, { Name = "Loot Types", ViewedInfo = "Every encounter will give you different type of loot: \n Health (max hp, hp regen, resistances) \n Damage (weapon damage, melee damage, crit rate) \n Weapon (non-damage weapon upgrades, ammo, blast radius, uber rate) \n Money (credits and discounts) \n Speed (move speed, fire rate, proj speed) \n Random Loot \n GREED (strong upside and downside, salvaging gives $0) \n Bourgeoisie are unique but strong buffs, (usually) not stackable, and sell for 100% \n Elite Fights gives a Random Epic" }, { Name = "Flexes", ViewedInfo = "Flexes are optional difficulty modifiers \n They range from small enemy buffs like more enemy variants to losing max health when damaged" }, { Name = "Enemies", ViewedInfo = "Their are 4 tiers of enemies: \n Common, Mini-giant, Giant, Elite \n Commons are the default sized enemies, weak but numerous \n Mini-giants are the bulk of a wave \n Giants are rare but strong \n Elites only show up on Elite Fights \n The number of Minigiants, Giants, and Elites scales with current wave \n Enemies have a chance to be a ''variant'' \n This is a randomly chosen buff for them \n GREED encounters have 5x the chance for enemies to be a variant \n Some enemies will lead their shots, watch out" }, } -- DEV FUNCTIONS {{ 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 GiveUpgrade( index, dev ) -- print( index, dev ) Upgradebought( dev, true, index ) -- print( IsUpgradeCapped( dev, tonumber(index), Upgrades[tonumber(index)].usePlayerUpgradesTable ) ) end function ForceSpawnEnemy( enemy, dev ) SpawnEnemy( ENEMY_TEMPLATES[tostring(enemy)] ) end function GiveCashFromWave( wave, dev ) dev.m_nCurrency = GetCashByWave( wave ) print(GetCashByWave( wave )) end -- }} MODIFY_VARIANT_CHANCE = 1 MODIFY_VOTE_OPTIONS = 2 MODIFY_ENEMY_GLOBALCASH = 3 MODIFY_ENEMY_MULT = 4 MODIFY_ENEMY_GOLDEN_CHANCE = 5 MODIFY_ENEMY_MAX_GIANTS = 6 MODIFY_ENEMY_MAX_MINIGIANTS = 7 ENCOUNTER_MODIFY_STATS = { [MODIFY_VARIANT_CHANCE] = 1, [MODIFY_VOTE_OPTIONS] = 0, [MODIFY_ENEMY_GLOBALCASH] = 1, [MODIFY_ENEMY_MULT] = 1, [MODIFY_ENEMY_GOLDEN_CHANCE] = 0, [MODIFY_ENEMY_MAX_GIANTS] = 1, [MODIFY_ENEMY_MAX_MINIGIANTS] = 2, } MISSION_MODIFY_EXTRABOSS = 1 MISSION_MODIFY_FAST_ENEMIES = 2 MISSION_MODIFY_VARIANT_INCREASED = 3 MISSION_MODIFY_EXTRAGIANT = 4 MISSION_MODIFY_LOSEHEALTHONHIT = 5 MISSION_MODIFY_NORESPAWN = 6 MISSION_MODIFY_REROLL_COST = 7 MISSION_MODIFY_ENEMY_SPAWNRATE = 8 MISSION_MODIFY_BOSSVARIANTS = 9 MISSION_MODIFY_GIANTVARIANTS = 10 MISSION_MODIFY_TIMELIME = 11 -- DO NOT QUESTION THE TIME LIME MISSION_MODIFY_BIG_PROBLEM = 12 MISSION_MODIFY_CHALLENGES = 13 MISSION_MODIFY_STATS = { [MISSION_MODIFY_EXTRABOSS] = {enabled = false, PrintName = "Extra Elites", Description = "+50% More elite bosses spawn", Weight = 1}, [MISSION_MODIFY_FAST_ENEMIES] = {enabled = false, PrintName = "Quick Silver", Description = "Enemies move 25% Faster", Weight = 1}, [MISSION_MODIFY_VARIANT_INCREASED] = {enabled = false, PrintName = "Variety Is The Spice Of Life", Description = "Enemies are 3x more likely to be a variant and unlimited enemies types active", Weight = 1}, [MISSION_MODIFY_EXTRAGIANT] = {enabled = false, PrintName = "Extra Giants", Description = "+50% Max giants active", Weight = 1}, [MISSION_MODIFY_LOSEHEALTHONHIT] = {enabled = false, PrintName = "Blood Loss", Description = "Permanently lose max health when taking damage from enemies", Weight = 0.5}, [MISSION_MODIFY_NORESPAWN] = {enabled = false, PrintName = "One Life", Description = "Natural respawning during fights is disabled, Revives are limited to 1 per encounter per person", Weight = 0.85}, [MISSION_MODIFY_REROLL_COST] = {enabled = false, PrintName = "Expensive Die", Description = "Rerolling is 4x more expensive", Weight = 1}, [MISSION_MODIFY_ENEMY_SPAWNRATE] = {enabled = false, PrintName = "Swarms", Description = "Enemies spawn 33% faster and max enemies increased by 50%", Weight = 1}, [MISSION_MODIFY_BOSSVARIANTS] = {enabled = false, PrintName = "Elite Variety Pack", Description = "Every elite enemy is a variant", Weight = 0.85}, [MISSION_MODIFY_GIANTVARIANTS] = {enabled = false, PrintName = "Giant Variety Pack", Description = "Every giant, non-elite enemy is a variant", Weight = 0.9}, [MISSION_MODIFY_TIMELIME] = {enabled = false, PrintName = "On Time", Description = "Every Wave will have a time limit, going into overtime will make you take increasing % damage passively", Weight = 1}, [MISSION_MODIFY_BIG_PROBLEM] = {enabled = false, PrintName = "Big Problem", Description = "Giants and Elites have 50% extra hp and spawn with high damage resistance that decays over time and is unpierceable", Weight = 1.5}, -- [MISSION_MODIFY_CHALLENGES] = {enabled = false, PrintName = "Challenge Run", Description = "[WIP] Every encounter has 2 randomly chosen challenges assigned to them", Weight = 0}, } VOTING = false VOTE_OPTIONS = 3 VOTE_RESULTS = {} VOTE_MENU = nil UPGRADE_DEFAULT_MENU = { timeout = 0, title = "Choose an Upgrade!", itemsPerPage = 5, flags = MENUFLAG_BUTTON_EXIT | MENUFLAG_NO_SOUND, onSelect = Upgradebought, onCancel = UpgradeSkipped, } UPGRADE_VIEW_MENU = { timeout = 0, title = "Your Upgrades (Press to salvage)", itemsPerPage = 5, flags = MENUFLAG_BUTTON_EXIT, onSelect = nil, onCancel = nil, } UPGRADE_WARNING_MENU = { timeout = 0, title = "Are you sure?", itemsPerPage = nil, flags = MENUFLAG_BUTTON_EXITBACK, onSelect = nil, onCancel = nil, } UPGRADE_VOTING_MENU = { timeout = 0, title = "Vote on Next Victory Loot!", itemsPerPage = nil, flags = 0, onSelect = Voted, onCancel = nil, } FLEX_MENU = { timeout = 0, title = "Enable/Disable optional flex modifier!", itemsPerPage = 5, flags = MENUFLAG_BUTTON_EXIT, onSelect = nil, onCancel = nil, } INFO_MENU_BASE = { timeout = 0, title = "Select an option to see detailed info about it", itemsPerPage = nil, flags = MENUFLAG_BUTTON_EXIT, onSelect = nil, onCancel = nil, } INFO_MENU_SUBTYPE = { timeout = 0, title = "", itemsPerPage = nil, flags = MENUFLAG_BUTTON_EXITBACK, onSelect = nil, onCancel = nil, } --[[----------------+ FUNCTIONS +------------------]]-- function GetArenaCount() for _, target in pairs( ents.FindAllByClass( "info_target" ) ) do local name = target:GetName() if string.starts( name, "ArenaCount" ) then local start_pos = string.len( "ArenaCount_" ) + 1 local max = tonumber( string.sub( name, start_pos ) ) -- print( max ) ARENA_MAX = max return max end end end function GetTeamSpawns( team ) local vectors = {} for _, spawn in pairs( ents.FindAllByClass( "info_player_teamspawn" ) ) do local name = spawn:GetName() local teamname = "Blue" if tonumber( team ) == TEAM_RED then teamname = "Red" end if ( name ~= "" ) and ( string.starts( name, "Arena" .. tostring( ARENA ) ) ) and ( string.match( name, "SpawnPoint" .. teamname ) ) then -- print( spawn:GetName(), spawn.m_iTeamNum ) table.insert( vectors, spawn:GetAbsOrigin() ) end end return vectors end function PlaySoundToPlayer( info ) local ply = info.player local sound = info.sound local volume = info.volume or 1 local pitch = info.pitch or 100 ply:RunScriptCode( "EmitSoundEx( { sound_name = `" .. tostring(sound) .. "`, entity = activator, volume = " .. tostring( volume ) .. ", pitch = " .. tostring( pitch ) .. ", filter_type = 4 } )", ply, ply ) -- ply:RunScriptCode( "printl( activator )", ply, ply ) end function SetupPlayerData( player ) GLOBAL_PLAYER_DATA[player.m_szNetworkIDString] = table.DeepCopy( PLAYER_DATA_TEMPLATE ) -- table.insert( GLOBAL_PLAYER_DATA[player.m_szNetworkIDString], PLAYER_DATA_TEMPLATE ) GLOBAL_PLAYER_DATA[player.m_szNetworkIDString]["LastWavePlayed"] = 0 ResetPlayerData( player ) end function WeaponMimic(properties) local mimic = ents.CreateWithKeys("tf_point_weapon_mimic", properties) return mimic end function removeCallbacks(player, callbacks) if not IsValid(player) then return end for _, callbackId in pairs(callbacks) do player:RemoveCallback(callbackId) end end function AddCapturePoint() ClearCapturePoints() local amount = WAVE_CAPTUREPOINTS_AMOUNT if WAVE_ISXL then amount = amount + WAVE_CAPTUREPOINTS_AMOUNT_XLADD end local tbl = WAVE_CAPTUREPOINTS_INFOTABLE[ARENA] for index = 1, amount do local data = tbl[index] local pos = data.pos local Manager = ents.CreateWithKeys( "info_target", { origin = tostring( pos ) }, true, true ) Manager:SetAbsOrigin( pos ) local Prop = ents.CreateWithKeys( "prop_dynamic", { disableshadows = 1, solid = 0, skin = 2, model = "models/props_gameplay/cap_square_" .. tostring(data.size) .. ".mdl", origin = tostring( pos ) }, true, true ) Prop:SetAbsOrigin( pos ) local Display = ents.CreateWithKeys( "point_worldtext", {message = "Capture Progress : 0%", font = 1, Color = "251 236 203 255", textsize = 10, orientation = 2, origin = tostring( pos + Vector( 0, 0, 64 ) ) }, true, true ) Display:SetAbsOrigin( pos + Vector( 0, 0, 64 ) ) local Glow = ents.CreateWithKeys( "tf_glow", { GlowColor = "153 204 255 210", Mode = 0, target = "", targetname = "cp_glow", StartDisabled = 0, origin = tostring( pos ) }, false, false ) Glow:SetParent( Prop ) Glow.m_hTarget = Prop WAVE_CAPTUREPOINTS_TRACKED[index] = {} WAVE_CAPTUREPOINTS_TRACKED[index].h_Manager = Manager WAVE_CAPTUREPOINTS_TRACKED[index].h_Prop = Prop WAVE_CAPTUREPOINTS_TRACKED[index].h_Display = Display WAVE_CAPTUREPOINTS_TRACKED[index].h_Glow = Glow WAVE_CAPTUREPOINTS_TRACKED[index].fl_Progress = 0 WAVE_CAPTUREPOINTS_TRACKED[index].fl_Duration = data.cap_duration WAVE_CAPTUREPOINTS_TRACKED[index].i_Size = data.size WAVE_CAPTUREPOINTS_TRACKED[index].b_IsCaptured = false end end function ClearCapturePoints() WAVE_CAPTUREPOINTS_CAPTURED = 0 for i, point in pairs( WAVE_CAPTUREPOINTS_TRACKED ) do if IsValid( point.h_Manager ) then point.h_Manager:Remove() end if IsValid( point.h_Prop ) then point.h_Prop:Remove() end if IsValid( point.h_Display ) then point.h_Display:Remove() end if IsValid( point.h_Glow ) then point.h_Glow:Remove() end end WAVE_CAPTUREPOINTS_TRACKED = {} end function TrackPoints() if #WAVE_CAPTUREPOINTS_TRACKED <= 0 then return end for i, point in pairs( WAVE_CAPTUREPOINTS_TRACKED ) do local manager = point.h_Manager local display = point.h_Display local glow = point.h_Glow local pos = manager:GetAbsOrigin() local size = point.i_Size / 2 local progress_per_player = ( 100 / point.fl_Duration ) / 10 -- logic runs 10/s local progress_decrease_pre_player = 0.2 local progress_penalty_per_enemy = 0.1 local progress_penalty_min = 0 local progress_increase = 0 local progress_penalty = 1 local players_on_point = 0 for _, ply in pairs( ents.FindInBox( pos + Vector( -size, -size, -size ), pos + Vector( size, size, size ), "player" ) ) do if ply:IsRealPlayer() and ply:IsAlive() and ( not ply:OnTeam( 1 ) ) then players_on_point = players_on_point + 1 if players_on_point <= 1 then progress_increase = progress_increase + progress_per_player else progress_increase = progress_increase + ( progress_per_player * progress_decrease_pre_player ) end elseif ply:IsBot() and ply:IsAlive() and ( not ply:OnTeam( 1 ) ) then progress_penalty = math.max( progress_penalty - progress_penalty_per_enemy, progress_penalty_min ) end end progress_increase = progress_increase * progress_penalty point.fl_Progress = math.min( point.fl_Progress + progress_increase, 100 ) display:SetText( "Capture Progress : " .. tostring( math.round( point.fl_Progress, 1 ) ) .. "%" ) if point.fl_Progress >= 100 then point.h_Prop:Skin( 1 ) point.h_Display:SetColor( "255 144 144 255" ) glow:SetGlowColor( "255 144 144 255" ) if not point.b_IsCaptured then point.b_IsCaptured = true WAVE_CAPTUREPOINTS_CAPTURED = WAVE_CAPTUREPOINTS_CAPTURED + 1 end elseif point.fl_Progress >= 50 then point.h_Prop:Skin( 0 ) point.h_Display:SetColor( "251 236 203 255" ) glow:SetGlowColor( "251 236 203 255" ) else point.h_Prop:Skin( 2 ) point.h_Display:SetColor( "153 204 255 255" ) glow:SetGlowColor( "153 204 255 255" ) end end local max = WAVE_CAPTUREPOINTS_AMOUNT if WAVE_ISXL then max = max + WAVE_CAPTUREPOINTS_AMOUNT_XLADD end if WAVE_CAPTUREPOINTS_CAPTURED >= max then print( "all points captured" ) EndWave( true ) 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 if tbl[key].RealValue then return tbl[key].RealValue; else return key; end end end end --[[ the table shoud look like tbl = { 1, 0.6, 0.2, } or tbl = { [1] = { Weight = 1 }, [2] = { Weight = 0.6 }, [3] = { Weight = 0.2 } } --]] 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 math.WeightedRandomEnemySummon = function( tbl ) local total = 0 for i, d in pairs( tbl ) do total = total + ( d.Weight ) end local random = math.randomfloat( 0, total ) local cumulativeProbability = 0 for key, item in pairs(tbl) do cumulativeProbability = cumulativeProbability + ( tbl[key].Weight ) if (cumulativeProbability >= random) then return key; end end end CEntity.GetAttributeValueClean = function(self, attribute, default, slot) -- IDIOT FACE if slot == nil then return default end if (IsValid(self)) then -- print( self, 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 return default 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 self elseif slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then return { self:GetPlayerItemBySlot( 0 ), self:GetPlayerItemBySlot( 1 ) } elseif slot == LOADOUT_POSITION_ALL then return { self:GetPlayerItemBySlot( 0 ), self:GetPlayerItemBySlot( 1 ), self:GetPlayerItemBySlot(2) } 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 ents.FindBySteamID = function( id ) for _, player in pairs( ents.GetAllRealPlayers() ) do if player.m_szNetworkIDString == id then return player end end return false end CEntity.SetAttributeValueTemporary = function( self, attribute, value, duration ) self:SetAttributeValue( attribute, value ) self["AttributeRemoveDelay"..tostring(self:GetNetIndex())..tostring(attribute)] = CurTime() + duration self["AttributeTimer"..tostring(self:GetNetIndex())..tostring(attribute)] = timer.Create( 0.1, function() if CurTime() >= self["AttributeRemoveDelay"..tostring(self:GetNetIndex())..tostring(attribute)] then self:SetAttributeValue( attribute, nil ) return false end end, 0 ) end function string.starts(String,Start) -- Copied from StackOverflow cause lazy return string.sub(String,1,string.len(Start))==Start end function GetCashByWave( wave ) local total = 0 for i = 1, wave do local total_thiswave = ENEMY_BASE_CASH * GetEnemyCountWave( wave ) total = total + total_thiswave end return total 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, debug_weapon_name ) debug_weapon_name = debug_weapon_name or "" if ( not weapon ) or ( weapon == nil ) then return false end if ( not list ) or ( list == nil ) then return true end if ( tostring(type(weapon)) ~= "table" ) and ( weapon:IsPlayer() ) then weapon = { weapon:GetPlayerItemBySlot(0), weapon:GetPlayerItemBySlot(1), weapon:GetPlayerItemBySlot(2), weapon:GetPlayerItemBySlot(LOADOUT_POSITION_BUILDING), weapon:GetPlayerItemBySlot(LOADOUT_POSITION_PDA), weapon:GetPlayerItemBySlot(LOADOUT_POSITION_PDA2) } 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 -- print( debug_weapon_name, wep:GetItemName(), wep:GetClassname(), list[string.lower(wep:GetClassname())], list[string.lower(wep:GetItemName())], list[wep:GetItemName()], list[string.lower(wep:GetItemName())] ~= nil, list[string.lower(wep:GetClassname())] ~= nil ) -- if wep:GetItemName() == "Ali Baba's Wee Booties" then -- print( debug_weapon_name, list[wep:GetItemName()], ( list[string.lower(wep:GetItemName())] or list[string.upper(wep:GetItemName())] ), ( list[string.lower(wep:GetItemName())] or list[string.upper(wep:GetItemName())] or list[wep:GetItemName()] ) ) -- PrintTable( list ) -- end if ( list[wep:GetItemName()] ~= nil ) or ( list[string.lower(wep:GetItemName())] ~= nil ) or ( list[string.upper(wep:GetItemName())] ~= nil ) then result[i] = ( list[wep:GetItemName()] or list[string.lower(wep:GetItemName())] or list[string.upper(wep:GetItemName())] ) or false elseif ( list[wep:GetClassname()] ~= nil ) or ( list[string.lower(wep:GetClassname())] ~= nil ) or ( list[string.upper(wep:GetClassname())] ~= nil ) then result[i] = ( list[wep:GetClassname()] or list[string.lower(wep:GetClassname())] or list[string.upper(wep:GetClassname())] ) or false else --failsafe result[i] = ( false ) end end -- if debug_weapon_name == "Stomp!" then -- PrintTable( result ) -- end return result else -- print( debug_weapon_name, weapon:GetItemName(), weapon:GetClassname(), list[weapon:GetItemName()], list[weapon:GetClassname()] ) -- if weapon:GetItemName() == "Ali Baba's Wee Booties" then -- PrintTable( list ) -- end if ( list[weapon:GetItemName()] ~= nil ) or ( list[string.lower(weapon:GetItemName())] ~= nil ) or ( list[string.upper(weapon:GetItemName())] ~= nil ) then return ( list[weapon:GetItemName()] or list[string.lower(weapon:GetItemName())] or list[string.upper(weapon:GetItemName())]) or false elseif ( list[weapon:GetClassname()] ~= nil ) or ( list[string.lower(weapon:GetClassname())] ~= nil ) or ( list[string.upper(weapon:GetClassname())] ~= nil ) then return ( list[weapon:GetClassname()] or list[string.lower(weapon:GetClassname())] or list[string.upper(weapon:GetClassname())] ) or false else --failsafe -- print( "Weapon was not found on provided list", weapon:GetItemName(), weapon:GetClassname() ) return false end end else if tostring(type(weapon)) == "table" then local result = {} for i, wep in pairs( weapon ) do if wep:GetClassname() == list or ( wep:GetItemName() == list ) then result[i] = true else result[i] = false end end return result else if weapon:GetClassname() == list or ( weapon:GetItemName() == list ) then return true else return false end 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 ) ) ENEMY_MAX_COUNT_PER_SET = math.floor( ENEMY_BASE_COUNT + ( ( 7 - 1 ) * ENEMY_BASE_COUNT_SCALER ) ) return math.floor( ( ENEMY_BASE_COUNT + ( ( WAVE - 1 ) * ENEMY_BASE_COUNT_SCALER ) ) - ( ENEMY_MAX_COUNT_PER_SET * ( ENEMY_BASE_COUNT_DROP_OFF * math.floor( ( WAVE - 1 ) / 7 ) ) ) ) end function GetEnemyCountWave(wave) -- return math.floor( ENEMY_BASE_COUNT + ( ( WAVE - 1 )^ENEMY_BASE_COUNT_SCALER ) ) ENEMY_MAX_COUNT_PER_SET = math.floor( ENEMY_BASE_COUNT + ( ( WAVE_NUMOFWAVESPERLOOP - 1 ) * ENEMY_BASE_COUNT_SCALER ) ) return math.floor( ( ENEMY_BASE_COUNT + ( ( wave - 1 ) * ENEMY_BASE_COUNT_SCALER ) ) - ( ENEMY_MAX_COUNT_PER_SET * ( ENEMY_BASE_COUNT_DROP_OFF * math.floor( ( wave - 1 ) / WAVE_NUMOFWAVESPERLOOP ) ) ) ) end function GetEnemyHealthMult() return WAVE^(ENEMY_BASE_HEALTH_SCALER + ( ( math.floor( (WAVE-1)/(WAVE_NUMOFWAVESPERLOOP) ) ) * ( ( ENEMY_BASE_HEALTH_SCALER_PER_PLAYER * #ents.GetAllRealPlayers() ) ) ) ) end function GetEnemyDamageMult() ENEMY_MAX_DAMAGE_PER_LOOP = 1 + ( ENEMY_BASE_DAMAGE_SCALER_LOOP * ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1) ) * ( 1 + ( math.floor( ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1 ) / WAVE_NUMOFWAVESPERLOOP ) * ENEMY_BASE_DAMAGE_SCALER_PER_PLAYER * #ents.GetAllRealPlayers() ) ) -- print( ENEMY_MAX_DAMAGE_PER_LOOP, ENEMY_BASE_DAMAGE_SCALER_DROP_OFF, math.floor( ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1 ) / WAVE_NUMOFWAVESPERLOOP ) ) return 1 + ( ( ENEMY_BASE_DAMAGE_SCALER_LOOP*( WAVE -1 ) ) * ( 1 + ( ( math.floor( ( WAVE - 1 ) / WAVE_NUMOFWAVESPERLOOP ) * ENEMY_BASE_DAMAGE_SCALER_PER_PLAYER * #ents.GetAllRealPlayers() ) ) ) ) - ( ENEMY_MAX_DAMAGE_PER_LOOP * ( ENEMY_BASE_DAMAGE_SCALER_DROP_OFF * math.floor( ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1 ) / ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) ) ) ) end function GetEnemyDamageMultRanged() ENEMY_MAX_DAMAGE_PER_LOOP = 1 + ( ( ( ENEMY_BASE_DAMAGE_SCALER_LOOP * ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1) ) * ( 1 + ( math.floor( ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1 ) / WAVE_NUMOFWAVESPERLOOP ) * ENEMY_BASE_DAMAGE_SCALER_PER_PLAYER * #ents.GetAllRealPlayers() ) ) ) * ENEMY_BASE_DAMAGE_SCALER_RANGED_COEF ) -- print( ENEMY_MAX_DAMAGE_PER_LOOP, ENEMY_BASE_DAMAGE_SCALER_DROP_OFF, math.floor( ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1 ) / WAVE_NUMOFWAVESPERLOOP ) ) return 1 + ( ( ( ( ENEMY_BASE_DAMAGE_SCALER_LOOP*( WAVE -1 ) ) * ( 1 + ( ( math.floor( ( WAVE - 1 ) / WAVE_NUMOFWAVESPERLOOP ) * ENEMY_BASE_DAMAGE_SCALER_PER_PLAYER * #ents.GetAllRealPlayers() ) ) ) ) - ( ENEMY_MAX_DAMAGE_PER_LOOP * ( ENEMY_BASE_DAMAGE_SCALER_DROP_OFF * math.floor( ( (WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND)-1 ) / ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) ) ) ) ) * ENEMY_BASE_DAMAGE_SCALER_RANGED_COEF ) end function GetEnemySpeedMult() if MISSION_MODIFY_STATS[MISSION_MODIFY_FAST_ENEMIES].enabled then return ( 1 + ( math.floor((WAVE - 1)/WAVE_NUMOFWAVESPERLOOP) * ENEMY_BASE_SPEED_SCALER ) ) * 1.25 else return 1 + ( math.floor((WAVE - 1)/WAVE_NUMOFWAVESPERLOOP) * ENEMY_BASE_SPEED_SCALER ) end end function GetEnemyKBRes() return ENEMY_BASE_KBRES_SCALER^( -1 * math.floor((WAVE - 1)/WAVE_NUMOFWAVESPERLOOP ) ) 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 ) / ( (WAVE_NUMOFWAVESPERLOOP-1) + ( math.floor( ( WAVE + 1.5 ) / (WAVE_NUMOFWAVESPERLOOP) ) * (WAVE_NUMOFWAVESPERLOOP) ) ) ) ) local loop = math.floor( ( WAVE - 1 ) / ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) ) + 1 local EndWave = WAVE == ( ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) * loop ) -- 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 and EndWave == false then for i=1, tonumber( VOTE_OPTIONS + ENCOUNTER_MODIFY_STATS[MODIFY_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 ) or category.IsShop do random = math.WeightedRandom( VOTE_CATEGORIES ) category = VOTE_CATEGORIES[ random ] name = category.name end -- end local type_tbl = {} for i, v in pairs( category.AvailableTypes ) do table.insert( type_tbl, { v, Weight = WAVE_TYPE_CHANCE_TABLE[v], RealValue = v } ) end local random_type = math.WeightedRandom( type_tbl ) if WAVE >= WAVE_XL_MIN_WAVE then if math.random() <= WAVE_XL_CHANCE then category.IsXL = true else category.IsXL = false end end -- print( random_type ) category.SelectedType = random_type table.insert( Categories, random ) VOTE_MENU[i] = { text = name, value = random, disabled = false } end elseif AboutToBeShop and ( not SHOP_WAVE ) and ( not EndWave ) then for i=1, tonumber( VOTE_OPTIONS + ENCOUNTER_MODIFY_STATS[MODIFY_VOTE_OPTIONS] ) do local random = math.WeightedRandom( VOTE_CATEGORIES ) local category = VOTE_CATEGORIES[ random ] local name = category.name VOTE_MENU["title"] = "Choose Which Shop You Go To \n(Voting replaced with ready up)" if i~=1 then while CategorySelected( random ) or ( not category.IsShop ) do random = math.WeightedRandom( VOTE_CATEGORIES ) category = VOTE_CATEGORIES[ random ] name = category.name end table.insert( Categories, random ) VOTE_MENU[i] = { text = name, value = random, disabled = false } else VOTE_MENU[1] = { text = VOTE_CATEGORIES[ UPGRADE_CATEGORY_SHOP ].name, value = UPGRADE_CATEGORY_SHOP, disabled = false } table.insert( Categories, UPGRADE_CATEGORY_SHOP ) end end elseif SHOP_WAVE and ( not AboutToBeShop ) and ( not EndWave ) then VOTE_CATEGORIES[ UPGRADE_CATEGORY_ELITE_BOSS ].SelectedType = WAVE_TYPE_STANDARD VOTE_MENU[1] = { text = VOTE_CATEGORIES[ UPGRADE_CATEGORY_ELITE_BOSS ].name, value = UPGRADE_CATEGORY_ELITE_BOSS, disabled = false } elseif EndWave and ( not AboutToBeShop ) and ( not SHOP_WAVE ) then VOTE_CATEGORIES[ UPGRADE_CATEGORY_END ].SelectedType = WAVE_TYPE_BLANK VOTE_CATEGORIES[ UPGRADE_CATEGORY_LOOP ].SelectedType = WAVE_TYPE_STANDARD VOTE_MENU[1] = { text = VOTE_CATEGORIES[ UPGRADE_CATEGORY_END ].name, value = UPGRADE_CATEGORY_END, disabled = false } VOTE_MENU[2] = { text = VOTE_CATEGORIES[ UPGRADE_CATEGORY_LOOP ].name, value = UPGRADE_CATEGORY_LOOP, disabled = false } end else for i=1, tonumber( VOTE_OPTIONS + ENCOUNTER_MODIFY_STATS[MODIFY_VOTE_OPTIONS] ) do if VOTE_MENU[i] then local add = "" if WAVE_TYPE_KEY[VOTE_CATEGORIES[tonumber( VOTE_MENU[i].value )].SelectedType] ~= nil then add = " (" .. WAVE_TYPE_KEY[VOTE_CATEGORIES[tonumber( VOTE_MENU[i].value )].SelectedType] .. ")" end if VOTE_CATEGORIES[tonumber( VOTE_MENU[i].value )].IsXL then add = add .. " [XL]" end local str = tostring( VOTE_CATEGORIES[tonumber( VOTE_MENU[i].value )].name ) .. add .. " " 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 end end -- local new_menu = VOTE_MENU -- if ( HasUpgrade( player, UPGRADE_BOURGEOISIE_RING_SAPPHIRE ) and ( not AboutToBeShop ) then -- new_menu["title"] = "Choose Your Next Victory Loot!" -- end player:DisplayMenu( VOTE_MENU ) end function Voted( player, index, value ) value = tonumber( value ) local old_val = nil player.VotedForLoot = tonumber( value ) player.Voted = true for i = 0, #VOTE_RESULTS do for index, id in pairs( VOTE_RESULTS[i] ) do if id == player:GetSteamId() then old_val = i table.remove( VOTE_RESULTS[i], index ) end end end if value == old_val then timer.Simple( 0.15, function() for i = 0, #VOTE_RESULTS do for index, id in pairs( VOTE_RESULTS[i] ) do if id == player:GetSteamId() then old_val = i table.remove( VOTE_RESULTS[i], index ) end end end table.insert( VOTE_RESULTS[value], player:GetSteamId() ) end) else -- for _, players in pairs( ents.GetAllRealPlayers() ) do player:PlaySoundToSelf( "Vote.Cast.Yes" ) -- end table.insert( VOTE_RESULTS[value], player:GetSteamId() ) end end function BuildUpgradeMenu( player, dev, loot_table, grace, overwrite_cate ) print( loot_table ) player = player or dev loot_table = loot_table or UPGRADE_LOOT_COMMON grace = grace or 0 if player.InMenu == true then return end local new_menu = {} local display_upgrades = player.DisplayedUpgrades local discount = math.max(player.CostMult * player.ShopCostMult, 0.3) if ( loot_table == UPGRADE_LOOT_SHOP ) or ( ( loot_table == UPGRADE_LOOT_SHOP_BOURGEOISIE ) ) then display_upgrades = player.DisplayedShopUpgrades player.InShop = true else player.InShop = false end if loot_table == UPGRADE_LOOT_BOURGEOISIE then display_upgrades = display_upgrades - 2 end display_upgrades = math.max( display_upgrades, 1 ) if player.DisplayedUpgrades <= 0 then player:Print( PRINT_TARGET_CENTER, "No loot found" ) util.PrintToChat( player, "No loot found" ) return end for key, val in pairs(UPGRADE_DEFAULT_MENU) do new_menu[key] = val end --new_menu[itemsPerPage] = player.DisplayedUpgrades local function UpgradeAlreadySelected( upgrade, table, max_upgrades ) if ( not upgrade ) or ( not table ) then return false end local output = false for i, v in pairs( table ) do if ( type(i) == "number" ) and ( i >= 1 ) and ( max_upgrades >= i ) then if upgrade == Upgrades[tonumber( v.value )] then output = true end end end return output end new_menu["onSelect"] = function( ply, index, value ) Upgradebought( ply,index,value ) end new_menu["onCancel"] = function( ply, reason ) UpgradeSkipped( ply,reason ) end local HasUpgradeRing = HasUpgrade( player, UPGRADE_BOURGEOISIE_RING_UPGRADE ) local extra_slots = 0 local i = 0 display_upgrades = display_upgrades -- don't ask me why I need to do it this way -- for i=1, tonumber( display_upgrades ) + extra_slots do while ( tonumber( display_upgrades ) + extra_slots ) > i do -- print( ( tonumber( display_upgrades ) + extra_slots ), extra_slots ) i = i + 1 local ShouldBeUpgradeItem = false if HasUpgradeRing and loot_table == UPGRADE_LOOT_SHOP then ShouldBeUpgradeItem = ( 1 / i^2 ) >= math.random() end local upgrades_list = {} local selected_upgrade = {} local force_disable = false local str = "" local rnd = 0 if not ShouldBeUpgradeItem then local cate = UPGRADE_LOOT_CATEGORY if ( loot_table == UPGRADE_LOOT_SHOP ) or ( HasUpgrade( player, UPGRADE_BOURGEOISIE_RING_SAPPHIRE ) and ( player.ShopLoot ~= nil ) ) then cate = player.ShopLoot or UPGRADE_LOOT_CATEGORY end if overwrite_cate then cate = overwrite_cate end local loot_upgraded = false local type = table.RandomChance( UPGRADE_LOOT_TABLE[ loot_table ] ) -- type 0-4 if cate == UPGRADE_CATEGORY_GREED then type = table.RandomChance( UPGRADE_LOOT_TABLE[ UPGRADE_LOOT_GREED ] ) end if cate == UPGRADE_CATEGORY_EPIC then type = table.RandomChance( UPGRADE_LOOT_TABLE[ UPGRADE_LOOT_EPIC ] ) end if cate == UPGRADE_CATEGORY_BOURGEOISIE then type = table.RandomChance( UPGRADE_LOOT_TABLE[ UPGRADE_LOOT_BOURGEOISIE ] ) end -- print( cate, type ) if ( type ~= UPGRADE_TYPE_GREED ) then -- print( player:GetAttributeValueClean( "freaky fair extra attrib1", 0, LOADOUT_POSITION_BODY ), player:GetAttributeValueClean( "freaky fair extra attrib1", 0, LOADOUT_POSITION_BODY ) >= math.random() ) if HasUpgrade( player, UPGRADE_RARE_MONEY_LUCKY ) then local second_type = table.RandomChance( UPGRADE_LOOT_TABLE[ loot_table ] ) -- type 0-4 if cate == UPGRADE_CATEGORY_GREED then second_type = table.RandomChance( UPGRADE_LOOT_TABLE[ UPGRADE_LOOT_GREED ] ) end if cate == UPGRADE_CATEGORY_EPIC then second_type = table.RandomChance( UPGRADE_LOOT_TABLE[ UPGRADE_LOOT_EPIC ] ) end if cate == UPGRADE_CATEGORY_BOURGEOISIE then second_type = table.RandomChance( UPGRADE_LOOT_TABLE[ UPGRADE_LOOT_BOURGEOISIE ] ) end type = math.max( type, second_type ) end if (loot_table ~= UPGRADE_LOOT_SHOP) then local upgrade_rarity_chance = ( player:GetAttributeValueClean( "freaky fair extra attrib1", 0, LOADOUT_POSITION_BODY ) / 100 ) + ( player:GetAttributeValueClean("freaky fair ammo potion", 0, LOADOUT_POSITION_BODY ) * player.Rerolls ) if upgrade_rarity_chance >= math.random() then type = math.min( type + 1, UPGRADE_TYPE_LEGENDARY ) loot_upgraded = true end end end upgrades_list = GetUpgradesTableFiltered(type, cate) rnd = math.random( 1, #upgrades_list ) selected_upgrade = Upgrades[ upgrades_list[rnd] ] local loops = 0 local IncorrectUpgradeType = false IncorrectUpgradeType = ( ( ( cate ~= UPGRADE_CATEGORY_ALL ) and ( type ~= UPGRADE_CATEGORY_GREED ) and ( cate ~= UPGRADE_CATEGORY_EPIC ) and ( cate ~= UPGRADE_CATEGORY_ELITE_BOSS ) and ( cate ~= UPGRADE_CATEGORY_GREED ) ) and ( selected_upgrade.Category ~= cate ) ) or ( ( selected_upgrade.Category == UPGRADE_CATEGORY_BOURGEOISIE ) and ( cate ~= UPGRADE_CATEGORY_BOURGEOISIE ) ) local function DoAdvancedFilter( upgrade, index, player ) if upgrade.AdvancedFilter then return upgrade.AdvancedFilter( index, upgrade, player ) else return true end end while IsUpgradeCapped( player, upgrades_list[rnd], selected_upgrade.usePlayerUpgradesTable ) or ( ( not selected_upgrade.usePlayerUpgradesTable ) and ( not HasTrue( WeaponAllowed( player:GetItemBySlotClean( selected_upgrade.Attributes[1].slot ), selected_upgrade.WeaponList, selected_upgrade.Name ) ) ) ) or ( IncorrectUpgradeType ) or ( UpgradeAlreadySelected( selected_upgrade, new_menu, display_upgrades ) ) or ( not DoAdvancedFilter( selected_upgrade, rnd, player ) ) do rnd = math.random( 1, #upgrades_list ) selected_upgrade = Upgrades[ upgrades_list[rnd] ] IncorrectUpgradeType = ( ( ( cate ~= UPGRADE_CATEGORY_ALL ) and ( type ~= UPGRADE_CATEGORY_GREED ) and ( cate ~= UPGRADE_CATEGORY_EPIC ) and ( cate ~= UPGRADE_CATEGORY_ELITE_BOSS ) and ( cate ~= UPGRADE_CATEGORY_GREED ) ) and ( selected_upgrade.Category ~= cate ) ) or ( ( selected_upgrade.Category == UPGRADE_CATEGORY_BOURGEOISIE ) and ( cate ~= UPGRADE_CATEGORY_BOURGEOISIE ) ) loops = loops + 1 -- failsafe to avoid stackoverflow if loops > 2000 then print( "breaking" ) force_disable = true break end end local cost = "" if player.InShop then if selected_upgrade.Category == UPGRADE_CATEGORY_BOURGEOISIE then discount = math.max( discount, 1 ) end local cost_num = math.floor( GetTypeCost(selected_upgrade.Type) * discount, 2 ) cost = "$" .. tostring( cost_num ) if 1 > discount then cost = cost .. " (-" .. tostring( math.round( 1-discount, 2 ) * 100 ) .. "%)" end if 1 < discount then cost = cost .. " (+" .. tostring( math.round( discount-1, 2 ) * 100 ) .. "%)" end -- new_menu["flags"] = MENUFLAG_NO_SOUND -- new_menu["onCancel"] = nil end --local str = tostring( UPGRADE_TYPE_TABLE[selected_upgrade.Type] ) .. ": " .. selected_upgrade.Name .. "\n" .. "Description: " .. "\n" ..selected_upgrade.Description str = tostring( UPGRADE_TYPE_TABLE[selected_upgrade.Type] ) .. ": " .. selected_upgrade.Name .. " " .. cost .. "\n" .. selected_upgrade.Description if loot_upgraded then str = "(UPGRADED) " .. str end else local type = table.RandomChance( UPGRADE_RING_PRIO_TABLE ) local loops = 0 local function HasUpgradeOfTier( tier ) for i, data in pairs( Upgrades ) do if GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][i] and ( GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][i] > 0 ) and ( Upgrades[i].Type == tier ) and ( not IsUpgradeCapped( player, i, Upgrades[i].usePlayerUpgradesTable ) ) then return true end end return false end local function GetUpgradesTable( tier ) local new_tbl = {} for i, data in pairs( Upgrades ) do if GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][i] and ( GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][i] > 0 ) and ( Upgrades[i].Type == tier ) and ( not IsUpgradeCapped( player, i, Upgrades[i].usePlayerUpgradesTable ) ) then table.insert( new_tbl, i ) end end return new_tbl end while ( not HasUpgradeOfTier( type ) ) do type = math.WeightedRandom( UPGRADE_RING_PRIO_TABLE ) if loops > 2500 then force_disable = true break end loops = loops + 1 end -- print( type ) upgrades_list = GetUpgradesTable( type ) rnd = math.random( 1, #upgrades_list ) selected_upgrade = Upgrades[ upgrades_list[rnd] ] loops = 1 while IsUpgradeCapped( player, upgrades_list[rnd], Upgrades[upgrades_list[rnd]].usePlayerUpgradesTable ) do rnd = math.random( 1, #upgrades_list ) selected_upgrade = Upgrades[ upgrades_list[rnd] ] if loops > 2500 then force_disable = true break end loops = loops + 1 end extra_slots = extra_slots + 1 local cost = "" if player.InShop then if selected_upgrade.Category == UPGRADE_CATEGORY_BOURGEOISIE then discount = math.max( discount, 1 ) end local cost_num = math.floor( GetTypeCost(selected_upgrade.Type) * discount, 2 ) cost = "$" .. tostring( cost_num ) if 1 > discount then cost = cost .. " (-" .. tostring( math.round( 1-discount, 2 ) * 100 ) .. "%)" end if 1 < discount then cost = cost .. " (+" .. tostring( math.round( discount-1, 2 ) * 100 ) .. "%)" end cost = cost .. " [Upgrade Perk]" -- new_menu["flags"] = MENUFLAG_NO_SOUND -- new_menu["onCancel"] = nil end --local str = tostring( UPGRADE_TYPE_TABLE[selected_upgrade.Type] ) .. ": " .. selected_upgrade.Name .. "\n" .. "Description: " .. "\n" ..selected_upgrade.Description str = tostring( UPGRADE_TYPE_TABLE[selected_upgrade.Type] ) .. ": " .. selected_upgrade.Name .. " " .. cost .. "\n" .. selected_upgrade.Description end if grace > 0 then force_disable = true end new_menu[i] = { text = str, value = upgrades_list[rnd], disabled = ( IsUpgradeCapped( player, upgrades_list[rnd], selected_upgrade.usePlayerUpgradesTable ) or force_disable ) } 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 ) if MISSION_MODIFY_STATS[MISSION_MODIFY_REROLL_COST].enabled then reroll_cost = reroll_cost * 4 end new_menu[(tonumber( display_upgrades ) + extra_slots) + 1] = { text = "Reroll Items $" .. tostring(reroll_cost) .. " " .. tostring(player.Rerolls) .. "/" .. tostring(player.MaxRerolls), value = UPGRADE_REROLL - loot_table, disabled = tobool( player.Rerolls >= player.MaxRerolls ) } local total = 0 local mult = 1.15 for i=1, display_upgrades + extra_slots do if ( new_menu[i] ) and ( new_menu[i].value ) and ( tonumber(new_menu[i].value) ) and ( Upgrades[tonumber(new_menu[i].value)] ) then -- print( new_menu[i].value ) if Upgrades[tonumber(new_menu[i].value)].Category == UPGRADE_CATEGORY_BOURGEOISIE then mult = 1.5 end total = total + ( GetTypeCost(Upgrades[tonumber(new_menu[i].value)].Type) * mult ) end end total = math.floor( total / ( display_upgrades + extra_slots ) ) if not player.InShop then new_menu["title"] = new_menu["title"] .. "\nSkip for $" .. tostring( total ) else new_menu["title"] = new_menu["title"] .. "\nClose shop when you're done" end --PrintTable( new_menu ) player.Menu = new_menu player.InMenu = true player:DisplayMenu( new_menu ) if grace > 0 then local grace_menu = new_menu timer.Simple( grace, function() if player.Menu ~= grace_menu then return end if ( not player.InMenu ) and ( not player.InRoll ) then return end for i, v in pairs( new_menu ) do if type(v) == "table" then if v.disabled and ( v.value >= 0 ) then v.disabled = false end end end player.Menu = new_menu player.InMenu = true player:DisplayMenu( new_menu ) end) end 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( GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] ) do if count > 0 then local greed = false local bour = false if Upgrades[index].Type == UPGRADE_TYPE_GREED then greed = true end if Upgrades[index].Category == UPGRADE_CATEGORY_BOURGEOISIE then bour = true end local extra_text = "" if greed then extra_text = "\n**GREED loot will not give cash back**" end if bour then extra_text = "\n*Bourgeoi$ie items salvage for 100%" 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 = false } ) 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 ) if player.InMenu then player:DisplayMenu( player.Menu ) end UpgradeSalvaged( ply, i, v ) end warning_menu["onCancel"] = function( ply, reason ) BuildViewUpgradeMenu( ply, ply ) end local salavge_mult = 0.5 if Upgrades[value].Category == UPGRADE_CATEGORY_BOURGEOISIE then salavge_mult = 1 end if Upgrades[value].Category == UPGRADE_CATEGORY_GREED then salavge_mult = 0 end warning_menu["title"] = "Salvage " .. tostring( UPGRADE_TYPE_TABLE[Upgrades[value].Type] ) .. ": " .. Upgrades[value].Name .. " x" .. tostring(GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value]) warning_menu[1] = { text = "Yes. \n" .. Upgrades[value].Description .. "\nYou'll get $" .. tostring( math.floor( GetTypeCost( Upgrades[value].Type ) * salavge_mult * GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value] ) ) .. "\nPress back to cancel", value = value, disabled = false } player:DisplayMenu( warning_menu ) end player:DisplayMenu( new_menu ) end function BuildBaseInfoMenu( player, dev ) player = player or dev local new_menu = {} for key, val in pairs(INFO_MENU_BASE) do new_menu[key] = val end for i, v in pairs( INFO_DISPLAY_TABLE ) do table.insert( new_menu, { text = v.Name, value = i, disabled = false } ) end new_menu["onSelect"] = function( ply, index, value ) local val = tonumber( value ) local ind = tonumber( index ) local submenu = {} for key, val in pairs(INFO_MENU_SUBTYPE) do submenu[key] = val end table.insert( submenu, { text = INFO_DISPLAY_TABLE[ind].ViewedInfo, value = ind, disabled = true } ) submenu["onCancel"] = function( ply, reason ) BuildBaseInfoMenu( ply, ply ) end player:DisplayMenu( submenu ) end new_menu["onCancel"] = function( ply, reason ) player.InRoll = false if player.InMenu then player:DisplayMenu( player.Menu ) end end player:DisplayMenu( new_menu ) end function BuildFlexMenu( player, dev ) player = player or dev -- if WaveStarted == true then player:Print( PRINT_TARGET_CENTER, "You cannot changed Flex options during the wave" ); return end local new_menu = {} for key, val in pairs(FLEX_MENU) do new_menu[key] = val end for i, v in pairs( MISSION_MODIFY_STATS ) do local enabled = tobool( v.enabled ) local ext_str = "Disabled" if enabled then ext_str = "Enabled" end table.insert( new_menu, { text = v.PrintName .. " (" .. ext_str .. ")" .. "\n" .. v.Description, value = i, disabled = WaveStarted } ) end new_menu["onSelect"] = function( ply, index, value ) local val = tonumber( value ) local ind = tonumber( index ) MISSION_MODIFY_STATS[val].enabled = ( not MISSION_MODIFY_STATS[val].enabled ) local enabled = tobool( MISSION_MODIFY_STATS[val].enabled ) local ext_str = "Disabled" if enabled then ext_str = "Enabled" end new_menu[ind] = { text = MISSION_MODIFY_STATS[val].PrintName .. " (" .. ext_str .. ")" .. "\n" .. MISSION_MODIFY_STATS[val].Description, value = val, disabled = WaveStarted } for _, other in pairs( ents.GetAllRealPlayers() ) do other:Print( PRINT_TARGET_CENTER, "Flex Modifier " .. ext_str .. "\x01: " .. MISSION_MODIFY_STATS[val].PrintName .. "\n" .. MISSION_MODIFY_STATS[val].Description ) end util.PrintToChatAll( "\x079219fcFlex Modifier " .. ext_str .. "\x01: " .. MISSION_MODIFY_STATS[val].PrintName .. ". " .. MISSION_MODIFY_STATS[val].Description ) player:DisplayMenu( new_menu ) end new_menu["onCancel"] = function( ply, reason ) player.InRoll = false if player.InMenu then player:DisplayMenu( player.Menu ) end end player:DisplayMenu( new_menu ) end function UpgradeSalvaged( player, index, value ) value = tonumber(value) local upgrade = Upgrades[value] local salavge_mult = 0.5 if Upgrades[value].Category == UPGRADE_CATEGORY_BOURGEOISIE then salavge_mult = 1 end if Upgrades[value].Category == UPGRADE_CATEGORY_GREED then salavge_mult = 0 end player:AddCurrency( math.floor(GetTypeCost( Upgrades[value].Type ) * salavge_mult * GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value] ) ) player:Print( PRINT_TARGET_CENTER, "Salvaged " .. tostring( UPGRADE_TYPE_TABLE[Upgrades[value].Type] ) .. ": " .. Upgrades[value].Name .. " x" .. tostring(GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value]) .. "\nFor $" .. tostring( math.floor(GetTypeCost( Upgrades[value].Type ) * salavge_mult * GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value] ) ) ) for i = 1, GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value] do if upgrade.SalvagedFunction then upgrade.SalvagedFunction( player, tonumber( value ) ) end if not upgrade.usePlayerUpgradesTable then for j = 1, #upgrade.Attributes do --print( upgrade.ApplyFunction ) --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].refund_set ) end elseif upgrade.Attributes[j].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then if tostring( upgrade.Attributes[j].type ) == "add" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 (IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].refund_set ) 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 elseif upgrade.Attributes[j].slot == LOADOUT_POSITION_ALL then if tostring( upgrade.Attributes[j].type ) == "add" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( 2 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) - upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "multiply" then if (IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( 2 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) / upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "subtract" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( 2 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) + upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "divide" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( 2 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) * upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "set" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].refund_set ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].default ) end if ( WeaponAllowed( player:GetPlayerItemBySlot( 2 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):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].refund_set ) end end end ::skip2:: end end end GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][value] = 0 end function ApplyMissingUpgrades( player ) if ( not GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] ) or ( #GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] <= 0 ) then return end for i, v in pairs( GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] ) do local upgrade = Upgrades[i] local Applied = 0 if( not Upgrades[i].Attributes ) then -- print(i) end if not upgrade.usePlayerUpgradesTable then for j = 1, ( #upgrade.Attributes ) do if v == 0 then goto skip end -- local unupgraded = false -- if upgrade.Attributes[j].slot == LOADOUT_POSITION_BODY then -- unupgraded = player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_BODY ) == upgrade.Attributes[j].default -- elseif upgrade.Attributes[j].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then -- if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then -- unupgraded = player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_PRIMARY ) == upgrade.Attributes[j].default -- end -- if not unupgraded then -- unupgraded = player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_SECONDARY ) == upgrade.Attributes[j].default -- end -- else -- unupgraded = player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, upgrade.Attributes[j].slot ) == upgrade.Attributes[j].default -- end for k = 1, v do if not HasTrue( WeaponAllowed( player:GetItemBySlotClean( upgrade.Attributes[1].slot ), upgrade.WeaponList, upgrade.Name ) ) then goto skip end if upgrade.ApplyFunction and ( Applied < GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][i] ) then upgrade.ApplyFunction( player, tonumber( i ) ) Applied = Applied + 1 end if upgrade.Attributes[j].slot == LOADOUT_POSITION_BODY then -- if unupgraded then -- pcall( upgrade.ApplyFunction, player, tonumber( i ) ) -- end 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end end elseif upgrade.Attributes[j].slot == LOADOUT_POSITION_ALL then if tostring( upgrade.Attributes[j].type ) == "add" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) and ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) + upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "multiply" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) and ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) * upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "subtract" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) and ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[j].attr, upgrade.Attributes[j].default, LOADOUT_POSITION_MELEE ) - upgrade.Attributes[j].increment ), cap ) end elseif tostring( upgrade.Attributes[j].type ) == "divide" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( 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 if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) and ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( WeaponAllowed( player:GetPlayerItemBySlot( 0 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) and ( WeaponAllowed( player:GetPlayerItemBySlot( 1 ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( tostring( upgrade.Attributes[j].attr ), upgrade.Attributes[j].increment ) end if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) and ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):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 else if GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][tonumber( i )] > 0 then for rep = 1, GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][tonumber( i )] do if upgrade.ApplyFunction then upgrade.ApplyFunction( player, tonumber( i ) ) end end end end end -- print( player:GetAttributeValueClean( "dmg penalty vs players", 0, LOADOUT_POSITION_BODY ), player.DamageMultGained ) player:SetAttributeValue( "max health additive bonus", player:GetAttributeValueClean( "max health additive bonus", 0, LOADOUT_POSITION_BODY ) + player.HealthGained ) -- player:SetAttributeValue( "dmg penalty vs players", player:GetAttributeValueClean( "dmg penalty vs players", 1, LOADOUT_POSITION_BODY ) + player.DamageMultGained ) -- player:SetAttributeValue( "fire rate penalty", player:GetAttributeValueClean( "fire rate penalty", 1, LOADOUT_POSITION_BODY ) * player.FireRateMultGained ) player:SetAttributeValue( "mult max health", math.max( player:GetAttributeValueClean( "mult max health", 0, LOADOUT_POSITION_BODY ) + player.HealthMultGained, 1 ) ) end function Upgradebought( player, index, value ) local reps = 1 player:PlaySoundToSelf( "ui/buttonrollover.wav" ) -- print( player, index, value ) if ( not WaveStarted ) and ( index ~= true ) then player.InShop = false player.InMenu = false player.InRoll = false player.InQueue = false return end if tonumber( value ) >= 0 then local upgrade = Upgrades[ tonumber( value ) ] if player.InShop then local discount = math.max(player.CostMult * player.ShopCostMult,0.3) local str = "" if player.CanHaggle and ( not player.Haggled ) then discount = discount - 0.30 player.Haggled = true str = "\nYou haggled and bought the price down by 30%" end if upgrade.Category == UPGRADE_CATEGORY_BOURGEOISIE then discount = math.max( discount, 1 ) end if ( player.m_nCurrency < ( math.floor(GetTypeCost(upgrade.Type) * discount, 2 ) ) ) 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 if HasUpgrade( player, UPGRADE_BOURGEOISIE_BOGO ) then if (not IsUpgradeCapped( player, tonumber( value ), upgrade.usePlayerUpgradesTable, 1 ) ) and ( math.random() <= UPGRADE_BOGO_CHANCE_TABLE[upgrade.Type] ) then str = str .. "\nBOGO Card got you a free copy!" reps = 2 end end local cost = math.floor(GetTypeCost(upgrade.Type) * discount, 2 ) player:RemoveCurrency( cost ) player:Print( PRINT_TARGET_CENTER, "bought: " .. tostring( UPGRADE_TYPE_TABLE[upgrade.Type] ) .. ": " .. upgrade.Name .. " $" .. tostring(cost) .. "\n" .. upgrade.Description .. str ) util.PrintToChat( player, "bought: " .. tostring( UPGRADE_TYPE_TABLE[upgrade.Type] ) .. ": " .. upgrade.Name .. " $" .. tostring(cost) .. "\n" .. upgrade.Description .. str ) 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 ) util.PrintToChat( player, "Picked up: " .. tostring( UPGRADE_TYPE_TABLE[upgrade.Type] ) .. ": " .. upgrade.Name .. "\n" .. upgrade.Description ) 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 rep = 1, reps do print( rep ) if upgrade.ApplyFunction then upgrade.ApplyFunction( player, tonumber( value ) ) end if not upgrade.usePlayerUpgradesTable then 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 elseif upgrade.Attributes[i].slot == LOADOUT_POSITION_ALL then if tostring( upgrade.Attributes[i].type ) == "add" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_MELEE ) + upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "multiply" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.min( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_MELEE ) * upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "subtract" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_MELEE ) - upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "divide" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):SetAttributeValue( tostring( upgrade.Attributes[i].attr ), math.max( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_MELEE ) / upgrade.Attributes[i].increment ), cap ) end elseif tostring( upgrade.Attributes[i].type ) == "set" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) and ( 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 if ( WeaponAllowed( player:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ), upgrade.WeaponList ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE):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 end GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][ tonumber( value ) ] = GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][ tonumber( value ) ] + 1 end local max = player.MaxUpgradesBuy if index ~= true then if not player.InShop then player.Upgradesbought = player.Upgradesbought + 1 player.Menu[ tonumber(index) ]["disabled"] = true -- print( player.MaxUpgradesBuy, max, player.Upgradesbought ) if player.Upgradesbought >= max then if not ( player.UpgradeQueue[1] and ( #player.UpgradeQueue > 0 ) ) then player.Rerolls = 0 --player.InShop = false player.InMenu = false player.InRoll = false player.InQueue = false else player.Rerolls = 0 --player.InShop = false player.InMenu = false player.InRoll = false player.InQueue = true player.Upgradesbought = 0 BuildUpgradeMenu( player, player, VOTE_CATEGORIES[player.UpgradeQueue[1]].LootTable, 1, player.UpgradeQueue[1] ) player.QueuedLoot = player.UpgradeQueue[1] table.remove( player.UpgradeQueue, 1 ) end else timer.Simple( 0.2, function() player.Menu["flags"] = MENUFLAG_NO_SOUND | MENUFLAG_BUTTON_EXIT player:DisplayMenu( player.Menu ) end) end else player.ShopUpgradesbought = player.ShopUpgradesbought + 1 if not ( HasUpgrade( player, UPGRADE_GREED_MONEY_SELLOUT ) ) then player.Menu[ tonumber(index) ]["disabled"] = true else timer.Simple( 0, function() player.Menu[ tonumber(index) ]["disabled"] = ( IsUpgradeCapped( player, tonumber( value ), Upgrades[tonumber(value)].usePlayerUpgradesTable ) ) end) end end end 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 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 ) if MISSION_MODIFY_STATS[MISSION_MODIFY_REROLL_COST].enabled then reroll_cost = reroll_cost * 4 end if ( player.m_nCurrency < reroll_cost ) 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( reroll_cost ) player.Rerolls = player.Rerolls + 1 player.InMenu = false player.InRoll = true if not ( player.InQueue ) then BuildUpgradeMenu( player, player, math.abs( tonumber( value ) + 1 ), 0.5 ) else BuildUpgradeMenu( player, player, VOTE_CATEGORIES[player.QueuedLoot].LootTable, 0.5, player.QueuedLoot ) end timer.Simple( 0.5, function() player.InRoll = false end) end end function UpgradeSkipped( player, reason ) if ( reason == MENU_CANCEL_NODISPLAY or reason == MENU_CANCEL_INTERRUPTED ) and ( player.Menu ~= nil ) then player:DisplayMenu( player.Menu ) return 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 if ( player.Menu[i] ) and ( player.Menu[i].value ) and ( tonumber(player.Menu[i].value) ) and ( Upgrades[tonumber(player.Menu[i].value)] ) then local bonus = 1.15 if Upgrades[tonumber(player.Menu[i].value)].Category == UPGRADE_CATEGORY_BOURGEOISIE then bonus = 1.5 end total = total + GetTypeCost(Upgrades[tonumber(player.Menu[i].value)].Type) * bonus end end total = math.floor( total / player.DisplayedUpgrades ) local sellout = HasUpgrade( player, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( player.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) total = total * decrease end player:AddCurrency( total ) if not ( player.UpgradeQueue[1] and ( #player.UpgradeQueue > 0 ) ) then player.InMenu = false player.InRoll = false player.InQueue = 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.Rerolls = 0 --player.InShop = false player.InMenu = false player.InRoll = false player.InQueue = true player:PlaySoundToSelf( "MVM.MoneyPickup" ) player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) BuildUpgradeMenu( player, player, VOTE_CATEGORIES[player.UpgradeQueue[1]].LootTable, 1, player.UpgradeQueue[1] ) player.QueuedLoot = player.UpgradeQueue[1] table.remove( player.UpgradeQueue, 1 ) end else if not ( player.UpgradeQueue[1] and ( #player.UpgradeQueue > 0 ) ) then player.InMenu = false player.InRoll = false player.InShop = false player.InQueue = false player.Rerolls = 0 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.InQueue = true player.Rerolls = 0 player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) player:PlaySoundToSelf( "ui/item_metal_tiny_drop.wav" ) BuildUpgradeMenu( player, player, VOTE_CATEGORIES[player.UpgradeQueue[1]].LootTable, 1, player.UpgradeQueue[1] ) player.QueuedLoot = player.UpgradeQueue[1] table.remove( player.UpgradeQueue, 1 ) end end -- end end function HasUpgrade( player, upgrade, amount ) amount = amount or 1 if ( not GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] ) or ( not GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][upgrade] ) then return false end return GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][upgrade] >= amount end function IsCateShop( cate ) return ( ( cate == UPGRADE_CATEGORY_SHOP ) or ( cate == UPGRADE_CATEGORY_SHOP_DAMAGE ) or ( cate == UPGRADE_CATEGORY_SHOP_BOURGEOISIE ) or ( cate == UPGRADE_CATEGORY_SHOP_EPIC ) or ( cate == UPGRADE_CATEGORY_SHOP_GREED ) or ( cate == UPGRADE_CATEGORY_SHOP_HEALTH ) or ( cate == UPGRADE_CATEGORY_SHOP_SPEED ) or ( cate == UPGRADE_CATEGORY_SHOP_WEAPON ) ) end function GetUpgradesTableFiltered( type, cate ) local new_tbl = {} -- local cate = UPGRADE_LOOT_CATEGORY if cate == UPGRADE_CATEGORY_ELITE_BOSS then cate = UPGRADE_CATEGORY_EPIC end -- print( type, cate ) for index, upgrade in pairs( Upgrades ) do if ( cate ~= UPGRADE_CATEGORY_ALL ) and ( cate ~= UPGRADE_CATEGORY_EPIC ) and ( cate ~= UPGRADE_CATEGORY_GREED ) and ( not IsCateShop( cate ) ) and ( type ~= UPGRADE_CATEGORY_GREED ) then -- print( upgrade.Type, type, upgrade.Type == type, upgrade.Category, cate, upgrade.Category == cate ) if ( ( upgrade.Type == type ) and ( upgrade.Category == cate ) and ( cate ~= UPGRADE_CATEGORY_BOURGEOISIE ) ) or ( ( cate == UPGRADE_CATEGORY_BOURGEOISIE ) and ( upgrade.Category == cate ) ) then table.insert( new_tbl, index ) end else if ( upgrade.Type == type and ( cate ~= UPGRADE_CATEGORY_BOURGEOISIE ) ) or ( ( cate == UPGRADE_CATEGORY_BOURGEOISIE ) and ( upgrade.Category == cate ) ) then -- I'm too fucking tired for this 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, usePlayerUpgradesTable, copies ) local capped = false usePlayerUpgradesTable = usePlayerUpgradesTable or false copies = copies or 0 if not usePlayerUpgradesTable then upgrade = Upgrades[upgrade] for i = 1, #upgrade.Attributes do -- print(i, upgrade.Name, upgrade.Attributes[i].cap, upgrade.Attributes[i].slot ) if upgrade.Attributes[i].cap == nil then goto skip end -- print( upgrade.Name, upgrade.Attributes[i].cap, upgrade.Attributes[i].slot, upgrade.Attributes[i].type, "\n", player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ), upgrade.Attributes[i].cap, ( ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) +0.001) >= upgrade.Attributes[i].cap ) ) if capped == true then return capped end if upgrade.Attributes[i].slot == LOADOUT_POSITION_BODY then local val = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + 0.0005 ) for rep = 1, copies do val = val + upgrade.Attributes[i].increment end if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then capped = ( val >= upgrade.Attributes[i].cap ) elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then capped = ( val <= upgrade.Attributes[i].cap ) elseif upgrade.Attributes[i].type == "set" then capped = ( val >= upgrade.Attributes[i].cap ) end elseif upgrade.Attributes[i].slot == LOADOUT_POSITION_PRIMARY_AND_SECONDARY then local val = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + 0.0005 ) for rep = 1, copies do val = val + upgrade.Attributes[i].increment end local valS = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + 0.0005 ) for rep = 1, copies do valS = valS + upgrade.Attributes[i].increment end if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then capped = ( val >= upgrade.Attributes[i].cap) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) ) then capped = ( valS >= upgrade.Attributes[i].cap) end elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then capped = ( val <= upgrade.Attributes[i].cap ) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) ) then capped = ( valS <= upgrade.Attributes[i].cap ) end elseif upgrade.Attributes[i].type == "set" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then capped = ( val >= upgrade.Attributes[i].cap ) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) ) then capped = ( valS >= upgrade.Attributes[i].cap ) end end elseif upgrade.Attributes[i].slot == LOADOUT_POSITION_ALL then local val = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + 0.0005 ) for rep = 1, copies do val = val + upgrade.Attributes[i].increment end local valS = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + 0.0005 ) for rep = 1, copies do valS = valS + upgrade.Attributes[i].increment end local valM = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, LOADOUT_POSITION_BODY ) + 0.0005 ) for rep = 1, copies do valM = valM + upgrade.Attributes[i].increment end if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then capped = ( val >= upgrade.Attributes[i].cap) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) ) then capped = ( valS >= upgrade.Attributes[i].cap) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) ) then capped = ( valM >= upgrade.Attributes[i].cap) end elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then capped = ( val <= upgrade.Attributes[i].cap ) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) ) then capped = ( valS <= upgrade.Attributes[i].cap ) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) ) then capped = ( valM <= upgrade.Attributes[i].cap ) end elseif upgrade.Attributes[i].type == "set" then if IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY)) then capped = ( val >= upgrade.Attributes[i].cap ) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY)) ) then capped = ( valS >= upgrade.Attributes[i].cap ) end if ( capped == false ) and ( IsValid(player:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE)) ) then capped = ( valM >= upgrade.Attributes[i].cap ) end end else local val = ( player:GetAttributeValueClean( upgrade.Attributes[i].attr, upgrade.Attributes[i].default, upgrade.Attributes[i].slot ) + 0.0005 ) for rep = 1, copies do val = val + upgrade.Attributes[i].increment end if upgrade.Attributes[i].type == "add" or upgrade.Attributes[i].type == "multiply" then capped = ( val >= upgrade.Attributes[i].cap) elseif upgrade.Attributes[i].type == "subtract" or upgrade.Attributes[i].type == "divide" then capped = ( val <= upgrade.Attributes[i].cap ) elseif upgrade.Attributes[i].type == "set" then capped = ( val >= upgrade.Attributes[i].cap ) end end end ::skip:: else -- return GLOBAL_PLAYER_UPGRADES[ player.m_szNetworkIDString ][upgrade] >= Upgrades[upgrade].Cap ------ THIS return HasUpgrade( player, upgrade, Upgrades[upgrade].Cap - copies ) end return capped end function OnPlayerConnected( player ) if player:IsRealPlayer() then player.DisplayedUpgrades = UPGRADE_DEFAULT_COUNT player.DisplayedShopUpgrades = UPGRADE_DEFAULT_COUNT_SHOP player.Upgradesbought = 0 player.ShopUpgradesbought = 0 player.ShopCostMult = 1 player.MaxUpgradesBuy = UPGRADE_DEFAULT_MAX_UPGRADES player.Rerolls = 0 player.MaxRerolls = UPGRADE_DEFAULT_MAX_REROLLS player.CostMult = 1 player.CashMult = 1 player.CanHaggle = false player.VoteMenu = nil player.Voted = false player.VotedForLoot = UPGRADE_CATEGORY_ALL player.IncomeDelay = 0 player.AdderanlineTime = 0 player.HypeEnable = false player.Hype = 0 player.HypeMax = 200 player.MoneyShotTimestamp = CurTime() player.MoneyShotDelay = CurTime() player.ThisMoneyShot = CurTime() player.MoneyShotVictims = {} -- player.HealingFromNotKits_Mult = 1 player.SpookDelayEnd = 0 player.DisguisedLast = 0 player.MadMilkDelay = CurTime() player.JarateDelay = CurTime() player.HealthLossDelay = CurTime() player.ReflectParticleDelay = CurTime() player.ReloadDamageTimer = 0 player.UpgradeQueue = {} player.InQueue = false player.QueuedLoot = UPGRADE_CATEGORY_ALL player.InMenu = false player.InRoll = false player.InShop = false player.Haggled = false player.Entrance = nil player.Exit = nil player.Reanimator = nil player.ReanimatorPos = Vector(0,0,0) player.Revives = 0 player.UsedMoleTunnel = false player.ShopLoot = UPGRADE_CATEGORY_ALL player.HealthGained = 0 player.HealthMultGained = 0 player.DamageMultGained = 0 player.FireRateMultGained = 1 if not GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] then GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] = {} for index, upgrade in pairs( Upgrades ) do GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][index] = 0 end end if not GLOBAL_PLAYER_DATA[player.m_szNetworkIDString] then SetupPlayerData( player ) end player:RemoveAllCallbacks( ON_DAMAGE_RECEIVED_PRE ) player:RemoveAllCallbacks( ON_DEATH ) player.DamageCallback = player:AddCallback( ON_DAMAGE_RECEIVED_PRE, function( ent, dmginfo ) if IsValid( ent:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) ) then local disguise = ent:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) local attr = disguise:GetAttributeValue( "disguise damage reduction" ) or 1 local fire_immune = ( player:GetAttributeValueClean("disguise no burn", 0, LOADOUT_POSITION_PDA ) > 0 ) and ent:InCond( TF_COND_DISGUISED ) if fire_immune and ( IsDamageType( dmginfo.DamageType, DMG_BURN ) or IsDamageType( dmginfo.DamageType, DMG_IGNITE ) ) then dmginfo.Damage = 0 return true end if ent:InCond( TF_COND_DISGUISED ) then dmginfo.Damage = dmginfo.Damage * attr return true end end if ( dmginfo.Inflictor ~= nil ) and ( dmginfo.Attacker ~= nil ) then if dmginfo.Inflictor.m_hLauncher == nil and ( dmginfo.Inflictor:GetOwner() == ent ) then dmginfo.Damage = 0 return true end end end) player.DeathCallback = player:AddCallback( ON_DEATH, function() if HasUpgrade( player, UPGRADE_BOURGEOISIE_MARTYRDOM ) then -- print( player, "Setup" ) local mimic = WeaponMimic({ TeamNum = player.m_iTeamNum, WeaponType = 1, -- grenade Damage = 100, Crits = 0, SplashRadius = 150, SpeedMax = 0, SpeedMin = 0, ["$dmgtype"] = DMG_BLAST | DMG_ALWAYSGIB | DMG_HALF_FALLOFF, ["$weaponname"] = "Martyrdom", ["$preventshootparent"] = 1, origin = tostring(player:GetAbsOrigin() + Vector( 0, 0, 16 ) ), angles = tostring(Vector(0, 00, 0)), }) mimic["$AddWeaponAttribute"](mimic, "fuse bonus|2" ) mimic["$AddWeaponAttribute"](mimic, "blast radius increased|2" ) mimic["$AddWeaponAttribute"](mimic, "dmg pierces resists absorbs|1" ) mimic:SetAbsAngles( Vector( 90, 0, 0 ) ) -- mimic["$SetOwner"](mimic, player) if mimic:IsValid() then mimic:FireOnce() timer.Simple(10, function() mimic:Remove() end) end end timer.Simple( 0.1, function() for _, reanimator in pairs(ents.FindInSphere(player:GetAbsOrigin(), 200)) do if reanimator:GetClassname() == "entity_revive_marker" then if reanimator.m_hOwner == player then player.Reanimator = reanimator player.ReanimatorPos = reanimator:GetAbsOrigin() if MISSION_MODIFY_STATS[MISSION_MODIFY_NORESPAWN].enabled then if player.Revives >= 1 then reanimator:Kill() end end end end end end) end) timer.Simple(0.1, function() if ( (not LEAVERS[tostring(player.m_szNetworkIDString)] ) and ( LEAVERS[tostring(player.m_szNetworkIDString)] ~= false ) ) or ( LEAVERS[tostring(player.m_szNetworkIDString)] == nil ) or ( ( LEAVERS[tostring(player.m_szNetworkIDString)] ~= true ) and ( LEAVERS[tostring(player.m_szNetworkIDString)] ~= false ) ) then player.IsNewPlayer = true end if InWave then LEAVERS[tostring(player.m_szNetworkIDString)] = true player.IsLeaver = true else LEAVERS[tostring(player.m_szNetworkIDString)] = false player.IsLeaver = false end print( "connected callback", player:GetPlayerName(), player.IsLeaver, player.IsNewPlayer, player.m_szNetworkIDString, LEAVERS[tostring(player.m_szNetworkIDString)] ) end) player.spawncallback = player:AddCallback( ON_SPAWN, function( ent ) timer.Simple(0.5, function() if player:OnTeam( 2 ) and player:IsAlive() and ( player.m_iClass > 0 ) then if IsOtherUpload then util.PrintToChat( player, "\x07fc206d **********" ) util.PrintToChat( player, "\x07fc206d This mission is NOT an official mission, I have no problem with this but I cannot guarantee that I will be able to fix any bugs you find here" ) util.PrintToChat( player, "\x07fc206d **********" ) end print( "spawning hook", player:GetPlayerName(), player.IsLeaver, player.IsNewPlayer, player.m_szNetworkIDString, LEAVERS[tostring(player.m_szNetworkIDString)] ) if ( player.IsLeaver ) or ( LEAVERS[tostring(player.m_szNetworkIDString)] ) then player:Suicide() player.IsLeaver = false end if ( player.IsNewPlayer ) then player.IsNewPlayer = false if WaveStarted then player:AddCurrency( GetCashByWave( WAVE - 1 ) ) if #UPGRADES_CATES > 0 then player.UpgradeQueue = {} for i, v in pairs( UPGRADES_CATES ) do table.insert( player.UpgradeQueue, v ) end player:Print( PRINT_TARGET_CENTER, "Welcome, catch up on missing upgrades when you can" ) util.PrintToChat( player, "Welcome, catch up on missing upgrades when you can" ) BuildUpgradeMenu( player, player, VOTE_CATEGORIES[player.UpgradeQueue[1]].LootTable, 1, player.UpgradeQueue[1] ) end end end LEAVERS[tostring(player.m_szNetworkIDString)] = false player:RemoveCallback( ON_SPAWN, player.spawncallback ) end end) end) player.InputCallback = player:AddCallback( ON_KEY_PRESSED, function( ent, key ) if ent.m_iClass == TF_CLASS_SNIPER then if ( key == IN_JUMP ) and ( ent.HookProjectile ) and ( IsValid(ent.HookProjectile) ) and ( ent.HookProjectile:GetClassname() == "tf_projectile_grapplinghook" ) then if ( ent:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and ent:InCond( TF_COND_GRAPPLINGHOOK ) ) then ent.HookProjectile:Remove() end end elseif ent.m_iClass == TF_CLASS_SPY then if ( key == IN_RELOAD ) and ( ent.HookProjectile ) and ( IsValid(ent.HookProjectile) ) and ( ent.HookProjectile:GetClassname() == "tf_projectile_grapplinghook" ) then if ( ent:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and ent:InCond( TF_COND_GRAPPLINGHOOK ) ) then ent.HookProjectile:Remove() end end end end) end end function OnPlayerDisconnected( player ) if ( IsValid( player ) ) and ( player:OnTeam( 2 ) ) and ( player.m_iClass > 0 ) then LEAVERS[tostring(player.m_szNetworkIDString)] = true end end function SetUpPlayerInfo( player ) player.DisplayedUpgrades = UPGRADE_DEFAULT_COUNT player.DisplayedShopUpgrades = UPGRADE_DEFAULT_COUNT_SHOP player.Upgradesbought = 0 player.ShopUpgradesbought = 0 player.ShopCostMult = 1 player.MaxUpgradesBuy = UPGRADE_DEFAULT_MAX_UPGRADES player.Rerolls = 0 player.MaxRerolls = UPGRADE_DEFAULT_MAX_REROLLS player.CostMult = 1 player.CashMult = 1 player.CanHaggle = false player.VoteMenu = nil player.IncomeDelay = 0 player.AdderanlineTime = 0 player.HypeEnable = false player.Hype = 0 player.HypeMax = 200 player.MoneyShotTimestamp = CurTime() player.MoneyShotDelay = CurTime() player.ThisMoneyShot = CurTime() player.MoneyShotVictims = {} -- player.HealingFromNotKits_Mult = 1 player.SpookDelayEnd = 0 player.DisguisedLast = 0 player.MadMilkDelay = CurTime() player.JarateDelay = CurTime() player.HealthLossDelay = CurTime() player.ReflectParticleDelay = CurTime() player.ReloadDamageTimer = 0 player.Entrance = nil player.Exit = nil if not GLOBAL_PLAYER_DATA[player.m_szNetworkIDString] then SetupPlayerData( player ) end -- PrintTable(GLOBAL_PLAYER_DATA[player.m_szNetworkIDString]) ResetPlayerData( player ) if not GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] then GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] = {} for index, upgrade in pairs( Upgrades ) do GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][index] = 0 end player.InMenu = false player.InRoll = false player.InShop = false player.Haggled = false player.Reanimator = nil player.ReanimatorPos = Vector(0,0,0) player.Revives = 0 player.Voted = false player.VotedForLoot = UPGRADE_CATEGORY_ALL player.UpgradeQueue = {} player.InQueue = false player.QueuedLoot = UPGRADE_CATEGORY_ALL player.UsedMoleTunnel = false player.HealthGained = 0 player.HealthMultGained = 0 player.DamageMultGained = 0 player.FireRateMultGained = 1 player.ShopLoot = UPGRADE_CATEGORY_ALL player:RemoveAllCallbacks( ON_DAMAGE_RECEIVED_PRE ) player:RemoveAllCallbacks( ON_DEATH ) player.DamageCallback = player:AddCallback( ON_DAMAGE_RECEIVED_PRE, function( ent, dmginfo ) if IsValid( ent:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) ) then local disguise = ent:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) local attr = disguise:GetAttributeValue( "disguise damage reduction" ) or 1 local fire_immune = ( player:GetAttributeValueClean("disguise no burn", 0, LOADOUT_POSITION_PDA ) > 0 ) and ent:InCond( TF_COND_DISGUISED ) if fire_immune and ( IsDamageType( dmginfo.DamageType, DMG_BURN ) or IsDamageType( dmginfo.DamageType, DMG_IGNITE ) ) then dmginfo.Damage = 0 return true end if ent:InCond( TF_COND_DISGUISED ) then dmginfo.Damage = dmginfo.Damage * attr return true end end if ( dmginfo.Inflictor ~= nil ) and ( dmginfo.Attacker ~= nil ) then if dmginfo.Inflictor.m_hLauncher == nil and ( dmginfo.Inflictor:GetOwner() == ent ) then dmginfo.Damage = 0 return true end end end) player.DeathCallback = player:AddCallback( ON_DEATH, function() if HasUpgrade( player, UPGRADE_BOURGEOISIE_MARTYRDOM ) then -- print( player, "Setup" ) local mimic = WeaponMimic({ TeamNum = player.m_iTeamNum, WeaponType = 1, -- grenade Damage = 100, Crits = 0, SplashRadius = 150, SpeedMax = 0, SpeedMin = 0, ["$dmgtype"] = DMG_BLAST | DMG_ALWAYSGIB | DMG_HALF_FALLOFF, ["$weaponname"] = "Martyrdom", ["$preventshootparent"] = 1, origin = tostring(player:GetAbsOrigin() + Vector( 0, 0, 16 ) ), angles = tostring(Vector(0, 00, 0)), }) mimic["$AddWeaponAttribute"](mimic, "fuse bonus|2" ) mimic["$AddWeaponAttribute"](mimic, "blast radius increased|2" ) mimic["$AddWeaponAttribute"](mimic, "dmg pierces resists absorbs|1" ) mimic:SetAbsAngles( Vector( 90, 0, 0 ) ) -- mimic["$SetOwner"](mimic, player) if mimic:IsValid() then mimic:FireOnce() timer.Simple(10, function() mimic:Remove() end) end end timer.Simple( 0.1, function() for _, reanimator in pairs(ents.FindInSphere(player:GetAbsOrigin(), 200)) do if reanimator:GetClassname() == "entity_revive_marker" then if reanimator.m_hOwner == player then player.Reanimator = reanimator player.ReanimatorPos = reanimator:GetAbsOrigin() if MISSION_MODIFY_STATS[MISSION_MODIFY_NORESPAWN].enabled then if player.Revives >= 1 then reanimator:Kill() end end end end end end) end) player.InputCallback = player:AddCallback( ON_KEY_PRESSED, function( ent, key ) if ent.m_iClass == TF_CLASS_SNIPER then if ( key == IN_JUMP ) and ( ent.HookProjectile ) and ( IsValid(ent.HookProjectile) ) and ( ent.HookProjectile:GetClassname() == "tf_projectile_grapplinghook" ) then if ( ent:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and ent:InCond( TF_COND_GRAPPLINGHOOK ) ) then ent.HookProjectile:Remove() end end elseif ent.m_iClass == TF_CLASS_SPY then if ( key == IN_RELOAD ) and ( ent.HookProjectile ) and ( IsValid(ent.HookProjectile) ) and ( ent.HookProjectile:GetClassname() == "tf_projectile_grapplinghook" ) then if ( ent:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and ent:InCond( TF_COND_GRAPPLINGHOOK ) ) then ent.HookProjectile:Remove() end end end end) end end function ResetPlayerInfo( player,dev ) player = dev or player player.DisplayedUpgrades = UPGRADE_DEFAULT_COUNT player.DisplayedShopUpgrades = UPGRADE_DEFAULT_COUNT_SHOP player.Upgradesbought = 0 player.ShopUpgradesbought = 0 player.ShopCostMult = 1 player.MaxUpgradesBuy = UPGRADE_DEFAULT_MAX_UPGRADES player.Rerolls = 0 player.MaxRerolls = UPGRADE_DEFAULT_MAX_REROLLS GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString] = {} player.CostMult = 1 player.CashMult = 1 player.CanHaggle = false player.VoteMenu = nil player.Voted = false player.VotedForLoot = UPGRADE_CATEGORY_ALL player.IncomeDelay = 0 player.MaxIncome = 100 player.IncomeThisWave = 0 player.AdderanlineTime = 0 player.HypeEnable = false player.Hype = 0 player.HypeMax = 200 -- player.HealingFromNotKits_Mult = 1 player.DisguisedLast = 0 player.MoneyShotTimestamp = CurTime() player.MoneyShotDelay = CurTime() player.ThisMoneyShot = CurTime() player.MoneyShotVictims = {} player.ReloadDamageTimer = 0 player.Reanimator = nil player.ReanimatorPos = Vector(0,0,0) player.Revives = 0 player.Entrance = nil player.Exit = nil SetupPlayerData( player ) player.UpgradeQueue = {} player.InQueue = false player.QueuedLoot = UPGRADE_CATEGORY_ALL for index, upgrade in pairs( Upgrades ) do GLOBAL_PLAYER_UPGRADES[player.m_szNetworkIDString][index] = 0 end player.InMenu = false player.InRoll = false player.InShop = false player.TurretMode = false player.TankMode = false player.Haggled = false player.UsedMoleTunnel = false player.SpookDelayEnd = 0 player.MadMilkDelay = CurTime() player.JarateDelay = CurTime() player.HealthLossDelay = CurTime() player.ReflectParticleDelay = CurTime() player.ShopLoot = UPGRADE_CATEGORY_ALL player.HealthGained = 0 player.HealthMultGained = 0 player.DamageMultGained = 0 player.FireRateMultGained = 1 player:RemoveAllCallbacks( ON_DAMAGE_RECEIVED_PRE ) player:RemoveAllCallbacks( ON_DEATH ) player.DamageCallback = player:AddCallback( ON_DAMAGE_RECEIVED_PRE, function( ent, dmginfo ) if IsValid( ent:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) ) then local disguise = ent:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) local attr = disguise:GetAttributeValue( "disguise damage reduction" ) or 1 local fire_immune = ( player:GetAttributeValueClean("disguise no burn", 0, LOADOUT_POSITION_PDA ) > 0 ) and ent:InCond( TF_COND_DISGUISED ) if fire_immune and ( IsDamageType( dmginfo.DamageType, DMG_BURN ) or IsDamageType( dmginfo.DamageType, DMG_IGNITE ) ) then dmginfo.Damage = 0 return true end if ent:InCond( TF_COND_DISGUISED ) then dmginfo.Damage = dmginfo.Damage * attr return true end end if ( dmginfo.Inflictor ~= nil ) and ( dmginfo.Attacker ~= nil ) then if dmginfo.Inflictor.m_hLauncher == nil and ( dmginfo.Inflictor:GetOwner() == ent ) then dmginfo.Damage = 0 return true end end end) player.DeathCallback = player:AddCallback( ON_DEATH, function() if HasUpgrade( player, UPGRADE_BOURGEOISIE_MARTYRDOM ) then -- print( player, "Reset" ) local mimic = WeaponMimic({ TeamNum = player.m_iTeamNum, WeaponType = 1, -- grenade Damage = 100, Crits = 0, SplashRadius = 150, SpeedMax = 0, SpeedMin = 0, ["$dmgtype"] = DMG_BLAST | DMG_ALWAYSGIB | DMG_HALF_FALLOFF, ["$weaponname"] = "Martyrdom", ["$preventshootparent"] = 1, origin = tostring(player:GetAbsOrigin() + Vector( 0, 0, 16 ) ), angles = tostring(Vector(0, 00, 0)), }) mimic["$AddWeaponAttribute"](mimic, "fuse bonus|2" ) mimic["$AddWeaponAttribute"](mimic, "blast radius increased|2" ) mimic["$AddWeaponAttribute"](mimic, "dmg pierces resists absorbs|1" ) mimic:SetAbsAngles( Vector( 90, 0, 0 ) ) -- mimic["$SetOwner"](mimic, player) if mimic:IsValid() then mimic:FireOnce() timer.Simple(10, function() mimic:Remove() end) end end timer.Simple( 0.1, function() for _, reanimator in pairs(ents.FindInSphere(player:GetAbsOrigin(), 200)) do if reanimator:GetClassname() == "entity_revive_marker" then if reanimator.m_hOwner == player then player.Reanimator = reanimator player.ReanimatorPos = reanimator:GetAbsOrigin() if MISSION_MODIFY_STATS[MISSION_MODIFY_NORESPAWN].enabled then if player.Revives >= 1 then reanimator:Kill() end end end end end end) end) player.InputCallback = player:AddCallback( ON_KEY_PRESSED, function( ent, key ) if ent.m_iClass == TF_CLASS_SNIPER then if ( key == IN_JUMP ) and ( ent.HookProjectile ) and ( IsValid(ent.HookProjectile) ) and ( ent.HookProjectile:GetClassname() == "tf_projectile_grapplinghook" ) then if ( ent:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and ent:InCond( TF_COND_GRAPPLINGHOOK ) ) then ent.HookProjectile:Remove() end end elseif ent.m_iClass == TF_CLASS_SPY then if ( key == IN_RELOAD ) and ( ent.HookProjectile ) and ( IsValid(ent.HookProjectile) ) and ( ent.HookProjectile:GetClassname() == "tf_projectile_grapplinghook" ) then if ( ent:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and ent:InCond( TF_COND_GRAPPLINGHOOK ) ) then ent.HookProjectile:Remove() end end end end) end function OnPlayerChat( player, text ) for i,s in pairs(COMMANDS_TABLE) do if ( string.match(string.lower(text), string.lower(s.name)) ) then if s.type == "viewupgrades" then player.InRoll = true BuildViewUpgradeMenu( player, player ) return end if s.type == "flexoptions" then player.InRoll = true BuildFlexMenu( player, player ) return end if s.type == "infomenu" then player.InRoll = true BuildBaseInfoMenu( player, player ) return end if s.type == "changelog" then for i, s in pairs( ChangeLog ) do if string.starts( s, "+ " ) then s = "\x0737cefc" .. s elseif string.starts( s, "- " ) then s = "\x07fc3737" .. s elseif string.starts( s, "= " ) then s = "\x07effc37" .. s end util.PrintToChat( player, s ) end return end end end end function HookBow( projectile, activator, weapon ) if ( ( projectile ) and ( IsValid(projectile) ) and ( projectile:GetClassname() == "tf_projectile_grapplinghook" ) ) and ( activator.HookProjectile and ( IsValid(projectile) ) and ( projectile:GetClassname() == "tf_projectile_grapplinghook" ) ) and ( projectile ~= activator.HookProjectile ) then weapon.m_iClip1 = weapon.m_iClip1 + 1 projectile:Remove() return end activator.HookProjectile = projectile if activator.m_iClass == TF_CLASS_SPY then activator.HookThink = timer.Create( 0.1, function() if not ( ( projectile ) and ( IsValid(projectile) ) and ( projectile:GetClassname() == "tf_projectile_grapplinghook" ) ) then return false end local distance = 300 local nearby_players = 0 for _, ply in pairs( ents.FindInSphere( projectile:GetAbsOrigin(), 96, "player" ) ) do if ply:IsPlayer() and ply:IsAlive() and ( not ply:OnTeam( 1 ) ) then nearby_players = 1 break end end if nearby_players > 0 then distance = 96 end if ( activator:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and activator:InCond( TF_COND_GRAPPLINGHOOK ) ) then if activator:GetAbsOrigin():Distance( projectile:GetAbsOrigin() ) <= distance then projectile:Remove() end end end, 0 ) elseif activator.m_iClass == TF_CLASS_ENGINEER then projectile.PullbackTime = CurTime() + 1 local RealIn = false activator.HookThink = timer.Create( 0, function() if ( not ( ( projectile ) and ( IsValid(projectile) ) and ( projectile:GetClassname() == "tf_projectile_grapplinghook" ) ) ) or ( not weapon ) then return false end if ( activator:InCond( TF_COND_GRAPPLINGHOOK_LATCHED ) and activator:InCond( TF_COND_GRAPPLINGHOOK ) ) then projectile:PlaySound( "=80|physics/metal/metal_box_break1.wav" ) activator:PlaySoundToSelf( "physics/metal/metal_box_break1.wav" ) weapon.m_flNextPrimaryAttack = CurTime() + 0.5 projectile:Remove() return end weapon.m_flNextPrimaryAttack = CurTime() + 1 if projectile.PullbackTime > CurTime() then local filt = ents.GetAllPlayers() table.insert( filt, projectile ) local Trace = util.Trace({ start = projectile:GetAbsOrigin(), distance = 100 * activator:GetAttributeValueClean( "projectile speed increased", 1, LOADOUT_POSITION_PRIMARY ), angles = projectile:GetAbsAngles(), mask = MASK_SOLID_BRUSHONLY, filter = filt, }) if Trace.Hit then projectile.PullbackTime = 0 activator:PlaySoundToSelf( "physics/metal/metal_grenade_impact_hard2.wav" ) end end if CurTime() >= projectile.PullbackTime then if not RealIn then activator:PlaySoundToSelf( "doors/door_metal_thin_open1.wav" ) RealIn = true end local speed = 300 * activator:GetAttributeValueClean( "projectile speed increased", 1, LOADOUT_POSITION_PRIMARY ) projectile:SetLocalVelocity( Vector( speed, speed, speed ) * ( projectile:GetAbsOrigin():FaceVector( activator:GetEyePos() ):GetForward() ) ) projectile:SetAbsAngles( activator:GetEyePos():FaceVector( projectile:GetAbsOrigin() ) ) if projectile:GetAbsOrigin():Distance( activator:GetEyePos() ) <= 128 then weapon.m_iClip1 = weapon.m_iClip1 + 1 weapon.m_flNextPrimaryAttack = CurTime() activator:PlaySoundToSelf( "Christmas.GiftPickup" ) projectile:Remove() end for _, other in pairs( ents.FindInSphere( projectile:GetAbsOrigin(), 300, "player" ) ) do if other:IsBot() and other:IsAlive() and ( not other:OnTeam(1) ) then local hookin_speed = 100 * activator:GetAttributeValueClean( "projectile speed increased", 1, LOADOUT_POSITION_PRIMARY ) if tobool( other.m_bIsMiniBoss ) then hookin_speed = hookin_speed * 0.2 end other:SetPlayerVelocity( other:GetPlayerVelocity() + ( Vector( hookin_speed, hookin_speed, hookin_speed ) * other:GetAbsOrigin():FaceVector( projectile:GetAbsOrigin() ):GetForward() ) ) end end end end, 0) end end function OnAttackLogic(projectile,activator,weapon) -- print( activator ) if IsValid(activator) and activator:IsAlive() and IsValid(weapon) and ( weapon ~= activator:GetPlayerItemBySlot(2) ) then if weapon:GetAttributeValue("airblast vulnerability multiplier hidden") ~= nil then local val = weapon:GetAttributeValue("airblast vulnerability multiplier hidden") local velo = activator:GetPlayerVelocity() - ( Vector( val, val, 0 ) * activator:GetEyeAngles():GetForward() ) activator:SetPlayerVelocity( velo ) end -- print( ( weapon:GetAttributeValue("tag__summer2014") ~= nil ) ) if ( weapon:GetAttributeValue("tag__summer2014") ~= nil ) and ( IsValid( projectile ) ) then ExplosiveArrows(projectile,activator,weapon) end if weapon:GetItemName() == "Triple Tap" then if not weapon.FiredTimeStamp then weapon.FiredTimeStamp = 0 end if not weapon.Bursts then weapon.Bursts = 1 end local delay = ( ( 1.5 * activator:GetAttributeValueByClass( "mult_postfiredelay", 1 ) ) * activator:GetAttributeValueClean("burst fire rate mult", 1, LOADOUT_POSITION_PRIMARY) ) - 0.015 local time_dif = CurTime() - weapon.FiredTimeStamp if time_dif <= delay then weapon.Bursts = weapon.Bursts + 1 else weapon.Bursts = 1 end -- print( weapon.FiredTimeStamp, time_dif, delay, weapon.Bursts, time_dif < delay, weapon.Bursts > weapon:GetAttributeValue("burst fire count") ) if weapon.Bursts >= activator:GetAttributeValueClean("burst fire count", 1, LOADOUT_POSITION_PRIMARY) then timer.Simple( 0, function() weapon.m_flNextPrimaryAttack = CurTime() + delay end) end weapon.FiredTimeStamp = CurTime() end if weapon:GetClassname() == "tf_weapon_minigun" then local penaltyPerTap = 0.1 local maxPenalty = 0.1 local penalty = 1 if not weapon.ShouldNextFireTimeStamp then weapon.ShouldNextFireTimeStamp = 0 end if not weapon.ShouldNextFireTimeStampWindup then weapon.ShouldNextFireTimeStampWindup = 0 end if not weapon.TapFireCount then weapon.TapFireCount = 0 end local delay = ( ( 0.105 * activator:GetAttributeValueByClass( "mult_postfiredelay", 1 ) ) ) - ( 3 / 66 ) local delay2 = ( ( 0.105 * activator:GetAttributeValueByClass( "mult_postfiredelay", 1 ) ) ) + ( 3 / 66 ) if weapon.ShouldNextFireTimeStamp >= CurTime() then weapon.TapFireCount = weapon.TapFireCount + 1 else weapon.TapFireCount = 0 end if ( weapon.ShouldNextFireTimeStampWindup + delay2 ) <= CurTime() then weapon.m_iConsecutiveShots = 0 end weapon.ShouldNextFireTimeStamp = CurTime() + delay weapon.ShouldNextFireTimeStampWindup = CurTime() + delay penalty = math.max( 1 - ( penaltyPerTap * weapon.TapFireCount ), maxPenalty ) weapon:SetAttributeValue( "dmg penalty vs players", penalty ) end if ( string.match( weapon:GetItemName(), "Hook" ) ) or ( weapon:GetItemName() == "Harpoon Gun" ) then HookBow( projectile, activator, weapon ) end end end function OnKillLogic(projectile,activator,caller) if( activator:GetAttributeValue("kill eater user 2") or 0 ) > 0 then activator.AdderanlineTime = CurTime() + 3 end if ( activator.m_hActiveWeapon:GetAttributeValue("disguise on backstab", true) or 0 ) > 0 then activator.DisguisedLast = CurTime() end if ( activator:GetAttributeValue( "unlimited quantity" ) or 0 ) > 0 then local amount = ENEMY_BASE_CASH * caller.CashMult * ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GLOBALCASH] * ( activator:GetAttributeValue( "unlimited quantity" ) or 1 ) local golddigger = HasUpgrade( activator, UPGRADE_BOURGEOISIE_GOLDDIGGER ) if golddigger then if caller.Golden then amount = amount * 2 else amount = 0 goto nextKillLogic end end local sellout = HasUpgrade( activator, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( activator.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) amount = amount * decrease end local Ent_DataTable = { model = "models/items/currencypack_large.mdl", solid = 0 } local cash = ents.CreateWithKeys( "prop_dynamic", Ent_DataTable, true, true ) cash:SetAbsOrigin( caller:GetAbsOrigin() ) cash.Amount = amount cash.Reward = activator cash.DieTime = CurTime() + 11 cash.ThinkTimer = timer.Create( 0.1, function() if ( not IsValid( cash ) ) then return false end cash:SetAbsAngles( cash:GetAbsAngles() + Vector(0,6,0 ) ) for _, player in pairs( ents.FindInSphere( cash:GetAbsOrigin(), 64, "player" ) ) do if IsValid(player) and (player == cash.Reward) then cash.Reward:AddCurrency( cash.Amount ) cash:PlaySound( "MVM.MoneyPickup" ) pcall( timer.Stop, cash.ThinkTimer ) cash:Kill() end end local pi = math.pi local timedif = cash.DieTime - CurTime() local blink = tobool( math.floor( math.sin( ( 2*pi * ( timedif ) ) - pi ) + 1 ) ) if timedif <= 5 then if blink then cash:Disable() else cash:Enable() end end if ( timedif <= 0 ) then util.ParticleEffect( "mvm_cash_explosion", cash:GetAbsOrigin() ) cash:PlaySound( "MVM.MoneyVanish" ) pcall( timer.Stop, cash.ThinkTimer ) cash:Kill() return false end end, 0 ) end ::nextKillLogic:: if HasUpgrade( activator, UPGRADE_BOURGEOISIE_GOLDDIGGER ) then local sellout = HasUpgrade( activator, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( activator.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) end if caller.Golden then activator:AddCurrency( ENEMY_BASE_CASH * caller.CashMult * activator.CashMult * ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GLOBALCASH] * decrease ) else activator:RemoveCurrency( ENEMY_BASE_CASH * caller.CashMult * activator.CashMult * ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GLOBALCASH] * decrease ) end end if ( activator.m_hActiveWeapon == activator:GetPlayerItemBySlot(2) ) and ( activator:GetAttributeValueClean( "crit on hard hit", 0, LOADOUT_POSITION_MELEE ) > 0 ) then local radius = 90 local function ExplosiveBlast( area, owner, victim, recursive ) print( area ) util.ParticleEffect( "mvm_loot_explosion", victim:GetEyePos() ) for _, bot in pairs( ents.FindInSphere( victim:GetAbsOrigin(), area, "player" ) ) do if ( bot:GetTeam() ~= owner:GetTeam() ) and ( bot:IsAlive() ) and ( not bot:OnTeam( 1 ) ) then local damage = 65 * owner:GetAttributeValueByClass( "mult_dmg", 1 ) * owner:GetAttributeValueClean( "dmg penalty vs players", 1, LOADOUT_POSITION_BODY ) * owner:GetAttributeValueClean( "dmg penalty vs players", 1, LOADOUT_POSITION_MELEE ) local dmginfo = { Damage = damage * 0.5, Attacker = owner, DamageType = DMG_BLAST, DamageCustom = TF_DMG_CUSTOM_TAUNTATK_GRENADE, Weapon = owner:GetPlayerItemBySlot(2), Inflictor = owner:GetPlayerItemBySlot(2) } if ( ( damage * 0.5 ) >= bot.m_iHealth ) and recursive and ( victim:GetAbsOrigin():Distance( owner:GetAbsOrigin() ) <= ( area + 25 ) ) then timer.Simple( 0.1, function() ExplosiveBlast( area, owner, bot, false ) end) end bot:TakeDamage( dmginfo ) end end end ExplosiveBlast( radius, activator, caller, HasUpgrade( activator, UPGRADE_EPIC_WEAPON_RECURSIVECLUSTER ) ) end if IsValid( activator:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY) ) and ( activator:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):GetItemName() == "Texan Ten Gallon" ) then RepairAllBuildings( 10, activator, caller ) end if caller.MarkForHealth then activator:SetAttributeValue( "max health additive bonus", ( activator:GetAttributeValue( "max health additive bonus" ) or 0 ) + ( activator:GetPlayerItemBySlot(2):GetAttributeValue( "duckstreaks active" ) or 0 ) ) activator.HealthGained = activator.HealthGained + ( activator:GetPlayerItemBySlot(2):GetAttributeValue("duckstreaks active") or 2 ) end end function OnBashLogic( damage, activator, caller ) if IsValid( activator ) and IsValid( caller ) and ( activator:GetTeam() ~= caller:GetTeam() ) then if IsValid( activator:GetPlayerItemBySlot( 1 ) ) then local refill_attr = activator:GetAttributeValueClean( "sniper charge per sec", 0, LOADOUT_POSITION_BODY ) local refill_val = 100 * refill_attr timer.Simple( 0, function() activator.m_flChargeMeter = math.min( activator.m_flChargeMeter + refill_val, 100 ) end) local heal_val = activator:GetAttributeValueClean( "freaky fair healing potion", 0, LOADOUT_POSITION_BODY ) timer.Simple( 0, function() activator:AddHealth( heal_val, false ) end) local minicrit_val = activator:GetAttributeValueClean( "freaky fair buster potion", 0, LOADOUT_POSITION_BODY ) timer.Simple( 0, function() if minicrit_val > 0 then activator:AddCond( TF_COND_OFFENSEBUFF, minicrit_val, activator ) end end) local explosive_val = activator:GetAttributeValueClean( "freaky fair relentless potion", 0, LOADOUT_POSITION_BODY ) timer.Simple( 0, function() if explosive_val > 0 then local mimic = WeaponMimic({ TeamNum = activator.m_iTeamNum, WeaponType = 3, -- sticky Damage = explosive_val * 200, Crits = 0, SplashRadius = 300, SpeedMax = 0, SpeedMin = 0, ["$dmgtype"] = DMG_BLAST | DMG_ALWAYSGIB | DMG_HALF_FALLOFF, ["$killicon"] = "loose_cannon_impact", ["$preventshootparent"] = 1, origin = tostring(activator:GetAbsOrigin()), angles = tostring(Vector(0, 0, 0)), }) mimic["$AddWeaponAttribute"](mimic, "blast radius increased|2" ) mimic["$AddWeaponAttribute"](mimic, "blast dmg to self increased|0" ) mimic["$SetOwner"](mimic, activator) if mimic:IsValid() then mimic:FireOnce() mimic:DetonateStickies() timer.Simple(0.1, function() mimic:Remove() end) end end end) local defense_val = activator:GetAttributeValueClean( "freaky fair uber potion", 0, LOADOUT_POSITION_BODY ) timer.Simple( 0, function() if defense_val > 0 then activator:AddCond( TF_COND_DEFENSEBUFF, defense_val, activator ) end end) end end end function StartTurretMode( player ) player.TurretMode = true end function StopTurretMode( player ) player.TurretMode = false end function StartTankMode(player) player.TankMode = true end function StopTankMode(player) player.TankMode = false end function OnBuildingCreated(entity, classname) print( entity ) timer.Simple( 0.1, function() print( entity:IsValid(), (IsValid( entity:GetBuilder() ) ) ) if entity:IsValid() and (IsValid( entity:GetBuilder() ) ) then local ply = entity:GetBuilder() if IsValid( ply:GetPlayerItemBySlot(LOADOUT_POSITION_MELEE) ) then local melee = ply:GetPlayerItemBySlot( LOADOUT_POSITION_MELEE ) print( melee, ( ( melee:GetAttributeValue( "merasmus hat level display ONLY" ) ) ~= nil ), ( not tobool( entity.m_bDisposableBuilding ) ) ) if ( ( melee:GetAttributeValue( "merasmus hat level display ONLY" ) ) ~= nil ) and ( not tobool( entity.m_bDisposableBuilding ) ) then entity.m_iHighestUpgradeLevel = ( melee:GetAttributeValue( "merasmus hat level display ONLY" ) + 1 ) end if ( ( melee:GetAttributeValue( "building instant upgrade" ) ) ~= nil ) and ( not tobool( entity.m_bDisposableBuilding ) ) then entity.m_iUpgradeMetal = 200 * ( melee:GetAttributeValue( "building instant upgrade" ) ) end end end if IsValid( entity.m_hBuilder ) and ( ( classname == "obj_dispenser" ) or ( classname == "obj_teleporter" ) ) then local owner = entity.m_hBuilder if IsValid( owner:GetPlayerItemBySlot(5) ) then local pda = owner:GetPlayerItemBySlot(LOADOUT_POSITION_PDA) if ( pda:GetItemName() == "Mini PDA" ) and ( entity.m_bPlacing == 1 ) then entity.m_bMiniBuilding = 1 end if ( pda:GetItemName() == "Mini PDA" ) and ( entity.m_iState == 0 ) then entity.m_iMaxHealth = 100 * ( owner:GetPlayerItemBySlot(LOADOUT_POSITION_PDA):GetAttributeValue("engy building health bonus" ) or 1 ) if entity.m_iHealth > entity.m_iMaxHealth then entity:AcceptInput("SetHealth", entity.m_iMaxHealth ) end end end end if IsValid( entity.m_hBuilder ) and ( classname == "obj_teleporter" ) then local ply = entity:GetBuilder() entity.ArenaBuiltIn = ARENA if entity.m_iObjectMode == 0 then ply.Entrance = entity else ply.Exit = entity end if IsValid( ply.Entrance ) and IsValid( ply.Exit ) and ( ply.Entrance:GetClassname() == "obj_teleporter" ) and ( ply.Exit:GetClassname() == "obj_teleporter" ) then local Entrance = ply.Entrance local Exit = ply.Exit if Entrance.ArenaBuiltIn ~= Exit.ArenaBuiltIn then Entrance:RemoveHealth( 694201337 ) Exit:RemoveHealth( 694201337 ) end end end end) end function ChanceGoldenChance( decrease, upgrade ) local mult = 1 if decrease then mult = -1 end ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GOLDEN_CHANCE] = ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GOLDEN_CHANCE] + ( upgrade.Attributes[1].increment * mult ) end function ProptectorGrenade(projectile,activator,weapon) projectile.RemovedCallback = projectile:AddCallback( ON_REMOVE, function( ent ) local vectors = { Vector( 0, 192, 0 ), Vector( 0, -192, 0 ), Vector( 192, 0, 0 ), Vector( -192, 0, 0 ), } local angles = { Vector( 0, 90, 0 ), Vector( 0, 270, 0 ), Vector( 0, 0, 0 ), Vector( 0, 180, 0 ), } for index, vec in pairs( vectors ) do local Ent_data = { spawnflags = 1, skin = 1, teamnum = 3 } local shield = ents.CreateWithKeys( "entity_medigun_shield", Ent_data, true, true ) shield:SetAbsOrigin( ent:GetAbsOrigin() + vec ) shield:SetAbsAngles( angles[index] ) shield.m_nRenderMode = 1 shield.DieTime = CurTime() + 5 shield.ThinkTimer = timer.Create( 0.1, function() if ( not IsValid( shield ) ) then return false end local pi = math.pi local timedif = shield.DieTime - CurTime() local blink = tobool( math.floor( math.sin( ( 2*pi * ( timedif ) ) - pi ) + 1 ) ) if ( timedif <= 0 )then pcall( timer.Stop, shield.ThinkTimer ) shield:Kill() return false end if ( timedif <= 3 ) then if blink then shield:Alpha( 125 ) else shield:Alpha( 255 ) end end end, 0 ) end ent:RemoveCallback( ON_REMOVE, ent.RemovedCallback ) end) end function ExplosiveArrows(projectile, activator, weapon) projectile.Owner = activator projectile.CollidedCallback = projectile:AddCallback( ON_START_TOUCH, function( ent, other, hitPos, hitNormal ) local function Explode( self, owner ) util.ParticleEffect( "mvm_loot_explosion", self:GetAbsOrigin() ) self:PlaySound("Weapon_LooseCannon.Explode") self:PlaySound("Weapon_LooseCannon.Explode") self:PlaySound("Weapon_LooseCannon.Explode") for _, others in pairs(ents.FindInSphere(self:GetAbsOrigin(),150)) do if others:IsRealPlayer() and ( others:GetTeam() ~= owner:GetTeam() ) then local BlastDamage = ( ( ( PlayerDefaultHealth[others.m_iClass] * others:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) + others:GetAttributeValueByClass("add_maxhealth", 0) ) * 0.1 ) / owner:GetAttributeValueByClass( "mult_dmg_vs_players", 1 ) others:TakeDamage({Damage = BlastDamage, Attacker = owner, DamageType = DMG_BLAST, Weapon = owner:GetPlayerItemBySlot(0)}) elseif others:IsBot() and ( others:GetTeam() ~= owner:GetTeam() ) then -- local BlastDamage = 150 + ( ( ( others.m_iMaxHealth + others:GetAttributeValueByClass("add_maxhealth", 0) ) * others:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) * 0.05 ) local BlastDamage = 300 * owner:GetAttributeValueByClass( "mult_dmg", 1 ) * owner:GetAttributeValueClean( "dmg penalty vs players", 1, LOADOUT_POSITION_PRIMARY ) others:TakeDamage({Damage = BlastDamage, Attacker = owner, DamageType = DMG_BLAST}) end end end if other:IsWorld() then timer.Simple( 2, function() Explode( ent, ent.Owner ) end) elseif other:IsPlayer() and other:IsAlive() and ( not other:OnTeam( 1 ) ) and ( other:GetTeam() ~= ent.Owner:GetTeam() ) and ( other:GetClassname() ~= ent:GetClassname() ) and ( other ~= ent.Owner ) then print( other, ent, ent.Owner ) Explode( ent, ent.Owner ) end timer.Simple( 0, function() if ent.m_vecAbsVelocity:Length() <= 0 then ent:RemoveCallback( ON_START_TOUCH, ent.CollidedCallback ) end end) end) end function AddHype(damage, activator, caller) --activator is the one with the weapon, caller is the one that got hit if activator.HypeEnable then activator.Hype = math.min(activator.Hype + damage, activator.HypeMax) activator:ShowHudText({channel = 5, y = 0.90, x = 0.74, r1 = 250, r2 = 250, g1 = 250, g2 = 250, b1 = 250, b2 = 250, effect = 0, fixTime = 0, fadeinTime = 0, fadeoutTime = 0, holdTime = 1.25}, "Hype: " .. math.round( ( activator.Hype / activator.HypeMax ) * 100, 2 ) .. "%" ) end end function MolotovExplode(projectile, activator, weapon) projectile.Owner = activator projectile.RemovedCallback = projectile:AddCallback( ON_REMOVE, function( ent ) util.ParticleEffect( "ExplosionCore_MidAir_Flare", ent:GetAbsOrigin() ) local Delay = 0.5 local DieTime = CurTime() + 10 + Delay local pos = ent:GetAbsOrigin() local owner = ent.Owner local ownerTeam = owner:GetTeam() timer.Simple( Delay, function() util.ParticleEffect( "mvm_hatch_destroy_burn", pos + Vector(0, 0, 256) ) -- util.ParticleEffect( "mvm_hatch_destroy_smolderembers", pos + Vector(0, 0, 192) ) -- util.ParticleEffect( "mvm_hatch_destroy_smolder", ent:GetAbsOrigin() + Vector(0, 0, 128) ) timer.Create( 0.1, function() if CurTime() >= DieTime then return false end for _, player in pairs( ents.FindInSphere( pos, 96, "player" ) ) do if IsValid( player ) and ( player:GetTeam() ~= ownerTeam ) and ( player:IsRealPlayer() ) then local damage = ( ( ( PlayerDefaultHealth[player.m_iClass] * player:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) + player:GetAttributeValueByClass("add_maxhealth", 0) ) / 30 ) / activator:GetAttributeValueByClass( "mult_dmg_vs_players", 1 ) player:TakeDamage({Damage = damage, Attacker = activator, DamageType = DMG_BURN, DamageCustom = TF_DMG_CUSTOM_DRAGONS_FURY_BONUS_BURNING }) player:IgnitePlayerDuration( 10, activator ) end end end, 0 ) end) ent:RemoveCallback( ON_REMOVE, ent.RemovedCallback ) end) end function RecountPlayers( teamnum ) PlayerCount = 0 for i,v in pairs(PlayerTable) do PlayerTable[i] = nil end if teamnum == nil or ( not teamnum ) then for index, player in pairs(ents.GetAllPlayers()) do if ( player:IsRealPlayer() ) then PlayerTable[PlayerCount] = player PlayerCount = PlayerCount + 1 end end else for index, player in pairs(ents.GetAllPlayers()) do if ( player.m_iTeamNum ~= teamnum ) and ( player.m_iTeamNum ~= 1 ) and ( player:IsAlive() ) then PlayerTable[PlayerCount] = player PlayerCount = PlayerCount + 1 end end end end function OrbitalRocket(projectile,activator,weapon) RecountPlayers( activator.m_iTeamNum ) local RandomPlayer = PlayerTable[math.random(0,#PlayerTable)] OrbitalRocketMain(RandomPlayer,activator,projectile:GetNetIndex(), ( activator:InCond(34) ) or ( activator:InCond(11) ) ) end function OrbitalRocketMain(target,attacker,index,critboosted) if (target:IsValid()) and (attacker:IsValid()) then timer.Simple(3, function() local KeyValues = { model = "models/props_mvm/indicator/indicator_circle_long.mdl", skin = 1, origin = target:GetAbsOrigin(), disableshadows = 1, targetname = 'indicator ' .. tostring(index) } local KeyValues2 = { origin = target:GetAbsOrigin() + Vector(0, 0, 1100), angles = "90 0 0", Crits = 0, SpreadAngle = 0, targetname = 'shooter ' .. tostring(index), teamnum = attacker.m_iTeamNum } local mimic = ents.CreateWithKeys("tf_point_weapon_mimic", KeyValues2, true, true) mimic:SetOwner( attacker ) mimic["$weaponname"] = "Orbital Strike Rocket" mimic:AddWeaponAttribute( "dmg penalty vs players|" .. tostring( ( ( attacker.m_hActiveWeapon:GetAttributeValue( "throwable damage" ) ) or 1 ) ) ) if critboosted then mimic["Crits"] = 1 end local ring = ents.CreateWithKeys("prop_dynamic", KeyValues, true, true) ring:AcceptInput("SetAnimation","start") timer.Create(0.1, function() -- util.StartLagCompensation(target) local Trace = util.Trace({ start = target:GetAbsOrigin() + Vector(0, 0, 16), mask = MASK_PLAYERSOLID, angles = Vector(90, 0, 0), mins = Vector(-16, -16, 0), maxs = Vector(16, 16, 0), filter = ents.GetAllPlayers() }) -- util.FinishLagCompensation(target) ring:SetAbsOrigin(Trace.HitPos + Vector(0, 0, 8)) mimic:SetAbsOrigin(Trace.HitPos + Vector(0, 0, 1100)) end, 2.5 / 0.1) timer.Simple(2.7, function() ring:AcceptInput("Skin", 0) end) timer.Simple(3.7, function() mimic:AcceptInput("FireOnce") end) timer.Simple(4.7, function() ring:AcceptInput("SetAnimation","end") end) timer.Simple(5.2, function() ents.FindByName('indicator ' .. tostring(index)):AcceptInput("kill") mimic:AcceptInput("kill") end) end) end end function LockTargetAim( _, activator, caller ) if not IsValid( caller ) then return end if not IsValid( activator ) then return end if caller:GetTeam() == activator:GetTeam() then return end caller:AcceptInput("$BotCommand", "interrupt_action -lookpos " .. tostring( activator:GetAbsOrigin() ) .. " -duration 5 -alwayslook -waituntildone" ) local EMPFX = ents.CreateWithKeys("info_particle_system", { effect_name = "medic_resist_blast", start_active = 1, flag_as_weather = 0, }, true, true) EMPFX:SetAbsOrigin(caller:GetAbsOrigin()) EMPFX:Start() EMPFX:AcceptInput("SetParent", "!activator", caller, nil) timer.Simple( 5.5, function() EMPFX:Remove() end) end function RepairAllBuildings( percent, activator, caller ) --activator is the one with the weapon for _, building in pairs( ents.FindAllByClass("obj_dispenser") ) do if IsValid( building.m_hBuilder ) then local owner = building.m_hBuilder -- print( building, owner, activator ) if owner == activator then building:AcceptInput("AddHealth", ( building.m_iMaxHealth * ( percent / 100 ) ) ) end end end for _, building in pairs( ents.FindAllByClass("obj_teleporter") ) do if IsValid( building.m_hBuilder ) then local owner = building.m_hBuilder if owner == activator then building:AcceptInput("AddHealth", ( building.m_iMaxHealth * ( percent / 100 ) ) ) end end end for _, building in pairs( ents.FindAllByClass("obj_sentrygun") ) do if IsValid( building.m_hBuilder ) then local owner = building.m_hBuilder if owner == activator then building:AcceptInput("AddHealth", ( building.m_iMaxHealth * ( percent / 100 ) ) ) end end end end function Drone(state,Drone) local owner = Drone.m_hBuilder local Update local posmod = 1 local accel = 0.015 local decel = 0.2 local height = 16 local fakevel = Drone:GetAbsOrigin() local buildpos = Drone:GetAbsOrigin() local dis = 96 local failcounter = 0 local targetable = {} local tar = owner local alive_players = ents.GetAllAliveRealPlayers() local dis_offset = 0 local target_pos = Vector(0,0,0) local offset = Vector( math.random( -6, 6 ), math.random( -6, 6 ), 0 ) * 16 if state == DRONE_STATE_FOLLOW_ENEMY then tar = alive_players[ math.random(1, #alive_players ) ] end Drone.State = state Drone.MaxSpeed = 0.8 Drone.LockPos = Drone:GetAbsOrigin() Drone.ChangeTargetTimer = 0 Drone.StuckTime = 0 Drone:AddCallback(ON_REMOVE, function() pcall(timer.Stop, Update) end) Drone:AddCallback(ON_DEATH, function() pcall(timer.Stop, Update) end) Update = timer.Create(0.015, function() if ( not IsValid(tar) ) then tar = owner end if ( Drone.m_bCarried == 1 ) then pcall(timer.Stop, Update) end local filt = { owner } for _, ent in pairs( ents.FindAllByClass( "obj_sentrygun" ) ) do if ent.m_hBuilder == owner then table.insert( filt, ent ) end end for _, ply in pairs( ents.GetAllBots() ) do table.insert( filt, ply ) end if ( Drone.State == DRONE_STATE_FOLLOW_ENEMY ) and ( not tar:IsAlive() ) then alive_players = ents.GetAllAliveRealPlayers() tar = alive_players[ math.random(1, #alive_players ) ] end if ( Drone.State == DRONE_STATE_FOLLOW_ENEMY ) then target_pos = tar:GetEyePos() + offset if ( not Drone:HasLineOfSight( tar ) ) then Drone.State = DRONE_STATE_HOVER Drone.LockPos = Drone:GetAbsOrigin() Drone.ChangeTargetTimer = CurTime () + 15 end elseif ( Drone.State == DRONE_STATE_FOLLOW_OWNER ) then dis_offset = math.random( -120, -72 ) local add = Vector( 0,0,0 ) if Drone.PosOffset == "positive" then add = ( Vector( 128, 128, 0 ) * ( owner:GetEyeAngles():Rotate( Vector( 0, 90, 0 ) ):ToAngles():GetForward() ) ) + Vector( 0, 0, 48 ) + ( Vector( 128, 128, 0 ) * ( owner:GetEyeAngles():ToAngles():GetForward() ) ) elseif Drone.PosOffset == "negative" then add = ( Vector( -128, -128, 0 ) * ( owner:GetEyeAngles():Rotate( Vector( 0, 90, 0 ) ):ToAngles():GetForward() ) ) + Vector( 0, 0, 48 ) + ( Vector( 128, 128, 0 ) * ( owner:GetEyeAngles():ToAngles():GetForward() ) ) end target_pos = owner:GetAbsOrigin() + add elseif ( Drone.State == DRONE_STATE_HOVER ) then if ( Drone:HasLineOfSight( tar ) ) then Drone.State = DRONE_STATE_FOLLOW_ENEMY Drone.ChangeTargetTimer = CurTime () + 15 elseif CurTime() >= Drone.ChangeTargetTimer then alive_players = ents.GetAllAliveRealPlayers() tar = alive_players[ math.random(1, #alive_players ) ] Drone.ChangeTargetTimer = CurTime () + 1 end elseif ( Drone.State == DRONE_STATE_FOLLOW_WRANGLER ) then local Trace = util.Trace( { start = owner:GetEyePos(), distance = 8192, angles = owner:GetEyeAngles(), filter = filt } ) target_pos = Trace.HitPos + offset if Trace.Hit then tar = Trace.Entity end end local pos = Drone:GetAbsOrigin() local ang = Vector(0,Drone:GetAbsAngles().y,Drone:GetAbsAngles().z) local ownerpos = owner:GetAbsOrigin() local desiredheight = tar:GetAbsOrigin().z + 72 + height local currentheight = pos.z -- local filter = { Drone, owner } local colliding = ( not CustomHitbox( pos, Vector( 30, 30, 90 ), Vector( -30, -30, 0 ), filt ) ) if colliding then Drone.StuckTime = Drone.StuckTime + 1 else Drone.StuckTime = 0 end if Drone.StuckTime > ( 66 * 3 ) then Drone:SetAbsOrigin( owner:GetEyePos() ) return end Drone.MaxSpeed = 2 -- if ( owner:IsAlive() ) then if ( IsValid(Drone) ) then dis = 128 + dis_offset if (Drone.m_iState ~= 0 ) and ( Drone.State ~= DRONE_STATE_HOVER ) and ( Drone.State ~= DRONE_STATE_HOVER_IDLE ) then local Trace = util.Trace( { start = Drone:GetAbsOrigin(), distance = 16 * Drone.MaxSpeed, angles = ang, filter = filt, mins = Vector( -8, -8, -8 ), maxs = Vector( 8, 8, 8 ) } ) if Trace.Hit then Drone:SetAbsAngles(Drone:GetAbsOrigin():FaceVector( target_pos ) ) return end if pos:Distance(target_pos) >= dis + 72 + height then --move towards if posmod < 1 then posmod = 1 end fakevel = pos + ( Vector(posmod * 2.5,posmod * 2.5,0) * ang:GetForward()) posmod = math.min(posmod + ( posmod * accel), Drone.MaxSpeed) elseif ( pos:Distance(target_pos) <= 72 + 72 + height ) and ( posmod * 5 < 6 ) then --move away fakevel = pos - ( Vector(Drone.MaxSpeed,Drone.MaxSpeed,0) * ang:GetForward()) posmod = -1 * Drone.MaxSpeed / 2.5 else --slow down fakevel = pos + ( Vector(posmod * 2.5,posmod * 2.5,0) * ang:GetForward()) if posmod < 0 then posmod = math.min( posmod + ( decel / 2 ), 0 ) else posmod = math.max( posmod - ( posmod * decel ), 0 ) end end if ( currentheight <= desiredheight ) and ( currentheight - desiredheight < 8 ) then if ( pos:Distance(target_pos) >= dis + 72 + height ) or ( ( pos:Distance(target_pos) <= 72 + 72 + height ) ) then fakevel = Vector(fakevel.x,fakevel.y,currentheight+math.max(1.5,( Drone.MaxSpeed * 1.5 ))) else fakevel = Vector(fakevel.x,fakevel.y,currentheight+math.max(0.25,( Drone.MaxSpeed * 1.5 ) / 6)) end elseif currentheight >= desiredheight and ( currentheight - desiredheight > 8 ) then if ( pos:Distance(target_pos) >= dis + 72 + height ) or ( ( pos:Distance(target_pos) <= 72 + 72 + height ) ) then fakevel = Vector(fakevel.x,fakevel.y,currentheight-math.max(1.5,( Drone.MaxSpeed * 1.5 ))) else fakevel = Vector(fakevel.x,fakevel.y,currentheight-math.max(0.25,( Drone.MaxSpeed * 1.5 ) / 6)) end else fakevel = Vector(fakevel.x,fakevel.y,currentheight) end elseif ( Drone.State == DRONE_STATE_HOVER ) or ( Drone.State == DRONE_STATE_HOVER_IDLE ) then if ( currentheight <= ( Drone.LockPos.z + 72 + height ) ) and ( currentheight - ( Drone.LockPos.z + 72 + height ) < 8 ) then fakevel = Vector(fakevel.x,fakevel.y,currentheight+math.max(0.25,( Drone.MaxSpeed * 1.5 ) / 6)) end posmod = 1 else if ( currentheight <= ( Drone.LockPos.z + 72 + height ) ) and ( currentheight - ( Drone.LockPos.z + 72 + height ) < 8 ) then fakevel = Vector(fakevel.x,fakevel.y,currentheight+0.25) end posmod = 1 end Drone:SetAbsOrigin(fakevel) Drone:SetAbsAngles(Drone:GetAbsOrigin():FaceVector( target_pos ) ) else failcounter = failcounter + 1 print("Error, Invaild Drone", failcounter) if failcounter >= 66 then pcall(timer.Stop, Update) end end -- else -- pcall(timer.Stop, Update) -- Drone:AcceptInput("RemoveHealth", Drone.m_iHealth + 10) -- end end, 0) end function ResetPlayerData( player ) for type, data in pairs ( GLOBAL_PLAYER_DATA[player.m_szNetworkIDString][1] ) do for i, info in pairs ( GLOBAL_PLAYER_DATA[player.m_szNetworkIDString][1][type] ) do GLOBAL_PLAYER_DATA[player.m_szNetworkIDString][1][type][i].Value = GLOBAL_PLAYER_DATA[player.m_szNetworkIDString][1][type][i].DefaultValue or 1 end end end function GetPlayerData( player, type, index ) if not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ] then return 0 end return GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value end function ChangePlayerData( player, type, index, value, operation ) if ( not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ] ) or ( not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1] ) or ( not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type] ) or ( not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index] ) then return false end local Data = GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index] if operation == "add" then GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value = GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value + value elseif operation == "subtract" then GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value = GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value - value elseif operation == "multiply" then GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value = GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value * value elseif operation == "divide" then GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value = GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value / value elseif operation == "set" then GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][type][index].Value = value end end function ApplyPlayerDataAttributes( player, dev ) player = player or dev if ( not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ] ) or ( not GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1] ) then return false end local Data = GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1] local AddValue = 1 local SubtractValue = 1 local MultiplyValue = 1 local DivideValue = 1 local ExponentValue = 1 for i, info in pairs( Data[PLAYER_DATA_TYPE_DMGMULT] ) do if info.Operation == "add" then AddValue = AddValue + info.Value elseif info.Operation == "subtract" then SubtractValue = SubtractValue * info.Value elseif info.Operation == "multiply" then MultiplyValue = MultiplyValue * info.Value elseif info.Operation == "divide" then DivideValue = DivideValue * info.Value elseif info.Operation == "exponent" then ExponentValue = ExponentValue * info.Value end end SubtractValue = SubtractValue - 1 -- print(not GLOBAL_PLAYER_DATA[player.m_szNetworkIDString], player.m_szNetworkIDString, player.m_szNetname,GLOBAL_PLAYER_DATA[ player.m_szNetworkIDString ][1][PLAYER_DATA_TYPE_DMGMULT][PLAYER_DATA_DAMAGE_DMGMULT_ADDITIVE].Value) player:SetAttributeValue( "dmg penalty vs players", ( ( AddValue - SubtractValue ) * ( MultiplyValue / DivideValue ) ) ^ ExponentValue ) AddValue = 1 SubtractValue = 1 MultiplyValue = 1 DivideValue = 1 ExponentValue = 1 for i, info in pairs( Data[PLAYER_DATA_TYPE_FIRERATEMULT] ) do if info.Operation == "add" then AddValue = AddValue + info.Value elseif info.Operation == "subtract" then SubtractValue = SubtractValue * info.Value elseif info.Operation == "multiply" then MultiplyValue = MultiplyValue * info.Value elseif info.Operation == "divide" then DivideValue = DivideValue * info.Value elseif info.Operation == "exponent" then ExponentValue = ExponentValue * info.Value end end SubtractValue = SubtractValue - 1 -- print( AddValue, SubtractValue, MultiplyValue, DivideValue, ExponentValue ) player:SetAttributeValue( "melee attack rate bonus", ( ( AddValue - SubtractValue ) * ( MultiplyValue / DivideValue ) ) ^ ExponentValue ) AddValue = 1 SubtractValue = 1 MultiplyValue = 1 DivideValue = 1 ExponentValue = 1 for i, info in pairs( Data[PLAYER_DATA_TYPE_RELOADMULT] ) do if info.Operation == "add" then AddValue = AddValue + info.Value elseif info.Operation == "subtract" then SubtractValue = SubtractValue * info.Value elseif info.Operation == "multiply" then MultiplyValue = MultiplyValue * info.Value elseif info.Operation == "divide" then DivideValue = DivideValue * info.Value elseif info.Operation == "exponent" then ExponentValue = ExponentValue * info.Value end end SubtractValue = SubtractValue - 1 player:SetAttributeValue( "faster reload rate", ( ( AddValue - SubtractValue ) * ( MultiplyValue / DivideValue ) ) ^ ExponentValue ) AddValue = 1 SubtractValue = 1 MultiplyValue = 1 DivideValue = 1 ExponentValue = 1 for i, info in pairs( Data[PLAYER_DATA_TYPE_BASEDMG] ) do if info.Operation == "add" then AddValue = AddValue + info.Value elseif info.Operation == "subtract" then SubtractValue = SubtractValue * info.Value elseif info.Operation == "multiply" then MultiplyValue = MultiplyValue * info.Value elseif info.Operation == "divide" then DivideValue = DivideValue * info.Value elseif info.Operation == "exponent" then ExponentValue = ExponentValue * info.Value end end SubtractValue = SubtractValue - 1 if IsValid( player:GetPlayerItemBySlot( 0 ) ) then player:GetPlayerItemBySlot( 0 ):SetAttributeValue( "CARD: damage bonus", ( ( AddValue - SubtractValue ) * ( MultiplyValue / DivideValue ) ) ^ ExponentValue ) end if IsValid( player:GetPlayerItemBySlot( 1 ) ) then player:GetPlayerItemBySlot( 1 ):SetAttributeValue( "CARD: damage bonus", ( ( AddValue - SubtractValue ) * ( MultiplyValue / DivideValue ) ) ^ ExponentValue ) end AddValue = 1 SubtractValue = 1 MultiplyValue = 1 DivideValue = 1 ExponentValue = 1 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, pos ) local callbacks = {} local botSpawn = findFreeBot() if not botSpawn then print( "GLOBAL BOT LIMIT REACHED" ) return end local Possible_Spawns = GetTeamSpawns( TEAM_BLUE ) pos = pos or Possible_Spawns[ math.random( 1, #Possible_Spawns ) ] + 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:SetFakeSendProp( "m_iTeamNum", 3 ) botSpawn:RunScriptCode( "activator.ForceChangeTeam(3, true)", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ForceChangeTeam(3, true)", botSpawn, botSpawn ) botSpawn.m_iszClassIcon = "" botSpawn.CashMult = data.BaseCashMult botSpawn:SetCustomModelWithClassAnimations( ENEMY_MODEL_TABLE[ 0 ][botSpawn.m_iClass] ) if data.OverrideModel then botSpawn:SetCustomModelWithClassAnimations( data.OverrideModel ) end botSpawn.DeathFunction = data.DeathFunc botSpawn.MarkForHealth = false botSpawn.IsGiant = false botSpawn.IsMiniGiant = false botSpawn.Golden = false botSpawn.m_bGlowEnabled = 0 botSpawn.PacketDelayDamage = {} botSpawn.BigProblem = false botSpawn.StuckTicker = 0 -- runs 10/s botSpawn.StuckTickerResolve = ENEMY_STUCK_RESOLVE_TIME * 10 -- 60s botSpawn.HealthLastTick = 0 if data.Type == ENEMY_TYPE_GIANT then ENEMY_CURRENT_NUMBER_OF_GIANTS = ENEMY_CURRENT_NUMBER_OF_GIANTS + 1 botSpawn.IsGiant = true end if data.Type == ENEMY_TYPE_MINIGIANT then ENEMY_CURRENT_NUMBER_OF_MINIGIANTS = ENEMY_CURRENT_NUMBER_OF_MINIGIANTS + 1 botSpawn.IsMiniGiant = true end if( ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GOLDEN_CHANCE] /100 ) > math.random() then -- print( "GOLDEN" ) botSpawn.CashMult = botSpawn.CashMult * 5 botSpawn.Golden = true -- botSpawn:SetAttributeValue( "attach particle effect static", 3083 ) -- Golden Glimmer botSpawn:Color("242 188 100" ) else botSpawn:Color( "255 255 255" ) end callbacks.died = botSpawn:AddCallback(ON_DEATH, function() for name, _ in pairs(botSpawn:GetAllAttributeValues()) do botSpawn:SetAttributeValue(name, nil) end botSpawn:SetAttributeValue( "attach particle effect static", nil ) if botSpawn.IsGiant then ENEMY_CURRENT_NUMBER_OF_GIANTS = ENEMY_CURRENT_NUMBER_OF_GIANTS - 1 end if botSpawn.IsMiniGiant then ENEMY_CURRENT_NUMBER_OF_MINIGIANTS = ENEMY_CURRENT_NUMBER_OF_MINIGIANTS - 1 end if botSpawn.IsSummon then ENEMY_SUMMONED_COUNT = ENEMY_SUMMONED_COUNT - 1 end botSpawn.IsSummon = false timer.Simple( 1, function() botSpawn:RunScriptCode( "activator.ForceChangeTeam(1, true)", botSpawn, botSpawn ) end) print( data.DeathFunc ) removeCallbacks(botSpawn, callbacks) pcall( timer.Stop, botSpawn.logicLoop ) pcall( data.DeathFunc, botSpawn ) -- print( botSpawn.CashMult ) for _, player in pairs( ents.GetAllRealPlayers() ) do if ( player:GetAttributeValue( "unlimited quantity" ) or 0 ) <= 0 then local sellout = HasUpgrade( player, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( player.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) end player:AddCurrency( ENEMY_BASE_CASH * botSpawn.CashMult * player.CashMult * ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GLOBALCASH] * decrease ) end end end) callbacks.Damaged = botSpawn:AddCallback( ON_DAMAGE_RECEIVED_PRE, function( ent, dmginfo ) local attacker = dmginfo.Attacker if attacker and attacker:IsRealPlayer() and ent and ent:IsBot() and ( dmginfo.Damage > 0 ) then if attacker:GetAttributeValueClean("air dash count", 0, LOADOUT_POSITION_BODY ) > 0 and attacker:IsMidair() then dmginfo.CritType = math.max( dmginfo.CritType, 1 ) end end if attacker and attacker:IsRealPlayer() and ent and ent:IsBot() and ( dmginfo.Damage > 0 ) and ( dmginfo.DamageCustom ~= TF_DMG_CUSTOM_TRIGGER_HURT ) then if ( attacker:GetAttributeValue("unlimited quantity hidden") or 0 ) > 0 then if not ent.PacketDelayDamage[ attacker.m_szNetworkIDString ] then ent.PacketDelayDamage[ attacker.m_szNetworkIDString ] = { DamageTaken = 0, DamageDelay = 0, DamageAttacker = attacker } end local damage = dmginfo.Damage if IsDamageType( dmginfo.DamageType, DMG_CRITICAL ) then damage = damage * 3 * attacker:GetAttributeValueClean("mult crit dmg", 0, LOADOUT_POSITION_BODY ) end if HasUpgrade( attacker, UPGRADE_BOURGEOISIE_FIRSTSTRIKE ) then if ent.m_iHealth >= ( ent.MaxHealth * 0.75 ) then damage = damage * 2 end end if ent.PacketDelayDamage[ attacker.m_szNetworkIDString ].DamageTaken == 0 then ent.PacketDelayDamage[ attacker.m_szNetworkIDString ].DamageDelay = CurTime() + attacker:GetAttributeValue("unlimited quantity hidden") end ent.PacketDelayDamage[ attacker.m_szNetworkIDString ].DamageTaken = ent.PacketDelayDamage[ attacker.m_szNetworkIDString ].DamageTaken + damage dmginfo.Damage = 0.0001 end if HasUpgrade( attacker, UPGRADE_BOURGEOISIE_FIRSTSTRIKE ) then if ent.m_iHealth >= ( ent.MaxHealth * 0.75 ) then dmginfo.Damage = dmginfo.Damage * 2 end end end return true end) timer.Simple(0, function() -- if not ( botSpawn:IsAlive() and IsValid( botSpawn ) and botSpawn:OnTeam( 3 ) ) then -- return -- end botSpawn:RunScriptCode( "activator.ForceChangeTeam(3, true)", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ClearBehaviorFlag(1023)", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ClearAllWeaponRestrictions()", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ClearAllBotAttributes()", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ClearAllBotTags()", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ClearAttentionFocus()", botSpawn, botSpawn ) botSpawn:RunScriptCode( "activator.ForceChangeTeam(3, true)", botSpawn, botSpawn ) botSpawn:SetAbsOrigin( pos ) botSpawn:SetAbsOrigin( pos ) botSpawn:SwitchClassInPlace( data.Class ) botSpawn:SwitchClassInPlace( data.Class ) local health = data.Health if ( ( data.Type == ENEMY_TYPE_GIANT ) or ( data.Type == ENEMY_TYPE_BOSS ) ) and ( MISSION_MODIFY_STATS[MISSION_MODIFY_BIG_PROBLEM].enabled ) then health = health * 1.5 botSpawn.BigProblem_Resistance = 0.1 botSpawn.BigProblem_TimeStamp = CurTime() botSpawn.BigProblem_ResistanceDecayTime = 15 botSpawn.BigProblem = true timer.Simple( 2, function() botSpawn.BigProblem_TimeStamp = CurTime() end) end botSpawn:SetAttributeValue("hidden maxhealth non buffed", ( health - botSpawn.m_iHealth ) ) botSpawn:SetAttributeValue("SET BONUS: max health additive bonus", math.min( ( health * GetEnemyHealthMult() ) - health, SIGNED_INT_LIMIT - 2500 ) ) if ( health * GetEnemyHealthMult() ) - health >= SIGNED_INT_LIMIT - 2500 then botSpawn:SetAttributeValue( "mult_dmgtaken_active", 1 / ( ( ( health * GetEnemyHealthMult() ) - health ) / ( SIGNED_INT_LIMIT - 2500 ) ) ) end botSpawn:SetAttributeValue("rage giving scale", ( 1 / GetEnemyHealthMult() ) * 3 ) botSpawn.MaxHealth = health * GetEnemyHealthMult() botSpawn.HealthLastTick = health * GetEnemyHealthMult() -- print( GetEnemyDamageMult(), GetEnemyDamageMultRanged(), GetEnemyCount(), GetEnemySpeedMult() ) botSpawn:SetAttributeValue("dmg penalty vs players", GetEnemyDamageMult() ) -- botSpawn:SetAttributeValue("dmg bonus vs buildings", GetEnemyDamageMult() ) if ( data.DefaultSlot ~= LOADOUT_POSITION_MELEE ) and ( ( not data.UseMeleeScaling ) or ( data.UseMeleeScaling == false ) ) then -- print( "Using Ranged Scaling", data.Name ) botSpawn:SetAttributeValue("dmg penalty vs players", GetEnemyDamageMultRanged() ) -- botSpawn:SetAttributeValue("dmg bonus vs buildings", GetEnemyDamageMultRanged() ) end if ( data.NoDamageScaling ) then botSpawn:SetAttributeValue("dmg penalty vs players", nil ) end botSpawn:SetAttributeValue( "major move speed bonus", GetEnemySpeedMult() ) botSpawn:SetAttributeValue( "not solid to players", 1 ) botSpawn:SetAttributeValue( "damage force increase hidden", GetEnemyKBRes() ) botSpawn:AddCond( TF_COND_MVM_BOT_STUN_RADIOWAVE, 0.1 ) botSpawn:SetAttributeValue( "mult_player_movespeed_active", 0.0001337 ) botSpawn:SetAttributeValue( "no_jump", 1 ) botSpawn:SetAttributeValue( "no_attack", 1 ) botSpawn:AddCond( TF_COND_MEGAHEAL, 2 ) botSpawn:AddCond( TF_COND_SAPPED, 2 ) botSpawn:AddCond( TF_COND_INVULNERABLE_CARD_EFFECT, 2 ) timer.Simple( 1, function() botSpawn:AddCond( TF_COND_INVULNERABLE_WEARINGOFF, 1 ) end) timer.Simple( 2, function() botSpawn:SetAttributeValue( "mult_player_movespeed_active", nil ) botSpawn:SetAttributeValue( "no_attack", nil ) botSpawn:SetAttributeValue( "no_jump", nil ) botSpawn:SetAttributeValue( "not solid to players", nil ) end) for i, val in pairs( data.Attributes ) do -- print( "activator.AddBotAttribute(" .. tostring( val ) .. ")" ) botSpawn:RunScriptCode( "activator.AddBotAttribute(" .. tostring( val ) .. ")", botSpawn, botSpawn ) end 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 timer.Simple( 0.1, function() botSpawn:SetCustomModelWithClassAnimations( ENEMY_MODEL_TABLE[ 0 ][botSpawn.m_iClass] ) if data.OverrideModel then botSpawn:SetCustomModelWithClassAnimations( data.OverrideModel ) 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:GetPlayerItemBySlot(i):SetAttributeValue(name, value) end end end for cond, _ in pairs( data.AddConds ) do botSpawn:AddCond( cond ) end if data.Tags ~= nil then for i, name in pairs( data.Tags ) do botSpawn:RunScriptCode( "activator.AddBotTag(`" .. tostring( name ) .. "`)", botSpawn, botSpawn ) end end timer.Simple( 0, function() if ( data.Variants ) and ( data.Variants[1] ) then local variant_chance = VARIANT_CHANCE * ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] if MISSION_MODIFY_STATS[MISSION_MODIFY_GIANTVARIANTS].enabled and data.Type == ENEMY_TYPE_GIANT then variant_chance = 1 end if MISSION_MODIFY_STATS[MISSION_MODIFY_BOSSVARIANTS].enabled and data.Type == ENEMY_TYPE_BOSS then variant_chance = 1 elseif data.Type == ENEMY_TYPE_BOSS and ( not MISSION_MODIFY_STATS[MISSION_MODIFY_BOSSVARIANTS].enabled ) then variant_chance = 0 end if ( math.random() <= ( variant_chance ) ) and ( WAVE >= ENEMY_VARIANT_MIN_WAVE ) 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 end) if ( data.Name == "Stomper" ) then botSpawn:SetAttributeValue("kb fall damage", botSpawn:GetAttributeValueClean( "kb fall damage", 25, LOADOUT_POSITION_BODY ) * GetEnemyDamageMult() ) end --print( botSpawn.m_nBotSkill ) 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 ) -- print( botSpawn.m_bIsMiniBoss, botSpawn.m_iClass, ENEMY_MODEL_TABLE ) --botSpawn:RunScriptCode( "printl( activator.HasBotAttribute( 16 ) )", botSpawn, botSpawn ) botSpawn:BotCommand( "switch_action Mobber" ) botSpawn:BotCommand( "switch_action " .. tostring( data.Action ) ) end) 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() ) or ( not IsValid( botSpawn ) ) or ( not botSpawn:OnTeam( 3 ) ) then -- print( botSpawn, "stopping logic loop" ) -- pcall( timer.Stop, botSpawn.logicLoop ) return false end pcall( data.LogicFunc, botSpawn, data ) if not StillSpawning then botSpawn.StuckTickerResolve = ENEMY_STUCK_RESOLVE_TIME_NOSPAWNING * 10 end for i, data in pairs( botSpawn.PacketDelayDamage ) do if data.DamageDelay < CurTime() then local dmginfo = { Damage = data.DamageTaken, Attacker = data.DamageAttacker, DamageCustom = TF_DMG_CUSTOM_TRIGGER_HURT, Weapon = ( data.DamageAttacker.m_hActiveWeapon or nil ) } data.DamageTaken = 0 botSpawn:TakeDamage( dmginfo ) end end if botSpawn.BigProblem then local frac = math.min( ( CurTime() - botSpawn.BigProblem_TimeStamp ) / botSpawn.BigProblem_ResistanceDecayTime, 1 ) botSpawn.BigProblem_Resistance = lerp( 0.1, 1, frac ) botSpawn:SetAttributeValue( "dmg taken increased", botSpawn.BigProblem_Resistance ) if botSpawn.BigProblem_Resistance >= 1 then botSpawn.BigProblem = false end end if WAVE_TYPE_CURRENT ~= WAVE_TYPE_HORDE then if botSpawn.m_iHealth >= botSpawn.HealthLastTick then botSpawn.StuckTicker = botSpawn.StuckTicker + 1 else botSpawn.StuckTicker = 0 botSpawn.HealthLastTick = botSpawn.m_iHealth end if botSpawn.StuckTicker >= botSpawn.StuckTickerResolve then print( "attemping unstuck" ) botSpawn.StuckTicker = 0 botSpawn.HealthLastTick = botSpawn.m_iHealth local HasLOS = false for _, player in pairs( ents.GetAllAliveRealPlayers() ) do if botSpawn:HasLineOfSight( player ) then print( "los found; aborting" ) HasLOS = true break end end if not HasLOS then print( "unstuck teleport" ) botSpawn:SetAbsOrigin( Possible_Spawns[ math.random( 1, #Possible_Spawns ) ] + Vector( 0, 0, 16 ) ) end end end 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_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_VOTE_OPTIONS] = 0 ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GLOBALCASH] = 1 ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MULT] = 1 ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GOLDEN_CHANCE] = 0 ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MAX_GIANTS] = 1 ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, } -- for i, v in pairs( MISSION_MODIFY_STATS ) do -- MISSION_MODIFY_STATS[i].enabled = false -- end end function InitWave() local ObjectiveResource = ents.FindByClass('tf_objective_resource') timer.Simple( 3, function() if not ( string.match( ObjectiveResource.m_iszMvMPopfileName, "roguelike_stable.pop" ) or string.match( ObjectiveResource.m_iszMvMPopfileName, "roguelike_test_beta.pop" ) ) then IsOtherUpload = true end if IsOtherUpload then util.PrintToChatAll( "\x07fc206d **********" ) util.PrintToChatAll( "\x07fc206d This mission is NOT an official mission, I have no problem with this but I cannot guarantee that I will be able to fix any bugs you find here" ) util.PrintToChatAll( "\x07fc206d **********" ) end end) pcall( timer.Stop, SpawnTimer ) HardResetEncounterModifiers() WAVE = 0 TotalCount = 0 SpawnTimer = nil WaveStarted = false InWave = false SHOP_WAVE = false ARENA = 1 ents.FindByClass('tf_objective_resource').m_nMannVsMachineMaxWaveCount = WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ents.FindByClass('tf_objective_resource').m_nMannVsMachineWaveCount = 0 if math.random(1, 100) == 1 then ents.FindByName('le_silly'):Enable() else ents.FindByName('le_silly'):Disable() end GetArenaCount() for i, tbl in pairs( VOTE_CATEGORIES ) do tbl.SelectedType = nil tbl.IsXL = false end GLOBAL_PLAYER_UPGRADES = {} -- big 'ol table of player upgrades, should save upgrades between rejoins LEAVERS = {} -- anti retry cheese UPGRADES_CATES = {} UPGRADE_LOOT_CATEGORY = UPGRADE_CATEGORY_ALL if #WAVE_FORCEDFLEXES > 0 then for i, v in pairs( WAVE_FORCEDFLEXES ) do for _, k in pairs( MISSION_MODIFY_STATS ) do if k.PrintName == v then k.enabled = false end end end end WAVE_FORCEDFLEXES = {} VOTING = false VOTE_OPTIONS = 3 VOTE_RESULTS = {} VOTE_MENU = nil for _, player in pairs( ents.GetAllRealPlayers() ) do ResetPlayerInfo( player ) for i=0, 2 do if( IsValid(player:GetPlayerItemBySlot(i)) ) then player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on attack", "popscript^$OnAttackLogic^" ) player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on kill", "popscript^$OnKillLogic^" ) if player:GetPlayerItemBySlot(i):GetClassname() == "tf_wearable_demoshield" then player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on hit", "popscript^$OnBashLogic^" ) end player:HideMenu() end end end ClearCapturePoints() end function StartWave() if UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_END then VOTE_MENU = nil WaveStarted = true for i = 0, #VOTE_CATEGORIES do VOTE_RESULTS[i] = {} end InWave = true ents.FindByName( "end_game" ):FinishWave() return end if UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_LOOP then UPGRADE_LOOT_CATEGORY = UPGRADE_CATEGORY_ALL local AvailableFlexs = {} for i, v in pairs( MISSION_MODIFY_STATS ) do if v.enabled == false then table.insert( AvailableFlexs, MISSION_MODIFY_STATS[i] ) end end if #AvailableFlexs > 0 then local rnd = math.WeightedRandom( AvailableFlexs ) AvailableFlexs[rnd].enabled = true util.PrintToChatAll( "\x079219fcFlex Modifier Enabled \x01: " .. AvailableFlexs[rnd].PrintName .. ". " .. AvailableFlexs[rnd].Description ) table.insert( WAVE_FORCEDFLEXES, AvailableFlexs[rnd].PrintName ) end end WAVE_REVIVES = 0 ENEMY_SUMMONED_COUNT = 0 VOTE_MENU = nil WaveStarted = true WAVE = WAVE + 1 WAVE_DURATION = CurTime() + WAVE_HORDE_DURATION + 5 local loop = math.floor( ( WAVE - 1 ) / ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) ) + 1 ents.FindByClass('tf_objective_resource').m_nMannVsMachineWaveCount = WAVE ents.FindByClass('tf_objective_resource').m_nMannVsMachineMaxWaveCount = ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) * loop local WaveAboutToEnd = false local WaveEndNow = false local PauseBotSpawning = false local arenas_allowed = ARENA_MAX -- if (UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_ELITE_BOSS) or ( UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_EPIC ) then -- arenas_allowed = #ARENA_SPAWNS - 1 -- end InWave = true -- WAVE_TYPE_CURRENT = math.WeightedRandom( WAVE_TYPE_CHANCE_TABLE ) WAVE_TYPE_CURRENT = VOTE_CATEGORIES[ UPGRADE_LOOT_CATEGORY ].SelectedType or WAVE_TYPE_STANDARD WAVE_ISXL = VOTE_CATEGORIES[ UPGRADE_LOOT_CATEGORY ].IsXL SHOP_WAVE = tobool( math.floor( WAVE / ( ( WAVE_NUMOFWAVESPERLOOP - 1 ) + ( math.floor( ( WAVE + 0.5 ) / (WAVE_NUMOFWAVESPERLOOP) ) * (WAVE_NUMOFWAVESPERLOOP) ) ) ) ) TotalCount = math.floor( GetEnemyCount() ) ARENA = math.random( 1, arenas_allowed ) StartDelay = nil LastWholeNumber = nil ResetEncounterModifiers() ENEMY_CURRENT_NUMBER_OF_GIANTS = 0 ENEMY_CURRENT_NUMBER_OF_MINIGIANTS = 0 ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MAX_GIANTS] = math.ceil( WAVE/(WAVE_NUMOFWAVESPERLOOP) ) if WAVE_ISXL then TotalCount = TotalCount * WAVE_XL_ENEMY_MULT WAVE_DURATION = CurTime() + ( ( WAVE_HORDE_DURATION + 5 ) * WAVE_XL_ENEMY_MULT ) end if WAVE_TYPE_CURRENT == WAVE_TYPE_CONTROL then -- ARENA = 1 -- dev testing WAVE_DURATION = CurTime() + WAVE_CAPTUREPOINTS_DURATION if WAVE_ISXL then WAVE_DURATION = CurTime() + WAVE_CAPTUREPOINTS_DURATION * 2 end end if #MINIGIANTS_USED < 1 then local iteration = 1 for i, data in pairs( ENEMY_TEMPLATES ) do if data.Type == ENEMY_TYPE_MINIGIANT then MINIGIANTS_USED[iteration] = false iteration = iteration + 1 end end end if #GIANTS_USED < 1 then local iteration = 1 for i, data in pairs( ENEMY_TEMPLATES ) do if data.Type == ENEMY_TYPE_GIANT then GIANTS_USED[iteration] = false iteration = iteration + 1 end end end local function AllEnemiesUsed( type ) local was_last_true = false if type == ENEMY_TYPE_MINIGIANT then for i, v in pairs( MINIGIANTS_USED ) do if i == 1 then if v then was_last_true = true else return false end else if not ( v and was_last_true ) then return false end end end elseif type == ENEMY_TYPE_GIANT then for i, v in pairs( GIANTS_USED ) do if i == 1 then if v then was_last_true = true else return false end else if not ( v and was_last_true ) then return false end end end end return true end local function ResetEnemiesUsed( type ) if type == ENEMY_TYPE_MINIGIANT then local iteration = 1 for i, data in pairs( ENEMY_TEMPLATES ) do if data.Type == ENEMY_TYPE_MINIGIANT then MINIGIANTS_USED[iteration] = false iteration = iteration + 1 end end elseif type == ENEMY_TYPE_GIANT then local iteration = 1 for i, data in pairs( ENEMY_TEMPLATES ) do if data.Type == ENEMY_TYPE_GIANT then GIANTS_USED[iteration] = false iteration = iteration + 1 end end end end local CuratedEnemyTable = {} local MiniGiantTable = {} local GiantTable = {} local SelectedEnemyTypes = {} local TotalMinigiants = 0 local TotalGiants = 0 local MaxMinigiants = math.max( ENEMY_DEFAULT_MAX_MINIGIANTS + math.floor( ( TotalCount - ENEMY_NEW_MINIGIANT_EVERY_X ) / 15 ), 1 ) local MaxGiants = math.max( ENEMY_DEFAULT_MAX_GIANTS + math.floor( ( TotalCount - ENEMY_NEW_GIANT_EVERY_X ) / 15 ), 1 ) local MaxActiveMinigiants = MaxMinigiants * 2 local MaxActiveGiants = ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MAX_GIANTS] local MaxActiveMult = 3 if WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE then MaxActiveMult = 2 end MaxActiveMinigiants = MaxMinigiants * MaxActiveMult MaxActiveGiants = ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MAX_GIANTS] * MaxActiveMult local EnemySpawnQueue = { MiniGiantsQueued = 0, GiantsQueued = 0 } -- print( AllEnemiesUsed( ENEMY_TYPE_MINIGIANT ) ) for i, data in pairs( ENEMY_TEMPLATES ) do if ( data.Type == ENEMY_TYPE_COMMON ) or ( data.Type == ENEMY_TYPE_BOSS ) then CuratedEnemyTable[i] = data end if data.Type == ENEMY_TYPE_MINIGIANT then -- print( i ) TotalMinigiants = TotalMinigiants + 1 data.TemplateName = i MiniGiantTable[TotalMinigiants] = data end if data.Type == ENEMY_TYPE_GIANT then TotalGiants = TotalGiants + 1 data.TemplateName = i GiantTable[TotalGiants] = data end end MaxMinigiants = math.min( MaxMinigiants, TotalMinigiants ) MaxGiants = math.min( MaxGiants, TotalGiants ) -- print( MaxMinigiants, MaxGiants, GetEnemyCount() ) for i=1, MaxMinigiants do if AllEnemiesUsed( ENEMY_TYPE_MINIGIANT ) then print( "Reseting Used Minigiants" ) ResetEnemiesUsed(ENEMY_TYPE_MINIGIANT) end local rnd = math.random( 1, TotalMinigiants ) local loops = 0 while ( CuratedEnemyTable[MiniGiantTable[rnd].TemplateName] ~= nil ) or ( MINIGIANTS_USED[rnd] ) do rnd = math.random( 1, TotalMinigiants ) if loops > 500 then print("vreaking") break end loops = loops + 1 end if CuratedEnemyTable[MiniGiantTable[rnd].TemplateName] == nil then MINIGIANTS_USED[rnd] = true CuratedEnemyTable[MiniGiantTable[rnd].TemplateName] = MiniGiantTable[rnd] end end for i=1, MaxGiants do if AllEnemiesUsed( ENEMY_TYPE_GIANT ) then ResetEnemiesUsed(ENEMY_TYPE_GIANT) end local rnd = math.random( 1, TotalGiants ) local loops = 0 while ( CuratedEnemyTable[GiantTable[rnd].TemplateName] ~= nil ) or ( GIANTS_USED[rnd] ) do rnd = math.random( 1, TotalGiants ) if loops > 500 then print("vreaking") break end loops = loops + 1 end if CuratedEnemyTable[GiantTable[rnd].TemplateName] == nil then GIANTS_USED[rnd] = true CuratedEnemyTable[GiantTable[rnd].TemplateName] = GiantTable[rnd] end end if MISSION_MODIFY_STATS[MISSION_MODIFY_VARIANT_INCREASED].enabled then CuratedEnemyTable = ENEMY_TEMPLATES end for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) or ( player.m_iClass <= 0 ) then goto continue end LEAVERS[tostring(player.m_szNetworkIDString)] = false player:ForceRespawnDead() player:RefillAmmo() player:AddHealth( SIGNED_INT_LIMIT, false ) player:PlaySoundToSelf( "Vote.Passed" ) for i=0, 2 do if( IsValid(player:GetPlayerItemBySlot(i)) ) then player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on attack", "popscript^$OnAttackLogic^" ) player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on kill", "popscript^$OnKillLogic^" ) if player:GetPlayerItemBySlot(i):GetClassname() == "tf_wearable_demoshield" then player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on hit", "popscript^$OnBashLogic^" ) end end end if ARENA == 3 then player:SetFogController( "fog_controller_hell" ) elseif ARENA == 4 then player:SetFogController( "fog_controller_bridge" ) else player:SetFogController( "fog_controller" ) end if ( player.Voted == false ) and ( WAVE > 1 ) then player.VotedForLoot = GetVoteWinner() end player.Voted = false if not player.InQueue then player.InShop = false player.InMenu = false player.InRoll = false player.Rerolls = 0 player.Menu = nil player:HideMenu() end ::continue:: end for i = 0, #VOTE_CATEGORIES do VOTE_RESULTS[i] = {} end local Modif = math.floor( WAVE/(WAVE_NUMOFWAVESPERLOOP) ) * (WAVE_NUMOFWAVESPERLOOP) if UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_EPIC then -- ENEMY_TYPE_CHANCE_MULT = { -- [ENEMY_TYPE_COMMON] = 0.9, -- [ENEMY_TYPE_MINIGIANT] = 1.5, -- [ENEMY_TYPE_GIANT] = 1.15, -- [ENEMY_TYPE_BOSS] = 0, -- } -- MaxActiveGiants = MaxActiveGiants + 1 ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 0, [ENEMY_TYPE_MINIGIANT] = 0, [ENEMY_TYPE_GIANT] = 0, [ENEMY_TYPE_BOSS] = 1, } TotalCount = math.max( math.floor( (WAVE+7)/(WAVE_NUMOFWAVESPERLOOP) ), 1 ) if MISSION_MODIFY_STATS[MISSION_MODIFY_EXTRABOSS].enabled then TotalCount = math.ceil( TotalCount*1.5 ) end if WAVE_ISXL then TotalCount = TotalCount * WAVE_XL_ENEMY_MULT end WAVE_TYPE_CURRENT = WAVE_TYPE_STANDARD 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 = math.max( math.floor( WAVE/(WAVE_NUMOFWAVESPERLOOP) ), 1 ) if MISSION_MODIFY_STATS[MISSION_MODIFY_EXTRABOSS].enabled then TotalCount = math.ceil( TotalCount*1.5 ) end if WAVE_ISXL then TotalCount = TotalCount * WAVE_XL_ENEMY_MULT end WAVE_TYPE_CURRENT = WAVE_TYPE_STANDARD elseif UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_GREED then ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] = 5 ENEMY_TYPE_CHANCE_MULT = { [ENEMY_TYPE_COMMON] = 1, [ENEMY_TYPE_MINIGIANT] = 1, [ENEMY_TYPE_GIANT] = 1, [ENEMY_TYPE_BOSS] = 0, } 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.8, [ENEMY_TYPE_GIANT] = 0.2, [ENEMY_TYPE_BOSS] = 0, } end if MISSION_MODIFY_STATS[MISSION_MODIFY_VARIANT_INCREASED].enabled then ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] = ENCOUNTER_MODIFY_STATS[MODIFY_VARIANT_CHANCE] * 3 end -- if ARENA == 4 then -- ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MAX_GIANTS] = ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MAX_GIANTS] - 1 -- ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_GIANT] = ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_GIANT] * 0.75 -- end if MISSION_MODIFY_STATS[MISSION_MODIFY_EXTRAGIANT].enabled then MaxActiveGiants = math.ceil(MaxActiveGiants * 1.5 ) 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 if WAVE_ISXL and ( ENEMY_TYPE_CHANCE_SCALE_MULT[ WAVE - Modif ][i] > 0 ) then ENEMY_TYPE_CHANCE_MULT[i] = ENEMY_TYPE_CHANCE_MULT[i] * 3 else ENEMY_TYPE_CHANCE_MULT[i] = ENEMY_TYPE_CHANCE_MULT[i] * ENEMY_TYPE_CHANCE_SCALE_MULT[ WAVE - Modif ][i] end end -- print( WAVE - Modif, "After Chance", ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_COMMON], ENEMY_TYPE_CHANCE_MULT[ENEMY_TYPE_MINIGIANT] ) timer.Simple( 2, function() for _, player in pairs( ents.GetAllRealPlayers() ) do if player.m_iClass == TF_CLASS_ENGINEER then if IsValid( player.Entrance ) and IsValid( player.Exit ) and ( player.Entrance:GetClassname() == "obj_teleporter" ) and ( player.Exit:GetClassname() == "obj_teleporter" ) then local Entrance = player.Entrance local Exit = player.Exit if Entrance.ArenaBuiltIn ~= Exit.ArenaBuiltIn then Entrance:RemoveHealth( 694201337 ) Exit:RemoveHealth( 694201337 ) end end end end if WAVE_TYPE_CURRENT == WAVE_TYPE_CONTROL then AddCapturePoint() end end) if MISSION_MODIFY_STATS[MISSION_MODIFY_TIMELIME].enabled then local extra_time = 1 + math.max( WAVE_TIMELIMIT_START - ( WAVE_TIMELIMIT_START * ( math.floor( WAVE / ( WAVE_NUMOFWAVESPERLOOP * WAVE_LOOPSTILEND ) ) * WAVE_TIMELIMIT_DECAY ) ) + WAVE_TIMELIMIT_MIN, WAVE_TIMELIMIT_MIN ) local max_delay = 2 local count = math.floor( GetEnemyCount() ) if WAVE_ISXL then count = count * WAVE_XL_ENEMY_MULT end if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then max_delay = 1.34 end if ( UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_EPIC ) or ( UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_ELITE_BOSS ) then extra_time = extra_time * 2 end local max_time = count / ( math.clamp(math.floor( count / 12 ),1, 8) / max_delay ) WAVE_TIMELIMIT_TIME = CurTime() + max_time * extra_time WAVE_TIMELIMIT_MAX = WAVE_TIMELIMIT_TIME - CurTime() if WAVE_TYPE_CURRENT == WAVE_TYPE_CONTROL then local duration = WAVE_CAPTUREPOINTS_DURATION if WAVE_ISXL then duration = WAVE_CAPTUREPOINTS_DURATION * 2 end WAVE_TIMELIMIT_TIME = CurTime() + duration WAVE_TIMELIMIT_MAX = WAVE_TIMELIMIT_TIME - CurTime() end -- print( extra_time, max_delay, count, max_time, WAVE_TIMELIMIT_TIME - CurTime() ) end if SHOP_WAVE == false then local Possible_Spawns = GetTeamSpawns( TEAM_RED ) for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end if not player.InQueue then player.Menu = nil player:HideMenu() player.InShop = false end player.IncomeThisWave = 0 player.Revives = 0 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 ) ] + 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 ) if( player:GetAttributeValue("kill eater user 1") or 0 ) > 0 then player:AddCond( TF_COND_RUNE_HASTE, 6 ) end player.UsedMoleTunnel = false if HasUpgrade( player, UPGRADE_BOURGEOISIE_RING_SPOOK ) then player.SpookDelayEnd = CurTime() + 15 timer.Simple( 14.5, function() if player:IsAlive() then player:PlaySound( "Halloween.HeadlessBossBoo" ) player:PlaySound( "Halloween.HeadlessBossBoo" ) timer.Simple( 0.5, function() util.ParticleEffect( "bomibomicon_ring", player:GetAbsOrigin() ) for _, others in pairs( ents.FindInSphere( player:GetAbsOrigin(), 384, "player" ) ) do if others:IsPlayer() and ( others:GetTeam() ~= player:GetTeam() ) then others:StunPlayer( 10, 0.8, TF_STUNFLAGS_GHOSTSCARE, player ) end end end) end end) end end) ::continue:: end local BaseEnemies = TotalCount local SpawnedCount = 0 local SpawnDelay = 0 StillSpawning = true TotalCount = math.floor( TotalCount * ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_MULT] ) -- print( math.WeightedRandomEnemy( ENEMY_TEMPLATES ), GetEnemyCount(), GetEnemyHealthMult() ) -- Do normal spawn stuff -- If attempted to spawn a giant or minigiant over the cap, then add it to queue and don't increase spawned enemies -- Don't do normal spawning if all non-queued enemies have spawn and queue isn't empty -- If spaawning an enemy normally, and queue isn't empty, spawn from queue. Giant > minigiant -- If normal spawning ended, then spawn from queue when able to. Giant > minigiant local WaitingForQueue = false local NumberInQueue = 0 timer.Simple( 2, function() SpawnTimer = timer.Create( 0.1, function() if WAVE_TYPE_CURRENT == WAVE_TYPE_STANDARD then local max_enemies = ENEMY_BASE_MAXACTIVE_COUNT if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then max_enemies = ENEMY_BASE_MAXACTIVE_COUNT * 1.5 end -- max_enemies = max_enemies - NumberInQueue if CurTime() >= SpawnDelay and ( #ents.GetAllAliveBots() < max_enemies ) then SpawnDelay = CurTime() + math.randomfloat( 1, 2 ) if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then SpawnDelay = CurTime() + math.randomfloat( 0.67, 1.34 ) end for i=1, math.random( math.clamp(math.floor( TotalCount / 12 ),1, 8), math.clamp(math.floor( TotalCount / 6 ), 1, 10) ) do if ( #ents.GetAllAliveBots() >= max_enemies ) then break end if SpawnedCount >= TotalCount then StillSpawning = false break end SpawnedCount = SpawnedCount + 1 local template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] local loop = 1 if ( template.Type == ENEMY_TYPE_GIANT ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS >= MaxActiveGiants ) then NumberInQueue = NumberInQueue + 1 EnemySpawnQueue[ "GiantsQueued" ] = EnemySpawnQueue[ "GiantsQueued" ] + 1 elseif ( template.Type == ENEMY_TYPE_MINIGIANT ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS >= MaxActiveMinigiants ) then print( "adding minigiant to queue" ) NumberInQueue = NumberInQueue + 1 EnemySpawnQueue[ "MiniGiantsQueued" ] = EnemySpawnQueue[ "MiniGiantsQueued" ] + 1 end while ( ( template.Type == ENEMY_TYPE_GIANT ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS >= MaxActiveGiants ) ) or ( ( template.Type == ENEMY_TYPE_MINIGIANT ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS >= MaxActiveMinigiants ) ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end if NumberInQueue > 0 then if ( EnemySpawnQueue[ "GiantsQueued" ] > 0 ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS < MaxActiveGiants ) then while ( template.Type ~= ENEMY_TYPE_GIANT ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end NumberInQueue = NumberInQueue - 1 EnemySpawnQueue[ "GiantsQueued" ] = EnemySpawnQueue[ "GiantsQueued" ] - 1 elseif ( EnemySpawnQueue[ "MiniGiantsQueued" ] > 0 ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS < MaxActiveMinigiants ) then print( "pulling minigiant from queue" ) while ( template.Type ~= ENEMY_TYPE_MINIGIANT ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end NumberInQueue = NumberInQueue - 1 EnemySpawnQueue[ "MiniGiantsQueued" ] = EnemySpawnQueue[ "MiniGiantsQueued" ] - 1 end end local bot = SpawnEnemy( template ) bot.CashMult = bot.CashMult / ( TotalCount / BaseEnemies ) end end -- print( StillSpawning, TotalCount, SpawnedCount, SpawnedCount >= TotalCount, #ents.GetAllAliveBots() ) if StillSpawning == false then local bots_alive = 0 if ( WaveEndNow == false ) and ( WaveAboutToEnd == false ) then for _, bot in pairs( ents.GetAllAliveBots() ) do if IsValid( bot ) and ( not bot:OnTeam( 1 ) ) and ( not bot.CountTowardsEnemyCount) then bots_alive = bots_alive + 1 end end if ( bots_alive <= ( max_enemies / 5 ) ) then for _, bot in pairs( ents.GetAllAliveBots() ) do bot.m_bGlowEnabled = 1 end end if ( bots_alive <= 0 ) then WaveEndNow = true end end if WaveEndNow then WaveAboutToEnd = true WaveEndNow = false timer.Simple( 1, function() bots_alive = 0 for _, bot in pairs( ents.GetAllAliveBots() ) do if IsValid( bot ) and ( not bot:OnTeam( 1 ) ) and ( not bot.CountTowardsEnemyCount) then bots_alive = bots_alive + 1 end end print( "Delay Check:", bots_alive, WaveEndNow ) if ( bots_alive <= 0 ) then pcall( timer.Stop, SpawnTimer ) EndWave( true ) return else WaveAboutToEnd = false end end) end end elseif WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE then local max_enemies = ENEMY_BASE_MAXACTIVE_COUNT if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then max_enemies = ENEMY_BASE_MAXACTIVE_COUNT * 1.5 end if CurTime() >= SpawnDelay and ( #ents.GetAllAliveBots() < max_enemies ) then SpawnDelay = CurTime() + 1 if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then SpawnDelay = CurTime() + 0.67 end for i=1, math.random( math.clamp(math.floor( TotalCount / 12 ),1, 8), math.clamp(math.floor( TotalCount / 5 ), 1, 8) ) do if ( #ents.GetAllAliveBots() >= max_enemies ) then break end SpawnedCount = SpawnedCount + 1 local template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] local loop = 1 if ( template.Type == ENEMY_TYPE_GIANT ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS >= MaxActiveGiants ) then NumberInQueue = NumberInQueue + 1 EnemySpawnQueue[ "GiantsQueued" ] = EnemySpawnQueue[ "GiantsQueued" ] + 1 elseif ( template.Type == ENEMY_TYPE_MINIGIANT ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS >= MaxActiveMinigiants ) then NumberInQueue = NumberInQueue + 1 EnemySpawnQueue[ "MiniGiantsQueued" ] = EnemySpawnQueue[ "MiniGiantsQueued" ] + 1 end while ( ( template.Type == ENEMY_TYPE_GIANT ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS >= MaxActiveGiants ) ) or ( ( template.Type == ENEMY_TYPE_MINIGIANT ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS >= MaxActiveMinigiants ) ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end if NumberInQueue > 0 then if ( EnemySpawnQueue[ "GiantsQueued" ] > 0 ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS < MaxActiveGiants ) then while ( template.Type ~= ENEMY_TYPE_GIANT ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end NumberInQueue = NumberInQueue - 1 EnemySpawnQueue[ "GiantsQueued" ] = EnemySpawnQueue[ "GiantsQueued" ] - 1 elseif ( EnemySpawnQueue[ "MiniGiantsQueued" ] > 0 ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS < MaxActiveMinigiants ) then while ( template.Type ~= ENEMY_TYPE_MINIGIANT ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end NumberInQueue = NumberInQueue - 1 EnemySpawnQueue[ "MiniGiantsQueued" ] = EnemySpawnQueue[ "MiniGiantsQueued" ] - 1 end end local bot = SpawnEnemy( template ) if BaseEnemies <= SpawnedCount then bot.CashMult = 0.5 end end end if CurTime() > WAVE_DURATION then pcall( timer.Stop, SpawnTimer ) -- timer.Simple( 1, function() EndWave( true ) -- end) return end elseif WAVE_TYPE_CURRENT == WAVE_TYPE_CONTROL then local max_enemies = ENEMY_BASE_MAXACTIVE_COUNT * 0.75 if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then max_enemies = ENEMY_BASE_MAXACTIVE_COUNT * 0.75 * 1.5 end TotalCount = math.min( TotalCount, max_enemies ) if CurTime() >= SpawnDelay and ( #ents.GetAllAliveBots() < max_enemies ) and ( not PauseBotSpawning ) then SpawnDelay = CurTime() + 1 if MISSION_MODIFY_STATS[MISSION_MODIFY_ENEMY_SPAWNRATE].enabled then SpawnDelay = CurTime() + 0.67 end for i=1, math.random( math.clamp(math.floor( TotalCount / 12 ),1, 8), math.clamp(math.floor( TotalCount / 5 ), 1, 8) ) do if ( #ents.GetAllAliveBots() >= max_enemies ) then break end SpawnedCount = SpawnedCount + 1 local template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] local loop = 1 if ( template.Type == ENEMY_TYPE_GIANT ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS >= MaxActiveGiants ) then NumberInQueue = NumberInQueue + 1 EnemySpawnQueue[ "GiantsQueued" ] = EnemySpawnQueue[ "GiantsQueued" ] + 1 elseif ( template.Type == ENEMY_TYPE_MINIGIANT ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS >= MaxActiveMinigiants ) then NumberInQueue = NumberInQueue + 1 EnemySpawnQueue[ "MiniGiantsQueued" ] = EnemySpawnQueue[ "MiniGiantsQueued" ] + 1 end while ( ( template.Type == ENEMY_TYPE_GIANT ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS >= MaxActiveGiants ) ) or ( ( template.Type == ENEMY_TYPE_MINIGIANT ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS >= MaxActiveMinigiants ) ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end if NumberInQueue > 0 then if ( EnemySpawnQueue[ "GiantsQueued" ] > 0 ) and ( ENEMY_CURRENT_NUMBER_OF_GIANTS < MaxActiveGiants ) then while ( template.Type ~= ENEMY_TYPE_GIANT ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end NumberInQueue = NumberInQueue - 1 EnemySpawnQueue[ "GiantsQueued" ] = EnemySpawnQueue[ "GiantsQueued" ] - 1 elseif ( EnemySpawnQueue[ "MiniGiantsQueued" ] > 0 ) and ( ENEMY_CURRENT_NUMBER_OF_MINIGIANTS < MaxActiveMinigiants ) then while ( template.Type ~= ENEMY_TYPE_MINIGIANT ) do template = ENEMY_TEMPLATES[math.WeightedRandomEnemy( CuratedEnemyTable )] if loop > 250 then print("breaking") break end loop = loop + 1 end NumberInQueue = NumberInQueue - 1 EnemySpawnQueue[ "MiniGiantsQueued" ] = EnemySpawnQueue[ "MiniGiantsQueued" ] - 1 end end local bot = SpawnEnemy( template ) if BaseEnemies <= SpawnedCount then bot.CashMult = 0.5 end if TotalCount <= SpawnedCount then PauseBotSpawning = true break end end end local bots_alive = 0 for _, bot in pairs( ents.GetAllAliveBots() ) do if IsValid( bot ) and ( not bot:OnTeam( 1 ) ) and ( not bot.CountTowardsEnemyCount) then bots_alive = bots_alive + 1 end end print( bots_alive, math.ceil( TotalCount / 5 ), PauseBotSpawning, SpawnDelay - CurTime() ) if PauseBotSpawning and ( bots_alive <= ( math.ceil( TotalCount / 5 ) ) ) then PauseBotSpawning = false SpawnedCount = 0 SpawnDelay = CurTime() + WAVE_CAPTUREPOINTS_WAVE_BREAKDURATION end if CurTime() > WAVE_DURATION then pcall( timer.Stop, SpawnTimer ) print( "times up " ) -- timer.Simple( 1, function() EndWave( false ) -- end) return end end end, 0) end) else for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end UPGRADE_LOOT_CATEGORY = UPGRADE_CATEGORY_ALL player:HideMenu() player.InShop = true player.Haggled = false player.IncomeThisWave = 0 player:AcceptInput( "RunScriptCode", "ScreenFade( !activator, 255, 255, 255, 255, 1, 0.5, 2 )", player, player ) player:AddCond( 87, 2 ) timer.Simple( 1, function() local pos = ents.FindByName( "Default_Spawn" ):GetAbsOrigin() player:SetAbsOrigin( pos ) 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 ) player.Upgradesbought = 0 player.ShopUpgradesbought = 0 BuildUpgradeMenu( player, player, UPGRADE_LOOT_SHOP, 1 ) end) ::continue:: end end end function EndWave( give_loot ) if give_loot == nil then give_loot = true end pcall( timer.Stop, SpawnTimer ) SpawnTimer = nil TotalCount = 0 InWave = false print( give_loot ) if give_loot then table.insert( UPGRADES_CATES, UPGRADE_LOOT_CATEGORY ) end for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end if not GLOBAL_PLAYER_DATA[player.m_szNetworkIDString] then SetupPlayerData( player ) end GLOBAL_PLAYER_DATA[player.m_szNetworkIDString]["LastWavePlayed"] = WAVE player:ForceRespawnDead() if not player.InQueue then player.Menu = nil player:HideMenu() player.InShop = false end player:PlaySoundToSelf( "Vote.Created" ) 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 -- elseif UPGRADE_LOOT_CATEGORY == UPGRADE_CATEGORY_BOURGEOISIE then -- loot = UPGRADE_LOOT_BOURGEOISIE -- end local cate = UPGRADE_LOOT_CATEGORY if HasUpgrade( player, UPGRADE_BOURGEOISIE_RING_SAPPHIRE ) then cate = player.VotedForLoot end loot = VOTE_CATEGORIES[cate].LootTable timer.Simple( 0.1, function() player.Upgradesbought = 0 player.VotedForLoot = 8 print( "loot table", loot ) if give_loot then if player.InQueue then table.insert( player.UpgradeQueue, cate ) if WAVE_ISXL then for i=1, WAVE_XL_LOOT_MULT - 1 do table.insert( player.UpgradeQueue, cate ) end end else if WAVE_ISXL then for i=1, WAVE_XL_LOOT_MULT - 1 do table.insert( player.UpgradeQueue, cate ) end end BuildUpgradeMenu( player, player, loot, 1 ) end else print( "Wave Lost; No loot given" ) player:Print( PRINT_TARGET_CENTER, "Wave Lost; No loot given" ) util.PrintToChat( player, "Wave Lost; No loot given" ) end end) if player.TankMode then player:SetAttributeValue( "mult max health", ( player:GetAttributeValue( "mult max health" ) or 0 ) + ( player:GetAttributeValue("sapper health bonus") or 0.05 ) ) player.HealthMultGained = player.HealthMultGained + ( player:GetAttributeValue("sapper health bonus") or 0.05 ) end if ( player:GetAttributeValue("sapper health penalty") or 0 ) > 0 then player:SetAttributeValue( "max health additive bonus", ( player:GetAttributeValue( "max health additive bonus" ) or 0 ) + ( player:GetAttributeValue("sapper health penalty") or 5 ) ) player.HealthGained = player.HealthGained + ( player:GetAttributeValue("sapper health penalty") or 5 ) end if ( player:GetAttributeValueClean( "strange restriction value 1", 0, LOADOUT_POSITION_BODY ) > 0 ) then -- player:SetAttributeValue( "dmg penalty vs players",player:GetAttributeValueClean( "dmg penalty vs players", 1, LOADOUT_POSITION_BODY ) + player:GetAttributeValueClean( "strange restriction value 1", 0.05, LOADOUT_POSITION_BODY ) ) player.DamageMultGained = player.DamageMultGained + player:GetAttributeValueClean( "strange restriction value 1", 0.05, LOADOUT_POSITION_BODY ) end if ( player:GetAttributeValueClean( "strange restriction type 2", 1, LOADOUT_POSITION_BODY ) < 1 ) then -- player:SetAttributeValue( "fire rate penalty", player:GetAttributeValueClean( "fire rate penalty", 1, LOADOUT_POSITION_BODY ) * player:GetAttributeValueClean( "strange restriction type 2", 0.95, LOADOUT_POSITION_BODY ) ) player.FireRateMultGained = player.FireRateMultGained * player:GetAttributeValueClean( "strange restriction type 2", 0.97, LOADOUT_POSITION_BODY ) end if( ( player:GetAttributeValue("currency bonus") or 0 ) > 0 ) then player.MaxIncome = ( player:GetAttributeValue("currency bonus") or 0 ) local sellout = HasUpgrade( player, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( player.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) end -- if ( player.MaxIncome >= player.IncomeThisWave ) then -- player:AddCurrency( ( player.MaxIncome - player.IncomeThisWave ) * decrease ) -- end player:AddCurrency( ( GetTypeCost( UPGRADE_TYPE_COMMON ) * player.MaxIncome ) * decrease ) end ::continue:: end for i, data in pairs( GLOBAL_PLAYER_DATA ) do if not ents.FindBySteamID( i ) then if (not GLOBAL_PLAYER_DATA[i] ) or ( ( WAVE - GLOBAL_PLAYER_DATA[i]["LastWavePlayed"] ) > PLAYER_WIPE_GRACE ) then print( i, "wiping player data" ) GLOBAL_PLAYER_DATA[i] = nil GLOBAL_PLAYER_UPGRADES[i] = nil end end end ClearCapturePoints() end function WaveThink() timer.Simple( 0.1, function() WaveThink() end) if not StartDelay then StartDelay = 0 end if not LastWholeNumber then LastWholeNumber = 0 end --print( ( not InWave ), ( WAVE > 0 ) ) if InWave then if #ents.GetAllAliveRealPlayers() <= 0 then InWave = false end if WAVE_TYPE_CURRENT == WAVE_TYPE_CONTROL then TrackPoints() end end if ( ( not InWave ) and ( WAVE > 0 ) ) or ( SHOP_WAVE == true ) then for _, bot in pairs( ents.GetAllAliveBots() ) do bot.CashMult = 0 bot:Suicide() end for _, building in pairs( ents.FindAllByClass( "obj_sentrygun" ) ) do if building.m_iTeamNum ~= TEAM_RED then building:RemoveHealth( building.m_iHealth + 10 ) end end for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end -- print( player.InMenu, player.InRoll ) if ( not player.InMenu ) and ( not player.InRoll ) then BuildVoteMenu( player ) end if StartDelay and ( StartDelay > CurTime() ) and ( LastWholeNumber ~= math.ceil( StartDelay - CurTime() ) ) then -- player:Print( PRINT_TARGET_HINT, "Next Wave Starts In: " .. tostring( math.ceil( StartDelay - CurTime() ) ) ) player:ShowHudText({channel = 2, x = -1, y = 0.15, effect = 2, r1 = 100, r2 = 50, g1 = 250, g2 = 250, b1 = 150, b2 = 100, fadeinTime = 0.01, fadeoutTime = 0.5, holdTime = 1, fxTime = 0.01}, "Next Wave Starts In: " .. tostring( math.ceil( StartDelay - CurTime() ) ) ) if SHOP_WAVE then if LastWholeNumber == 60 then player:PlaySoundToSelf( "Announcer.RoundBegins60Seconds") elseif LastWholeNumber == 30 then player:PlaySoundToSelf( "Announcer.RoundBegins30Seconds") elseif LastWholeNumber == 11 then player:PlaySoundToSelf( "Announcer.RoundBegins10Seconds") elseif LastWholeNumber == 6 then player:PlaySoundToSelf( "Announcer.RoundBegins5Seconds") elseif LastWholeNumber == 5 then player:PlaySoundToSelf( "Announcer.RoundBegins4Seconds") elseif LastWholeNumber == 4 then player:PlaySoundToSelf( "Announcer.RoundBegins3Seconds") elseif LastWholeNumber == 3 then player:PlaySoundToSelf( "Announcer.RoundBegins2Seconds") elseif LastWholeNumber == 2 then player:PlaySoundToSelf( "Announcer.RoundBegins1Seconds") end end end ::continue:: end local Delay = WAVE_AUTO_START_TIMER if SHOP_WAVE then Delay = WAVE_AUTO_START_TIMER_SHOP end local Voted = GetAllVotes() local Dif = Delay - WAVE_AUTO_START_MIN_JUMP local Players = #ents.GetAllRealPlayers() local MinTime = CurTime() + ( Delay - ( ( Voted - 1 ) * ( Dif / math.max( Players - 1, 1 ) ) ) ) -- local MinTime = CurTime() + lerp( Delay, WAVE_AUTO_START_MIN_JUMP, math.max( ( Voted - 1 ) / ( Players - 1 ), 0 ) ) -- print( lerp( Delay, WAVE_AUTO_START_MIN_JUMP, math.max( ( Voted - 1 ) / ( Players - 1 ), 0 ) ) ) if StartDelay and ( StartDelay > CurTime() ) and ( LastWholeNumber ~= math.ceil( StartDelay - CurTime() ) ) then LastWholeNumber = math.ceil( StartDelay - CurTime() ) end if GetAllVotes() >= #ents.GetAllRealPlayers() then if ( StartDelay == 0 ) or ( StartDelay > ( CurTime() + WAVE_AUTO_START_MIN_JUMP + 0.05 ) ) then StartDelay = CurTime() + WAVE_AUTO_START_MIN_JUMP end end if GetAllVotes() >= 1 then if ( StartDelay == 0 ) then StartDelay = CurTime() + Delay else if StartDelay > ( MinTime + 0.05 ) then StartDelay = MinTime end end -- for _, player in pairs( ents.GetAllRealPlayers() ) do -- print( player.VotedForLoot, VOTE_CATEGORIES[player.VotedForLoot], VOTE_CATEGORIES[player.VotedForLoot].CorrespondingCate ) -- end if CurTime() >= StartDelay then UPGRADE_LOOT_CATEGORY = GetVoteWinner() for _, player in pairs( ents.GetAllRealPlayers() ) do if IsValid( player ) and player:OnTeam( 2 ) then player.ShopLoot = VOTE_CATEGORIES[player.VotedForLoot].CorrespondingCate end end StartWave() end end end if ( InWave and ( WAVE > 0 ) ) and ( SHOP_WAVE == false ) then if #ents.GetAllAliveRealPlayers() <= 1 then for _, player in pairs( ents.GetAllAliveRealPlayers() ) do if HasUpgrade( player, UPGRADE_BOURGEOISIE_RING_RUBY ) then player:AddCond( TF_COND_OFFENSEBUFF, 0.15 ) player:AddCond( TF_COND_SPEED_BOOST, 0.15 ) end end end for _, building in pairs( ents.FindAllByClass( "obj_teleporter" ) ) do if building.ArenaBuiltIn and ( building.ArenaBuiltIn ~= ARENA ) then building.m_flRechargeTime = CurTime() + 1 building.m_flCurrentRechargeDuration = CurTime() - 10 building.m_iState = 6 end end for _, player in pairs( ents.GetAllRealPlayers() ) do local DISGUISE_AUTO_DROP = DISGUISE_AUTO_DROP_DELAY if WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE then DISGUISE_AUTO_DROP = DISGUISE_AUTO_DROP_DELAY_HORDE end if (IsValid(player:GetPlayerItemBySlot(2))) and ( player:GetPlayerItemBySlot(2):GetAttributeValue("disguise on backstab", true) or 0 ) > 0 then DISGUISE_AUTO_DROP = math.ceil(DISGUISE_AUTO_DROP * 0.75) end if player.SpookDelayEnd > CurTime() then player:ShowHudText({channel = 3, x = -1, y = 0.1, effect = 2, r1 = 25, r2 = 25, g1 = 125, g2 = 125, b1 = 250, b2 = 250, fadeinTime = 0, fadeoutTime = 0.3, holdTime = 0.3, fxTime = 0}, "Time Until Spook: " .. tostring(math.ceil( player.SpookDelayEnd - CurTime() ) ) .. " Seconds" ) end if player.m_iClass == TF_CLASS_SPY then if player:InCond(TF_COND_DISGUISED) then if player.DisguisedLast == 0 then player.DisguisedLast = CurTime() end else player.DisguisedLast = 0 end end if ( ( CurTime() - player.DisguisedLast ) >= DISGUISE_AUTO_DROP ) and ( player:InCond( TF_COND_DISGUISED ) ) then player:RemoveCond( TF_COND_DISGUISED ) player:RemoveCond( TF_COND_DISGUISING ) end if ( player.m_iClass == TF_CLASS_SPY ) and ( player.DisguisedLast ~= 0 ) then player:ShowHudText({channel =43, x = -1, y = 0.2, effect = 2, r1 = 250, r2 = 250, g1 = 125, g2 = 125, b1 = 25, b2 = 25, fadeinTime = 0, fadeoutTime = 0.3, holdTime = 0.3, fxTime = 0}, "Disguise Will Drop In: " .. tostring(DISGUISE_AUTO_DROP - math.ceil( CurTime() - player.DisguisedLast ) ) .. " Seconds" ) end end end local HasFlexes = false local FlexesEnabled = 0 local FullFlex = false for i, v in pairs (MISSION_MODIFY_STATS ) do if v.enabled then HasFlexes = true; FlexesEnabled = FlexesEnabled + 1; end end if FlexesEnabled >= #MISSION_MODIFY_STATS then FullFlex = true end for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) or ( not player:IsAlive() ) then goto continue end if MISSION_MODIFY_STATS[MISSION_MODIFY_NORESPAWN].enabled then player:SetAttributeValue( "min respawn time", 666666 ) else player:SetAttributeValue( "min respawn time", 30 ) end if( player.m_iClass == TF_CLASS_MEDIC ) then player:SetAttributeValue( "revive rate", 1/math.max( WAVE_REVIVES^WAVE_REVIVE_PENALTY, 1 ) ) end if( player.m_iClass == TF_CLASS_HEAVYWEAPONS ) and (IsValid(player:GetPlayerItemBySlot(0))) then if ( player:GetAttributeValueClean( "bullets per shot bonus", 1, LOADOUT_POSITION_BODY ) > 1 ) and ( not WeaponAllowed( player:GetPlayerItemBySlot(0), WEAPONS_LIST_BULLETCOUNT ) ) then player:GetPlayerItemBySlot(0):SetAttributeValue( "bullets per shot bonus", ( 1 / player:GetAttributeValueClean( "bullets per shot bonus", 1, LOADOUT_POSITION_BODY ) ) ) else player:GetPlayerItemBySlot(0):SetAttributeValue( "bullets per shot bonus", nil ) end local weapon = player:GetPlayerItemBySlot(0) if weapon and ( weapon:GetClassname() == "tf_weapon_minigun" ) and ( weapon.ShouldNextFireTimeStamp ) then if weapon.ShouldNextFireTimeStamp < CurTime() then weapon.ShouldNextFireTimeStamp = 0 weapon.TapFireCount = 0 end end end if( player.TurretMode and player:IsAlive() ) then player:SetAttributeValue( "weapon spread bonus", player:GetAttributeValue("throwable fire speed") ) player:SetAttributeValue( "move accuracy mult", player:GetAttributeValue("throwable fire speed") * 2 ) if ( player.m_hActiveWeapon:GetAttributeValue("spread penalty") or 1 ) > 1.1 then player:SetAttributeValue( "fire rate bonus HIDDEN", player:GetAttributeValue("throwable fire speed") * 2 ) else player:SetAttributeValue( "fire rate bonus HIDDEN", player:GetAttributeValue("throwable fire speed") ) end end if HasUpgrade( player, UPGRADE_BOURGEOISIE_MOLE ) then if tobool(player.m_bUsingActionSlot) then if not player.UsedMoleTunnel then local Possible_Spawns = GetTeamSpawns( TEAM_BLUE ) local pos = Possible_Spawns[ math.random( 1, #Possible_Spawns ) ] + Vector( 0, 0, 16 ) local particle = ents.CreateWithKeys( "info_particle_system", { ["effect_name"] = "dxhr_sniper_rail_red", ["start_active"] = 1, ["origin"] = player:GetEyePos()[1] .. " " .. player:GetEyePos()[2] .. " " .. (player:GetEyePos()[3]), ["cpoint1"] = player }, true, true) PlaySound("Building_Teleporter.Send", player:GetAbsOrigin()) PlaySound("Building_Teleporter.Send", player:GetAbsOrigin()) util.ParticleEffect( "teleported_red", player:GetAbsOrigin() ) util.ParticleEffect( "teleportedin_red", pos ) local angles = pos:FaceVector(player:GetAbsOrigin()) player:Teleport( pos, angles, Vector( 0, 0, 0 ) ) PlaySound("Building_Teleporter.Receive", player:GetAbsOrigin()) PlaySound("Building_Teleporter.Receive", player:GetAbsOrigin()) player:AcceptInput( "RunScriptCode", "ScreenFade( !activator, 250, 150, 150, 100, 1, 0, 1 )", player, player ) particle.m_hControlPointEnts[1] = player timer.Simple( 0.1, function() --feedback particle:Remove() end) player.UsedMoleTunnel = true end end end if ( player:GetAttributeValue("freaky fair rtd potion") or 0 ) > 0 then local max_movespeed = PlayerDefaultMovespeed[player.m_iClass] * player:GetAttributeValueByClass( "mult_player_movespeed", 1 ) * player:GetAttributeValueByClass( "major_mult_player_movespeed", 1 ) local speed = Vector( player:GetPlayerVelocity().x, player:GetPlayerVelocity().y, 0 ):Length() local speed_over = math.max( speed - max_movespeed, 0 ) ChangePlayerData( player, PLAYER_DATA_TYPE_DMGMULT, PLAYER_DATA_DAMAGE_DMGMULT_DYNAMO, ( speed_over * player:GetAttributeValue("freaky fair rtd potion") ) / 100, "set" ) end if ( player:GetAttributeValue("Halloween Spellbook Page: Tumidum") or 0 ) > 0 then ChangePlayerData( player, PLAYER_DATA_TYPE_DMGMULT, PLAYER_DATA_DAMAGE_DMGMULT_PRISMATIC, 1 + ( FlexesEnabled * player:GetAttributeValue("Halloween Spellbook Page: Tumidum") ), "set" ) end if GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_BULLETS_MAGSHOT ) > 0 then local bullets = math.clamp( ( player.m_hActiveWeapon.m_iClip1 or 0 ), 0, GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_BULLETS_MAGSHOT ) ) if ( WeaponAllowed( player.m_hActiveWeapon, WEAPONS_LIST_ENERGYWEAPON ) ) then bullets = bullets / 5 -- dumbass valve moment end if ( WeaponAllowed( player.m_hActiveWeapon, WEAPONS_LIST_RESERVEAMMO_INSTEADOF_CLIP ) ) then local slot = TF_AMMO_PRIMARY if player.m_hActiveWeapon == player:GetPlayerItemBySlot( 1 ) then slot = TF_AMMO_SECONDARY end bullets = math.clamp( ( player.m_iAmmo[slot] or 0 ), 0, GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_BULLETS_MAGSHOT ) ) end ChangePlayerData( player, PLAYER_DATA_TYPE_BASEDMG, PLAYER_DATA_DAMAGE_BASEDMG_MAGSHOT, ( bullets * 0.01 ), "set" ) end if GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_BULLETS_WINDUP ) > 0 then local bullets = math.clamp( ( player.m_hActiveWeapon.m_iConsecutiveShots or 0 ), 0, GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_BULLETS_WINDUP ) ) ChangePlayerData( player, PLAYER_DATA_TYPE_BASEDMG, PLAYER_DATA_DAMAGE_BASEDMG_WINDUP, ( bullets * 0.04 ), "set" ) end if ( player:GetAttributeValue("panic_attack_negative") or 0 ) > 0 then local max_spread = 2 * player:GetAttributeValue("panic_attack_negative") local max_health = ( PlayerDefaultHealth[player.m_iClass] * player:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) + player:GetAttributeValueByClass("add_maxhealth", 0) local spread = max_spread * ( 1 - ( player.m_iHealth / max_health ) ) player:SetAttributeValue("projectile spread angle penalty", spread ) end if ( player:GetAttributeValue("kill eater user 2") or 0 ) > 0 then if player.AdderanlineTime >= CurTime() then ChangePlayerData( player, PLAYER_DATA_TYPE_FIRERATEMULT, PLAYER_DATA_SPEED_FIRERATEMULT_ADRENALINE, 1.5, "set" ) ChangePlayerData( player, PLAYER_DATA_TYPE_RELOADMULT, PLAYER_DATA_SPEED_RELOADRATE_ADRENALINE, 1.5, "set" ) else ChangePlayerData( player, PLAYER_DATA_TYPE_FIRERATEMULT, PLAYER_DATA_SPEED_FIRERATEMULT_ADRENALINE, 1, "set" ) ChangePlayerData( player, PLAYER_DATA_TYPE_RELOADMULT, PLAYER_DATA_SPEED_RELOADRATE_ADRENALINE, 1, "set" ) end end if ( IsValid( player:GetPlayerItemBySlot( 2 ) ) ) and ( player:GetPlayerItemBySlot( 2 ):GetItemName() == "Conniver's Kunai" ) then local max_health = ( PlayerDefaultHealth[player.m_iClass] * player:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) + player:GetAttributeValueByClass("add_maxhealth", 0) local max_overheal = max_health * 1.5 player.m_iHealth = math.min( player.m_iHealth, max_overheal ) end if( player:InCond( 113 ) ) and ( player:GetAttributeValueClean( "add cond when active", 0, LOADOUT_POSITION_BODY ) == 113 ) then for _, others in pairs( ents.FindInSphere( player:GetAbsOrigin(), 350, "player" ) ) do if IsValid( others ) and ( others:GetTeam() == player:GetTeam() ) and ( others ~= player ) then others:AddCond( 113, 0.15 ) end end end if( player:InCond( TF_COND_SHIELD_CHARGE ) ) and ( player:GetAttributeValueClean( "zoom speed mod disabled", 0, LOADOUT_POSITION_BODY ) > 0 ) then for _, others in pairs( ents.FindInSphere( player:GetAbsOrigin(), 350, "player" ) ) do if IsValid( others ) and ( others:GetTeam() == player:GetTeam() ) and ( others ~= player ) then others:AddCond( TF_COND_RUNE_AGILITY, 5 ) end end end if( player:InCond( TF_COND_SHIELD_CHARGE ) ) and ( player:GetAttributeValueClean( "freaky fair ghost potion", 0, LOADOUT_POSITION_BODY ) > 0 ) then player:AddCond( TF_COND_OBSCURED_SMOKE, 0.15 ) end if( player:InCond( TF_COND_SHIELD_CHARGE ) ) and ( player:GetAttributeValueClean( "sniper full charge damage bonus", 1, LOADOUT_POSITION_BODY ) > 1 ) then ChangePlayerData( player, PLAYER_DATA_TYPE_DMGMULT, PLAYER_DATA_DAMAGE_DMGMULT_DEMOSHIELD, player:GetAttributeValueClean( "sniper full charge damage bonus", 1, LOADOUT_POSITION_BODY ), "set" ) else ChangePlayerData( player, PLAYER_DATA_TYPE_DMGMULT, PLAYER_DATA_DAMAGE_DMGMULT_DEMOSHIELD, 0, "set" ) end if( player:InCond( TF_COND_SHIELD_CHARGE ) ) and ( player:GetAttributeValueClean( "pyro year number", 1, LOADOUT_POSITION_BODY ) > 1 ) then ChangePlayerData( player, PLAYER_DATA_TYPE_FIRERATEMULT, PLAYER_DATA_SPEED_FIRERATEMULT_DEMOSHIELD, player:GetAttributeValueClean( "pyro year number", 1, LOADOUT_POSITION_BODY ), "set" ) else ChangePlayerData( player, PLAYER_DATA_TYPE_FIRERATEMULT, PLAYER_DATA_SPEED_FIRERATEMULT_DEMOSHIELD, 1, "set" ) end if( player.m_iClass == TF_CLASS_SPY ) then if( IsValid( player:GetPlayerItemBySlot( LOADOUT_POSITION_PDA2 ) ) ) and ( player:GetAttributeValueClean("move speed bonus resource level", 1, LOADOUT_POSITION_PDA2 ) > 1 ) then local val = player:GetAttributeValueClean("move speed bonus resource level", 1, LOADOUT_POSITION_PDA2 ) local str = "major move speed bonus|" .. tostring( val ) player:SetAttributeValue( "effect add attributes", str ) end if ( IsValid( player:GetPlayerItemBySlot( LOADOUT_POSITION_PDA ) ) ) and ( player:GetAttributeValueClean("disguise no burn", 0, LOADOUT_POSITION_PDA ) > 0 ) and ( player:InCond( TF_COND_DISGUISED ) ) then player:RemoveCond( TF_COND_BURNING ) end end if( player.m_iClass == TF_CLASS_PYRO ) then if ( IsValid( player:GetPlayerItemBySlot( LOADOUT_POSITION_PRIMARY ) ) ) and ( string.lower(player:GetPlayerItemBySlot( LOADOUT_POSITION_PRIMARY ):GetClassname()) == "tf_weapon_flamethrower" ) then player:SetAttributeValue( "flame ammopersec decreased", player:GetAttributeValueClean( "melee attack rate bonus", 1, LOADOUT_POSITION_BODY ) ) end end if ( player:GetAttributeValueClean("stun on hit slow", 0, LOADOUT_POSITION_PRIMARY ) > 0 ) or ( player:GetAttributeValueClean("stun on hit slow", 0, LOADOUT_POSITION_SECONDARY ) > 0 ) then local val = player:GetAttributeValueClean("stun on hit slow", 1, LOADOUT_POSITION_PRIMARY ) if player:GetAttributeValueClean("stun on hit slow", 1, LOADOUT_POSITION_SECONDARY ) > val then val = player:GetAttributeValueClean("stun on hit slow", 1, LOADOUT_POSITION_SECONDARY ) end val = val * (2/3) val = val + 1 local str = "fire rate penalty HIDDEN|" .. tostring( val ) .. "|1.5" player:SetAttributeValue( "add attributes on hit", str ) end if( WeaponAllowed( player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY), WEAPONS_LIST_RESERVEAMMO_INSTEADOF_CLIP ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( "maxammo primary reduced", 1 * ( player:GetAttributeValueClean( "clip size bonus", 1, LOADOUT_POSITION_PRIMARY ) ) * ( player:GetAttributeValueClean( "clip size penalty", 1, LOADOUT_POSITION_PRIMARY ) ) * ( player:GetAttributeValueClean( "clip size bonus", 1, LOADOUT_POSITION_BODY ) ) * ( player:GetAttributeValueClean( "clip size penalty", 1, LOADOUT_POSITION_BODY ) ) ) end if( WeaponAllowed( player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY), WEAPONS_LIST_ENERGYWEAPON ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( "clip size bonus upgrade", 1 * ( player:GetAttributeValueClean( "clip size bonus", 1, LOADOUT_POSITION_PRIMARY ) ) * ( player:GetAttributeValueClean( "clip size penalty", 1, LOADOUT_POSITION_PRIMARY ) ) * ( player:GetAttributeValueClean( "clip size bonus", 1, LOADOUT_POSITION_BODY ) ) * ( player:GetAttributeValueClean( "clip size penalty", 1, LOADOUT_POSITION_BODY ) ) ) end if( WeaponAllowed( player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY), WEAPONS_LIST_ENERGYWEAPON ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( "clip size bonus upgrade", 1 * ( player:GetAttributeValueClean( "clip size bonus", 1, LOADOUT_POSITION_SECONDARY ) ) * ( player:GetAttributeValueClean( "clip size penalty", 1, LOADOUT_POSITION_SECONDARY ) ) * ( player:GetAttributeValueClean( "clip size bonus", 1, LOADOUT_POSITION_BODY ) ) * ( player:GetAttributeValueClean( "clip size penalty", 1, LOADOUT_POSITION_BODY ) ) ) end if ( WeaponAllowed( player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY), WEAPONS_LIST_EXPLOSIVEBULLET ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( "explosive bullets", 150 * ( player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):GetAttributeValueByClass( "mult_explosion_radius", 1 ) ) ) end if ( WeaponAllowed( player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY), WEAPONS_LIST_EXPLOSIVEBULLET ) ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):SetAttributeValue( "explosive bullets", 150 * ( player:GetPlayerItemBySlot(LOADOUT_POSITION_PRIMARY):GetAttributeValueByClass( "mult_explosion_radius", 1 ) ) ) end if IsValid( player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY) ) and ( player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):GetItemName() == "Pulse SMG" ) then player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):SetAttributeValue( "dmg penalty vs players", 0.67 * ( player:GetPlayerItemBySlot(LOADOUT_POSITION_SECONDARY):GetAttributeValueByClass( "mult_dmg", 1 ) ) ) end player:SetAttributeValue( "mult dmg before distance specify", 350 * player:GetAttributeValueClean("strange part new counter ID", 1, LOADOUT_POSITION_BODY ) ) if IsValid(player:GetPlayerItemBySlot(1)) then local attr = player:GetPlayerItemBySlot(1):GetAttributeValue("boost on damage") or 0 if attr > 0 then player.HypeEnable = true else player.HypeEnable = false end if player.HypeEnable then player.Hype = math.max( player.Hype - 0.1, 0 ) player:SetAttributeValue("major move speed bonus", 1 + ( 0.5 * ( player.Hype / player.HypeMax ) ) ) player:ShowHudText({channel = 5, y = 0.90, x = 0.74, r1 = 250, r2 = 250, g1 = 250, g2 = 250, b1 = 250, b2 = 250, effect = 0, fixTime = 0, fadeinTime = 0, fadeoutTime = 0, holdTime = 0.15}, "Hype: " .. math.round( ( player.Hype / player.HypeMax ) * 100, 2 ).. "%" ) end end player:SetAttributeValue( "is australium item", ENCOUNTER_MODIFY_STATS[MODIFY_ENEMY_GOLDEN_CHANCE] ) if WeaponAllowed( player:GetItemBySlotClean( LOADOUT_POSITION_PRIMARY_AND_SECONDARY ), WEAPONS_LIST_RELOADABLE ) and ( GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_DMG_RELOAD ) > 0 ) then local weapon = player.m_hActiveWeapon if IsValid( weapon ) then if not weapon.PreviousAmmo then weapon.PreviousAmmo = weapon.m_iClip1 end local ammo = weapon.m_iClip1 if WeaponAllowed( weapon, WEAPONS_LIST_ENERGYWEAPON ) then ammo = weapon.m_flEnergy end -- print( weapon.m_flNextPrimaryAttack - CurTime() ) if ( ammo > weapon.PreviousAmmo ) and ( ( player.m_flNextAttack - CurTime() ) <= 0 ) and ( ( weapon.m_flNextPrimaryAttack - CurTime() - 0.2 ) <= 0 ) then player:Print( PRINT_TARGET_CENTER, "Reload Bonuses Given" ) player.ReloadDamageTimer = CurTime() + 2 ChangePlayerData( player, PLAYER_DATA_TYPE_BASEDMG, PLAYER_DATA_DAMAGE_BASEDMG_RELOADDAMAGE, GetPlayerData( player, PLAYER_DATA_TYPE_MISC, PLAYER_DATA_MISC_DMG_RELOAD ), "set" ) end if ( weapon.PreviousAmmo ~= ammo ) and ( ( player.m_flNextAttack - CurTime() ) <= 0 ) then weapon.PreviousAmmo = ammo end end end if ( player.ReloadDamageTimer ) and ( CurTime() >= player.ReloadDamageTimer ) and ( GetPlayerData( player, PLAYER_DATA_TYPE_BASEDMG, PLAYER_DATA_DAMAGE_BASEDMG_RELOADDAMAGE ) ) then ChangePlayerData( player, PLAYER_DATA_TYPE_BASEDMG, PLAYER_DATA_DAMAGE_BASEDMG_RELOADDAMAGE, 0, "set" ) end if player.DamageMultGained then ChangePlayerData( player, PLAYER_DATA_TYPE_DMGMULT, PLAYER_DATA_DAMAGE_DMGMULT_GEMSTONE, player.DamageMultGained, "set" ) ChangePlayerData( player, PLAYER_DATA_TYPE_FIRERATEMULT, PLAYER_DATA_SPEED_FIRERATEMULT_SPEEDRING, player.FireRateMultGained, "set" ) end ApplyPlayerDataAttributes( player ) if HasFlexes and ( not WaveStarted ) then if not (FullFlex) then player:ShowHudText({channel = 3, x = -1, y = 0.1, effect = 2, r1 = 146, r2 = 146, g1 = 25, g2 = 25, b1 = 252, b2 = 252, fadeinTime = 0, fadeoutTime = 0.3, holdTime = 0.3, fxTime = 0}, "◬Flexes Enabled◬" ) else player:ShowHudText({channel = 3, x = -1, y = 0.1, effect = 2, r1 = 146, r2 = 146, g1 = 25, g2 = 25, b1 = 252, b2 = 252, fadeinTime = 0, fadeoutTime = 0.3, holdTime = 0.3, fxTime = 0}, "◬◬All Flexes Enabled◬◬" ) end end ::continue:: end for _, bot in pairs( ents.GetAllAliveBots() ) do if bot:InCond( TF_COND_SAPPED ) then local attacker = bot:GetConditionProvider( TF_COND_SAPPED ) if ( IsValid( attacker ) ) and attacker:IsPlayer() and ( attacker.m_iClass == TF_CLASS_SPY ) then if IsValid( attacker:GetPlayerItemBySlot( LOADOUT_POSITION_BUILDING ) ) then local mark = attacker:GetAttributeValueClean( "duck badge level", 0, LOADOUT_POSITION_BUILDING ) local milk = attacker:GetAttributeValueClean( "display duck leaderboard", 0, LOADOUT_POSITION_BUILDING ) if tobool( mark ) then bot:AddCond( TF_COND_MARKEDFORDEATH, 1, attacker ) end if tobool( milk ) then bot:AddCond( TF_COND_MAD_MILK, 1, attacker ) end end end end if bot:InCond( TF_COND_TMPDAMAGEBONUS ) then local attacker = bot:GetConditionProvider( TF_COND_TMPDAMAGEBONUS ) if ( IsValid( attacker ) ) and attacker:IsPlayer() and ( attacker.m_iClass == TF_CLASS_SPY ) then if IsValid( attacker:GetPlayerItemBySlot( LOADOUT_POSITION_BUILDING ) ) then local mark = attacker:GetAttributeValueClean( "duck badge level", 0, LOADOUT_POSITION_BUILDING ) local milk = attacker:GetAttributeValueClean( "display duck leaderboard", 0, LOADOUT_POSITION_BUILDING ) if tobool( mark ) then bot:AddCond( TF_COND_MARKEDFORDEATH, 1, attacker ) end if tobool( milk ) then bot:AddCond( TF_COND_MAD_MILK, 1, attacker ) end end end end end if InWave and ( WAVE_TYPE_CURRENT == WAVE_TYPE_HORDE ) then for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end if WAVE_DURATION and ( WAVE_DURATION > CurTime() ) and ( LastWholeNumber ~= math.ceil( WAVE_DURATION - CurTime() ) ) then -- player:Print( PRINT_TARGET_HINT, "¡HORDE! Time Left: " .. tostring( math.ceil( WAVE_DURATION - CurTime() ) ) .. " Seconds" ) player:ShowHudText({channel = 2, x = -1, y = 0.15, effect = 2, r1 = 250, r2 = 250, g1 = 250, g2 = 200, b1 = 100, b2 = 0, fadeinTime = 0.01, fadeoutTime = 0.5, holdTime = 1, fxTime = 0.01}, "¡HORDE! Time Left: " .. tostring( math.ceil( WAVE_DURATION - CurTime() ) ) .. " Seconds" ) end ::continue:: end if WAVE_DURATION and ( WAVE_DURATION > CurTime() ) and ( LastWholeNumber ~= math.ceil( WAVE_DURATION - CurTime() ) ) then LastWholeNumber = math.ceil( WAVE_DURATION - CurTime() ) end end if InWave and ( WAVE_TYPE_CURRENT == WAVE_TYPE_CONTROL ) then if not MISSION_MODIFY_STATS[ MISSION_MODIFY_TIMELIME ].enabled then for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end if WAVE_DURATION and ( WAVE_DURATION > CurTime() ) and ( LastWholeNumber ~= math.ceil( WAVE_DURATION - CurTime() ) ) then -- player:Print( PRINT_TARGET_HINT, "¡HORDE! Time Left: " .. tostring( math.ceil( WAVE_DURATION - CurTime() ) ) .. " Seconds" ) player:ShowHudText({channel = 2, x = -1, y = 0.15, effect = 2, r1 = 250, r2 = 250, g1 = 250, g2 = 200, b1 = 100, b2 = 0, fadeinTime = 0.01, fadeoutTime = 0.5, holdTime = 1, fxTime = 0.01}, "Wave Failure In: " .. tostring( math.ceil( WAVE_DURATION - CurTime() ) ) .. " Seconds" ) end ::continue:: end if WAVE_DURATION and ( WAVE_DURATION > CurTime() ) and ( LastWholeNumber ~= math.ceil( WAVE_DURATION - CurTime() ) ) then LastWholeNumber = math.ceil( WAVE_DURATION - CurTime() ) end end end if InWave and ( WAVE_TYPE_CURRENT ~= WAVE_TYPE_HORDE ) and ( not SHOP_WAVE ) then if MISSION_MODIFY_STATS[ MISSION_MODIFY_TIMELIME ].enabled then for _, player in pairs( ents.GetAllRealPlayers() ) do if ( not IsValid( player ) ) or ( not player:OnTeam( 2 ) ) then goto continue end if WAVE_TIMELIMIT_TIME and ( WAVE_TIMELIMIT_TIME > CurTime() ) and ( LastWholeNumber ~= math.ceil( WAVE_TIMELIMIT_TIME - CurTime() ) ) then -- player:Print( PRINT_TARGET_HINT, "¡HORDE! Time Left: " .. tostring( math.ceil( WAVE_DURATION - CurTime() ) ) .. " Seconds" ) player:ShowHudText({channel = 2, x = -1, y = 0.15, effect = 2, r1 = 250, r2 = 250, g1 = 100, g2 = 50, b1 = 50, b2 = 0, fadeinTime = 0.01, fadeoutTime = 0.5, holdTime = 1, fxTime = 0.01}, "Time Until Defeat: " .. tostring( math.ceil( WAVE_TIMELIMIT_TIME - CurTime() ) ) .. " Seconds" ) local warning_start_time = 10 warning_start_time = ( WAVE_TIMELIMIT_MAX ) - warning_start_time local offset = 1 local frac = math.max( math.min( ( math.abs(WAVE_TIMELIMIT_MAX - ( WAVE_TIMELIMIT_TIME - CurTime() - offset )) ) - warning_start_time, WAVE_TIMELIMIT_MAX - warning_start_time ) / ( (WAVE_TIMELIMIT_MAX) - warning_start_time ), 0 ) -- print( frac ) PlaySoundToPlayer( { player = player, sound = ")misc/halloween/clock_tick.wav", volume = frac, pitch = 100 } ) PlaySoundToPlayer( { player = player, sound = ")misc/halloween/clock_tick.wav", volume = frac * 0.75, pitch = 80 } ) PlaySoundToPlayer( { player = player, sound = ")misc/halloween/clock_tick.wav", volume = frac * 0.67, pitch = 60 } ) end if CurTime() >= WAVE_TIMELIMIT_TIME then -- ents.FindByName("bots_win"):RoundWin() local dif = ( WAVE_TIMELIMIT_TIME - CurTime() ) local abs_dif = math.abs( dif ) local frac = lerp( 0, 1, abs_dif / 30 ) -- player:RemoveCond( TF_COND_INVULNERABLE ) -- player:RemoveCond( TF_COND_INVULNERABLE_USER_BUFF ) if ( LastWholeNumber ~= math.ceil( WAVE_TIMELIMIT_TIME - CurTime() ) ) then local max_hp = ( ( PlayerDefaultHealth[player.m_iClass] + player:GetAttributeValueByClass("add_maxhealth", 0) ) * player:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) print( max_hp ) PlaySoundToPlayer( { player = player, sound = ")misc/halloween/clock_tick.wav", volume = 1, pitch = 80 } ) PlaySoundToPlayer( { player = player, sound = ")misc/halloween/clock_tick.wav", volume = 1 * 0.75, pitch = 60 } ) PlaySoundToPlayer( { player = player, sound = ")misc/halloween/clock_tick.wav", volume = 1 * 0.67, pitch = 40 } ) player:TakeDamage( { Damage = max_hp * frac, Attacker = player, DamageType = DMG_GENERIC } ) end end ::continue:: end if WAVE_TIMELIMIT_TIME and ( LastWholeNumber ~= math.ceil( WAVE_TIMELIMIT_TIME - CurTime() ) ) then LastWholeNumber = math.ceil( WAVE_TIMELIMIT_TIME - CurTime() ) end end 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 -- if CHEATERS[player.m_szNetworkIDString] then -- player:Kill() -- util.PrintToChatAll( player:GetPlayerName() .. " left the game (You have been blacklisted from Wacev missions)" ) -- return -- end 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() ) for i=0, 12 do player:WeaponStripSlot( i ) end -- for _, weapon in pairs( items ) do -- player:GiveItem( weapon ) -- end for attr, val in pairs( player:GetAllAttributeValues() ) do player:SetAttributeValue( attr, nil ) end if (player.ReanimatorPos) and ( player:GetAbsOrigin():Distance(player.ReanimatorPos) <= 100 ) then WAVE_REVIVES = WAVE_REVIVES + 1 player.Revives = player.Revives + 1 end timer.Simple( 0.015, function() player:Regenerate() -- if ( ( player.OldClass ~= nil ) and ( class ~= player.OldClass ) ) then ApplyMissingUpgrades( player ) -- end -- player.OldClass = class for i=0, 2 do if( IsValid(player:GetPlayerItemBySlot(i)) ) then player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on attack", "popscript^$OnAttackLogic^" ) player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on kill", "popscript^$OnKillLogic^" ) if player:GetPlayerItemBySlot(i):GetClassname() == "tf_wearable_demoshield" then player:GetPlayerItemBySlot(i):SetAttributeValue( "fire input on hit", "popscript^$OnBashLogic^" ) end end end if WaveStarted and ( not SHOP_WAVE ) then local Possible_Spawns = GetTeamSpawns( TEAM_RED ) player:SetAbsOrigin( Possible_Spawns[ math.random( 1, #Possible_Spawns ) ] + Vector( 0, 0, 16 ) ) player:AddCond( TF_COND_INVULNERABLE_CARD_EFFECT, 3 ) player:AddCond( TF_COND_INVULNERABLE_WEARINGOFF, 3 ) player:AddCond( TF_COND_SPEED_BOOST, 3 ) if ARENA == 3 then player:SetFogController( "fog_controller_hell" ) elseif ARENA == 4 then player:SetFogController( "fog_controller_bridge" ) else player:SetFogController( "fog_controller" ) end for _, oth in pairs( ents.FindInSphere( player:GetAbsOrigin(), 192, "player" ) ) do if oth:IsBot() and oth:IsAlive() and ( not oth:OnTeam( 1 ) ) then oth:StunPlayer( 5, 1, TF_STUNFLAGS_NORMALBONK, player ) end end end end) end) end -- if player:IsValid() then -- player.ArmorPlates = 0 -- player.MaxArmorPlates = 6 -- end end) hooks.Add( "ON_TAKE_DAMAGE", "player_hurt", function( event ) local victim = ents.GetPlayerByUserId( tonumber( event.userid ) ) local attacker = ents.GetPlayerByUserId( ( tonumber( event.attacker ) or 0 ) ) local weapon = nil local melee = nil local dmg = event.damageamount if IsValid( attacker ) then weapon = attacker.m_hActiveWeapon melee = attacker:GetPlayerItemBySlot(2) end if IsValid( victim ) and victim:IsBot() and IsValid( attacker ) and attacker:IsRealPlayer() and IsValid( weapon ) and ( victim ~= attacker ) and ( dmg >= 1 ) then if ( ( weapon:GetAttributeValue( "throwable damage" ) or 0 ) > 0 ) and ( attacker.JarateDelay < CurTime() ) then local amount = ( weapon:GetAttributeValue( "throwable damage" ) or 0 ) / 100 if amount >= math.random() then victim:AddCond( TF_COND_URINE, 3 ) end attacker.JarateDelay = CurTime() + 0.2 end if ( ( weapon:GetAttributeValue( "throwable healing" ) or 0 ) > 0 ) and ( attacker.MadMilkDelay < CurTime() ) then local amount = ( weapon:GetAttributeValue( "throwable healing" ) or 0 ) / 100 if amount >= math.random() then victim:AddCond( TF_COND_MAD_MILK, 2 ) end attacker.MadMilkDelay = CurTime() + 0.2 end if IsValid( melee ) and ( weapon == melee ) and ( ( melee:GetAttributeValue( "duckstreaks active" ) or 0 ) > 0 ) then victim.MarkForHealth = true end if ( attacker:GetAttributeValue( "expiration date" ) or 0 ) > 0 then local LastVictim attacker.ThisMoneyShot = CurTime() local function AlreadyVictim( tbl, ply ) for i, v in pairs( tbl ) do if v == ply then return true end end return false end if ( attacker.MoneyShotDelay <= CurTime() ) then attacker.MoneyShotTimestamp = CurTime() attacker.MoneyShotVictims = {} end if ( attacker.MoneyShotDelay <= CurTime() ) or ( ( attacker.ThisMoneyShot == attacker.MoneyShotTimestamp ) and ( not AlreadyVictim( attacker.MoneyShotVictims, victim ) ) ) then local amount = ( attacker:GetAttributeValue( "expiration date" ) or 0 ) local sellout = HasUpgrade( attacker, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( attacker.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) amount = amount * decrease end attacker:AddCurrency( amount ) attacker.MoneyShotDelay = CurTime() + 0.2 table.insert( attacker.MoneyShotVictims, victim ) end end if ( weapon ~= melee ) and ( ( weapon:GetAttributeValue( "deactive date" ) or 0 ) > 0 ) then victim:SetAttributeValueTemporary( "dmg from melee increased", 2, 3 ) end if HasUpgrade( attacker, UPGRADE_BOURGEOISIE_TEAMKILL ) then victim:SetAttributeValueTemporary( "allow friendly fire", 1, 5 ) end end if IsValid( victim ) and victim:IsBot() and IsValid( attacker ) and ( attacker:GetTeam() ~= victim:GetTeam() ) then local maxhp = victim.MaxHealth or ( 25 * GetEnemyHealthMult() ) if ( victim.m_iHealth + dmg ) >= maxhp then victim.OneshotEligible = true timer.Simple( 0.5, function() victim.OneshotEligible = false end) end if IsValid( attacker ) and ( attacker:GetTeam() ~= victim:GetTeam() ) then if ( dmg >= maxhp and ( ( victim.m_iHealth + dmg ) >= maxhp ) ) or ( ( victim.OneshotEligible == true ) and ( victim.m_iHealth <= 0 ) ) then local attr = attacker:GetAttributeValueClean( "non economy", 1, LOADOUT_POSITION_BODY ) local extra_cash = ( ENEMY_BASE_CASH * attr ) - ENEMY_BASE_CASH local sellout = HasUpgrade( attacker, UPGRADE_GREED_MONEY_SELLOUT ) local decrease = 1 if sellout then decrease = math.max( 1 - ( attacker.ShopUpgradesbought * ( 1 / 15 ) ), 0 ) extra_cash = extra_cash * decrease end attacker:AddCurrency( extra_cash ) end end end if IsValid( victim) and victim:IsRealPlayer() then if ( ( (victim:GetAttributeValue("sapper health bonus") or 0 ) > 0 ) or ( MISSION_MODIFY_STATS[MISSION_MODIFY_LOSEHEALTHONHIT].enabled ) ) and ( math.floor( dmg ) > 1 ) and ( IsValidPlayer( attacker ) ) and ( attacker ~= victim ) and ( victim.HealthLossDelay < CurTime() ) then victim:SetAttributeValue( "max health additive bonus", ( victim:GetAttributeValue("max health additive bonus") or 0 ) - ( 1 + ( math.floor( ( ( PlayerDefaultHealth[victim.m_iClass] * victim:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) + victim:GetAttributeValueByClass("add_maxhealth", 0) ) / 450 ) ) ) ) victim.HealthGained = victim.HealthGained - ( 1 + ( math.floor( ( ( PlayerDefaultHealth[victim.m_iClass] * victim:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) ) + victim:GetAttributeValueByClass("add_maxhealth", 0) ) / 450 ) ) ) victim.HealthLossDelay = CurTime() + 0.2 end end if IsValid( victim ) and victim:IsRealPlayer() and IsValid( attacker ) and attacker:IsBot() and ( victim ~= attacker ) and ( dmg >= 1 ) then if HasUpgrade( victim, UPGRADE_BOURGEOISIE_RING_THORNS ) then -- local reflect_dmg = dmg + ( victim.m_iMaxHealth + victim:GetAttributeValueByClass("add_maxhealth", 0) ) * victim:GetAttributeValueClean( "mult max health", 1, LOADOUT_POSITION_BODY ) local reflect_dmg = dmg * 5 local dmginfo = { Attacker = victim, Damage = reflect_dmg, DamageType = DMG_DIRECT } attacker:TakeDamage( dmginfo ) if CurTime() >= victim.ReflectParticleDelay then local particle = ents.CreateWithKeys( "info_particle_system", { ["effect_name"] = "arm_muzzleflash_zap", ["start_active"] = 1, ["origin"] = victim:GetEyePos()[1] .. " " .. victim:GetEyePos()[2] .. " " .. (victim:GetEyePos()[3] + 10), ["cpoint1"] = attacker }, true, true) victim:PlaySound("Powerup.Reflect.Reflect") particle.m_hControlPointEnts[1] = attacker timer.Simple( 0.1, function() --feedback particle:Remove() end) victim.ReflectParticleDelay = CurTime() + 0.2 end end end end) hooks.Add( "ON_HEALED_CROSSBOW", "player_healed", function( event ) local ply = ents.GetPlayerByUserId(event.patient) local healer = ents.GetPlayerByUserId(event.healer) local amount = tonumber( event.amount ) if healer and ply and ( ply:GetTeam() == healer:GetTeam() ) and ( ply ~= healer ) and ( healer:IsBot() ) and ( IsValid(healer:GetPlayerItemBySlot( LOADOUT_POSITION_PRIMARY )) ) and ( healer:GetPlayerItemBySlot( LOADOUT_POSITION_PRIMARY ):GetClassname() == "tf_weapon_crossbow" ) then local Heal = 0.1 if tobool( ply.m_bIsMiniBoss ) then Heal = 0.025 end if ( IsValid(ply:GetPlayerItemBySlot( LOADOUT_POSITION_PRIMARY )) ) and ( ply:GetPlayerItemBySlot( LOADOUT_POSITION_PRIMARY ):GetClassname() == "tf_weapon_crossbow" ) then Heal = 0.005 end if string.match( healer.m_szNetname, "Healing " ) then Heal = Heal * 2.5 end ply:AddHealth( ply.MaxHealth * Heal, false ) end end) -- hooks.Add( "ON_HEALED", "player_healed", function( event ) -- too many things don't call this -- local ply = ents.GetPlayerByUserId(event.patient) -- local healer = ents.GetPlayerByUserId(event.healer) -- local amount = event.amount -- if IsValid(ply) and ply:IsRealPlayer() and IsValid(healer) and ( healer:IsPlayer() ) then -- ply.m_iHealth = ply.m_iHealth - ( amount * ( 1 - ply.HealingFromNotKits_Mult ) ) -- end -- end) --[[----------------+ SETUP +------------------]]-- WaveThink() for i = 0, #VOTE_CATEGORIES do VOTE_RESULTS[i] = {} end for ent, _ in pairs(buildings) do ents.AddCreateCallback(ent, OnBuildingCreated) end print( "hippopotamus-ass" )