1
0
mirror of https://github.com/veden/Rampant.git synced 2025-09-16 09:16:43 +02:00

working adjustments and refactoring

This commit is contained in:
Aaron Veden
2017-11-20 23:27:03 -08:00
parent f6d9c5d02f
commit 4f4945971b
44 changed files with 971 additions and 1834 deletions

View File

@@ -48,7 +48,6 @@ Configure Options not in game menu:
- Pathfinding - Unit groups will use potential fields to perform only single step pathfinding allowing for efficient and dynamic pathing
- Peace mode - If something sets peace mode, Rampant will respect it
- Ion Cannon Reaction - Firing the Ion Cannon will cause nests around the blast site to form into an attack wave and agitate all biters
- Item Collector + Technology to unlock it - An entity that collects the items on the ground around itself
# Planned Features
@@ -61,6 +60,14 @@ Configure Options not in game menu:
# Version History
0.15.23 -
- Fixed: Retreat radius being centered on chunk corner instead of on the unit dying
- Fixed: Spitter flamethrower sound fx
- Moved: Item Collector into separate mod
- Balance: Adjusted spitter and worms damages, ranges, and cooldowns to be more inline with vanilla
- Optimization: Reduced memory footprint for faster saving and loading
- Framework: Reorganization of files
0.15.22 -
- Contribution - Martok88, Improvement: Added optional attack wave message per player

View File

@@ -8,7 +8,6 @@ local mathUtils = require("libs/MathUtils")
-- constants
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
-- imported functions
@@ -16,7 +15,7 @@ local roundToNearest = mathUtils.roundToNearest
-- module code
function upgrade.attempt(natives, world)
function upgrade.attempt(natives)
local starting = global.version
if (global.version == nil) then
natives.squads = {}
@@ -157,25 +156,14 @@ function upgrade.attempt(natives, world)
game.surfaces[1].print("Rampant - Version 0.15.17")
global.version = constants.VERSION_27
end
if (global.version < constants.VERSION_28) then
if (global.version < constants.VERSION_33) then
if (world == nil) then
global.world = {}
world = global.world
end
world.itemCollectorLookup = {}
world.itemCollectorEvents = {}
global.world = nil
game.surfaces[1].print("Rampant - Version 0.15.18")
global.version = constants.VERSION_28
game.surfaces[1].print("Rampant - Version 0.15.23")
global.version = constants.VERSION_33
end
if (global.version < constants.VERSION_32) then
game.surfaces[1].print("Rampant - Version 0.15.22")
global.version = constants.VERSION_32
end
return starting ~= global.version, natives, world
return starting ~= global.version, natives
end
function upgrade.compareTable(entities, option, new)

View File

@@ -1,6 +1,5 @@
-- imports
local entityUtils = require("libs/EntityUtils")
local mapUtils = require("libs/MapUtils")
local unitGroupUtils = require("libs/UnitGroupUtils")
local chunkProcessor = require("libs/ChunkProcessor")
@@ -14,14 +13,10 @@ local aiAttackWave = require("libs/AIAttackWave")
local aiPlanning = require("libs/AIPlanning")
local interop = require("libs/Interop")
local tests = require("tests")
local chunkUtils = require("libs/ChunkUtils")
local upgrade = require("Upgrade")
local baseRegisterUtils = require("libs/BaseRegisterUtils")
local mathUtils = require("libs/MathUtils")
local config = require("config")
local worldProcessor = require("libs/WorldProcessor")
local inventoryUtils = require("libs/InventoryUtils")
local playerUtils = require("libs/PlayerUtils")
local buildUtils = require("libs/BuildUtils")
-- constants
@@ -30,12 +25,7 @@ local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local DEFINES_INVENTORY_PLAYER_MAIN = defines.inventory.player_main
local DEFINES_INVENTORY_GOD_MAIN = defines.inventory.god_main
local DEFINES_INVENTORY_PLAYER_QUICKBAR = defines.inventory.player_quickbar
local DEFINES_INVENTORY_GOD_QUICKBAR = defines.inventory.god_quickbar
local ITEM_COLLECTOR_MAX_QUEUE_SIZE = constants.ITEM_COLLECTOR_MAX_QUEUE_SIZE
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
local AI_MAX_OVERFLOW_POINTS = constants.AI_MAX_OVERFLOW_POINTS
@@ -49,17 +39,6 @@ local getChunkByPosition = mapUtils.getChunkByPosition
local processPendingChunks = chunkProcessor.processPendingChunks
local buildComplexEntity = buildUtils.buildComplexEntity
local mineComplexEntity = buildUtils.mineComplexEntity
local getPlayerCursorStack = playerUtils.getPlayerCursorStack
local swapItemStack = inventoryUtils.swapItemStack
local swapItemInventory = inventoryUtils.swapItemInventory
local topOffHand = inventoryUtils.topOffHand
local processWorld = worldProcessor.processWorld
local processMap = mapProcessor.processMap
local processPlayers = mapProcessor.processPlayers
local scanMap = mapProcessor.scanMap
@@ -80,23 +59,22 @@ local squadsBeginAttack = squadAttack.squadsBeginAttack
local retreatUnits = squadDefense.retreatUnits
local addRemovePlayerEntity = entityUtils.addRemovePlayerEntity
local unregisterEnemyBaseStructure = baseRegisterUtils.unregisterEnemyBaseStructure
local registerEnemyBaseStructure = baseRegisterUtils.registerEnemyBaseStructure
local makeImmortalEntity = entityUtils.makeImmortalEntity
local addRemovePlayerEntity = chunkUtils.addRemovePlayerEntity
local unregisterEnemyBaseStructure = chunkUtils.unregisterEnemyBaseStructure
local registerEnemyBaseStructure = chunkUtils.registerEnemyBaseStructure
local makeImmortalEntity = chunkUtils.makeImmortalEntity
local processBases = baseProcessor.processBases
local mRandom = math.random
local getPlayerInventory = playerUtils.getPlayerInventory
local positionToChunkXY = mapUtils.positionToChunkXY
-- local references to global
local regionMap -- manages the chunks that make up the game world
local natives -- manages the enemy units, structures, and ai
local pendingChunks -- chunks that have yet to be processed by the mod
local world -- manages the player built structures and any other events in the world
-- hook functions
@@ -110,13 +88,10 @@ local function onIonCannonFired(event)
if (natives.points > AI_MAX_OVERFLOW_POINTS) then
natives.points = AI_MAX_OVERFLOW_POINTS
end
local chunk = getChunkByPosition(regionMap, event.position.x, event.position.y)
if chunk then
rallyUnits(chunk,
regionMap,
surface,
natives,
event.tick)
local chunkX, chunkY = positionToChunkXY(event.position)
local chunk = getChunkByPosition(regionMap, chunkX, chunkY)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
rallyUnits(chunk, regionMap, surface, natives, event.tick)
end
end
end
@@ -134,7 +109,6 @@ local function onLoad()
regionMap = global.regionMap
natives = global.natives
pendingChunks = global.pendingChunks
world = global.world
hookEvents()
end
@@ -158,9 +132,28 @@ local function rebuildRegionMap()
regionMap.processQueue = {}
regionMap.processIndex = 1
regionMap.scanIndex = 1
regionMap.chunkToHives = {}
regionMap.chunkToNests = {}
regionMap.chunkToWorms = {}
regionMap.chunkToRetreats = {}
regionMap.chunkToRallys = {}
regionMap.chunkToPlayerBase = {}
regionMap.chunkToResource = {}
-- preallocating memory to be used in code, making it fast by reducing garbage generated.
regionMap.neighbors = { nil, nil, nil, nil, nil, nil, nil, nil }
regionMap.cardinalNeighbors = { nil, nil, nil, nil }
regionMap.neighbors = { SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK }
regionMap.cardinalNeighbors = { SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK }
regionMap.chunkTiles = {}
regionMap.position = {x=0,
y=0}
@@ -242,7 +235,7 @@ end
local function onConfigChanged()
local upgraded
upgraded, natives, world = upgrade.attempt(natives, world)
upgraded, natives = upgrade.attempt(natives)
if upgraded and onModSettingsChange(nil) then
rebuildRegionMap()
end
@@ -271,8 +264,6 @@ local function onTick(event)
cleanSquads(natives)
regroupSquads(natives)
processWorld(surface, world, tick)
processPlayers(players, regionMap, surface, natives, tick)
if natives.useCustomAI then
@@ -288,7 +279,7 @@ local function onTick(event)
end
local function onBuild(event)
local entity = buildComplexEntity(event.created_entity, world)
local entity = event.created_entity
addRemovePlayerEntity(regionMap, entity, natives, true, false)
if natives.safeBuildings then
if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then
@@ -298,24 +289,20 @@ local function onBuild(event)
end
local function onMine(event)
mineComplexEntity(addRemovePlayerEntity(regionMap,
event.entity,
natives,
false,
false),
world)
addRemovePlayerEntity(regionMap, event.entity, natives, false, false)
end
local function onDeath(event)
local entity = event.entity
local surface = entity.surface
if (surface.index == 1) then
local entityPosition = entity.position
local chunkX, chunkY = positionToChunkXY(entityPosition)
if (entity.force.name == "enemy") then
if (entity.type == "unit") then
local entityPosition = entity.position
local deathChunk = getChunkByPosition(regionMap, entityPosition.x, entityPosition.y)
local deathChunk = getChunkByPosition(regionMap, chunkX, chunkY)
if deathChunk then
if (deathChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
-- drop death pheromone where unit died
deathScent(deathChunk)
@@ -324,18 +311,14 @@ local function onDeath(event)
retreatUnits(deathChunk,
entityPosition,
convertUnitGroupToSquad(natives,
entity.unit_group),
convertUnitGroupToSquad(natives, entity.unit_group),
regionMap,
surface,
natives,
tick)
if (mRandom() < natives.rallyThreshold) and not surface.peaceful_mode then
rallyUnits(deathChunk,
regionMap,
surface,
natives,
tick)
rallyUnits(deathChunk, regionMap, surface, natives, tick)
end
end
end
@@ -345,32 +328,24 @@ local function onDeath(event)
end
elseif (entity.force.name == "player") then
local creditNatives = false
local entityPosition = entity.position
if (event.force ~= nil) and (event.force.name == "enemy") then
creditNatives = true
local victoryChunk = getChunkByPosition(regionMap, entityPosition.x, entityPosition.y)
if victoryChunk then
local victoryChunk = getChunkByPosition(regionMap, chunkX, chunkY)
if (victoryChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
victoryScent(victoryChunk, entity.type)
end
end
if creditNatives and natives.safeBuildings and (natives.safeEntities[entity.type] or natives.safeEntityName[entity.name]) then
makeImmortalEntity(surface, entity)
else
mineComplexEntity(addRemovePlayerEntity(regionMap,
entity,
natives,
false,
creditNatives),
world,
true)
addRemovePlayerEntity(regionMap, entity, natives, false, creditNatives)
end
end
end
end
local function onEnemyBaseBuild(event)
local entity = event.entity
registerEnemyBaseStructure(regionMap, entity, nil)
registerEnemyBaseStructure(regionMap, event.entity, nil)
end
local function onSurfaceTileChange(event)
@@ -384,128 +359,30 @@ local function onInit()
global.regionMap = {}
global.pendingChunks = {}
global.natives = {}
global.world = {}
regionMap = global.regionMap
natives = global.natives
pendingChunks = global.pendingChunks
world = global.world
onConfigChanged()
hookEvents()
end
-- local function onBuildingRotated(event)
-- end
local function onCursorChange(event)
if settings.startup["rampant-enableBuildings"].value then
local player = game.players[event.player_index]
swapItemStack(getPlayerCursorStack(player),
"item-collector-base-rampant",
"item-collector-base-overlay-rampant")
local inventory = getPlayerInventory(player,
DEFINES_INVENTORY_PLAYER_QUICKBAR,
DEFINES_INVENTORY_GOD_QUICKBAR)
topOffHand(inventory,
player.cursor_stack,
"item-collector-base-rampant",
"item-collector-base-overlay-rampant")
inventory = getPlayerInventory(player,
DEFINES_INVENTORY_PLAYER_MAIN,
DEFINES_INVENTORY_GOD_MAIN)
topOffHand(inventory,
player.cursor_stack,
"item-collector-base-rampant",
"item-collector-base-overlay-rampant")
end
end
local function onPlayerDropped(event)
if settings.startup["rampant-enableBuildings"].value then
local item = event.entity
if item.valid then
swapItemStack(item.stack,
"item-collector-base-overlay-rampant",
"item-collector-base-rampant")
end
end
end
local function onMainInventoryChanged(event)
if settings.startup["rampant-enableBuildings"].value then
local player = game.players[event.player_index]
local inventory = getPlayerInventory(player,
DEFINES_INVENTORY_PLAYER_MAIN,
DEFINES_INVENTORY_GOD_MAIN)
swapItemInventory(inventory,
"item-collector-base-overlay-rampant",
"item-collector-base-rampant")
end
end
local function onQuickInventoryChanged(event)
if settings.startup["rampant-enableBuildings"].value then
local player = game.players[event.player_index]
local inventory = getPlayerInventory(player,
DEFINES_INVENTORY_PLAYER_QUICKBAR,
DEFINES_INVENTORY_GOD_QUICKBAR)
swapItemInventory(inventory,
"item-collector-base-overlay-rampant",
"item-collector-base-rampant")
topOffHand(inventory,
player.cursor_stack,
"item-collector-base-rampant",
"item-collector-base-overlay-rampant")
end
end
local function onScannedSector(event)
local radar = event.radar
if (radar.name == "item-collector-base-rampant") then
local count = #world.itemCollectorEvents
if (count <= ITEM_COLLECTOR_MAX_QUEUE_SIZE) then
world.itemCollectorEvents[count+1] = radar.unit_number
end
end
end
-- hooks
script.on_init(onInit)
script.on_load(onLoad)
script.on_event(defines.events.on_runtime_mod_setting_changed,
onModSettingsChange)
script.on_event(defines.events.on_runtime_mod_setting_changed, onModSettingsChange)
script.on_configuration_changed(onConfigChanged)
-- script.on_event(defines.events.on_player_rotated_entity,
-- onBuildingRotated)
script.on_event(defines.events.on_player_built_tile, onSurfaceTileChange)
script.on_event(defines.events.on_player_cursor_stack_changed,
onCursorChange)
script.on_event(defines.events.on_player_main_inventory_changed,
onMainInventoryChanged)
script.on_event(defines.events.on_player_quickbar_inventory_changed,
onQuickInventoryChanged)
script.on_event(defines.events.on_biter_base_built,
onEnemyBaseBuild)
script.on_event(defines.events.on_biter_base_built, onEnemyBaseBuild)
script.on_event({defines.events.on_player_mined_entity,
defines.events.on_robot_mined_entity},
onMine)
defines.events.on_robot_mined_entity}, onMine)
script.on_event({defines.events.on_built_entity,
defines.events.on_robot_built_entity},
onBuild)
script.on_event(defines.events.on_player_dropped_item,
onPlayerDropped)
defines.events.on_robot_built_entity}, onBuild)
script.on_event(defines.events.on_sector_scanned,
onScannedSector)
script.on_event(defines.events.on_entity_died, onDeath)
script.on_event(defines.events.on_tick, onTick)
script.on_event(defines.events.on_chunk_generated, onChunkGenerated)

