2016-08-18 07:55:08 +02:00
|
|
|
local aiAttack = {}
|
|
|
|
|
2016-08-20 04:52:27 +02:00
|
|
|
-- imports
|
2016-08-18 07:55:08 +02:00
|
|
|
|
|
|
|
local constants = require("Constants")
|
|
|
|
local mapUtils = require("MapUtils")
|
|
|
|
local unitGroupUtils = require("UnitGroupUtils")
|
2016-08-27 08:44:17 +02:00
|
|
|
local playerUtils = require("PlayerUtils")
|
|
|
|
local neighborUtils = require("NeighborUtils")
|
2016-08-18 07:55:08 +02:00
|
|
|
|
2016-08-20 04:52:27 +02:00
|
|
|
-- constants
|
|
|
|
|
|
|
|
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
|
|
|
|
local DEATH_PHEROMONE = constants.DEATH_PHEROMONE
|
|
|
|
local ENEMY_BASE_PHEROMONE = constants.ENEMY_BASE_PHEROMONE
|
|
|
|
local PLAYER_BASE_PHEROMONE = constants.PLAYER_BASE_PHEROMONE
|
2016-09-14 19:12:29 +02:00
|
|
|
--local PLAYER_DEFENSE_PHEROMONE = constants.PLAYER_DEFENSE_PHEROMONE
|
2016-08-20 04:52:27 +02:00
|
|
|
|
|
|
|
local SQUAD_RAIDING = constants.SQUAD_RAIDING
|
|
|
|
local SQUAD_SUICIDE_RAID = constants.SQUAD_SUICIDE_RAID
|
|
|
|
local SQUAD_HUNTING = constants.SQUAD_HUNTING
|
|
|
|
local SQUAD_GUARDING = constants.SQUAD_GUARDING
|
|
|
|
local SQUAD_SUICIDE_HUNT = constants.SQUAD_SUICIDE_HUNT
|
|
|
|
|
2016-09-14 19:12:29 +02:00
|
|
|
--local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
|
|
|
|
--local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
|
2016-08-20 04:52:27 +02:00
|
|
|
|
2016-09-14 19:12:29 +02:00
|
|
|
--local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
|
|
|
|
--local CHUNK_SIZE = constants.CHUNK_SIZE
|
2016-08-20 04:52:27 +02:00
|
|
|
|
|
|
|
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
|
|
|
|
local PLAYER_DEFENSE_GENERATOR = constants.PLAYER_DEFENSE_GENERATOR
|
|
|
|
|
|
|
|
-- imported functions
|
|
|
|
|
2016-08-26 00:20:06 +02:00
|
|
|
local getCardinalChunksWithDirection = mapUtils.getCardinalChunksWithDirection
|
2016-08-28 02:57:20 +02:00
|
|
|
local getChunkByPosition = mapUtils.getChunkByPosition
|
|
|
|
local canMoveChunkDirectionCardinal = mapUtils.canMoveChunkDirectionCardinal
|
2016-08-20 04:52:27 +02:00
|
|
|
local addSquadMovementPenalty = unitGroupUtils.addSquadMovementPenalty
|
|
|
|
local lookupSquadMovementPenalty = unitGroupUtils.lookupSquadMovementPenalty
|
2016-08-27 08:44:17 +02:00
|
|
|
local positionFromDirectionAndChunkCardinal = mapUtils.positionFromDirectionAndChunkCardinal
|
|
|
|
|
2016-09-14 19:12:29 +02:00
|
|
|
local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
|
|
|
|
|
2016-08-27 08:44:17 +02:00
|
|
|
local playersWithinProximityToPosition = playerUtils.playersWithinProximityToPosition
|
|
|
|
|
|
|
|
local scoreNeighborsWithDirection = neighborUtils.scoreNeighborsWithDirection
|
2016-08-18 07:55:08 +02:00
|
|
|
|
2016-09-14 19:12:29 +02:00
|
|
|
|
2016-08-28 02:57:20 +02:00
|
|
|
|
2016-08-20 04:52:27 +02:00
|
|
|
-- module code
|
|
|
|
|
2016-08-27 08:44:17 +02:00
|
|
|
local function validLocation(x, chunk, neighborChunk)
|
2016-08-28 02:57:20 +02:00
|
|
|
return canMoveChunkDirectionCardinal(x, chunk, neighborChunk)
|
2016-08-27 08:44:17 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
local function scoreAttackLocation(position, squad, neighborChunk, surface)
|
|
|
|
local squadMovementPenalty = lookupSquadMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY)
|
|
|
|
local damageScore = surface.get_pollution(position) + neighborChunk[PLAYER_BASE_PHEROMONE] + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[PLAYER_DEFENSE_GENERATOR]
|
|
|
|
local avoidScore = neighborChunk[DEATH_PHEROMONE] + neighborChunk[ENEMY_BASE_PHEROMONE]
|
|
|
|
return damageScore - avoidScore - squadMovementPenalty
|
|
|
|
end
|
|
|
|
|
|
|
|
local function scoreHuntPlayerLocation(position, squad, neighborChunk, surface)
|
|
|
|
local squadMovementPenalty = lookupSquadMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY)
|
|
|
|
local damageScore = neighborChunk[PLAYER_PHEROMONE]
|
2016-08-28 02:57:20 +02:00
|
|
|
local avoidScore = neighborChunk[DEATH_PHEROMONE] + neighborChunk[ENEMY_BASE_PHEROMONE] + neighborChunk[PLAYER_DEFENSE_GENERATOR]
|
2016-08-27 08:44:17 +02:00
|
|
|
return damageScore - avoidScore - squadMovementPenalty
|
|
|
|
end
|
|
|
|
|
2016-09-14 19:12:29 +02:00
|
|
|
function aiAttack.squadAttack(regionMap, surface, natives)
|
2016-08-26 00:20:06 +02:00
|
|
|
local squads = natives.squads
|
2016-09-14 19:12:29 +02:00
|
|
|
local attackPosition
|
|
|
|
local attackCmd
|
|
|
|
if (#squads > 0) then
|
|
|
|
attackPosition = {x=0, y=0}
|
|
|
|
attackCmd = { type = defines.command.attack_area,
|
|
|
|
destination = attackPosition,
|
|
|
|
radius = 16,
|
|
|
|
distraction = defines.distraction.by_enemy }
|
|
|
|
end
|
2016-08-26 00:20:06 +02:00
|
|
|
for i=1,#squads do
|
|
|
|
local squad = squads[i]
|
2016-08-18 07:55:08 +02:00
|
|
|
local group = squad.group
|
2016-08-28 22:48:19 +02:00
|
|
|
local raiding = false
|
|
|
|
local hunting = false
|
|
|
|
local scoreLocation
|
|
|
|
if (squad.status == SQUAD_RAIDING) or (squad.status == SQUAD_SUICIDE_RAID) then
|
|
|
|
raiding = true
|
|
|
|
scoreLocation = scoreAttackLocation
|
|
|
|
elseif (squad.status == SQUAD_HUNTING) or (squad.status == SQUAD_SUICIDE_HUNT) then
|
|
|
|
hunting = true
|
|
|
|
scoreLocation = scoreHuntPlayerLocation
|
2016-08-18 07:55:08 +02:00
|
|
|
end
|
2016-08-28 22:48:19 +02:00
|
|
|
if group.valid and (raiding or hunting) then
|
2016-09-14 19:12:29 +02:00
|
|
|
if (group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering) or ((group.state == defines.group_state.moving) and (squad.cycles == 0)) then
|
2016-08-28 02:57:20 +02:00
|
|
|
local chunk = getChunkByPosition(regionMap, group.position.x, group.position.y)
|
2016-08-27 08:44:17 +02:00
|
|
|
if (chunk ~= nil) then
|
|
|
|
addSquadMovementPenalty(squad, chunk.cX, chunk.cY)
|
|
|
|
local attackChunk, attackDirection = scoreNeighborsWithDirection(chunk,
|
2016-09-13 01:23:26 +02:00
|
|
|
getCardinalChunksWithDirection(regionMap, chunk.cX, chunk.cY),
|
2016-08-27 08:44:17 +02:00
|
|
|
validLocation,
|
2016-08-28 22:48:19 +02:00
|
|
|
scoreLocation,
|
2016-08-27 08:44:17 +02:00
|
|
|
squad,
|
2016-08-30 06:08:22 +02:00
|
|
|
surface,
|
|
|
|
attackPosition)
|
2016-08-27 08:44:17 +02:00
|
|
|
if (attackChunk ~= nil) then
|
|
|
|
if ((attackChunk[PLAYER_BASE_GENERATOR] == 0) and (attackChunk[PLAYER_DEFENSE_GENERATOR] == 0)) or
|
|
|
|
((group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering)) then
|
|
|
|
|
2016-09-14 19:12:29 +02:00
|
|
|
positionFromDirectionAndChunkCardinal(attackDirection, squad.group.position, attackPosition)
|
|
|
|
|
|
|
|
squad.cycles = 3
|
|
|
|
|
|
|
|
if not squad.rabid and squad.frenzy and (euclideanDistanceNamed(squad.group.position, squad.frenzyPosition) > 100) then
|
|
|
|
squad.frenzy = false
|
|
|
|
end
|
|
|
|
|
|
|
|
if squad.rabid or squad.frenzy then
|
|
|
|
attackCmd.distraction = defines.distraction.by_anything
|
|
|
|
else
|
|
|
|
attackCmd.distraction = defines.distraction.by_enemy
|
|
|
|
end
|
|
|
|
|
|
|
|
group.set_command(attackCmd)
|
2016-08-27 08:44:17 +02:00
|
|
|
group.start_moving()
|
2016-09-14 19:12:29 +02:00
|
|
|
elseif not squad.frenzy and not squad.rabid and
|
|
|
|
(((group.state == defines.group_state.attacking_distraction) or (group.state == defines.group_state.attacking_distraction)) or
|
|
|
|
(attackChunk[PLAYER_BASE_GENERATOR] ~= 0) or (attackChunk[PLAYER_DEFENSE_GENERATOR] ~= 0)) then
|
|
|
|
squad.frenzy = true
|
|
|
|
squad.frenzyPosition.x = squad.group.position.x
|
|
|
|
squad.frenzyPosition.y = squad.group.position.y
|
|
|
|
end
|
2016-08-20 04:52:27 +02:00
|
|
|
end
|
2016-08-18 07:55:08 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-08-27 08:44:17 +02:00
|
|
|
function aiAttack.squadBeginAttack(natives, players, evolution_factor)
|
2016-08-26 00:20:06 +02:00
|
|
|
local squads = natives.squads
|
|
|
|
for i=1,#squads do
|
|
|
|
local squad = squads[i]
|
2016-08-27 08:44:17 +02:00
|
|
|
if (squad.status == SQUAD_GUARDING) and squad.group.valid then
|
2016-08-28 04:03:36 +02:00
|
|
|
local threshold = 0.05 + (evolution_factor * 0.20) + (#squad.group.members * 0.0033)
|
2016-09-14 19:12:29 +02:00
|
|
|
|
|
|
|
local playerNearby = playersWithinProximityToPosition(players, squad.group.position, 100)
|
|
|
|
if playerNearby then
|
|
|
|
squad.frenzy = true
|
|
|
|
squad.frenzyPosition.x = squad.group.position.x
|
|
|
|
squad.frenzyPosition.y = squad.group.position.y
|
|
|
|
end
|
|
|
|
|
2016-08-27 08:44:17 +02:00
|
|
|
-- check to hunt player
|
2016-09-14 19:12:29 +02:00
|
|
|
if (math.random() < 0.30) and playerNearby then
|
2016-08-28 04:03:36 +02:00
|
|
|
if (math.random() < threshold) then
|
2016-08-27 08:44:17 +02:00
|
|
|
squad.status = SQUAD_SUICIDE_HUNT
|
|
|
|
else
|
|
|
|
squad.status = SQUAD_HUNTING
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- check to raid base
|
|
|
|
if (squad.status == SQUAD_GUARDING) and (math.random() < 0.70) then
|
2016-09-14 19:12:29 +02:00
|
|
|
if (math.random() < threshold) then
|
2016-08-27 08:44:17 +02:00
|
|
|
squad.status = SQUAD_SUICIDE_RAID
|
|
|
|
else
|
|
|
|
squad.status = SQUAD_RAIDING
|
|
|
|
end
|
2016-08-18 07:55:08 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-08-30 06:08:22 +02:00
|
|
|
return aiAttack
|