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
global.world = nil
world.itemCollectorLookup = {}
world.itemCollectorEvents = {}
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
@@ -40,25 +42,30 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin
local y = topLeft.y
local chunk = createChunk(x, y)
areaBoundingBox[1] = chunk
offset[1] = x + CHUNK_SIZE
offset[2] = y + CHUNK_SIZE
chunk = checkChunkPassability(chunkTiles, chunk, surface)
local chunkX = chunk.cX
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
if regionMap[chunkX] == nil then
regionMap[chunkX] = {}
end
regionMap[chunkX][chunk.cY] = chunk
areaBoundingBox[1] = chunk
offset[1] = x + CHUNK_SIZE
offset[2] = y + CHUNK_SIZE
checkChunkPassability(chunkTiles, chunk, surface)
if vanillaAI then
registerChunkEnemies(chunk, surface, query)
else
remakeChunk(regionMap, chunk, surface, natives, tick, query)
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
@@ -144,7 +257,12 @@ function chunkUtils.checkChunkPassability(chunkTiles, chunk, surface)
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[chunkX+1]
xChunks = regionMap[x+CHUNK_SIZE]
if xChunks then
neighbors[3] = xChunks[chunkYRow1]
neighbors[5] = xChunks[chunkY]
neighbors[8] = xChunks[chunkYRow3]
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] = nil
neighbors[5] = nil
neighbors[8] = nil
neighbors[3] = SENTINEL_IMPASSABLE_CHUNK
neighbors[5] = SENTINEL_IMPASSABLE_CHUNK
neighbors[8] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[chunkX]
xChunks = regionMap[x]
if xChunks then
neighbors[2] = xChunks[chunkYRow1]
neighbors[7] = xChunks[chunkYRow3]
neighbors[2] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[7] = xChunks[chunkYRow3] or SENTINEL_IMPASSABLE_CHUNK
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,43 +34,44 @@ 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
end
@@ -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

@@ -194,17 +194,6 @@ data:extend({
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",
name = "rampant-attack-warning",

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