mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-03-17 21:08:08 +02:00
Updates/frontier v6 (#1431)
* New terrain gen * Artillery targeting * Fix silo positioning * Frontier v6 tweaks
This commit is contained in:
parent
a5a61b9a32
commit
7789ac4ec6
@ -1,24 +1,75 @@
|
||||
local Event = require 'utils.event'
|
||||
local Global = require 'utils.global'
|
||||
local Task = require 'utils.task'
|
||||
local Token = require 'utils.token'
|
||||
local register_on_entity_destroyed = script.register_on_entity_destroyed
|
||||
|
||||
local Public = {}
|
||||
local turrets_map = {}
|
||||
local register_map = {}
|
||||
local primitives = { index = nil }
|
||||
local Artillery = {
|
||||
enabled = true,
|
||||
turret_name = 'artillery-turret',
|
||||
character_projectile = 'artillery-projectile',
|
||||
structure_projectile = 'rocket',
|
||||
target_force = 'player',
|
||||
last_fire = 0,
|
||||
fire_interval = 60, -- ticks
|
||||
cooldown = 480, -- ticks
|
||||
min_range = 32, -- tiles
|
||||
max_range = 224, -- tiles
|
||||
max_targeting_attempts = 10,
|
||||
range_modifier = nil,
|
||||
manual_range_modifier = nil,
|
||||
creation_distance = 1.6,
|
||||
target_offset = { -0.15625, -0.07812 },
|
||||
target_entities = {
|
||||
'artillery-turret',
|
||||
'artillery-wagon',
|
||||
'car',
|
||||
'cargo-wagon',
|
||||
'character',
|
||||
'flamethrower-turret',
|
||||
'fluid-wagon',
|
||||
'furnace',
|
||||
'gun-turret',
|
||||
'lab',
|
||||
'laser-turret',
|
||||
'locomotive',
|
||||
'radar',
|
||||
'silo',
|
||||
'spidertron',
|
||||
'tank',
|
||||
},
|
||||
}
|
||||
|
||||
Global.register(
|
||||
{
|
||||
turrets_map = turrets_map,
|
||||
register_map = register_map,
|
||||
primitives = primitives,
|
||||
artillery = Artillery,
|
||||
},
|
||||
function(tbl)
|
||||
turrets_map = tbl.turrets_map
|
||||
register_map = tbl.register_map
|
||||
primitives = tbl.primitives
|
||||
Artillery = tbl.artillery
|
||||
end)
|
||||
|
||||
local function distance(posA, posB)
|
||||
return math.sqrt((posA.x - posB.x)^2 + (posA.y - posB.y)^2)
|
||||
end
|
||||
|
||||
function Public.get_artillery_settings()
|
||||
return Artillery
|
||||
end
|
||||
|
||||
function Public.set_artillery_settings(key, value)
|
||||
Artillery[key] = value
|
||||
end
|
||||
|
||||
function Public.register(entity, refill_name)
|
||||
if not (entity and entity.valid) then
|
||||
return
|
||||
@ -30,8 +81,9 @@ function Public.register(entity, refill_name)
|
||||
|
||||
local is_item = game.item_prototypes[refill_name] and true or false
|
||||
local is_fluid = game.fluid_prototypes[refill_name] and true or false
|
||||
local is_artillery = entity.prototype.type == Artillery.turret_name
|
||||
|
||||
if not (is_item or is_fluid )then
|
||||
if not (is_item or is_fluid or is_artillery) then
|
||||
return
|
||||
end
|
||||
|
||||
@ -42,11 +94,18 @@ function Public.register(entity, refill_name)
|
||||
entity = entity,
|
||||
refill = refill_name,
|
||||
is_fluid = is_fluid,
|
||||
is_artillery = is_artillery,
|
||||
destroy_id = destroy_id
|
||||
}
|
||||
|
||||
if data.is_artillery then
|
||||
data.next_fire = game.tick
|
||||
end
|
||||
if data.is_fluid then
|
||||
data.capacity = data.entity.fluidbox.get_capacity(1)
|
||||
data.fluid_stack = {
|
||||
name = data.refill,
|
||||
amount = data.entity.fluidbox.get_capacity(1) or 100
|
||||
}
|
||||
else
|
||||
data.item_stack = {
|
||||
name = data.refill,
|
||||
@ -75,6 +134,65 @@ function Public.reset()
|
||||
end
|
||||
end
|
||||
|
||||
local artillery_projectile_token = Token.register(function(data)
|
||||
local surface = data.surface
|
||||
local target = data.target
|
||||
local source = data.source
|
||||
if not (surface and surface.valid) then return end
|
||||
if not (target and target.valid) then return end
|
||||
if not (source and source.valid) then return end
|
||||
surface.create_entity{
|
||||
name = data.name,
|
||||
position = data.position,
|
||||
target = target,
|
||||
source = source,
|
||||
force = 'enemy',
|
||||
speed = 1.5,
|
||||
}
|
||||
end)
|
||||
|
||||
local function simulate_automatic_artillery(data)
|
||||
if data.next_fire > game.tick then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = data.entity
|
||||
local surface = entity.surface
|
||||
local source_position = entity.position
|
||||
local range_modifier = Artillery.range_modifier or entity.force.artillery_range_modifier or 0
|
||||
local manual_range_modifier = Artillery.manual_range_modifier or entity.prototype.manual_range_modifier or 0
|
||||
local params = {
|
||||
position = source_position,
|
||||
radius = Artillery.max_range * (1 + range_modifier) * (1 + manual_range_modifier),
|
||||
name = Artillery.target_entities,
|
||||
force = Artillery.target_force,
|
||||
limit = Artillery.max_targeting_attempts,
|
||||
}
|
||||
local targets = surface.find_entities_filtered(params)
|
||||
|
||||
if #targets == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
if distance(source_position, target.position) > Artillery.min_range then
|
||||
Task.set_timeout_in_ticks(Artillery.fire_interval, artillery_projectile_token, {
|
||||
surface = surface,
|
||||
name = target.name == 'character' and Artillery.character_projectile or Artillery.structure_projectile,
|
||||
position = target.position,
|
||||
target = target,
|
||||
source = entity,
|
||||
force = 'enemy',
|
||||
speed = 1.5,
|
||||
})
|
||||
data.next_fire = game.tick + Artillery.cooldown
|
||||
Artillery.last_fire = game.tick
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_entity_destroyed(event)
|
||||
local destroy_id = event.registration_number
|
||||
local unit_id = event.unit_number
|
||||
@ -96,10 +214,11 @@ local function on_tick()
|
||||
return
|
||||
end
|
||||
|
||||
if Artillery.enabled and data.is_artillery and ((Artillery.last_fire + Artillery.fire_interval) < game.tick) then
|
||||
simulate_automatic_artillery(data)
|
||||
end
|
||||
if data.is_fluid then
|
||||
local fb = data.entity.fluidbox[1]
|
||||
fb.name = data.refill
|
||||
fb.amount = data.capacity
|
||||
data.entity.insert_fluid(data.fluid_stack)
|
||||
else
|
||||
data.entity.insert(data.item_stack)
|
||||
end
|
||||
|
@ -397,13 +397,18 @@ Event.on_init(function()
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_research_finished, function(event)
|
||||
for _, effect in pairs(event.research.effects or {}) do
|
||||
local technology = event.research
|
||||
if technology.force.name ~= 'player' then
|
||||
return
|
||||
end
|
||||
|
||||
for _, effect in pairs(technology.effects or {}) do
|
||||
if effect.recipe then
|
||||
add_recipe_products(game.forces.player.recipes[effect.recipe])
|
||||
end
|
||||
end
|
||||
|
||||
if event.research.name == 'space-science-pack' then
|
||||
if technology.name == 'space-science-pack' then
|
||||
item_unlocked['space-science-pack'] = item_worths['space-science-pack']
|
||||
end
|
||||
end)
|
||||
|
@ -84,8 +84,10 @@ Enemy.commands = {
|
||||
data.target = target
|
||||
data.stage = Enemy.stages.attack
|
||||
unit_group.set_command {
|
||||
type = defines.command.attack,
|
||||
target = target,
|
||||
type = defines.command.attack_area,--defines.command.attack,
|
||||
destination = target.position,
|
||||
radius = 15,
|
||||
--target = target,
|
||||
distraction = defines.distraction.by_damage
|
||||
}
|
||||
if Public.get()._DEBUG_AI then
|
||||
@ -102,6 +104,31 @@ Enemy.stages = {
|
||||
fail = 5,
|
||||
}
|
||||
|
||||
Enemy.turret_raffle = {
|
||||
-- sets must have at least 1 valid turret and 1 valid refill for function(x_distance, evolution)
|
||||
['base'] = {
|
||||
['gun-turret'] = { weight = 16, min_distance = 0, refill = { 'firearm-magazine', 'piercing-rounds-magazine', 'uranium-rounds-magazine' } },
|
||||
['flamethrower-turret'] = { weight = 2, min_distance = 800, refill = { 'crude-oil', 'heavy-oil', 'light-oil' } },
|
||||
['artillery-turret'] = { weight = 16, min_distance = 2800, refill = { 'artillery-shell' } }
|
||||
},
|
||||
['Krastorio2'] = {
|
||||
['gun-turret'] = { weight = 16, min_distance = 0, refill = { 'rifle-magazine', 'armor-piercing-rifle-magazine', 'uranium-rifle-magazine', 'imersite-rifle-magazine' } },
|
||||
['flamethrower-turret'] = { weight = 2, min_distance = 800, refill = { 'crude-oil', 'heavy-oil', 'light-oil' } },
|
||||
['kr-railgun-turret'] = { weight = 128, min_distance = 1400, refill = { 'basic-railgun-shell', 'explosion-railgun-shell', 'antimatter-railgun-shell' } },
|
||||
['kr-rocket-turret'] = { weight = 16, min_distance = 2000, refill = { 'explosive-turret-rocket', 'nuclear-turret-rocket', 'antimatter-turret-rocket' } },
|
||||
['artillery-turret'] = { weight = 16, min_distance = 2800, refill = { 'artillery-shell', 'nuclear-artillery-shell', 'antimatter-artillery-shell' } },
|
||||
},
|
||||
['zombiesextended-core'] = {
|
||||
['gun-turret'] = { weight = 16, min_distance = 0, refill = { 'firearm-magazine', 'assault-ammo-mk1', 'uranium-rounds-magazine', 'assault-ammo-mk2' } },
|
||||
['gun-turret-mk1'] = { weight = 64, min_distance = 750, refill = { 'piercing-rounds-magazine', 'assault-ammo-mk1', 'assault-ammo-mk2', 'assault-ammo-mk3' } },
|
||||
['gun-turret-mk2'] = { weight = 256, min_distance = 1500, refill = { 'assault-ammo-mk1', 'uranium-rounds-magazine', 'assault-ammo-mk2', 'assault-ammo-mk3' } },
|
||||
['flamethrower-turret'] = { weight = 2, min_distance = 800, refill = { 'crude-oil', 'heavy-oil', 'light-oil' } },
|
||||
['flamethrower-turret-mk1'] = { weight = 4, min_distance = 1200, refill = { 'crude-oil', 'heavy-oil', 'light-oil' } },
|
||||
['flamethrower-turret-mk2'] = { weight = 16, min_distance = 1500, refill = { 'crude-oil', 'heavy-oil', 'light-oil' } },
|
||||
['artillery-turret'] = { weight = 128, min_distance = 2800, refill = { 'artillery-shell' } }
|
||||
}
|
||||
}
|
||||
|
||||
function Enemy.ai_take_control(unit_group)
|
||||
local this = Public.get()
|
||||
if not this.unit_groups[unit_group.group_number] then
|
||||
@ -302,6 +329,37 @@ function Enemy.on_spawner_died(event)
|
||||
end
|
||||
end
|
||||
|
||||
function Enemy.roll_turret(x_distance, evolution)
|
||||
local set = Enemy.turret_raffle['base']
|
||||
if script.active_mods['Krastorio2'] then
|
||||
set = Enemy.turret_raffle['Krastorio2']
|
||||
end
|
||||
if script.active_mods['zombiesextended-core'] then
|
||||
set = Enemy.turret_raffle['zombiesextended-core']
|
||||
end
|
||||
|
||||
local weighted_turrets_table = {}
|
||||
for name, data in pairs(set) do
|
||||
if data.min_distance < x_distance then
|
||||
table.insert(weighted_turrets_table, { name, data.weight })
|
||||
end
|
||||
end
|
||||
|
||||
local turret = Table.get_random_weighted(weighted_turrets_table)
|
||||
local refills = set[turret].refill
|
||||
local refill
|
||||
if evolution < 0.2 then
|
||||
refill = refills[1]
|
||||
elseif evolution < 0.5 then
|
||||
refill = refills[2] or refills[#refills]
|
||||
elseif evolution < 0.75 then
|
||||
refill = refills[3] or refills[#refills]
|
||||
else
|
||||
refill = refills[4] or refills[#refills]
|
||||
end
|
||||
return turret, refill
|
||||
end
|
||||
|
||||
function Enemy.spawn_turret_outpost(position)
|
||||
local this = Public.get()
|
||||
if position.x < this.right_boundary * 32 + this.wall_width then
|
||||
@ -325,31 +383,25 @@ function Enemy.spawn_turret_outpost(position)
|
||||
end
|
||||
|
||||
local evolution = game.forces.enemy.evolution_factor
|
||||
local ammo = 'firearm-magazine'
|
||||
if math_random() < evolution then
|
||||
ammo = 'piercing-rounds-magazine'
|
||||
end
|
||||
if math_random() < evolution then
|
||||
ammo = 'uranium-rounds-magazine'
|
||||
end
|
||||
|
||||
for _, v in pairs({
|
||||
{ x = -5, y = 0 },
|
||||
{ x = 5, y = 0 },
|
||||
{ x = 0, y = 5 },
|
||||
{ x = 0, y = -5 },
|
||||
{ x = -5, y = 0, direction = defines.direction.west },
|
||||
{ x = 5, y = 0, direction = defines.direction.east },
|
||||
{ x = 0, y = 5, direction = defines.direction.south },
|
||||
{ x = 0, y = -5, direction = defines.direction.north },
|
||||
}) do
|
||||
local pos = surface.find_non_colliding_position('gun-turret', { position.x + v.x, position.y + v.y }, 2, 0.5)
|
||||
local turret_name, refill_name = Enemy.roll_turret(position.x, evolution)
|
||||
local pos = surface.find_non_colliding_position(turret_name, { position.x + v.x, position.y + v.y }, 2, 0.5)
|
||||
if pos then
|
||||
local turret = surface.create_entity {
|
||||
name = 'gun-turret',
|
||||
name = turret_name,
|
||||
position = pos,
|
||||
force = 'enemy',
|
||||
move_stuck_players = true,
|
||||
create_build_effect_smoke = true,
|
||||
direction = v.direction,
|
||||
}
|
||||
if turret and turret.valid then
|
||||
EnemyTurret.register(turret, ammo)
|
||||
EnemyTurret.register(turret, refill_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -405,4 +457,12 @@ function Enemy.artillery_explosion(data)
|
||||
end
|
||||
Enemy.artillery_explosion_token = Token.register(Enemy.artillery_explosion)
|
||||
|
||||
function Enemy.on_research_finished(technology)
|
||||
if technology.force.name ~= 'player' then
|
||||
return
|
||||
end
|
||||
|
||||
game.forces.enemy.technologies[technology.name].researched = true
|
||||
end
|
||||
|
||||
return Enemy
|
||||
|
@ -9,7 +9,6 @@ local Enemy = require 'map_gen.maps.frontier.modules.enemy'
|
||||
local Terrain = require 'map_gen.maps.frontier.modules.terrain'
|
||||
local math_abs = math.abs
|
||||
local math_ceil = math.ceil
|
||||
local math_clamp = math.clamp
|
||||
local math_floor = math.floor
|
||||
local math_max = math.max
|
||||
local math_min = math.min
|
||||
@ -134,6 +133,7 @@ function RocketSilo.move_silo(position)
|
||||
|
||||
if old_silo then
|
||||
local result_inventory = old_silo.get_output_inventory().get_contents()
|
||||
Terrain.clear_area{ surface = surface, position = new_position, size = 12 }
|
||||
new_silo = old_silo.clone { position = new_position, force = old_silo.force, create_build_effect_smoke = true }
|
||||
old_silo.destroy()
|
||||
local chest = surface.create_entity { name = 'steel-chest', position = old_position, force = 'player', move_stuck_players = true }
|
||||
@ -160,10 +160,11 @@ function RocketSilo.move_silo(position)
|
||||
for _ = 1, 12 do
|
||||
Task.set_timeout_in_ticks(math_random(30, 4 * 60), Enemy.artillery_explosion_token, { surface_name = surface.name, position = spawn_target.position })
|
||||
end
|
||||
for t = 1, math_clamp(math_floor((#game.connected_players) / 2 + 0.5), 1, 5) do
|
||||
if this.spawn_enemy_wave then
|
||||
for t = 1, math.clamp(math_floor((#game.connected_players) / 2 + 0.5), 1, 5) do
|
||||
Task.set_timeout(15 * t, Enemy.spawn_enemy_wave_token, spawn_target.position)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -172,6 +173,7 @@ function RocketSilo.move_silo(position)
|
||||
enemy_evolution.time_factor = enemy_evolution.time_factor * 1.01
|
||||
else
|
||||
init = true
|
||||
Terrain.clear_area{ surface = surface, position = new_position, size = 12 }
|
||||
new_silo = surface.create_entity { name = 'rocket-silo', position = new_position, force = 'player', move_stuck_players = true }
|
||||
end
|
||||
|
||||
@ -194,6 +196,8 @@ function RocketSilo.move_silo(position)
|
||||
game.print({'frontier.silo_backward', x_diff})
|
||||
end
|
||||
end
|
||||
else
|
||||
log('Could not place silo '..serpent.line(new_position))
|
||||
end
|
||||
end
|
||||
RocketSilo.move_silo_token = Token.register(RocketSilo.move_silo)
|
||||
@ -352,4 +356,21 @@ function RocketSilo.kraken_eat_entity(entity)
|
||||
entity.die('enemy')
|
||||
end
|
||||
|
||||
function RocketSilo.on_research_finished(technology)
|
||||
if technology.force.name ~= 'player' then
|
||||
return
|
||||
end
|
||||
local this = Public.get()
|
||||
local recipes = technology.force.recipes
|
||||
if recipes['rocket-silo'] then
|
||||
recipes['rocket-silo'].enabled = false
|
||||
end
|
||||
if this.rockets_launched == 0 and recipes['landfill'].enabled then
|
||||
recipes['landfill'].enabled = false
|
||||
end
|
||||
if this.rockets_launched > 0 and not recipes['landfill'].enabled then
|
||||
recipes['landfill'].enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
return RocketSilo
|
||||
|
@ -6,6 +6,7 @@ local Queue = require 'utils.queue'
|
||||
local RS = require 'map_gen.shared.redmew_surface'
|
||||
local Public = require 'map_gen.maps.frontier.shared.core'
|
||||
local math_abs = math.abs
|
||||
local math_ceil = math.ceil
|
||||
local math_clamp = math.clamp
|
||||
local math_floor = math.floor
|
||||
local math_max = math.max
|
||||
@ -17,23 +18,39 @@ local q_pop = Queue.pop
|
||||
local simplex = Noise.d2
|
||||
|
||||
local autoplace_controls = {
|
||||
['coal'] = { frequency = 1.1, richness = 0.6, size = 0.75 },
|
||||
['copper-ore'] = { frequency = 1.2, richness = 0.6, size = 0.75 },
|
||||
['crude-oil'] = { frequency = 1, richness = 0.6, size = 0.75 },
|
||||
['coal'] = { frequency = 1.3, richness = 0.7, size = 0.80 },
|
||||
['copper-ore'] = { frequency = 1.4, richness = 0.7, size = 0.85 },
|
||||
['crude-oil'] = { frequency = 1, richness = 0.9, size = 0.95 },
|
||||
['enemy-base'] = { frequency = 6, richness = 0.6, size = 4 },
|
||||
['iron-ore'] = { frequency = 1.135, richness = 0.6, size = 0.85 },
|
||||
['iron-ore'] = { frequency = 1.6, richness = 0.8, size = 1.15 },
|
||||
['stone'] = { frequency = 1, richness = 0.6, size = 0.65 },
|
||||
['trees'] = { frequency = 1, richness = 0.6, size = 1 },
|
||||
['uranium-ore'] = { frequency = 0.5, richness = 0.6, size = 0.5 },
|
||||
['trees'] = { frequency = 1, richness = 0.6, size = 1.2 },
|
||||
['uranium-ore'] = { frequency = 0.5, richness = 0.6, size = 0.6 },
|
||||
}
|
||||
local blacklisted_resources = {
|
||||
['uranium-ore'] = true,
|
||||
['crude-oil'] = true,
|
||||
}
|
||||
local noise_weights = {
|
||||
{ modifier = 0.0042, weight = 1.000 },
|
||||
{ modifier = 0.0310, weight = 0.080 },
|
||||
{ modifier = 0.1000, weight = 0.025 },
|
||||
local noises = {
|
||||
['dungeon_sewer'] = {{ modifier = 0.00055, weight = 1.05 }, { modifier = 0.0062, weight = 0.024 }, { modifier = 0.0275, weight = 0.00135 }},
|
||||
['cave_miner_01'] = {{ modifier = 0.002, weight = 1 }, { modifier = 0.003, weight = 0.5 }, { modifier = 0.01, weight = 0.01 }, { modifier = 0.1, weight = 0.015 }},
|
||||
['oasis'] = {{ modifier = 0.00165, weight = 1.1 }, { modifier = 0.00275, weight = 0.55 }, { modifier = 0.011, weight = 0.165 }, { modifier = 0.11, weight = 0.0187 }},
|
||||
['dungeons'] = {{ modifier = 0.0028, weight = 0.99 }, { modifier = 0.0059, weight = 0.21 }},
|
||||
['cave_rivers_2'] = {{ modifier = 0.0035, weight = 0.90 }, { modifier = 0.0088, weight = 0.15 }, { modifier = 0.051, weight = 0.011 }},
|
||||
['cave_miner_02'] = {{ modifier = 0.006, weight = 1 }, { modifier = 0.02, weight = 0.15 }, { modifier = 0.25, weight = 0.025 }},
|
||||
['large_caves'] = {{ modifier = 0.055, weight = 0.045 }, { modifier = 0.11, weight = 0.042 }, { modifier = 0.00363, weight = 1.05 }, { modifier = 0.01, weight = 0.23 }},
|
||||
['no_rocks'] = {{ modifier = 0.00495, weight = 0.945 }, { modifier = 0.01665, weight = 0.2475 }, { modifier = 0.0435, weight = 0.0435 }, { modifier = 0.07968, weight = 0.0315 }},
|
||||
['scrapyard'] = {{ modifier = 0.0055, weight = 1.1 }, { modifier = 0.011, weight = 0.385 }, { modifier = 0.055, weight = 0.253 }, { modifier = 0.11, weight = 0.121 }},
|
||||
['scrapyard_2'] = {{ modifier = 0.0066, weight = 1.1 }, { modifier = 0.044, weight = 0.165 }, { modifier = 0.242, weight = 0.055 }, { modifier = 0.055, weight = 0.352 }},
|
||||
['smol_areas'] = {{ modifier = 0.0052, weight = 0.83 }, { modifier = 0.139, weight = 0.144 }, { modifier = 0.129, weight = 0.072 }, { modifier = 0.111, weight = 0.01 }},
|
||||
['cave_rivers'] = {{ modifier = 0.0053, weight = 0.71 }, { modifier = 0.0086, weight = 0.24 }, { modifier = 0.070, weight = 0.025 }},
|
||||
['small_caves'] = {{ modifier = 0.0066, weight = 1.1 }, { modifier = 0.044, weight = 0.165 }, { modifier = 0.242, weight = 0.055 }},
|
||||
['forest_location'] = {{ modifier = 0.0066, weight = 1.1 }, { modifier = 0.011, weight = 0.275 }, { modifier = 0.055, weight = 0.165 }, { modifier = 0.11, weight = 0.0825 }},
|
||||
['small_caves_2'] = {{ modifier = 0.0099, weight = 1.1 }, { modifier = 0.055, weight = 0.275 }, { modifier = 0.275, weight = 0.055 }},
|
||||
['forest_density'] = {{ modifier = 0.01, weight = 1 }, { modifier = 0.05, weight = 0.5 }, { modifier = 0.1, weight = 0.025 }},
|
||||
['cave_ponds'] = {{ modifier = 0.014, weight = 0.77 }, { modifier = 0.18, weight = 0.085 }},
|
||||
['no_rocks_2'] = {{ modifier = 0.0184, weight = 1.265 }, { modifier = 0.143, weight = 0.1045 }},
|
||||
['mixed_ore'] = {{ modifier = 0.0042, weight = 1.000 }, { modifier = 0.0310, weight = 0.080 }, { modifier = 0.1000, weight = 0.025 }},
|
||||
}
|
||||
local mixed_ores = { 'iron-ore', 'copper-ore', 'iron-ore', 'stone', 'copper-ore', 'iron-ore', 'copper-ore', 'iron-ore', 'coal', 'iron-ore', 'copper-ore', 'iron-ore', 'stone', 'copper-ore', 'coal'}
|
||||
|
||||
@ -119,8 +136,9 @@ function Terrain.get_map()
|
||||
return map
|
||||
end
|
||||
|
||||
function Terrain.noise_pattern(position, seed)
|
||||
function Terrain.noise_pattern(feature, position, seed)
|
||||
local noise, d = 0, 0
|
||||
local noise_weights = noises[feature]
|
||||
for i = 1, #noise_weights do
|
||||
local nw = noise_weights[i]
|
||||
noise = noise + simplex(position.x * nw.modifier, position.y * nw.modifier, seed) * nw.weight
|
||||
@ -163,7 +181,7 @@ function Terrain.mixed_resources(surface, area)
|
||||
for y = 0, 31 do
|
||||
local position = { x = left_top.x + x, y = left_top.y + y }
|
||||
if can_place_entity({ name = 'iron-ore', position = position }) then
|
||||
local noise = Terrain.noise_pattern(position, seed)
|
||||
local noise = Terrain.noise_pattern('mixed_ore', position, seed)
|
||||
if math_abs(noise) > 0.77 then
|
||||
local idx = math_floor(noise * 25 + math_abs(position.x) * 0.05) % #mixed_ores + 1
|
||||
local amount = this.ore_base_quantity * chunks * 35 + math_random(100)
|
||||
@ -285,4 +303,194 @@ function Terrain.reveal_spawn_area()
|
||||
surface.force_generate_chunk_requests()
|
||||
end
|
||||
|
||||
function Terrain.block_tile_placement(event)
|
||||
local this = Public.get()
|
||||
local surface = game.get_surface(event.surface_index)
|
||||
if surface.name ~= Public.surface().name then
|
||||
return
|
||||
end
|
||||
local left = -(this.kraken_distance + this.left_boundary * 32)
|
||||
local tiles = {}
|
||||
for _, tile in pairs(event.tiles) do
|
||||
if tile.position.x <= left then
|
||||
tiles[#tiles + 1] = { name = tile.old_tile.name, position = tile.position }
|
||||
end
|
||||
end
|
||||
if #tiles > 0 then
|
||||
surface.set_tiles(tiles, true)
|
||||
end
|
||||
end
|
||||
|
||||
function Terrain.reshape_land(surface, area)
|
||||
local this = Public.get()
|
||||
local right_boundary = this.right_boundary * 32 + this.wall_width + 4
|
||||
local left_top = { x = math_max(area.left_top.x, -this.left_boundary * 32), y = area.left_top.y }
|
||||
local right_bottom = area.right_bottom
|
||||
if left_top.x >= right_bottom.x then
|
||||
return
|
||||
end
|
||||
|
||||
local seed = surface.map_gen_settings.seed
|
||||
local count_entities = surface.count_entities_filtered
|
||||
local noise_pattern = Terrain.noise_pattern
|
||||
|
||||
local function is_ore(position)
|
||||
return count_entities{
|
||||
position = { x = position.x + 0.5, y = position.y + 0.5 },
|
||||
type = 'resource',
|
||||
limit = 1,
|
||||
} > 0
|
||||
end
|
||||
|
||||
local function do_tile(x, y)
|
||||
if math_abs(y) > this.height * 16 then
|
||||
return
|
||||
end
|
||||
if math_abs(x) < 16 and math_abs(y) < 16 then
|
||||
return
|
||||
end
|
||||
if math_abs(x - this.x) < 16 and math_abs(y - this.y) < 16 then
|
||||
return
|
||||
end
|
||||
|
||||
local p = { x = x, y = y }
|
||||
local cave_rivers = noise_pattern('cave_rivers', p, seed)
|
||||
local no_rocks = noise_pattern('no_rocks', p, seed)
|
||||
local cave_ponds = noise_pattern('cave_ponds', p, 2 * seed)
|
||||
local small_caves = noise_pattern('dungeons', p, 2 * seed)
|
||||
|
||||
-- Chasms
|
||||
if cave_ponds < 0.110 and cave_ponds > 0.112 then
|
||||
if small_caves > 0.5 or small_caves < -0.5 then
|
||||
return { name = 'out-of-map', position = p }
|
||||
end
|
||||
end
|
||||
|
||||
-- Rivers
|
||||
if cave_rivers < 0.044 and cave_rivers > -0.072 then
|
||||
if cave_ponds > 0.1 then
|
||||
if not is_ore(p) then
|
||||
return { name = 'water-shallow', position = p }
|
||||
else
|
||||
return { name = 'cyan-refined-concrete', position = p }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Water Ponds
|
||||
if cave_ponds > 0.6 then
|
||||
if cave_ponds > 0.74 then
|
||||
return { name = x < right_boundary and 'acid-refined-concrete' or 'orange-refined-concrete', position = p }
|
||||
end
|
||||
if not is_ore(p) then
|
||||
return { name = x < right_boundary and 'green-refined-concrete' or 'red-refined-concrete', position = p }
|
||||
else
|
||||
return { name = 'cyan-refined-concrete', position = p }
|
||||
end
|
||||
end
|
||||
|
||||
if cave_ponds > 0.622 then
|
||||
if cave_ponds > 0.542 then
|
||||
if cave_rivers > -0.302 then
|
||||
return { name = 'refined-hazard-concrete-right', position = p }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Worm oil
|
||||
if no_rocks < 0.029 and no_rocks > -0.245 then
|
||||
if small_caves > 0.081 then
|
||||
return { name = x < right_boundary and'brown-refined-concrete' or 'black-refined-concrete', position = p }
|
||||
end
|
||||
end
|
||||
|
||||
-- Chasms2
|
||||
if small_caves < -0.54 and cave_ponds < -0.5 then
|
||||
if not is_ore(p) then
|
||||
return { name = 'out-of-map', position = p }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
for x = 0, math_min(right_bottom.x - left_top.x, 31) do
|
||||
for y = 0, 31 do
|
||||
local tile = do_tile(left_top.x + x, left_top.y + y)
|
||||
if tile then tiles[#tiles +1] = tile end
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles, true)
|
||||
end
|
||||
|
||||
function Terrain.clear_area(args)
|
||||
if not (args.position and args.surface) then
|
||||
return
|
||||
end
|
||||
local surface = args.surface
|
||||
local position = args.position
|
||||
|
||||
surface.request_to_generate_chunks({ x = position.x, y = position.x }, math_ceil((args.radius or args.size or 32) / 32))
|
||||
surface.force_generate_chunk_requests()
|
||||
|
||||
if args.name then
|
||||
local cb = game.entity_prototypes[args.name].collision_box
|
||||
local area = {
|
||||
left_top = {
|
||||
x = position.x - cb.left_top.x,
|
||||
y = position.y - cb.left_top.y,
|
||||
},
|
||||
right_bottom = {
|
||||
x = position.x + cb.right_bottom.x,
|
||||
y = position.y + cb.right_bottom.y,
|
||||
}
|
||||
}
|
||||
for _, e in pairs(surface.find_entities_filtered{ area = area, collision_mask = {'player-layer', 'object-layer'}}) do
|
||||
e.destroy()
|
||||
end
|
||||
local tiles = {}
|
||||
for _, t in pairs(surface.find_tiles_filtered{ area = area }) do
|
||||
if t.collides_with('item-layer') then
|
||||
tiles[#tiles +1] = { name = 'nuclear-ground', position = t.position }
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles, true)
|
||||
return true
|
||||
elseif args.radius then
|
||||
for _, e in pairs(surface.find_entities_filtered{ position = position, radius = args.radius, collision_mask = {'player-layer', 'object-layer'}}) do
|
||||
e.destroy()
|
||||
end
|
||||
local tiles = {}
|
||||
for _, t in pairs(surface.find_tiles_filtered{ position = position, radius = args.radius }) do
|
||||
if t.collides_with('item-layer') then
|
||||
tiles[#tiles +1] = { name = 'nuclear-ground', position = t.position }
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles, true)
|
||||
return true
|
||||
elseif args.size then
|
||||
local size = args.size
|
||||
local area = {
|
||||
left_top = {
|
||||
x = position.x - size,
|
||||
y = position.y - size,
|
||||
},
|
||||
right_bottom = {
|
||||
x = position.x + size,
|
||||
y = position.y + size,
|
||||
}
|
||||
}
|
||||
for _, e in pairs(surface.find_entities_filtered{ area = area, collision_mask = {'player-layer', 'object-layer'}}) do
|
||||
e.destroy()
|
||||
end
|
||||
local tiles = {}
|
||||
for _, t in pairs(surface.find_tiles_filtered{ area = area }) do
|
||||
if t.collides_with('item-layer') then
|
||||
tiles[#tiles +1] = { name = 'nuclear-ground', position = t.position }
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles, true)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return Terrain
|
||||
|
@ -1,5 +1,6 @@
|
||||
local Command = require 'utils.command'
|
||||
local Event = require 'utils.event'
|
||||
local DebugTerrain = require 'map_gen.maps.frontier.shared.debug_terrain'
|
||||
local math = require 'utils.math'
|
||||
local Ranks = require 'resources.ranks'
|
||||
local ScenarioInfo = require 'features.gui.info'
|
||||
@ -161,6 +162,11 @@ local function on_chunk_generated(event)
|
||||
return
|
||||
end
|
||||
|
||||
if Public.get()._DEBUG_NOISE then
|
||||
DebugTerrain.on_chunk_generated(event)
|
||||
return
|
||||
end
|
||||
|
||||
-- scale freshly generated ore by a scale factor
|
||||
Terrain.scale_resource_richness(surface, area)
|
||||
|
||||
@ -169,9 +175,18 @@ local function on_chunk_generated(event)
|
||||
|
||||
-- add extra rocks
|
||||
Terrain.rich_rocks(surface, area)
|
||||
|
||||
-- special tiles
|
||||
Terrain.reshape_land(surface, area)
|
||||
end
|
||||
Event.add(defines.events.on_chunk_generated, on_chunk_generated)
|
||||
|
||||
local function on_tile_built(event)
|
||||
Terrain.block_tile_placement(event)
|
||||
end
|
||||
Event.add(defines.events.on_player_built_tile, on_tile_built)
|
||||
Event.add(defines.events.on_robot_built_tile, on_tile_built)
|
||||
|
||||
local function on_entity_died(event)
|
||||
local entity = event.entity
|
||||
if not (entity and entity.valid) then
|
||||
@ -201,9 +216,13 @@ end
|
||||
Event.add(defines.events.on_entity_died, on_entity_died)
|
||||
|
||||
local function on_research_finished(event)
|
||||
local recipes = event.research.force.recipes
|
||||
if recipes['rocket-silo'] then
|
||||
recipes['rocket-silo'].enabled = false
|
||||
local technology = event.research
|
||||
if not (technology and technology.valid) then
|
||||
return
|
||||
end
|
||||
if technology.force.name == 'player' then
|
||||
RocketSilo.on_research_finished(technology)
|
||||
Enemy.on_research_finished(technology)
|
||||
end
|
||||
end
|
||||
Event.add(defines.events.on_research_finished, on_research_finished)
|
||||
|
@ -26,16 +26,14 @@ Public.PROD_PENALTY = 1.2 * 1.4^5
|
||||
|
||||
local this = {
|
||||
rounds = 0,
|
||||
|
||||
-- Map gen
|
||||
chart_queue = Queue.new(),
|
||||
silo_starting_x = 1700,
|
||||
|
||||
height = 36, -- in chunks, height of the ribbon world
|
||||
left_boundary = 8, -- in chunks, distance to water body
|
||||
right_boundary = 11, -- in chunks, distance to wall/biter presence
|
||||
wall_width = 5, -- in tiles
|
||||
rock_richness = 1, -- how many rocks/chunk
|
||||
|
||||
ore_base_quantity = 11, -- base ore quantity, everything is scaled up from this
|
||||
ore_chunk_scale = 32 * 20,-- sets how fast the ore will increase from spawn, lower = faster
|
||||
|
||||
@ -44,26 +42,22 @@ local this = {
|
||||
kraken_contributors = {}, -- list of players eaten by kraken
|
||||
death_contributions = {}, -- list of all players deaths
|
||||
|
||||
-- Satellites to win
|
||||
-- Rocket silo position management
|
||||
x = 0,
|
||||
y = 0,
|
||||
rocket_silo = nil,
|
||||
silo_starting_x = 1700,
|
||||
move_buffer = 0,
|
||||
rocket_step = 500, -- rocket/tiles ratio
|
||||
min_step = 500, -- minimum tiles to move
|
||||
max_distance = 100000, -- maximum x distance of rocket silo
|
||||
rockets_to_win = 1,
|
||||
rockets_launched = 0,
|
||||
rockets_per_death = 0, -- how many extra launch needed for each death
|
||||
scenario_finished = false,
|
||||
|
||||
-- Loot chests
|
||||
loot_budget = 48,
|
||||
loot_richness = 1,
|
||||
|
||||
-- Rocket silo position management
|
||||
x = 0,
|
||||
y = 0,
|
||||
rocket_silo = nil,
|
||||
move_buffer = 0,
|
||||
rocket_step = 500, -- rocket/tiles ratio
|
||||
min_step = 500, -- minimum tiles to move
|
||||
max_distance = 100000, -- maximum x distance of rocket silo
|
||||
|
||||
-- Enemy data
|
||||
spawn_enemy_wave = false,
|
||||
invincible = {},
|
||||
target_entities = {},
|
||||
unit_groups = {},
|
||||
@ -74,8 +68,11 @@ local this = {
|
||||
-- Debug
|
||||
_DEBUG_AI = false,
|
||||
_DEBUG_SHOP = false,
|
||||
_DEBUG_NOISE = false,
|
||||
|
||||
-- Markets
|
||||
loot_budget = 48,
|
||||
loot_richness = 1,
|
||||
banned_items = {
|
||||
['rocket-silo'] = true,
|
||||
['space-science-pack'] = true,
|
||||
|
@ -19,4 +19,39 @@ function Debug.log(data)
|
||||
log(serpent.block(data))
|
||||
end
|
||||
|
||||
local function noise_to_tile_map(noise)
|
||||
if noise > 80 / 100 then
|
||||
return 'red-refined-concrete'
|
||||
elseif noise > 45 / 100 then
|
||||
return 'orange-refined-concrete'
|
||||
elseif noise > 10 / 100 then
|
||||
return 'yellow-refined-concrete'
|
||||
elseif noise > -10 / 100 then
|
||||
return 'green-refined-concrete'
|
||||
elseif noise > -45 / 100 then
|
||||
return 'cyan-refined-concrete'
|
||||
elseif noise > -80 / 100 then
|
||||
return 'blue-refined-concrete'
|
||||
else
|
||||
return 'black-refined-concrete'
|
||||
end
|
||||
end
|
||||
|
||||
function Debug.show_noise_value(surface, position, noise, render_values)
|
||||
surface.set_tiles({{
|
||||
name = noise_to_tile_map(noise),
|
||||
position = position,
|
||||
}}, false, true, true, false)
|
||||
if render_values then
|
||||
rendering.draw_text{
|
||||
text = string.format('%.2f', noise),
|
||||
surface = surface,
|
||||
target = position,
|
||||
draw_on_ground = true,
|
||||
color = { 255, 255, 255 },
|
||||
only_in_alt_mode = true,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return Debug
|
||||
|
104
map_gen/maps/frontier/shared/debug_terrain.lua
Normal file
104
map_gen/maps/frontier/shared/debug_terrain.lua
Normal file
@ -0,0 +1,104 @@
|
||||
local Public = {}
|
||||
local Debug = require 'map_gen.maps.frontier.shared.debug'
|
||||
local Noise = require 'map_gen.shared.simplex_noise'
|
||||
local simplex_noise = Noise.d2
|
||||
|
||||
-- add or use noise templates from here
|
||||
local noises = {
|
||||
['dungeon_sewer'] = {{ modifier = 0.00055, weight = 1.05 }, { modifier = 0.0062, weight = 0.024 }, { modifier = 0.0275, weight = 0.00135 }},
|
||||
['cave_miner_01'] = {{ modifier = 0.002, weight = 1 }, { modifier = 0.003, weight = 0.5 }, { modifier = 0.01, weight = 0.01 }, { modifier = 0.1, weight = 0.015 }},
|
||||
['oasis'] = {{ modifier = 0.00165, weight = 1.1 }, { modifier = 0.00275, weight = 0.55 }, { modifier = 0.011, weight = 0.165 }, { modifier = 0.11, weight = 0.0187 }},
|
||||
['dungeons'] = {{ modifier = 0.0028, weight = 0.99 }, { modifier = 0.0059, weight = 0.21 }},
|
||||
['cave_rivers_2'] = {{ modifier = 0.0035, weight = 0.90 }, { modifier = 0.0088, weight = 0.15 }, { modifier = 0.051, weight = 0.011 }},
|
||||
['cave_miner_02'] = {{ modifier = 0.006, weight = 1 }, { modifier = 0.02, weight = 0.15 }, { modifier = 0.25, weight = 0.025 }},
|
||||
['large_caves'] = {{ modifier = 0.055, weight = 0.045 }, { modifier = 0.11, weight = 0.042 }, { modifier = 0.00363, weight = 1.05 }, { modifier = 0.01, weight = 0.23 }},
|
||||
['no_rocks'] = {{ modifier = 0.00495, weight = 0.945 }, { modifier = 0.01665, weight = 0.2475 }, { modifier = 0.0435, weight = 0.0435 }, { modifier = 0.07968, weight = 0.0315 }},
|
||||
['scrapyard'] = {{ modifier = 0.0055, weight = 1.1 }, { modifier = 0.011, weight = 0.385 }, { modifier = 0.055, weight = 0.253 }, { modifier = 0.11, weight = 0.121 }},
|
||||
['scrapyard_2'] = {{ modifier = 0.0066, weight = 1.1 }, { modifier = 0.044, weight = 0.165 }, { modifier = 0.242, weight = 0.055 }, { modifier = 0.055, weight = 0.352 }},
|
||||
['smol_areas'] = {{ modifier = 0.0052, weight = 0.83 }, { modifier = 0.139, weight = 0.144 }, { modifier = 0.129, weight = 0.072 }, { modifier = 0.111, weight = 0.01 }},
|
||||
['cave_rivers'] = {{ modifier = 0.0053, weight = 0.71 }, { modifier = 0.0086, weight = 0.24 }, { modifier = 0.070, weight = 0.025 }},
|
||||
['small_caves'] = {{ modifier = 0.0066, weight = 1.1 }, { modifier = 0.044, weight = 0.165 }, { modifier = 0.242, weight = 0.055 }},
|
||||
['forest_location'] = {{ modifier = 0.0066, weight = 1.1 }, { modifier = 0.011, weight = 0.275 }, { modifier = 0.055, weight = 0.165 }, { modifier = 0.11, weight = 0.0825 }},
|
||||
['small_caves_2'] = {{ modifier = 0.0099, weight = 1.1 }, { modifier = 0.055, weight = 0.275 }, { modifier = 0.275, weight = 0.055 }},
|
||||
['forest_density'] = {{ modifier = 0.01, weight = 1 }, { modifier = 0.05, weight = 0.5 }, { modifier = 0.1, weight = 0.025 }},
|
||||
['cave_ponds'] = {{ modifier = 0.014, weight = 0.77 }, { modifier = 0.18, weight = 0.085 }},
|
||||
['no_rocks_2'] = {{ modifier = 0.0184, weight = 1.265 }, { modifier = 0.143, weight = 0.1045 }},
|
||||
}
|
||||
|
||||
-- returns a float number between -1 and 1
|
||||
function Public.get_noise(name, pos, seed)
|
||||
local noise = 0
|
||||
local d = 0
|
||||
for i = 1, #noises[name] do
|
||||
local mod = noises[name]
|
||||
noise = noise + simplex_noise(pos.x * mod[i].modifier, pos.y * mod[i].modifier, seed) * mod[i].weight
|
||||
d = d + mod[i].weight
|
||||
seed = seed + seed / seed
|
||||
end
|
||||
noise = noise / d
|
||||
return noise
|
||||
end
|
||||
|
||||
function Public.on_chunk_generated(event)
|
||||
local area = event.area
|
||||
local right_boundary = 12 * 32
|
||||
local left_top = { x = math.max(area.left_top.x, right_boundary), y = area.left_top.y }
|
||||
local right_bottom = area.right_bottom
|
||||
if left_top.x >= right_bottom.x then
|
||||
return
|
||||
end
|
||||
|
||||
local x_min = left_top.x
|
||||
local surface = event.surface
|
||||
local seed = surface.map_gen_settings.seed
|
||||
local STEP = 18 * 32
|
||||
local limit = right_boundary + STEP
|
||||
local noise_id = false
|
||||
|
||||
if not noise_id and x_min < limit then noise_id = 'dungeon_sewer' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'cave_miner_01' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'oasis' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'dungeons' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'cave_rivers_2' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'cave_miner_02' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'large_caves' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'no_rocks' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'scrapyard' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'scrapyard_2' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'smol_areas' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'cave_rivers' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'small_caves' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'forest_location' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'small_caves_2' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'forest_density' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'cave_ponds' end limit = limit + STEP
|
||||
if not noise_id and x_min < limit then noise_id = 'no_rocks_2' end
|
||||
if not noise_id then return end
|
||||
|
||||
local compute_noise = Public.get_noise
|
||||
local show_noise = Debug.show_noise_value
|
||||
|
||||
for x = 0, 31 do
|
||||
for y = 0, 31 do
|
||||
local position = { x = left_top.x + x, y = left_top.y + y }
|
||||
local noise = compute_noise(noise_id, position, seed)
|
||||
show_noise(surface, position, noise)
|
||||
end
|
||||
end
|
||||
|
||||
local chunkpos = event.position
|
||||
if (chunkpos.x) % 18 == 0 and (chunkpos.y == 1) then
|
||||
rendering.draw_text{
|
||||
text = noise_id,
|
||||
surface = surface,
|
||||
target = { x = left_top.x + 16, y = left_top.y + 16},
|
||||
target_offset = {x = - 2 * 32, y = 0 },
|
||||
scale = 128,
|
||||
color = { 255, 255, 255 },
|
||||
draw_on_ground = true,
|
||||
only_in_alt_mode = true,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return Public
|
Loading…
x
Reference in New Issue
Block a user