2022-01-14 14:08:58 -08:00
-- 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/>.
2023-03-11 12:53:06 -08:00
if BaseUtilsG then
return BaseUtilsG
2019-02-15 20:17:30 -08:00
end
2023-03-11 12:53:06 -08:00
local BaseUtils = { }
--
local Universe
2017-05-07 23:56:11 -07:00
-- imports
2023-03-11 20:51:13 -08:00
local Utils = require ( " Utils " )
local MathUtils = require ( " MathUtils " )
local Constants = require ( " Constants " )
local MapUtils = require ( " MapUtils " )
2017-06-07 17:57:24 -07:00
2023-03-11 20:51:13 -08:00
-- Constants
2017-05-07 23:56:11 -07:00
2023-03-21 23:09:51 -07:00
local PENDING_UPGRADE_CREATION_THESHOLD = Constants.PENDING_UPGRADE_CREATION_THESHOLD
2023-03-11 20:51:13 -08:00
local TIERS = Constants.TIERS
local EVO_TO_TIER_MAPPING = Constants.EVO_TO_TIER_MAPPING
local BUILDING_HIVE_TYPE_LOOKUP = Constants.BUILDING_HIVE_TYPE_LOOKUP
local COST_LOOKUP = Constants.COST_LOOKUP
local UPGRADE_LOOKUP = Constants.UPGRADE_LOOKUP
2022-07-06 21:14:00 -07:00
2023-03-11 20:51:13 -08:00
local ENEMY_ALIGNMENT_LOOKUP = Constants.ENEMY_ALIGNMENT_LOOKUP
2022-07-06 21:14:00 -07:00
2023-03-11 20:51:13 -08:00
local EVOLUTION_TABLE_ALIGNMENT = Constants.EVOLUTION_TABLE_ALIGNMENT
local BUILDING_EVOLVE_LOOKUP = Constants.BUILDING_EVOLVE_LOOKUP
2022-07-06 21:14:00 -07:00
2023-03-11 20:51:13 -08:00
local BASE_AI_STATE_RAIDING = Constants.BASE_AI_STATE_RAIDING
local BASE_AI_STATE_AGGRESSIVE = Constants.BASE_AI_STATE_AGGRESSIVE
local BASE_AI_STATE_MIGRATING = Constants.BASE_AI_STATE_MIGRATING
local BASE_AI_STATE_SIEGE = Constants.BASE_AI_STATE_SIEGE
local BASE_AI_STATE_ONSLAUGHT = Constants.BASE_AI_STATE_ONSLAUGHT
2019-12-17 17:09:08 -08:00
2023-03-11 20:51:13 -08:00
local MINIMUM_BUILDING_COST = Constants.MINIMUM_BUILDING_COST
local FACTION_MUTATION_MAPPING = Constants.FACTION_MUTATION_MAPPING
2018-08-01 20:18:52 -07:00
2023-03-11 20:51:13 -08:00
local MAGIC_MAXIMUM_NUMBER = Constants.MAGIC_MAXIMUM_NUMBER
2021-07-21 17:48:52 -07:00
2023-03-11 20:51:13 -08:00
local FACTIONS_BY_DAMAGE_TYPE = Constants.FACTIONS_BY_DAMAGE_TYPE
2019-02-11 22:30:13 -08:00
2023-03-11 20:51:13 -08:00
local BASE_GENERATION_STATE_ACTIVE = Constants.BASE_GENERATION_STATE_ACTIVE
2017-05-07 23:56:11 -07:00
2023-03-11 20:51:13 -08:00
local BASE_DISTANCE_THRESHOLD = Constants.BASE_DISTANCE_THRESHOLD
local BASE_DISTANCE_LEVEL_BONUS = Constants.BASE_DISTANCE_LEVEL_BONUS
local BASE_DISTANCE_TO_EVO_INDEX = Constants.BASE_DISTANCE_TO_EVO_INDEX
2018-01-15 23:21:12 -08:00
2023-03-11 20:51:13 -08:00
local CHUNK_SIZE = Constants.CHUNK_SIZE
local BASE_AI_STATE_PEACEFUL = Constants.BASE_AI_STATE_PEACEFUL
local BASE_RALLY_CHANCE = Constants.BASE_RALLY_CHANCE
local BONUS_RALLY_CHANCE = Constants.BONUS_RALLY_CHANCE
local RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN = Constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN
local RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX = Constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX
local MINIMUM_AI_POINTS = Constants.MINIMUM_AI_POINTS
local AI_POINT_GENERATOR_AMOUNT = Constants.AI_POINT_GENERATOR_AMOUNT
local BASE_AI_MIN_STATE_DURATION = Constants.BASE_AI_MIN_STATE_DURATION
local BASE_AI_MAX_STATE_DURATION = Constants.BASE_AI_MAX_STATE_DURATION
local NO_RETREAT_BASE_PERCENT = Constants.NO_RETREAT_BASE_PERCENT
local NO_RETREAT_EVOLUTION_BONUS_MAX = Constants.NO_RETREAT_EVOLUTION_BONUS_MAX
local AI_UNIT_REFUND = Constants.AI_UNIT_REFUND
local AI_MAX_POINTS = Constants.AI_MAX_POINTS
local BASE_GENERATION_MIN_STATE_DURATION = Constants.BASE_GENERATION_MIN_STATE_DURATION
local BASE_GENERATION_MAX_STATE_DURATION = Constants.BASE_GENERATION_MAX_STATE_DURATION
local BASE_GENERATION_STATE_DORMANT = Constants.BASE_GENERATION_STATE_DORMANT
local STATE_ENGLISH = Constants.STATE_ENGLISH
local ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS = Constants.ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS
local ALL_NESTS_PER_EXPANSION_GROUPS = Constants.ALL_NESTS_PER_EXPANSION_GROUPS
local TEMPERAMENT_RANGE_MAX = Constants.TEMPERAMENT_RANGE_MAX
local TEMPERAMENT_RANGE_MIN = Constants.TEMPERAMENT_RANGE_MIN
local TEMPERAMENT_DIVIDER = Constants.TEMPERAMENT_DIVIDER
local UNIT_DEATH_POINT_COST = Constants.UNIT_DEATH_POINT_COST
local BASE_PROCESS_INTERVAL = Constants.BASE_PROCESS_INTERVAL
2022-02-27 18:45:42 -08:00
2017-05-07 23:56:11 -07:00
-- imported functions
2023-03-11 20:51:13 -08:00
local getTimeStringFromTick = Utils.getTimeStringFromTick
local randomTickEvent = MathUtils.randomTickEvent
local linearInterpolation = MathUtils.linearInterpolation
local randomTickDuration = MathUtils.randomTickDuration
local isMember = Utils.isMember
2023-01-02 15:31:07 -08:00
2023-03-11 20:51:13 -08:00
local setPositionXYInQuery = Utils.setPositionXYInQuery
2021-12-11 10:42:49 -08:00
2023-03-11 20:51:13 -08:00
local euclideanDistancePoints = MathUtils.euclideanDistancePoints
2017-05-07 23:56:11 -07:00
2023-03-11 20:51:13 -08:00
local getChunkByPosition = MapUtils.getChunkByPosition
2018-01-28 17:51:24 -08:00
2023-03-11 20:51:13 -08:00
local gaussianRandomRangeRG = MathUtils.gaussianRandomRangeRG
2018-01-15 23:21:12 -08:00
2017-05-31 18:46:53 -07:00
local mFloor = math.floor
2023-03-11 20:51:13 -08:00
local mCeil = math.ceil
local tableSize = table_size
2017-05-31 18:46:53 -07:00
2023-01-16 12:28:40 -08:00
local tableRemove = table.remove
2018-01-20 23:42:47 -08:00
local mMin = math.min
local mMax = math.max
2020-05-19 19:37:16 -07:00
local next = next
2017-05-19 00:47:24 -07:00
-- module code
2017-05-07 23:56:11 -07:00
2023-03-11 12:53:06 -08:00
local function evoToTier ( evolutionFactor , maxSkips )
2019-11-29 16:49:22 -08:00
local v
2021-11-26 14:00:08 -08:00
local skipsRemaining = maxSkips
2022-07-09 14:27:53 -07:00
for i = TIERS , 1 , - 1 do
2022-07-06 21:14:00 -07:00
if EVO_TO_TIER_MAPPING [ i ] <= evolutionFactor then
2019-11-29 16:49:22 -08:00
v = i
2023-03-11 12:53:06 -08:00
if ( skipsRemaining == 0 ) or ( Universe.random ( ) <= 0.75 ) then
2019-11-29 16:49:22 -08:00
break
2019-03-11 23:03:26 -07:00
end
2021-11-26 14:00:08 -08:00
skipsRemaining = skipsRemaining - 1
2019-10-19 12:13:48 -07:00
end
2019-03-11 23:03:26 -07:00
end
2019-11-29 16:49:22 -08:00
return v
2018-08-01 20:18:52 -07:00
end
2023-03-11 12:53:06 -08:00
local function findBaseMutation ( targetEvolution , excludeFactions )
local tier = evoToTier ( targetEvolution or Universe.evolutionLevel , 2 )
2023-01-02 15:31:07 -08:00
local availableAlignments
2022-07-06 21:14:00 -07:00
local alignments = EVOLUTION_TABLE_ALIGNMENT [ tier ]
2019-02-02 22:01:28 -08:00
2023-01-02 15:31:07 -08:00
if excludeFactions then
availableAlignments = { }
for _ , alignment in pairs ( alignments ) do
if not isMember ( alignment [ 2 ] , excludeFactions ) then
availableAlignments [ # availableAlignments + 1 ] = alignment
end
end
else
availableAlignments = alignments
end
2023-03-11 12:53:06 -08:00
local roll = Universe.random ( )
2023-01-02 15:31:07 -08:00
for i = 1 , # availableAlignments do
local alignment = availableAlignments [ i ]
2018-02-10 12:57:54 -08:00
2019-11-29 16:49:22 -08:00
roll = roll - alignment [ 1 ]
2019-11-03 22:19:22 -08:00
2019-11-29 16:49:22 -08:00
if ( roll <= 0 ) then
return alignment [ 2 ]
end
2018-02-10 12:57:54 -08:00
end
2023-01-02 15:31:07 -08:00
return availableAlignments [ # availableAlignments ]
2019-11-29 16:49:22 -08:00
end
2019-02-02 22:01:28 -08:00
2023-03-12 16:13:32 -07:00
local function initialEntityUpgrade ( baseAlignment , tier , maxTier , useHiveType , entityType )
2019-11-29 16:49:22 -08:00
local entity
2019-12-15 17:16:56 -08:00
local useTier
2023-03-11 12:53:06 -08:00
local tierRoll = Universe.random ( )
2019-12-15 17:16:56 -08:00
if ( tierRoll < 0.4 ) then
useTier = maxTier
elseif ( tierRoll < 0.7 ) then
useTier = mMax ( maxTier - 1 , tier )
elseif ( tierRoll < 0.9 ) then
useTier = mMax ( maxTier - 2 , tier )
else
useTier = mMax ( maxTier - 3 , tier )
end
2022-07-06 21:14:00 -07:00
local alignmentTable = BUILDING_EVOLVE_LOOKUP [ baseAlignment ]
2022-10-20 22:29:12 -07:00
if not alignmentTable then
2023-01-02 15:31:07 -08:00
alignmentTable = BUILDING_EVOLVE_LOOKUP [ " neutral " ]
2022-10-20 22:29:12 -07:00
end
2021-12-14 20:28:16 -08:00
local upgrades = alignmentTable [ useTier ]
2019-12-15 17:16:56 -08:00
2022-10-20 22:29:12 -07:00
if alignmentTable and upgrades then
2019-12-15 17:16:56 -08:00
if useHiveType then
for ui = 1 , # upgrades do
local upgrade = upgrades [ ui ]
if upgrade [ 3 ] == useHiveType then
2023-03-11 12:53:06 -08:00
entity = upgrade [ 2 ] [ Universe.random ( # upgrade [ 2 ] ) ]
2019-12-15 17:16:56 -08:00
break
2019-03-11 23:03:26 -07:00
end
2019-12-15 17:16:56 -08:00
end
2020-05-23 20:47:14 -07:00
end
if not entity then
2019-12-15 17:16:56 -08:00
for ui = 1 , # upgrades do
local upgrade = upgrades [ ui ]
2022-01-16 12:05:34 -08:00
if upgrade [ 3 ] == entityType then
2023-03-11 12:53:06 -08:00
entity = upgrade [ 2 ] [ Universe.random ( # upgrade [ 2 ] ) ]
2022-01-16 12:05:34 -08:00
end
end
if not entity then
local mapTypes = FACTION_MUTATION_MAPPING [ entityType ]
for i = 1 , # mapTypes do
local mappedType = mapTypes [ i ]
for ui = 1 , # upgrades do
local upgrade = upgrades [ ui ]
if upgrade [ 3 ] == mappedType then
2023-03-11 12:53:06 -08:00
return upgrade [ 2 ] [ Universe.random ( # upgrade [ 2 ] ) ]
2022-01-16 12:05:34 -08:00
end
end
2019-03-11 23:03:26 -07:00
end
end
2019-11-03 22:19:22 -08:00
end
2019-11-29 16:49:22 -08:00
end
2019-12-15 17:16:56 -08:00
2019-11-29 16:49:22 -08:00
return entity
end
2023-03-12 16:13:32 -07:00
local function entityUpgrade ( baseAlignment , tier , maxTier , originalEntity )
2019-11-29 16:49:22 -08:00
local entity
2022-07-06 21:14:00 -07:00
local hiveType = BUILDING_HIVE_TYPE_LOOKUP [ originalEntity.name ]
2019-11-29 16:49:22 -08:00
2019-12-17 17:09:08 -08:00
for t = maxTier , tier , - 1 do
2022-07-06 21:14:00 -07:00
local factionLookup = UPGRADE_LOOKUP [ baseAlignment ] [ t ]
2019-12-17 17:09:08 -08:00
local upgrades = factionLookup [ hiveType ]
if not upgrades then
local mapTypes = FACTION_MUTATION_MAPPING [ hiveType ]
for i = 1 , # mapTypes do
local upgrade = factionLookup [ mapTypes [ i ] ]
if upgrade and ( # upgrade > 0 ) then
2023-03-11 12:53:06 -08:00
entity = upgrade [ Universe.random ( # upgrade ) ]
if Universe.random ( ) < 0.55 then
2019-12-17 17:09:08 -08:00
return entity
end
end
end
elseif ( # upgrades > 0 ) then
2023-03-11 12:53:06 -08:00
entity = upgrades [ Universe.random ( # upgrades ) ]
if Universe.random ( ) < 0.55 then
2020-05-23 20:47:14 -07:00
return entity
2019-03-11 23:03:26 -07:00
end
2019-11-03 22:19:22 -08:00
end
end
2018-01-25 16:52:26 -08:00
return entity
end
2023-03-21 23:09:51 -07:00
local function findEntityCreation ( baseAlignment , evoIndex , position , map , entityType )
local tier = evoToTier ( evoIndex , 5 )
local maxTier = evoToTier ( evoIndex , 4 )
if ( tier > maxTier ) then
maxTier = tier
end
local chunk = getChunkByPosition ( map , position )
if not entityType then
if Universe.random ( ) < 0.5 then
entityType = " biter-spawner "
else
entityType = " spitter-spawner "
end
end
local roll = Universe.random ( )
local makeHive = ( chunk ~= - 1 ) and
(
( entityType == " biter-spawner " ) or ( entityType == " spitter-spawner " )
)
and
(
(
( roll <= 0.01 )
)
or
(
( roll <= 0.210 ) and
chunk.resourceGenerator
)
)
return initialEntityUpgrade ( baseAlignment , tier , maxTier , ( makeHive and " hive " ) , entityType )
end
local function findEntityUpgrade ( baseAlignment , currentEvo , evoIndex , originalEntity , map , evolve )
2019-11-29 16:49:22 -08:00
local adjCurrentEvo = mMax (
2022-07-06 21:14:00 -07:00
( ( baseAlignment ~= ENEMY_ALIGNMENT_LOOKUP [ originalEntity.name ] ) and 0 ) or currentEvo ,
2019-11-29 16:49:22 -08:00
0
)
2023-03-11 12:53:06 -08:00
local tier = evoToTier ( adjCurrentEvo , 5 )
local maxTier = evoToTier ( evoIndex , 4 )
2018-01-25 16:52:26 -08:00
2019-11-29 16:49:22 -08:00
if ( tier > maxTier ) then
2021-12-04 11:04:13 -08:00
maxTier = tier
2019-11-29 16:49:22 -08:00
end
2019-12-15 17:16:56 -08:00
2019-11-29 16:49:22 -08:00
if evolve then
2021-02-19 23:31:36 -08:00
local chunk = getChunkByPosition ( map , originalEntity.position )
2022-01-16 16:51:43 -08:00
local entityName = originalEntity.name
2022-07-06 21:14:00 -07:00
local entityType = BUILDING_HIVE_TYPE_LOOKUP [ entityName ]
2022-01-16 12:05:34 -08:00
if not entityType then
2023-03-11 12:53:06 -08:00
if Universe.random ( ) < 0.5 then
2022-01-16 12:05:34 -08:00
entityType = " biter-spawner "
else
entityType = " spitter-spawner "
end
end
2023-03-11 12:53:06 -08:00
local roll = Universe.random ( )
2022-01-16 12:05:34 -08:00
local makeHive = ( chunk ~= - 1 ) and
2022-01-16 16:51:43 -08:00
(
( entityType == " biter-spawner " ) or ( entityType == " spitter-spawner " )
)
and
(
(
2023-03-13 22:32:22 -07:00
( roll <= 0.01 )
2022-01-16 16:51:43 -08:00
)
or
(
2022-06-12 22:34:49 -07:00
( roll <= 0.210 ) and
2023-03-11 17:21:22 -08:00
chunk.resourceGenerator
2022-01-16 16:51:43 -08:00
)
)
2023-03-12 16:13:32 -07:00
return initialEntityUpgrade ( baseAlignment , tier , maxTier , ( makeHive and " hive " ) , entityType )
2019-11-29 16:49:22 -08:00
else
2023-03-12 16:13:32 -07:00
return entityUpgrade ( baseAlignment , tier , maxTier , originalEntity )
2019-12-15 17:16:56 -08:00
end
2019-11-29 16:49:22 -08:00
end
2023-01-02 15:31:07 -08:00
-- local
2023-03-11 12:53:06 -08:00
function BaseUtils . findBaseInitialAlignment ( evoIndex , excludeFactions )
2021-11-26 14:09:10 -08:00
local dev = evoIndex * 0.15
2023-03-11 12:53:06 -08:00
local evoTop = gaussianRandomRangeRG ( evoIndex - ( evoIndex * 0.075 ) , dev , 0 , evoIndex , Universe.random )
2018-01-25 16:52:26 -08:00
2019-11-29 16:49:22 -08:00
local result
2023-03-11 12:53:06 -08:00
if Universe.random ( ) < 0.05 then
result = { findBaseMutation ( evoTop , excludeFactions ) , findBaseMutation ( evoTop , excludeFactions ) }
2019-11-29 16:49:22 -08:00
else
2023-03-11 12:53:06 -08:00
result = { findBaseMutation ( evoTop , excludeFactions ) }
2018-01-15 23:21:12 -08:00
end
2019-02-02 22:01:28 -08:00
2019-11-29 16:49:22 -08:00
return result
2018-01-15 23:21:12 -08:00
end
2023-03-11 12:53:06 -08:00
function BaseUtils . recycleBases ( )
local bases = Universe.bases
local id = Universe.recycleBaseIterator
2021-11-24 20:49:22 -08:00
local base
if not id then
id , base = next ( bases , nil )
else
base = bases [ id ]
end
2021-02-19 21:41:30 -08:00
if not id then
2023-03-11 12:53:06 -08:00
Universe.recycleBaseIterator = nil
2021-02-19 21:41:30 -08:00
else
2023-03-11 12:53:06 -08:00
Universe.recycleBaseIterator = next ( bases , id )
2022-04-09 19:24:15 -07:00
local map = base.map
if ( base.chunkCount == 0 ) or not map.surface . valid then
2021-02-19 21:41:30 -08:00
bases [ id ] = nil
2023-03-11 12:53:06 -08:00
if Universe.processBaseAIIterator == id then
Universe.processBaseAIIterator = nil
2022-04-29 20:18:50 -07:00
end
2022-04-09 19:24:15 -07:00
if map.surface . valid then
2023-03-11 12:53:06 -08:00
Universe.bases [ id ] = nil
2022-04-09 19:24:15 -07:00
end
2019-02-02 22:01:28 -08:00
end
2018-01-25 21:48:12 -08:00
end
end
2023-03-11 20:51:13 -08:00
function BaseUtils . canAttack ( base )
local isAggressive = ( ( base.stateAI == BASE_AI_STATE_AGGRESSIVE )
and ( base.sentAggressiveGroups < base.maxAggressiveGroups ) )
local isRaiding = ( base.stateAI == BASE_AI_STATE_RAIDING )
local isOnslaught = ( base.stateAI == BASE_AI_STATE_ONSLAUGHT )
local isRaidSieging = Universe.raidAIToggle
and ( base.stateAI == BASE_AI_STATE_SIEGE )
and ( base.sentExpansionGroups >= base.maxExpansionGroups )
local goodAI = isAggressive or isRaiding or isOnslaught or isRaidSieging
if not goodAI then
return false
end
local surface = base.map . surface
if surface.peaceful_mode then
return false
end
local nocturalMode = Universe.aiNocturnalMode
local noctural = ( not nocturalMode ) or ( nocturalMode and surface.darkness > 0.65 )
if not noctural then
return false
end
return true
end
function BaseUtils . canMigrate ( base )
local badAIState = ( base.stateAI ~= BASE_AI_STATE_MIGRATING ) and ( base.stateAI ~= BASE_AI_STATE_SIEGE )
if badAIState then
return false
end
if not Universe.expansion then
return false
end
local surface = base.map . surface
if surface.peaceful_mode then
return false
end
local nocturalMode = Universe.aiNocturnalMode
local noctural = ( not nocturalMode ) or ( nocturalMode and surface.darkness > 0.65 )
if not noctural then
return false
end
return true
end
2023-03-19 10:25:46 -07:00
function BaseUtils . queueUpgrade ( entity , base , disPos , evolve , timeDelay )
local map = base.map
local baseAlignment = base.alignment
local position = disPos or entity.position
local pickedBaseAlignment
if baseAlignment [ 2 ] then
if Universe.random ( ) < 0.75 then
pickedBaseAlignment = baseAlignment [ 2 ]
else
pickedBaseAlignment = baseAlignment [ 1 ]
end
else
pickedBaseAlignment = baseAlignment [ 1 ]
end
local currentEvo = entity.prototype . build_base_evolution_requirement or 0
2023-03-21 23:09:51 -07:00
local distance = mMin (
1 ,
euclideanDistancePoints ( position.x , position.y , 0 , 0 ) * BASE_DISTANCE_TO_EVO_INDEX
)
2023-03-19 10:25:46 -07:00
local evoIndex = mMax ( distance , Universe.evolutionLevel )
2023-03-21 23:09:51 -07:00
local name = findEntityUpgrade ( pickedBaseAlignment ,
currentEvo ,
evoIndex ,
entity ,
map ,
evolve )
2023-03-19 10:25:46 -07:00
2023-03-21 23:09:51 -07:00
local entityName = entity.name
if name == entityName then
2023-03-19 10:25:46 -07:00
return
end
2023-03-21 23:09:51 -07:00
local surface = map.surface
2023-03-19 10:25:46 -07:00
if not evolve and Universe.printBaseUpgrades then
surface.print (
" [ " .. base.id .. " ]: " .. surface.name .. " Upgrading "
2023-03-21 23:09:51 -07:00
.. entityName .. " to " .. name
2023-03-19 10:25:46 -07:00
.. " [gps= " .. position.x .. " , " .. position.y .. " ] "
)
end
2023-03-21 23:09:51 -07:00
local unitNumber = entity.unit_number
position = surface.find_non_colliding_position (
name ,
position ,
8 ,
1 ,
true
)
Universe.pendingUpgrades [ Universe.upgradeId ] = {
2023-03-19 10:25:46 -07:00
[ " position " ] = position ,
[ " map " ] = map ,
[ " name " ] = name ,
2023-01-02 13:24:18 -08:00
[ " entity " ] = entity ,
2023-03-19 10:25:46 -07:00
[ " delayTLL " ] = timeDelay ,
2023-03-21 23:09:51 -07:00
[ " hive " ] = Universe.hives [ unitNumber ] or Universe.hiveData [ unitNumber ] ,
2023-03-19 10:25:46 -07:00
[ " state " ] = 1
2023-01-02 13:24:18 -08:00
}
2023-03-21 23:09:51 -07:00
Universe.pendingUpgradesLength = Universe.pendingUpgradesLength + 1
Universe.upgradeId = Universe.upgradeId + 1
end
function BaseUtils . queueCreation ( base , position , entityType , timeDelay , hiveData )
local map = base.map
local baseAlignment = base.alignment
if Universe.pendingUpgradesLength > PENDING_UPGRADE_CREATION_THESHOLD then
return
end
local pickedBaseAlignment
if baseAlignment [ 2 ] then
if Universe.random ( ) < 0.75 then
pickedBaseAlignment = baseAlignment [ 2 ]
else
pickedBaseAlignment = baseAlignment [ 1 ]
end
else
pickedBaseAlignment = baseAlignment [ 1 ]
end
local distance = mMin (
1 ,
euclideanDistancePoints ( position.x , position.y , 0 , 0 ) * BASE_DISTANCE_TO_EVO_INDEX
)
local evoIndex = mMax ( distance , Universe.evolutionLevel )
local name = findEntityCreation (
pickedBaseAlignment ,
evoIndex ,
position ,
map ,
entityType
)
if not name then
return
end
position = map.surface . find_non_colliding_position (
name ,
position ,
8 ,
1 ,
true
)
if not position then
return
end
Universe.pendingUpgrades [ Universe.upgradeId ] = {
[ " position " ] = position ,
[ " map " ] = map ,
[ " name " ] = name ,
[ " delayTLL " ] = timeDelay ,
[ " hive " ] = hiveData ,
[ " state " ] = 2
}
Universe.pendingUpgradesLength = Universe.pendingUpgradesLength + 1
Universe.upgradeId = Universe.upgradeId + 1
2019-02-11 22:30:13 -08:00
end
2023-03-11 12:53:06 -08:00
local function pickMutationFromDamageType ( damageType , roll , base )
2021-07-19 21:22:38 -07:00
local baseAlignment = base.alignment
2021-07-21 17:48:52 -07:00
local damageFactions = FACTIONS_BY_DAMAGE_TYPE [ damageType ]
2021-07-25 17:25:14 -07:00
local mutation
2022-12-26 17:43:41 -08:00
local mutated = false
2021-07-19 21:22:38 -07:00
2022-12-26 17:43:41 -08:00
if damageFactions and ( # damageFactions > 0 ) then
2023-03-11 12:53:06 -08:00
mutation = damageFactions [ Universe.random ( # damageFactions ) ]
2023-01-16 12:28:40 -08:00
if not isMember ( mutation , base.alignmentHistory ) then
if baseAlignment [ 2 ] then
if ( baseAlignment [ 1 ] ~= mutation ) and ( baseAlignment [ 2 ] ~= mutation ) then
mutated = true
if ( roll < 0.05 ) then
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 1 ]
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 2 ]
baseAlignment [ 1 ] = mutation
baseAlignment [ 2 ] = nil
elseif ( roll < 0.75 ) then
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 1 ]
baseAlignment [ 1 ] = mutation
else
baseAlignment [ 2 ] = mutation
end
end
elseif ( baseAlignment [ 1 ] ~= mutation ) then
2022-12-26 17:43:41 -08:00
mutated = true
2023-01-16 12:28:40 -08:00
if ( roll < 0.85 ) then
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 1 ]
baseAlignment [ 1 ] = mutation
else
baseAlignment [ 2 ] = mutation
end
2021-07-21 17:48:52 -07:00
end
end
2021-07-19 21:22:38 -07:00
else
2023-03-11 12:53:06 -08:00
mutation = findBaseMutation ( )
2023-01-16 12:28:40 -08:00
if not isMember ( mutation , base.alignmentHistory ) then
if baseAlignment [ 2 ] then
if ( baseAlignment [ 1 ] ~= mutation ) and ( baseAlignment [ 2 ] ~= mutation ) then
mutated = true
if ( roll < 0.05 ) then
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 1 ]
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 2 ]
baseAlignment [ 1 ] = mutation
baseAlignment [ 2 ] = nil
elseif ( roll < 0.75 ) then
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 1 ]
baseAlignment [ 1 ] = mutation
else
baseAlignment [ 2 ] = mutation
end
end
elseif ( baseAlignment [ 1 ] ~= mutation ) then
2022-12-26 17:43:41 -08:00
mutated = true
2023-01-16 12:28:40 -08:00
if ( roll < 0.85 ) then
base.alignmentHistory [ # base.alignmentHistory + 1 ] = baseAlignment [ 1 ]
base.alignment [ 1 ] = mutation
else
base.alignment [ 2 ] = mutation
end
2021-07-19 21:22:38 -07:00
end
end
end
2023-03-11 12:53:06 -08:00
if mutated and Universe.printBaseAdaptation then
2021-11-25 09:04:52 -08:00
if baseAlignment [ 2 ] then
2021-07-25 17:25:14 -07:00
game.print ( { " description.rampant--adaptation2DebugMessage " ,
2023-01-02 18:49:56 -08:00
base.id ,
base.map . surface.name ,
2021-07-25 17:25:14 -07:00
damageType ,
{ " description.rampant-- " .. baseAlignment [ 1 ] .. " EnemyName " } ,
{ " description.rampant-- " .. baseAlignment [ 2 ] .. " EnemyName " } ,
base.x ,
2021-11-25 09:04:52 -08:00
base.y ,
base.mutations ,
2023-03-11 12:53:06 -08:00
Universe.MAX_BASE_MUTATIONS } )
2021-07-25 17:25:14 -07:00
else
game.print ( { " description.rampant--adaptation1DebugMessage " ,
2023-01-02 18:49:56 -08:00
base.id ,
base.map . surface.name ,
2021-07-25 17:25:14 -07:00
damageType ,
{ " description.rampant-- " .. baseAlignment [ 1 ] .. " EnemyName " } ,
base.x ,
2021-11-25 09:04:52 -08:00
base.y ,
base.mutations ,
2023-03-11 12:53:06 -08:00
Universe.MAX_BASE_MUTATIONS } )
2021-07-25 17:25:14 -07:00
end
end
2023-03-11 20:51:13 -08:00
local alignmentCount = tableSize ( base.alignmentHistory )
2023-03-11 12:53:06 -08:00
while ( alignmentCount > Universe.MAX_BASE_ALIGNMENT_HISTORY ) do
2023-01-16 12:28:40 -08:00
tableRemove ( base.alignmentHistory , 1 )
alignmentCount = alignmentCount - 1
end
2022-12-26 17:43:41 -08:00
return mutated
2021-07-19 21:22:38 -07:00
end
2023-03-11 12:53:06 -08:00
function BaseUtils . upgradeBaseBasedOnDamage ( base )
2021-07-19 21:22:38 -07:00
local total = 0
for _ , amount in pairs ( base.damagedBy ) do
total = total + amount
end
2021-07-24 18:20:13 -07:00
local mutationAmount = total * 0.176471
2021-11-25 09:13:43 -08:00
base.damagedBy [ " RandomMutation " ] = mutationAmount
2021-07-19 21:22:38 -07:00
total = total + mutationAmount
local pickedDamage
2023-03-11 12:53:06 -08:00
local roll = Universe.random ( )
2021-07-24 18:20:13 -07:00
for damageTypeName , amount in pairs ( base.damagedBy ) do
base.damagedBy [ damageTypeName ] = amount / total
end
2021-07-19 21:22:38 -07:00
for damageType , amount in pairs ( base.damagedBy ) do
2021-12-08 20:14:21 -08:00
roll = roll - amount
if ( roll <= 0 ) then
2021-07-19 21:22:38 -07:00
pickedDamage = damageType
break
end
end
2023-03-11 12:53:06 -08:00
return pickMutationFromDamageType ( pickedDamage , roll , base )
2021-07-19 21:22:38 -07:00
end
2023-03-11 12:53:06 -08:00
function BaseUtils . processBaseMutation ( chunk , map , base )
2023-03-21 23:09:51 -07:00
if not base.alignment [ 1 ]
or ( base.stateGeneration ~= BASE_GENERATION_STATE_ACTIVE )
or ( Universe.random ( ) >= 0.30 )
or ( Universe.pendingUpgradesLength > PENDING_UPGRADE_CREATION_THESHOLD )
2022-03-19 16:29:12 -07:00
then
2022-02-23 21:04:34 -08:00
return
end
2022-03-19 16:29:12 -07:00
if ( base.points >= MINIMUM_BUILDING_COST ) then
local surface = map.surface
2023-03-11 12:53:06 -08:00
setPositionXYInQuery ( Universe.pbFilteredEntitiesPointQueryLimited ,
chunk.x + ( CHUNK_SIZE * Universe.random ( ) ) ,
chunk.y + ( CHUNK_SIZE * Universe.random ( ) ) )
2019-11-03 22:19:22 -08:00
2023-03-11 12:53:06 -08:00
local entities = surface.find_entities_filtered ( Universe.pbFilteredEntitiesPointQueryLimited )
2021-02-20 15:31:48 -08:00
if # entities ~= 0 then
local entity = entities [ 1 ]
2022-07-06 21:14:00 -07:00
local cost = ( COST_LOOKUP [ entity.name ] or MAGIC_MAXIMUM_NUMBER )
2021-02-20 15:31:48 -08:00
if ( base.points >= cost ) then
2023-01-02 18:10:13 -08:00
local position = entity.position
2023-03-11 12:53:06 -08:00
BaseUtils.modifyBaseSpecialPoints ( base , - cost , " Scheduling Entity upgrade " , position.x , position.y )
2023-03-19 10:25:46 -07:00
BaseUtils.queueUpgrade ( entity , base , nil , false )
2019-11-29 16:49:22 -08:00
end
2019-02-02 22:01:28 -08:00
end
2021-04-30 21:25:55 -07:00
end
2018-01-20 23:42:47 -08:00
end
2023-03-11 12:53:06 -08:00
function BaseUtils . createBase ( map , chunk , tick )
2018-01-14 23:41:55 -08:00
local x = chunk.x
local y = chunk.y
local distance = euclideanDistancePoints ( x , y , 0 , 0 )
2018-01-15 23:21:12 -08:00
2019-11-29 16:49:22 -08:00
local meanLevel = mFloor ( distance * 0.005 )
2018-01-15 23:21:12 -08:00
2019-02-02 22:01:28 -08:00
local distanceIndex = mMin ( 1 , distance * BASE_DISTANCE_TO_EVO_INDEX )
2023-03-11 12:53:06 -08:00
local evoIndex = mMax ( distanceIndex , Universe.evolutionLevel )
2019-11-03 22:19:22 -08:00
2023-01-02 16:11:19 -08:00
local alignment =
2023-03-11 12:53:06 -08:00
( Universe.NEW_ENEMIES and BaseUtils.findBaseInitialAlignment ( evoIndex ) )
2023-01-02 16:11:19 -08:00
or { " neutral " }
2019-02-02 22:01:28 -08:00
2022-03-19 16:29:12 -07:00
local baseLevel = gaussianRandomRangeRG ( meanLevel ,
meanLevel * 0.3 ,
meanLevel * 0.50 ,
meanLevel * 1.50 ,
2023-03-11 12:53:06 -08:00
Universe.random )
2021-12-05 15:33:24 -08:00
local baseDistanceThreshold = gaussianRandomRangeRG ( BASE_DISTANCE_THRESHOLD ,
BASE_DISTANCE_THRESHOLD * 0.2 ,
BASE_DISTANCE_THRESHOLD * 0.75 ,
BASE_DISTANCE_THRESHOLD * 1.50 ,
2023-03-11 12:53:06 -08:00
Universe.random )
2018-01-25 21:13:47 -08:00
local distanceThreshold = ( baseLevel * BASE_DISTANCE_LEVEL_BONUS ) + baseDistanceThreshold
2017-05-11 21:50:06 -07:00
local base = {
2019-02-02 22:01:28 -08:00
x = x ,
y = y ,
2023-03-21 00:02:49 -07:00
totalX = 0 ,
totalY = 0 ,
2023-03-11 12:53:06 -08:00
distanceThreshold = distanceThreshold * Universe.baseDistanceModifier ,
2022-03-19 16:29:12 -07:00
tick = tick ,
2019-11-29 16:49:22 -08:00
alignment = alignment ,
2023-01-16 12:28:40 -08:00
alignmentHistory = { } ,
2021-04-29 22:24:14 -07:00
damagedBy = { } ,
2021-07-24 18:20:13 -07:00
deathEvents = 0 ,
2021-11-25 09:04:52 -08:00
mutations = 0 ,
2022-03-19 16:29:12 -07:00
stateGeneration = BASE_GENERATION_STATE_ACTIVE ,
2022-02-27 18:45:42 -08:00
stateGenerationTick = 0 ,
2021-12-04 11:04:13 -08:00
chunkCount = 0 ,
2019-03-09 14:47:35 -08:00
createdTick = tick ,
2020-05-19 19:37:16 -07:00
points = 0 ,
2022-02-23 21:04:34 -08:00
unitPoints = 0 ,
2022-02-27 18:45:42 -08:00
stateAI = BASE_AI_STATE_PEACEFUL ,
2022-03-19 16:29:12 -07:00
stateAITick = 0 ,
maxAggressiveGroups = 0 ,
sentAggressiveGroups = 0 ,
2022-10-26 10:55:00 -07:00
resetExpensionGroupsTick = 0 ,
maxExpansionGroups = 0 ,
sentExpansionGroups = 0 ,
2022-02-27 18:45:42 -08:00
activeRaidNests = 0 ,
activeNests = 0 ,
destroyPlayerBuildings = 0 ,
lostEnemyUnits = 0 ,
lostEnemyBuilding = 0 ,
rocketLaunched = 0 ,
builtEnemyBuilding = 0 ,
ionCannonBlasts = 0 ,
artilleryBlasts = 0 ,
2023-01-18 21:38:07 -08:00
resourceChunks = { } ,
resourceChunkCount = 0 ,
2022-02-27 18:45:42 -08:00
temperament = 0.5 ,
temperamentScore = 0 ,
2022-04-09 19:24:15 -07:00
map = map ,
2023-03-11 12:53:06 -08:00
id = Universe.baseId
2017-05-11 21:50:06 -07:00
}
2023-03-11 12:53:06 -08:00
Universe.baseId = Universe.baseId + 1
2018-01-14 23:41:55 -08:00
2021-02-19 23:31:36 -08:00
map.bases [ base.id ] = base
2023-03-11 12:53:06 -08:00
Universe.bases [ base.id ] = base
2019-02-02 22:01:28 -08:00
2017-05-11 21:50:06 -07:00
return base
2017-05-07 23:56:11 -07:00
end
2023-03-11 12:53:06 -08:00
function BaseUtils . modifyBaseUnitPoints ( base , points , tag , x , y )
2021-04-04 21:46:43 -07:00
2023-01-02 18:10:13 -08:00
if points > 0 and base.stateAI == BASE_AI_STATE_PEACEFUL then
return
end
tag = tag or " "
x = x or nil
y = y or nil
base.unitPoints = base.unitPoints + points
local overflowMessage = " "
2023-03-11 12:53:06 -08:00
if base.unitPoints > Universe.maxOverflowPoints then
base.unitPoints = Universe.maxOverflowPoints
2023-01-02 18:10:13 -08:00
overflowMessage = " [Point cap reached] "
end
local printPointChange = " "
2023-03-11 12:53:06 -08:00
if points > 0 and Universe.aiPointsPrintGainsToChat then
2023-01-02 18:10:13 -08:00
printPointChange = " + " .. string.format ( " %.2f " , points )
2023-03-11 12:53:06 -08:00
elseif points < 0 and Universe.aiPointsPrintSpendingToChat then
2023-01-02 18:10:13 -08:00
printPointChange = string.format ( " %.2f " , points )
end
if printPointChange ~= " " then
local gps = " "
if x ~= nil then
gps = " [gps= " .. x .. " , " .. y .. " ] "
2021-04-04 21:46:43 -07:00
end
2023-01-02 18:10:13 -08:00
game.print ( " [ " .. base.id .. " ]: " .. base.map . surface.name .. " " .. printPointChange .. " [ " .. tag .. " ] Unit Total: " .. string.format ( " %.2f " , base.unitPoints ) .. overflowMessage .. gps )
2021-04-04 21:46:43 -07:00
end
2018-01-20 23:42:47 -08:00
end
2023-03-11 12:53:06 -08:00
function BaseUtils . modifyBaseSpecialPoints ( base , points , tag , x , y )
2022-12-02 14:19:14 -05:00
if points > 0 and base.stateAI == BASE_AI_STATE_PEACEFUL then
return
end
2022-11-30 18:50:36 -05:00
tag = tag or " "
x = x or nil
y = y or nil
2023-01-02 18:10:13 -08:00
base.points = base.points + points
2022-11-30 18:50:36 -05:00
local overflowMessage = " "
2023-03-11 12:53:06 -08:00
if base.points > Universe.maxOverflowPoints then
base.points = Universe.maxOverflowPoints
2022-11-30 18:50:36 -05:00
overflowMessage = " [Point cap reached] "
end
local printPointChange = " "
2023-03-11 12:53:06 -08:00
if points > 0 and Universe.aiPointsPrintGainsToChat then
2022-11-30 18:50:36 -05:00
printPointChange = " + " .. string.format ( " %.2f " , points )
2023-03-11 12:53:06 -08:00
elseif points < 0 and Universe.aiPointsPrintSpendingToChat then
2022-11-30 18:50:36 -05:00
printPointChange = string.format ( " %.2f " , points )
end
2023-01-02 18:10:13 -08:00
2022-11-30 18:50:36 -05:00
if printPointChange ~= " " then
local gps = " "
if x ~= nil then
gps = " [gps= " .. x .. " , " .. y .. " ] "
end
2023-01-02 18:10:13 -08:00
game.print ( " [ " .. base.id .. " ]: " .. base.map . surface.name .. " " .. printPointChange .. " [ " .. tag .. " ] Special Total: " .. string.format ( " %.2f " , base.points ) .. overflowMessage .. gps )
2022-11-30 18:50:36 -05:00
end
end
2023-03-11 20:51:13 -08:00
function BaseUtils . planning ( evolutionLevel )
Universe.evolutionLevel = evolutionLevel
local maxPoints = mMax ( AI_MAX_POINTS * evolutionLevel , MINIMUM_AI_POINTS )
Universe.maxPoints = maxPoints
local maxOverflowPoints = maxPoints * 3
Universe.maxOverflowPoints = maxOverflowPoints
local attackWaveMaxSize = Universe.attackWaveMaxSize
Universe.retreatThreshold = linearInterpolation ( evolutionLevel ,
RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN ,
RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX )
Universe.rallyThreshold = BASE_RALLY_CHANCE + ( evolutionLevel * BONUS_RALLY_CHANCE )
Universe.formSquadThreshold = mMax ( ( 0.35 * evolutionLevel ) , 0.1 )
Universe.attackWaveSize = attackWaveMaxSize * ( evolutionLevel ^ 1.4 )
Universe.attackWaveDeviation = ( Universe.attackWaveSize * 0.333 )
Universe.attackWaveUpperBound = Universe.attackWaveSize + ( Universe.attackWaveSize * 0.35 )
if ( Universe.attackWaveSize < 1 ) then
Universe.attackWaveSize = 2
Universe.attackWaveDeviation = 1
Universe.attackWaveUpperBound = 3
end
Universe.settlerWaveSize = linearInterpolation ( evolutionLevel ^ 1.66667 ,
Universe.expansionMinSize ,
Universe.expansionMaxSize )
Universe.settlerWaveDeviation = ( Universe.settlerWaveSize * 0.33 )
Universe.unitRefundAmount = AI_UNIT_REFUND * evolutionLevel
Universe.kamikazeThreshold = NO_RETREAT_BASE_PERCENT + ( evolutionLevel * NO_RETREAT_EVOLUTION_BONUS_MAX )
end
local function processBase ( base , tick )
base.maxAggressiveGroups = mCeil ( base.activeNests / ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS )
base.maxExpansionGroups = mCeil ( ( base.activeNests + base.activeRaidNests ) / ALL_NESTS_PER_EXPANSION_GROUPS )
if ( base.stateAI == BASE_AI_STATE_MIGRATING or base.stateAI == BASE_AI_STATE_SIEGE )
and base.resetExpensionGroupsTick <= tick
then
2023-03-22 17:54:41 -07:00
local randomDuration = randomTickDuration ( Universe.random ,
Universe.expansionMinTime ,
mFloor ( linearInterpolation ( Universe.evolutionLevel ^ 1.66667 ,
Universe.expansionMaxTime ,
Universe.expansionMinTime ) ) )
base.resetExpensionGroupsTick = tick + randomDuration
2023-03-11 20:51:13 -08:00
base.sentExpansionGroups = 0
end
local points = ( AI_POINT_GENERATOR_AMOUNT * Universe.random ( ) ) +
( base.activeNests * 0.144 ) +
( AI_POINT_GENERATOR_AMOUNT * mMax ( Universe.evolutionLevel ^ 2.5 , 0.1 ) )
if ( base.temperament == 0 ) or ( base.temperament == 1 ) then
points = points + 24
elseif ( base.temperament < 0.20 ) or ( base.temperament > 0.80 ) then
points = points + 14.4
elseif ( base.temperament < 0.35 ) or ( base.temperament > 0.65 ) then
points = points + 9.6
elseif ( base.temperament < 0.45 ) or ( base.temperament > 0.55 ) then
points = points + 4.8
end
if ( base.stateAI == BASE_AI_STATE_ONSLAUGHT ) then
points = points * 2
end
points = points * Universe.aiPointsScaler
local currentPoints = base.unitPoints
if ( currentPoints <= 0 ) then
currentPoints = 0
end
if ( currentPoints < Universe.maxPoints ) then
BaseUtils.modifyBaseUnitPoints ( base , points , " Logic Cycle " , base.x , base.y )
end
if ( base.points < Universe.maxPoints ) then
BaseUtils.modifyBaseSpecialPoints ( base , ( points * 0.75 ) , " Logic Cycle " , base.x , base.y )
end
if Universe.NEW_ENEMIES then
local deathThreshold = 0
local evolutionLevel = Universe.evolutionLevel
if ( evolutionLevel < Universe.minimumAdaptationEvolution ) then
base.deathEvents = 0
elseif ( evolutionLevel < 0.5 ) then
deathThreshold = 17000
elseif ( evolutionLevel < 0.7 ) then
deathThreshold = 34000
elseif ( evolutionLevel < 0.9 ) then
deathThreshold = 60000
else
deathThreshold = 100000
end
deathThreshold = Universe.adaptationModifier * deathThreshold
if ( ( base.deathEvents > deathThreshold ) and ( Universe.random ( ) > 0.95 ) ) then
if ( base.mutations < Universe.MAX_BASE_MUTATIONS ) then
if BaseUtils.upgradeBaseBasedOnDamage ( base ) then
base.mutations = base.mutations + 1
end
elseif ( base.mutations == Universe.MAX_BASE_MUTATIONS ) then
local roll = Universe.random ( )
if ( roll < 0.001 ) then
base.mutations = 0
if ( Universe.printBaseAdaptation ) then
game.print ( { " description.rampant--adaptationResetDebugMessage " ,
base.x ,
base.y ,
base.mutations ,
Universe.MAX_BASE_MUTATIONS } )
end
elseif ( roll > 0.999 ) then
base.mutations = base.mutations + 1
if ( Universe.printBaseAdaptation ) then
game.print ( { " description.rampant--adaptationFrozenDebugMessage " ,
base.x ,
base.y } )
end
end
end
base.damagedBy = { }
base.deathEvents = 0
end
else
base.damagedBy = { }
base.deathEvents = 0
end
if ( base.stateGenerationTick <= tick ) then
local roll = Universe.random ( )
if ( roll < 0.85 ) then
base.stateGeneration = BASE_GENERATION_STATE_ACTIVE
else
base.stateGeneration = BASE_GENERATION_STATE_DORMANT
end
base.stateGenerationTick = randomTickEvent ( Universe.random ,
tick ,
BASE_GENERATION_MIN_STATE_DURATION ,
BASE_GENERATION_MAX_STATE_DURATION )
end
base.tick = tick
end
local function temperamentPlanner ( base , evolutionLevel )
local destroyPlayerBuildings = base.destroyPlayerBuildings
local lostEnemyUnits = base.lostEnemyUnits
local lostEnemyBuilding = base.lostEnemyBuilding
local rocketLaunched = base.rocketLaunched
local builtEnemyBuilding = base.builtEnemyBuilding
local ionCannonBlasts = base.ionCannonBlasts
local artilleryBlasts = base.artilleryBlasts
local activeNests = base.activeNests
local activeRaidNests = base.activeRaidNests
local currentTemperament = base.temperamentScore
local delta = 0
if activeNests > 0 then
local val = ( 5.76 * activeNests )
delta = delta + val
else
delta = delta - 5.553792
end
if destroyPlayerBuildings > 0 then
if currentTemperament > 0 then
delta = delta - ( 5.553792 * destroyPlayerBuildings )
else
delta = delta + ( 5.553792 * destroyPlayerBuildings )
end
end
if activeRaidNests > 0 then
local val = ( 0.2304 * activeRaidNests )
delta = delta - val
else
delta = delta - 3.84
end
if lostEnemyUnits > 0 then
local multipler
if evolutionLevel < 0.3 then
multipler = 0.083328
elseif evolutionLevel < 0.5 then
multipler = 0.041472
elseif evolutionLevel < 0.7 then
multipler = 0.020736
elseif evolutionLevel < 0.9 then
multipler = 0.010368
elseif evolutionLevel < 0.9 then
multipler = 0.005184
else
multipler = 0.002592
end
local val = ( multipler * lostEnemyUnits )
if ( currentTemperament > 0 ) then
delta = delta - val
else
delta = delta + val
end
end
if lostEnemyBuilding > 0 then
local val = ( 0.576 * lostEnemyBuilding )
if ( currentTemperament > 0 ) then
delta = delta - val
else
delta = delta + val
end
end
if builtEnemyBuilding > 0 then
local val = ( 0.261952 * builtEnemyBuilding )
if ( currentTemperament > 0 ) then
delta = delta - val
else
delta = delta + val
end
else
delta = delta - 2.777088
end
if ( rocketLaunched > 0 ) then
local val = ( 27.76 * rocketLaunched )
delta = delta + val
end
if ( ionCannonBlasts > 0 ) then
local val = ( 13.924864 * ionCannonBlasts )
delta = delta + val
end
if ( artilleryBlasts > 0 ) then
local val = ( 13.924864 * artilleryBlasts )
delta = delta + val
end
delta = delta * Universe.temperamentRateModifier
base.temperamentScore = mMin ( TEMPERAMENT_RANGE_MAX , mMax ( TEMPERAMENT_RANGE_MIN , currentTemperament + delta ) )
base.temperament = ( ( base.temperamentScore + TEMPERAMENT_RANGE_MAX ) * TEMPERAMENT_DIVIDER )
if Universe.debugTemperament then
local strConsole = " Rampant Stats: \n baseId: " .. base.id .. " , aN: " .. base.activeNests ..
" , aRN: " .. base.activeRaidNests .. " , dPB: " ..
base.destroyPlayerBuildings .. " , lEU: " .. base.lostEnemyUnits .. " , lEB: " ..
base.lostEnemyBuilding .. " , rL: " .. base.rocketLaunched .. " , bEB: " ..
base.builtEnemyBuilding .. " , iCB: " .. base.ionCannonBlasts .. " , aB: " ..
base.artilleryBlasts .. " , temp: " .. base.temperament .. " , tempScore: " .. base.temperamentScore ..
" , points: " .. base.points .. " , unitPoints: " .. base.unitPoints .. " , state: " ..
STATE_ENGLISH [ base.stateAI ] .. " , surface: " .. base.map . surface.index .. " [ " ..
base.map . surface.name .. " ] " .. " , aS: " .. Universe.squadCount .. " , aB: " .. Universe.builderCount ..
" , atkSize: " .. Universe.attackWaveSize .. " , stlSize: " .. Universe.settlerWaveSize ..
" , formGroup: " .. Universe.formSquadThreshold .. " , sAgg: " .. base.sentAggressiveGroups ..
" , mAgg: " .. base.maxAggressiveGroups .. " , baseState: " .. base.stateGeneration .. " , sE: "
.. base.sentExpansionGroups .. " , mE: " .. base.maxExpansionGroups
game.print ( strConsole )
print ( strConsole )
end
end
local function processState ( base , tick )
if ( base.stateAITick > tick ) or not Universe.awake then
if ( not Universe.awake ) and ( tick >= Universe.initialPeaceTime ) then
Universe.awake = true
if Universe.printAwakenMessage then
game.print ( { " description.rampant--planetHasAwoken " } )
end
else
return
end
end
local roll = Universe.random ( )
if ( base.temperament < 0.05 ) then -- 0 - 0.05
if Universe.enabledMigration then
if ( roll < 0.30 ) then
base.stateAI = BASE_AI_STATE_MIGRATING
elseif ( roll < 0.50 ) and Universe.raidAIToggle then
base.stateAI = BASE_AI_STATE_RAIDING
elseif Universe.siegeAIToggle then
base.stateAI = BASE_AI_STATE_SIEGE
else
base.stateAI = BASE_AI_STATE_MIGRATING
end
else
if Universe.raidAIToggle then
if ( roll < 0.70 ) then
base.stateAI = BASE_AI_STATE_RAIDING
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
end
elseif ( base.temperament < 0.20 ) then -- 0.05 - 0.2
if ( Universe.enabledMigration ) then
if ( roll < 0.4 ) then
base.stateAI = BASE_AI_STATE_MIGRATING
elseif ( roll < 0.55 ) and Universe.raidAIToggle then
base.stateAI = BASE_AI_STATE_RAIDING
elseif Universe.siegeAIToggle then
base.stateAI = BASE_AI_STATE_SIEGE
else
base.stateAI = BASE_AI_STATE_MIGRATING
end
else
if Universe.raidAIToggle then
if ( roll < 0.40 ) then
base.stateAI = BASE_AI_STATE_AGGRESSIVE
else
base.stateAI = BASE_AI_STATE_RAIDING
end
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
end
elseif ( base.temperament < 0.4 ) then -- 0.2 - 0.4
if ( Universe.enabledMigration ) then
if ( roll < 0.2 ) and Universe.raidAIToggle then
base.stateAI = BASE_AI_STATE_RAIDING
elseif ( roll < 0.2 ) then
base.stateAI = BASE_AI_STATE_AGGRESSIVE
elseif ( roll < 0.8 ) then
base.stateAI = BASE_AI_STATE_MIGRATING
elseif Universe.peacefulAIToggle then
base.stateAI = BASE_AI_STATE_PEACEFUL
else
base.stateAI = BASE_AI_STATE_MIGRATING
end
else
if ( roll < 0.3 ) then
base.stateAI = BASE_AI_STATE_AGGRESSIVE
elseif ( roll < 0.6 ) and Universe.raidAIToggle then
base.stateAI = BASE_AI_STATE_RAIDING
elseif ( roll < 0.6 ) then
base.stateAI = BASE_AI_STATE_AGGRESSIVE
elseif Universe.peacefulAIToggle then
base.stateAI = BASE_AI_STATE_PEACEFUL
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
end
elseif ( base.temperament < 0.6 ) then -- 0.4 - 0.6
if ( roll < 0.4 ) then
base.stateAI = BASE_AI_STATE_AGGRESSIVE
elseif ( roll < 0.5 ) and Universe.raidAIToggle then
base.stateAI = BASE_AI_STATE_RAIDING
elseif ( roll < 0.75 ) and Universe.peacefulAIToggle then
base.stateAI = BASE_AI_STATE_PEACEFUL
else
if Universe.enabledMigration then
base.stateAI = BASE_AI_STATE_MIGRATING
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
end
elseif ( base.temperament < 0.8 ) then -- 0.6 - 0.8
if ( roll < 0.4 ) then
base.stateAI = BASE_AI_STATE_AGGRESSIVE
elseif ( roll < 0.6 ) then
base.stateAI = BASE_AI_STATE_ONSLAUGHT
elseif ( roll < 0.8 ) then
base.stateAI = BASE_AI_STATE_RAIDING
elseif Universe.peacefulAIToggle then
base.stateAI = BASE_AI_STATE_PEACEFUL
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
elseif ( base.temperament < 0.95 ) then -- 0.8 - 0.95
if ( Universe.enabledMigration and Universe.raidAIToggle ) then
if ( roll < 0.20 ) and Universe.siegeAIToggle then
base.stateAI = BASE_AI_STATE_SIEGE
elseif ( roll < 0.45 ) then
base.stateAI = BASE_AI_STATE_RAIDING
elseif ( roll < 0.85 ) then
base.stateAI = BASE_AI_STATE_ONSLAUGHT
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
elseif ( Universe.enabledMigration ) then
if ( roll < 0.20 ) and Universe.siegeAIToggle then
base.stateAI = BASE_AI_STATE_SIEGE
elseif ( roll < 0.75 ) then
base.stateAI = BASE_AI_STATE_ONSLAUGHT
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
elseif ( Universe.raidAIToggle ) then
if ( roll < 0.45 ) then
base.stateAI = BASE_AI_STATE_ONSLAUGHT
elseif ( roll < 0.75 ) then
base.stateAI = BASE_AI_STATE_RAIDING
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
else
if ( roll < 0.65 ) then
base.stateAI = BASE_AI_STATE_ONSLAUGHT
else
base.stateAI = BASE_AI_STATE_AGGRESSIVE
end
end
else
if ( Universe.enabledMigration and Universe.raidAIToggle ) then
if ( roll < 0.30 ) and Universe.siegeAIToggle then
base.stateAI = BASE_AI_STATE_SIEGE
elseif ( roll < 0.65 ) then
base.stateAI = BASE_AI_STATE_RAIDING
else
base.stateAI = BASE_AI_STATE_ONSLAUGHT
end
elseif ( Universe.enabledMigration ) then
if ( roll < 0.30 ) and Universe.siegeAIToggle then
base.stateAI = BASE_AI_STATE_SIEGE
else
base.stateAI = BASE_AI_STATE_ONSLAUGHT
end
elseif ( Universe.raidAIToggle ) then
if ( roll < 0.45 ) then
base.stateAI = BASE_AI_STATE_ONSLAUGHT
else
base.stateAI = BASE_AI_STATE_RAIDING
end
else
base.stateAI = BASE_AI_STATE_ONSLAUGHT
end
end
local remainingUnits = base.lostEnemyUnits % 20
if remainingUnits ~= 0 then
BaseUtils.modifyBaseUnitPoints ( base , - ( remainingUnits * UNIT_DEATH_POINT_COST ) , remainingUnits .. " Units Lost " )
end
base.destroyPlayerBuildings = 0
base.lostEnemyUnits = 0
base.lostEnemyBuilding = 0
base.rocketLaunched = 0
base.builtEnemyBuilding = 0
base.ionCannonBlasts = 0
base.artilleryBlasts = 0
base.stateAITick = randomTickEvent ( Universe.random , tick , BASE_AI_MIN_STATE_DURATION , BASE_AI_MAX_STATE_DURATION )
if Universe.printAIStateChanges then
game.print ( base.id .. " : AI is now: " .. STATE_ENGLISH [ base.stateAI ] .. " , Next state change is in "
.. string.format ( " %.2f " , ( base.stateAITick - tick ) / ( 60 * 60 ) ) .. " minutes @ " ..
getTimeStringFromTick ( base.stateAITick ) .. " playtime " )
end
end
function BaseUtils . processBaseAIs ( tick )
local baseId = Universe.processBaseAIIterator
local base
if not baseId then
baseId , base = next ( Universe.bases , nil )
else
base = Universe.bases [ baseId ]
end
if not baseId then
Universe.processBaseAIIterator = nil
return
else
Universe.processBaseAIIterator = next ( Universe.bases , baseId )
if ( tick - base.tick ) <= BASE_PROCESS_INTERVAL then
return
end
temperamentPlanner ( base , Universe.evolutionLevel )
processState ( base , tick )
processBase ( base , tick )
end
end
2023-03-11 12:53:06 -08:00
function BaseUtils . init ( universe )
Universe = universe
end
BaseUtilsG = BaseUtils
return BaseUtils