Merge pull request #163 from Cooldude2606/feature/store-convert

Converted everything dependent on expcore.store
This commit is contained in:
Cooldude2606
2020-05-30 23:35:08 +01:00
committed by GitHub
14 changed files with 315 additions and 975 deletions

View File

@@ -7,7 +7,6 @@
local Gui = require 'expcore.gui' --- @dep expcore.gui
local Roles = require 'expcore.roles' --- @dep expcore.roles
local Store = require 'expcore.store' --- @dep expcore.store
local Game = require 'utils.game' --- @dep utils.game
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
local Warnings = require 'modules.control.warnings' --- @dep modules.control.warnings
@@ -15,11 +14,9 @@ local Jail = require 'modules.control.jail' --- @dep modules.control.jail
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
local selected_player_store = ''
local selected_action_store = ''
local function set_store_uids(player,action)
selected_player_store = player
selected_action_store = action
local SelectedPlayer, SelectedAction
local function set_datastores(player, action)
SelectedPlayer, SelectedAction = player, action
end
-- auth that will only allow when on player's of lower roles
@@ -33,13 +30,13 @@ end
-- gets the action player and a coloured name for the action to be used on
local function get_action_player_name(player)
local selected_player_name = Store.get(selected_player_store,player)
local selected_player_name = SelectedPlayer:get(player)
local selected_player = Game.get_player_from_any(selected_player_name)
local selected_player_color = format_chat_player_name(selected_player)
return selected_player_name, selected_player_color
end
-- telports one player to another
-- teleports one player to another
local function teleport(from_player,to_player)
local surface = to_player.surface
local position = surface.find_non_colliding_position('character',to_player.position,32,1)
@@ -109,7 +106,7 @@ local report_player = new_button('utility/spawn_flag',{'player-list.report-playe
if Reports.is_reported(selected_player_name,player.name) then
player.print({'expcom-report.already-reported'},Colors.orange_red)
else
Store.set(selected_action_store,player,'command/report')
SelectedAction:set(player, 'command/report')
end
end)
@@ -125,7 +122,7 @@ end
-- @element warn_player
local warn_player = new_button('utility/spawn_flag',{'player-list.warn-player'})
:on_click(function(player)
Store.set(selected_action_store,player,'command/give-warning')
SelectedAction:set(player, 'command/give-warning')
end)
local function warn_player_callback(player,reason)
@@ -143,7 +140,7 @@ local jail_player = new_button('utility/multiplayer_waiting_icon',{'player-list.
if Jail.is_jailed(selected_player_name) then
player.print({'expcom-jail.already-jailed', selected_player_color},Colors.orange_red)
else
Store.set(selected_action_store,player,'command/jail')
SelectedAction:set(player, 'command/jail')
end
end)
@@ -162,7 +159,7 @@ local temp_ban_player = new_button('utility/warning_white',{'player-list.temp-ba
if Jail.is_jailed(selected_player_name) then
player.print({'expcom-jail.already-banned', selected_player_color},Colors.orange_red)
else
Store.set(selected_action_store,player,'command/temp-ban')
SelectedAction:set(player, 'command/temp-ban')
end
end)
@@ -177,7 +174,7 @@ end
-- @element kick_player
local kick_player = new_button('utility/warning_icon',{'player-list.kick-player'})
:on_click(function(player)
Store.set(selected_action_store,player,'command/kick')
SelectedAction:set(player, 'command/kick')
end)
local function kick_player_callback(player,reason)
@@ -189,7 +186,7 @@ end
-- @element ban_player
local ban_player = new_button('utility/danger_icon',{'player-list.ban-player'})
:on_click(function(player)
Store.set(selected_action_store,player,'command/ban')
SelectedAction:set(player, 'command/ban')
end)
local function ban_player_callback(player,reason)
@@ -198,7 +195,7 @@ local function ban_player_callback(player,reason)
end
return {
set_store_uids = set_store_uids,
set_datastores = set_datastores,
buttons = {
['command/teleport'] = {
auth=function(player,selected_player)

View File

@@ -10,7 +10,7 @@ return {
-- Warp cooldowns
bypass_warp_cooldown = 'expcore.roles', --- @setting bypass_warp_cooldown dictates who the warp cooldown is applied to; values: all, admin, expcore.roles, none
expcore_roles_bypass_warp_cooldown = 'gui/warp-list/bypass-cooldown', --- @setting expcore_roles_bypass_warp_cooldown if expcore.roles is used then this is the required permission
cooldown_duraction = 60, --- @setting cooldown_duraction the duration of the warp cooldown in seconds
cooldown_duration = 60, --- @setting cooldown_duration the duration of the warp cooldown in seconds
-- Warp proximity
bypass_warp_proximity = 'expcore.roles', --- @setting bypass_warp_proximity dictates who the warp proximity is applied to; values: all, admin, expcore.roles, none

View File

@@ -251,7 +251,8 @@ function DatastoreManager.ingest(action, datastoreName, key, valueJson)
elseif action == 'propagate' or action == 'request' then
local success, value = pcall(game.json_to_table, valueJson)
if not success or value == nil then value = tonumber(valueJson) or valueJson end
value = datastore:raise_event('on_load', key, value)
local old_value = datastore:raw_get(key)
value = datastore:raise_event('on_load', key, value, old_value)
datastore:set(key, value)
end
@@ -495,12 +496,13 @@ ExampleData:set('TestKey', 'Foo')
]]
function Datastore:set(key, value)
key = self:serialize(key)
local old_value = self:raw_get(key)
if value == self.default and not self.allow_set_to_default then
self:raw_set(key)
else
self:raw_set(key, value)
end
self:raise_event('on_update', key, value)
self:raise_event('on_update', key, value, old_value)
if self.auto_save then self:save(key) end
return value
end
@@ -535,11 +537,12 @@ end)
function Datastore:update(key, callback)
key = self:serialize(key)
local value = self:raw_get(key)
local old_value = copy(self:raw_get(key))
local success, new_value = xpcall(callback, update_error, key, value)
if success and new_value ~= nil then
self:set(key, new_value)
else
self:raise_event('on_update', key, value)
self:raise_event('on_update', key, value, old_value)
if self.auto_save then self:save(key) end
end
end
@@ -554,8 +557,9 @@ ExampleData:remove('TestKey')
]]
function Datastore:remove(key)
key = self:serialize(key)
local old_value = self:raw_get(key)
self:raw_set(key)
self:raise_event('on_update', key)
self:raise_event('on_update', key, nil, old_value)
if self.save_to_disk then self:write_action('remove', key) end
if self.parent and self.parent.auto_save then return self.parent:save(key) end
end
@@ -622,11 +626,12 @@ end)
function Datastore:update_all(callback)
local data = self:get_all()
for key, value in pairs(data) do
local old_value = copy(value)
local success, new_value = xpcall(callback, update_error, key, value)
if success and new_value ~= nil then
self:set(key, new_value)
else
self:raise_event('on_update', key, value)
self:raise_event('on_update', key, value, old_value)
if self.auto_save then self:save(key) end
end
end
@@ -749,12 +754,13 @@ local function event_error(err) log('An error ocurred in a datastore event handl
value = self:raise_event('on_save', key, value)
]]
function Datastore:raise_event(event_name, key, value, source)
function Datastore:raise_event(event_name, key, value, old_value, source)
-- Raise the event for the children of this datastore
if source ~= 'child' and next(self.children) then
if type(value) ~= 'table' then value = {} end
for value_name, child in pairs(self.children) do
value[value_name] = child:raise_event(event_name, key, value[value_name], 'parent')
local old_child_value = old_value and old_value[value_name] or nil
value[value_name] = child:raise_event(event_name, key, value[value_name], old_child_value, 'parent')
end
end
@@ -762,14 +768,15 @@ function Datastore:raise_event(event_name, key, value, source)
local handlers = self.events[event_name]
if handlers then
for _, handler in ipairs(handlers) do
local success, new_value = xpcall(handler, event_error, key, value)
local success, new_value = xpcall(handler, event_error, key, value, old_value)
if success and new_value ~= nil then value = new_value end
end
end
-- Raise the event for the parent of this datastore
if source ~= 'parent' and self.parent then
self.parent:raise_event(event_name, key, self.parent:raw_get(key, true), 'child')
local parent_value = self.parent:raw_get(key, true)
self.parent:raise_event(event_name, key, parent_value, parent_value, 'child')
end
-- If this is the save event and the table is empty then return nil

