Move files to exp_legacy
This commit is contained in:
120
exp_legacy/module/modules/control/jail.lua
Normal file
120
exp_legacy/module/modules/control/jail.lua
Normal file
@@ -0,0 +1,120 @@
|
||||
--[[-- Control Module - Jail
|
||||
- Adds a way to jail players.
|
||||
@control Jail
|
||||
@alias Jail
|
||||
|
||||
@usage
|
||||
-- import the module from the control modules
|
||||
local Jail = require 'modules.control.jail' --- @dep modules.control.jail
|
||||
|
||||
-- This will move 'MrBiter' to the jail role and remove all other roles from them
|
||||
-- the player name and reason are only so they can be included in the event for user feedback
|
||||
Jail.jail_player('MrBiter', 'Cooldude2606', 'Likes biters too much')
|
||||
|
||||
-- This will give 'MrBiter' all his roles back and remove him from jail
|
||||
-- again as above the player name is only used in the event for user feedback
|
||||
Jail.unjail_player('MrBiter', 'Cooldude2606')
|
||||
]]
|
||||
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Game = require 'utils.game' --- @dep utils.game
|
||||
|
||||
local valid_player = Game.get_player_from_any
|
||||
local assign_roles = Roles.assign_player
|
||||
local unassign_roles = Roles.unassign_player
|
||||
local has_role = Roles.player_has_role
|
||||
local get_roles = Roles.get_player_roles
|
||||
|
||||
local Jail = {
|
||||
old_roles = {},
|
||||
events = {
|
||||
--- When a player is assigned to jail
|
||||
-- @event on_player_jailed
|
||||
-- @tparam number player_index the index of the player who was jailed
|
||||
-- @tparam string by_player_name the name of the player who jailed the other player
|
||||
-- @tparam string reason the reason that the player was jailed
|
||||
on_player_jailed=script.generate_event_name(),
|
||||
--- When a player is unassigned from jail
|
||||
-- @event on_player_unjailed
|
||||
-- @tparam number player_index the index of the player who was unjailed
|
||||
-- @tparam string by_player_name the name of the player who unjailed the other player
|
||||
on_player_unjailed=script.generate_event_name(),
|
||||
}
|
||||
}
|
||||
|
||||
--- Used to emit the jail related events
|
||||
-- @tparam number event the name of the event that will be emited
|
||||
-- @tparam LuaPlayer player the player who is being acted on
|
||||
-- @tparam string by_player_name the player who is doing the action
|
||||
-- @tparam string reason the reason for the action (jail)
|
||||
local function event_emit(event, player, by_player_name, reason)
|
||||
script.raise_event(event, {
|
||||
name=event,
|
||||
tick=game.tick,
|
||||
player_index=player.index,
|
||||
by_player_name=by_player_name,
|
||||
reason=reason
|
||||
})
|
||||
end
|
||||
|
||||
--- Jail.
|
||||
-- Functions related to jail
|
||||
-- @section jail-functions
|
||||
|
||||
--- Checks if the player is currently in jail
|
||||
-- @tparam LuaPlayer player the player to check if they are in jail
|
||||
-- @treturn boolean whether the player is currently in jail
|
||||
function Jail.is_jailed(player)
|
||||
return has_role(player, 'Jail')
|
||||
end
|
||||
|
||||
--- Moves a player to jail and removes all other roles
|
||||
-- @tparam LuaPlayer player the player who will be jailed
|
||||
-- @tparam string by_player_name the name of the player who is doing the jailing
|
||||
-- @tparam[opt='Non given.'] string reason the reason that the player is being jailed
|
||||
-- @treturn boolean wheather the user was jailed successfully
|
||||
function Jail.jail_player(player, by_player_name, reason)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
if not by_player_name then return end
|
||||
|
||||
reason = reason or 'Non given.'
|
||||
|
||||
if has_role(player, 'Jail') then return end
|
||||
local roles = get_roles(player)
|
||||
|
||||
player.walking_state = { walking = false, direction = player.walking_state.direction }
|
||||
player.riding_state = { acceleration = defines.riding.acceleration.nothing, direction = player.riding_state.direction }
|
||||
player.mining_state = { mining = false }
|
||||
player.shooting_state = { state = defines.shooting.not_shooting, position = player.shooting_state.position }
|
||||
player.picking_state = false
|
||||
player.repair_state = { repairing = false, position = player.repair_state.position }
|
||||
|
||||
unassign_roles(player, roles, by_player_name, nil, true)
|
||||
assign_roles(player, 'Jail', by_player_name, nil, true)
|
||||
assign_roles(player, roles, by_player_name, nil, true)
|
||||
|
||||
event_emit(Jail.events.on_player_jailed, player, by_player_name, reason)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Moves a player out of jail and restores all roles previously removed
|
||||
-- @tparam LuaPlayer player the player that will be unjailed
|
||||
-- @tparam string by_player_name the name of the player that is doing the unjail
|
||||
-- @treturn boolean whether the player was unjailed successfully
|
||||
function Jail.unjail_player(player, by_player_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
if not by_player_name then return end
|
||||
|
||||
if not has_role(player, 'Jail') then return end
|
||||
|
||||
unassign_roles(player, 'Jail', by_player_name, nil, true)
|
||||
|
||||
event_emit(Jail.events.on_player_unjailed, player, by_player_name)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return Jail
|
||||
233
exp_legacy/module/modules/control/production.lua
Normal file
233
exp_legacy/module/modules/control/production.lua
Normal file
@@ -0,0 +1,233 @@
|
||||
--[[-- Control Module - Production
|
||||
- Common functions used to track production of items
|
||||
@control Production
|
||||
@alias Production
|
||||
|
||||
@usage
|
||||
-- import the module from the control modules
|
||||
local Production = require 'modules.control.production' --- @dep modules.control.production
|
||||
|
||||
-- This will return the less precise index from the one given
|
||||
-- this means that one_second will return one_minute or ten_hours will return fifty_hours
|
||||
-- the other precision work like wise
|
||||
Production.precision_up(defines.flow_precision_index.one_second)
|
||||
|
||||
-- The get production function is used to get production, consumion and net
|
||||
-- it may be used for any item and with any precision level, use total for total
|
||||
Production.get_production(game.forces.player, 'iron-plate', defines.flow_precision_index.one_minute)
|
||||
|
||||
-- The fluctuations works by compearing recent production with the average over time
|
||||
-- again any precision may be used, apart from one_thousand_hours as there would be no valid average
|
||||
Production.get_fluctuations(game.forces.player, 'iron-plate', defines.flow_precision_index.one_minute)
|
||||
|
||||
-- ETA is calculated based on what function you use but all share a similar method
|
||||
-- for production eta it will take current production average given by the precision
|
||||
-- and work out how many ticks it will require to make the required amount (1000 by default)
|
||||
Production.get_production_eta(game.forces.player, 'iron-plate', defines.flow_precision_index.one_minute, 250000)
|
||||
|
||||
-- Both get_color and format_number are helper functions to help format production stats
|
||||
-- get_color will return green, orange, red, or grey based on the active_value
|
||||
-- the passive_value is used when active_value is 0 and can only return orange, red, or grey
|
||||
Production.get_color(clamp, active_value, passive_value)
|
||||
|
||||
]]
|
||||
|
||||
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
|
||||
local precision_index = defines.flow_precision_index
|
||||
local Production = {}
|
||||
|
||||
--- Precision.
|
||||
-- Functions which are used to do basic things
|
||||
-- @section precision
|
||||
|
||||
--- Gets the next lesser precision index value, eg 5 seconds -> 1 minute
|
||||
-- @tparam defines.flow_precision_index precision
|
||||
-- @treturn[1] defines.flow_precision_index the next precision value
|
||||
-- @treturn[1] number the multiplicive difference between the values
|
||||
function Production.precision_up(precision)
|
||||
if precision == precision_index.five_seconds then return precision_index.one_minute, 60
|
||||
elseif precision == precision_index.one_minute then return precision_index.ten_minutes, 10
|
||||
elseif precision == precision_index.ten_minutes then return precision_index.one_hour, 6
|
||||
elseif precision == precision_index.one_hour then return precision_index.ten_hours, 10
|
||||
elseif precision == precision_index.ten_hours then return precision_index.fifty_hours, 5
|
||||
elseif precision == precision_index.fifty_hours then return precision_index.two_hundred_fifty_hours, 5
|
||||
elseif precision == precision_index.two_hundred_fifty_hours then return precision_index.one_thousand_hours, 4
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the next greater precision index value, eg 1 minute -> 5 seconds
|
||||
-- @tparam defines.flow_precision_index precision
|
||||
-- @treturn[1] defines.flow_precision_index the next precision value
|
||||
-- @treturn[1] number the multiplicive difference between the values
|
||||
function Production.precision_down(precision)
|
||||
if precision == precision_index.one_minute then return precision_index.five_seconds, 60
|
||||
elseif precision == precision_index.ten_minutes then return precision_index.one_minute, 10
|
||||
elseif precision == precision_index.one_hour then return precision_index.ten_minutes, 6
|
||||
elseif precision == precision_index.ten_hours then return precision_index.one_hour, 10
|
||||
elseif precision == precision_index.fifty_hours then return precision_index.ten_hours, 5
|
||||
elseif precision == precision_index.two_hundred_fifty_hours then return precision_index.fifty_hours, 5
|
||||
elseif precision == precision_index.one_thousand_hours then return precision_index.two_hundred_fifty_hours, 4
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the number of tick that precision is given over, eg 1 minute -> 3600 ticks
|
||||
-- @tparam defines.flow_precision_index precision
|
||||
-- @treturn number the number of ticks in this time
|
||||
function Production.precision_ticks(precision)
|
||||
if precision == precision_index.five_seconds then return 300
|
||||
elseif precision == precision_index.one_minute then return 3600
|
||||
elseif precision == precision_index.ten_minutes then return 36000
|
||||
elseif precision == precision_index.one_hour then return 216000
|
||||
elseif precision == precision_index.ten_hours then return 2160000
|
||||
elseif precision == precision_index.fifty_hours then return 10800000
|
||||
elseif precision == precision_index.two_hundred_fifty_hours then return 54000000
|
||||
elseif precision == precision_index.one_thousand_hours then return 216000000
|
||||
end
|
||||
end
|
||||
|
||||
--- Statistics.
|
||||
-- Functions used to get information about production
|
||||
-- @section stats
|
||||
|
||||
--- Returns the production data for the whole game time
|
||||
-- @tparam LuaForce force the force to get the data for
|
||||
-- @tparam string item_name the name of the item that you want the data about
|
||||
-- @treturn table contains total made, used and net
|
||||
function Production.get_production_total(force, item_name)
|
||||
local stats = force.item_production_statistics
|
||||
local made = stats.get_input_count(item_name) or 0
|
||||
local used = stats.get_output_count(item_name) or 0
|
||||
|
||||
return {
|
||||
made=made,
|
||||
used=used,
|
||||
net=made-used
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
--- Returns the production data for the given precision game time
|
||||
-- @tparam LuaForce force the force to get the data for
|
||||
-- @tparam string item_name the name of the item that you want the data about
|
||||
-- @tparam defines.flow_precision_index precision the precision that you want the data given to
|
||||
-- @treturn table contains made, used and net
|
||||
function Production.get_production(force, item_name, precision)
|
||||
local stats = force.item_production_statistics.get_flow_count
|
||||
local made = stats{name=item_name, input=true, precision_index=precision} or 0
|
||||
local used = stats{name=item_name, input=false, precision_index=precision} or 0
|
||||
|
||||
return {
|
||||
made=made,
|
||||
used=used,
|
||||
net=made-used
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
--- Returns the current fluctuation from the average
|
||||
-- @tparam LuaForce force the force to get the data for
|
||||
-- @tparam string item_name the name of the item that you want the data about
|
||||
-- @tparam defines.flow_precision_index precision the precision that you want the data given to
|
||||
-- @treturn table contains made, used and net
|
||||
function Production.get_fluctuations(force, item_name, precision)
|
||||
local precision_up = Production.precision_up(precision)
|
||||
local current = Production.get_production(force, item_name, precision)
|
||||
local previous = Production.get_production(force, item_name, precision_up)
|
||||
|
||||
return {
|
||||
made=(current.made/previous.made)-1,
|
||||
used=(current.used/previous.used)-1,
|
||||
net=(current.net/previous.net)-1,
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
--- Returns the amount of ticks required to produce a certain amount
|
||||
-- @tparam LuaForce force the force to get the data for
|
||||
-- @tparam string item_name the name of the item that you want the data about
|
||||
-- @tparam defines.flow_precision_index precision the precision that you want the data given to
|
||||
-- @tparam[opt=1000] number required the number of items that are required to be made
|
||||
-- @treturn number the number of ticks required to produce this ammount of items
|
||||
function Production.get_production_eta(force, item_name, precision, required)
|
||||
required = required or 1000
|
||||
local ticks = Production.precision_ticks(precision)
|
||||
local production = Production.get_production(force, item_name, precision)
|
||||
return production.made == 0 and -1 or ticks*required/production.made
|
||||
end
|
||||
|
||||
--- Returns the amount of ticks required to consume a certain amount
|
||||
-- @tparam LuaForce force the force to get the data for
|
||||
-- @tparam string item_name the name of the item that you want the data about
|
||||
-- @tparam defines.flow_precision_index precision the precision that you want the data given to
|
||||
-- @tparam[opt=1000] number required the number of items that are required to be consumed
|
||||
-- @treturn number the number of ticks required to consume this ammount of items
|
||||
function Production.get_consumsion_eta(force, item_name, precision, required)
|
||||
required = required or 1000
|
||||
local ticks = Production.precision_ticks(precision)
|
||||
local production = Production.get_production(force, item_name, precision)
|
||||
return production.used == 0 and -1 or ticks*required/production.used
|
||||
end
|
||||
|
||||
--- Returns the amount of ticks required to produce but not consume a certain amount
|
||||
-- @tparam LuaForce force the force to get the data for
|
||||
-- @tparam string item_name the name of the item that you want the data about
|
||||
-- @tparam defines.flow_precision_index precision the precision that you want the data given to
|
||||
-- @tparam[opt=1000] number required the number of items that are required to be made but not used
|
||||
-- @treturn number the number of ticks required to produce, but not use, this ammount of items
|
||||
function Production.get_net_eta(force, item_name, precision, required)
|
||||
required = required or 1000
|
||||
local ticks = Production.precision_ticks(precision)
|
||||
local production = Production.get_production(force, item_name, precision)
|
||||
return production.net == 0 and -1 or ticks*required/production.net
|
||||
end
|
||||
|
||||
--- Formating.
|
||||
-- Functions used to format production values
|
||||
-- @section formating
|
||||
|
||||
--- Returns a color value based on the value that was given
|
||||
-- @tparam number cutoff value which separates the different colours
|
||||
-- @tparam number active_value first value tested, tested against cutoff
|
||||
-- @tparam number passive_value second value tested, tested against 0 when active is 0
|
||||
-- @treturn table contains r,g,b keys
|
||||
function Production.get_color(cutoff, active_value, passive_value)
|
||||
if active_value > cutoff then
|
||||
return Colors.light_green
|
||||
elseif active_value < -cutoff then
|
||||
return Colors.indian_red
|
||||
elseif active_value ~= 0 then
|
||||
return Colors.orange
|
||||
elseif passive_value and passive_value > 0 then
|
||||
return Colors.orange
|
||||
elseif passive_value and passive_value < 0 then
|
||||
return Colors.indian_red
|
||||
else
|
||||
return Colors.grey
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns three parts used to format a number
|
||||
-- @tparam number value the value to format
|
||||
-- @treturn[1] string the sign for the number
|
||||
-- @treturn[1] string the surfix for any unit used
|
||||
function Production.format_number(value)
|
||||
local rtn = format_number(math.round(value, 1), true)
|
||||
local surfix = rtn:sub(-1)
|
||||
|
||||
if value > 0 then
|
||||
rtn = '+'..rtn
|
||||
elseif value == 0 and rtn:sub(1, 1) == '-' then
|
||||
rtn = rtn:sub(2)
|
||||
end
|
||||
|
||||
if not tonumber(surfix) then
|
||||
return surfix, rtn:sub(1, -2)
|
||||
else
|
||||
return '', rtn
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Production
|
||||
206
exp_legacy/module/modules/control/protection.lua
Normal file
206
exp_legacy/module/modules/control/protection.lua
Normal file
@@ -0,0 +1,206 @@
|
||||
--[[-- Control Module - Protection
|
||||
- Controls protected entities
|
||||
@control Protection
|
||||
@alias Protection
|
||||
]]
|
||||
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.protection' --- @dep config.protection
|
||||
local EntityProtection = {
|
||||
protected_entity_names = table.deep_copy(config.always_protected_names),
|
||||
protected_entity_types = table.deep_copy(config.always_protected_types),
|
||||
events = {
|
||||
--- When a player mines a protected entity
|
||||
-- @event on_player_mined_protected
|
||||
-- @tparam number player_index the player index of the player who got mined the entity
|
||||
-- @tparam LuaEntity entity the entity which was mined
|
||||
on_player_mined_protected = script.generate_event_name(),
|
||||
--- When a player repeatedly mines protected entities
|
||||
-- @event on_repeat_violation
|
||||
-- @tparam number player_index the player index of the player who got mined the entities
|
||||
-- @tparam LuaEntity entity the last entity which was mined
|
||||
on_repeat_violation = script.generate_event_name(),
|
||||
}
|
||||
}
|
||||
|
||||
-- Convert config tables into lookup tables
|
||||
for _, config_key in ipairs{'always_protected_names', 'always_protected_types', 'always_trigger_repeat_names', 'always_trigger_repeat_types'} do
|
||||
local tbl = config[config_key]
|
||||
for key, value in ipairs(tbl) do
|
||||
tbl[key] = nil
|
||||
tbl[value] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Require roles if a permission is assigned in the config
|
||||
local Roles
|
||||
if config.ignore_permission then
|
||||
Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
end
|
||||
|
||||
----- Global Variables -----
|
||||
--- Variables stored in the global table
|
||||
|
||||
local protected_entities = {} -- All entities which are protected
|
||||
local protected_areas = {} -- All areas which are protected
|
||||
local repeats = {} -- Stores repeat removals by players
|
||||
|
||||
Global.register({
|
||||
protected_entities = protected_entities,
|
||||
protected_areas = protected_areas,
|
||||
repeats = repeats
|
||||
}, function(tbl)
|
||||
protected_entities = tbl.protected_entities
|
||||
protected_areas = tbl.protected_areas
|
||||
repeats = tbl.repeats
|
||||
end)
|
||||
|
||||
----- Local Functions -----
|
||||
--- Functions used internally to search and add to the protected array
|
||||
|
||||
--- Get the key used in protected_entities
|
||||
local function get_entity_key(entity)
|
||||
return string.format('%i,%i', math.floor(entity.position.x), math.floor(entity.position.y))
|
||||
end
|
||||
|
||||
--- Get the key used in protected_areas
|
||||
local function get_area_key(area)
|
||||
return string.format('%i,%i', math.floor(area.left_top.x), math.floor(area.left_top.y))
|
||||
end
|
||||
|
||||
--- Check if an entity is always protected
|
||||
local function check_always_protected(entity)
|
||||
return config.always_protected_names[entity.name] or config.always_protected_types[entity.type] or false
|
||||
end
|
||||
|
||||
--- Check if an entity always triggers repeat protection
|
||||
local function check_always_trigger_repeat(entity)
|
||||
return config.always_trigger_repeat_names[entity.name] or config.always_trigger_repeat_types[entity.type] or false
|
||||
end
|
||||
|
||||
----- Public Functions -----
|
||||
--- Functions used to add and remove protected entities
|
||||
|
||||
--- Add an entity to the protected list
|
||||
function EntityProtection.add_entity(entity)
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then
|
||||
entities = {}
|
||||
protected_entities[entity.surface.index] = entities
|
||||
end
|
||||
entities[get_entity_key(entity)] = entity
|
||||
end
|
||||
|
||||
--- Remove an entity from the protected list
|
||||
function EntityProtection.remove_entity(entity)
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then return end
|
||||
entities[get_entity_key(entity)] = nil
|
||||
end
|
||||
|
||||
--- Get all protected entities on a surface
|
||||
function EntityProtection.get_entities(surface)
|
||||
return protected_entities[surface.index] or {}
|
||||
end
|
||||
|
||||
--- Check if an entity is protected
|
||||
function EntityProtection.is_entity_protected(entity)
|
||||
if check_always_protected(entity) then return true end
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then return false end
|
||||
return entities[get_entity_key(entity)] == entity
|
||||
end
|
||||
|
||||
--- Add an area to the protected list
|
||||
function EntityProtection.add_area(surface, area)
|
||||
local areas = protected_areas[surface.index]
|
||||
if not areas then
|
||||
areas = {}
|
||||
protected_areas[surface.index] = areas
|
||||
end
|
||||
areas[get_area_key(area)] = area
|
||||
end
|
||||
|
||||
--- Remove an area from the protected list
|
||||
function EntityProtection.remove_area(surface, area)
|
||||
local areas = protected_areas[surface.index]
|
||||
if not areas then return end
|
||||
areas[get_area_key(area)] = nil
|
||||
end
|
||||
|
||||
--- Get all protected areas on a surface
|
||||
function EntityProtection.get_areas(surface)
|
||||
return protected_areas[surface.index] or {}
|
||||
end
|
||||
|
||||
--- Check if an entity is protected
|
||||
function EntityProtection.is_position_protected(surface, position)
|
||||
local areas = protected_areas[surface.index]
|
||||
if not areas then return false end
|
||||
for _, area in pairs(areas) do
|
||||
if area.left_top.x <= position.x and area.left_top.y <= position.y
|
||||
and area.right_bottom.x >= position.x and area.right_bottom.y >= position.y
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
----- Events -----
|
||||
--- All events registered by this module
|
||||
|
||||
--- Raise events for protected entities
|
||||
Event.add(defines.events.on_pre_player_mined_item, function(event)
|
||||
local entity = event.entity
|
||||
local player = game.get_player(event.player_index)
|
||||
-- Check if the player should be ignored
|
||||
if config.ignore_admins and player.admin then return end
|
||||
if entity.last_user == nil or entity.last_user.index == player.index then return end
|
||||
if config.ignore_permission and Roles.player_allowed(player, config.ignore_permission) then return end
|
||||
|
||||
-- Check if the entity is protected
|
||||
if EntityProtection.is_entity_protected(entity)
|
||||
or EntityProtection.is_position_protected(entity.surface, entity.position)
|
||||
then
|
||||
-- Update repeats
|
||||
local player_repeats = repeats[player.name]
|
||||
if not player_repeats then
|
||||
player_repeats = { last = game.tick, count = 0 }
|
||||
repeats[player.name] = player_repeats
|
||||
end
|
||||
player_repeats.last = game.tick
|
||||
player_repeats.count = player_repeats.count + 1
|
||||
-- Send events
|
||||
event.name = EntityProtection.events.on_player_mined_protected
|
||||
script.raise_event(EntityProtection.events.on_player_mined_protected, event)
|
||||
if check_always_trigger_repeat(entity) or player_repeats.count >= config.repeat_count then
|
||||
player_repeats.count = 0 -- Reset to avoid spamming of events
|
||||
event.name = EntityProtection.events.on_repeat_violation
|
||||
script.raise_event(EntityProtection.events.on_repeat_violation, event)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- Remove old repeats
|
||||
Event.on_nth_tick(config.refresh_rate, function()
|
||||
local old = game.tick - config.repeat_lifetime
|
||||
for player_name, player_repeats in pairs(repeats) do
|
||||
if player_repeats.last <= old then
|
||||
repeats[player_name] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- When an entity is removed remove it from the protection list
|
||||
local function event_remove_entity(event)
|
||||
EntityProtection.remove_entity(event.entity)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_pre_player_mined_item, event_remove_entity)
|
||||
Event.add(defines.events.on_robot_pre_mined, event_remove_entity)
|
||||
Event.add(defines.events.on_entity_died, event_remove_entity)
|
||||
Event.add(defines.events.script_raised_destroy, event_remove_entity)
|
||||
|
||||
return EntityProtection
|
||||
225
exp_legacy/module/modules/control/reports.lua
Normal file
225
exp_legacy/module/modules/control/reports.lua
Normal file
@@ -0,0 +1,225 @@
|
||||
--[[-- Control Module - Reports
|
||||
- Adds a way to report players and store report messages.
|
||||
@control Reports
|
||||
@alias Reports
|
||||
|
||||
@usage
|
||||
-- import the module from the control modules
|
||||
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
|
||||
|
||||
-- This will place a report on "MrBiter" (must be a valid player) the report will have been made
|
||||
-- by "Cooldude2606" (must be the player name) with the reason 'Liking biters too much' this can be
|
||||
-- seen by using Reports.get_report.
|
||||
Reports.report_player('MrBiter', 'Cooldude2606', 'Liking biters too much') -- true
|
||||
|
||||
-- The other get methods can be used to get all the reports on a player or to test if a player is reported.
|
||||
Reports.get_report('MrBiter', 'Cooldude2606') -- 'Liking biters too much'
|
||||
|
||||
-- This will remove the warning on 'MrBiter' (must be a valid player) which was made by 'Cooldude2606'.
|
||||
Reports.remove_report('MrBiter', 'Cooldude2606') -- true
|
||||
|
||||
-- This will remove all the report that have been made against 'MrBiter'. Note that the remove event will
|
||||
-- be triggered once per report issused.
|
||||
Reports.remove_all('MrBiter') -- true
|
||||
|
||||
]]
|
||||
|
||||
local Game = require 'utils.game' --- @dep utils.game
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
|
||||
local valid_player = Game.get_player_from_any
|
||||
|
||||
local Reports = {
|
||||
user_reports={}, -- stores all user reports, global table
|
||||
events = {
|
||||
--- When a player is reported
|
||||
-- @event on_player_reported
|
||||
-- @tparam number player_index the player index of the player who got reported
|
||||
-- @tparam string by_player_name the name of the player who made the report
|
||||
-- @tparam string reason the reason given for the report
|
||||
on_player_reported = script.generate_event_name(),
|
||||
--- When a report is removed from a player
|
||||
-- @event on_report_removed
|
||||
-- @tparam number player_index the player index of the player who has the report removed
|
||||
-- @tparam string reported_by_name the name of the player who made the removed report
|
||||
-- @tparam string removed_by_name the name of the player who removed the report
|
||||
-- @tparam number batch_count the number of reports removed in this batch, always one when not a batch
|
||||
-- @tparam number batch the index of this event in a batch, always one when not a batch
|
||||
on_report_removed = script.generate_event_name()
|
||||
}
|
||||
}
|
||||
|
||||
local user_reports = Reports.user_reports
|
||||
Global.register(user_reports, function(tbl)
|
||||
Reports.user_reports = tbl
|
||||
user_reports = Reports.user_reports
|
||||
end)
|
||||
|
||||
--- Getters.
|
||||
-- Functions used to get information from reports
|
||||
-- @section get-functions
|
||||
|
||||
--- Gets a list of all reports that a player has against them
|
||||
-- @tparam LuaPlayer player the player to get the report for
|
||||
-- @treturn table a list of all reports, key is by player name, value is reason
|
||||
function Reports.get_reports(player)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
return user_reports[player.name] or {}
|
||||
end
|
||||
|
||||
--- Gets a single report against a player given the name of the player who made the report
|
||||
-- @tparam LuaPlayer player the player to get the report for
|
||||
-- @tparam string by_player_name the name of the player who made the report
|
||||
-- @treturn ?string|nil string is the reason that the player was reported, if the player is not reported
|
||||
function Reports.get_report(player, by_player_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
if not by_player_name then return end
|
||||
|
||||
local reports = user_reports[player.name]
|
||||
return reports and reports[by_player_name]
|
||||
end
|
||||
|
||||
--- Checks if a player is reported, option to get if reported by a certain player
|
||||
-- @tparam LuaPlayer player the player to check if reported
|
||||
-- @tparam[opt] string by_player_name when given will check if reported by this player
|
||||
-- @treturn boolean if the player has been reported
|
||||
function Reports.is_reported(player, by_player_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
local reports = user_reports[player.name] or {}
|
||||
if by_player_name then
|
||||
return reports[by_player_name] ~= nil
|
||||
else
|
||||
return table_size(reports) > 0
|
||||
end
|
||||
end
|
||||
|
||||
--- Counts the number of reports that a player has aganist them
|
||||
-- @tparam LuaPlayer player the player to count the reports for
|
||||
-- @tparam[opt] function custom_count when given this function will be used to count the reports
|
||||
-- @treturn number the number of reports that the user has
|
||||
function Reports.count_reports(player, custom_count)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
local reports = user_reports[player.name] or {}
|
||||
if custom_count then
|
||||
local ctn = 0
|
||||
for by_player_name, reason in pairs(reports) do
|
||||
ctn = ctn + custom_count(player, by_player_name, reason)
|
||||
end
|
||||
return ctn
|
||||
else
|
||||
return table_size(reports)
|
||||
end
|
||||
end
|
||||
|
||||
--- Setters.
|
||||
-- Functions used to get information from reports
|
||||
-- @section set-functions
|
||||
|
||||
--- Adds a report to a player, each player can only report another player once
|
||||
-- @tparam LuaPlayer player the player to add the report to
|
||||
-- @tparam string by_player_name the name of the player that is making the report
|
||||
-- @tparam[opt='Non given.'] string reason the reason that the player is being reported
|
||||
-- @treturn boolean whether the report was added successfully
|
||||
function Reports.report_player(player, by_player_name, reason)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
local player_name = player.name
|
||||
|
||||
if reason == nil or not reason:find("%S") then reason = 'No reason given' end
|
||||
|
||||
local reports = user_reports[player_name]
|
||||
if not reports then
|
||||
reports = {}
|
||||
user_reports[player_name] = reports
|
||||
end
|
||||
|
||||
if reports[by_player_name] then
|
||||
return false
|
||||
else
|
||||
reports[by_player_name] = reason
|
||||
end
|
||||
|
||||
script.raise_event(Reports.events.on_player_reported, {
|
||||
name = Reports.events.on_player_reported,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
by_player_name = by_player_name,
|
||||
reason = reason
|
||||
})
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Used to emit the report removed event, own function due to repeated use in Report.remove_all
|
||||
-- @tparam LuaPlayer player the player who is having the report removed from them
|
||||
-- @tparam string reported_by_name the player who had the report
|
||||
-- @tparam string removed_by_name the player who is clearing the report
|
||||
-- @tparam number batch the index of this event in a batch, always one when not a batch
|
||||
-- @tparam number batch_count the number of reports removed in this batch, always one when not a batch
|
||||
local function report_removed_event(player, reported_by_name, removed_by_name, batch, batch_count)
|
||||
script.raise_event(Reports.events.on_report_removed, {
|
||||
name = Reports.events.on_report_removed,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
reported_by_name = reported_by_name,
|
||||
removed_by_name = removed_by_name,
|
||||
batch_count = batch_count or 1,
|
||||
batch = batch or 1
|
||||
})
|
||||
end
|
||||
|
||||
--- Removes a report from a player
|
||||
-- @tparam LuaPlayer player the player to remove the report from
|
||||
-- @tparam string reported_by_name the name of the player that made the report
|
||||
-- @tparam string removed_by_name the name of the player who removed the report
|
||||
-- @treturn boolean whether the report was removed successfully
|
||||
function Reports.remove_report(player, reported_by_name, removed_by_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
local reports = user_reports[player.name]
|
||||
if not reports then
|
||||
return false
|
||||
end
|
||||
|
||||
local reason = reports[reported_by_name]
|
||||
if not reason then
|
||||
return false
|
||||
end
|
||||
|
||||
report_removed_event(player, reported_by_name, removed_by_name)
|
||||
|
||||
reports[reported_by_name] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
--- Removes all reports from a player
|
||||
-- @tparam LuaPlayer player the player to remove the reports from
|
||||
-- @tparam string removed_by_name the name of the player who removed the report
|
||||
-- @treturn boolean whether the reports were removed successfully
|
||||
function Reports.remove_all(player, removed_by_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
local reports = user_reports[player.name]
|
||||
if not reports then
|
||||
return false
|
||||
end
|
||||
local ctn, total = 0, #reports
|
||||
for reported_by_name, _ in pairs(reports) do
|
||||
ctn = ctn + 1
|
||||
report_removed_event(player, reported_by_name, removed_by_name, ctn, total)
|
||||
end
|
||||
|
||||
user_reports[player.name] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
return Reports
|
||||
218
exp_legacy/module/modules/control/rockets.lua
Normal file
218
exp_legacy/module/modules/control/rockets.lua
Normal file
@@ -0,0 +1,218 @@
|
||||
--[[-- Control Module - Rockets
|
||||
- Stores rocket stats for each force.
|
||||
@control Rockets
|
||||
@alias Rockets
|
||||
|
||||
@usage
|
||||
-- import the module from the control modules
|
||||
local Rockets = require 'modules.control.rockets' --- @dep modules.control.rockets
|
||||
|
||||
-- Some basic information is stored for each silo that has been built
|
||||
-- the data includes: the tick it was built, the rockets launched from it and more
|
||||
Rockets.get_silo_data(rocket_silo_entity)
|
||||
|
||||
-- Some information is also stored for each force
|
||||
Rockets.get_stats('player')
|
||||
|
||||
-- You can get the rocket data for all silos for a force by using get_silos
|
||||
Rockets.get_silos('player')
|
||||
|
||||
-- You can get the launch time for a rocket, meaning what game tick the 50th rocket was launched
|
||||
Rockets.get_rocket_time('player', 50)
|
||||
|
||||
-- The rolling average will work out the time to launch one rocket based on the last X rockets
|
||||
Rockets.get_rolling_average('player', 10)
|
||||
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local config = require 'config.gui.rockets' --- @dep config.rockets
|
||||
|
||||
local largest_rolling_avg = 0
|
||||
for _, avg_over in pairs(config.stats.rolling_avg) do
|
||||
if avg_over > largest_rolling_avg then
|
||||
largest_rolling_avg = avg_over
|
||||
end
|
||||
end
|
||||
|
||||
local Rockets = {
|
||||
times = {},
|
||||
stats = {},
|
||||
silos = {}
|
||||
}
|
||||
|
||||
local rocket_times = Rockets.times
|
||||
local rocket_stats = Rockets.stats
|
||||
local rocket_silos = Rockets.silos
|
||||
Global.register({
|
||||
rocket_times = rocket_times,
|
||||
rocket_stats = rocket_stats,
|
||||
rocket_silos = rocket_silos
|
||||
}, function(tbl)
|
||||
Rockets.times = tbl.rocket_times
|
||||
Rockets.stats = tbl.rocket_stats
|
||||
Rockets.silos = tbl.rocket_silos
|
||||
rocket_times = Rockets.times
|
||||
rocket_stats = Rockets.stats
|
||||
rocket_silos = Rockets.silos
|
||||
end)
|
||||
|
||||
--- Gets the silo data for a given silo entity
|
||||
-- @tparam LuaEntity silo the rocket silo entity
|
||||
-- @treturn table the data table for this silo, contains rockets launch, silo status, and its force
|
||||
function Rockets.get_silo_data(silo)
|
||||
local position = silo.position
|
||||
local silo_name = math.floor(position.x)..':'..math.floor(position.y)
|
||||
return rocket_silos[silo_name]
|
||||
end
|
||||
|
||||
--- Gets the silo data for a given silo entity
|
||||
-- @tparam string silo_name the silo name that is stored in its data
|
||||
-- @treturn table the data table for this silo, contains rockets launch, silo status, and its force
|
||||
function Rockets.get_silo_data_by_name(silo_name)
|
||||
return rocket_silos[silo_name]
|
||||
end
|
||||
|
||||
--- Gets the silo entity from its silo name, reverse to get_silo_data
|
||||
-- @tparam string silo_name the silo name that is stored in its data
|
||||
-- @treturn LuaEntity the rocket silo entity
|
||||
function Rockets.get_silo_entity(silo_name)
|
||||
local data = rocket_silos[silo_name]
|
||||
return data.entity
|
||||
end
|
||||
|
||||
--- Gets the rocket stats for a force
|
||||
-- @tparam string force_name the name of the force to get the stats for
|
||||
-- @treturn table the table of stats for the force
|
||||
function Rockets.get_stats(force_name)
|
||||
return rocket_stats[force_name] or {}
|
||||
end
|
||||
|
||||
--- Gets all the rocket silos that belong to a force
|
||||
-- @tparam string force_name the name of the force to get the silos for
|
||||
-- @treturn table an array of silo data that all belong to this force
|
||||
function Rockets.get_silos(force_name)
|
||||
local rtn = {}
|
||||
for _, silo_data in pairs(rocket_silos) do
|
||||
if silo_data.force == force_name then
|
||||
table.insert(rtn, silo_data)
|
||||
end
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Gets the launch time of a given rocket, due to cleaning not all counts are valid
|
||||
-- @tparam string force_name the name of the force to get the count for
|
||||
-- @tparam number rocket_number the number of the rocket to get the launch time for
|
||||
-- @treturn number the game tick that the rocket was lanuched on
|
||||
function Rockets.get_rocket_time(force_name, rocket_number)
|
||||
return rocket_times[force_name] and rocket_times[force_name][rocket_number] or nil
|
||||
end
|
||||
|
||||
--- Gets the number of rockets that a force has launched
|
||||
-- @tparam string force_name the name of the force to get the count for
|
||||
-- @treturn number the number of rockets that the force has launched
|
||||
function Rockets.get_rocket_count(force_name)
|
||||
local force = game.forces[force_name]
|
||||
return force.rockets_launched
|
||||
end
|
||||
|
||||
--- Gets the total number of rockets launched by all forces
|
||||
-- @treturn number the total number of rockets launched this game
|
||||
function Rockets.get_game_rocket_count()
|
||||
local rtn = 0
|
||||
for _, force in pairs(game.forces) do
|
||||
rtn = rtn + force.rockets_launched
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Gets the rolling average time to launch a rocket
|
||||
-- @tparam string force_name the name of the force to get the average for
|
||||
-- @tparam number count the distance to get the rolling average over
|
||||
-- @treturn number the number of ticks required to launch one rocket
|
||||
function Rockets.get_rolling_average(force_name, count)
|
||||
local force = game.forces[force_name]
|
||||
local rocket_count = force.rockets_launched
|
||||
if rocket_count == 0 then return 0 end
|
||||
local last_launch_time = rocket_times[force_name][rocket_count]
|
||||
local start_rocket_time = 0
|
||||
if count < rocket_count then
|
||||
start_rocket_time = rocket_times[force_name][rocket_count-count+1]
|
||||
rocket_count = count
|
||||
end
|
||||
return math.floor((last_launch_time-start_rocket_time)/rocket_count)
|
||||
end
|
||||
|
||||
--- Event used to update the stats and the hui when a rocket is launched
|
||||
Event.add(defines.events.on_rocket_launched, function(event)
|
||||
local entity = event.rocket_silo
|
||||
local silo_data = Rockets.get_silo_data(entity)
|
||||
local force = event.rocket_silo.force
|
||||
local force_name = force.name
|
||||
local rockets_launched = force.rockets_launched
|
||||
|
||||
--- Handles updates to the rocket stats
|
||||
local stats = rocket_stats[force_name]
|
||||
if not stats then
|
||||
rocket_stats[force_name] = {}
|
||||
stats = rocket_stats[force_name]
|
||||
end
|
||||
|
||||
if rockets_launched == 1 then
|
||||
stats.first_launch = event.tick
|
||||
stats.fastest_launch = event.tick
|
||||
elseif event.tick-stats.last_launch < stats.fastest_launch then
|
||||
stats.fastest_launch = event.tick-stats.last_launch
|
||||
end
|
||||
|
||||
stats.last_launch = event.tick
|
||||
|
||||
--- Appends the new rocket into the array
|
||||
if not rocket_times[force_name] then
|
||||
rocket_times[force_name] = {}
|
||||
end
|
||||
|
||||
rocket_times[force_name][rockets_launched] = event.tick
|
||||
|
||||
local remove_rocket = rockets_launched-largest_rolling_avg
|
||||
if remove_rocket > 0 and not table.contains(config.milestones, remove_rocket) then
|
||||
rocket_times[force_name][remove_rocket] = nil
|
||||
end
|
||||
|
||||
--- Adds this 1 to the launch count for this silo
|
||||
silo_data.launched = silo_data.launched+1
|
||||
end)
|
||||
|
||||
--- When a launch is reiggered it will await reset
|
||||
Event.add(defines.events.on_rocket_launch_ordered, function(event)
|
||||
local entity = event.rocket_silo
|
||||
local silo_data = Rockets.get_silo_data(entity)
|
||||
silo_data.awaiting_reset = true
|
||||
end)
|
||||
|
||||
--- Adds a silo to the list when it is built
|
||||
local function on_built(event)
|
||||
local entity = event.created_entity
|
||||
if entity.valid and entity.name == 'rocket-silo' then
|
||||
local force = entity.force
|
||||
local force_name = force.name
|
||||
local position = entity.position
|
||||
local silo_name = math.floor(position.x)..':'..math.floor(position.y)
|
||||
|
||||
rocket_silos[silo_name] = {
|
||||
name=silo_name,
|
||||
force=force_name,
|
||||
entity=entity,
|
||||
launched=0,
|
||||
awaiting_reset=false,
|
||||
built=game.tick
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_built_entity, on_built)
|
||||
Event.add(defines.events.on_robot_built_entity, on_built)
|
||||
|
||||
return Rockets
|
||||
172
exp_legacy/module/modules/control/selection.lua
Normal file
172
exp_legacy/module/modules/control/selection.lua
Normal file
@@ -0,0 +1,172 @@
|
||||
--[[-- Control Module - Selection
|
||||
- Controls players who have a selection planner, mostly event handlers
|
||||
@control Selection
|
||||
@alias Selection
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Selection = {
|
||||
events = {
|
||||
--- When a player enters selection mode
|
||||
-- @event on_player_selection_start
|
||||
-- @tparam number player_index the player index of the player who entered selection mode
|
||||
-- @tparam string selection the name of the selection being made
|
||||
on_player_selection_start = script.generate_event_name(),
|
||||
--- When a player leaves selection mode
|
||||
-- @event on_player_selection_end
|
||||
-- @tparam number player_index the player index of the player who left selection mode
|
||||
-- @tparam string selection the name of the selection which ended
|
||||
on_player_selection_end = script.generate_event_name(),
|
||||
}
|
||||
}
|
||||
|
||||
local selection_tool = { name='selection-tool' }
|
||||
|
||||
local selections = {}
|
||||
Global.register({
|
||||
selections = selections
|
||||
}, function(tbl)
|
||||
selections = tbl.selections
|
||||
end)
|
||||
|
||||
--- Let a player select an area by providing a selection planner
|
||||
-- @tparam LuaPlayer player The player to place into selection mode
|
||||
-- @tparam string selection_name The name of the selection to start, used with on_selection
|
||||
-- @tparam[opt=false] boolean single_use When true the selection will stop after first use
|
||||
function Selection.start(player, selection_name, single_use, ...)
|
||||
if not player or not player.valid then return end
|
||||
if selections[player.index] then
|
||||
-- Raise the end event if a selection was already in progress
|
||||
script.raise_event(Selection.events.on_player_selection_end, {
|
||||
name = Selection.events.on_player_selection_end,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
selection = selections[player.index].name
|
||||
})
|
||||
end
|
||||
|
||||
-- Set the selection data
|
||||
selections[player.index] = {
|
||||
name = selection_name,
|
||||
arguments = { ... },
|
||||
single_use = single_use == true,
|
||||
character = player.character
|
||||
}
|
||||
|
||||
-- Raise the event
|
||||
script.raise_event(Selection.events.on_player_selection_start, {
|
||||
name = Selection.events.on_player_selection_start,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
selection = selection_name
|
||||
})
|
||||
|
||||
-- Give a selection tool if one is not in use
|
||||
if player.cursor_stack.is_selection_tool then return end
|
||||
player.clear_cursor() -- Clear the current item
|
||||
player.cursor_stack.set_stack(selection_tool)
|
||||
|
||||
-- Make a slot to place the selection tool even if inventory is full
|
||||
if not player.character then return end
|
||||
player.character_inventory_slots_bonus = player.character_inventory_slots_bonus + 1
|
||||
player.hand_location = { inventory = defines.inventory.character_main, slot = #player.get_main_inventory() }
|
||||
end
|
||||
|
||||
--- Stop a player selection by removing the selection planner
|
||||
-- @tparam LuaPlayer player The player to exit out of selection mode
|
||||
function Selection.stop(player)
|
||||
if not selections[player.index] then return end
|
||||
local character = selections[player.index].character
|
||||
local selection = selections[player.index].name
|
||||
selections[player.index] = nil
|
||||
|
||||
-- Raise the event
|
||||
script.raise_event(Selection.events.on_player_selection_end, {
|
||||
name = Selection.events.on_player_selection_end,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
selection = selection
|
||||
})
|
||||
|
||||
-- Remove the selection tool
|
||||
if player.cursor_stack.is_selection_tool then
|
||||
player.cursor_stack.clear()
|
||||
else
|
||||
player.remove_item(selection_tool)
|
||||
end
|
||||
|
||||
-- Remove the extra slot
|
||||
if character and character == player.character then
|
||||
player.character_inventory_slots_bonus = player.character_inventory_slots_bonus - 1
|
||||
player.hand_location = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the selection arguments for a player
|
||||
-- @tparam LuaPlayer player The player to get the selection arguments for
|
||||
function Selection.get_arguments(player)
|
||||
if not selections[player.index] then return end
|
||||
return selections[player.index].arguments
|
||||
end
|
||||
|
||||
--- Test if a player is selecting something
|
||||
-- @tparam LuaPlayer player The player to test
|
||||
-- @tparam[opt] string selection_name If given will only return true if the selection is this selection
|
||||
function Selection.is_selecting(player, selection_name)
|
||||
if selection_name ~= nil then
|
||||
if not selections[player.index] then return false end
|
||||
return selections[player.index].name == selection_name
|
||||
else
|
||||
return player.cursor_stack.is_selection_tool
|
||||
end
|
||||
end
|
||||
|
||||
--- Filter on_player_selected_area to this custom selection, appends the selection arguments
|
||||
-- @tparam string selection_name The name of the selection to listen for
|
||||
-- @tparam function handler The event handler
|
||||
function Selection.on_selection(selection_name, handler)
|
||||
Event.add(defines.events.on_player_selected_area, function(event)
|
||||
local selection = selections[event.player_index]
|
||||
if not selection or selection.name ~= selection_name then return end
|
||||
handler(event, table.unpack(selection.arguments))
|
||||
end)
|
||||
end
|
||||
|
||||
--- Filter on_player_alt_selected_area to this custom selection, appends the selection arguments
|
||||
-- @param string selection_name The name of the selection to listen for
|
||||
-- @param function handler The event handler
|
||||
function Selection.on_alt_selection(selection_name, handler)
|
||||
Event.add(defines.events.on_player_alt_selected_area, function(event)
|
||||
local selection = selections[event.player_index]
|
||||
if not selection or selection.name ~= selection_name then return end
|
||||
handler(event, table.unpack(selection.arguments))
|
||||
end)
|
||||
end
|
||||
|
||||
--- Stop selection if the selection tool is removed from the cursor
|
||||
Event.add(defines.events.on_player_cursor_stack_changed, function(event)
|
||||
local player = game.get_player(event.player_index) ---@cast player -nil
|
||||
if player.cursor_stack.is_selection_tool then return end
|
||||
Selection.stop(player)
|
||||
end)
|
||||
--- Stop selection after an event such as death or leaving the game
|
||||
local function stop_after_event(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
Selection.stop(player)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_pre_player_left_game, stop_after_event)
|
||||
Event.add(defines.events.on_pre_player_died, stop_after_event)
|
||||
|
||||
--- Stop selection after a single use if single_use was true during Selection.start
|
||||
local function stop_after_use(event)
|
||||
if not selections[event.player_index] then return end
|
||||
if not selections[event.player_index].single_use then return end
|
||||
stop_after_event(event)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_selected_area, stop_after_use)
|
||||
Event.add(defines.events.on_player_alt_selected_area, stop_after_use)
|
||||
|
||||
return Selection
|
||||
173
exp_legacy/module/modules/control/spectate.lua
Normal file
173
exp_legacy/module/modules/control/spectate.lua
Normal file
@@ -0,0 +1,173 @@
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
|
||||
----- Locals -----
|
||||
local follow_label -- Gui constructor
|
||||
local following = {}
|
||||
local spectating = {}
|
||||
local Public = {}
|
||||
|
||||
----- Global data -----
|
||||
Global.register({
|
||||
following = following,
|
||||
spectating = spectating
|
||||
}, function(tbl)
|
||||
following = tbl.following
|
||||
spectating = tbl.spectating
|
||||
end)
|
||||
|
||||
----- Public Functions -----
|
||||
|
||||
--- Test if a player is in spectator mode
|
||||
-- @tparam LuaPlayer player The player to test the controller type of
|
||||
-- @treturn boolean Returns true if the player is in spectator mode
|
||||
function Public.is_spectating(player)
|
||||
assert(player and player.valid, 'Invalid player given to follower')
|
||||
return player.controller_type == defines.controllers.spectator
|
||||
end
|
||||
|
||||
--- Puts a player into spectator mode while maintaining an association to their character
|
||||
-- @tparam LuaPlayer player The player that will be placed into spectator mode
|
||||
-- @treturn boolean Returns false if the player was already in spectator mode
|
||||
function Public.start_spectate(player)
|
||||
assert(player and player.valid, 'Invalid player given to follower')
|
||||
if spectating[player.index] or not player.character then return false end
|
||||
local character = player.character
|
||||
local opened = player.opened
|
||||
player.set_controller{ type = defines.controllers.spectator }
|
||||
player.associate_character(character)
|
||||
spectating[player.index] = character
|
||||
if opened then player.opened = opened end -- Maintain opened after controller change
|
||||
return true
|
||||
end
|
||||
|
||||
--- Return a player from spectator mode back to their character, if their character was killed then respawn them
|
||||
-- @tparam LuaPlayer player The player that will leave spectator mode
|
||||
function Public.stop_spectate(player)
|
||||
assert(player and player.valid, 'Invalid player given to follower')
|
||||
local character = spectating[player.index]
|
||||
spectating[player.index] = nil
|
||||
if character and character.valid then
|
||||
local opened = player.opened
|
||||
player.teleport(character.position, character.surface)
|
||||
player.set_controller{ type = defines.controllers.character, character = character }
|
||||
if opened then player.opened = opened end -- Maintain opened after controller change
|
||||
else
|
||||
player.ticks_to_respawn = 300
|
||||
end
|
||||
end
|
||||
|
||||
--- Test if a player is in follow mode
|
||||
-- @tparam LuaPlayer player The player to test the follow mode of
|
||||
-- @treturn boolean Returns true if the player is in follow mode
|
||||
function Public.is_following(player)
|
||||
assert(player and player.valid, 'Invalid player given to follower')
|
||||
return following[player.index] ~= nil
|
||||
end
|
||||
|
||||
--- Puts a player into spectator mode and follows an entity as it moves
|
||||
-- @tparam LuaPlayer player The player that will follow the entity
|
||||
-- @tparam ?LuaPlayer|LuaEntity entity The player or entity that will be followed
|
||||
function Public.start_follow(player, entity)
|
||||
assert(player and player.valid, 'Invalid player given to follower')
|
||||
assert(entity and entity.valid, 'Invalid entity given to follower')
|
||||
local spectate = Public.start_spectate(player)
|
||||
|
||||
player.close_map()
|
||||
follow_label(player.gui.screen, entity)
|
||||
player.teleport(entity.position, entity.surface)
|
||||
following[player.index] = { player, entity, entity.position, spectate }
|
||||
end
|
||||
|
||||
--- Returns camera control to the player, will return a player to their character if start_follow placed them into spectator mode
|
||||
-- @tparam LuaPlayer player The player that will regain control of their camera
|
||||
function Public.stop_follow(player)
|
||||
assert(player and player.valid, 'Invalid player given to follower')
|
||||
if following[player.index] and following[player.index][4] and Public.is_spectating(player) then
|
||||
Public.stop_spectate(player)
|
||||
end
|
||||
|
||||
Gui.destroy_if_valid(player.gui.screen[follow_label.name])
|
||||
following[player.index] = nil
|
||||
end
|
||||
|
||||
--- Returns camera control to all players, will return a player to their character if start_follow placed them into spectator mode
|
||||
function Public.stop_all()
|
||||
for key, data in pairs(following) do
|
||||
Public.stop_follow(data[1])
|
||||
end
|
||||
end
|
||||
|
||||
----- Gui -----
|
||||
|
||||
--- Label used to show that the player is following, also used to allow esc to stop following
|
||||
-- @element follow_label
|
||||
follow_label =
|
||||
Gui.element(function(definition, parent, target)
|
||||
Gui.destroy_if_valid(parent[definition.name])
|
||||
|
||||
local label = parent.add{
|
||||
type = 'label',
|
||||
style = 'heading_1_label',
|
||||
caption = 'Following '..target.name..'.\nClick here or press esc to stop following.',
|
||||
name = definition.name
|
||||
}
|
||||
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local res = player.display_resolution
|
||||
label.location = {0, res.height-150}
|
||||
label.style.width = res.width
|
||||
label.style.horizontal_align = 'center'
|
||||
player.opened = label
|
||||
|
||||
return label
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:on_click(Public.stop_follow)
|
||||
:on_close(function(player)
|
||||
-- Don't call set_controller during on_close as it invalidates the controller
|
||||
-- Setting an invalid position (as to not equal their current) will call stop_follow on the next tick
|
||||
following[player.index][3] = {}
|
||||
end)
|
||||
|
||||
----- Events -----
|
||||
|
||||
--- Updates the location of the player as well as doing some sanity checks
|
||||
-- @tparam LuaPlayer player The player to update the position of
|
||||
-- @tparam ?LuaPlayer|LuaEntity entity The player or entity being followed
|
||||
local function update_player_location(player, entity, old_position)
|
||||
if player.character or not entity.valid then
|
||||
Public.stop_follow(player)
|
||||
elseif player.position.x ~= old_position.x or player.position.y ~= old_position.y then
|
||||
Public.stop_follow(player)
|
||||
else
|
||||
player.teleport(entity.position, entity.surface)
|
||||
end
|
||||
end
|
||||
|
||||
--- Updates the locations of all players currently following something
|
||||
local function update_all()
|
||||
for _, data in pairs(following) do
|
||||
update_player_location(data[1], data[2], data[3])
|
||||
data[3] = data[1].position
|
||||
end
|
||||
end
|
||||
|
||||
-- Update the location of all players each tick
|
||||
Event.add(defines.events.on_tick, update_all)
|
||||
|
||||
-- Check for player leaving
|
||||
Event.add(defines.events.on_pre_player_left_game, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
Public.stop_follow(player)
|
||||
for _, data in pairs(following) do
|
||||
if data[2] == player then
|
||||
Public.stop_follow(data[1])
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
----- Module Return -----
|
||||
return Public
|
||||
176
exp_legacy/module/modules/control/tasks.lua
Normal file
176
exp_legacy/module/modules/control/tasks.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
--[[-- Control Module - Tasks
|
||||
- Stores tasks for each force.
|
||||
@control Tasks
|
||||
@alias Tasks
|
||||
|
||||
@usage-- Making and then editing a new task
|
||||
local task_id = Tasks.add_task(game.player.force.name, nil, game.player.name)
|
||||
|
||||
Tasks.update_task(task_id, 'We need more iron!', game.player.name)
|
||||
|
||||
]]
|
||||
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
|
||||
--- 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 = {_uid=1}
|
||||
Global.register(force_tasks, function(tbl)
|
||||
force_tasks = tbl
|
||||
end)
|
||||
|
||||
--- Setters.
|
||||
-- functions used to created and alter tasks
|
||||
-- @section setters
|
||||
|
||||
--[[-- Add a new task for a force, the task can be placed into a certain position for that force
|
||||
@tparam string force_name the name of the force to add the task for
|
||||
@tparam[opt] string player_name the player who added this task, will cause them to be listed under editing
|
||||
@tparam[opt] string task_title the task title, if not given default is used
|
||||
@tparam[opt] string task_body the task body, if not given default is used
|
||||
@treturn string the uid of the task which was created
|
||||
|
||||
@usage-- Adding a new task for your force
|
||||
local task_id = Tasks.add_task(game.player.force.name, game.player.name, nil, nil)
|
||||
|
||||
]]
|
||||
function Tasks.add_task(force_name, player_name, task_title, task_body)
|
||||
-- Get a new task id
|
||||
local task_id = tostring(force_tasks._uid)
|
||||
force_tasks._uid = force_tasks._uid + 1
|
||||
|
||||
-- Get the existing tasks for this force
|
||||
local task_ids = force_tasks[force_name]
|
||||
if not task_ids then
|
||||
task_ids = {}
|
||||
force_tasks[force_name] = task_ids
|
||||
end
|
||||
|
||||
-- Insert the task id into the forces tasks
|
||||
table.insert(task_ids, task_id)
|
||||
|
||||
-- Add the new task to the store
|
||||
TaskData:set(task_id, {
|
||||
task_id = task_id,
|
||||
force_name = force_name,
|
||||
title = task_title or '',
|
||||
body = task_body or '',
|
||||
last_edit_name = player_name or '<server>',
|
||||
last_edit_time = game.tick,
|
||||
currently_editing = {}
|
||||
})
|
||||
|
||||
return task_id
|
||||
end
|
||||
|
||||
--[[-- Removes a task and any data that is linked with it
|
||||
@tparam string task_id the uid of the task which you want to remove
|
||||
|
||||
@usage-- Removing a task
|
||||
Tasks.remove_task(task_id)
|
||||
|
||||
]]
|
||||
function Tasks.remove_task(task_id)
|
||||
local task = TaskData:get(task_id)
|
||||
local force_name = task.force_name
|
||||
table.remove_element(force_tasks[force_name], task_id)
|
||||
TaskData:remove(task_id)
|
||||
end
|
||||
|
||||
--[[-- Update the message and last edited information for a task
|
||||
@tparam string task_id the uid of the task to update
|
||||
@tparam string player_name the name of the player who made the edit
|
||||
@tparam string task_title the title of the task to update to
|
||||
@tparam string task_body the body of the task to update to
|
||||
|
||||
@usage-- Updating the message for on a task
|
||||
Task.update_task(task_id, game.player.name, 'We need more iron!', 'Build more iron outposts.')
|
||||
|
||||
]]
|
||||
function Tasks.update_task(task_id, player_name, task_title, task_body)
|
||||
TaskData:update(task_id, function(_, task)
|
||||
task.last_edit_name = player_name
|
||||
task.last_edit_time = game.tick
|
||||
task.title = task_title
|
||||
task.body = task_body
|
||||
end)
|
||||
end
|
||||
|
||||
--[[-- Set the editing state for a player, can be used as a warning or to display a text field
|
||||
@tparam string task_id the uid of the task that you want to effect
|
||||
@tparam string player_name the name of the player you want to set the state for
|
||||
@tparam boolean state the new state to set editing to
|
||||
|
||||
@usage-- Setting your editing state to true
|
||||
Tasks.set_editing(task_id, game.player.name, true)
|
||||
|
||||
]]
|
||||
function Tasks.set_editing(task_id, player_name, state)
|
||||
TaskData:update(task_id, function(_, task)
|
||||
task.currently_editing[player_name] = state
|
||||
end)
|
||||
end
|
||||
|
||||
--[[-- Adds an update handler for when a task is added, removed, or updated
|
||||
@tparam function handler the handler which is called when a task is updated
|
||||
|
||||
@usage-- Add a game print when a task is updated
|
||||
Tasks.on_update(function(task)
|
||||
game.print(task.force_name..' now has the task: '..task.message)
|
||||
end)
|
||||
|
||||
]]
|
||||
function Tasks.on_update(handler)
|
||||
TaskData:on_update(handler)
|
||||
end
|
||||
|
||||
--- Getters.
|
||||
-- function used to get information about tasks
|
||||
-- @section getters
|
||||
|
||||
--[[-- Gets the task information that is linked with this id
|
||||
@tparam string task_id the uid of the task you want to get
|
||||
@treturn table the task information
|
||||
|
||||
@usage-- Getting task information outside of on_update
|
||||
local task = Tasks.get_task(task_id)
|
||||
|
||||
]]
|
||||
function Tasks.get_task(task_id)
|
||||
return TaskData:get(task_id)
|
||||
end
|
||||
|
||||
--[[-- Gets all the task ids that a force has
|
||||
@tparam string force_name the name of the force that you want the task ids for
|
||||
@treturn table an array of all the task ids
|
||||
|
||||
@usage-- Getting the task ids for a force
|
||||
local task_ids = Tasks.get_force_task_ids(game.player.force.name)
|
||||
|
||||
]]
|
||||
function Tasks.get_force_task_ids(force_name)
|
||||
return force_tasks[force_name] or {}
|
||||
end
|
||||
|
||||
--[[-- Gets the editing state for a player
|
||||
@tparam string task_id the uid of the task you want to check
|
||||
@tparam string player_name the name of the player that you want to check
|
||||
@treturn boolean weather the player is currently editing this task
|
||||
|
||||
@usage-- Check if a player is editing a task or not
|
||||
local editing = Tasks.get_editing(task_id, game.player.name)
|
||||
|
||||
]]
|
||||
function Tasks.get_editing(task_id, player_name)
|
||||
local task = TaskData:get(task_id)
|
||||
return task.currently_editing[player_name]
|
||||
end
|
||||
|
||||
-- Module Return
|
||||
return Tasks
|
||||
762
exp_legacy/module/modules/control/vlayer.lua
Normal file
762
exp_legacy/module/modules/control/vlayer.lua
Normal file
@@ -0,0 +1,762 @@
|
||||
--[[-- Control Module - vlayer
|
||||
- Adds a virtual layer to store power to save space.
|
||||
@control vlayer
|
||||
@alias vlayer
|
||||
]]
|
||||
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.vlayer' --- @dep config.vlayer
|
||||
local move_items_stack = _C.move_items_stack
|
||||
|
||||
local mega = 1000000
|
||||
|
||||
local vlayer = {}
|
||||
local vlayer_data = {
|
||||
entity_interfaces = {
|
||||
energy = {},
|
||||
circuit = {},
|
||||
storage_input = {},
|
||||
storage_output = {}
|
||||
},
|
||||
properties = {
|
||||
total_surface_area = 0,
|
||||
used_surface_area = 0,
|
||||
production = 0,
|
||||
discharge = 0,
|
||||
capacity = 0
|
||||
},
|
||||
storage = {
|
||||
items = {},
|
||||
power_items = {},
|
||||
energy = 0,
|
||||
unallocated = {}
|
||||
},
|
||||
surface = table.deep_copy(config.surface)
|
||||
}
|
||||
|
||||
Global.register(vlayer_data, function(tbl)
|
||||
vlayer_data = tbl
|
||||
end)
|
||||
|
||||
for name, properties in pairs(config.allowed_items) do
|
||||
properties.modded = false
|
||||
|
||||
if properties.power then
|
||||
vlayer_data.storage.power_items[name] = {
|
||||
value = properties.fuel_value * 1000000,
|
||||
count = 0
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- For all modded items, create a config for them
|
||||
for item_name, properties in pairs(config.modded_items) do
|
||||
local base_properties = config.allowed_items[properties.base_game_equivalent]
|
||||
local m = properties.multiplier
|
||||
|
||||
config.allowed_items[item_name] = {
|
||||
starting_value = properties.starting_value or 0,
|
||||
required_area = base_properties.required_area or 0,
|
||||
surface_area = (base_properties.surface_area or 0) * m,
|
||||
production = (base_properties.production or 0) * m,
|
||||
capacity = (base_properties.capacity or 0) * m,
|
||||
modded = true
|
||||
}
|
||||
end
|
||||
|
||||
--- Get all items in storage, do not modify
|
||||
-- @treturn table a dictionary of all items stored in the vlayer
|
||||
function vlayer.get_items()
|
||||
return vlayer_data.storage.items
|
||||
end
|
||||
|
||||
--- Get all unallocated items in storage
|
||||
-- @treturn table a dictionary of all unallocated items stored in the vlayer
|
||||
function vlayer.get_unallocated_items()
|
||||
return vlayer_data.storage.unallocated
|
||||
end
|
||||
|
||||
--- Get all allocated items in storage
|
||||
-- @treturn table a dictionary of all allocated items stored in the vlayer
|
||||
function vlayer.get_allocated_items()
|
||||
local r = {}
|
||||
|
||||
for k, v in pairs(vlayer_data.storage.items) do
|
||||
r[k] = v
|
||||
|
||||
if vlayer_data.storage.unallocated[k] then
|
||||
r[k] = r[k] - vlayer_data.storage.unallocated[k]
|
||||
end
|
||||
end
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
--- Get the actual defecit of land
|
||||
local function get_actual_land_defecit()
|
||||
local n = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
|
||||
|
||||
for k, v in pairs(vlayer.get_unallocated_items()) do
|
||||
n = n - (config.allowed_items[k].required_area * v)
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
--- Get interface counts
|
||||
-- @treturn table a dictionary of the vlayer interface counts
|
||||
function vlayer.get_interface_counts()
|
||||
local interfaces = vlayer_data.entity_interfaces
|
||||
|
||||
return {
|
||||
energy = #interfaces.energy,
|
||||
circuit = #interfaces.circuit,
|
||||
storage_input = #interfaces.storage_input,
|
||||
storage_output = #interfaces.storage_output,
|
||||
}
|
||||
end
|
||||
|
||||
--- Get interfaces
|
||||
-- @treturn table a dictionary of the vlayer interfaces
|
||||
function vlayer.get_interfaces()
|
||||
local interfaces = vlayer_data.entity_interfaces
|
||||
|
||||
return {
|
||||
energy = interfaces.energy,
|
||||
circuit = interfaces.circuit,
|
||||
storage_input = interfaces.storage_input,
|
||||
storage_output = interfaces.storage_output,
|
||||
}
|
||||
end
|
||||
|
||||
--[[
|
||||
25,000 / 416 s
|
||||
昼 208秒 ソーラー効率100%
|
||||
夕方 83秒 1秒ごとにソーラー発電量が約1.2%ずつ下がり、やがて0%になる
|
||||
夜 41秒 ソーラー発電量が0%になる
|
||||
朝方 83秒 1秒ごとにソーラー発電量が約1.2%ずつ上がり、やがて100%になる
|
||||
|
||||
(surface.dawn) 0.75 18,750 Day 12,500 208s
|
||||
0.00 0 Noon
|
||||
(surface.dusk) 0.25 6,250 Sunset 5,000 83s
|
||||
(surface.evening) 0.45 11,250 Night 2,500 41s
|
||||
(surface.morning) 0.55 13,750 Sunrise 5,000 83s
|
||||
]]
|
||||
|
||||
--- Get the power multiplier based on the surface time
|
||||
local function get_production_multiplier()
|
||||
local mul = vlayer_data.surface.solar_power_multiplier
|
||||
local surface = vlayer_data.surface
|
||||
|
||||
if surface.always_day then
|
||||
-- Surface is always day, so full production is used
|
||||
return mul
|
||||
end
|
||||
|
||||
if surface.darkness then
|
||||
-- We are using a real surface, our config does not contain 'darkness'
|
||||
local brightness = 1 - surface.darkness
|
||||
|
||||
if brightness >= surface.min_brightness then
|
||||
return mul * (brightness - surface.min_brightness) / (1 - surface.min_brightness)
|
||||
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Caused by using a set config rather than a surface
|
||||
local tick = game.tick % surface.ticks_per_day
|
||||
local daytime = tick / surface.ticks_per_day
|
||||
surface.daytime = daytime
|
||||
|
||||
if daytime <= surface.dusk then -- Noon to Sunset
|
||||
return mul
|
||||
|
||||
elseif daytime <= surface.evening then -- Sunset to Night
|
||||
return mul * (1 - ((daytime - surface.dusk) / (surface.evening - surface.dusk)))
|
||||
|
||||
elseif daytime <= surface.morning then -- Night to Sunrise
|
||||
return 0
|
||||
|
||||
elseif daytime <= surface.dawn then -- Sunrise to Morning
|
||||
return mul * ((surface.daytime - surface.morning) / (surface.dawn - surface.morning))
|
||||
|
||||
else -- Morning to Noon
|
||||
return mul
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the sustained power multiplier, this needs improving
|
||||
local function get_sustained_multiplier()
|
||||
local mul = vlayer_data.surface.solar_power_multiplier
|
||||
local surface = vlayer_data.surface
|
||||
|
||||
if surface.always_day then
|
||||
-- Surface is always day, so full production is used
|
||||
return mul
|
||||
end
|
||||
|
||||
-- For nauvis vanilla: 208s + (1/2 x (83s + 83s))
|
||||
local day_duration = 1 - surface.dawn + surface.dusk
|
||||
local sunset_duration = surface.evening - surface.dusk
|
||||
local sunrise_duration = surface.dawn - surface.morning
|
||||
|
||||
return mul * (day_duration + (0.5 * (sunset_duration + sunrise_duration)))
|
||||
end
|
||||
|
||||
--- Internal, Allocate items in the vlayer, this will increase the property values of the vlayer such as production and capacity
|
||||
-- Does not increment item storage, so should not be called before insert_item unless during init
|
||||
-- Does not validate area requirements, so checks must be performed before calling this function
|
||||
-- Accepts negative count for deallocating items
|
||||
-- @tparam string item_name The name of the item to allocate
|
||||
-- @tparam number count The count of the item to allocate
|
||||
function vlayer.allocate_item(item_name, count)
|
||||
local item_properties = config.allowed_items[item_name]
|
||||
assert(item_properties, 'Item not allowed in vlayer: ' .. tostring(item_name))
|
||||
|
||||
if item_properties.production then
|
||||
vlayer_data.properties.production = vlayer_data.properties.production + item_properties.production * count
|
||||
end
|
||||
|
||||
if item_properties.capacity then
|
||||
vlayer_data.properties.capacity = vlayer_data.properties.capacity + item_properties.capacity * count
|
||||
end
|
||||
|
||||
if item_properties.discharge then
|
||||
vlayer_data.properties.discharge = vlayer_data.properties.discharge + item_properties.discharge * count
|
||||
end
|
||||
|
||||
if item_properties.surface_area then
|
||||
vlayer_data.properties.total_surface_area = vlayer_data.properties.total_surface_area + item_properties.surface_area * count
|
||||
end
|
||||
|
||||
if item_properties.required_area and item_properties.required_area > 0 then
|
||||
vlayer_data.properties.used_surface_area = vlayer_data.properties.used_surface_area + item_properties.required_area * count
|
||||
end
|
||||
end
|
||||
|
||||
-- For all allowed items, setup their starting values, default 0
|
||||
for item_name, properties in pairs(config.allowed_items) do
|
||||
vlayer_data.storage.items[item_name] = properties.starting_value or 0
|
||||
|
||||
if properties.required_area and properties.required_area > 0 then
|
||||
vlayer_data.storage.unallocated[item_name] = 0
|
||||
end
|
||||
|
||||
vlayer.allocate_item(item_name, properties.starting_value)
|
||||
end
|
||||
|
||||
--- Insert an item into the vlayer, this will increment its count in storage and allocate it if possible
|
||||
-- @tparam string item_name The name of the item to insert
|
||||
-- @tparam number count The count of the item to insert
|
||||
function vlayer.insert_item(item_name, count)
|
||||
local item_properties = config.allowed_items[item_name]
|
||||
assert(item_properties, 'Item not allowed in vlayer: ' .. tostring(item_name))
|
||||
vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] + count
|
||||
|
||||
if not config.unlimited_surface_area and item_properties.required_area and item_properties.required_area > 0 then
|
||||
-- Calculate how many can be allocated
|
||||
local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
|
||||
local allocate_count = math.min(count, math.floor(surplus_area / item_properties.required_area))
|
||||
|
||||
if allocate_count > 0 then
|
||||
vlayer.allocate_item(item_name, allocate_count)
|
||||
end
|
||||
|
||||
vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] + count - allocate_count
|
||||
|
||||
else
|
||||
vlayer.allocate_item(item_name, count)
|
||||
end
|
||||
end
|
||||
|
||||
--- Remove an item from the vlayer, this will decrement its count in storage and prioritise unallocated items over deallocation
|
||||
-- Can not always fulfil the remove request for items which provide surface area, therefore returns the amount actually removed
|
||||
-- @tparam string item_name The name of the item to remove
|
||||
-- @tparam number count The count of the item to remove
|
||||
-- @treturn number The count of the item actually removed
|
||||
function vlayer.remove_item(item_name, count)
|
||||
local item_properties = config.allowed_items[item_name]
|
||||
assert(item_properties, 'Item not allowed in vlayer: ' .. tostring(item_name))
|
||||
local remove_unallocated = 0
|
||||
|
||||
if not config.unlimited_surface_area and item_properties.required_area and item_properties.required_area > 0 then
|
||||
-- Remove from the unallocated storage first
|
||||
remove_unallocated = math.min(count, vlayer_data.storage.unallocated[item_name])
|
||||
|
||||
if remove_unallocated > 0 then
|
||||
vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] - count
|
||||
vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] - count
|
||||
end
|
||||
|
||||
-- Check if any more items need to be removed
|
||||
count = count - remove_unallocated
|
||||
|
||||
if count == 0 then
|
||||
return remove_unallocated
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate the amount to remove based on items in storage
|
||||
local remove_count = math.min(count, vlayer_data.storage.items[item_name])
|
||||
|
||||
if item_properties.surface_area and item_properties.surface_area > 0 then
|
||||
-- If the item provides surface area then it has additional limitations
|
||||
local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
|
||||
remove_count = math.min(remove_count, math.floor(surplus_area / item_properties.surface_area))
|
||||
|
||||
if remove_count <= 0 then
|
||||
return remove_unallocated
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove the item from allocated storage
|
||||
vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] - remove_count
|
||||
vlayer.allocate_item(item_name, -remove_count)
|
||||
|
||||
return remove_unallocated + remove_count
|
||||
end
|
||||
|
||||
--- Create a new storage input interface
|
||||
-- @tparam LuaSurface surface The surface to place the interface onto
|
||||
-- @tparam MapPosition position The position on the surface to place the interface at
|
||||
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
|
||||
-- @treturn LuaEntity The entity that was created for the interface
|
||||
function vlayer.create_input_interface(surface, position, circuit, last_user)
|
||||
local interface = surface.create_entity{name='logistic-chest-storage', position=position, force='neutral'}
|
||||
table.insert(vlayer_data.entity_interfaces.storage_input, interface)
|
||||
|
||||
if last_user then
|
||||
interface.last_user = last_user
|
||||
end
|
||||
|
||||
if circuit then
|
||||
for k, _ in pairs(circuit) do
|
||||
for _, v in pairs(circuit[k]) do
|
||||
interface.connect_neighbour({wire=defines.wire_type[k], target_entity=v})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
interface.destructible = false
|
||||
interface.minable = false
|
||||
interface.operable = true
|
||||
|
||||
return interface
|
||||
end
|
||||
|
||||
--- Handle all input interfaces, will take their contents and insert it into the vlayer storage
|
||||
local function handle_input_interfaces()
|
||||
for index, interface in pairs(vlayer_data.entity_interfaces.storage_input) do
|
||||
if not interface.valid then
|
||||
vlayer_data.entity_interfaces.storage_input[index] = nil
|
||||
|
||||
else
|
||||
local inventory = interface.get_inventory(defines.inventory.chest)
|
||||
|
||||
for name, count in pairs(inventory.get_contents()) do
|
||||
if config.allowed_items[name] then
|
||||
if config.allowed_items[name].modded then
|
||||
if config.modded_auto_downgrade then
|
||||
vlayer.insert_item(config.modded_items[name].base_game_equivalent, count * config.modded_items[name].multiplier)
|
||||
|
||||
else
|
||||
vlayer.insert_item(name, count)
|
||||
end
|
||||
|
||||
else
|
||||
if vlayer_data.storage.power_items[name] then
|
||||
vlayer_data.storage.power_items[name].count = vlayer_data.storage.power_items[name].count + count
|
||||
|
||||
else
|
||||
vlayer.insert_item(name, count)
|
||||
end
|
||||
end
|
||||
|
||||
inventory.remove({name=name, count=count})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new storage output interface
|
||||
-- @tparam LuaSurface surface The surface to place the interface onto
|
||||
-- @tparam MapPosition position The position on the surface to place the interface at
|
||||
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
|
||||
-- @treturn LuaEntity The entity that was created for the interface
|
||||
function vlayer.create_output_interface(surface, position, circuit, last_user)
|
||||
local interface = surface.create_entity{name='logistic-chest-requester', position=position, force='neutral'}
|
||||
table.insert(vlayer_data.entity_interfaces.storage_output, interface)
|
||||
|
||||
if last_user then
|
||||
interface.last_user = last_user
|
||||
end
|
||||
|
||||
if circuit then
|
||||
for k, _ in pairs(circuit) do
|
||||
for _, v in pairs(circuit[k]) do
|
||||
interface.connect_neighbour({wire=defines.wire_type[k], target_entity=v})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
interface.destructible = false
|
||||
interface.minable = false
|
||||
interface.operable = true
|
||||
|
||||
return interface
|
||||
end
|
||||
|
||||
--- Handle all output interfaces, will take their requests and remove it from the vlayer storage
|
||||
local function handle_output_interfaces()
|
||||
for index, interface in pairs(vlayer_data.entity_interfaces.storage_output) do
|
||||
if not interface.valid then
|
||||
vlayer_data.entity_interfaces.storage_output[index] = nil
|
||||
|
||||
else
|
||||
local inventory = interface.get_inventory(defines.inventory.chest)
|
||||
|
||||
for i = 1, interface.request_slot_count do
|
||||
local request = interface.get_request_slot(i)
|
||||
|
||||
if request and config.allowed_items[request.name] then
|
||||
local current_amount = inventory.get_item_count(request.name)
|
||||
local request_amount = math.min(request.count - current_amount, vlayer_data.storage.items[request.name])
|
||||
|
||||
if request_amount > 0 and inventory.can_insert({name=request.name, count=request_amount}) then
|
||||
local removed_item_count = vlayer.remove_item(request.name, request_amount)
|
||||
|
||||
if removed_item_count > 0 then
|
||||
inventory.insert({name=request.name, count=removed_item_count})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handle the unallocated items because more surface area may have been added
|
||||
local function handle_unallocated()
|
||||
-- unallocated cant happen when its unlimited
|
||||
if config.unlimited_surface_area then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get the total unallocated area so items can be allocated in equal amounts
|
||||
local unallocated_area = 0
|
||||
|
||||
for item_name, count in pairs(vlayer_data.storage.unallocated) do
|
||||
local item_properties = config.allowed_items[item_name]
|
||||
unallocated_area = unallocated_area + item_properties.required_area * count
|
||||
end
|
||||
|
||||
if unallocated_area == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Allocate items in an equal distribution
|
||||
local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
|
||||
|
||||
for item_name, count in pairs(vlayer_data.storage.unallocated) do
|
||||
local allocation_count = math.min(count, math.floor(count * surplus_area / unallocated_area))
|
||||
|
||||
if allocation_count > 0 then
|
||||
vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] - allocation_count
|
||||
vlayer.allocate_item(item_name, allocation_count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the statistics for the vlayer
|
||||
function vlayer.get_statistics()
|
||||
local vdp = vlayer_data.properties.production * mega
|
||||
local gdm = get_production_multiplier()
|
||||
|
||||
return {
|
||||
total_surface_area = vlayer_data.properties.total_surface_area,
|
||||
used_surface_area = vlayer_data.properties.used_surface_area,
|
||||
remaining_surface_area = get_actual_land_defecit(),
|
||||
production_multiplier = gdm,
|
||||
energy_max = vdp,
|
||||
energy_production = vdp * gdm,
|
||||
energy_sustained = vdp * get_sustained_multiplier(),
|
||||
energy_capacity = vlayer_data.properties.capacity * mega,
|
||||
energy_storage = vlayer_data.storage.energy,
|
||||
day_time = math.floor(vlayer_data.surface.daytime * vlayer_data.surface.ticks_per_day),
|
||||
day_length = vlayer_data.surface.ticks_per_day,
|
||||
tick = game.tick
|
||||
}
|
||||
end
|
||||
|
||||
--- add or reduce vlayer power
|
||||
function vlayer.energy_changed(power)
|
||||
vlayer_data.storage.energy = vlayer_data.storage.energy + power
|
||||
end
|
||||
|
||||
--- Circuit signals used for the statistics
|
||||
function vlayer.get_circuits()
|
||||
return {
|
||||
total_surface_area = 'signal-A',
|
||||
used_surface_area = 'signal-U',
|
||||
remaining_surface_area = 'signal-R',
|
||||
production_multiplier = 'signal-M',
|
||||
energy_production = 'signal-P',
|
||||
energy_sustained = 'signal-S',
|
||||
energy_capacity = 'signal-C',
|
||||
energy_storage = 'signal-E',
|
||||
day_time = 'signal-D',
|
||||
day_length = 'signal-L',
|
||||
tick = 'signal-T',
|
||||
}
|
||||
end
|
||||
|
||||
--- Create a new circuit interface
|
||||
-- @tparam LuaSurface surface The surface to place the interface onto
|
||||
-- @tparam MapPosition position The position on the surface to place the interface at
|
||||
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
|
||||
-- @treturn LuaEntity The entity that was created for the interface
|
||||
function vlayer.create_circuit_interface(surface, position, circuit, last_user)
|
||||
local interface = surface.create_entity{name='constant-combinator', position=position, force='neutral'}
|
||||
table.insert(vlayer_data.entity_interfaces.circuit, interface)
|
||||
|
||||
if last_user then
|
||||
interface.last_user = last_user
|
||||
end
|
||||
|
||||
if circuit then
|
||||
for k, _ in pairs(circuit) do
|
||||
for _, v in pairs(circuit[k]) do
|
||||
interface.connect_neighbour({wire=defines.wire_type[k], target_entity=v})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
interface.destructible = false
|
||||
interface.minable = false
|
||||
interface.operable = true
|
||||
|
||||
return interface
|
||||
end
|
||||
|
||||
--- Handle all circuit interfaces, updating their signals to match the vlayer statistics
|
||||
local function handle_circuit_interfaces()
|
||||
local stats = vlayer.get_statistics()
|
||||
|
||||
for index, interface in pairs(vlayer_data.entity_interfaces.circuit) do
|
||||
if not interface.valid then
|
||||
vlayer_data.entity_interfaces.circuit[index] = nil
|
||||
|
||||
else
|
||||
local circuit_oc = interface.get_or_create_control_behavior()
|
||||
local max_signals = circuit_oc.signals_count
|
||||
local signal_index = 1
|
||||
local circuit = vlayer.get_circuits()
|
||||
|
||||
-- Set the virtual signals based on the vlayer stats
|
||||
for stat_name, signal_name in pairs(circuit) do
|
||||
if stat_name:find('energy') then
|
||||
circuit_oc.set_signal(signal_index, {signal={type='virtual', name=signal_name}, count=math.floor(stats[stat_name] / mega)})
|
||||
|
||||
elseif stat_name == 'production_multiplier' then
|
||||
circuit_oc.set_signal(signal_index, {signal={type='virtual', name=signal_name}, count=math.floor(stats[stat_name] * 10000)})
|
||||
|
||||
else
|
||||
circuit_oc.set_signal(signal_index, {signal={type='virtual', name=signal_name}, count=math.floor(stats[stat_name])})
|
||||
end
|
||||
|
||||
signal_index = signal_index + 1
|
||||
end
|
||||
|
||||
-- Set the item signals based on stored items
|
||||
for item_name, count in pairs(vlayer_data.storage.items) do
|
||||
if game.item_prototypes[item_name] and count > 0 then
|
||||
circuit_oc.set_signal(signal_index, {signal={type='item', name=item_name}, count=count})
|
||||
signal_index = signal_index + 1
|
||||
if signal_index > max_signals then
|
||||
return -- No more signals can be added
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear remaining signals to prevent outdated values being present (caused by count > 0 check)
|
||||
for clear_index = signal_index, max_signals do
|
||||
if not circuit_oc.get_signal(clear_index).signal then
|
||||
break -- There are no more signals to clear
|
||||
end
|
||||
|
||||
circuit_oc.set_signal(clear_index, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new energy interface
|
||||
-- @tparam LuaSurface surface The surface to place the interface onto
|
||||
-- @tparam MapPosition position The position on the surface to place the interface at
|
||||
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
|
||||
-- @treturn LuaEntity The entity that was created for the interface, or nil if it could not be created
|
||||
function vlayer.create_energy_interface(surface, position, last_user)
|
||||
if not surface.can_place_entity{name='electric-energy-interface', position=position} then
|
||||
return nil
|
||||
end
|
||||
|
||||
local interface = surface.create_entity{name='electric-energy-interface', position=position, force='neutral'}
|
||||
table.insert(vlayer_data.entity_interfaces.energy, interface)
|
||||
|
||||
if last_user then
|
||||
interface.last_user = last_user
|
||||
end
|
||||
|
||||
interface.destructible = false
|
||||
interface.minable = false
|
||||
interface.operable = false
|
||||
interface.electric_buffer_size = 0
|
||||
interface.power_production = 0
|
||||
interface.power_usage = 0
|
||||
interface.energy = 0
|
||||
|
||||
return interface
|
||||
end
|
||||
|
||||
--- Handle all energy interfaces as well as the energy storage
|
||||
local function handle_energy_interfaces()
|
||||
-- Add the newly produced power
|
||||
local production = vlayer_data.properties.production * mega * (config.update_tick_energy / 60)
|
||||
vlayer_data.storage.energy = vlayer_data.storage.energy + math.floor(production * get_production_multiplier())
|
||||
|
||||
-- Calculate how much power is present in the network, that is storage + all interfaces
|
||||
if #vlayer_data.entity_interfaces.energy > 0 then
|
||||
local available_energy = vlayer_data.storage.energy
|
||||
|
||||
for index, interface in pairs(vlayer_data.entity_interfaces.energy) do
|
||||
if not interface.valid then
|
||||
vlayer_data.entity_interfaces.energy[index] = nil
|
||||
|
||||
else
|
||||
available_energy = available_energy + interface.energy
|
||||
end
|
||||
end
|
||||
|
||||
-- Distribute the energy between all interfaces
|
||||
local discharge_rate = 2 * (production + vlayer_data.properties.discharge * mega) / #vlayer_data.entity_interfaces.energy
|
||||
local fill_to = math.min(discharge_rate, math.floor(available_energy / #vlayer_data.entity_interfaces.energy))
|
||||
|
||||
for _, interface in pairs(vlayer_data.entity_interfaces.energy) do
|
||||
interface.electric_buffer_size = math.max(discharge_rate, interface.energy) -- prevent energy loss
|
||||
local delta = fill_to - interface.energy -- positive means storage to interface
|
||||
vlayer_data.storage.energy = vlayer_data.storage.energy - delta
|
||||
interface.energy = interface.energy + delta
|
||||
end
|
||||
end
|
||||
|
||||
-- Cap the stored energy to the allowed capacity
|
||||
if not config.unlimited_capacity and vlayer_data.storage.energy > vlayer_data.properties.capacity * mega then
|
||||
vlayer_data.storage.energy = vlayer_data.properties.capacity * mega
|
||||
|
||||
-- burn the trash to produce power
|
||||
elseif vlayer_data.storage.power_items then
|
||||
for k, v in pairs(vlayer_data.storage.power_items) do
|
||||
local max_burning = (vlayer_data.properties.capacity * mega / 2) - vlayer_data.storage.energy
|
||||
|
||||
if v.count > 0 and max_burning > 0 then
|
||||
local to_burn = math.min(v.count, math.floor(max_burning / v.value))
|
||||
|
||||
vlayer_data.storage.energy = vlayer_data.storage.energy + (to_burn * v.value)
|
||||
vlayer_data.storage.power_items[k].count = vlayer_data.storage.power_items[k].count - to_burn
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Remove the entity interface using the given position
|
||||
-- @tparam LuaSurface surface The surface to search for an interface on
|
||||
-- @tparam MapPosition position The position of the item
|
||||
-- @treturn string The type of interface that was removed, or nil if no interface was found
|
||||
-- @treturn MapPosition The position the interface was at, or nil if no interface was found
|
||||
function vlayer.remove_interface(surface, position)
|
||||
local entities = surface.find_entities_filtered{
|
||||
name = {'logistic-chest-storage', 'logistic-chest-requester', 'constant-combinator', 'electric-energy-interface'},
|
||||
force = 'neutral',
|
||||
position = position,
|
||||
radius = 2,
|
||||
limit = 1
|
||||
}
|
||||
|
||||
-- Get the details which will be returned
|
||||
if #entities == 0 then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local interface = entities[1]
|
||||
local name = interface.name
|
||||
local pos = interface.position
|
||||
|
||||
-- Return the type of interface removed and do some clean up
|
||||
if name == 'logistic-chest-storage' then
|
||||
move_items_stack(interface.get_inventory(defines.inventory.chest).get_contents())
|
||||
table.remove_element(vlayer_data.entity_interfaces.storage_input, interface)
|
||||
interface.destroy()
|
||||
|
||||
return 'storage-input', pos
|
||||
|
||||
elseif name == 'logistic-chest-requester' then
|
||||
move_items_stack(interface.get_inventory(defines.inventory.chest).get_contents())
|
||||
table.remove_element(vlayer_data.entity_interfaces.storage_output, interface)
|
||||
interface.destroy()
|
||||
|
||||
return 'storage-output', pos
|
||||
|
||||
elseif name == 'constant-combinator' then
|
||||
table.remove_element(vlayer_data.entity_interfaces.circuit, interface)
|
||||
interface.destroy()
|
||||
|
||||
return 'circuit', pos
|
||||
|
||||
elseif name == 'electric-energy-interface' then
|
||||
vlayer_data.storage.energy = vlayer_data.storage.energy + interface.energy
|
||||
table.remove_element(vlayer_data.entity_interfaces.energy, interface)
|
||||
interface.destroy()
|
||||
|
||||
return 'energy', pos
|
||||
end
|
||||
end
|
||||
|
||||
local function on_surface_event()
|
||||
if config.mimic_surface then
|
||||
local surface = game.get_surface(config.mimic_surface)
|
||||
|
||||
if surface then
|
||||
vlayer_data.surface = surface
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not vlayer_data.surface.index then
|
||||
-- Our fake surface data never has an index, we test for this to avoid unneeded copies from the config
|
||||
vlayer_data.surface = table.deep_copy(config.surface)
|
||||
end
|
||||
end
|
||||
|
||||
--- Handle all storage IO and attempt allocation of unallocated items
|
||||
Event.on_nth_tick(config.update_tick_storage, function(_)
|
||||
handle_input_interfaces()
|
||||
handle_output_interfaces()
|
||||
handle_unallocated()
|
||||
end)
|
||||
|
||||
--- Handle all energy and circuit updates
|
||||
Event.on_nth_tick(config.update_tick_energy, function(_)
|
||||
handle_circuit_interfaces()
|
||||
handle_energy_interfaces()
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_surface_created, on_surface_event)
|
||||
Event.add(defines.events.on_surface_renamed, on_surface_event)
|
||||
Event.add(defines.events.on_surface_imported, on_surface_event)
|
||||
Event.on_init(on_surface_event) -- Default surface always exists, does not trigger on_surface_created
|
||||
|
||||
return vlayer
|
||||
331
exp_legacy/module/modules/control/warnings.lua
Normal file
331
exp_legacy/module/modules/control/warnings.lua
Normal file
@@ -0,0 +1,331 @@
|
||||
--[[-- Control Module - Warnings
|
||||
- Adds a way to give and remove warnings to players.
|
||||
@control Warnings
|
||||
@alias Warnings
|
||||
|
||||
@usage
|
||||
-- import the module from the control modules
|
||||
local Warnings = require 'modules.control.warnings' --- @dep modules.control.warnings
|
||||
|
||||
-- This will add a warning to the player
|
||||
Warnings.add_warning('MrBiter', 'Cooldude2606', 'Killed too many biters')
|
||||
|
||||
-- This will remove a warning from a player, second name is just who is doing the action
|
||||
Warnings.remove_warning('MrBiter', 'Cooldude2606')
|
||||
|
||||
-- Script warning as similar to normal warning but are designed to have no effect for a short amount of time
|
||||
-- this is so it can be used for greifer protection without being too agressive
|
||||
Warnings.add_script_warning('MrBiter', 'Killed too many biters')
|
||||
|
||||
-- Both normal and script warnings can also be cleared, this will remove all warnings
|
||||
Warnings.clear_warnings('MrBiter', 'Cooldude2606')
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Game = require 'utils.game' --- @dep utils.game
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local config = require 'config.warnings' --- @dep config.warnings
|
||||
|
||||
local valid_player = Game.get_player_from_any
|
||||
|
||||
--- Stores the quickbar filters for a player
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local PlayerWarnings = PlayerData.Required:combine('Warnings')
|
||||
PlayerWarnings:set_metadata{
|
||||
stringify = function(value)
|
||||
if not value then return 'You have no warnings' end
|
||||
local count = 0
|
||||
for _ in pairs(value) do count = count + 1 end
|
||||
return 'You have '..count..' warnings'
|
||||
end
|
||||
}
|
||||
|
||||
local Warnings = {
|
||||
user_warnings=PlayerWarnings,
|
||||
user_script_warnings={},
|
||||
events = {
|
||||
--- When a warning is added to a player
|
||||
-- @event on_warning_added
|
||||
-- @tparam number player_index the index of the player who recived the warning
|
||||
-- @tparam string by_player_name the name of the player who gave the warning
|
||||
-- @tparam string reason the reason that the player was given a warning
|
||||
-- @tparam number warning_count the new number of warnings that the player has
|
||||
on_warning_added = script.generate_event_name(),
|
||||
--- When a warning is removed from a player
|
||||
-- @event on_warning_removed
|
||||
-- @tparam number player_index the index of the player who is having the warning removed
|
||||
-- @tparam string warning_by_name the name of the player who gave the warning
|
||||
-- @tparam string removed_by_name the name of the player who is removing the warning
|
||||
-- @tparam number warning_count the new number of warnings that the player has
|
||||
-- @tparam number batch_count the number of warnings removed in this batch, always one when not a batch
|
||||
-- @tparam number batch the index of this event in a batch, always one when not a batch
|
||||
on_warning_removed = script.generate_event_name(),
|
||||
--- When a warning is added to a player, by the script
|
||||
-- @event on_script_warning_added
|
||||
-- @tparam number player_index the index of the player who recived the warning
|
||||
-- @tparam string reason the reason that the player was given a warning
|
||||
-- @tparam number warning_count the new number of warnings that the player has
|
||||
on_script_warning_added = script.generate_event_name(),
|
||||
--- When a warning is removed from a player, by the script
|
||||
-- @event on_script_warning_removed
|
||||
-- @tparam number player_index the index of the player who is having the warning removed
|
||||
-- @tparam number warning_count the new number of warnings that the player has
|
||||
on_script_warning_removed = script.generate_event_name(),
|
||||
}
|
||||
}
|
||||
|
||||
local user_script_warnings = Warnings.user_script_warnings
|
||||
Global.register(user_script_warnings, function(tbl)
|
||||
Warnings.user_script_warnings = tbl
|
||||
user_script_warnings = tbl
|
||||
end)
|
||||
|
||||
--- Gets an array of warnings that the player has, always returns a list even if empty
|
||||
-- @tparam LuaPlayer player the player to get the warning for
|
||||
-- @treturn table an array of all the warnings on this player, contains tick, by_player_name and reason
|
||||
function Warnings.get_warnings(player)
|
||||
return PlayerWarnings:get(player.name, {})
|
||||
end
|
||||
|
||||
--- Gets the number of warnings that a player has on them
|
||||
-- @tparam LuaPlayer player the player to count the warnings for
|
||||
-- @treturn number the number of warnings that the player has
|
||||
function Warnings.count_warnings(player)
|
||||
local warnings = PlayerWarnings:get(player.name, {})
|
||||
return #warnings
|
||||
end
|
||||
|
||||
--- Adds a warning to a player, when a warning is added a set action is done based on the number of warnings and the config file
|
||||
-- @tparam LuaPlayer player the player to add a warning to
|
||||
-- @tparam string by_player_name the name of the player who is doing the action
|
||||
-- @tparam[opt='Non given.'] string reason the reason that the player is being warned
|
||||
-- @treturn number the number of warnings that the player has
|
||||
function Warnings.add_warning(player, by_player_name, reason)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
if not by_player_name then return end
|
||||
|
||||
reason = reason or 'None given.'
|
||||
|
||||
local warning_count
|
||||
PlayerWarnings:update(player.name, function(_, warnings)
|
||||
local warning = {
|
||||
by_player_name = by_player_name,
|
||||
reason = reason
|
||||
}
|
||||
|
||||
if not warnings then
|
||||
warning_count = 1
|
||||
return {warning}
|
||||
else
|
||||
table.insert(warnings, warning)
|
||||
warning_count = #warnings
|
||||
end
|
||||
end)
|
||||
|
||||
script.raise_event(Warnings.events.on_warning_added, {
|
||||
name = Warnings.events.on_warning_added,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
warning_count = warning_count,
|
||||
by_player_name = by_player_name,
|
||||
reason = reason
|
||||
})
|
||||
|
||||
local action = config.actions[warning_count]
|
||||
if action then
|
||||
local _type = type(action)
|
||||
if _type == 'function' then
|
||||
action(player, by_player_name, warning_count)
|
||||
elseif _type == 'table' then
|
||||
local current = table.deepcopy(action)
|
||||
table.insert(current, 2,by_player_name)
|
||||
table.insert(current, 3,warning_count)
|
||||
player.print(current)
|
||||
elseif type(action) == 'string' then
|
||||
player.print(action)
|
||||
end
|
||||
end
|
||||
|
||||
return warning_count
|
||||
end
|
||||
|
||||
--- Event trigger for removing a waring due to it being looped in clear warnings
|
||||
-- @tparam LuaPlayer player the player who is having a warning removed
|
||||
-- @tparam string warning_by_name the name of the player who made the warning
|
||||
-- @tparam string removed_by_name the name of the player who is doing the action
|
||||
-- @tparam number warning_count the number of warnings that the player how has
|
||||
-- @tparam number batch the index of this event in a batch, always one when not a batch
|
||||
-- @tparam number batch_count the number of reports removed in this batch, always one when not a batch
|
||||
local function warning_removed_event(player, warning_by_name, removed_by_name, warning_count, batch, batch_count)
|
||||
script.raise_event(Warnings.events.on_warning_removed, {
|
||||
name = Warnings.events.on_warning_removed,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
warning_count = warning_count,
|
||||
warning_by_name = warning_by_name,
|
||||
removed_by_name = removed_by_name,
|
||||
batch_count = batch_count or 1,
|
||||
batch = batch or 1
|
||||
})
|
||||
end
|
||||
|
||||
--- Removes a warning from a player, always removes the earliest warning, fifo
|
||||
-- @tparam LuaPlayer player the player to remove a warning from
|
||||
-- @tparam string by_player_name the name of the player who is doing the action
|
||||
-- @treturn number the number of warnings that the player has
|
||||
function Warnings.remove_warning(player, by_player_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
if not by_player_name then return end
|
||||
|
||||
local warning, warning_count
|
||||
PlayerWarnings:update(player.name, function(_, warnings)
|
||||
if not warnings then return end
|
||||
warning = table.remove(warnings, 1)
|
||||
warning_count = #warnings
|
||||
end)
|
||||
|
||||
if not warning then return end
|
||||
warning_removed_event(player, warning.by_player_name, by_player_name, warning_count)
|
||||
|
||||
return warning_count
|
||||
end
|
||||
|
||||
--- Removes all warnings from a player, will trigger remove event for each warning
|
||||
-- @tparam LuaPlayer player the player to clear the warnings from
|
||||
-- @tparam string by_player_name the name of the player who is doing the action
|
||||
-- @treturn boolean true when warnings were cleared succesfully
|
||||
function Warnings.clear_warnings(player, by_player_name)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
if not by_player_name then return end
|
||||
|
||||
local warnings = PlayerWarnings:get(player)
|
||||
if not warnings then return end
|
||||
|
||||
local warning_count = #warnings
|
||||
for n, warning in pairs(warnings) do
|
||||
warning_removed_event(player, warning.by_player_name, by_player_name, warning_count-n, n, warning_count)
|
||||
end
|
||||
|
||||
PlayerWarnings:remove(player)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Gets an array of all the script warnings that a player has
|
||||
-- @tparam LuaPlayer player the player to get the script warnings of
|
||||
-- @treturn table a table of all the script warnings a player has, contains tick and reason
|
||||
function Warnings.get_script_warnings(player)
|
||||
return user_script_warnings[player.name] or {}
|
||||
end
|
||||
|
||||
--- Gets the number of script warnings that a player has on them
|
||||
-- @tparam LuaPlayer player the player to count the script warnings of
|
||||
-- @treturn number the number of script warnings that the player has
|
||||
function Warnings.count_script_warnings(player)
|
||||
local warnings = user_script_warnings[player.name] or {}
|
||||
return #warnings
|
||||
end
|
||||
|
||||
--- Adds a script warning to a player, this may add a full warning if max script warnings is met
|
||||
-- @tparam LuaPlayer player the player to add a script warning to
|
||||
-- @tparam[opt='Non given.'] string reason the reason that the player is being warned
|
||||
-- @treturn number the number of script warnings that the player has
|
||||
function Warnings.add_script_warning(player, reason)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
reason = reason or 'Non given.'
|
||||
|
||||
local warnings = user_script_warnings[player.name]
|
||||
if not warnings then
|
||||
warnings = {}
|
||||
user_script_warnings[player.name] = warnings
|
||||
end
|
||||
|
||||
table.insert(warnings, {
|
||||
tick = game.tick,
|
||||
reason = reason
|
||||
})
|
||||
|
||||
local warning_count = #warnings
|
||||
|
||||
script.raise_event(Warnings.events.on_script_warning_added, {
|
||||
name = Warnings.events.on_script_warning_added,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
warning_count = warning_count,
|
||||
reason = reason
|
||||
})
|
||||
|
||||
if warning_count > config.script_warning_limit then
|
||||
Warnings.add_warning(player, '<server>', reason)
|
||||
end
|
||||
|
||||
return warning_count
|
||||
end
|
||||
|
||||
--- Script warning removed event trigger due to it being looped in clear script warnings
|
||||
-- @tparam LuaPlayer player the player who is having a script warning removed
|
||||
-- @tparam number warning_count the number of warnings that the player has
|
||||
local function script_warning_removed_event(player, warning_count)
|
||||
script.raise_event(Warnings.events.on_script_warning_removed, {
|
||||
name = Warnings.events.on_script_warning_removed,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
warning_count = warning_count
|
||||
})
|
||||
end
|
||||
|
||||
--- Removes a script warning from a player
|
||||
-- @tparam LuaPlayer player the player to remove a script warning from
|
||||
-- @treturn number the number of script warnings that the player has
|
||||
function Warnings.remove_script_warning(player)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
local warnings = user_script_warnings[player.name]
|
||||
if not warnings then return end
|
||||
|
||||
table.remove(warnings, 1)
|
||||
|
||||
script_warning_removed_event(player)
|
||||
|
||||
return #warnings
|
||||
end
|
||||
|
||||
--- Removes all script warnings from a player, emits event for each warning removed
|
||||
-- @tparam LuaPlayer player the player to clear the script warnings from
|
||||
function Warnings.clear_script_warnings(player)
|
||||
player = valid_player(player)
|
||||
if not player then return end
|
||||
|
||||
local warnings = user_script_warnings[player.name]
|
||||
if not warnings then return end
|
||||
|
||||
local warning_count = #warnings
|
||||
for n, _ in pairs(warnings) do
|
||||
script_warning_removed_event(player, warning_count-n)
|
||||
end
|
||||
|
||||
user_script_warnings[player.name] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
-- script warnings are removed after a certain amount of time to make them even more lienient
|
||||
local script_warning_cool_down = config.script_warning_cool_down*3600
|
||||
Event.on_nth_tick(script_warning_cool_down/4, function()
|
||||
local cutoff = game.tick - script_warning_cool_down
|
||||
for player_name, script_warnings in pairs(user_script_warnings) do
|
||||
if #script_warnings > 0 then
|
||||
for _, warning in pairs(script_warnings) do
|
||||
if warning.tick < cutoff then
|
||||
Warnings.remove_script_warning(player_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return Warnings
|
||||
483
exp_legacy/module/modules/control/warps.lua
Normal file
483
exp_legacy/module/modules/control/warps.lua
Normal file
@@ -0,0 +1,483 @@
|
||||
--[[-- Control Module - Warps
|
||||
- Stores warps for each force.
|
||||
@control Warps
|
||||
@alias Warps
|
||||
|
||||
@usage-- Making a new spawn warp
|
||||
local player = game.player
|
||||
local force = player.force
|
||||
local spawn_id = Warps.add_warp(force.name, player.surface, player.position, player.name, 'Spawn')
|
||||
|
||||
Warps.set_spawn_warp(spawn_id, force)
|
||||
Warps.make_warp_tag(spawn_id)
|
||||
|
||||
@usage-- Making a new warp with a warp area
|
||||
local player = game.player
|
||||
local force = player.force
|
||||
local warp_id = Warps.add_warp(force.name, player.surface, player.position, player.name)
|
||||
|
||||
Warps.make_warp_area(warp_id)
|
||||
Warps.make_warp_tag(warp_id)
|
||||
|
||||
]]
|
||||
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
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 = {_uid=1}
|
||||
---@cast force_warps table<string, { spawn: string, [number]: string }>
|
||||
Global.register(force_warps, function(tbl)
|
||||
force_warps = tbl
|
||||
end)
|
||||
|
||||
-- Create an array of entity names that will be added to the remove filter
|
||||
local remove_warp_area_entity_names = {}
|
||||
for _, entity in pairs(config.entities) do
|
||||
table.insert(remove_warp_area_entity_names, entity[1])
|
||||
end
|
||||
|
||||
-- When a warp is updated change its chat tag and restore the warp order
|
||||
WrapData:on_update(function(warp_id, warp, old_warp)
|
||||
if warp then
|
||||
warp.updates = warp.updates + 1
|
||||
-- Update the map chart tag if there is one
|
||||
if warp.tag then
|
||||
Warps.make_warp_tag(warp_id)
|
||||
end
|
||||
|
||||
-- Check that the name of the warp has been changed
|
||||
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
|
||||
local warp_ids = force_warps[force_name]
|
||||
local spawn_id = warp_ids.spawn
|
||||
|
||||
local warp_names = {}
|
||||
for _, next_warp_id in pairs(warp_ids) do
|
||||
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
|
||||
end
|
||||
|
||||
-- Sort the warp names in alphabetical order
|
||||
local new_warp_ids = table.get_values(table.keysort(warp_names))
|
||||
table.insert(new_warp_ids, 1,spawn_id)
|
||||
new_warp_ids.spawn = spawn_id
|
||||
force_warps[force_name] = new_warp_ids
|
||||
end
|
||||
end)
|
||||
|
||||
--- Map Integration.
|
||||
-- functions used to create and alter warps with in the map
|
||||
-- @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
|
||||
@treturn boolean true if a new tag was made, false if it was updated
|
||||
|
||||
@usage-- Adding a chart tag for a new warp
|
||||
local tag_added = Warps.make_warp_tag(warp_id)
|
||||
|
||||
]]
|
||||
function Warps.make_warp_tag(warp_id)
|
||||
local warp = WrapData:get(warp_id)
|
||||
local name = warp.name
|
||||
local icon = warp.icon
|
||||
|
||||
-- Edit the existing tag if it is present
|
||||
local tag = warp.tag
|
||||
|
||||
if tag and tag.valid then
|
||||
tag.text = 'Warp: ' .. name
|
||||
tag.icon = icon
|
||||
return false
|
||||
end
|
||||
|
||||
-- Make a new tag if one did not exist
|
||||
local force = game.forces[warp.force_name]
|
||||
local surface = warp.surface
|
||||
local position = warp.position
|
||||
|
||||
tag = force.add_chart_tag(surface, {
|
||||
position = {position.x+0.5, position.y+0.5},
|
||||
text = 'Warp: ' .. name,
|
||||
icon = icon
|
||||
})
|
||||
|
||||
-- Add the tag to this warp, store.update not needed as we dont want it to trigger
|
||||
warp.tag = tag
|
||||
return true
|
||||
end
|
||||
|
||||
--[[-- Remove the chart tag for this warp
|
||||
@tparam string warp_id the uid for the warp that you want to remove the chart tag from
|
||||
@treturn boolean true if the tag was valid and was removed, false if the tag was invalid
|
||||
|
||||
@usage-- Removing the chart tag from a warp
|
||||
local removed = Warps.remove_warp_tag(warp_id)
|
||||
|
||||
]]
|
||||
function Warps.remove_warp_tag(warp_id)
|
||||
local warp = WrapData:get(warp_id)
|
||||
|
||||
-- Check there is a tag to remove
|
||||
local tag = warp.tag
|
||||
if not tag or not tag.valid then
|
||||
warp.tag = nil
|
||||
return false
|
||||
end
|
||||
|
||||
-- Remove the warp chart tag if it is valid
|
||||
tag.destroy()
|
||||
warp.tag = nil
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[-- Add a warp area for the warp, purely cosmetic
|
||||
@tparam string warp_id the uid of the warp you want the area for
|
||||
|
||||
@usage-- Adding a warp area for a warp
|
||||
Warps.make_warp_area(warp_id)
|
||||
|
||||
]]
|
||||
function Warps.make_warp_area(warp_id)
|
||||
local warp = WrapData:get(warp_id)
|
||||
local surface = warp.surface
|
||||
local position = warp.position
|
||||
local posx = position.x
|
||||
local posy = position.y
|
||||
|
||||
-- Get the tile that is being replaced, store.update not needed as we don't want it to trigger
|
||||
local old_tile = surface.get_tile(position).name
|
||||
warp.old_tile = old_tile
|
||||
|
||||
-- Add a tile pattern on top of the base
|
||||
local tiles = {}
|
||||
for _, tile in pairs(config.tiles) do
|
||||
table.insert(tiles, {name=tile[1], position={tile[2]+posx, tile[3]+posy}})
|
||||
end
|
||||
surface.set_tiles(tiles)
|
||||
|
||||
-- Add entities to the warp structure
|
||||
for _, entity in pairs(config.entities) do
|
||||
entity = surface.create_entity{
|
||||
name=entity[1],
|
||||
position={entity[2]+posx, entity[3]+posy},
|
||||
force='neutral'
|
||||
}
|
||||
entity.destructible = false
|
||||
entity.health = 0
|
||||
entity.minable = false
|
||||
entity.rotatable = false
|
||||
|
||||
-- Save reference of the last power pole
|
||||
if entity.type == 'electric-pole' then
|
||||
warp.electric_pole = entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[-- Remove the warp area for a warp
|
||||
@tparam string warp_id the uid of the warp that you want to remove the area for
|
||||
|
||||
@usage-- Remove the warp area for a warp
|
||||
Warps.remove_warp_area(warp_id)
|
||||
|
||||
]]
|
||||
function Warps.remove_warp_area(warp_id)
|
||||
local warp = WrapData:get(warp_id)
|
||||
local position = warp.position
|
||||
local surface = warp.surface
|
||||
local radius = config.standard_proximity_radius
|
||||
|
||||
-- Check that a warp area was created previously
|
||||
local old_tile = warp.old_tile
|
||||
if not old_tile then return end
|
||||
|
||||
-- Restore the original tiles before the creation of the warp
|
||||
local tiles = {}
|
||||
|
||||
for _, tile in pairs(config.tiles) do
|
||||
table.insert(tiles, {name=old_tile, position={tile[2]+position.x, tile[3]+position.y}})
|
||||
end
|
||||
|
||||
surface.set_tiles(tiles)
|
||||
|
||||
local area = {
|
||||
{position.x-radius, position.y-radius},
|
||||
{position.x+radius, position.y+radius}
|
||||
}
|
||||
|
||||
-- Remove warp structure entities
|
||||
local entities = surface.find_entities_filtered{ force='neutral', area=area, name = remove_warp_area_entity_names }
|
||||
for _, entity in pairs(entities) do
|
||||
-- Destroy them, this will leave corpses of the entities that it destroyed.
|
||||
if entity and entity.valid and entity.destructible == false then
|
||||
entity.destructible = true
|
||||
entity.die(entity.force)
|
||||
end
|
||||
end
|
||||
-- Rechart map area, useful if warp is not covered by a radar
|
||||
game.forces[warp.force_name].chart(surface, area)
|
||||
end
|
||||
|
||||
--[[-- Set a warp to be the spawn point for a force, force must own this warp
|
||||
@tparam string warp_id the uid of the warp that you want to be the spawn for the force
|
||||
@tparam LuaForce force the force that you want to set the spawn for
|
||||
|
||||
@usage-- Set your forces spawn to a warp
|
||||
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 = WrapData:get(warp_id)
|
||||
if warp.force_name ~= force.name then
|
||||
return
|
||||
end
|
||||
-- Set this warp as the spawn
|
||||
local warp_ids = force_warps[warp.force_name]
|
||||
if not warp_ids then
|
||||
warp_ids = {}
|
||||
force_warps[warp.force_name] = warp_ids
|
||||
end
|
||||
warp_ids.spawn = warp_id
|
||||
-- Set the forces spawn to this warp
|
||||
force.set_spawn_position(warp.position, warp.surface)
|
||||
end
|
||||
|
||||
--[[-- Teleport a player to a warp point
|
||||
@tparam string warp_id the uid of the warp to send the player to
|
||||
@tparam LuaPlayer player the player to teleport to the warp
|
||||
|
||||
@usage-- Teleport yourself to a warp point
|
||||
Warps.teleport_player(warp_id, game.player)
|
||||
|
||||
]]
|
||||
function Warps.teleport_player(warp_id, player)
|
||||
local warp = WrapData:get(warp_id)
|
||||
local surface = warp.surface
|
||||
local position = {
|
||||
x=warp.position.x+0.5,
|
||||
y=warp.position.y+0.5
|
||||
}
|
||||
|
||||
if player.vehicle then
|
||||
-- Teleport the entity
|
||||
local entity = player.vehicle
|
||||
local goto_position = surface.find_non_colliding_position(entity.name, position, 32, 1)
|
||||
-- Surface teleport can only be done for players and cars at the moment. (with surface as an peramitor it gives this error)
|
||||
if entity.type == "car" then
|
||||
entity.teleport(goto_position, surface)
|
||||
elseif surface.index == entity.surface.index then
|
||||
-- Try teleport the entity
|
||||
if not entity.teleport(goto_position) then
|
||||
player.driving = false
|
||||
-- Need to calculate new goto_position because entities have different collision boxes
|
||||
goto_position = surface.find_non_colliding_position('character', position, 32, 1)
|
||||
player.teleport(goto_position, surface)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Teleport the player
|
||||
local goto_position = surface.find_non_colliding_position('character', position, 32, 1)
|
||||
if player.driving then player.driving = false end
|
||||
player.teleport(goto_position, surface)
|
||||
end
|
||||
end
|
||||
|
||||
--- Setters.
|
||||
-- functions used to created and alter warps
|
||||
-- @section setters
|
||||
|
||||
--[[-- Add a new warp for a force, the warp must have a surface and a position
|
||||
@tparam string force_name the name of the force to add the warp for
|
||||
@tparam LuaSurface surface the surface that the warp will be on
|
||||
@tparam Concepts.Position position the position that the warp will be on
|
||||
@tparam[opt] string player_name the name of the player that is adding the warp
|
||||
@tparam[opt] string warp_name the name of the warp that is being added, if omited default is used
|
||||
@treturn string the uid of the warp which was created
|
||||
|
||||
@usage-- Adding a new warp for your force at your position
|
||||
local player = game.player
|
||||
local warp_id = Warps.add_warp(player.force.name, player.surface, player.position, player.name)
|
||||
|
||||
]]
|
||||
function Warps.add_warp(force_name, surface, position, player_name, warp_name)
|
||||
-- Get new warp id
|
||||
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
|
||||
local warp_ids = force_warps[force_name]
|
||||
if not warp_ids then
|
||||
warp_ids = {}
|
||||
force_warps[force_name] = warp_ids
|
||||
end
|
||||
|
||||
-- Insert the warp id into the force warps
|
||||
table.insert(warp_ids, warp_id)
|
||||
|
||||
-- Create the editing table
|
||||
local editing = {}
|
||||
if player_name then
|
||||
editing[player_name] = true
|
||||
end
|
||||
|
||||
-- Add the new warp to the store
|
||||
WrapData:set(warp_id, {
|
||||
warp_id = warp_id,
|
||||
force_name = force_name,
|
||||
name = warp_name,
|
||||
icon = { type = config.default_icon.type, name = config.default_icon.name },
|
||||
surface = surface,
|
||||
position = {
|
||||
x = math.floor(position.x),
|
||||
y = math.floor(position.y)
|
||||
},
|
||||
last_edit_name = player_name or '<server>',
|
||||
last_edit_time = game.tick,
|
||||
currently_editing = editing,
|
||||
updates = 0,
|
||||
})
|
||||
|
||||
return warp_id
|
||||
end
|
||||
|
||||
--[[-- Removes a warp and any data linked to it
|
||||
@tparam string warp_id the uid of the warp that you want to remove
|
||||
|
||||
@usage-- Removing a warp
|
||||
Warps.remove_warp(warp_id)
|
||||
|
||||
]]
|
||||
function Warps.remove_warp(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)
|
||||
WrapData:remove(warp_id)
|
||||
table.remove_element(force_warps[force_name], warp_id)
|
||||
end
|
||||
|
||||
--[[-- Update the name and icon for a warp
|
||||
@tparam string warp_id the uid of the warp that you want to update
|
||||
@tparam[opt] string new_name the new name that you want the warp to have
|
||||
@tparam[opt] string new_icon the new icon that you want the warp to have
|
||||
@tparam[opt='server'] string player_name the name of the player that made the edit
|
||||
|
||||
@usage-- Changing the name and icon for a warp
|
||||
Warps.update_warp(warp_id, 'My Warp', 'iron-plate', game.player.name)
|
||||
|
||||
]]
|
||||
function Warps.update_warp(warp_id, new_name, new_icon, player_name)
|
||||
WrapData:update(warp_id, function(_, warp)
|
||||
-- If the icon is not valid then replace with the old icon
|
||||
if new_icon and not new_icon.name or not new_icon.type then
|
||||
new_icon = warp.icon
|
||||
end
|
||||
|
||||
warp.last_edit_name = player_name or '<server>'
|
||||
warp.last_edit_time = game.tick
|
||||
warp.name = new_name or warp.name
|
||||
warp.icon = new_icon or warp.icon
|
||||
end)
|
||||
end
|
||||
|
||||
--[[-- Set the editing state for a player, can be used as a warning or to display a text field
|
||||
@tparam string warp_id the uid of the warp that you want to effect
|
||||
@tparam string player_name the name of the player you want to set the state for
|
||||
@tparam boolean state the new state to set editing to
|
||||
|
||||
@usage-- Setting your editing state to true
|
||||
Warps.set_editing(warp_id, game.player.name, true)
|
||||
|
||||
]]
|
||||
function Warps.set_editing(warp_id, player_name, state)
|
||||
WrapData:update(warp_id, function(_, warp)
|
||||
warp.currently_editing[player_name] = state
|
||||
end)
|
||||
end
|
||||
|
||||
--[[-- Adds an update handler for when a warp is added, removed, or updated
|
||||
@tparam function handler the handler which is called when a warp is updated
|
||||
|
||||
@usage-- Add a game print when a warp is updated
|
||||
Warps.on_update(function(warp)
|
||||
game.print(warp.force_name..' now has the warp: '..warp.name)
|
||||
end)
|
||||
|
||||
]]
|
||||
function Warps.on_update(handler)
|
||||
WrapData:on_update(handler)
|
||||
end
|
||||
|
||||
--- Getters.
|
||||
-- function used to get information about warps
|
||||
-- @section getters
|
||||
|
||||
--[[-- Gets the warp information that is linked with this id
|
||||
@tparam string warp_id the uid of the warp you want to get
|
||||
@treturn table the warp information
|
||||
|
||||
@usage-- Getting warp information outside of on_update
|
||||
local warp = Warps.get_warp(warp_id)
|
||||
|
||||
]]
|
||||
function Warps.get_warp(warp_id)
|
||||
return WrapData:get(warp_id)
|
||||
end
|
||||
|
||||
--[[-- Gets all the warp ids that a force has
|
||||
@tparam string force_name the name of the force that you want the warp ids for
|
||||
@treturn table an array of all the warp ids
|
||||
|
||||
@usage-- Getting the warp ids for a force
|
||||
local warp_ids = Warps.get_force_warp_ids(game.player.force.name)
|
||||
|
||||
]]
|
||||
function Warps.get_force_warp_ids(force_name)
|
||||
return force_warps[force_name] or {}
|
||||
end
|
||||
|
||||
--[[-- Get the id of the spawn warp
|
||||
@tparam string force_name the name of the force that you want to get the spawn warp for
|
||||
@treturn ?string|nil the uid of the spawn warp for this force if there is one
|
||||
|
||||
@usage-- Getting the spawn warp id
|
||||
local spawn_id = Warps.get_spawn_warp_id(game.player.force.name)
|
||||
|
||||
]]
|
||||
function Warps.get_spawn_warp_id(force_name)
|
||||
local warp_ids = force_warps[force_name] or {}
|
||||
return warp_ids.spawn
|
||||
end
|
||||
|
||||
--[[-- Gets the editing state for a player
|
||||
@tparam string warp_id the uid of the warp you want to check
|
||||
@tparam string player_name the name of the player that you want to check
|
||||
@treturn boolean weather the player is currently editing this warp
|
||||
|
||||
@usage-- Check if a player is editing a warp or not
|
||||
local editing = Warps.get_editing(warp_id, game.player.name)
|
||||
|
||||
]]
|
||||
function Warps.get_editing(warp_id, player_name)
|
||||
local warp = WrapData:get(warp_id)
|
||||
return warp.currently_editing[player_name]
|
||||
end
|
||||
|
||||
-- Module return
|
||||
return Warps
|
||||
Reference in New Issue
Block a user