mirror of
https://github.com/veden/Rampant.git
synced 2026-06-19 22:15:20 +02:00
lots of refactoring
This commit is contained in:
@@ -56,9 +56,19 @@ Configure Options not in game menu:
|
||||
# Version History
|
||||
|
||||
0.15.11 -
|
||||
- Fixed: Player region scan can no longer overlap passive map scan causing double processing
|
||||
- Tweak: Increased rally cry chance from 0.02(@100 evo) to 0.08(@100 evo) compensate for the once per logic cycle per chunk
|
||||
- Improvement: Changed biter base detection from slow map scan to event
|
||||
- Improvement: Added negative score contribution to nest causing biters to move around nests instead of through
|
||||
- Improvement: Increased player pheromone for weight multipler from 25 to 50 for hunting parties
|
||||
- Optimization: Player region scan can no longer overlap passive map scan causing double processing
|
||||
- Optimization: Short circuit rally cry search if not enough points to make a squad
|
||||
- Optimization: Short circuit vengence squad search if not enough points to make a squad
|
||||
- Optimization: Short circuit attack groups search if not enough points to make a squad
|
||||
- Optimization: Precompute retreatThreshold, maxSquads, rallyThreshold, formSquadThreshold, attackWaveSize, attackWaveSizeLowerBound, attackWaveSizeUpperBound, attackWaveSizeDeviation, kamikazeThreshold, attackWaveThreshold once per logic cycle
|
||||
- Optimization: Reduced garbage generated by pheromone map processing
|
||||
- Optimization: Reduced garbage generated by attack formation scanning
|
||||
- Framework: renamed chunk pX to x, so chunks could be used in calls to things like get_pollution
|
||||
|
||||
0.15.10 -
|
||||
- Fix: nil chunk in pheromone utils(https://mods.factorio.com/mods/Veden/Rampant/discussion/13806)
|
||||
|
||||
+26
-21
@@ -19,12 +19,6 @@ local roundToNearest = mathUtils.roundToNearest
|
||||
function upgrade.attempt(natives, regionMap)
|
||||
local starting = global.version
|
||||
if (global.version == nil) then
|
||||
|
||||
-- removed in version 9
|
||||
-- regionMap.pQ = {{}} -- processing queue
|
||||
-- regionMap.pI = 1 -- insertion location for chunk processing
|
||||
-- regionMap.pP = 1 -- index for the chunk set to process
|
||||
-- regionMap.pR = -1 -- current processing roll
|
||||
natives.squads = {}
|
||||
natives.scouts = {}
|
||||
natives.tunnels = {}
|
||||
@@ -32,16 +26,6 @@ function upgrade.attempt(natives, regionMap)
|
||||
|
||||
global.version = constants.VERSION_5
|
||||
end
|
||||
if (global.version < constants.VERSION_9) then
|
||||
|
||||
-- remove version 5 references
|
||||
regionMap.pQ = nil
|
||||
regionMap.pI = nil
|
||||
regionMap.pP = nil
|
||||
regionMap.pR = nil
|
||||
|
||||
global.version = constants.VERSION_9
|
||||
end
|
||||
if (global.version < constants.VERSION_10) then
|
||||
for _,squad in pairs(natives.squads) do
|
||||
squad.frenzy = false
|
||||
@@ -98,9 +82,6 @@ function upgrade.attempt(natives, regionMap)
|
||||
-- been made redundant
|
||||
natives.rallyCries = nil
|
||||
|
||||
-- switched over to tick event
|
||||
regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
|
||||
regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS)
|
||||
-- needs to be on inner logic tick loop interval
|
||||
natives.stateTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
|
||||
natives.temperamentTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
|
||||
@@ -131,9 +112,24 @@ function upgrade.attempt(natives, regionMap)
|
||||
end
|
||||
if (global.version < constants.VERSION_23) then
|
||||
|
||||
game.forces.enemy.ai_controllable = false
|
||||
|
||||
-- used to precompute some values per logic cycle
|
||||
natives.retreatThreshold = 0
|
||||
natives.maxSquads = 0
|
||||
natives.rallyThreshold = 0
|
||||
natives.formSquadThreshold = 0
|
||||
natives.attackWaveSize = 0
|
||||
natives.attackWaveDeviation = 0
|
||||
natives.attackWaveLowerBound = 0
|
||||
natives.attackWaveUpperBound = 0
|
||||
natives.unitRefundAmount = 0
|
||||
natives.attackWaveThreshold = 0
|
||||
|
||||
natives.useCustomAI = settings.startup["rampant-useCustomAI"].value
|
||||
if natives.useCustomAI then
|
||||
game.forces.enemy.ai_controllable = false
|
||||
else
|
||||
game.forces.enemy.ai_controllable = true
|
||||
end
|
||||
natives.bases = {}
|
||||
natives.baseDistanceMin = 0
|
||||
natives.baseIndex = 1
|
||||
@@ -145,4 +141,13 @@ function upgrade.attempt(natives, regionMap)
|
||||
return starting ~= global.version
|
||||
end
|
||||
|
||||
function upgrade.compareTable(entities, option, new)
|
||||
local changed = false
|
||||
if (entities[option] ~= new) then
|
||||
entities[option] = new
|
||||
changed = true
|
||||
end
|
||||
return changed, new
|
||||
end
|
||||
|
||||
return upgrade
|
||||
|
||||
+13
-6
@@ -1,19 +1,26 @@
|
||||
local config = {}
|
||||
|
||||
-- imported
|
||||
|
||||
local mathUtils = require("libs/MathUtils")
|
||||
|
||||
-- imported functions
|
||||
|
||||
local gaussianRandomRange = mathUtils.gaussianRandomRange
|
||||
local mCeil = math.ceil
|
||||
|
||||
-- configurations
|
||||
|
||||
--[[
|
||||
attackWaveScaling is used to calculate the attack wave size from the evolutionFactor
|
||||
default is natives.attackWaveMaxSize * (evolutionFactor ^ 1.666667)
|
||||
DOES NOT affect vanilla biters waves
|
||||
--]]
|
||||
config.attackWaveScaling = function (evolutionFactor, natives)
|
||||
local attackWaveMaxSize = natives.attackWaveMaxSize
|
||||
return math.ceil(gaussianRandomRange(attackWaveMaxSize * (evolutionFactor ^ 1.66667),
|
||||
(attackWaveMaxSize * 0.5) * 0.333,
|
||||
1,
|
||||
attackWaveMaxSize + (attackWaveMaxSize * 0.25)))
|
||||
config.attackWaveScaling = function (natives)
|
||||
return mCeil(gaussianRandomRange(natives.attackWaveSize,
|
||||
natives.attackWaveDeviation,
|
||||
natives.attackWaveLowerBound,
|
||||
natives.attackWaveUpperBound))
|
||||
end
|
||||
|
||||
return config
|
||||
|
||||
+91
-68
@@ -16,6 +16,7 @@ local interop = require("libs/Interop")
|
||||
local tests = require("tests")
|
||||
local upgrade = require("Upgrade")
|
||||
local baseUtils = require("libs/BaseUtils")
|
||||
local mathUtils = require("libs/MathUtils")
|
||||
|
||||
-- constants
|
||||
|
||||
@@ -24,10 +25,9 @@ local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
|
||||
|
||||
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
|
||||
|
||||
local BASE_RALLY_CHANCE = constants.BASE_RALLY_CHANCE
|
||||
local BONUS_RALLY_CHANCE = constants.BONUS_RALLY_CHANCE
|
||||
-- imported functions
|
||||
|
||||
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
|
||||
local roundToNearest = mathUtils.roundToNearest
|
||||
|
||||
-- imported functions
|
||||
|
||||
@@ -57,6 +57,7 @@ local retreatUnits = aiDefense.retreatUnits
|
||||
|
||||
local addRemovePlayerEntity = entityUtils.addRemovePlayerEntity
|
||||
local unregisterEnemyBaseStructure = baseUtils.unregisterEnemyBaseStructure
|
||||
local registerEnemyBaseStructure = baseUtils.registerEnemyBaseStructure
|
||||
local makeImmortalEntity = entityUtils.makeImmortalEntity
|
||||
|
||||
local processBases = baseProcessor.processBases
|
||||
@@ -83,25 +84,34 @@ local function onChunkGenerated(event)
|
||||
end
|
||||
end
|
||||
|
||||
local function reprocessChunks()
|
||||
game.surfaces[1].print("Rampant - Reindexing chunks, please wait")
|
||||
local function rebuildRegionMap()
|
||||
game.surfaces[1].print("Rampant - Reindexing chunks, please wait.")
|
||||
-- clear old regionMap processing Queue
|
||||
-- prevents queue adding duplicate chunks
|
||||
-- chunks are by key, so should overwrite old
|
||||
|
||||
global.regionMap = {}
|
||||
regionMap = global.regionMap
|
||||
regionMap.processQueue = {}
|
||||
regionMap.processPointer = 1
|
||||
regionMap.scanPointer = 1
|
||||
|
||||
-- switched over to tick event
|
||||
regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
|
||||
regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS)
|
||||
|
||||
-- clear pending chunks, will be added when loop runs below
|
||||
pendingChunks = {}
|
||||
|
||||
-- queue all current chunks that wont be generated during play
|
||||
local surface = game.surfaces[1]
|
||||
local tick = game.tick
|
||||
for chunk in surface.get_chunks() do
|
||||
onChunkGenerated({ tick = game.tick,
|
||||
onChunkGenerated({ tick = tick,
|
||||
surface = surface,
|
||||
area = { left_top = { x = chunk.x * 32,
|
||||
y = chunk.y * 32 }}})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onModSettingsChange(event)
|
||||
@@ -110,45 +120,51 @@ local function onModSettingsChange(event)
|
||||
return false
|
||||
end
|
||||
|
||||
natives.safeBuildings = settings.global["rampant-safeBuildings"].value
|
||||
upgrade.compareTable(natives, "safeBuildings", settings.global["rampant-safeBuildings"].value)
|
||||
|
||||
natives.safeEntities["curved-rail"] = settings.global["rampant-safeBuildings-curvedRail"].value
|
||||
natives.safeEntities["straight-rail"] = settings.global["rampant-safeBuildings-straightRail"].value
|
||||
natives.safeEntities["rail-signal"] = settings.global["rampant-safeBuildings-railSignals"].value
|
||||
natives.safeEntities["rail-chain-signal"] = settings.global["rampant-safeBuildings-railChainSignals"].value
|
||||
natives.safeEntities["train-stop"] = settings.global["rampant-safeBuildings-trainStops"].value
|
||||
upgrade.compareTable(natives.safeEntities, "curved-rail", settings.global["rampant-safeBuildings-curvedRail"].value)
|
||||
upgrade.compareTable(natives.safeEntities, "straight-rail", settings.global["rampant-safeBuildings-straightRail"].value)
|
||||
upgrade.compareTable(natives.safeEntities, "rail-signal", settings.global["rampant-safeBuildings-railSignals"].value)
|
||||
upgrade.compareTable(natives.safeEntities, "rail-chain-signal", settings.global["rampant-safeBuildings-railChainSignals"].value)
|
||||
upgrade.compareTable(natives.safeEntities, "train-stop", settings.global["rampant-safeBuildings-trainStops"].value)
|
||||
|
||||
local poles = settings.global["rampant-safeBuildings-bigElectricPole"].value
|
||||
natives.safeEntityName["big-electric-pole"] = poles
|
||||
natives.safeEntityName["big-electric-pole-2"] = poles
|
||||
natives.safeEntityName["big-electric-pole-3"] = poles
|
||||
natives.safeEntityName["big-electric-pole-4"] = poles
|
||||
local changed, newValue = upgrade.compareTable(natives.safeEntityName,
|
||||
"big-electric-pole",
|
||||
settings.global["rampant-safeBuildings-bigElectricPole"].value)
|
||||
if changed then
|
||||
natives.safeEntityName["big-electric-pole"] = newValue
|
||||
natives.safeEntityName["big-electric-pole-2"] = newValue
|
||||
natives.safeEntityName["big-electric-pole-3"] = newValue
|
||||
natives.safeEntityName["big-electric-pole-4"] = newValue
|
||||
end
|
||||
|
||||
natives.attackUsePlayer = settings.global["rampant-attackWaveGenerationUsePlayerProximity"].value
|
||||
natives.attackUsePollution = settings.global["rampant-attackWaveGenerationUsePollution"].value
|
||||
upgrade.compareTable(natives, "attackUsePlayer", settings.global["rampant-attackWaveGenerationUsePlayerProximity"].value)
|
||||
upgrade.compareTable(natives, "attackUsePollution", settings.global["rampant-attackWaveGenerationUsePollution"].value)
|
||||
|
||||
natives.attackThresholdMin = settings.global["rampant-attackWaveGenerationThresholdMin"].value
|
||||
natives.attackThresholdMax = settings.global["rampant-attackWaveGenerationThresholdMax"].value
|
||||
natives.attackThresholdRange = natives.attackThresholdMax - natives.attackThresholdMin
|
||||
natives.attackWaveMaxSize = settings.global["rampant-attackWaveMaxSize"].value
|
||||
natives.attackPlayerThreshold = settings.global["rampant-attackPlayerThreshold"].value
|
||||
natives.aiNocturnalMode = settings.global["rampant-permanentNocturnal"].value
|
||||
natives.aiPointsScaler = settings.global["rampant-aiPointsScaler"].value
|
||||
upgrade.compareTable(natives, "attackThresholdMin", settings.global["rampant-attackWaveGenerationThresholdMin"].value)
|
||||
upgrade.compareTable(natives, "attackThresholdMax", settings.global["rampant-attackWaveGenerationThresholdMax"].value)
|
||||
upgrade.compareTable(natives, "attackThresholdRange", natives.attackThresholdMax - natives.attackThresholdMin)
|
||||
upgrade.compareTable(natives, "attackWaveMaxSize", settings.global["rampant-attackWaveMaxSize"].value)
|
||||
upgrade.compareTable(natives, "attackPlayerThreshold", settings.global["rampant-attackPlayerThreshold"].value)
|
||||
upgrade.compareTable(natives, "aiNocturnalMode", settings.global["rampant-permanentNocturnal"].value)
|
||||
upgrade.compareTable(natives, "aiPointsScaler", settings.global["rampant-aiPointsScaler"].value)
|
||||
|
||||
local useCustomAI = natives.useCustomAI
|
||||
natives.useCustomAI = settings.startup["rampant-useCustomAI"].value
|
||||
if not useCustomAI and natives.useCustomAI then
|
||||
reprocessChunks()
|
||||
changed, newValue = upgrade.compareTable(natives, "useCustomAI", settings.startup["rampant-useCustomAI"].value)
|
||||
if natives.useCustomAI then
|
||||
game.forces.enemy.ai_controllable = false
|
||||
else
|
||||
game.forces.enemy.ai_controllable = true
|
||||
end
|
||||
if changed and newValue then
|
||||
rebuildRegionMap()
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function onConfigChanged()
|
||||
if upgrade.attempt(natives, regionMap) and onModSettingsChange(nil) then
|
||||
reprocessChunks()
|
||||
rebuildRegionMap()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -157,31 +173,34 @@ local function onTick(event)
|
||||
if (tick == regionMap.processTick) then
|
||||
regionMap.processTick = regionMap.processTick + INTERVAL_PROCESS
|
||||
local surface = game.surfaces[1]
|
||||
local evolutionFactor = game.forces.enemy.evolution_factor
|
||||
local players = game.players
|
||||
|
||||
processPendingChunks(natives, regionMap, surface, pendingChunks, tick)
|
||||
scanMap(regionMap, surface, natives, evolutionFactor)
|
||||
scanMap(regionMap, surface, natives)
|
||||
|
||||
if (tick == regionMap.logicTick) then
|
||||
regionMap.logicTick = regionMap.logicTick + INTERVAL_LOGIC
|
||||
|
||||
planning(natives, evolutionFactor, tick, surface)
|
||||
|
||||
cleanSquads(natives, evolutionFactor)
|
||||
regroupSquads(natives, evolutionFactor)
|
||||
|
||||
processPlayers(players, regionMap, surface, natives, evolutionFactor, tick)
|
||||
local players = game.players
|
||||
|
||||
if (natives.useCustomAI) then
|
||||
processBases(regionMap, surface, natives, tick)
|
||||
end
|
||||
planning(natives,
|
||||
game.forces.enemy.evolution_factor,
|
||||
tick,
|
||||
surface)
|
||||
|
||||
squadBeginAttack(natives, players, evolutionFactor)
|
||||
cleanSquads(natives)
|
||||
-- regroupSquads(natives)
|
||||
|
||||
processPlayers(players, regionMap, surface, natives, tick)
|
||||
|
||||
-- if (natives.useCustomAI) then
|
||||
-- processBases(regionMap, surface, natives, tick)
|
||||
-- end
|
||||
|
||||
squadBeginAttack(natives, players)
|
||||
squadAttack(regionMap, surface, natives)
|
||||
end
|
||||
|
||||
processMap(regionMap, surface, natives, evolutionFactor)
|
||||
processMap(regionMap, surface, natives, tick)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -212,27 +231,24 @@ local function onDeath(event)
|
||||
-- drop death pheromone where unit died
|
||||
deathScent(deathChunk)
|
||||
|
||||
if event.force and (event.force.name == "player") then
|
||||
local evolutionFactor = entity.force.evolution_factor
|
||||
if event.force and (event.force.name == "player") and (deathChunk[MOVEMENT_PHEROMONE] < natives.retreatThreshold) then
|
||||
local tick = event.tick
|
||||
|
||||
if (deathChunk[MOVEMENT_PHEROMONE] < -(evolutionFactor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)) then
|
||||
retreatUnits(deathChunk,
|
||||
convertUnitGroupToSquad(natives,
|
||||
entity.unit_group),
|
||||
regionMap,
|
||||
surface,
|
||||
natives,
|
||||
tick)
|
||||
local rallyThreshold = BASE_RALLY_CHANCE + (evolutionFactor * BONUS_RALLY_CHANCE)
|
||||
if (math.random() < rallyThreshold) then
|
||||
rallyUnits(deathChunk,
|
||||
regionMap,
|
||||
surface,
|
||||
natives,
|
||||
evolutionFactor,
|
||||
tick)
|
||||
end
|
||||
|
||||
retreatUnits(deathChunk,
|
||||
convertUnitGroupToSquad(natives,
|
||||
entity.unit_group),
|
||||
regionMap,
|
||||
surface,
|
||||
natives,
|
||||
tick)
|
||||
if (math.random() < natives.rallyThreshold) and not surface.peaceful_mode then
|
||||
local tempNeighbors = {false, false, false, false, false, false, false, false}
|
||||
rallyUnits(deathChunk,
|
||||
regionMap,
|
||||
surface,
|
||||
natives,
|
||||
tick,
|
||||
tempNeighbors)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -259,6 +275,11 @@ local function onDeath(event)
|
||||
end
|
||||
end
|
||||
|
||||
local function onEnemyBaseBuild(event)
|
||||
local entity = event.entity
|
||||
registerEnemyBaseStructure(regionMap, entity, nil)
|
||||
end
|
||||
|
||||
local function onSurfaceTileChange(event)
|
||||
local player = game.players[event.player_index]
|
||||
if (player.surface.index == 1) then
|
||||
@@ -288,6 +309,8 @@ script.on_configuration_changed(onConfigChanged)
|
||||
|
||||
script.on_event(defines.events.on_player_built_tile, onSurfaceTileChange)
|
||||
|
||||
script.on_event(defines.events.on_biter_base_built,
|
||||
onEnemyBaseBuild)
|
||||
script.on_event({defines.events.on_preplayer_mined_item,
|
||||
defines.events.on_robot_pre_mined},
|
||||
onPickUp)
|
||||
|
||||
+12
-10
@@ -62,8 +62,10 @@ function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
local squads = natives.squads
|
||||
local attackPosition
|
||||
local attackCmd
|
||||
local tempNeighbors
|
||||
|
||||
if (#squads > 0) then
|
||||
tempNeighbors = {false, false, false, false, false, false, false, false}
|
||||
attackPosition = {x=0, y=0}
|
||||
attackCmd = { type = DEFINES_COMMAND_ATTACK_AREA,
|
||||
destination = attackPosition,
|
||||
@@ -80,7 +82,7 @@ function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
local chunk = getChunkByPosition(regionMap, groupPosition.x, groupPosition.y)
|
||||
if chunk then
|
||||
local attackChunk, attackDirection = scoreNeighborsWithDirection(chunk,
|
||||
getNeighborChunksWithDirection(regionMap, chunk.cX, chunk.cY),
|
||||
getNeighborChunksWithDirection(regionMap, chunk.cX, chunk.cY, tempNeighbors),
|
||||
validLocation,
|
||||
scoreAttackLocation,
|
||||
squad,
|
||||
@@ -124,24 +126,24 @@ function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
end
|
||||
end
|
||||
|
||||
function aiAttack.squadBeginAttack(natives, players, evolution_factor)
|
||||
function aiAttack.squadBeginAttack(natives, players)
|
||||
local squads = natives.squads
|
||||
for i=1,#squads do
|
||||
local squad = squads[i]
|
||||
if (squad.status == SQUAD_GUARDING) and squad.group.valid then
|
||||
local kamikazeThreshold = calculateKamikazeThreshold(squad, natives, evolution_factor)
|
||||
local group = squad.group
|
||||
if (squad.status == SQUAD_GUARDING) and group.valid then
|
||||
local groupPosition = group.position
|
||||
local kamikazeThreshold = calculateKamikazeThreshold(squad, natives)
|
||||
|
||||
local playerNearby = playersWithinProximityToPosition(players, squad.group.position, 100)
|
||||
local playerNearby = playersWithinProximityToPosition(players, groupPosition, 100)
|
||||
if playerNearby then
|
||||
squad.frenzy = true
|
||||
squad.frenzyPosition.x = squad.group.position.x
|
||||
squad.frenzyPosition.y = squad.group.position.y
|
||||
squad.frenzyPosition.x = groupPosition.x
|
||||
squad.frenzyPosition.y = groupPosition.y
|
||||
end
|
||||
|
||||
if (math.random() < 0.70) then
|
||||
if (math.random() < kamikazeThreshold) then
|
||||
squad.kamikaze = true
|
||||
end
|
||||
squad.kamikaze = math.random() < kamikazeThreshold
|
||||
squad.status = SQUAD_RAIDING
|
||||
end
|
||||
end
|
||||
|
||||
+38
-45
@@ -15,8 +15,6 @@ local BASE_PHEROMONE = constants.BASE_PHEROMONE
|
||||
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
|
||||
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
|
||||
|
||||
local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT
|
||||
|
||||
local AI_SQUAD_COST = constants.AI_SQUAD_COST
|
||||
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
|
||||
|
||||
@@ -24,7 +22,7 @@ local RALLY_TRIGGERED = constants.RALLY_TRIGGERED
|
||||
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
|
||||
|
||||
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
|
||||
local NORTH_SOUTH_PASSABLE = constants.NORTH_SOUTH_PASSABLE
|
||||
local EAST_WEST_PASSABLE = constants.EAST_WEST_PASSABLE
|
||||
|
||||
@@ -43,27 +41,22 @@ local scoreNeighbors = neighborUtils.scoreNeighbors
|
||||
local createSquad = unitGroupUtils.createSquad
|
||||
local attackWaveScaling = config.attackWaveScaling
|
||||
|
||||
local mMax = math.max
|
||||
|
||||
-- module code
|
||||
|
||||
local function attackWaveValidCandidate(chunk, natives, surface, evolutionFactor)
|
||||
local function attackWaveValidCandidate(chunk, natives, surface)
|
||||
local total = 0;
|
||||
|
||||
if natives.attackUsePlayer then
|
||||
local playerPheromone = chunk[PLAYER_PHEROMONE]
|
||||
if (playerPheromone > natives.attackPlayerThreshold) and (playerPheromone > 0) then
|
||||
if (playerPheromone > natives.attackPlayerThreshold) then
|
||||
total = total + chunk[PLAYER_PHEROMONE]
|
||||
end
|
||||
end
|
||||
if natives.attackUsePollution then
|
||||
total = total + surface.get_pollution(chunk)
|
||||
end
|
||||
|
||||
local threshold = natives.attackThresholdRange
|
||||
local delta = threshold * evolutionFactor
|
||||
|
||||
return total > ((threshold - delta) + natives.attackThresholdMin)
|
||||
return total > natives.attackWaveThreshold
|
||||
end
|
||||
|
||||
local function scoreUnitGroupLocation(squad, neighborChunk, surface)
|
||||
@@ -71,57 +64,57 @@ local function scoreUnitGroupLocation(squad, neighborChunk, surface)
|
||||
end
|
||||
|
||||
local function validUnitGroupLocation(x, chunk, neighborChunk)
|
||||
return neighborChunk[NORTH_SOUTH_PASSABLE] and neighborChunk[EAST_WEST_PASSABLE] and neighborChunk[NEST_COUNT] ~= 0
|
||||
return neighborChunk[NORTH_SOUTH_PASSABLE] and neighborChunk[EAST_WEST_PASSABLE] and (neighborChunk[NEST_COUNT] ~= 0)
|
||||
end
|
||||
|
||||
function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFactor, tick)
|
||||
if (tick - chunk[RALLY_TRIGGERED] > INTERVAL_LOGIC) then
|
||||
function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, tick, tempNeighbors)
|
||||
if (tick - chunk[RALLY_TRIGGERED] > INTERVAL_LOGIC) and (natives.points >= AI_VENGENCE_SQUAD_COST) then
|
||||
chunk[RALLY_TRIGGERED] = tick
|
||||
local cX = chunk.cX
|
||||
local cY = chunk.cY
|
||||
for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE do
|
||||
for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE do
|
||||
local rallyChunk = getChunkByIndex(regionMap, x, y)
|
||||
if rallyChunk and (x ~= cX) and (y ~= cY) and (rallyChunk[NEST_COUNT] ~= 0) then
|
||||
aiBuilding.formSquads(regionMap, surface, natives, rallyChunk, evolutionFactor, AI_VENGENCE_SQUAD_COST)
|
||||
if rallyChunk and (rallyChunk[NEST_COUNT] ~= 0) and (x ~= cX) and (y ~= cY) then
|
||||
aiBuilding.formSquads(regionMap, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST, tempNeighbors)
|
||||
if (natives.points < AI_VENGENCE_SQUAD_COST) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function aiBuilding.formSquads(regionMap, surface, natives, chunk, evolution_factor, cost)
|
||||
if (natives.points > cost) and (chunk[NEST_COUNT] ~= 0) and (#natives.squads < (AI_MAX_SQUAD_COUNT * evolution_factor)) then
|
||||
local valid = not surface.peaceful_mode and
|
||||
((cost == AI_VENGENCE_SQUAD_COST) or
|
||||
((cost == AI_SQUAD_COST) and attackWaveValidCandidate(chunk, natives, surface, evolution_factor)))
|
||||
function aiBuilding.formSquads(regionMap, surface, natives, chunk, cost, tempNeighbors)
|
||||
local valid = ((cost == AI_VENGENCE_SQUAD_COST) or
|
||||
((cost == AI_SQUAD_COST) and attackWaveValidCandidate(chunk, natives, surface)))
|
||||
|
||||
if valid and (math.random() < mMax((0.25 * evolution_factor), 0.10)) then
|
||||
if valid and (math.random() < natives.formSquadThreshold) then
|
||||
|
||||
local squadPath, _ = scoreNeighbors(chunk,
|
||||
getNeighborChunks(regionMap, chunk.cX, chunk.cY, tempNeighbors),
|
||||
validUnitGroupLocation,
|
||||
scoreUnitGroupLocation,
|
||||
nil,
|
||||
surface,
|
||||
false)
|
||||
if squadPath then
|
||||
local squadPosition = { x = squadPath.x + HALF_CHUNK_SIZE,
|
||||
y = squadPath.y + HALF_CHUNK_SIZE }
|
||||
|
||||
local squadPath, _ = scoreNeighbors(chunk,
|
||||
getNeighborChunks(regionMap, chunk.cX, chunk.cY),
|
||||
validUnitGroupLocation,
|
||||
scoreUnitGroupLocation,
|
||||
nil,
|
||||
surface,
|
||||
false)
|
||||
if squadPath then
|
||||
local squadPosition = { x = squadPath.x + HALF_CHUNK_SIZE,
|
||||
y = squadPath.y + HALF_CHUNK_SIZE }
|
||||
|
||||
local squad = createSquad(squadPosition, surface, natives)
|
||||
|
||||
squad.rabid = math.random() < 0.03
|
||||
local squad = createSquad(squadPosition, surface, natives)
|
||||
|
||||
squad.rabid = math.random() < 0.03
|
||||
|
||||
local scaledWaveSize = attackWaveScaling(evolution_factor, natives)
|
||||
local foundUnits = surface.set_multi_command({ command = { type = DEFINES_COMMAND_GROUP,
|
||||
group = squad.group,
|
||||
distraction = DEFINES_DISTRACTION_NONE },
|
||||
unit_count = scaledWaveSize,
|
||||
unit_search_distance = (CHUNK_SIZE * 3)})
|
||||
if (foundUnits > 0) then
|
||||
natives.points = natives.points - cost
|
||||
end
|
||||
local scaledWaveSize = attackWaveScaling(natives)
|
||||
local foundUnits = surface.set_multi_command({ command = { type = DEFINES_COMMAND_GROUP,
|
||||
group = squad.group,
|
||||
distraction = DEFINES_DISTRACTION_NONE },
|
||||
unit_count = scaledWaveSize,
|
||||
unit_search_distance = TRIPLE_CHUNK_SIZE })
|
||||
if (foundUnits > 0) then
|
||||
natives.points = natives.points - cost
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+2
-1
@@ -54,6 +54,7 @@ function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick)
|
||||
if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (chunk[NEST_COUNT] == 0) and (chunk[WORM_COUNT] == 0) then
|
||||
local performRetreat = false
|
||||
local enemiesToSquad = nil
|
||||
local tempNeighbors = {false, false, false, false, false, false, false, false}
|
||||
|
||||
if not squad then
|
||||
enemiesToSquad = surface.find_enemy_units(chunk, RETREAT_GRAB_RADIUS)
|
||||
@@ -65,7 +66,7 @@ function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick)
|
||||
if performRetreat then
|
||||
chunk[RETREAT_TRIGGERED] = tick
|
||||
local exitPath,_ = scoreNeighborsWithDirection(chunk,
|
||||
getNeighborChunksWithDirection(regionMap, chunk.cX, chunk.cY),
|
||||
getNeighborChunksWithDirection(regionMap, chunk.cX, chunk.cY, tempNeighbors),
|
||||
validRetreatLocation,
|
||||
scoreRetreatLocation,
|
||||
nil,
|
||||
|
||||
+29
-2
@@ -5,13 +5,18 @@ local aiPlanning = {}
|
||||
local constants = require("Constants")
|
||||
local mathUtils = require("MathUtils")
|
||||
local aiPredicates = require("AIPredicates")
|
||||
|
||||
|
||||
-- constants
|
||||
|
||||
local NO_RETREAT_BASE_PERCENT = constants.NO_RETREAT_BASE_PERCENT
|
||||
local NO_RETREAT_EVOLUTION_BONUS_MAX = constants.NO_RETREAT_EVOLUTION_BONUS_MAX
|
||||
|
||||
local AI_STATE_PEACEFUL = constants.AI_STATE_PEACEFUL
|
||||
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
|
||||
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
|
||||
|
||||
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
|
||||
|
||||
local AI_MAX_POINTS = constants.AI_MAX_POINTS
|
||||
local AI_POINT_GENERATOR_AMOUNT = constants.AI_POINT_GENERATOR_AMOUNT
|
||||
|
||||
@@ -22,6 +27,11 @@ local AI_MAX_TEMPERAMENT_DURATION = constants.AI_MAX_TEMPERAMENT_DURATION
|
||||
|
||||
local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT
|
||||
|
||||
local BASE_RALLY_CHANCE = constants.BASE_RALLY_CHANCE
|
||||
local BONUS_RALLY_CHANCE = constants.BONUS_RALLY_CHANCE
|
||||
|
||||
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
|
||||
|
||||
local TICKS_A_MINUTE = constants.TICKS_A_MINUTE
|
||||
|
||||
-- imported functions
|
||||
@@ -44,9 +54,25 @@ end
|
||||
|
||||
function aiPlanning.planning(natives, evolution_factor, tick, surface)
|
||||
local maxPoints = AI_MAX_POINTS * evolution_factor
|
||||
|
||||
if natives.aiNocturnalMode then
|
||||
maxPoints = maxPoints * 0.85
|
||||
end
|
||||
|
||||
local attackWaveMaxSize = natives.attackWaveMaxSize
|
||||
natives.retreatThreshold = -(evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)
|
||||
natives.maxSquads = AI_MAX_SQUAD_COUNT * evolution_factor
|
||||
natives.rallyThreshold = BASE_RALLY_CHANCE + (evolution_factor * BONUS_RALLY_CHANCE)
|
||||
natives.formSquadThreshold = mMax((0.25 * evolution_factor), 0.10)
|
||||
natives.attackWaveSize = attackWaveMaxSize * (evolution_factor ^ 1.66667)
|
||||
natives.attackWaveDeviation = (attackWaveMaxSize * 0.5) * 0.333
|
||||
natives.attackWaveLowerBound = 1
|
||||
natives.attackWaveUpperBound = attackWaveMaxSize + (attackWaveMaxSize * 0.25)
|
||||
natives.unitRefundAmount = AI_UNIT_REFUND * evolution_factor
|
||||
natives.kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolution_factor * NO_RETREAT_EVOLUTION_BONUS_MAX)
|
||||
local threshold = natives.attackThresholdRange
|
||||
natives.attackWaveThreshold = (threshold - (threshold * evolution_factor)) + natives.attackThresholdMin
|
||||
|
||||
if (natives.points < maxPoints) then
|
||||
natives.points = natives.points + math.floor((AI_POINT_GENERATOR_AMOUNT * math.random()) +
|
||||
((AI_POINT_GENERATOR_AMOUNT * 0.7) * (evolution_factor ^ 2.5)) * natives.aiPointsScaler)
|
||||
@@ -61,7 +87,7 @@ function aiPlanning.planning(natives, evolution_factor, tick, surface)
|
||||
local roll = math.random() * mMax(1 - evolution_factor, 0.15)
|
||||
if (roll > natives.temperament) then
|
||||
natives.state = AI_STATE_PEACEFUL
|
||||
elseif (natives.aiNocturnalMode) then
|
||||
elseif natives.aiNocturnalMode then
|
||||
natives.state = AI_STATE_NOCTURNAL
|
||||
else
|
||||
natives.state = AI_STATE_AGGRESSIVE
|
||||
@@ -73,6 +99,7 @@ function aiPlanning.planning(natives, evolution_factor, tick, surface)
|
||||
natives.lastShakeMessage = tick
|
||||
surface.print("Rampant: The ground begins to shake")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return aiPlanning
|
||||
|
||||
@@ -16,7 +16,9 @@ local canAttackNocturnal = nocturnalUtils.canAttack
|
||||
-- module code
|
||||
|
||||
function aiPredicates.canAttack(natives, surface)
|
||||
return (natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)
|
||||
return ((natives.state == AI_STATE_AGGRESSIVE) or
|
||||
canAttackNocturnal(natives, surface)) and not surface.peaceful_mode and
|
||||
(#natives.squads < natives.maxSquads)
|
||||
end
|
||||
|
||||
return aiPredicates
|
||||
|
||||
+34
-10
@@ -23,7 +23,7 @@ local WORM_BASE = constants.WORM_BASE
|
||||
local AI_NEST_COST = constants.AI_NEST_COST
|
||||
local AI_WORM_COST = constants.AI_WORM_COST
|
||||
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
local DOUBLE_CHUNK_SIZE = constants.DOUBLE_CHUNK_SIZE
|
||||
|
||||
local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
|
||||
local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER
|
||||
@@ -36,6 +36,10 @@ local getEntityOverlapChunks = entityUtils.getEntityOverlapChunks
|
||||
|
||||
local gaussianRandomRange = mathUtils.gaussianRandomRange
|
||||
|
||||
local mFloor = math.floor
|
||||
|
||||
|
||||
|
||||
-- module code
|
||||
|
||||
function baseUtils.findNearbyBase(natives, position)
|
||||
@@ -45,7 +49,7 @@ function baseUtils.findNearbyBase(natives, position)
|
||||
for i=1,#bases do
|
||||
local base = bases[i]
|
||||
local distance = euclideanDistancePoints(base.x, base.y, position.x, position.y)
|
||||
if (distance <= BASE_DISTANCE_THRESHOLD) and (distance < closest) then
|
||||
if (distance <= (BASE_DISTANCE_THRESHOLD + (base.level * 100))) and (distance < closest) then
|
||||
closest = distance
|
||||
foundBase = base
|
||||
end
|
||||
@@ -55,11 +59,16 @@ end
|
||||
|
||||
function baseUtils.buildHive(regionMap, base, surface)
|
||||
local valid = false
|
||||
local position = surface.find_non_colliding_position("biter-spawner-hive", {x=base.x, y=base.y}, 2*CHUNK_SIZE, 2)
|
||||
local position = surface.find_non_colliding_position("biter-spawner-hive", base, DOUBLE_CHUNK_SIZE, 2)
|
||||
if position then
|
||||
local biterSpawner = {name="biter-spawner-hive", position=position}
|
||||
base.hives[#base.hives+1] = surface.create_entity(biterSpawner)
|
||||
baseUtils.registerEnemyBaseStructure(regionMap, base.hive, base)
|
||||
local hive = surface.create_entity(biterSpawner)
|
||||
if (#base.hives == 0) then
|
||||
base.x = hive.position.x
|
||||
base.y = hive.position.y
|
||||
end
|
||||
base.hives[#base.hives+1] = hive
|
||||
baseUtils.registerEnemyBaseStructure(regionMap, hive, base)
|
||||
valid = true
|
||||
end
|
||||
return valid
|
||||
@@ -74,7 +83,12 @@ function baseUtils.buildTendril(natives, base, surface, tick, startPosition, end
|
||||
end
|
||||
|
||||
function baseUtils.buildOrder(regionMap, natives, base, surface, tick)
|
||||
if (#base.hives == 0) or (base.upgradePoints < 10) then
|
||||
local foundHive = false
|
||||
for _,_ in pairs(base.hives) do
|
||||
foundHive = true
|
||||
break
|
||||
end
|
||||
if not foundHive or (base.upgradePoints < 10) then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -83,6 +97,7 @@ function baseUtils.buildOrder(regionMap, natives, base, surface, tick)
|
||||
|
||||
for level=0,base.level do
|
||||
local slices = (level * 3)
|
||||
slices = gaussianRandomRange(slices, slices * 0.1, slices * 0.6, slices * 1.4, generator)
|
||||
local slice = (2 * math.pi) / slices
|
||||
local pos = 0
|
||||
local thing
|
||||
@@ -106,7 +121,7 @@ function baseUtils.buildOrder(regionMap, natives, base, surface, tick)
|
||||
local nestPosition = {x = base.x + (distortion * math.cos(pos)),
|
||||
y = base.y + (distortion * math.sin(pos))}
|
||||
local biterSpawner = {name=thing, position=nestPosition}
|
||||
if surface.can_place_entity(biterSpawner) then
|
||||
if surface.can_place_entity(biterSpawner) then
|
||||
baseUtils.registerEnemyBaseStructure(regionMap, surface.create_entity(biterSpawner), base)
|
||||
base.upgradePoints = base.upgradePoints - cost
|
||||
end
|
||||
@@ -117,6 +132,7 @@ end
|
||||
|
||||
function baseUtils.createBase(regionMap, natives, position, surface, tick)
|
||||
local bases = natives.bases
|
||||
local distance = euclideanDistancePoints(position.x, position.y, 0, 0)
|
||||
local base = {
|
||||
x = position.x,
|
||||
y = position.y,
|
||||
@@ -129,7 +145,7 @@ function baseUtils.createBase(regionMap, natives, position, surface, tick)
|
||||
upgradePoints = 0,
|
||||
growth = tick,
|
||||
pattern = math.random(MAGIC_MAXIMUM_BASE_NUMBER),
|
||||
level = 3
|
||||
level = mFloor(distance / 200)
|
||||
}
|
||||
if not baseUtils.buildHive(regionMap, base, surface) then
|
||||
return nil
|
||||
@@ -145,7 +161,11 @@ function baseUtils.addEnemyStructureToChunk(chunk, entity, base)
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexChunk = chunk[NEST_BASE]
|
||||
if base then
|
||||
indexBase = base.nests
|
||||
if base.hives[entity.unit_number] then
|
||||
indexBase = base.hives
|
||||
else
|
||||
indexBase = base.nests
|
||||
end
|
||||
end
|
||||
countChunk = NEST_COUNT
|
||||
elseif (entity.type == "turret") then
|
||||
@@ -176,7 +196,11 @@ function baseUtils.removeEnemyStructureFromChunk(chunk, entity)
|
||||
local indexBase
|
||||
if base then
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexBase = base.nests
|
||||
if base.hives[entity.unit_number] then
|
||||
indexBase = base.hives
|
||||
else
|
||||
indexBase = base.nests
|
||||
end
|
||||
elseif (entity.type == "turret") then
|
||||
indexBase = base.worms
|
||||
end
|
||||
|
||||
+30
-5
@@ -3,6 +3,11 @@ local chunkProcessor = {}
|
||||
-- imports
|
||||
|
||||
local chunkUtils = require("ChunkUtils")
|
||||
local constants = require("Constants")
|
||||
|
||||
-- constants
|
||||
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
|
||||
-- imported functions
|
||||
|
||||
@@ -14,13 +19,27 @@ local scoreChunk = chunkUtils.scoreChunk
|
||||
|
||||
function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendingStack, tick)
|
||||
local processQueue = regionMap.processQueue
|
||||
|
||||
local offset = {0, 0}
|
||||
local areaBoundingBox = {false, offset}
|
||||
local query = {area=areaBoundingBox,
|
||||
force=false}
|
||||
|
||||
for _=#pendingStack, 1, -1 do
|
||||
local event = pendingStack[#pendingStack]
|
||||
pendingStack[#pendingStack] = nil
|
||||
local count = 0
|
||||
local start = #pendingStack
|
||||
for i=start, 1, -1 do
|
||||
local event = pendingStack[i]
|
||||
count = count + 1
|
||||
pendingStack[i] = nil
|
||||
|
||||
local chunk = createChunk(event.area.left_top.x, event.area.left_top.y)
|
||||
local x = event.area.left_top.x
|
||||
local y = event.area.left_top.y
|
||||
local chunk = createChunk(x, y)
|
||||
|
||||
areaBoundingBox[1] = chunk
|
||||
offset[1] = x + CHUNK_SIZE
|
||||
offset[2] = y + CHUNK_SIZE
|
||||
|
||||
local chunkX = chunk.cX
|
||||
|
||||
if regionMap[chunkX] == nil then
|
||||
@@ -29,8 +48,14 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin
|
||||
regionMap[chunkX][chunk.cY] = chunk
|
||||
|
||||
checkChunkPassability(chunk, surface)
|
||||
scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
scoreChunk(regionMap, chunk, surface, natives, tick, query)
|
||||
processQueue[#processQueue+1] = chunk
|
||||
if (count >= 3500) then
|
||||
break
|
||||
end
|
||||
end
|
||||
if (count >= 3500) and (start > 0) then
|
||||
surface.print("Rampant " .. (start - count) .. " Remaining chunks to process")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+15
-19
@@ -39,9 +39,9 @@ local addEnemyStructureToChunk = baseUtils.addEnemyStructureToChunk
|
||||
-- module code
|
||||
|
||||
local function checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, get_tile)
|
||||
local tile
|
||||
|
||||
for x=iteratingCoordinate, iteratingCoordinate + 31 do
|
||||
local tile
|
||||
if (direction == NORTH_SOUTH) then
|
||||
tile = get_tile(constantCoordinate, x)
|
||||
else
|
||||
@@ -62,13 +62,13 @@ function chunkUtils.checkChunkPassability(chunk, surface)
|
||||
local passableNorthSouth = false
|
||||
local passableEastWest = false
|
||||
for xi=x, x + 31 do
|
||||
if (not checkForDeadendTiles(xi, y, NORTH_SOUTH, get_tile)) then
|
||||
if not checkForDeadendTiles(xi, y, NORTH_SOUTH, get_tile) then
|
||||
passableNorthSouth = true
|
||||
break
|
||||
end
|
||||
end
|
||||
for yi=y, y + 31 do
|
||||
if (not checkForDeadendTiles(yi, x, EAST_WEST, get_tile)) then
|
||||
if not checkForDeadendTiles(yi, x, EAST_WEST, get_tile) then
|
||||
passableEastWest = true
|
||||
break
|
||||
end
|
||||
@@ -78,21 +78,11 @@ function chunkUtils.checkChunkPassability(chunk, surface)
|
||||
chunk[NORTH_SOUTH_PASSABLE] = passableNorthSouth
|
||||
end
|
||||
|
||||
function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
local x = chunk.x
|
||||
local y = chunk.y
|
||||
|
||||
local areaBoundingBox = {
|
||||
chunk,
|
||||
{x + 32, y + 32}
|
||||
}
|
||||
local enemyChunkQuery = {area=areaBoundingBox,
|
||||
force="enemy"}
|
||||
local playerChunkQuery = {area=areaBoundingBox,
|
||||
force="player"}
|
||||
|
||||
function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick, tempQuery)
|
||||
local useCustomAI = natives.useCustomAI
|
||||
local enemies = surface.find_entities_filtered(enemyChunkQuery)
|
||||
|
||||
tempQuery.force = "enemy"
|
||||
local enemies = surface.find_entities_filtered(tempQuery)
|
||||
|
||||
local nests = 0
|
||||
local worms = 0
|
||||
@@ -112,7 +102,12 @@ function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
if useCustomAI then
|
||||
if (nests > 0) or (worms > 0) or (biters > 0) then
|
||||
for f=1, #enemies do
|
||||
enemies[f].destroy()
|
||||
local enemy = enemies[f]
|
||||
if (enemy.name ~= "biter-spawner-hive") then
|
||||
enemy.destroy()
|
||||
else
|
||||
nests = nests - 1
|
||||
end
|
||||
end
|
||||
local foundBase = findNearbyBase(natives, chunk) or createBase(regionMap, natives, chunk, surface, tick)
|
||||
if foundBase then
|
||||
@@ -129,7 +124,8 @@ function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
end
|
||||
end
|
||||
|
||||
local entities = surface.find_entities_filtered(playerChunkQuery)
|
||||
tempQuery.force = "player"
|
||||
local entities = surface.find_entities_filtered(tempQuery)
|
||||
|
||||
local playerObjects = 0
|
||||
local safeBuildings = natives.safeBuildings
|
||||
|
||||
+7
-9
@@ -3,19 +3,12 @@ local constants = {}
|
||||
-- versions
|
||||
|
||||
constants.VERSION_5 = 5
|
||||
constants.VERSION_9 = 9
|
||||
constants.VERSION_10 = 10
|
||||
constants.VERSION_11 = 11
|
||||
constants.VERSION_12 = 12
|
||||
constants.VERSION_13 = 13
|
||||
constants.VERSION_14 = 14
|
||||
constants.VERSION_15 = 15
|
||||
constants.VERSION_16 = 16
|
||||
constants.VERSION_17 = 17
|
||||
constants.VERSION_18 = 18
|
||||
constants.VERSION_19 = 19
|
||||
constants.VERSION_20 = 20
|
||||
constants.VERSION_21 = 21
|
||||
constants.VERSION_22 = 22
|
||||
constants.VERSION_23 = 23
|
||||
|
||||
@@ -25,9 +18,9 @@ constants.MAGIC_MAXIMUM_NUMBER = 1e99 -- used in loops trying to find the lowest
|
||||
constants.MAGIC_MAXIMUM_BASE_NUMBER = 100000000
|
||||
constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL = 10000
|
||||
|
||||
constants.PROCESS_QUEUE_SIZE = 500
|
||||
constants.PROCESS_QUEUE_SIZE = 450
|
||||
constants.SCAN_QUEUE_SIZE = 6
|
||||
constants.BASE_QUEUE_SIZE = 10
|
||||
constants.BASE_QUEUE_SIZE = 1
|
||||
constants.PROCESS_PLAYER_BOUND = 4
|
||||
|
||||
constants.TICKS_A_SECOND = 60
|
||||
@@ -39,8 +32,11 @@ constants.INTERVAL_LOGIC = 38
|
||||
-- chunk properties
|
||||
|
||||
constants.CHUNK_SIZE = 32
|
||||
constants.DOUBLE_CHUNK_SIZE = constants.CHUNK_SIZE * 2
|
||||
constants.TRIPLE_CHUNK_SIZE = constants.CHUNK_SIZE * 3
|
||||
constants.HALF_CHUNK_SIZE = constants.CHUNK_SIZE / 2
|
||||
constants.QUARTER_CHUNK_SIZE = constants.HALF_CHUNK_SIZE / 2
|
||||
|
||||
constants.NORTH_SOUTH = 1
|
||||
constants.EAST_WEST = 2
|
||||
|
||||
@@ -56,6 +52,8 @@ constants.AI_SETTLER_COST = 75
|
||||
constants.AI_BASE_BUILDING_COST = 500
|
||||
constants.AI_TUNNEL_COST = 100
|
||||
constants.AI_MAX_POINTS = 10000
|
||||
constants.AI_MAX_OVERFLOW_POINTS = constants.AI_MAX_POINTS * 3
|
||||
|
||||
|
||||
constants.AI_UNIT_REFUND = 3
|
||||
|
||||
|
||||
+67
-63
@@ -13,12 +13,11 @@ local playerUtils = require("PlayerUtils")
|
||||
|
||||
local PROCESS_QUEUE_SIZE = constants.PROCESS_QUEUE_SIZE
|
||||
|
||||
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
|
||||
|
||||
local SCAN_QUEUE_SIZE = constants.SCAN_QUEUE_SIZE
|
||||
|
||||
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
local DOUBLE_CHUNK_SIZE = constants.DOUBLE_CHUNK_SIZE
|
||||
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
|
||||
|
||||
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
|
||||
local CHUNK_TICK = constants.CHUNK_TICK
|
||||
@@ -26,7 +25,7 @@ local CHUNK_TICK = constants.CHUNK_TICK
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
local WORM_COUNT = constants.WORM_COUNT
|
||||
|
||||
local AI_MAX_POINTS = constants.AI_MAX_POINTS
|
||||
local AI_MAX_OVERFLOW_POINTS = constants.AI_MAX_OVERFLOW_POINTS
|
||||
local AI_SQUAD_COST = constants.AI_SQUAD_COST
|
||||
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
|
||||
|
||||
@@ -77,7 +76,7 @@ end
|
||||
In theory, this might be fine as smaller bases have less surface to attack and need to have
|
||||
pheromone dissipate at a faster rate.
|
||||
--]]
|
||||
function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
function mapProcessor.processMap(regionMap, surface, natives, tick)
|
||||
local roll = regionMap.processRoll
|
||||
local index = regionMap.processPointer
|
||||
|
||||
@@ -85,21 +84,29 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
roll = math.random()
|
||||
regionMap.processRoll = roll
|
||||
end
|
||||
|
||||
local tempNeighbors = {false, false, false, false}
|
||||
local tempNeighborsAll = {false, false, false, false, false, false, false, false}
|
||||
|
||||
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.35)
|
||||
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.35) and (natives.points >= AI_SQUAD_COST)
|
||||
|
||||
local processQueue = regionMap.processQueue
|
||||
local endIndex = mMin(index + PROCESS_QUEUE_SIZE, #processQueue)
|
||||
for x=index,endIndex do
|
||||
local chunk = processQueue[x]
|
||||
|
||||
processPheromone(regionMap, chunk)
|
||||
if (chunk[CHUNK_TICK] ~= tick) then
|
||||
chunk[CHUNK_TICK] = tick
|
||||
|
||||
processPheromone(regionMap, chunk, tempNeighbors)
|
||||
|
||||
if squads then
|
||||
formSquads(regionMap, surface, natives, chunk, evolution_factor, AI_SQUAD_COST)
|
||||
end
|
||||
|
||||
scents(chunk)
|
||||
if squads and (chunk[NEST_COUNT] ~= 0) then
|
||||
formSquads(regionMap, surface, natives, chunk, AI_SQUAD_COST, tempNeighborsAll)
|
||||
squads = natives.points >= AI_SQUAD_COST
|
||||
end
|
||||
|
||||
scents(chunk)
|
||||
end
|
||||
end
|
||||
|
||||
if (endIndex == #processQueue) then
|
||||
@@ -115,15 +122,19 @@ end
|
||||
vs
|
||||
the slower passive version processing the entire map in multiple passes.
|
||||
--]]
|
||||
function mapProcessor.processPlayers(players, regionMap, surface, natives, evolution_factor, tick)
|
||||
function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
|
||||
-- put down player pheromone for player hunters
|
||||
-- randomize player order to ensure a single player isn't singled out
|
||||
local playerOrdering = nonRepeatingRandom(players)
|
||||
|
||||
local vengenceThreshold = -(evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)
|
||||
local roll = math.random()
|
||||
local roll = math.random()
|
||||
|
||||
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.20)
|
||||
local allowingAttacks = canAttack(natives, surface)
|
||||
|
||||
local squads = allowingAttacks and (0.11 <= roll) and (roll <= 0.20) and (natives.points >= AI_SQUAD_COST)
|
||||
|
||||
local tempNeighbors = {false, false, false, false}
|
||||
local tempNeighborsAll = {false, false, false, false, false, false, false, false}
|
||||
|
||||
for i=1,#playerOrdering do
|
||||
local player = players[playerOrdering[i]]
|
||||
@@ -143,21 +154,23 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
|
||||
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
|
||||
|
||||
if playerChunk then
|
||||
local vengence = canAttack(natives, surface) and ((playerChunk[NEST_COUNT] ~= 0) or (playerChunk[WORM_COUNT] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < vengenceThreshold))
|
||||
|
||||
local vengence = allowingAttacks and ((playerChunk[NEST_COUNT] ~= 0) or (playerChunk[WORM_COUNT] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < natives.retreatThreshold)) and (natives.points >= AI_VENGENCE_SQUAD_COST)
|
||||
|
||||
for x=playerChunk.cX - PROCESS_PLAYER_BOUND, playerChunk.cX + PROCESS_PLAYER_BOUND do
|
||||
for y=playerChunk.cY - PROCESS_PLAYER_BOUND, playerChunk.cY + PROCESS_PLAYER_BOUND do
|
||||
local chunk = getChunkByIndex(regionMap, x, y)
|
||||
if chunk and (chunk[CHUNK_TICK] ~= tick) then
|
||||
chunk[CHUNK_TICK] = tick
|
||||
|
||||
processPheromone(regionMap, chunk)
|
||||
processPheromone(regionMap, chunk, tempNeighbors)
|
||||
|
||||
if squads then
|
||||
formSquads(regionMap, surface, natives, chunk, evolution_factor, AI_SQUAD_COST)
|
||||
formSquads(regionMap, surface, natives, chunk, AI_SQUAD_COST, tempNeighborsAll)
|
||||
squads = natives.points >= AI_SQUAD_COST
|
||||
end
|
||||
if vengence then
|
||||
formSquads(regionMap, surface, natives, chunk, evolution_factor, AI_VENGENCE_SQUAD_COST)
|
||||
formSquads(regionMap, surface, natives, chunk, AI_VENGENCE_SQUAD_COST, tempNeighborsAll)
|
||||
vengence = natives.points >= AI_VENGENCE_SQUAD_COST
|
||||
end
|
||||
|
||||
scents(chunk)
|
||||
@@ -173,67 +186,60 @@ end
|
||||
--[[
|
||||
Passive scan to find entities that have been generated outside the factorio event system
|
||||
--]]
|
||||
function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
function mapProcessor.scanMap(regionMap, surface, natives)
|
||||
local index = regionMap.scanPointer
|
||||
|
||||
local chunkBox = {false,
|
||||
{x=0,
|
||||
y=0}}
|
||||
|
||||
local offset = {0, 0}
|
||||
local chunkBox = {false, offset}
|
||||
local playerQuery = {area = chunkBox,
|
||||
force = "player"}
|
||||
local spawnerQuery = {area = chunkBox,
|
||||
type = "unit-spawner",
|
||||
force = "enemy"}
|
||||
local wormsQuery = {area = chunkBox,
|
||||
type = "turret",
|
||||
force = "enemy"}
|
||||
local unitCountQuery = {area = chunkBox,
|
||||
type = "unit",
|
||||
force = "enemy"}
|
||||
local unitCountQuery = { area = chunkBox,
|
||||
type = "unit",
|
||||
force = "enemy",
|
||||
limit = 301 }
|
||||
|
||||
local processQueue = regionMap.processQueue
|
||||
local endIndex = mMin(index + SCAN_QUEUE_SIZE, #processQueue)
|
||||
|
||||
local unitRefundAmount = natives.unitRefundAmount
|
||||
|
||||
for x=index,endIndex do
|
||||
local chunk = processQueue[x]
|
||||
|
||||
chunkBox[1] = chunk
|
||||
|
||||
chunkBox[2].x = chunk.x + CHUNK_SIZE
|
||||
chunkBox[2].y = chunk.y + CHUNK_SIZE
|
||||
|
||||
local entities = surface.find_entities_filtered(playerQuery)
|
||||
|
||||
local spawners = surface.count_entities_filtered(spawnerQuery)
|
||||
|
||||
local worms = surface.count_entities_filtered(wormsQuery)
|
||||
offset[1] = chunk.x + CHUNK_SIZE
|
||||
offset[2] = chunk.y + CHUNK_SIZE
|
||||
|
||||
local unitCount = surface.count_entities_filtered(unitCountQuery)
|
||||
|
||||
local closeBy = false
|
||||
if (unitCount > 300) then
|
||||
for i=1,#natives.squads do
|
||||
local squadGroup = natives.squads[i].group
|
||||
if squadGroup.valid and (euclideanDistanceNamed(squadGroup.position, chunk) < CHUNK_SIZE * 2) then
|
||||
local closeBy = false
|
||||
local squads = natives.squads
|
||||
for i=1, #squads do
|
||||
local squadGroup = squads[i].group
|
||||
if squadGroup.valid and (euclideanDistanceNamed(squadGroup.position, chunk) < DOUBLE_CHUNK_SIZE) then
|
||||
closeBy = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not closeBy then
|
||||
local units = surface.find_enemy_units(chunk, TRIPLE_CHUNK_SIZE)
|
||||
|
||||
unitCount = #units
|
||||
for i=1,unitCount do
|
||||
units[i].destroy()
|
||||
end
|
||||
natives.points = natives.points + (unitCount * unitRefundAmount)
|
||||
|
||||
if (natives.points > AI_MAX_OVERFLOW_POINTS) then
|
||||
natives.points = AI_MAX_OVERFLOW_POINTS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (unitCount > 300) and not closeBy then
|
||||
local weight = AI_UNIT_REFUND * evolution_factor
|
||||
local units = surface.find_enemy_units(chunk, CHUNK_SIZE * 3)
|
||||
|
||||
for i=1,#units do
|
||||
units[i].destroy()
|
||||
natives.points = natives.points + weight
|
||||
end
|
||||
|
||||
if (natives.points > (AI_MAX_POINTS * 3)) then
|
||||
natives.points = (AI_MAX_POINTS * 3)
|
||||
end
|
||||
end
|
||||
|
||||
local entities = surface.find_entities_filtered(playerQuery)
|
||||
local playerBaseGenerator = 0
|
||||
local safeBuildings = natives.safeBuildings
|
||||
for i=1,#entities do
|
||||
@@ -249,8 +255,6 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
end
|
||||
end
|
||||
|
||||
chunk[NEST_COUNT] = spawners
|
||||
chunk[WORM_COUNT] = worms
|
||||
chunk[PLAYER_BASE_GENERATOR] = playerBaseGenerator
|
||||
end
|
||||
|
||||
|
||||
+6
-6
@@ -44,8 +44,8 @@ end
|
||||
/|\
|
||||
6 7 8
|
||||
]]--
|
||||
function mapUtils.getNeighborChunks(regionMap, chunkX, chunkY)
|
||||
local neighbors = {false,false,false,false,false,false,false,false}
|
||||
function mapUtils.getNeighborChunks(regionMap, chunkX, chunkY, neighbors)
|
||||
--local neighbors = {false,false,false,false,false,false,false,false}
|
||||
local chunkYRow1 = chunkY - 1
|
||||
local chunkYRow3 = chunkY + 1
|
||||
local xChunks = regionMap[chunkX-1]
|
||||
@@ -83,8 +83,8 @@ function mapUtils.canMoveChunkDirection(direction, startChunk, endChunk)
|
||||
return canMove
|
||||
end
|
||||
|
||||
function mapUtils.getNeighborChunksWithDirection(regionMap, chunkX, chunkY)
|
||||
local neighbors = {false,false,false,false,false,false,false,false}
|
||||
function mapUtils.getNeighborChunksWithDirection(regionMap, chunkX, chunkY, neighbors)
|
||||
--local neighbors = {false,false,false,false,false,false,false,false}
|
||||
local chunkYRow1 = chunkY - 1
|
||||
local chunkYRow3 = chunkY + 1
|
||||
|
||||
@@ -137,8 +137,8 @@ function mapUtils.getCardinalChunksWithDirection(regionMap, chunkX, chunkY)
|
||||
return neighbors
|
||||
end
|
||||
|
||||
function mapUtils.getCardinalChunks(regionMap, chunkX, chunkY)
|
||||
local neighbors = {false,false,false,false}
|
||||
function mapUtils.getCardinalChunks(regionMap, chunkX, chunkY, neighbors)
|
||||
--local neighbors = {false,false,false,false}
|
||||
local xChunks = regionMap[chunkX]
|
||||
if xChunks then
|
||||
neighbors[1] = xChunks[chunkY-1]
|
||||
|
||||
@@ -58,13 +58,14 @@ function pheromoneUtils.playerScent(playerChunk)
|
||||
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
||||
end
|
||||
|
||||
function pheromoneUtils.processPheromone(regionMap, chunk)
|
||||
function pheromoneUtils.processPheromone(regionMap, chunk, tempNeighbors)
|
||||
|
||||
if not chunk[NORTH_SOUTH_PASSABLE] and not chunk[EAST_WEST_PASSABLE] then
|
||||
return
|
||||
end
|
||||
|
||||
getCardinalChunks(regionMap, chunk.cX, chunk.cY, tempNeighbors)
|
||||
|
||||
local neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
|
||||
local chunkMovement = chunk[MOVEMENT_PHEROMONE]
|
||||
local chunkBase = chunk[BASE_PHEROMONE]
|
||||
local chunkPlayer = chunk[PLAYER_PHEROMONE]
|
||||
@@ -74,7 +75,7 @@ function pheromoneUtils.processPheromone(regionMap, chunk)
|
||||
local totalPlayer = 0
|
||||
|
||||
for i=1,4 do
|
||||
local neighborChunk = neighbors[i]
|
||||
local neighborChunk = tempNeighbors[i]
|
||||
if neighborChunk then
|
||||
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement)
|
||||
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase)
|
||||
|
||||
+31
-37
@@ -19,12 +19,9 @@ local SQUAD_RETREATING = constants.SQUAD_RETREATING
|
||||
local SQUAD_GUARDING = constants.SQUAD_GUARDING
|
||||
local GROUP_MERGE_DISTANCE = constants.GROUP_MERGE_DISTANCE
|
||||
|
||||
local NO_RETREAT_BASE_PERCENT = constants.NO_RETREAT_BASE_PERCENT
|
||||
local NO_RETREAT_EVOLUTION_BONUS_MAX = constants.NO_RETREAT_EVOLUTION_BONUS_MAX
|
||||
local NO_RETREAT_SQUAD_SIZE_BONUS_MAX = constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX
|
||||
|
||||
local AI_MAX_POINTS = constants.AI_MAX_POINTS
|
||||
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
|
||||
local AI_MAX_OVERFLOW_POINTS = constants.AI_MAX_OVERFLOW_POINTS
|
||||
|
||||
local AI_MAX_BITER_GROUP_SIZE = constants.AI_MAX_BITER_GROUP_SIZE
|
||||
|
||||
@@ -32,7 +29,6 @@ local AI_MAX_BITER_GROUP_SIZE = constants.AI_MAX_BITER_GROUP_SIZE
|
||||
|
||||
local mLog = math.log10
|
||||
|
||||
|
||||
local tableRemove = table.remove
|
||||
local tableInsert = table.insert
|
||||
local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
|
||||
@@ -41,6 +37,7 @@ local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
|
||||
|
||||
function unitGroupUtils.findNearBySquad(natives, position, distance, filter)
|
||||
local squads = natives.squads
|
||||
|
||||
for i=1,#squads do
|
||||
local squad = squads[i]
|
||||
local unitGroup = squad.group
|
||||
@@ -83,27 +80,26 @@ function unitGroupUtils.membersToSquad(squad, members, overwriteGroup)
|
||||
end
|
||||
|
||||
function unitGroupUtils.convertUnitGroupToSquad(natives, unitGroup)
|
||||
local returnSquad
|
||||
if (unitGroup ~= nil) then
|
||||
local squads = natives.squads
|
||||
for i=1,#squads do
|
||||
local squad = squads[i]
|
||||
if (squad.group == unitGroup) then
|
||||
return squad
|
||||
end
|
||||
end
|
||||
returnSquad = { group = unitGroup,
|
||||
status = SQUAD_GUARDING,
|
||||
penalties = {},
|
||||
rabid = false,
|
||||
frenzy = false,
|
||||
kamikaze = false,
|
||||
frenzyPosition = {x = 0,
|
||||
y = 0},
|
||||
cycles = 0 }
|
||||
squads[#squads+1] = returnSquad
|
||||
if not unitGroup then
|
||||
return nil
|
||||
end
|
||||
|
||||
local squads = natives.squads
|
||||
for i=1,#squads do
|
||||
local squad = squads[i]
|
||||
if (squad.group == unitGroup) then
|
||||
return squad
|
||||
end
|
||||
end
|
||||
local returnSquad = { group = unitGroup,
|
||||
status = SQUAD_GUARDING,
|
||||
penalties = {},
|
||||
rabid = false,
|
||||
frenzy = false,
|
||||
kamikaze = false,
|
||||
frenzyPosition = {x = 0,
|
||||
y = 0},
|
||||
cycles = 0 }
|
||||
squads[#squads+1] = returnSquad
|
||||
return returnSquad
|
||||
end
|
||||
|
||||
@@ -135,11 +131,9 @@ function unitGroupUtils.lookupSquadMovementPenalty(squad, chunkX, chunkY)
|
||||
return 0
|
||||
end
|
||||
|
||||
function unitGroupUtils.calculateKamikazeThreshold(squad, natives, evolution_factor)
|
||||
local maxSize = natives.attackWaveMaxSize
|
||||
local kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolution_factor * NO_RETREAT_EVOLUTION_BONUS_MAX)
|
||||
local squadSizeBonus = mLog((#squad.group.members / maxSize) + 0.1) + 1
|
||||
return kamikazeThreshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus)
|
||||
function unitGroupUtils.calculateKamikazeThreshold(squad, natives)
|
||||
local squadSizeBonus = mLog((#squad.group.members / natives.attackWaveMaxSize) + 0.1) + 1
|
||||
return natives.kamikazeThreshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus)
|
||||
end
|
||||
|
||||
local function isAttacking(group)
|
||||
@@ -147,11 +141,11 @@ local function isAttacking(group)
|
||||
return (state == DEFINES_GROUP_STATE_ATTACKING_TARGET) or (state == DEFINES_GROUP_STATE_ATTACKING_DISTRACTION)
|
||||
end
|
||||
|
||||
function unitGroupUtils.cleanSquads(natives, evolution_factor)
|
||||
function unitGroupUtils.cleanSquads(natives)
|
||||
local squads = natives.squads
|
||||
local squadCount = #squads
|
||||
|
||||
local weight = AI_UNIT_REFUND * evolution_factor
|
||||
local weight = natives.unitRefundAmount
|
||||
|
||||
local cleanSquads = {}
|
||||
|
||||
@@ -166,11 +160,11 @@ function unitGroupUtils.cleanSquads(natives, evolution_factor)
|
||||
local members = group.members
|
||||
for x=1,memberCount do
|
||||
members[x].destroy()
|
||||
natives.points = natives.points + weight
|
||||
end
|
||||
natives.points = natives.points + (memberCount * weight)
|
||||
|
||||
if (natives.points > (AI_MAX_POINTS * 3)) then
|
||||
natives.points = (AI_MAX_POINTS * 3)
|
||||
if (natives.points > AI_MAX_OVERFLOW_POINTS) then
|
||||
natives.points = AI_MAX_OVERFLOW_POINTS
|
||||
end
|
||||
group.destroy()
|
||||
else
|
||||
@@ -197,7 +191,7 @@ function unitGroupUtils.cleanSquads(natives, evolution_factor)
|
||||
end
|
||||
|
||||
|
||||
function unitGroupUtils.regroupSquads(natives, evolution_factor)
|
||||
function unitGroupUtils.regroupSquads(natives)
|
||||
local groupThreshold = AI_MAX_BITER_GROUP_SIZE * 0.75
|
||||
|
||||
local squads = natives.squads
|
||||
@@ -235,7 +229,7 @@ function unitGroupUtils.regroupSquads(natives, evolution_factor)
|
||||
end
|
||||
end
|
||||
if mergedSquads and not squad.kamikaze then
|
||||
local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, natives, evolution_factor)
|
||||
local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, natives)
|
||||
if (math.random() < kamikazeThreshold) then
|
||||
squad.kamikaze = true
|
||||
end
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ data:extend({
|
||||
name = "rampant-useCustomAI",
|
||||
description = "rampant-useCustomAI",
|
||||
setting_type = 'startup',
|
||||
default_value = false,
|
||||
default_value = true,
|
||||
order = "h[total]-a[ai]",
|
||||
per_user = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user