448 lines
15 KiB
Lua
448 lines
15 KiB
Lua
if (undergroundAttackG) then
|
|
return undergroundAttackG
|
|
end
|
|
local undergroundAttack = {}
|
|
|
|
local mapUtils = require("libs/MapUtils")
|
|
local mathUtils = require("libs/MathUtils")
|
|
local scoreChunks = require("ScoreChunks")
|
|
local unitGroupUtils = require("libs/UnitGroupUtils")
|
|
|
|
local positionToChunkXY = mapUtils.positionToChunkXY
|
|
local getChunkListInRange = mapUtils.getChunkListInRange
|
|
local getChunkByXY = mapUtils.getChunkByXY
|
|
local scoreAttackLocation = scoreChunks.scoreAttackLocation
|
|
local createSquad = unitGroupUtils.createSquad
|
|
|
|
local mRandom = math.random
|
|
|
|
local processingInterval = 120
|
|
local undergroundSpeed = 5 -- tiles
|
|
local undergroundScanRange = 15
|
|
local playerMilitaryInRange_Query = {
|
|
--force = universe.activePlayerForces,
|
|
force = nil,
|
|
type = {"ammo-turret", "fluid-turret", "electric-turret", "radar"},
|
|
position = nil,
|
|
radius = undergroundScanRange,
|
|
limit=1
|
|
}
|
|
|
|
local playerRandomUndergroundTarget_Query = {
|
|
--force = universe.activePlayerForces,
|
|
force = nil,
|
|
type = {"mining-drill", "generator", "artillery-turret", "radar", "train-stop"}
|
|
}
|
|
|
|
|
|
function undergroundAttack.drawDigInDust(surface, position)
|
|
surface.create_trivial_smoke({name = "digIn-dust-nonTriggerCloud-rampant", position = position})
|
|
end
|
|
|
|
function undergroundAttack.drawDigOutDust(surface, position)
|
|
surface.create_entity({name = "digOut-dust-cloud-rampant", position = position})
|
|
end
|
|
|
|
function undergroundAttack.drawUndegroundDust(surface, position)
|
|
surface.create_entity({name = "undeground-dust-cloud-rampant", position = position})
|
|
surface.create_trivial_smoke({name = "undergroundTrace-dust-nonTriggerCloud-rampant", position = position})
|
|
|
|
end
|
|
|
|
local sideVectors = {{1,0}, {0,1}, {-1,0}, {0,-1}}
|
|
local function digOut(undergroundSquad, targetPosition)
|
|
local map = undergroundSquad.map
|
|
if (not map) or (not map.surface.valid) then
|
|
return
|
|
end
|
|
local universe = map.universe
|
|
local surface = map.surface
|
|
|
|
local squadMembers = undergroundSquad.squadMembers
|
|
local members = squadMembers.members
|
|
-- game.print("digOut: [gps=" .. undergroundSquad.position.x .. "," .. undergroundSquad.position.y .."]") -- debug
|
|
local queuedMembers = {}
|
|
local decomressLater = false
|
|
local squad = createSquad(undergroundSquad.position, map, nil, false)
|
|
local group
|
|
squad.disbandTick = game.tick + 18000
|
|
|
|
if squad and squad.group and squad.group.valid then
|
|
squad.rabid = true
|
|
local digOutPositions = {}
|
|
-------------------
|
|
if targetPosition then
|
|
digOutPositions = {}
|
|
local dx
|
|
local dy
|
|
if targetPosition.x > undergroundSquad.position.x then
|
|
dx = 1
|
|
else
|
|
dx = -1
|
|
end
|
|
if targetPosition.y > undergroundSquad.position.y then
|
|
dy = 1
|
|
else
|
|
dy = -1
|
|
end
|
|
for x = undergroundSquad.position.x - 5*dx, undergroundSquad.position.x, dx do
|
|
for y = undergroundSquad.position.y - 5*dy, undergroundSquad.position.y, dy do
|
|
local tile = surface.get_tile({x, y})
|
|
if tile and (not tile.collides_with("resource-layer")) then
|
|
digOutPositions[#digOutPositions+1] = {x = x, y = y}
|
|
end
|
|
end
|
|
end
|
|
|
|
if #digOutPositions < 5 then
|
|
for x = undergroundSquad.position.x, targetPosition.x, dx do
|
|
for y = undergroundSquad.position.y, targetPosition.y, dy do
|
|
local tile = surface.get_tile({x , y})
|
|
if tile and (not tile.collides_with("resource-layer")) then
|
|
digOutPositions[#digOutPositions+1] = {x = x, y = y}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #digOutPositions == 0 then
|
|
digOutPositions[1] = {x = targetPosition.x, y = targetPosition.y}
|
|
end
|
|
|
|
else
|
|
|
|
for dx = 1, 5 do
|
|
for dy = 1, 5 do
|
|
for _, vector in pairs(sideVectors) do
|
|
local x = undergroundSquad.position.x + dx*vector[1]
|
|
local y = undergroundSquad.position.y + dx*vector[2]
|
|
local tile = surface.get_tile({x, y})
|
|
if tile and (not tile.collides_with("resource-layer")) then
|
|
digOutPositions[#digOutPositions+1] = {x = x, y = y}
|
|
end
|
|
end
|
|
end
|
|
if #digOutPositions == 0 then
|
|
digOutPositions[1] = {x = undergroundSquad.position.x, y = undergroundSquad.position.y}
|
|
end
|
|
end
|
|
|
|
end
|
|
-------------------
|
|
|
|
|
|
group = squad.group
|
|
|
|
local undergroundTotal = 0
|
|
local unitsInTile = 999
|
|
local digOutIndex
|
|
local digOutPosition
|
|
|
|
for entityName, count in pairs (members) do
|
|
if count > 1 then
|
|
for i = 1, count do
|
|
if unitsInTile > 5 then
|
|
unitsInTile = 0
|
|
digOutIndex, digOutPosition = next(digOutPositions, digOutIndex)
|
|
if not digOutIndex then
|
|
digOutIndex, digOutPosition = next(digOutPositions, nil)
|
|
end
|
|
end
|
|
local prototypeList = game.get_filtered_entity_prototypes({{filter = "name", name = entityName}})
|
|
|
|
if prototypeList and (#prototypeList>0) then
|
|
local newEntity = surface.create_entity({
|
|
name = entityName,
|
|
position = digOutPosition,
|
|
force = "enemy",
|
|
})
|
|
if newEntity and newEntity.valid then
|
|
unitsInTile = unitsInTile + 1
|
|
group.add_member(newEntity)
|
|
|
|
newEntity.destructible = false
|
|
universe.oneTickImmunityUnits[#universe.oneTickImmunityUnits+1] = {entity = newEntity, tick = game.tick + 1}
|
|
undergroundTotal = undergroundTotal + 1
|
|
|
|
if undergroundTotal >= 200 then
|
|
queuedMembers[newEntity.unit_number] = {
|
|
count = count - i,
|
|
name = entityName,
|
|
position = {x = newEntity.position.x, y = newEntity.position.y},
|
|
direction = newEntity.direction,
|
|
force = "enemy"
|
|
}
|
|
decomressLater = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if undergroundTotal > 0 then
|
|
undergroundAttack.drawDigOutDust(surface, undergroundSquad.position)
|
|
universe.squadCount = universe.squadCount + 1
|
|
universe.groupNumberToSquad[squad.groupNumber] = squad
|
|
end
|
|
universe.undergroundSquads[undergroundSquad.groupNumber] = nil
|
|
|
|
if decomressLater then
|
|
universe.decomressQueue[group] = queuedMembers
|
|
end
|
|
end
|
|
|
|
|
|
return squad
|
|
end
|
|
|
|
local function getRandomUndergroundTarget(map)
|
|
local surface = map.surface
|
|
local entities = surface.find_entities_filtered(playerRandomUndergroundTarget_Query)
|
|
if #entities > 0 then
|
|
local entity = entities[mRandom(1, #entities)]
|
|
if entity.valid then
|
|
return entity
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function showFlyingText(parameters)
|
|
for i, player in pairs(game.connected_players) do
|
|
if player.valid then
|
|
if (not parameters.surface) or (parameters.surface.index == ((player.surface and player.surface.index) or 0)) then
|
|
player.create_local_flying_text(parameters)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function undergroundAttack.createUndergroudAttack(map, squad)
|
|
if (not squad) then
|
|
return
|
|
end
|
|
local group = squad.group
|
|
if not group then
|
|
return
|
|
end
|
|
local universe = map.universe
|
|
local compressedUnits = universe.compressedUnits
|
|
|
|
local surface = map.surface
|
|
local squadMembers = {total = 0, members = {}}
|
|
local members = squadMembers.members
|
|
local dustCloudPositions = {}
|
|
for _, entity in pairs(group.members) do
|
|
if entity.valid then
|
|
local positionIndex = "x"..math.ceil(entity.position.x).."y"..math.ceil(entity.position.y)
|
|
local entityIndex = entity.name
|
|
local compressedUnit = compressedUnits[entity.unit_number]
|
|
if compressedUnit then
|
|
members[entityIndex] = (members[entityIndex] or 0) + compressedUnit.count
|
|
squadMembers.total = squadMembers.total + compressedUnit.count
|
|
|
|
compressedUnits[entity.unit_number] = nil
|
|
else
|
|
members[entityIndex] = (members[entityIndex] or 0) + 1
|
|
squadMembers.total = squadMembers.total + 1
|
|
end
|
|
dustCloudPositions[positionIndex] = {x = entity.position.x, y = entity.position.y}
|
|
entity.destroy()
|
|
end
|
|
end
|
|
|
|
local undergroundSquad = createSquad(group.position, map, group, false)
|
|
undergroundSquad.position = {x = group.position.x, y = group.position.y}
|
|
undergroundSquad.nextTick = game.tick + processingInterval
|
|
undergroundSquad.squadMembers = squadMembers
|
|
if squad.undergoundAttack == "randomTarget" then
|
|
playerRandomUndergroundTarget_Query.force = universe.activePlayerForces
|
|
local target = getRandomUndergroundTarget(map)
|
|
if target then
|
|
undergroundSquad.targetPosition = {x = target.position.x, y = target.position.y}
|
|
end
|
|
elseif (squad.undergoundAttack == "position") and (squad.targetPosition) then
|
|
undergroundSquad.targetPosition = {x = squad.targetPosition.x, y = squad.targetPosition.y}
|
|
end
|
|
-- debug
|
|
-- if undergroundSquad.targetPosition then
|
|
-- rendering.draw_line{surface = group.surface, from = group.position, to = undergroundSquad.targetPosition, color = {1,0,0}, width = 2, time_to_live = 3600}
|
|
-- end
|
|
|
|
for _, position in pairs(dustCloudPositions) do
|
|
undergroundAttack.drawDigInDust(surface, position)
|
|
end
|
|
|
|
map.universe.undergroundSquads[undergroundSquad.groupNumber] = undergroundSquad
|
|
-- game.print(undergroundSquad.groupNumber..": undergroundAttack.nextTick = "..undergroundSquad.nextTick.."[gps=" .. squad.group.position.x .. "," .. squad.group.position.y .."]") -- debug
|
|
|
|
group.destroy()
|
|
-- squad will be deleted in squadAttack.cleanSquads
|
|
return undergroundSquad
|
|
end
|
|
|
|
function undergroundAttack.onUnitKilled_DigIn(map, entity, cause)
|
|
if (not entity) or not (entity.valid) then
|
|
return
|
|
end
|
|
local group = entity.unit_group
|
|
if not group then
|
|
return
|
|
end
|
|
local squad = map.universe.groupNumberToSquad[group.group_number] -- or universe.nonRampantCompressedSquads[group.group_number]
|
|
if (not squad) or squad.rabid or squad.frenzy then
|
|
return
|
|
end
|
|
local roll = math.random()
|
|
if roll < 0.2 then
|
|
squad.undergoundAttack = "randomTarget"
|
|
elseif roll < 0.6 then
|
|
squad.undergoundAttack = "common"
|
|
else
|
|
squad.undergoundAttack = "position"
|
|
squad.targetPosition = {x = cause.position.x, y = cause.position.y}
|
|
end
|
|
undergroundAttack.createUndergroudAttack(map, squad)
|
|
end
|
|
|
|
local function findNextAttackChunk(map, undergroundSquad)
|
|
local x, y = positionToChunkXY(undergroundSquad.position)
|
|
local chunk = getChunkByXY(map, x, y)
|
|
local chunkList = getChunkListInRange(map, x, y, undergroundSquad.range or 2)
|
|
|
|
local highestScore = -1
|
|
local highestChunk = -1
|
|
if chunk ~= -1 then
|
|
highestScore = scoreAttackLocation(map, chunk)
|
|
end
|
|
for i = 1, #chunkList do
|
|
local neighborChunk = chunkList[i]
|
|
if neighborChunk~=-1 then
|
|
local score = scoreAttackLocation(map, neighborChunk)
|
|
if (score > highestScore) then
|
|
highestScore = score
|
|
highestChunk = neighborChunk
|
|
end
|
|
end
|
|
end
|
|
return highestChunk
|
|
end
|
|
|
|
-- move to target position, check for military, dig out.
|
|
local function processUndergroundSquad(map, undergroundSquad)
|
|
local surface = map.surface
|
|
|
|
local traveled = 0
|
|
local dx = 0
|
|
local dy = 0
|
|
local range = 1
|
|
if undergroundSquad.targetPosition then
|
|
dx = undergroundSquad.targetPosition.x - undergroundSquad.position.x
|
|
dy = undergroundSquad.targetPosition.y - undergroundSquad.position.y
|
|
range = mathUtils.euclideanDistancePoints(undergroundSquad.position.x, undergroundSquad.position.y, undergroundSquad.targetPosition.x, undergroundSquad.targetPosition.y)
|
|
end
|
|
|
|
if range <= undergroundSpeed then
|
|
if undergroundSquad.targetPosition then
|
|
undergroundSquad.position.x = undergroundSquad.targetPosition.x
|
|
undergroundSquad.position.y = undergroundSquad.targetPosition.y
|
|
end
|
|
local nextAttackChunk = findNextAttackChunk(map, undergroundSquad)
|
|
if nextAttackChunk == - 1 then
|
|
--game.print("processUndergroundSquad : no nextAttackChunk") -- debug
|
|
digOut(undergroundSquad)
|
|
return
|
|
else
|
|
undergroundSquad.targetPosition = {x = nextAttackChunk.x, y = nextAttackChunk.y}
|
|
traveled = range
|
|
|
|
dx = undergroundSquad.targetPosition.x - undergroundSquad.position.x
|
|
dy = undergroundSquad.targetPosition.y - undergroundSquad.position.y
|
|
range = mathUtils.euclideanDistancePoints(undergroundSquad.position.x, undergroundSquad.position.y, undergroundSquad.targetPosition.x, undergroundSquad.targetPosition.y)
|
|
end
|
|
end
|
|
local kf = math.min((undergroundSpeed - traveled)/ range , 1)
|
|
undergroundSquad.position.x = math.ceil(undergroundSquad.position.x + dx * kf)
|
|
undergroundSquad.position.y = math.ceil(undergroundSquad.position.y + dy * kf)
|
|
undergroundSquad.nextTick = game.tick + processingInterval
|
|
|
|
|
|
playerMilitaryInRange_Query.force = map.universe.activePlayerForces
|
|
playerMilitaryInRange_Query.position = undergroundSquad.position
|
|
local entities = surface.find_entities_filtered(playerMilitaryInRange_Query)
|
|
if #entities > 0 then
|
|
-- game.print("processUndergroundSquad : digOut, military ") -- debug
|
|
digOut(undergroundSquad, entities[1].position)
|
|
else
|
|
undergroundAttack.drawUndegroundDust(surface, undergroundSquad.position)
|
|
showFlyingText({text = tostring(undergroundSquad.squadMembers.total).." [entity=big-biter]", surface = surface, position = undergroundSquad.position, color = {1, 0, 0}, speed = 1, time_to_live = 30})
|
|
if not map.universe.firtstUndergroundAlertShown then
|
|
local chunkX = math.floor(undergroundSquad.position.x / 32)
|
|
local chunkY = math.floor(undergroundSquad.position.y / 32)
|
|
if game.forces["player"].is_chunk_visible(surface, {x = chunkX, y = chunkY}) then
|
|
game.print({"", {"description.rampantFixed--firstUndergroundAttackWarning"},": ", ("[gps=" .. undergroundSquad.position.x .. "," .. undergroundSquad.position.y .."]")})
|
|
map.universe.firtstUndergroundAlertShown = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local maxSquadsPerProcessing = 10
|
|
function undergroundAttack.processUndergroundSquads(universe)
|
|
if not universe.undergroundAttack then
|
|
return
|
|
end
|
|
local squadsProcessed = 0
|
|
for i, undergroundSquad in pairs(universe.undergroundSquads) do
|
|
map = undergroundSquad.map
|
|
if map and map.surface and map.surface.valid then
|
|
if undergroundSquad.nextTick <= game.tick then
|
|
processUndergroundSquad(map, undergroundSquad)
|
|
squadsProcessed = squadsProcessed + 1
|
|
if squadsProcessed >= maxSquadsPerProcessing then
|
|
break
|
|
end
|
|
end
|
|
else
|
|
universe.undergroundSquads[i] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function undergroundAttack.updateUndergroundAttackProbability(universe)
|
|
if not universe.undergroundAttack then
|
|
return
|
|
end
|
|
|
|
local newUndergroundAttackProbability
|
|
local evolution_factor = game.forces.enemy.evolution_factor
|
|
local minEvo = 0.4
|
|
local maxEvo = 0.8
|
|
if evolution_factor <= minEvo then
|
|
newUndergroundAttackProbability = 0
|
|
elseif evolution_factor >= maxEvo then
|
|
newUndergroundAttackProbability = settings.global["rampantFixed--undergroundAttackProbability"].value
|
|
else
|
|
newUndergroundAttackProbability = settings.global["rampantFixed--undergroundAttackProbability"].value * ((evolution_factor - minEvo) / (maxEvo - minEvo))
|
|
if newUndergroundAttackProbability < 0.001 then
|
|
newUndergroundAttackProbability = 0
|
|
end
|
|
end
|
|
if universe.undergroundAttackProbability == 0 then
|
|
universe.undergroundAttackProbability = newUndergroundAttackProbability
|
|
if newUndergroundAttackProbability > 0 then
|
|
game.print({"description.rampantFixed--undergroundAttackWarning"})
|
|
end
|
|
else
|
|
universe.undergroundAttackProbability = math.max(newUndergroundAttackProbability, 0.0001)
|
|
end
|
|
end
|
|
|
|
undergroundAttackG = undergroundAttack
|
|
return undergroundAttack
|