1
0
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:
Aaron Veden
2017-05-27 21:50:37 -07:00
parent 2b3fa0ca0e
commit afe7cacb0f
21 changed files with 367 additions and 300 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
@@ -247,7 +261,7 @@ end
local function onSurfaceTileChange(event)
local player = game.players[event.player_index]
if (player.surface.index==1) then
if (player.surface.index == 1) then
aiBuilding.fillTunnel(regionMap, player.surface, natives, event.positions)
end
end
@@ -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
}
)

View File

@@ -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",

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -77,8 +77,8 @@
(copyDirectory "prototypes" modFolder)))
(define (run)
;; (copyFiles modFolder)
;; (copyFiles zipModFolder)
(makeZip modFolder)
(copyFiles modFolder)
(copyFiles zipModFolder)
;;(makeZip modFolder)
)
)

View File

@@ -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,

View File

@@ -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
}
})

View File

@@ -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()