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

348 lines
15 KiB
Lua
Raw Normal View History

2019-02-15 20:17:30 -08:00
if (aiAttackWaveG) then
return aiAttackWaveG
end
local aiAttackWave = {}
-- imports
2016-08-18 19:02:13 -07:00
local constants = require("Constants")
local mapUtils = require("MapUtils")
2018-09-23 21:56:45 -07:00
local chunkPropertyUtils = require("ChunkPropertyUtils")
2016-08-18 19:02:13 -07:00
local unitGroupUtils = require("UnitGroupUtils")
2017-11-20 23:27:03 -08:00
local movementUtils = require("MovementUtils")
local mathUtils = require("MathUtils")
2019-02-27 18:53:59 -08:00
local config = require("__Rampant__/config")
2021-04-29 22:24:14 -07:00
local baseUtils = require("BaseUtils")
-- constants
2016-10-14 17:00:18 -07:00
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
2019-04-24 23:13:22 -07:00
local AGGRESSIVE_CAN_ATTACK_WAIT_MAX_DURATION = constants.AGGRESSIVE_CAN_ATTACK_WAIT_MAX_DURATION
local AGGRESSIVE_CAN_ATTACK_WAIT_MIN_DURATION = constants.AGGRESSIVE_CAN_ATTACK_WAIT_MIN_DURATION
2016-10-14 17:00:18 -07:00
local AI_SQUAD_COST = constants.AI_SQUAD_COST
2019-02-19 22:16:43 -08:00
local AI_SETTLER_COST = constants.AI_SETTLER_COST
2016-11-04 00:26:19 -07:00
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
2019-04-24 23:13:22 -07:00
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
2016-10-14 17:00:18 -07:00
2020-05-19 19:37:16 -07:00
local COOLDOWN_RALLY = constants.COOLDOWN_RALLY
2017-05-13 15:32:16 -07:00
2017-06-10 01:38:20 -07:00
local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS
2017-11-20 23:27:03 -08:00
local CHUNK_SIZE = constants.CHUNK_SIZE
2017-05-26 17:58:33 -07:00
local RALLY_CRY_DISTANCE = constants.RALLY_CRY_DISTANCE
2019-02-05 22:25:43 -08:00
local AI_STATE_SIEGE = constants.AI_STATE_SIEGE
local AI_STATE_RAIDING = constants.AI_STATE_RAIDING
-- imported functions
2021-04-29 22:24:14 -07:00
local findNearbyBase = baseUtils.findNearbyBase
2019-04-24 23:13:22 -07:00
local randomTickEvent = mathUtils.randomTickEvent
2020-05-23 20:47:14 -07:00
local calculateKamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold
local mRandom = math.random
2017-06-07 17:57:24 -07:00
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
2018-09-23 21:56:45 -07:00
local getPassable = chunkPropertyUtils.getPassable
local getNestCount = chunkPropertyUtils.getNestCount
2019-02-19 22:16:43 -08:00
local getRaidNestActiveness = chunkPropertyUtils.getRaidNestActiveness
local getNestActiveness = chunkPropertyUtils.getNestActiveness
2018-09-23 21:56:45 -07:00
local getRallyTick = chunkPropertyUtils.getRallyTick
local setRallyTick = chunkPropertyUtils.setRallyTick
2017-11-20 23:27:03 -08:00
local gaussianRandomRange = mathUtils.gaussianRandomRange
local getNeighborChunks = mapUtils.getNeighborChunks
local getChunkByXY = mapUtils.getChunkByXY
2017-11-20 23:27:03 -08:00
local scoreNeighborsForFormation = movementUtils.scoreNeighborsForFormation
local scoreNeighborsForResource = movementUtils.scoreNeighborsForResource
local createSquad = unitGroupUtils.createSquad
local attackWaveScaling = config.attackWaveScaling
local settlerWaveScaling = config.settlerWaveScaling
2020-05-23 20:47:14 -07:00
local getDeathGenerator = chunkPropertyUtils.getDeathGenerator
-- module code
2016-08-18 19:02:13 -07:00
2021-02-19 23:31:36 -08:00
local function attackWaveValidCandidate(chunk, map)
2019-02-19 22:16:43 -08:00
local isValid = getNestActiveness(map, chunk)
2021-02-19 23:31:36 -08:00
if map.state == AI_STATE_RAIDING then
2019-02-19 22:16:43 -08:00
isValid = isValid + getRaidNestActiveness(map, chunk)
2017-06-10 01:38:20 -07:00
end
2019-02-19 22:16:43 -08:00
return (isValid > 0)
end
2020-05-23 20:47:14 -07:00
local function scoreSettlerLocation(map, neighborChunk)
return neighborChunk[RESOURCE_PHEROMONE] +
2020-05-23 20:47:14 -07:00
-getDeathGenerator(map, neighborChunk) +
-neighborChunk[PLAYER_PHEROMONE]
end
2020-05-23 20:47:14 -07:00
local function scoreSiegeSettlerLocation(map, neighborChunk)
return neighborChunk[RESOURCE_PHEROMONE] +
neighborChunk[BASE_PHEROMONE] +
2020-05-23 20:47:14 -07:00
-getDeathGenerator(map, neighborChunk) +
-neighborChunk[PLAYER_PHEROMONE]
2019-02-05 22:25:43 -08:00
end
2020-05-23 20:47:14 -07:00
local function scoreUnitGroupLocation(map, neighborChunk)
return neighborChunk[PLAYER_PHEROMONE] +
2020-05-23 20:47:14 -07:00
-getDeathGenerator(map, neighborChunk) +
neighborChunk[BASE_PHEROMONE]
2019-02-05 22:25:43 -08:00
end
local function validSiegeSettlerLocation(map, neighborChunk)
return (getPassable(map, neighborChunk) == CHUNK_ALL_DIRECTIONS) and
(getNestCount(map, neighborChunk) == 0)
end
2019-02-05 22:25:43 -08:00
local function validSettlerLocation(map, chunk, neighborChunk)
local chunkResource = chunk[RESOURCE_PHEROMONE]
return (getPassable(map, neighborChunk) == CHUNK_ALL_DIRECTIONS) and
(getNestCount(map, neighborChunk) == 0) and
2021-04-29 22:24:14 -07:00
(neighborChunk[RESOURCE_PHEROMONE] >= chunkResource)
end
2018-01-13 21:48:21 -08:00
local function validUnitGroupLocation(map, neighborChunk)
return getPassable(map, neighborChunk) == CHUNK_ALL_DIRECTIONS and
(getNestCount(map, neighborChunk) == 0)
end
2020-05-19 19:37:16 -07:00
local function visitPattern(o, cX, cY, distance)
local startX
local endX
local stepX
local startY
local endY
local stepY
if (o == 0) then
2021-02-13 20:49:54 -08:00
startX = cX - distance
endX = cX + distance
2020-05-19 19:37:16 -07:00
stepX = 32
2021-02-13 20:49:54 -08:00
startY = cY - distance
endY = cY + distance
2020-05-19 19:37:16 -07:00
stepY = 32
elseif (o == 1) then
2021-02-13 20:49:54 -08:00
startX = cX + distance
endX = cX - distance
2020-05-19 19:37:16 -07:00
stepX = -32
2021-02-13 20:49:54 -08:00
startY = cY + distance
endY = cY - distance
2020-05-19 19:37:16 -07:00
stepY = -32
elseif (o == 2) then
2021-02-13 20:49:54 -08:00
startX = cX - distance
endX = cX + distance
2020-05-19 19:37:16 -07:00
stepX = 32
2021-02-13 20:49:54 -08:00
startY = cY + distance
endY = cY - distance
2020-05-19 19:37:16 -07:00
stepY = -32
elseif (o == 3) then
2021-02-13 20:49:54 -08:00
startX = cX + distance
endX = cX - distance
2020-05-19 19:37:16 -07:00
stepX = -32
2021-02-13 20:49:54 -08:00
startY = cY - distance
endY = cY + distance
2020-05-19 19:37:16 -07:00
stepY = 32
end
2020-05-19 19:37:16 -07:00
return startX, endX, stepX, startY, endY, stepY
2017-01-19 21:58:36 -08:00
end
2021-02-13 20:49:54 -08:00
function aiAttackWave.rallyUnits(chunk, map, tick)
2021-02-19 23:31:36 -08:00
if ((tick - getRallyTick(map, chunk) > COOLDOWN_RALLY) and (map.points >= AI_VENGENCE_SQUAD_COST)) then
2020-05-19 19:37:16 -07:00
setRallyTick(map, chunk, tick)
2019-12-06 21:57:20 -08:00
local cX = chunk.x
local cY = chunk.y
2020-05-19 19:37:16 -07:00
local startX, endX, stepX, startY, endY, stepY = visitPattern(tick % 4, cX, cY, RALLY_CRY_DISTANCE)
2021-02-19 23:31:36 -08:00
local vengenceQueue = map.vengenceQueue
2020-05-19 19:37:16 -07:00
for x=startX, endX, stepX do
for y=startY, endY, stepY do
2019-12-06 21:57:20 -08:00
if (x ~= cX) and (y ~= cY) then
local rallyChunk = getChunkByXY(map, x, y)
2020-05-15 13:51:38 -07:00
if (rallyChunk ~= -1) and (getNestCount(map, rallyChunk) > 0) then
2020-05-19 19:37:16 -07:00
local count = vengenceQueue[rallyChunk]
if not count then
count = 0
vengenceQueue[rallyChunk] = count
2019-12-06 21:57:20 -08:00
end
2020-05-19 19:37:16 -07:00
vengenceQueue[rallyChunk] = count + 1
2019-12-06 21:57:20 -08:00
end
end
end
end
2020-05-19 19:37:16 -07:00
return true
2018-02-16 19:31:29 -08:00
end
end
2021-02-19 21:41:30 -08:00
function aiAttackWave.formSettlers(map, chunk)
2021-02-19 23:31:36 -08:00
local universe = map.universe
if (universe.builderCount < universe.AI_MAX_BUILDER_COUNT) and
(mRandom() < universe.formSquadThreshold) and
((map.points - AI_SETTLER_COST) > 0)
2020-05-22 12:43:44 -07:00
then
2021-02-19 21:41:30 -08:00
local surface = map.surface
2019-02-05 22:25:43 -08:00
local squadPath, squadDirection
2021-02-19 23:31:36 -08:00
if (map.state == AI_STATE_SIEGE) then
2019-02-05 22:25:43 -08:00
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
2020-05-19 19:37:16 -07:00
if (squadPath ~= -1) then
local squadPosition = surface.find_non_colliding_position("chunk-scanner-squad-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
universe.position,
0.98),
CHUNK_SIZE,
4,
2019-03-05 22:18:03 -08:00
true)
if squadPosition then
local squad = createSquad(squadPosition, surface, nil, true)
2021-02-19 23:31:36 -08:00
squad.maxDistance = gaussianRandomRange(universe.expansionMaxDistance * 0.5,
universe.expansionMaxDistanceDerivation,
10,
2021-02-19 23:31:36 -08:00
universe.expansionMaxDistance)
2019-02-05 22:25:43 -08:00
2021-02-19 23:31:36 -08:00
local scaledWaveSize = settlerWaveScaling(universe)
universe.formGroupCommand.group = squad.group
universe.formCommand.unit_count = scaledWaveSize
local foundUnits = surface.set_multi_command(universe.formCommand)
if (foundUnits > 0) then
2021-04-29 22:24:14 -07:00
if universe.NEW_ENEMIES then
squad.base = findNearbyBase(map, chunk)
end
squad.kamikaze = mRandom() < calculateKamikazeThreshold(foundUnits, universe)
2021-02-19 23:31:36 -08:00
universe.builderCount = universe.builderCount + 1
map.points = map.points - AI_SETTLER_COST
map.groupNumberToSquad[squad.groupNumber] = squad
2019-03-06 22:12:39 -08:00
else
if (squad.group.valid) then
squad.group.destroy()
end
end
end
end
end
end
2021-02-19 21:41:30 -08:00
function aiAttackWave.formVengenceSquad(map, chunk)
2021-02-19 23:31:36 -08:00
local universe = map.universe
if (universe.squadCount < universe.AI_MAX_SQUAD_COUNT) and
(mRandom() < universe.formSquadThreshold) and
((map.points - AI_VENGENCE_SQUAD_COST) > 0)
2020-05-23 14:29:56 -07:00
then
2021-02-19 21:41:30 -08:00
local surface = map.surface
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
validUnitGroupLocation,
scoreUnitGroupLocation,
map)
2020-05-15 13:51:38 -07:00
if (squadPath ~= -1) then
local squadPosition = surface.find_non_colliding_position("chunk-scanner-squad-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
universe.position,
0.98),
CHUNK_SIZE,
4,
2019-03-05 22:18:03 -08:00
true)
if squadPosition then
local squad = createSquad(squadPosition, surface)
2019-02-05 22:25:43 -08:00
squad.rabid = mRandom() < 0.03
2019-02-19 22:16:43 -08:00
2021-02-19 23:31:36 -08:00
local scaledWaveSize = attackWaveScaling(universe)
universe.formGroupCommand.group = squad.group
universe.formCommand.unit_count = scaledWaveSize
local foundUnits = surface.set_multi_command(universe.formCommand)
if (foundUnits > 0) then
2021-04-29 22:24:14 -07:00
if universe.NEW_ENEMIES then
squad.base = findNearbyBase(map, chunk)
end
squad.kamikaze = mRandom() < calculateKamikazeThreshold(foundUnits, universe)
2021-02-19 23:31:36 -08:00
map.groupNumberToSquad[squad.groupNumber] = squad
universe.squadCount = universe.squadCount + 1
map.points = map.points - AI_VENGENCE_SQUAD_COST
2019-03-06 22:12:39 -08:00
else
if (squad.group.valid) then
squad.group.destroy()
end
end
end
end
2019-02-19 22:16:43 -08:00
end
end
2021-02-19 21:41:30 -08:00
function aiAttackWave.formSquads(map, chunk, tick)
2021-02-19 23:31:36 -08:00
local universe = map.universe
if (universe.squadCount < universe.AI_MAX_SQUAD_COUNT) and
attackWaveValidCandidate(chunk, map) and
(mRandom() < universe.formSquadThreshold) and
((map.points - AI_SQUAD_COST) > 0)
2019-02-19 22:16:43 -08:00
then
2021-02-19 23:31:36 -08:00
local surface = map.surface
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
validUnitGroupLocation,
scoreUnitGroupLocation,
map)
2020-05-15 13:51:38 -07:00
if (squadPath ~= -1) then
local squadPosition = surface.find_non_colliding_position("chunk-scanner-squad-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
universe.position,
0.98),
CHUNK_SIZE,
4,
2019-03-05 22:18:03 -08:00
true)
if squadPosition then
local squad = createSquad(squadPosition, surface)
2019-02-05 22:25:43 -08:00
squad.rabid = mRandom() < 0.03
2017-06-08 22:18:59 -07:00
2021-02-19 23:31:36 -08:00
local scaledWaveSize = attackWaveScaling(universe)
universe.formGroupCommand.group = squad.group
universe.formCommand.unit_count = scaledWaveSize
local foundUnits = surface.set_multi_command(universe.formCommand)
if (foundUnits > 0) then
2021-04-29 22:24:14 -07:00
if universe.NEW_ENEMIES then
squad.base = findNearbyBase(map, chunk)
end
squad.kamikaze = mRandom() < calculateKamikazeThreshold(foundUnits, universe)
2021-02-19 23:31:36 -08:00
map.points = map.points - AI_SQUAD_COST
universe.squadCount = universe.squadCount + 1
map.groupNumberToSquad[squad.groupNumber] = squad
if tick and (map.state == AI_STATE_AGGRESSIVE) then
map.canAttackTick = randomTickEvent(tick,
2021-04-29 22:24:14 -07:00
AGGRESSIVE_CAN_ATTACK_WAIT_MIN_DURATION,
AGGRESSIVE_CAN_ATTACK_WAIT_MAX_DURATION)
2019-04-24 23:13:22 -07:00
end
2019-03-06 22:12:39 -08:00
else
if (squad.group.valid) then
squad.group.destroy()
end
end
2019-03-06 22:12:39 -08:00
end
end
2016-08-18 19:02:13 -07:00
end
end
2019-02-19 22:16:43 -08:00
2019-02-15 20:17:30 -08:00
aiAttackWaveG = aiAttackWave
return aiAttackWave