1
0
mirror of https://github.com/veden/Rampant.git synced 2025-03-17 20:58:35 +02:00

Merge branch 'bases' into baseMerger

This commit is contained in:
Aaron Veden 2017-05-26 22:56:45 -07:00
commit 2b3fa0ca0e
20 changed files with 780 additions and 143 deletions

View File

@ -132,6 +132,21 @@ 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()
game.forces.enemy.ai_controllable = false
game.surfaces[1].print("Rampant - Version 0.15.10")
global.version = constants.VERSION_22
end
return starting ~= global.version
end

View File

@ -1,10 +1,10 @@
-- imports
local upgrade = require("Upgrade")
local entityUtils = require("libs/EntityUtils")
local mapUtils = require("libs/MapUtils")
local unitGroupUtils = require("libs/UnitGroupUtils")
local chunkProcessor = require("libs/ChunkProcessor")
local baseProcessor = require("libs/BaseProcessor")
local mapProcessor = require("libs/MapProcessor")
local constants = require("libs/Constants")
local pheromoneUtils = require("libs/PheromoneUtils")
@ -14,6 +14,7 @@ local aiBuilding = require("libs/AIBuilding")
local aiPlanning = require("libs/AIPlanning")
local interop = require("libs/Interop")
local tests = require("tests")
local upgrade = require("Upgrade")
-- constants
@ -53,8 +54,11 @@ local squadBeginAttack = aiAttack.squadBeginAttack
local retreatUnits = aiDefense.retreatUnits
local addRemoveEntity = entityUtils.addRemoveEntity
local makeImmortalEntity = entityUtils.makeImmortalEntity
local addRemovePlayerEntity = entityUtils.addRemovePlayerEntity
local removeEnemyBase = entityUtils.removeEnemyBase
--local makeImmortalEntity = entityUtils.makeImmortalEntity
local processBases = baseProcessor.processBases
-- local references to global
@ -128,7 +132,8 @@ local function onConfigChanged()
-- queue all current chunks that wont be generated during play
local surface = game.surfaces[1]
for chunk in surface.get_chunks() do
onChunkGenerated({ surface = surface,
onChunkGenerated({ tick = game.tick,
surface = surface,
area = { left_top = { x = chunk.x * 32,
y = chunk.y * 32 }}})
end
@ -143,7 +148,7 @@ local function onTick(event)
local evolutionFactor = game.forces.enemy.evolution_factor
local players = game.players
processPendingChunks(natives, regionMap, surface, pendingChunks)
processPendingChunks(natives, regionMap, surface, pendingChunks, tick)
scanMap(regionMap, surface, natives, evolutionFactor)
if (tick == regionMap.logicTick) then
@ -155,6 +160,9 @@ local function onTick(event)
regroupSquads(natives, evolutionFactor)
processPlayers(players, regionMap, surface, natives, evolutionFactor, tick)
processBases(regionMap, surface, natives, tick)
squadBeginAttack(natives, players, evolutionFactor)
squadAttack(regionMap, surface, natives)
end
@ -174,7 +182,7 @@ local function onBuild(event)
end
local function onPickUp(event)
addRemoveEntity(regionMap, event.entity, natives, false, false)
addRemovePlayerEntity(regionMap, event.entity, natives, false, false)
end
local function onDeath(event)
@ -183,7 +191,7 @@ local function onDeath(event)
if (surface.index == 1) then
if (entity.force.name == "enemy") then
if (entity.type == "unit") then
local entityPosition = entity.position
local entityPosition = entity.position
local deathChunk = getChunkByPosition(regionMap, entityPosition.x, entityPosition.y)
if deathChunk then
@ -216,7 +224,7 @@ local function onDeath(event)
end
elseif (entity.type == "unit-spawner") or (entity.type == "turret") then
addRemoveEntity(regionMap, entity, natives, false, false)
removeEnemyBase(regionMap, entity)
end
elseif (entity.force.name == "player") then
local creditNatives = false
@ -231,17 +239,17 @@ local function onDeath(event)
if creditNatives and natives.safeBuildings and (natives.safeEntities[entity.type] or natives.safeEntityName[entity.name]) then
makeImmortalEntity(surface, entity)
else
addRemoveEntity(regionMap, entity, natives, false, creditNatives)
addRemovePlayerEntity(regionMap, entity, natives, false, creditNatives)
end
end
end
end
local function onSurfaceTileChange(event)
-- local player = game.players[event.player_index]
-- if (player.surface.index==1) then
-- aiBuilding.fillTunnel(global.regionMap, player.surface, global.natives, event.positions)
-- end
local player = game.players[event.player_index]
if (player.surface.index==1) then
aiBuilding.fillTunnel(regionMap, player.surface, natives, event.positions)
end
end
local function onInit()
@ -277,18 +285,26 @@ script.on_event(defines.events.on_entity_died, onDeath)
script.on_event(defines.events.on_tick, onTick)
script.on_event(defines.events.on_chunk_generated, onChunkGenerated)
remote.add_interface("rampantTests", {
test1 = tests.test1,
test2 = tests.test2,
test3 = tests.test3,
test4 = tests.test4,
test5 = tests.test5,
test6 = tests.test6,
test7 = tests.test7,
test8 = tests.test8,
test9 = tests.test9,
test10 = tests.test10,
test11 = tests.test11
})
remote.add_interface("rampantTests",
{
pheromoneLevels = tests.pheromoneLevels,
activeSquads = tests.activeSquads,
entitiesOnPlayerChunk = tests.entitiesOnPlayerChunk,
findNearestPlayerEnemy = tests.findNearestPlayerEnemy,
aiStats = tests.aiStats,
fillableDirtTest = tests.fillableDirtTest,
tunnelTest = tests.tunnelTest,
createEnemy = tests.createEnemy,
attackOrigin = tests.attackOrigin,
cheatMode = tests.cheatMode,
gaussianRandomTest = tests.gaussianRandomTest,
reveal = tests.reveal,
showMovementGrid = tests.showMovementGrid,
baseStats = tests.baseStats,
baseTiles = tests.baseTiles,
mergeBases = tests.mergeBases,
clearBases = tests.clearBases
}
)
remote.add_interface("rampant", interop)

View File

@ -2,6 +2,7 @@ require("prototypes/enemies/AttackAcidBall")
require("prototypes/enemies/AttackAcidFlame")
require("prototypes/buildings/tunnel")
require("prototypes/buildings/UnitSpawners")
require("prototypes/tile/fillableDirt")

View File

@ -18,7 +18,6 @@ local SQUAD_RAIDING = constants.SQUAD_RAIDING
local SQUAD_GUARDING = constants.SQUAD_GUARDING
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local DEFINES_GROUP_FINISHED = defines.group_state.finished
local DEFINES_GROUP_GATHERING = defines.group_state.gathering
@ -52,7 +51,7 @@ end
local function scoreAttackLocation(position, 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]
local r = surface.get_pollution(position) + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 25) --- neighborChunk[ENEMY_BASE_GENERATOR]
return r - squadMovementPenalty
end
@ -118,9 +117,8 @@ function aiAttack.squadAttack(regionMap, surface, natives)
squad.cycles = 4
end
if not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) > 100) then
squad.frenzy = false
end
local outsideFrenzyRadius = not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) > 100)
squad.frenzy = not outsideFrenzyRadius
if squad.rabid or squad.frenzy then
attackCmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING

