diff --git a/config/expcore/roles.lua b/config/expcore/roles.lua index df059559..e28ac848 100644 --- a/config/expcore/roles.lua +++ b/config/expcore/roles.lua @@ -2,6 +2,8 @@ -- @config Roles local Roles = require 'expcore.roles' --- @dep expcore.roles +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local Statistics = PlayerData.Statistics --- Role flags that will run when a player changes roles Roles.define_flag_trigger('is_admin',function(player,state) @@ -166,6 +168,7 @@ Roles.new_role('Partner','Part') 'command/unjail' } +local hours10, hours250 = 10*216000, 250*60 Roles.new_role('Veteran','Vet') :set_permission_group('Trusted') :set_custom_color{r=140,g=120,b=200} @@ -174,8 +177,12 @@ Roles.new_role('Veteran','Vet') 'command/chat-bot', } :set_auto_assign_condition(function(player) - if player.online_time > 10*216000 then + if player.online_time >= hours10 then return true + else + local stats = Statistics:get(player, {}) + local playTime, afkTime, mapCount = stats.Playtime or 0, stats.AfkTime or 0, stats.MapsPlayed or 0 + return playTime - afkTime >= hours250 and mapCount >= 25 end end) @@ -192,6 +199,7 @@ Roles.new_role('Member','Mem') 'command/save-quickbar' } +local hours3, hours15 = 3*216000, 15*60 Roles.new_role('Regular','Reg') :set_permission_group('Standard') :set_custom_color{r=79,g=155,b=163} @@ -204,8 +212,12 @@ Roles.new_role('Regular','Reg') 'standard-decon' } :set_auto_assign_condition(function(player) - if player.online_time > 3*216000 then + if player.online_time >= hours3 then return true + else + local stats = Statistics:get(player, {}) + local playTime, afkTime, mapCount = stats.Playtime or 0, stats.AfkTime or 0, stats.MapsPlayed or 0 + return playTime - afkTime >= hours15 and mapCount >= 5 end end) @@ -238,7 +250,7 @@ local default = Roles.new_role('Guest','') Roles.new_role('Jail') :set_permission_group('Restricted') :set_custom_color{r=50,g=50,b=50} -:set_block_auto_promote(true) +:set_block_auto_assign(true) :disallow(default.allowed) --- System defaults which are required to be set diff --git a/config/statistics.lua b/config/statistics.lua index 856af59d..8693e486 100644 --- a/config/statistics.lua +++ b/config/statistics.lua @@ -3,6 +3,7 @@ local e = defines.events -- order as per lua api as it was easier just to go down the list return { + MapsPlayed = true, --- @setting MapsPlayed If the number of maps which a player has played should be tracked Playtime = true, --- @setting Playtime If playtime is tracked for a player, play time measured in minutes AfkTime = true, --- @setting AfkTime If afk time is tracked for a player, play time measured in minutes, afk is once a player does nothing for 5 minutes DistanceTravelled = true, --- @setting DistanceTravelled If distance Travelled is checked, only counts if not afk @@ -21,7 +22,6 @@ return { ItemsPickedUp = e.on_picked_up_item, TilesBuilt = e.on_player_built_tile, ItemsCrafted = e.on_player_crafted_item, - MapsPlayed = e.on_player_created, DeconstructionPlannerUsed = e.on_player_deconstructed_area, Deaths = e.on_player_died, JoinCount = e.on_player_joined_game, diff --git a/expcore/roles.lua b/expcore/roles.lua index f97771a7..edcc989e 100644 --- a/expcore/roles.lua +++ b/expcore/roles.lua @@ -123,7 +123,8 @@ local Roles = { roles = {}, -- Contains the raw info for the roles, indexed by role name flags = {}, -- Contains functions that run when a flag is added/removed from a player internal = {}, -- Contains all internally accessed roles, such as root, default - players = {} -- Contains the roles that players have + players = {}, -- Contains the roles that players have + auto_assign = {} -- Contains references to all roles which have auto assign conditions }, events = { on_role_assigned = script.generate_event_name(), @@ -807,7 +808,7 @@ end --[[-- Sets an auto assign condition that is checked every 60 seconds, if true is returned then the player will receive the role nb: this is one way, failing false after already gaining the role will not revoke the role -@tparam function callback receives only one param which is player to promote, return true to promote the player +@tparam function callback receives only one param which is player to assign, return true to assign the player @treturn Roles._prototype allows chaining @usage-- Give this role to a user if there are admin, ran every 60 seconds @@ -818,21 +819,33 @@ end) ]] function Roles._prototype:set_auto_assign_condition(callback) _C.error_if_runtime() - self.auto_promote_condition = callback + self.auto_assign_condition = true + Roles.config.auto_assign[self.name] = callback return self end +--[[-- Get the auto assign condition for this role, returns nil if no condition is set +@treturn function The callback which was assigned as the auto assign condition + +@usage-- Give this role to a user if there are admin, ran every 60 seconds +local condition = role:get_auto_assign_condition() + +]] +function Roles._prototype:get_auto_assign_condition() + return Roles.config.auto_assign[self.name] +end + --[[-- Sets the role to not allow players to have auto assign effect them, useful to keep people locked to a role @tparam[opt=true] boolean state when true the players with this role will not be auto assigned to other roles @treturn Roles._prototype allows chaining @usage-- Make a role stop players from being auto assigned to other roles -role:set_block_auto_promote() +role:set_block_auto_assign() ]] -function Roles._prototype:set_block_auto_promote(state) +function Roles._prototype:set_block_auto_assign(state) if state == nil then state = true end - self.block_auto_promote = not not state -- forces a boolean value + self.block_auto_assign = not not state -- forces a boolean value return self end @@ -993,35 +1006,43 @@ local function role_update(event) end end +--- Used internally to test if a player should be auto assigned a role +local function auto_assign(event) + local player = game.players[event.player_index] + local roles = Roles.config.players[player.name] or {} + + local lookup = {} + for _, role in ipairs(roles) do lookup[role] = true end + + local assigns, ctn = {}, 0 + for role, condition in pairs(Roles.config.auto_assign) do + if not lookup[role] then + local success, rtn = pcall(condition, player) + if not success then + log{'expcore-roles.error-log-format-assign', role.name, rtn} + elseif rtn == true then + ctn = ctn + 1 + assigns[ctn] = role + end + end + end + + if ctn > 0 then Roles.assign_player(player, assigns) end +end + --- When a player joined or has a role change then the update is triggered Event.add(Roles.events.on_role_assigned, role_update) Event.add(Roles.events.on_role_unassigned, role_update) Event.add(defines.events.on_player_joined_game, role_update) --- Every 60 seconds the auto promote check is preformed + +--- Every 60 seconds and on join auto role assignment is checked +Event.add(defines.events.on_player_joined_game, auto_assign) Event.on_nth_tick(3600, function() - local promotes = {} - for _, player in pairs(game.connected_players) do - for _, role in ipairs(Roles.config.roles) do - if role.auto_promote_condition then - local success, err = pcall(role.auto_promote_condition, player) - if not success then - log{'expcore-roles.error-log-format-promote', role.name, err} - else - if err == true and not Roles.player_has_role(player, role) then - if promotes[player.name] then - table.insert(promotes[player.name], role.name) - else - promotes[player.name] = {role.name} - end - end - end - end - end - end - for player_name, roles in pairs(promotes) do - Roles.assign_player(player_name, roles) + for _, player in ipairs(game.connected_players) do + auto_assign{ player_index = player.index } end end) + -- Return Roles return Roles \ No newline at end of file diff --git a/locale/en/expcore.cfg b/locale/en/expcore.cfg index e5fc44df..3c3280d6 100644 --- a/locale/en/expcore.cfg +++ b/locale/en/expcore.cfg @@ -25,7 +25,7 @@ command-error-log-format=[ERROR] command/__1__ :: __2__ [expcore-roles] error-log-format-flag=[ERROR] roleFlag/__1__ :: __2__ -error-log-format-promote=[ERROR] rolePromote/__1__ :: __2__ +error-log-format-assign=[ERROR] rolePromote/__1__ :: __2__ game-message-assign=__1__ has been assigned to __2__ by __3__ game-message-unassign=__1__ has been unassigned from __2__ by __3__ reject-role=Invalid Role Name. diff --git a/modules/data/statistics.lua b/modules/data/statistics.lua index 709610fb..da0071c6 100644 --- a/modules/data/statistics.lua +++ b/modules/data/statistics.lua @@ -1,10 +1,17 @@ local Event = require 'utils.event' ---@dep utils.event +local Global = require 'utils.global' ---@dep utils.global local config = require 'config.statistics' ---@dep config.statistics local format_time = _C.format_time local floor = math.floor local afk_required = 5*3600 -- 5 minutes +--- Stores players who have been created, required to avoid loss of data +local new_players = {} +Global.register(new_players, function(tbl) + new_players = tbl +end) + --- Stores the statistics on a player local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data local AllPlayerData = PlayerData.All @@ -18,6 +25,8 @@ Statistics:on_load(function(player_name, player_statistics) local existing_data = AllPlayerData:get(player_name) if existing_data and existing_data.valid then return end local counters = config.counters + + -- Merge all data from before you data loaded for key, value in pairs(Statistics:get(player_name, {})) do if config[key] or counters[key] then if not player_statistics[key] then @@ -27,6 +36,14 @@ Statistics:on_load(function(player_name, player_statistics) end end end + + -- Increment your maps played if this is your first time on this map + if new_players[player_name] then + new_players[player_name] = nil + local ctn = player_statistics.MapsPlayed + player_statistics.MapsPlayed = ctn and ctn + 1 or 1 + end + return player_statistics end) @@ -39,6 +56,15 @@ local function format_minutes(value) }) end +--- Add MapsPlayed if it is enabled +if config.MapsPlayed then + Statistics:combine('MapsPlayed') + Event.add(defines.events.on_player_created, function(event) + local player = game.players[event.player_index] + new_players[player.name] = true + end) +end + --- Add Playtime and AfkTime if it is enabled if config.Playtime or config.AfkTime then local playtime, afk_time