1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2024-12-30 23:17:53 +02:00
ComfyFactorio/maps/planet_prison.lua
2022-07-19 00:25:42 +02:00

1902 lines
49 KiB
Lua

-- map by Cogito
-- heavily modified by Gerkiz
local Global = require 'utils.global'
local Session = require 'utils.datastore.session_data'
local Event = require 'utils.event'
local Freeplay = require 'utils.freeplay'
local Server = require 'utils.server'
local MapFuntions = require 'tools.map_functions'
local CommonFunctions = require 'utils.common'
local LayersFunctions = require 'maps.planet_prison.mod.layers'
local AIFunctions = require 'maps.planet_prison.ai'
local Blueprints = require 'maps.planet_prison.mod.bp'
local AfkFunctions = require 'maps.planet_prison.mod.afk'
local Timers = require 'utils.timers'
local ClaimsFunctions = require 'maps.planet_prison.mod.claims'
local MapConfig = require 'maps.planet_prison.config'
local Token = require 'utils.token'
local Color = require 'utils.color_presets'
-- require 'modules.thirst'
local this = {
entities_cache = nil,
active_surface = nil,
last_friend = nil,
remove_offline_players = {
players = {},
time = 216000, -- 1h
enabled = true
}
}
local floor = math.floor
local ceil = math.ceil
local Public = {}
local insert = table.insert
local remove = table.remove
local random = math.random
Global.register(
this,
function(tbl)
this = tbl
end
)
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
}
}
},
{
name = 'swampy-rivers',
height = 2500,
width = 2500,
water = 1,
terrain_segmentation = 6,
property_expression_names = {
moisture = 0,
temperature = 25.
},
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 = 900,
size = 1
},
['trees'] = {
frequency = 4
},
['enemy-base'] = {
frequency = 0
}
}
}
}
local function assign_perks(player)
this.perks[player.name] = {
flashlight_enable = true,
minimap = false,
chat_global = true
}
return this.perks[player.name]
end
local 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 set_noise_hostile_hook =
Token.register(
function(data)
local ent = data
if not ent or not ent.valid then
return
end
ent.force = 'enemy'
if ent.name == 'character' then
assign_camouflage(ent, CommonFunctions)
if CommonFunctions.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 set_neutral_to_entity =
Token.register(
function(entity)
entity.force = 'neutral'
end
)
local fetch_common =
Token.register(
function()
return CommonFunctions
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 = {
'crash-site-spaceship-wreck-small-1',
'crash-site-spaceship-wreck-small-2',
'crash-site-spaceship-wreck-small-3',
'crash-site-spaceship-wreck-small-4',
'crash-site-spaceship-wreck-small-5',
'crash-site-spaceship-wreck-small-6'
},
elevation = 0.5,
resolution = 0.1,
hook = set_neutral_to_entity,
deps = nil
},
{
type = 'LuaEntity',
name = 'walls',
objects = {
'stone-wall'
},
elevation = 0.5,
resolution = 0.09,
hook = set_neutral_to_entity,
deps = nil
},
{
type = 'LuaEntity',
name = 'hostile',
objects = {
'character',
'gun-turret'
},
elevation = 0.92,
resolution = 0.99,
hook = set_noise_hostile_hook,
deps = fetch_common
},
{
type = 'LuaEntity',
name = 'structures',
objects = {
'big-electric-pole',
'medium-electric-pole'
},
elevation = 0.9,
resolution = 0.9,
hook = set_neutral_to_entity,
deps = nil
}
}
local industrial_zone_layers_modded = {
{
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 = set_neutral_to_entity,
deps = nil
},
{
type = 'LuaEntity',
name = 'walls',
objects = {
'stone-wall'
},
elevation = 0.5,
resolution = 0.09,
hook = set_neutral_to_entity,
deps = nil
},
{
type = 'LuaEntity',
name = 'hostile',
objects = {
'character',
'gun-turret'
},
elevation = 0.92,
resolution = 0.99,
hook = set_noise_hostile_hook,
deps = fetch_common
},
{
type = 'LuaEntity',
name = 'structures',
objects = {
'big-electric-pole',
'medium-electric-pole'
},
elevation = 0.9,
resolution = 0.9,
hook = set_neutral_to_entity,
deps = nil
}
}
local swampy_rivers_layers = {
{
type = 'LuaTile',
name = 'speedy_tiles',
objects = {
'black-refined-concrete'
},
elevation = 0.3,
resolution = 0.2,
hook = nil,
deps = nil
},
{
type = 'LuaTile',
name = 'nuclear',
objects = {
'nuclear-ground'
},
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 = 'rocky',
objects = {
'sand-rock-big',
'rock-big',
'rock-huge'
},
elevation = 0.5,
resolution = 0.1,
hook = set_neutral_to_entity,
deps = nil
},
{
type = 'LuaEntity',
name = 'walls',
objects = {
'stone-wall'
},
elevation = 0.5,
resolution = 0.09,
hook = set_neutral_to_entity,
deps = nil
},
{
type = 'LuaEntity',
name = 'hostile',
objects = {
'character',
'gun-turret'
},
elevation = 0.92,
resolution = 0.99,
hook = set_noise_hostile_hook,
deps = fetch_common
},
{
type = 'LuaEntity',
name = 'structures',
objects = {
'big-electric-pole',
'medium-electric-pole'
},
elevation = 0.9,
resolution = 0.9,
hook = set_neutral_to_entity,
deps = nil
}
}
this.presets = {
['flooded-metropolia'] = industrial_zone_layers,
['swampy-rivers'] = swampy_rivers_layers
}
this.presets_modded = {
['flooded-metropolia'] = industrial_zone_layers_modded,
['swampy-rivers'] = swampy_rivers_layers
}
local function pick_map()
return this.maps[CommonFunctions.rand_range(1, #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 init_player_ship_bp =
Token.register(
function(data)
local player = data.player
local entity = data.entity
entity.force = player.force
if entity.name == 'crash-site-chest-1' then
for _, stack in pairs(MapConfig.player_ship_loot) do
entity.insert(stack)
end
end
end
)
this.events = {
merchant = {
alive = false,
moving = false,
spawn_tick = 0,
embark_tick = 0,
position = {x = 0, y = 0},
offer = MapConfig.merchant_offer
}
}
local init_merchant_bp =
Token.register(
function(data)
local entity = data.entity
entity.force = 'merchant'
entity.rotatable = false
entity.minable = false
if entity.name ~= 'market' then
entity.operable = false
else
for _, entry in pairs(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(MapConfig.permission_orbit) do
orbit.set_allows_action(perm, false)
end
end
this.bp = {
player_ship = require('planet_prison.bp.player_ship'),
merchant = require('planet_prison.bp.merchant')
}
local function init_game()
Freeplay.set('disabled', true)
LayersFunctions.init()
ClaimsFunctions.init(MapConfig.claim_markers, MapConfig.claim_max_distance)
local map = pick_map()
local preset
if is_game_modded() then
preset = this.presets_modded[map.name]
else
preset = this.presets[map.name]
end
local surface = game.create_surface('arena', map)
surface.brightness_visual_weights = {
1 / 0.85,
1 / 0.85,
1 / 0.85
}
surface.ticks_per_day = 25000 * 4
this.active_surface = surface.index
this.perks = {}
this.events.merchant.spawn_tick = game.tick + 5000
this.events.raid_groups = {}
this.events.raid_init = false
this.events.annihilation = false
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.3
game.difficulty_settings.research_queue_setting = 'always'
LayersFunctions.set_collision_mask({'water-tile'})
for _, layer in pairs(preset) do
LayersFunctions.add_noise_layer(layer.type, layer.name, layer.objects, layer.elevation, layer.resolution)
if layer.hook ~= nil then
if layer.hook and type(layer.hook) == 'number' then
LayersFunctions.add_noise_layer_hook(layer.name, layer.hook)
else
local token = Token.register(layer.hook)
LayersFunctions.add_noise_layer_hook(layer.name, token)
end
end
if layer.deps ~= nil then
if layer.deps and type(layer.deps) == 'number' then
LayersFunctions.add_noise_layer_dependency(layer.name, layer.deps)
else
local token = Token.register(layer.deps)
LayersFunctions.add_noise_layer_dependency(layer.name, token)
end
end
end
Blueprints.push_blueprint('player_ship', this.bp.player_ship)
Blueprints.set_blueprint_hook('player_ship', init_player_ship_bp)
Blueprints.push_blueprint('merchant', this.bp.merchant)
Blueprints.set_blueprint_hook('merchant', init_merchant_bp)
end
local explode_ship_update =
Token.register(
function(data)
local id = data.id
local time_left = data.time_left
local ship = data.ship
local time = CommonFunctions.get_time(time_left)
for _, ent in pairs(ship.entities) do
if not ent.valid then
return false
end
end
rendering.set_text(id, time)
return true
end
)
local explode_ship =
Token.register(
function(data)
local ship = data.ship
local id = data.id
local active_surface = data.active_surface
local surface = game.get_surface(active_surface)
if not surface or not surface.valid then
return
end
for _, ent in pairs(Blueprints.reference_get_entities(ship)) do
if not ent.valid then
goto continue
end
local explosion = {
name = 'massive-explosion',
position = ent.position
}
surface.create_entity(explosion)
::continue::
end
local bb = Blueprints.reference_get_bounding_box(ship)
LayersFunctions.remove_excluding_bounding_box(bb)
Blueprints.destroy_reference(surface, ship)
rendering.destroy(id)
end
)
local function do_spawn_point(player)
local point = {
x = CommonFunctions.get_axis(player.position, 'x'),
y = CommonFunctions.get_axis(player.position, 'y') - 2
}
local instance = Blueprints.build(player.surface, 'player_ship', point, player)
LayersFunctions.push_excluding_bounding_box(instance.bb)
local time_left = MapConfig.self_explode
local object = {
text = CommonFunctions.get_time(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 id = rendering.draw_text(object)
local data = {id = id, time_left = time_left, ship = instance, active_surface = player.surface.index}
local timer = Timers.set_timer(time_left, explode_ship)
Timers.set_timer_on_update(timer, explode_ship_update)
Timers.set_timer_dependency(timer, data)
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 draw_normal_gui(player)
local button
local merchant = 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 = this.perks[player.name]
if not perks then
perks = assign_perks(player)
end
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 = this.events.merchant.position
local perks = this.perks[player.name]
if not perks then
perks = assign_perks(player)
end
if perks and perks.minimap then
player.print(string.format('>> You received a broadcast with [gps=%d,%d,%s] coordinates', position.x, position.y, player.surface.name))
else
player.print(string.format('>> You were able to spot him %s from your location', CommonFunctions.get_readable_direction(player.position, position)))
end
end
local function on_tick_reset()
if this.events.reset_time == nil then
return
end
if this.events.reset_time > game.tick then
return
end
Server.start_scenario('planet_prison')
this.events.reset_time = nil
end
local function annihilate(caller)
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
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 = this.perks[p.name]
if not perks then
perks = assign_perks(p)
end
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
p.print('Global chat is disabled.', Color.success)
else
elem.caption = 'Global chat'
perks.chat_global = true
p.print('Global chat is enabled.', Color.success)
end
elseif elem.name == 'flashlight_toggle' then
if perks.flashlight_enable then
perks.flashlight_enable = false
if p.character and p.character.valid then
p.character.disable_flashlight()
p.print('Flashlight is disabled.', Color.success)
end
else
perks.flashlight_enable = true
if p.character and p.character.valid then
p.character.enable_flashlight()
p.print('Flashlight is enabled.', Color.success)
end
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 = MapConfig.manual,
name = 'manual_toggle_frame'
}
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 == 'manual_toggle_frame' then
local children = p.gui.center.children
if #children >= 1 then
p.gui.center.clear()
return
end
elseif elem.name == 'annihilate' then
if this.events.annihilation == true then
return
end
elem.destroy()
annihilate(p)
end
end
local function init_player(p)
p.teleport({0, 0}, 'arena')
local s = p.surface
local position = get_non_obstructed_position(s, 10)
this.perks[p.name] = nil
p.teleport(position, 'arena')
--p.name = get_random_name() --player name is read only
local pf = game.forces[p.name]
if not pf then
p.force = game.create_force(p.name)
else
p.force = pf
end
p.force.set_friend('neutral', true)
p.force.set_friend('player', false)
p.force.share_chart = false
this.perks[p.name] = {
flashlight_enable = true,
minimap = false,
chat_global = true,
init = true
}
for i = 1, 7 do
p.force.technologies['inserter-capacity-bonus-' .. i].enabled = false
p.force.technologies['inserter-capacity-bonus-' .. i].researched = false
end
if not p.character or not p.character.valid then
p.set_controller({type = defines.controllers.god})
p.create_character()
end
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(MapConfig.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 player_reconnected(connected)
local offline_players = this.remove_offline_players
if not offline_players then
return
end
if not offline_players.enabled then
return
end
if #offline_players.players > 0 then
for i = 1, #offline_players.players do
if offline_players.players[i] then
local player = game.get_player(offline_players.players[i].index)
if player and player.valid and player.index == connected.index then
offline_players.players[i] = nil
end
end
end
end
end
local function on_player_joined_game(e)
local p = game.players[e.player_index]
player_reconnected(p)
redraw_gui(p)
if this.perks and this.perks[p.name] and this.perks[p.name].init then
return
end
init_player(p)
end
local function _build_merchant_bp(surf, position)
local instance = Blueprints.build(surf, 'merchant', position, nil)
LayersFunctions.push_excluding_bounding_box(instance.bb)
end
local function _remove_merchant_bp(surf)
local refs = Blueprints.get_references('merchant')
local bb = Blueprints.reference_get_bounding_box(refs[1])
LayersFunctions.remove_excluding_bounding_box(bb)
Blueprints.destroy_references(surf, 'merchant')
this.events.merchant.position = {
x = 0,
y = 0
}
end
local function spawn_merchant(s)
local merchant = 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)
this.events.merchant.alive = false
this.events.merchant.moving = true
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 = 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 CommonFunctions.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
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
return 0
end
local cost = 0
local costs = MapConfig.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(MapConfig.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 = CommonFunctions.create_bounding_box_by_points(claim)
local info = _get_raid_info(surf, inner)
if info == nil then
return {}
end
local outer = CommonFunctions.deepcopy(inner)
CommonFunctions.enlarge_bounding_box(outer, 10)
local points = {}
local deps = {
points = points,
inner = inner
}
CommonFunctions.for_bounding_box_extra(surf, outer, _get_outer_points, deps)
local agents = {}
for i, point in ipairs(points) do
if CommonFunctions.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
insert(stash, prop)
end
for _, stack in pairs(stash) do
agent.insert(stack)
end
assign_camouflage(agent, CommonFunctions)
insert(agents, agent)
::continue::
end
return agents
end
local function populate_raid_event(surf)
local claims, group
local status = false
local groups = this.events.raid_groups
for _, p in pairs(game.connected_players) do
groups[p.name] = {}
claims = ClaimsFunctions.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
}
insert(groups[p.name], group)
::continue::
end
end
return status
end
local function on_pre_player_left_game(event)
local offline_players = this.remove_offline_players
if not offline_players then
return
end
if not offline_players.enabled then
return
end
local player = game.players[event.player_index]
local ticker = game.tick
if player.character then
offline_players.players[#offline_players.players + 1] = {
index = event.player_index,
name = player.name,
tick = ticker
}
end
end
local function remove_offline_players()
local offline_players = this.remove_offline_players
if not offline_players then
return
end
if not offline_players.enabled then
return
end
if #offline_players.players > 0 then
for i = 1, #offline_players.players, 1 do
if offline_players.players[i] then
local player = game.get_player(offline_players.players[i].index)
if player and player.valid then
if player.connected then
offline_players.players[i] = nil
else
if offline_players.players[i].tick < game.tick - offline_players.time then
if this.perks and this.perks[player.name] then
this.perks[player.name] = nil
end
ClaimsFunctions.on_player_died(player)
ClaimsFunctions.clear_player_base(player)
if game.forces[player.name] then
game.merge_forces(player.name, 'neutral')
end
Session.clear_player(player)
game.remove_offline_players({player})
offline_players.players[i] = nil
end
end
end
end
end
end
end
local function raid_event(surf)
local raid_groups = this.events.raid_groups
if 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
remove(agents, j)
end
if #agents == 0 then
remove(group, i)
end
end
if #groups == 0 then
raid_groups[name] = nil
end
end
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
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
AIFunctions.do_job(surf, AIFunctions.command.attack_objects, group)
end
::continue::
end
end
local function cause_event(s)
merchant_event(s)
raid_event(s)
end
local function on_tick()
local s = this.active_surface
if not s then
log('on_tick: surface empty!')
return
end
local tick = game.tick
local surf = game.get_surface(this.active_surface)
if not surf or not surf.valid then
return
end
if tick % 4 == 0 then
AIFunctions.do_job(surf, AIFunctions.command.seek_and_destroy_player)
end
LayersFunctions.do_job(surf)
cause_event(surf)
if (tick + 1) % 60 == 0 then
Timers.do_job()
end
if (tick + 1) % 100 == 0 then
AfkFunctions.on_inactive_players(15)
end
if (tick + 1) % 500 == 0 then
remove_offline_players()
end
end
local function make_ore_patch(e)
if CommonFunctions.rand_range(1, 30) ~= 1 then
return
end
local surf = e.surface
local point = e.area.left_top
MapFuntions.draw_entity_circle(point, 'stone', surf, 6, true, 1000000)
MapFuntions.draw_entity_circle(point, 'coal', surf, 12, true, 1000000)
MapFuntions.draw_entity_circle(point, 'copper-ore', surf, 18, true, 1000000)
MapFuntions.draw_entity_circle(point, 'iron-ore', surf, 24, true, 1000000)
MapFuntions.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)
LayersFunctions.push_chunk(e.position)
end
local valid_ents = {
['crash-site-spaceship-wreck-small-1'] = true,
['crash-site-spaceship-wreck-small-2'] = true,
['crash-site-spaceship-wreck-small-3'] = true,
['crash-site-spaceship-wreck-small-4'] = true,
['crash-site-spaceship-wreck-small-5'] = true,
['crash-site-spaceship-wreck-small-6'] = true,
['mineable-wreckage'] = true,
['sand-rock-big'] = true,
['rock-big'] = true,
['rock-huge'] = true
}
local function mined_wreckage(e)
local ent = e.entity
if not ent.valid then
return
end
if not valid_ents[ent.name] then
return
end
e.buffer.clear()
local candidates = {}
local chance = CommonFunctions.rand_range(0, 1000)
for name, attrs in pairs(MapConfig.wreck_loot) do
local prob = attrs.rare * 100
if prob < chance then
local cand = {
name = name,
count = CommonFunctions.rand_range(attrs.count[1], attrs.count[2])
}
insert(candidates, cand)
end
end
local count = #candidates
if count == 0 then
return
end
local cand = candidates[CommonFunctions.rand_range(1, count)]
if e.buffer and cand then
e.buffer.insert(cand)
end
end
local function on_player_mined_entity(e)
local ent = e.entity
if not ent.valid then
return
end
mined_wreckage(e)
-- ClaimsFunctions.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]
ClaimsFunctions.on_player_died(p)
ClaimsFunctions.clear_player_base(p)
if game.forces[p.name] then
game.merge_forces(p.name, 'neutral')
end
p.force = 'player'
if p.connected then
return
end
Session.clear_player(p)
game.remove_offline_players({p})
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 this.last_friend then
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 this.last_friend[peer.name] == p.name then
p.force.set_cease_fire(peer.name, true)
p.force.set_friend(peer.name, true)
peer.force.set_cease_fire(p.name, true)
peer.force.set_friend(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))
this.last_friend[p.name] = ''
this.last_friend[peer.name] = ''
return
end
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)
p.force.set_friend(peer.name, false)
peer.force.set_cease_fire(p.name, false)
peer.force.set_friend(p.name, false)
this.last_friend[p.name] = ''
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 = this.perks[e.force.name]
game.forces.neutral.clear_chart()
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 = CommonFunctions.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 = {
(CommonFunctions.rand_range(-20, 20) / 100.0) + coeff,
(CommonFunctions.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)
this.events.merchant.alive = false
this.events.merchant.moving = false
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 coin_drops = {
['character'] = true,
['gun-turret'] = true
}
local function hostile_death(e)
local ent = e.entity
local loot = e.loot
if not coin_drops[ent.name] then
return false
end
loot.insert({name = 'coin', count = random(30, 70)})
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)
-- ClaimsFunctions.on_entity_died(e.entity)
if valid_ents[e.entity.name] then
e.entity.destroy()
end
end
local function merchant_exploit_check(ent)
if ent.type ~= 'electric-pole' then
return
end
local refs = Blueprints.get_references('merchant')
if not refs or #refs <= 0 then
return
end
local bp_ent = Blueprints.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
-- ClaimsFunctions.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 = this.perks[p.name]
if not perks then
perks = assign_perks(p)
end
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 = floor(r * 255)
end
if g <= 1 then
g = floor(g * 255)
end
if b <= 1 then
b = 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 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 this.perks[p.name].chat_global then
for _, peer in pairs(game.players) do
if peer.name ~= p.name then
local perks = this.perks[peer.name]
if not perks then
perks = assign_perks(peer)
end
if perks and 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 = this.perks[peer.name]
if not perks then
perks = assign_perks(peer)
end
if perks and 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 = 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_marked_for_deconstruction(event)
local entity = event.entity
local player = game.get_player(event.player_index)
if entity and entity.valid and player and player.valid then
entity.cancel_deconstruction(player.force.name)
end
end
local function on_rocket_launched(e)
local surf = game.get_surface(this.active_surface)
if not surf or not surf.valid then
return
end
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
Public.explode_ship = explode_ship
Event.on_init(init_game)
Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(defines.events.on_robot_built_entity, on_built_entity)
Event.add(defines.events.on_research_finished, on_research_finished)
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
Event.add(defines.events.on_chunk_generated, on_chunk_generated)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_player_died, on_player_died)
Event.add(defines.events.on_player_kicked, on_player_died)
Event.add(defines.events.on_player_banned, on_player_died)
Event.add(defines.events.on_pre_player_left_game, on_pre_player_left_game)
Event.add(defines.events.on_player_respawned, on_player_respawned)
Event.add(defines.events.on_player_dropped_item, on_player_dropped_item)
Event.add(defines.events.on_marked_for_deconstruction, on_marked_for_deconstruction)
Event.add(defines.events.on_entity_damaged, on_entity_damaged)
Event.add(defines.events.on_entity_died, on_entity_died)
Event.add(defines.events.on_market_item_purchased, on_market_item_purchased)
Event.add(defines.events.on_chunk_charted, on_chunk_charted)
Event.add(defines.events.on_console_chat, on_console_chat)
Event.add(defines.events.on_gui_click, on_gui_click)
Event.add(defines.events.on_tick, on_tick)
Event.add(defines.events.on_tick, on_tick_reset)
Event.add(defines.events.on_rocket_launched, on_rocket_launched)
return Public