diff --git a/README.md b/README.md index b579f60..33e88eb 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/Upgrade.lua b/Upgrade.lua index 66c4107..6c2c8e1 100644 --- a/Upgrade.lua +++ b/Upgrade.lua @@ -10,8 +10,6 @@ local mathUtils = require("libs/MathUtils") local INTERVAL_LOGIC = constants.INTERVAL_LOGIC local INTERVAL_PROCESS = constants.INTERVAL_PROCESS -local MAX_RALLY_CRIES = constants.MAX_RALLY_CRIES - -- imported functions local roundToNearest = mathUtils.roundToNearest @@ -80,7 +78,6 @@ function upgrade.attempt(natives, regionMap) global.version = constants.VERSION_16 end if (global.version < constants.VERSION_18) then - print(global.version) natives.safeEntities = {} natives.safeEntityName = {} @@ -132,20 +129,18 @@ function upgrade.attempt(natives, regionMap) game.surfaces[1].print("Rampant - Version 0.15.10") global.version = constants.VERSION_22 end - if (global.version < constants.VERSION_22) then - - natives.bases = {} - - natives.baseDistanceMin = 0 - - natives.baseIndex = 1 - - natives.randomGenerator = game.create_random_generator() + if (global.version < constants.VERSION_23) then game.forces.enemy.ai_controllable = false - game.surfaces[1].print("Rampant - Version 0.15.10") - global.version = constants.VERSION_22 + natives.useCustomAI = settings.startup["rampant-useCustomAI"].value + natives.bases = {} + natives.baseDistanceMin = 0 + natives.baseIndex = 1 + natives.randomGenerator = game.create_random_generator() + + game.surfaces[1].print("Rampant - Version 0.15.11") + global.version = constants.VERSION_23 end return starting ~= global.version end diff --git a/control.lua b/control.lua index 6598e00..da964ba 100644 --- a/control.lua +++ b/control.lua @@ -15,6 +15,7 @@ local aiPlanning = require("libs/AIPlanning") local interop = require("libs/Interop") local tests = require("tests") local upgrade = require("Upgrade") +local baseUtils = require("libs/BaseUtils") -- constants @@ -55,8 +56,8 @@ local squadBeginAttack = aiAttack.squadBeginAttack local retreatUnits = aiDefense.retreatUnits local addRemovePlayerEntity = entityUtils.addRemovePlayerEntity -local removeEnemyBase = entityUtils.removeEnemyBase ---local makeImmortalEntity = entityUtils.makeImmortalEntity +local unregisterEnemyBaseStructure = baseUtils.unregisterEnemyBaseStructure +local makeImmortalEntity = entityUtils.makeImmortalEntity local processBases = baseProcessor.processBases @@ -82,10 +83,31 @@ local function onChunkGenerated(event) end end +local function reprocessChunks() + game.surfaces[1].print("Rampant - Reindexing chunks, please wait") + -- clear old regionMap processing Queue + -- prevents queue adding duplicate chunks + -- chunks are by key, so should overwrite old + regionMap.processQueue = {} + regionMap.processPointer = 1 + regionMap.scanPointer = 1 + -- clear pending chunks, will be added when loop runs below + pendingChunks = {} + + -- queue all current chunks that wont be generated during play + local surface = game.surfaces[1] + for chunk in surface.get_chunks() do + onChunkGenerated({ tick = game.tick, + surface = surface, + area = { left_top = { x = chunk.x * 32, + y = chunk.y * 32 }}}) + end +end + local function onModSettingsChange(event) if event and (string.sub(event.setting, 1, 7) ~= "rampant") then - return + return false end natives.safeBuildings = settings.global["rampant-safeBuildings"].value @@ -112,31 +134,21 @@ local function onModSettingsChange(event) natives.attackPlayerThreshold = settings.global["rampant-attackPlayerThreshold"].value natives.aiNocturnalMode = settings.global["rampant-permanentNocturnal"].value natives.aiPointsScaler = settings.global["rampant-aiPointsScaler"].value + + local useCustomAI = natives.useCustomAI + natives.useCustomAI = settings.startup["rampant-useCustomAI"].value + if not useCustomAI and natives.useCustomAI then + reprocessChunks() + return false + end + return true end + + local function onConfigChanged() - - if upgrade.attempt(natives, regionMap) then - onModSettingsChange(nil) - - game.surfaces[1].print("Rampant - Reindexing chunks, please wait") - -- clear old regionMap processing Queue - -- prevents queue adding duplicate chunks - -- chunks are by key, so should overwrite old - regionMap.processQueue = {} - regionMap.processPointer = 1 - regionMap.scanPointer = 1 - -- clear pending chunks, will be added when loop runs below - pendingChunks = {} - - -- queue all current chunks that wont be generated during play - local surface = game.surfaces[1] - for chunk in surface.get_chunks() do - onChunkGenerated({ tick = game.tick, - surface = surface, - area = { left_top = { x = chunk.x * 32, - y = chunk.y * 32 }}}) - end + if upgrade.attempt(natives, regionMap) and onModSettingsChange(nil) then + reprocessChunks() end end @@ -161,7 +173,9 @@ local function onTick(event) processPlayers(players, regionMap, surface, natives, evolutionFactor, tick) - processBases(regionMap, surface, natives, 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 } ) diff --git a/info.json b/info.json index 7ea1640..08890fc 100644 --- a/info.json +++ b/info.json @@ -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", diff --git a/libs/AIAttack.lua b/libs/AIAttack.lua index 842ae94..bc8e45f 100644 --- a/libs/AIAttack.lua +++ b/libs/AIAttack.lua @@ -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,40 +52,20 @@ 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) local squads = natives.squads 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 diff --git a/libs/AIBuilding.lua b/libs/AIBuilding.lua index 190e67f..82e9f9a 100644 --- a/libs/AIBuilding.lua +++ b/libs/AIBuilding.lua @@ -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) diff --git a/libs/AIDefense.lua b/libs/AIDefense.lua index 0ed3623..eccf045 100644 --- a/libs/AIDefense.lua +++ b/libs/AIDefense.lua @@ -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) diff --git a/libs/AIPlanning.lua b/libs/AIPlanning.lua index fc3c9ae..fb2e467 100644 --- a/libs/AIPlanning.lua +++ b/libs/AIPlanning.lua @@ -4,8 +4,8 @@ local aiPlanning = {} local constants = require("Constants") local mathUtils = require("MathUtils") -local nocturnalUtils = require("NocturnalUtils") - +local aiPredicates = require("AIPredicates") + -- constants local AI_STATE_PEACEFUL = constants.AI_STATE_PEACEFUL @@ -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 diff --git a/libs/BaseUtils.lua b/libs/BaseUtils.lua index 3e8fd8f..e2dabbe 100644 --- a/libs/BaseUtils.lua +++ b/libs/BaseUtils.lua @@ -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 diff --git a/libs/ChunkUtils.lua b/libs/ChunkUtils.lua index 27bfc01..a173b54 100644 --- a/libs/ChunkUtils.lua +++ b/libs/ChunkUtils.lua @@ -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,77 +79,83 @@ 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, force="enemy"} 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 + 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 safeBuildings = natives.safeBuildings + local entities = surface.find_entities_filtered(playerChunkQuery) + + local playerObjects = 0 + local safeBuildings = natives.safeBuildings for i=1, #entities do local entity = entities[i] - local entityType = entity.type + local entityType = entity.type if safeBuildings then if natives.safeEntities[entityType] or natives.safeEntityName[entity.name] then entity.destructible = false 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 end - + chunk[PLAYER_BASE_GENERATOR] = playerObjects 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 diff --git a/libs/Constants.lua b/libs/Constants.lua index a16336f..8d3ffbc 100644 --- a/libs/Constants.lua +++ b/libs/Constants.lua @@ -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 diff --git a/libs/EntityUtils.lua b/libs/EntityUtils.lua index b5b8cb2..e5ba4e0 100644 --- a/libs/EntityUtils.lua +++ b/libs/EntityUtils.lua @@ -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 diff --git a/libs/MapProcessor.lua b/libs/MapProcessor.lua index a10e0d2..c94fec1 100644 --- a/libs/MapProcessor.lua +++ b/libs/MapProcessor.lua @@ -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[2].x = chunkPosition.x + CHUNK_SIZE - chunkBox[2].y = chunkPosition.y + CHUNK_SIZE + chunkBox[1] = chunk + + 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 diff --git a/libs/MapUtils.lua b/libs/MapUtils.lua index 661325e..5505a60 100644 --- a/libs/MapUtils.lua +++ b/libs/MapUtils.lua @@ -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 diff --git a/libs/NeighborUtils.lua b/libs/NeighborUtils.lua index afba73e..ef55ac7 100644 --- a/libs/NeighborUtils.lua +++ b/libs/NeighborUtils.lua @@ -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 diff --git a/libs/UnitGroupUtils.lua b/libs/UnitGroupUtils.lua index 4854ba5..2428264 100644 --- a/libs/UnitGroupUtils.lua +++ b/libs/UnitGroupUtils.lua @@ -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 diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index d6a44c2..13e1412 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -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 @@ -57,4 +62,5 @@ rampant-safeBuildings-trainStops=Make train stops safe from biters rampant-attackPlayerThreshold=The score that a chunk must reach for it to contribute to the attack threshold. Increasing reduces player pheromone cloud impact. 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. \ No newline at end of file +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. \ No newline at end of file diff --git a/make.rkt b/make.rkt index e91b0c1..da24291 100644 --- a/make.rkt +++ b/make.rkt @@ -77,8 +77,8 @@ (copyDirectory "prototypes" modFolder))) (define (run) - ;; (copyFiles modFolder) - ;; (copyFiles zipModFolder) - (makeZip modFolder) + (copyFiles modFolder) + (copyFiles zipModFolder) + ;;(makeZip modFolder) ) ) diff --git a/prototypes/buildings/UnitSpawners.lua b/prototypes/buildings/UnitSpawners.lua index bad0f7f..3d9605a 100644 --- a/prototypes/buildings/UnitSpawners.lua +++ b/prototypes/buildings/UnitSpawners.lua @@ -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, diff --git a/settings.lua b/settings.lua index 23a6884..1a2274f 100644 --- a/settings.lua +++ b/settings.lua @@ -172,6 +172,16 @@ data:extend({ default_value = false, order = "g[modifier]-a[damage]", per_user = false + }, + + { + type = "bool-setting", + name = "rampant-useCustomAI", + description = "rampant-useCustomAI", + setting_type = 'startup', + default_value = false, + order = "h[total]-a[ai]", + per_user = false } }) diff --git a/tests.lua b/tests.lua index 24608f0..942a761 100644 --- a/tests.lua +++ b/tests.lua @@ -3,6 +3,7 @@ local tests = {} local constants = require("libs/Constants") local mathUtils = require("libs/MathUtils") local chunkUtils = require("libs/ChunkUtils") +local mapUtils = require("libs/MapUtils") local baseUtils = require("libs/BaseUtils") function tests.pheromoneLevels() @@ -22,7 +23,7 @@ function tests.pheromoneLevels() for i=1,#chunk do str = str .. " " .. tostring(i) .. "/" .. tostring(chunk[i]) end - str = str .. " " .. "p/" .. game.surfaces[1].get_pollution({x=chunk.pX, y=chunk.pY}) + str = str .. " " .. "p/" .. game.surfaces[1].get_pollution(chunk) if (chunk.cX == playerChunkX) and (chunk.cY == playerChunkY) then print("*", chunk.cX, chunk.cY, str) else @@ -73,6 +74,14 @@ function tests.findNearestPlayerEnemy() print("--") end +function tests.getOffsetChunk(x, y) + local playerPosition = game.players[1].position + local chunkX = math.floor(playerPosition.x * 0.03125) + local chunkY = math.floor(playerPosition.y * 0.03125) + local chunk = mapUtils.getChunkByIndex(global.regionMap, chunkX + x, chunkY + y) + print(serpent.dump(chunk)) +end + function tests.aiStats() print(global.natives.points, game.tick, global.natives.state, global.natives.temperament, global.natives.stateTick, global.natives.temperamentTick) end @@ -99,7 +108,14 @@ function tests.createEnemy(x) local playerPosition = game.players[1].position local chunkX = math.floor(playerPosition.x * 0.03125) * 32 local chunkY = math.floor(playerPosition.y * 0.03125) * 32 - game.surfaces[1].create_entity({name=x, position={chunkX, chunkY}}) + return game.surfaces[1].create_entity({name=x, position={chunkX, chunkY}}) +end + +function tests.registeredNest(x) + local entity = tests.createEnemy(x) + baseUtils.registerEnemyBaseStructure(global.regionMap, + entity, + nil) end function tests.attackOrigin()