View File

@@ -90,9 +90,8 @@ local check_data_loaded = Async.register(function(player)
end)
--- When player data loads tell the player if the load had failed previously
PlayerData:on_load(function(player_name, player_data)
PlayerData:on_load(function(player_name, player_data, existing_data)
if not player_data or player_data.valid == false then return end
local existing_data = PlayerData:get(player_name)
if existing_data and existing_data.valid == false then
game.players[player_name].print{'expcore-data.data-restore'}
end
@@ -133,7 +132,7 @@ Event.add(defines.events.on_player_left_game, function(event)
local player_data = PlayerData:get(player)
if player_data.valid == true then
PlayerData:unload(player)
else PlayerData:raw_set(player) end
else PlayerData:raw_set(player.name) end
end)
----- Module Return -----

View File

@@ -1,523 +0,0 @@
--[[-- Core Module - Store
- Used to store and watch for updates for values in the global table
@core Store
@alias Store
@usage-- Require the module and add a store with no keys
-- Store with no keys does not need a serializer
local Store = require 'expcore.store' --- @dep expcore.store
local scenario_diffculty = Store.register()
-- When the store is changed this function will trigger
Store.watch(scenario_diffculty, function(value)
game.print('The scenario diffculty has been set to '..value)
end)
Store.set(scenario_diffculty, 'hard') -- Set the value stored to 'hard'
Store.get(scenario_diffculty) -- Returns 'hard'
Store.update(scenario_diffculty, function(value) -- Will set value to 'normal' if no value is present
return not value and 'normal'
end)
@usage-- Require the module and add a store with keys
-- Store with keys does not require a serializer but it can be helpful
local Store = require 'expcore.store' --- @dep expcore.store
local player_scores = Store.register(function(player) -- Use player name as the key
return player.name
end)
-- When any key in the store is changed this function will trigger
Store.watch(player_scores, function(value, key, old_value)
game.print(key..' now has a score of '..value)
end)
Store.set(player_scores, game.player, 10) -- Set your score to 10
Store.get(scenario_diffculty, game.player) -- Returns 10
Store.update(scenario_diffculty, game.player, function(value) -- Add 1 to your score
return value + 1
end)
]]
local Event = require 'utils.event' --- @dep utils.event
local Store = {
--- The current highest uid that is being used, will not increase during runtime
-- @field uid
uid = 0,
--- An array of the serializers that stores are using, key is store uids
-- @table serializers
serializers = {},
--- An array of watchers that stores will trigger, key is store uids
-- @table watchers
watchers = {},
--- An index used for debuging to find the file where different stores where registered
-- @table file_paths
file_paths = {}
}
-- All data is stored in global.data_store and is accessed here with data_store
local data_store = {}
global.data_store = data_store
Event.on_load(function()
data_store = global.data_store
end)
--- Store Setup.
-- @section setup
--[[-- An error checking and serializing function for checking store uids and keys, note key is not required
@tparam number store the uid of the store that you want to check is valid
@tparam[opt] ?string|any key the key that you want to serialize or check is a string
@tparam[opt=1] number error_stack the position in the stack relative to the current function (1) to raise this error on
@treturn string if key is given and a serializer is registered, or key was already a string, then the key is returned
@usage-- Registering a new store and checking that it is valid
-- New store will use player names as the keys
local player_scores = Store.register(function(player)
return player.name
end)
-- player_scores is a valid store and key will be your player name
local key = Store.validate(player_scores, game.player)
]]
function Store.validate(store, key, error_stack)
error_stack = error_stack or 1
if type(store) ~= 'number' then
-- Store is not a number and so if not valid
error('Store uid given is not a number; recived type '..type(store), error_stack+1)
elseif store > Store.uid then
-- Store is a number but it is out of range, ie larger than the current highest uid
error('Store uid is out of range; recived '..tostring(store), error_stack+1)
elseif key ~= nil and type(key) ~= 'string' and Store.serializers[store] == nil then
-- Key is present but is not a string and there is no serializer registered
error('Store key is not a string and no serializer has been registered; recived '..type(key), error_stack+1)
elseif key ~= nil then
-- Key is present and so it is serialized and returned
local serializer = Store.serializers[store]
if type(key) ~= 'string' then
local success, serialized_key = pcall(serializer, key)
if not success then
-- Serializer casued an error while serializing the key
error('Store watcher casued an error:\n\t'..key, error_stack+1)
elseif type(serialized_key) ~= 'string' then
-- Serializer was successful but failed to return a string value
error('Store key serializer did not return a string; recived type '..type(key), error_stack+1)
end
return serialized_key
end
return key
end
end
--[[-- Required to create new stores and register an serializer to a store, serializer not required
@tparam[opt] function serializer the function used to convert non string keys into strings to be used in the store
@treturn number the uid for the new store that you have created, use this as the first param to all other functions
@usage-- Creating a store with no serializer
local scenario_diffculty = Store.register()
@usage-- Creating a store which can take LuaPlayer
local player_scores = Store.register(function(player)
return player.name
end)
]]
function Store.register(serializer)
if _LIFECYCLE ~= _STAGE.control then
-- Only allow this function to be called during the control stage
error('Store can not be registered durring runtime', 2)
end
-- Increment the uid counter
local uid = Store.uid + 1
Store.uid = uid
-- Register the serializer if given
if serializer then
Store.serializers[uid] = serializer
end
-- Add entry in the debug table
local file_path = debug.getinfo(2, 'S').source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
Store.file_paths[uid] = file_path
-- Return the new uid
return uid
end
--[[-- Register a watch function to a store that is called when the value in the store is changed, triggers for any key
@tparam number store the uid of the store that you want to watch for changes to
@tparam function watcher the function that will be called when there is a change to the store
@usage-- Printing the changed value to all players, no keys
-- Register the new store, we are not using keys so we dont need a serializer
local scenario_diffculty = Store.register()
-- Register the watcher so that when we change the value the message is printed
Store.watch(scenario_diffculty, function(value)
game.print('The scenario diffculty has been set to '..value)
end)
-- Set a new value for the diffculty and see that it has printed to the game
Store.set(scenario_diffculty, 'hard')
@usage-- Printing the changed value to all players, with keys
-- Register the new store, we are not using player names as the keys so it would be useful to accept LuaPlayer objects
local player_scores = Store.register(function(player)
return player.name
end)
-- Register the watcher so that when we change the value the message is printed
Store.watch(player_scores, function(value, key, old_value)
game.print(key..' now has a score of '..value)
end)
-- Set a new value for your score and see that it has printed to the game
Store.set(player_scores, game.player, 10)
]]
function Store.watch(store, watcher)
if _LIFECYCLE ~= _STAGE.control then
-- Only allow this function to be called during the control stage
error('Store watcher can not be registered durring runtime', 2)
end
Store.validate(store, nil, 2)
-- Add the watchers table if it does not exist
local watchers = Store.watchers[store]
if not watchers then
watchers = {}
Store.watchers[store] = watchers
end
-- Append the new watcher function
watchers[#watchers+1] = watcher
end
--- Store Data Management.
-- @section data
--[[-- Used to retrive the current data that is stored, key is optional depending on if you are using them
@tparam number store the uid of the store that you want to get the value from
@tparam[opt] ?string|any key the key that you want to get the value of, must be a string unless you have a serializer
@treturn any the data that is stored
@usage-- Getting the value of a store with no keys
-- Register the new store, we are not using keys so we dont need a serializer
local scenario_diffculty = Store.register()
-- Get the current diffculty for the scenario
local diffculty = Store.get(scenario_diffculty)
@usage-- Getting the data from a store with keys
-- Register the new store, we are not using player names as the keys so it would be useful to accept LuaPlayer objects
local player_scores = Store.register(function(player)
return player.name
end)
-- Get your current score
local my_score = Store.get(player_scores, game.player)
-- Get all scores
lcoal scores = Store.get(player_scores)
]]
function Store.get(store, key)
key = Store.validate(store, key, 2)
-- Get the data from the data store
local data = data_store[store]
if key then
if type(data) ~= 'table' then
data_store[store] = {_value = data_store[store]}
return nil
else
return data[key]
end
end
-- Return all data if there is no key
return data
end
--[[-- Used to clear the data in a store, will trigger any watchers, key is optional depending on if you are using them
@tparam number store the uid of the store that you want to clear
@tparam[opt] ?string|any key the key that you want to clear, must be a string unless you have a serializer
@usage-- Clear a store which does not use keys
-- Register the new store, we are not using keys so we dont need a serializer
local scenario_diffculty = Store.register()
-- Clear the scenario diffculty
Store.clear(scenario_diffculty)
@usage-- Clear data that is in a store with keys
-- Register the new store, we are not using player names as the keys so it would be useful to accept LuaPlayer objects
local player_scores = Store.register(function(player)
return player.name
end)
-- Clear your score
Store.clear(player_scores, game.player)
-- Clear all scores
Store.clear(player_scores)
]]
function Store.clear(store, key)
key = Store.validate(store, key, 2)
local old_value
-- Check if there is a key being used
if key then
if type(data_store[store]) == 'table' then
old_value = data_store[store][key]
data_store[store][key] = nil
end
else
old_value = data_store[store]
data_store[store] = nil
end
-- Trigger any watch functions
Store.raw_trigger(store, key, nil, old_value)
end
--[[-- Used to set the data in a store, will trigger any watchers, key is optional depending on if you are using them
@tparam number store the uid of the store that you want to set
@tparam[opt] ?string|any key the key that you want to set, must be a string unless you have a serializer
@tparam any value the value that you want to set
@usage-- Setting a store which does not use keys
-- Register the new store, we are not using keys so we dont need a serializer
local scenario_diffculty = Store.register()
-- Set the new scenario diffculty
Store.set(scenario_diffculty, 'hard')
@usage-- Set data in a store with keys
-- Register the new store, we are not using player names as the keys so it would be useful to accept LuaPlayer objects
local player_scores = Store.register(function(player)
return player.name
end)
-- Set your current score
Store.set(player_scores, game.player, 10)
-- Set all scores, note this might not have much use
Store.set(player_scores, {
[game.player.name] = 10,
['SomeOtherPlayer'] = 0
})
]]
function Store.set(store, key, value)
-- Allow for key to be optional
if value == nil then
value = key
key = nil
end
-- Check the store is valid
key = Store.validate(store, key, 2)
local old_value
-- If there is a key being used then the store must be a able
if key then
if type(data_store[store]) ~= 'table' then
data_store[store] = {_value = data_store[store]}
end
old_value = data_store[store][key]
data_store[store][key] = value
else
old_value = data_store[store]
data_store[store] = value
end
-- Trigger any watchers
Store.raw_trigger(store, key, value, old_value)
end
--[[-- Used to update the data in a store, use this with tables, will trigger any watchers, key is optional depending on if you are using them
@tparam number store the uid of the store that you want to update
@tparam[opt] ?string|any key the key that you want to update, must be a string unless you have a serializer
@tparam function updater the function which is called to make changes to the value, such as changing table keys, if a value is returned it will replace the current value in the store
@usage-- Incrementing a global score
-- Because we are only going to have one score so we will not need keys or a serializer
local game_score = Store.register()
-- Setting a default value
Store.set(game_score, 0)
-- We now will update the game score by one, we return the value so that it is set as the new value in the store
Store.update(game_score, function(value)
return value + 1
end)
@usage-- Updating keys in a table of data
-- Register the new store, we are not using player names as the keys so it would be useful to accept LuaPlayer objects
local player_data = Store.register(function(player)
return player.name
end)
-- Setting a default value for your player, used to show the table structure
Store.set(player_data, game.player, {
group = 'Admin',
role = 'Owner',
show_group_config = false
})
-- Updating the show_group_config key in your player data, note that it would be harder to call set every time
-- We do not need to return anything in this case as we are not replacing all the data
Store.update(player_data, game.player, function(data)
data.show_group_config = not data.show_group_config
end)
]]
function Store.update(store, key, updater)
-- Allow for key to be nil
if updater == nil then
updater = key
key = nil
end
-- Check the store is valid
key = Store.validate(store, key, 2)
local value, old_value
-- If a key is used then the store must be a table
if key then
if type(data_store[store]) ~= 'table' then
data_store[store] = {_value = data_store[store]}
end
-- Call the updater and if it returns a value then set this value
local rtn = updater(data_store[store][key])
if rtn then
old_value = data_store[store][key]
data_store[store][key] = rtn
end
value = data_store[store][key]
else
-- Call the updater and if it returns a value then set this value
local rtn = updater(data_store[store])
if rtn then
old_value = data_store[store][key]
data_store[store] = rtn
end
value = data_store[store]
end
-- Trigger any watchers
Store.raw_trigger(store, key, value, old_value)
end
--[[-- Used to update all values that are in a store, similar to Store.update but acts on all keys at once, will trigger watchers for every key present
@tparam number store the uid of the store that you want to map
@tparam function updater the function that is called on every key in this store
@usage-- Updating keys in a table of data
-- Register the new store, we are not using player names as the keys so it would be useful to accept LuaPlayer objects
local player_data = Store.register(function(player)
return player.name
end)
-- Setting a default value for your player, used to show the table structure
Store.set(player_data, game.player, {
group = 'Admin',
role = 'Owner',
show_group_config = false
})
-- Updating the show_group_config key for all players, note that it would be harder to call set every time
-- We do not need to return anything in this case as we are not replacing all the data
-- We also have access to the current key being updated if needed
Store.map(player_data, function(data, key)
data.show_group_config = not data.show_group_config
end)
]]
function Store.map(store, updater)
Store.validate(store, nil, 2)
-- Get all that data in the store and check its a table
local data = data_store[store]
if type(data) ~= 'table' then
return
end
-- Loop over all the keys and call the updater, setting value if returned, and calling watcher functions
for key, value in pairs(data) do
local rtn = updater(value, key)
if rtn then
data[key] = rtn
end
Store.raw_trigger(store, key, data[key], value)
end
end
--[[-- Used to trigger watcher functions, this may be used to trigger them if you did not use Store.update or Store.set
@tparam number store the uid of the store that you want to trigger
@tparam[opt] ?string|any key the key that you want to trigger, must be a string unless you have a serializer
@usage-- Faking the update to a store
-- The type of store we use does not really matter for this as long as you pass it what you watchers are expecting
local scenario_diffculty = Store.register()
-- Trigger the watchers with a fake change of diffculty
Store.trigger(scenario_diffculty)
]]
function Store.trigger(store, key)
key = Store.validate(store, key, 2)
-- Get the data from the data store
local data = data_store[store]
if key then
data = data[key]
Store.raw_trigger(store, key, data, data)
else
Store.raw_trigger(store, key, data, data)
end
end
--[[-- Used to trigger watcher functions, the value and key are passed directly to the watchers regardless if the value is correct
@tparam number store the uid of the store that you want to trigger
@tparam[opt] ?string|any key the key that you want to trigger, must be a string unless you have a serializer
@tparam[opt] any value the new value that is at this key or store, passed directly to the watcher
@tparam[opt] any old_value the old value that was at this key or store often the same if value is a table, passed directly to the watcher
@usage-- Triggering a manule call of the watchers
-- The type of store we use does not really matter for this as long as you pass it what you watchers are expecting
local scenario_diffculty = Store.register()
-- Trigger the watchers with a fake change of diffculty
-- This is mostly used internally but it can be useful in other cases
Store.raw_trigger(scenario_diffculty, nil, 'normal', 'normal')
]]
function Store.raw_trigger(store, key, value, old_value)
key = Store.validate(store, key, 2)
-- Get the watchers and then loop over them
local watchers = Store.watchers[store] or {}
for _, watcher in pairs(watchers) do
local success, err = pcall(watcher, value, key, old_value)
if not success then
error('Store watcher casued an error:\n\t'..err)
end
end
end
-- Module return
return Store

View File

@@ -15,7 +15,6 @@ local interface_modules = {
['output']=Common.player_return,
['Group']='expcore.permission_groups',
['Roles']='expcore.roles',
['Store']='expcore.store',
['Gui']='expcore.gui',
['Async']='expcore.async',
['Datastore']='expcore.datastore'

View File

@@ -10,22 +10,21 @@ Tasks.update_task(task_id, 'We need more iron!', game.player.name)
]]
local Store = require 'expcore.store' --- @dep expcore.store
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
local Global = require 'utils.global' --- @dep utils.global
local Token = require 'utils.token' --- @dep utils.token
--- Stores all data for the warp gui
local TaskData = Datastore.connect('TaskData')
TaskData:set_serializer(function(raw_key) return raw_key.task_id end)
local Tasks = {}
-- Global lookup table for force name to task ids
local force_tasks = {}
local force_tasks = {_uid=1}
Global.register(force_tasks, function(tbl)
force_tasks = tbl
end)
-- Task store is keyed by task id, value is a table
local task_store = Store.register()
Tasks.store = task_store
--- Setters.
-- functions used to created and alter tasks
-- @section setters
@@ -43,8 +42,9 @@ local task_id = Tasks.add_task(game.player.force.name, nil, game.player.name)
]]
function Tasks.add_task(force_name, task_number, player_name, task_message)
-- Get a new task id
local task_id = tostring(Token.uid())
local task_id = tostring(force_tasks._uid)
task_message = task_message or 'New Task'
force_tasks._uid = force_tasks._uid + 1
-- Get the existing tasks for this force
local tasks = force_tasks[force_name]
@@ -67,13 +67,13 @@ function Tasks.add_task(force_name, task_number, player_name, task_message)
end
-- Add the new task to the store
Store.set(task_store, task_id, {
TaskData:set(task_id, {
task_id = task_id,
force_name = force_name,
message = task_message,
last_edit_name = player_name or '<server>',
last_edit_time = game.tick,
curently_editing = editing
currently_editing = editing
})
return task_id
@@ -87,10 +87,10 @@ Tasks.remove_task(task_id)
]]
function Tasks.remove_task(task_id)
local task = Store.get(task_store, task_id)
local task = TaskData:get(task_id)
local force_name = task.force_name
table.remove_element(force_tasks[force_name], task_id)
Store.clear(task_store, task_id)
TaskData:remove(task_id)
end
--[[-- Update the message and last edited information for a task
@@ -103,7 +103,7 @@ Task.update_task(task_id, 'We need more iron!', game.player.name)
]]
function Tasks.update_task(task_id, new_message, player_name)
Store.update(task_store, task_id, function(task)
TaskData:update(task_id, function(_, task)
task.last_edit_name = player_name or '<server>'
task.last_edit_time = game.tick
task.message = new_message
@@ -120,8 +120,8 @@ Tasks.set_editing(task_id, game.player.name, true)
]]
function Tasks.set_editing(task_id, player_name, state)
Store.update(task_store, task_id, function(task)
task.curently_editing[player_name] = state
TaskData:update(task_id, function(_, task)
task.currently_editing[player_name] = state
end)
end
@@ -135,7 +135,7 @@ end)
]]
function Tasks.on_update(handler)
Store.watch(task_store, handler)
TaskData:on_update(handler)
end
--- Getters.
@@ -151,7 +151,7 @@ local task = Tasks.get_task(task_id)
]]
function Tasks.get_task(task_id)
return Store.get(task_store, task_id)
return TaskData:get(task_id)
end
--[[-- Gets all the task ids that a force has
@@ -176,8 +176,8 @@ local editing = Tasks.get_editing(task_id, game.player.name)
]]
function Tasks.get_editing(task_id, player_name)
local task = Store.get(task_store, task_id)
return task.curently_editing[player_name]
local task = TaskData:get(task_id)
return task.currently_editing[player_name]
end
-- Module Return

