mirror of
https://github.com/veden/Rampant.git
synced 2025-09-16 09:16:43 +02:00
Stable merged with ai toggle
This commit is contained in:
@@ -55,6 +55,11 @@ Configure Options not in game menu:
|
||||
|
||||
# Version History
|
||||
|
||||
0.15.11 -
|
||||
- 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: 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
|
||||
|
||||
0.15.10 -
|
||||
- Fix: nil chunk in pheromone utils(https://mods.factorio.com/mods/Veden/Rampant/discussion/13806)
|
||||
- Tweak: Increased failed behaviors before dispanding from 3 to 6
|
||||
|
23
Upgrade.lua
23
Upgrade.lua
@@ -10,8 +10,6 @@ local mathUtils = require("libs/MathUtils")
|
||||
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
|
||||
local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
|
||||
|
||||
local MAX_RALLY_CRIES = constants.MAX_RALLY_CRIES
|
||||
|
||||
-- imported functions
|
||||
|
||||
local roundToNearest = mathUtils.roundToNearest
|
||||
@@ -80,7 +78,6 @@ function upgrade.attempt(natives, regionMap)
|
||||
global.version = constants.VERSION_16
|
||||
end
|
||||
if (global.version < constants.VERSION_18) then
|
||||
print(global.version)
|
||||
|
||||
natives.safeEntities = {}
|
||||
natives.safeEntityName = {}
|
||||
@@ -132,20 +129,18 @@ function upgrade.attempt(natives, regionMap)
|
||||
game.surfaces[1].print("Rampant - Version 0.15.10")
|
||||
global.version = constants.VERSION_22
|
||||
end
|
||||
if (global.version < constants.VERSION_22) then
|
||||
|
||||
natives.bases = {}
|
||||
|
||||
natives.baseDistanceMin = 0
|
||||
|
||||
natives.baseIndex = 1
|
||||
|
||||
natives.randomGenerator = game.create_random_generator()
|
||||
if (global.version < constants.VERSION_23) then
|
||||
|
||||
game.forces.enemy.ai_controllable = false
|
||||
|
||||
game.surfaces[1].print("Rampant - Version 0.15.10")
|
||||
global.version = constants.VERSION_22
|
||||
natives.useCustomAI = settings.startup["rampant-useCustomAI"].value
|
||||
natives.bases = {}
|
||||
natives.baseDistanceMin = 0
|
||||
natives.baseIndex = 1
|
||||
natives.randomGenerator = game.create_random_generator()
|
||||
|
||||
game.surfaces[1].print("Rampant - Version 0.15.11")
|
||||
global.version = constants.VERSION_23
|
||||
end
|
||||
return starting ~= global.version
|
||||
end
|
||||
|
74
control.lua
74
control.lua
@@ -15,6 +15,7 @@ local aiPlanning = require("libs/AIPlanning")
|
||||
local interop = require("libs/Interop")
|
||||
local tests = require("tests")
|
||||
local upgrade = require("Upgrade")
|
||||
local baseUtils = require("libs/BaseUtils")
|
||||
|
||||
-- constants
|
||||
|
||||
@@ -55,8 +56,8 @@ local squadBeginAttack = aiAttack.squadBeginAttack
|
||||
local retreatUnits = aiDefense.retreatUnits
|
||||
|
||||
local addRemovePlayerEntity = entityUtils.addRemovePlayerEntity
|
||||
local removeEnemyBase = entityUtils.removeEnemyBase
|
||||
--local makeImmortalEntity = entityUtils.makeImmortalEntity
|
||||
local unregisterEnemyBaseStructure = baseUtils.unregisterEnemyBaseStructure
|
||||
local makeImmortalEntity = entityUtils.makeImmortalEntity
|
||||
|
||||
local processBases = baseProcessor.processBases
|
||||
|
||||
@@ -82,10 +83,31 @@ local function onChunkGenerated(event)
|
||||
end
|
||||
end
|
||||
|
||||
local function reprocessChunks()
|
||||
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
|
||||
regionMap.processQueue = {}
|
||||
regionMap.processPointer = 1
|
||||
regionMap.scanPointer = 1
|
||||
-- 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]
|
||||
for chunk in surface.get_chunks() do
|
||||
onChunkGenerated({ tick = game.tick,
|
||||
surface = surface,
|
||||
area = { left_top = { x = chunk.x * 32,
|
||||
y = chunk.y * 32 }}})
|
||||
end
|
||||
end
|
||||
|
||||
local function onModSettingsChange(event)
|
||||
|
||||
if event and (string.sub(event.setting, 1, 7) ~= "rampant") then
|
||||
return
|
||||
return false
|
||||
end
|
||||
|
||||
natives.safeBuildings = settings.global["rampant-safeBuildings"].value
|
||||
@@ -112,31 +134,21 @@ local function onModSettingsChange(event)
|
||||
natives.attackPlayerThreshold = settings.global["rampant-attackPlayerThreshold"].value
|
||||
natives.aiNocturnalMode = settings.global["rampant-permanentNocturnal"].value
|
||||
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()
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function onConfigChanged()
|
||||
|
||||
if upgrade.attempt(natives, regionMap) then
|
||||
onModSettingsChange(nil)
|
||||
|
||||
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
|
||||
regionMap.processQueue = {}
|
||||
regionMap.processPointer = 1
|
||||
regionMap.scanPointer = 1
|
||||
-- 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]
|
||||
for chunk in surface.get_chunks() do
|
||||
onChunkGenerated({ tick = game.tick,
|
||||
surface = surface,
|
||||
area = { left_top = { x = chunk.x * 32,
|
||||
y = chunk.y * 32 }}})
|
||||
end
|
||||
if upgrade.attempt(natives, regionMap) and onModSettingsChange(nil) then
|
||||
reprocessChunks()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -161,7 +173,9 @@ local function onTick(event)
|
||||
|
||||
processPlayers(players, regionMap, surface, natives, evolutionFactor, tick)
|
||||
|
||||
if (natives.useCustomAI) then
|
||||
processBases(regionMap, surface, natives, tick)
|
||||
end
|
||||
|
||||
squadBeginAttack(natives, players, evolutionFactor)
|
||||
squadAttack(regionMap, surface, natives)
|
||||
@@ -173,7 +187,7 @@ end
|
||||
|
||||
local function onBuild(event)
|
||||
local entity = event.created_entity
|
||||
addRemoveEntity(regionMap, entity, natives, true, false)
|
||||
addRemovePlayerEntity(regionMap, entity, natives, true, false)
|
||||
if natives.safeBuildings then
|
||||
if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then
|
||||
entity.destructible = false
|
||||
@@ -198,7 +212,7 @@ local function onDeath(event)
|
||||
-- drop death pheromone where unit died
|
||||
deathScent(deathChunk)
|
||||
|
||||
if ((event.force ~= nil) and (event.force.name == "player")) then
|
||||
if event.force and (event.force.name == "player") then
|
||||
local evolutionFactor = entity.force.evolution_factor
|
||||
local tick = event.tick
|
||||
|
||||
@@ -224,7 +238,7 @@ local function onDeath(event)
|
||||
end
|
||||
|
||||
elseif (entity.type == "unit-spawner") or (entity.type == "turret") then
|
||||
removeEnemyBase(regionMap, entity)
|
||||
unregisterEnemyBaseStructure(regionMap, entity)
|
||||
end
|
||||
elseif (entity.force.name == "player") then
|
||||
local creditNatives = false
|
||||
@@ -303,7 +317,9 @@ remote.add_interface("rampantTests",
|
||||
baseStats = tests.baseStats,
|
||||
baseTiles = tests.baseTiles,
|
||||
mergeBases = tests.mergeBases,
|
||||
clearBases = tests.clearBases
|
||||
clearBases = tests.clearBases,
|
||||
getOffsetChunk = tests.getOffsetChunk,
|
||||
registeredNest = tests.registeredNest
|
||||
}
|
||||
)
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name" : "Rampant",
|
||||
"factorio_version" : "0.15",
|
||||
"version" : "0.15.10",
|
||||
"version" : "0.15.11",
|
||||
"title" : "Rampant",
|
||||
"author" : "Veden",
|
||||
"homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445",
|
||||
|
@@ -19,6 +19,9 @@ local SQUAD_GUARDING = constants.SQUAD_GUARDING
|
||||
|
||||
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
|
||||
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
|
||||
local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area
|
||||
local DEFINES_GROUP_FINISHED = defines.group_state.finished
|
||||
local DEFINES_GROUP_GATHERING = defines.group_state.gathering
|
||||
local DEFINES_GROUP_MOVING = defines.group_state.moving
|
||||
@@ -49,10 +52,10 @@ local function validLocation(x, chunk, neighborChunk)
|
||||
return canMoveChunkDirection(x, chunk, neighborChunk)
|
||||
end
|
||||
|
||||
local function scoreAttackLocation(position, squad, neighborChunk, surface)
|
||||
local function scoreAttackLocation(squad, neighborChunk, surface)
|
||||
local squadMovementPenalty = lookupSquadMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY)
|
||||
local r = surface.get_pollution(position) + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 25) --- neighborChunk[ENEMY_BASE_GENERATOR]
|
||||
return r - squadMovementPenalty
|
||||
local r = surface.get_pollution(neighborChunk) + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 50)
|
||||
return r - squadMovementPenalty - (neighborChunk[NEST_COUNT] * 30)
|
||||
end
|
||||
|
||||
function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
@@ -60,29 +63,9 @@ function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
local attackPosition
|
||||
local attackCmd
|
||||
|
||||
--[[
|
||||
Constants populated by the factorio runtime
|
||||
--]]
|
||||
-- local DEFINES_GROUP_FINISHED
|
||||
-- local DEFINES_GROUP_GATHERING
|
||||
-- local DEFINES_GROUP_MOVING
|
||||
-- local DEFINES_GROUP_ATTACKING_DISTRACTION
|
||||
-- local DEFINES_GROUP_ATTACKING_TARGET
|
||||
|
||||
-- local DEFINES_DISTRACTION_BY_ENEMY
|
||||
-- local DEFINES_DISTRACTION_BY_ANYTHING
|
||||
|
||||
if (#squads > 0) then
|
||||
-- DEFINES_GROUP_FINISHED = defines.group_state.finished
|
||||
-- DEFINES_GROUP_GATHERING = defines.group_state.gathering
|
||||
-- DEFINES_GROUP_MOVING = defines.group_state.moving
|
||||
-- DEFINES_GROUP_ATTACKING_DISTRACTION = defines.group_state.attacking_distraction
|
||||
-- DEFINES_GROUP_ATTACKING_TARGET = defines.group_state.attacking_target
|
||||
-- DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
|
||||
-- DEFINES_DISTRACTION_BY_ANYTHING = defines.distraction.by_anything
|
||||
|
||||
attackPosition = {x=0, y=0}
|
||||
attackCmd = { type = defines.command.attack_area,
|
||||
attackCmd = { type = DEFINES_COMMAND_ATTACK_AREA,
|
||||
destination = attackPosition,
|
||||
radius = 28,
|
||||
distraction = DEFINES_DISTRACTION_BY_ENEMY }
|
||||
@@ -102,7 +85,6 @@ function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
scoreAttackLocation,
|
||||
squad,
|
||||
surface,
|
||||
attackPosition,
|
||||
true)
|
||||
addSquadMovementPenalty(squad, chunk.cX, chunk.cY)
|
||||
if attackChunk then
|
||||
@@ -117,8 +99,8 @@ function aiAttack.squadAttack(regionMap, surface, natives)
|
||||
squad.cycles = 4
|
||||
end
|
||||
|
||||
local outsideFrenzyRadius = not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) > 100)
|
||||
squad.frenzy = not outsideFrenzyRadius
|
||||
local moreFrenzy = not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) < 100)
|
||||
squad.frenzy = moreFrenzy
|
||||
|
||||
if squad.rabid or squad.frenzy then
|
||||
attackCmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING
|
||||
|
@@ -33,7 +33,7 @@ local RALLY_CRY_DISTANCE = constants.RALLY_CRY_DISTANCE
|
||||
local DEFINES_COMMAND_GROUP = defines.command.group
|
||||
local DEFINES_DISTRACTION_NONE = defines.distraction.none
|
||||
|
||||
local CHUNK_BASE = constants.CHUNK_BASE
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
|
||||
-- imported functions
|
||||
|
||||
@@ -57,7 +57,7 @@ local function attackWaveValidCandidate(chunk, natives, surface, evolutionFactor
|
||||
end
|
||||
end
|
||||
if natives.attackUsePollution then
|
||||
total = total + surface.get_pollution({chunk.pX, chunk.pY})
|
||||
total = total + surface.get_pollution(chunk)
|
||||
end
|
||||
|
||||
local threshold = natives.attackThresholdRange
|
||||
@@ -66,12 +66,12 @@ local function attackWaveValidCandidate(chunk, natives, surface, evolutionFactor
|
||||
return total > ((threshold - delta) + natives.attackThresholdMin)
|
||||
end
|
||||
|
||||
local function scoreUnitGroupLocation(position, squad, neighborChunk, surface)
|
||||
return surface.get_pollution(position) + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
|
||||
local function scoreUnitGroupLocation(squad, neighborChunk, surface)
|
||||
return surface.get_pollution(neighborChunk) + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
|
||||
end
|
||||
|
||||
local function validUnitGroupLocation(x, chunk, neighborChunk)
|
||||
return neighborChunk[NORTH_SOUTH_PASSABLE] and neighborChunk[EAST_WEST_PASSABLE] and neighborChunk[CHUNK_BASE] ~= nil
|
||||
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)
|
||||
@@ -82,7 +82,7 @@ function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFact
|
||||
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[ENEMY_BASE_GENERATOR] ~= 0) then
|
||||
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)
|
||||
end
|
||||
end
|
||||
@@ -91,24 +91,23 @@ function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFact
|
||||
end
|
||||
|
||||
function aiBuilding.formSquads(regionMap, surface, natives, chunk, evolution_factor, cost)
|
||||
if (natives.points > cost) and (chunk[CHUNK_BASE] ~= nil) and (#natives.squads < (AI_MAX_SQUAD_COUNT * evolution_factor)) then
|
||||
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)))
|
||||
|
||||
if valid and (math.random() < mMax((0.25 * evolution_factor), 0.10)) then
|
||||
local squadPosition = {x=0, y=0}
|
||||
|
||||
local squadPath, _ = scoreNeighbors(chunk,
|
||||
getNeighborChunks(regionMap, chunk.cX, chunk.cY),
|
||||
validUnitGroupLocation,
|
||||
scoreUnitGroupLocation,
|
||||
nil,
|
||||
surface,
|
||||
squadPosition,
|
||||
false)
|
||||
if (squadPath ~= nil) then
|
||||
squadPosition.x = squadPath.pX + HALF_CHUNK_SIZE
|
||||
squadPosition.y = squadPath.pY + HALF_CHUNK_SIZE
|
||||
if squadPath then
|
||||
local squadPosition = { x = squadPath.x + HALF_CHUNK_SIZE,
|
||||
y = squadPath.y + HALF_CHUNK_SIZE }
|
||||
|
||||
local squad = createSquad(squadPosition, surface, natives)
|
||||
|
||||
|
@@ -25,7 +25,8 @@ local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
|
||||
|
||||
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
|
||||
|
||||
local NEST_BASE = constants.NEST_BASE
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
local WORM_COUNT = constants.WORM_COUNT
|
||||
|
||||
-- imported functions
|
||||
|
||||
@@ -43,20 +44,19 @@ local function validRetreatLocation(x, chunk, neighborChunk)
|
||||
return canMoveChunkDirection(x, chunk, neighborChunk)
|
||||
end
|
||||
|
||||
local function scoreRetreatLocation(position, squad, neighborChunk, surface)
|
||||
local function scoreRetreatLocation(squad, neighborChunk, surface)
|
||||
local safeScore = -neighborChunk[BASE_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE]
|
||||
local dangerScore = surface.get_pollution(position) + (neighborChunk[PLAYER_PHEROMONE] * 100) --+ (neighborChunk[ENEMY_BASE_GENERATOR] * 50)
|
||||
local dangerScore = surface.get_pollution(neighborChunk) + (neighborChunk[PLAYER_PHEROMONE] * 100) --+ (neighborChunk[ENEMY_BASE_GENERATOR] * 50)
|
||||
return safeScore - dangerScore
|
||||
end
|
||||
|
||||
function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick)
|
||||
if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (#chunk[NEST_BASE] == 0) then
|
||||
if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (chunk[NEST_COUNT] == 0) and (chunk[WORM_COUNT] == 0) then
|
||||
local performRetreat = false
|
||||
local enemiesToSquad
|
||||
local enemiesToSquad = nil
|
||||
|
||||
if (squad == nil) then
|
||||
enemiesToSquad = surface.find_enemy_units({x=chunk.pX,
|
||||
y=chunk.pY}, RETREAT_GRAB_RADIUS)
|
||||
if not squad then
|
||||
enemiesToSquad = surface.find_enemy_units(chunk, RETREAT_GRAB_RADIUS)
|
||||
performRetreat = #enemiesToSquad > 0
|
||||
elseif squad.group.valid and (squad.status ~= SQUAD_RETREATING) and not squad.kamikaze then
|
||||
performRetreat = #squad.group.members > 1
|
||||
@@ -64,31 +64,29 @@ function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick)
|
||||
|
||||
if performRetreat then
|
||||
chunk[RETREAT_TRIGGERED] = tick
|
||||
local retreatPosition = {x=0, y=0}
|
||||
local exitPath,_ = scoreNeighborsWithDirection(chunk,
|
||||
getNeighborChunksWithDirection(regionMap, chunk.cX, chunk.cY),
|
||||
validRetreatLocation,
|
||||
scoreRetreatLocation,
|
||||
nil,
|
||||
surface,
|
||||
retreatPosition,
|
||||
false)
|
||||
if exitPath then
|
||||
retreatPosition.x = exitPath.pX + HALF_CHUNK_SIZE
|
||||
retreatPosition.y = exitPath.pY + HALF_CHUNK_SIZE
|
||||
local retreatPosition = { x = exitPath.x + HALF_CHUNK_SIZE,
|
||||
y = exitPath.y + HALF_CHUNK_SIZE }
|
||||
|
||||
-- in order for units in a group attacking to retreat, we have to create a new group and give the command to join
|
||||
-- to each unit, this is the only way I have found to have snappy mid battle retreats even after 0.14.4
|
||||
|
||||
local newSquad = findNearBySquad(natives, retreatPosition, HALF_CHUNK_SIZE, RETREAT_FILTER)
|
||||
|
||||
if (newSquad == nil) then
|
||||
if not newSquad then
|
||||
newSquad = createSquad(retreatPosition, surface, natives)
|
||||
newSquad.status = SQUAD_RETREATING
|
||||
newSquad.cycles = 4
|
||||
end
|
||||
|
||||
if (enemiesToSquad ~= nil) then
|
||||
if enemiesToSquad then
|
||||
membersToSquad(newSquad, enemiesToSquad, false)
|
||||
else
|
||||
membersToSquad(newSquad, squad.group.members, true)
|
||||
|
@@ -4,7 +4,7 @@ local aiPlanning = {}
|
||||
|
||||
local constants = require("Constants")
|
||||
local mathUtils = require("MathUtils")
|
||||
local nocturnalUtils = require("NocturnalUtils")
|
||||
local aiPredicates = require("AIPredicates")
|
||||
|
||||
-- constants
|
||||
|
||||
@@ -26,7 +26,7 @@ local TICKS_A_MINUTE = constants.TICKS_A_MINUTE
|
||||
|
||||
-- imported functions
|
||||
|
||||
local canAttackNocturnal = nocturnalUtils.canAttack
|
||||
local canAttack = aiPredicates.canAttack
|
||||
|
||||
local randomTickEvent = mathUtils.randomTickEvent
|
||||
|
||||
@@ -34,13 +34,22 @@ local mMax = math.max
|
||||
|
||||
-- module code
|
||||
|
||||
local function isShockwaveReady(evolution_factor, natives, surface, tick, maxPoints)
|
||||
return canAttack(natives, surface) and
|
||||
(tick - natives.lastShakeMessage > TICKS_A_MINUTE * 5) and
|
||||
((evolution_factor > 0.7) and
|
||||
(natives.points > maxPoints * 0.85) and
|
||||
(#natives.squads > AI_MAX_SQUAD_COUNT * 0.35))
|
||||
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
|
||||
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)
|
||||
natives.points = natives.points + math.floor((AI_POINT_GENERATOR_AMOUNT * math.random()) +
|
||||
((AI_POINT_GENERATOR_AMOUNT * 0.7) * (evolution_factor ^ 2.5)) * natives.aiPointsScaler)
|
||||
end
|
||||
|
||||
if (natives.temperamentTick == tick) then
|
||||
@@ -60,7 +69,7 @@ function aiPlanning.planning(natives, evolution_factor, tick, surface)
|
||||
natives.stateTick = randomTickEvent(tick, AI_MIN_STATE_DURATION, AI_MAX_STATE_DURATION)
|
||||
end
|
||||
|
||||
if ((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)) and (tick - natives.lastShakeMessage > TICKS_A_MINUTE * 5) and ((evolution_factor > 0.7) and (natives.points > maxPoints * 0.85) and (#natives.squads > AI_MAX_SQUAD_COUNT * 0.35)) then
|
||||
if isShockwaveReady(evolution_factor, natives, surface, tick, maxPoints) then
|
||||
natives.lastShakeMessage = tick
|
||||
surface.print("Rampant: The ground begins to shake")
|
||||
end
|
||||
|
@@ -5,6 +5,7 @@ local baseUtils = {}
|
||||
local mapUtils = require("MapUtils")
|
||||
local constants = require("Constants")
|
||||
local mathUtils = require("MathUtils")
|
||||
|
||||
local entityUtils = require("EntityUtils")
|
||||
|
||||
-- constants
|
||||
@@ -13,6 +14,12 @@ local BASE_DISTANCE_THRESHOLD = constants.BASE_DISTANCE_THRESHOLD
|
||||
|
||||
local BASE_ALIGNMENT_NEUTRAL = constants.BASE_ALIGNMENT_NEUTRAL
|
||||
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
local WORM_COUNT = constants.WORM_COUNT
|
||||
|
||||
local NEST_BASE = constants.NEST_BASE
|
||||
local WORM_BASE = constants.WORM_BASE
|
||||
|
||||
local AI_NEST_COST = constants.AI_NEST_COST
|
||||
local AI_WORM_COST = constants.AI_WORM_COST
|
||||
|
||||
@@ -25,34 +32,34 @@ local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER
|
||||
|
||||
local euclideanDistancePoints = mapUtils.euclideanDistancePoints
|
||||
|
||||
local gaussianRandomRange = mathUtils.gaussianRandomRange
|
||||
local getEntityOverlapChunks = entityUtils.getEntityOverlapChunks
|
||||
|
||||
local addEnemyBase = entityUtils.addEnemyBase
|
||||
local gaussianRandomRange = mathUtils.gaussianRandomRange
|
||||
|
||||
-- module code
|
||||
|
||||
function baseUtils.annexNest(natives, position)
|
||||
function baseUtils.findNearbyBase(natives, position)
|
||||
local bases = natives.bases
|
||||
local annex = nil
|
||||
local foundBase
|
||||
local closest = MAGIC_MAXIMUM_NUMBER
|
||||
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
|
||||
closest = distance
|
||||
annex = base
|
||||
foundBase = base
|
||||
end
|
||||
end
|
||||
return annex
|
||||
return foundBase
|
||||
end
|
||||
|
||||
function baseUtils.buildHive(regionMap, base, surface)
|
||||
local valid = false
|
||||
local position = surface.find_non_colliding_position("biter-spawner", {x=base.x, y=base.y}, 2*CHUNK_SIZE, 10)
|
||||
local position = surface.find_non_colliding_position("biter-spawner-hive", {x=base.x, y=base.y}, 2*CHUNK_SIZE, 2)
|
||||
if position then
|
||||
local biterSpawner = {name="biter-spawner", position=position}
|
||||
base.hive = surface.create_entity(biterSpawner)
|
||||
addEnemyBase(regionMap, base.hive, base)
|
||||
local biterSpawner = {name="biter-spawner-hive", position=position}
|
||||
base.hives[#base.hives+1] = surface.create_entity(biterSpawner)
|
||||
baseUtils.registerEnemyBaseStructure(regionMap, base.hive, base)
|
||||
valid = true
|
||||
end
|
||||
return valid
|
||||
@@ -67,7 +74,7 @@ function baseUtils.buildTendril(natives, base, surface, tick, startPosition, end
|
||||
end
|
||||
|
||||
function baseUtils.buildOrder(regionMap, natives, base, surface, tick)
|
||||
if not base.hive or (base.upgradePoints < 10) then
|
||||
if (#base.hives == 0) or (base.upgradePoints < 10) then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -100,7 +107,7 @@ function baseUtils.buildOrder(regionMap, natives, base, surface, tick)
|
||||
y = base.y + (distortion * math.sin(pos))}
|
||||
local biterSpawner = {name=thing, position=nestPosition}
|
||||
if surface.can_place_entity(biterSpawner) then
|
||||
addEnemyBase(regionMap, surface.create_entity(biterSpawner), base)
|
||||
baseUtils.registerEnemyBaseStructure(regionMap, surface.create_entity(biterSpawner), base)
|
||||
base.upgradePoints = base.upgradePoints - cost
|
||||
end
|
||||
pos = pos + slice
|
||||
@@ -115,7 +122,7 @@ function baseUtils.createBase(regionMap, natives, position, surface, tick)
|
||||
y = position.y,
|
||||
created = tick,
|
||||
alignment = { BASE_ALIGNMENT_NEUTRAL },
|
||||
hive = nil,
|
||||
hives = {},
|
||||
nests = {},
|
||||
worms = {},
|
||||
eggs = {},
|
||||
@@ -131,5 +138,93 @@ function baseUtils.createBase(regionMap, natives, position, surface, tick)
|
||||
return base
|
||||
end
|
||||
|
||||
function baseUtils.addEnemyStructureToChunk(chunk, entity, base)
|
||||
local indexChunk
|
||||
local indexBase
|
||||
local countChunk
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexChunk = chunk[NEST_BASE]
|
||||
if base then
|
||||
indexBase = base.nests
|
||||
end
|
||||
countChunk = NEST_COUNT
|
||||
elseif (entity.type == "turret") then
|
||||
indexChunk = chunk[WORM_BASE]
|
||||
if base then
|
||||
indexBase = base.worms
|
||||
end
|
||||
countChunk = WORM_COUNT
|
||||
end
|
||||
chunk[countChunk] = chunk[countChunk] + 1
|
||||
if indexBase then
|
||||
indexChunk[entity.unit_number] = base
|
||||
indexBase[entity.unit_number] = entity
|
||||
end
|
||||
end
|
||||
|
||||
function baseUtils.removeEnemyStructureFromChunk(chunk, entity)
|
||||
local indexChunk
|
||||
local countChunk
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexChunk = chunk[NEST_BASE]
|
||||
countChunk = NEST_COUNT
|
||||
elseif (entity.type == "turret") then
|
||||
indexChunk = chunk[WORM_BASE]
|
||||
countChunk = WORM_COUNT
|
||||
end
|
||||
local base = indexChunk[entity.unit_number]
|
||||
local indexBase
|
||||
if base then
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexBase = base.nests
|
||||
elseif (entity.type == "turret") then
|
||||
indexBase = base.worms
|
||||
end
|
||||
indexBase[entity.unit_number] = nil
|
||||
end
|
||||
chunk[countChunk] = chunk[countChunk] - 1
|
||||
end
|
||||
|
||||
function baseUtils.registerEnemyBaseStructure(regionMap, entity, base)
|
||||
local entityType = entity.type
|
||||
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
|
||||
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
|
||||
|
||||
if leftTop then
|
||||
baseUtils.addEnemyStructureToChunk(leftTop, entity, base)
|
||||
end
|
||||
if rightTop then
|
||||
baseUtils.addEnemyStructureToChunk(rightTop, entity, base)
|
||||
end
|
||||
if leftBottom then
|
||||
baseUtils.addEnemyStructureToChunk(leftBottom, entity, base)
|
||||
end
|
||||
if rightBottom then
|
||||
baseUtils.addEnemyStructureToChunk(rightBottom, entity, base)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function baseUtils.unregisterEnemyBaseStructure(regionMap, entity)
|
||||
local entityType = entity.type
|
||||
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
|
||||
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
|
||||
|
||||
if leftTop then
|
||||
baseUtils.removeEnemyStructureFromChunk(leftTop, entity)
|
||||
end
|
||||
if rightTop then
|
||||
baseUtils.removeEnemyStructureFromChunk(rightTop, entity)
|
||||
end
|
||||
if leftBottom then
|
||||
baseUtils.removeEnemyStructureFromChunk(leftBottom, entity)
|
||||
end
|
||||
if rightBottom then
|
||||
baseUtils.removeEnemyStructureFromChunk(rightBottom, entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return baseUtils
|
||||
|
||||
|
@@ -14,6 +14,8 @@ local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
|
||||
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
|
||||
local NEST_BASE = constants.NEST_BASE
|
||||
local WORM_BASE = constants.WORM_BASE
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
local WORM_COUNT = constants.WORM_COUNT
|
||||
|
||||
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
|
||||
|
||||
@@ -30,13 +32,13 @@ local RALLY_TRIGGERED = constants.RALLY_TRIGGERED
|
||||
|
||||
-- imported functions
|
||||
|
||||
local annexNest = baseUtils.annexNest
|
||||
local findNearbyBase = baseUtils.findNearbyBase
|
||||
local createBase = baseUtils.createBase
|
||||
local addEnemyStructureToChunk = baseUtils.addEnemyStructureToChunk
|
||||
|
||||
-- module code
|
||||
|
||||
local function checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, surface)
|
||||
local get_tile = surface.get_tile
|
||||
local function checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, get_tile)
|
||||
|
||||
for x=iteratingCoordinate, iteratingCoordinate + 31 do
|
||||
local tile
|
||||
@@ -53,19 +55,20 @@ local function checkForDeadendTiles(constantCoordinate, iteratingCoordinate, dir
|
||||
end
|
||||
|
||||
function chunkUtils.checkChunkPassability(chunk, surface)
|
||||
local x = chunk.pX
|
||||
local y = chunk.pY
|
||||
local x = chunk.x
|
||||
local y = chunk.y
|
||||
local get_tile = surface.get_tile
|
||||
|
||||
local passableNorthSouth = false
|
||||
local passableEastWest = false
|
||||
for xi=x, x + 31 do
|
||||
if (not checkForDeadendTiles(xi, y, NORTH_SOUTH, surface)) 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, surface)) then
|
||||
if (not checkForDeadendTiles(yi, x, EAST_WEST, get_tile)) then
|
||||
passableEastWest = true
|
||||
break
|
||||
end
|
||||
@@ -76,12 +79,11 @@ function chunkUtils.checkChunkPassability(chunk, surface)
|
||||
end
|
||||
|
||||
function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
local x = chunk.pX
|
||||
local y = chunk.pY
|
||||
local x = chunk.x
|
||||
local y = chunk.y
|
||||
|
||||
local chunkPosition = {x=x, y=y}
|
||||
local areaBoundingBox = {
|
||||
chunkPosition,
|
||||
chunk,
|
||||
{x + 32, y + 32}
|
||||
}
|
||||
local enemyChunkQuery = {area=areaBoundingBox,
|
||||
@@ -89,25 +91,47 @@ function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
local playerChunkQuery = {area=areaBoundingBox,
|
||||
force="player"}
|
||||
|
||||
local useCustomAI = natives.useCustomAI
|
||||
local enemies = surface.find_entities_filtered(enemyChunkQuery)
|
||||
|
||||
local nestsRemoved = 0
|
||||
local wormsRemoved = 0
|
||||
local bitersRemoved = 0
|
||||
local nests = 0
|
||||
local worms = 0
|
||||
local biters = 0
|
||||
|
||||
for i=1, #enemies do
|
||||
local entityType = enemies[i].type
|
||||
if (entityType == "unit-spawner") then
|
||||
nestsRemoved = nestsRemoved + 3
|
||||
nests = nests + 1
|
||||
elseif (entityType == "turret") then
|
||||
wormsRemoved = wormsRemoved + 2
|
||||
elseif (entityType == "unit") then
|
||||
bitersRemoved = bitersRemoved + 1
|
||||
worms = worms + 1
|
||||
elseif useCustomAI and (entityType == "unit") then
|
||||
biters = biters + 1
|
||||
end
|
||||
end
|
||||
|
||||
entities = surface.find_entities_filtered(playerChunkQuery)
|
||||
if useCustomAI then
|
||||
if (nests > 0) or (worms > 0) or (biters > 0) then
|
||||
for f=1, #enemies do
|
||||
enemies[f].destroy()
|
||||
end
|
||||
local foundBase = findNearbyBase(natives, chunk) or createBase(regionMap, natives, chunk, surface, tick)
|
||||
if foundBase then
|
||||
foundBase.upgradePoints = foundBase.upgradePoints + (nests*3) + (worms*2) + biters
|
||||
end
|
||||
end
|
||||
else
|
||||
for i=1, #enemies do
|
||||
local enemy = enemies[i]
|
||||
local enemyType = enemy.type
|
||||
if (enemyType == "unit-spawner") or (enemyType == "turret") then
|
||||
addEnemyStructureToChunk(chunk, enemy, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local entities = surface.find_entities_filtered(playerChunkQuery)
|
||||
|
||||
local playerObjects = 0
|
||||
local safeBuildings = natives.safeBuildings
|
||||
for i=1, #entities do
|
||||
local entity = entities[i]
|
||||
@@ -119,22 +143,7 @@ function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
|
||||
end
|
||||
end
|
||||
|
||||
if (nestsRemoved > 0) or (wormsRemoved > 0) or (bitersRemoved > 0) then
|
||||
for i=1, #enemies do
|
||||
enemies[i].destroy()
|
||||
end
|
||||
local foundBase = annexNest(natives, chunkPosition) or createBase(regionMap, natives, chunkPosition, surface, tick)
|
||||
if foundBase then
|
||||
foundBase.upgradePoints = foundBase.upgradePoints + nestsRemoved + wormsRemoved + bitersRemoved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local playerObjects = 0
|
||||
local entities = surface.find_entities_filtered(playerChunkQuery)
|
||||
|
||||
for i=1, #entities do
|
||||
local entityScore = BUILDING_PHEROMONES[entities[i].type]
|
||||
local entityScore = BUILDING_PHEROMONES[entityType]
|
||||
if (entityScore ~= nil) then
|
||||
playerObjects = playerObjects + entityScore
|
||||
end
|
||||
@@ -145,8 +154,8 @@ end
|
||||
|
||||
function chunkUtils.createChunk(topX, topY)
|
||||
local chunk = {
|
||||
pX = topX,
|
||||
pY = topY,
|
||||
x = topX,
|
||||
y = topY,
|
||||
cX = topX * 0.03125,
|
||||
cY = topY * 0.03125
|
||||
}
|
||||
@@ -161,6 +170,8 @@ function chunkUtils.createChunk(topX, topY)
|
||||
chunk[RALLY_TRIGGERED] = 0
|
||||
chunk[NEST_BASE] = {}
|
||||
chunk[WORM_BASE] = {}
|
||||
chunk[NEST_COUNT] = 0
|
||||
chunk[WORM_COUNT] = 0
|
||||
return chunk
|
||||
end
|
||||
|
||||
|
@@ -17,6 +17,7 @@ constants.VERSION_19 = 19
|
||||
constants.VERSION_20 = 20
|
||||
constants.VERSION_21 = 21
|
||||
constants.VERSION_22 = 22
|
||||
constants.VERSION_23 = 23
|
||||
|
||||
-- misc
|
||||
|
||||
@@ -91,7 +92,7 @@ constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX = 0.40
|
||||
-- pheromone amounts
|
||||
|
||||
constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = 500
|
||||
constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = 30
|
||||
--constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = 30
|
||||
constants.DEATH_PHEROMONE_GENERATOR_AMOUNT = 500
|
||||
constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT = 150
|
||||
|
||||
@@ -120,6 +121,8 @@ constants.RETREAT_TRIGGERED = 9
|
||||
constants.RALLY_TRIGGERED = 10
|
||||
constants.NEST_BASE = 11
|
||||
constants.WORM_BASE = 12
|
||||
constants.NEST_COUNT = 13
|
||||
constants.WORM_COUNT = 14
|
||||
|
||||
-- Squad status
|
||||
|
||||
@@ -132,8 +135,8 @@ constants.SQUAD_RAIDING = 4 -- used when player stuff is close
|
||||
|
||||
constants.RETREAT_GRAB_RADIUS = 24
|
||||
|
||||
constants.BASE_RALLY_CHANCE = 0.01
|
||||
constants.BONUS_RALLY_CHANCE = 0.01
|
||||
constants.BASE_RALLY_CHANCE = 0.02
|
||||
constants.BONUS_RALLY_CHANCE = 0.06
|
||||
|
||||
constants.MAX_RALLY_CRIES = 2
|
||||
constants.RALLY_CRY_DISTANCE = 3
|
||||
|
@@ -11,9 +11,6 @@ local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
|
||||
|
||||
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
|
||||
|
||||
local NEST_BASE = constants.NEST_BASE
|
||||
local WORM_BASE = constants.WORM_BASE
|
||||
|
||||
local DEFINES_DIRECTION_EAST = defines.direction.east
|
||||
local DEFINES_WIRE_TYPE_RED = defines.wire_type.red
|
||||
local DEFINES_WIRE_TYPE_GREEN = defines.wire_type.green
|
||||
@@ -26,8 +23,8 @@ local mFloor = math.floor
|
||||
|
||||
-- module code
|
||||
|
||||
local function getEntityOverlapChunks(regionMap, entity)
|
||||
local boundingBox = entity.prototype.selection_box;
|
||||
function entityUtils.getEntityOverlapChunks(regionMap, entity)
|
||||
local boundingBox = entity.prototype.selection_box or entity.prototype.collision_box;
|
||||
|
||||
local leftTopChunk
|
||||
local rightTopChunk
|
||||
@@ -88,102 +85,28 @@ function entityUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject
|
||||
if (BUILDING_PHEROMONES[entity.type] ~= nil) and (entity.force.name == "player") then
|
||||
entityValue = BUILDING_PHEROMONES[entity.type]
|
||||
|
||||
leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
|
||||
leftTop, rightTop, leftBottom, rightBottom = entityUtils.getEntityOverlapChunks(regionMap, entity)
|
||||
if not addObject then
|
||||
if creditNatives then
|
||||
natives.points = natives.points + entityValue
|
||||
end
|
||||
entityValue = -entityValue
|
||||
end
|
||||
if (leftTop ~= nil) then
|
||||
if leftTop then
|
||||
leftTop[PLAYER_BASE_GENERATOR] = leftTop[PLAYER_BASE_GENERATOR] + entityValue
|
||||
end
|
||||
if (rightTop ~= nil) then
|
||||
if rightTop then
|
||||
rightTop[PLAYER_BASE_GENERATOR] = rightTop[PLAYER_BASE_GENERATOR] + entityValue
|
||||
end
|
||||
if (leftBottom ~= nil) then
|
||||
if leftBottom then
|
||||
leftBottom[PLAYER_BASE_GENERATOR] = leftBottom[PLAYER_BASE_GENERATOR] + entityValue
|
||||
end
|
||||
if (rightBottom ~= nil) then
|
||||
if rightBottom then
|
||||
rightBottom[PLAYER_BASE_GENERATOR] = rightBottom[PLAYER_BASE_GENERATOR] + entityValue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function addBaseToChunk(chunk, entity, base)
|
||||
local indexChunk
|
||||
local indexBase
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexChunk = chunk[NEST_BASE]
|
||||
indexBase = base.nests
|
||||
elseif (entity.type == "turret") then
|
||||
indexChunk = chunk[WORM_BASE]
|
||||
indexBase = base.worms
|
||||
end
|
||||
indexChunk[entity.unit_number] = base
|
||||
indexBase[entity.unit_number] = entity
|
||||
end
|
||||
|
||||
local function removeBaseFromChunk(chunk, entity)
|
||||
local indexChunk
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexChunk = chunk[NEST_BASE]
|
||||
elseif (entity.type == "turret") then
|
||||
indexChunk = chunk[WORM_BASE]
|
||||
end
|
||||
local base = indexChunk[entity.unit_number]
|
||||
local indexBase
|
||||
if base then
|
||||
if (entity.type == "unit-spawner") then
|
||||
indexBase = base.nests
|
||||
elseif (entity.type == "turret") then
|
||||
indexBase = base.worms
|
||||
end
|
||||
indexBase[entity.unit_number] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function entityUtils.addEnemyBase(regionMap, entity, base)
|
||||
local entityType = entity.type
|
||||
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
|
||||
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
|
||||
|
||||
if (leftTop ~= nil) then
|
||||
addBaseToChunk(leftTop, entity, base)
|
||||
end
|
||||
if (rightTop ~= nil) then
|
||||
addBaseToChunk(rightTop, entity, base)
|
||||
end
|
||||
if (leftBottom ~= nil) then
|
||||
addBaseToChunk(leftBottom, entity, base)
|
||||
end
|
||||
if (rightBottom ~= nil) then
|
||||
addBaseToChunk(rightBottom, entity, base)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function entityUtils.removeEnemyBase(regionMap, entity)
|
||||
local entityType = entity.type
|
||||
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
|
||||
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
|
||||
|
||||
if (leftTop ~= nil) then
|
||||
removeBaseFromChunk(leftTop, entity)
|
||||
end
|
||||
if (rightTop ~= nil) then
|
||||
removeBaseFromChunk(rightTop, entity)
|
||||
end
|
||||
if (leftBottom ~= nil) then
|
||||
removeBaseFromChunk(leftBottom, entity)
|
||||
end
|
||||
if (rightBottom ~= nil) then
|
||||
removeBaseFromChunk(rightBottom, entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function entityUtils.makeImmortalEntity(surface, entity)
|
||||
local repairPosition = entity.position
|
||||
local repairName = entity.name
|
||||
|
@@ -23,7 +23,8 @@ local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
|
||||
local CHUNK_TICK = constants.CHUNK_TICK
|
||||
|
||||
local NEST_BASE = constants.NEST_BASE
|
||||
local NEST_COUNT = constants.NEST_COUNT
|
||||
local WORM_COUNT = constants.WORM_COUNT
|
||||
|
||||
local AI_MAX_POINTS = constants.AI_MAX_POINTS
|
||||
local AI_SQUAD_COST = constants.AI_SQUAD_COST
|
||||
@@ -47,6 +48,8 @@ local playerScent = pheromoneUtils.playerScent
|
||||
|
||||
local canAttack = aiPredicates.canAttack
|
||||
|
||||
local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
|
||||
|
||||
local mMin = math.min
|
||||
|
||||
local validPlayer = playerUtils.validPlayer
|
||||
@@ -140,12 +143,11 @@ 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_BASE] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < vengenceThreshold))
|
||||
local vengence = canAttack(natives, surface) and ((playerChunk[NEST_COUNT] ~= 0) or (playerChunk[WORM_COUNT] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < vengenceThreshold))
|
||||
|
||||
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
|
||||
|
||||
@@ -172,11 +174,11 @@ end
|
||||
Passive scan to find entities that have been generated outside the factorio event system
|
||||
--]]
|
||||
function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
-- local index = regionMap.scanPointer
|
||||
local index = regionMap.scanPointer
|
||||
|
||||
local chunkPosition = {x=0,y=0}
|
||||
local chunkBox = {chunkPosition,
|
||||
{x=chunkPosition.x + CHUNK_SIZE, y=chunkPosition.y + CHUNK_SIZE}}
|
||||
local chunkBox = {false,
|
||||
{x=0,
|
||||
y=0}}
|
||||
local playerQuery = {area = chunkBox,
|
||||
force = "player"}
|
||||
local spawnerQuery = {area = chunkBox,
|
||||
@@ -195,11 +197,10 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
for x=index,endIndex do
|
||||
local chunk = processQueue[x]
|
||||
|
||||
chunkPosition.x = chunk.pX
|
||||
chunkPosition.y = chunk.pY
|
||||
chunkBox[1] = chunk
|
||||
|
||||
chunkBox[2].x = chunkPosition.x + CHUNK_SIZE
|
||||
chunkBox[2].y = chunkPosition.y + CHUNK_SIZE
|
||||
chunkBox[2].x = chunk.x + CHUNK_SIZE
|
||||
chunkBox[2].y = chunk.y + CHUNK_SIZE
|
||||
|
||||
local entities = surface.find_entities_filtered(playerQuery)
|
||||
|
||||
@@ -213,7 +214,7 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
if (unitCount > 300) then
|
||||
for i=1,#natives.squads do
|
||||
local squadGroup = natives.squads[i].group
|
||||
if squadGroup.valid and (euclideanDistanceNamed(squadGroup.position, chunkPosition) < CHUNK_SIZE * 2) then
|
||||
if squadGroup.valid and (euclideanDistanceNamed(squadGroup.position, chunk) < CHUNK_SIZE * 2) then
|
||||
closeBy = true
|
||||
end
|
||||
end
|
||||
@@ -221,7 +222,7 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
|
||||
if (unitCount > 300) and not closeBy then
|
||||
local weight = AI_UNIT_REFUND * evolution_factor
|
||||
local units = surface.find_enemy_units(chunkPosition, CHUNK_SIZE * 3)
|
||||
local units = surface.find_enemy_units(chunk, CHUNK_SIZE * 3)
|
||||
|
||||
for i=1,#units do
|
||||
units[i].destroy()
|
||||
@@ -248,7 +249,8 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
|
||||
end
|
||||
end
|
||||
|
||||
chunk[ENEMY_BASE_GENERATOR] = (spawners * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT) + worms
|
||||
chunk[NEST_COUNT] = spawners
|
||||
chunk[WORM_COUNT] = worms
|
||||
chunk[PLAYER_BASE_GENERATOR] = playerBaseGenerator
|
||||
end
|
||||
|
||||
|
@@ -49,21 +49,21 @@ function mapUtils.getNeighborChunks(regionMap, chunkX, chunkY)
|
||||
local chunkYRow1 = chunkY - 1
|
||||
local chunkYRow3 = chunkY + 1
|
||||
local xChunks = regionMap[chunkX-1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[1] = xChunks[chunkYRow1]
|
||||
neighbors[4] = xChunks[chunkY]
|
||||
neighbors[6] = xChunks[chunkYRow3]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX+1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[3] = xChunks[chunkYRow1]
|
||||
neighbors[5] = xChunks[chunkY]
|
||||
neighbors[8] = xChunks[chunkYRow3]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[2] = xChunks[chunkYRow1]
|
||||
neighbors[7] = xChunks[chunkYRow3]
|
||||
end
|
||||
@@ -89,21 +89,21 @@ function mapUtils.getNeighborChunksWithDirection(regionMap, chunkX, chunkY)
|
||||
local chunkYRow3 = chunkY + 1
|
||||
|
||||
local xChunks = regionMap[chunkX-1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[1] = xChunks[chunkYRow1]
|
||||
neighbors[4] = xChunks[chunkY]
|
||||
neighbors[6] = xChunks[chunkYRow3]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX+1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[3] = xChunks[chunkYRow1]
|
||||
neighbors[5] = xChunks[chunkY]
|
||||
neighbors[8] = xChunks[chunkYRow3]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[2] = xChunks[chunkYRow1]
|
||||
neighbors[7] = xChunks[chunkYRow3]
|
||||
end
|
||||
@@ -120,18 +120,18 @@ end
|
||||
function mapUtils.getCardinalChunksWithDirection(regionMap, chunkX, chunkY)
|
||||
local neighbors = {false, false, false, false}
|
||||
local xChunks = regionMap[chunkX]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[1] = xChunks[chunkY-1]
|
||||
neighbors[4] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX-1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[2] = xChunks[chunkY]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX+1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[3] = xChunks[chunkY]
|
||||
end
|
||||
return neighbors
|
||||
@@ -140,18 +140,18 @@ end
|
||||
function mapUtils.getCardinalChunks(regionMap, chunkX, chunkY)
|
||||
local neighbors = {false,false,false,false}
|
||||
local xChunks = regionMap[chunkX]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[1] = xChunks[chunkY-1]
|
||||
neighbors[4] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX-1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[2] = xChunks[chunkY]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX+1]
|
||||
if (xChunks ~= nil) then
|
||||
if xChunks then
|
||||
neighbors[3] = xChunks[chunkY]
|
||||
end
|
||||
return neighbors
|
||||
|
@@ -14,17 +14,14 @@ local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
|
||||
--[[
|
||||
Expects all neighbors adjacent to a chunk
|
||||
--]]
|
||||
function neighborUtils.scoreNeighborsWithDirection(chunk, neighborDirectionChunks, validFunction, scoreFunction, squad, surface, position, scoreSelf)
|
||||
function neighborUtils.scoreNeighborsWithDirection(chunk, neighborDirectionChunks, validFunction, scoreFunction, squad, surface, scoreSelf)
|
||||
local highestChunk
|
||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
||||
local highestDirection
|
||||
for x=1,8 do
|
||||
local neighborChunk = neighborDirectionChunks[x]
|
||||
|
||||
if neighborChunk and validFunction(x, chunk, neighborChunk) then
|
||||
position.x = neighborChunk.pX
|
||||
position.y = neighborChunk.pY
|
||||
local score = scoreFunction(position, squad, neighborChunk, surface)
|
||||
local score = scoreFunction(squad, neighborChunk, surface)
|
||||
if (score > highestScore) then
|
||||
highestScore = score
|
||||
highestChunk = neighborChunk
|
||||
@@ -33,7 +30,7 @@ function neighborUtils.scoreNeighborsWithDirection(chunk, neighborDirectionChunk
|
||||
end
|
||||
end
|
||||
|
||||
if scoreSelf and scoreFunction(position, squad, chunk, surface) > highestScore then
|
||||
if scoreSelf and scoreFunction(squad, chunk, surface) > highestScore then
|
||||
return nil, -1
|
||||
end
|
||||
|
||||
@@ -44,15 +41,13 @@ end
|
||||
--[[
|
||||
Expects all neighbors adjacent to a chunk
|
||||
--]]
|
||||
function neighborUtils.scoreNeighbors(chunk, neighborChunks, validFunction, scoreFunction, squad, surface, position, scoreSelf)
|
||||
function neighborUtils.scoreNeighbors(chunk, neighborChunks, validFunction, scoreFunction, squad, surface, scoreSelf)
|
||||
local highestChunk
|
||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
||||
for x=1,8 do
|
||||
local neighborChunk = neighborChunks[x]
|
||||
if neighborChunk and validFunction(x, chunk, neighborChunk) then
|
||||
position.x = neighborChunk.pX
|
||||
position.y = neighborChunk.pY
|
||||
local score = scoreFunction(position, squad, neighborChunk, surface)
|
||||
local score = scoreFunction(squad, neighborChunk, surface)
|
||||
if (score > highestScore) then
|
||||
highestScore = score
|
||||
highestChunk = neighborChunk
|
||||
@@ -60,7 +55,7 @@ function neighborUtils.scoreNeighbors(chunk, neighborChunks, validFunction, scor
|
||||
end
|
||||
end
|
||||
|
||||
if scoreSelf and scoreFunction(position, squad, chunk, surface) > highestScore then
|
||||
if scoreSelf and scoreFunction(squad, chunk, surface) > highestScore then
|
||||
return nil, -1
|
||||
end
|
||||
|
||||
|
@@ -12,6 +12,8 @@ local MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERAT
|
||||
local DEFINES_GROUP_STATE_FINISHED = defines.group_state.finished
|
||||
local DEFINES_GROUP_STATE_ATTACKING_TARGET = defines.group_state.attacking_target
|
||||
local DEFINES_GROUP_STATE_ATTACKING_DISTRACTION = defines.group_state.attacking_distraction
|
||||
local DEFINES_COMMAND_GROUP = defines.command.group
|
||||
local DEFINES_DISTRACTION_NONE = defines.distraction.none
|
||||
|
||||
local SQUAD_RETREATING = constants.SQUAD_RETREATING
|
||||
local SQUAD_GUARDING = constants.SQUAD_GUARDING
|
||||
@@ -42,7 +44,7 @@ function unitGroupUtils.findNearBySquad(natives, position, distance, filter)
|
||||
for i=1,#squads do
|
||||
local squad = squads[i]
|
||||
local unitGroup = squad.group
|
||||
if unitGroup.valid and ((filter == nil) or (filter ~= nil and filter[squad.status])) then
|
||||
if unitGroup.valid and (not filter or (filter and filter[squad.status])) then
|
||||
if (euclideanDistanceNamed(unitGroup.position, position) <= distance) then
|
||||
return squad
|
||||
end
|
||||
@@ -68,12 +70,12 @@ end
|
||||
|
||||
function unitGroupUtils.membersToSquad(squad, members, overwriteGroup)
|
||||
if (members ~= nil) then
|
||||
local cmd = { type = defines.command.group,
|
||||
local cmd = { type = DEFINES_COMMAND_GROUP,
|
||||
group = squad.group,
|
||||
distraction = defines.distraction.none }
|
||||
distraction = DEFINES_DISTRACTION_NONE }
|
||||
for i=1,#members do
|
||||
local member = members[i]
|
||||
if member.valid and (overwriteGroup or (not overwriteGroup and (member.unit_group == nil))) then
|
||||
if member.valid and (overwriteGroup or (not overwriteGroup and not member.unit_group)) then
|
||||
member.set_command(cmd)
|
||||
end
|
||||
end
|
||||
|
@@ -9,6 +9,8 @@ behemoth-suicide-biter=Behemoth Suicide Biter
|
||||
|
||||
small-fire-spitter=Small Fire Spitter
|
||||
|
||||
biter-spawner-hive=Small Hive
|
||||
|
||||
[entity-description]
|
||||
tunnel-entrance=This tunnel is used by the biters to bypass player defenses. Fill the hole using landfill.
|
||||
|
||||
@@ -19,6 +21,8 @@ behemoth-suicide-biter=These biters will explode at close range
|
||||
|
||||
small-fire-spitter=These biters will spit fire
|
||||
|
||||
biter-spawner-hive=Small Hive
|
||||
|
||||
[mod-setting-name]
|
||||
rampant-useDumbProjectiles=Use Dumb Projectiles
|
||||
rampant-useNEUnitLaunchers=Use Natural Evolution Unit Launchers (Needs NE)
|
||||
@@ -38,6 +42,7 @@ rampant-attackPlayerThreshold=Player score contribution threshold
|
||||
rampant-permanentNocturnal=Permanent Nocturnal Mode
|
||||
rampant-aiPointsScaler=Difficulty Scaling
|
||||
rampant-addWallResistanceAcid=Increase wall resistance to spitters
|
||||
rampant-useCustomAI=Use Custom AI
|
||||
|
||||
[mod-setting-description]
|
||||
rampant-useDumbProjectiles=Turns off homing projectiles for worms and spitters
|
||||
@@ -58,3 +63,4 @@ rampant-attackPlayerThreshold=The score that a chunk must reach for it to contri
|
||||
rampant-permanentNocturnal=Toggling this will cause Rampant attack waves to spawn at night. DOES NOT turn off vanilla attack groups yet. Works better with Day/Night extender mod.
|
||||
rampant-aiPointsScaler=Between 0.0 and 5.0. This scales how many action points the ai gets per logic cycle to perform actions like making attack waves. 0.3 - very easy, 0.75 - easy, 1.0 - medium, 1.25+ - hard
|
||||
rampant-addWallResistanceAcid=Toggling this will cause a %60 acid resistance to be added to all wall entities to reduce the damage done by spitters to walls back to vanilla levels.
|
||||
rampant-useCustomAI=Having this enabled will completely remove the vanilla ai and change how biters build, produce units, and attack.
|
6
make.rkt
6
make.rkt
@@ -77,8 +77,8 @@
|
||||
(copyDirectory "prototypes" modFolder)))
|
||||
|
||||
(define (run)
|
||||
;; (copyFiles modFolder)
|
||||
;; (copyFiles zipModFolder)
|
||||
(makeZip modFolder)
|
||||
(copyFiles modFolder)
|
||||
(copyFiles zipModFolder)
|
||||
;;(makeZip modFolder)
|
||||
)
|
||||
)
|
||||
|
@@ -95,14 +95,14 @@ function spawner_die_animation(variation, tint)
|
||||
end
|
||||
|
||||
|
||||
local biter_spawner_powered_tint = {r=1.0, g=1.0, b=1.0, a=1.0}
|
||||
local biter_spawner_powered_tint = {r=1.0, g=0, b=0, a=1.0}
|
||||
|
||||
data:extend({
|
||||
|
||||
|
||||
{
|
||||
type = "unit-spawner",
|
||||
name = "biter-spawner-powered",
|
||||
name = "biter-spawner-hive",
|
||||
icon = "__base__/graphics/icons/biter-spawner.png",
|
||||
flags = {"placeable-player", "placeable-enemy", "not-repairable"},
|
||||
max_health = 350,
|
||||
@@ -148,8 +148,8 @@ data:extend({
|
||||
}
|
||||
},
|
||||
healing_per_tick = 0.02,
|
||||
collision_box = {{-3.2, -2.2}, {2.2, 2.2}},
|
||||
selection_box = {{-3.5, -2.5}, {2.5, 2.5}},
|
||||
collision_box = {{-6, -6}, {6, 6}},
|
||||
selection_box = {{-6, -6}, {6, 6}},
|
||||
-- in ticks per 1 pu
|
||||
pollution_absorbtion_absolute = 20,
|
||||
pollution_absorbtion_proportional = 0.01,
|
||||
|
10
settings.lua
10
settings.lua
@@ -172,6 +172,16 @@ data:extend({
|
||||
default_value = false,
|
||||
order = "g[modifier]-a[damage]",
|
||||
per_user = false
|
||||
},
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "rampant-useCustomAI",
|
||||
description = "rampant-useCustomAI",
|
||||
setting_type = 'startup',
|
||||
default_value = false,
|
||||
order = "h[total]-a[ai]",
|
||||
per_user = false
|
||||
}
|
||||
|
||||
})
|
||||
|
20
tests.lua
20
tests.lua
@@ -3,6 +3,7 @@ local tests = {}
|
||||
local constants = require("libs/Constants")
|
||||
local mathUtils = require("libs/MathUtils")
|
||||
local chunkUtils = require("libs/ChunkUtils")
|
||||
local mapUtils = require("libs/MapUtils")
|
||||
local baseUtils = require("libs/BaseUtils")
|
||||
|
||||
function tests.pheromoneLevels()
|
||||
@@ -22,7 +23,7 @@ function tests.pheromoneLevels()
|
||||
for i=1,#chunk do
|
||||
str = str .. " " .. tostring(i) .. "/" .. tostring(chunk[i])
|
||||
end
|
||||
str = str .. " " .. "p/" .. game.surfaces[1].get_pollution({x=chunk.pX, y=chunk.pY})
|
||||
str = str .. " " .. "p/" .. game.surfaces[1].get_pollution(chunk)
|
||||
if (chunk.cX == playerChunkX) and (chunk.cY == playerChunkY) then
|
||||
print("*", chunk.cX, chunk.cY, str)
|
||||
else
|
||||
@@ -73,6 +74,14 @@ function tests.findNearestPlayerEnemy()
|
||||
print("--")
|
||||
end
|
||||
|
||||
function tests.getOffsetChunk(x, y)
|
||||
local playerPosition = game.players[1].position
|
||||
local chunkX = math.floor(playerPosition.x * 0.03125)
|
||||
local chunkY = math.floor(playerPosition.y * 0.03125)
|
||||
local chunk = mapUtils.getChunkByIndex(global.regionMap, chunkX + x, chunkY + y)
|
||||
print(serpent.dump(chunk))
|
||||
end
|
||||
|
||||
function tests.aiStats()
|
||||
print(global.natives.points, game.tick, global.natives.state, global.natives.temperament, global.natives.stateTick, global.natives.temperamentTick)
|
||||
end
|
||||
@@ -99,7 +108,14 @@ function tests.createEnemy(x)
|
||||
local playerPosition = game.players[1].position
|
||||
local chunkX = math.floor(playerPosition.x * 0.03125) * 32
|
||||
local chunkY = math.floor(playerPosition.y * 0.03125) * 32
|
||||
game.surfaces[1].create_entity({name=x, position={chunkX, chunkY}})
|
||||
return game.surfaces[1].create_entity({name=x, position={chunkX, chunkY}})
|
||||
end
|
||||
|
||||
function tests.registeredNest(x)
|
||||
local entity = tests.createEnemy(x)
|
||||
baseUtils.registerEnemyBaseStructure(global.regionMap,
|
||||
entity,
|
||||
nil)
|
||||
end
|
||||
|
||||
function tests.attackOrigin()
|
||||
|
Reference in New Issue
Block a user