1
0
mirror of https://github.com/veden/Rampant.git synced 2024-12-28 21:08:22 +02:00

Merge branch 'master' into bases

This commit is contained in:
Aaron Veden 2017-05-14 19:32:32 -07:00
commit 5f2e6be7fc
15 changed files with 268 additions and 30 deletions

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, breaching, 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. Difficulty setting in mod options menu.
# Forum Post
@ -21,21 +21,23 @@ Configure Options not in game menu:
# Features
- Difficulty Scaling - A mod option to control how quickly the ai can perform actions like making attack waves.
- Nocturnal Mode - A mod option to force biters to only attack at night. Does not yet affect vanilla attacks. Best use with daynight extender mod
- 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 causing 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
- Unit Group Merging - if multiple unit groups retreat at the same time there is a chance the groups will merge
- Unit Group Forming - any chunks with spawners in it that is covered by a pollution or player clouds will form groups based on the evolution factor
- Tactical Retreats - These will take place when a unit group is in a chunk that has reached a death threshold
- Unit Group Merging - If multiple unit groups retreat at the same time there is a chance the groups will merge
- Unit Group Forming - Any chunks with spawners in it that is covered by a pollution or player clouds will form groups based on the evolution factor
- Probing Behavior Against Defenses - unit groups will attempt to avoid chunks that are soaked in death
- Player Hunting - unit groups will track the player based on there emitted pheromone cloud
- Rallying Death Cry - when a unit is killed on a chunk that is past the retreat threshold, the unit will attempt to summon reinforcements to help them
- Counterattacks - when the player is in combat near nests they will send reinforcements to unit groups
- Reinforcements - nests will send assistance to nearby nests under attack by the player
- No Homing Projectiles - all projectiles are fired at locations and no longer track the player
- Pathfinding - unit groups will use potential fields to perform only single step pathfinding allowing for efficient and dynamic pathing
- Peace mode - if something sets peace mode, Rampant will respect it
- Player Hunting - Unit groups will track the player based on there emitted pheromone cloud
- Rallying Death Cry - When a unit is killed on a chunk that is past the retreat threshold, the unit will attempt to summon reinforcements to help them
- Counterattacks - When the player is in combat near nests they will send reinforcements to unit groups
- Reinforcements - Nests will send assistance to nearby nests under attack by the player
- No Homing Projectiles - All projectiles are fired at locations and no longer track the player
- Pathfinding - Unit groups will use potential fields to perform only single step pathfinding allowing for efficient and dynamic pathing
- Peace mode - If something sets peace mode, Rampant will respect it
# Planned Features
@ -48,6 +50,17 @@ Configure Options not in game menu:
# Version History
0.15.8 -
- Fixed: aiPointsScaler being nil
0.15.7 -
- Feature: Difficulty Scaling - A mod options to control how quickly the ai performs actions
- Fixed: Spelling on mod option
- Improvement: Exposed nocturnal toggle and difficulty scaling to remote interop
0.15.6 -
- Feature: Nocturnal Mode - Causes Rampant attacks waves to only spawn at night. Best use with daynight extender mod.
0.15.5 -
- Tweak: Increased ai refund from 2 to 3
- Fix: Signals, Chain Signals, and Train stops now correctly rebuild when the corresponding make safe is toggled

130
Upgrade.lua Normal file
View File