View File

@@ -11,8 +11,3 @@ require("prototypes/enemies/UnitFireSpitters")
require("prototypes/enemies/UnitTendril")
if settings.startup["rampant-enableBuildings"].value then
require("prototypes/buildings/ItemCollector")
end

View File

@@ -1,7 +1,7 @@
{
"name" : "Rampant",
"factorio_version" : "0.15",
"version" : "0.15.22",
"version" : "0.15.23",
"title" : "Rampant",
"author" : "Veden",
"homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445",

View File

@@ -4,8 +4,9 @@ local aiAttackWave = {}
local constants = require("Constants")
local mapUtils = require("MapUtils")
local chunkUtils = require("ChunkUtils")
local unitGroupUtils = require("UnitGroupUtils")
local neighborUtils = require("NeighborUtils")
local movementUtils = require("MovementUtils")
package.path = "../?.lua;" .. package.path
local config = require("config")
@@ -18,19 +19,21 @@ local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local AI_SQUAD_COST = constants.AI_SQUAD_COST
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
local RALLY_TRIGGERED = constants.RALLY_TRIGGERED
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
local PASSABLE = constants.PASSABLE
local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS
local CHUNK_SIZE = constants.CHUNK_SIZE
local RALLY_CRY_DISTANCE = constants.RALLY_CRY_DISTANCE
local DEFINES_COMMAND_GROUP = defines.command.group
local DEFINES_DISTRACTION_NONE = defines.distraction.none
local NEST_COUNT = constants.NEST_COUNT
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
local PASSABLE = constants.PASSABLE
-- imported functions
@@ -38,9 +41,13 @@ local mRandom = math.random
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
local getNestCount = chunkUtils.getNestCount
local getRallyTick = chunkUtils.getRallyTick
local setRallyTick = chunkUtils.setRallyTick
local getNeighborChunks = mapUtils.getNeighborChunks
local getChunkByIndex = mapUtils.getChunkByIndex
local scoreNeighborsForFormation = neighborUtils.scoreNeighborsForFormation
local getChunkByPosition = mapUtils.getChunkByPosition
local scoreNeighborsForFormation = movementUtils.scoreNeighborsForFormation
local createSquad = unitGroupUtils.createSquad
local attackWaveScaling = config.attackWaveScaling
@@ -74,21 +81,21 @@ local function scoreUnitGroupLocation(neighborChunk)
return neighborChunk[PLAYER_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
end
local function validUnitGroupLocation(neighborChunk)
return (neighborChunk[PASSABLE] == CHUNK_ALL_DIRECTIONS) and (neighborChunk[NEST_COUNT] == 0)
local function validUnitGroupLocation(regionMap, neighborChunk)
return neighborChunk[PASSABLE] == CHUNK_ALL_DIRECTIONS and (getNestCount(regionMap, neighborChunk) == 0)
end
function aiAttackWave.rallyUnits(chunk, regionMap, surface, natives, tick)
if ((tick - chunk[RALLY_TRIGGERED] > INTERVAL_LOGIC) and (natives.points >= AI_VENGENCE_SQUAD_COST) and
if ((tick - getRallyTick(regionMap, chunk) > INTERVAL_LOGIC) and (natives.points >= AI_VENGENCE_SQUAD_COST) and
(#natives.squads < natives.maxSquads)) then
chunk[RALLY_TRIGGERED] = tick
local cX = chunk.cX
local cY = chunk.cY
for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE do
for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE do
setRallyTick(regionMap, chunk, tick)
local cX = chunk.x
local cY = chunk.y
for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE, 32 do
for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE, 32 do
if (x ~= cX) and (y ~= cY) then
local rallyChunk = getChunkByIndex(regionMap, x, y)
if rallyChunk and (rallyChunk[NEST_COUNT] ~= 0) then
local rallyChunk = getChunkByPosition(regionMap, x, y)
if (rallyChunk ~= SENTINEL_IMPASSABLE_CHUNK) and (getNestCount(regionMap, rallyChunk) > 0) then
aiAttackWave.formSquads(regionMap, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST)
if (natives.points < AI_VENGENCE_SQUAD_COST) and (#natives.squads < natives.maxSquads) then
return
@@ -105,16 +112,17 @@ function aiAttackWave.formSquads(regionMap, surface, natives, chunk, cost)
if valid and (mRandom() < natives.formSquadThreshold) then
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(regionMap, chunk.cX, chunk.cY),
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(regionMap, chunk.x, chunk.y),
validUnitGroupLocation,
scoreUnitGroupLocation)
if squadPath then
local squadPosition = surface.find_non_colliding_position("biter-spawner-hive",
scoreUnitGroupLocation,
regionMap)
if (squadPath ~= SENTINEL_IMPASSABLE_CHUNK) then
local squadPosition = surface.find_non_colliding_position("biter-spawner-hive-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
regionMap.position,
0.98),
32,
CHUNK_SIZE,
4)
if squadPosition then
local squad = createSquad(squadPosition, surface, natives)

View File

@@ -3,21 +3,28 @@ local aiPredicates = {}
-- imports
local constants = require("Constants")
local nocturnalUtils = require("NocturnalUtils")
-- constants
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
-- imported functions
local canAttackNocturnal = nocturnalUtils.canAttack
-- module code
function aiPredicates.canAttack(natives, surface)
return (((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface))
return (((natives.state == AI_STATE_AGGRESSIVE) or aiPredicates.canAttackDark(natives, surface))
and not surface.peaceful_mode and (#natives.squads < natives.maxSquads))
end
function aiPredicates.isDark(surface)
return surface.darkness > 0.65
end
function aiPredicates.canAttackDark(natives, surface)
return aiPredicates.isDark(surface) and natives.state == AI_STATE_NOCTURNAL
end
return aiPredicates

View File

@@ -1,118 +0,0 @@
local baseRegisterUtils = {}
-- imports
local constants = require("Constants")
local entityUtils = require("EntityUtils")
-- constants
local NEST_COUNT = constants.NEST_COUNT
local WORM_COUNT = constants.WORM_COUNT
local NEST_BASE = constants.NEST_BASE
local WORM_BASE = constants.WORM_BASE
-- imported functions
local getEntityOverlapChunks = entityUtils.getEntityOverlapChunks
-- module code
function baseRegisterUtils.addEnemyStructureToChunk(chunk, entity, base)
local indexChunk
local indexBase
local countChunk
if (entity.type == "unit-spawner") then
indexChunk = chunk[NEST_BASE]
if base then
if base.hives[entity.unit_number] then
indexBase = base.hives
else
indexBase = base.nests
end
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 baseRegisterUtils.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
if base.hives[entity.unit_number] then
indexBase = base.hives
else
indexBase = base.nests
end
elseif (entity.type == "turret") then
indexBase = base.worms
end
indexBase[entity.unit_number] = nil
end
chunk[countChunk] = chunk[countChunk] - 1
end
function baseRegisterUtils.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
baseRegisterUtils.addEnemyStructureToChunk(leftTop, entity, base)
end
if rightTop then
baseRegisterUtils.addEnemyStructureToChunk(rightTop, entity, base)
end
if leftBottom then
baseRegisterUtils.addEnemyStructureToChunk(leftBottom, entity, base)
end
if rightBottom then
baseRegisterUtils.addEnemyStructureToChunk(rightBottom, entity, base)
end
end
end
function baseRegisterUtils.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
baseRegisterUtils.removeEnemyStructureFromChunk(leftTop, entity)
end
if rightTop then
baseRegisterUtils.removeEnemyStructureFromChunk(rightTop, entity)
end
if leftBottom then
baseRegisterUtils.removeEnemyStructureFromChunk(leftBottom, entity)
end
if rightBottom then
baseRegisterUtils.removeEnemyStructureFromChunk(rightBottom, entity)
end
end
end
return baseRegisterUtils

View File

@@ -5,7 +5,7 @@ local baseUtils = {}
local mathUtils = require("MathUtils")
local constants = require("Constants")
local tendrilUtils = require("TendrilUtils")
-- local tendrilUtils = require("TendrilUtils")
local nestUtils = require("NestUtils")
@@ -26,7 +26,7 @@ local buildHive = nestUtils.buildHive
local mFloor = math.floor
local buildTendril = tendrilUtils.buildTendril
-- local buildTendril = tendrilUtils.buildTendril
local mRandom = math.random
@@ -68,7 +68,7 @@ function baseUtils.createBase(regionMap, natives, position, surface, tick)
if not buildHive(regionMap, base, surface) then
return nil
end
buildTendril(regionMap, natives, base, surface, tick)
-- buildTendril(regionMap, natives, base, surface, tick)
bases[#bases+1] = base
return base
end

View File

@@ -1,78 +0,0 @@
local buildUtils = {}
-- module code
function buildUtils.buildComplexEntity(entity, world)
if (entity.name == "item-collector-base-overlay-rampant") or (entity.name == "item-collector-base-rampant") then
local surface = entity.surface
local x = entity.position.x
local y = entity.position.y
local chest
local position = entity.position
local force = entity.force
if (entity.name == "item-collector-base-overlay-rampant") then
entity.destroy()
chest = surface.create_entity({name = "item-collector-chest-rampant",
position = position,
force = force})
entity = surface.create_entity({name="item-collector-base-rampant",
position = position,
force = force})
else
local ghosts = surface.find_entities_filtered({name="entity-ghost",
area={{x=x-1,
y=y-1},
{x=x+1,
y=y+1}},
limit=1})
if (#ghosts > 0) then
local ghost = ghosts[1]
if (ghost.ghost_name == "item-collector-chest-rampant") then
local conflicts
conflicts, chest = ghost.revive()
end
else
chest = surface.create_entity({name = "item-collector-chest-rampant",
position = position,
force = force})
end
end
if chest and chest.valid then
local pair = { chest, entity }
world.itemCollectorLookup[chest.unit_number] = pair
world.itemCollectorLookup[entity.unit_number] = pair
entity.backer_name = ""
end
end
return entity
end
function buildUtils.mineComplexEntity(entity, world, destroyed)
if (entity.name == "item-collector-chest-rampant") or (entity.name == "item-collector-base-rampant") then
local pair = world.itemCollectorLookup[entity.unit_number]
if pair then
local chest = pair[1]
local dish = pair[2]
if chest and chest.valid then
world.itemCollectorLookup[chest.unit_number] = nil
if destroyed and (entity == chest) then
chest.die()
elseif (entity ~= chest) then
chest.destroy()
end
end
if dish and dish.valid then
world.itemCollectorLookup[dish.unit_number] = nil
if destroyed and (entity ~= dish) then
dish.die()
elseif (entity ~= dish) then
dish.destroy()
end
end
end
end
return entity
end
return buildUtils

View File

@@ -9,6 +9,8 @@ local constants = require("Constants")
local CHUNK_SIZE = constants.CHUNK_SIZE
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local remakeChunk = chunkUtils.remakeChunk
@@ -39,26 +41,31 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin
local x = topLeft.x
local y = topLeft.y
local chunk = createChunk(x, y)
areaBoundingBox[1] = chunk
offset[1] = x + CHUNK_SIZE
offset[2] = y + CHUNK_SIZE
local chunkX = chunk.cX
if regionMap[chunkX] == nil then
regionMap[chunkX] = {}
end
regionMap[chunkX][chunk.cY] = chunk
checkChunkPassability(chunkTiles, chunk, surface)
if vanillaAI then
registerChunkEnemies(chunk, surface, query)
else
remakeChunk(regionMap, chunk, surface, natives, tick, query)
chunk = checkChunkPassability(chunkTiles, chunk, surface)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
areaBoundingBox[1] = chunk
offset[1] = x + CHUNK_SIZE
offset[2] = y + CHUNK_SIZE
if vanillaAI then
registerChunkEnemies(regionMap, chunk, surface, query)
else
remakeChunk(regionMap, chunk, surface, natives, tick, query)
end
scoreChunk(regionMap, chunk, surface, natives, query)
local chunkX = chunk.x
if regionMap[chunkX] == nil then
regionMap[chunkX] = {}
end
regionMap[chunkX][chunk.y] = chunk
processQueue[#processQueue+1] = chunk
end
scoreChunk(chunk, surface, natives, query)
processQueue[#processQueue+1] = chunk
end
end

View File

@@ -3,31 +3,25 @@ local chunkUtils = {}
-- imports
local constants = require("Constants")
local baseUtils = require("BaseUtils")
local baseRegisterUtils = require("BaseRegisterUtils")
local mapUtils = require("MapUtils")
-- constants
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
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local RESOURCE_PHEROMONE = constants.RESOURCE_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
local RESOURCE_GENERATOR = constants.RESOURCE_GENERATOR
local CHUNK_NORTH_SOUTH = constants.CHUNK_NORTH_SOUTH
local CHUNK_EAST_WEST = constants.CHUNK_EAST_WEST
local PASSABLE = constants.PASSABLE
local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS
local CHUNK_IMPASSABLE = constants.CHUNK_IMPASSABLE
@@ -35,14 +29,13 @@ local CHUNK_TICK = constants.CHUNK_TICK
local PATH_RATING = constants.PATH_RATING
local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
local RALLY_TRIGGERED = constants.RALLY_TRIGGERED
local PASSABLE = constants.PASSABLE
-- imported functions
local findNearbyBase = baseUtils.findNearbyBase
local createBase = baseUtils.createBase
local addEnemyStructureToChunk = baseRegisterUtils.addEnemyStructureToChunk
local getChunkByPosition = mapUtils.getChunkByPosition
local mFloor = math.floor
-- module code
@@ -109,6 +102,126 @@ local function spotCheck(x, y, get_tile)
return valid
end
local function addEnemyStructureToChunk(regionMap, chunk, entity, base)
local lookup
if (entity.type == "unit-spawner") then
lookup = regionMap.chunkToNests
elseif (entity.type == "turret") then
lookup = regionMap.chunkToWorms
else
return
end
local entityCollection = lookup[chunk]
if not entityCollection then
entityCollection = {}
lookup[chunk] = entityCollection
end
entityCollection[#entityCollection+1] = entity
-- if base then
-- local baseCollection = regionMap.chunkToBases[chunk]
-- if not baseCollection then
-- baseCollection = {}
-- regionMap.chunkToBases[chunk] = baseCollection
-- end
-- baseCollection[base.id] = chunk
-- end
end
local function removeEnemyStructureFromChunk(regionMap, chunk, entity)
local lookup
if (entity.type == "unit-spawner") then
lookup = regionMap.chunkToNests
elseif (entity.type == "turret") then
lookup = regionMap.chunkToWorms
else
return
end
-- local base = indexChunk[entity.unit_number]
-- local indexBase
-- if base then
-- if (entity.type == "unit-spawner") then
-- if base.hives[entity.unit_number] then
-- indexBase = base.hives
-- else
-- indexBase = base.nests
-- end
-- elseif (entity.type == "turret") then
-- indexBase = base.worms
-- end
-- indexBase[entity.unit_number] = nil
-- end
local collection = lookup[chunk]
if collection then
for i=1,#collection do
local e = collection[i]
if e.valid and (e == entity) then
table.remove(collection, i)
break
end
end
end
end
local function getEntityOverlapChunks(regionMap, entity)
local boundingBox = entity.prototype.selection_box or entity.prototype.collision_box;
local leftTopChunk = SENTINEL_IMPASSABLE_CHUNK
local rightTopChunk = SENTINEL_IMPASSABLE_CHUNK
local leftBottomChunk = SENTINEL_IMPASSABLE_CHUNK
local rightBottomChunk = SENTINEL_IMPASSABLE_CHUNK
if (boundingBox ~= nil) then
local center = entity.position
local topXOffset
local topYOffset
local bottomXOffset
local bottomYOffset
if (entity.direction == DEFINES_DIRECTION_EAST) then
topXOffset = boundingBox.left_top.y
topYOffset = boundingBox.left_top.x
bottomXOffset = boundingBox.right_bottom.y
bottomYOffset = boundingBox.right_bottom.x
else
topXOffset = boundingBox.left_top.x
topYOffset = boundingBox.left_top.y
bottomXOffset = boundingBox.right_bottom.x
bottomYOffset = boundingBox.right_bottom.y
end
local leftTopChunkX = mFloor(center.x + topXOffset)
local leftTopChunkY = mFloor(center.y + topYOffset)
-- used to force things on chunk boundary to not spill over 0.0001
local rightTopChunkX = mFloor(center.x + bottomXOffset - 0.0001)
local rightTopChunkY = leftTopChunkY
-- used to force things on chunk boundary to not spill over 0.0001
local leftBottomChunkX = leftTopChunkX
local leftBottomChunkY = mFloor(center.y + bottomYOffset - 0.0001)
local rightBottomChunkX = rightTopChunkX
local rightBottomChunkY = leftBottomChunkY
leftTopChunk = getChunkByPosition(regionMap, leftTopChunkX, leftTopChunkY)
if (leftTopChunkX ~= rightTopChunkX) then
rightTopChunk = getChunkByPosition(regionMap, rightTopChunkX, rightTopChunkY)
end
if (leftTopChunkY ~= leftBottomChunkY) then
leftBottomChunk = getChunkByPosition(regionMap, leftBottomChunkX, leftBottomChunkY)
end
if (leftTopChunkX ~= rightBottomChunkX) and (leftTopChunkY ~= rightBottomChunkY) then
rightBottomChunk = getChunkByPosition(regionMap, rightBottomChunkX, rightBottomChunkY)
end
end
return leftTopChunk, rightTopChunk, leftBottomChunk, rightBottomChunk
end
-- external functions
function chunkUtils.checkChunkPassability(chunkTiles, chunk, surface)
local x = chunk.x
local y = chunk.y
@@ -139,12 +252,17 @@ function chunkUtils.checkChunkPassability(chunkTiles, chunk, surface)
pass = CHUNK_NORTH_SOUTH
else
pass = CHUNK_IMPASSABLE
end
end
end
else
chunk[PATH_RATING] = 1
end
chunk[PASSABLE] = pass
if (pass == CHUNK_IMPASSABLE) then
return SENTINEL_IMPASSABLE_CHUNK
else
chunk[PASSABLE] = pass
return chunk
end
end
function chunkUtils.remakeChunk(regionMap, chunk, surface, natives, tick, tempQuery)
@@ -155,7 +273,7 @@ function chunkUtils.remakeChunk(regionMap, chunk, surface, natives, tick, tempQu
for f=1, #enemies do
local enemy = enemies[f]
local entityType = enemies[f].type
if not ((enemy.name == "small-tendril-biter-rampant") or (enemy.name == "biter-spawner-hive")) then
if not ((enemy.name == "small-tendril-biter-rampant") or (enemy.name == "biter-spawner-hive-rampant")) then
if (entityType == "unit-spawner") then
points = points + 3
elseif (entityType == "turret") then
@@ -166,13 +284,13 @@ function chunkUtils.remakeChunk(regionMap, chunk, surface, natives, tick, tempQu
enemy.destroy()
end
end
local foundBase = findNearbyBase(natives, chunk) or createBase(regionMap, natives, chunk, surface, tick)
if foundBase then
foundBase.upgradePoints = foundBase.upgradePoints + points
end
-- local foundBase = findNearbyBase(natives, chunk) or createBase(regionMap, natives, chunk, surface, tick)
-- if foundBase then
-- foundBase.upgradePoints = foundBase.upgradePoints + points
-- end
end
function chunkUtils.registerChunkEnemies(chunk, surface, tempQuery)
function chunkUtils.registerChunkEnemies(regionMap, chunk, surface, tempQuery)
tempQuery.force = "enemy"
local enemies = surface.find_entities_filtered(tempQuery)
@@ -180,15 +298,61 @@ function chunkUtils.registerChunkEnemies(chunk, surface, tempQuery)
local enemy = enemies[i]
local enemyType = enemy.type
if (enemyType == "unit-spawner") or (enemyType == "turret") then
addEnemyStructureToChunk(chunk, enemy, nil)
addEnemyStructureToChunk(regionMap, chunk, enemy, nil)
end
end
end
function chunkUtils.scoreChunk(chunk, surface, natives, tempQuery)
function chunkUtils.getNestCount(regionMap, chunk)
local nests = regionMap.chunkToNests[chunk]
return (nests and #nests) or 0
end
function chunkUtils.getWormCount(regionMap, chunk)
local worms = regionMap.chunkToWorms[chunk]
return (worms and #worms) or 0
end
function chunkUtils.getRetreatTick(regionMap, chunk)
return regionMap.chunkToRetreats[chunk] or 0
end
function chunkUtils.getRallyTick(regionMap, chunk)
return regionMap.chunkToRallys[chunk] or 0
end
function chunkUtils.setRallyTick(regionMap, chunk, tick)
regionMap.chunkToRallys[chunk] = tick
end
function chunkUtils.setRetreatTick(regionMap, chunk, tick)
regionMap.chunkToRetreats[chunk] = tick
end
function chunkUtils.setResourceGenerator(regionMap, chunk, playerGenerator)
regionMap.chunkToResource[chunk] = playerGenerator
end
function chunkUtils.getResourceGenerator(regionMap, chunk)
return regionMap.chunkToResource[chunk] or 0
end
function chunkUtils.getPlayerBaseGenerator(regionMap, chunk)
return regionMap.chunkToPlayerBase[chunk] or 0
end
function chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerGenerator)
regionMap.chunkToPlayerBase[chunk] = playerGenerator
end
function chunkUtils.addPlayerBaseGenerator(regionMap, chunk, playerGenerator)
regionMap.chunkToPlayerBase[chunk] = regionMap.chunkToPlayerBase[chunk] + playerGenerator
end
function chunkUtils.scoreChunk(regionMap, chunk, surface, natives, tempQuery)
tempQuery.force = nil
tempQuery.type = "resource"
chunk[RESOURCE_GENERATOR] = surface.count_entities_filtered(tempQuery) * 0.001
chunkUtils.setResourceGenerator(regionMap, chunk, surface.count_entities_filtered(tempQuery) * 0.001)
tempQuery.type = nil
tempQuery.force = "player"
@@ -207,36 +371,27 @@ function chunkUtils.scoreChunk(chunk, surface, natives, tempQuery)
end
local entityScore = BUILDING_PHEROMONES[entityType]
if (entityScore ~= nil) then
if entityScore then
playerObjects = playerObjects + entityScore
end
end
chunk[PLAYER_BASE_GENERATOR] = playerObjects
chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerObjects)
end
function chunkUtils.createChunk(topX, topY)
local chunk = {
x = topX,
y = topY,
cX = topX * 0.03125,
cY = topY * 0.03125
y = topY
}
chunk[MOVEMENT_PHEROMONE] = 0
chunk[BASE_PHEROMONE] = 0
chunk[PLAYER_PHEROMONE] = 0
chunk[RESOURCE_PHEROMONE] = 0
chunk[PLAYER_BASE_GENERATOR] = 0
chunk[RESOURCE_GENERATOR] = 0
chunk[PASSABLE] = 0
chunk[CHUNK_TICK] = 0
chunk[RETREAT_TRIGGERED] = 0
chunk[RALLY_TRIGGERED] = 0
chunk[NEST_BASE] = {}
chunk[WORM_BASE] = {}
chunk[NEST_COUNT] = 0
chunk[WORM_COUNT] = 0
chunk[PATH_RATING] = 0
return chunk
end
@@ -250,4 +405,109 @@ function chunkUtils.colorChunk(x, y, tileType, surface)
surface.set_tiles(tiles, false)
end
function chunkUtils.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 ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, leftTop, entity, base)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, rightTop, entity, base)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, leftBottom, entity, base)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, rightBottom, entity, base)
end
end
end
function chunkUtils.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 ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, leftTop, entity)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, rightTop, entity)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, leftBottom, entity)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, rightBottom, entity)
end
end
end
function chunkUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject, creditNatives)
local leftTop, rightTop, leftBottom, rightBottom
local entityValue
if (BUILDING_PHEROMONES[entity.type] ~= nil) and (entity.force.name == "player") then
entityValue = BUILDING_PHEROMONES[entity.type]
leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
if not addObject then
if creditNatives then
natives.points = natives.points + entityValue
end
entityValue = -entityValue
end
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, leftTop, entityValue)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, rightTop, entityValue)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, leftBottom, entityValue)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, rightBottom, entityValue)
end
end
return entity
end
function chunkUtils.makeImmortalEntity(surface, entity)
local repairPosition = entity.position
local repairName = entity.name
local repairForce = entity.force
local repairDirection = entity.direction
local wires
if (entity.type == "electric-pole") then
wires = entity.neighbours
end
entity.destroy()
local newEntity = surface.create_entity({position=repairPosition,
name=repairName,
direction=repairDirection,
force=repairForce})
if wires then
for connectType,neighbourGroup in pairs(wires) do
if connectType == "copper" then
for _,v in pairs(neighbourGroup) do
newEntity.connect_neighbour(v);
end
elseif connectType == "red" then
for _,v in pairs(neighbourGroup) do
newEntity.connect_neighbour({wire = DEFINES_WIRE_TYPE_RED, target_entity = v});
end
elseif connectType == "green" then
for _,v in pairs(neighbourGroup) do
newEntity.connect_neighbour({wire = DEFINES_WIRE_TYPE_GREEN, target_entity = v});
end
end
end
end
newEntity.destructible = false
end
return chunkUtils

View File

@@ -15,10 +15,11 @@ constants.VERSION_25 = 25
constants.VERSION_26 = 26
constants.VERSION_27 = 27
constants.VERSION_28 = 28
constants.VERSION_32 = 32
constants.VERSION_33 = 33
-- misc
constants.CHUNK_SIZE_DIVIDER = 0.03125
constants.MAGIC_MAXIMUM_NUMBER = 1e99 -- used in loops trying to find the lowest/highest score
constants.MAGIC_MAXIMUM_BASE_NUMBER = 100000000
constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL = 10000
@@ -28,7 +29,7 @@ constants.SCAN_QUEUE_SIZE = 5
constants.ITEM_COLLECTOR_QUEUE_SIZE = 6
constants.BASE_QUEUE_SIZE = 1
constants.SQUAD_QUEUE_SIZE = 2
constants.PROCESS_PLAYER_BOUND = 4
constants.PROCESS_PLAYER_BOUND = 128
constants.ITEM_COLLECTOR_MAX_QUEUE_SIZE = 20
@@ -42,6 +43,18 @@ constants.PLAYER_PHEROMONE_MULTIPLER = 100
constants.DEV_CUSTOM_AI = false
-- mask
constants.MASK_PASSABLE = 3
constants.MASK_PASSABLE_SIZE = 2
constants.MASK_NEST_COUNT = 511
constants.MASK_NEST_COUNT_SIZE = 9
constants.MASK_WORM_COUNT = 511
constants.MASK_WORM_COUNT_SIZE = 9
constants.MASK_PASSABLE_AND_NEST_COUNT = 2047
constants.MASK_PASSABLE_AND_NEST_COUNT_SIZE = 11
-- item collector
constants.ITEM_COLLECTOR_DISTANCE = 50
@@ -127,19 +140,11 @@ constants.BASE_PHEROMONE = 2
constants.PLAYER_PHEROMONE = 3
constants.RESOURCE_PHEROMONE = 4
constants.PLAYER_BASE_GENERATOR = 5
constants.RESOURCE_GENERATOR = 6
constants.PASSABLE = 5
constants.PASSABLE = 7
constants.CHUNK_TICK = 6
constants.CHUNK_TICK = 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
constants.PATH_RATING = 15
constants.PATH_RATING = 7
-- Squad status
@@ -155,7 +160,7 @@ constants.RETREAT_GRAB_RADIUS = 24
constants.BASE_RALLY_CHANCE = 0.02
constants.BONUS_RALLY_CHANCE = 0.06
constants.RALLY_CRY_DISTANCE = 3
constants.RALLY_CRY_DISTANCE = 96
constants.GROUP_MERGE_DISTANCE = 28
@@ -211,6 +216,20 @@ constants.UNIT_GROUP_MAX_SPEED_UP = 1.1
constants.UNIT_GROUP_MAX_SLOWDOWN = 1.0
constants.UNIT_GROUP_SLOWDOWN_FACTOR = 0.9
-- sentinels
constants.SENTINEL_IMPASSABLE_CHUNK = {}
constants.SENTINEL_IMPASSABLE_CHUNK[constants.MOVEMENT_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT
constants.SENTINEL_IMPASSABLE_CHUNK[constants.BASE_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT
constants.SENTINEL_IMPASSABLE_CHUNK[constants.PLAYER_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT
constants.SENTINEL_IMPASSABLE_CHUNK[constants.RESOURCE_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT
constants.SENTINEL_IMPASSABLE_CHUNK[constants.PASSABLE] = constants.CHUNK_IMPASSABLE
constants.SENTINEL_IMPASSABLE_CHUNK[constants.CHUNK_TICK] = 0
constants.SENTINEL_IMPASSABLE_CHUNK[constants.PATH_RATING] = 0
constants.SENTINEL_IMPASSABLE_CHUNK.x = 0
constants.SENTINEL_IMPASSABLE_CHUNK.y = 0
return constants
--[[

View File

@@ -1,147 +0,0 @@
local entityUtils = {}
-- imports
local mapUtils = require("MapUtils")
local constants = require("Constants")
-- constants
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
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
-- imported functions
local getChunkByIndex = mapUtils.getChunkByIndex
local mFloor = math.floor
-- module code
function entityUtils.getEntityOverlapChunks(regionMap, entity)
local boundingBox = entity.prototype.selection_box or entity.prototype.collision_box;
local leftTopChunk
local rightTopChunk
local leftBottomChunk
local rightBottomChunk
if (boundingBox ~= nil) then
local center = entity.position
local topXOffset
local topYOffset
local bottomXOffset
local bottomYOffset
if (entity.direction == DEFINES_DIRECTION_EAST) then
topXOffset = boundingBox.left_top.y
topYOffset = boundingBox.left_top.x
bottomXOffset = boundingBox.right_bottom.y
bottomYOffset = boundingBox.right_bottom.x
else
topXOffset = boundingBox.left_top.x
topYOffset = boundingBox.left_top.y
bottomXOffset = boundingBox.right_bottom.x
bottomYOffset = boundingBox.right_bottom.y
end
local leftTopChunkX = mFloor((center.x + topXOffset) * 0.03125)
local leftTopChunkY = mFloor((center.y + topYOffset) * 0.03125)
-- used to force things on chunk boundary to not spill over 0.0001
local rightTopChunkX = mFloor((center.x + bottomXOffset - 0.0001) * 0.03125)
local rightTopChunkY = leftTopChunkY
-- used to force things on chunk boundary to not spill over 0.0001
local leftBottomChunkX = leftTopChunkX
local leftBottomChunkY = mFloor((center.y + bottomYOffset - 0.0001) * 0.03125)
local rightBottomChunkX = rightTopChunkX
local rightBottomChunkY = leftBottomChunkY
leftTopChunk = getChunkByIndex(regionMap, leftTopChunkX, leftTopChunkY)
if (leftTopChunkX ~= rightTopChunkX) then
rightTopChunk = getChunkByIndex(regionMap, rightTopChunkX, rightTopChunkY)
end
if (leftTopChunkY ~= leftBottomChunkY) then
leftBottomChunk = getChunkByIndex(regionMap, leftBottomChunkX, leftBottomChunkY)
end
if (leftTopChunkX ~= rightBottomChunkX) and (leftTopChunkY ~= rightBottomChunkY) then
rightBottomChunk = getChunkByIndex(regionMap, rightBottomChunkX, rightBottomChunkY)
end
end
return leftTopChunk, rightTopChunk, leftBottomChunk, rightBottomChunk
end
function entityUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject, creditNatives)
local leftTop, rightTop, leftBottom, rightBottom
local entityValue
if (BUILDING_PHEROMONES[entity.type] ~= nil) and (entity.force.name == "player") then
entityValue = BUILDING_PHEROMONES[entity.type]
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 then
leftTop[PLAYER_BASE_GENERATOR] = leftTop[PLAYER_BASE_GENERATOR] + entityValue
end
if rightTop then
rightTop[PLAYER_BASE_GENERATOR] = rightTop[PLAYER_BASE_GENERATOR] + entityValue
end
if leftBottom then
leftBottom[PLAYER_BASE_GENERATOR] = leftBottom[PLAYER_BASE_GENERATOR] + entityValue
end
if rightBottom then
rightBottom[PLAYER_BASE_GENERATOR] = rightBottom[PLAYER_BASE_GENERATOR] + entityValue
end
end
return entity
end
function entityUtils.makeImmortalEntity(surface, entity)
local repairPosition = entity.position
local repairName = entity.name
local repairForce = entity.force
local repairDirection = entity.direction
local wires
if (entity.type == "electric-pole") then
wires = entity.neighbours
end
entity.destroy()
local newEntity = surface.create_entity({position=repairPosition,
name=repairName,
direction=repairDirection,
force=repairForce})
if wires then
for connectType,neighbourGroup in pairs(wires) do
if connectType == "copper" then
for _,v in pairs(neighbourGroup) do
newEntity.connect_neighbour(v);
end
elseif connectType == "red" then
for _,v in pairs(neighbourGroup) do
newEntity.connect_neighbour({wire = DEFINES_WIRE_TYPE_RED, target_entity = v});
end
elseif connectType == "green" then
for _,v in pairs(neighbourGroup) do
newEntity.connect_neighbour({wire = DEFINES_WIRE_TYPE_GREEN, target_entity = v});
end
end
end
end
newEntity.destructible = false
end
return entityUtils

View File

@@ -1,46 +0,0 @@
local inventoryUtils = {}
-- imported functions
local mMin = math.min
-- modules code
function inventoryUtils.swapItemInventory(inventory, src, dst)
if inventory and inventory.valid then
local itemCount = inventory.get_item_count(src)
if (itemCount > 0) then
inventory.remove({name = src, count = itemCount})
inventory.insert({name = dst, count = itemCount})
end
end
end
function inventoryUtils.topOffHand(inventory, handStack, src, dst)
if inventory and inventory.valid then
local itemCount = inventory.get_item_count(src)
if (itemCount > 0) then
if handStack and handStack.valid and handStack.valid_for_read and (handStack.prototype.name == dst) then
local remaining = mMin(itemCount, handStack.prototype.stack_size - handStack.count)
if (remaining > 0) then
local stack = { name = src, count = remaining + handStack.count }
if (handStack.can_set_stack(stack)) and handStack.set_stack(stack) then
inventory.remove({name = src, count = remaining})
end
end
end
end
end
end
function inventoryUtils.swapItemStack(stack, src, dst)
if stack and stack.valid_for_read and (stack.name == src) then
local item = { name = dst, count = stack.count }
if stack.can_set_stack(item) then
stack.set_stack(item)
end
end
end
return inventoryUtils

View File

@@ -3,14 +3,13 @@ local mapProcessor = {}
-- imports
local unitGroupUtils = require("UnitGroupUtils")
local pheromoneUtils = require("PheromoneUtils")
local aiAttackWave = require("AIAttackWave")
local aiPredicates = require("AIPredicates")
local constants = require("Constants")
local mapUtils = require("MapUtils")
local playerUtils = require("PlayerUtils")
local chunkUtils = require("ChunkUtils")
local mathUtils = require("MathUtils")
-- constants
@@ -26,30 +25,33 @@ local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
local CHUNK_TICK = constants.CHUNK_TICK
local NEST_COUNT = constants.NEST_COUNT
local WORM_COUNT = constants.WORM_COUNT
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
local AI_SQUAD_COST = constants.AI_SQUAD_COST
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local RESOURCE_GENERATOR = constants.RESOURCE_GENERATOR
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
-- imported functions
local scents = pheromoneUtils.scents
local processPheromone = pheromoneUtils.processPheromone
local playerScent = pheromoneUtils.playerScent
local formSquads = aiAttackWave.formSquads
local getChunkByIndex = mapUtils.getChunkByIndex
local getChunkByPosition = mapUtils.getChunkByPosition
local positionToChunkXY = mapUtils.positionToChunkXY
local recycleBiters = unitGroupUtils.recycleBiters
local playerScent = pheromoneUtils.playerScent
local validPlayer = playerUtils.validPlayer
local setResourceGenerator = chunkUtils.setResourceGenerator
local setPlayerBaseGenerator = chunkUtils.setPlayerBaseGenerator
local getNestCount = chunkUtils.getNestCount
local getWormCount = chunkUtils.getWormCount
local canAttack = aiPredicates.canAttack
@@ -57,8 +59,6 @@ local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed
local mMin = math.min
local validPlayer = playerUtils.validPlayer
local mRandom = math.random
-- module code
@@ -105,12 +105,12 @@ function mapProcessor.processMap(regionMap, surface, natives, tick)
processPheromone(regionMap, chunk)
if squads and (chunk[NEST_COUNT] ~= 0) then
if squads and (getNestCount(regionMap, chunk) > 0) then
formSquads(regionMap, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) and (#natives.squads < natives.maxSquads)
end
scents(chunk)
scents(regionMap, chunk)
end
end
@@ -141,10 +141,10 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if validPlayer(player) then
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
local chunkX, chunkY = positionToChunkXY(player.character.position)
local playerChunk = getChunkByPosition(regionMap, chunkX, chunkY)
if playerChunk then
if (playerChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
playerScent(playerChunk)
end
end
@@ -152,21 +152,24 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if validPlayer(player) then
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
local chunkX, chunkY = positionToChunkXY(player.character.position)
local playerChunk = getChunkByPosition(regionMap, chunkX, chunkY)
if playerChunk then
local vengence = allowingAttacks and ((playerChunk[NEST_COUNT] ~= 0) or (playerChunk[WORM_COUNT] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < natives.retreatThreshold)) and (natives.points >= AI_VENGENCE_SQUAD_COST)
if (playerChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local vengence = (allowingAttacks and
(natives.points >= AI_VENGENCE_SQUAD_COST) and
((getWormCount(regionMap, playerChunk) > 0) or (getNestCount(regionMap, playerChunk) > 0) or (playerChunk[MOVEMENT_PHEROMONE] < natives.retreatThreshold)))
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
for x=playerChunk.x - PROCESS_PLAYER_BOUND, playerChunk.x + PROCESS_PLAYER_BOUND, 32 do
for y=playerChunk.y - PROCESS_PLAYER_BOUND, playerChunk.y + PROCESS_PLAYER_BOUND, 32 do
local chunk = getChunkByPosition(regionMap, x, y)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) and (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
processPheromone(regionMap, chunk)
if (chunk[NEST_COUNT] ~= 0) then
if (getNestCount(regionMap, chunk) > 0) then
if squads then
formSquads(regionMap, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) and (#natives.squads < natives.maxSquads)
@@ -177,7 +180,7 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
end
end
scents(chunk)
scents(regionMap, chunk)
end
end
end
@@ -232,7 +235,7 @@ function mapProcessor.scanMap(regionMap, surface, natives)
end
end
chunk[RESOURCE_GENERATOR] = surface.count_entities_filtered(resourceQuery) * 0.001
setResourceGenerator(regionMap, chunk, surface.count_entities_filtered(resourceQuery) * 0.001)
local entities = surface.find_entities_filtered(playerQuery)
local playerBaseGenerator = 0
@@ -250,7 +253,7 @@ function mapProcessor.scanMap(regionMap, surface, natives)
end
end
chunk[PLAYER_BASE_GENERATOR] = playerBaseGenerator
setPlayerBaseGenerator(regionMap, chunk, playerBaseGenerator)
end
if (endIndex == #processQueue) then

View File

@@ -13,10 +13,12 @@ local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS
local PASSABLE = constants.PASSABLE
local CHUNK_IMPASSABLE = constants.CHUNK_IMPASSABLE
local CHUNK_SIZE = constants.CHUNK_SIZE
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
local CHUNK_SIZE_DIVIDER = constants.CHUNK_SIZE_DIVIDER
-- imported functions
local mFloor = math.floor
@@ -24,19 +26,17 @@ local mFloor = math.floor
-- module code
function mapUtils.getChunkByPosition(regionMap, x, y)
local chunkX = regionMap[mFloor(x * 0.03125)]
if chunkX then
return chunkX[mFloor(y * 0.03125)]
end
return nil
end
function mapUtils.getChunkByIndex(regionMap, x, y)
local chunkX = regionMap[x]
if chunkX then
return chunkX[y]
return chunkX[y] or SENTINEL_IMPASSABLE_CHUNK
end
return nil
return SENTINEL_IMPASSABLE_CHUNK
end
function mapUtils.positionToChunkXY(position)
local chunkX = mFloor(position.x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE
local chunkY = mFloor(position.y * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE
return chunkX, chunkY
end
--[[
@@ -46,39 +46,39 @@ end
/|\
6 7 8
]]--
function mapUtils.getNeighborChunks(regionMap, chunkX, chunkY)
function mapUtils.getNeighborChunks(regionMap, x, y)
local neighbors = regionMap.neighbors
local chunkYRow1 = chunkY - 1
local chunkYRow3 = chunkY + 1
local xChunks = regionMap[chunkX-1]
local chunkYRow1 = y - CHUNK_SIZE
local chunkYRow3 = y + CHUNK_SIZE
local xChunks = regionMap[x-CHUNK_SIZE]
if xChunks then
neighbors[1] = xChunks[chunkYRow1]
neighbors[4] = xChunks[chunkY]
neighbors[6] = xChunks[chunkYRow3]
neighbors[1] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[4] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
neighbors[6] = xChunks[chunkYRow3] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[1] = nil
neighbors[4] = nil
neighbors[6] = nil
neighbors[1] = SENTINEL_IMPASSABLE_CHUNK
neighbors[4] = SENTINEL_IMPASSABLE_CHUNK
neighbors[6] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[x+CHUNK_SIZE]
if xChunks then
neighbors[3] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[5] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
neighbors[8] = xChunks[chunkYRow3] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[3] = SENTINEL_IMPASSABLE_CHUNK
neighbors[5] = SENTINEL_IMPASSABLE_CHUNK
neighbors[8] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[chunkX+1]
xChunks = regionMap[x]
if xChunks then
neighbors[3] = xChunks[chunkYRow1]
neighbors[5] = xChunks[chunkY]
neighbors[8] = xChunks[chunkYRow3]
neighbors[2] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[7] = xChunks[chunkYRow3] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[3] = nil
neighbors[5] = nil
neighbors[8] = nil
end
xChunks = regionMap[chunkX]
if xChunks then
neighbors[2] = xChunks[chunkYRow1]
neighbors[7] = xChunks[chunkYRow3]
else
neighbors[2] = nil
neighbors[7] = nil
neighbors[2] = SENTINEL_IMPASSABLE_CHUNK
neighbors[7] = SENTINEL_IMPASSABLE_CHUNK
end
return neighbors
end
@@ -87,41 +87,41 @@ function mapUtils.canMoveChunkDirection(direction, startChunk, endChunk)
local canMove = false
local startPassable = startChunk[PASSABLE]
local endPassable = endChunk[PASSABLE]
if ((direction == 2) or (direction == 7)) and (startPassable == CHUNK_NORTH_SOUTH) and (endPassable == CHUNK_NORTH_SOUTH) then
canMove = true
elseif ((direction == 4) or (direction == 5)) and (startPassable == CHUNK_EAST_WEST) and (endPassable == CHUNK_EAST_WEST) then
canMove = true
elseif (startPassable == CHUNK_ALL_DIRECTIONS) and (endPassable == CHUNK_ALL_DIRECTIONS) then
if (startPassable == CHUNK_ALL_DIRECTIONS) and (endPassable == CHUNK_ALL_DIRECTIONS) then
canMove = true
elseif (startPassable ~= CHUNK_IMPASSABLE) and (endPassable == CHUNK_ALL_DIRECTIONS) then
elseif ((direction == 2) or (direction == 7)) and (startPassable == CHUNK_NORTH_SOUTH) and (endPassable == CHUNK_NORTH_SOUTH) then
canMove = true
elseif ((direction == 4) or (direction == 5)) and (startPassable == CHUNK_EAST_WEST) and (endPassable == CHUNK_EAST_WEST) then
canMove = true
elseif (startChunk ~= SENTINEL_IMPASSABLE_CHUNK) and (endPassable == CHUNK_ALL_DIRECTIONS) then
canMove = true
end
return canMove
end
function mapUtils.getCardinalChunks(regionMap, chunkX, chunkY)
function mapUtils.getCardinalChunks(regionMap, x, y)
local neighbors = regionMap.cardinalNeighbors
local xChunks = regionMap[chunkX]
local xChunks = regionMap[x]
if xChunks then
neighbors[1] = xChunks[chunkY-1]
neighbors[4] = xChunks[chunkY+1]
neighbors[1] = xChunks[y-CHUNK_SIZE] or SENTINEL_IMPASSABLE_CHUNK
neighbors[4] = xChunks[y+CHUNK_SIZE] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[1] = nil
neighbors[4] = nil
neighbors[1] = SENTINEL_IMPASSABLE_CHUNK
neighbors[4] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[chunkX-1]
xChunks = regionMap[x-CHUNK_SIZE]
if xChunks then
neighbors[2] = xChunks[chunkY]
neighbors[2] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[2] = nil
neighbors[2] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[chunkX+1]
xChunks = regionMap[x+CHUNK_SIZE]
if xChunks then
neighbors[3] = xChunks[chunkY]
neighbors[3] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[3] = nil
neighbors[3] = SENTINEL_IMPASSABLE_CHUNK
end
return neighbors
end

View File

@@ -4,7 +4,7 @@ local movementUtils = {}
local constants = require("Constants")
local unitGroupUtils = require("UnitGroupUtils")
local mapUtils = require("MapUtils")
local mathUtils = require("MathUtils")
-- constants
@@ -12,8 +12,16 @@ local mathUtils = require("MathUtils")
local MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
local MAX_PENALTY_BEFORE_PURGE = constants.MAX_PENALTY_BEFORE_PURGE
local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local canMoveChunkDirection = mapUtils.canMoveChunkDirection
local recycleBiters = unitGroupUtils.recycleBiters
local tableRemove = table.remove
@@ -31,11 +39,11 @@ function movementUtils.findMovementPosition(surface, position, distort)
return (distort and distortPosition(pos)) or pos
end
function movementUtils.addMovementPenalty(natives, units, chunkX, chunkY)
function movementUtils.addMovementPenalty(natives, units, x, y)
local penalties = units.penalties
for i=1,#penalties do
local penalty = penalties[i]
if (penalty.x == chunkX) and (penalty.y == chunkY) then
if (penalty.x == x) and (penalty.y == y) then
penalty.v = penalty.v + MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
if (penalty.v > MAX_PENALTY_BEFORE_PURGE) then
local group = units.group
@@ -53,15 +61,15 @@ function movementUtils.addMovementPenalty(natives, units, chunkX, chunkY)
tableRemove(penalties, 7)
end
tableInsert(penalties, 1, { v = MOVEMENT_PHEROMONE_GENERATOR_AMOUNT,
x = chunkX,
y = chunkY })
x = x,
y = y })
end
function movementUtils.lookupMovementPenalty(squad, chunkX, chunkY)
function movementUtils.lookupMovementPenalty(squad, x, y)
local penalties = squad.penalties
for i=1,#penalties do
local penalty = penalties[i]
if (penalty.x == chunkX) and (penalty.y == chunkY) then
if (penalty.x == x) and (penalty.y == y) then
return penalty.v
end
end
@@ -69,4 +77,101 @@ function movementUtils.lookupMovementPenalty(squad, chunkX, chunkY)
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function movementUtils.scoreNeighborsForAttack(chunk, neighborDirectionChunks, scoreFunction, squad)
local highestChunk = SENTINEL_IMPASSABLE_CHUNK
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and canMoveChunkDirection(x, chunk, neighborChunk) then
local score = scoreFunction(squad, neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
if scoreFunction(squad, chunk) > highestScore then
return SENTINEL_IMPASSABLE_CHUNK, -1
end
return highestChunk, highestDirection
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function movementUtils.scoreNeighborsForResource(chunk, neighborDirectionChunks, scoreFunction, squad, threshold)
local highestChunk = SENTINEL_IMPASSABLE_CHUNK
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and canMoveChunkDirection(x, chunk, neighborChunk) and (neighborChunk[RESOURCE_PHEROMONE] > (threshold or 1)) then
local score = scoreFunction(squad, neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
if scoreFunction(squad, chunk) > highestScore then
return SENTINEL_IMPASSABLE_CHUNK, -1
end
return highestChunk, highestDirection
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function movementUtils.scoreNeighborsForRetreat(chunk, neighborDirectionChunks, scoreFunction, regionMap)
local highestChunk = SENTINEL_IMPASSABLE_CHUNK
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and canMoveChunkDirection(x, chunk, neighborChunk) then
local score = scoreFunction(regionMap, neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
return highestChunk, highestDirection
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function movementUtils.scoreNeighborsForFormation(neighborChunks, validFunction, scoreFunction, regionMap)
local highestChunk = SENTINEL_IMPASSABLE_CHUNK
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborChunks[x]
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and validFunction(regionMap, neighborChunk) then
local score = scoreFunction(neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
return highestChunk, highestDirection
end
return movementUtils

View File

@@ -1,119 +0,0 @@
local neighborUtils = {}
-- imports
local mapUtils = require("MapUtils")
local constants = require("Constants")
-- constants
local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
-- imported functions
local canMoveChunkDirection = mapUtils.canMoveChunkDirection
-- module code
--[[
Expects all neighbors adjacent to a chunk
--]]
function neighborUtils.scoreNeighborsForAttack(chunk, neighborDirectionChunks, scoreFunction, squad)
local highestChunk
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if neighborChunk and canMoveChunkDirection(x, chunk, neighborChunk) then
local score = scoreFunction(squad, neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
if scoreFunction(squad, chunk) > highestScore then
return nil, -1
end
return highestChunk, highestDirection
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function neighborUtils.scoreNeighborsForResource(chunk, neighborDirectionChunks, scoreFunction, squad, threshold)
local highestChunk
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if neighborChunk and canMoveChunkDirection(x, chunk, neighborChunk) and (neighborChunk[RESOURCE_PHEROMONE] > (threshold or 1)) then
local score = scoreFunction(squad, neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
if scoreFunction(squad, chunk) > highestScore then
return nil, -1
end
return highestChunk, highestDirection
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function neighborUtils.scoreNeighborsForRetreat(chunk, neighborDirectionChunks, scoreFunction)
local highestChunk
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if neighborChunk and canMoveChunkDirection(x, chunk, neighborChunk) then
local score = scoreFunction(neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
return highestChunk, highestDirection
end
--[[
Expects all neighbors adjacent to a chunk
--]]
function neighborUtils.scoreNeighborsForFormation(neighborChunks, validFunction, scoreFunction)
local highestChunk
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborChunks[x]
if neighborChunk and validFunction(neighborChunk) then
local score = scoreFunction(neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
highestDirection = x
end
end
end
return highestChunk, highestDirection
end
return neighborUtils

View File

@@ -4,10 +4,8 @@ local nestUtils = {}
local constants = require("Constants")
local mathUtils = require("MathUtils")
local baseRegisterUtils = require("BaseRegisterUtils")
local mapUtils = require("MapUtils")
local chunkUtils = require("ChunkUtils")
-- constants
@@ -20,9 +18,11 @@ local NEST_COUNT = constants.NEST_COUNT
local DOUBLE_CHUNK_SIZE = constants.DOUBLE_CHUNK_SIZE
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local registerEnemyBaseStructure = baseRegisterUtils.registerEnemyBaseStructure
local registerEnemyBaseStructure = chunkUtils.registerEnemyBaseStructure
local gaussianRandomRange = mathUtils.gaussianRandomRange
local mRandom = math.random
@@ -35,7 +35,7 @@ function nestUtils.buildNest(regionMap, base, surface, targetPosition, name)
local position = surface.find_non_colliding_position(name, targetPosition, DOUBLE_CHUNK_SIZE, 2)
local chunk = getChunkByPosition(regionMap, position.x, position.y)
local nest = nil
if position and chunk and (chunk[NEST_COUNT] < 3) then
if position and (chunk ~= SENTINEL_IMPASSABLE_CHUNK) and (chunk[NEST_COUNT] < 3) then
local biterSpawner = {name=name, position=position}
nest = surface.create_entity(biterSpawner)
registerEnemyBaseStructure(regionMap, nest, base)
@@ -45,7 +45,7 @@ end
function nestUtils.buildHive(regionMap, base, surface)
local valid = false
local hive = nestUtils.buildNest(regionMap, base, surface, base, "biter-spawner-hive")
local hive = nestUtils.buildNest(regionMap, base, surface, base, "biter-spawner-hive-rampant")
if hive then
if (#base.hives == 0) then
base.x = hive.position.x
@@ -101,7 +101,7 @@ function nestUtils.buildOutpost(regionMap, natives, base, surface, tendril)
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)
registerEnemyBaseStructure(natives, regionMap, surface.create_entity(biterSpawner), base)
base.upgradePoints = base.upgradePoints - cost
end
pos = pos + slice
@@ -149,7 +149,7 @@ function nestUtils.buildOrder(regionMap, natives, base, surface)
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)
registerEnemyBaseStructure(natives, regionMap, surface.create_entity(biterSpawner), base)
base.upgradePoints = base.upgradePoints - cost
end
pos = pos + slice

View File

@@ -1,21 +0,0 @@
local nocturnalUtils = {}
-- imports
local constants = require("Constants")
-- constants
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
-- module code
function nocturnalUtils.isDark(surface)
return surface.darkness > 0.65
end
function nocturnalUtils.canAttack(natives, surface)
return nocturnalUtils.isDark(surface) and natives.state == AI_STATE_NOCTURNAL
end
return nocturnalUtils

View File

@@ -4,6 +4,7 @@ local pheromoneUtils = {}
local mapUtils = require("MapUtils")
local constants = require("Constants")
local chunkUtils = require("ChunkUtils")
-- constants
@@ -14,9 +15,6 @@ local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local RESOURCE_GENERATOR = constants.RESOURCE_GENERATOR
local PLAYER_PHEROMONE_GENERATOR_AMOUNT = constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT
local DEATH_PHEROMONE_GENERATOR_AMOUNT = constants.DEATH_PHEROMONE_GENERATOR_AMOUNT
@@ -25,63 +23,45 @@ local BASE_PHEROMONE_PERSISTANCE = constants.BASE_PHEROMONE_PERSISTANCE
local PLAYER_PHEROMONE_PERSISTANCE = constants.PLAYER_PHEROMONE_PERSISTANCE
local RESOURCE_PHEROMONE_PERSISTANCE = constants.RESOURCE_PHEROMONE_PERSISTANCE
local CHUNK_IMPASSABLE = constants.CHUNK_IMPASSABLE
local PASSABLE = constants.PASSABLE
local NEST_COUNT = constants.NEST_COUNT
local PATH_RATING = constants.PATH_RATING
local IMPASSABLE_TERRAIN_GENERATOR_AMOUNT = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT
-- imported functions
local getCardinalChunks = mapUtils.getCardinalChunks
local mMax = math.max
local getNestCount = chunkUtils.getNestCount
local getWormCount = chunkUtils.getWormCount
local getPlayerBaseGenerator = chunkUtils.getPlayerBaseGenerator
local getResourceGenerator = chunkUtils.getResourceGenerator
-- module code
function pheromoneUtils.scents(chunk)
if (chunk[PASSABLE] == CHUNK_IMPASSABLE) then
chunk[BASE_PHEROMONE] = IMPASSABLE_TERRAIN_GENERATOR_AMOUNT;
else
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + chunk[PLAYER_BASE_GENERATOR]
local resourceGenerator = chunk[RESOURCE_GENERATOR]
if (resourceGenerator > 0) and (chunk[NEST_COUNT] == 0) then
chunk[RESOURCE_PHEROMONE] = chunk[RESOURCE_PHEROMONE] + mMax(resourceGenerator * 100, 90)
end
function pheromoneUtils.scents(regionMap, chunk)
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + getPlayerBaseGenerator(regionMap, chunk)
local resourceGenerator = getResourceGenerator(regionMap, chunk)
if (resourceGenerator > 0) and (getNestCount(regionMap, chunk) == 0) and (getWormCount(regionMap, chunk) == 0) then
chunk[RESOURCE_PHEROMONE] = chunk[RESOURCE_PHEROMONE] + mMax(resourceGenerator * 100, 90)
end
end
function pheromoneUtils.victoryScent(chunk, entityType)
local value = BUILDING_PHEROMONES[entityType]
if value and (chunk[PASSABLE] ~= CHUNK_IMPASSABLE) then
if value then
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] + (value * 100000)
end
end
function pheromoneUtils.deathScent(chunk)
if (chunk[PASSABLE] ~= CHUNK_IMPASSABLE) then
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] - DEATH_PHEROMONE_GENERATOR_AMOUNT
end
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] - DEATH_PHEROMONE_GENERATOR_AMOUNT
end
function pheromoneUtils.playerScent(playerChunk)
if (playerChunk[PASSABLE] ~= CHUNK_IMPASSABLE) then
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
end
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
end
function pheromoneUtils.processPheromone(regionMap, chunk)
if (chunk[PASSABLE] == CHUNK_IMPASSABLE) then
return
end
local chunkMovement = chunk[MOVEMENT_PHEROMONE]
local chunkBase = chunk[BASE_PHEROMONE]
local chunkPlayer = chunk[PLAYER_PHEROMONE]
@@ -93,17 +73,31 @@ function pheromoneUtils.processPheromone(regionMap, chunk)
local totalPlayer = 0
local totalResource = 0
local tempNeighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
local tempNeighbors = getCardinalChunks(regionMap, chunk.x, chunk.y)
for i=1,4 do
local neighborChunk = tempNeighbors[i]
if neighborChunk then
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement)
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase)
totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunkPlayer)
totalResource = totalResource + (neighborChunk[RESOURCE_PHEROMONE] - chunkResource)
end
end
local neighborChunk = tempNeighbors[1]
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement)
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase)
totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunkPlayer)
totalResource = totalResource + (neighborChunk[RESOURCE_PHEROMONE] - chunkResource)
neighborChunk = tempNeighbors[2]
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement)
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase)
totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunkPlayer)
totalResource = totalResource + (neighborChunk[RESOURCE_PHEROMONE] - chunkResource)
neighborChunk = tempNeighbors[3]
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement)
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase)
totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunkPlayer)
totalResource = totalResource + (neighborChunk[RESOURCE_PHEROMONE] - chunkResource)
neighborChunk = tempNeighbors[4]
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement)
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase)
totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunkPlayer)
totalResource = totalResource + (neighborChunk[RESOURCE_PHEROMONE] - chunkResource)
chunk[MOVEMENT_PHEROMONE] = (chunkMovement + (0.125 * totalMovement)) * MOVEMENT_PHEROMONE_PERSISTANCE * chunkPathRating
chunk[BASE_PHEROMONE] = (chunkBase + (0.25 * totalBase)) * BASE_PHEROMONE_PERSISTANCE * chunkPathRating

