From ab1fe32be5334ab3f04d60ae407a2911a5f9e0f2 Mon Sep 17 00:00:00 2001 From: Aaron Veden Date: Mon, 12 Jun 2017 20:16:43 -0700 Subject: [PATCH] refactoring files for movement code and tendril stuff --- README.md | 3 + control.lua | 20 ++-- data.lua | 1 + libs/{AIBuilding.lua => AIAttackWave.lua} | 10 +- libs/BaseProcessor.lua | 6 +- libs/BaseUtils.lua | 85 +------------ libs/BuildUtils.lua | 139 ++++++++++++++++++++++ libs/ChunkUtils.lua | 8 +- libs/Constants.lua | 3 +- libs/MapProcessor.lua | 4 +- libs/MapUtils.lua | 34 ------ libs/MathUtils.lua | 33 +++++ libs/MovementUtils.lua | 72 +++++++++++ libs/{AIAttack.lua => SquadAttack.lua} | 42 +++---- libs/{AIDefense.lua => SquadDefense.lua} | 1 + libs/TendrilUtils.lua | 128 ++++++++++++-------- libs/UnitGroupUtils.lua | 39 ------ locale/en/locale.cfg | 4 + make.rkt | 4 +- prototypes/enemies/UnitTendril.lua | 42 +++++++ 20 files changed, 424 insertions(+), 254 deletions(-) rename libs/{AIBuilding.lua => AIAttackWave.lua} (90%) create mode 100644 libs/BuildUtils.lua create mode 100644 libs/MovementUtils.lua rename libs/{AIAttack.lua => SquadAttack.lua} (81%) rename libs/{AIDefense.lua => SquadDefense.lua} (97%) create mode 100644 prototypes/enemies/UnitTendril.lua diff --git a/README.md b/README.md index 61046b8..f1f0058 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ Configure Options not in game menu: # Version History +0.15.16 - + + 0.15.15 - - Fixed: Desync when reindexing chunks (https://forums.factorio.com/viewtopic.php?f=94&t=31445&start=180#p287941) - Fixed: Switched from "and" to "or" for player pheromone and base pheromone to form squads diff --git a/control.lua b/control.lua index 53a7fc8..f759f74 100644 --- a/control.lua +++ b/control.lua @@ -8,9 +8,9 @@ local baseProcessor = require("libs/BaseProcessor") local mapProcessor = require("libs/MapProcessor") local constants = require("libs/Constants") local pheromoneUtils = require("libs/PheromoneUtils") -local aiDefense = require("libs/AIDefense") -local aiAttack = require("libs/AIAttack") -local aiBuilding = require("libs/AIBuilding") +local squadDefense = require("libs/SquadDefense") +local squadAttack = require("libs/SquadAttack") +local aiAttackWave = require("libs/AIAttackWave") local aiPlanning = require("libs/AIPlanning") local interop = require("libs/Interop") local tests = require("tests") @@ -44,7 +44,7 @@ local scanMap = mapProcessor.scanMap local planning = aiPlanning.planning -local rallyUnits = aiBuilding.rallyUnits +local rallyUnits = aiAttackWave.rallyUnits local deathScent = pheromoneUtils.deathScent local victoryScent = pheromoneUtils.victoryScent @@ -53,10 +53,10 @@ local cleanSquads = unitGroupUtils.cleanSquads local regroupSquads = unitGroupUtils.regroupSquads local convertUnitGroupToSquad = unitGroupUtils.convertUnitGroupToSquad -local squadAttack = aiAttack.squadAttack -local squadBeginAttack = aiAttack.squadBeginAttack +local squadsAttack = squadAttack.squadsAttack +local squadsBeginAttack = squadAttack.squadsBeginAttack -local retreatUnits = aiDefense.retreatUnits +local retreatUnits = squadDefense.retreatUnits local addRemovePlayerEntity = entityUtils.addRemovePlayerEntity local unregisterEnemyBaseStructure = baseRegisterUtils.unregisterEnemyBaseStructure @@ -209,8 +209,8 @@ local function onTick(event) processBases(regionMap, surface, natives, tick) end - squadBeginAttack(natives, players) - squadAttack(regionMap, surface, natives) + squadsBeginAttack(natives, players) + squadsAttack(regionMap, surface, natives) end processMap(regionMap, surface, natives, tick) @@ -316,7 +316,7 @@ end local function onSurfaceTileChange(event) -- local player = game.players[event.player_index] -- if (player.surface.index == 1) then - -- aiBuilding.fillTunnel(regionMap, player.surface, natives, event.positions) + -- aiAttackWave.fillTunnel(regionMap, player.surface, natives, event.positions) -- end end diff --git a/data.lua b/data.lua index c5839e6..55a2476 100644 --- a/data.lua +++ b/data.lua @@ -8,3 +8,4 @@ require("prototypes/tile/fillableDirt") require("prototypes/enemies/UnitSuicideBiters") require("prototypes/enemies/UnitFireSpitters") +require("prototypes/enemies/UnitTendril") diff --git a/libs/AIBuilding.lua b/libs/AIAttackWave.lua similarity index 90% rename from libs/AIBuilding.lua rename to libs/AIAttackWave.lua index ee1c511..caba527 100644 --- a/libs/AIBuilding.lua +++ b/libs/AIAttackWave.lua @@ -1,4 +1,4 @@ -local aiBuilding = {} +local aiAttackWave = {} -- imports @@ -76,7 +76,7 @@ local function validUnitGroupLocation(neighborChunk) return (neighborChunk[PASSABLE] == CHUNK_ALL_DIRECTIONS) and (neighborChunk[NEST_COUNT] == 0) end -function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, tick) +function aiAttackWave.rallyUnits(chunk, regionMap, surface, natives, tick) if (tick - chunk[RALLY_TRIGGERED] > INTERVAL_LOGIC) and (natives.points >= AI_VENGENCE_SQUAD_COST) then chunk[RALLY_TRIGGERED] = tick local cX = chunk.cX @@ -86,7 +86,7 @@ function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, tick) if (x ~= cX) and (y ~= cY) then local rallyChunk = getChunkByIndex(regionMap, x, y) if rallyChunk and (rallyChunk[NEST_COUNT] ~= 0) then - aiBuilding.formSquads(regionMap, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST) + aiAttackWave.formSquads(regionMap, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST) if (natives.points < AI_VENGENCE_SQUAD_COST) then return end @@ -97,7 +97,7 @@ function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, tick) end end -function aiBuilding.formSquads(regionMap, surface, natives, chunk, cost) +function aiAttackWave.formSquads(regionMap, surface, natives, chunk, cost) local valid = (cost == AI_VENGENCE_SQUAD_COST) or ((cost == AI_SQUAD_COST) and attackWaveValidCandidate(chunk, natives, surface)) if valid and (math.random() < natives.formSquadThreshold) then @@ -131,4 +131,4 @@ function aiBuilding.formSquads(regionMap, surface, natives, chunk, cost) end end -return aiBuilding +return aiAttackWave diff --git a/libs/BaseProcessor.lua b/libs/BaseProcessor.lua index cc763ef..4a2029b 100644 --- a/libs/BaseProcessor.lua +++ b/libs/BaseProcessor.lua @@ -2,7 +2,7 @@ local baseProcessor = {} -- imports -local baseUtils = require("BaseUtils") +local buildUtils = require("BuildUtils") local tendrilUtils = require("TendrilUtils") local constants = require("Constants") @@ -14,7 +14,7 @@ local BASE_QUEUE_SIZE = constants.BASE_QUEUE_SIZE local mMin = math.min -local buildOrder = baseUtils.buildOrder +local buildOrder = buildUtils.buildOrder local advanceTendrils = tendrilUtils.advanceTendrils -- module code @@ -28,7 +28,7 @@ function baseProcessor.processBases(regionMap, surface, natives, tick) local base = bases[index] buildOrder(regionMap, natives, base, surface, tick) - advanceTendrils(regionMap, base, surface, tick) + advanceTendrils(regionMap, base, surface, tick, natives) end if (endIndex == #bases) then diff --git a/libs/BaseUtils.lua b/libs/BaseUtils.lua index 5f1a8f5..413e62d 100644 --- a/libs/BaseUtils.lua +++ b/libs/BaseUtils.lua @@ -4,23 +4,17 @@ local baseUtils = {} local mapUtils = require("MapUtils") local constants = require("Constants") -local mathUtils = require("MathUtils") - -local baseRegisterUtils = require("BaseRegisterUtils") local tendrilUtils = require("TendrilUtils") +local buildUtils = require("BuildUtils") + -- constants local BASE_DISTANCE_THRESHOLD = constants.BASE_DISTANCE_THRESHOLD local BASE_ALIGNMENT_NEUTRAL = constants.BASE_ALIGNMENT_NEUTRAL -local AI_NEST_COST = constants.AI_NEST_COST -local AI_WORM_COST = constants.AI_WORM_COST - -local DOUBLE_CHUNK_SIZE = constants.DOUBLE_CHUNK_SIZE - local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER @@ -28,14 +22,12 @@ local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER local euclideanDistancePoints = mapUtils.euclideanDistancePoints -local gaussianRandomRange = mathUtils.gaussianRandomRange +local buildHive = buildUtils.buildHive local mFloor = math.floor local buildTendril = tendrilUtils.buildTendril -local registerEnemyBaseStructure = baseRegisterUtils.registerEnemyBaseStructure - -- module code function baseUtils.findNearbyBase(natives, position) @@ -53,75 +45,6 @@ function baseUtils.findNearbyBase(natives, position) return foundBase end -function baseUtils.buildHive(regionMap, base, surface) - local valid = false - local position = surface.find_non_colliding_position("biter-spawner-hive", base, DOUBLE_CHUNK_SIZE, 2) - if position then - local biterSpawner = {name="biter-spawner-hive", position=position} - local hive = surface.create_entity(biterSpawner) - if (#base.hives == 0) then - base.x = hive.position.x - base.y = hive.position.y - end - base.hives[#base.hives+1] = hive - registerEnemyBaseStructure(regionMap, hive, base) - valid = true - end - return valid -end - -function baseUtils.buildOutpost(natives, base, surface, tick, position) - -end - -function baseUtils.buildOrder(regionMap, natives, base, surface, tick) - local foundHive = false - for _,_ in pairs(base.hives) do - foundHive = true - break - end - if not foundHive or (base.upgradePoints < 10) then - return - end - - local generator = natives.randomGenerator - generator.re_seed(base.pattern) - - for level=0,base.level do - local slices = (level * 3) - slices = gaussianRandomRange(slices, slices * 0.1, slices * 0.6, slices * 1.4, generator) - local slice = (2 * math.pi) / slices - local pos = 0 - local thing - local cost - local radiusAdjustment - if (generator() < 0.3) then - thing = "small-worm-turret" - cost = AI_WORM_COST - radiusAdjustment = -4 - else - thing = "biter-spawner" - cost = AI_NEST_COST - radiusAdjustment = 0 - end - for _ = 1, slices do - if (base.upgradePoints < 10) then - return - end - local radius = 10 * level - local distortion = gaussianRandomRange(radius, 10, radius - 7.5 + radiusAdjustment, radius + 7.5 + radiusAdjustment, generator) - local nestPosition = {x = base.x + (distortion * math.cos(pos)), - y = base.y + (distortion * math.sin(pos))} - local biterSpawner = {name=thing, position=nestPosition} - if surface.can_place_entity(biterSpawner) then - registerEnemyBaseStructure(regionMap, surface.create_entity(biterSpawner), base) - base.upgradePoints = base.upgradePoints - cost - end - pos = pos + slice - end - end -end - function baseUtils.createBase(regionMap, natives, position, surface, tick) local bases = natives.bases local distance = euclideanDistancePoints(position.x, position.y, 0, 0) @@ -140,7 +63,7 @@ function baseUtils.createBase(regionMap, natives, position, surface, tick) pattern = math.random(MAGIC_MAXIMUM_BASE_NUMBER), level = mFloor(distance / 200) } - if not baseUtils.buildHive(regionMap, base, surface) then + if not buildHive(regionMap, base, surface) then return nil end buildTendril(regionMap, natives, base, surface, tick) diff --git a/libs/BuildUtils.lua b/libs/BuildUtils.lua new file mode 100644 index 0000000..c453f3d --- /dev/null +++ b/libs/BuildUtils.lua @@ -0,0 +1,139 @@ +local buildUtils = {} + +-- imports + +local constants = require("Constants") +local mathUtils = require("MathUtils") + +local baseRegisterUtils = require("BaseRegisterUtils") + +-- constants + +local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER + +-- imported functions + +local AI_NEST_COST = constants.AI_NEST_COST +local AI_WORM_COST = constants.AI_WORM_COST + +local DOUBLE_CHUNK_SIZE = constants.DOUBLE_CHUNK_SIZE + +local registerEnemyBaseStructure = baseRegisterUtils.registerEnemyBaseStructure +local gaussianRandomRange = mathUtils.gaussianRandomRange + +-- module code + +function buildUtils.buildHive(regionMap, base, surface) + local valid = false + local position = surface.find_non_colliding_position("biter-spawner-hive", base, DOUBLE_CHUNK_SIZE, 2) + if position then + local biterSpawner = {name="biter-spawner-hive", position=position} + local hive = surface.create_entity(biterSpawner) + if (#base.hives == 0) then + base.x = hive.position.x + base.y = hive.position.y + end + base.hives[#base.hives+1] = hive + registerEnemyBaseStructure(regionMap, hive, base) + valid = true + end + return valid +end + +function buildUtils.buildOutpost(regionMap, natives, base, surface, position) + local foundHive = false + for _,_ in pairs(base.hives) do + foundHive = true + break + end + if not foundHive or (base.upgradePoints < 10) then + return + end + + local generator = natives.randomGenerator + generator.re_seed(math.random(MAGIC_MAXIMUM_BASE_NUMBER)) + + for level=0,(base.level * 0.5) do + local slices = (level * 3) + slices = gaussianRandomRange(slices, slices * 0.1, slices * 0.6, slices * 1.4, generator) + local slice = (2 * math.pi) / slices + local pos = 0 + local thing + local cost + local radiusAdjustment + if (generator() < 0.3) then + thing = "small-worm-turret" + cost = AI_WORM_COST + radiusAdjustment = -4 + else + thing = "biter-spawner" + cost = AI_NEST_COST + radiusAdjustment = 0 + end + for _ = 1, slices do + if (base.upgradePoints < 10) then + return + end + local radius = 10 * level + local distortion = gaussianRandomRange(radius, 10, radius - 7.5 + radiusAdjustment, radius + 7.5 + radiusAdjustment, generator) + local nestPosition = {x = position.x + (distortion * math.cos(pos)), + y = position.y + (distortion * math.sin(pos))} + local biterSpawner = {name=thing, position=nestPosition} + if surface.can_place_entity(biterSpawner) then + registerEnemyBaseStructure(regionMap, surface.create_entity(biterSpawner), base) + base.upgradePoints = base.upgradePoints - cost + end + pos = pos + slice + end + end +end + +function buildUtils.buildOrder(regionMap, natives, base, surface) + local foundHive = false + for _,_ in pairs(base.hives) do + foundHive = true + break + end + if not foundHive or (base.upgradePoints < 10) then + return + end + + local generator = natives.randomGenerator + generator.re_seed(base.pattern) + + for level=0,base.level do + local slices = (level * 3) + slices = gaussianRandomRange(slices, slices * 0.1, slices * 0.6, slices * 1.4, generator) + local slice = (2 * math.pi) / slices + local pos = 0 + local thing + local cost + local radiusAdjustment + if (generator() < 0.3) then + thing = "small-worm-turret" + cost = AI_WORM_COST + radiusAdjustment = -4 + else + thing = "biter-spawner" + cost = AI_NEST_COST + radiusAdjustment = 0 + end + for _ = 1, slices do + if (base.upgradePoints < 10) then + return + end + local radius = 10 * level + local distortion = gaussianRandomRange(radius, 10, radius - 7.5 + radiusAdjustment, radius + 7.5 + radiusAdjustment, generator) + local nestPosition = {x = base.x + (distortion * math.cos(pos)), + y = base.y + (distortion * math.sin(pos))} + local biterSpawner = {name=thing, position=nestPosition} + if surface.can_place_entity(biterSpawner) then + registerEnemyBaseStructure(regionMap, surface.create_entity(biterSpawner), base) + base.upgradePoints = base.upgradePoints - cost + end + pos = pos + slice + end + end +end + +return buildUtils diff --git a/libs/ChunkUtils.lua b/libs/ChunkUtils.lua index 6dfa6ba..c99ebdd 100644 --- a/libs/ChunkUtils.lua +++ b/libs/ChunkUtils.lua @@ -130,10 +130,12 @@ function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tick, tempQue if (nests > 0) or (worms > 0) or (biters > 0) then for f=1, #enemies do local enemy = enemies[f] - if (enemy.name ~= "biter-spawner-hive") then - enemy.destroy() - else + if (enemy.name == "small-tendril-biter-rampant") then + biters = biters - 1 + elseif (enemy.name == "biter-spawner-hive") then nests = nests - 1 + else + enemy.destroy() end end local foundBase = findNearbyBase(natives, chunk) or createBase(regionMap, natives, chunk, surface, tick) diff --git a/libs/Constants.lua b/libs/Constants.lua index a00a885..8dddb8f 100644 --- a/libs/Constants.lua +++ b/libs/Constants.lua @@ -12,6 +12,7 @@ constants.VERSION_20 = 20 constants.VERSION_22 = 22 constants.VERSION_23 = 23 constants.VERSION_25 = 25 +constants.VERSION_26 = 26 -- misc @@ -33,7 +34,7 @@ constants.INTERVAL_LOGIC = 38 constants.PLAYER_PHEROMONE_MULTIPLER = 100 -constants.DEV_CUSTOM_AI = false +constants.DEV_CUSTOM_AI = true -- chunk properties diff --git a/libs/MapProcessor.lua b/libs/MapProcessor.lua index 1a084cc..e57c611 100644 --- a/libs/MapProcessor.lua +++ b/libs/MapProcessor.lua @@ -5,7 +5,7 @@ local mapProcessor = {} local unitGroupUtils = require("UnitGroupUtils") local pheromoneUtils = require("PheromoneUtils") -local aiBuilding = require("AIBuilding") +local aiAttackWave = require("AIAttackWave") local aiPredicates = require("AIPredicates") local constants = require("Constants") local mapUtils = require("MapUtils") @@ -40,7 +40,7 @@ local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES local scents = pheromoneUtils.scents local processPheromone = pheromoneUtils.processPheromone -local formSquads = aiBuilding.formSquads +local formSquads = aiAttackWave.formSquads local getChunkByIndex = mapUtils.getChunkByIndex local getChunkByPosition = mapUtils.getChunkByPosition diff --git a/libs/MapUtils.lua b/libs/MapUtils.lua index c05d815..3188f89 100644 --- a/libs/MapUtils.lua +++ b/libs/MapUtils.lua @@ -4,8 +4,6 @@ local mapUtils = {} local constants = require("Constants") -local mathUtils = require("MathUtils") - -- constants local CHUNK_NORTH_SOUTH = constants.CHUNK_NORTH_SOUTH @@ -21,8 +19,6 @@ local CHUNK_SIZE = constants.CHUNK_SIZE -- imported functions -local gaussianRandomRange = mathUtils.gaussianRandomRange - local mFloor = math.floor -- module code @@ -43,10 +39,6 @@ function mapUtils.getChunkByIndex(regionMap, x, y) return nil end -function mapUtils.positionToChunkOffset(position) - return mFloor(position.x * 0.03125), mFloor(position.y * 0.03125) -end - --[[ 1 2 3 \|/ @@ -116,33 +108,7 @@ function mapUtils.getCardinalChunks(regionMap, chunkX, chunkY) return neighbors end -function mapUtils.euclideanDistanceNamed(p1, p2) - local xs = p1.x - p2.x - local ys = p1.y - p2.y - return ((xs * xs) + (ys * ys)) ^ 0.5 -end - -function mapUtils.euclideanDistancePoints(x1, y1, x2, y2) - local xs = x1 - x2 - local ys = y1 - y2 - return ((xs * xs) + (ys * ys)) ^ 0.5 -end - -function mapUtils.euclideanDistanceArray(p1, p2) - local xs = p1[1] - p2[1] - local ys = p1[2] - p2[2] - return ((xs * xs) + (ys * ys)) ^ 0.5 -end - -function mapUtils.distortPosition(position) - local xDistort = gaussianRandomRange(1, 0.5, 0, 2) - 1 - local yDistort = gaussianRandomRange(1, 0.5, 0, 2) - 1 - position.x = position.x + (xDistort * 48) - position.y = position.y + (yDistort * 48) -end - function mapUtils.positionFromDirectionAndChunk(direction, startPosition, position, scaling) - -- local position = {x=0, y=0} if (direction == 1) then position.x = startPosition.x - CHUNK_SIZE * scaling position.y = startPosition.y - CHUNK_SIZE * scaling diff --git a/libs/MathUtils.lua b/libs/MathUtils.lua index 6995d16..32b9137 100644 --- a/libs/MathUtils.lua +++ b/libs/MathUtils.lua @@ -16,6 +16,10 @@ local mMax = math.max local mSqrt = math.sqrt local mLog10 = math.log10 +local mFloor = math.floor + +-- module code + function mathUtils.roundToNearest(number, multiple) local num = number + (multiple * 0.5) return num - (num % multiple) @@ -60,4 +64,33 @@ function mathUtils.gaussianRandomRange(mean, std_dev, min, max, rg) return q end +function mathUtils.positionToChunkOffset(position) + return mFloor(position.x * 0.03125), mFloor(position.y * 0.03125) +end + +function mathUtils.euclideanDistanceNamed(p1, p2) + local xs = p1.x - p2.x + local ys = p1.y - p2.y + return ((xs * xs) + (ys * ys)) ^ 0.5 +end + +function mathUtils.euclideanDistancePoints(x1, y1, x2, y2) + local xs = x1 - x2 + local ys = y1 - y2 + return ((xs * xs) + (ys * ys)) ^ 0.5 +end + +function mathUtils.euclideanDistanceArray(p1, p2) + local xs = p1[1] - p2[1] + local ys = p1[2] - p2[2] + return ((xs * xs) + (ys * ys)) ^ 0.5 +end + +function mathUtils.distortPosition(position) + local xDistort = mathUtils.gaussianRandomRange(1, 0.5, 0, 2) - 1 + local yDistort = mathUtils.gaussianRandomRange(1, 0.5, 0, 2) - 1 + position.x = position.x + (xDistort * 48) + position.y = position.y + (yDistort * 48) +end + return mathUtils diff --git a/libs/MovementUtils.lua b/libs/MovementUtils.lua new file mode 100644 index 0000000..0cf17dd --- /dev/null +++ b/libs/MovementUtils.lua @@ -0,0 +1,72 @@ +local movementUtils = {} + +-- imports + +local constants = require("Constants") +local unitGroupUtils = require("UnitGroupUtils") + +local mathUtils = require("MathUtils") + +-- constants + +local MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT +local MAX_PENALTY_BEFORE_PURGE = constants.MAX_PENALTY_BEFORE_PURGE + +-- imported functions + +local recycleBiters = unitGroupUtils.recycleBiters + +local tableRemove = table.remove +local tableInsert = table.insert + +local distortPosition = mathUtils.distortPosition + +-- module code + +function movementUtils.findMovementPosition(surface, position, distort) + local pos = position + if not surface.can_place_entity({name="behemoth-biter", position=position}) then + pos = surface.find_non_colliding_position("behemoth-biter", position, 5, 2) or position + end + return (distort and distortPosition(pos)) or pos +end + +function movementUtils.addMovementPenalty(natives, units, chunkX, chunkY) + local penalties = units.penalties + for i=1,#penalties do + local penalty = penalties[i] + if (penalty.x == chunkX) and (penalty.y == chunkY) then + penalty.v = penalty.v + MOVEMENT_PHEROMONE_GENERATOR_AMOUNT + if (penalty.v > MAX_PENALTY_BEFORE_PURGE) then + local group = units.group + if group then + recycleBiters(natives, group.members) + group.destroy() + else + units.unit.destroy() + end + end + return + end + end + if (#penalties == 7) then + tableRemove(penalties, 7) + end + tableInsert(penalties, 1, { v = MOVEMENT_PHEROMONE_GENERATOR_AMOUNT, + x = chunkX, + y = chunkY }) +end + +function movementUtils.lookupMovementPenalty(squad, chunkX, chunkY) + local penalties = squad.penalties + for i=1,#penalties do + local penalty = penalties[i] + if (penalty.x == chunkX) and (penalty.y == chunkY) then + return penalty.v + end + end + return 0 +end + + +return movementUtils diff --git a/libs/AIAttack.lua b/libs/SquadAttack.lua similarity index 81% rename from libs/AIAttack.lua rename to libs/SquadAttack.lua index aa89cd1..a13033b 100644 --- a/libs/AIAttack.lua +++ b/libs/SquadAttack.lua @@ -1,4 +1,4 @@ -local aiAttack = {} +local squadAttack = {} -- imports @@ -7,6 +7,7 @@ local mapUtils = require("MapUtils") local unitGroupUtils = require("UnitGroupUtils") local playerUtils = require("PlayerUtils") local neighborUtils = require("NeighborUtils") +local movementUtils = require("MovementUtils") -- constants @@ -32,9 +33,11 @@ local DEFINES_DISTRACTION_BY_ANYTHING = defines.distraction.by_anything -- imported functions +local findMovementPoint = movementUtils.findMovementPoint + local getNeighborChunks = mapUtils.getNeighborChunks local getChunkByPosition = mapUtils.getChunkByPosition -local addSquadMovementPenalty = unitGroupUtils.addSquadMovementPenalty +local addMovementPenalty = unitGroupUtils.addMovementPenalty local lookupSquadMovementPenalty = unitGroupUtils.lookupSquadMovementPenalty local calculateKamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk @@ -52,7 +55,7 @@ local function scoreAttackLocation(squad, neighborChunk) return damage - lookupSquadMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY) end -function aiAttack.squadAttack(regionMap, surface, natives) +function squadAttack.squadsAttack(regionMap, surface, natives) local squads = natives.squads local attackPosition local attackCmd @@ -80,18 +83,12 @@ function aiAttack.squadAttack(regionMap, surface, natives) chunk.cY), scoreAttackLocation, squad) - addSquadMovementPenalty(natives, squad, chunk.cX, chunk.cY) + addMovementPenalty(natives, squad, chunk.cX, chunk.cY) if group.valid and attackChunk then if (attackChunk[PLAYER_BASE_GENERATOR] == 0) or ((groupState == DEFINES_GROUP_FINISHED) or (groupState == DEFINES_GROUP_GATHERING)) then - positionFromDirectionAndChunk(attackDirection, groupPosition, attackPosition, 1.35) - - if (#squad.group.members > 80) then - squad.cycles = 6 - else - squad.cycles = 4 - end + squad.cycles = ((#squad.group.members > 80) and 6) or 4 local moreFrenzy = not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) < 100) squad.frenzy = moreFrenzy @@ -102,19 +99,18 @@ function aiAttack.squadAttack(regionMap, surface, natives) attackCmd.distraction = DEFINES_DISTRACTION_BY_ENEMY end - if surface.can_place_entity({name="behemoth-biter", position=attackPosition}) then + local position = findMovementPoint(surface, + positionFromDirectionAndChunk(attackDirection, + groupPosition, + attackPosition, + 1.35)) + if position then + attackPosition.x = position.x + attackPosition.y = position.y group.set_command(attackCmd) group.start_moving() else - local newAttackPosition = surface.find_non_colliding_position("behemoth-biter", attackPosition, 5, 2) - if newAttackPosition then - attackPosition.x = newAttackPosition.x - attackPosition.y = newAttackPosition.y - group.set_command(attackCmd) - group.start_moving() - else - addSquadMovementPenalty(natives, squad, attackChunk.cX, attackChunk.cY) - end + addMovementPenalty(natives, squad, attackChunk.cX, attackChunk.cY) end elseif not squad.frenzy and not squad.rabid and ((groupState == DEFINES_GROUP_ATTACKING_DISTRACTION) or (groupState == DEFINES_GROUP_ATTACKING_TARGET) or @@ -130,7 +126,7 @@ function aiAttack.squadAttack(regionMap, surface, natives) end end -function aiAttack.squadBeginAttack(natives, players) +function squadAttack.squadsBeginAttack(natives, players) local squads = natives.squads for i=1,#squads do local squad = squads[i] @@ -154,4 +150,4 @@ function aiAttack.squadBeginAttack(natives, players) end end -return aiAttack +return squadAttack diff --git a/libs/AIDefense.lua b/libs/SquadDefense.lua similarity index 97% rename from libs/AIDefense.lua rename to libs/SquadDefense.lua index ef7f73a..5ef732a 100644 --- a/libs/AIDefense.lua +++ b/libs/SquadDefense.lua @@ -67,6 +67,7 @@ function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, nati if exitPath then local retreatPosition = positionFromDirectionAndChunk(exitDirection, position, {x=0,y=0}, 0.98) + if not surface.can_place_entity({name="behemoth-biter", position=retreatPosition}) then local newRetreatPosition = surface.find_non_colliding_position("behemoth-biter", retreatPosition, 5, 2) if newRetreatPosition then diff --git a/libs/TendrilUtils.lua b/libs/TendrilUtils.lua index f6e29b0..72321c8 100644 --- a/libs/TendrilUtils.lua +++ b/libs/TendrilUtils.lua @@ -7,6 +7,8 @@ local mapUtils = require("MapUtils") local baseRegisterUtils = require("BaseRegisterUtils") local neighborsUtils = require("NeighborUtils") +local buildUtils = require("BuildUtils") + -- constants local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE @@ -15,7 +17,7 @@ local NEST_COUNT = constants.NEST_COUNT -- imported functions -local scoreNeighborsWithDirection = neighborsUtils.scoreNeighborsWithDirection +local scoreNeighborsForAttack = neighborsUtils.scoreNeighborsForAttack local getNeighborChunks = mapUtils.getNeighborChunks @@ -25,30 +27,26 @@ local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk local registerEnemyBaseStructure = baseRegisterUtils.registerEnemyBaseStructure -local canMoveChunkDirection = mapUtils.canMoveChunkDirection +local buildOutpost = buildUtils.buildOutpost local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed -- module code -local function scoreTendrilChunk(squad, chunk, surface) +local function scoreTendrilChunk(squad, chunk) return chunk[RESOURCE_PHEROMONE] end -local function validTendrilChunk(x, chunk, neighborChunk) - return canMoveChunkDirection(x, chunk, neighborChunk) +local function colorChunk(x, y, tileType, surface) + local tiles = {} + for xi=x+5, x + 27 do + for yi=y+5, y + 27 do + tiles[#tiles+1] = {name=tileType, position={xi, yi}} + end + end + surface.set_tiles(tiles, false) end --- local function colorChunk(x, y, tileType, surface) --- local tiles = {} --- for xi=x+5, x + 27 do --- for yi=y+5, y + 27 do --- tiles[#tiles+1] = {name=tileType, position={xi, yi}} --- end --- end --- surface.set_tiles(tiles, false) --- end - local function removeTendril(base, tendril) for i=1,#base.tendrils do if (base.tendrils[i] == tendril) then @@ -58,57 +56,84 @@ local function removeTendril(base, tendril) end end -local function buildTendrilPath(regionMap, tendril, surface, base, tick) - local chunk = getChunkByPosition(regionMap, tendril.x, tendril.y) +local function buildTendrilPath(regionMap, tendril, surface, base, tick, natives) + local tendrilUnit = tendril.unit + if not tendrilUnit.valid then + return + end + if (tendril.cycles > 0) then + tendril.cycles = tendril.cycles - 1 + return + end + local tendrilPosition = tendrilUnit.position + local chunk = getChunkByPosition(regionMap, tendrilPosition.x, tendrilPosition.y) if chunk then - local tendrilPath,tendrilDirection = scoreNeighborsWithDirection(chunk, - getNeighborChunks(regionMap, - chunk.cX, - chunk.cY), - validTendrilChunk, - scoreTendrilChunk, - nil, - surface, - true) + local tendrilPath,tendrilDirection = scoreNeighborsForAttack(chunk, + getNeighborChunks(regionMap, + chunk.cX, + chunk.cY), + scoreTendrilChunk, + nil) if (tendrilDirection == -1) then - removeTendril(base, tendril) - tendrilUtils.buildTendril(regionMap, nil, base, surface, tick) - -- colorChunk(chunk.x, chunk.y, "hazard-concrete-left", surface) + -- removeTendril(base, tendril) + -- buildOutpost(regionMap, natives, base, surface, tendril) + -- tendrilUtils.buildTendril(regionMap, natives, base, surface, tick) + colorChunk(chunk.x, chunk.y, "hazard-concrete-left", surface) return elseif tendrilPath then - positionFromDirectionAndChunk(tendrilDirection, tendril, tendril, 0.5) - mapUtils.distortPosition(tendril) - tendril.path[#tendril.path] = chunk + positionFromDirectionAndChunk(tendrilDirection, tendrilPosition, tendrilPosition, 0.5) + mapUtils.distortPosition(tendrilPosition) + -- tendril.path[#tendril.path] = chunk local position = surface.find_non_colliding_position("spitter-spawner", - {x=tendril.x,y=tendril.y}, + tendrilPosition, 32, - 4) - if position and (tendrilPath[NEST_COUNT] <= 3)then - tendril.x = position.x - tendril.y = position.y - local biterSpawner = {name="spitter-spawner", position=position} - local hive = surface.create_entity(biterSpawner) - registerEnemyBaseStructure(regionMap, hive, base) - elseif not position then - removeTendril(base, tendril) + 2) + if position then + tendril.target = position + tendrilUnit.set_command({ type = defines.command.go_to_location, + destination = position, + distraction = defines.distraction.by_damage }) + + -- needs to check to make sure unit still exists as well. + --tendril.cycles = 2 + -- tendril.x = position.x + -- tendril.y = position.y + -- local biterSpawner = {name="spitter-spawner", position=position} + -- local hive = surface.create_entity(biterSpawner) + -- registerEnemyBaseStructure(regionMap, hive, base) + -- elseif not position then + -- removeTendril(base, tendril) end end - -- colorChunk(chunk.x, chunk.y, "concrete", surface) + colorChunk(chunk.x, chunk.y, "concrete", surface) end end -function tendrilUtils.advanceTendrils(regionMap, base, surface, tick) +function tendrilUtils.advanceTendrils(regionMap, base, surface, tick, natives) for i=1, #base.tendrils do - buildTendrilPath(regionMap, base.tendrils[i], surface, base, tick) + buildTendrilPath(regionMap, base.tendrils[i], surface, base, tick, natives) end end -function tendrilUtils.createTendril(base) +function tendrilUtils.createTendril(base, surface) + local position = surface.find_non_colliding_position("small-tendril-biter-rampant", + {x=base.x,y=base.y}, + 32, + 4) + if not position then + return nil + end + + local entity = surface.create_entity({name="small-tendril-biter-rampant", position=position}) + local tendril = { - x = base.x, - y = base.y, + unit = entity, + penalties = {}, + target = entity.position, + cycles = 0, path = {} } + return tendril end @@ -117,10 +142,11 @@ function tendrilUtils.buildTendril(regionMap, natives, base, surface, tick) -- if chunk then -- local tempNeighbors = {nil, nil, nil, nil, nil, nil, nil, nil} -- buildTendrilPath(regionMap, chunk, surface, base, tempNeighbors) - base.tendrils[#base.tendrils+1] = tendrilUtils.createTendril(base) + local tendril = tendrilUtils.createTendril(base, surface) + if tendril then + base.tendrils[#base.tendrils+1] = tendril + end -- end end - - return tendrilUtils diff --git a/libs/UnitGroupUtils.lua b/libs/UnitGroupUtils.lua index 021d2dd..0de9ad2 100644 --- a/libs/UnitGroupUtils.lua +++ b/libs/UnitGroupUtils.lua @@ -7,8 +7,6 @@ local constants = require("Constants") -- constants -local MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT - local SQUAD_QUEUE_SIZE = constants.SQUAD_QUEUE_SIZE local DEFINES_GROUP_STATE_FINISHED = defines.group_state.finished @@ -17,8 +15,6 @@ local DEFINES_GROUP_STATE_ATTACKING_DISTRACTION = defines.group_state.attacking_ local DEFINES_COMMAND_GROUP = defines.command.group local DEFINES_DISTRACTION_NONE = defines.distraction.none -local MAX_PENALTY_BEFORE_PURGE = constants.MAX_PENALTY_BEFORE_PURGE - local SQUAD_RETREATING = constants.SQUAD_RETREATING local SQUAD_GUARDING = constants.SQUAD_GUARDING local GROUP_MERGE_DISTANCE = constants.GROUP_MERGE_DISTANCE @@ -36,8 +32,6 @@ local mLog = math.log10 local mMin = math.min -local tableRemove = table.remove -local tableInsert = table.insert local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed -- module code @@ -110,39 +104,6 @@ function unitGroupUtils.convertUnitGroupToSquad(natives, unitGroup) return returnSquad end -function unitGroupUtils.addSquadMovementPenalty(natives, squad, chunkX, chunkY) - local penalties = squad.penalties - for i=1,#penalties do - local penalty = penalties[i] - if (penalty.x == chunkX) and (penalty.y == chunkY) then - penalty.v = penalty.v + MOVEMENT_PHEROMONE_GENERATOR_AMOUNT - if (penalty.v > MAX_PENALTY_BEFORE_PURGE) then - local group = squad.group - unitGroupUtils.recycleBiters(natives, group.members) - group.destroy() - end - return - end - end - if (#penalties == 7) then - tableRemove(penalties, 7) - end - tableInsert(penalties, 1, { v = MOVEMENT_PHEROMONE_GENERATOR_AMOUNT, - x = chunkX, - y = chunkY }) -end - -function unitGroupUtils.lookupSquadMovementPenalty(squad, chunkX, chunkY) - local penalties = squad.penalties - for i=1,#penalties do - local penalty = penalties[i] - if (penalty.x == chunkX) and (penalty.y == chunkY) then - return penalty.v - end - end - return 0 -end - function unitGroupUtils.calculateKamikazeThreshold(squad, natives) local squadSizeBonus = mLog((#squad.group.members / natives.attackWaveMaxSize) + 0.1) + 1 return natives.kamikazeThreshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus) diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index 134740d..858ed33 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -11,6 +11,8 @@ small-fire-spitter=Small Fire Spitter biter-spawner-hive=Small Hive +small-tendril-biter-rampant=Small Tendril + [entity-description] tunnel-entrance=This tunnel is used by the biters to bypass player defenses. Fill the hole using landfill. @@ -23,6 +25,8 @@ small-fire-spitter=These biters will spit fire biter-spawner-hive=Small Hive +small-tendril-biter-rampant=Small Tendril + [mod-setting-name] rampant-useDumbProjectiles=Use Dumb Projectiles rampant-useNEUnitLaunchers=Use Natural Evolution Unit Launchers (Needs NE) diff --git a/make.rkt b/make.rkt index 08eafa0..fd903df 100644 --- a/make.rkt +++ b/make.rkt @@ -77,8 +77,8 @@ (copyDirectory "prototypes" modFolder))) (define (run) - ;; (copyFiles modFolder) + (copyFiles modFolder) ;; (copyFiles zipModFolder) - (makeZip modFolder) + ;;(makeZip modFolder) ) ) diff --git a/prototypes/enemies/UnitTendril.lua b/prototypes/enemies/UnitTendril.lua new file mode 100644 index 0000000..4744344 --- /dev/null +++ b/prototypes/enemies/UnitTendril.lua @@ -0,0 +1,42 @@ + +-- imports + +local biterUtils = require("BiterUtils") + +-- import functions + +local makeBiter = biterUtils.makeBiter +local createSuicideAttack = biterUtils.createSuicideAttack +local makeResistance = biterUtils.makeResistance + +-- local + +local smallSuicideBiterScale = 0.75 +local smallSuicideBiterTint1 = {r=0.0, g=0.0, b=0.70, a=0.8} +local smallSuicideBiterTint2 = {r=0.0, g=0.0, b=0.72, a=0.4} + +-- module code + +data:extend({ + makeBiter({name = "small-tendril-biter-rampant", + health = 30, + movement = 0.21, + distancePerFrame = 0.1, + healing = 0.01, + scale = smallSuicideBiterScale, + tint1 = smallSuicideBiterTint1, + tint2 = smallSuicideBiterTint2, + explosion = "blood-explosion-small"}, + createSuicideAttack({ area = 3.5, + damage = 20, + explosion = "explosion", + scorchmark = "small-scorchmark", + explosionCount = 2, + explosionDistance = 2, + scale = smallSuicideBiterScale, + tint1 = smallSuicideBiterTint1, + tint2 = smallSuicideBiterTint2}), + {makeResistance("explosion", 0, -50), + makeResistance("laser", 1, 0), + makeResistance("fire", 0, -60)}), +})