@ -0,0 +1,130 @@
local upgrade = {}
-- imports
local constants = require("libs/Constants")
local mathUtils = require("libs/MathUtils")
-- constants
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
local MAX_RALLY_CRIES = constants.MAX_RALLY_CRIES
-- imported functions
local roundToNearest = mathUtils.roundToNearest
-- module code
function upgrade.attempt(natives, regionMap)
local starting = global.version
if (global.version == nil) then
-- removed in version 9
-- regionMap.pQ = {{}} -- processing queue
-- regionMap.pI = 1 -- insertion location for chunk processing
-- regionMap.pP = 1 -- index for the chunk set to process
-- regionMap.pR = -1 -- current processing roll
natives.squads = {}
natives.scouts = {}
natives.tunnels = {}
natives.points = 0
global.version = constants.VERSION_5
end
if (global.version < constants.VERSION_9) then
-- remove version 5 references
regionMap.pQ = nil
regionMap.pI = nil
regionMap.pP = nil
regionMap.pR = nil
global.version = constants.VERSION_9
end
if (global.version < constants.VERSION_10) then
for _,squad in pairs(natives.squads) do
squad.frenzy = false
squad.frenzyPosition = {x=0,y=0}
squad.rabid = false
end
global.version = constants.VERSION_10
end
if (global.version < constants.VERSION_11) then
natives.state = constants.AI_STATE_AGGRESSIVE
natives.temperament = 0
-- needs to be on inner logic tick loop interval
natives.stateTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
natives.temperamentTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
global.version = constants.VERSION_11
end
if (global.version < constants.VERSION_12) then
for _,squad in pairs(natives.squads) do
squad.status = constants.SQUAD_GUARDING
squad.kamikaze = false
end
-- reset ai build points due to error in earning points
natives.points = 0
global.version = constants.VERSION_12
end
if (global.version < constants.VERSION_13) then
-- switched over to tick event
regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS)
-- used to rate limit the number of rally cries during a period of time
natives.rallyCries = MAX_RALLY_CRIES
global.version = constants.VERSION_13
end
if (global.version < constants.VERSION_14) then
game.map_settings.unit_group.member_disown_distance = 5
game.map_settings.unit_group.max_member_speedup_when_behind = 1.1
game.map_settings.unit_group.max_member_slowdown_when_ahead = 1.0
game.map_settings.unit_group.max_group_slowdown_factor = 0.9
game.surfaces[1].print("Rampant - Version 0.14.11")
global.version = constants.VERSION_14
end
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
game.surfaces[1].print("Rampant - Version 0.14.13")
global.version = constants.VERSION_16
end
if (global.version < constants.VERSION_18) then
natives.safeEntities = {}
natives.safeEntityName = {}
game.surfaces[1].print("Rampant - Version 0.15.5")
global.version = constants.VERSION_18
end
if (global.version < constants.VERSION_20) then
natives.aiPointsScaler = settings.global["rampant-aiPointsScaler"].value
natives.aiNocturnalMode = settings.global["rampant-permanentNocturnal"].value
game.surfaces[1].print("Rampant - Version 0.15.8")
global.version = constants.VERSION_20
end
if (global.version < constants.VERSION_21) then
game.surfaces[1].print("Rampant - Version 0.15.9")
global.version = constants.VERSION_21
end
return starting ~= global.version
end
return upgrade

View File

@ -1,5 +1,6 @@
-- imports
local upgrade = require("Upgrade")
local entityUtils = require("libs/EntityUtils")
local mapUtils = require("libs/MapUtils")
local unitGroupUtils = require("libs/UnitGroupUtils")
@ -105,12 +106,15 @@ local function onModSettingsChange(event)
natives.attackThresholdRange = natives.attackThresholdMax - natives.attackThresholdMin
natives.attackWaveMaxSize = settings.global["rampant-attackWaveMaxSize"].value
natives.attackPlayerThreshold = settings.global["rampant-attackPlayerThreshold"].value
natives.aiNocturnalMode = settings.global["rampant-permanentNocturnal"].value
natives.aiPointsScaler = settings.global["rampant-aiPointsScaler"].value
end
local function onConfigChanged()
if (upgrade.attempt(natives, regionMap)) then
if upgrade.attempt(natives, regionMap) then
onModSettingsChange(nil)
game.surfaces[1].print("Indexing chunks, please wait")
game.surfaces[1].print("Reindexing chunks, please wait")
-- clear old regionMap processing Queue
-- prevents queue adding duplicate chunks
-- chunks are by key, so should overwrite old
@ -128,9 +132,7 @@ local function onConfigChanged()
area = { left_top = { x = chunk.x * 32,
y = chunk.y * 32 }}})
end
end
onModSettingsChange(nil)
end
end
local function onTick(event)

View File

