1
0
mirror of https://github.com/veden/Rampant.git synced 2025-03-11 14:49:32 +02:00

working on uni classes, still need base and entity upgrade logic finished

This commit is contained in:
Aaron Veden 2018-01-15 23:21:12 -08:00
parent 8e76817a19
commit 33815a27b7
14 changed files with 751 additions and 174 deletions

View File

@ -115,7 +115,7 @@ local function unitSetToProbabilityTable(points, upgradeTable, unitSet)
return result
end
local function upgradeEntity(points, entity, upgradeTable)
local function upgradeEntity(points, entity, upgradeTable, tierMultipler)
local remainingPoints = points
if upgradeTable then
while (remainingPoints > 0) do
@ -129,28 +129,40 @@ local function upgradeEntity(points, entity, upgradeTable)
if bonus.mapping then
entity.attributes[bonus.name] = bonus.mapping[entity.attributes[bonus.name] or "default"]
else
entity.attributes[bonus.name] = (entity.attributes[bonus.name] or 0) + bonus.adjustment
local adj = bonus.adjustment * tierMultipler
if adj < 0 then
adj = -adj
adj = -gaussianRandomRangeRG(adj, adj * 0.2, adj * 0.75, adj * 1.25, xorRandom)
else
adj = gaussianRandomRangeRG(adj, adj * 0.2, adj * 0.75, adj * 1.25, xorRandom)
end
entity.attributes[bonus.name] = (entity.attributes[bonus.name] or 0) + adj
end
end
if (bonus.type == "resistance") then
local field = bonus.resistance.name
local field = bonus.name
if not entity.resistances[field] then
entity.resistances[field] = {}
end
if bonus.resistance.decrease then
entity.resistances[field].decrease = (entity.resistances[field].decrease or 0) + bonus.resistance.decrease
if bonus.decrease then
entity.resistances[field].decrease = (entity.resistances[field].decrease or 0) + bonus.decrease
end
if bonus.resistance.percentage then
entity.resistances[field].percentage = (entity.resistances[field].percentage or 0) + bonus.resistance.percentage
if bonus.percent then
entity.resistances[field].percent = mMin((entity.resistances[field].percent or 0) + bonus.percent, 100)
end
end
if (bonus.type == "attack") then
local attack = bonus.attack
entity.attack[attack.name] = (entity.attack[attack.name] or 0) + attack.adjustment
if attack.mapping then
entity.attributes[attack.name] = attack.mapping[entity.attributes[attack.name] or "default"]
if bonus.mapping then
entity.attack[bonus.name] = bonus.mapping[entity.attack[bonus.name] or "default"]
else
entity.attributes[attack.name] = (entity.attributes[attack.name] or 0) + attack.adjustment
local adj = bonus.adjustment * tierMultipler
if adj < 0 then
adj = -adj
adj = -gaussianRandomRangeRG(adj, adj * 0.2, adj * 0.75, adj * 1.25, xorRandom)
else
adj = gaussianRandomRangeRG(adj, adj * 0.2, adj * 0.75, adj * 1.25, xorRandom)
end
entity.attack[bonus.name] = (entity.attack[bonus.name] or 0) + adj
end
end
end
@ -205,14 +217,12 @@ local function buildUnits(startingPoints, template, attackGenerator, upgradeTabl
for t=1, tiers do
local result = {}
local allottedPoints = startingPoints * t
for i=1,variations do
local unit = deepcopy(template)
unit.name = unit.name .. "-v" .. i .. "-t" .. t
unit.name = unit.name .. "-v" .. i .. "-t" .. t .. "-rampant"
generateApperance(unit, t)
upgradeEntity(allottedPoints, unit, upgradeTable)
upgradeEntity(startingPoints, unit, upgradeTable, tiers)
local entity
if (unit.type == "spitter") then
@ -246,17 +256,17 @@ function swarmUtils.buildUnitSpawner(points, templates, upgradeTable, attackGene
variations.unit,
tiers.unit)
for t=1, tiers do
for t=1, tiers.unitSpawner do
local allottedPoints = points.unitSpawner * t
for i=1,variations.unitSpawner do
local unitSpawner = deepcopy(templates.unitSpawner)
unitSpawner.name = unitSpawner.name .. "-v" .. i .. "-t" .. t
unitSpawner.name = unitSpawner.name .. "-v" .. i .. "-t" .. t .. "-rampant"
local unitTable = unitSetToProbabilityTable(points.probabilityTable,
upgradeTable.probabilityTable,
unitSet)
generateApperance(unitSpawner, t)
upgradeEntity(allottedPoints, unitSpawner, upgradeTable.unitSpawner)
upgradeEntity(allottedPoints, unitSpawner, upgradeTable.unitSpawner, t / tiers)
data:extend({
makeUnitSpawner(unitSpawner.name,
@ -275,9 +285,9 @@ function swarmUtils.buildWorm(startingPoints, template, attackGenerator, upgrade
for i=1,variations do
local worm = deepcopy(template)
worm.name = worm.name .. "-v" .. i .. "-t" .. t
worm.name = worm.name .. "-v" .. i .. "-t" .. t .. "-rampant"
generateApperance(worm, t)
upgradeEntity(allottedPoints, worm, upgradeTable)
upgradeEntity(allottedPoints, worm, upgradeTable, t / tiers)
data:extend({
makeWorm(worm.name,

View File

@ -2,6 +2,16 @@
local biterUtils = require("prototypes/enemies/BiterUtils")
local swarmUtils = require("SwarmUtils")
local constants = require("libs/Constants")
-- constants
local SUICIDE_BITER_NEST_TIERS = constants.SUICIDE_BITER_NEST_TIERS
local SUICIDE_BITER_NEST_VARIATIONS = constants.SUICIDE_BITER_NEST_VARIATIONS
local NEUTRAL_NEST_TIERS = constants.NEUTRAL_NEST_TIERS
local NEUTRAL_NEST_VARIATIONS = constants.NEUTRAL_NEST_VARIATIONS
-- imported functions
@ -10,10 +20,13 @@ local swarmUtils = require("SwarmUtils")
local buildUnitSpawner = swarmUtils.buildUnitSpawner
local createSuicideAttack = biterUtils.createSuicideAttack
local createMeleeAttack = biterUtils.createMeleeAttack
-- module code
-- suicide
buildUnitSpawner(
{
unit = 10,
@ -22,7 +35,7 @@ buildUnitSpawner(
},
{
unit = {
name = "rampant-suicide-biter",
name = "suicide-biter",
attributes = {
health = 30,
@ -44,15 +57,15 @@ buildUnitSpawner(
resistances = {
explosion = {
decrease = 0,
percentage = -50
percent = -50
},
laser = {
decrease = 1,
percentage = 0
percent = 0
},
fire = {
decrease = 0,
percentage = -60
percent = -60
}
},
@ -63,7 +76,7 @@ buildUnitSpawner(
},
unitSpawner = {
name = "rampant-suicide-biter-nest",
name = "suicide-biter-nest",
attributes = {
health = 30,
healing = 0.01,
@ -77,15 +90,15 @@ buildUnitSpawner(
resistances = {
explosion = {
decrease = 0,
percentage = -50
percent = -50
},
laser = {
decrease = 1,
percentage = 0
percent = 0
},
fire = {
decrease = 0,
percentage = -60
percent = -60
}
},
scale = 0.95,
@ -178,12 +191,375 @@ buildUnitSpawner(
{
unit = 5,
unitSpawner = SUICIDE_NEST_VARIATIONS
unitSpawner = SUICIDE_BITER_NEST_VARIATIONS
},
{
unit = 10,
unitSpawner = SUICIDE_NEST_TIERS
unitSpawner = SUICIDE_BITER_NEST_TIERS
}
)
-- neutral
buildUnitSpawner(
{
unit = 50,
unitSpawner = 3,
probabilityTable = 3
},
{
unit = {
name = "neutral-biter",
attributes = {
-- health = 10,
-- movement = 0.19,
-- distancePerFrame = 0.08,
-- healing = 0.01,
explosion = "blood-explosion-small"
},
attack = {
-- damage = 3,
-- range = 0.5,
cooldown = 30
},
resistances = {
-- acid = {
-- decrease = 3,
-- percent = 20
-- },
},
type = "biter",
scale = 0.5,
tint1 = {r=0.56, g=0.46, b=0.42, a=0.65},
tint2 = {r=1, g=0.63, b=0, a=0.4}
},
unitSpawner = {
name = "neutral-biter-nest",
attributes = {
health = 300,
healing = 0.01,
unitsOwned = 7,
unitsToSpawn = 5,
spawingCooldownStart = 360,
spawingCooldownStop = 150,
},
resistances = {
explosion = {
decrease = 3,
percent = 10
},
physical = {
decrease = 1,
percent = 10
},
fire = {
decrease = 1.5,
percent = 40
}
},
scale = 1,
tint = {r=1.0, g=1.0, b=1.0, a=1.0}
}
},
{
unit = {
{
cost = 1,
bonus = {
{
type = "attribute",
name = "health",
low = 2,
high = 500
},
{
type = "attribute",
name = "movement",
adjustment = -0.001
},
{
type = "attribute",
name = "distancePerFrame",
adjustment = 0.001
},
{
type = "attribute",
name = "pollutionToAttack",
adjustment = 50
}
}
},
{
cost = 1,
bonus = {
{
type = "attack",
name = "damage",
adjustment = 5
},
{
type = "attribute",
name = "pollutionToAttack",
adjustment = 20
}
}
},
{
cost = 1,
bonus = {
{
type = "attribute",
name = "healing",
adjustment = 0.005
}
}
},
{
cost = 1,
bonus = {
{
type = "attribute",
name = "movement",
adjustment = 0.02
},
{
type = "attribute",
name = "distancePerFrame",
adjustment = 0.02
}
}
},
{
cost = 1,
bonus = {
{
type = "resistance",
name = "physical",
decrease = 0.5,
percent = 3
},
{
type = "resistance",
name = "explosion",
decrease = 0.25,
percent = 3
},
{
type = "attribute",
name = "pollutionToAttack",
adjustment = 50
}
}
},
{
cost = 1,
bonus = {
{
type = "attack",
name = "damage",
adjustment = 2
},
{
type = "attack",
name = "range",
adjustment = 0.15
},
{
type = "attribute",
name = "pollutionToAttack",
adjustment = 20
}
}
},
},
unitSpawner = {
{
cost = 1,
bonus = {
{
type = "attribute",
name = "health",
adjustment = 50
}
}
},
{
cost = 1,
bonus = {
{
type = "attribute",
name = "healing",
adjustment = 0.02
}
}
},
{
cost = 1,
bonus = {
{
type = "attribute",
name = "spawingCooldownStart",
adjustment = -10
},
{
type = "attribute",
name = "spawingCooldownEnd",
adjustment = -10
},
{
type = "attribute",
name = "evolutionRequirement",
adjustment = 0.01
}
}
},
{
cost = 1,
bonus = {
{
type = "attribute",
name = "unitsOwned",
adjustment = 2
},
{
type = "attribute",
name = "unitsToSpawn",
adjustment = 1
},
{
type = "attribute",
name = "evolutionRequirement",
adjustment = 0.01
}
}
},
{
cost = 1,
bonus = {
{
type = "resistance",
name = "physical",
decrease = 0.5,
percent = 2
},
{
type = "resistance",
name = "explosion",
decrease = 1,
percent = 2
},
{
type = "resistance",
name = "fire",
decrease = 0.7,
percent = 5
}
}
}
},
probabilityTable = {
{
cost = 1,
index = 1,
adjustment = 1
},
{
cost = 1,
index = 2,
adjustment = 1
},
{
cost = 1,
index = 3,
adjustment = 1
},
{
cost = 1,
index = 4,
adjustment = 1
},
{
cost = 1,
index = 5,
adjustment = 1
},
{
cost = 1,
index = 6,
adjustment = 1
},
{
cost = 1,
index = 7,
adjustment = 1
},
{
cost = 1,
index = 8,
adjustment = 1
},
{
cost = 1,
index = 9,
adjustment = 1
},
{
cost = 1,
index = 10,
adjustment = 1
}
}
},
createMeleeAttack,
{
unit = 10,
unitSpawner = NEUTRAL_NEST_VARIATIONS
},
{
unit = 10,
unitSpawner = NEUTRAL_NEST_TIERS
}
)
for k,v in pairs(data.raw.unit) do
print(k)
end
print(serpent.dump(data.raw.unit['neutral-biter-v1-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v2-t10-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v3-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v4-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v5-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v6-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v7-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v8-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v9-t1-rampant']))
print(serpent.dump(data.raw.unit['neutral-biter-v10-t1-rampant']))
--print(serpent.dump(data.raw.unit))
constants.et()

View File

@ -168,6 +168,9 @@ function upgrade.attempt(natives)
for _,squad in pairs(natives.squads) do
squad.chunk = nil
end
natives.bases = {}
natives.baseIndex = 1
global.regionMap = nil

View File

@ -1,6 +1,6 @@
-- imports
local baseUtils = require("BaseUtils")
local baseUtils = require("libs/BaseUtils")
local mapUtils = require("libs/MapUtils")
local unitGroupUtils = require("libs/UnitGroupUtils")
local chunkProcessor = require("libs/ChunkProcessor")
@ -21,6 +21,14 @@ local config = require("config")
-- constants
local SUICIDE_BITER_NEST_TIERS = constants.SUICIDE_BITER_NEST_TIERS
local SUICIDE_BITER_NEST_VARIATIONS = constants.SUICIDE_BITER_NEST_VARIATIONS
local NEUTRAL_NEST_TIERS = constants.NEUTRAL_NEST_TIERS
local NEUTRAL_NEST_VARIATIONS = constants.NEUTRAL_NEST_VARIATIONS
local BASE_ALIGNMENT_NEUTRAL = constants.BASE_ALIGNMENT_NEUTRAL
local BASE_ALIGNMENT_SUICIDE = constants.BASE_ALIGNMENT_SUICIDE
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
local INTERVAL_CHUNK = constants.INTERVAL_CHUNK
@ -42,6 +50,8 @@ local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area
local CHUNK_SIZE = constants.CHUNK_SIZE
local EVOLUTION_INCREMENTS = constants.EVOLUTION_INCREMENTS
local DEFINES_DISTRACTION_NONE = defines.distraction.none
local DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
@ -87,6 +97,7 @@ local upgradeEntity = baseUtils.upgradeEntity
local processBases = baseProcessor.processBases
local mFloor = math.floor
local mRandom = math.random
-- local references to global
@ -151,6 +162,7 @@ local function rebuildMap()
map.processIndex = 1
map.scanIndex = 1
map.chunkToBase = {}
map.chunkToHives = {}
map.chunkToNests = {}
map.chunkToWorms = {}
@ -160,6 +172,8 @@ local function rebuildMap()
map.chunkToResource = {}
map.chunkToPassScan = {}
map.chunkToSquad = {}
natives.bases = {}
-- preallocating memory to be used in code, making it fast by reducing garbage generated.
map.neighbors = { SENTINEL_IMPASSABLE_CHUNK,
@ -219,74 +233,50 @@ local function rebuildMap()
y = chunk.y * 32 }}})
end
processPendingChunks(natives, map, surface, pendingChunks, tick)
end
local function onModSettingsChange(event)
if event and (string.sub(event.setting, 1, 7) ~= "rampant") then
return false
end
upgrade.compareTable(natives, "safeBuildings", settings.global["rampant-safeBuildings"].value)
upgrade.compareTable(natives.safeEntities, "curved-rail", settings.global["rampant-safeBuildings-curvedRail"].value)
upgrade.compareTable(natives.safeEntities, "straight-rail", settings.global["rampant-safeBuildings-straightRail"].value)
upgrade.compareTable(natives.safeEntities, "rail-signal", settings.global["rampant-safeBuildings-railSignals"].value)
upgrade.compareTable(natives.safeEntities, "rail-chain-signal", settings.global["rampant-safeBuildings-railChainSignals"].value)
upgrade.compareTable(natives.safeEntities, "train-stop", settings.global["rampant-safeBuildings-trainStops"].value)
upgrade.compareTable(natives.safeEntities, "lamp", settings.global["rampant-safeBuildings-lamps"].value)
local changed, newValue = upgrade.compareTable(natives.safeEntityName,
"big-electric-pole",
settings.global["rampant-safeBuildings-bigElectricPole"].value)
if changed then
natives.safeEntityName["big-electric-pole"] = newValue
natives.safeEntityName["big-electric-pole-2"] = newValue
natives.safeEntityName["big-electric-pole-3"] = newValue
natives.safeEntityName["big-electric-pole-4"] = newValue
end
upgrade.compareTable(natives, "attackUsePlayer", settings.global["rampant-attackWaveGenerationUsePlayerProximity"].value)
upgrade.compareTable(natives, "attackUsePollution", settings.global["rampant-attackWaveGenerationUsePollution"].value)
upgrade.compareTable(natives, "attackThresholdMin", settings.global["rampant-attackWaveGenerationThresholdMin"].value)
upgrade.compareTable(natives, "attackThresholdMax", settings.global["rampant-attackWaveGenerationThresholdMax"].value)
upgrade.compareTable(natives, "attackThresholdRange", natives.attackThresholdMax - natives.attackThresholdMin)
upgrade.compareTable(natives, "attackWaveMaxSize", settings.global["rampant-attackWaveMaxSize"].value)
upgrade.compareTable(natives, "attackPlayerThreshold", settings.global["rampant-attackPlayerThreshold"].value)
upgrade.compareTable(natives, "aiNocturnalMode", settings.global["rampant-permanentNocturnal"].value)
upgrade.compareTable(natives, "aiPointsScaler", settings.global["rampant-aiPointsScaler"].value)
-- RE-ENABLE WHEN COMPLETE
natives.useCustomAI = constants.DEV_CUSTOM_AI
-- changed, newValue = upgrade.compareTable(natives, "useCustomAI", settings.startup["rampant-useCustomAI"].value)
-- if natives.useCustomAI then
-- game.forces.enemy.ai_controllable = false
-- else
-- game.forces.enemy.ai_controllable = true
-- end
-- if changed and newValue then
-- rebuildMap()
-- return false
-- end
return true
processPendingChunks(natives, map, surface, pendingChunks, tick, game.forces.enemy.evolution_factor)
end
local function rebuildNativeTables()
local position = { x=0, y=0 }
for v=1,SUICIDE_NEST_VARIATIONS do
for t=1,SUICIDE_NEST_TIERS do
local entity = surface.create_entity({
name="rampant-suicide-biter-nest-v" .. v .. "-t" .. t,
position=position
})
local evoRequirement = entity.prototype.build_base_evolution_requirement
natives.evolutionTable[#natives.evolutionTable[evoRequirement]+1] = entity.name
entity.die()
natives.evolutionTable = {}
local fileEntity = function(baseAlignment, entity)
local evoRequirement = mFloor(entity.prototype.build_base_evolution_requirement/EVOLUTION_INCREMENTS) * EVOLUTION_INCREMENTS
local eTable = natives.evolutionTable[baseAlignment]
if not eTable then
eTable = {}
natives.evolutionTable[baseAlignment] = eTable
end
local aTable = eTable[evoRequirement]
if not aTable then
aTable = {}
eTable[evoRequirement] = aTable
end
aTable[#aTable+1] = entity.name
end
local surface = game.surfaces[1]
local position = { x = 0, y = 0 }
for v = 1, SUICIDE_BITER_NEST_VARIATIONS do
for t = 1, SUICIDE_BITER_NEST_TIERS do
local entity = surface.create_entity({
name="suicide-biter-nest-v" .. v .. "-t" .. t .. "-rampant",
position = position
})
fileEntity(BASE_ALIGNMENT_SUICIDE, entity)
entity.destroy()
end
end
for v=1,NEUTRAL_NEST_VARIATIONS do
for t=1,NEUTRAL_NEST_TIERS do
local entity = surface.create_entity({
name="neutral-biter-nest-v" .. v .. "-t" .. t .. "-rampant",
position = position
})
fileEntity(BASE_ALIGNMENT_NEUTRAL, entity)
entity.destroy()
end
end
-- for v=1,ACID_NEST_VARIATIONS do
-- for t=1,ACID_NEST_TIERS do
@ -350,6 +340,60 @@ local function rebuildNativeTables()
-- end
end
local function onModSettingsChange(event)
if event and (string.sub(event.setting, 1, 7) ~= "rampant") then
return false
end
upgrade.compareTable(natives, "safeBuildings", settings.global["rampant-safeBuildings"].value)
upgrade.compareTable(natives.safeEntities, "curved-rail", settings.global["rampant-safeBuildings-curvedRail"].value)
upgrade.compareTable(natives.safeEntities, "straight-rail", settings.global["rampant-safeBuildings-straightRail"].value)
upgrade.compareTable(natives.safeEntities, "rail-signal", settings.global["rampant-safeBuildings-railSignals"].value)
upgrade.compareTable(natives.safeEntities, "rail-chain-signal", settings.global["rampant-safeBuildings-railChainSignals"].value)
upgrade.compareTable(natives.safeEntities, "train-stop", settings.global["rampant-safeBuildings-trainStops"].value)
upgrade.compareTable(natives.safeEntities, "lamp", settings.global["rampant-safeBuildings-lamps"].value)
local changed, newValue = upgrade.compareTable(natives.safeEntityName,
"big-electric-pole",
settings.global["rampant-safeBuildings-bigElectricPole"].value)
if changed then
natives.safeEntityName["big-electric-pole"] = newValue
natives.safeEntityName["big-electric-pole-2"] = newValue
natives.safeEntityName["big-electric-pole-3"] = newValue
natives.safeEntityName["big-electric-pole-4"] = newValue
end
upgrade.compareTable(natives, "attackUsePlayer", settings.global["rampant-attackWaveGenerationUsePlayerProximity"].value)
upgrade.compareTable(natives, "attackUsePollution", settings.global["rampant-attackWaveGenerationUsePollution"].value)
upgrade.compareTable(natives, "attackThresholdMin", settings.global["rampant-attackWaveGenerationThresholdMin"].value)
upgrade.compareTable(natives, "attackThresholdMax", settings.global["rampant-attackWaveGenerationThresholdMax"].value)
upgrade.compareTable(natives, "attackThresholdRange", natives.attackThresholdMax - natives.attackThresholdMin)
upgrade.compareTable(natives, "attackWaveMaxSize", settings.global["rampant-attackWaveMaxSize"].value)
upgrade.compareTable(natives, "attackPlayerThreshold", settings.global["rampant-attackPlayerThreshold"].value)
upgrade.compareTable(natives, "aiNocturnalMode", settings.global["rampant-permanentNocturnal"].value)
upgrade.compareTable(natives, "aiPointsScaler", settings.global["rampant-aiPointsScaler"].value)
if upgrade.compareTable(natives, "enemySeed", settings.startup["rampant-enemySeed"].value) then
rebuildNativeTables()
end
-- RE-ENABLE WHEN COMPLETE
natives.useCustomAI = constants.DEV_CUSTOM_AI
-- changed, newValue = upgrade.compareTable(natives, "useCustomAI", settings.startup["rampant-useCustomAI"].value)
-- if natives.useCustomAI then
-- game.forces.enemy.ai_controllable = false
-- else
-- game.forces.enemy.ai_controllable = true
-- end
-- if changed and newValue then
-- rebuildMap()
-- return false
-- end
return true
end
local function onConfigChanged()
local upgraded
upgraded, natives = upgrade.attempt(natives)
@ -375,7 +419,7 @@ local function onTick(event)
map.scanTick = map.scanTick + INTERVAL_SCAN
local surface = game.surfaces[1]
processPendingChunks(natives, map, surface, pendingChunks, tick)
processPendingChunks(natives, map, surface, pendingChunks, tick, game.forces.enemy.evolution_factor)
scanMap(map, surface, natives)
@ -505,7 +549,7 @@ local function onEnemyBaseBuild(event)
local surface = entity.surface
if (surface.index == 1) then
entity = upgradeEntity(map, entity, surface, natives)
event.entity = registerEnemyBaseStructure(map, entity)
event.entity = registerEnemyBaseStructure(map, entity, natives, game.forces.enemy.evolution_factor, surface, event.tick)
end
end
@ -613,12 +657,14 @@ remote.add_interface("rampantTests",
gaussianRandomTest = tests.gaussianRandomTest,
reveal = tests.reveal,
showMovementGrid = tests.showMovementGrid,
showBaseGrid = tests.showBaseGrid,
baseStats = tests.baseStats,
mergeBases = tests.mergeBases,
clearBases = tests.clearBases,
getOffsetChunk = tests.getOffsetChunk,
registeredNest = tests.registeredNest,
colorResourcePoints = tests.colorResourcePoints,
entityStats = tests.entityStats,
stepAdvanceTendrils = tests.stepAdvanceTendrils,
exportAiState = tests.exportAiState(onTick)
}

View File

@ -27,6 +27,8 @@ function baseProcessor.processBases(map, surface, natives, tick)
for index = baseIndex, endIndex do
local base = bases[index]
if base.created then
end
-- buildOrder(map, natives, base, surface, tick)
-- advanceTendrils(map, base, surface, tick, natives)

View File

@ -11,23 +11,31 @@ local chunkPropertyUtils = require("ChunkPropertyUtils")
-- constants
local BASE_DISTANCE_THRESHOLD = constants.BASE_DISTANCE_THRESHOLD
local BASE_DISTANCE_LEVEL_BONUS = constants.BASE_DISTANCE_LEVEL_BONUS
local BASE_ALIGNMENT_NEUTRAL = constants.BASE_ALIGNMENT_NEUTRAL
local CHUNK_SIZE = constants.CHUNK_SIZE
local EVOLUTION_INCREMENTS = constants.EVOLUTION_INCREMENTS
local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
local MAGIC_MAXIMUM_BASE_NUMBER = constants.MAGIC_MAXIMUM_BASE_NUMBER
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local euclideanDistancePoints = mathUtils.euclideanDistancePoints
local isRampant = stringUtils.isRampant
local gaussianRandomRange = mathUtils.gaussianRandomRange
local mFloor = math.floor
local positionToChunkXY = mapUtils.positionToChunkXY
local getChunkByPosition = mapUtils.getChunkByPosition
local getChunkByXY = mapUtils.getChunkByXY
local getChunkBase = chunkPropertyUtils.getChunkBase
local setChunkBase = chunkPropertyUtils.setChunkBase
@ -36,6 +44,12 @@ local mRandom = math.random
-- module code
function baseUtils.findNearbyBase(map, chunk, chunkRadius)
if (chunk == SENTINEL_IMPASSABLE_CHUNK) then
return nil
end
local tested = {}
local x = chunk.x
local y = chunk.y
@ -48,12 +62,15 @@ function baseUtils.findNearbyBase(map, chunk, chunkRadius)
for xi = x-chunkRadius, x+chunkRadius, CHUNK_SIZE do
for yi = y-chunkRadius, y+chunkRadius, CHUNK_SIZE do
if (xi ~= x) and (yi ~= y) then
local base = getChunkBase(map, positionToChunkXY(map, xi, yi))
local base = getChunkBase(map, getChunkByXY(map, xi, yi))
if base then
local distance = euclideanDistancePoints(base.x, base.y, x, y)
if (distance <= (BASE_DISTANCE_THRESHOLD + (base.level * 100))) and (distance < closest) then
closest = distance
foundBase = base
if not tested[base] then
tested[base] = true
local distance = euclideanDistancePoints(base.x, base.y, x, y)
if (distance <= (BASE_DISTANCE_THRESHOLD + (base.level * BASE_DISTANCE_LEVEL_BONUS))) and (distance < closest) then
closest = distance
foundBase = base
end
end
end
end
@ -63,20 +80,52 @@ function baseUtils.findNearbyBase(map, chunk, chunkRadius)
return foundBase
end
local function findUpgrade(base, evoIndex, natives)
local used = { }
local alignments = base.alignments
local alignmentPick = base.alignments[mRandom(#base.alignments)]
while alignmentPick do
used[alignmentPick] = true
for evo=evoIndex, 0, -EVOLUTION_INCREMENTS do
local entitySet = natives.evolutionTable[alignmentPick][evo]
if (#entitySet > 0) then
return entitySet[mRandom(#entitySet)]
end
end
alignmentPick = nil
for x = #alignments, 1, -1 do
if not used[alignments[x]] then
alignmentPick = alignments[x]
break
end
end
end
return nil
end
function baseUtils.upgradeEntity(map, entity, surface, natives, evolutionFactor, tick)
if not isRampant(entity.name) then
local position = entity.position
entity.die()
local chunk = positionToChunkXY(map, position)
local chunk = getChunkByPosition(map, position)
local base = getChunkBase(map, chunk)
if not base then
baseUtils.createBase(map, natives, evolutionFactor, chunk, surface, tick)
end
entity = surface.creaate_entity({name = "rampant-suicide-nest-v" .. mRandom(5) .. "-t1",
position = position})
-- local distance = euclideanDistancePoints(position.x, position.y, 0, 0)
local evoIndex = mFloor(evolutionFactor/EVOLUTION_INCREMENTS) * EVOLUTION_INCREMENTS
local spawnerName = findUpgrade(base, evoIndex, natives)
if spawnerName then
entity = surface.create_entity({name = spawnerName, position = position})
end
end
return entity
@ -86,13 +135,19 @@ function baseUtils.createBase(map, natives, evolutionFactor, chunk, surface, tic
local x = chunk.x
local y = chunk.y
local distance = euclideanDistancePoints(x, y, 0, 0)
local meanLevel = mFloor(distance / 200)
local alignments = { BASE_ALIGNMENT_NEUTRAL }
local base = {
x = x,
y = y,
created = tick,
alignment = { BASE_ALIGNMENT_NEUTRAL },
evolution = evolutionFactor,
alignments = alignments,
pattern = mRandom(MAGIC_MAXIMUM_BASE_NUMBER),
level = mFloor(distance / 200)
level = gaussianRandomRange(meanLevel, meanLevel * 0.15, meanLevel * 0.50, meanLevel * 1.50)
}
setChunkBase(map, chunk, base)
@ -101,7 +156,7 @@ function baseUtils.createBase(map, natives, evolutionFactor, chunk, surface, tic
-- return nil
-- end
natives.bases[natives.bases+1] = base
natives.bases[#natives.bases+1] = base
return base
end

View File

@ -19,7 +19,7 @@ local chunkPassScan = chunkUtils.chunkPassScan
-- module code
function chunkProcessor.processPendingChunks(natives, map, surface, pendingStack, tick)
function chunkProcessor.processPendingChunks(natives, map, surface, pendingStack, tick, evolutionFactor)
local processQueue = map.processQueue
local area = map.area
@ -41,7 +41,7 @@ function chunkProcessor.processPendingChunks(natives, map, surface, pendingStack
bottomOffset[1] = x + CHUNK_SIZE
bottomOffset[2] = y + CHUNK_SIZE
chunk = initialScan(chunk, natives, surface, map, tick)
chunk = initialScan(chunk, natives, surface, map, tick, evolutionFactor)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local chunkX = chunk.x

View File

@ -41,12 +41,23 @@ local RESOURCE_GENERATOR_INCREMENT = constants.RESOURCE_GENERATOR_INCREMENT
-- imported functions
local setNestCount = chunkPropertyUtils.setNestCount
local setPlayerBaseGenerator = chunkPropertyUtils.setPlayerBaseGenerator
local addPlayerBaseGenerator = chunkPropertyUtils.addPlayerBaseGenerator
local setResourceGenerator = chunkPropertyUtils.setResourceGenerator
local addResourceGenerator = chunkPropertyUtils.addResourceGenerator
local setWormCount = chunkPropertyUtils.setWormCount
local getPlayerBaseGenerator = chunkPropertyUtils.getPlayerBaseGenerator
local getNestCount = chunkPropertyUtils.getNestCount
local findNearbyBase = baseUtils.findNearbyBase
local createBase = baseUtils.createBase
local setChunkBase = chunkPropertyUtils.setChunkBase
local getEnemyStructureCount = chunkPropertyUtils.getEnemyStructureCount
local getChunkByUnalignedXY = mapUtils.getChunkByUnalignedXY
local getChunkByPosition = mapUtils.getChunkByPosition
local mFloor = math.floor
@ -85,7 +96,7 @@ local function fullScan(chunk, can_place_entity, canPlaceQuery)
return passableNorthSouth, passableEastWest
end
local function addEnemyStructureToChunk(map, chunk, entity)
local function addEnemyStructureToChunk(map, chunk, entity, base)
local lookup
if (entity.type == "unit-spawner") then
lookup = map.chunkToNests
@ -101,15 +112,7 @@ local function addEnemyStructureToChunk(map, chunk, entity)
end
lookup[chunk] = lookup[chunk] + 1
-- if base then
-- local baseCollection = map.chunkToBases[chunk]
-- if not baseCollection then
-- baseCollection = {}
-- map.chunkToBases[chunk] = baseCollection
-- end
-- baseCollection[base.id] = chunk
-- end
setChunkBase(map, chunk, base)
end
local function removeEnemyStructureFromChunk(map, chunk, entity)
@ -122,20 +125,9 @@ local function removeEnemyStructureFromChunk(map, chunk, entity)
return
end
-- local base = indexChunk[entity.unit_number]
-- local indexBase
-- if base then
-- if (entity.type == "unit-spawner") then
-- if base.hives[entity.unit_number] then
-- indexBase = base.hives
-- else
-- indexBase = base.nests
-- end
-- elseif (entity.type == "turret") then
-- indexBase = base.worms
-- end
-- indexBase[entity.unit_number] = nil
-- end
if (getEnemyStructureCount(map, chunk) == 0) then
setChunkBase(map, chunk, nil)
end
if lookup[chunk] then
if ((lookup[chunk] - 1) <= 0) then
@ -297,10 +289,10 @@ function chunkUtils.initialScan(chunk, natives, surface, map, tick, evolutionFac
end
end
chunkUtils.setNestCount(map, chunk, nests)
chunkUtils.setPlayerBaseGenerator(map, chunk, playerObjects)
chunkUtils.setResourceGenerator(map, chunk, resources)
chunkUtils.setWormCount(map, chunk, worms)
setNestCount(map, chunk, nests)
setPlayerBaseGenerator(map, chunk, playerObjects)
setResourceGenerator(map, chunk, resources)
setWormCount(map, chunk, worms)
chunk[PASSABLE] = pass
chunk[PATH_RATING] = passScore
@ -317,9 +309,9 @@ function chunkUtils.chunkPassScan(chunk, surface, map)
if (passScore >= 0.40) then
local pass = chunkUtils.scanChunkPaths(chunk, surface, map)
local playerObjects = chunkUtils.getPlayerBaseGenerator(map, chunk)
local playerObjects = getPlayerBaseGenerator(map, chunk)
local nests = chunkUtils.getNestCount(map, chunk)
local nests = getNestCount(map, chunk)
if ((playerObjects > 0) or (nests > 0)) and (pass == CHUNK_IMPASSABLE) then
pass = CHUNK_ALL_DIRECTIONS
@ -336,7 +328,7 @@ end
function chunkUtils.analyzeChunk(chunk, natives, surface, map)
local playerObjects = chunkUtils.scorePlayerBuildings(surface, map, natives)
chunkUtils.setPlayerBaseGenerator(map, chunk, playerObjects)
setPlayerBaseGenerator(map, chunk, playerObjects)
end
function chunkUtils.createChunk(topX, topY)
@ -382,22 +374,31 @@ function chunkUtils.entityForPassScan(map, entity)
end
end
function chunkUtils.registerEnemyBaseStructure(map, entity)
function chunkUtils.registerEnemyBaseStructure(map, entity, natives, evolutionFactor, surface, tick)
local entityType = entity.type
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
local chunk = getChunkByPosition(map, entity.position)
local base
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
base = findNearbyBase(map, chunk, BASE_SEARCH_RADIUS)
if not base then
base = createBase(map, natives, evolutionFactor, chunk, surface, tick)
end
end
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(map, leftTop, entity)
addEnemyStructureToChunk(map, leftTop, entity, base)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(map, rightTop, entity)
addEnemyStructureToChunk(map, rightTop, entity, base)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(map, leftBottom, entity)
addEnemyStructureToChunk(map, leftBottom, entity, base)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(map, rightBottom, entity)
addEnemyStructureToChunk(map, rightBottom, entity, base)
end
end
@ -411,27 +412,15 @@ function chunkUtils.unregisterEnemyBaseStructure(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(map, leftTop, entity)
if getEnemyStructureCount(map, leftTop) == 0 then
setChunkBase(map, leftTop, nil)
end
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(map, rightTop, entity)
if getEnemyStructureCount(map, rightTop) == 0 then
setChunkBase(map, rightTop, nil)
end
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(map, leftBottom, entity)
if getEnemyStructureCount(map, leftBottom) == 0 then
setChunkBase(map, leftBottom, nil)
end
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(map, rightBottom, entity)
if getEnemyStructureCount(map, rightBottom) == 0 then
setChunkBase(map, rightBottom, nil)
end
end
end
end
@ -450,16 +439,16 @@ function chunkUtils.addRemovePlayerEntity(map, entity, natives, addObject, credi
entityValue = -entityValue
end
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(map, leftTop, entityValue)
addPlayerBaseGenerator(map, leftTop, entityValue)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(map, rightTop, entityValue)
addPlayerBaseGenerator(map, rightTop, entityValue)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(map, leftBottom, entityValue)
addPlayerBaseGenerator(map, leftBottom, entityValue)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(map, rightBottom, entityValue)
addPlayerBaseGenerator(map, rightBottom, entityValue)
end
end
return entity
@ -472,16 +461,16 @@ function chunkUtils.unregisterResource(entity, map)
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(map, leftTop, -RESOURCE_GENERATOR_INCREMENT)
addResourceGenerator(map, leftTop, -RESOURCE_GENERATOR_INCREMENT)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(map, rightTop, -RESOURCE_GENERATOR_INCREMENT)
addResourceGenerator(map, rightTop, -RESOURCE_GENERATOR_INCREMENT)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(map, leftBottom, -RESOURCE_GENERATOR_INCREMENT)
addResourceGenerator(map, leftBottom, -RESOURCE_GENERATOR_INCREMENT)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(map, rightBottom, -RESOURCE_GENERATOR_INCREMENT)
addResourceGenerator(map, rightBottom, -RESOURCE_GENERATOR_INCREMENT)
end
end

View File

@ -64,6 +64,7 @@ constants.CHUNK_ALL_DIRECTIONS = 3
-- constants.CHUNK_PLAYER_INTERIOR = 5
constants.BASE_SEARCH_RADIUS = 4 * constants.CHUNK_SIZE
constants.EVOLUTION_INCREMENTS = 0.05
-- ai
@ -97,13 +98,37 @@ constants.AI_MAX_TEMPERAMENT_DURATION = 15
-- ai base
constants.BASE_DISTANCE_THRESHOLD = 15 * constants.CHUNK_SIZE
constants.BASE_DISTANCE_THRESHOLD = 40 * constants.CHUNK_SIZE
constants.BASE_DISTANCE_LEVEL_BONUS = 300
constants.BASE_ALIGNMENT_NEUTRAL = 1
constants.BASE_ALIGNMENT_FIRE = 2
constants.BASE_ALIGNMENT_BURROW = 3
constants.BASE_ALIGNMENT_SUICIDE = 4
constants.BASE_ALIGNMENT_INFEST = 5
constants.BASE_ALIGNMENT_ACID = 6
constants.BASE_ALIGNMENT_FIRE = 7
constants.BASE_ALIGNMENT_PHYSICAL = 8
constants.BASE_ALIGNMENT_LASER = 9
constants.BASE_ALIGNMENT_INFERNO = 10
constants.BASE_ALIGNMENT_POSION = 11
constants.BASE_ALIGNMENT_TROLL = 12
constants.BASE_ALIGNMENT_FAST = 13
constants.BASE_ALIGNMENT_WEB = 14
constants.BASE_ALIGNMENT_DECAYING = 15
constants.BASE_ALIGNMENT_UNDYING = 16
constants.BASE_ALIGNMENT_NEUTRAL_ADVANCED = 17
constants.BASE_ALIGNMENT_ENERGY_THIEF = 18
constants.BASE_ALIGNMENT_ELECTRIC = 19
local neutralPath = {}
neutralPath[constants.BASE_ALIGNMENT_ACID] = true
neutralPath[constants.BASE_ALIGNMENT_FIRE] = true
neutralPath[constants.BASE_ALIGNMENT_PHYSICAL] = true
neutralPath[constants.BASE_ALIGNMENT_LASER] = true
constants.BASE_ALIGNMENT_PATHS = {}
constants.BASE_ALIGNMENT_PATHS[constants.BASE_ALIGNMENT_NEUTRAL] = neutralPath
-- ai retreat
@ -228,7 +253,13 @@ constants.SENTINEL_IMPASSABLE_CHUNK.y = -1
-- unit spawners
constants.SUICIDE_NEST_TIERS = 7
constants.SUICIDE_NEST_VARIATIONS = 5
constants.SUICIDE_BITER_NEST_TIERS = 7
constants.SUICIDE_BITER_NEST_VARIATIONS = 5
constants.NEUTRAL_NEST_TIERS = 7
constants.NEUTRAL_NEST_VARIATIONS = 20
constants.ACID_NEST_TIERS = 7
constants.ACID_NEST_VARIATIONS = 5
return constants

View File

@ -43,7 +43,7 @@ function mapUtils.getChunkByPosition(map, position)
end
function mapUtils.getChunkByUnalignedXY(map, x, y)
local chunkX = map[mFloor(x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE]
local chunkX = map[mFloor(x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE]
if chunkX then
local chunkY = mFloor(y * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE
return chunkX[chunkY] or SENTINEL_IMPASSABLE_CHUNK

View File

@ -117,7 +117,7 @@ function mathUtils.gaussianRandomRangeRG(mean, std_dev, min, max, rg)
repeat
iid1 = 2 * rg() + -1
iid2 = 2 * rg() + -1
q = (iid1 * iid1) + (iid2 * iid2)
q = (iid1 * iid1) + (iid2 * iid2)
until (q ~= 0) and (q < 1)
local s = mSqrt((-2 * mLog10(q)) / q)
local v = iid1 * s

View File

@ -27,6 +27,7 @@ function biterFunctions.makeBiter(name, biterAttributes, biterAttack, biterResis
attack_parameters = biterAttack,
vision_distance = biterAttributes.vision or 30,
movement_speed = biterAttributes.movement,
spawning_time_modifier = biterAttributes.spawningTimeModifer or 0,
distance_per_frame = biterAttributes.distancePerFrame or 0.1,
pollution_to_join_attack = biterAttributes.pollutionToAttack or 200,
distraction_cooldown = biterAttributes.distractionCooldown or 300,
@ -65,6 +66,7 @@ function biterFunctions.makeSpitter(name, biterAttributes, biterAttack, biterRes
attack_parameters = biterAttack,
vision_distance = biterAttributes.vision or 30,
movement_speed = biterAttributes.movement,
spawning_time_modifier = biterAttributes.spawningTimeModifer or 0,
distance_per_frame = biterAttributes.distancePerFrame or 0.1,
pollution_to_join_attack = biterAttributes.pollutionToAttack or 200,
distraction_cooldown = biterAttributes.distractionCooldown or 300,
@ -306,6 +308,35 @@ function biterFunctions.findTint(entity)
return entity.run_animation.layers[2].tint
end
function biterFunctions.createMeleeAttack(attributes)
return
{
type = "projectile",
range = attributes.range or 0.5,
cooldown = attributes.cooldown or 30,
ammo_category = "melee",
ammo_type = {
category = "melee",
target_type = "entity",
action =
{
type = "direct",
action_delivery =
{
type = "instant",
target_effects =
{
type = "damage",
damage = { amount = attributes.damage, type = "physical"}
}
}
}
},
sound = make_biter_roars(0.4),
animation = biterattackanimation(attributes.scale, attributes.tint1, attributes.tint2)
}
end
function biterFunctions.createFireAttack(attributes, fireAttack)
local attack = {
type = "stream",

View File

@ -270,6 +270,29 @@ function tests.mergeBases()
baseUtils.mergeBases(natives)
end
function tests.showBaseGrid()
local n = {}
for k,v in pairs(global.natives.bases) do
n[v] = k % 5
end
local chunks = global.map.chunkToBase
for chunk,base in pairs(chunks) do
local pick = n[base]
local color = "concrete"
if (pick == 1) then
color = "hazard-concrete-left"
elseif (pick == 2) then
color = "deepwater"
elseif (pick == 3) then
color = "water-green"
elseif (pick == 4) then
color = "water"
end
chunkUtils.colorChunk(chunk.x, chunk.y, color, game.surfaces[1])
end
end
function tests.showMovementGrid()
local chunks = global.map.processQueue
@ -303,6 +326,17 @@ function tests.colorResourcePoints()
end
end
function tests.entityStats(name, d)
local playerPosition = game.players[1].position
local chunkX = math.floor(playerPosition.x * 0.03125) * 32
local chunkY = math.floor(playerPosition.y * 0.03125) * 32
local a = game.surfaces[1].create_entity({name=name, position={chunkX, chunkY}})
if d then
a['direction'] = d
end
print(serpent.dump(a))
a.destroy()
end
function tests.exportAiState(onTick)