View File

@@ -21,25 +21,24 @@ Warps.make_warp_tag(warp_id)
]]
local Store = require 'expcore.store' --- @dep expcore.store
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
local Global = require 'utils.global' --- @dep utils.global
local Token = require 'utils.token' --- @dep utils.token
local config = require 'config.gui.warps' --- @dep config.warps
--- Stores all data for the warp system
local WrapData = Datastore.connect('WrapData')
WrapData:set_serializer(function(raw_key) return raw_key.warp_id end)
local Warps = {}
-- Global lookup table for force name to task ids
local force_warps = {}
local force_warps = {_uid=1}
Global.register(force_warps, function(tbl)
force_warps = tbl
end)
-- Warp store is keyed by warp id, value is a table
local warp_store = Store.register()
Warps.store = warp_store
-- When a warp is updated change its chat tag and resort the warp order
Store.watch(warp_store, function(warp, warp_id)
WrapData:on_update(function(warp_id, warp, old_warp)
if warp then
-- Update the map chart tag if there is one
if warp.tag then
@@ -47,7 +46,7 @@ Store.watch(warp_store, function(warp, warp_id)
end
-- Check that the name of the warp has been changed
if warp.name == warp.old_name then return end
if not old_warp or warp.name == old_warp.name then return end
-- Get the names of all the warp points for this force
local force_name = warp.force_name
@@ -56,7 +55,7 @@ Store.watch(warp_store, function(warp, warp_id)
local warp_names = {}
for _, next_warp_id in pairs(warp_ids) do
local next_warp = Store.get(warp_store, next_warp_id)
local next_warp = WrapData:get(next_warp_id)
if next_warp_id ~= spawn_id then
warp_names[next_warp.name..next_warp_id] = next_warp_id
end
@@ -70,9 +69,9 @@ Store.watch(warp_store, function(warp, warp_id)
end
end)
--- Map Intergration.
--- Map Integration.
-- functions used to create and alter warps with in the map
-- @section mapIntergration
-- @section mapIntegration
--[[-- Add or update the chat tag for this warp
@tparam string warp_id the uid of the warp you want the chart tag for
@@ -83,7 +82,7 @@ local tag_added = Warps.make_warp_tag(warp_id)
]]
function Warps.make_warp_tag(warp_id)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
local name = warp.name
local icon = warp.icon
@@ -120,7 +119,7 @@ local removed = Warps.remove_warp_tag(warp_id)
]]
function Warps.remove_warp_tag(warp_id)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
-- Check there is a tag to remove
local tag = warp.tag
@@ -144,7 +143,7 @@ Warps.make_warp_area(warp_id)
]]
function Warps.make_warp_area(warp_id)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
local surface = warp.surface
local position = warp.position
local posx = position.x
@@ -170,7 +169,7 @@ function Warps.make_warp_area(warp_id)
end
surface.set_tiles(base_tiles)
-- Add a tile patern ontop of the base
-- Add a tile pattern on top of the base
local tiles = {}
for _, pos in pairs(config.tiles) do
table.insert(tiles, {name=base_tile, position={pos[1]+posx, pos[2]+posy}})
@@ -199,7 +198,7 @@ Warps.remove_warp_area(warp_id)
]]
function Warps.remove_warp_area(warp_id)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
local position = warp.position
local surface = warp.surface
local radius = config.standard_proximity_radius
@@ -222,7 +221,7 @@ function Warps.remove_warp_area(warp_id)
end
surface.set_tiles(tiles)
-- Remove all the entites that are in the area
-- Remove all the entities that are in the area
local entities = surface.find_entities_filtered{
force='neutral',
area={
@@ -243,7 +242,7 @@ Warps.set_spawn_warp(warp_id, game.player.force)
]]
function Warps.set_spawn_warp(warp_id, force)
-- Check the force owns this warp
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
if warp.force_name ~= force.name then return end
-- Set this warp as the spawn
@@ -267,7 +266,7 @@ Warps.teleport_player(warp_id, game.player)
]]
function Warps.teleport_player(warp_id, player)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
local surface = warp.surface
local position = {
x=warp.position.x+0.5,
@@ -299,7 +298,8 @@ local warp_id = Warps.add_warp(player.force.name, player.surface, player.positio
]]
function Warps.add_warp(force_name, surface, position, player_name, warp_name)
-- Get new warp id
local warp_id = tostring(Token.uid())
local warp_id = tostring(force_warps._uid)
force_warps._uid = force_warps._uid + 1
warp_name = warp_name or 'New warp'
-- Get the existing warps for this force
@@ -319,7 +319,7 @@ function Warps.add_warp(force_name, surface, position, player_name, warp_name)
end
-- Add the new warp to the store
Store.set(warp_store, warp_id, {
WrapData:set(warp_id, {
warp_id = warp_id,
force_name = force_name,
name = warp_name,
@@ -345,11 +345,11 @@ Warps.remove_warp(warp_id)
]]
function Warps.remove_warp(warp_id)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
local force_name = warp.force_name
Warps.remove_warp_tag(warp_id)
Warps.remove_warp_area(warp_id)
Store.clear(warp_store, warp_id)
WrapData:remove(warp_id)
table.remove_element(force_warps[force_name], warp_id)
end
@@ -364,10 +364,9 @@ Warps.update_warp(warp_id, 'My Warp', 'iron-plate', game.player.name)
]]
function Warps.update_warp(warp_id, new_name, new_icon, player_name)
Store.update(warp_store, warp_id, function(warp)
WrapData:update(warp_id, function(_, warp)
warp.last_edit_name = player_name or '<server>'
warp.last_edit_time = game.tick
warp.old_name = warp.name
warp.name = new_name or warp.name
warp.icon = new_icon or warp.icon
end)
@@ -383,7 +382,7 @@ Warps.set_editing(warp_id, game.player.name, true)
]]
function Warps.set_editing(warp_id, player_name, state)
Store.update(warp_store, warp_id, function(warp)
WrapData:update(warp_id, function(_, warp)
warp.currently_editing[player_name] = state
end)
end
@@ -398,7 +397,7 @@ end)
]]
function Warps.on_update(handler)
Store.watch(warp_store, handler)
WrapData:on_update(handler)
end
--- Getters.
@@ -414,7 +413,7 @@ local warp = Warps.get_warp(warp_id)
]]
function Warps.get_warp(warp_id)
return Store.get(warp_store, warp_id)
return WrapData:get(warp_id)
end
--[[-- Gets all the warp ids that a force has
@@ -452,7 +451,7 @@ local editing = Warps.get_editing(warp_id, game.player.name)
]]
function Warps.get_editing(warp_id, player_name)
local warp = Store.get(warp_store, warp_id)
local warp = WrapData:get(warp_id)
return warp.currently_editing[player_name]
end