@ -1,10 +1,10 @@
{
"name" : "Rampant",
"factorio_version" : "0.15",
"version" : "0.15.8",
"version" : "0.15.9",
"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, breaching, rallying death cry, and player hunting. Also removes homing biter projectiles.",
"dependencies" : ["base >= 0.13.17", "? bobenemies", "? Natural_Evolution_Enemies"]
"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. Difficulty setting in mod options menu.",
"dependencies" : ["base >= 0.13.17", "? bobenemies", "? Natural_Evolution_Enemies", "? DayNightExtender"]
}

View File

@ -9,7 +9,7 @@ local playerUtils = require("PlayerUtils")
local neighborUtils = require("NeighborUtils")
-- constants
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
@ -125,7 +125,7 @@ function aiAttack.squadBeginAttack(natives, players, evolution_factor)
squad.frenzyPosition.x = squad.group.position.x
squad.frenzyPosition.y = squad.group.position.y
end
if (math.random() < 0.70) then
if (math.random() < kamikazeThreshold) then
squad.kamikaze = true

View File

@ -6,6 +6,7 @@ local constants = require("Constants")
local mapUtils = require("MapUtils")
local unitGroupUtils = require("UnitGroupUtils")
local neighborUtils = require("NeighborUtils")
local nocturnalUtils = require("NocturnalUtils")
package.path = "../?.lua;" .. package.path
local config = require("config")
@ -20,6 +21,8 @@ local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT
local AI_SQUAD_COST = constants.AI_SQUAD_COST
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
local CHUNK_SIZE = constants.CHUNK_SIZE
local NORTH_SOUTH_PASSABLE = constants.NORTH_SOUTH_PASSABLE
@ -37,6 +40,8 @@ local scoreNeighbors = neighborUtils.scoreNeighbors
local createSquad = unitGroupUtils.createSquad
local attackWaveScaling = config.attackWaveScaling
local canAttackNocturnal = nocturnalUtils.canAttack
local mMax = math.max
-- module code

View File

@ -4,11 +4,13 @@ local aiPlanning = {}
local constants = require("Constants")
local mathUtils = require("MathUtils")
local nocturnalUtils = require("NocturnalUtils")
-- constants
local AI_STATE_PEACEFUL = constants.AI_STATE_PEACEFUL
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
local AI_MAX_POINTS = constants.AI_MAX_POINTS
local AI_POINT_GENERATOR_AMOUNT = constants.AI_POINT_GENERATOR_AMOUNT
@ -22,6 +24,8 @@ local TICKS_A_MINUTE = constants.TICKS_A_MINUTE
-- imported functions
local canAttackNocturnal = nocturnalUtils.canAttack
local randomTickEvent = mathUtils.randomTickEvent
local mMax = math.max
@ -30,8 +34,17 @@ local mMax = math.max
function aiPlanning.planning(natives, evolution_factor, tick, surface)
local maxPoints = AI_MAX_POINTS * evolution_factor
if natives.aiNocturnalMode then
maxPoints = maxPoints * 0.85
end
if (natives.points < maxPoints) then
natives.points = natives.points + math.floor((AI_POINT_GENERATOR_AMOUNT * math.random()) + ((AI_POINT_GENERATOR_AMOUNT * 0.7) * (evolution_factor ^ 2.5)))
--[[ check for ai points scaler being nil, potential race condition with mod config being run
discovered 0.15.8
--]]
-- if not natives.aiPointsScaler then
-- natives.aiPointsScaler = settings.global["rampant-aiPointsScaler"].value
-- end
natives.points = natives.points + math.floor((AI_POINT_GENERATOR_AMOUNT * math.random()) + ((AI_POINT_GENERATOR_AMOUNT * 0.7) * (evolution_factor ^ 2.5)) * natives.aiPointsScaler)
end
if (natives.temperamentTick == tick) then
@ -43,13 +56,15 @@ function aiPlanning.planning(natives, evolution_factor, tick, surface)
local roll = math.random() * mMax(1 - evolution_factor, 0.15)
if (roll > natives.temperament) then
natives.state = AI_STATE_PEACEFUL
elseif (natives.aiNocturnalMode) then
natives.state = AI_STATE_NOCTURNAL
else
natives.state = AI_STATE_AGGRESSIVE
end
natives.stateTick = randomTickEvent(tick, AI_MIN_STATE_DURATION, AI_MAX_STATE_DURATION)
end
if (natives.state == AI_STATE_AGGRESSIVE) and (tick - natives.lastShakeMessage > TICKS_A_MINUTE * 5) and (natives.points > AI_MAX_POINTS) then
if ((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)) and (tick - natives.lastShakeMessage > TICKS_A_MINUTE * 5) and (natives.points > AI_MAX_POINTS) then
natives.lastShakeMessage = tick
surface.print("Rampant: The ground begins to shake")
end

