-- ============================================================================ -- ENDLESS MODE INFINITO - TF2 MANN vs MACHINE + SIGMOD -- Autor: Adrian (com ajuda do Codex) -- ============================================================================ -- COMO FUNCIONA (AGORA INFINITO DE VERDADE!): -- * Logo no inicio este script LIGA o modo endless nativo do TF2 -- (convar "tf_mvm_endless_force_on"). Com isso, quando a ultima onda do -- popfile acaba, o jogo NAO termina: ele faz LOOP das ondas, subindo a -- dificuldade sozinho. -- * Cada robo INIMIGO que nasce recebe um GENE aleatorio unico. -- * Os robos AJUDANTES (Tag "ajudante") sao colocados no time RED pelo Lua -- e ganham vida extra escalada pela onda (para aguentar o infinito). -- * A HUD mostra so "Wave N" (sem o "/20"). -- -- CALLBACKS DO SIGMOD QUE USAMOS: -- OnWaveStart(wave) -> anunciando, preparando e arrumando a HUD -- OnWaveSpawnBot(bot, wave, tags) -> modificando cada robo -- OnWaveSuccess(wave) -> comemoracao -- OnWaveFail(wave) -> animacao -- OnWaveReset() -> reiniciar o estado -- ============================================================================ print("[ENDLESS MODE] Script carregado! Ativando o modo infinito...") -- ======================= ATIVA O MODO INFINITO ============================ local function ativar_endless() pcall(function() convar.SetValue("tf_mvm_endless_force_on", 1) end) end ativar_endless() if convar.IsValid("tf_mvm_endless_force_on") then print("[ENDLESS MODE] Modo endless INFINITO ativado com sucesso!") else print("[ENDLESS MODE] AVISO: convar tf_mvm_endless_force_on indisponivel!") end -- ======================= CONFIGURACAO (mude a vontade!) ===================== local CONFIG = { vida_por_onda = 0.10, -- +10% de vida por onda (robos inimigos) dano_por_onda = 0.045, -- +4.5% de dano por onda (robos inimigos) vel_maxima = 2.2, -- teto da velocidade para nao ficar absurdo duracao_cond = 9999, -- duracao das condicoes visuais (a onda toda) prefixo_chat = "\x07FFD700[ENDLESS]\x01 ", chance_anunciar = 8, -- 1 em X robos anuncia o gene no chat -- Robos ajudantes (time RED): ajudante_vida_base = 4.0, -- multiplicador de vida base (x5) ajudante_vida_por_onda = 0.15, -- vida extra por onda (aguenta o infinito) } -- ======================= BIBLIOTECA DE GENES ================================ -- Cada gene e um "modificador genetico" do robo INIMIGO. Eles sao aplicados -- quando nascem. Os valores de vida/dano sao multiplicados pelo escalonamento -- da onda (ficando mais fortes com o passar das ondas, sem limite!). local GENES = { { id = "corredor", nome = "Corredor", cor = "\x0700FF00", attrs = { ["move speed bonus"] = 1.55, ["damage bonus"] = 0.85 }, }, { id = "blindado", nome = "Blindado", cor = "\x07AAAAAA", attrs = { ["max health % bonus"] = 2.4, ["damage force reduction"] = 0.15 }, cond = 26, -- TF_COND_DEFENSEBUFF (escudo) }, { id = "furioso", nome = "Furioso", cor = "\x07FF3300", attrs = { ["fire rate bonus"] = 0.35, ["damage bonus"] = 1.35 }, }, { id = "colosso", nome = "Colosso", cor = "\x07FF00FF", attrs = { ["max health % bonus"] = 3.2, ["move speed bonus"] = 0.7 }, cond = 74, -- TF_COND_HALLOWEEN_GIANT (gigante!) }, { id = "pulga", nome = "Pulga", cor = "\x07FFFF00", attrs = { ["move speed bonus"] = 1.8, ["damage bonus"] = 1.3, ["max health % bonus"] = 0.55 }, cond = 75, -- TF_COND_HALLOWEEN_TINY (pequenininho!) }, { id = "critico", nome = "Critico", cor = "\x07FF6600", attrs = { ["max health % bonus"] = 1.3 }, cond = 11, -- TF_COND_CRITBOOSTED (dano critico!) }, { id = "atirador", nome = "Atirador", cor = "\x0766CCFF", attrs = { ["projectile speed increased"] = 2.0, ["fire rate bonus"] = 0.6, ["damage bonus"] = 1.25 }, }, { id = "vampiro", nome = "Vampiro", cor = "\x07990000", attrs = { ["heal on kill"] = 150, ["damage bonus"] = 1.2 }, }, { id = "medico_combate", nome = "Medico de Combate", cor = "\x07FFFFFF", attrs = { ["healing rate bonus"] = 2.5, ["ubercharge rate bonus"] = 2.5, ["move speed bonus"] = 1.3 }, so_para_tag = "medic", -- so se aplica em robos com a Tag "medic" }, { id = "berserker", nome = "Berserker", cor = "\x07CC0000", attrs = { ["damage bonus"] = 1.7, ["max health % bonus"] = 0.75, ["move speed bonus"] = 1.25 }, }, } -- ======================= NOMES PARA AS ONDAS ESPECIAIS ===================== local ONDAS_ESPECIAIS_BASE = { "A CORRIDA MALUCA", "A INVASAO DOS TANQUES", "O CAOS TOTAL", "O APOCALIPSE", } local ADJETIVOS = { "Sorrateira", "Explosiva", "Relampago", "Faminta", "Furiosa", "Implacavel", "Sonolenta", "Festiva", "Teleguiada", "Caotica", "Maluca", "Perfeita", "Vingativa", "Eletrica", "Sombria", } -- ======================= ESTADO INTERNO DO SCRIPT ========================== local estado = { onda_atual = 1, genes_disponiveis = {}, -- fila embaralhada de genes da onda genes_usados_total = {}, -- conta quantas vezes cada gene ja foi usado bots_gerados = 0, -- contador de bots criados nesta onda ajudantes_anunciados = false, spawns_red_criados = false, -- spawn extra "Red" perto da base RED } for _, gene in ipairs(GENES) do estado.genes_usados_total[gene.id] = 0 end -- ======================= FUNCOES UTILITARIAS ================================ local function embaralhar(t) for i = #t, 2, -1 do local j = math.random(1, i) t[i], t[j] = t[j], t[i] end return t end local function stats_da_onda(onda) local vida = 1.0 + (onda - 1) * CONFIG.vida_por_onda local dano = 1.0 + (onda - 1) * CONFIG.dano_por_onda return vida, dano end local function tem_tag(tags, nome) if tags == nil then return false end for _, t in ipairs(tags) do if t == nome then return true end end return false end -- ======================= ARRUMA A HUD "wave N" ============================== local function arrumar_hud(onda) local obj = nil pcall(function() obj = ents.FindByClass("tf_objective_resource") end) if not IsValid(obj) then return end pcall(function() obj:SetFakeSendProp("m_nMannVsMachineWaveCount", onda) end) pcall(function() obj:SetFakeSendProp("m_nMannVsMachineMaxWaveCount", 0) end) end -- ======================= TRATA O ROBO AJUDANTE (RED!) ====================== -- Os robos com Tag "ajudante" sao os "Upgraded Giant Soldier Buff Banner". -- Eles nascem BLU mas o Lua os joga para o time RED, e ganham vida extra -- escalada pela onda para aguentarem as ondas infinitas. local function tratar_ajudante(bot, wave) -- Joga o robo para o time RED (2). Assim ele ataca os robos BLU e -- ajuda os jogadores! Usamos pcall para nao quebrar se falhar. pcall(function() bot.m_iTeamNum = 2 end) -- Vida extra escalada pela onda (na onda 20 ele tem mais que o triplo) local vida_extra = CONFIG.ajudante_vida_base + (wave - 1) * CONFIG.ajudante_vida_por_onda pcall(function() bot:SetAttributeValue("max health % bonus", vida_extra) end) -- Avisa uma vez por missao que os ajudantes chegaram if not estado.ajudantes_anunciados then estado.ajudantes_anunciados = true util.PrintToChatAll(CONFIG.prefixo_chat .. "\x0700FF00Upgraded Giant Soldier Buff Banner\x01 reprogramado!" .. " 3 aliados gigantes defendem a base RED!") end end -- ======================= PREPARACAO DE UMA NOVA ONDA ======================= local function preparar_genes_da_onda() local candidatos = {} for _, gene in ipairs(GENES) do table.insert(candidatos, gene) end table.sort(candidatos, function(a, b) return estado.genes_usados_total[a.id] < estado.genes_usados_total[b.id] end) local metade = math.max(1, math.floor(#candidatos / 2)) local grupo = {} for i = 1, metade do table.insert(grupo, candidatos[i]) end embaralhar(grupo) for i = 1, metade do candidatos[i] = grupo[i] end estado.genes_disponiveis = candidatos end local function proximo_gene() if #estado.genes_disponiveis == 0 then preparar_genes_da_onda() end local gene = table.remove(estado.genes_disponiveis, 1) estado.genes_usados_total[gene.id] = estado.genes_usados_total[gene.id] + 1 return gene end -- ======================= CRIA O SPAWN EXTRA "Red" =========================== -- O Adrian pediu para NAO usar o "respawnpoints" (pode quebrar). Em vez -- disso, criamos aqui um info_player_teamspawn extra chamado "Red", do time -- RED (teamnum 2), POSICIONADO FORA DA SALA DE UPGRADE mas perto da base. -- -- Coordenadas obtidas lendo o mvm_bigrock.bsp: -- * Sala de upgrade/respawn RED = entre os func_respawnroomvisualizer em -- (800,-2434) e (32,-2826). A porta de saida da base fica em (800,-2426). -- * FORA da sala, no caminho principal, existem itens no chao em Z=450: -- item_ammopack_medium (736, -1536, 450) -- item_healthkit_medium (736, -1600, 450) -- Dois itens a 64 unidades de distancia na MESMA altura = trecho plano! -- * Usamos 3 pontos espalhados ao longo desse trecho (eixo Y = o caminho), -- para os 3 soldados gigantes nao nascerem todos no mesmo lugar. local SPAWN_RED_POSICOES = { { x = 736, y = -1500, z = 450 }, { x = 736, y = -1560, z = 450 }, { x = 736, y = -1620, z = 450 }, } local function criar_spawns_ajudantes() if estado.spawns_red_criados then return end for _, pos in ipairs(SPAWN_RED_POSICOES) do local ok, ent = pcall(function() return ents.CreateWithKeys("info_player_teamspawn", { targetname = "Red", teamnum = 2, -- 2 = RED (BLU = 3) origin = Vector(pos.x, pos.y, pos.z), angles = Vector(0, 90, 0), -- olhando para o campo }, true, true) end) if ok and IsValid(ent) then print(string.format("[ENDLESS MODE] Spawn 'Red' criado em (%.0f, %.0f, %.0f)", pos.x, pos.y, pos.z)) estado.spawns_red_criados = true else print("[ENDLESS MODE] AVISO: nao consegui criar o spawn 'Red'!") end end end -- ======================= CALLBACKS DO SIGMOD ================================ function OnWaveStart(wave) estado.onda_atual = wave preparar_genes_da_onda() arrumar_hud(wave) criar_spawns_ajudantes() local vida, dano = stats_da_onda(wave) local nome = nil if wave % 5 == 0 then local indice = math.floor(wave / 5) % #ONDAS_ESPECIAIS_BASE if indice == 0 then indice = #ONDAS_ESPECIAIS_BASE end nome = "ONDA " .. wave .. " - " .. ONDAS_ESPECIAIS_BASE[indice] end if not nome then local adj = ADJETIVOS[math.random(1, #ADJETIVOS)] or "COMUM" nome = "ONDA " .. wave .. " - " .. adj end util.PrintToChatAll(CONFIG.prefixo_chat .. "==================================") util.PrintToChatAll(CONFIG.prefixo_chat .. nome) util.PrintToChatAll(CONFIG.prefixo_chat .. "Robos: vida x" .. string.format("%.2f", vida) .. " | dano x" .. string.format("%.2f", dano)) util.PrintToChatAll(CONFIG.prefixo_chat .. "==================================") end function OnWaveSpawnBot(bot, wave, tags) if not IsValid(bot) then return end estado.bots_gerados = estado.bots_gerados + 1 -- >>> ROBO AJUDANTE: vira RED, ganha vida escalada, NAO ganha gene <<< if tem_tag(tags, "ajudante") then tratar_ajudante(bot, wave) return end -- >>> ROBOS INIMIGOS: recebem um GENE aleatorio unico <<< local gene = proximo_gene() local vida, dano = stats_da_onda(wave) if gene.so_para_tag and not tem_tag(tags, gene.so_para_tag) then estado.genes_usados_total[gene.id] = estado.genes_usados_total[gene.id] - 1 gene = proximo_gene() end for attr, valor in pairs(gene.attrs) do local v = valor if attr == "max health % bonus" then v = valor * vida elseif attr == "damage bonus" then v = valor * dano elseif attr == "move speed bonus" then v = math.min(valor, CONFIG.vel_maxima) end pcall(function() bot:SetAttributeValue(attr, v) end) end if gene.cond then pcall(function() bot:AddCond(gene.cond, CONFIG.duracao_cond, bot) end) end if math.random(1, CONFIG.chance_anunciar) == 1 then util.PrintToChatAll(CONFIG.prefixo_chat .. gene.cor .. "Um robo " .. gene.nome .. "\x01 apareceu!") end end function OnWaveSuccess(wave) local proxima = wave if proxima <= 1 then proxima = 1 end if proxima % 5 == 0 then util.PrintToChatAll(CONFIG.prefixo_chat .. "Onda especial chegando! Preparem-se para a " .. proxima .. "!") else util.PrintToChatAll(CONFIG.prefixo_chat .. "Proxima onda: " .. proxima .. ". O modo infinito continua!") end end function OnWaveFail(wave) util.PrintToChatAll(CONFIG.prefixo_chat .. "A bomba foi entregue! Nao desistam, ainda temos muito pela frente!") end function OnWaveReset() estado.bots_gerados = 0 estado.ajudantes_anunciados = false for _, gene in ipairs(GENES) do estado.genes_usados_total[gene.id] = 0 end ativar_endless() end print("[ENDLESS MODE] Pronto! Que comecem as ondas INFINITAS!")