see readme

This commit is contained in:
Aaron Veden
2017-04-21 16:14:04 -07:00
parent 8d3b9cb1a5
commit 9cd4766c8c
12 changed files with 236 additions and 161 deletions
+22 -8
View File
@@ -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
@@ -15,16 +15,18 @@ There will be a slight pause the first time this is started up due to indexing a
MP should be working
Configure Options:
- Max biter wave size
- Ramp up to max biter wave size
- Use dumb projectiles
- Use Natural Evolution unit launchers (Requires NE)
- Togglable attack wave triggers (pollution, player)
- Attack wave pollution trigger threshold
Configure Options:
- Max biter wave size
- Ramp up to max biter wave size
- Use dumb projectiles
- Use Natural Evolution unit launchers (Requires NE)
- Togglable attack wave triggers (pollution, player)
- Attack wave pollution trigger threshold
# 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
+21 -14
View File
@@ -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,8 +158,14 @@ 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
-- chunks are by key, so should overwrite old
@@ -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
retreatUnits(deathChunk,
convertUnitGroupToSquad(natives,
entity.unit_group),
regionMap,
surface,
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)
+2 -2
View File
@@ -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"]
}
+4 -1
View File
@@ -54,7 +54,10 @@ local function attackWaveValidCandidate(chunk, surface, evolutionFactor)
local total = 0;
if CONFIG_USE_PLAYER_PROXIMITY then
total = total + chunk[PLAYER_PHEROMONE]
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})
+8 -3
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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
+46 -13
View File
@@ -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
@@ -91,17 +95,17 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
local endIndex = mMin(index + PROCESS_QUEUE_SIZE, #processQueue)
for x=index,endIndex do
local chunk = processQueue[x]
scents(chunk)
processPheromone(regionMap, chunk)
if scouts then
makeScouts(surface, natives, chunk, evolution_factor)
end
if squads then
formSquads(regionMap, surface, natives, chunk, evolution_factor, AI_SQUAD_COST)
end
processPheromone(regionMap, chunk)
end
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)
@@ -174,8 +179,9 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
if vengence then
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
@@ -196,17 +202,44 @@ function mapProcessor.scanMap(regionMap, surface)
local chunk = processQueue[x]
local entities = surface.find_entities_filtered({area = {{chunk.pX, chunk.pY},
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
force = "player"})
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
force = "player"})
local spawners = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY},
{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]
@@ -219,7 +252,7 @@ function mapProcessor.scanMap(regionMap, surface)
chunk[ENEMY_BASE_GENERATOR] = (spawners * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT) + worms
chunk[PLAYER_BASE_GENERATOR] = playerBaseGenerator
end
if (endIndex == #processQueue) then
regionMap.scanPointer = 1
else
+31 -52
View File
@@ -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)
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
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
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) 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
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
+71 -43
View File
@@ -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,63 +139,91 @@ 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 squad = squads[i]
if squad.group.valid and not isAttacking(squad) then
local squadPosition = squad.group.position
local mergedSquads = false
for x=i+1, #squads 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 mergeSquad.kamikaze then
squad.kamikaze = true
end
mergedSquads = true
mergeGroup.destroy()
end
end
if mergedSquads and not squad.kamikaze then
local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, evolution_factor)
if (math.random() < kamikazeThreshold) then
squad.kamikaze = true
end
local squadCount = #squads
for i=1,squadCount do
local squad = squads[i]
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
for i=#squads,1,-1 do
local groupThreshold = AI_MAX_BITER_GROUP_SIZE * 0.75
for i=1,squadCount 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 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,squadCount do
local mergeSquad = squads[x]
local mergeGroup = mergeSquad.group
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)
if (math.random() < kamikazeThreshold) then
squad.kamikaze = true
end
end
end
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
+1 -1
View File
@@ -71,7 +71,7 @@
(copyDirectory "prototypes" modFolder)))
(define (run)
;; (copyFiles modFolder)
;;(copyFiles modFolder)
(makeZip modFolder)
)
)