View File

@ -14,6 +14,8 @@ constants.VERSION_16 = 16
constants.VERSION_17 = 17
constants.VERSION_18 = 18
constants.VERSION_19 = 19
constants.VERSION_20 = 20
constants.VERSION_21 = 21
-- misc
@ -49,6 +51,7 @@ constants.AI_MAX_BITER_GROUP_SIZE = 450
constants.AI_STATE_PEACEFUL = 1
constants.AI_STATE_AGGRESSIVE = 2
constants.AI_STATE_NOCTURNAL = 3
constants.AI_MIN_STATE_DURATION = 1
constants.AI_MAX_STATE_DURATION = 4

View File

@ -8,6 +8,22 @@ function interop.getAIPoints()
return global.natives.points
end
function interop.setNocturnalMode(flag)
global.natives.aiNocturnalMode = flag
end
function interop.getNocturnalMode()
return global.natives.aiNocturnalMode
end
function interop.setPointsPerCycleScaling(scale)
global.natives.aiPointsScaler = scale
end
function interop.getPointsPerCycleScaling()
return global.natives.aiPointsScaler
end
function interop.changeState(aiState)
global.natives.state = aiState
end

View File

@ -6,6 +6,7 @@ local pheromoneUtils = require("PheromoneUtils")
local aiBuilding = require("AIBuilding")
local constants = require("Constants")
local mapUtils = require("MapUtils")
local nocturnalUtils = require("NocturnalUtils")
-- constants
@ -19,6 +20,7 @@ local SCAN_QUEUE_SIZE = constants.SCAN_QUEUE_SIZE
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
local CHUNK_SIZE = constants.CHUNK_SIZE
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
local CHUNK_TICK = constants.CHUNK_TICK
@ -45,6 +47,8 @@ local getChunkByPosition = mapUtils.getChunkByPosition
local playerScent = pheromoneUtils.playerScent
local canAttackNocturnal = nocturnalUtils.canAttack
local mMin = math.min
-- module code
@ -79,8 +83,8 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
roll = math.random()
regionMap.processRoll = roll
end
if (natives.state == AI_STATE_AGGRESSIVE) and (0.11 <= roll) and (roll <= 0.35) then
if ((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)) and (0.11 <= roll) and (roll <= 0.35) then
squads = true
end
@ -120,7 +124,7 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
local vengenceThreshold = -(evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)
local roll = math.random()
if (natives.state == AI_STATE_AGGRESSIVE) and (0.11 <= roll) and (roll <= 0.20) then
if ((natives.state == AI_STATE_AGGRESSIVE) or canAttackNocturnal(natives, surface)) and (0.11 <= roll) and (roll <= 0.20) then
squads = true
end
@ -143,7 +147,8 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, evolu
if (playerChunk ~= nil) then
local vengence = false
if (playerChunk[CHUNK_BASE] ~= nil) or (playerChunk[MOVEMENT_PHEROMONE] < vengenceThreshold) then
if ((playerChunk[ENEMY_BASE_GENERATOR] ~= 0) or (playerChunk[MOVEMENT_PHEROMONE] < vengenceThreshold)) and
(natives.state == AI_STATE_AGGRESSIVE or canAttackNocturnal(natives, surface)) then
vengence = true
end
for x=playerChunk.cX - PROCESS_PLAYER_BOUND, playerChunk.cX + PROCESS_PLAYER_BOUND do
@ -205,7 +210,7 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor)
if (unitCount > 550) then
local weight = AI_UNIT_REFUND * evolution_factor
local units = surface.find_enemy_units({chunk.pX, chunk.pY},
CHUNK_SIZE * 3)
CHUNK_SIZE * 3)
for i=1,#units do
units[i].destroy()