View File

@ -15,8 +15,6 @@ local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT
local AI_SQUAD_COST = constants.AI_SQUAD_COST
@ -35,6 +33,8 @@ 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
-- imported functions
local getNeighborChunks = mapUtils.getNeighborChunks
@ -63,11 +63,7 @@ local function attackWaveValidCandidate(chunk, natives, surface, evolutionFactor
local threshold = natives.attackThresholdRange
local delta = threshold * evolutionFactor
if (total > ((threshold - delta) + natives.attackThresholdMin)) then
return true
else
return false
end
return total > ((threshold - delta) + natives.attackThresholdMin)
end
local function scoreUnitGroupLocation(position, squad, neighborChunk, surface)
@ -75,7 +71,7 @@ local function scoreUnitGroupLocation(position, squad, neighborChunk, surface)
end
local function validUnitGroupLocation(x, chunk, neighborChunk)
return neighborChunk[NORTH_SOUTH_PASSABLE] and neighborChunk[EAST_WEST_PASSABLE] and neighborChunk[ENEMY_BASE_GENERATOR] == 0
return neighborChunk[NORTH_SOUTH_PASSABLE] and neighborChunk[EAST_WEST_PASSABLE] and neighborChunk[CHUNK_BASE] ~= nil
end
function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFactor, tick)
@ -95,15 +91,11 @@ 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[ENEMY_BASE_GENERATOR] ~= 0) and (#natives.squads < (AI_MAX_SQUAD_COUNT * evolution_factor)) then
local valid = false
if not surface.peaceful_mode then
if (cost == AI_VENGENCE_SQUAD_COST) then
valid = true
elseif (cost == AI_SQUAD_COST) then
valid = attackWaveValidCandidate(chunk, natives, surface, evolution_factor)
end
end
if (natives.points > cost) and (chunk[CHUNK_BASE] ~= nil) 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,
@ -120,9 +112,7 @@ function aiBuilding.formSquads(regionMap, surface, natives, chunk, evolution_fac
local squad = createSquad(squadPosition, surface, natives)
if (math.random() < 0.03) then
squad.rabid = true
end
squad.rabid = math.random() < 0.03
local scaledWaveSize = attackWaveScaling(evolution_factor, natives)
local foundUnits = surface.set_multi_command({ command = { type = DEFINES_COMMAND_GROUP,

View File

@ -15,8 +15,6 @@ local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
local SQUAD_RETREATING = constants.SQUAD_RETREATING
@ -27,6 +25,8 @@ local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local NEST_BASE = constants.NEST_BASE
-- imported functions
local getNeighborChunksWithDirection = mapUtils.getNeighborChunksWithDirection
@ -45,12 +45,12 @@ end
local function scoreRetreatLocation(position, 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(position) + (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[ENEMY_BASE_GENERATOR] == 0) then
if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (#chunk[NEST_BASE] == 0) then
local performRetreat = false
local enemiesToSquad

22
libs/AIPredicates.lua Normal file
View File

@ -0,0 +1,22 @@
local aiPredicates = {}
-- imports
local constants = require("Constants")
local nocturnalUtils = require("NocturnalUtils")
-- constants
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
-- imported functions
local canAttackNocturnal = nocturnalUtils.canAttack
-- module code
function aiPredicates.canAttack(natives, surface)
return (natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)
end
return aiPredicates

39
libs/BaseProcessor.lua Normal file
View File

@ -0,0 +1,39 @@
local baseProcessor = {}
-- imports
local baseUtils = require("BaseUtils")
local constants = require("Constants")
-- constants
local BASE_QUEUE_SIZE = constants.BASE_QUEUE_SIZE
-- imported functions
local mMin = math.min
local buildOrder = baseUtils.buildOrder
-- module code
function baseProcessor.processBases(regionMap, surface, natives, tick)
local baseIndex = natives.baseIndex
local bases = natives.bases
local endIndex = mMin(baseIndex+BASE_QUEUE_SIZE, #bases)
for index = baseIndex, endIndex do
local base = bases[index]
buildOrder(regionMap, natives, base, surface, tick)
end
if (endIndex == #bases) then
natives.baseIndex = 1
else
natives.baseIndex = endIndex + 1
end
end
return baseProcessor

135
libs/BaseUtils.lua Normal file
View File

@ -0,0 +1,135 @@
local baseUtils = {}
-- imports
local mapUtils = require("MapUtils")
local constants = require("Constants")
local mathUtils = require("MathUtils")
local entityUtils = require("EntityUtils")
-- constants
local BASE_DISTANCE_THRESHOLD = constants.BASE_DISTANCE_THRESHOLD
local BASE_ALIGNMENT_NEUTRAL = constants.BASE_ALIGNMENT_NEUTRAL
local AI_NEST_COST = constants.AI_NEST_COST
local AI_WORM_COST = constants.AI_WORM_COST
local CHUNK_SIZE = constants.CHUNK_SIZE
local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER
-- imported functions
local euclideanDistancePoints = mapUtils.euclideanDistancePoints
local gaussianRandomRange = mathUtils.gaussianRandomRange
local addEnemyBase = entityUtils.addEnemyBase
-- module code
function baseUtils.annexNest(natives, position)
local bases = natives.bases
local annex = nil
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
end
end
return annex
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)
if position then
local biterSpawner = {name="biter-spawner", position=position}
base.hive = surface.create_entity(biterSpawner)
addEnemyBase(regionMap, base.hive, base)
valid = true
end
return valid
end
function baseUtils.buildOutpost(natives, base, surface, tick, position)
end
function baseUtils.buildTendril(natives, base, surface, tick, startPosition, endPosition)
end
function baseUtils.buildOrder(regionMap, natives, base, surface, tick)
if not base.hive or (base.upgradePoints < 10) then
return
end
local generator = natives.randomGenerator
generator.re_seed(base.pattern)
for level=0,base.level do
local slices = (level * 3)
local slice = (2 * math.pi) / slices
local pos = 0
local thing
local cost
local radiusAdjustment
if (generator() < 0.3) then
thing = "small-worm-turret"
cost = AI_WORM_COST
radiusAdjustment = -4
else
thing = "biter-spawner"
cost = AI_NEST_COST
radiusAdjustment = 0
end
for _ = 1, slices do
if (base.upgradePoints < 10) then
return
end
local radius = 10 * level
local distortion = gaussianRandomRange(radius, 10, radius - 7.5 + radiusAdjustment, radius + 7.5 + radiusAdjustment, generator)
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
addEnemyBase(regionMap, surface.create_entity(biterSpawner), base)
base.upgradePoints = base.upgradePoints - cost
end
pos = pos + slice
end
end
end
function baseUtils.createBase(regionMap, natives, position, surface, tick)
local bases = natives.bases
local base = {
x = position.x,
y = position.y,
created = tick,
alignment = { BASE_ALIGNMENT_NEUTRAL },
hive = nil,
nests = {},
worms = {},
eggs = {},
upgradePoints = 0,
growth = tick,
pattern = math.random(MAGIC_MAXIMUM_BASE_NUMBER),
level = 3
}
if not baseUtils.buildHive(regionMap, base, surface) then
return nil
end
bases[#bases+1] = base
return base
end
return baseUtils

View File

@ -12,7 +12,7 @@ local scoreChunk = chunkUtils.scoreChunk
-- module code
function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendingStack)
function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendingStack, tick)
local processQueue = regionMap.processQueue
for _=#pendingStack, 1, -1 do
@ -29,7 +29,7 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin
regionMap[chunkX][chunk.cY] = chunk
checkChunkPassability(chunk, surface)
scoreChunk(natives, chunk, surface)
scoreChunk(regionMap, chunk, surface, natives, tick)
processQueue[#processQueue+1] = chunk
end
end

View File

@ -4,15 +4,18 @@ local chunkUtils = {}
local constants = require("Constants")
local baseUtils = require("BaseUtils")
-- constants
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
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 PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local NORTH_SOUTH = constants.NORTH_SOUTH
local EAST_WEST = constants.EAST_WEST
@ -20,16 +23,19 @@ local EAST_WEST = constants.EAST_WEST
local EAST_WEST_PASSABLE = constants.EAST_WEST_PASSABLE
local NORTH_SOUTH_PASSABLE = constants.NORTH_SOUTH_PASSABLE
local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
local CHUNK_TICK = constants.CHUNK_TICK
local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
local RALLY_TRIGGERED = constants.RALLY_TRIGGERED
-- imported functions
local annexNest = baseUtils.annexNest
local createBase = baseUtils.createBase
-- module code
function chunkUtils.checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, surface)
local function checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, surface)
local get_tile = surface.get_tile
for x=iteratingCoordinate, iteratingCoordinate + 31 do
@ -53,13 +59,13 @@ function chunkUtils.checkChunkPassability(chunk, surface)
local passableNorthSouth = false
local passableEastWest = false
for xi=x, x + 31 do
if (not chunkUtils.checkForDeadendTiles(xi, y, NORTH_SOUTH, surface)) then
if (not checkForDeadendTiles(xi, y, NORTH_SOUTH, surface)) then
passableNorthSouth = true
break
end
end
for yi=y, y + 31 do
if (not chunkUtils.checkForDeadendTiles(yi, x, EAST_WEST, surface)) then
if (not checkForDeadendTiles(yi, x, EAST_WEST, surface)) then
passableEastWest = true
break
end
@ -69,28 +75,36 @@ function chunkUtils.checkChunkPassability(chunk, surface)
chunk[NORTH_SOUTH_PASSABLE] = passableNorthSouth
end
function chunkUtils.scoreChunk(natives, chunk, surface)
function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick)
local x = chunk.pX
local y = chunk.pY
local chunkPosition = {x=x, y=y}
local areaBoundingBox = {
{x, y},
chunkPosition,
{x + 32, y + 32}
}
local enemyChunkQuery = {area=areaBoundingBox,
type="unit-spawner",
force="enemy"}
local enemyWormChunkQuery = {area=areaBoundingBox,
type="turret",
force="enemy"}
local playerChunkQuery = {area=areaBoundingBox,
force="player"}
local entities = surface.count_entities_filtered(enemyChunkQuery)
local worms = surface.count_entities_filtered(enemyWormChunkQuery)
local playerObjects = 0
local enemies = surface.find_entities_filtered(enemyChunkQuery)
local nestsRemoved = 0
local wormsRemoved = 0
local bitersRemoved = 0
chunk[ENEMY_BASE_GENERATOR] = (entities * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT) + worms
for i=1, #enemies do
local entityType = enemies[i].type
if (entityType == "unit-spawner") then
nestsRemoved = nestsRemoved + 3
elseif (entityType == "turret") then
wormsRemoved = wormsRemoved + 2
elseif (entityType == "unit") then
bitersRemoved = bitersRemoved + 1
end
end
entities = surface.find_entities_filtered(playerChunkQuery)
@ -105,10 +119,25 @@ function chunkUtils.scoreChunk(natives, chunk, surface)
end
end
local entityScore = BUILDING_PHEROMONES[entityType]
if (entityScore ~= nil) then
playerObjects = playerObjects + entityScore
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]
if (entityScore ~= nil) then
playerObjects = playerObjects + entityScore
end
end
chunk[PLAYER_BASE_GENERATOR] = playerObjects
@ -124,13 +153,14 @@ function chunkUtils.createChunk(topX, topY)
chunk[MOVEMENT_PHEROMONE] = 0
chunk[BASE_PHEROMONE] = 0
chunk[PLAYER_PHEROMONE] = 0
chunk[ENEMY_BASE_GENERATOR] = 0
chunk[PLAYER_BASE_GENERATOR] = 0
chunk[NORTH_SOUTH_PASSABLE] = false
chunk[EAST_WEST_PASSABLE] = false
chunk[CHUNK_TICK] = 0
chunk[RETREAT_TRIGGERED] = 0
chunk[RALLY_TRIGGERED] = 0
chunk[NEST_BASE] = {}
chunk[WORM_BASE] = {}
return chunk
end

View File

@ -21,10 +21,12 @@ constants.VERSION_22 = 22
-- misc
constants.MAGIC_MAXIMUM_NUMBER = 1e99 -- used in loops trying to find the lowest/highest score
constants.MAGIC_MAXIMUM_BASE_NUMBER = 100000000
constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL = 10000
constants.PROCESS_QUEUE_SIZE = 500
constants.SCAN_QUEUE_SIZE = 6
constants.BASE_QUEUE_SIZE = 10
constants.PROCESS_PLAYER_BOUND = 4
constants.TICKS_A_SECOND = 60
@ -33,11 +35,21 @@ constants.TICKS_A_MINUTE = constants.TICKS_A_SECOND * 60
constants.INTERVAL_PROCESS = 19
constants.INTERVAL_LOGIC = 38
-- chunk properties
constants.CHUNK_SIZE = 32
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
-- ai
constants.AI_POINT_GENERATOR_AMOUNT = 6
constants.AI_SCOUT_COST = 45
constants.AI_SQUAD_COST = 175
constants.AI_NEST_COST = 10
constants.AI_WORM_COST = 2
constants.AI_VENGENCE_SQUAD_COST = 45
constants.AI_SETTLER_COST = 75
constants.AI_BASE_BUILDING_COST = 500
@ -58,19 +70,23 @@ constants.AI_MAX_STATE_DURATION = 4
constants.AI_MIN_TEMPERAMENT_DURATION = 5
constants.AI_MAX_TEMPERAMENT_DURATION = 15
-- ai base
constants.BASE_DISTANCE_THRESHOLD = 15 * constants.CHUNK_SIZE
constants.BASE_ALIGNMENT_NEUTRAL = 1
constants.BASE_ALIGNMENT_FIRE = 2
constants.BASE_ALIGNMENT_BURROW = 3
constants.BASE_ALIGNMENT_SUICIDE = 4
constants.BASE_ALIGNMENT_INFEST = 5
-- ai retreat
constants.NO_RETREAT_BASE_PERCENT = 0.10
constants.NO_RETREAT_EVOLUTION_BONUS_MAX = 0.25
constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX = 0.40
-- chunk properties
constants.CHUNK_SIZE = 32
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
-- pheromone amounts
@ -92,8 +108,8 @@ constants.PLAYER_PHEROMONE_PERSISTANCE = 0.98
constants.MOVEMENT_PHEROMONE = 1
constants.BASE_PHEROMONE = 2
constants.PLAYER_PHEROMONE = 3
constants.RESOURCE_PHEROMONE = 4
constants.ENEMY_BASE_GENERATOR = 4
constants.PLAYER_BASE_GENERATOR = 5
constants.NORTH_SOUTH_PASSABLE = 6
@ -102,6 +118,8 @@ constants.EAST_WEST_PASSABLE = 7
constants.CHUNK_TICK = 8
constants.RETREAT_TRIGGERED = 9
constants.RALLY_TRIGGERED = 10
constants.NEST_BASE = 11
constants.WORM_BASE = 12
-- Squad status

View File

@ -9,10 +9,10 @@ local constants = require("Constants")
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
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
@ -82,39 +82,104 @@ local function getEntityOverlapChunks(regionMap, entity)
return leftTopChunk, rightTopChunk, leftBottomChunk, rightBottomChunk
end
function entityUtils.addRemoveEntity(regionMap, entity, natives, addObject, creditNatives)
function entityUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject, creditNatives)
local leftTop, rightTop, leftBottom, rightBottom
local entityValue
local pheromoneType
if (BUILDING_PHEROMONES[entity.type] ~= nil) and (entity.force.name == "player") then
entityValue = BUILDING_PHEROMONES[entity.type]
pheromoneType = PLAYER_BASE_GENERATOR
elseif (entity.type == "unit-spawner") and (entity.force.name == "enemy") then
entityValue = ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
pheromoneType = ENEMY_BASE_GENERATOR
elseif (entity.type == "turret") and (entity.force.name == "enemy") then
entityValue = 1
pheromoneType = ENEMY_BASE_GENERATOR
end
if (entityValue ~= nil) then
leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
if not addObject then
if creditNatives and (pheromoneType ~= ENEMY_BASE_GENERATOR) then
natives.points = natives.points + entityValue
end
entityValue = -entityValue
if creditNatives then
natives.points = natives.points + entityValue
end
entityValue = -entityValue
end
if (leftTop ~= nil) then
leftTop[PLAYER_BASE_GENERATOR] = leftTop[PLAYER_BASE_GENERATOR] + entityValue
end
if (rightTop ~= nil) then
rightTop[PLAYER_BASE_GENERATOR] = rightTop[PLAYER_BASE_GENERATOR] + entityValue
end
if (leftBottom ~= nil) then
leftBottom[PLAYER_BASE_GENERATOR] = leftBottom[PLAYER_BASE_GENERATOR] + entityValue
end
if (rightBottom ~= nil) 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
leftTop[pheromoneType] = leftTop[pheromoneType] + entityValue
addBaseToChunk(leftTop, entity, base)
end
if (rightTop ~= nil) then
rightTop[pheromoneType] = rightTop[pheromoneType] + entityValue
addBaseToChunk(rightTop, entity, base)
end
if (leftBottom ~= nil) then
leftBottom[pheromoneType] = leftBottom[pheromoneType] + entityValue
addBaseToChunk(leftBottom, entity, base)
end
if (rightBottom ~= nil) then
rightBottom[pheromoneType] = rightBottom[pheromoneType] + entityValue
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

View File

@ -4,14 +4,14 @@ local mapProcessor = {}
local pheromoneUtils = require("PheromoneUtils")
local aiBuilding = require("AIBuilding")
local aiPredicates = require("AIPredicates")
local constants = require("Constants")
local mapUtils = require("MapUtils")
local nocturnalUtils = require("NocturnalUtils")
local playerUtils = require("PlayerUtils")
-- constants
local PROCESS_QUEUE_SIZE = constants.PROCESS_QUEUE_SIZE
local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
@ -19,12 +19,12 @@ local SCAN_QUEUE_SIZE = constants.SCAN_QUEUE_SIZE
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
local CHUNK_SIZE = constants.CHUNK_SIZE
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
local CHUNK_TICK = constants.CHUNK_TICK
local NEST_BASE = constants.NEST_BASE
local AI_MAX_POINTS = constants.AI_MAX_POINTS
local AI_SQUAD_COST = constants.AI_SQUAD_COST
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
@ -45,12 +45,12 @@ local getChunkByPosition = mapUtils.getChunkByPosition
local playerScent = pheromoneUtils.playerScent
local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
local canAttackNocturnal = nocturnalUtils.canAttack
local canAttack = aiPredicates.canAttack
local mMin = math.min
local validPlayer = playerUtils.validPlayer
-- module code
local function nonRepeatingRandom(players)
@ -83,7 +83,7 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
regionMap.processRoll = roll
end
local squads = ((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)) and (0.11 <= roll) and (roll <= 0.35)
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.35)
local processQueue = regionMap.processQueue
local endIndex = mMin(index + PROCESS_QUEUE_SIZE, #processQueue)
@ -120,11 +120,11 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
local vengenceThreshold = -(evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)
local roll = math.random()
local squads = ((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)) and (0.11 <= roll) and (roll <= 0.20)
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.20)
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
if validPlayer(player) then
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
@ -135,13 +135,13 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
end
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
if validPlayer(player) then
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
if playerChunk then
local vengence = ((playerChunk[ENEMY_BASE_GENERATOR] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < vengenceThreshold)) and
(natives.state == AI_STATE_AGGRESSIVE or canAttackNocturnal(natives, surface))
local vengence = canAttack(natives, surface) and ((#playerChunk[NEST_BASE] ~= 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)
@ -172,7 +172,7 @@ 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,

View File

@ -163,6 +163,12 @@ function mapUtils.euclideanDistanceNamed(p1, p2)
return ((xs * xs) + (ys * ys)) ^ 0.5
end
function mapUtils.euclideanDistancePoints(x1, y1, x2, y2)
local xs = x1 - x2
local ys = y1 - y2
return ((xs * xs) + (ys * ys)) ^ 0.5
end
function mapUtils.euclideanDistanceArray(p1, p2)
local xs = p1[1] - p2[1]
local ys = p1[2] - p2[2]

View File

@ -30,27 +30,32 @@ end
--[[
Used for gaussian random numbers
--]]
local function marsagliaPolarMethod()
local function marsagliaPolarMethod(rg)
local iid1
local iid2
local q
repeat
iid1 = 2 * math.random() + -1
iid2 = 2 * math.random() + -1
if rg then
iid1 = 2 * rg() + -1
iid2 = 2 * rg() + -1
else
iid1 = 2 * math.random() + -1
iid2 = 2 * math.random() + -1
end
q = (iid1 * iid1) + (iid2 * iid2)
until (q ~= 0) and (q < 1)
local s = mSqrt((-2 * mLog10(q)) / q)
return iid1 * s
end
function mathUtils.gaussianRandom(mean, std_dev)
return mean + (marsagliaPolarMethod() * std_dev)
function mathUtils.gaussianRandom(mean, std_dev, rg)
return mean + (marsagliaPolarMethod(rg) * std_dev)
end
function mathUtils.gaussianRandomRange(mean, std_dev, min, max)
function mathUtils.gaussianRandomRange(mean, std_dev, min, max, rg)
local q
repeat
q = mathUtils.gaussianRandom(mean, std_dev)
q = mathUtils.gaussianRandom(mean, std_dev, rg)
until (q >= min) and (q <= max)
return q
end

View File

@ -10,6 +10,10 @@ local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
-- module code
function playerUtils.validPlayer(player)
return (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1)
end
function playerUtils.playersWithinProximityToPosition(players, position, distance)
for _,player in pairs(players) do
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then

View File

@ -0,0 +1,190 @@
function spawner_idle_animation(variation, tint)
return
{
layers =
{
{
filename = "__base__/graphics/entity/spawner/spawner-idle.png",
line_length = 8,
width = 243,
height = 181,
frame_count = 8,
animation_speed = 0.18,
direction_count = 1,
run_mode = "forward-then-backward",
shift = {0.140625 - 0.65, -0.234375},
y = variation * 181
},
{
filename = "__base__/graphics/entity/spawner/spawner-idle-mask.png",
flags = { "mask" },
width = 166,
height = 148,
frame_count = 8,
animation_speed = 0.18,
run_mode = "forward-then-backward",
shift = {-0.34375 - 0.65, -0.375},
line_length = 8,
tint = tint,
y = variation * 148
}
}
}
end
function spawner_die_animation(variation, tint)
return
{
layers =
{
{
width = 255,
height = 184,
frame_count = 20,
direction_count = 1,
shift = {-0.015625 - 0.65, -0.28125},
stripes =
{
{
filename = "__base__/graphics/entity/spawner/spawner-die-01.png",
width_in_frames = 7,
height_in_frames = 4,
y = variation * 184
},
{
filename = "__base__/graphics/entity/spawner/spawner-die-02.png",
width_in_frames = 7,
height_in_frames = 4,
y = variation * 184
},
{
filename = "__base__/graphics/entity/spawner/spawner-die-03.png",
width_in_frames = 6,
height_in_frames = 4,
y = variation * 184
}
}
},
{
flags = { "mask" },
width = 166,
height = 148,
frame_count = 20,
direction_count = 1,
shift = {-0.34375 - 0.65, -0.375},
tint = tint,
stripes =
{
{
filename = "__base__/graphics/entity/spawner/spawner-die-mask-01.png",
width_in_frames = 10,
height_in_frames = 4,
y = variation * 148
},
{
filename = "__base__/graphics/entity/spawner/spawner-die-mask-02.png",
width_in_frames = 10,
height_in_frames = 4,
y = variation * 148
}
}
}
}
}
end
local biter_spawner_powered_tint = {r=1.0, g=1.0, b=1.0, a=1.0}
data:extend({
{
type = "unit-spawner",
name = "biter-spawner-powered",
icon = "__base__/graphics/icons/biter-spawner.png",
flags = {"placeable-player", "placeable-enemy", "not-repairable"},
max_health = 350,
order="b-b-g",
subgroup="enemies",
resistances =
{
{
type = "physical",
decrease = 2,
percent = 15
},
{
type = "explosion",
decrease = 5,
percent = 15,
},
{
type = "fire",
decrease = 3,
percent = 60,
}
},
working_sound = {
sound =
{
{
filename = "__base__/sound/creatures/spawner.ogg",
volume = 1.0
}
},
apparent_volume = 2
},
dying_sound =
{
{
filename = "__base__/sound/creatures/spawner-death-1.ogg",
volume = 1.0
},
{
filename = "__base__/sound/creatures/spawner-death-2.ogg",
volume = 1.0
}
},
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}},
-- in ticks per 1 pu
pollution_absorbtion_absolute = 20,
pollution_absorbtion_proportional = 0.01,
corpse = "biter-spawner-corpse",
dying_explosion = "blood-explosion-huge",
max_count_of_owned_units = 7,
max_friends_around_to_spawn = 5,
animations =
{
spawner_idle_animation(0, biter_spawner_powered_tint),
spawner_idle_animation(1, biter_spawner_powered_tint),
spawner_idle_animation(2, biter_spawner_powered_tint),
spawner_idle_animation(3, biter_spawner_powered_tint)
},
result_units = (function()
local res = {}
res[1] = {"small-biter", {{0.0, 0.3}, {0.6, 0.0}}}
if not data.is_demo then
-- from evolution_factor 0.3 the weight for medium-biter is linearly rising from 0 to 0.3
-- this means for example that when the evolution_factor is 0.45 the probability of spawning
-- a small biter is 66% while probability for medium biter is 33%.
res[2] = {"medium-biter", {{0.2, 0.0}, {0.6, 0.3}, {0.7, 0.1}}}
-- for evolution factor of 1 the spawning probabilities are: small-biter 0%, medium-biter 1/8, big-biter 4/8, behemoth biter 3/8
res[3] = {"big-biter", {{0.5, 0.0}, {1.0, 0.4}}}
res[4] = {"behemoth-biter", {{0.9, 0.0}, {1.0, 0.3}}}
end
return res
end)(),
-- With zero evolution the spawn rate is 6 seconds, with max evolution it is 2.5 seconds
spawning_cooldown = {360, 150},
spawning_radius = 10,
spawning_spacing = 3,
max_spawn_shift = 0,
max_richness_for_spawn_shift = 100,
call_for_help_radius = 50
}
})

View File

@ -173,4 +173,5 @@ data:extend({
order = "g[modifier]-a[damage]",
per_user = false
}
})

130
tests.lua
View File

@ -2,8 +2,10 @@ local tests = {}
local constants = require("libs/Constants")
local mathUtils = require("libs/MathUtils")
local chunkUtils = require("libs/ChunkUtils")
local baseUtils = require("libs/BaseUtils")
function tests.test1()
function tests.pheromoneLevels()
local player = game.player.character
local playerChunkX = math.floor(player.position.x / 32)
local playerChunkY = math.floor(player.position.y / 32)
@ -34,18 +36,18 @@ function tests.test1()
end
end
function tests.test2()
function tests.activeSquads()
print("--")
for i=1, #global.natives.squads do
local squad = global.natives.squads[i]
if squad.group.valid then
print(math.floor(squad.group.position.x * 0.03125), math.floor(squad.group.position.y * 0.03125), squad.status, squad.group.state, #squad.group.members)
print(math.floor(squad.group.position.x * 0.03125), math.floor(squad.group.position.y * 0.03125), squad.status, squad.group.state)
print(serpent.dump(squad))
end
end
end
function tests.test3()
function tests.entitiesOnPlayerChunk()
local playerPosition = game.players[1].position
local chunkX = math.floor(playerPosition.x * 0.03125) * 32
local chunkY = math.floor(playerPosition.y * 0.03125) * 32
@ -58,7 +60,7 @@ function tests.test3()
print("--")
end
function tests.test4()
function tests.findNearestPlayerEnemy()
local playerPosition = game.players[1].position
local chunkX = math.floor(playerPosition.x * 0.03125) * 32
local chunkY = math.floor(playerPosition.y * 0.03125) * 32
@ -71,11 +73,11 @@ function tests.test4()
print("--")
end
function tests.test5()
function tests.aiStats()
print(global.natives.points, game.tick, global.natives.state, global.natives.temperament, global.natives.stateTick, global.natives.temperamentTick)
end
function tests.test6()
function tests.fillableDirtTest()
local playerPosition = game.players[1].position
local chunkX = math.floor(playerPosition.x * 0.03125) * 32
local chunkY = math.floor(playerPosition.y * 0.03125) * 32
@ -86,22 +88,21 @@ function tests.test6()
false)
end
function tests.test7()
function tests.tunnelTest()
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="tunnel-entrance", position={chunkX, chunkY}})
end
function tests.test8(x)
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
local entity = game.surfaces[1].create_entity({name=x, force="enemy", position={chunkX, chunkY}})
--entity.insert({ name = "flame-thrower-ammo", count = 1})
game.surfaces[1].create_entity({name=x, position={chunkX, chunkY}})
end
function tests.test9()
function tests.attackOrigin()
local enemy = game.surfaces[1].find_nearest_enemy({position={0,0},
max_distance = 1000})
if (enemy ~= nil) and enemy.valid then
@ -112,12 +113,12 @@ function tests.test9()
end
end
function tests.test10()
function tests.cheatMode()
game.players[1].cheat_mode = true
game.forces.player.research_all_technologies()
end
function tests.test11()
function tests.gaussianRandomTest()
local result = {}
for x=0,100,1 do
result[x] = 0
@ -131,4 +132,105 @@ function tests.test11()
end
end
function tests.reveal (size)
game.player.force.chart(game.player.surface,
{{x=-size, y=-size}, {x=size, y=size}})
end
function tests.baseStats()
local natives = global.natives
print ("cX", "cY", "pX", "pY", "created", "align", "str", "upgradePoints", "#nest", "#worms", "#eggs", "hive")
for i=1, #natives.bases do
local base = natives.bases[i]
local nestCount = 0
local wormCount = 0
local eggCount = 0
for _,_ in pairs(base.nests) do
nestCount = nestCount + 1
end
for _,_ in pairs(base.worms) do
wormCount = wormCount + 1
end
for _,_ in pairs(base.eggs) do
eggCount = eggCount + 1
end
print(base.x, base.y, base.x * 32, base.y * 32, base.created, base.alignment, base.strength, base.upgradePoints, nestCount, wormCount, eggCount, base.hive)
end
end
function tests.baseTiles()
local natives = global.natives
for i=1, #natives.bases do
local base = natives.bases[i]
-- local color = "concrete"
-- if (i % 3 == 0) then
-- color = "deepwater"
-- elseif (i % 2 == 0) then
-- color = "water"
-- end
-- for x=1,#base.chunks do
-- local chunk = base.chunks[x]
-- chunkUtils.colorChunk(chunk.pX, chunk.pY, color, game.surfaces[1])
-- end
chunkUtils.colorChunk(base.x, base.y, "deepwater-green", game.surfaces[1])
end
end
function tests.clearBases()
local surface = game.surfaces[1]
for x=#global.natives.bases,1,-1 do
local base = global.natives.bases[x]
for c=1,#base.chunks do
local chunk = base.chunks[c]
chunkUtils.clearChunkNests(chunk, surface)
end
base.chunks = {}
if (surface.can_place_entity({name="biter-spawner-powered", position={base.cX * 32, base.cY * 32}})) then
surface.create_entity({name="biter-spawner-powered", position={base.cX * 32, base.cY * 32}})
local slice = math.pi / 12
local pos = 0
for i=1,24 do
if (math.random() < 0.8) then
local distance = mathUtils.roundToNearest(mathUtils.gaussianRandomRange(45, 5, 37, 60), 1)
if (surface.can_place_entity({name="biter-spawner", position={base.cX * 32 + (distance*math.sin(pos)), base.cY * 32 + (distance*math.cos(pos))}})) then
if (math.random() < 0.3) then
surface.create_entity({name="small-worm-turret", position={base.cX * 32 + (distance*math.sin(pos)), base.cY * 32 + (distance*math.cos(pos))}})
else
surface.create_entity({name="biter-spawner", position={base.cX * 32 + (distance*math.sin(pos)), base.cY * 32 + (distance*math.cos(pos))}})
end
end
end
pos = pos + slice
end
else
table.remove(global.natives.bases, x)
end
end
end
function tests.mergeBases()
local natives = global.natives
baseUtils.mergeBases(natives)
end
function tests.showMovementGrid()
local chunks = global.regionMap.processQueue
for i=1,#chunks do
local chunk = chunks[i]
local color = "deepwater-green"
if (chunk[constants.NORTH_SOUTH_PASSABLE] and chunk[constants.EAST_WEST_PASSABLE]) then
color = "water"
elseif chunk[constants.NORTH_SOUTH_PASSABLE] then
color = "deepwater"
elseif chunk[constants.EAST_WEST_PASSABLE] then
color = "water-green"
end
chunkUtils.colorChunk(chunk.pX, chunk.pY, color, game.surfaces[1])
end
end
return tests