831 lines
30 KiB
Lua
831 lines
30 KiB
Lua
-- Copyright (C) 2022 veden
|
|
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
-- (at your option) any later version.
|
|
|
|
-- This program is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU General Public License for more details.
|
|
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
-- imports
|
|
|
|
local gui = require("libs/Gui")
|
|
|
|
--[[
|
|
Vanilla factors
|
|
time_factor
|
|
:: double
|
|
The amount evolution naturally progresses by every second. Defaults to 0.000004.
|
|
|
|
destroy_factor
|
|
:: double
|
|
The amount evolution progresses for every destroyed spawner. Defaults to 0.002.
|
|
|
|
pollution_factor
|
|
:: double
|
|
The amount evolution progresses for every unit of pollution. Defaults to 0.0000009.
|
|
|
|
|
|
Pollution production is the total pollution produced by buildings per tick, not the
|
|
pollution spreading on the map, so it is not reduced by trees or other absorbers.
|
|
e.g. : 10 boilers produce 300 pollution in one minute, raising the evolution factor
|
|
by around 0.027%.
|
|
|
|
The percentages are applied on the base of (1 - current_evolution_factor)². So
|
|
for instance destroying enemy spawners in the beginning of the game results in
|
|
increase of evolution factor by 0.002 (0.2%) while doing this when the evolution
|
|
factor is 0.5 the increase is only 0.0005 (0.05%).
|
|
|
|
This also means that the evolution factor approaches 1 asymptotically - generally,
|
|
increases past 0.9 or so are very slow and the number never actually reaches 1.0.
|
|
--]]
|
|
|
|
-- constants
|
|
|
|
local SETTINGS_TO_PERCENT = 1e-7
|
|
local SHORT_EVOLUTION_CHECK_DURATION = 5 * 60 * 60
|
|
local LONG_EVOLUTION_CHECK_DURATION = 30 * 60 * 60
|
|
local LONG_LONG_EVOLUTION_CHECK_DURATION = 60 * 60 * 60
|
|
|
|
|
|
local LOW_VALUE_PLAYER_STRUCTURES = {
|
|
"turret",
|
|
"ammo-turret",
|
|
"electric-turret",
|
|
"fluid-turret",
|
|
"artillery-turret",
|
|
"electric-pole"
|
|
}
|
|
|
|
local MEDIUM_VALUE_PLAYER_STRUCTURES = {
|
|
"solar-panel",
|
|
"accumulator",
|
|
"radar",
|
|
"storage-tank",
|
|
"container",
|
|
"logistic-container",
|
|
"lab"
|
|
}
|
|
|
|
local HIGH_VALUE_PLAYER_STRUCTURES = {
|
|
"assembling-machine",
|
|
"furnace",
|
|
"roboport",
|
|
"beacon",
|
|
"boiler",
|
|
"generator",
|
|
"mining-drill",
|
|
"reactor",
|
|
"rocket-silo"
|
|
}
|
|
|
|
-- imported functions
|
|
|
|
local sFind = string.find
|
|
local mMin = math.min
|
|
local mMax = math.max
|
|
local roundTo = gui.roundTo
|
|
local calculateDisplayValue = gui.calculateDisplayValue
|
|
|
|
-- local references
|
|
|
|
local world
|
|
|
|
-- module code
|
|
|
|
local function linearInterpolation(percent, min, max)
|
|
return ((max - min) * percent) + min
|
|
end
|
|
|
|
local function variableInterpolation(percent, min, max, exponent)
|
|
return ((max - min) * (percent^exponent)) + min
|
|
end
|
|
|
|
local function onStatsGrabPollution()
|
|
local pollutionStats = game.pollution_statistics
|
|
local counts = pollutionStats.output_counts
|
|
|
|
for name,count in pairs(counts) do
|
|
local previousCount = world.pollutionConsumed[name]
|
|
world.pollutionConsumed[name] = count
|
|
local delta
|
|
if not previousCount then
|
|
delta = count
|
|
else
|
|
delta = count - previousCount
|
|
end
|
|
|
|
if delta ~= 0 then
|
|
world.pollutionDeltas[name] = (world.pollutionDeltas[name] or 0) + delta
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onStatsGrabTotalPollution()
|
|
if world.evolutionPerPollution ~= 0 then
|
|
local pollutionStats = game.pollution_statistics
|
|
local counts = pollutionStats.input_counts
|
|
|
|
for name,count in pairs(counts) do
|
|
local previousCount = world.pollutionProduced[name]
|
|
world.pollutionProduced[name] = count
|
|
local delta
|
|
if not previousCount then
|
|
delta = count
|
|
else
|
|
delta = count - previousCount
|
|
end
|
|
|
|
if delta ~= 0 then
|
|
world.pollutionDeltas["totalPollution"] = (world.pollutionDeltas["totalPollution"] or 0) + delta
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onStatsGrabKill()
|
|
local killStats = game.forces.enemy.kill_count_statistics
|
|
|
|
local counts = killStats.output_counts
|
|
for name,count in pairs(counts) do
|
|
local previousCount = world.kills[name]
|
|
world.kills[name] = count
|
|
local delta
|
|
if not previousCount then
|
|
delta = count
|
|
else
|
|
delta = count - previousCount
|
|
end
|
|
|
|
if delta ~= 0 then
|
|
world.killDeltas[name] = (world.killDeltas[name] or 0) + delta
|
|
end
|
|
end
|
|
|
|
counts = killStats.input_counts
|
|
for name,count in pairs(counts) do
|
|
local previousCount = world.kills[name]
|
|
world.kills[name] = count
|
|
local delta
|
|
if not previousCount then
|
|
delta = count
|
|
else
|
|
delta = count - previousCount
|
|
end
|
|
|
|
if delta ~= 0 then
|
|
world.killDeltas[name] = (world.killDeltas[name] or 0) + delta
|
|
end
|
|
end
|
|
end
|
|
|
|
local function reset()
|
|
world.killDeltasIterator = nil
|
|
world.pollutionDeltasIterator = nil
|
|
world.kills = {}
|
|
world.killDeltas = {
|
|
["time"] = math.floor(game.tick / 60)
|
|
}
|
|
world.totalEvolution = 0
|
|
world.researchCompleted = 0
|
|
world.totalResearch = 0
|
|
world.ticksAccrued = 0
|
|
world.researchEvolutionMultiplier = 0
|
|
world.tickEvolutionMultiplier = 0
|
|
world.totalPostiveEvolution = 0
|
|
world.totalNegativeEvolution = 0
|
|
world.pollutionConsumed = {}
|
|
world.pollutionProduced = {}
|
|
world.pollutionDeltas = {}
|
|
world.stats = {
|
|
["tile"] = 0,
|
|
["tree"] = 0,
|
|
["dyingTree"] = 0,
|
|
["absorbed"] = 0,
|
|
["spawner"] = 0,
|
|
["hive"] = 0,
|
|
["unit"] = 0,
|
|
["worm"] = 0,
|
|
["totalPollution"] = 0,
|
|
["time"] = 0,
|
|
["evolutionMultiplier"] = 0,
|
|
["minimumEvolution"] = 0,
|
|
["researchEvolutionCap"] = 0,
|
|
["lowPlayer"] = 0,
|
|
["mediumPlayer"] = 0,
|
|
["highPlayer"] = 0
|
|
}
|
|
end
|
|
|
|
local function onModSettingsChange(event)
|
|
|
|
if event and (string.sub(event.setting, 1, #"rampant-evolution") ~= "rampant-evolution") then
|
|
return false
|
|
end
|
|
|
|
world.processingPerTick = settings.global["rampant-evolution--processingPerTick"].value
|
|
|
|
world.evolutionPerSpawnerAbsorbed = settings.global["rampant-evolution-evolutionPerSpawnerAbsorbed"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerTreeAbsorbed = settings.global["rampant-evolution-evolutionPerTreeAbsorbed"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerTreeDied = settings.global["rampant-evolution-evolutionPerTreeDied"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerTileAbsorbed = settings.global["rampant-evolution-evolutionPerTileAbsorbed"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerSpawnerKilled = settings.global["rampant-evolution-evolutionPerSpawnerKilled"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerUnitKilled = settings.global["rampant-evolution-evolutionPerUnitKilled"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerHiveKilled = settings.global["rampant-evolution-evolutionPerHiveKilled"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerWormKilled = settings.global["rampant-evolution--evolutionPerWormKilled"].value * SETTINGS_TO_PERCENT
|
|
|
|
world.evolutionPerTime = settings.global["rampant-evolution--evolutionPerTime"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerPollution = settings.global["rampant-evolution--evolutionPerPollution"].value * SETTINGS_TO_PERCENT
|
|
|
|
world.displayEvolutionMsg = settings.global["rampant-evolution--displayEvolutionMsg"].value
|
|
world.displayEvolutionMsgInterval = math.ceil(settings.global["rampant-evolution--displayEvolutionMsgInterval"].value * (60 * 60))
|
|
|
|
world.minimumDevolutionPercentage = settings.global["rampant-evolution--minimumDevolutionPercentage"].value
|
|
|
|
world.evolutionResolutionLevel = settings.global["rampant-evolution--evolutionResolutionLevel"].value
|
|
|
|
world.spawnerLookup = {}
|
|
world.hiveLookup = {}
|
|
world.wormLookup = {}
|
|
world.unitLookup = {}
|
|
|
|
if settings.global["rampant-evolution--recalculateAllEvolution"].value then
|
|
reset()
|
|
game.forces.enemy.evolution_factor = 0
|
|
game.print({"description.rampant-evolution--refreshingEvolution"})
|
|
end
|
|
|
|
for entityName, entityPrototype in pairs(game.entity_prototypes) do
|
|
if (entityPrototype.type == "unit-spawner") and sFind(entityName, "-spawner") then
|
|
world.spawnerLookup[entityName] = 1
|
|
elseif (entityPrototype.type == "unit-spawner") and sFind(entityName, "-hive") then
|
|
world.hiveLookup[entityName] = 1
|
|
elseif (entityPrototype.type == "turret") and sFind(entityName, "-worm") then
|
|
world.wormLookup[entityName] = 1
|
|
elseif (entityPrototype.type == "unit") and (sFind(entityName, "biter") or sFind(entityName, "spitter")) then
|
|
world.unitLookup[entityName] = 1
|
|
end
|
|
end
|
|
|
|
world.enabledResearchEvolutionCap = settings.global["rampant-evolution--researchEvolutionCap"].value
|
|
world.researchLookup = {}
|
|
world.researchTotals = {}
|
|
world.researchCurrent = {}
|
|
|
|
local sciencePackWeightLookup = {
|
|
[1] = settings.global["rampant-evolution--technology-automation-science-multiplier"].value,
|
|
[2] = settings.global["rampant-evolution--technology-logistic-science-multiplier"].value,
|
|
[3] = settings.global["rampant-evolution--technology-military-science-multiplier"].value,
|
|
[4] = settings.global["rampant-evolution--technology-chemical-science-multiplier"].value,
|
|
[5] = settings.global["rampant-evolution--technology-production-science-multiplier"].value,
|
|
[6] = settings.global["rampant-evolution--technology-utility-science-multiplier"].value,
|
|
[7] = settings.global["rampant-evolution--technology-space-science-multiplier"].value
|
|
}
|
|
local sciencePackOrder = {
|
|
"automation-science-pack",
|
|
"logistic-science-pack",
|
|
"military-science-pack",
|
|
"chemical-science-pack",
|
|
"production-science-pack",
|
|
"utility-science-pack",
|
|
"space-science-pack"
|
|
}
|
|
local sciencePackOrderLookup = {}
|
|
for index, science in pairs(sciencePackOrder) do
|
|
sciencePackOrderLookup[science] = index
|
|
world.researchTotals[index] = 0
|
|
world.researchCurrent[index] = 0
|
|
end
|
|
|
|
local totalTechnology = 0
|
|
local includeUpgrades = settings.global["rampant-evolution--researchEvolutionCapIncludeUpgrades"].value
|
|
|
|
for technologyName, technologyPrototype in pairs(game.technology_prototypes) do
|
|
local highestOrder = -1
|
|
|
|
if not technologyPrototype.research_unit_count_formula
|
|
and technologyPrototype.enabled
|
|
and not technologyPrototype.hidden
|
|
and ((not technologyPrototype.upgrade) or (technologyPrototype.upgrade and includeUpgrades))
|
|
then
|
|
for _, ingredient in pairs(technologyPrototype.research_unit_ingredients) do
|
|
if sciencePackOrderLookup[ingredient.name] then
|
|
if highestOrder < sciencePackOrderLookup[ingredient.name] then
|
|
highestOrder = sciencePackOrderLookup[ingredient.name]
|
|
end
|
|
end
|
|
end
|
|
if (highestOrder ~= -1) then
|
|
local weight = sciencePackWeightLookup[highestOrder]
|
|
totalTechnology = totalTechnology + weight
|
|
world.researchTotals[highestOrder] = (world.researchTotals[highestOrder] or 0) + weight
|
|
world.researchLookup[technologyName] = {weight, highestOrder}
|
|
world.totalResearch = world.totalResearch + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
for tech, value in pairs(world.researchLookup) do
|
|
world.researchLookup[tech][1] = value[1] / totalTechnology
|
|
end
|
|
for scienceIndex=1,#sciencePackOrder do
|
|
world.researchTotals[scienceIndex] = world.researchTotals[scienceIndex] / totalTechnology
|
|
end
|
|
|
|
for technologyName, technology in pairs(game.forces.player.technologies) do
|
|
if technology.researched then
|
|
local evolutionIncrease = world.researchLookup[technologyName]
|
|
if evolutionIncrease then
|
|
world.researchCompleted = world.researchCompleted + 1
|
|
if world.enabledResearchEvolutionCap then
|
|
world.researchCurrent[evolutionIncrease[2]] = world.researchCurrent[evolutionIncrease[2]] + evolutionIncrease[1]
|
|
world.stats["researchEvolutionCap"] = world.stats["researchEvolutionCap"] + evolutionIncrease[1]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if not world.enabledResearchEvolutionCap then
|
|
world.stats["researchEvolutionCap"] = 0.9999999999999
|
|
end
|
|
|
|
world.toggleResearchEvolutionMultiplier = settings.global["rampant-evolution--toggleResearchEvolutionMultiplier"].value
|
|
world.startResearchEvolutionMultiplier = settings.global["rampant-evolution--startResearchMultiplier"].value
|
|
world.endResearchEvolutionMultiplier = settings.global["rampant-evolution--endResearchMultiplier"].value
|
|
world.researchMultiplierExponent = settings.global["rampant-evolution--researchMultiplierExponent"].value
|
|
|
|
world.toggleTickEvolutionMultiplier = settings.global["rampant-evolution--toggleTickEvolutionMultiplier"].value
|
|
world.startTickEvolutionMultiplier = settings.global["rampant-evolution--startTickMultiplier"].value
|
|
world.endTickEvolutionMultiplier = settings.global["rampant-evolution--endTickMultiplier"].value
|
|
world.tickMultiplierExponent = settings.global["rampant-evolution--tickMultiplierExponent"].value
|
|
|
|
if world.toggleResearchEvolutionMultiplier then
|
|
world.researchEvolutionMultiplier = variableInterpolation(
|
|
(world.researchCompleted / world.totalResearch),
|
|
world.startResearchEvolutionMultiplier,
|
|
world.endResearchEvolutionMultiplier,
|
|
world.researchMultiplierExponent
|
|
)
|
|
|
|
world.stats.evolutionMultiplier = world.researchEvolutionMultiplier + world.tickEvolutionMultiplier
|
|
end
|
|
|
|
world.totalTicksAccruable = settings.global["rampant-evolution--totalTickMultiplier"].value * (60 * 60)
|
|
|
|
world.evolutionPerLowPlayer = settings.global["rampant-evolution--evolutionPerLowPlayer"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerMediumPlayer = settings.global["rampant-evolution--evolutionPerMediumPlayer"].value * SETTINGS_TO_PERCENT
|
|
world.evolutionPerHighPlayer = settings.global["rampant-evolution--evolutionPerHighPlayer"].value * SETTINGS_TO_PERCENT
|
|
|
|
local structureTypeLookup = {}
|
|
for _, structure in pairs(LOW_VALUE_PLAYER_STRUCTURES) do
|
|
structureTypeLookup[structure] = {world.evolutionPerLowPlayer, "lowPlayer"}
|
|
end
|
|
for _, structure in pairs(MEDIUM_VALUE_PLAYER_STRUCTURES) do
|
|
structureTypeLookup[structure] = {world.evolutionPerMediumPlayer, "mediumPlayer"}
|
|
end
|
|
for _, structure in pairs(HIGH_VALUE_PLAYER_STRUCTURES) do
|
|
structureTypeLookup[structure] = {world.evolutionPerHighPlayer, "highPlayer"}
|
|
end
|
|
|
|
world.playerStructureLookup = {}
|
|
for entityName, entityPrototype in pairs(game.entity_prototypes) do
|
|
local structureType = structureTypeLookup[entityPrototype.type]
|
|
if structureType then
|
|
world.playerStructureLookup[entityName] = structureType
|
|
end
|
|
end
|
|
|
|
if settings.global["rampant-evolution--setMapSettingsToZero"].value then
|
|
game.map_settings.enemy_evolution.enabled = false
|
|
else
|
|
game.map_settings.enemy_evolution.enabled = true
|
|
end
|
|
|
|
for playerIndex in pairs(world.playerGuiTick) do
|
|
world.playerGuiTick[playerIndex] = nil
|
|
end
|
|
|
|
onStatsGrabPollution()
|
|
onStatsGrabKill()
|
|
onStatsGrabTotalPollution()
|
|
|
|
if not settings.global["rampant-evolution--recalculateAllEvolution"].value then
|
|
world.killDeltasIterator = nil
|
|
world.pollutionDeltasIterator = nil
|
|
world.killDeltas = {}
|
|
world.pollutionDeltas = {}
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function onConfigChanged()
|
|
if not world.version or world.version < 11 then
|
|
world.version = 11
|
|
|
|
reset()
|
|
|
|
world.tickModAdded = game.tick
|
|
|
|
world.playerGuiOpen = {}
|
|
world.playerGuiTick = {}
|
|
|
|
world.lastChangeShortTick = 0
|
|
world.lastChangeShortEvolution = 0
|
|
world.lastChangeShort = 0
|
|
world.lastChangeLongTick = 0
|
|
world.lastChangeLongEvolution = 0
|
|
world.lastChangeLong = 0
|
|
world.lastChangeLongLongTick = 0
|
|
world.lastChangeLongLongEvolution = 0
|
|
world.lastChangeLongLong = 0
|
|
world.playerIterator = nil
|
|
|
|
onModSettingsChange()
|
|
|
|
game.print("Rampant Evolution - Version 1.6.4")
|
|
end
|
|
end
|
|
|
|
local function calculateEvolution(evo, evolutionModifier, stats, statField, runsRemaining)
|
|
if world.toggleTickEvolutionMultiplier
|
|
and (statField == "time")
|
|
then
|
|
world.ticksAccrued = world.ticksAccrued + (60 * runsRemaining)
|
|
|
|
world.tickEvolutionMultiplier = variableInterpolation(
|
|
world.ticksAccrued / world.totalTicksAccruable,
|
|
world.startTickEvolutionMultiplier,
|
|
world.endTickEvolutionMultiplier,
|
|
world.tickMultiplierExponent
|
|
)
|
|
|
|
world.stats.evolutionMultiplier = world.researchEvolutionMultiplier + world.tickEvolutionMultiplier
|
|
end
|
|
|
|
if (evolutionModifier ~= 0) then
|
|
local totalEvolution = world.totalEvolution
|
|
local totalPostiveEvolution = world.totalPostiveEvolution
|
|
local totalNegativeEvolution = world.totalNegativeEvolution
|
|
local minimumEvolution = stats.minimumEvolution
|
|
local evolutionMultiplier = 1 + stats.evolutionMultiplier
|
|
local maximumEvolution = mMin(stats.researchEvolutionCap, 0.9999999999999)
|
|
local minimumTotalEvolution = mMax(minimumEvolution / (1 - minimumEvolution), 0)
|
|
local maximumTotalEvolution = maximumEvolution / (1 - maximumEvolution)
|
|
local process = true
|
|
|
|
while (runsRemaining > 0) and process do
|
|
runsRemaining = runsRemaining - 1
|
|
local contribution = (((1 - evo)^2) * evolutionModifier)
|
|
local adjustedEvo = totalEvolution + (contribution * evolutionMultiplier)
|
|
if adjustedEvo <= minimumTotalEvolution then
|
|
contribution = minimumTotalEvolution - totalEvolution
|
|
process = false
|
|
elseif adjustedEvo >= maximumTotalEvolution then
|
|
contribution = maximumTotalEvolution - totalEvolution
|
|
process = false
|
|
end
|
|
|
|
if contribution > 0 then
|
|
if process then
|
|
contribution = contribution * evolutionMultiplier
|
|
end
|
|
totalPostiveEvolution = totalPostiveEvolution + contribution
|
|
else
|
|
totalNegativeEvolution = totalNegativeEvolution + contribution
|
|
end
|
|
|
|
totalEvolution = totalEvolution + contribution
|
|
evo = totalEvolution / (1+totalEvolution)
|
|
stats[statField] = stats[statField] + contribution
|
|
end
|
|
local newMinimumEvolution = world.minimumDevolutionPercentage * evo
|
|
if newMinimumEvolution > minimumEvolution then
|
|
stats.minimumEvolution = newMinimumEvolution
|
|
end
|
|
world.totalNegativeEvolution = totalNegativeEvolution
|
|
world.totalPostiveEvolution = totalPostiveEvolution
|
|
world.totalEvolution = totalEvolution
|
|
end
|
|
return evo
|
|
end
|
|
|
|
local function processKill(evo, initialRunsRemaining)
|
|
local name = world.killDeltasIterator
|
|
local count
|
|
if not name then
|
|
name,count = next(world.killDeltas, nil)
|
|
else
|
|
count = world.killDeltas[name]
|
|
end
|
|
if not name then
|
|
return evo
|
|
end
|
|
world.killDeltasIterator = next(world.killDeltas, name)
|
|
local runsRemaining = math.min(initialRunsRemaining, count)
|
|
count = count - runsRemaining
|
|
if count <= 0 then
|
|
world.killDeltas[name] = nil
|
|
else
|
|
world.killDeltas[name] = count
|
|
end
|
|
|
|
local evolutionModifier = 0
|
|
local statField
|
|
|
|
if name == "time" then
|
|
evolutionModifier = world.evolutionPerTime
|
|
statField = "time"
|
|
elseif world.spawnerLookup[name] then
|
|
evolutionModifier = world.evolutionPerSpawnerKilled
|
|
statField = "spawner"
|
|
elseif world.hiveLookup[name] then
|
|
evolutionModifier = world.evolutionPerHiveKilled
|
|
statField = "hive"
|
|
elseif world.wormLookup[name] then
|
|
evolutionModifier = world.evolutionPerWormKilled
|
|
statField = "worm"
|
|
elseif world.unitLookup[name] then
|
|
evolutionModifier = world.evolutionPerUnitKilled
|
|
statField = "unit"
|
|
elseif world.playerStructureLookup[name] then
|
|
local evolutionDeltaPair = world.playerStructureLookup[name]
|
|
if evolutionDeltaPair and evolutionDeltaPair[1] ~= 0 then
|
|
evolutionModifier = evolutionDeltaPair[1]
|
|
statField = evolutionDeltaPair[2]
|
|
end
|
|
end
|
|
|
|
return calculateEvolution(
|
|
evo,
|
|
evolutionModifier,
|
|
world.stats,
|
|
statField,
|
|
runsRemaining
|
|
)
|
|
end
|
|
|
|
local function processPollution(evo, initialRunsRemaining)
|
|
local name = world.pollutionDeltasIterator
|
|
local count
|
|
if not name then
|
|
name,count = next(world.pollutionDeltas, nil)
|
|
else
|
|
count = world.pollutionDeltas[name]
|
|
end
|
|
if not name then
|
|
return evo
|
|
end
|
|
world.pollutionDeltasIterator = next(world.pollutionDeltas, name)
|
|
local runsRemaining = math.min(initialRunsRemaining, count)
|
|
count = count - runsRemaining
|
|
if count <= 0 then
|
|
world.pollutionDeltas[name] = nil
|
|
else
|
|
world.pollutionDeltas[name] = count
|
|
end
|
|
|
|
local evolutionModifier = 0
|
|
local statField
|
|
|
|
if (name == "tile-proxy") then
|
|
evolutionModifier = world.evolutionPerTileAbsorbed
|
|
statField = "tile"
|
|
elseif (name == "tree-proxy") then
|
|
evolutionModifier = world.evolutionPerTreeAbsorbed
|
|
statField = "tree"
|
|
elseif (name == "tree-dying-proxy") then
|
|
evolutionModifier = world.evolutionPerTreeDied
|
|
statField = "dyingTree"
|
|
elseif (name == "totalPollution") then
|
|
evolutionModifier = world.evolutionPerPollution
|
|
statField = "totalPollution"
|
|
elseif world.spawnerLookup[name] then
|
|
evolutionModifier = world.evolutionPerSpawnerAbsorbed
|
|
statField = "absorbed"
|
|
end
|
|
|
|
return calculateEvolution(
|
|
evo,
|
|
evolutionModifier,
|
|
world.stats,
|
|
statField,
|
|
runsRemaining
|
|
)
|
|
end
|
|
|
|
local function printEvolutionMsg()
|
|
local enemy = game.forces.enemy
|
|
local stats = world.stats
|
|
local enemyEvo = enemy.evolution_factor
|
|
game.print({
|
|
"description.rampant-evolution--displayEvolutionMsg",
|
|
roundTo(enemyEvo*100,0.001),
|
|
roundTo(calculateDisplayValue(stats["tile"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["tree"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["dyingTree"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["absorbed"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["spawner"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["hive"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["unit"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["worm"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["totalPollution"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["time"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["lowPlayer"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["mediumPlayer"], world, enemyEvo)*100, 0.001),
|
|
roundTo(calculateDisplayValue(stats["highPlayer"], world, enemyEvo)*100, 0.001),
|
|
roundTo(stats["evolutionMultiplier"]*100, 0.001),
|
|
roundTo(stats["minimumEvolution"]*100, 0.001),
|
|
roundTo(stats["researchEvolutionCap"]*100, 0.001),
|
|
roundTo(world.lastChangeShort*100, 0.001),
|
|
roundTo(world.lastChangeLong*100, 0.001),
|
|
roundTo(world.lastChangeLongLong*100, 0.001)
|
|
})
|
|
end
|
|
|
|
local function processing(resolutionLevel, evo)
|
|
return processKill(
|
|
processPollution(
|
|
evo,
|
|
resolutionLevel
|
|
),
|
|
resolutionLevel
|
|
)
|
|
end
|
|
|
|
local function onProcessingWrapper(event)
|
|
local enemy = game.forces.enemy
|
|
local tick = event.tick
|
|
local resolutionLevel = world.evolutionResolutionLevel
|
|
if resolutionLevel == 0 then
|
|
local x = tick / 43200000 -- (60 * 60 * 60 * 200)
|
|
resolutionLevel = linearInterpolation(
|
|
mMin(x, 1),
|
|
20,
|
|
4000
|
|
)
|
|
end
|
|
|
|
local evo = enemy.evolution_factor
|
|
for _ = 1, world.processingPerTick do
|
|
evo = processing(resolutionLevel, evo)
|
|
end
|
|
enemy.evolution_factor = evo
|
|
|
|
if (tick % 60) == 0 then
|
|
world.killDeltas["time"] = (world.killDeltas["time"] or 0) + 1
|
|
end
|
|
|
|
if (tick - world.lastChangeShortTick) >= SHORT_EVOLUTION_CHECK_DURATION then
|
|
world.lastChangeShortTick = tick
|
|
world.lastChangeShort = evo - world.lastChangeShortEvolution
|
|
world.lastChangeShortEvolution = evo
|
|
end
|
|
|
|
if (tick - world.lastChangeLongTick) >= LONG_EVOLUTION_CHECK_DURATION then
|
|
world.lastChangeLongTick = tick
|
|
world.lastChangeLong = evo - world.lastChangeLongEvolution
|
|
world.lastChangeLongEvolution = evo
|
|
end
|
|
|
|
if (tick - world.lastChangeLongLongTick) >= LONG_LONG_EVOLUTION_CHECK_DURATION then
|
|
world.lastChangeLongLongTick = tick
|
|
world.lastChangeLongLong = evo - world.lastChangeLongLongEvolution
|
|
world.lastChangeLongLongEvolution = evo
|
|
end
|
|
|
|
if world.displayEvolutionMsg and ((tick % world.displayEvolutionMsgInterval) == 0) then
|
|
printEvolutionMsg()
|
|
end
|
|
|
|
local playerId = world.playerIterator
|
|
if not playerId then
|
|
world.playerIterator = next(game.connected_players, world.playerIterator)
|
|
else
|
|
world.playerIterator = next(game.connected_players, playerId)
|
|
gui.update(world, playerId, tick)
|
|
end
|
|
end
|
|
|
|
local function onInit()
|
|
global.world = {}
|
|
|
|
world = global.world
|
|
|
|
onConfigChanged()
|
|
end
|
|
|
|
local function onLoad()
|
|
world = global.world
|
|
end
|
|
|
|
local function onLuaShortcut(event)
|
|
if event.prototype_name == "rampant-evolution--info" then
|
|
local playerIndex = event.player_index
|
|
local guiPanel = world.playerGuiOpen[playerIndex]
|
|
if not guiPanel then
|
|
world.playerGuiOpen[playerIndex] = gui.create(game.players[playerIndex], world)
|
|
else
|
|
gui.close(world, event.player_index)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onPlayerRemoved(event)
|
|
world.playerIterator = nil
|
|
end
|
|
|
|
local function onResearchCompleted(event)
|
|
if not world.researchLookup then
|
|
return
|
|
end
|
|
local research = event.research
|
|
local technologyName = research.name
|
|
local evolutionIncrease = world.researchLookup[technologyName]
|
|
if world.toggleResearchEvolutionMultiplier
|
|
and evolutionIncrease
|
|
and research.force.name == "player"
|
|
then
|
|
world.researchCompleted = world.researchCompleted + 1
|
|
world.researchEvolutionMultiplier = variableInterpolation(
|
|
(world.researchCompleted / world.totalResearch),
|
|
world.startResearchEvolutionMultiplier,
|
|
world.endResearchEvolutionMultiplier,
|
|
world.researchMultiplierExponent
|
|
)
|
|
|
|
world.stats.evolutionMultiplier = world.researchEvolutionMultiplier + world.tickEvolutionMultiplier
|
|
|
|
if world.enabledResearchEvolutionCap then
|
|
world.researchCurrent[evolutionIncrease[2]] = world.researchCurrent[evolutionIncrease[2]] + evolutionIncrease[1]
|
|
world.stats["researchEvolutionCap"] = world.stats["researchEvolutionCap"] + evolutionIncrease[1]
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onResearchUncompleted(event)
|
|
if not world.researchLookup then
|
|
return
|
|
end
|
|
local research = event.research
|
|
local technologyName = research.name
|
|
local evolutionIncrease = world.researchLookup[technologyName]
|
|
if world.toggleResearchEvolutionMultiplier
|
|
and evolutionIncrease
|
|
and research.force.name == "player"
|
|
then
|
|
world.researchCompleted = world.researchCompleted - 1
|
|
world.researchEvolutionMultiplier = variableInterpolation(
|
|
(world.researchCompleted / world.totalResearch),
|
|
world.startResearchEvolutionMultiplier,
|
|
world.endResearchEvolutionMultiplier,
|
|
world.researchMultiplierExponent
|
|
)
|
|
|
|
world.stats.evolutionMultiplier = world.researchEvolutionMultiplier + world.tickEvolutionMultiplier
|
|
|
|
if world.enabledResearchEvolutionCap then
|
|
world.researchCurrent[evolutionIncrease[2]] = world.researchCurrent[evolutionIncrease[2]] + evolutionIncrease[1]
|
|
world.stats["researchEvolutionCap"] = world.stats["researchEvolutionCap"] - evolutionIncrease[1]
|
|
end
|
|
end
|
|
end
|
|
|
|
-- hooks
|
|
|
|
script.on_event(
|
|
{
|
|
defines.events.on_player_left_game,
|
|
defines.events.on_player_kicked,
|
|
defines.events.on_player_removed,
|
|
defines.events.on_player_banned
|
|
},
|
|
onPlayerRemoved)
|
|
script.on_event(defines.events.on_lua_shortcut, onLuaShortcut)
|
|
script.on_nth_tick((2*60*60)+0, onStatsGrabPollution)
|
|
script.on_nth_tick((2*60*60)+1, onStatsGrabKill)
|
|
script.on_nth_tick((2*60*60)+2, onStatsGrabTotalPollution)
|
|
script.on_event(defines.events.on_tick, onProcessingWrapper)
|
|
script.on_event(defines.events.on_research_finished, onResearchCompleted)
|
|
script.on_event(defines.events.on_research_reversed, onResearchUncompleted)
|
|
|
|
script.on_init(onInit)
|
|
script.on_load(onLoad)
|
|
script.on_event(defines.events.on_runtime_mod_setting_changed, onModSettingsChange)
|
|
script.on_configuration_changed(onConfigChanged)
|
|
|
|
-- commands
|
|
|
|
local function rampantEvolution(event)
|
|
printEvolutionMsg()
|
|
end
|
|
|
|
commands.add_command('rampantEvolution', "", rampantEvolution)
|