2017-05-15 02:09:43 +02:00
|
|
|
local upgrade = {}
|
|
|
|
|
|
|
|
-- imports
|
|
|
|
|
|
|
|
local constants = require("libs/Constants")
|
2021-11-26 19:52:30 +02:00
|
|
|
local mathUtils = require("libs/MathUtils")
|
2017-05-15 02:09:43 +02:00
|
|
|
|
|
|
|
-- constants
|
|
|
|
|
2021-02-20 07:41:30 +02:00
|
|
|
local DEFINES_COMMAND_GROUP = defines.command.group
|
|
|
|
local DEFINES_COMMAND_WANDER = defines.command.wander
|
|
|
|
local DEFINES_COMMAND_BUILD_BASE = defines.command.build_base
|
|
|
|
local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area
|
|
|
|
local DEFINES_COMMAND_GO_TO_LOCATION = defines.command.go_to_location
|
|
|
|
local DEFINES_COMMMAD_COMPOUND = defines.command.compound
|
|
|
|
local DEFINES_COMMAND_FLEE = defines.command.flee
|
|
|
|
local DEFINES_COMMAND_STOP = defines.command.stop
|
|
|
|
|
|
|
|
local DEFINES_COMPOUND_COMMAND_RETURN_LAST = defines.compound_command.return_last
|
|
|
|
|
|
|
|
local DEFINES_DISTRACTION_NONE = defines.distraction.none
|
|
|
|
local DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
|
|
|
|
local DEFINES_DISTRACTION_BY_ANYTHING = defines.distraction.by_anything
|
|
|
|
|
2018-02-14 10:28:42 +02:00
|
|
|
local CHUNK_SIZE = constants.CHUNK_SIZE
|
2021-02-20 07:41:30 +02:00
|
|
|
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
|
2017-05-15 02:09:43 +02:00
|
|
|
|
|
|
|
-- imported functions
|
|
|
|
|
2021-11-26 19:52:30 +02:00
|
|
|
local euclideanDistancePoints = mathUtils.euclideanDistancePoints
|
|
|
|
|
2017-05-15 02:09:43 +02:00
|
|
|
-- module code
|
|
|
|
|
2021-02-20 07:41:30 +02:00
|
|
|
local function addCommandSet(queriesAndCommands)
|
|
|
|
-- preallocating memory to be used in code, making it fast by reducing garbage generated.
|
|
|
|
queriesAndCommands.neighbors = {
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
queriesAndCommands.cardinalNeighbors = {
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
queriesAndCommands.position = {
|
|
|
|
x=0,
|
|
|
|
y=0
|
|
|
|
}
|
|
|
|
queriesAndCommands.position2 = {
|
|
|
|
x=0,
|
|
|
|
y=0
|
|
|
|
}
|
|
|
|
queriesAndCommands.position3 = {
|
|
|
|
x=0,
|
|
|
|
y=0
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.chunkOverlapArray = {
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1,
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.position2Top = {0, 0}
|
|
|
|
queriesAndCommands.position2Bottom = {0, 0}
|
|
|
|
--this is shared between two different queries
|
|
|
|
queriesAndCommands.area = {
|
|
|
|
{0, 0},
|
|
|
|
{0, 0}
|
|
|
|
}
|
|
|
|
queriesAndCommands.area2 = {
|
|
|
|
queriesAndCommands.position2Top,
|
|
|
|
queriesAndCommands.position2Bottom
|
|
|
|
}
|
|
|
|
queriesAndCommands.buildPositionTop = {0, 0}
|
|
|
|
queriesAndCommands.buildPositionBottom = {0, 0}
|
|
|
|
queriesAndCommands.buildArea = {
|
|
|
|
queriesAndCommands.buildPositionTop,
|
|
|
|
queriesAndCommands.buildPositionBottom
|
|
|
|
}
|
|
|
|
queriesAndCommands.countResourcesQuery = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
type="resource"
|
|
|
|
}
|
|
|
|
queriesAndCommands.filteredEntitiesUnitQuery = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force="enemy",
|
|
|
|
type="unit"
|
|
|
|
}
|
|
|
|
queriesAndCommands.hasPlayerStructuresQuery = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force={"enemy","neutral"},
|
|
|
|
invert=true,
|
|
|
|
limit=1
|
|
|
|
}
|
|
|
|
queriesAndCommands.filteredEntitiesEnemyStructureQuery = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force="enemy",
|
|
|
|
type={
|
|
|
|
"turret",
|
|
|
|
"unit-spawner"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
queriesAndCommands.filteredEntitiesPointQueryLimited = {
|
|
|
|
position = queriesAndCommands.position,
|
|
|
|
radius = 10,
|
|
|
|
limit = 1,
|
|
|
|
force = "enemy",
|
|
|
|
type = {
|
|
|
|
"unit-spawner",
|
|
|
|
"turret"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
queriesAndCommands.createBuildCloudQuery = {
|
|
|
|
name = "build-clear-cloud-rampant",
|
|
|
|
position = queriesAndCommands.position
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.activePlayerForces = {"player"}
|
|
|
|
|
|
|
|
for _,force in pairs(game.forces) do
|
|
|
|
local add = true
|
|
|
|
|
|
|
|
if (force.name ~= "neutral") and (force.name ~= "enemy") then
|
|
|
|
for i=1,#queriesAndCommands.activePlayerForces do
|
|
|
|
if (queriesAndCommands.activePlayerForces[i] == force.name) then
|
|
|
|
add = false
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if add then
|
|
|
|
queriesAndCommands.activePlayerForces[#queriesAndCommands.activePlayerForces+1] = force.name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
queriesAndCommands.filteredEntitiesPlayerQueryLowest = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force=queriesAndCommands.activePlayerForces,
|
|
|
|
collision_mask = "player-layer",
|
|
|
|
type={
|
|
|
|
"wall",
|
|
|
|
"transport-belt"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.filteredEntitiesPlayerQueryLow = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force=queriesAndCommands.activePlayerForces,
|
|
|
|
collision_mask = "player-layer",
|
|
|
|
type={
|
|
|
|
"splitter",
|
|
|
|
"pump",
|
|
|
|
"offshore-pump",
|
|
|
|
"lamp",
|
|
|
|
"solar-panel",
|
|
|
|
"programmable-speaker",
|
|
|
|
"accumulator",
|
|
|
|
"assembling-machine",
|
|
|
|
"turret",
|
|
|
|
"ammo-turret"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.filteredEntitiesPlayerQueryHigh = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force=queriesAndCommands.activePlayerForces,
|
|
|
|
collision_mask = "player-layer",
|
|
|
|
type={
|
|
|
|
"furnace",
|
|
|
|
"lab",
|
|
|
|
"roboport",
|
|
|
|
"beacon",
|
|
|
|
"radar",
|
|
|
|
"electric-turret",
|
|
|
|
"boiler",
|
|
|
|
"generator",
|
|
|
|
"fluid-turret",
|
|
|
|
"mining-drill"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.filteredEntitiesPlayerQueryHighest = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
force=queriesAndCommands.activePlayerForces,
|
|
|
|
collision_mask = "player-layer",
|
|
|
|
type={
|
|
|
|
"artillery-turret",
|
|
|
|
"reactor",
|
|
|
|
"rocket-silo"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.filteredEntitiesChunkNeutral = {
|
|
|
|
area=queriesAndCommands.area,
|
|
|
|
collision_mask = "player-layer",
|
|
|
|
type={
|
|
|
|
"tree",
|
|
|
|
"simple-entity"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
local sharedArea = {
|
|
|
|
{0,0},
|
|
|
|
{0,0}
|
|
|
|
}
|
|
|
|
queriesAndCommands.filteredEntitiesCliffQuery = {
|
|
|
|
area=sharedArea,
|
|
|
|
type="cliff",
|
|
|
|
limit = 1
|
|
|
|
}
|
|
|
|
queriesAndCommands.filteredTilesPathQuery = {
|
|
|
|
area=sharedArea,
|
|
|
|
collision_mask="water-tile",
|
|
|
|
limit = 1
|
|
|
|
}
|
|
|
|
queriesAndCommands.cliffQuery = {
|
|
|
|
area=queriesAndCommands.area2,
|
|
|
|
type="cliff"
|
|
|
|
}
|
|
|
|
queriesAndCommands.canPlaceQuery = {
|
|
|
|
name="",
|
|
|
|
position={0,0}
|
|
|
|
}
|
|
|
|
queriesAndCommands.filteredTilesQuery = {
|
|
|
|
collision_mask="water-tile",
|
|
|
|
area=queriesAndCommands.area
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.upgradeEntityQuery = {
|
|
|
|
name = "",
|
|
|
|
position = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.attackCommand = {
|
|
|
|
type = DEFINES_COMMAND_ATTACK_AREA,
|
|
|
|
destination = queriesAndCommands.position,
|
|
|
|
radius = CHUNK_SIZE * 1.5,
|
|
|
|
distraction = DEFINES_DISTRACTION_BY_ANYTHING
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.moveCommand = {
|
|
|
|
type = DEFINES_COMMAND_GO_TO_LOCATION,
|
|
|
|
destination = queriesAndCommands.position,
|
|
|
|
pathfind_flags = { cache = true },
|
|
|
|
distraction = DEFINES_DISTRACTION_BY_ENEMY
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.settleCommand = {
|
|
|
|
type = DEFINES_COMMAND_BUILD_BASE,
|
|
|
|
destination = queriesAndCommands.position,
|
|
|
|
distraction = DEFINES_DISTRACTION_BY_ENEMY,
|
|
|
|
ignore_planner = true
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.wonderCommand = {
|
|
|
|
type = DEFINES_COMMAND_WANDER,
|
|
|
|
wander_in_group = false,
|
|
|
|
radius = TRIPLE_CHUNK_SIZE*2,
|
|
|
|
ticks_to_wait = 36000
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.stopCommand = {
|
|
|
|
type = DEFINES_COMMAND_STOP
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.compoundSettleCommand = {
|
|
|
|
type = DEFINES_COMMMAD_COMPOUND,
|
|
|
|
structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST,
|
|
|
|
commands = {
|
|
|
|
queriesAndCommands.wonder2Command,
|
|
|
|
queriesAndCommands.settleCommand
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.retreatCommand = {
|
|
|
|
type = DEFINES_COMMAND_GROUP,
|
|
|
|
group = nil,
|
|
|
|
distraction = DEFINES_DISTRACTION_BY_ANYTHING,
|
|
|
|
use_group_distraction = true
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.fleeCommand = {
|
|
|
|
type = DEFINES_COMMAND_FLEE,
|
|
|
|
from = nil,
|
|
|
|
distraction = DEFINES_DISTRACTION_NONE
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.compoundRetreatGroupCommand = {
|
|
|
|
type = DEFINES_COMMMAD_COMPOUND,
|
|
|
|
structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST,
|
|
|
|
commands = {
|
|
|
|
queriesAndCommands.stopCommand,
|
|
|
|
queriesAndCommands.fleeCommand,
|
|
|
|
queriesAndCommands.retreatCommand
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.formGroupCommand = {
|
|
|
|
type = DEFINES_COMMAND_GROUP,
|
|
|
|
group = nil,
|
|
|
|
distraction = DEFINES_DISTRACTION_BY_ANYTHING,
|
|
|
|
use_group_distraction = false
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.formCommand = {
|
|
|
|
command = queriesAndCommands.formGroupCommand,
|
|
|
|
unit_count = 0,
|
|
|
|
unit_search_distance = TRIPLE_CHUNK_SIZE
|
|
|
|
}
|
|
|
|
|
|
|
|
queriesAndCommands.formRetreatCommand = {
|
|
|
|
command = queriesAndCommands.compoundRetreatGroupCommand,
|
|
|
|
unit_count = 1,
|
|
|
|
unit_search_distance = CHUNK_SIZE
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
function upgrade.attempt(universe)
|
2017-05-15 02:09:43 +02:00
|
|
|
local starting = global.version
|
2021-02-20 22:44:52 +02:00
|
|
|
if not global.version or global.version < 114 then
|
|
|
|
global.version = 114
|
|
|
|
|
|
|
|
if not universe then
|
|
|
|
universe = {}
|
|
|
|
global.universe = universe
|
|
|
|
end
|
2020-04-28 05:41:18 +02:00
|
|
|
game.forces.enemy.kill_all_units()
|
2017-05-19 09:47:24 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.safeEntities = {}
|
2019-02-03 08:01:28 +02:00
|
|
|
|
2021-02-20 22:44:52 +02:00
|
|
|
universe.aiPointsScaler = settings.global["rampant--aiPointsScaler"].value
|
2021-05-01 19:26:05 +02:00
|
|
|
|
2021-04-30 15:15:29 +02:00
|
|
|
universe.aiPointsPrintGainsToChat = settings.global["rampant--aiPointsPrintGainsToChat"].value
|
2021-05-01 05:36:34 +02:00
|
|
|
universe.aiPointsPrintSpendingToChat = settings.global["rampant--aiPointsPrintSpendingToChat"].value
|
2021-05-01 19:26:05 +02:00
|
|
|
|
2021-02-20 22:44:52 +02:00
|
|
|
universe.aiNocturnalMode = settings.global["rampant--permanentNocturnal"].value
|
2019-02-03 08:01:28 +02:00
|
|
|
|
2021-02-21 01:31:48 +02:00
|
|
|
universe.mapIterator = nil
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.retreatThreshold = 0
|
|
|
|
universe.rallyThreshold = 0
|
|
|
|
universe.formSquadThreshold = 0
|
|
|
|
universe.attackWaveSize = 0
|
|
|
|
universe.attackWaveDeviation = 0
|
|
|
|
universe.attackWaveUpperBound = 0
|
|
|
|
universe.unitRefundAmount = 0
|
|
|
|
universe.regroupIndex = 1
|
2021-02-20 22:44:52 +02:00
|
|
|
universe.randomGenerator = game.create_random_generator(settings.startup["rampant--enemySeed"].value+1024)
|
2019-10-14 07:49:52 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
game.map_settings.path_finder.min_steps_to_check_path_find_termination =
|
|
|
|
constants.PATH_FINDER_MIN_STEPS_TO_CHECK_PATH
|
2019-10-14 07:49:52 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.evolutionTableAlignment = {}
|
2019-10-14 07:49:52 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.kamikazeThreshold = 0
|
|
|
|
universe.attackWaveLowerBound = 1
|
2019-02-19 02:43:01 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.expansion = game.map_settings.enemy_expansion.enabled
|
|
|
|
universe.expansionMaxDistance = game.map_settings.enemy_expansion.max_expansion_distance * CHUNK_SIZE
|
|
|
|
universe.expansionMaxDistanceDerivation = universe.expansionMaxDistance * 0.33
|
|
|
|
universe.expansionMinTime = game.map_settings.enemy_expansion.min_expansion_cooldown
|
|
|
|
universe.expansionMaxTime = game.map_settings.enemy_expansion.max_expansion_cooldown
|
|
|
|
universe.expansionMinSize = game.map_settings.enemy_expansion.settler_group_min_size
|
|
|
|
universe.expansionMaxSize = game.map_settings.enemy_expansion.settler_group_max_size
|
2019-11-12 08:05:54 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.settlerCooldown = 0
|
|
|
|
universe.settlerWaveDeviation = 0
|
|
|
|
universe.settlerWaveSize = 0
|
2019-11-04 08:19:22 +02:00
|
|
|
|
2021-02-20 22:44:52 +02:00
|
|
|
universe.enabledMigration = universe.expansion and settings.global["rampant--enableMigration"].value
|
2021-04-16 22:47:43 +02:00
|
|
|
universe.peacefulAIToggle = settings.global["rampant--peacefulAIToggle"].value
|
2021-04-16 15:54:58 +02:00
|
|
|
universe.printAIStateChanges = settings.global["rampant--printAIStateChanges"].value
|
2021-04-16 22:47:43 +02:00
|
|
|
universe.debugTemperament = settings.global["rampant--debugTemperament"].value
|
2019-11-04 08:19:22 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.enemyAlignmentLookup = {}
|
2019-11-04 08:19:22 +02:00
|
|
|
|
2019-10-14 07:49:52 +02:00
|
|
|
game.map_settings.unit_group.min_group_radius = constants.UNIT_GROUP_MAX_RADIUS * 0.5
|
|
|
|
game.map_settings.unit_group.max_group_radius = constants.UNIT_GROUP_MAX_RADIUS
|
|
|
|
|
|
|
|
game.map_settings.unit_group.max_member_speedup_when_behind = constants.UNIT_GROUP_MAX_SPEED_UP
|
|
|
|
game.map_settings.unit_group.max_member_slowdown_when_ahead = constants.UNIT_GROUP_MAX_SLOWDOWN
|
2019-11-04 08:19:22 +02:00
|
|
|
game.map_settings.unit_group.max_group_slowdown_factor = constants.UNIT_GROUP_SLOWDOWN_FACTOR
|
2019-10-20 22:45:43 +02:00
|
|
|
|
|
|
|
game.map_settings.max_failed_behavior_count = 3
|
2021-02-20 22:44:52 +02:00
|
|
|
game.map_settings.unit_group.member_disown_distance = 10
|
|
|
|
game.map_settings.unit_group.tick_tolerance_when_member_arrives = 60
|
|
|
|
game.forces.enemy.ai_controllable = true
|
2019-12-09 05:31:51 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.evolutionLevel = game.forces.enemy.evolution_factor
|
2020-05-20 04:37:16 +02:00
|
|
|
global.pendingChunks = nil
|
2021-02-20 22:44:52 +02:00
|
|
|
global.natives = nil
|
|
|
|
global.map = nil
|
2021-02-14 06:49:54 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
universe.builderCount = 0
|
|
|
|
universe.squadCount = 0
|
2020-05-20 04:37:16 +02:00
|
|
|
|
2021-02-20 09:31:36 +02:00
|
|
|
addCommandSet(universe)
|
2021-04-30 07:24:14 +02:00
|
|
|
end
|
|
|
|
if global.version < 116 then
|
|
|
|
global.version = 116
|
|
|
|
|
|
|
|
universe.maxPoints = 0
|
|
|
|
|
2021-07-20 06:22:38 +02:00
|
|
|
if (universe.maps) then
|
|
|
|
for _,map in pairs(universe.maps) do
|
|
|
|
for _,base in pairs(map.bases) do
|
|
|
|
base.damagedBy = {}
|
|
|
|
base.deathEvents = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-07-26 02:25:14 +02:00
|
|
|
game.print("Rampant - Version 1.1.4")
|
2019-10-13 22:53:36 +02:00
|
|
|
end
|
2021-11-26 08:49:28 +02:00
|
|
|
if global.version < 120 then
|
|
|
|
global.version = 120
|
2021-11-25 19:04:52 +02:00
|
|
|
|
|
|
|
if (universe.maps) then
|
|
|
|
for _,map in pairs(universe.maps) do
|
2021-11-26 08:49:28 +02:00
|
|
|
map.pendingUpgrades = {}
|
2021-11-26 19:52:30 +02:00
|
|
|
for i=1,#map.processQueue do
|
|
|
|
local chunk = map.processQueue[i]
|
|
|
|
map.processQueue[i].dOrgin = euclideanDistancePoints(chunk.x, chunk.y, 0, 0)
|
|
|
|
end
|
2021-11-25 19:04:52 +02:00
|
|
|
for _,base in pairs(map.bases) do
|
|
|
|
base.mutations = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
game.print("Rampant - Version 1.2.0")
|
|
|
|
end
|
2019-11-04 08:19:22 +02:00
|
|
|
|
2021-02-20 22:44:52 +02:00
|
|
|
return (starting ~= global.version) and global.version
|
2017-05-15 02:09:43 +02:00
|
|
|
end
|
|
|
|
|
2017-06-01 03:46:53 +02:00
|
|
|
function upgrade.compareTable(entities, option, new)
|
|
|
|
local changed = false
|
|
|
|
if (entities[option] ~= new) then
|
2019-10-14 07:49:52 +02:00
|
|
|
entities[option] = new
|
|
|
|
changed = true
|
2017-06-01 03:46:53 +02:00
|
|
|
end
|
|
|
|
return changed, new
|
|
|
|
end
|
|
|
|
|
2017-05-15 02:09:43 +02:00
|
|
|
return upgrade
|