View File

@@ -25,24 +25,4 @@ function playerUtils.playersWithinProximityToPosition(players, position, distanc
return false
end
function playerUtils.getPlayerInventory(player, withChar, withoutChar)
local inventory = nil
if player and player.valid and player.connected then
if player.character and player.character.valid then
inventory = player.character.get_inventory(withChar)
else
inventory = player.get_inventory(withoutChar)
end
end
return inventory
end
function playerUtils.getPlayerCursorStack(player)
local result = nil
if player and player.valid then
result = player.cursor_stack
end
return result
end
return playerUtils

View File

@@ -6,9 +6,9 @@ local constants = require("Constants")
local mapUtils = require("MapUtils")
local unitGroupUtils = require("UnitGroupUtils")
local playerUtils = require("PlayerUtils")
local neighborUtils = require("NeighborUtils")
local movementUtils = require("MovementUtils")
local mathUtils = require("MathUtils")
local chunkUtils = require("ChunkUtils")
-- constants
@@ -19,7 +19,7 @@ local BASE_PHEROMONE = constants.BASE_PHEROMONE
local SQUAD_RAIDING = constants.SQUAD_RAIDING
local SQUAD_GUARDING = constants.SQUAD_GUARDING
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local CHUNK_SIZE = constants.CHUNK_SIZE
local PLAYER_PHEROMONE_MULTIPLER = constants.PLAYER_PHEROMONE_MULTIPLER
@@ -32,6 +32,8 @@ local DEFINES_GROUP_ATTACKING_TARGET = defines.group_state.attacking_target
local DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
local DEFINES_DISTRACTION_BY_ANYTHING = defines.distraction.by_anything
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local mRandom = math.random
@@ -48,14 +50,17 @@ local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed
local playersWithinProximityToPosition = playerUtils.playersWithinProximityToPosition
local getPlayerBaseGenerator = chunkUtils.getPlayerBaseGenerator
local scoreNeighborsForAttack = neighborUtils.scoreNeighborsForAttack
local positionToChunkXY = mapUtils.positionToChunkXY
local scoreNeighborsForAttack = movementUtils.scoreNeighborsForAttack
-- module code
local function scoreAttackLocation(squad, neighborChunk)
local damage = (2*neighborChunk[MOVEMENT_PHEROMONE]) + neighborChunk[BASE_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER)
return damage - lookupMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY)
return damage - lookupMovementPenalty(squad, neighborChunk.x, neighborChunk.y)
end
function squadAttack.squadsAttack(regionMap, surface, natives)
@@ -67,7 +72,7 @@ function squadAttack.squadsAttack(regionMap, surface, natives)
attackPosition = regionMap.position
attackCmd = { type = DEFINES_COMMAND_ATTACK_AREA,
destination = attackPosition,
radius = 32,
radius = CHUNK_SIZE,
distraction = DEFINES_DISTRACTION_BY_ENEMY }
end
@@ -78,18 +83,17 @@ function squadAttack.squadsAttack(regionMap, surface, natives)
local groupState = group.state
if (groupState == DEFINES_GROUP_FINISHED) or (groupState == DEFINES_GROUP_GATHERING) or ((groupState == DEFINES_GROUP_MOVING) and (squad.cycles == 0)) then
local groupPosition = group.position
local chunk = getChunkByPosition(regionMap, groupPosition.x, groupPosition.y)
if chunk then
local chunkX, chunkY = positionToChunkXY(groupPosition)
local chunk = getChunkByPosition(regionMap, chunkX, chunkY)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local attackChunk, attackDirection = scoreNeighborsForAttack(chunk,
getNeighborChunks(regionMap,
chunk.cX,
chunk.cY),
getNeighborChunks(regionMap, chunkX, chunkY),
scoreAttackLocation,
squad)
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
addMovementPenalty(natives, squad, chunkX, chunkY)
if group.valid and (attackChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local playerBaseGenerator = getPlayerBaseGenerator(regionMap, attackChunk)
if (playerBaseGenerator == 0) or ((groupState == DEFINES_GROUP_FINISHED) or (groupState == DEFINES_GROUP_GATHERING)) then
squad.cycles = ((#squad.group.members > 80) and 6) or 4
@@ -102,22 +106,18 @@ function squadAttack.squadsAttack(regionMap, surface, natives)
attackCmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
end
local position = findMovementPosition(surface,
positionFromDirectionAndChunk(attackDirection,
groupPosition,
attackPosition,
1.35))
local position = findMovementPosition(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
addMovementPenalty(natives, squad, attackChunk.cX, attackChunk.cY)
addMovementPenalty(natives, squad, attackChunk.x, attackChunk.y)
end
elseif not squad.frenzy and not squad.rabid and
((groupState == DEFINES_GROUP_ATTACKING_DISTRACTION) or (groupState == DEFINES_GROUP_ATTACKING_TARGET) or
(attackChunk[PLAYER_BASE_GENERATOR] ~= 0)) then
(playerBaseGenerator ~= 0)) then
squad.frenzy = true
squad.frenzyPosition.x = groupPosition.x
squad.frenzyPosition.y = groupPosition.y

View File

@@ -5,14 +5,13 @@ local aiDefense = {}
local constants = require("Constants")
local mapUtils = require("MapUtils")
local unitGroupUtils = require("UnitGroupUtils")
local neighborUtils = require("NeighborUtils")
local movementUtils = require("MovementUtils")
local chunkUtils = require("ChunkUtils")
-- constants
local RETREAT_GRAB_RADIUS = constants.RETREAT_GRAB_RADIUS
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
@@ -23,12 +22,9 @@ local SQUAD_RETREATING = constants.SQUAD_RETREATING
local RETREAT_FILTER = constants.RETREAT_FILTER
local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local NEST_COUNT = constants.NEST_COUNT
local WORM_COUNT = constants.WORM_COUNT
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
@@ -38,42 +34,43 @@ local findNearBySquad = unitGroupUtils.findNearBySquad
local addMovementPenalty = movementUtils.addMovementPenalty
local createSquad = unitGroupUtils.createSquad
local membersToSquad = unitGroupUtils.membersToSquad
local scoreNeighborsForRetreat = neighborUtils.scoreNeighborsForRetreat
local scoreNeighborsForRetreat = movementUtils.scoreNeighborsForRetreat
local findMovementPosition = movementUtils.findMovementPosition
local getRetreatTick = chunkUtils.getRetreatTick
local getPlayerBaseGenerator = chunkUtils.getPlayerBaseGenerator
local setRetreatTick = chunkUtils.setRetreatTick
local getNestCount = chunkUtils.getNestCount
local getWormCount = chunkUtils.getWormCount
-- module code
local function scoreRetreatLocation(neighborChunk)
return -(neighborChunk[BASE_PHEROMONE] + -neighborChunk[MOVEMENT_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 100) + (neighborChunk[PLAYER_BASE_GENERATOR] * 20))
local function scoreRetreatLocation(regionMap, neighborChunk)
return -(neighborChunk[BASE_PHEROMONE] + -neighborChunk[MOVEMENT_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 100) + (getPlayerBaseGenerator(regionMap, neighborChunk) * 20))
end
function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, natives, tick)
if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (chunk[NEST_COUNT] == 0) and (chunk[WORM_COUNT] == 0) then
if (tick - getRetreatTick(regionMap, chunk) > INTERVAL_LOGIC) and (getNestCount(regionMap, chunk) == 0) and (getWormCount(regionMap, chunk) == 0) then
local performRetreat = false
local enemiesToSquad = nil
if not squad then
enemiesToSquad = surface.find_enemy_units(chunk, RETREAT_GRAB_RADIUS)
enemiesToSquad = surface.find_enemy_units(position, 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
end
if performRetreat then
chunk[RETREAT_TRIGGERED] = tick
setRetreatTick(regionMap, chunk, tick)
local exitPath,exitDirection = scoreNeighborsForRetreat(chunk,
getNeighborChunks(regionMap,
chunk.cX,
chunk.cY),
scoreRetreatLocation)
if exitPath then
getNeighborChunks(regionMap, chunk.x, chunk.y),
scoreRetreatLocation,
regionMap)
if (exitPath ~= SENTINEL_IMPASSABLE_CHUNK) then
local retreatPosition = findMovementPosition(surface,
positionFromDirectionAndChunk(exitDirection,
position,
regionMap.position,
0.98),
positionFromDirectionAndChunk(exitDirection, position, regionMap.position, 0.98),
false)
if not retreatPosition then
return
@@ -100,7 +97,7 @@ function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, nati
newSquad.rabid = true
end
end
addMovementPenalty(natives, newSquad, chunk.cX, chunk.cY)
addMovementPenalty(natives, newSquad, chunk.x, chunk.y)
end
end
end

View File

@@ -4,24 +4,18 @@ local tendrilUtils = {}
local constants = require("Constants")
local mapUtils = require("MapUtils")
local baseRegisterUtils = require("BaseRegisterUtils")
local neighborsUtils = require("NeighborUtils")
local mathUtils = require("MathUtils")
local nestUtils = require("NestUtils")
local mathUtils = require("MathUtils")
local movementUtils = require("MovementUtils")
-- constants
local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
local RESOURCE_GENERATOR = constants.RESOURCE_GENERATOR
local NEST_COUNT = constants.NEST_COUNT
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local scoreNeighborsForResource = neighborsUtils.scoreNeighborsForResource
local scoreNeighborsForResource = movementUtils.scoreNeighborsForResource
local getNeighborChunks = mapUtils.getNeighborChunks
@@ -74,11 +68,9 @@ local function buildTendrilPath(regionMap, tendril, surface, base, tick, natives
end
local tendrilPosition = tendrilUnit.position
local chunk = getChunkByPosition(regionMap, tendrilPosition.x, tendrilPosition.y)
if chunk then
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local tendrilPath,tendrilDirection = scoreNeighborsForResource(chunk,
getNeighborChunks(regionMap,
chunk.cX,
chunk.cY),
getNeighborChunks(regionMap, chunk.x, chunk.y),
scoreTendrilChunk,
nil)
if (tendrilDirection == -1) then

View File

@@ -1,83 +0,0 @@
local worldProcessor = {}
-- imports
local constants = require("Constants")
-- constants
local ITEM_COLLECTOR_DISTANCE = constants.ITEM_COLLECTOR_DISTANCE
local ITEM_COLLECTOR_QUEUE_SIZE = constants.ITEM_COLLECTOR_QUEUE_SIZE
-- imported functions
-- module code
function worldProcessor.processWorld(surface, world, tick)
local collectors = world.itemCollectorEvents
if (#collectors > 0) then
local collectorLookup = world.itemCollectorLookup
local inserter = {name="", count=0}
local topLeftPosition = {x = 0, y = 0}
local bottomRightPosition = {x = 0, y = 0}
local boundingArea = {topLeftPosition,
bottomRightPosition}
local count = 0
for index = #collectors, 1, -1 do
count = count + 1
local itemCollectorPair = collectorLookup[collectors[index]]
collectors[index] = nil
if itemCollectorPair then
local chest = itemCollectorPair[1]
local dish = itemCollectorPair[2]
if chest.valid and dish.valid then
local collectorPosition = dish.position
topLeftPosition.x = collectorPosition.x - ITEM_COLLECTOR_DISTANCE
topLeftPosition.y = collectorPosition.y - ITEM_COLLECTOR_DISTANCE
bottomRightPosition.x = collectorPosition.x + ITEM_COLLECTOR_DISTANCE
bottomRightPosition.y = collectorPosition.y + ITEM_COLLECTOR_DISTANCE
local items = surface.find_entities_filtered({area = boundingArea,
name = "item-on-ground"})
local counts = {}
if (#items > 0) then
for x=1,#items do
local item = items[x]
local itemName = item.stack.name
if not counts[itemName] then
counts[itemName] = {item}
else
counts[itemName][#counts[itemName]+1] = item
end
end
for k,a in pairs(counts) do
inserter.name = k
inserter.count = #a
local stored = chest.insert(inserter)
for i=1,stored do
a[i].destroy()
end
end
-- dish.surface.create_entity({name="item-collector-base-particle-rampant",
-- position=dish.position})
end
end
end
if (count >= ITEM_COLLECTOR_QUEUE_SIZE) then
return
end
end
end
end
return worldProcessor

View File

@@ -1,6 +1,6 @@
[entity-name]
tunnel-entrance=Tunnel Entrance
tunnel-entrance-rampant=Tunnel Entrance
small-suicide-biter=Small Suicide Biter
medium-suicide-biter=Medium Suicide Biter
@@ -9,7 +9,7 @@ behemoth-suicide-biter=Behemoth Suicide Biter
small-fire-spitter=Small Fire Spitter
biter-spawner-hive=Small Hive
biter-spawner-hive-rampant=Small Hive
small-tendril-biter-rampant=Small Tendril
item-collector-base-rampant=Item Collector
@@ -32,7 +32,7 @@ short-range-electrodynamics-1-rampant=Short-range Electrodynamics
short-range-electrodynamics-1-rampant=Buildings that generate strong electromagnetic fields
[entity-description]
tunnel-entrance=This tunnel is used by the biters to bypass player defenses. Fill the hole using landfill.
tunnel-entrance-rampant=This tunnel is used by the biters to bypass player defenses. Fill the hole using landfill.
small-suicide-biter=These biters will explode at close range
medium-suicide-biter=These biters will explode at close range
@@ -41,7 +41,7 @@ behemoth-suicide-biter=These biters will explode at close range
small-fire-spitter=These biters will spit fire
biter-spawner-hive=Small Hive
biter-spawner-hive-rampant=Small Hive
small-tendril-biter-rampant=Small Tendril
item-collector-base-rampant=Scans the surrounding area for items on the ground, if any items are found they are pulled into the storage chest when the sector is finished scanning.

View File

@@ -36,6 +36,7 @@
(string->path "README.md")
(string->path "NOTICE")
(string->path "libs")
(string->path "sounds")
(string->path "locale")
(string->path "graphics")
(string->path "prototypes")))
@@ -72,12 +73,13 @@
(copyFile "tests.lua" modFolder)
(copyDirectory "libs" modFolder)
(copyDirectory "locale" modFolder)
(copyDirectory "sounds" modFolder)
(copyDirectory "graphics" modFolder)
(copyDirectory "prototypes" modFolder)))
(define (run)
;;(copyFiles modFolder)
(copyFiles modFolder)
;;(copyFiles zipModFolder)
(makeZip modFolder)
;;(makeZip modFolder)
)
)

View File

@@ -1,197 +0,0 @@
-- overlays
local radar = util.table.deepcopy(data.raw["radar"]["radar"])
radar.name = "item-collector-base-rampant"
radar.icon = "__Rampant__/graphics/icon/itemCollectorIcon.png"
radar.collision_box = {{-0.35, -0.35}, {0.35, 0.35}}
radar.selection_box = {{-0.485, -0.7}, {0.465, -0.1}}
radar.energy_per_sector = "27MJ"
radar.max_distance_of_nearby_sector_revealed = 1
radar.max_distance_of_sector_revealed = 0
radar.energy_per_nearby_scan = "27MJ"
radar.energy_usage = "450KW"
radar.flags[#radar.flags+1] = "not-deconstructable"
radar.pictures = {
filename = "__Rampant__/graphics/entities/chest/itemCollector.png",
priority = "low",
width = 46,
height = 49,
apply_projection = false,
direction_count = 64,
line_length = 8,
shift = {0.1875, -0.24}
}
radar.minable = nil
-- local particle = {
-- type = "explosion",
-- name = "item-collector-base-particle-rampant",
-- flags = {"not-on-map"},
-- animations =
-- {
-- {
-- filename = "__Rampant__/graphics/entities/chest/itemCollectorParticle.png",
-- priority = "extra-high",
-- width = 46,
-- height = 49,
-- frame_count = 16,
-- line_length = 8,
-- animation_speed = 0.2
-- }
-- },
-- light = {intensity = 1, size = 20, color = {r=1.0, g=1.0, b=1.0}},
-- smoke = "smoke-fast",
-- smoke_count = 0,
-- smoke_slow_down_factor = 1,
-- sound =
-- {
-- aggregation =
-- {
-- max_count = 1,
-- remove = true
-- },
-- variations =
-- {
-- {
-- filename = "__base__/sound/fight/small-explosion-1.ogg",
-- volume = 0.75
-- }
-- }
-- }
-- }
local radarOverlay = util.table.deepcopy(radar)
radarOverlay.name = "item-collector-base-overlay-rampant"
radarOverlay.pictures.filename = "__Rampant__/graphics/entities/chest/itemCollectorOverlay2.png"
radarOverlay.pictures.width = 2048
radarOverlay.pictures.height = 2048
radarOverlay.pictures.direction_count = 1
radarOverlay.pictures.line_length = 1
radarOverlay.pictures.shift[2] = 0.07
radarOverlay.pictures.hr_version = {
filename = "__Rampant__/graphics/entities/chest/itemCollectorOverlay2.5.png",
priority = "low",
width = 3200,
height = 3200,
apply_projection = false,
direction_count = 1,
line_length = 1,
shift = {0.1875, -0.24}
}
local chest = util.table.deepcopy(data.raw["container"]["steel-chest"])
chest.name = "item-collector-chest-rampant"
chest.picture = {
filename = "__core__/graphics/empty.png",
priority = "low",
width = 46,
height = 49,
line_length = 1,
shift = {0.1875, -0.2}
}
chest.selection_box = {{-0.485, -0.1}, {0.465, 0.6}}
chest.collision_mask = {}
chest.minable.result = "item-collector-base-rampant"
data:extend({
radar,
radarOverlay,
chest-- ,
-- particle
})
data:extend({
{
type = "recipe",
name = "item-collector-base-rampant",
normal = {
enabled = false,
energy_required = 10,
ingredients = {
{"steel-chest", 1},
{"accumulator", 1},
{"radar", 1}
},
result = "item-collector-base-rampant",
requester_paste_multiplier = 4
},
expensive = {
enabled = false,
energy_required = 10,
ingredients = {
{"steel-chest", 2},
{"accumulator", 2},
{"radar", 2}
},
result = "item-collector-base-rampant",
requester_paste_multiplier = 4
}
},
{
type = "item",
name = "item-collector-base-rampant",
icon = "__Rampant__/graphics/icon/itemCollectorIcon.png",
flags = {"goes-to-quickbar"},
subgroup = "storage",
order = "a[items]-c[steel-collector]",
place_result = "item-collector-base-rampant",
stack_size = 50
},
{
type = "item",
name = "item-collector-base-overlay-rampant",
icon = "__Rampant__/graphics/icon/itemCollectorIcon.png",
flags = {"goes-to-quickbar"},
subgroup = "storage",
order = "a[items]-c[steel-collector]",
place_result = "item-collector-base-overlay-rampant",
stack_size = 50
},
{
type = "item",
name = "item-collector-chest-rampant",
icon = "__Rampant__/graphics/icon/itemCollectorIcon.png",
flags = {"goes-to-quickbar"},
subgroup = "storage",
order = "a[items]-c[steel-collector]",
place_result = "item-collector-chest-rampant",
stack_size = 50
}
})
-- technology insertions
data:extend({
{
type = "technology",
name = "short-range-electrodynamics-1-rampant",
icon = "__Rampant__/graphics/technology/itemCollectorTech.png",
icon_size = 128,
localised_name = {"technology-name.short-range-electrodynamics-1-rampant"},
effects =
{
{
type = "unlock-recipe",
recipe = "item-collector-base-rampant"
}
},
prerequisites = {"electric-energy-accumulators-1"},
unit =
{
count = 200,
ingredients =
{
{"science-pack-1", 1},
{"science-pack-2", 1}
},
time = 22
},
order = "c-e-a",
},
})

View File

@@ -102,7 +102,7 @@ data:extend({
{
type = "unit-spawner",
name = "biter-spawner-hive",
name = "biter-spawner-hive-rampant",
icon = "__base__/graphics/icons/biter-spawner.png",
flags = {"placeable-player", "placeable-enemy", "not-repairable"},
max_health = 350,

View File

@@ -1,7 +1,7 @@
data:extend({
{
type = "simple-entity",
name = "tunnel-entrance",
name = "tunnel-entrance-rampant",
flags = {"placeable-neutral", "placeable-off-grid", "not-on-map"},
icon = "__base__/graphics/icons/small-scorchmark.png",
subgroup = "grass",

View File

@@ -10,278 +10,95 @@ local makeColor = colorUtils.makeColor
-- dumb acid projectiles
makeStream({
name = "acid-ball",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
local templateDamage = { amount = 4, type = "acid" }
local templateArea = {
type = "area",
perimeter = 1.2,
action_delivery =
{
{
type = "area",
perimeter = 1.5,
action_delivery =
type = "instant",
target_effects =
{
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 15, type = "acid" }
}
}
type = "damage",
damage = templateDamage
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
}
local templateActions = {
templateArea,
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
local template = {
name = "acid-ball",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = templateActions
}
makeStream(template)
--
makeStream({
name = "acid-ball-1",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "area",
perimeter = 1.5,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 22, type = "acid" }
},
{
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
template.name = "acid-ball-1"
templateDamage.amount = 9
templateArea.perimeter = 1.3
makeStream(template)
--
makeStream({
name = "acid-ball-2",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "area",
perimeter = 1.5,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 32, type = "acid" }
},
{
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
template.name = "acid-ball-2"
templateDamage.amount = 14
templateArea.perimeter = 1.4
makeStream(template)
--
makeStream({
name = "acid-ball-3",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "area",
perimeter = 1.5,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 45, type = "acid" }
},
{
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
template.name = "acid-ball-3"
templateDamage.amount = 23
templateArea.perimeter = 1.5
makeStream(template)
--
makeStream({
name = "wide-acid-ball",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "area",
perimeter = 3,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 35, type = "acid" }
},
{
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
template.name = "wide-acid-ball"
templateDamage.amount = 18
templateArea.perimeter = 3
makeStream(template)
--
makeStream({
name = "acid-ball-4",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "area",
perimeter = 2,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 95, type = "acid" }
},
{
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
template.name = "acid-ball-4"
templateDamage.amount = 25
templateArea.perimeter = 1.75
makeStream(template)
--
makeStream({
name = "acid-ball-5",
particleTint = {r=0, g=1, b=1, a=0.5},
spineAnimationTint = {r=0, g=1, b=1, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "area",
perimeter = 2,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 145, type = "acid" }
},
{
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
},
{
type = "direct",
action_delivery = {
type = "instant",
target_effects = {
type= "create-entity",
entity_name = "acid-splash-purple"
}
}
}
}
})
template.name = "acid-ball-5"
templateDamage.amount = 50
templateArea.perimeter = 2
makeStream(template)
--
template.name = "acid-ball-6"
templateDamage.amount = 70
templateArea.perimeter = 2.5
makeStream(template)

View File

@@ -57,57 +57,7 @@ makeStream({
{
{
type = "damage",
damage = { amount = 35, type = "explosion" }
}
}
}
}
}
})
--
makeStream({
name = "bob-explosive-ball-1",
particleTint = {r=1, g=0.97, b=0.34, a=0.5},
spineAnimationTint = {r=1, g=0.97, b=0.34, a=0.5},
softSmokeTint = makeColor(0.3, 0.75, 0.3, 0.1),
actions = {
{
type = "direct",
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "create-entity",
entity_name = "small-scorchmark",
check_buildability = true
},
{
type = "create-entity",
entity_name = "big-explosion",
check_buildability = true
},
{
type = "create-entity",
entity_name = "small-fire-cloud"
}
}
}
},
{
type = "area",
perimeter = 3,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 55, type = "explosion" }
damage = { amount = 25, type = "explosion" }
}
}
}
@@ -166,7 +116,7 @@ makeStream({
},
{
type = "damage",
damage = { amount = 43, type = "fire" }
damage = { amount = 20, type = "fire" }
}
}
}
@@ -214,47 +164,6 @@ makeStream({
}
})
--
makeStream({
name = "bob-poison-ball-1",
particleTint = {r=0.1, g=0.5, b=1, a=0.5},
spineAnimationTint = {r=0, g=0, b=1, a=0.5},
softSmokeTint = makeColor(0.7, 0.4, 0.2, 0.1),
actions = {
{
type = "direct",
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "create-entity",
entity_name = "small-poison-cloud"
}
}
}
},
{
type = "area",
perimeter = 2,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 43, type = "poison" }
}
}
}
}
}
})
-- piercing
data:extend({
@@ -318,7 +227,7 @@ makeStream({
{
{
type = "damage",
damage = { amount = 43, type = "bob-pierce" }
damage = { amount = 30, type = "bob-pierce" }
}
}
}
@@ -400,47 +309,7 @@ makeStream({
{
{
type = "damage",
damage = { amount = 10, type = "electric" }
}
}
}
}
}
})
--
makeStream({
name = "bob-electric-ball-1",
particleTint = {r=0, g=0.1, b=1, a=1},
spineAnimationTint = {r=0, g=0.1, b=1, a=1},
softSmokeTint = makeColor(0.7, 0.4, 0.2, 0.1),
actions = {
{
type = "cluster",
cluster_count = 10,
distance = 4,
distance_deviation = 3,
action_delivery =
{
type = "projectile",
projectile = "electric-spike-rampant",
direction_deviation = 0.6,
starting_speed = 0.65,
starting_speed_deviation = 0.0
}
},
{
type = "area",
perimeter = 3,
action_delivery =
{
type = "instant",
target_effects =
{
{
type = "damage",
damage = { amount = 55, type = "electric" }
damage = { amount = 25, type = "electric" }
}
}
}
@@ -465,7 +334,7 @@ makeStream({
{
{
type = "create-entity",
entity_name = "small-poison-cloud"
entity_name = "small-fire-cloud"
},
{
type = "create-entity",
@@ -484,19 +353,15 @@ makeStream({
{
{
type = "damage",
damage = { amount = 15, type = "electric" }
damage = { amount = 10, type = "electric" }
},
{
type = "damage",
damage = { amount = 15, type = "explosion" }
damage = { amount = 10, type = "explosion" }
},
{
type = "damage",
damage = { amount = 15, type = "fire" }
},
{
type = "damage",
damage = { amount = 15, type = "poison" }
damage = { amount = 10, type = "fire" }
}
}
}
@@ -615,7 +480,7 @@ makeStream({
},
{
type = "damage",
damage = { amount = 20, type = "explosion" }
damage = { amount = 15, type = "explosion" }
},
{
type = "damage",
@@ -631,7 +496,7 @@ makeStream({
},
{
type = "damage",
damage = { amount = 20, type = "acid" }
damage = { amount = 15, type = "acid" }
}
}
}

View File

@@ -42,7 +42,7 @@ makeStream({
},
{
type = "damage",
damage = {amount = 25, type = "poison"}
damage = {amount = 24, type = "poison"}
}
}
}
@@ -91,11 +91,11 @@ makeStream({
{
{
type = "damage",
damage = { amount = 5, type = "explosion" }
damage = { amount = 8, type = "explosion" }
},
{
type = "damage",
damage = { amount = 15, type = "acid" }
damage = { amount = 18, type = "acid" }
}
}
}
@@ -135,11 +135,11 @@ makeStream({
{
{
type = "damage",
damage = { amount = 3, type = "explosion" }
damage = { amount = 5, type = "explosion" }
},
{
type = "damage",
damage = { amount = 7, type = "poison" }
damage = { amount = 12, type = "poison" }
}
}
}
@@ -183,11 +183,11 @@ makeStream({
{
{
type = "damage",
damage = { amount = 7, type = "explosion" }
damage = { amount = 5, type = "explosion" }
},
{
type = "damage",
damage = { amount = 14, type = "acid" }
damage = { amount = 12, type = "acid" }
}
}
}

View File

@@ -218,22 +218,50 @@ function biterFunctions.createFireAttack(attributes, fireAttack)
begin_sound =
{
{
filename = "__base__/sound/fight/flamethrower-start.ogg",
filename = "__base__/sound/creatures/spitter-1.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-2.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-3.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-4.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-5.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-6.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-7.ogg",
volume = 0.7
},
{
filename = "__base__/sound/creatures/spitter-8.ogg",
volume = 0.7
}
},
middle_sound =
{
{
filename = "__base__/sound/fight/flamethrower-mid.ogg",
volume = 0.7
filename = attributes.midSound or "__Rampant__/sounds/attacks/acid-mid.ogg",
volume = 0.5
}
},
end_sound =
{
{
filename = "__base__/sound/fight/flamethrower-end.ogg",
volume = 0.7
filename = attributes.endSound or "__Rampant__/sounds/attacks/acid-end.ogg",
volume = 0.5
}
}
}

View File

@@ -10,7 +10,8 @@ local makeSmokeSoft = smokeUtils.makeSmokeSoft
-- module code
function streamUtils.makeStream(attributes)
function streamUtils.makeStream(info)
local attributes = util.table.deepcopy(info)
local softSmokeName = attributes.softSmokeName or makeSmokeSoft(attributes)
data:extend(
{
@@ -79,7 +80,7 @@ function streamUtils.makeStream(attributes)
height = 64,
frame_count = 32,
line_length = 8
},
}
}
}
)

View File

@@ -7,19 +7,19 @@ function bobsUpdates.useDumbProjectiles()
turrets["bob-big-explosive-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 25,
cooldown = 60,
range = 26,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
scale = 1.2
},
"bob-explosive-ball-1-stream-rampant")
"bob-explosive-ball-stream-rampant")
turrets["bob-big-fire-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 25,
cooldown = 60,
range = 26,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
@@ -29,19 +29,19 @@ function bobsUpdates.useDumbProjectiles()
turrets["bob-big-poison-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 25,
cooldown = 60,
range = 26,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
scale = 1.2
},
"bob-poison-ball-1-stream-rampant")
"bob-poison-ball-stream-rampant")
turrets["bob-big-piercing-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 25,
cooldown = 60,
range = 26,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
@@ -51,44 +51,44 @@ function bobsUpdates.useDumbProjectiles()
turrets["bob-big-electric-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 25,
cooldown = 60,
range = 26,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
scale = 1.2
},
"bob-electric-ball-1-stream-rampant")
"bob-electric-ball-stream-rampant")
turrets["bob-giant-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
cooldown = 60,
range = 28,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
scale = 1.6
},
"acid-ball-4-stream-rampant")
"acid-ball-5-stream-rampant")
turrets["bob-behemoth-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
cooldown = 60,
range = 30,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
scale = 2
},
"acid-ball-5-stream-rampant")
"acid-ball-6-stream-rampant")
local units = data.raw["unit"]
local unit = units["behemoth-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
turn_range = 1,
warmup = 30,
@@ -102,10 +102,11 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-big-electric-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
cooldown = 90,
range = 15,
min_range = 3,
turn_range = 1,
damageModifier = 0.6,
warmup = 30,
fire_penalty = 0,
scale = biterUtils.findRunScale(unit),
@@ -117,11 +118,12 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-huge-explosive-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
warmup = 30,
turn_range = 1,
damageModifier = 0.8,
fire_penalty = 15,
scale = biterUtils.findRunScale(unit),
tint1 = biterUtils.findTint(unit),
@@ -132,8 +134,8 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-huge-acid-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
turn_range = 1,
warmup = 30,
@@ -147,8 +149,8 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-giant-fire-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
turn_range = 1,
warmup = 30,
@@ -162,8 +164,8 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-giant-poison-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
turn_range = 1,
warmup = 30,
@@ -177,8 +179,8 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-titan-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
turn_range = 1,
warmup = 30,
@@ -192,8 +194,8 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-behemoth-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
turn_range = 1,
warmup = 30,
@@ -208,8 +210,8 @@ function bobsUpdates.useDumbProjectiles()
unit = units["bob-leviathan-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 17,
min_range = 3,
warmup = 30,
turn_range = 1,

View File

@@ -7,8 +7,8 @@ function NEUpdates.useNEUnitLaunchers ()
turrets["medium-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 22,
cooldown = 60,
range = 25,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
@@ -19,8 +19,8 @@ function NEUpdates.useNEUnitLaunchers ()
turrets["big-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 27,
cooldown = 60,
range = 30,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
@@ -40,13 +40,13 @@ function NEUpdates.useDumbProjectiles()
turret["attack_parameters"].range = 22
turret["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 22,
cooldown = 60,
range = 25,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
damageModifier = 4.5,
scale = 1.0
scale = 1.2
},
"ne-infected-ball-stream-rampant")
@@ -54,13 +54,13 @@ function NEUpdates.useDumbProjectiles()
turret["attack_parameters"].range = 27
turret["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 27,
cooldown = 60,
range = 30,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
damageModifier = 5.5,
scale = 1.2
scale = 1.6
},
"ne-mutated-ball-stream-rampant")
@@ -69,7 +69,7 @@ function NEUpdates.useDumbProjectiles()
local unit = units["small-spitter-Mk2"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 150,
cooldown = 100,
range = 13,
min_range = 3,
turn_range = 1,
@@ -85,7 +85,7 @@ function NEUpdates.useDumbProjectiles()
unit = units["small-spitter-Mk3"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 150,
cooldown = 100,
range = 13,
min_range = 3,
turn_range = 1,
@@ -102,7 +102,7 @@ function NEUpdates.useDumbProjectiles()
unit = units["medium-spitter-Mk2"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 150,
cooldown = 100,
range = 14,
min_range = 3,
turn_range = 1,
@@ -118,7 +118,7 @@ function NEUpdates.useDumbProjectiles()
unit = units["medium-spitter-Mk3"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 150,
cooldown = 100,
range = 14,
min_range = 3,
turn_range = 1,
@@ -134,7 +134,7 @@ function NEUpdates.useDumbProjectiles()
unit = units["big-spitter-Mk2"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 150,
cooldown = 100,
range = 15,
min_range = 3,
turn_range = 1,
@@ -151,7 +151,7 @@ function NEUpdates.useDumbProjectiles()
unit = units["big-spitter-Mk3"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 150,
cooldown = 100,
range = 15,
min_range = 3,
turn_range = 1,

View File

@@ -7,44 +7,46 @@ function vanillaUpdates.useDumbProjectiles()
turrets["small-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 50,
range = 18,
cooldown = 60,
range = 21,
min_range = 5,
turn_range = 1,
fire_penalty = 0,
damageModifier = 0.9,
scale = 0.8
},
"acid-ball-stream-rampant")
"acid-ball-2-stream-rampant")
turrets["medium-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 20,
cooldown = 60,
range = 25,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
damageModifier = 0.87,
scale = 1
},
"acid-ball-1-stream-rampant")
"acid-ball-3-stream-rampant")
turrets["big-worm-turret"]["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 25,
cooldown = 60,
range = 26,
min_range = 3,
turn_range = 1,
fire_penalty = 0,
scale = 1.2
},
"acid-ball-2-stream-rampant")
"acid-ball-4-stream-rampant")
local units = data.raw["unit"];
local unit = units["small-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
cooldown = 100,
range = 13,
warmup = 30,
min_range = 3,
@@ -59,7 +61,7 @@ function vanillaUpdates.useDumbProjectiles()
unit = units["medium-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
cooldown = 95,
range = 14,
min_range = 3,
warmup = 30,
@@ -74,7 +76,7 @@ function vanillaUpdates.useDumbProjectiles()
unit = units["big-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
cooldown = 90,
range = 15,
min_range = 3,
warmup = 30,
@@ -89,8 +91,8 @@ function vanillaUpdates.useDumbProjectiles()
unit = units["behemoth-spitter"]
unit["attack_parameters"] = biterUtils.createFireAttack(
{
cooldown = 80,
range = 15,
cooldown = 90,
range = 16,
min_range = 3,
warmup = 30,
turn_range = 1,

View File

@@ -193,17 +193,6 @@ data:extend({
order = "h[modifier]-a[optimize]",
per_user = false
},
{
type = "bool-setting",
name = "rampant-enableBuildings",
description = "rampant-enableBuildings",
setting_type = "startup",
default_value = true,
order = "i[modifier]-a[buildings]",
per_user = false
},
{
type = "bool-setting",

BIN
sounds/attacks/acid-end.ogg Executable file

Binary file not shown.

BIN
sounds/attacks/acid-mid.ogg Executable file

Binary file not shown.

View File

@@ -5,22 +5,23 @@ local mathUtils = require("libs/MathUtils")
local chunkUtils = require("libs/ChunkUtils")
local mapUtils = require("libs/MapUtils")
local baseUtils = require("libs/BaseUtils")
local baseRegisterUtils = require("libs/BaseRegisterUtils")
local tendrilUtils = require("libs/TendrilUtils")
-- local tendrilUtils = require("libs/TendrilUtils")
function tests.pheromoneLevels(size)
local player = game.player.character
local playerChunkX = math.floor(player.position.x / 32)
local playerChunkY = math.floor(player.position.y / 32)
local playerChunkX = math.floor(player.position.x / 32) * constants.CHUNK_SIZE
local playerChunkY = math.floor(player.position.y / 32) * constants.CHUNK_SIZE
if not size then
size = 3
size = 3 * constants.CHUNK_SIZE
else
size = size * constants.CHUNK_SIZE
end
print("------")
print(#global.regionMap.processQueue)
print(playerChunkX .. ", " .. playerChunkY)
print("--")
for y=playerChunkY-size, playerChunkY+size do
for x=playerChunkX-size, playerChunkX+size do
for y=playerChunkY-size, playerChunkY+size,32 do
for x=playerChunkX-size, playerChunkX+size,32 do
if (global.regionMap[x] ~= nil) then
local chunk = global.regionMap[x][y]
if (chunk ~= nil) then
@@ -29,12 +30,12 @@ function tests.pheromoneLevels(size)
str = str .. " " .. tostring(i) .. "/" .. tostring(chunk[i])
end
str = str .. " " .. "p/" .. game.surfaces[1].get_pollution(chunk)
if (chunk.cX == playerChunkX) and (chunk.cY == playerChunkY) then
if (chunk.x == playerChunkX) and (chunk.y == playerChunkY) then
print("=============")
print(chunk.cX, chunk.cY, str)
print(chunk.x, chunk.y, str)
print("=============")
else
print(chunk.cX, chunk.cY, str)
print(chunk.x, chunk.y, str)
end
-- print(str)
print("----")
@@ -109,7 +110,7 @@ function tests.tunnelTest()
local playerPosition = game.players[1].position
local chunkX = math.floor(playerPosition.x * 0.03125) * 32
local chunkY = math.floor(playerPosition.y * 0.03125) * 32
game.surfaces[1].create_entity({name="tunnel-entrance", position={chunkX, chunkY}})
game.surfaces[1].create_entity({name="tunnel-entrance-rampant", position={chunkX, chunkY}})
end
function tests.createEnemy(x)
@@ -121,7 +122,7 @@ end
function tests.registeredNest(x)
local entity = tests.createEnemy(x)
baseRegisterUtils.registerEnemyBaseStructure(global.regionMap,
chunk.registerEnemyBaseStructure(global.regionMap,
entity,
nil)
end
@@ -280,9 +281,9 @@ function tests.showMovementGrid()
end
function tests.stepAdvanceTendrils()
for _, base in pairs(global.natives.bases) do
tendrilUtils.advanceTendrils(global.regionMap, base, game.surfaces[1], {nil,nil,nil,nil,nil,nil,nil,nil})
end
-- for _, base in pairs(global.natives.bases) do
-- tendrilUtils.advanceTendrils(global.regionMap, base, game.surfaces[1], {nil,nil,nil,nil,nil,nil,nil,nil})
-- end
end
return tests