mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-03-17 21:08:08 +02:00
Add config-restart command.
- change Restart command to use server.start_game (offers setting the mod pack). - Change danger ores and crashsite maps to use the new restart command.
This commit is contained in:
parent
66883a87b3
commit
ca381aa289
11
control.lua
11
control.lua
@ -4,12 +4,13 @@
|
||||
require 'resources.data_stages'
|
||||
_LIFECYCLE = _STAGE.control -- Control stage
|
||||
|
||||
-- Util libraries, omitting is a very bad idea
|
||||
require 'utils.math'
|
||||
require 'utils.string'
|
||||
|
||||
-- Overrides the _G.print function
|
||||
require 'utils.print_override'
|
||||
|
||||
-- Omitting the math library is a very bad idea
|
||||
require 'utils.math'
|
||||
|
||||
-- Global Debug and make sure our version file is registered
|
||||
Debug = require 'utils.debug'
|
||||
require 'resources.version'
|
||||
@ -161,6 +162,10 @@ end
|
||||
|
||||
--require 'features.snake.control'
|
||||
|
||||
_G.Server = require 'features.server'
|
||||
|
||||
require 'features.restart_command'
|
||||
|
||||
-- Debug-only modules
|
||||
if _DEBUG then
|
||||
require 'features.scenario_data_manipulation'
|
||||
|
416
features/restart_command.lua
Normal file
416
features/restart_command.lua
Normal file
@ -0,0 +1,416 @@
|
||||
local Gui = require 'utils.gui'
|
||||
local Global = require 'utils.global'
|
||||
local Server = require 'features.server'
|
||||
local Command = require 'utils.command'
|
||||
local Ranks = require 'resources.ranks'
|
||||
local Token = require 'utils.token'
|
||||
local Task = require 'utils.task'
|
||||
local Popup = require 'features.gui.popup'
|
||||
require 'utils.string'
|
||||
|
||||
local Public = {}
|
||||
|
||||
local game_types = {scenario = 'scenario', save = 'save'}
|
||||
Public.game_types = game_types
|
||||
|
||||
local memory = {mod_pack_text = '', restarting = nil}
|
||||
local start_game_data = {type = game_types.scenario, name = '', mod_pack = nil}
|
||||
|
||||
Global.register({start_game_data = start_game_data, memory = memory}, function(tbl)
|
||||
start_game_data = tbl.start_game_data
|
||||
memory = tbl.memory
|
||||
end)
|
||||
|
||||
local function default_can_restart_func(player)
|
||||
return player.valid and player.admin
|
||||
end
|
||||
|
||||
local registered = false
|
||||
local server_can_restart_func = default_can_restart_func
|
||||
local server_restart_callback = nil
|
||||
|
||||
local server_player = {name = '<server>', print = print, admin = true}
|
||||
|
||||
local function double_print(str)
|
||||
game.print(str)
|
||||
print(str)
|
||||
end
|
||||
|
||||
local restart_callback_token
|
||||
restart_callback_token = Token.register(function(data)
|
||||
if not memory.restarting then
|
||||
return
|
||||
end
|
||||
|
||||
local state = data.state
|
||||
if state == 0 then
|
||||
if server_restart_callback then
|
||||
server_restart_callback()
|
||||
end
|
||||
|
||||
Server.start_game(start_game_data)
|
||||
double_print('restarting')
|
||||
memory.restarting = nil
|
||||
return
|
||||
elseif state == 1 then
|
||||
Popup.all('\nServer restarting!\nInitiated by ' .. data.player_name .. '\n' .. 'Next map: '
|
||||
.. start_game_data.name)
|
||||
end
|
||||
|
||||
double_print(state)
|
||||
|
||||
data.state = state - 1
|
||||
Task.set_timeout_in_ticks(60, restart_callback_token, data)
|
||||
end)
|
||||
|
||||
local function get_start_data(player)
|
||||
local message = {'Start Game Data:', '\nType: ', start_game_data.type, '\nName: ', start_game_data.name}
|
||||
|
||||
local mod_pack = start_game_data.mod_pack
|
||||
if mod_pack then
|
||||
message[#message + 1] = '\nMod Pack: '
|
||||
message[#message + 1] = mod_pack
|
||||
end
|
||||
|
||||
local text = table.concat(message)
|
||||
player.print(text)
|
||||
end
|
||||
|
||||
local function sanitize_set_start_data_str(str)
|
||||
str = str:trim()
|
||||
|
||||
local first_char = str:sub(1, 1)
|
||||
if first_char == "'" or first_char == '"' or first_char == '{' then
|
||||
return str
|
||||
end
|
||||
|
||||
return '"' .. str .. '"'
|
||||
end
|
||||
|
||||
local function set_start_data(player, str)
|
||||
str = sanitize_set_start_data_str(str)
|
||||
|
||||
local func, err = loadstring('return ' .. str)
|
||||
if not func then
|
||||
player.print(err)
|
||||
return false
|
||||
end
|
||||
|
||||
local suc, value = pcall(func)
|
||||
if not suc then
|
||||
if value then
|
||||
local i = value:find('\n')
|
||||
if i then
|
||||
player.print(value:sub(1, i))
|
||||
return false
|
||||
end
|
||||
|
||||
i = value:find('%s')
|
||||
if i then
|
||||
player.print(value:sub(i + 1))
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
game.print(value)
|
||||
Public.set_start_game_data(value)
|
||||
|
||||
player.print('Start Game Data set')
|
||||
get_start_data(player)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function restart(args, player)
|
||||
player = player or server_player
|
||||
|
||||
if memory.restarting then
|
||||
player.print('Restart already in progress')
|
||||
return
|
||||
end
|
||||
|
||||
if not server_can_restart_func(player) then
|
||||
return
|
||||
end
|
||||
|
||||
local str = args.str:trim()
|
||||
if str ~= '' and player.admin then
|
||||
if not set_start_data(player, str) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
memory.restarting = true
|
||||
|
||||
double_print('#################-Attention-#################')
|
||||
double_print('Server restart initiated by ' .. player.name)
|
||||
double_print('Next map: ' .. start_game_data.name)
|
||||
double_print('###########################################')
|
||||
|
||||
for _, p in pairs(game.players) do
|
||||
if p.admin then
|
||||
p.print('Abort restart with /abort')
|
||||
end
|
||||
end
|
||||
print('Abort restart with /abort')
|
||||
Task.set_timeout_in_ticks(60, restart_callback_token, {state = 10, player_name = player.name})
|
||||
end
|
||||
|
||||
local function abort(_, player)
|
||||
player = player or server_player
|
||||
|
||||
if memory.restarting then
|
||||
memory.restarting = nil
|
||||
double_print('Restart aborted by ' .. player.name)
|
||||
else
|
||||
player.print('Cannot abort a restart that is not in progress.')
|
||||
end
|
||||
end
|
||||
|
||||
function Public.register(can_restart_func, restart_callback)
|
||||
if registered then
|
||||
error('Register can only be called once', 2)
|
||||
end
|
||||
|
||||
if _LIFECYCLE == 8 then
|
||||
error('Calling Token.register after on_init() or on_load() has run is a desync risk.', 2)
|
||||
end
|
||||
|
||||
registered = true
|
||||
server_can_restart_func = can_restart_func or default_can_restart_func
|
||||
server_restart_callback = restart_callback
|
||||
end
|
||||
|
||||
local main_frame_name = Gui.uid_name()
|
||||
local close_button_name = Gui.uid_name()
|
||||
local scenario_radio_button_name = Gui.uid_name()
|
||||
local save_radio_button_name = Gui.uid_name()
|
||||
local name_textfield_name = Gui.uid_name()
|
||||
local set_mod_pack_checkbox_name = Gui.uid_name()
|
||||
local mod_pack_name_textfield_name = Gui.uid_name()
|
||||
|
||||
Public._main_frame_name = main_frame_name
|
||||
Public._close_button_name = close_button_name
|
||||
Public._scenario_radio_button_name = scenario_radio_button_name
|
||||
Public._save_radio_button_name = save_radio_button_name
|
||||
Public._name_textfield_name = name_textfield_name
|
||||
Public._set_mod_pack_checkbox_name = set_mod_pack_checkbox_name
|
||||
Public._mod_pack_name_textfield_name = mod_pack_name_textfield_name
|
||||
|
||||
local function value_of_type_or_deafult(value, value_type, default)
|
||||
if type(value) == value_type then
|
||||
return value
|
||||
end
|
||||
|
||||
return default
|
||||
end
|
||||
|
||||
--- Gets the data used to start the next game when restart is used.
|
||||
-- @returns data<table> {type<string:'scenario'|'save'>, name<string>, mod_pack<string?>}
|
||||
function Public.get_start_game_data()
|
||||
return {type = start_game_data.type, name = start_game_data.name, mod_pack = start_game_data.mod_pack}
|
||||
end
|
||||
|
||||
--- Sets the data used to start the next game when restart is used.
|
||||
-- @params data<table|string> {type<string?:'scenario'|'save'='scenario'>, name<string>, mod_pack<string?>}
|
||||
-- If mod_pack is nil that means to use the current mod pack, set to empty string ('') to use no mod pack.
|
||||
-- When data is a string: type is scenario, name is data and mod_pack is nil.
|
||||
-- Note: name and mod_pack are case sensitive.
|
||||
function Public.set_start_game_data(data)
|
||||
local data_type = type(data)
|
||||
if data_type == 'string' then
|
||||
data = {type = game_types.scenario, name = data}
|
||||
elseif data_type ~= 'table' then
|
||||
error('data must be a table or string', 2)
|
||||
end
|
||||
|
||||
local game_type = value_of_type_or_deafult(data.type, 'string', game_types.scenario)
|
||||
local name = value_of_type_or_deafult(data.name, 'string', '')
|
||||
local mod_pack = value_of_type_or_deafult(data.mod_pack, 'string', nil)
|
||||
|
||||
game_type = game_type:lower()
|
||||
if game_type ~= game_types.save then
|
||||
game_type = game_types.scenario
|
||||
end
|
||||
|
||||
start_game_data.type = game_type
|
||||
start_game_data.name = name
|
||||
start_game_data.mod_pack = mod_pack
|
||||
|
||||
if mod_pack then
|
||||
memory.mod_pack_text = mod_pack
|
||||
else
|
||||
memory.mod_pack_text = ''
|
||||
end
|
||||
end
|
||||
|
||||
local function draw_main_frame(player)
|
||||
if player == server_player then
|
||||
player.print('/config-restart with no arguments cannot be used from the server.')
|
||||
return
|
||||
end
|
||||
|
||||
local center = player.gui.center
|
||||
local main_frame = center[main_frame_name]
|
||||
if main_frame and main_frame.valid then
|
||||
Gui.destroy(main_frame)
|
||||
end
|
||||
|
||||
main_frame = center.add {
|
||||
type = 'frame',
|
||||
name = main_frame_name,
|
||||
caption = 'Configure Restart',
|
||||
direction = 'vertical'
|
||||
}
|
||||
|
||||
local is_scenario = start_game_data.type == game_types.scenario
|
||||
local radio_button_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
radio_button_flow.add {type = 'label', caption = 'Type:'}
|
||||
local scenario_radio_button = radio_button_flow.add {
|
||||
type = 'radiobutton',
|
||||
name = scenario_radio_button_name,
|
||||
caption = 'scenario',
|
||||
state = is_scenario
|
||||
}
|
||||
local save_radio_button = radio_button_flow.add {
|
||||
type = 'radiobutton',
|
||||
name = save_radio_button_name,
|
||||
caption = 'save',
|
||||
state = not is_scenario
|
||||
}
|
||||
|
||||
local radio_data = {scenario_radio_button = scenario_radio_button, save_radio_button = save_radio_button}
|
||||
|
||||
Gui.set_data(scenario_radio_button, radio_data)
|
||||
Gui.set_data(save_radio_button, radio_data)
|
||||
|
||||
local name_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
name_flow.add {type = 'label', caption = 'Name:'}
|
||||
name_flow.add {type = 'textfield', name = name_textfield_name, text = start_game_data.name}
|
||||
|
||||
local is_set_mod_pack = start_game_data.mod_pack ~= nil
|
||||
local set_mod_pack_checkbox = main_frame.add {
|
||||
type = 'checkbox',
|
||||
name = set_mod_pack_checkbox_name,
|
||||
caption = 'Set mod pack (uncheck to not change current)',
|
||||
state = is_set_mod_pack
|
||||
}
|
||||
local mod_pack_name_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
mod_pack_name_flow.add {type = 'label', caption = 'Mod Pack (empty to set none):'}
|
||||
local mod_pack_name_textfield = mod_pack_name_flow.add {
|
||||
type = 'textfield',
|
||||
name = mod_pack_name_textfield_name,
|
||||
text = memory.mod_pack_text
|
||||
}
|
||||
mod_pack_name_textfield.enabled = is_set_mod_pack
|
||||
|
||||
Gui.set_data(set_mod_pack_checkbox, mod_pack_name_textfield)
|
||||
|
||||
local bottom_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
bottom_flow.add {
|
||||
type = 'button',
|
||||
name = close_button_name,
|
||||
caption = {'common.close_button'},
|
||||
style = 'back_button'
|
||||
}
|
||||
end
|
||||
|
||||
Gui.on_click(close_button_name, function(event)
|
||||
local main_frame = event.player.gui.center[main_frame_name]
|
||||
if main_frame and main_frame.valid then
|
||||
Gui.destroy(main_frame)
|
||||
end
|
||||
end)
|
||||
|
||||
local function set_game_type(radio_data, game_type)
|
||||
radio_data.scenario_radio_button.state = game_type == game_types.scenario
|
||||
radio_data.save_radio_button.state = game_type == game_types.save
|
||||
|
||||
start_game_data.type = game_type
|
||||
end
|
||||
|
||||
Gui.on_checked_state_changed(scenario_radio_button_name, function(event)
|
||||
local radio_data = Gui.get_data(event.element)
|
||||
set_game_type(radio_data, game_types.scenario)
|
||||
end)
|
||||
|
||||
Gui.on_checked_state_changed(save_radio_button_name, function(event)
|
||||
local radio_data = Gui.get_data(event.element)
|
||||
set_game_type(radio_data, game_types.save)
|
||||
end)
|
||||
|
||||
Gui.on_text_changed(name_textfield_name, function(event)
|
||||
start_game_data.name = event.element.text
|
||||
end)
|
||||
|
||||
Gui.on_checked_state_changed(set_mod_pack_checkbox_name, function(event)
|
||||
local set_mod_pack_checkbox = event.element
|
||||
local mod_pack_name_textfield = Gui.get_data(set_mod_pack_checkbox)
|
||||
|
||||
if set_mod_pack_checkbox.state then
|
||||
mod_pack_name_textfield.enabled = true
|
||||
start_game_data.mod_pack = memory.mod_pack_text
|
||||
else
|
||||
mod_pack_name_textfield.enabled = false
|
||||
start_game_data.mod_pack = nil
|
||||
end
|
||||
end)
|
||||
|
||||
Gui.on_text_changed(mod_pack_name_textfield_name, function(event)
|
||||
local text = event.element.text
|
||||
start_game_data.mod_pack = text
|
||||
memory.mod_pack_text = text
|
||||
end)
|
||||
|
||||
local function config_restart(args, player)
|
||||
local str = args.str
|
||||
player = player or server_player
|
||||
|
||||
if str == '' then
|
||||
draw_main_frame(player)
|
||||
elseif str == 'get' then
|
||||
get_start_data(player)
|
||||
elseif str:sub(1, 3) == 'set' then
|
||||
str = str:sub(4) -- remove 'set' from start of str.
|
||||
set_start_data(player, str)
|
||||
else
|
||||
player.print('Invalid arguments')
|
||||
end
|
||||
end
|
||||
|
||||
Public._config_restart = config_restart
|
||||
|
||||
Command.add('config-restart', {
|
||||
description = [[
|
||||
configure the restart command
|
||||
use /config-restart to open a gui,
|
||||
use /config-restart get to prints the values,
|
||||
use /config-restart set scenario_name<string> | {type<string?>, name<string>, mod_pack<string?>} to set the values
|
||||
e.g. /config-restart set 'develop'
|
||||
or /config-restart set {type = 'save', name = 'file.zip'}
|
||||
or /config-restart set {type = 'scenario', name = 'develop', mod_pack = 'mod'}
|
||||
]],
|
||||
arguments = {'str'},
|
||||
default_values = {str = ''},
|
||||
capture_excess_arguments = true,
|
||||
required_rank = Ranks.admin,
|
||||
allowed_by_server = true,
|
||||
allowed_by_player = true
|
||||
}, config_restart)
|
||||
|
||||
Command.add('abort',
|
||||
{description = {'command_description.abort'}, required_rank = Ranks.admin, allowed_by_server = true}, abort)
|
||||
|
||||
Command.add('restart', {
|
||||
description = {'command_description.restart'},
|
||||
arguments = {'str'},
|
||||
capture_excess_arguments = true,
|
||||
default_values = {str = ''},
|
||||
required_rank = Ranks.guest,
|
||||
allowed_by_server = true
|
||||
}, restart)
|
||||
|
||||
return Public
|
390
features/restart_command_tests.lua
Normal file
390
features/restart_command_tests.lua
Normal file
@ -0,0 +1,390 @@
|
||||
local Declare = require 'utils.test.declare'
|
||||
local Helper = require 'utils.test.helper'
|
||||
local RestartCommand = require 'features.restart_command'
|
||||
local Assert = require 'utils.test.assert'
|
||||
local Gui = require 'utils.gui'
|
||||
local Command = require 'utils.command'
|
||||
|
||||
local function test_teardown(context)
|
||||
RestartCommand.set_start_game_data({type = RestartCommand.game_types.scenario, name = '', mod_pack = nil})
|
||||
|
||||
context:add_teardown(function()
|
||||
local main_frame = context.player.gui.center[RestartCommand._main_frame_name]
|
||||
if main_frame and main_frame.valid then
|
||||
Gui.destroy(main_frame)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function declare_test(name, func)
|
||||
local function test_func(context)
|
||||
test_teardown(context)
|
||||
func(context)
|
||||
end
|
||||
|
||||
Declare.test(name, test_func)
|
||||
end
|
||||
|
||||
local function assert_view_matches_start_game_data(player, is_save, is_scenario, name, is_mod_pack_set, mod_pack_name)
|
||||
local center = player.gui.center
|
||||
local scenario_radio_button = Helper.get_gui_element_by_name(center, RestartCommand._scenario_radio_button_name)
|
||||
local save_radio_button = Helper.get_gui_element_by_name(center, RestartCommand._save_radio_button_name)
|
||||
local name_textfield = Helper.get_gui_element_by_name(center, RestartCommand._name_textfield_name)
|
||||
local set_mod_pack_checkbox = Helper.get_gui_element_by_name(center, RestartCommand._set_mod_pack_checkbox_name)
|
||||
local mod_pack_name_textfield = Helper.get_gui_element_by_name(center, RestartCommand._mod_pack_name_textfield_name)
|
||||
|
||||
Assert.equal(is_scenario, scenario_radio_button.state)
|
||||
Assert.equal(is_save, save_radio_button.state)
|
||||
Assert.equal(name, name_textfield.text)
|
||||
Assert.equal(is_mod_pack_set, set_mod_pack_checkbox.state)
|
||||
Assert.equal(mod_pack_name, mod_pack_name_textfield.text)
|
||||
end
|
||||
|
||||
local function assert_start_game_data(type, name, mod_pack)
|
||||
local start_game_data = RestartCommand.get_start_game_data()
|
||||
|
||||
Assert.equal(type, start_game_data.type)
|
||||
Assert.equal(name, start_game_data.name)
|
||||
Assert.equal(mod_pack, start_game_data.mod_pack)
|
||||
end
|
||||
|
||||
local function run_config_command(player, parameter)
|
||||
Command._raise_command('config-restart', player.index, parameter or '')
|
||||
end
|
||||
|
||||
local function run_restart_command(player, parameter)
|
||||
Command._raise_command('restart', player.index, parameter or '')
|
||||
end
|
||||
|
||||
local function run_abort_command(player)
|
||||
Command._raise_command('abort', player.index)
|
||||
end
|
||||
|
||||
Declare.module({'features', 'restart_command'}, function()
|
||||
local inital_start_game_data
|
||||
|
||||
Declare.module_startup(function()
|
||||
inital_start_game_data = RestartCommand.get_start_game_data()
|
||||
end)
|
||||
|
||||
Declare.module_teardown(function()
|
||||
RestartCommand.set_start_game_data(inital_start_game_data)
|
||||
end)
|
||||
|
||||
declare_test('Shows start game data when scenario.', function(context)
|
||||
-- Arrange.
|
||||
local start_game_data = {
|
||||
type = RestartCommand.game_types.scenario,
|
||||
name = 'some_name',
|
||||
mod_pack = 'some_mod_pack'
|
||||
}
|
||||
RestartCommand.set_start_game_data(start_game_data)
|
||||
|
||||
-- Act.
|
||||
run_config_command(context.player)
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_view_matches_start_game_data(context.player, false, true, start_game_data.name, true,
|
||||
start_game_data.mod_pack)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Shows start game data when save.', function(context)
|
||||
-- Arrange.
|
||||
local start_game_data = {type = RestartCommand.game_types.save, name = 'some_name', mod_pack = 'some_mod_pack'}
|
||||
RestartCommand.set_start_game_data(start_game_data)
|
||||
|
||||
-- Act.
|
||||
run_config_command(context.player)
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_view_matches_start_game_data(context.player, true, false, start_game_data.name, true,
|
||||
start_game_data.mod_pack)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Shows start game data when no mod pack.', function(context)
|
||||
-- Arrange.
|
||||
local start_game_data = {type = RestartCommand.game_types.scenario, name = 'some_name'}
|
||||
RestartCommand.set_start_game_data(start_game_data)
|
||||
|
||||
-- Act.
|
||||
run_config_command(context.player)
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_view_matches_start_game_data(context.player, false, true, start_game_data.name, false, '')
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Shows start game data when mod pack empty string.', function(context)
|
||||
-- Arrange.
|
||||
local start_game_data = {type = RestartCommand.game_types.scenario, name = 'some_name', mod_pack = ''}
|
||||
RestartCommand.set_start_game_data(start_game_data)
|
||||
|
||||
-- Act.
|
||||
run_config_command(context.player)
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_view_matches_start_game_data(context.player, false, true, start_game_data.name, true,
|
||||
start_game_data.mod_pack)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Requires admin to run command.', function(context)
|
||||
-- Arrange.
|
||||
local player = context.player
|
||||
Helper.modify_lua_object(context, player, 'admin', false)
|
||||
Helper.modify_lua_object(context, game, 'get_player', function()
|
||||
return player
|
||||
end)
|
||||
|
||||
-- Act.
|
||||
run_config_command(player)
|
||||
|
||||
-- Assert.
|
||||
local center = player.gui.center
|
||||
local main_frame = Helper.get_gui_element_by_name(center, RestartCommand._main_frame_name)
|
||||
Assert.is_nil(main_frame)
|
||||
end)
|
||||
|
||||
declare_test('get returns start game data.', function(context)
|
||||
-- Arrange.
|
||||
local player = context.player
|
||||
local actual = nil
|
||||
Helper.modify_lua_object(context, player, 'print', function(str)
|
||||
actual = str
|
||||
end)
|
||||
Helper.modify_lua_object(context, game, 'get_player', function()
|
||||
return player
|
||||
end)
|
||||
|
||||
local start_game_data = {
|
||||
type = RestartCommand.game_types.scenario,
|
||||
name = 'some_name',
|
||||
mod_pack = 'some_mod_pack'
|
||||
}
|
||||
RestartCommand.set_start_game_data(start_game_data)
|
||||
|
||||
-- Act.
|
||||
run_config_command(player, 'get')
|
||||
|
||||
-- Assert.
|
||||
local expected = [[
|
||||
Start Game Data:
|
||||
Type: scenario
|
||||
Name: some_name
|
||||
Mod Pack: some_mod_pack]]
|
||||
Assert.equal(expected, actual)
|
||||
end)
|
||||
|
||||
declare_test('set does set start game data.', function(context)
|
||||
-- Act.
|
||||
run_config_command(context.player, "set {type = '" .. RestartCommand.game_types.save
|
||||
.. "', name = 'new_name', mod_pack = 'new_mod_pack_name'}")
|
||||
|
||||
-- Assert.
|
||||
local start_game_data = RestartCommand.get_start_game_data()
|
||||
Assert.equal(RestartCommand.game_types.save, start_game_data.type)
|
||||
Assert.equal('new_name', start_game_data.name)
|
||||
Assert.equal('new_mod_pack_name', start_game_data.mod_pack)
|
||||
end)
|
||||
|
||||
for _, data in pairs({'new_name', ' new_name ', "'new_name'", '"new_name"', " 'new_name'"}) do
|
||||
declare_test('set does set start game data for string ' .. data, function(context)
|
||||
-- Act.
|
||||
run_config_command(context.player, 'set ' .. data)
|
||||
|
||||
-- Assert.
|
||||
local start_game_data = RestartCommand.get_start_game_data()
|
||||
Assert.equal(RestartCommand.game_types.scenario, start_game_data.type)
|
||||
Assert.equal('new_name', start_game_data.name)
|
||||
Assert.equal(nil, start_game_data.mod_pack)
|
||||
end)
|
||||
end
|
||||
|
||||
declare_test('Close button closes gui', function(context)
|
||||
-- Arrange.
|
||||
local player = context.player
|
||||
run_config_command(player)
|
||||
|
||||
local center = player.gui.center
|
||||
local close_button = Helper.get_gui_element_by_name(center, RestartCommand._close_button_name)
|
||||
|
||||
-- Act.
|
||||
context:next(function()
|
||||
Helper.click(close_button)
|
||||
end)
|
||||
|
||||
-- Assert
|
||||
context:next(function()
|
||||
local main_frame = Helper.get_gui_element_by_name(center, RestartCommand._main_frame_name)
|
||||
Assert.is_nil(main_frame)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Can change start game data from gui.', function(context)
|
||||
-- Arrange.
|
||||
local player = context.player
|
||||
run_config_command(player)
|
||||
|
||||
local center = player.gui.center
|
||||
local save_radio_button = Helper.get_gui_element_by_name(center, RestartCommand._save_radio_button_name)
|
||||
local name_textfield = Helper.get_gui_element_by_name(center, RestartCommand._name_textfield_name)
|
||||
local mod_pack_checkbox = Helper.get_gui_element_by_name(center, RestartCommand._set_mod_pack_checkbox_name)
|
||||
local mod_pack_name_textfield = Helper.get_gui_element_by_name(center,
|
||||
RestartCommand._mod_pack_name_textfield_name)
|
||||
|
||||
-- Act.
|
||||
Helper.click(save_radio_button)
|
||||
Helper.set_text(name_textfield, 'new_name')
|
||||
Helper.set_checkbox(mod_pack_checkbox, true)
|
||||
Helper.set_text(mod_pack_name_textfield, 'new_mod_pack_name')
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_start_game_data(RestartCommand.game_types.save, 'new_name', 'new_mod_pack_name')
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Can change start game data to scenario from gui.', function(context)
|
||||
-- Arrange.
|
||||
RestartCommand.set_start_game_data({type = RestartCommand.game_types.save})
|
||||
local player = context.player
|
||||
run_config_command(player)
|
||||
|
||||
local center = player.gui.center
|
||||
local scenario_radio_button = Helper.get_gui_element_by_name(center, RestartCommand._scenario_radio_button_name)
|
||||
|
||||
-- Act.
|
||||
Helper.click(scenario_radio_button)
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_start_game_data(RestartCommand.game_types.scenario, '', nil)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Can change start game data to no mod pack from gui.', function(context)
|
||||
-- Arrange.
|
||||
RestartCommand.set_start_game_data({mod_pack = 'some_mod_pack'})
|
||||
local player = context.player
|
||||
run_config_command(player)
|
||||
|
||||
local center = player.gui.center
|
||||
local mod_pack_checkbox = Helper.get_gui_element_by_name(center, RestartCommand._set_mod_pack_checkbox_name)
|
||||
local mod_pack_name_textfield = Helper.get_gui_element_by_name(center,
|
||||
RestartCommand._mod_pack_name_textfield_name)
|
||||
|
||||
-- Act.
|
||||
Helper.set_checkbox(mod_pack_checkbox, false)
|
||||
|
||||
-- Assert.
|
||||
context:next(function()
|
||||
assert_start_game_data(RestartCommand.game_types.scenario, '', nil)
|
||||
Assert.equal('some_mod_pack', mod_pack_name_textfield.text)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('Mod pack is remembered when not set and gui is closed and reopened.', function(context)
|
||||
-- Arrange.
|
||||
RestartCommand.set_start_game_data({mod_pack = 'some_mod_pack'})
|
||||
local player = context.player
|
||||
run_config_command(player)
|
||||
|
||||
local center = player.gui.center
|
||||
local mod_pack_checkbox = Helper.get_gui_element_by_name(center, RestartCommand._set_mod_pack_checkbox_name)
|
||||
local close_button = Helper.get_gui_element_by_name(center, RestartCommand._close_button_name)
|
||||
|
||||
Helper.set_checkbox(mod_pack_checkbox, false)
|
||||
|
||||
context:next(function()
|
||||
Helper.click(close_button)
|
||||
end):next(function()
|
||||
-- Make sure gui closed
|
||||
local main_frame = Helper.get_gui_element_by_name(center, RestartCommand._main_frame_name)
|
||||
Assert.is_nil(main_frame)
|
||||
end):next(function()
|
||||
-- Reopen gui.
|
||||
run_config_command(player)
|
||||
end):next(function()
|
||||
assert_start_game_data(RestartCommand.game_types.scenario, '', nil)
|
||||
|
||||
local mod_pack_name_textfield = Helper.get_gui_element_by_name(center,
|
||||
RestartCommand._mod_pack_name_textfield_name)
|
||||
Assert.equal('some_mod_pack', mod_pack_name_textfield.text)
|
||||
|
||||
-- Re-enable mod pack
|
||||
mod_pack_checkbox = Helper.get_gui_element_by_name(center, RestartCommand._set_mod_pack_checkbox_name)
|
||||
Helper.set_checkbox(mod_pack_checkbox, true)
|
||||
end):next(function()
|
||||
assert_start_game_data(RestartCommand.game_types.scenario, '', 'some_mod_pack')
|
||||
|
||||
local mod_pack_name_textfield = Helper.get_gui_element_by_name(center,
|
||||
RestartCommand._mod_pack_name_textfield_name)
|
||||
Assert.equal('some_mod_pack', mod_pack_name_textfield.text)
|
||||
end)
|
||||
end)
|
||||
|
||||
declare_test('restart command starts restart.', function(context)
|
||||
-- Arrange.
|
||||
RestartCommand.set_start_game_data({name = 'new_name', type = RestartCommand.game_types.scenario})
|
||||
|
||||
local player = context.player
|
||||
|
||||
context:add_teardown(function()
|
||||
run_abort_command(player)
|
||||
end)
|
||||
|
||||
local output = {}
|
||||
|
||||
local function game_print(str)
|
||||
output[#output + 1] = str
|
||||
end
|
||||
|
||||
Helper.modify_lua_object(context, game, 'print', game_print)
|
||||
|
||||
-- Act.
|
||||
run_restart_command(player)
|
||||
|
||||
-- Assert.
|
||||
Assert.array_contains(output, 'Server restart initiated by ' .. player.name)
|
||||
Assert.array_contains(output, 'Next map: new_name')
|
||||
end)
|
||||
|
||||
for _, argument in pairs({'other_game', '{name = "other_game", type = "scenario"}'}) do
|
||||
declare_test('restart command starts restart and sets start data with argument ' .. argument, function(context)
|
||||
-- Arrange.
|
||||
RestartCommand.set_start_game_data({
|
||||
name = 'new_name',
|
||||
type = RestartCommand.game_types.save,
|
||||
mod_pack = 'mod_pack'
|
||||
})
|
||||
|
||||
local player = context.player
|
||||
|
||||
context:add_teardown(function()
|
||||
run_abort_command(player)
|
||||
end)
|
||||
|
||||
local output = {}
|
||||
|
||||
local function game_print(str)
|
||||
output[#output + 1] = str
|
||||
end
|
||||
|
||||
Helper.modify_lua_object(context, game, 'print', game_print)
|
||||
|
||||
-- Act.
|
||||
run_restart_command(player, argument)
|
||||
|
||||
-- Assert.
|
||||
Assert.array_contains(output, 'Server restart initiated by ' .. player.name)
|
||||
Assert.array_contains(output, 'Next map: other_game')
|
||||
assert_start_game_data(RestartCommand.game_types.scenario, 'other_game', nil)
|
||||
end)
|
||||
end
|
||||
end)
|
@ -46,6 +46,7 @@ local discord_named_bold_tag = '[DISCORD-NAMED-BOLD]'
|
||||
local discord_named_embed_tag = '[DISCORD-NAMED-EMBED]'
|
||||
local discord_named_embed_raw_tag = '[DISCORD-NAMED-EMBED-RAW]'
|
||||
local start_scenario_tag = '[START-SCENARIO]'
|
||||
local start_game_tag ='[START-GAME]'
|
||||
local ping_tag = '[PING]'
|
||||
local data_set_tag = '[DATA-SET]'
|
||||
local data_get_tag = '[DATA-GET]'
|
||||
@ -195,6 +196,25 @@ function Public.start_scenario(scenario_name)
|
||||
raw_print(message)
|
||||
end
|
||||
|
||||
--- Stops the server and starts a new game.
|
||||
-- @params start_game_data either string which is the scenario name or a table with the following fields
|
||||
-- type:string<scenario|save> optional defaults to scenario.
|
||||
-- name:string the name of the scenario or save to start.
|
||||
-- mod_pack:string optional the name of the mod pack to use.
|
||||
function Public.start_game(start_game_data)
|
||||
local data
|
||||
if type(start_game_data) == 'string' then
|
||||
data = {type = 'scenario', name = start_game_data}
|
||||
elseif type(start_game_data) == 'table' then
|
||||
data = start_game_data
|
||||
else
|
||||
error('start_game_data must be a string or table')
|
||||
end
|
||||
|
||||
local json = game.table_to_json(data)
|
||||
raw_print(start_game_tag .. json)
|
||||
end
|
||||
|
||||
local default_ping_token = Token.register(function(sent_tick)
|
||||
local now = game.tick
|
||||
local diff = now - sent_tick
|
||||
|
@ -1,9 +1,7 @@
|
||||
local Command = require 'utils.command'
|
||||
local Rank = require 'features.rank_system'
|
||||
local Task = require 'utils.task'
|
||||
local Token = require 'utils.token'
|
||||
local Server = require 'features.server'
|
||||
local Popup = require 'features.gui.popup'
|
||||
local Global = require 'utils.global'
|
||||
local Event = require 'utils.event'
|
||||
local Retailer = require 'features.retailer'
|
||||
@ -15,34 +13,27 @@ local Utils = require 'utils.core'
|
||||
local Discord = require 'resources.discord'
|
||||
local ScoreTracker = require 'utils.score_tracker'
|
||||
local PlayerStats = require 'features.player_stats'
|
||||
local Restart = require 'features.restart_command'
|
||||
local set_timeout_in_ticks = Task.set_timeout_in_ticks
|
||||
|
||||
-- Use these settings for live
|
||||
local map_promotion_channel = Discord.channel_names.map_promotion
|
||||
local crash_site_role_mention = Discord.role_mentions.crash_site
|
||||
-- Use these settings for testing
|
||||
--local map_promotion_channel = Discord.channel_names.bot_playground
|
||||
--local crash_site_role_mention = Discord.role_mentions.test
|
||||
-- local map_promotion_channel = Discord.channel_names.bot_playground
|
||||
-- local crash_site_role_mention = Discord.role_mentions.test
|
||||
|
||||
local Public = {}
|
||||
|
||||
function Public.control(config)
|
||||
Restart.set_start_game_data({type = Restart.game_types.scenario, name = config.scenario_name or 'crashsite'})
|
||||
|
||||
local server_player = {name = '<server>', print = print}
|
||||
local global_data = {restarting = nil}
|
||||
local airstrike_data = {radius_level = 1, count_level = 1}
|
||||
local default_name = config.scenario_name or 'crashsite'
|
||||
|
||||
Global.register({global_data = global_data, airstrike_data = airstrike_data}, function(tbl)
|
||||
global_data = tbl.global_data
|
||||
Global.register({airstrike_data = airstrike_data}, function(tbl)
|
||||
airstrike_data = tbl.airstrike_data
|
||||
end)
|
||||
|
||||
local function double_print(str)
|
||||
game.print(str)
|
||||
print(str)
|
||||
end
|
||||
|
||||
local static_entities_to_check = {
|
||||
'spitter-spawner',
|
||||
'biter-spawner',
|
||||
@ -87,165 +78,13 @@ function Public.control(config)
|
||||
['crashsite-world'] = 'Crash Site World Map',
|
||||
['crashsite-desert'] = 'Crash Site Desert',
|
||||
['crashsite-arrakis'] = 'Crash Site Arrakis'
|
||||
}
|
||||
}
|
||||
|
||||
local callback
|
||||
callback = Token.register(function(data)
|
||||
if not global_data.restarting then
|
||||
return
|
||||
local function can_restart(player)
|
||||
if player.admin then
|
||||
return true
|
||||
end
|
||||
|
||||
local state = data.state
|
||||
local next_scenario = data.scenario_name
|
||||
if state == 0 then
|
||||
Server.start_scenario(next_scenario)
|
||||
double_print('restarting')
|
||||
global_data.restarting = nil
|
||||
return
|
||||
elseif state == 1 then
|
||||
|
||||
local end_epoch = Server.get_current_time()
|
||||
if end_epoch == nil then
|
||||
end_epoch = -1 -- end_epoch is nil if the restart command is used locally rather than on the server
|
||||
end
|
||||
|
||||
local player_data = {}
|
||||
for _, p in pairs(game.players) do
|
||||
player_data[p.index] = {
|
||||
name = p.name,
|
||||
total_kills = ScoreTracker.get_for_player(p.index, PlayerStats.player_total_kills_name),
|
||||
spawners_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_spawners_killed_name),
|
||||
worms_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_worms_killed_name),
|
||||
units_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_units_killed_name),
|
||||
turrets_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_turrets_killed_name),
|
||||
distance_walked = ScoreTracker.get_for_player(p.index,PlayerStats.player_distance_walked_name),
|
||||
player_deaths = ScoreTracker.get_for_player(p.index, PlayerStats.player_deaths_name),
|
||||
coins_earned = ScoreTracker.get_for_player(p.index, PlayerStats.coins_earned_name),
|
||||
entities_built = ScoreTracker.get_for_player(p.index,PlayerStats.player_entities_built_name),
|
||||
entities_crafted = ScoreTracker.get_for_player(p.index,PlayerStats.player_items_crafted_name),
|
||||
fish_eaten = ScoreTracker.get_for_player(p.index,PlayerStats.player_fish_eaten_name),
|
||||
time_played = p.online_time
|
||||
}
|
||||
end
|
||||
|
||||
local statistics = {
|
||||
scenario = config.scenario_name,
|
||||
start_epoch = Server.get_start_time(),
|
||||
end_epoch = end_epoch, -- stored as key already, useful to have it as part of same structure
|
||||
game_ticks = game.ticks_played,
|
||||
enemy_entities = count_enemy_entities(),
|
||||
biters_killed = ScoreTracker.get_for_global(PlayerStats.aliens_killed_name),
|
||||
total_players = #game.players,
|
||||
entities_built = ScoreTracker.get_for_global(PlayerStats.built_by_players_name),
|
||||
player_data = player_data
|
||||
}
|
||||
|
||||
local awards = {
|
||||
['total_kills'] = {value = 0, player = ""},
|
||||
['units_killed'] = {value = 0, player = ""},
|
||||
['spawners_killed'] = {value = 0, player = ""},
|
||||
['worms_killed'] = {value = 0, player = ""},
|
||||
['player_deaths'] = {value = 0, player = ""},
|
||||
['time_played'] = {value = 0, player = ""},
|
||||
['entities_built'] = {value = 0, player = ""},
|
||||
['entities_crafted'] = {value = 0, player = ""},
|
||||
['distance_walked'] = {value = 0, player = ""},
|
||||
['coins_earned'] = {value = 0, player = ""},
|
||||
['fish_eaten'] = {value = 0, player = ""}
|
||||
}
|
||||
|
||||
for k, v in pairs(statistics.player_data) do
|
||||
if v.total_kills > awards.total_kills.value then
|
||||
awards.total_kills.value = v.total_kills
|
||||
awards.total_kills.player = v.name
|
||||
end
|
||||
if v.units_killed > awards.units_killed.value then
|
||||
awards.units_killed.value = v.units_killed
|
||||
awards.units_killed.player = v.name
|
||||
end
|
||||
if v.spawners_killed > awards.spawners_killed.value then
|
||||
awards.spawners_killed.value = v.spawners_killed
|
||||
awards.spawners_killed.player = v.name
|
||||
end
|
||||
if v.worms_killed > awards.worms_killed.value then
|
||||
awards.worms_killed.value = v.worms_killed
|
||||
awards.worms_killed.player = v.name
|
||||
end
|
||||
if v.player_deaths > awards.player_deaths.value then
|
||||
awards.player_deaths.value = v.player_deaths
|
||||
awards.player_deaths.player = v.name
|
||||
end
|
||||
if v.time_played > awards.time_played.value then
|
||||
awards.time_played.value = v.time_played
|
||||
awards.time_played.player = v.name
|
||||
end
|
||||
if v.entities_built > awards.entities_built.value then
|
||||
awards.entities_built.value = v.entities_built
|
||||
awards.entities_built.player = v.name
|
||||
end
|
||||
if v.entities_crafted > awards.entities_crafted.value then
|
||||
awards.entities_crafted.value = v.entities_crafted
|
||||
awards.entities_crafted.player = v.name
|
||||
end
|
||||
if v.distance_walked > awards.distance_walked.value then
|
||||
awards.distance_walked.value = v.distance_walked
|
||||
awards.distance_walked.player = v.name
|
||||
end
|
||||
if v.coins_earned > awards.coins_earned.value then
|
||||
awards.coins_earned.value = v.coins_earned
|
||||
awards.coins_earned.player = v.name
|
||||
end
|
||||
if v.fish_eaten > awards.fish_eaten.value then
|
||||
awards.fish_eaten.value = v.fish_eaten
|
||||
awards.fish_eaten.player = v.name
|
||||
end
|
||||
end
|
||||
|
||||
local time_string = Core.format_time(game.ticks_played)
|
||||
if statistics.enemy_entities < 1000 then
|
||||
Server.to_discord_named_embed(map_promotion_channel, 'Crash Site map won!\\n\\n'
|
||||
.. 'Statistics:\\n'
|
||||
.. 'Map time: '..time_string..'\\n'
|
||||
.. 'Total kills: '..statistics.biters_killed..'\\n'
|
||||
.. 'Biters remaining on map: '..statistics.enemy_entities..'\\n'
|
||||
.. 'Players: '..statistics.total_players..'\\n'
|
||||
.. 'Total entities built: '..statistics.entities_built..'\\n\\n'
|
||||
.. 'Awards:\\n'
|
||||
.. 'Most kills overall: '..awards.total_kills.player..' ('..awards.total_kills.value..')\\n'
|
||||
.. 'Most biters/spitters killed: '..awards.units_killed.player..' ('..awards.units_killed.value..')\\n'
|
||||
.. 'Most spawners killed: '..awards.spawners_killed.player..' ('..awards.spawners_killed.value..')\\n'
|
||||
.. 'Most worms killed: '..awards.worms_killed.player..' ('..awards.worms_killed.value..')\\n'
|
||||
.. 'Most deaths: '..awards.player_deaths.player..' ('..awards.player_deaths.value..')\\n'
|
||||
.. 'Most items crafted: '..awards.entities_crafted.player..' ('..awards.entities_crafted.value..')\\n'
|
||||
.. 'Most entities built: '..awards.entities_built.player..' ('..awards.entities_built.value..')\\n'
|
||||
.. 'Most time played: '..awards.time_played.player..' ('..Core.format_time(awards.time_played.value)..')\\n'
|
||||
.. 'Furthest walked: '..awards.distance_walked.player..' ('..math.floor(awards.distance_walked.value)..')\\n'
|
||||
.. 'Most coins earned: '..awards.coins_earned.player..' ('..awards.coins_earned.value..')\\n'
|
||||
.. 'Seafood lover: '..awards.fish_eaten.player..' ('..awards.fish_eaten.value..' fish eaten)\\n'
|
||||
)
|
||||
else
|
||||
Server.to_discord_named_embed(map_promotion_channel, 'Crash Site map failed!\\n\\n'
|
||||
.. 'Statistics:\\n'
|
||||
.. 'Map time: '..time_string..'\\n'
|
||||
.. 'Total kills: '..statistics.biters_killed..'\\n'
|
||||
.. 'Biters remaining on map: '..statistics.enemy_entities..'\\n'
|
||||
.. 'Players: '..statistics.total_players..'\\n'
|
||||
)
|
||||
end
|
||||
Server.to_discord_named_raw(map_promotion_channel, crash_site_role_mention .. ' **'..scenario_display_name[default_name]..' has just restarted!!**')
|
||||
|
||||
Server.set_data('crash_site_data', tostring(end_epoch), statistics) -- Store the table, with end_epoch as the key
|
||||
Popup.all('\nServer restarting!\nInitiated by ' .. data.name .. '\n')
|
||||
end
|
||||
|
||||
double_print(state)
|
||||
|
||||
data.state = state - 1
|
||||
Task.set_timeout_in_ticks(60, callback, data)
|
||||
end)
|
||||
|
||||
local function map_cleared(player)
|
||||
player = player or server_player
|
||||
local get_entity_count = game.forces["enemy"].get_entity_count
|
||||
-- Check how many of each turrets, worms and spawners are left and return false if there are any of each left.
|
||||
for i = 1, #static_entities_to_check do
|
||||
@ -269,52 +108,148 @@ function Public.control(config)
|
||||
return true
|
||||
end
|
||||
|
||||
local function restart(args, player)
|
||||
player = player or server_player
|
||||
local sanitised_scenario = args.scenario_name
|
||||
|
||||
if global_data.restarting then
|
||||
player.print('Restart already in progress')
|
||||
return
|
||||
local function restart_callback()
|
||||
local end_epoch = Server.get_current_time()
|
||||
if end_epoch == nil then
|
||||
end_epoch = -1 -- end_epoch is nil if the restart command is used locally rather than on the server
|
||||
end
|
||||
|
||||
if player ~= server_player and Rank.less_than(player.name, Ranks.admin) then
|
||||
-- Check enemy count
|
||||
if not map_cleared(player) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Limit the ability of non-admins to call the restart function with arguments to change the scenario
|
||||
-- If not an admin, restart the same scenario always
|
||||
sanitised_scenario = config.scenario_name
|
||||
end
|
||||
|
||||
global_data.restarting = true
|
||||
|
||||
double_print('#################-Attention-#################')
|
||||
double_print('Server restart initiated by ' .. player.name)
|
||||
double_print('###########################################')
|
||||
|
||||
local player_data = {}
|
||||
for _, p in pairs(game.players) do
|
||||
if p.admin then
|
||||
p.print('Abort restart with /abort')
|
||||
player_data[p.index] = {
|
||||
name = p.name,
|
||||
total_kills = ScoreTracker.get_for_player(p.index, PlayerStats.player_total_kills_name),
|
||||
spawners_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_spawners_killed_name),
|
||||
worms_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_worms_killed_name),
|
||||
units_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_units_killed_name),
|
||||
turrets_killed = ScoreTracker.get_for_player(p.index, PlayerStats.player_turrets_killed_name),
|
||||
distance_walked = ScoreTracker.get_for_player(p.index, PlayerStats.player_distance_walked_name),
|
||||
player_deaths = ScoreTracker.get_for_player(p.index, PlayerStats.player_deaths_name),
|
||||
coins_earned = ScoreTracker.get_for_player(p.index, PlayerStats.coins_earned_name),
|
||||
entities_built = ScoreTracker.get_for_player(p.index, PlayerStats.player_entities_built_name),
|
||||
entities_crafted = ScoreTracker.get_for_player(p.index, PlayerStats.player_items_crafted_name),
|
||||
fish_eaten = ScoreTracker.get_for_player(p.index, PlayerStats.player_fish_eaten_name),
|
||||
time_played = p.online_time
|
||||
}
|
||||
end
|
||||
|
||||
local statistics = {
|
||||
scenario = config.scenario_name,
|
||||
start_epoch = Server.get_start_time(),
|
||||
end_epoch = end_epoch, -- stored as key already, useful to have it as part of same structure
|
||||
game_ticks = game.ticks_played,
|
||||
enemy_entities = count_enemy_entities(),
|
||||
biters_killed = ScoreTracker.get_for_global(PlayerStats.aliens_killed_name),
|
||||
total_players = #game.players,
|
||||
entities_built = ScoreTracker.get_for_global(PlayerStats.built_by_players_name),
|
||||
player_data = player_data
|
||||
}
|
||||
|
||||
local awards = {
|
||||
['total_kills'] = {value = 0, player = ""},
|
||||
['units_killed'] = {value = 0, player = ""},
|
||||
['spawners_killed'] = {value = 0, player = ""},
|
||||
['worms_killed'] = {value = 0, player = ""},
|
||||
['player_deaths'] = {value = 0, player = ""},
|
||||
['time_played'] = {value = 0, player = ""},
|
||||
['entities_built'] = {value = 0, player = ""},
|
||||
['entities_crafted'] = {value = 0, player = ""},
|
||||
['distance_walked'] = {value = 0, player = ""},
|
||||
['coins_earned'] = {value = 0, player = ""},
|
||||
['fish_eaten'] = {value = 0, player = ""}
|
||||
}
|
||||
|
||||
for _, v in pairs(statistics.player_data) do
|
||||
if v.total_kills > awards.total_kills.value then
|
||||
awards.total_kills.value = v.total_kills
|
||||
awards.total_kills.player = v.name
|
||||
end
|
||||
if v.units_killed > awards.units_killed.value then
|
||||
awards.units_killed.value = v.units_killed
|
||||
awards.units_killed.player = v.name
|
||||
end
|
||||
if v.spawners_killed > awards.spawners_killed.value then
|
||||
awards.spawners_killed.value = v.spawners_killed
|
||||
awards.spawners_killed.player = v.name
|
||||
end
|
||||
if v.worms_killed > awards.worms_killed.value then
|
||||
awards.worms_killed.value = v.worms_killed
|
||||
awards.worms_killed.player = v.name
|
||||
end
|
||||
if v.player_deaths > awards.player_deaths.value then
|
||||
awards.player_deaths.value = v.player_deaths
|
||||
awards.player_deaths.player = v.name
|
||||
end
|
||||
if v.time_played > awards.time_played.value then
|
||||
awards.time_played.value = v.time_played
|
||||
awards.time_played.player = v.name
|
||||
end
|
||||
if v.entities_built > awards.entities_built.value then
|
||||
awards.entities_built.value = v.entities_built
|
||||
awards.entities_built.player = v.name
|
||||
end
|
||||
if v.entities_crafted > awards.entities_crafted.value then
|
||||
awards.entities_crafted.value = v.entities_crafted
|
||||
awards.entities_crafted.player = v.name
|
||||
end
|
||||
if v.distance_walked > awards.distance_walked.value then
|
||||
awards.distance_walked.value = v.distance_walked
|
||||
awards.distance_walked.player = v.name
|
||||
end
|
||||
if v.coins_earned > awards.coins_earned.value then
|
||||
awards.coins_earned.value = v.coins_earned
|
||||
awards.coins_earned.player = v.name
|
||||
end
|
||||
if v.fish_eaten > awards.fish_eaten.value then
|
||||
awards.fish_eaten.value = v.fish_eaten
|
||||
awards.fish_eaten.player = v.name
|
||||
end
|
||||
end
|
||||
print('Abort restart with /abort')
|
||||
Task.set_timeout_in_ticks(60, callback, {name = player.name, scenario_name = sanitised_scenario, state = 10})
|
||||
end
|
||||
|
||||
local function abort(_, player)
|
||||
player = player or server_player
|
||||
|
||||
if global_data.restarting then
|
||||
global_data.restarting = nil
|
||||
double_print('Restart aborted by ' .. player.name)
|
||||
local time_string = Core.format_time(game.ticks_played)
|
||||
if statistics.enemy_entities < 1000 then
|
||||
Server.to_discord_named_embed(map_promotion_channel, 'Crash Site map won!\\n\\n'
|
||||
.. 'Statistics:\\n'
|
||||
.. 'Map time: '..time_string..'\\n'
|
||||
.. 'Total kills: '..statistics.biters_killed..'\\n'
|
||||
.. 'Biters remaining on map: '..statistics.enemy_entities..'\\n'
|
||||
.. 'Players: '..statistics.total_players..'\\n'
|
||||
.. 'Total entities built: '..statistics.entities_built..'\\n\\n'
|
||||
.. 'Awards:\\n'
|
||||
.. 'Most kills overall: '..awards.total_kills.player..' ('..awards.total_kills.value..')\\n'
|
||||
.. 'Most biters/spitters killed: '..awards.units_killed.player..' ('..awards.units_killed.value..')\\n'
|
||||
.. 'Most spawners killed: '..awards.spawners_killed.player..' ('..awards.spawners_killed.value..')\\n'
|
||||
.. 'Most worms killed: '..awards.worms_killed.player..' ('..awards.worms_killed.value..')\\n'
|
||||
.. 'Most deaths: '..awards.player_deaths.player..' ('..awards.player_deaths.value..')\\n'
|
||||
.. 'Most items crafted: '..awards.entities_crafted.player..' ('..awards.entities_crafted.value..')\\n'
|
||||
.. 'Most entities built: '..awards.entities_built.player..' ('..awards.entities_built.value..')\\n'
|
||||
.. 'Most time played: '..awards.time_played.player..' ('..Core.format_time(awards.time_played.value)..')\\n'
|
||||
.. 'Furthest walked: '..awards.distance_walked.player..' ('..math.floor(awards.distance_walked.value)..')\\n'
|
||||
.. 'Most coins earned: '..awards.coins_earned.player..' ('..awards.coins_earned.value..')\\n'
|
||||
.. 'Seafood lover: '..awards.fish_eaten.player..' ('..awards.fish_eaten.value..' fish eaten)\\n'
|
||||
)
|
||||
else
|
||||
player.print('Cannot abort a restart that is not in progress.')
|
||||
Server.to_discord_named_embed(map_promotion_channel, 'Crash Site map failed!\\n\\n'
|
||||
.. 'Statistics:\\n'
|
||||
.. 'Map time: '..time_string..'\\n'
|
||||
.. 'Total kills: '..statistics.biters_killed..'\\n'
|
||||
.. 'Biters remaining on map: '..statistics.enemy_entities..'\\n'
|
||||
.. 'Players: '..statistics.total_players..'\\n'
|
||||
)
|
||||
end
|
||||
|
||||
local start_game_data = Restart.get_start_game_data()
|
||||
local new_map_name = start_game_data.name
|
||||
|
||||
Server.to_discord_named_raw(map_promotion_channel,
|
||||
crash_site_role_mention .. ' **' .. scenario_display_name[config.scenario_name] .. ' has just restarted!!\\n'
|
||||
.. 'Next map: ' .. new_map_name .. '**')
|
||||
|
||||
Server.set_data('crash_site_data', tostring(end_epoch), statistics) -- Store the table, with end_epoch as the key
|
||||
end
|
||||
|
||||
Restart.register(can_restart, restart_callback)
|
||||
|
||||
local chart_area_callback = Token.register(function(data)
|
||||
local xpos = data.xpos
|
||||
local ypos = data.ypos
|
||||
@ -345,7 +280,7 @@ function Public.control(config)
|
||||
-- process each set of coordinates
|
||||
local i = 1
|
||||
local xpos = coords[i]
|
||||
local ypos = coords[i+1]
|
||||
local ypos = coords[i + 1]
|
||||
while xpos ~= nil and ypos ~= nil do
|
||||
local coin_count = inv.get_item_count("coin")
|
||||
|
||||
@ -359,14 +294,15 @@ function Public.control(config)
|
||||
for j = 1, 15 do
|
||||
set_timeout_in_ticks(60 * j, chart_area_callback, {player = player, xpos = xpos, ypos = ypos})
|
||||
end
|
||||
game.print({'command_description.crash_site_spy_success', player_name, spy_cost, xpos, ypos}, Color.success)
|
||||
game.print({'command_description.crash_site_spy_success', player_name, spy_cost, xpos, ypos},
|
||||
Color.success)
|
||||
inv.remove({name = "coin", count = spy_cost})
|
||||
end
|
||||
|
||||
-- move to the next set of coordinates
|
||||
i = i+2
|
||||
i = i + 2
|
||||
xpos = coords[i]
|
||||
ypos = coords[i+1]
|
||||
ypos = coords[i + 1]
|
||||
end
|
||||
end
|
||||
|
||||
@ -395,29 +331,67 @@ function Public.control(config)
|
||||
local player = data.player
|
||||
local tag = player.force.add_chart_tag(player.surface, {
|
||||
icon = {type = 'item', name = 'poison-capsule'},
|
||||
position = {xpos,ypos},
|
||||
position = {xpos, ypos},
|
||||
text = player.name
|
||||
})
|
||||
set_timeout_in_ticks(60*30, map_chart_tag_clear_callback, tag) -- To clear the tag after 30 seconds
|
||||
set_timeout_in_ticks(60 * 30, map_chart_tag_clear_callback, tag) -- To clear the tag after 30 seconds
|
||||
end)
|
||||
|
||||
local function render_crosshair(data)
|
||||
local red = {r = 0.5, g = 0, b = 0, a = 0.5}
|
||||
local timeout = 5*60
|
||||
local timeout = 5 * 60
|
||||
local line_width = 10
|
||||
local line_length = 2
|
||||
local s = data.player.surface
|
||||
local f = data.player.force
|
||||
rendering.draw_circle{color=red, radius=1.5, width=line_width, filled=false, target=data.position, surface=s, time_to_live=timeout, forces={f}}
|
||||
rendering.draw_line{color=red, width=line_width, from={data.position.x-line_length, data.position.y}, to={data.position.x+line_length, data.position.y}, surface=s, time_to_live=timeout, forces={f}}
|
||||
rendering.draw_line{color=red, width=line_width, from={data.position.x, data.position.y-line_length}, to={data.position.x, data.position.y+line_length}, surface=s, time_to_live=timeout, forces={f}}
|
||||
s.create_entity{name="flying-text", position={data.position.x+3, data.position.y}, text = "[item=poison-capsule] "..data.player.name, color = {r = 1, g = 1, b = 1, a = 1} }
|
||||
rendering.draw_circle {
|
||||
color = red,
|
||||
radius = 1.5,
|
||||
width = line_width,
|
||||
filled = false,
|
||||
target = data.position,
|
||||
surface = s,
|
||||
time_to_live = timeout,
|
||||
forces = {f}
|
||||
}
|
||||
rendering.draw_line {
|
||||
color = red,
|
||||
width = line_width,
|
||||
from = {data.position.x - line_length, data.position.y},
|
||||
to = {data.position.x + line_length, data.position.y},
|
||||
surface = s,
|
||||
time_to_live = timeout,
|
||||
forces = {f}
|
||||
}
|
||||
rendering.draw_line {
|
||||
color = red,
|
||||
width = line_width,
|
||||
from = {data.position.x, data.position.y - line_length},
|
||||
to = {data.position.x, data.position.y + line_length},
|
||||
surface = s,
|
||||
time_to_live = timeout,
|
||||
forces = {f}
|
||||
}
|
||||
s.create_entity {
|
||||
name = "flying-text",
|
||||
position = {data.position.x + 3, data.position.y},
|
||||
text = "[item=poison-capsule] " .. data.player.name,
|
||||
color = {r = 1, g = 1, b = 1, a = 1}
|
||||
}
|
||||
end
|
||||
|
||||
local function render_radius(data)
|
||||
local timeout = 20*60
|
||||
local timeout = 20 * 60
|
||||
local blue = {r = 0, g = 0, b = 0.1, a = 0.1}
|
||||
rendering.draw_circle{color=blue, radius=data.radius+10, filled=true, target=data.position, surface=data.player.surface, time_to_live=timeout, players={data.player}}
|
||||
rendering.draw_circle {
|
||||
color = blue,
|
||||
radius = data.radius + 10,
|
||||
filled = true,
|
||||
target = data.position,
|
||||
surface = data.player.surface,
|
||||
time_to_live = timeout,
|
||||
players = {data.player}
|
||||
}
|
||||
end
|
||||
|
||||
local function strike(args, player)
|
||||
@ -459,16 +433,17 @@ function Public.control(config)
|
||||
-- process each set of coordinates with a 10 strike limit
|
||||
local i = 1
|
||||
local xpos = coords[i]
|
||||
local ypos = coords[i+1]
|
||||
local ypos = coords[i + 1]
|
||||
while xpos ~= nil and ypos ~= nil and i < 20 do
|
||||
-- Check the contents of the chest by spawn for enough poison capsules to use as payment
|
||||
local inv = dropbox.get_inventory(defines.inventory.chest)
|
||||
local capCount = inv.get_item_count("poison-capsule")
|
||||
|
||||
if capCount < strikeCost then
|
||||
player.print(
|
||||
{'command_description.crash_site_airstrike_insufficient_currency_error', strikeCost - capCount},
|
||||
Color.fail)
|
||||
player.print({
|
||||
'command_description.crash_site_airstrike_insufficient_currency_error',
|
||||
strikeCost - capCount
|
||||
}, Color.fail)
|
||||
return
|
||||
end
|
||||
|
||||
@ -500,9 +475,9 @@ function Public.control(config)
|
||||
set_timeout_in_ticks(60, map_chart_tag_place_callback, {player = player, xpos = xpos, ypos = ypos})
|
||||
|
||||
-- move to the next set of coordinates
|
||||
i = i+2
|
||||
i = i + 2
|
||||
xpos = coords[i]
|
||||
ypos = coords[i+1]
|
||||
ypos = coords[i + 1]
|
||||
end
|
||||
end
|
||||
|
||||
@ -531,8 +506,13 @@ function Public.control(config)
|
||||
if name == 'airstrike_damage' then
|
||||
airstrike_data.count_level = airstrike_data.count_level + 1
|
||||
|
||||
Toast.toast_all_players(15, {'command_description.crash_site_airstrike_damage_upgrade_success', player_name, count_level})
|
||||
Server.to_discord_bold('*** '..player_name..' has upgraded Airstrike Damage to level '..count_level..' ***')
|
||||
Toast.toast_all_players(15, {
|
||||
'command_description.crash_site_airstrike_damage_upgrade_success',
|
||||
player_name,
|
||||
count_level
|
||||
})
|
||||
Server.to_discord_bold('*** ' .. player_name .. ' has upgraded Airstrike Damage to level ' .. count_level
|
||||
.. ' ***')
|
||||
item.name_label = {'command_description.crash_site_airstrike_count_name_label', (count_level + 1)}
|
||||
item.price = math.floor(math.exp(airstrike_data.count_level ^ 0.8) / 2) * 1000
|
||||
item.description = {
|
||||
@ -545,8 +525,13 @@ function Public.control(config)
|
||||
Retailer.set_item(market_id, item) -- this updates the retailer with the new item values.
|
||||
elseif name == 'airstrike_radius' then
|
||||
airstrike_data.radius_level = airstrike_data.radius_level + 1
|
||||
Toast.toast_all_players(15, {'command_description.crash_site_airstrike_radius_upgrade_success', player_name, radius_level})
|
||||
Server.to_discord_bold('*** '..player_name..' has upgraded Airstrike Radius to level '..radius_level..' ***')
|
||||
Toast.toast_all_players(15, {
|
||||
'command_description.crash_site_airstrike_radius_upgrade_success',
|
||||
player_name,
|
||||
radius_level
|
||||
})
|
||||
Server.to_discord_bold('*** ' .. player_name .. ' has upgraded Airstrike Radius to level ' .. radius_level
|
||||
.. ' ***')
|
||||
item.name_label = {'command_description.crash_site_airstrike_radius_name_label', (radius_level + 1)}
|
||||
item.description = {
|
||||
'command_description.crash_site_airstrike_radius',
|
||||
@ -559,34 +544,6 @@ function Public.control(config)
|
||||
end
|
||||
end)
|
||||
|
||||
Command.add('crash-site-restart-abort', {
|
||||
description = {'command_description.crash_site_restart_abort'},
|
||||
required_rank = Ranks.admin,
|
||||
allowed_by_server = true
|
||||
}, abort)
|
||||
|
||||
Command.add('abort', {
|
||||
description = {'command_description.crash_site_restart_abort'},
|
||||
required_rank = Ranks.admin,
|
||||
allowed_by_server = true
|
||||
}, abort)
|
||||
|
||||
Command.add('crash-site-restart', {
|
||||
description = {'command_description.crash_site_restart'},
|
||||
arguments = {'scenario_name'},
|
||||
default_values = {scenario_name = default_name},
|
||||
required_rank = Ranks.admin,
|
||||
allowed_by_server = true
|
||||
}, restart)
|
||||
|
||||
Command.add('restart', {
|
||||
description = {'command_description.crash_site_restart'},
|
||||
arguments = {'scenario_name'},
|
||||
default_values = {scenario_name = default_name},
|
||||
required_rank = Ranks.guest,
|
||||
allowed_by_server = true
|
||||
}, restart)
|
||||
|
||||
Command.add('spy', {
|
||||
description = {'command_description.crash_site_spy'},
|
||||
arguments = {'location'},
|
||||
|
@ -1,113 +1,47 @@
|
||||
local Command = require 'utils.command'
|
||||
local Rank = require 'features.rank_system'
|
||||
local Ranks = require 'resources.ranks'
|
||||
local Global = require 'utils.global'
|
||||
local Discord = require 'resources.discord'
|
||||
local Server = require 'features.server'
|
||||
local Popup = require 'features.gui.popup'
|
||||
local Task = require 'utils.task'
|
||||
local Token = require 'utils.token'
|
||||
local Core = require 'utils.core'
|
||||
local Restart = require 'features.restart_command'
|
||||
local ShareGlobals = require 'map_gen.maps.danger_ores.modules.shared_globals'
|
||||
|
||||
return function(config)
|
||||
local default_name = config.scenario_name or 'danger-ore-next'
|
||||
|
||||
local map_promotion_channel = Discord.channel_names.map_promotion
|
||||
local danger_ore_role_mention = Discord.role_mentions.danger_ore
|
||||
|
||||
local server_player = {name = '<server>', print = print}
|
||||
local global_data = {restarting = nil}
|
||||
Restart.set_start_game_data({type = Restart.game_types.scenario, name = config.scenario_name or 'danger-ore-next'})
|
||||
|
||||
Global.register(global_data, function(tbl)
|
||||
global_data = tbl
|
||||
end)
|
||||
local function can_restart(player)
|
||||
if player.admin then
|
||||
return true
|
||||
end
|
||||
|
||||
local function double_print(str)
|
||||
game.print(str)
|
||||
print(str)
|
||||
if not ShareGlobals.data.map_won then
|
||||
player.print({'command_description.danger_ore_restart_condition_not_met'})
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local callback
|
||||
callback = Token.register(function(data)
|
||||
if not global_data.restarting then
|
||||
return
|
||||
end
|
||||
local function restart_callback()
|
||||
local start_game_data = Restart.get_start_game_data()
|
||||
local new_map_name = start_game_data.name
|
||||
|
||||
local state = data.state
|
||||
if state == 0 then
|
||||
Server.start_scenario(data.scenario_name)
|
||||
double_print('restarting')
|
||||
global_data.restarting = nil
|
||||
return
|
||||
elseif state == 1 then
|
||||
Popup.all('\nServer restarting!\nInitiated by ' .. data.name .. '\n')
|
||||
local time_string = Core.format_time(game.ticks_played)
|
||||
|
||||
local time_string = Core.format_time(game.ticks_played)
|
||||
Server.to_discord_named_raw(map_promotion_channel, danger_ore_role_mention
|
||||
.. ' **Danger Ore has just restarted! Previous map lasted: ' .. time_string .. '!**')
|
||||
end
|
||||
local message = {
|
||||
danger_ore_role_mention,
|
||||
' **Danger Ore has just restarted! Previous map lasted: ',
|
||||
time_string,
|
||||
'!\\n',
|
||||
'Next map: ',
|
||||
new_map_name,
|
||||
'**'
|
||||
}
|
||||
message = table.concat(message)
|
||||
|
||||
double_print(state)
|
||||
|
||||
data.state = state - 1
|
||||
Task.set_timeout_in_ticks(60, callback, data)
|
||||
end)
|
||||
|
||||
local function restart(args, player)
|
||||
player = player or server_player
|
||||
local sanitised_scenario = args.scenario_name
|
||||
|
||||
if global_data.restarting then
|
||||
player.print('Restart already in progress')
|
||||
return
|
||||
end
|
||||
|
||||
if player ~= server_player and Rank.less_than(player.name, Ranks.admin) then
|
||||
if not ShareGlobals.data.map_won then
|
||||
player.print({'command_description.danger_ore_restart_condition_not_met'})
|
||||
return
|
||||
end
|
||||
|
||||
-- Limit the ability of non-admins to call the restart function with arguments to change the scenario
|
||||
-- If not an admin, restart the same scenario always
|
||||
sanitised_scenario = config.scenario_name
|
||||
end
|
||||
|
||||
global_data.restarting = true
|
||||
|
||||
double_print('#################-Attention-#################')
|
||||
double_print('Server restart initiated by ' .. player.name)
|
||||
double_print('###########################################')
|
||||
|
||||
for _, p in pairs(game.players) do
|
||||
if p.admin then
|
||||
p.print('Abort restart with /abort')
|
||||
end
|
||||
end
|
||||
print('Abort restart with /abort')
|
||||
Task.set_timeout_in_ticks(60, callback, {name = player.name, scenario_name = sanitised_scenario, state = 10})
|
||||
Server.to_discord_named_raw(map_promotion_channel, message)
|
||||
end
|
||||
|
||||
local function abort(_, player)
|
||||
player = player or server_player
|
||||
|
||||
if global_data.restarting then
|
||||
global_data.restarting = nil
|
||||
double_print('Restart aborted by ' .. player.name)
|
||||
else
|
||||
player.print('Cannot abort a restart that is not in progress.')
|
||||
end
|
||||
end
|
||||
|
||||
Command.add('abort',
|
||||
{description = {'command_description.abort'}, required_rank = Ranks.admin, allowed_by_server = true}, abort)
|
||||
|
||||
Command.add('restart', {
|
||||
description = {'command_description.restart'},
|
||||
arguments = {'scenario_name'},
|
||||
default_values = {scenario_name = default_name},
|
||||
required_rank = Ranks.guest,
|
||||
allowed_by_server = true
|
||||
}, restart)
|
||||
Restart.register(can_restart, restart_callback)
|
||||
end
|
||||
|
@ -150,7 +150,7 @@ rocket_launched(
|
||||
)
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-bobs-ores'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -160,7 +160,7 @@ rocket_launched(
|
||||
)
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-bobs-ores'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -92,7 +92,7 @@ Event.on_init(
|
||||
)
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -109,7 +109,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-chessboard'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -109,7 +109,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-chessboard-uniform'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -135,7 +135,7 @@ rocket_launched(
|
||||
)
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-deadlock-beltboxes'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -109,7 +109,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-gradient'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -116,7 +116,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-hub-spiral'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -108,7 +108,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-landfill'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -140,7 +140,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 5000})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger_ore_normal_science'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -115,7 +115,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-spiral'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -109,7 +109,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'danger-ore-split'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -109,7 +109,7 @@ local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launche
|
||||
rocket_launched({win_satellite_count = 500})
|
||||
|
||||
local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command'
|
||||
restart_command({scenario_name = 'danger-ore-next'})
|
||||
restart_command({scenario_name = 'terraforming-danger-ore'})
|
||||
|
||||
local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump'
|
||||
container_dump({entity_name = 'coal'})
|
||||
|
@ -149,7 +149,7 @@ function Command.add(command_name, options, callback)
|
||||
help_text,
|
||||
function(command)
|
||||
local print -- custom print reference in case no player is present
|
||||
local player = game.player
|
||||
local player = game.get_player(command.player_index)
|
||||
local player_name = player and player.valid and player.name or '<server>'
|
||||
if not player or not player.valid then
|
||||
print = log
|
||||
@ -308,4 +308,30 @@ end
|
||||
|
||||
Event.add(defines.events.on_console_command, on_command)
|
||||
|
||||
-- Backdoor for testing
|
||||
if _DEBUG then
|
||||
local EventCore = require 'utils.event_core'
|
||||
|
||||
local commands_store = {}
|
||||
_G.commands_store = commands_store
|
||||
|
||||
local old_add_command = commands.add_command
|
||||
commands.add_command = function(name, desc, func)
|
||||
old_add_command(name, desc, func)
|
||||
commands_store[name] = func
|
||||
end
|
||||
|
||||
function Command._raise_command(name, player_index, parameter)
|
||||
local func = commands_store[name] or error('command \'' .. name .. '\' not found.', 2)
|
||||
func({name = name, tick = game.tick, player_index = player_index, parameter = parameter })
|
||||
|
||||
EventCore.on_event({
|
||||
name = defines.events.on_console_command,
|
||||
tick = game.tick,
|
||||
player_index = player_index,
|
||||
command = name,
|
||||
parameters = parameter })
|
||||
end
|
||||
end
|
||||
|
||||
return Command
|
||||
|
@ -1,7 +1,7 @@
|
||||
local Declare = require 'utils.test.declare'
|
||||
local EventFactory = require 'utils.test.event_factory'
|
||||
local Gui = require 'utils.gui'
|
||||
local Assert = require 'utils.test.assert'
|
||||
local Helper = require 'utils.test.helper'
|
||||
|
||||
Declare.module({'utils', 'Gui'}, function()
|
||||
Declare.module('can toggle top buttons', function()
|
||||
@ -18,9 +18,8 @@ Declare.module({'utils', 'Gui'}, function()
|
||||
return
|
||||
end
|
||||
|
||||
local event = EventFactory.on_gui_click(element, player.index)
|
||||
local click_action = function()
|
||||
EventFactory.raise(event)
|
||||
Helper.click(element)
|
||||
end
|
||||
|
||||
local before_count = count_gui_elements(player.gui)
|
||||
|
9
utils/string.lua
Normal file
9
utils/string.lua
Normal file
@ -0,0 +1,9 @@
|
||||
--luacheck: globals string
|
||||
|
||||
--- Removes whitespace from the start and end of the string.
|
||||
-- http://lua-users.org/wiki/StringTrim
|
||||
function string.trim(str)
|
||||
return (str:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
return string
|
@ -31,12 +31,12 @@ function Public.area(area)
|
||||
return area
|
||||
end
|
||||
|
||||
function Public.on_gui_click(element, player_index)
|
||||
function Public.on_gui_click(element)
|
||||
return {
|
||||
name = defines.events.on_gui_click,
|
||||
tick = game.tick,
|
||||
element = element,
|
||||
player_index = player_index,
|
||||
player_index = element.player_index,
|
||||
button = defines.mouse_button_type.left,
|
||||
alt = false,
|
||||
control = false,
|
||||
@ -44,6 +44,25 @@ function Public.on_gui_click(element, player_index)
|
||||
}
|
||||
end
|
||||
|
||||
function Public.on_gui_text_changed(element, text)
|
||||
return {
|
||||
name = defines.events.on_gui_text_changed,
|
||||
tick = game.tick,
|
||||
element = element,
|
||||
player_index = element.player_index,
|
||||
text = text
|
||||
}
|
||||
end
|
||||
|
||||
function Public.on_gui_checked_state_changed(element)
|
||||
return {
|
||||
name = defines.events.on_gui_checked_state_changed,
|
||||
tick = game.tick,
|
||||
element = element,
|
||||
player_index = element.player_index
|
||||
}
|
||||
end
|
||||
|
||||
function Public.on_player_deconstructed_area(player_index, surface, area, item)
|
||||
return {
|
||||
name = defines.events.on_player_deconstructed_area,
|
||||
|
@ -1,4 +1,5 @@
|
||||
local Global = require 'utils.global'
|
||||
local EventFactory = require 'utils.test.event_factory'
|
||||
|
||||
local Public = {}
|
||||
|
||||
@ -98,4 +99,70 @@ function Public.modify_lua_object(context, object, key, value)
|
||||
end)
|
||||
end
|
||||
|
||||
local function get_gui_element_by_name(parent, name)
|
||||
if parent.name == name then
|
||||
return parent
|
||||
end
|
||||
|
||||
for _, child in pairs(parent.children) do
|
||||
local found = get_gui_element_by_name(child, name)
|
||||
if found then
|
||||
return found
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Public.get_gui_element_by_name(parent, name)
|
||||
if name == nil or name == '' then
|
||||
return nil
|
||||
end
|
||||
|
||||
return get_gui_element_by_name(parent, name)
|
||||
end
|
||||
|
||||
function Public.click(element)
|
||||
local element_type = element.type
|
||||
|
||||
if element_type == 'checkbox' then
|
||||
element.state = not element.state
|
||||
local state_event = EventFactory.on_gui_checked_state_changed(element)
|
||||
EventFactory.raise(state_event)
|
||||
elseif element_type == 'radiobutton' and not element.state then
|
||||
element.state = true
|
||||
local state_event = EventFactory.on_gui_checked_state_changed(element)
|
||||
EventFactory.raise(state_event)
|
||||
end
|
||||
|
||||
local click_event = EventFactory.on_gui_click(element)
|
||||
EventFactory.raise(click_event)
|
||||
end
|
||||
|
||||
function Public.set_checkbox(element, state)
|
||||
if element.type ~= 'checkbox' then
|
||||
error('element is not a checkbox', 2)
|
||||
end
|
||||
|
||||
local old_state = not not element.state
|
||||
if old_state == state then
|
||||
return
|
||||
end
|
||||
|
||||
element.state = state
|
||||
local state_event = EventFactory.on_gui_checked_state_changed(element)
|
||||
EventFactory.raise(state_event)
|
||||
|
||||
local click_event = EventFactory.on_gui_click(element)
|
||||
EventFactory.raise(click_event)
|
||||
end
|
||||
|
||||
function Public.set_text(element, text)
|
||||
if element.text == text then
|
||||
return
|
||||
end
|
||||
|
||||
element.text = text
|
||||
local text_event = EventFactory.on_gui_text_changed(element, text)
|
||||
EventFactory.raise(text_event)
|
||||
end
|
||||
|
||||
return Public
|
||||
|
Loading…
x
Reference in New Issue
Block a user