View File

@@ -89,7 +89,7 @@ if config.DamageDealt then
local stat = Statistics:combine('Kills')
Event.add(defines.events.on_entity_died, function(event)
local character = event.cause -- Check character is valid
if not character.valid or character.type ~= 'character' then return end
if not character or not character.valid or character.type ~= 'character' then return end
local player = character.player -- Check player is valid
if not player.valid or not player.connected then return end
local entity = event.entity -- Check entity is valid

View File

@@ -1,128 +0,0 @@
local Gui = require 'utils.gui' --- @dep utils.gui
local Store = require 'expcore.store' --- @dep utils.global
local Color = require 'utils.color_presets' --- @dep utils.color_presets
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
local dump = Model.dump
local dump_text = Model.dump_text
local concat = table.concat
local Public = {}
local header_name = Gui.uid_name()
local left_panel_name = Gui.uid_name()
local right_panel_name = Gui.uid_name()
local input_text_box_name = Gui.uid_name()
local refresh_name = Gui.uid_name()
Public.name = 'Store'
function Public.show(container)
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
local left_panel_style = left_panel.style
left_panel_style.width = 300
for store_id, file_path in pairs(Store.file_paths) do
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = store_id..' - '..file_path}
Gui.set_data(header, store_id)
end
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
local input_text_box_style = input_text_box.style
input_text_box_style.horizontally_stretchable = true
input_text_box_style.height = 32
input_text_box_style.maximal_width = 1000
local refresh_button =
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
local refresh_button_style = refresh_button.style
refresh_button_style.width = 32
refresh_button_style.height = 32
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
right_panel.read_only = true
right_panel.selectable = true
local right_panel_style = right_panel.style
right_panel_style.vertically_stretchable = true
right_panel_style.horizontally_stretchable = true
right_panel_style.maximal_width = 1000
right_panel_style.maximal_height = 1000
local data = {
right_panel = right_panel,
input_text_box = input_text_box,
selected_header = nil
}
Gui.set_data(input_text_box, data)
Gui.set_data(left_panel, data)
Gui.set_data(refresh_button, data)
end
Gui.on_click(
header_name,
function(event)
local element = event.element
local store_id = Gui.get_data(element)
local left_panel = element.parent.parent
local data = Gui.get_data(left_panel)
local right_panel = data.right_panel
local selected_header = data.selected_header
local input_text_box = data.input_text_box
if selected_header then
selected_header.style.font_color = Color.white
end
element.style.font_color = Color.orange
data.selected_header = element
input_text_box.text = concat {'global.data_store[', store_id, ']'}
input_text_box.style.font_color = Color.black
local content = dump(Store.get(store_id)) or 'nil'
right_panel.text = content
end
)
local function update_dump(text_input, data, player)
local suc, ouput = dump_text(text_input.text, player)
if not suc then
text_input.style.font_color = Color.red
else
text_input.style.font_color = Color.black
data.right_panel.text = ouput
end
end
Gui.on_text_changed(
input_text_box_name,
function(event)
local element = event.element
local data = Gui.get_data(element)
update_dump(element, data, event.player)
end
)
Gui.on_click(
refresh_name,
function(event)
local element = event.element
local data = Gui.get_data(element)
local input_text_box = data.input_text_box
update_dump(input_text_box, data, event.player)
end
)
return Public

