1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-01-22 03:38:48 +02:00
ComfyFactorio/maps/planet_prison.lua
cogito 198b5fd17a planet_prison: add timer to player spawn
These generators from ships are way to op as you can easily place
radars around it. We'll explode the ships after 10 minutes, so they
don't clutter the map and don't give exploits.
2020-01-18 08:58:28 +01:00

1032 lines
25 KiB
Lua

global.this = {}
local _global = require("utils.global")
local _evt = require("utils.event")
local _map = require("tools.map_functions")
local _timers = require("planet_prison.mod.timers")
local _common = require("planet_prison.mod.common")
local _layers = require("planet_prison.mod.layers")
local _ai = require("planet_prison.mod.ai")
local _bp = require("planet_prison.mod.bp")
local _afk = require("planet_prison.mod.afk")
global.this._config = require("planet_prison.config")
global.this.maps = {
{
name = "flooded-metropolia",
height = 2000,
width = 2000,
water = 1,
terrain_segmentation = 8,
property_expression_names = {
moisture = 0,
temperature = 30.
},
starting_area = "none",
autoplace_controls = {
["iron-ore"] = {
frequency = 0,
},
["copper-ore"] = {
frequency = 0,
},
["stone"] = {
frequency = 0,
},
["coal"] = {
frequency = 0,
},
["crude-oil"] = {
frequency = 1000,
size = 1,
},
["trees"] = {
frequency = 4,
},
["enemy-base"] = {
frequency = 0,
}
},
}
}
local function noise_hostile_hook(ent, common)
ent.force = "enemy"
if ent.name == "character" then
local shade = common.rand_range(20, 200)
ent.color = {
r = shade,
g = shade,
b = shade
}
if common.rand_range(1, 5) == 1 then
ent.insert({name="shotgun", count=1})
ent.insert({name="shotgun-shell", count=20})
else
ent.insert({name="pistol", count=1})
ent.insert({name="firearm-magazine", count=20})
end
else
ent.insert({name="firearm-magazine", count=200})
end
end
local function noise_set_neutral_hook(ent)
ent.force = "neutral"
end
local industrial_zone_layers = {
{
type = "LuaTile",
name = "concrete",
objects = {
"concrete",
},
elevation = 0.3,
resolution = 0.2,
hook = nil,
deps = nil,
},
{
type = "LuaTile",
name = "stones",
objects = {
"stone-path",
},
elevation = 0.2,
resolution = 0.4,
hook = nil,
deps = nil,
},
{
type = "LuaTile",
name = "shallows",
objects = {
"water-shallow",
},
elevation = 0.6,
resolution = 0.005,
hook = nil,
deps = nil,
},
{
type = "LuaEntity",
name = "scrap",
objects = {
"mineable-wreckage",
},
elevation = 0.5,
resolution = 0.1,
hook = nil,
deps = nil,
},
{
type = "LuaEntity",
name = "walls",
objects = {
"stone-wall"
},
elevation = 0.5,
resolution = 0.09,
hook = noise_set_neutral_hook,
deps = nil,
},
{
type = "LuaEntity",
name = "hostile",
objects = {
"character",
"gun-turret",
},
elevation = 0.92,
resolution = 0.99,
hook = noise_hostile_hook,
deps = _common,
},
{
type = "LuaEntity",
name = "structures",
objects = {
"big-electric-pole",
"medium-electric-pole",
},
elevation = 0.9,
resolution = 0.9,
hook = noise_set_neutral_hook,
deps = nil,
},
}
global.this.presets = {
["flooded-metropolia"] = industrial_zone_layers,
}
global.this.entities_cache = nil
global.this.surface = nil
global.this.last_friend = nil
local function pick_map()
return global.this.maps[_common.rand_range(1, #global.this.maps)]
end
local function find_force(name)
for _, f in pairs(game.forces) do
if f.name == name then
return f
end
end
return nil
end
local function init_player_ship_bp(entity, player)
entity.force = player.force
if entity.name == "crash-site-chest-1" then
for _, stack in pairs(global.this._config.player_ship_loot) do
entity.insert(stack)
end
end
if entity.name == "crash-site-generator" then
entity.electric_buffer_size = 2000
entity.power_production = 2000
end
end
global.this.events = {
merchant = {
alive = false,
moving = false,
spawn_tick = 0,
embark_tick = 0,
position = { x = 0, y = 0 },
offer = global.this._config.merchant_offer,
}
}
local function init_merchant_bp(entity, _)
entity.force = "merchant"
entity.rotatable = false
entity.minable = false
if entity.name ~= "market" then
entity.operable = false
else
for _, entry in pairs(global.this.events.merchant.offer) do
entity.add_market_item(entry)
end
end
end
global.this.bp = {
player_ship = require("planet_prison.bp.player_ship"),
merchant = require("planet_prison.bp.merchant")
}
local function init_game()
local map = pick_map()
local preset = global.this.presets[map.name]
global.this.surface = game.create_surface("arena", map)
global.this.surface.min_brightness = 0
global.this.surface.ticks_per_day = 25000 * 4
global.this.perks = {}
global.this.events.merchant.spawn_tick = game.tick + 5000
game.map_settings.pollution.enabled = false
game.map_settings.enemy_evolution.enabled = false
game.difficulty_settings.technology_price_multiplier = 0.1
game.difficulty_settings.research_queue_setting = "always"
_timers.init()
_layers.init()
_layers.set_collision_mask({"water-tile"})
for _, layer in pairs(preset) do
_layers.add_noise_layer(layer.type, layer.name,
layer.objects, layer.elevation,
layer.resolution)
if layer.hook ~= nil then
_layers.add_noise_layer_hook(layer.name, layer.hook)
end
if layer.deps ~= nil then
_layers.add_noise_layer_dependency(layer.name, layer.deps)
end
end
_bp.push_blueprint("player_ship", global.this.bp.player_ship)
_bp.set_blueprint_hook("player_ship", init_player_ship_bp)
_bp.push_blueprint("merchant", global.this.bp.merchant)
_bp.set_blueprint_hook("merchant", init_merchant_bp)
end
local function explode_ship(deps)
local bp = deps.modules.bp
local layers = deps.modules.layers
for _, ent in pairs(bp.reference_get_entities(deps.ship)) do
if not ent.valid then
goto continue
end
local explosion = {
name = "massive-explosion",
position = ent.position
}
deps.surf.create_entity(explosion)
::continue::
end
local bb = bp.reference_get_bounding_box(deps.ship)
layers.remove_excluding_bounding_box(bb)
bp.destroy_reference(deps.surf, deps.ship)
rendering.destroy(deps.id)
end
local function explode_ship_update(left, deps)
local common = deps.modules.common
for _, ent in pairs(deps.ship.entities) do
if not ent.valid then
return false
end
end
rendering.set_text(deps.id, common.get_time(left))
return true
end
local function do_spawn_point(player)
local point = {
x = _common.get_axis(player.position, "x"),
y = _common.get_axis(player.position, "y") - 2
}
local instance = _bp.build(player.surface, "player_ship", point, player)
_layers.push_excluding_bounding_box(instance.bb)
local left = global.this._config.self_explode
local object = {
text = _common.get_time(left),
surface = player.surface,
color = {
r = 255,
g = 20,
b = 20
},
target = {
x = point.x - 2,
y = point.y - 3,
},
scale = 2.0
}
local entry = {
id = rendering.draw_text(object),
ship = instance,
modules = {
bp = _bp,
layers = _layers,
common = _common,
timers = _timers,
},
surf = player.surface,
}
local timer = _timers.set_timer(left, explode_ship)
_timers.set_timer_on_update(timer, explode_ship_update)
_timers.set_timer_dependency(timer, entry)
_timers.set_timer_start(timer)
end
local function get_non_obstructed_position(s, radius)
local chunk
for i = 1, 32 do
chunk = s.get_random_chunk()
chunk.x = chunk.x * 32
chunk.y = chunk.y * 32
local search_info = {
position = chunk,
radius = radius,
}
local tiles = s.find_tiles_filtered(search_info)
for _, tile in pairs(tiles) do
if string.find(tile.name, "water") ~= nil
or string.find(tile.name, "out") ~= nil then
goto continue
end
end
search_info = {
position = chunk,
radius = radius,
force = {"neutral", "enemy"},
invert = true
}
local ents = s.find_entities_filtered(search_info)
if not ents or #ents == 0 then
break
end
::continue::
end
return chunk
end
local function redraw_gui(p)
p.gui.left.clear()
local merchant = global.this.events.merchant
local perks = global.this.perks[p.name]
local chat_type = "Global chat"
if not perks.chat_global then
chat_type = "NAP chat"
end
local button = {
type = "button",
name = "manual_toggle",
caption = "Manual"
}
p.gui.left.add(button)
button = {
type = "button",
name = "chat_toggle",
caption = chat_type,
}
p.gui.left.add(button)
if merchant.alive then
button = {
type = "button",
name = "merchant_find",
caption = "Merchant",
}
p.gui.left.add(button)
end
button = {
type = "button",
name = "flashlight_toggle",
caption = "Toggle flashlight"
}
p.gui.left.add(button)
end
local function print_merchant_position(player)
local position = global.this.events.merchant.position
local perks = global.this.perks[player.name]
if not perks.minimap then
player.print(string.format(">> You were able to spot him %s from your location",
_common.get_readable_direction(player.position, position)))
else
player.print(string.format(">> You received a broadcast with [gps=%d,%d] coordinates", position.x, position.y))
end
end
local function on_gui_click(e)
local elem = e.element
local p = game.players[e.player_index]
local perks = global.this.perks[p.name]
if not elem.valid then
return
end
if elem.name == "chat_toggle" then
if perks.chat_global then
elem.caption = "NAP chat"
perks.chat_global = false
else
elem.caption = "Global chat"
perks.chat_global = true
end
elseif elem.name == "flashlight_toggle" then
if perks.flashlight_enable then
perks.flashlight_enable = false
p.character.disable_flashlight()
else
perks.flashlight_enable = true
p.character.enable_flashlight()
end
elseif elem.name == "merchant_find" then
print_merchant_position(p)
elseif elem.name == "manual_toggle" then
local children = p.gui.center.children
if #children >= 1 then
p.gui.center.clear()
return
end
local text_box = {
type = "text-box",
text = global.this._config.manual
}
text_box = p.gui.center.add(text_box)
text_box.style.minimal_width = 512
text_box.read_only = true
text_box.word_wrap = true
end
end
local function get_random_name()
while true do
local id = _common.rand_range(1000, 9999)
local name = string.format("inmate_%d", id)
if global.this.perks[name] == nil then
return name
end
end
end
local function init_player(p)
p.teleport({0, 0}, "arena")
local s = p.surface
local position = get_non_obstructed_position(s, 10)
global.this.perks[p.name] = nil
p.teleport(position, "arena")
p.name = get_random_name()
p.force = game.create_force(p.name)
p.force.set_friend("neutral", true)
global.this.perks[p.name] = {
flashlight_enable = true,
minimap = false,
chat_global = true,
}
local merch = find_force("merchant")
if merch then
p.force.set_friend(merch, true)
merch.set_friend(p.force, true)
end
p.force.research_queue_enabled = true
for _, tech in pairs(p.force.technologies) do
for name, status in pairs(global.this._config.technologies) do
if tech.name == name then
tech.researched = status
tech.enabled = status
end
end
end
p.minimap_enabled = false
redraw_gui(p)
do_spawn_point(p)
end
local function on_player_joined_game(e)
local p = game.players[e.player_index]
init_player(p)
end
local function _build_merchant_bp(surf, position)
local instance = _bp.build(surf, "merchant", position, nil)
_layers.push_excluding_bounding_box(instance.bb)
end
local function _remove_merchant_bp(surf)
local refs = _bp.get_references("merchant")
local bb = _bp.reference_get_bounding_box(refs[1])
_layers.remove_excluding_bounding_box(bb)
_bp.destroy_references(surf, "merchant")
global.this.events.merchant.position = {
x = 0,
y = 0
}
end
local function spawn_merchant(s)
local merchant = global.this.events.merchant
local position = get_non_obstructed_position(s, 10)
local merch
if not merchant.moving then
merch = game.create_force("merchant")
else
merch = find_force("merchant")
end
merchant.position = position
merchant.alive = true
merchant.moving = false
merchant.embark_tick = game.tick + 90000
_build_merchant_bp(s, position)
s.print(">> Merchant appeared in the area")
for _, p in pairs(game.players) do
p.force.set_friend(merch, true)
merch.set_friend(p.force, true)
print_merchant_position(p)
redraw_gui(p)
end
end
local function embark_merchant(s)
global.this.events.merchant.alive = false
global.this.events.merchant.moving = true
global.this.events.merchant.spawn_tick = game.tick + 10000
s.print(">> Merchant is moving to new location")
_remove_merchant_bp(s)
for _, player in pairs(game.players) do
redraw_gui(player)
end
end
local function merchant_event(s)
local e = global.this.events
local m = e.merchant
if not m.alive and m.spawn_tick <= game.tick then
spawn_merchant(s)
end
if m.alive and not m.moving and m.embark_tick <= game.tick then
embark_merchant(s)
end
end
local function cause_event(s)
merchant_event(s)
end
local function kill_player(p)
p.character.die()
end
local function on_tick()
local s = global.this.surface
if not s then
log("on_tick: surface empty!")
return
end
local surf = global.this.surface
if game.tick % 4 == 0 then
_ai.do_job(surf, _ai.command.seek_and_destroy_player)
end
_layers.do_job(surf)
cause_event(s)
if (game.tick + 1) % 100 == 0 then
_afk.on_inactive_players(90, kill_player)
end
if (game.tick + 1) % 60 == 0 then
_timers.do_job()
end
end
local function make_ore_patch(e)
if _common.rand_range(1, 30) ~= 1 then
return
end
local surf = e.surface
local point = e.area.left_top
_map.draw_entity_circle(point, "stone", surf, 6, true, 1000000)
_map.draw_entity_circle(point, "coal", surf, 12, true, 1000000)
_map.draw_entity_circle(point, "copper-ore", surf, 18, true, 1000000)
_map.draw_entity_circle(point, "iron-ore", surf, 24, true, 1000000)
_map.draw_noise_tile_circle(point, "water", surf, 4)
end
local function on_chunk_generated(e)
if e.surface.name ~= "arena" then
return
end
make_ore_patch(e)
_layers.push_chunk(e.position)
end
local function on_player_mined_entity(e)
if e.entity.name ~= "mineable-wreckage" then
return
end
local candidates = {}
local chance = _common.rand_range(0, 1000)
for name, attrs in pairs(global.this._config.wreck_loot) do
local prob = attrs.rare * 100
if prob < chance then
local cand = {
name = name,
count = _common.rand_range(attrs.count[1], attrs.count[2]),
}
table.insert(candidates, cand)
end
end
local count = #candidates
if count == 0 then
return
end
local cand = candidates[_common.rand_range(1, count)]
e.buffer.insert(cand)
end
local function on_player_died(e)
local index = e.player_index
if not index then
return -- banned/kicked somewhere else
end
local p = game.players[index]
game.merge_forces(p.name, "neutral")
end
local function on_player_respawned(e)
local p = game.players[e.player_index]
init_player(p)
end
local function on_player_dropped_item(e)
if not global.this.last_friend then
global.this.last_friend = {}
end
local p = game.players[e.player_index]
local ent = e.entity
if ent.stack.name == "raw-fish" then
local ent_list = p.surface.find_entities_filtered({
name = "character",
position = ent.position,
radius = 2,
})
if not ent_list then
return
end
local peer = nil
for _, char in pairs(ent_list) do
if char.player and char.player.name ~= p.name then
peer = char.player
break
end
end
if peer == nil then
return
end
if p.force.get_cease_fire(peer.name) then
p.print(string.format("You're in the NAP with %s already", peer.name))
return
end
if global.this.last_friend[peer.name] == p.name then
p.force.set_cease_fire(peer.name, true)
peer.force.set_cease_fire(p.name, true)
p.print(string.format("The NAP was formed with %s", peer.name))
peer.print(string.format("The NAP was formed with %s", p.name))
global.this.last_friend[p.name] = ""
global.this.last_friend[peer.name] = ""
return
end
global.this.last_friend[p.name] = peer.name
p.print(string.format("You want to form the NAP with %s", peer.name))
peer.print(string.format("The %s wants to form NAP with you", p.name))
elseif ent.stack.name == "coal" then
local ent_list = p.surface.find_entities_filtered({
name = "character",
position = ent.position,
radius = 2,
})
if not ent_list then
return
end
local peer = nil
for _, char in pairs(ent_list) do
if char.player and char.player.name ~= p.name then
peer = char.player
break
end
end
if peer == nil then
return
end
if not p.force.get_cease_fire(peer.name) then
p.print(string.format("You don't have the NAP with %s", p.name))
return
end
p.force.set_cease_fire(peer.name, false)
peer.force.set_cease_fire(p.name, false)
global.this.last_friend[p.name] = ""
global.this.last_friend[peer.name] = ""
p.print(string.format("You're no longer in the NAP with %s", peer.name))
peer.print(string.format("You're no longer in the NAP with %s", p.name))
end
end
local function on_chunk_charted(e)
local f_perks = global.this.perks[e.force.name]
if not f_perks then
return
end
if not f_perks.minimap then
e.force.clear_chart()
end
end
local function on_entity_damaged(e)
local ent = e.entity
if ent.force.name == "merchant" then
if not ent.force.get_friend(e.force) then
return
end
ent.force.set_friend(e.force, false)
e.force.set_friend(ent.force, false)
end
end
local function merchant_death(e)
local ent = e.entity
if ent.force.name ~= "merchant" then
return false
end
if ent.name ~= "character" and ent.name ~= "market" then
return false
end
local s = ent.surface
local explosion = {
name = "massive-explosion",
position = ent.position
}
s.create_entity(explosion)
_remove_merchant_bp(s)
global.this.events.merchant.alive = false
global.this.events.merchant.moving = false
global.this.events.merchant.spawn_tick = game.tick + 1000
game.merge_forces("merchant", "neutral")
s.print(">> Merchant died")
for _, player in pairs(game.players) do
redraw_gui(player)
end
return true
end
local function hostile_death(e)
local ent = e.entity
local loot = e.loot
if ent.name ~= "character" then
return false
end
if ent.player then
loot.insert({name = "coin", count = 70})
else
loot.insert({name = "coin", count = 10})
end
return true
end
local function on_entity_died(e)
if not e.entity.valid then
return
end
if merchant_death(e) then
return
end
hostile_death(e)
end
local function merchant_exploit_check(e)
local ent = e.created_entity
if ent.type ~= "electric-pole" then
return
end
local refs = _bp.get_references("merchant")
if not refs or #refs <= 0 then
return
end
local bp_ent = _bp.reference_get_entities(refs[1])[1]
local surf = bp_ent.surface
local query = {
type = "electric-pole",
position = bp_ent.position,
radius = 15
}
local ents = surf.find_entities_filtered(query)
for _, s_ent in pairs(ents) do
if s_ent.valid and s_ent.force.name ~= "merchant" then
s_ent.die()
end
end
end
local function on_built_entity(e)
merchant_exploit_check(e)
end
local function on_market_item_purchased(e)
local p = game.players[e.player_index]
local m = e.market
local o = m.get_market_items()[e.offer_index].offer
local perks = global.this.perks[p.name]
if o.effect_description == "Construct a GPS receiver" then
perks.minimap = true
p.minimap_enabled = true
end
end
local function stringify_color(color)
local r, g, b = color.r, color.g, color.b
if r <= 1 then
r = math.floor(r * 255)
end
if g <= 1 then
g = math.floor(g * 255)
end
if b <= 1 then
b = math.floor(b * 255)
end
return string.format("%d,%d,%d", r, g, b)
end
local function create_console_message(p, message)
local prefix_fmt = "[color=%s]%s:[/color]"
local msg_fmt = "[color=%s]%s[/color]"
local color = stringify_color(p.chat_color)
local prefix = string.format(prefix_fmt, color, p.name)
local p_msg = string.format(msg_fmt, color, message)
if global.this.perks[p.name].chat_global then
msg_fmt = "[color=red]global:[/color] %s %s"
else
msg_fmt = "[color=green]nap:[/color] %s %s"
end
return string.format(msg_fmt, prefix, p_msg)
end
local function filter_out_gps(message)
local msg = string.gsub(message, '%[gps=%-?%d+%,?%s*%-?%d+%]', '[gps]')
return msg
end
local function on_console_chat(e)
local pid = e.player_index
if not pid then
return
end
local p = game.players[pid]
local msg = create_console_message(p, e.message)
if global.this.perks[p.name].chat_global then
for _, peer in pairs(game.players) do
if peer.name ~= p.name then
local perks = global.this.perks[peer.name]
if perks.minimap then
peer.print(msg)
else
peer.print(filter_out_gps(msg))
end
end
end
else
for _, f in pairs(game.forces) do
if p.force.get_cease_fire(f) then
local peer = f.players[1]
if peer.name ~= p.name then
local perks = global.this.perks[peer.name]
if perks.minimap then
peer.print(msg)
else
peer.print(filter_out_gps(msg))
end
end
end
end
end
end
local function on_research_finished(e)
local r = e.research
if not r.valid then
return
end
local reward = {
name = "coin",
count = math.ceil(r.research_unit_count * 3)
}
local f = r.force
for _, player in pairs(f.players) do
if player.can_insert(reward) then
player.insert(reward)
end
end
end
local function on_rocket_launched(e)
local surf = global.this.surface
local pid = e.player_index
surf.print(">> The rocket was launched")
if pid == nil then
surf.print(">> Nobody escaped by it")
else
local p = game.players[pid]
surf.print(string.format(">> The %s was able to escape", p.name))
on_player_died({player_index = pid})
p.character.die()
end
end
_evt.on_init(init_game)
_evt.add(defines.events.on_built_entity, on_built_entity)
_evt.add(defines.events.on_research_finished, on_research_finished)
_evt.add(defines.events.on_player_joined_game, on_player_joined_game)
_evt.add(defines.events.on_chunk_generated, on_chunk_generated)
_evt.add(defines.events.on_player_mined_entity, on_player_mined_entity)
_evt.add(defines.events.on_player_died, on_player_died)
_evt.add(defines.events.on_player_kicked, on_player_died)
_evt.add(defines.events.on_player_banned, on_player_died)
_evt.add(defines.events.on_player_respawned, on_player_respawned)
_evt.add(defines.events.on_player_dropped_item, on_player_dropped_item)
_evt.add(defines.events.on_pre_player_left_game, on_player_died)
_evt.add(defines.events.on_entity_damaged, on_entity_damaged)
_evt.add(defines.events.on_entity_died, on_entity_died)
_evt.add(defines.events.on_market_item_purchased, on_market_item_purchased)
_evt.add(defines.events.on_chunk_charted, on_chunk_charted)
_evt.add(defines.events.on_console_chat, on_console_chat)
_evt.add(defines.events.on_gui_click, on_gui_click)
_evt.add(defines.events.on_tick, on_tick)
_evt.add(defines.events.on_rocket_launched, on_rocket_launched)
_global.register_init({},
function(tbl)
tbl.this = global.this
end,
function(tbl)
global.this = tbl.this
end)