diff --git a/expcore/store.lua b/expcore/store.lua deleted file mode 100644 index c6353d64..00000000 --- a/expcore/store.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/modules/data/statistics.lua b/modules/data/statistics.lua index fd554b8b..5aa305fa 100644 --- a/modules/data/statistics.lua +++ b/modules/data/statistics.lua @@ -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