1
0
mirror of https://github.com/veden/Rampant.git synced 2025-01-03 22:52:20 +02:00
Rampant/libs/AIAttackWave.lua

260 lines
9.7 KiB
Lua
Raw Normal View History

local aiAttackWave = {}
-- imports
2016-08-19 04:02:13 +02:00
local constants = require("Constants")
local mapUtils = require("MapUtils")
2018-09-24 06:56:45 +02:00
local chunkPropertyUtils = require("ChunkPropertyUtils")
2016-08-19 04:02:13 +02:00
local unitGroupUtils = require("UnitGroupUtils")
2017-11-21 09:27:03 +02:00
local movementUtils = require("MovementUtils")
local mathUtils = require("MathUtils")
package.path = "../?.lua;" .. package.path
local config = require("config")
-- constants
2016-10-15 02:00:18 +02:00
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
2016-10-15 02:00:18 +02:00
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
2016-10-15 02:00:18 +02:00
local AI_SQUAD_COST = constants.AI_SQUAD_COST
2018-09-24 06:56:45 +02:00
local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT
2016-11-04 09:26:19 +02:00
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
2016-10-15 02:00:18 +02:00
2018-02-12 05:21:28 +02:00
local INTERVAL_RALLY = constants.INTERVAL_RALLY
2017-05-14 00:32:16 +02:00
2017-06-01 03:46:53 +02:00
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
2017-06-10 10:38:20 +02:00
local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS
2017-11-21 09:27:03 +02:00
local CHUNK_SIZE = constants.CHUNK_SIZE
2017-05-27 02:58:33 +02:00
local RALLY_CRY_DISTANCE = constants.RALLY_CRY_DISTANCE
2018-02-17 05:31:29 +02:00
local SETTLER_DISTANCE = constants.SETTLER_DISTANCE
2017-05-27 02:58:33 +02:00
local RESOURCE_MINIMUM_FORMATION_DELTA = constants.RESOURCE_MINIMUM_FORMATION_DELTA
2019-02-06 08:25:43 +02:00
local AI_STATE_SIEGE = constants.AI_STATE_SIEGE
2017-05-27 02:58:33 +02:00
local DEFINES_COMMAND_GROUP = defines.command.group
local DEFINES_DISTRACTION_NONE = defines.distraction.none
2017-01-20 07:58:36 +02:00
local RAIDING_MINIMUM_BASE_THRESHOLD = constants.RAIDING_MINIMUM_BASE_THRESHOLD
local AI_STATE_RAIDING = constants.AI_STATE_RAIDING
2017-11-21 09:27:03 +02:00
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
2018-09-24 06:56:45 +02:00
-- local PASSABLE = constants.PASSABLE
2017-05-12 06:50:06 +02:00
-- imported functions
local mRandom = math.random
2017-06-08 02:57:24 +02:00
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
2018-09-24 06:56:45 +02:00
local getPassable = chunkPropertyUtils.getPassable
local getNestCount = chunkPropertyUtils.getNestCount
local getChunkSettlerTick = chunkPropertyUtils.getChunkSettlerTick
local setChunkSettlerTick = chunkPropertyUtils.setChunkSettlerTick
local getRallyTick = chunkPropertyUtils.getRallyTick
local setRallyTick = chunkPropertyUtils.setRallyTick
2017-11-21 09:27:03 +02:00
local gaussianRandomRange = mathUtils.gaussianRandomRange
local getNeighborChunks = mapUtils.getNeighborChunks
local getChunkByXY = mapUtils.getChunkByXY
2017-11-21 09:27:03 +02:00
local scoreNeighborsForFormation = movementUtils.scoreNeighborsForFormation
local scoreNeighborsForResource = movementUtils.scoreNeighborsForResource
local createSquad = unitGroupUtils.createSquad
local attackWaveScaling = config.attackWaveScaling
local settlerWaveScaling = config.settlerWaveScaling
-- module code
2016-08-19 04:02:13 +02:00
2017-06-01 03:46:53 +02:00
local function attackWaveValidCandidate(chunk, natives, surface)
local total = 0;
2017-06-10 10:38:20 +02:00
local hasPlayerPheromone = false
2017-05-06 11:03:28 +02:00
if natives.attackUsePlayer then
2017-04-22 01:14:04 +02:00
local playerPheromone = chunk[PLAYER_PHEROMONE]
2017-06-01 03:46:53 +02:00
if (playerPheromone > natives.attackPlayerThreshold) then
2017-04-22 01:14:04 +02:00
total = total + chunk[PLAYER_PHEROMONE]
2017-06-10 10:38:20 +02:00
hasPlayerPheromone = true
elseif (playerPheromone > 0) then
hasPlayerPheromone = true
2017-04-22 01:14:04 +02:00
end
end
2017-06-10 10:38:20 +02:00
local hasBasePheromone = false
local basePheromone = chunk[BASE_PHEROMONE]
if (basePheromone > 0) then
2017-06-10 10:38:20 +02:00
hasBasePheromone = true
if (natives.state == AI_STATE_RAIDING) then
if (basePheromone > RAIDING_MINIMUM_BASE_THRESHOLD) then
total = total + basePheromone
end
end
2017-06-10 10:38:20 +02:00
end
2017-05-06 11:03:28 +02:00
if natives.attackUsePollution then
2017-05-28 06:50:37 +02:00
total = total + surface.get_pollution(chunk)
end
2019-02-06 08:25:43 +02:00
2017-06-11 02:59:06 +02:00
return (total > natives.attackWaveThreshold) and (hasBasePheromone or hasPlayerPheromone)
end
local function scoreSettlerLocation(neighborChunk)
return neighborChunk[RESOURCE_PHEROMONE] + -neighborChunk[MOVEMENT_PHEROMONE] + -neighborChunk[PLAYER_PHEROMONE]
end
2019-02-06 08:25:43 +02:00
local function scoreSiegeSettlerLocation(neighborChunk)
return neighborChunk[RESOURCE_PHEROMONE] + neighborChunk[BASE_PHEROMONE] + -neighborChunk[MOVEMENT_PHEROMONE] + -neighborChunk[PLAYER_PHEROMONE]
end
local function validSiegeSettlerLocation(map, neighborChunk)
return (getPassable(map, neighborChunk) == CHUNK_ALL_DIRECTIONS) and (getNestCount(map, neighborChunk) == 0)
end
local function validSettlerLocation(map, chunk, neighborChunk)
local chunkResource = chunk[RESOURCE_PHEROMONE]
2018-09-24 06:56:45 +02:00
return (getPassable(map, neighborChunk) == CHUNK_ALL_DIRECTIONS) and (getNestCount(map, neighborChunk) == 0) and (neighborChunk[RESOURCE_PHEROMONE] >= (chunkResource * RESOURCE_MINIMUM_FORMATION_DELTA))
end
2017-06-11 02:59:06 +02:00
local function scoreUnitGroupLocation(neighborChunk)
return neighborChunk[PLAYER_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
end
2018-01-14 07:48:21 +02:00
local function validUnitGroupLocation(map, neighborChunk)
2018-09-24 06:56:45 +02:00
return getPassable(map, neighborChunk) == CHUNK_ALL_DIRECTIONS and (getNestCount(map, neighborChunk) == 0)
end
2018-01-14 07:48:21 +02:00
function aiAttackWave.rallyUnits(chunk, map, surface, natives, tick)
2018-02-17 05:31:29 +02:00
if ((tick - getRallyTick(map, chunk) > INTERVAL_RALLY) and (natives.points >= AI_VENGENCE_SQUAD_COST)) then
2018-01-14 07:48:21 +02:00
setRallyTick(map, chunk, tick)
2017-11-21 09:27:03 +02:00
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
2017-06-11 02:59:06 +02:00
if (x ~= cX) and (y ~= cY) then
2018-01-14 07:48:21 +02:00
local rallyChunk = getChunkByXY(map, x, y)
if (rallyChunk ~= SENTINEL_IMPASSABLE_CHUNK) and (getNestCount(map, rallyChunk) > 0) then
2018-02-12 05:21:28 +02:00
if not aiAttackWave.formSquads(map, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST) then
2017-06-11 02:59:06 +02:00
return
end
2017-06-01 03:46:53 +02:00
end
end
2017-01-20 07:58:36 +02:00
end
end
end
2017-01-20 07:58:36 +02:00
end
2018-02-17 05:31:29 +02:00
local function noNearbySettlers(map, chunk, tick)
local cX = chunk.x
local cY = chunk.y
for x=cX - SETTLER_DISTANCE, cX + SETTLER_DISTANCE, 32 do
for y=cY - SETTLER_DISTANCE, cY + SETTLER_DISTANCE, 32 do
if (x ~= cX) and (y ~= cY) then
local c = getChunkByXY(map, x, y)
if (c ~= SENTINEL_IMPASSABLE_CHUNK) and ((tick - getChunkSettlerTick(map, c)) < 0) then
return false
end
end
end
end
return true
end
function aiAttackWave.formSettlers(map, surface, natives, chunk, cost, tick)
2018-09-24 06:56:45 +02:00
if (mRandom() < natives.formSquadThreshold) and (#natives.squads < AI_MAX_SQUAD_COUNT) then
2019-02-06 08:25:43 +02:00
local squadPath, squadDirection
if (natives.state == AI_STATE_SIEGE) then
squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
validSiegeSettlerLocation,
scoreSiegeSettlerLocation,
map)
else
squadPath, squadDirection = scoreNeighborsForResource(chunk,
getNeighborChunks(map, chunk.x, chunk.y),
validSettlerLocation,
scoreSettlerLocation,
map)
end
2018-02-17 05:31:29 +02:00
if (squadPath ~= SENTINEL_IMPASSABLE_CHUNK) and noNearbySettlers(map, chunk, tick) then
local squadPosition = surface.find_non_colliding_position("chunk-scanner-squad-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
map.position,
0.98),
CHUNK_SIZE,
4)
if squadPosition then
local squad = createSquad(squadPosition, surface, natives)
squad.settlers = true
squad.maxDistance = gaussianRandomRange(natives.expansionMaxDistance,
natives.expansionMaxDistanceDerivation,
CHUNK_SIZE * 1,
natives.expansionMaxDistance)
squad.originPosition.x = squadPosition.x
squad.originPosition.y = squadPosition.y
2019-02-06 08:25:43 +02:00
local scaledWaveSize = settlerWaveScaling(natives)
local foundUnits = surface.set_multi_command({ command = { type = DEFINES_COMMAND_GROUP,
group = squad.group,
distraction = DEFINES_DISTRACTION_NONE },
unit_count = scaledWaveSize,
unit_search_distance = TRIPLE_CHUNK_SIZE })
if (foundUnits > 0) then
2018-02-17 05:31:29 +02:00
setChunkSettlerTick(map, squadPath, tick + natives.settlerCooldown)
natives.points = natives.points - cost
end
end
end
end
2019-02-06 08:25:43 +02:00
return (natives.points - cost) > 0
end
2018-01-14 07:48:21 +02:00
function aiAttackWave.formSquads(map, surface, natives, chunk, cost)
2017-06-11 02:59:06 +02:00
local valid = (cost == AI_VENGENCE_SQUAD_COST) or ((cost == AI_SQUAD_COST) and attackWaveValidCandidate(chunk, natives, surface))
2017-06-01 03:46:53 +02:00
2018-09-24 06:56:45 +02:00
if valid and (mRandom() < natives.formSquadThreshold) and (#natives.squads < AI_MAX_SQUAD_COUNT) then
2019-02-06 08:25:43 +02:00
2018-01-14 07:48:21 +02:00
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
2017-06-11 02:59:06 +02:00
validUnitGroupLocation,
2017-11-21 09:27:03 +02:00
scoreUnitGroupLocation,
2018-01-14 07:48:21 +02:00
map)
2017-11-21 09:27:03 +02:00
if (squadPath ~= SENTINEL_IMPASSABLE_CHUNK) then
local squadPosition = surface.find_non_colliding_position("chunk-scanner-squad-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
2018-01-14 07:48:21 +02:00
map.position,
0.98),
2017-11-21 09:27:03 +02:00
CHUNK_SIZE,
4)
2017-06-09 07:18:59 +02:00
if squadPosition then
local squad = createSquad(squadPosition, surface, natives)
2019-02-06 08:25:43 +02:00
squad.rabid = mRandom() < 0.03
2017-06-09 07:18:59 +02:00
local scaledWaveSize = attackWaveScaling(natives)
local foundUnits = surface.set_multi_command({ command = { type = DEFINES_COMMAND_GROUP,
group = squad.group,
distraction = DEFINES_DISTRACTION_NONE },
unit_count = scaledWaveSize,
unit_search_distance = TRIPLE_CHUNK_SIZE })
if (foundUnits > 0) then
natives.points = natives.points - cost
end
end
end
2016-08-19 04:02:13 +02:00
end
2019-02-06 08:25:43 +02:00
2018-02-12 05:21:28 +02:00
return (natives.points - cost) > 0
2016-08-19 04:02:13 +02:00
end
return aiAttackWave