mirror of
https://github.com/veden/Rampant.git
synced 2026-06-19 22:15:20 +02:00
see readme
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Rampant Tactics
|
||||
Factorio Mod - Improves the enemies tactics by using potential fields/pheromones allowing probing of defenses, retreats, reinforcements, counterattacking, rallying death cry, and player hunting. Also removes homing biter projectiles.
|
||||
Factorio Mod - Improves the enemies tactics by using potential fields/pheromones allowing probing of defenses, retreats, reinforcements, counterattacking, breaching, rallying death cry, and player hunting. Also removes homing biter projectiles.
|
||||
|
||||
# Forum Post
|
||||
|
||||
@@ -25,6 +25,8 @@ Configure Options:
|
||||
|
||||
# Features
|
||||
|
||||
- Recycling Biters - When large groups of biters form on the game map and aren't assigned to a unit group or near a base will be periodically removed and refunded to the ai causes attack waves proportional to the number of units removed.
|
||||
- Breaching - When biters are destroying structures nearby unit groups will come to join them
|
||||
- Frenzy squads - When a unit group gets close to a player or start combat they switch to attacking everything in there path for a set radius or until there is nothing left
|
||||
- Rabid squads - Is in a permanent frenzied state as soon as the group is formed
|
||||
- Tactical Retreats - these will take place when a unit group is in a chunk that has reached a death threshold
|
||||
@@ -50,6 +52,18 @@ Configure Options:
|
||||
|
||||
# Version History
|
||||
|
||||
0.14.13 -
|
||||
- Reverted: Pheromone generation tweaks from 0.14.11
|
||||
- Feature: Recycling large biter swarms that are stuck (causes a big attack wave, and pops up a message)
|
||||
- Feature: Biters destroying player buildings will attract other unit groups
|
||||
- Tweak: Reduced unit group max size from 600 to 450
|
||||
- Tweak: Reduced unit group radius from 30 to 20
|
||||
- Fix: Unit groups merging during combat
|
||||
- Optimization: Cleaned up regroup squad function to scale better
|
||||
- Optimization: Retreats only fire once per chunk for every logic cycle
|
||||
- Improvement: Cleaned up dispersion function for Pheromones
|
||||
- Improvement: Increased attack wave frequency based on evolution factor (mainly for endgame)
|
||||
|
||||
0.14.12 -
|
||||
- Tweak: Decreased slow map scan chunks scanned per logic cycle from 8 to 6
|
||||
- Fix: Didn't set new version number forcing chunk recalculation every load
|
||||
|
||||
+16
-9
@@ -47,6 +47,7 @@ local planning = aiPlanning.planning
|
||||
local rallyUnits = aiBuilding.rallyUnits
|
||||
|
||||
local deathScent = pheromoneUtils.deathScent
|
||||
local victoryScent = pheromoneUtils.victoryScent
|
||||
|
||||
local regroupSquads = unitGroupUtils.regroupSquads
|
||||
local convertUnitGroupToSquad = unitGroupUtils.convertUnitGroupToSquad
|
||||
@@ -145,7 +146,6 @@ local function onConfigChanged()
|
||||
|
||||
-- used to rate limit the number of rally cries during a period of time
|
||||
natives.rallyCries = MAX_RALLY_CRIES
|
||||
natives.retreats = MAX_RETREATS
|
||||
|
||||
global.version = constants.VERSION_13
|
||||
end
|
||||
@@ -158,7 +158,13 @@ local function onConfigChanged()
|
||||
game.surfaces[1].print("Rampant - Version 0.14.11")
|
||||
global.version = constants.VERSION_14
|
||||
end
|
||||
if (global.version < constants.VERSION_15) then
|
||||
if (global.version < constants.VERSION_16) then
|
||||
|
||||
natives.lastShakeMessage = 0
|
||||
--remove version 14 retreat limit, it has been made redundant
|
||||
natives.retreats = nil
|
||||
|
||||
game.map_settings.unit_group.max_group_radius = 20
|
||||
|
||||
-- clear old regionMap processing Queue
|
||||
-- prevents queue adding duplicate chunks
|
||||
@@ -178,8 +184,8 @@ local function onConfigChanged()
|
||||
y = chunk.y * 32 }}})
|
||||
end
|
||||
|
||||
game.surfaces[1].print("Rampant - Version 0.14.12")
|
||||
global.version = constants.VERSION_15
|
||||
game.surfaces[1].print("Rampant - Version 0.14.13")
|
||||
global.version = constants.VERSION_16
|
||||
end
|
||||
end
|
||||
|
||||
@@ -192,7 +198,7 @@ local function onTick(event)
|
||||
local players = game.players
|
||||
|
||||
processPendingChunks(regionMap, surface, pendingChunks)
|
||||
scanMap(regionMap, surface)
|
||||
scanMap(regionMap, surface, natives, evolutionFactor, tick)
|
||||
|
||||
if (tick == regionMap.logicTick) then
|
||||
regionMap.logicTick = regionMap.logicTick + INTERVAL_LOGIC
|
||||
@@ -242,15 +248,13 @@ local function onDeath(event)
|
||||
local evolutionFactor = game.evolution_factor
|
||||
|
||||
if (deathChunk[MOVEMENT_PHEROMONE] < -(evolutionFactor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)) then
|
||||
if (natives.retreats >= 0) then
|
||||
natives.retreats = natives.retreats - 1
|
||||
retreatUnits(deathChunk,
|
||||
convertUnitGroupToSquad(natives,
|
||||
entity.unit_group),
|
||||
regionMap,
|
||||
surface,
|
||||
natives)
|
||||
end
|
||||
natives,
|
||||
event.tick)
|
||||
local rallyThreshold = BASE_RALLY_CHANCE + (evolutionFactor * BONUS_RALLY_CHANCE)
|
||||
if (natives.rallyCries >= 0) and (math.random() < rallyThreshold) then
|
||||
natives.rallyCries = natives.rallyCries - 1
|
||||
@@ -272,6 +276,9 @@ local function onDeath(event)
|
||||
local creditNatives = false
|
||||
if (event.force ~= nil) and (event.force.name == "enemy") then
|
||||
creditNatives = true
|
||||
local entityPosition = entity.position
|
||||
local victoryChunk = getChunkByPosition(regionMap, entityPosition.x, entityPosition.y)
|
||||
victoryScent(victoryChunk, entity.type)
|
||||
end
|
||||
if config.safeBuildings and (config.safeEntities[entity.type] or config.safeEntityName[entity.name]) then
|
||||
makeImmortalEntity(surface, entity)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name" : "Rampant",
|
||||
"factorio_version" : "0.14",
|
||||
"version" : "0.14.12",
|
||||
"version" : "0.14.13",
|
||||
"title" : "Rampant AI",
|
||||
"author" : "Veden",
|
||||
"homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445",
|
||||
"description" : "Improves the enemies tactics by using potential fields/pheromones allowing probing of defenses, retreats, reinforcements, counterattacking, rallying death cry, and player hunting. Also removes homing biter projectiles.",
|
||||
"description" : "Improves the enemies tactics by using potential fields/pheromones allowing probing of defenses, retreats, reinforcements, counterattacking, breaching, rallying death cry, and player hunting. Also removes homing biter projectiles.",
|
||||
"dependencies" : ["base >= 0.13.17", "? bobenemies", "? Natural_Evolution_Enemies"]
|
||||
}
|
||||
|
||||
@@ -54,8 +54,11 @@ local function attackWaveValidCandidate(chunk, surface, evolutionFactor)
|
||||
local total = 0;
|
||||
|
||||
if CONFIG_USE_PLAYER_PROXIMITY then
|
||||
local playerPheromone = chunk[PLAYER_PHEROMONE]
|
||||
if (playerPheromone > 7) then
|
||||
total = total + chunk[PLAYER_PHEROMONE]
|
||||
end
|
||||
end
|
||||
if CONFIG_USE_POLLUTION_PROXIMITY then
|
||||
total = total + surface.get_pollution({chunk.pX, chunk.pY})
|
||||
end
|
||||
|
||||
+8
-3
@@ -23,6 +23,10 @@ local SQUAD_RETREATING = constants.SQUAD_RETREATING
|
||||
|
||||
local RETREAT_FILTER = constants.RETREAT_FILTER
|
||||
|
||||
local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
|
||||
|
||||
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
|
||||
|
||||
-- imported functions
|
||||
|
||||
local getNeighborChunksWithDirection = mapUtils.getNeighborChunksWithDirection
|
||||
@@ -41,12 +45,12 @@ end
|
||||
|
||||
local function scoreRetreatLocation(position, squad, neighborChunk, surface)
|
||||
local safeScore = -neighborChunk[BASE_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE]
|
||||
local dangerScore = surface.get_pollution(position) + (neighborChunk[PLAYER_PHEROMONE] * 25) + (neighborChunk[ENEMY_BASE_GENERATOR] * 50)
|
||||
local dangerScore = surface.get_pollution(position) + (neighborChunk[PLAYER_PHEROMONE] * 100) + (neighborChunk[ENEMY_BASE_GENERATOR] * 50)
|
||||
return safeScore - dangerScore
|
||||
end
|
||||
|
||||
function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives)
|
||||
if (chunk[ENEMY_BASE_GENERATOR] == 0) then
|
||||
function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick)
|
||||
if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (chunk[ENEMY_BASE_GENERATOR] == 0) then
|
||||
local performRetreat = false
|
||||
local enemiesToSquad
|
||||
|
||||
@@ -63,6 +67,7 @@ function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives)
|
||||
end
|
||||
|
||||
if performRetreat then
|
||||
chunk[RETREAT_TRIGGERED] = tick
|
||||
local retreatPosition = {x=0, y=0}
|
||||
local exitPath,_ = scoreNeighborsWithDirection(chunk,
|
||||
getNeighborChunksWithDirection(regionMap, chunk.cX, chunk.cY),
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ local mMax = math.max
|
||||
function aiPlanning.planning(natives, evolution_factor, tick)
|
||||
local maxPoints = AI_MAX_POINTS * evolution_factor
|
||||
if (natives.points < maxPoints) then
|
||||
natives.points = natives.points + math.floor(AI_POINT_GENERATOR_AMOUNT * math.random())
|
||||
natives.points = natives.points + math.floor((AI_POINT_GENERATOR_AMOUNT * math.random()) + ((AI_POINT_GENERATOR_AMOUNT * 0.7) * (evolution_factor ^ 2.5)))
|
||||
end
|
||||
|
||||
if (natives.temperamentTick == tick) then
|
||||
|
||||
@@ -24,6 +24,8 @@ local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GEN
|
||||
|
||||
local CHUNK_TICK = constants.CHUNK_TICK
|
||||
|
||||
local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED
|
||||
|
||||
-- module code
|
||||
|
||||
function chunkUtils.checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, surface)
|
||||
@@ -116,6 +118,7 @@ function chunkUtils.createChunk(topX, topY)
|
||||
chunk[NORTH_SOUTH_PASSABLE] = false
|
||||
chunk[EAST_WEST_PASSABLE] = false
|
||||
chunk[CHUNK_TICK] = 0
|
||||
chunk[RETREAT_TRIGGERED] = 0
|
||||
return chunk
|
||||
end
|
||||
|
||||
|
||||
+26
-23
@@ -10,6 +10,7 @@ constants.VERSION_12 = 12
|
||||
constants.VERSION_13 = 13
|
||||
constants.VERSION_14 = 14
|
||||
constants.VERSION_15 = 15
|
||||
constants.VERSION_16 = 16
|
||||
|
||||
-- misc
|
||||
|
||||
@@ -37,8 +38,10 @@ constants.AI_BASE_BUILDING_COST = 500
|
||||
constants.AI_TUNNEL_COST = 100
|
||||
constants.AI_MAX_POINTS = 10000
|
||||
|
||||
constants.AI_UNIT_REFUND = 2
|
||||
|
||||
constants.AI_MAX_SQUAD_COUNT = 40
|
||||
constants.AI_MAX_BITER_GROUP_SIZE = 600
|
||||
constants.AI_MAX_BITER_GROUP_SIZE = 450
|
||||
|
||||
constants.AI_STATE_PEACEFUL = 1
|
||||
constants.AI_STATE_AGGRESSIVE = 2
|
||||
@@ -66,17 +69,16 @@ constants.EAST_WEST = 2
|
||||
|
||||
constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = 500
|
||||
constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = 30
|
||||
constants.DEATH_PHEROMONE_GENERATOR_AMOUNT = 75
|
||||
constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT = 100
|
||||
constants.DEATH_PHEROMONE_GENERATOR_AMOUNT = 500
|
||||
constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT = 150
|
||||
|
||||
constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT = -30
|
||||
|
||||
-- pheromone diffusion amounts
|
||||
|
||||
constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT = 0.1
|
||||
constants.MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT = 0.02
|
||||
|
||||
constants.MOVEMENT_PHEROMONE_PERSISTANCE = 0.98
|
||||
constants.REDUCED_MOVEMENT_PHEROMONE_PERSISTANCE = 0.65
|
||||
constants.STANDARD_PHEROMONE_PERSISTANCE = 0.98
|
||||
constants.BASE_PHEROMONE_PERSISTANCE = 0.99
|
||||
constants.PLAYER_PHEROMONE_PERSISTANCE = 0.98
|
||||
|
||||
-- chunk attributes
|
||||
|
||||
@@ -91,6 +93,7 @@ constants.NORTH_SOUTH_PASSABLE = 6
|
||||
constants.EAST_WEST_PASSABLE = 7
|
||||
|
||||
constants.CHUNK_TICK = 8
|
||||
constants.RETREAT_TRIGGERED = 9
|
||||
|
||||
-- Squad status
|
||||
|
||||
@@ -116,26 +119,26 @@ constants.GROUP_MERGE_DISTANCE = 28
|
||||
-- player building pheromones
|
||||
|
||||
constants.BUILDING_PHEROMONES = {}
|
||||
constants.BUILDING_PHEROMONES["generator"] = 12
|
||||
constants.BUILDING_PHEROMONES["pump"] = 5
|
||||
constants.BUILDING_PHEROMONES["offshore-pump"] = 5
|
||||
constants.BUILDING_PHEROMONES["transport-belt"] = 2
|
||||
constants.BUILDING_PHEROMONES["accumulator"] = 14
|
||||
constants.BUILDING_PHEROMONES["solar-panel"] = 12
|
||||
constants.BUILDING_PHEROMONES["boiler"] = 16
|
||||
constants.BUILDING_PHEROMONES["assembling-machine"] = 16
|
||||
constants.BUILDING_PHEROMONES["roboport"] = 14
|
||||
constants.BUILDING_PHEROMONES["beacon"] = 14
|
||||
constants.BUILDING_PHEROMONES["furnace"] = 16
|
||||
constants.BUILDING_PHEROMONES["mining-drill"] = 19
|
||||
constants.BUILDING_PHEROMONES["generator"] = 8
|
||||
constants.BUILDING_PHEROMONES["pump"] = 2
|
||||
constants.BUILDING_PHEROMONES["offshore-pump"] = 2
|
||||
constants.BUILDING_PHEROMONES["transport-belt"] = 1
|
||||
constants.BUILDING_PHEROMONES["accumulator"] = 10
|
||||
constants.BUILDING_PHEROMONES["solar-panel"] = 8
|
||||
constants.BUILDING_PHEROMONES["boiler"] = 12
|
||||
constants.BUILDING_PHEROMONES["assembling-machine"] = 10
|
||||
constants.BUILDING_PHEROMONES["roboport"] = 10
|
||||
constants.BUILDING_PHEROMONES["beacon"] = 10
|
||||
constants.BUILDING_PHEROMONES["furnace"] = 12
|
||||
constants.BUILDING_PHEROMONES["mining-drill"] = 15
|
||||
|
||||
-- player defense pheromones
|
||||
|
||||
constants.BUILDING_PHEROMONES["ammo-turret"] = 5
|
||||
constants.BUILDING_PHEROMONES["wall"] = 0.55
|
||||
constants.BUILDING_PHEROMONES["ammo-turret"] = 2.5
|
||||
constants.BUILDING_PHEROMONES["wall"] = 0.25
|
||||
constants.BUILDING_PHEROMONES["electric-turret"] = 7
|
||||
constants.BUILDING_PHEROMONES["fluid-turret"] = 9
|
||||
constants.BUILDING_PHEROMONES["turret"] = 5
|
||||
constants.BUILDING_PHEROMONES["turret"] = 2.5
|
||||
|
||||
constants.retreatFilter = {}
|
||||
constants.retreatFilter[constants.SQUAD_RETREATING] = true
|
||||
|
||||
+38
-5
@@ -16,6 +16,7 @@ local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LE
|
||||
|
||||
local SCAN_QUEUE_SIZE = constants.SCAN_QUEUE_SIZE
|
||||
|
||||
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
|
||||
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
|
||||
@@ -23,6 +24,7 @@ local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
|
||||
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
|
||||
local CHUNK_TICK = constants.CHUNK_TICK
|
||||
|
||||
local AI_MAX_POINTS = constants.AI_MAX_POINTS
|
||||
local AI_SQUAD_COST = constants.AI_SQUAD_COST
|
||||
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
|
||||
|
||||
@@ -30,6 +32,8 @@ local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
|
||||
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
|
||||
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
|
||||
|
||||
local TICKS_A_MINUTE = constants.TICKS_A_MINUTE
|
||||
|
||||
-- imported functions
|
||||
|
||||
local scents = pheromoneUtils.scents
|
||||
@@ -92,7 +96,7 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
for x=index,endIndex do
|
||||
local chunk = processQueue[x]
|
||||
|
||||
scents(chunk)
|
||||
processPheromone(regionMap, chunk)
|
||||
|
||||
if scouts then
|
||||
makeScouts(surface, natives, chunk, evolution_factor)
|
||||
@@ -101,7 +105,7 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
formSquads(regionMap, surface, natives, chunk, evolution_factor, AI_SQUAD_COST)
|
||||
end
|
||||
|
||||
processPheromone(regionMap, chunk)
|
||||
scents(chunk)
|
||||
end
|
||||
|
||||
if (endIndex == #processQueue) then
|
||||
@@ -163,7 +167,8 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
|
||||
|
||||
if (chunk ~= nil) and (chunk[CHUNK_TICK] ~= tick) then
|
||||
chunk[CHUNK_TICK] = tick
|
||||
scents(chunk)
|
||||
|
||||
processPheromone(regionMap, chunk)
|
||||
|
||||
if scouts then
|
||||
makeScouts(surface, natives, chunk, evolution_factor)
|
||||
@@ -175,7 +180,8 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
|
||||
formSquads(regionMap, surface, natives, chunk, evolution_factor, AI_VENGENCE_SQUAD_COST)
|
||||
end
|
||||
|
||||
processPheromone(regionMap, chunk)
|
||||
scents(chunk)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -187,7 +193,7 @@ end
|
||||
--[[
|
||||
Passive scan to find entities that have been generated outside the factorio event system
|
||||
--]]
|
||||
function mapProcessor.scanMap(regionMap, surface)
|
||||
function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor, tick)
|
||||
local index = regionMap.scanPointer
|
||||
|
||||
local processQueue = regionMap.processQueue
|
||||
@@ -203,10 +209,37 @@ function mapProcessor.scanMap(regionMap, surface)
|
||||
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
|
||||
type = "unit-spawner",
|
||||
force = "enemy"})
|
||||
|
||||
local worms = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY},
|
||||
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
|
||||
type = "turret",
|
||||
force = "enemy"})
|
||||
|
||||
local unitCount = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY},
|
||||
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
|
||||
type = "unit",
|
||||
force = "enemy"})
|
||||
|
||||
if (unitCount > 550) then
|
||||
local weight = AI_UNIT_REFUND * evolution_factor
|
||||
local units = surface.find_enemy_units({chunk.pX, chunk.pY},
|
||||
CHUNK_SIZE * 3)
|
||||
|
||||
for i=1,#units do
|
||||
units[i].destroy()
|
||||
natives.points = natives.points + weight
|
||||
end
|
||||
|
||||
if (natives.points > (AI_MAX_POINTS * 3)) then
|
||||
natives.points = (AI_MAX_POINTS * 3)
|
||||
end
|
||||
|
||||
if (tick - natives.lastShakeMessage > TICKS_A_MINUTE * 5) then
|
||||
natives.lastShakeMessage = tick
|
||||
surface.print("Rampant: The ground begins to shake")
|
||||
end
|
||||
end
|
||||
|
||||
local playerBaseGenerator = 0
|
||||
for i=1,#entities do
|
||||
local entity = entities[i]
|
||||
|
||||
+30
-51
@@ -11,40 +11,45 @@ local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
|
||||
local BASE_PHEROMONE = constants.BASE_PHEROMONE
|
||||
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
|
||||
|
||||
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
|
||||
|
||||
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
|
||||
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
|
||||
|
||||
local PLAYER_PHEROMONE_GENERATOR_AMOUNT = constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
||||
local DEATH_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
|
||||
|
||||
local STANDARD_PHERONOME_DIFFUSION_AMOUNT = constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT
|
||||
local MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT = constants.MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT
|
||||
local DEATH_PHEROMONE_GENERATOR_AMOUNT = constants.DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||
|
||||
local MOVEMENT_PHEROMONE_PERSISTANCE = constants.MOVEMENT_PHEROMONE_PERSISTANCE
|
||||
local REDUCED_MOVEMENT_PHEROMONE_PERSISTANCE = constants.REDUCED_MOVEMENT_PHEROMONE_PERSISTANCE
|
||||
local STANDARD_PHEROMONE_PERSISTANCE = constants.STANDARD_PHEROMONE_PERSISTANCE
|
||||
local BASE_PHEROMONE_PERSISTANCE = constants.BASE_PHEROMONE_PERSISTANCE
|
||||
local PLAYER_PHEROMONE_PERSISTANCE = constants.PLAYER_PHEROMONE_PERSISTANCE
|
||||
|
||||
local NORTH_SOUTH_PASSABLE = constants.NORTH_SOUTH_PASSABLE
|
||||
local EAST_WEST_PASSABLE = constants.EAST_WEST_PASSABLE
|
||||
|
||||
local IMPASSABLE_TERRAIN_GENERATOR_AMOUNT = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT
|
||||
|
||||
-- imported functions
|
||||
|
||||
local getChunkByPosition = mapUtils.getChunkByPosition
|
||||
local getCardinalChunks = mapUtils.getCardinalChunks
|
||||
|
||||
-- module code
|
||||
|
||||
function pheromoneUtils.scents(chunk)
|
||||
-- if (chunk[]) then
|
||||
-- end
|
||||
|
||||
if not chunk[NORTH_SOUTH_PASSABLE] and not chunk[EAST_WEST_PASSABLE] then
|
||||
chunk[BASE_PHEROMONE] = -100;
|
||||
chunk[BASE_PHEROMONE] = IMPASSABLE_TERRAIN_GENERATOR_AMOUNT;
|
||||
else
|
||||
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + chunk[PLAYER_BASE_GENERATOR] -- chunk[ENEMY_BASE_GENERATOR]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function pheromoneUtils.victoryScent(chunk, entityType)
|
||||
local value = BUILDING_PHEROMONES[entityType]
|
||||
if (value ~= nil) then
|
||||
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] + (value * 10000)
|
||||
end
|
||||
end
|
||||
|
||||
function pheromoneUtils.deathScent(chunk)
|
||||
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] - DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||
end
|
||||
@@ -54,52 +59,26 @@ function pheromoneUtils.playerScent(playerChunk)
|
||||
end
|
||||
|
||||
function pheromoneUtils.processPheromone(regionMap, chunk)
|
||||
local neighbors
|
||||
|
||||
-- pheromone level indexes on chunks are 1 - 3
|
||||
-- unrolled loop one level
|
||||
local diffusionAmount = MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT
|
||||
local persistence
|
||||
if (chunk[BASE_PHEROMONE] < 0) then
|
||||
persistence = REDUCED_MOVEMENT_PHEROMONE_PERSISTANCE
|
||||
else
|
||||
persistence = MOVEMENT_PHEROMONE_PERSISTANCE
|
||||
if not chunk[NORTH_SOUTH_PASSABLE] and not chunk[EAST_WEST_PASSABLE] then
|
||||
return
|
||||
end
|
||||
local totalDiffused = 0
|
||||
local chunkValue = chunk[MOVEMENT_PHEROMONE] * persistence
|
||||
local diffusedAmount = chunkValue * diffusionAmount
|
||||
if (diffusedAmount > 1.5) or (diffusedAmount < -1.5) then
|
||||
neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
|
||||
|
||||
local totalMovement = 0
|
||||
local totalBase = 0
|
||||
local totalPlayer = 0
|
||||
local neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
|
||||
for i=1,#neighbors do
|
||||
local neighborChunk = neighbors[i]
|
||||
if (neighborChunk ~= nil) and (neighborChunk[NORTH_SOUTH_PASSABLE] or neighborChunk[EAST_WEST_PASSABLE]) then
|
||||
totalDiffused = totalDiffused + diffusedAmount
|
||||
neighborChunk[MOVEMENT_PHEROMONE] = neighborChunk[MOVEMENT_PHEROMONE] + diffusedAmount
|
||||
if (neighborChunk ~= nil) then
|
||||
totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunk[MOVEMENT_PHEROMONE])
|
||||
totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunk[BASE_PHEROMONE])
|
||||
totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunk[PLAYER_PHEROMONE])
|
||||
end
|
||||
end
|
||||
end
|
||||
chunk[MOVEMENT_PHEROMONE] = (chunkValue - totalDiffused)
|
||||
|
||||
diffusionAmount = STANDARD_PHERONOME_DIFFUSION_AMOUNT
|
||||
persistence = STANDARD_PHEROMONE_PERSISTANCE
|
||||
for x=2,3 do
|
||||
totalDiffused = 0
|
||||
chunkValue = chunk[x] * persistence
|
||||
diffusedAmount = chunkValue * diffusionAmount
|
||||
if (diffusedAmount > 1.5) or (diffusedAmount < -1.5) then
|
||||
if (neighbors == nil) then
|
||||
neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
|
||||
end
|
||||
for i=1,#neighbors do
|
||||
local neighborChunk = neighbors[i]
|
||||
if (neighborChunk ~= nil) and (neighborChunk[NORTH_SOUTH_PASSABLE] or neighborChunk[EAST_WEST_PASSABLE]) then
|
||||
totalDiffused = totalDiffused + diffusedAmount
|
||||
neighborChunk[x] = neighborChunk[x] + diffusedAmount
|
||||
end
|
||||
end
|
||||
end
|
||||
chunk[x] = (chunkValue - totalDiffused)
|
||||
end
|
||||
chunk[MOVEMENT_PHEROMONE] = (chunk[MOVEMENT_PHEROMONE] + (0.125 * totalMovement)) * MOVEMENT_PHEROMONE_PERSISTANCE
|
||||
chunk[BASE_PHEROMONE] = (chunk[BASE_PHEROMONE] + (0.25 * totalBase)) * BASE_PHEROMONE_PERSISTANCE
|
||||
chunk[PLAYER_PHEROMONE] = (chunk[PLAYER_PHEROMONE] + (0.25 * totalPlayer)) * PLAYER_PHEROMONE_PERSISTANCE
|
||||
end
|
||||
|
||||
return pheromoneUtils
|
||||
|
||||
+57
-29
@@ -42,7 +42,7 @@ function unitGroupUtils.findNearBySquad(natives, position, distance, filter)
|
||||
for i=1,#squads do
|
||||
local squad = squads[i]
|
||||
local unitGroup = squad.group
|
||||
if (unitGroup ~= nil) and unitGroup.valid and ((filter == nil) or (filter ~= nil and filter[squad.status])) then
|
||||
if unitGroup.valid and ((filter == nil) or (filter ~= nil and filter[squad.status])) then
|
||||
if (euclideanDistanceNamed(unitGroup.position, position) <= distance) then
|
||||
return squad
|
||||
end
|
||||
@@ -139,28 +139,59 @@ function unitGroupUtils.calculateKamikazeThreshold(squad, evolution_factor)
|
||||
return kamikazeThreshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus)
|
||||
end
|
||||
|
||||
local function isAttacking(squad)
|
||||
return (squad.state == GROUP_STATE_ATTACKING_TARGET) or (squad.state == GROUP_STATE_ATTACKING_DISTRACTION)
|
||||
local function isAttacking(group)
|
||||
local state = group.state
|
||||
return (state == GROUP_STATE_ATTACKING_TARGET) or (state == GROUP_STATE_ATTACKING_DISTRACTION)
|
||||
end
|
||||
|
||||
function unitGroupUtils.regroupSquads(natives, evolution_factor)
|
||||
local squads = natives.squads
|
||||
for i=1,#squads do
|
||||
local squadCount = #squads
|
||||
|
||||
for i=1,squadCount do
|
||||
local squad = squads[i]
|
||||
if squad.group.valid and not isAttacking(squad) then
|
||||
local squadPosition = squad.group.position
|
||||
local group = squad.group
|
||||
if group.valid then
|
||||
local memberCount = #group.members
|
||||
if (memberCount == 0) or (memberCount > AI_MAX_BITER_GROUP_SIZE) then
|
||||
group.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local groupThreshold = AI_MAX_BITER_GROUP_SIZE * 0.75
|
||||
|
||||
for i=1,squadCount do
|
||||
local squad = squads[i]
|
||||
local group = squad.group
|
||||
if group.valid then
|
||||
local status = squad.status
|
||||
local memberCount = #group.members
|
||||
local squadPosition = group.position
|
||||
|
||||
if not isAttacking(group) and (memberCount < groupThreshold) then
|
||||
local mergedSquads = false
|
||||
for x=i+1, #squads do
|
||||
for x=i+1,squadCount do
|
||||
local mergeSquad = squads[x]
|
||||
local mergeGroup = mergeSquad.group
|
||||
if mergeGroup.valid and ((#mergeGroup.members + #squad.group.members) < AI_MAX_BITER_GROUP_SIZE) and (mergeSquad.status == squad.status) and not isAttacking(mergeSquad) and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < GROUP_MERGE_DISTANCE) then
|
||||
unitGroupUtils.membersToSquad(squad, mergeGroup.members, true)
|
||||
if mergeGroup.valid then
|
||||
local mergeMembers = mergeGroup.members
|
||||
local mergeCount = #mergeMembers
|
||||
if ((mergeCount + memberCount) < AI_MAX_BITER_GROUP_SIZE) and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < GROUP_MERGE_DISTANCE) and (mergeSquad.status == status) and not isAttacking(mergeGroup) then
|
||||
for memberIndex=1, mergeCount do
|
||||
group.add_member(mergeMembers[memberIndex])
|
||||
end
|
||||
if mergeSquad.kamikaze then
|
||||
squad.kamikaze = true
|
||||
end
|
||||
mergedSquads = true
|
||||
mergeGroup.destroy()
|
||||
end
|
||||
memberCount = memberCount + mergeCount
|
||||
if (memberCount > groupThreshold) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if mergedSquads and not squad.kamikaze then
|
||||
local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, evolution_factor)
|
||||
@@ -169,33 +200,30 @@ function unitGroupUtils.regroupSquads(natives, evolution_factor)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i=#squads,1,-1 do
|
||||
local squad = squads[i]
|
||||
if (squad.group == nil) then
|
||||
tableRemove(squads, i)
|
||||
elseif not squad.group.valid then
|
||||
tableRemove(squads, i)
|
||||
elseif (#squad.group.members == 0) then
|
||||
squad.group.destroy()
|
||||
tableRemove(squads, i)
|
||||
elseif (#squad.group.members > AI_MAX_BITER_GROUP_SIZE) then
|
||||
squad.group.destroy()
|
||||
tableRemove(squads, i)
|
||||
else
|
||||
if (squad.status == SQUAD_RETREATING) and (squad.cycles == 0) then
|
||||
local cycles = squad.cycles
|
||||
if (status == SQUAD_RETREATING) and (cycles == 0) then
|
||||
squad.status = SQUAD_GUARDING
|
||||
squad.frenzy = true
|
||||
squad.frenzyPosition.x = squad.group.position.x
|
||||
squad.frenzyPosition.y = squad.group.position.y
|
||||
elseif (squad.group.state == GROUP_STATE_FINISHED) then
|
||||
squad.frenzyPosition.x = squadPosition.x
|
||||
squad.frenzyPosition.y = squadPosition.y
|
||||
elseif (group.state == GROUP_STATE_FINISHED) then
|
||||
squad.status = SQUAD_GUARDING
|
||||
elseif (squad.cycles > 0) then
|
||||
squad.cycles = squad.cycles - 1
|
||||
elseif (cycles > 0) then
|
||||
squad.cycles = cycles - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cleanSquads = {}
|
||||
for i=1,squadCount do
|
||||
local squad = squads[i]
|
||||
if squad.group.valid then
|
||||
cleanSquads[#cleanSquads+1] = squad
|
||||
end
|
||||
end
|
||||
|
||||
natives.squads = cleanSquads
|
||||
end
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user