local DIRECTOR_TOTAL_CREDITS = 5000 local TOTAL_WAVES = 7 -- function GenerateSubwave(wave, subwaveIndex, botTemplate) -- local totalCount = math.random(1, 2) -- local subwaveData = { -- Name = tostring(wave) .. "_" .. tostring(subwaveIndex), -- Where = "spawnbot", -- WaitBeforeStarting = 0, -- WaitBetweenSpawns = 0, -- TotalCount = totalCount, -- SpawnCount = totalCount, -- MaxActive = totalCount, -- TotalCurrency = 10, -- Templates = { -- botTemplate, -- }, -- } -- return subwaveData -- end local function printError(err) util.PrintToChatAll("[MISSION-GENERATOR][ERROR]: " .. err) end function ChooseRandomSubwavePattern(subwaveIndex, usedPatterns, waveCredits) local weightedPatterns = {} for patternName, patternInfo in pairs(DIRECTOR_SUBWAVE_PATTERNS) do local weight = 1 local preferredIndex = patternInfo.PreferredIndex if preferredIndex then if TableContains(preferredIndex, subwaveIndex) then weight = weight * 10 else weight = weight / 10 end end local dislikedIndex = patternInfo.DislikedIndex if dislikedIndex and TableContains(dislikedIndex, subwaveIndex) then weight = weight / 10 end if usedPatterns[patternName] then weight = weight / 1000 end if patternInfo.Cost > waveCredits then weight = weight / 100000 end table.insert(weightedPatterns, { Name = patternName, Info = patternInfo, Weight = weight, }) end local totalWeight = 0 for _, entry in ipairs(weightedPatterns) do totalWeight = totalWeight + entry.Weight end local roll = math.random() * totalWeight local current = 0 for _, entry in ipairs(weightedPatterns) do current = current + entry.Weight if roll <= current then return entry.Name, entry.Info end end end function ChooseTemplatesForSequence(sequence, usedTemplates, isGiant) local weightedTemplates = {} local templatesPool = isGiant and ROBOT_GIANT or ROBOT_STANDARD for templateName, templateData in pairs(templatesPool) do local canChoose = true if sequence.IsMission then if not templateData.DirectorMissionOnly then canChoose = false end else if templateData.DirectorMissionOnly then canChoose = false end end local directorTags = templateData.DirectorTags or {} if TableContains(directorTags, DIRECTOR_BOT_TAGS.Boss) then canChoose = false end if sequence.AllowedClasses then if not TableContains(sequence.AllowedClasses, templateData.Class) then canChoose = false end end if sequence.AllowedTags then for _, tag in pairs(sequence.AllowedTags) do if not TableContains(directorTags, tag) then canChoose = false break end end end if sequence.AllowedTagsAny then local hasTag = false for _, tag in pairs(sequence.AllowedTagsAny) do if TableContains(directorTags, tag) then hasTag = true break end end if not hasTag then canChoose = false end end if sequence.DisallowedTags then for _, tag in pairs(sequence.DisallowedTags) do if TableContains(directorTags, tag) then canChoose = false break end end end if canChoose then local weight = 1 if usedTemplates[templateName] then weight = weight / 1000 end table.insert(weightedTemplates, { Name = templateName, Data = templateData, Weight = weight, }) end end local totalWeight = 0 for _, entry in ipairs(weightedTemplates) do totalWeight = totalWeight + entry.Weight end local roll = math.random() * totalWeight local current = 0 for _, entry in ipairs(weightedTemplates) do current = current + entry.Weight if roll <= current then return entry.Name, entry.Data end end end local function generateSubwaveName(wave, subwaveIndex, subfix) local subwaveName = tostring(wave) .. "_" .. tostring(subwaveIndex) if subfix then subwaveName = subwaveName .. subfix end return subwaveName end function BuildSubwave(sequence, templates, subwaveName) local totalCount if sequence.TotalCount then totalCount = sequence.TotalCount elseif sequence.TotalCountMin and sequence.TotalCountMax then totalCount = math.random(sequence.TotalCountMin, sequence.TotalCountMax) else printError(sequence.Name .. " HAS INVALID TotalCount DEFINITION") end if not sequence.SpawnCount then printError(sequence.Name .. " HAS NO SpawnCount set") end if not sequence.MaxActive then printError(sequence.Name .. " HAS NO MaxActive set") end local waitBetweenSpawns local waitBetweenSpawnsAfterDeath if sequence.WaitBetweenSpawnsAfterDeath then if sequence.WaitBetweenSpawns then printError(sequence.Name .. " HAS BOTH WaitBetweenSpawnsAfterDeath AND WaitBetweenSpawns") end waitBetweenSpawns = nil waitBetweenSpawnsAfterDeath = sequence.WaitBetweenSpawnsAfterDeath else waitBetweenSpawns = sequence.WaitBetweenSpawns or 0 waitBetweenSpawnsAfterDeath = nil end local subwaveData = { Name = subwaveName, Where = "spawnbot", WaitBeforeStarting = sequence.WaitBeforeStarting or 0, WaitBetweenSpawns = waitBetweenSpawns, WaitBetweenSpawnsAfterDeath = waitBetweenSpawnsAfterDeath, TotalCount = totalCount, SpawnCount = sequence.SpawnCount, MaxActive = sequence.MaxActive, TotalCurrency = 10, Templates = templates, } return subwaveData end function GenerateWave(wave) print("generate wave " .. tostring(wave)) local waveData = {} local usedTemplates = {} local usedPatterns = {} local lastSubwaveData local lastSequence local WAVE_CREDITS = 3 local curSubwaveIndex = 0 while WAVE_CREDITS > 0 do curSubwaveIndex = curSubwaveIndex + 1 local patternName, patternInfo = ChooseRandomSubwavePattern(curSubwaveIndex, usedPatterns, WAVE_CREDITS) WAVE_CREDITS = WAVE_CREDITS - patternInfo.Cost print(tostring(curSubwaveIndex) .. " chosen pattern: " .. patternName) usedPatterns[patternName] = true local subwaveName = generateSubwaveName(wave, curSubwaveIndex) local sequenceSubwaves = {} for i, sequence in pairs(patternInfo.Sequences) do local isGiant = sequence.IsGiant or false local constructedSubwave if sequence.FixedTemplates then constructedSubwave = BuildSubwave(sequence, sequence.FixedTemplates, subwaveName) else local templateName, templateData = ChooseTemplatesForSequence(sequence, usedTemplates, isGiant) usedTemplates[templateName] = true constructedSubwave = BuildSubwave(sequence, { templateData }, subwaveName) end if sequence.WaitForAllDeadSequence then constructedSubwave.WaitForAllDead = sequenceSubwaves[sequence.WaitForAllDeadSequence].Name elseif sequence.WaitForAllSpawnedSequence then constructedSubwave.WaitForAllSpawned = sequenceSubwaves[sequence.WaitForAllSpawnedSequence].Name else if not sequence.IgnoreLastSequenceRule then if lastSubwaveData and lastSequence then if lastSequence.FollowUpWaitForAllSpawned then constructedSubwave.WaitForAllSpawned = lastSubwaveData.Name elseif lastSequence.FollowUpWaitForAllDead then -- default behavior: wait for last subwave to finish constructedSubwave.WaitForAllDead = lastSubwaveData.Name end end end end if sequence.SupportLimited then constructedSubwave.SupportLimited = true elseif sequence.SupportInfinite then constructedSubwave.SupportInfinite = true end if sequence.IsMission then constructedSubwave.IsMission = true end lastSubwaveData = constructedSubwave lastSequence = sequence sequenceSubwaves[i] = sequence table.insert(waveData, constructedSubwave) end end return waveData end local allWaves = {} for i = 1, TOTAL_WAVES do local waveData = GenerateWave(i) allWaves[i] = waveData end local missionInfo = { PopfileNameDisplay = "wave director test", IncludeScripts = { -- "tankextensions_main", -- "tankextensions/combattank", -- "tankextensions/combattank_weapons/minigun", -- "tankextensions/combattank_weapons/rocketpod", -- "tankextensions/tankdozer", }, } local function waveStart(wave) util.PrintToChatAll("[MISSION-GENERATOR]: STARTING WAVE " .. tostring(wave)) if allWaves[wave] then RunWave(allWaves[wave]) else util.PrintToChatAll("[MISSION-GENERATOR]: MISSING WAVE DATA FOR WAVE " .. tostring(wave)) end end local function waveInit(wave) InitMission(missionInfo) local waveData = allWaves[wave] if waveData then ParseWave(waveData, false) WaveBarGen(waveData) end end if OnWaveStartFuncs then table.insert(OnWaveStartFuncs, waveStart) else function OnWaveStart(wave) waveStart(wave) end end if OnWaveInitFuncs then table.insert(OnWaveInitFuncs, waveInit) else function OnWaveInit(wave) waveInit(wave) end end