mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-06 00:23:49 +02:00
2794ea02c6
player name is read-only, failure blocks rest of scenario functions cannot be saved in global anymore - as of Factorio 0.18.28
1437 lines
35 KiB
Lua
1437 lines
35 KiB
Lua
global.this = {}
|
|
local _global = require("utils.global")
|
|
local _evt = require("utils.event")
|
|
local _server = require("utils.server")
|
|
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")
|
|
local _claims = require("planet_prison.mod.claims")
|
|
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.
|
|
},
|
|
cliff_settings = {
|
|
richness = 0,
|
|
},
|
|
starting_area = "none",
|
|
autoplace_controls = {
|
|
["iron-ore"] = {
|
|
frequency = 0,
|
|
},
|
|
["copper-ore"] = {
|
|
frequency = 0,
|
|
},
|
|
["uranium-ore"] = {
|
|
frequency = 0,
|
|
},
|
|
["stone"] = {
|
|
frequency = 0,
|
|
},
|
|
["coal"] = {
|
|
frequency = 0,
|
|
},
|
|
["crude-oil"] = {
|
|
frequency = 1000,
|
|
size = 1,
|
|
},
|
|
["trees"] = {
|
|
frequency = 4,
|
|
},
|
|
["enemy-base"] = {
|
|
frequency = 0,
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
global.this.assign_camouflage = function(ent, common)
|
|
local shade = common.rand_range(20, 200)
|
|
ent.color = {
|
|
r = shade,
|
|
g = shade,
|
|
b = shade
|
|
}
|
|
ent.disable_flashlight()
|
|
end
|
|
|
|
local function noise_hostile_hook(ent, common)
|
|
ent.force = "enemy"
|
|
if ent.name == "character" then
|
|
global.this.assign_camouflage(ent, common)
|
|
|
|
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.7,
|
|
resolution = 0.01,
|
|
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
|
|
|
|
local function create_orbit_group()
|
|
local orbit = game.permissions.create_group("orbit")
|
|
for _, perm in pairs(global.this._config.permission_orbit) do
|
|
orbit.set_allows_action(perm, false)
|
|
end
|
|
end
|
|
|
|
global.this.bp = {
|
|
player_ship = require("planet_prison.bp.player_ship"),
|
|
merchant = require("planet_prison.bp.merchant")
|
|
}
|
|
local function init_game()
|
|
_common.init()
|
|
_layers.init()
|
|
_bp.init()
|
|
_ai.init()
|
|
_timers.init()
|
|
_claims.init(global.this._config.claim_markers,
|
|
global.this._config.claim_max_distance)
|
|
|
|
local map = pick_map()
|
|
local preset = global.this.presets[map.name]
|
|
global.this.surface = game.create_surface("arena", map)
|
|
global.this.surface.brightness_visual_weights = {
|
|
1 / 0.85,
|
|
1 / 0.85,
|
|
1 / 0.85
|
|
}
|
|
global.this.surface.ticks_per_day = 25000 * 4
|
|
global.this.perks = {}
|
|
global.this.events.merchant.spawn_tick = game.tick + 5000
|
|
global.this.events.raid_groups = {}
|
|
global.this.events.raid_init = false
|
|
global.this.events.annihilation = false
|
|
global.this.events.reset_time = nil
|
|
|
|
create_orbit_group()
|
|
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"
|
|
|
|
_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,
|
|
func = _bp.destroy_reference,
|
|
},
|
|
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 switchable_perk(caption, status)
|
|
if status then
|
|
return string.format("[color=0,80,0]%s[/color]", caption)
|
|
end
|
|
|
|
return string.format("[color=80,0,0]%s[/color]", caption)
|
|
end
|
|
|
|
local function draw_normal_gui(player)
|
|
local button
|
|
local merchant = global.this.events.merchant
|
|
if merchant.alive then
|
|
button = {
|
|
type = "button",
|
|
name = "merchant_find",
|
|
caption = "Merchant",
|
|
}
|
|
player.gui.left.add(button)
|
|
end
|
|
|
|
button = {
|
|
type = "button",
|
|
name = "flashlight_toggle",
|
|
caption = "Toggle flashlight"
|
|
}
|
|
player.gui.left.add(button)
|
|
end
|
|
|
|
local function draw_common_gui(player)
|
|
local perks = global.this.perks[player.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"
|
|
}
|
|
player.gui.left.add(button)
|
|
|
|
button = {
|
|
type = "button",
|
|
name = "chat_toggle",
|
|
caption = chat_type,
|
|
}
|
|
player.gui.left.add(button)
|
|
end
|
|
|
|
local function draw_orbit_gui(player)
|
|
local button = {
|
|
type = "button",
|
|
name = "annihilate",
|
|
caption = "Annihilate"
|
|
}
|
|
player.gui.left.add(button)
|
|
end
|
|
|
|
local function redraw_gui(player)
|
|
player.gui.left.clear()
|
|
draw_common_gui(player)
|
|
if player.spectator == true then
|
|
draw_orbit_gui(player)
|
|
else
|
|
draw_normal_gui(player)
|
|
end
|
|
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_tick_reset()
|
|
if global.this.events.reset_time == nil then
|
|
return
|
|
end
|
|
|
|
if global.this.events.reset_time > game.tick then
|
|
return
|
|
end
|
|
|
|
_server.start_scenario('planet_prison')
|
|
global.this.events.reset_time = nil
|
|
end
|
|
|
|
local function annihilate(caller)
|
|
global.this.events.annihilation = true
|
|
for _, player in pairs(game.connected_players) do
|
|
if player.name == caller.name then
|
|
goto continue
|
|
end
|
|
|
|
local coeff
|
|
for i = 1, 5 do
|
|
if i % 2 == 0 then
|
|
coeff = -1
|
|
else
|
|
coeff = 1
|
|
end
|
|
|
|
local query = {
|
|
name = "atomic-rocket",
|
|
position = {
|
|
player.position.x - 100,
|
|
player.position.y - 100,
|
|
},
|
|
target = {
|
|
player.position.x + (8 * i * coeff),
|
|
player.position.y + (8 * i * coeff),
|
|
},
|
|
speed = 0.1,
|
|
}
|
|
|
|
player.surface.create_entity(query)
|
|
player.print(">> Annihilation in progress...")
|
|
end
|
|
::continue::
|
|
end
|
|
|
|
global.this.events.reset_time = game.tick + (60 * 15)
|
|
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
|
|
elseif elem.name == "annihilate" then
|
|
if global.this.events.annihilation == true then
|
|
return
|
|
end
|
|
|
|
elem.destroy()
|
|
annihilate(p)
|
|
end
|
|
end
|
|
|
|
local function get_random_name()
|
|
while true do
|
|
local id = _common.rand_range(100, 999)
|
|
local name = string.format("#%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() --player name is read only
|
|
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 _get_outer_points(surf, x, y, deps)
|
|
local inner = deps.inner
|
|
local points = deps.points
|
|
|
|
local point = {
|
|
x = x,
|
|
y = y,
|
|
}
|
|
|
|
if _common.point_in_bounding_box(point, inner) then
|
|
return
|
|
end
|
|
|
|
local tile = surf.get_tile(point)
|
|
if string.find(tile.name, "water") ~= nil
|
|
or string.find(tile.name, "out") ~= nil then
|
|
return
|
|
end
|
|
|
|
table.insert(points, point)
|
|
end
|
|
|
|
local function _calculate_attack_costs(surf, bb)
|
|
local query = {
|
|
area = bb,
|
|
force = {
|
|
"enemy",
|
|
"neutral",
|
|
"player",
|
|
},
|
|
invert = true,
|
|
}
|
|
local objects = surf.find_entities_filtered(query)
|
|
if next(objects) == nil then
|
|
log("B")
|
|
return 0
|
|
end
|
|
|
|
local cost = 0
|
|
local costs = global.this._config.base_costs
|
|
for _, obj in pairs(objects) do
|
|
for name, coeff in pairs(costs) do
|
|
if obj.name == name then
|
|
cost = cost + coeff
|
|
end
|
|
end
|
|
end
|
|
|
|
return cost
|
|
end
|
|
|
|
local function _get_raid_info(surf, bb)
|
|
local pick = nil
|
|
local cost = _calculate_attack_costs(surf, bb)
|
|
for _, entry in pairs(global.this._config.raid_costs) do
|
|
if entry.cost <= cost then
|
|
pick = entry
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
return pick
|
|
end
|
|
|
|
local function _create_npc_group(claim, surf)
|
|
local inner = _common.create_bounding_box_by_points(claim)
|
|
local info = _get_raid_info(surf, inner)
|
|
if info == nil then
|
|
return {}
|
|
end
|
|
|
|
local outer = _common.deepcopy(inner)
|
|
_common.enlarge_bounding_box(outer, 10)
|
|
|
|
local points = {}
|
|
local deps = {
|
|
points = points,
|
|
inner = inner,
|
|
}
|
|
_common.for_bounding_box_extra(surf, outer, _get_outer_points, deps)
|
|
|
|
local agents = {}
|
|
for i, point in ipairs(points) do
|
|
if _common.rand_range(1, info.chance) ~= 1 then
|
|
goto continue
|
|
end
|
|
|
|
local query = {
|
|
name = "character",
|
|
position = point
|
|
}
|
|
|
|
local agent = surf.create_entity(query)
|
|
local stash = {}
|
|
for attr, value in pairs(info.gear[(i % #info.gear) + 1]) do
|
|
local prop = {
|
|
name = value
|
|
}
|
|
|
|
if attr == "ammo" then
|
|
prop.count = 20
|
|
elseif attr == "weap" then
|
|
prop.count = 1
|
|
elseif attr == "armor" then
|
|
prop.count = 1
|
|
end
|
|
|
|
table.insert(stash, prop)
|
|
end
|
|
|
|
for _, stack in pairs(stash) do
|
|
agent.insert(stack)
|
|
end
|
|
|
|
global.this.assign_camouflage(agent, _common)
|
|
|
|
table.insert(agents, agent)
|
|
::continue::
|
|
end
|
|
|
|
return agents
|
|
end
|
|
|
|
local function populate_raid_event(surf)
|
|
local claims, group
|
|
local status = false
|
|
local groups = global.this.events.raid_groups
|
|
|
|
for _, p in pairs(game.connected_players) do
|
|
groups[p.name] = {}
|
|
claims = _claims.get_claims(p.name)
|
|
for _, claim in pairs(claims) do
|
|
if #claim == 0 then
|
|
goto continue
|
|
end
|
|
|
|
status = true
|
|
group = {
|
|
agents = _create_npc_group(claim, surf),
|
|
objects = claim
|
|
}
|
|
table.insert(groups[p.name], group)
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
return status
|
|
end
|
|
|
|
local function raid_event(surf)
|
|
local raid_groups = global.this.events.raid_groups
|
|
if global.this.events.raid_init then
|
|
if surf.daytime > 0.01 and surf.daytime <= 0.1 then
|
|
for name, groups in pairs(raid_groups) do
|
|
for i = #groups, 1, -1 do
|
|
local group = groups[i]
|
|
local agents = group.agents
|
|
for j = #agents, 1, -1 do
|
|
local agent = agents[j]
|
|
if agent.valid then
|
|
agent.destroy()
|
|
end
|
|
|
|
table.remove(agents, j)
|
|
end
|
|
|
|
if #agents == 0 then
|
|
table.remove(group, i)
|
|
end
|
|
end
|
|
|
|
if #groups == 0 then
|
|
raid_groups[name] = nil
|
|
end
|
|
end
|
|
|
|
global.this.events.raid_init = false
|
|
end
|
|
else
|
|
if surf.daytime < 0.4 or surf.daytime > 0.6 then
|
|
return
|
|
end
|
|
|
|
if populate_raid_event(surf) then
|
|
global.this.events.raid_init = true
|
|
end
|
|
end
|
|
|
|
if game.tick % 4 ~= 0 then
|
|
return
|
|
end
|
|
|
|
for name, groups in pairs(raid_groups) do
|
|
local exists = false
|
|
for _, p in pairs(game.connected_players) do
|
|
if p.name == name then
|
|
exists = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if not exists then
|
|
raid_groups[name] = nil
|
|
goto continue
|
|
end
|
|
|
|
for _, group in pairs(groups) do
|
|
_ai.do_job(surf, _ai.command.attack_objects, group)
|
|
end
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
local function cause_event(s)
|
|
merchant_event(s)
|
|
raid_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 mined_wreckage(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_mined_entity(e)
|
|
local ent = e.entity
|
|
if not ent.valid then
|
|
return
|
|
end
|
|
|
|
mined_wreckage(e)
|
|
_claims.on_player_mined_entity(ent)
|
|
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]
|
|
_claims.on_player_died(p)
|
|
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
|
|
|
|
if ent.name == "character" then
|
|
local hp = 1.0 - ent.get_health_ratio()
|
|
local particles = 45 * hp
|
|
local coeff = _common.rand_range(-20, 20) / 100.0
|
|
for i = 1, particles do
|
|
local blood = {
|
|
name = "blood-particle",
|
|
position = {
|
|
x = ent.position.x,
|
|
y = ent.position.y,
|
|
},
|
|
movement = {
|
|
(_common.rand_range(-20, 20) / 100.0) + coeff,
|
|
(_common.rand_range(-20, 20) / 100.0) + coeff,
|
|
},
|
|
frame_speed = 0.01,
|
|
vertical_speed = 0.02,
|
|
height = 0.01,
|
|
}
|
|
ent.surface.create_particle(blood)
|
|
end
|
|
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 character_death(e)
|
|
local ent = e.entity
|
|
if ent.name ~= "character" then
|
|
return false
|
|
end
|
|
|
|
local explosion = {
|
|
name = "blood-explosion-big",
|
|
position = ent.position,
|
|
}
|
|
ent.surface.create_entity(explosion)
|
|
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)
|
|
character_death(e)
|
|
_claims.on_entity_died(e.entity)
|
|
end
|
|
|
|
|
|
local function merchant_exploit_check(ent)
|
|
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 = 18
|
|
}
|
|
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)
|
|
local ent = e.created_entity
|
|
if not ent or not ent.valid then
|
|
return
|
|
end
|
|
|
|
_claims.on_built_entity(ent)
|
|
merchant_exploit_check(ent)
|
|
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 move_to_orbit(player)
|
|
local char = player.character
|
|
player.character = nil
|
|
char.destroy()
|
|
|
|
game.merge_forces(player.name, "neutral")
|
|
player.spectator = true
|
|
redraw_gui(player)
|
|
|
|
local orbit_perms = game.permissions.get_group("orbit")
|
|
orbit_perms.add_player(player)
|
|
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 player = game.players[pid]
|
|
surf.print(string.format(">> The %s was able to escape", player.name))
|
|
move_to_orbit(player)
|
|
end
|
|
end
|
|
|
|
_evt.on_init(init_game)
|
|
_evt.add(defines.events.on_built_entity, on_built_entity)
|
|
_evt.add(defines.events.on_robot_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_tick, on_tick_reset)
|
|
_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)
|