View File

@@ -6,7 +6,6 @@ local Public = {}
local pages = {
require 'modules.gui.debug.redmew_global_view',
require 'modules.gui.debug.expcore_datastore_view',
require 'modules.gui.debug.expcore_store_view',
require 'modules.gui.debug.expcore_gui_view',
require 'modules.gui.debug.global_view',
require 'modules.gui.debug.package_view',

View File

@@ -7,25 +7,21 @@
-- luacheck:ignore 211/Colors
local Gui = require 'expcore.gui' --- @dep expcore.gui
local Roles = require 'expcore.roles' --- @dep expcore.roles
local Store = require 'expcore.store' --- @dep expcore.store
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
local Game = require 'utils.game' --- @dep utils.game
local Event = require 'utils.event' --- @dep utils.event
local config = require 'config.gui.player_list_actions' --- @dep config.gui.player_list_actions
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
local format_time = _C.format_time --- @dep expcore.common
-- Stores the name of the player a player has selected
local selected_player_store = Store.register(function(player)
return player.name
end)
-- Stores the current action that a player wants to do
local selected_action_store = Store.register(function(player)
return player.name
end)
--- Stores all data for the warp gui
local PlayerListData = Datastore.connect('PlayerListData')
PlayerListData:set_serializer(Datastore.name_serializer)
local SelectedPlayer = PlayerListData:combine('SelectedPlayer')
local SelectedAction = PlayerListData:combine('SelectedAction')
-- Set the config to use these stores
config.set_store_uids(selected_player_store, selected_action_store)
config.set_datastores(SelectedPlayer, SelectedAction)
--- Button used to open the action bar
-- @element open_action_bar
@@ -43,11 +39,11 @@ Gui.element{
}
:on_click(function(player, element, _)
local selected_player_name = element.parent.name
local old_selected_player_name = Store.get(selected_player_store, player)
local old_selected_player_name = SelectedPlayer:get(player)
if selected_player_name == old_selected_player_name then
Store.clear(selected_player_store, player)
SelectedPlayer:remove(player)
else
Store.set(selected_player_store, player, selected_player_name)
SelectedPlayer:set(player, selected_player_name)
end
end)
@@ -62,8 +58,8 @@ Gui.element{
}
:style(Gui.sprite_style(30, -1, { top_margin = -1, right_margin = -1 }))
:on_click(function(player, _)
Store.clear(selected_player_store, player)
Store.clear(selected_action_store, player)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end)
--- Button used to confirm a reason
@@ -78,11 +74,11 @@ Gui.element{
:style(Gui.sprite_style(30, -1, { left_margin = -2, right_margin = -1 }))
:on_click(function(player, element)
local reason = element.parent.entry.text or 'Non Given'
local action_name = Store.get(selected_action_store, player)
local action_name = SelectedAction:get(player)
local reason_callback = config.buttons[action_name].reason_callback
reason_callback(player, reason)
Store.clear(selected_player_store, player)
Store.clear(selected_action_store, player)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
element.parent.entry.text = ''
end)
@@ -126,12 +122,12 @@ end)
event.player.zoom_to_world(position, 1.75)
else
-- RMB will toggle the settings
local old_selected_player_name = Store.get(selected_player_store, player)
local old_selected_player_name = SelectedPlayer:get(player)
if selected_player_name == old_selected_player_name then
Store.clear(selected_player_store, player)
Store.clear(selected_action_store, player)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
else
Store.set(selected_player_store, player, selected_player_name)
SelectedPlayer:set(player, selected_player_name)
end
end
end)
@@ -174,7 +170,7 @@ end)
--- Updates the visible state of the action bar buttons
local function update_action_bar(element)
local player = Gui.get_player_from_element(element)
local selected_player_name = Store.get(selected_player_store, player)
local selected_player_name = SelectedPlayer:get(player)
if not selected_player_name then
-- Hide the action bar when no player is selected
@@ -185,8 +181,8 @@ local function update_action_bar(element)
if not selected_player.connected then
-- If the player is offline then reest stores
element.visible = false
Store.clear(selected_player_store, player)
Store.clear(selected_action_store, player)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
else
-- Otherwise check what actions the player is allowed to use
@@ -367,10 +363,10 @@ Event.add(defines.events.on_player_left_game, function(event)
local scroll_table = frame.container.scroll.table
remove_player_base(scroll_table, remove_player)
local selected_player_name = Store.get(selected_player_store, player)
local selected_player_name = SelectedPlayer:get(player)
if selected_player_name == remove_player.name then
Store.clear(selected_player_store, player)
Store.clear(selected_action_store, player)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end
end
end)
@@ -393,7 +389,7 @@ Event.add(Roles.events.on_role_assigned, redraw_player_list)
Event.add(Roles.events.on_role_unassigned, redraw_player_list)
--- When the action player is changed the action bar will update
Store.watch(selected_player_store, function(value, player_name)
SelectedPlayer:on_update(function(player_name, selected_player)
local player = Game.get_player_from_any(player_name)
local frame = Gui.get_left_element(player, player_list_container)
local scroll_table = frame.container.scroll.table
@@ -401,7 +397,7 @@ Store.watch(selected_player_store, function(value, player_name)
for _, next_player in pairs(game.connected_players) do
local element = scroll_table[next_player.name][open_action_bar.name]
local style = 'frame_button'
if next_player.name == value then
if next_player.name == selected_player then
style = 'tool_button'
end
element.style = style
@@ -413,20 +409,20 @@ Store.watch(selected_player_store, function(value, player_name)
end)
--- When the action name is changed the reason input will update
Store.watch(selected_action_store, function(value, player_name)
SelectedAction:on_update(function(player_name, selected_action)
local player = Game.get_player_from_any(player_name)
local frame = Gui.get_left_element(player, player_list_container)
local element = frame.container.reason_bar
if value then
if selected_action then
-- if there is a new value then check the player is still online
local selected_player_name = Store.get(selected_player_store, player_name)
local selected_player_name = SelectedPlayer:get(player_name)
local selected_player = Game.get_player_from_any(selected_player_name)
if selected_player.connected then
element.visible = true
else
-- Clear if the player is offline
Store.clear(selected_player_store, player_name)
Store.clear(selected_action_store, player_name)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end
else

View File

@@ -240,7 +240,7 @@ local function update_task(player, task_table, task_id)
-- Update the edit flow
local edit_flow = task_table['edit-'..task_id]
local player_allowed_edit = check_player_permissions(player, task)
local players_editing = table.get_keys(task.curently_editing)
local players_editing = table.get_keys(task.currently_editing)
local edit_task_element = edit_flow[edit_task.name]
local discard_task_element = edit_flow[discard_task.name]
@@ -257,7 +257,7 @@ local function update_task(player, task_table, task_id)
-- Check if the player is was editing and/or currently editing
local task_entry = task_flow[task_editing.name] or task_label(task_flow, task)
local player_was_editing = task_entry.type == 'textfield'
local player_is_editing = task.curently_editing[player.name]
local player_is_editing = task.currently_editing[player.name]
-- Update the task flow
if not player_was_editing and not player_is_editing then
@@ -361,14 +361,14 @@ Gui.left_toolbar_button('utility/not_enough_repair_packs_icon', {'task-list.main
return Roles.player_allowed(player, 'gui/task-list')
end)
--- When a new task is added it will udpate the task list for everyone on that force
Tasks.on_update(function(task, task_id, removed_task)
--- When a new task is added it will update the task list for everyone on that force
Tasks.on_update(function(task_id, task, old_task)
-- Get the force to update, task is nil when removed
local force
if task then
force = game.forces[task.force_name]
else
force = game.forces[removed_task.force_name]
force = game.forces[old_task.force_name]
end
-- Update the task for all the players on the force

View File

@@ -5,33 +5,30 @@
]]
local Gui = require 'expcore.gui' --- @dep expcore.gui
local Store = require 'expcore.store' --- @dep expcore.store
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
local Global = require 'utils.global' --- @dep utils.global
local Event = require 'utils.event' --- @dep utils.event
local Game = require 'utils.game' --- @dep utils.game
local Roles = require 'expcore.roles' --- @dep expcore.roles
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
local config = require 'config.gui.warps' --- @dep config.gui.warps
local Warps = require 'modules.control.warps' --- @dep modules.control.warps
local format_time = _C.format_time --- @dep expcore.common
-- Stores a boolean value indexed by player name
local player_in_range_store = Store.register(function(player)
return player.name
end)
--- Stores all data for the warp gui
local WrapGuiData = Datastore.connect('WrapGuiData')
WrapGuiData:set_serializer(Datastore.name_serializer)
local PlayerInRange = WrapGuiData:combine('PlayerInRange')
PlayerInRange:set_default(false)
local PlayerCooldown = WrapGuiData:combine('PlayerCooldown')
PlayerCooldown:set_default(0)
-- Stores the time remaing for a players warp cooldown
local player_warp_cooldown_store = Store.register(function(player)
return player.name
end)
-- Table that stores a boolean value of weather to keep the warp gui open
--- Table that stores a boolean value of weather to keep the warp gui open
local keep_gui_open = {}
Global.register(keep_gui_open, function(tbl)
keep_gui_open = tbl
end)
-- Styles used for sprite buttons
--- Styles used for sprite buttons
local Styles = {
sprite20 = Gui.sprite_style(20),
sprite22 = Gui.sprite_style(20, nil, { right_margin = -3 }),
@@ -55,7 +52,7 @@ local function check_player_permissions(player, action, warp)
end
end
-- Check player has permisison based on value in the config
-- Check player has permission based on value in the config
local action_config = config[action]
if action_config == 'all' then
return true
@@ -65,7 +62,7 @@ local function check_player_permissions(player, action, warp)
return Roles.player_allowed(player, config['expcore_roles_'..action])
end
-- Return false as all other condidtions have not been met
-- Return false as all other conditions have not been met
return false
end
@@ -187,7 +184,7 @@ Gui.element{
Warps.set_editing(warp_id, player.name)
end)
--- Editing state for a warp, contrins a text field and the two edit buttons
--- Editing state for a warp, contains a text field and the two edit buttons
-- @element warp_editing
warp_editing =
Gui.element(function(event_trigger, parent, warp)
@@ -246,7 +243,7 @@ end)
player.zoom_to_world(position, 1.5)
end)
local update_wrap_buttons
--- Default state for the warp icon, when pressed teleports the player
-- @element warp_icon_button
warp_icon_button =
@@ -269,8 +266,8 @@ end)
-- Reset the warp cooldown if the player does not have unlimited warps
if not check_player_permissions(player, 'bypass_warp_cooldown') then
Store.set(player_warp_cooldown_store, player, config.cooldown_duraction)
Store.trigger(player_in_range_store, player)
PlayerCooldown:set(player, config.cooldown_duration)
update_wrap_buttons(player)
end
end)
@@ -293,15 +290,43 @@ end)
local warp_timer =
Gui.element{
type = 'progressbar',
tooltip = {'warp-list.timer-tooltip', config.cooldown_duraction},
tooltip = {'warp-list.timer-tooltip', config.cooldown_duration},
minimum_value = 0,
maximum_value = config.cooldown_duraction*config.update_smoothing
maximum_value = config.cooldown_duration*config.update_smoothing
}
:style{
horizontally_stretchable = true,
color = Colors.light_blue
}
local warp_list_container
--- Update the warp buttons for a player
function update_wrap_buttons(player, timer, in_range)
-- Get the warp table
local frame = Gui.get_left_element(player, warp_list_container)
local scroll_table = frame.container.scroll.table
-- Check if the buttons should be active
timer = timer or PlayerCooldown:get(player)
in_range = in_range or PlayerInRange:get(player)
local button_disabled = timer > 0 or not in_range
-- Change the enabled state of the warp buttons
local warp_ids = Warps.get_force_warp_ids(player.force.name)
for _, warp_id in pairs(warp_ids) do
local element = scroll_table['icon-'..warp_id][warp_icon_button.name]
if element and element.valid then
element.enabled = not button_disabled
if button_disabled then
element.tooltip = {'warp-list.goto-disabled'}
else
local position = Warps.get_warp(warp_id).position
element.tooltip = {'warp-list.goto-tooltip', position.x, position.y}
end
end
end
end
--- Updates a warp for a player
local function update_warp(player, warp_table, warp_id)
local warp = Warps.get_warp(warp_id)
@@ -358,10 +383,10 @@ local function update_warp(player, warp_table, warp_id)
icon_flow.clear()
local warp_icon_element = warp_icon_button(icon_flow, warp)
local timer = Store.get(player_warp_cooldown_store, player)
local in_range = Store.get(player_in_range_store, player)
local timer = PlayerCooldown:get(player)
local in_range = PlayerInRange:get(player)
local apply_proximity = not check_player_permissions(player, 'bypass_warp_proximity')
if (timer and timer > 0) or (apply_proximity and not in_range) then
if timer > 0 or (apply_proximity and not in_range) then
warp_icon_element.enabled = false
warp_icon_element.tooltip = {'warp-list.goto-disabled'}
end
@@ -381,7 +406,20 @@ end
-- Update all the warps for a player
local function update_all_warps(player, warp_table)
local warp_ids = Warps.get_force_warp_ids(player.force.name)
if #warp_ids > 0 then
warp_table.clear()
for _, warp_id in ipairs(warp_ids) do
update_warp(player, warp_table, warp_id)
end
end
-- Update all warps for all players on a force
local function update_all_wrap_force(force)
local warp_ids = Warps.get_force_warp_ids(force.name)
for _, player in pairs(force.connected_players) do
local frame = Gui.get_left_element(player, warp_list_container)
local warp_table = frame.container.scroll.table
warp_table.clear()
for _, warp_id in ipairs(warp_ids) do
update_warp(player, warp_table, warp_id)
end
@@ -390,7 +428,7 @@ end
--- Main warp list container for the left flow
-- @element warp_list_container
local warp_list_container =
warp_list_container =
Gui.element(function(event_trigger, parent)
-- Draw the internal container
local container = Gui.container(parent, event_trigger, 200)
@@ -399,7 +437,7 @@ Gui.element(function(event_trigger, parent)
local header = Gui.header(
container,
{'warp-list.main-caption'},
{'warp-list.sub-tooltip', config.cooldown_duraction, config.standard_proximity_radius},
{'warp-list.sub-tooltip', config.cooldown_duration, config.standard_proximity_radius},
true
)
@@ -421,16 +459,16 @@ Gui.element(function(event_trigger, parent)
-- Change the progress of the warp timer
local progress = 1
local timer = Store.get(player_warp_cooldown_store, player)
if timer and timer > 0 then
progress = 1 - (timer/config.cooldown_duraction)
local timer = PlayerCooldown:get(player)
if timer > 0 then
progress = 1 - (timer/config.cooldown_duration)
end
warp_timer_element.value = progress
-- Add any existing warps
update_all_warps(player, scroll_table)
-- Return the exteral container
-- Return the external container
return container.parent
end)
:add_to_left_flow()
@@ -446,26 +484,140 @@ end)
end)
--- When the name of a warp is updated this is triggered
Warps.on_update(function(warp, _,removed_warp)
Warps.on_update(function(_, warp, old_warp)
-- Get the force to update, warp is nil when removed
local force
if warp then
force = game.forces[warp.force_name]
update_all_wrap_force(game.forces[warp.force_name])
else
force = game.forces[removed_warp.force_name]
update_all_wrap_force(game.forces[old_warp.force_name])
end
end)
--- When the player leaves or enters range of a warp this is triggered
PlayerInRange:on_update(function(player_name, player_in_range)
local player = game.players[player_name]
-- Change if the frame is visible based on if the player is in range
if not keep_gui_open[player.name] then
Gui.toggle_left_element(player, warp_list_container, player_in_range)
end
-- Update the gui for selected players
local warp_ids = Warps.get_force_warp_ids(force.name)
for _, player in pairs(force.connected_players) do
local frame = Gui.get_left_element(player, warp_list_container)
local scroll_table = frame.container.scroll.table
-- Check if the player requires proximity
if not check_player_permissions(player, 'bypass_warp_proximity') then
update_wrap_buttons(player, nil, player_in_range)
end
end)
-- Update the gui
scroll_table.clear()
for _, next_warp_id in ipairs(warp_ids) do
update_warp(player, scroll_table, next_warp_id)
--- Update the warp cooldown progress bars to match the current cooldown
PlayerCooldown:on_update(function(player_name, player_cooldown)
-- Get the progress bar element
local player = game.players[player_name]
local frame = Gui.get_left_element(player, warp_list_container)
local warp_timer_element = frame.container[warp_timer.name]
-- Set the progress
local progress = 1
if player_cooldown and player_cooldown > 0 then
progress = 1 - (player_cooldown/config.cooldown_duration)
end
warp_timer_element.value = progress
-- Trigger update of buttons if cooldown is now 0
if player_cooldown == 0 then
update_wrap_buttons(player, player_cooldown, nil)
end
end)
--- Handles updating the timer and checking distance from a warp
local r2 = config.standard_proximity_radius^2
local rs2 = config.spawn_proximity_radius^2
local mr2 = config.minimum_distance^2
Event.on_nth_tick(math.floor(60/config.update_smoothing), function()
PlayerCooldown:update_all(function(_, player_cooldown)
if player_cooldown > 0 then return player_cooldown - 1 end
end)
local force_warps = {}
local warps = {}
for _, player in pairs(game.connected_players) do
local was_in_range = PlayerInRange:get(player)
-- Get the ids of all the warps on the players force
local force_name = player.force.name
local warp_ids = force_warps[force_name]
if not warp_ids then
warp_ids = Warps.get_force_warp_ids(force_name)
force_warps[force_name] = warp_ids
end
-- Check if the force has any warps
local closest_warp
local closest_distance
if #warp_ids > 0 then
local surface = player.surface
local pos = player.position
local px, py = pos.x, pos.y
-- Loop over each warp
for _, warp_id in ipairs(warp_ids) do
-- Check if warp id is cached
local warp = warps[warp_id]
if not warp then
warp = Warps.get_warp(warp_id)
warps[warp_id] = warp
end
-- Check if the player is within range
local warp_pos = warp.position
if warp.surface == surface then
local dx, dy = px-warp_pos.x, py-warp_pos.y
local dist = (dx*dx)+(dy*dy)
if closest_distance == nil or dist < closest_distance then
closest_warp = warp
closest_distance = dist
if dist < r2 then break end
end
end
end
-- Check the dist to the closest warp
local in_range = closest_warp.warp_id == warp_ids.spawn and closest_distance < rs2 or closest_distance < r2
if was_in_range and not in_range then
PlayerInRange:set(player, false)
elseif not was_in_range and in_range then
PlayerInRange:set(player, true)
end
-- Change the enabled state of the add warp button
local frame = Gui.get_left_element(player, warp_list_container)
local add_warp_element = frame.container.header.alignment[add_new_warp.name]
local was_able_to_make_warp = add_warp_element.enabled
local can_make_warp = closest_distance > mr2
if can_make_warp and not was_able_to_make_warp then
add_warp_element.enabled = true
add_warp_element.tooltip = {'warp-list.add-tooltip'}
elseif not can_make_warp and was_able_to_make_warp then
add_warp_element.enabled = false
add_warp_element.tooltip = {'warp-list.too-close', closest_warp.name}
end
end
end
end)
--- When a player is created make sure that there is a spawn warp created
Event.add(defines.events.on_player_created, function(event)
-- If the force has no spawn then make a spawn warp
local player = game.players[event.player_index]
local force = player.force
local spawn_id = Warps.get_spawn_warp_id(force.name)
if not spawn_id then
local spawn_position = force.get_spawn_position(player.surface)
spawn_id = Warps.add_warp(force.name, player.surface, spawn_position, nil, 'Spawn')
Warps.set_spawn_warp(spawn_id, force)
Warps.make_warp_tag(spawn_id)
end
end)
@@ -494,163 +646,6 @@ end
Event.add(Roles.events.on_role_assigned, role_update_event)
Event.add(Roles.events.on_role_unassigned, role_update_event)
--- When the player leaves or enters range of a warp this is triggered
Store.watch(player_in_range_store, function(value, player_name)
local player = game.players[player_name]
local force = player.force
-- Change if the frame is visible based on if the player is in range
if not keep_gui_open[player.name] then
Gui.toggle_left_element(player, warp_list_container, value)
end
-- Check if the player requires proximity
if check_player_permissions(player, 'bypass_warp_proximity') then
return
end
-- Get the warp table
local frame = Gui.get_left_element(player, warp_list_container)
local scroll_table = frame.container.scroll.table
-- Check if the buttons should be active
local timer = Store.get(player_warp_cooldown_store, player)
local button_disabled = timer and timer > 0 or not value
-- Change the enabled state of the warp buttons
local warp_ids = Warps.get_force_warp_ids(force.name)
for _, warp_id in pairs(warp_ids) do
local element = scroll_table['icon-'..warp_id][warp_icon_button.name]
if element and element.valid then
element.enabled = not button_disabled
if button_disabled then
element.tooltip = {'warp-list.goto-disabled'}
else
local position = Warps.get_warp(warp_id).position
element.tooltip = {'warp-list.goto-tooltip', position.x, position.y}
end
end
end
end)
--- Update the warp cooldown progress bars to match the store
Store.watch(player_warp_cooldown_store, function(value, player_name, old_value)
if value == old_value then return end
-- Get the progress bar element
local player = game.players[player_name]
local frame = Gui.get_left_element(player, warp_list_container)
local warp_timer_element = frame.container[warp_timer.name]
-- Set the progress
local progress = 1
local timer = Store.get(player_warp_cooldown_store, player)
if timer and timer > 0 then
progress = 1 - (timer/config.cooldown_duraction)
end
warp_timer_element.value = progress
-- Trigger update of buttons if cooldown is now 0
if value == 0 then
Store.trigger(player_in_range_store, player_name)
end
end)
--- Handles updating the timer and checking distance from a warp
local r2 = config.standard_proximity_radius^2
local rs2 = config.spawn_proximity_radius^2
local mr2 = config.minimum_distance^2
Event.on_nth_tick(math.floor(60/config.update_smoothing), function()
Store.map(player_warp_cooldown_store, function(value)
if value > 0 then
return value - 1
end
end)
local force_warps = {}
local warps = {}
for _, player in pairs(game.connected_players) do
local was_in_range = Store.get(player_in_range_store, player)
-- Get the ids of all the warps on the players force
local force_name = player.force.name
local warp_ids = force_warps[force_name]
if not warp_ids then
warp_ids = Warps.get_force_warp_ids(force_name)
force_warps[force_name] = warp_ids
end
-- Check if the force has any warps
local closest_warp
local closest_distance
if #warp_ids > 0 then
local surface = player.surface
local pos = player.position
local px, py = pos.x, pos.y
-- Loop over each warp
for _, warp_id in ipairs(warp_ids) do
-- Check if warp id is chached
local warp = warps[warp_id]
if not warp then
warp = Warps.get_warp(warp_id)
warps[warp_id] = warp
end
-- Check if the player is within range
local warp_pos = warp.position
if warp.surface == surface then
local dx, dy = px-warp_pos.x, py-warp_pos.y
local dist = (dx*dx)+(dy*dy)
if closest_distance == nil or dist < closest_distance then
closest_warp = warp
closest_distance = dist
if dist < r2 then break end
end
end
end
-- Check the dist to the closest warp
local in_range = closest_warp.warp_id == warp_ids.spawn and closest_distance < rs2 or closest_distance < r2
if was_in_range and not in_range then
Store.set(player_in_range_store, player, false)
elseif not was_in_range and in_range then
Store.set(player_in_range_store, player, true)
end
-- Change the enabled state of the add warp button
local frame = Gui.get_left_element(player, warp_list_container)
local add_warp_element = frame.container.header.alignment[add_new_warp.name]
local was_able_to_make_warp = add_warp_element.enabled
local can_make_warp = closest_distance > mr2
if can_make_warp and not was_able_to_make_warp then
add_warp_element.enabled = true
add_warp_element.tooltip = {'warp-list.add-tooltip'}
elseif not can_make_warp and was_able_to_make_warp then
add_warp_element.enabled = false
add_warp_element.tooltip = {'warp-list.too-close', closest_warp.name}
end
end
end
end)
--- When a player is created make sure that there is a spawn warp created
Event.add(defines.events.on_player_created, function(event)
-- If the force has no spawn then make a spawn warp
local player = Game.get_player_by_index(event.player_index)
local force = player.force
local spawn_id = Warps.get_spawn_warp_id(force.name)
if not spawn_id then
local spawn_position = force.get_spawn_position(player.surface)
spawn_id = Warps.add_warp(force.name, player.surface, spawn_position, nil, 'Spawn')
Warps.set_spawn_warp(spawn_id, force)
Store.trigger(Warps.store, spawn_id)
Warps.make_warp_tag(spawn_id)
end
end)
--- When a chart tag is removed or edited make sure it is not one that belongs to a warp
local function maintain_tag(event)
if not event.player_index then return end
@@ -659,8 +654,8 @@ local function maintain_tag(event)
local warp_ids = Warps.get_force_warp_ids(force_name)
for _, warp_id in pairs(warp_ids) do
local warp = Warps.get_warp(warp_id)
local wtag = warp.tag
if not wtag or not wtag.valid or wtag == tag then
local warp_tag = warp.tag
if not warp_tag or not warp_tag.valid or warp_tag == tag then
if event.name == defines.events.on_chart_tag_removed then
warp.tag = nil
end