21
libs/NocturnalUtils.lua Normal file
View File

@ -0,0 +1,21 @@
local nocturnalUtils = {}
-- imports
local constants = require("Constants")
-- constants
local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
-- module code
function nocturnalUtils.isDark(surface)
return surface.darkness > 0.65
end
function nocturnalUtils.canAttack(natives, surface)
return nocturnalUtils.isDark(surface) and natives.state == AI_STATE_NOCTURNAL
end
return nocturnalUtils

View File

@ -4,6 +4,7 @@ local unitGroupUtils = {}
local mapUtils = require("MapUtils")
local constants = require("Constants")
local nocturnalUtils = require("NocturnalUtils")
-- constants

View File

@ -1,5 +1,4 @@
[entity-name]
tunnel-entrance=Tunnel Entrance
@ -36,6 +35,8 @@ rampant-safeBuildings-railChainSignals=Make rail chain signals safe from biters
rampant-safeBuildings-railSignals=Make rail signals safe from biters
rampant-safeBuildings-trainStops=Make train stops safe from biters
rampant-attackPlayerThreshold=Player score contribution threshold
rampant-permanentNocturnal=Permanent Nocturnal Mode
rampant-aiPointsScaler=Difficulty Scaling
[mod-setting-description]
rampant-useDumbProjectiles=Turns off homing projectiles for worms and spitters
@ -53,3 +54,5 @@ rampant-safeBuildings-railChainSignals=Make rail chain signals safe from biters
rampant-safeBuildings-railSignals=Make rail signals safe from biters
rampant-safeBuildings-trainStops=Make train stops safe from biters
rampant-attackPlayerThreshold=The score that a chunk must reach for it to contribute to the attack threshold. Increasing reduces player pheromone cloud impact.
rampant-permanentNocturnal=Toggling this will cause Rampant attack waves to spawn at night. DOES NOT turn off vanilla attack groups yet. Works better with Day/Night extender mod.
rampant-aiPointsScaler=Between 0.0 and 5.0. This scales how many action points the ai gets per logic cycle to perform actions like making attack waves. 0.3 - very easy, 0.75 - easy, 1.0 - medium, 1.25+ - hard

View File

@ -31,6 +31,7 @@
(string->path "data-updates.lua")
(string->path "LICENSE.md")
(string->path "tests.lua")
(string->path "Upgrade.lua")
(string->path "settings.lua")
(string->path "README.md")
(string->path "NOTICE")
@ -68,6 +69,7 @@
(copyFile "upgrade.lua" modFolder)
(copyFile "data-updates.lua" modFolder)
(copyFile "settings.lua" modFolder)
(copyFile "Upgrade.lua" modFolder)
(copyFile "tests.lua" modFolder)
(copyDirectory "libs" modFolder)
(copyDirectory "locale" modFolder)

View File

@ -43,7 +43,7 @@ data:extend({
setting_type = "runtime-global",
minimum_value = 0,
default_value = 7,
order = "c[modifier]-b[threshold]",
order = "c[modifier]-c[threshold]",
per_user = false
},
@ -140,6 +140,28 @@ data:extend({
default_value = false,
order = "e[modifier]-g[safe]",
per_user = false
},
{
type = "bool-setting",
name = "rampant-permanentNocturnal",
description = "rampant-permanentNocturnal",
setting_type = "runtime-global",
default_value = false,
order = "f[modifier]-a[ai]",
per_user = false
},
{
type = "double-setting",
name = "rampant-aiPointsScaler",
description = "rampant-aiPointsScaler",
setting_type = "runtime-global",
default_value = 1.0,
minimum_value = 0.0,
maximum_value = 5.0,
order = "f[modifier]-b[ai]",
per_user = false
}
})