Move files to exp_legacy
This commit is contained in:
54
exp_legacy/module/modules/addons/advanced-start.lua
Normal file
54
exp_legacy/module/modules/addons/advanced-start.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--- Adds a better method of player starting items based on production levels.
|
||||
-- @addon Advanced-Start
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.advanced_start' --- @dep config.advanced_start
|
||||
local items = config.items
|
||||
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
-- game init settings
|
||||
if event.player_index == 1 then
|
||||
player.force.friendly_fire = config.friendly_fire
|
||||
game.map_settings.enemy_expansion.enabled = config.enemy_expansion
|
||||
local r = config.chart_radius
|
||||
local p = player.position
|
||||
player.force.chart(player.surface, {{p.x-r, p.y-r}, {p.x+r, p.y+r}})
|
||||
end
|
||||
-- spawn items
|
||||
for item, callback in pairs(items) do
|
||||
if type(callback) == 'function' then
|
||||
local stats = player.force.item_production_statistics
|
||||
local made = stats.get_input_count(item)
|
||||
local success, count = pcall(callback, made, stats.get_input_count, player)
|
||||
count = math.floor(count)
|
||||
if success and count > 0 then
|
||||
player.insert{name=item, count=count}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if config.armor.enable then
|
||||
player.insert{name=config.armor.main, count=1}
|
||||
|
||||
for _, item in pairs(config.armor.item) do
|
||||
player.insert{name=item.equipment, count=item.count}
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on_init(function()
|
||||
remote.call('freeplay', 'set_created_items', {})
|
||||
remote.call('freeplay', 'set_chart_distance', 0)
|
||||
remote.call('freeplay', 'set_skip_intro', config.skip_intro)
|
||||
if config.research_queue_from_start then
|
||||
for _, force in pairs(game.forces) do
|
||||
force.research_queue_enabled = true
|
||||
end
|
||||
end
|
||||
if not config.disable_base_game_silo_script then
|
||||
if config.skip_victory then
|
||||
remote.call('silo_script', 'set_no_victory', true)
|
||||
end
|
||||
end
|
||||
end)
|
||||
66
exp_legacy/module/modules/addons/afk-kick.lua
Normal file
66
exp_legacy/module/modules/addons/afk-kick.lua
Normal file
@@ -0,0 +1,66 @@
|
||||
--- Kicks players when all players on the server are afk
|
||||
-- @addon afk-kick
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local config = require 'config.afk_kick' --- @dep config.afk_kick
|
||||
local Async = require 'expcore.async' --- @dep expcore.async
|
||||
|
||||
--- Optional roles require
|
||||
local Roles
|
||||
if config.active_role then
|
||||
Roles = require 'expcore.roles'
|
||||
end
|
||||
|
||||
--- Globals
|
||||
local primitives = { last_active = 0 }
|
||||
Global.register(primitives, function(tbl)
|
||||
primitives = tbl
|
||||
end)
|
||||
|
||||
--- Kicks an afk player, used to add a delay so the gui has time to appear
|
||||
local kick_player =
|
||||
Async.register(function(player)
|
||||
if game.tick - primitives.last_active < config.kick_time then return end -- Safety Catch
|
||||
game.kick_player(player, 'AFK while no active players on the server')
|
||||
end)
|
||||
|
||||
--- Check for an active player every update_time number of ticks
|
||||
Event.on_nth_tick(config.update_time, function()
|
||||
-- Check for active players
|
||||
for _, player in ipairs(game.connected_players) do
|
||||
if player.afk_time < config.afk_time
|
||||
or config.admin_as_active and player.admin
|
||||
or config.trust_as_active and player.online_time > config.trust_time
|
||||
or config.active_role and (Roles.get_player_highest_role(player).index >= Roles.get_role_from_any(config.active_role).index) then
|
||||
-- Active player was found
|
||||
primitives.last_active = game.tick
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- No active player was found, check if players should be kicked
|
||||
if game.tick - primitives.last_active < config.kick_time then return end
|
||||
|
||||
-- Kick time exceeded, kick all players
|
||||
for _, player in ipairs(game.connected_players) do
|
||||
-- Add a frame to say why the player was kicked
|
||||
local res = player.display_resolution
|
||||
local uis = player.display_scale
|
||||
player.gui.screen.add{
|
||||
type = 'frame',
|
||||
name = 'afk-kick',
|
||||
caption = {'afk-kick.message'},
|
||||
}.location = { x=res.width*(0.5 - 0.11*uis), y=res.height*(0.5 - 0.14*uis) }
|
||||
|
||||
-- Kick the player, some delay needed because network delay
|
||||
Async.wait(10, kick_player, player)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Remove the screen gui if it is present
|
||||
Event.add(defines.events.on_player_joined_game, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
local frame = player.gui.screen["afk-kick"]
|
||||
if frame and frame.valid then frame.destroy() end
|
||||
end)
|
||||
38
exp_legacy/module/modules/addons/chat-popups.lua
Normal file
38
exp_legacy/module/modules/addons/chat-popups.lua
Normal file
@@ -0,0 +1,38 @@
|
||||
--- Creates flying text entities when a player sends a message in chat;
|
||||
-- also displays a ping above users who are named in the message
|
||||
-- @addon Chat-Popups
|
||||
|
||||
local Game = require 'utils.game' --- @dep utils.game
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.popup_messages' --- @dep config.popup_messages
|
||||
|
||||
local send_text = Game.print_player_floating_text -- (player_index, text, color)
|
||||
|
||||
Event.add(defines.events.on_console_chat, function(event)
|
||||
if not event.player_index or event.player_index < 1 then return end
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
-- Some basic sanity checks
|
||||
if not player then return end
|
||||
if not event.message then return end
|
||||
|
||||
-- Sends the message as text above them
|
||||
if config.show_player_messages then
|
||||
send_text(player.index, {'chat-popup.message', player.name, event.message}, player.chat_color)
|
||||
end
|
||||
|
||||
if not config.show_player_mentions then return end
|
||||
|
||||
-- Makes lower and removes white space from the message
|
||||
local search_string = event.message:lower():gsub("%s+", "")
|
||||
|
||||
-- Loops over online players to see if they name is included
|
||||
for _, mentioned_player in pairs(game.connected_players) do
|
||||
if mentioned_player.index ~= player.index then
|
||||
if search_string:find(mentioned_player.name:lower(), 1, true) then
|
||||
send_text(mentioned_player.index, {'chat-popup.ping', player.name}, player.chat_color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
52
exp_legacy/module/modules/addons/chat-reply.lua
Normal file
52
exp_legacy/module/modules/addons/chat-reply.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--- Adds auto replies to chat messages; as well as chat commands
|
||||
-- @addon Chat-Reply
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local config = require 'config.chat_reply' --- @dep config.chat_reply
|
||||
|
||||
Event.add(defines.events.on_console_chat, function(event)
|
||||
local player_index = event.player_index
|
||||
if not player_index or player_index < 1 then return end
|
||||
local player = game.players[player_index]
|
||||
local message = event.message:lower():gsub("%s+", "")
|
||||
local allowed = true
|
||||
if config.command_admin_only and not player.admin then allowed = false end
|
||||
if config.command_permission and not Roles.player_allowed(player, config.command_permission) then allowed = false end
|
||||
|
||||
local prefix = config.command_prefix
|
||||
for key_word, reply in pairs(config.messages) do
|
||||
if message:find(key_word) then
|
||||
local is_command = message:find(prefix..key_word)
|
||||
if type(reply) == 'function' then
|
||||
reply = reply(player, is_command)
|
||||
end
|
||||
|
||||
if is_command and allowed then
|
||||
game.print{'chat-bot.reply', reply}
|
||||
elseif is_command then
|
||||
player.print{'chat-bot.disallow'}
|
||||
elseif not allowed then
|
||||
player.print{'chat-bot.reply', reply}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not allowed then return end
|
||||
|
||||
for key_word, reply in pairs(config.commands) do
|
||||
if message:find(prefix..key_word) then
|
||||
if type(reply) == 'function' then
|
||||
local msg = reply(player, true)
|
||||
|
||||
if reply then
|
||||
game.print{'chat-bot.reply', msg}
|
||||
end
|
||||
|
||||
else
|
||||
game.print{'chat-bot.reply', reply}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end)
|
||||
106
exp_legacy/module/modules/addons/compilatron.lua
Normal file
106
exp_legacy/module/modules/addons/compilatron.lua
Normal file
@@ -0,0 +1,106 @@
|
||||
--- Adds a compilatron that walks around the spawn area; adapted from redmew code
|
||||
-- @addon Compilatron
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Task = require 'utils.task' --- @dep utils.task
|
||||
local Token = require 'utils.token' --- @dep utils.token
|
||||
local config = require 'config.compilatron' --- @dep config.compilatron
|
||||
local messages = config.messages
|
||||
local locations = config.locations
|
||||
|
||||
local Public = {
|
||||
compilatrons={},
|
||||
current_messages={}
|
||||
}
|
||||
|
||||
Global.register({
|
||||
compilatrons = Public.compilatrons,
|
||||
current_messages = Public.current_messages
|
||||
}, function(tbl)
|
||||
Public.compilatrons = tbl.compilatrons
|
||||
Public.current_messages = tbl.current_messages
|
||||
end)
|
||||
|
||||
--- This will re-create the speech bubble after it de-spawns called with set_timeout
|
||||
local callback =
|
||||
Token.register(
|
||||
function(data)
|
||||
local ent = data.ent
|
||||
local name = data.name
|
||||
local msg_number = data.msg_number
|
||||
local message =
|
||||
ent.surface.create_entity(
|
||||
{name = 'compi-speech-bubble', text = messages[name][msg_number], position = {0, 0}, source = ent}
|
||||
)
|
||||
Public.current_messages[name] = {message = message, msg_number = msg_number}
|
||||
end
|
||||
)
|
||||
|
||||
--- This will move the messages onto the next message in the loop
|
||||
local function circle_messages()
|
||||
for name, ent in pairs(Public.compilatrons) do
|
||||
if not ent.valid then
|
||||
Public.spawn_compilatron(game.players[1].surface, name)
|
||||
end
|
||||
local current_message = Public.current_messages[name]
|
||||
local msg_number
|
||||
local message
|
||||
if current_message ~= nil then
|
||||
message = current_message.message
|
||||
if message ~= nil then
|
||||
message.destroy()
|
||||
end
|
||||
msg_number = current_message.msg_number
|
||||
msg_number = (msg_number < #messages[name]) and msg_number + 1 or 1
|
||||
else
|
||||
msg_number = 1
|
||||
end
|
||||
-- this calls the callback above to re-spawn the message after some time
|
||||
Task.set_timeout_in_ticks(300, callback, {ent = ent, name = name, msg_number = msg_number})
|
||||
end
|
||||
end
|
||||
|
||||
Event.on_nth_tick(config.message_cycle, circle_messages)
|
||||
|
||||
--- This will add a compilatron to the global and start his message cycle
|
||||
-- @tparam LuaEntity entity the compilatron entity that moves around
|
||||
-- @tparam string name the name of the location that the compilatron is at
|
||||
function Public.add_compilatron(entity, name)
|
||||
if not entity and not entity.valid then
|
||||
return
|
||||
end
|
||||
|
||||
if name == nil then
|
||||
return
|
||||
end
|
||||
|
||||
Public.compilatrons[name] = entity
|
||||
local message =
|
||||
entity.surface.create_entity(
|
||||
{name = 'compi-speech-bubble', text = messages[name][1], position = {0, 0}, source = entity}
|
||||
)
|
||||
Public.current_messages[name] = {message = message, msg_number = 1}
|
||||
end
|
||||
|
||||
--- This spawns a new compilatron on a surface with the given location tag (not a position)
|
||||
-- @tparam LuaSurface surface the surface to spawn the compilatron on
|
||||
-- @tparam string location the location tag that is in the config file
|
||||
function Public.spawn_compilatron(surface, location)
|
||||
local position = locations[location]
|
||||
local pos = surface.find_non_colliding_position('compilatron', position, 1.5, 0.5)
|
||||
local compi = surface.create_entity {name='compilatron', position=pos, force=game.forces.neutral}
|
||||
Public.add_compilatron(compi, location)
|
||||
end
|
||||
|
||||
-- When the first player is created this will create all compilatrons that are resisted in the config
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
if event.player_index ~= 1 then return end
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
for location in pairs(locations) do
|
||||
Public.spawn_compilatron(player.surface, location)
|
||||
end
|
||||
end)
|
||||
|
||||
return Public
|
||||
42
exp_legacy/module/modules/addons/damage-popups.lua
Normal file
42
exp_legacy/module/modules/addons/damage-popups.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
--- Displays the amount of dmg that is done by players to entities;
|
||||
-- also shows player health when a player is attacked
|
||||
-- @addon Damage-Popups
|
||||
|
||||
local Game = require 'utils.game' --- @dep utils.game
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.popup_messages' --- @dep config.popup_messages
|
||||
|
||||
Event.add(defines.events.on_entity_damaged, function(event)
|
||||
local entity = event.entity
|
||||
local cause = event.cause
|
||||
local damage = math.floor(event.original_damage_amount)
|
||||
local health = math.floor(entity.health)
|
||||
local health_percentage = entity.get_health_ratio()
|
||||
local text_colour = {r=1-health_percentage, g=health_percentage, b=0}
|
||||
|
||||
-- Gets the location of the text
|
||||
local size = entity.get_radius()
|
||||
if size < 1 then size = 1 end
|
||||
local r = (math.random()-0.5)*size*config.damage_location_variance
|
||||
local p = entity.position
|
||||
local position = {x=p.x+r, y=p.y-size}
|
||||
|
||||
-- Sets the message
|
||||
local message
|
||||
if entity.name == 'character' and config.show_player_health then
|
||||
message = {'damage-popup.player-health', health}
|
||||
elseif entity.name ~= 'character' and cause and cause.name == 'character' and config.show_player_damage then
|
||||
message = {'damage-popup.player-damage', damage}
|
||||
end
|
||||
|
||||
-- Outputs the message as floating text
|
||||
if message then
|
||||
Game.print_floating_text(
|
||||
entity.surface,
|
||||
position,
|
||||
message,
|
||||
text_colour
|
||||
)
|
||||
end
|
||||
|
||||
end)
|
||||
149
exp_legacy/module/modules/addons/death-logger.lua
Normal file
149
exp_legacy/module/modules/addons/death-logger.lua
Normal file
@@ -0,0 +1,149 @@
|
||||
--- Makes markers on the map where places have died and reclaims items if not recovered
|
||||
-- @addon Death-Logger
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local config = require 'config.death_logger' --- @dep config.death_logger
|
||||
local format_time, move_items = _C.format_time, _C.move_items_stack --- @dep expcore.common
|
||||
|
||||
-- Max amount of ticks a corpse can be alive
|
||||
local corpse_lifetime = 60*60*15
|
||||
|
||||
local deaths = {
|
||||
archive={} -- deaths moved here after body is gone
|
||||
--{player_name='Cooldude2606', time_of_death='15H 15M', position={x=0, y=0}, corpse=LuaEntity, tag=LuaCustomChartTag}
|
||||
}
|
||||
Global.register(deaths, function(tbl)
|
||||
deaths = tbl
|
||||
end)
|
||||
|
||||
--- Creates a new death marker and saves it to the given death
|
||||
local function create_map_tag(death)
|
||||
local player = game.players[death.player_name]
|
||||
local message = player.name..' died'
|
||||
if config.include_time_of_death then
|
||||
local time = format_time(death.time_of_death, {hours=true, minutes=true, string=true})
|
||||
message = message..' at '..time
|
||||
end
|
||||
death.tag = player.force.add_chart_tag(player.surface, {
|
||||
position=death.position,
|
||||
icon=config.map_icon,
|
||||
text=message
|
||||
})
|
||||
end
|
||||
|
||||
--- Checks that all map tags are present and valid
|
||||
-- adds missing ones, deletes expired ones
|
||||
local function check_map_tags()
|
||||
for index, death in ipairs(deaths) do
|
||||
local map_tag = death.tag
|
||||
local corpse = death.corpse
|
||||
-- Check the corpse is valid
|
||||
if corpse and corpse.valid then
|
||||
-- Corpse is valid check the map tag
|
||||
if not map_tag or not map_tag.valid then
|
||||
-- Map tag is not valid make a new one
|
||||
create_map_tag(death)
|
||||
end
|
||||
else
|
||||
-- Corpse is not valid so remove the map tag
|
||||
if map_tag and map_tag.valid then
|
||||
map_tag.destroy()
|
||||
end
|
||||
-- Move the death to the archive
|
||||
death.corpse = nil
|
||||
death.tag = nil
|
||||
table.insert(deaths.archive, death)
|
||||
table.remove(deaths, index)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- when a player dies a new death is added to the records and a map marker is made
|
||||
Event.add(defines.events.on_player_died, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local corpse = player.surface.find_entity('character-corpse', player.position)
|
||||
if config.use_chests_as_bodies then
|
||||
local items = corpse.get_inventory(defines.inventory.character_corpse)
|
||||
local chest = move_items(items, corpse.surface, corpse.position)
|
||||
chest.destructible = false
|
||||
corpse.destroy()
|
||||
corpse = chest
|
||||
end
|
||||
local death = {
|
||||
player_name = player.name,
|
||||
time_of_death = event.tick,
|
||||
position = player.position,
|
||||
corpse = corpse
|
||||
}
|
||||
if config.show_map_markers then
|
||||
create_map_tag(death)
|
||||
end
|
||||
table.insert(deaths, death)
|
||||
|
||||
-- Draw a light attached to the corpse with the player color
|
||||
if config.show_light_at_corpse then
|
||||
rendering.draw_light{
|
||||
sprite = 'utility/light_medium',
|
||||
color = player.color,
|
||||
target = corpse,
|
||||
force = player.force,
|
||||
surface = player.surface
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
-- Draw lines to the player corpse
|
||||
if config.show_line_to_corpse then
|
||||
Event.add(defines.events.on_player_respawned, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
-- New deaths are added at the end of the deaths array, this is why
|
||||
-- we are itterating over the array in reverse. This saves on the amount
|
||||
-- of itterations we do.
|
||||
for index = #deaths, 1, -1 do
|
||||
local death = deaths[index]
|
||||
|
||||
-- If the corpse has already expired break out of the loop because
|
||||
-- all the deaths that will follow will be expired.
|
||||
if game.tick - death.time_of_death > corpse_lifetime then break end
|
||||
|
||||
-- Check if the death body is from the player
|
||||
-- Check if the corpse entity is still valid
|
||||
if death.player_name == player.name and death.corpse and death.corpse.valid then
|
||||
local line_color = player.color
|
||||
line_color.a = .3
|
||||
rendering.draw_line{
|
||||
color = line_color,
|
||||
from = player.character,
|
||||
to = death.corpse,
|
||||
players = { event.player_index },
|
||||
width = 2,
|
||||
dash_length = 1,
|
||||
gap_length = 1,
|
||||
surface = player.surface,
|
||||
draw_on_ground = true
|
||||
}
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- every 5 min all bodies are checked for valid map tags
|
||||
if config.show_map_markers then
|
||||
local check_period = 60*60*5 -- five minutes
|
||||
Event.on_nth_tick(check_period, function()
|
||||
check_map_tags()
|
||||
end)
|
||||
end
|
||||
|
||||
if config.auto_collect_bodies then
|
||||
Event.add(defines.events.on_character_corpse_expired, function(event)
|
||||
local corpse = event.corpse
|
||||
local items = corpse.get_inventory(defines.inventory.character_corpse)
|
||||
move_items(items, corpse.surface, {x=0, y=0})
|
||||
end)
|
||||
end
|
||||
|
||||
-- this is so other modules can access the logs
|
||||
return deaths
|
||||
141
exp_legacy/module/modules/addons/deconlog.lua
Normal file
141
exp_legacy/module/modules/addons/deconlog.lua
Normal file
@@ -0,0 +1,141 @@
|
||||
--- Log certain actions into a file when events are triggered
|
||||
-- @addon Deconlog
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
local config = require 'config.deconlog' --- @dep config.deconlog
|
||||
|
||||
local filepath = "log/decon.log"
|
||||
|
||||
local function add_log(data)
|
||||
game.write_file(filepath, data .. "\n", true, 0) -- write data
|
||||
end
|
||||
|
||||
local function get_secs()
|
||||
return format_time(game.tick, { hours = true, minutes = true, seconds = true, string = true })
|
||||
end
|
||||
|
||||
local function pos_to_string(pos)
|
||||
return tostring(pos.x) .. "," .. tostring(pos.y)
|
||||
end
|
||||
|
||||
local function pos_to_gps_string(pos)
|
||||
return '[gps=' .. string.format('%.1f', pos.x) .. ',' .. string.format('%.1f', pos.y) .. ']'
|
||||
end
|
||||
|
||||
--- Print a message to all players who match the value of admin
|
||||
local function print_to_players(admin, message)
|
||||
for _, player in ipairs(game.connected_players) do
|
||||
if player.admin == admin then
|
||||
player.print(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.on_init(function()
|
||||
game.write_file(filepath, "\n", false, 0) -- write data
|
||||
end)
|
||||
|
||||
if config.decon_area then
|
||||
Event.add(defines.events.on_player_deconstructed_area, function(e)
|
||||
if e.alt then
|
||||
return
|
||||
end
|
||||
|
||||
local player = game.get_player(e.player_index)
|
||||
|
||||
if Roles.player_has_flag(player, 'deconlog-bypass') then
|
||||
return
|
||||
end
|
||||
|
||||
local items = e.surface.find_entities_filtered{area=e.area, force=player.force}
|
||||
|
||||
if #items > 250 then
|
||||
print_to_players(true, {'deconlog.decon', player.name, e.surface.name, pos_to_gps_string(e.area.left_top), pos_to_gps_string(e.area.right_bottom), format_number(#items)})
|
||||
end
|
||||
|
||||
add_log(get_secs() .. ',' .. player.name .. ',decon_area,' .. e.surface.name .. ',' .. pos_to_string(e.area.left_top) .. ',' .. pos_to_string(e.area.right_bottom))
|
||||
end)
|
||||
end
|
||||
|
||||
if config.built_entity then
|
||||
Event.add(defines.events.on_built_entity, function (e)
|
||||
if not e.player_index then return end
|
||||
local player = game.get_player(e.player_index)
|
||||
if Roles.player_has_flag(player, "deconlog-bypass") then
|
||||
return
|
||||
end
|
||||
local ent = e.created_entity
|
||||
add_log(get_secs() .. "," .. player.name .. ",built_entity," .. ent.name .. "," .. pos_to_string(ent.position) .. "," .. tostring(ent.direction) .. "," .. tostring(ent.orientation))
|
||||
end)
|
||||
end
|
||||
|
||||
if config.mined_entity then
|
||||
Event.add(defines.events.on_player_mined_entity, function (e)
|
||||
local player = game.get_player(e.player_index)
|
||||
if Roles.player_has_flag(player, "deconlog-bypass") then
|
||||
return
|
||||
end
|
||||
local ent = e.entity
|
||||
add_log(get_secs() .. "," .. player.name .. ",mined_entity," .. ent.name .. "," .. pos_to_string(ent.position) .. "," .. tostring(ent.direction) .. "," .. tostring(ent.orientation))
|
||||
end)
|
||||
end
|
||||
|
||||
if config.fired_rocket then
|
||||
Event.add(defines.events.on_player_ammo_inventory_changed, function (e)
|
||||
local player = game.get_player(e.player_index)
|
||||
if Roles.player_has_flag(player, "deconlog-bypass") then
|
||||
return
|
||||
end
|
||||
local ammo_inv = player.get_inventory(defines.inventory.character_ammo)
|
||||
local item = ammo_inv[player.character.selected_gun_index]
|
||||
if not item or not item.valid or not item.valid_for_read then
|
||||
return
|
||||
end
|
||||
if item.name == "rocket" then
|
||||
add_log(get_secs() .. "," .. player.name .. ",shot-rocket," .. pos_to_string(player.position) .. "," .. pos_to_string(player.shooting_state.position))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if config.fired_explosive_rocket then
|
||||
Event.add(defines.events.on_player_ammo_inventory_changed, function (e)
|
||||
local player = game.get_player(e.player_index)
|
||||
|
||||
if Roles.player_has_flag(player, "deconlog-bypass") then
|
||||
return
|
||||
end
|
||||
local ammo_inv = player.get_inventory(defines.inventory.character_ammo)
|
||||
local item = ammo_inv[player.character.selected_gun_index]
|
||||
|
||||
if not item or not item.valid or not item.valid_for_read then
|
||||
return
|
||||
end
|
||||
if item.name == "explosive-rocket" then
|
||||
add_log(get_secs() .. "," .. player.name .. ",shot-explosive-rocket," .. pos_to_string(player.position) .. "," .. pos_to_string(player.shooting_state.position))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if config.fired_nuke then
|
||||
Event.add(defines.events.on_player_ammo_inventory_changed, function (e)
|
||||
local player = game.get_player(e.player_index)
|
||||
|
||||
if Roles.player_has_flag(player, "deconlog-bypass") then
|
||||
return
|
||||
end
|
||||
|
||||
local ammo_inv = player.get_inventory(defines.inventory.character_ammo)
|
||||
local item = ammo_inv[player.character.selected_gun_index]
|
||||
|
||||
if not item or not item.valid or not item.valid_for_read then
|
||||
return
|
||||
end
|
||||
|
||||
if item.name == "atomic-bomb" then
|
||||
add_log(get_secs() .. "," .. player.name .. ",shot-nuke," .. pos_to_string(player.position) .. "," .. pos_to_string(player.shooting_state.position))
|
||||
end
|
||||
end)
|
||||
end
|
||||
303
exp_legacy/module/modules/addons/discord-alerts.lua
Normal file
303
exp_legacy/module/modules/addons/discord-alerts.lua
Normal file
@@ -0,0 +1,303 @@
|
||||
--- Sends alert messages to our discord server when certain events are triggered
|
||||
-- @addon Discord-Alerts
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local write_json, format_time = _C.write_json, _C.format_time --- @dep expcore.common
|
||||
local config = require 'config.discord_alerts' --- @dep config.discord_alerts
|
||||
|
||||
local playtime_format = {hours = true, minutes = true, short = true, string = true}
|
||||
|
||||
local function append_playtime(player_name)
|
||||
if not config.show_playtime then
|
||||
return player_name
|
||||
end
|
||||
|
||||
local player = game.get_player(player_name)
|
||||
|
||||
if not player then
|
||||
return player_name
|
||||
end
|
||||
|
||||
return player.name ..' (' .. format_time(player.online_time, playtime_format) .. ')'
|
||||
end
|
||||
|
||||
local function get_player_name(event)
|
||||
local player = game.players[event.player_index]
|
||||
return player.name, event.by_player_name
|
||||
end
|
||||
|
||||
local function to_hex(color)
|
||||
local hex_digits = '0123456789ABCDEF'
|
||||
local function hex(bit)
|
||||
local major, minor = math.modf(bit/16)
|
||||
major, minor = major+1, minor*16+1
|
||||
return hex_digits:sub(major, major) .. hex_digits:sub(minor, minor)
|
||||
end
|
||||
|
||||
return '0x' .. hex(color.r) .. hex(color.g) .. hex(color.b)
|
||||
end
|
||||
|
||||
local function emit_event(args)
|
||||
local title = args.title or ''
|
||||
local color = args.color or '0x0'
|
||||
local description = args.description or ''
|
||||
|
||||
if type(color) == 'table' then
|
||||
color = to_hex(color)
|
||||
end
|
||||
|
||||
local tick = args.tick or game.tick
|
||||
local tick_formatted = format_time(tick, {days = false, hours = true, minutes = true, short = true, string = true})
|
||||
|
||||
local players_online = 0
|
||||
local admins_online = 0
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
players_online = players_online + 1
|
||||
|
||||
if player.admin then
|
||||
admins_online = admins_online + 1
|
||||
end
|
||||
end
|
||||
|
||||
local done = {title=true, color=true, description=true}
|
||||
local fields = {{
|
||||
name='Server Details',
|
||||
value=string.format('Server: ${serverName} Time: %s\nTotal: %d Online: %d Admins: %d', tick_formatted, #game.players, players_online, admins_online)
|
||||
}}
|
||||
|
||||
for key, value in pairs(args) do
|
||||
if not done[key] then
|
||||
done[key] = true
|
||||
local field = {
|
||||
name=key,
|
||||
value=value,
|
||||
inline=false
|
||||
}
|
||||
|
||||
local new_value, inline = value:gsub('<inline>', '', 1)
|
||||
if inline > 0 then
|
||||
field.value = new_value
|
||||
field.inline = true
|
||||
end
|
||||
|
||||
table.insert(fields, field)
|
||||
end
|
||||
end
|
||||
|
||||
write_json('ext/discord.out',{
|
||||
title=title,
|
||||
description=description,
|
||||
color=color,
|
||||
fields=fields
|
||||
})
|
||||
end
|
||||
|
||||
--- Repeated protected entity mining
|
||||
if config.entity_protection then
|
||||
local EntityProtection = require 'modules.control.protection' --- @dep modules.control.protection
|
||||
Event.add(EntityProtection.events.on_repeat_violation, function(event)
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Entity Protection',
|
||||
description='A player removed protected entities',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>' .. append_playtime(player_name),
|
||||
['Entity']='<inline>' .. event.entity.name,
|
||||
['Location']='X ' .. event.entity.position.x .. ' Y ' .. event.entity.position.y
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- Reports added and removed
|
||||
if config.player_reports then
|
||||
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
|
||||
Event.add(Reports.events.on_player_reported, function(event)
|
||||
local player_name, by_player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Report',
|
||||
description='A player was reported',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>' .. append_playtime(player_name),
|
||||
['By']='<inline>' .. append_playtime(by_player_name),
|
||||
['Report Count']='<inline>' .. Reports.count_reports(player_name),
|
||||
['Reason']=event.reason
|
||||
}
|
||||
end)
|
||||
Event.add(Reports.events.on_report_removed, function(event)
|
||||
if event.batch ~= 1 then return end
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Reports Removed',
|
||||
description='A player has a report removed',
|
||||
color=Colors.green,
|
||||
['Player']='<inline>' .. player_name,
|
||||
['By']='<inline>' .. event.removed_by_name,
|
||||
['Report Count']='<inline>' .. event.batch_count
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- Warnings added and removed
|
||||
if config.player_warnings then
|
||||
local Warnings = require 'modules.control.warnings' --- @dep modules.control.warnings
|
||||
Event.add(Warnings.events.on_warning_added, function(event)
|
||||
local player_name, by_player_name = get_player_name(event)
|
||||
local player = game.get_player(player_name)
|
||||
emit_event{
|
||||
title='Warning',
|
||||
description='A player has been given a warning',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>' .. player_name,
|
||||
['By']='<inline>' .. by_player_name,
|
||||
['Warning Count']='<inline>' .. Warnings.count_warnings(player),
|
||||
['Reason']=event.reason
|
||||
}
|
||||
end)
|
||||
Event.add(Warnings.events.on_warning_removed, function(event)
|
||||
if event.batch ~= 1 then return end
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Warnings Removed',
|
||||
description='A player has a warning removed',
|
||||
color=Colors.green,
|
||||
['Player']='<inline>' .. player_name,
|
||||
['By']='<inline>' .. event.removed_by_name,
|
||||
['Warning Count']='<inline>' .. event.batch_count
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- When a player is jailed or unjailed
|
||||
if config.player_jail then
|
||||
local Jail = require 'modules.control.jail'
|
||||
Event.add(Jail.events.on_player_jailed, function(event)
|
||||
local player_name, by_player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Jail',
|
||||
description='A player has been jailed',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>' .. player_name,
|
||||
['By']='<inline>' .. by_player_name,
|
||||
['Reason']=event.reason
|
||||
}
|
||||
end)
|
||||
Event.add(Jail.events.on_player_unjailed, function(event)
|
||||
local player_name, by_player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Unjail',
|
||||
description='A player has been unjailed',
|
||||
color=Colors.green,
|
||||
['Player']='<inline>' .. player_name,
|
||||
['By']='<inline>' .. by_player_name
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- Ban and unban
|
||||
if config.player_bans then
|
||||
Event.add(defines.events.on_player_banned, function(event)
|
||||
if event.by_player then
|
||||
local by_player = game.players[event.by_player]
|
||||
emit_event{
|
||||
title='Banned',
|
||||
description='A player has been banned',
|
||||
color=Colors.red,
|
||||
['Player']='<inline>' .. event.player_name,
|
||||
['By']='<inline>' .. by_player.name,
|
||||
['Reason']=event.reason
|
||||
}
|
||||
end
|
||||
end)
|
||||
Event.add(defines.events.on_player_unbanned, function(event)
|
||||
if event.by_player then
|
||||
local by_player = game.players[event.by_player]
|
||||
emit_event{
|
||||
title='Un-Banned',
|
||||
description='A player has been un-banned',
|
||||
color=Colors.green,
|
||||
['Player']='<inline>' .. event.player_name,
|
||||
['By']='<inline>' .. by_player.name
|
||||
}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Mute and unmute
|
||||
if config.player_mutes then
|
||||
Event.add(defines.events.on_player_muted, function(event)
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Muted',
|
||||
description='A player has been muted',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>' .. player_name
|
||||
}
|
||||
end)
|
||||
Event.add(defines.events.on_player_unmuted, function(event)
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Un-Muted',
|
||||
description='A player has been un-muted',
|
||||
color=Colors.green,
|
||||
['Player']='<inline>' .. player_name
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- Kick
|
||||
if config.player_kicks then
|
||||
Event.add(defines.events.on_player_kicked, function(event)
|
||||
if event.by_player then
|
||||
local player_name = get_player_name(event)
|
||||
local by_player = game.players[event.by_player]
|
||||
emit_event{
|
||||
title='Kick',
|
||||
description='A player has been kicked',
|
||||
color=Colors.orange,
|
||||
['Player']='<inline>' .. player_name,
|
||||
['By']='<inline>' .. by_player.name,
|
||||
['Reason']=event.reason
|
||||
}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Promote and demote
|
||||
if config.player_promotes then
|
||||
Event.add(defines.events.on_player_promoted, function(event)
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Promote',
|
||||
description='A player has been promoted',
|
||||
color=Colors.green,
|
||||
['Player']='<inline>' .. player_name
|
||||
}
|
||||
end)
|
||||
Event.add(defines.events.on_player_demoted, function(event)
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Demote',
|
||||
description='A player has been demoted',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>' .. player_name
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- Other commands
|
||||
Event.add(defines.events.on_console_command, function(event)
|
||||
if event.player_index then
|
||||
local player_name = get_player_name(event)
|
||||
if config[event.command] then
|
||||
emit_event{
|
||||
title=event.command:gsub('^%l', string.upper),
|
||||
description='/' .. event.command .. ' was used',
|
||||
color=Colors.grey,
|
||||
['By']='<inline>' .. player_name,
|
||||
['Details'] = event.parameters ~= '' and event.parameters or nil
|
||||
}
|
||||
end
|
||||
end
|
||||
end)
|
||||
19
exp_legacy/module/modules/addons/fagc.lua
Normal file
19
exp_legacy/module/modules/addons/fagc.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
--- Allows the FAGC clientside bot to receive information about bans and unbans and propagate that information to other servers
|
||||
-- @addon FAGC
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
|
||||
-- Clear the file on startup to minimize its size
|
||||
Event.on_init(function()
|
||||
game.write_file("fagc-actions.txt", "", false, 0)
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_banned, function(e)
|
||||
local text = "ban;" .. e.player_name .. ";" .. (e.by_player or "") .. ";" .. (e.reason or "") .. "\n"
|
||||
game.write_file("fagc-actions.txt", text, true, 0)
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_unbanned, function(e)
|
||||
local text = "unban;" .. e.player_name .. ";" .. (e.by_player or "") .. ";" .. (e.reason or "") .. "\n"
|
||||
game.write_file("fagc-actions.txt", text, true, 0)
|
||||
end)
|
||||
23
exp_legacy/module/modules/addons/inserter.lua
Normal file
23
exp_legacy/module/modules/addons/inserter.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
local Event = require 'utils.event'
|
||||
|
||||
local controllers_with_inventory = {
|
||||
[defines.controllers.character] = true,
|
||||
[defines.controllers.god] = true,
|
||||
[defines.controllers.editor] = true,
|
||||
}
|
||||
|
||||
Event.add(defines.events.on_player_mined_entity, function(event)
|
||||
if (not event.entity.valid) or (event.entity.type ~= 'inserter') or event.entity.drop_target then
|
||||
return
|
||||
end
|
||||
|
||||
local item_entity = event.entity.surface.find_entity('item-on-ground', event.entity.drop_position)
|
||||
|
||||
if item_entity then
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
if controllers_with_inventory[player.controller_type] then
|
||||
player.mine_entity(item_entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
15
exp_legacy/module/modules/addons/inventory-clear.lua
Normal file
15
exp_legacy/module/modules/addons/inventory-clear.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
--- Will move players items to spawn when they are banned or kicked, option to clear on leave
|
||||
-- @addon Inventory-Clear
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local events = require 'config.inventory_clear' --- @dep config.inventory_clear
|
||||
local move_items_stack = _C.move_items_stack --- @dep expcore.common
|
||||
|
||||
local function clear_items(event)
|
||||
local player = game.players[event.player_index]
|
||||
local inv = player.get_main_inventory()
|
||||
move_items_stack(inv)
|
||||
inv.clear()
|
||||
end
|
||||
|
||||
for _, event_name in ipairs(events) do Event.add(event_name, clear_items) end
|
||||
59
exp_legacy/module/modules/addons/lawnmower.lua
Normal file
59
exp_legacy/module/modules/addons/lawnmower.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
--[[-- Addon Lawnmower
|
||||
- Adds a command that clean up biter corpse and nuclear hole
|
||||
@addon Lawnmower
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.lawnmower' --- @dep config.lawnmower
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
Commands.new_command('lawnmower', {'expcom-lawnmower.description'}, 'Clean up biter corpse, decoratives and nuclear hole')
|
||||
:add_param('range', false, 'integer-range', 1, 200)
|
||||
:register(function(player, range)
|
||||
local tile_to_do = {}
|
||||
|
||||
player.surface.destroy_decoratives({position=player.position, radius=range})
|
||||
|
||||
local entities = player.surface.find_entities_filtered{position=player.position, radius=range, type='corpse'}
|
||||
|
||||
for _, entity in pairs(entities) do
|
||||
if (entity.name ~= 'transport-caution-corpse' and entity.name ~= 'invisible-transport-caution-corpse') then
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
local tiles = player.surface.find_tiles_filtered{position=player.position, radius=range, name={'nuclear-ground'}}
|
||||
|
||||
for _, tile in pairs(tiles) do
|
||||
table.insert(tile_to_do, {name='grass-1', position=tile.position})
|
||||
end
|
||||
|
||||
player.surface.set_tiles(tile_to_do)
|
||||
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
local function destroy_decoratives(entity)
|
||||
if entity.type ~= 'entity-ghost' and entity.type ~= 'tile-ghost' and entity.prototype.selectable_in_game then
|
||||
entity.surface.destroy_decoratives{area=entity.selection_box}
|
||||
end
|
||||
end
|
||||
|
||||
if config.destroy_decoratives then
|
||||
Event.add(defines.events.on_built_entity, function(event)
|
||||
destroy_decoratives(event.created_entity)
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_robot_built_entity, function(event)
|
||||
destroy_decoratives(event.created_entity)
|
||||
end)
|
||||
|
||||
Event.add(defines.events.script_raised_built, function(event)
|
||||
destroy_decoratives(event.entity)
|
||||
end)
|
||||
|
||||
Event.add(defines.events.script_raised_revive, function(event)
|
||||
destroy_decoratives(event.entity)
|
||||
end)
|
||||
end
|
||||
71
exp_legacy/module/modules/addons/logging.lua
Normal file
71
exp_legacy/module/modules/addons/logging.lua
Normal file
@@ -0,0 +1,71 @@
|
||||
--[[-- Addon Logging
|
||||
@addon Logging
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.logging' --- @dep config.logging
|
||||
local config_res = require 'config.research' --- @dep config.research
|
||||
|
||||
local function add_log(data)
|
||||
game.write_file(config.file_name, data, true, 0)
|
||||
game.write_file(config.file_name, '\n', true, 0)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_rocket_launched, function(event)
|
||||
if event and event.rocket and event.rocket.force and event.rocket.force.rockets_launched then
|
||||
if event.rocket.force.rockets_launched >= config.rocket_launch_display_rate and event.rocket.force.rockets_launched % config.rocket_launch_display_rate == 0 then
|
||||
add_log('[ROCKET] ' .. event.rocket.force.rockets_launched .. ' rockets launched')
|
||||
|
||||
elseif config.rocket_launch_display[event.rocket.force.rockets_launched] then
|
||||
add_log('[ROCKET] ' .. event.rocket.force.rockets_launched .. ' rockets launched')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_pre_player_died, function(event)
|
||||
if event and event.player_index then
|
||||
if event.cause then
|
||||
if event.cause.type and event.cause.type == 'character' and event.cause.player and event.cause.player.index then
|
||||
add_log('[DEATH] ' .. game.players[event.player_index].name .. ' died because of ' .. (game.players[event.cause.player.index].name or 'unknown reason'))
|
||||
|
||||
else
|
||||
add_log('[DEATH] ' .. game.players[event.player_index].name .. ' died because of ' .. (event.cause.name or 'unknown reason'))
|
||||
end
|
||||
|
||||
else
|
||||
add_log('[DEATH] ' .. game.players[event.player_index].name .. ' died because of unknown reason')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_research_finished, function(event)
|
||||
if event and event.research then
|
||||
if event.by_script then
|
||||
return
|
||||
end
|
||||
|
||||
if (event.research.level and config_res.inf_res[event.research.name]) and (event.research.level >= config_res.inf_res[event.research.name]) then
|
||||
add_log({'logging.add-l', event.research.prototype.localised_name, event.research.level - 1})
|
||||
|
||||
else
|
||||
add_log({'logging.add-n', event.research.prototype.localised_name})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, function(event)
|
||||
if event and event.player_index then
|
||||
add_log('[JOIN] ' .. game.players[event.player_index].name .. ' joined the game')
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_left_game, function(event)
|
||||
if event and event.player_index then
|
||||
if event.reason then
|
||||
add_log('[LEAVE] ' .. game.players[event.player_index].name .. config.disconnect_reason[event.reason])
|
||||
|
||||
else
|
||||
add_log('[LEAVE] ' .. game.players[event.player_index].name .. config.disconnect_reason[defines.disconnect_reason.quit])
|
||||
end
|
||||
end
|
||||
end)
|
||||
181
exp_legacy/module/modules/addons/miner.lua
Normal file
181
exp_legacy/module/modules/addons/miner.lua
Normal file
@@ -0,0 +1,181 @@
|
||||
local Event = require 'utils.event_core' --- @dep utils.event_core
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local config = require 'config.miner' --- @dep config.miner
|
||||
|
||||
local miner_data = {}
|
||||
|
||||
Global.register(miner_data, function(tbl)
|
||||
miner_data = tbl
|
||||
end)
|
||||
|
||||
miner_data.queue = {}
|
||||
|
||||
local function drop_target(entity)
|
||||
if entity.drop_target then
|
||||
return entity.drop_target
|
||||
|
||||
else
|
||||
local entities = entity.surface.find_entities_filtered{position=entity.drop_position}
|
||||
|
||||
if #entities > 0 then
|
||||
return entities[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_entity(entity)
|
||||
if entity.to_be_deconstructed(entity.force) then
|
||||
-- if it is already waiting to be deconstruct
|
||||
return true
|
||||
end
|
||||
|
||||
if next(entity.circuit_connected_entities.red) ~= nil or next(entity.circuit_connected_entities.green) ~= nil then
|
||||
-- connected to circuit network
|
||||
return true
|
||||
end
|
||||
|
||||
if not entity.minable then
|
||||
-- if it is minable
|
||||
return true
|
||||
end
|
||||
|
||||
if not entity.prototype.selectable_in_game then
|
||||
-- if it can select
|
||||
return true
|
||||
end
|
||||
|
||||
if entity.has_flag('not-deconstructable') then
|
||||
-- if it can deconstruct
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function chest_check(entity)
|
||||
local target = drop_target(entity)
|
||||
|
||||
if check_entity(entity) then
|
||||
return
|
||||
end
|
||||
|
||||
if target.type ~= 'logistic-container' and target.type ~= 'container' then
|
||||
-- not a chest
|
||||
return
|
||||
end
|
||||
|
||||
local radius = 2
|
||||
local entities = target.surface.find_entities_filtered{area={{target.position.x - radius, target.position.y - radius}, {target.position.x + radius, target.position.y + radius}}, type={'mining-drill', 'inserter'}}
|
||||
|
||||
for _, e in pairs(entities) do
|
||||
if drop_target(e) == target then
|
||||
if not e.to_be_deconstructed(entity.force) and e ~= entity then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if check_entity(target) then
|
||||
table.insert(miner_data.queue, {t=game.tick + 10, e=target})
|
||||
end
|
||||
end
|
||||
|
||||
local function miner_check(entity)
|
||||
local ep = entity.position
|
||||
local es = entity.surface
|
||||
local ef = entity.force
|
||||
local er = entity.prototype.mining_drill_radius
|
||||
|
||||
for _, r in pairs(entity.surface.find_entities_filtered{area={{x=ep.x - er, y=ep.y - er}, {x=ep.x + er, y=ep.y + er}}, type='resource'}) do
|
||||
if r.amount > 0 then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
entity.status ~= defines.entity_status.no_minable_resources
|
||||
]]
|
||||
|
||||
if check_entity(entity) then
|
||||
return
|
||||
end
|
||||
|
||||
local pipe_build = {}
|
||||
|
||||
if config.fluid and entity.fluidbox and #entity.fluidbox > 0 then
|
||||
-- if require fluid to mine
|
||||
table.insert(pipe_build, {x=0, y=0})
|
||||
|
||||
local half = math.floor(entity.get_radius())
|
||||
local r = 1 + er
|
||||
|
||||
local entities = es.find_entities_filtered{area={{ep.x - r, ep.y - r}, {ep.x + r, ep.y + r}}, type={'mining-drill', 'pipe', 'pipe-to-ground'}}
|
||||
local entities_t = es.find_entities_filtered{area={{ep.x - r, ep.y - r}, {ep.x + r, ep.y + r}}, ghost_type={'pipe', 'pipe-to-ground'}}
|
||||
|
||||
table.array_insert(entities, entities_t)
|
||||
|
||||
for _, e in pairs(entities) do
|
||||
if (e.position.x > ep.x) and (e.position.y == ep.y) then
|
||||
for h=1, half do
|
||||
table.insert(pipe_build, {x=h, y=0})
|
||||
end
|
||||
|
||||
elseif (e.position.x < ep.x) and (e.position.y == ep.y) then
|
||||
for h=1, half do
|
||||
table.insert(pipe_build, {x=-h, y=0})
|
||||
end
|
||||
|
||||
elseif (e.position.x == ep.x) and (e.position.y > ep.y) then
|
||||
for h=1, half do
|
||||
table.insert(pipe_build, {x=0, y=h})
|
||||
end
|
||||
|
||||
elseif (e.position.x == ep.x) and (e.position.y < ep.y) then
|
||||
for h=1, half do
|
||||
table.insert(pipe_build, {x=0, y=-h})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if config.chest then
|
||||
chest_check(entity)
|
||||
end
|
||||
|
||||
table.insert(miner_data.queue, {t=game.tick + 5, e=entity})
|
||||
|
||||
for _, pos in ipairs(pipe_build) do
|
||||
es.create_entity{name='entity-ghost', position={x=ep.x + pos.x, y=ep.y + pos.y}, force=ef, inner_name='pipe', raise_built=true}
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_resource_depleted, function(event)
|
||||
if event.entity.prototype.infinite_resource then
|
||||
return
|
||||
end
|
||||
|
||||
local entities = event.entity.surface.find_entities_filtered{area={{event.entity.position.x - 1, event.entity.position.y - 1}, {event.entity.position.x + 1, event.entity.position.y + 1}}, type='mining-drill'}
|
||||
|
||||
if #entities == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
for _, entity in pairs(entities) do
|
||||
if ((math.abs(entity.position.x - event.entity.position.x) < entity.prototype.mining_drill_radius) and (math.abs(entity.position.y - event.entity.position.y) < entity.prototype.mining_drill_radius)) then
|
||||
miner_check(entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(10, function(event)
|
||||
for k, q in pairs(miner_data.queue) do
|
||||
if not q.e or not q.e.valid then
|
||||
table.remove(miner_data.queue, k)
|
||||
break
|
||||
|
||||
elseif event.tick >= q.t then
|
||||
q.e.order_deconstruction(q.e.force)
|
||||
table.remove(miner_data.queue, k)
|
||||
end
|
||||
end
|
||||
end)
|
||||
46
exp_legacy/module/modules/addons/nukeprotect.lua
Normal file
46
exp_legacy/module/modules/addons/nukeprotect.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
--- Disable new players from having certain items in their inventory, most commonly nukes
|
||||
-- @addon Nukeprotect
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local config = require 'config.nukeprotect' --- @dep config.nukeprotect
|
||||
local move_items_stack = _C.move_items_stack --- @dep expcore.common
|
||||
|
||||
|
||||
local function check_items(player, type)
|
||||
-- if the player has perms to be ignored, then they should be
|
||||
if config.ignore_permisison and Roles.player_allowed(player, config.ignore_permisison) then return end
|
||||
-- if the players
|
||||
if config.ignore_admins and player.admin then return end
|
||||
|
||||
local inventory = player.get_inventory(type)
|
||||
for i = 1, #inventory do
|
||||
local item = inventory[i]
|
||||
if item.valid and item.valid_for_read and config[tostring(type)][item.name] then
|
||||
player.print({ "nukeprotect.found", { "item-name." .. item.name } })
|
||||
-- insert the items into the table so all items are transferred at once
|
||||
move_items_stack({ item })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, inventory in ipairs(config.inventories) do
|
||||
if #inventory.items > 0 then
|
||||
Event.add(inventory.event, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
if player and player.valid then
|
||||
check_items(player, inventory.inventory)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if config.disable_nuke_research then
|
||||
Event.add(defines.events.on_research_started, function(event)
|
||||
local name = event.research.name
|
||||
if config.disable_nuke_research_names[name] then
|
||||
event.research.force.cancel_current_research()
|
||||
end
|
||||
end)
|
||||
end
|
||||
16
exp_legacy/module/modules/addons/pollution-grading.lua
Normal file
16
exp_legacy/module/modules/addons/pollution-grading.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
--- Makes polution look much nice of the map, ie not one big red mess
|
||||
-- @addon Pollution-Grading
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.pollution_grading' --- @dep config.pollution_grading
|
||||
|
||||
local delay = config.update_delay * 3600 -- convert from minutes to ticks
|
||||
Event.on_nth_tick(delay, function()
|
||||
local surface = game.surfaces[1]
|
||||
local true_max = surface.get_pollution(config.reference_point)
|
||||
local max = true_max*config.max_scalar
|
||||
local min = max*config.min_scalar
|
||||
local settings = game.map_settings.pollution
|
||||
settings.expected_max_per_chunk = max
|
||||
settings.min_to_show_per_chunk = min
|
||||
end)
|
||||
40
exp_legacy/module/modules/addons/protection-jail.lua
Normal file
40
exp_legacy/module/modules/addons/protection-jail.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
--- When a player triggers protection multiple times they are automatically jailed
|
||||
-- @addon protection-jail
|
||||
|
||||
local Event = require 'utils.event' ---@dep utils.event
|
||||
local Global = require 'utils.global' ---@dep utils.global
|
||||
local Jail = require 'modules.control.jail' ---@dep modules.control.jail
|
||||
local Protection = require 'modules.control.protection' --- @dep modules.control.protection
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
|
||||
--- Stores how many times the repeat violation was triggered
|
||||
local repeat_count = {}
|
||||
Global.register(repeat_count, function(tbl)
|
||||
repeat_count = tbl
|
||||
end)
|
||||
|
||||
--- When a protection is triggered increment their counter and jail if needed
|
||||
Event.add(Protection.events.on_repeat_violation, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
-- Increment the counter
|
||||
if repeat_count[player.index] then
|
||||
repeat_count[player.index] = repeat_count[player.index] + 1
|
||||
else
|
||||
repeat_count[player.index] = 1
|
||||
end
|
||||
|
||||
-- Jail if needed
|
||||
if repeat_count[player.index] < 3 then
|
||||
return
|
||||
end
|
||||
|
||||
local player_name_color = format_chat_player_name(player)
|
||||
Jail.jail_player(player, '<protection>', 'Removed too many protected entities, please wait for a moderator.')
|
||||
game.print{'protection-jail.jail', player_name_color}
|
||||
end)
|
||||
|
||||
--- Clear the counter when they leave the game (stops a build up of data)
|
||||
Event.add(defines.events.on_player_left_game, function(event)
|
||||
repeat_count[event.player_index] = nil
|
||||
end)
|
||||
28
exp_legacy/module/modules/addons/report-jail.lua
Normal file
28
exp_legacy/module/modules/addons/report-jail.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--- When a player is reported, the player is automatically jailed if the combined playtime of the reporters exceeds the reported player
|
||||
-- @addon report-jail
|
||||
|
||||
local Event = require 'utils.event' ---@dep utils.event
|
||||
local Jail = require 'modules.control.jail' ---@dep modules.control.jail
|
||||
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
|
||||
--- Returns the playtime of the reporter. Used when calculating the total playtime of all reporters
|
||||
local function reporter_playtime(_, by_player_name, _)
|
||||
local player = game.get_player(by_player_name)
|
||||
if player == nil then
|
||||
return 0
|
||||
end
|
||||
return player.online_time
|
||||
end
|
||||
|
||||
Event.add(Reports.events.on_player_reported, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
local total_playtime = Reports.count_reports(player, reporter_playtime)
|
||||
|
||||
-- player less than 30 min
|
||||
if (Reports.count_reports(player) > 1) and (total_playtime > math.max(player.online_time * 2, 108000)) then
|
||||
local player_name_color = format_chat_player_name(player)
|
||||
Jail.jail_player(player, '<reports>', 'Reported by too many players, please wait for a moderator.')
|
||||
game.print{'report-jail.jail', player_name_color}
|
||||
end
|
||||
end)
|
||||
138
exp_legacy/module/modules/addons/scorched-earth.lua
Normal file
138
exp_legacy/module/modules/addons/scorched-earth.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
--- When a player walks around the tiles under them will degrade over time, the same is true when entites are built
|
||||
-- @addon Scorched-Earth
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local print_grid_value, clear_flying_text = _C.print_grid_value, _C.clear_flying_text --- @dep expcore.common
|
||||
local config = require 'config.scorched_earth' --- @dep config.scorched_earth
|
||||
|
||||
-- Loops over the config and finds the wile which has the highest value for strength
|
||||
local max_strength = 0
|
||||
for _, strength in pairs(config.strengths) do
|
||||
if strength > max_strength then
|
||||
max_strength = strength
|
||||
end
|
||||
end
|
||||
|
||||
-- Used for debugging the degrade chances
|
||||
local debug_players = {}
|
||||
Global.register(debug_players, function(tbl)
|
||||
debug_players = tbl
|
||||
end)
|
||||
|
||||
-- Will degrade a tile down to the next tile when called
|
||||
local function degrade(surface, position)
|
||||
local tile = surface.get_tile(position)
|
||||
local tile_name = tile.name
|
||||
local degrade_tile_name = config.degrade_order[tile_name]
|
||||
if not degrade_tile_name then return end
|
||||
surface.set_tiles{{name=degrade_tile_name, position=position}}
|
||||
end
|
||||
|
||||
-- Same as degrade but will degrade all tiles that are under an entity
|
||||
local function degrade_entity(entity)
|
||||
local surface = entity.surface
|
||||
local position = entity.position
|
||||
local tiles = {}
|
||||
if not config.entities[entity.name] then return end
|
||||
local box = entity.prototype.collision_box
|
||||
local lt = box.left_top
|
||||
local rb = box.right_bottom
|
||||
for x = lt.x, rb.x do -- x loop
|
||||
local px = position.x+x
|
||||
for y = lt.y, rb.y do -- y loop
|
||||
local p = {x=px, y=position.y+y}
|
||||
local tile = surface.get_tile(p)
|
||||
local tile_name = tile.name
|
||||
local degrade_tile_name = config.degrade_order[tile_name]
|
||||
if not degrade_tile_name then return end
|
||||
table.insert(tiles, {name=degrade_tile_name, position=p})
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles)
|
||||
end
|
||||
|
||||
-- Turns the strength of a tile into a probability (0 = impossible, 1 = certain)
|
||||
local function get_probability(strength)
|
||||
local v1 = strength/max_strength
|
||||
local dif = 1 - v1
|
||||
local v2 = dif/2
|
||||
return (1-v1+v2)/config.weakness_value
|
||||
end
|
||||
|
||||
-- Gets the mean of the strengths around a tile to give the strength at that position
|
||||
local function get_tile_strength(surface, position)
|
||||
local tile = surface.get_tile(position)
|
||||
local tile_name = tile.name
|
||||
local strength = config.strengths[tile_name]
|
||||
if not strength then return end
|
||||
for x = -1, 1 do -- x loop
|
||||
local px = position.x + x
|
||||
for y = -1, 1 do -- y loop
|
||||
local check_tile = surface.get_tile{x=px, y=position.y+y}
|
||||
local check_tile_name = check_tile.name
|
||||
local check_strength = config.strengths[check_tile_name] or 0
|
||||
strength = strength + check_strength
|
||||
end
|
||||
end
|
||||
return strength/9
|
||||
end
|
||||
|
||||
-- Same as get_tile_strength but returns to a in game text rather than as a value
|
||||
local function debug_get_tile_strength(surface, position)
|
||||
for x = -3, 3 do -- x loop
|
||||
local px = position.x+x
|
||||
for y = -3, 3 do -- y loop
|
||||
local p = {x=px, y=position.y+y}
|
||||
local strength = get_tile_strength(surface, p) or 0
|
||||
local tile = surface.get_tile(p)
|
||||
print_grid_value(get_probability(strength)*config.weakness_value, surface, tile.position)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- When the player changes position the tile will have a chance to downgrade, debug check is here
|
||||
Event.add(defines.events.on_player_changed_position, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local surface = player.surface
|
||||
local position = player.position
|
||||
local strength = get_tile_strength(surface, position)
|
||||
if not strength then return end
|
||||
if get_probability(strength) > math.random() then
|
||||
degrade(surface, position)
|
||||
end
|
||||
if debug_players[player.name] then
|
||||
debug_get_tile_strength(surface, position)
|
||||
end
|
||||
end)
|
||||
|
||||
-- When an entity is build there is a much higher chance that the tiles will degrade
|
||||
Event.add(defines.events.on_built_entity, function(event)
|
||||
local entity = event.created_entity
|
||||
local surface = entity.surface
|
||||
local position = entity.position
|
||||
local strength = get_tile_strength(surface, position)
|
||||
if not strength then return end
|
||||
if get_probability(strength)*config.weakness_value > math.random() then
|
||||
degrade_entity(entity)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Same as above but with robots
|
||||
Event.add(defines.events.on_robot_built_entity, function(event)
|
||||
local entity = event.created_entity
|
||||
local surface = entity.surface
|
||||
local position = entity.position
|
||||
local strength = get_tile_strength(surface, position)
|
||||
if not strength then return end
|
||||
if get_probability(strength)*config.weakness_value > math.random() then
|
||||
degrade_entity(entity)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Used as a way to access the global table
|
||||
return function(player_name, state)
|
||||
local player = game.players[player_name]
|
||||
clear_flying_text(player.surface)
|
||||
debug_players[player_name] = state
|
||||
end
|
||||
236
exp_legacy/module/modules/addons/spawn-area.lua
Normal file
236
exp_legacy/module/modules/addons/spawn-area.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
--- Adds a custom spawn area with chests and afk turrets
|
||||
-- @addon Spawn-Area
|
||||
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.spawn_area' --- @dep config.spawn_area
|
||||
|
||||
local turrets = config.turrets.locations
|
||||
Global.register(turrets, function(tbl)
|
||||
turrets = tbl
|
||||
end)
|
||||
|
||||
-- Apply an offset to a LuaPosition
|
||||
local function apply_offset(position, offset)
|
||||
return {x=position.x + (offset.x or offset[1]), y=position.y + (offset.y or offset[2])}
|
||||
end
|
||||
|
||||
-- Apply the offset to the turrets default position
|
||||
for _, turret in ipairs(turrets) do
|
||||
turret.position = apply_offset(turret.position, config.turrets.offset)
|
||||
end
|
||||
|
||||
-- Get or create the force used for entities in spawn
|
||||
local function get_spawn_force()
|
||||
local force = game.forces['spawn']
|
||||
|
||||
if force and force.valid then
|
||||
return force
|
||||
end
|
||||
|
||||
force = game.create_force('spawn')
|
||||
force.set_cease_fire('player', true)
|
||||
-- force.set_friend('player', true)
|
||||
game.forces['player'].set_cease_fire('spawn', true)
|
||||
-- game.forces['player'].set_friend('spawn', true)
|
||||
|
||||
return force
|
||||
end
|
||||
|
||||
-- Protects an entity
|
||||
-- and sets its force to the spawn force
|
||||
local function protect_entity(entity, set_force)
|
||||
if entity and entity.valid then
|
||||
entity.destructible = false
|
||||
entity.minable = false
|
||||
entity.rotatable = false
|
||||
entity.operable = false
|
||||
|
||||
if set_force then
|
||||
entity.force = get_spawn_force()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Will spawn all infinite ammo turrets and keep them refilled
|
||||
local function spawn_turrets()
|
||||
for _, turret_pos in pairs(turrets) do
|
||||
local surface = game.surfaces[turret_pos.surface]
|
||||
local pos = turret_pos.position
|
||||
local turret = surface.find_entity('gun-turret', pos)
|
||||
|
||||
-- Makes a new turret if it is not found
|
||||
if not turret or not turret.valid then
|
||||
turret = surface.create_entity{name='gun-turret', position=pos, force='spawn'}
|
||||
protect_entity(turret)
|
||||
end
|
||||
|
||||
-- Adds ammo to the turret
|
||||
local inv = turret.get_inventory(defines.inventory.turret_ammo)
|
||||
if inv.can_insert{name=config.turrets.ammo_type, count=10} then
|
||||
inv.insert{name=config.turrets.ammo_type, count=10}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Makes a 2x2 afk belt at the locations in the config
|
||||
local function spawn_belts(surface, position)
|
||||
position = apply_offset(position, config.afk_belts.offset)
|
||||
local belt_type = config.afk_belts.belt_type
|
||||
local belt_details = {{-0.5, -0.5, 2}, {0.5, -0.5, 4}, {-0.5, 0.5, 0}, {0.5, 0.5, 6}} -- x, y,dir
|
||||
|
||||
for _, belt_set in pairs(config.afk_belts.locations) do
|
||||
local set_position = apply_offset(position, belt_set)
|
||||
for _, belt in pairs(belt_details) do
|
||||
local pos = apply_offset(set_position, belt)
|
||||
local belt_entity = surface.create_entity{name=belt_type, position=pos, force='neutral', direction=belt[3]}
|
||||
|
||||
if config.afk_belts.protected then
|
||||
protect_entity(belt_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Generates extra tiles in a set pattern as defined in the config
|
||||
local function spawn_pattern(surface, position)
|
||||
position = apply_offset(position, config.pattern.offset)
|
||||
local tiles_to_make = {}
|
||||
local pattern_tile = config.pattern.pattern_tile
|
||||
|
||||
for _, tile in pairs(config.pattern.locations) do
|
||||
table.insert(tiles_to_make, {name=pattern_tile, position=apply_offset(position, tile)})
|
||||
end
|
||||
|
||||
surface.set_tiles(tiles_to_make)
|
||||
end
|
||||
|
||||
-- Generates extra water as defined in the config
|
||||
local function spawn_water(surface, position)
|
||||
position = apply_offset(position, config.water.offset)
|
||||
local tiles_to_make = {}
|
||||
local water_tile = config.water.water_tile
|
||||
for _, tile in pairs(config.water.locations) do
|
||||
table.insert(tiles_to_make, {name=water_tile, position=apply_offset(position, tile)})
|
||||
end
|
||||
surface.set_tiles(tiles_to_make)
|
||||
end
|
||||
|
||||
-- Generates the entities that are in the config
|
||||
local function spawn_entities(surface, position)
|
||||
position = apply_offset(position, config.entities.offset)
|
||||
for _, entity in pairs(config.entities.locations) do
|
||||
local pos = apply_offset(position, {x=entity[2], y=entity[3]})
|
||||
entity = surface.create_entity{name=entity[1], position=pos, force='neutral'}
|
||||
|
||||
if config.entities.protected then
|
||||
protect_entity(entity)
|
||||
end
|
||||
|
||||
entity.operable = config.entities.operable
|
||||
end
|
||||
end
|
||||
|
||||
-- Generates an area with no water or entities, no water area is larger
|
||||
local function spawn_area(surface, position)
|
||||
local dr = config.spawn_area.deconstruction_radius
|
||||
local tr = config.spawn_area.tile_radius
|
||||
local tr2 = tr^2
|
||||
local decon_tile = config.spawn_area.deconstruction_tile
|
||||
|
||||
local fr = config.spawn_area.landfill_radius
|
||||
local fr2 = fr^2
|
||||
local fill_tile = surface.get_tile(position).name
|
||||
|
||||
-- Make sure a non water tile is used for each tile
|
||||
if surface.get_tile(position).collides_with('player-layer') then fill_tile = 'landfill' end
|
||||
if decon_tile == nil then decon_tile = fill_tile end
|
||||
|
||||
local tiles_to_make = {}
|
||||
for x = -fr, fr do -- loop over x
|
||||
local x2 = (x+0.5)^2
|
||||
for y = -fr, fr do -- loop over y
|
||||
local y2 = (y+0.5)^2
|
||||
local dst = x2+y2
|
||||
local pos = {x=position.x+x, y=position.y+y}
|
||||
if dst < tr2 then
|
||||
-- If it is inside the decon radius always set the tile
|
||||
table.insert(tiles_to_make, {name=decon_tile, position=pos})
|
||||
elseif dst < fr2 and surface.get_tile(pos).collides_with('player-layer') then
|
||||
-- If it is inside the fill radius only set the tile if it is water
|
||||
table.insert(tiles_to_make, {name=fill_tile, position=pos})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove entities then set the tiles
|
||||
local entities_to_remove = surface.find_entities_filtered{position=position, radius=dr, name='character', invert=true}
|
||||
for _, entity in pairs(entities_to_remove) do
|
||||
entity.destroy()
|
||||
end
|
||||
surface.set_tiles(tiles_to_make)
|
||||
end
|
||||
|
||||
local function spawn_resource_tiles(surface)
|
||||
for _, v in ipairs(config.resource_tiles.resources) do
|
||||
if v.enabled then
|
||||
for x=v.offset[1], v.offset[1] + v.size[1] do
|
||||
for y=v.offset[2], v.offset[2] + v.size[2] do
|
||||
surface.create_entity({name=v.name, amount=v.amount, position={x, y}})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function spawn_resource_patches(surface)
|
||||
for _, v in ipairs(config.resource_patches.resources) do
|
||||
if v.enabled then
|
||||
for i=1, v.num_patches do
|
||||
surface.create_entity({name=v.name, amount=v.amount, position={v.offset[1] + v.offset_next[1] * (i - 1), v.offset[2] + v.offset_next[2] * (i - 1)}})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Only add a event handler if the turrets are enabled
|
||||
if config.turrets.enabled then
|
||||
Event.on_nth_tick(config.turrets.refill_time, function()
|
||||
if game.tick < 10 then return end
|
||||
spawn_turrets()
|
||||
end)
|
||||
end
|
||||
|
||||
if config.resource_refill_nearby.enabled then
|
||||
-- could have a flag in global that early returns if true, and reset it on_tick
|
||||
Event.on_nth_tick(36000, function()
|
||||
if game.tick < 10 then
|
||||
return
|
||||
end
|
||||
|
||||
for _, ore in pairs(game.players[1].surface.find_entities_filtered{position=game.players[1].force.get_spawn_position(game.players[1].surface), radius=config.resource_refill_nearby.range, name=config.resource_refill_nearby.resources_name}) do
|
||||
ore.amount = ore.amount + math.random(config.resource_refill_nearby.amount[1], config.resource_refill_nearby.amount[2])
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- When the first player joins create the spawn area
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
if event.player_index ~= 1 then return end
|
||||
local player = game.players[event.player_index]
|
||||
local p = {x=0, y=0}
|
||||
local s = player.surface
|
||||
get_spawn_force()
|
||||
spawn_area(s, p)
|
||||
if config.pattern.enabled then spawn_pattern(s, p) end
|
||||
if config.water.enabled then spawn_water(s, p) end
|
||||
if config.afk_belts.enabled then spawn_belts(s, p) end
|
||||
if config.turrets.enabled then spawn_turrets() end
|
||||
if config.entities.enabled then spawn_entities(s, p) end
|
||||
if config.resource_tiles.enabled then spawn_resource_tiles(s) end
|
||||
if config.resource_patches.enabled then spawn_resource_patches(s) end
|
||||
player.teleport(p, s)
|
||||
end)
|
||||
|
||||
-- Way to access global table
|
||||
return turrets
|
||||
91
exp_legacy/module/modules/addons/station-auto-name.lua
Normal file
91
exp_legacy/module/modules/addons/station-auto-name.lua
Normal file
@@ -0,0 +1,91 @@
|
||||
---LuaPlayerBuiltEntityEventFilters
|
||||
---Events.set_event_filter(defines.events.on_built_entity, {{filter = "name", name = "fast-inserter"}})
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.station_auto_name' --- @dep config.chat_reply
|
||||
|
||||
--Credit to Cooldude2606 for using his lua magic to make this function.
|
||||
local directions = {
|
||||
['W'] = -0.875,
|
||||
['NW'] = -0.625,
|
||||
['N'] = -0.375,
|
||||
['NE'] = -0.125,
|
||||
['E'] = 0.125,
|
||||
['SE'] = 0.375,
|
||||
['S'] = 0.625,
|
||||
['SW'] = 0.875
|
||||
}
|
||||
|
||||
local function Angle(entity)
|
||||
local angle = math.atan2(entity.position.y, entity.position.x)/math.pi
|
||||
for direction, requiredAngle in pairs(directions) do
|
||||
if angle < requiredAngle then
|
||||
return direction
|
||||
end
|
||||
end
|
||||
return 'W'
|
||||
end
|
||||
|
||||
local custom_string = ' *'
|
||||
local custom_string_len = #custom_string
|
||||
|
||||
local function station_name_changer(event)
|
||||
local entity = event.created_entity
|
||||
local name = entity.name
|
||||
if name == "entity-ghost" then
|
||||
if entity.ghost_name ~= "train-stop" then return end
|
||||
local backername = entity.backer_name
|
||||
if backername ~= '' then
|
||||
entity.backer_name = backername..custom_string
|
||||
end
|
||||
|
||||
elseif name == "train-stop" then --only do the event if its a train stop
|
||||
local backername = entity.backer_name
|
||||
if backername:sub(-custom_string_len) == custom_string then
|
||||
entity.backer_name = backername:sub(1, -custom_string_len-1)
|
||||
return
|
||||
end
|
||||
|
||||
local boundingBox = entity.bounding_box
|
||||
-- expanded box for recourse search:
|
||||
local bounding2 = {{boundingBox.left_top.x-100 ,boundingBox.left_top.y-100} , {boundingBox.right_bottom.x+100, boundingBox.right_bottom.y+100 }}
|
||||
-- gets all resources in bounding_box2:
|
||||
local recourses = game.surfaces[1].find_entities_filtered{area = bounding2, type = "resource"}
|
||||
if #recourses > 0 then -- save cpu time if their are no recourses in bounding_box2
|
||||
local closest_distance
|
||||
local px, py = boundingBox.left_top.x, boundingBox.left_top.y
|
||||
local recourse_closed
|
||||
|
||||
--Check which recourse is closest
|
||||
for i, item in ipairs(recourses) do
|
||||
local dx, dy = px - item.bounding_box.left_top.x, py - item.bounding_box.left_top.y
|
||||
local distance = (dx*dx)+(dy*dy)
|
||||
if not closest_distance or distance < closest_distance then
|
||||
recourse_closed = item
|
||||
closest_distance = distance
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local item_name = recourse_closed.name
|
||||
if item_name then -- prevent errors if something went wrong
|
||||
local item_name2 = item_name:gsub("^%l", string.upper):gsub('-', ' ') -- removing the - and making first letter capital
|
||||
|
||||
local item_type = 'item'
|
||||
if item_name == 'crude-oil' then
|
||||
item_type = 'fluid'
|
||||
end
|
||||
|
||||
entity.backer_name = config.station_name:gsub('__icon__', '[img=' .. item_type .. '.' .. item_name .. ']')
|
||||
:gsub('__item_name__', item_name2)
|
||||
:gsub('__backer_name__', entity.backer_name)
|
||||
:gsub('__direction__', Angle(entity))
|
||||
:gsub('__x__', math.floor(entity.position.x))
|
||||
:gsub('__y__', math.floor(entity.position.y))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add handler to robot and player build entities
|
||||
Event.add(defines.events.on_built_entity, station_name_changer)
|
||||
Event.add(defines.events.on_robot_built_entity, station_name_changer)
|
||||
125
exp_legacy/module/modules/addons/tree-decon.lua
Normal file
125
exp_legacy/module/modules/addons/tree-decon.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
--- Makes trees which are marked for decon "decay" quickly to allow faster building
|
||||
-- @addon Tree-Decon
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
|
||||
-- Global queue used to store trees that need to be removed, also cache for player roles
|
||||
local cache = {}
|
||||
local tree_queue = { _head=0 }
|
||||
Global.register({tree_queue, cache}, function(tbl)
|
||||
tree_queue = tbl[1]
|
||||
cache = tbl[2]
|
||||
end)
|
||||
|
||||
local function get_permission(player_index)
|
||||
if cache[player_index] == nil then
|
||||
local player = game.players[player_index]
|
||||
if Roles.player_allowed(player, 'fast-tree-decon') then cache[player_index] = 'fast'
|
||||
elseif Roles.player_allowed(player, 'standard-decon') then cache[player_index] = 'standard'
|
||||
else cache[player_index] = player.force end
|
||||
end
|
||||
|
||||
return cache[player_index]
|
||||
end
|
||||
|
||||
-- Left menu button to toggle between fast decon and normal decon marking
|
||||
local HasEnabledDecon = PlayerData.Settings:combine('HasEnabledDecon')
|
||||
HasEnabledDecon:set_default(false)
|
||||
|
||||
Gui.toolbar_toggle_button("entity/tree-01", {'tree-decon.main-tooltip'}, function (player)
|
||||
return Roles.player_allowed(player, "fast-tree-decon")
|
||||
end)
|
||||
:on_event(Gui.events.on_toolbar_button_toggled, function(player, _, event)
|
||||
HasEnabledDecon:set(player, event.state)
|
||||
player.print{'tree-decon.toggle-msg', event.state and {'tree-decon.enabled'} or {'tree-decon.disabled'}}
|
||||
end)
|
||||
|
||||
|
||||
-- Add trees to queue when marked, only allows simple entities and for players with role permission
|
||||
Event.add(defines.events.on_marked_for_deconstruction, function(event)
|
||||
-- Check which type of decon a player is allowed
|
||||
local index = event.player_index
|
||||
if not index then return end
|
||||
|
||||
-- Check what should happen to this entity
|
||||
local entity = event.entity
|
||||
if not entity or not entity.valid then return end
|
||||
|
||||
-- Not allowed to decon this entity
|
||||
local last_user = entity.last_user
|
||||
local allow = get_permission(index)
|
||||
if last_user and allow ~= 'standard' and allow ~= 'fast' then
|
||||
entity.cancel_deconstruction(allow)
|
||||
return
|
||||
end
|
||||
|
||||
-- Allowed to decon this entity, but not fast
|
||||
if allow ~= 'fast' then return end
|
||||
|
||||
local player = game.get_player(index)
|
||||
if not HasEnabledDecon:get(player) then return end
|
||||
|
||||
-- Allowed fast decon on this entity, just trees
|
||||
local head = tree_queue._head + 1
|
||||
if not last_user and entity.type ~= 'cliff' then
|
||||
tree_queue[head] = entity
|
||||
tree_queue._head = head
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
-- Remove trees at random till the queue is empty
|
||||
Event.add(defines.events.on_tick, function()
|
||||
local head = tree_queue._head
|
||||
if head == 0 then return end
|
||||
|
||||
local max_remove = math.floor(head/100)+1
|
||||
local remove_count = math.random(0, max_remove)
|
||||
while remove_count > 0 and head > 0 do
|
||||
local remove_index = math.random(1, head)
|
||||
local entity = tree_queue[remove_index]
|
||||
tree_queue[remove_index] = tree_queue[head]
|
||||
head = head - 1
|
||||
if entity and entity.valid then
|
||||
remove_count = remove_count - 1
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
tree_queue._head = head
|
||||
end)
|
||||
|
||||
-- Clear the cache
|
||||
Event.on_nth_tick(300, function()
|
||||
for key, _ in pairs(cache) do
|
||||
cache[key] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
-- Clear trees when hit with a car
|
||||
Event.add(defines.events.on_entity_damaged, function(event)
|
||||
if not (event.damage_type.name == 'impact' and event.force) then
|
||||
return
|
||||
end
|
||||
|
||||
if not (event.entity.type == 'tree' or event.entity.type == 'simple-entity') then
|
||||
return
|
||||
end
|
||||
|
||||
if (not event.cause) or (event.cause.type ~= 'car')then
|
||||
return
|
||||
end
|
||||
|
||||
local driver = event.cause.get_driver()
|
||||
if not driver then return end
|
||||
|
||||
local allow = get_permission(driver.player.index)
|
||||
if allow == "fast" and HasEnabledDecon:get(driver.player) then
|
||||
event.entity.destroy()
|
||||
else
|
||||
event.entity.order_deconstruction(event.force, driver.player)
|
||||
end
|
||||
end)
|
||||
27
exp_legacy/module/modules/commands/admin-chat.lua
Normal file
27
exp_legacy/module/modules/commands/admin-chat.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
--[[-- Commands Module - Admin Chat
|
||||
- Adds a command that allows admins to talk in a private chat
|
||||
@commands Admin-Chat
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Sends a message in chat that only admins can see
|
||||
-- @command admin-chat
|
||||
-- @tparam string message the message to send in the admin chat
|
||||
Commands.new_command('admin-chat', {'expcom-admin-chat.description'}, 'Sends a message in chat that only admins can see.')
|
||||
:add_param('message', false)
|
||||
:enable_auto_concat()
|
||||
:set_flag('admin_only')
|
||||
:add_alias('ac')
|
||||
:register(function(player, message)
|
||||
local player_name_colour = format_chat_player_name(player)
|
||||
|
||||
for _, return_player in pairs(game.connected_players) do
|
||||
if return_player.admin then
|
||||
return_player.print{'expcom-admin-chat.format', player_name_colour, message}
|
||||
end
|
||||
end
|
||||
return Commands.success -- prevents command complete message from showing
|
||||
end)
|
||||
88
exp_legacy/module/modules/commands/admin-markers.lua
Normal file
88
exp_legacy/module/modules/commands/admin-markers.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
--[[-- Commands Module - Admin Markers
|
||||
- Adds a command that creates map markers which can only be edited by admins
|
||||
@commands Admin-Markers
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
|
||||
local admins = {} -- Stores all players in admin marker mode
|
||||
local markers = {} -- Stores all admin markers
|
||||
|
||||
--- Global variables
|
||||
Global.register({
|
||||
admins = admins,
|
||||
markers = markers
|
||||
}, function(tbl)
|
||||
admins = tbl.admins
|
||||
markers = tbl.markers
|
||||
end)
|
||||
|
||||
--- Toggle admin marker mode, can only be applied to yourself
|
||||
-- @command admin-marker
|
||||
Commands.new_command('admin-marker', {'expcom-admin-marker.description'}, 'Toggles admin marker mode, new markers can only be edited by admins')
|
||||
:set_flag('admin_only')
|
||||
:add_alias('am', 'admin-markers')
|
||||
:register(function(player)
|
||||
if admins[player.name] then
|
||||
-- Exit admin mode
|
||||
admins[player.name] = nil
|
||||
return Commands.success{'expcom-admin-marker.exit'}
|
||||
else
|
||||
-- Enter admin mode
|
||||
admins[player.name] = true
|
||||
return Commands.success{'expcom-admin-marker.enter'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Listen for new map markers being added, add admin marker if done by player in admin mode
|
||||
Event.add(defines.events.on_chart_tag_added, function(event)
|
||||
if not event.player_index then return end
|
||||
local player = game.get_player(event.player_index)
|
||||
if not admins[player.name] then return end
|
||||
local tag = event.tag
|
||||
markers[tag.force.name..tag.tag_number] = true
|
||||
Commands.print({'expcom-admin-marker.place'}, nil, player)
|
||||
end)
|
||||
|
||||
--- Listen for players leaving the game, leave admin mode to avoid unexpected admin markers
|
||||
Event.add(defines.events.on_player_left_game, function(event)
|
||||
if not event.player_index then return end
|
||||
local player = game.get_player(event.player_index)
|
||||
admins[player.name] = nil
|
||||
end)
|
||||
|
||||
--- Listen for tags being removed or edited, maintain tags edited by non admins
|
||||
local function maintain_tag(event)
|
||||
local tag = event.tag
|
||||
if not event.player_index then return end
|
||||
if not markers[tag.force.name..tag.tag_number] then return end
|
||||
local player = game.get_player(event.player_index)
|
||||
if player.admin then
|
||||
-- Player is admin, tell them it was an admin marker
|
||||
Commands.print({'expcom-admin-marker.edit'}, nil, player)
|
||||
elseif event.name == defines.events.on_chart_tag_modified then
|
||||
-- Tag was modified, revert the changes
|
||||
tag.text = event.old_text
|
||||
tag.last_user = event.old_player
|
||||
if event.old_icon then tag.icon = event.old_icon end
|
||||
player.play_sound{path='utility/wire_pickup'}
|
||||
Commands.print({'expcom-admin-marker.revert'}, nil, player)
|
||||
else
|
||||
-- Tag was removed, remake the tag
|
||||
player.play_sound{path='utility/wire_pickup'}
|
||||
Commands.print({'expcom-admin-marker.revert'}, 'orange_red', player)
|
||||
local new_tag = tag.force.add_chart_tag(tag.surface, {
|
||||
last_user = tag.last_user,
|
||||
position = tag.position,
|
||||
icon = tag.icon,
|
||||
text = tag.text,
|
||||
})
|
||||
markers[tag.force.name..tag.tag_number] = nil
|
||||
markers[new_tag.force.name..new_tag.tag_number] = true
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_chart_tag_modified, maintain_tag)
|
||||
Event.add(defines.events.on_chart_tag_removed, maintain_tag)
|
||||
84
exp_legacy/module/modules/commands/artillery.lua
Normal file
84
exp_legacy/module/modules/commands/artillery.lua
Normal file
@@ -0,0 +1,84 @@
|
||||
--[[-- Commands Module - Artillery
|
||||
- Adds a command that help shot artillery
|
||||
@commands Artillery
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
local Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
local SelectionArtyArea = 'ArtyArea'
|
||||
|
||||
local function location_break(player, pos)
|
||||
if player.force.is_chunk_charted(player.surface, {x=math.floor(pos.left_top.x / 32), y=math.floor(pos.left_top.y / 32)}) then
|
||||
return true
|
||||
|
||||
elseif player.force.is_chunk_charted(player.surface, {x=math.floor(pos.left_top.x / 32), y=math.floor(pos.right_bottom.y / 32)}) then
|
||||
return true
|
||||
|
||||
elseif player.force.is_chunk_charted(player.surface, {x=math.floor(pos.right_bottom.x / 32), y=math.floor(pos.left_top.y / 32)}) then
|
||||
return true
|
||||
|
||||
elseif player.force.is_chunk_charted(player.surface, {x=math.floor(pos.right_bottom.x / 32), y=math.floor(pos.right_bottom.y / 32)}) then
|
||||
return true
|
||||
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- align an aabb to the grid by expanding it
|
||||
local function aabb_align_expand(aabb)
|
||||
return {
|
||||
left_top = {x = math.floor(aabb.left_top.x), y = math.floor(aabb.left_top.y)},
|
||||
right_bottom = {x = math.ceil(aabb.right_bottom.x), y = math.ceil(aabb.right_bottom.y)}
|
||||
}
|
||||
end
|
||||
|
||||
--- when an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionArtyArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
if player == nil then
|
||||
return
|
||||
end
|
||||
|
||||
if not (game.players[event.player_index].cheat_mode or location_break(player, event.area)) then
|
||||
return Commands.error
|
||||
end
|
||||
|
||||
local count = 0
|
||||
local hit = {}
|
||||
|
||||
for _, e in pairs(player.surface.find_entities_filtered({area=area, type={'unit-spawner', 'turret'}, force='enemy'})) do
|
||||
local skip = false
|
||||
|
||||
for _, pos in ipairs(hit) do
|
||||
if math.sqrt(math.abs(e.position.x - pos.x) ^ 2 + math.abs(e.position.y - pos.y) ^ 2) < 6 then
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not skip then
|
||||
player.surface.create_entity{name='artillery-flare', position=e.position, force=player.force, life_time=240, movement={0, 0}, height=0, vertical_speed=0, frame_speed=0}
|
||||
table.insert(hit, e.position)
|
||||
count = count + 1
|
||||
|
||||
if count > 400 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.new_command('artillery-target-remote', {'expcom-artillery.description'}, 'Artillery Target Remote')
|
||||
:register(function(player)
|
||||
if Selection.is_selecting(player, SelectionArtyArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionArtyArea)
|
||||
end
|
||||
|
||||
return Commands.success
|
||||
end)
|
||||
25
exp_legacy/module/modules/commands/bot-queue.lua
Normal file
25
exp_legacy/module/modules/commands/bot-queue.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
--[[-- Commands Module - Bot queue
|
||||
- Adds a command that allows changing bot queue
|
||||
@commands Bot Queue
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
Commands.new_command('bot-queue-get', {'expcom-bot-queue.description-get'}, 'Get bot queue')
|
||||
:set_flag('admin_only')
|
||||
:register(function(player)
|
||||
local s = player.force.max_successful_attempts_per_tick_per_construction_queue
|
||||
local f = player.force.max_failed_attempts_per_tick_per_construction_queue
|
||||
return Commands.success{'expcom-bot-queue.result', player.name, s, f}
|
||||
end)
|
||||
|
||||
Commands.new_command('bot-queue-set', {'expcom-bot-queue.description-set'}, 'Set bot queue')
|
||||
:add_param('amount', 'integer-range', 1, 20)
|
||||
:set_flag('admin_only')
|
||||
:register(function(player, amount)
|
||||
player.force.max_successful_attempts_per_tick_per_construction_queue = 3 * amount
|
||||
player.force.max_failed_attempts_per_tick_per_construction_queue = 1 * amount
|
||||
game.print{'expcom-bot-queue.result', player.name, 3 * amount, 1 * amount}
|
||||
return Commands.success
|
||||
end)
|
||||
45
exp_legacy/module/modules/commands/cheat-mode.lua
Normal file
45
exp_legacy/module/modules/commands/cheat-mode.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
--[[-- Commands Module - Cheat Mode
|
||||
- Adds a command that allows players to enter cheat mode
|
||||
@commands Cheat-Mode
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Toggles cheat mode for your player, or another player.
|
||||
-- @command toggle-cheat-mode
|
||||
-- @tparam[opt=self] LuaPlayer player player to toggle chest mode of, can be nil for self
|
||||
Commands.new_command('toggle-cheat-mode', {'expcom-cheat.description-cheat'}, 'Toggles cheat mode for your player, or another player.')
|
||||
:add_param('player', true, 'player')
|
||||
:set_defaults{player=function(player)
|
||||
return player -- default is the user using the command
|
||||
end}
|
||||
:set_flag('admin_only')
|
||||
:register(function(_, player)
|
||||
player.cheat_mode = not player.cheat_mode
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
Commands.new_command('research-all', {'expcom-cheat.description-res'}, 'Set all research for your force.')
|
||||
:set_flag('admin_only')
|
||||
:add_param('force', true, 'force')
|
||||
:set_defaults{force=function(player)
|
||||
return player.force
|
||||
end}
|
||||
:register(function(player, force)
|
||||
force.research_all_technologies()
|
||||
game.print{'expcom-cheat.res', player.name}
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
Commands.new_command('toggle-always-day', {'expcom-cheat.description-day'}, 'Toggles always day in surface.')
|
||||
:set_flag('admin_only')
|
||||
:add_param('surface', true, 'surface')
|
||||
:set_defaults{surface=function(player)
|
||||
return player.surface
|
||||
end}
|
||||
:register(function(player, surface)
|
||||
surface.always_day = not surface.always_day
|
||||
game.print{'expcom-cheat.day', player.name, surface.always_day}
|
||||
return Commands.success
|
||||
end)
|
||||
23
exp_legacy/module/modules/commands/clear-inventory.lua
Normal file
23
exp_legacy/module/modules/commands/clear-inventory.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
--[[-- Commands Module - Clear Inventory
|
||||
- Adds a command that allows admins to clear people's inventorys
|
||||
@commands Clear-Inventory
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local move_items_stack = _C.move_items_stack --- @dep expcore.common
|
||||
require 'config.expcore.command_role_parse'
|
||||
|
||||
--- Clears a players inventory
|
||||
-- @command clear-inventory
|
||||
-- @tparam LuaPlayer player the player to clear the inventory of
|
||||
Commands.new_command('clear-inventory', {'expcom-clr-inv.description'}, 'Clears a players inventory')
|
||||
:add_param('player', false, 'player-role')
|
||||
:add_alias('clear-inv', 'move-inventory', 'move-inv')
|
||||
:register(function(_, player)
|
||||
local inv = player.get_main_inventory()
|
||||
if not inv then
|
||||
return Commands.error{'expcore-commands.reject-player-alive'}
|
||||
end
|
||||
move_items_stack(inv)
|
||||
inv.clear()
|
||||
end)
|
||||
107
exp_legacy/module/modules/commands/connect.lua
Normal file
107
exp_legacy/module/modules/commands/connect.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
--[[-- Commands Module - Connect
|
||||
- Adds a commands that allows you to request a player move to another server
|
||||
@commands Connect
|
||||
]]
|
||||
|
||||
local Async = require 'expcore.async' --- @dep expcore.async
|
||||
local External = require 'expcore.external' --- @dep expcore.external
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_role_parse'
|
||||
local concat = table.concat
|
||||
|
||||
local request_connection = Async.register(External.request_connection)
|
||||
|
||||
local function get_server_id(server)
|
||||
local current_server = External.get_current_server()
|
||||
local current_version = current_server.version
|
||||
local servers = External.get_servers_filtered(server)
|
||||
|
||||
local server_names_before, server_names = {}, {}
|
||||
local server_count_before, server_count = 0, 0
|
||||
for next_server_id, server_details in pairs(servers) do
|
||||
server_count_before = server_count_before + 1
|
||||
server_names_before[server_count_before] = server_details.name
|
||||
if server_details.version == current_version then
|
||||
server_count = server_count + 1
|
||||
server_names[server_count] = server_details.name
|
||||
else
|
||||
servers[next_server_id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if server_count > 1 then
|
||||
return false, Commands.error{'expcom-connect.too-many-matching', concat(server_names, ', ')}
|
||||
elseif server_count == 1 then
|
||||
local server_id, server_details = next(servers)
|
||||
local status = External.get_server_status(server_id)
|
||||
if server_id == current_server.id then
|
||||
return false, Commands.error{'expcom-connect.same-server', server_details.name}
|
||||
elseif status == 'Offline' then
|
||||
return false, Commands.error{'expcom-connect.offline', server_details.name}
|
||||
end
|
||||
return true, server_id
|
||||
elseif server_count_before > 0 then
|
||||
return false, Commands.error{'expcom-connect.wrong-version', concat(server_names_before, ', ')}
|
||||
else
|
||||
return false, Commands.error{'expcom-connect.none-matching'}
|
||||
end
|
||||
end
|
||||
|
||||
--- Connect to a different server
|
||||
-- @command connect
|
||||
-- @tparam string server The address or name of the server to connect to
|
||||
-- @tparam[opt=false] boolean is_address If an address was given for the server param
|
||||
Commands.new_command('connect', {'expcom-connect.description'}, 'Connect to another server')
|
||||
:add_param('server')
|
||||
:add_param('is_address', true, 'boolean')
|
||||
:add_alias('join', 'server')
|
||||
:register(function(player, server, is_address)
|
||||
local server_id = server
|
||||
if not is_address and External.valid() then
|
||||
local success, new_server_id = get_server_id(server)
|
||||
if not success then return new_server_id end
|
||||
server_id = new_server_id
|
||||
end
|
||||
|
||||
Async(request_connection, player, server_id, true)
|
||||
end)
|
||||
|
||||
--- Connect a player to a different server
|
||||
-- @command connect-player
|
||||
-- @tparam string address The address or name of the server to connect to
|
||||
-- @tparam LuaPlayer player The player to connect to a different server
|
||||
-- @tparam[opt=false] boolean is_address If an address was given for the server param
|
||||
Commands.new_command('connect-player', {'expcom-connect.description-player'}, 'Send a player to a different server')
|
||||
:add_param('player', 'player-role')
|
||||
:add_param('server')
|
||||
:add_param('is_address', true, 'boolean')
|
||||
:register(function(_, player, server, is_address)
|
||||
local server_id = server
|
||||
if not is_address and External.valid() then
|
||||
local success, new_server_id = get_server_id(server)
|
||||
if not success then return new_server_id end
|
||||
server_id = new_server_id
|
||||
end
|
||||
|
||||
External.request_connection(player, server_id)
|
||||
end)
|
||||
|
||||
--- Connect all players to a different server
|
||||
-- @command connect-all
|
||||
-- @tparam string address The address or name of the server to connect to
|
||||
-- @tparam[opt=false] boolean is_address If an address was given for the server param
|
||||
Commands.new_command('connect-all', {'expcom-connect.description-all'}, 'Connect all players to another server')
|
||||
:add_param('server')
|
||||
:add_param('is_address', true, 'boolean')
|
||||
:register(function(_, server, is_address)
|
||||
local server_id = server
|
||||
if not is_address and External.valid() then
|
||||
local success, new_server_id = get_server_id(server)
|
||||
if not success then return new_server_id end
|
||||
server_id = new_server_id
|
||||
end
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
External.request_connection(player, server_id)
|
||||
end
|
||||
end)
|
||||
14
exp_legacy/module/modules/commands/debug.lua
Normal file
14
exp_legacy/module/modules/commands/debug.lua
Normal file
@@ -0,0 +1,14 @@
|
||||
--[[-- Commands Module - Debug
|
||||
- Adds a command that opens the debug frame
|
||||
@commands Debug
|
||||
]]
|
||||
|
||||
local DebugView = require 'modules.gui.debug.main_view' --- @dep modules.gui.debug.main_view
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
|
||||
--- Opens the debug pannel for viewing tables.
|
||||
-- @command debug
|
||||
Commands.new_command('debug', {'expcom-debug.description'}, 'Opens the debug pannel for viewing tables.')
|
||||
:register(function(player)
|
||||
DebugView.open_dubug(player)
|
||||
end)
|
||||
29
exp_legacy/module/modules/commands/enemy.lua
Normal file
29
exp_legacy/module/modules/commands/enemy.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
--[[-- Commands Module - Enemy
|
||||
- Adds a command of handling enemy
|
||||
@commands Enemy
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
Commands.new_command('kill-biters', {'expcom-enemy.description-kill'}, 'Kill all biters only')
|
||||
:set_flag('admin_only')
|
||||
:register(function(_, _)
|
||||
game.forces['enemy'].kill_all_units()
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
Commands.new_command('remove-biters', {'expcom-enemy.description-remove'}, 'Remove biters and prevent generation')
|
||||
:set_flag('admin_only')
|
||||
:add_param('surface', true, 'surface')
|
||||
:set_defaults{surface=function(player)
|
||||
return player.surface
|
||||
end}
|
||||
:register(function(_, surface)
|
||||
for _, entity in pairs(surface.find_entities_filtered({force='enemy'})) do
|
||||
entity.destroy()
|
||||
end
|
||||
|
||||
surface.map_gen_settings.autoplace_controls['enemy-base'].size = 'none'
|
||||
return Commands.success
|
||||
end)
|
||||
19
exp_legacy/module/modules/commands/find.lua
Normal file
19
exp_legacy/module/modules/commands/find.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
--[[-- Commands Module - Find
|
||||
- Adds a command that zooms in on the given player
|
||||
@commands Find
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Find a player on your map.
|
||||
-- @command find-on-map
|
||||
-- @tparam LuaPlayer the player to find on the map
|
||||
Commands.new_command('find-on-map', {'expcom-find.description'}, 'Find a player on your map.')
|
||||
:add_param('player', false, 'player-online')
|
||||
:add_alias('find', 'zoom-to')
|
||||
:register(function(player, action_player)
|
||||
local position = action_player.position
|
||||
player.zoom_to_world(position, 1.75)
|
||||
return Commands.success -- prevents command complete message from showing
|
||||
end)
|
||||
19
exp_legacy/module/modules/commands/friendly-fire.lua
Normal file
19
exp_legacy/module/modules/commands/friendly-fire.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
--[[-- Commands Module - Toggle Friendly Fire
|
||||
- Adds a command that toggle all friendly fire
|
||||
@commands Toggle Friendly Fire
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
-- For Modded Server Use
|
||||
Commands.new_command('toggle-friendly-fire', {'expcom-ff.description'}, 'Toggle friendly fire')
|
||||
:add_param('force', true, 'force')
|
||||
:set_defaults{force=function(player)
|
||||
return player.force
|
||||
end}
|
||||
:register(function(player, force)
|
||||
force.friendly_fire = not force.friendly_fire
|
||||
game.print{'expcom-ff.ff', player.name, force.friendly_fire}
|
||||
return Commands.success
|
||||
end)
|
||||
100
exp_legacy/module/modules/commands/help.lua
Normal file
100
exp_legacy/module/modules/commands/help.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
--[[-- Commands Module - Help
|
||||
- Adds a better help command that allows searching of descriotions and names
|
||||
@commands Help
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
local results_per_page = 5
|
||||
|
||||
local search_cache = {}
|
||||
Global.register(search_cache, function(tbl)
|
||||
search_cache = tbl
|
||||
end)
|
||||
|
||||
--- Searches for a keyword in all commands you are allowed to use.
|
||||
-- @command chelp
|
||||
-- @tparam string keyword the keyword that will be looked for
|
||||
-- @tparam number page the page of help to view, must be in range of pages
|
||||
Commands.new_command('search-help', {'expcom-chelp.description'}, 'Searches for a keyword in all commands you are allowed to use.')
|
||||
:add_alias('chelp', 'shelp', 'commands')
|
||||
:add_param('keyword', true)
|
||||
:add_param('page', true, 'integer')
|
||||
:set_defaults{keyword='', page=1}
|
||||
:register(function(player, keyword, page)
|
||||
local player_index = player and player.index or 0
|
||||
|
||||
-- if keyword is a number then treat it as page number
|
||||
if tonumber(keyword) then
|
||||
page = math.floor(tonumber(keyword))
|
||||
keyword = ''
|
||||
end
|
||||
|
||||
-- gets a value for pages, might have result in cache
|
||||
local pages
|
||||
local found = 0
|
||||
|
||||
if search_cache[player_index] and search_cache[player_index].keyword == keyword:lower() then
|
||||
pages = search_cache[player_index].pages
|
||||
found = search_cache[player_index].found
|
||||
|
||||
else
|
||||
pages = {{}}
|
||||
local current_page = 1
|
||||
local page_count = 0
|
||||
local commands = Commands.search(keyword, player)
|
||||
|
||||
-- loops other all commands returned by search, includes game commands
|
||||
for _, command_data in pairs(commands) do
|
||||
-- if the number of results if greater than the number already added then it moves onto a new page
|
||||
if page_count >= results_per_page then
|
||||
page_count = 0
|
||||
current_page = current_page + 1
|
||||
table.insert(pages, {})
|
||||
end
|
||||
|
||||
-- adds the new command to the page
|
||||
page_count = page_count + 1
|
||||
found = found + 1
|
||||
local alias_format = #command_data.aliases > 0 and {'expcom-chelp.alias', table.concat(command_data.aliases, ', ')} or ''
|
||||
table.insert(pages[current_page], {
|
||||
'expcom-chelp.format',
|
||||
command_data.name,
|
||||
command_data.description,
|
||||
command_data.help,
|
||||
alias_format
|
||||
})
|
||||
end
|
||||
|
||||
-- adds the result to the cache
|
||||
search_cache[player_index] = {
|
||||
keyword=keyword:lower(),
|
||||
pages=pages,
|
||||
found=found
|
||||
}
|
||||
end
|
||||
|
||||
-- print the requested page
|
||||
keyword = keyword == '' and '<all>' or keyword
|
||||
Commands.print({'expcom-chelp.title', keyword}, 'cyan')
|
||||
|
||||
if pages[page] then
|
||||
for _, command in pairs(pages[page]) do
|
||||
Commands.print(command)
|
||||
end
|
||||
|
||||
Commands.print({'expcom-chelp.footer', found, page, #pages}, 'cyan')
|
||||
|
||||
else
|
||||
Commands.print({'expcom-chelp.footer', found, page, #pages}, 'cyan')
|
||||
return Commands.error{'expcom-chelp.out-of-range', page}
|
||||
end
|
||||
|
||||
-- blocks command complete message
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
-- way to access global
|
||||
return search_cache
|
||||
83
exp_legacy/module/modules/commands/home.lua
Normal file
83
exp_legacy/module/modules/commands/home.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
--[[-- Commands Module - Home
|
||||
- Adds a command that allows setting and teleporting to your home position
|
||||
@commands Home
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
local homes = {}
|
||||
Global.register(homes, function(tbl)
|
||||
homes = tbl
|
||||
end)
|
||||
|
||||
local function teleport(player, position)
|
||||
local surface = player.surface
|
||||
local pos = surface.find_non_colliding_position('character', position, 32, 1)
|
||||
if not position then return false end
|
||||
if player.driving then player.driving = false end -- kicks a player out a vehicle if in one
|
||||
player.teleport(pos, surface)
|
||||
return true
|
||||
end
|
||||
|
||||
local function floor_pos(position)
|
||||
return {
|
||||
x=math.floor(position.x),
|
||||
y=math.floor(position.y)
|
||||
}
|
||||
end
|
||||
|
||||
--- Teleports you to your home location
|
||||
-- @command home
|
||||
Commands.new_command('home', {'expcom-home.description-home'}, 'Teleports you to your home location')
|
||||
:register(function(player)
|
||||
local home = homes[player.index]
|
||||
if not home or not home[1] then
|
||||
return Commands.error{'expcom-home.no-home'}
|
||||
end
|
||||
local rtn = floor_pos(player.position)
|
||||
teleport(player, home[1])
|
||||
home[2] = rtn
|
||||
Commands.print{'expcom-home.return-set', rtn.x, rtn.y}
|
||||
end)
|
||||
|
||||
--- Sets your home location to your current position
|
||||
-- @command home-set
|
||||
Commands.new_command('home-set', {'expcom-home.description-home-set'}, 'Sets your home location to your current position')
|
||||
:register(function(player)
|
||||
local home = homes[player.index]
|
||||
if not home then
|
||||
home = {}
|
||||
homes[player.index] = home
|
||||
end
|
||||
local pos = floor_pos(player.position)
|
||||
home[1] = pos
|
||||
Commands.print{'expcom-home.home-set', pos.x, pos.y}
|
||||
end)
|
||||
|
||||
--- Returns your current home location
|
||||
-- @command home-get
|
||||
Commands.new_command('home-get', {'expcom-home.description-home-get'}, 'Returns your current home location')
|
||||
:register(function(player)
|
||||
local home = homes[player.index]
|
||||
if not home or not home[1] then
|
||||
return Commands.error{'expcom-home.no-home'}
|
||||
end
|
||||
local pos = home[1]
|
||||
Commands.print{'expcom-home.home-get', pos.x, pos.y}
|
||||
end)
|
||||
|
||||
--- Teleports you to previous location
|
||||
-- @command return
|
||||
Commands.new_command('return', {'expcom-home.description-return'}, 'Teleports you to previous location')
|
||||
:register(function(player)
|
||||
local home = homes[player.index]
|
||||
if not home or not home[2] then
|
||||
return Commands.error{'expcom-home.no-return'}
|
||||
end
|
||||
local rtn = floor_pos(player.position)
|
||||
teleport(player, home[2])
|
||||
home[2] = rtn
|
||||
Commands.print{'expcom-home.return-set', rtn.x, rtn.y}
|
||||
end)
|
||||
116
exp_legacy/module/modules/commands/interface.lua
Normal file
116
exp_legacy/module/modules/commands/interface.lua
Normal file
@@ -0,0 +1,116 @@
|
||||
--[[-- Commands Module - Interface
|
||||
- Adds a command that acts as a direct link to the the active softmod, for debug use
|
||||
@commands Interface
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
|
||||
-- modules that are loaded into the interface env to be accessed
|
||||
local interface_modules = {
|
||||
['Commands'] = Commands,
|
||||
['output'] = _C.player_return,
|
||||
['Group'] = 'expcore.permission_groups',
|
||||
['Roles'] = 'expcore.roles',
|
||||
['Gui'] = 'expcore.gui',
|
||||
['Async'] = 'expcore.async',
|
||||
['Datastore'] = 'expcore.datastore',
|
||||
['External'] = 'expcore.external'
|
||||
}
|
||||
|
||||
-- loads all the modules given in the above table
|
||||
for key, value in pairs(interface_modules) do
|
||||
if type(value) == 'string' then
|
||||
interface_modules[key] = _C.opt_require(value)
|
||||
end
|
||||
end
|
||||
|
||||
local interface_env = {} -- used as a persistent sandbox for interface commands
|
||||
local interface_callbacks = {} -- saves callbacks which can load new values per use
|
||||
Global.register(interface_env, function(tbl)
|
||||
interface_env = tbl
|
||||
end)
|
||||
|
||||
--- Adds a static module that can be accessed with the interface
|
||||
-- @tparam string name The name that the value is assigned to
|
||||
-- @tparam any value The value that will be accessible in the interface env
|
||||
-- callback param - player: LuaPlayer - the player who used the command
|
||||
local function add_interface_module(name, value)
|
||||
interface_modules[name] = value
|
||||
end
|
||||
|
||||
--- Adds a dynamic value that is calculated when the interface is used
|
||||
-- @tparam string name The name that the value is assigned to
|
||||
-- @tparam function callback The function that will be called to get the value
|
||||
local function add_interface_callback(name, callback)
|
||||
if type(callback) == 'function' then
|
||||
interface_callbacks[name] = callback
|
||||
end
|
||||
end
|
||||
|
||||
--- Internal, this is a meta function for __index when self[key] is nil
|
||||
local function get_index(_, key)
|
||||
if interface_env[key] then
|
||||
return interface_env[key]
|
||||
elseif interface_modules[key] then
|
||||
return interface_modules[key]
|
||||
elseif _G[key] then
|
||||
return _G[key]
|
||||
end
|
||||
end
|
||||
|
||||
--- Sends an invocation to be ran and returns the result.
|
||||
-- @command interface
|
||||
-- @tparam string invocation the command that will be run
|
||||
Commands.new_command('interface', {'expcom-interface.description'}, 'Sends an invocation to be ran and returns the result.')
|
||||
:add_param('invocation', false)
|
||||
:enable_auto_concat()
|
||||
:set_flag('admin_only')
|
||||
:register(function(player, invocation)
|
||||
-- If the invocation has no white space then prepend return to it
|
||||
if not invocation:find('%s') and not invocation:find('return') then
|
||||
invocation = 'return '..invocation
|
||||
end
|
||||
|
||||
-- _env will be the new _ENV that the invocation will run inside of
|
||||
local _env = setmetatable({}, {
|
||||
__index = get_index,
|
||||
__newindex = interface_env
|
||||
})
|
||||
|
||||
-- If the command is ran by a player then load the dynamic values
|
||||
if player then
|
||||
for name, callback in pairs(interface_callbacks) do
|
||||
local _, rtn = pcall(callback, player)
|
||||
rawset(_env, name, rtn)
|
||||
end
|
||||
end
|
||||
|
||||
-- Compile the invocation with the custom _env value
|
||||
local invocation_func, compile_error = load(invocation, 'interface', nil, _env)
|
||||
if compile_error then return Commands.error(compile_error) end
|
||||
|
||||
-- Run the invocation
|
||||
local success, rtn = pcall(invocation_func)
|
||||
if not success then
|
||||
local err = rtn:gsub('%.%.%..-/temp/currently%-playing', '')
|
||||
return Commands.error(err)
|
||||
end
|
||||
return Commands.success(rtn)
|
||||
end)
|
||||
|
||||
-- Adds some basic callbacks for the interface
|
||||
add_interface_callback('player', function(player) return player end)
|
||||
add_interface_callback('surface', function(player) return player.surface end)
|
||||
add_interface_callback('force', function(player) return player.force end)
|
||||
add_interface_callback('position', function(player) return player.position end)
|
||||
add_interface_callback('entity', function(player) return player.selected end)
|
||||
add_interface_callback('tile', function(player) return player.surface.get_tile(player.position) end)
|
||||
|
||||
-- Module Return
|
||||
return {
|
||||
add_interface_module = add_interface_module,
|
||||
add_interface_callback = add_interface_callback,
|
||||
interface_env = interface_env,
|
||||
clean_stack_trace = function(str) return str:gsub('%.%.%..-/temp/currently%-playing', '') end
|
||||
}
|
||||
47
exp_legacy/module/modules/commands/jail.lua
Normal file
47
exp_legacy/module/modules/commands/jail.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
--[[-- Commands Module - Jail
|
||||
- Adds a commands that allow admins to jail and unjail
|
||||
@commands Jail
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Jail = require 'modules.control.jail' --- @dep modules.control.jail
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
require 'config.expcore.command_role_parse'
|
||||
|
||||
--- Puts a player into jail and removes all other roles.
|
||||
-- @command jail
|
||||
-- @tparam LuaPlayer player the player that will be jailed
|
||||
-- @tparam[opt] string reason the reason why the player is being jailed
|
||||
Commands.new_command('jail', {'expcom-jail.description-jail'}, 'Puts a player into jail and removes all other roles.')
|
||||
:add_param('player', false, 'player-role')
|
||||
:add_param('reason', true)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, action_player, reason)
|
||||
reason = reason or 'Non Given.'
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
local by_player_name_color = format_chat_player_name(player)
|
||||
local player_name = player and player.name or '<server>'
|
||||
if Jail.jail_player(action_player, player_name, reason) then
|
||||
game.print{'expcom-jail.give', action_player_name_color, by_player_name_color, reason}
|
||||
else
|
||||
return Commands.error{'expcom-jail.already-jailed', action_player_name_color}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Removes a player from jail.
|
||||
-- @command unjail
|
||||
-- @tparam LuaPlayer the player that will be unjailed
|
||||
Commands.new_command('unjail', {'expcom-jail.description-unjail'}, 'Removes a player from jail.')
|
||||
:add_param('player', false, 'player-role')
|
||||
:add_alias('clear-jail', 'remove-jail')
|
||||
:enable_auto_concat()
|
||||
:register(function(player, action_player)
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
local by_player_name_color = format_chat_player_name(player)
|
||||
local player_name = player and player.name or '<server>'
|
||||
if Jail.unjail_player(action_player, player_name) then
|
||||
game.print{'expcom-jail.remove', action_player_name_color, by_player_name_color}
|
||||
else
|
||||
return Commands.error{'expcom-jail.not-jailed', action_player_name_color}
|
||||
end
|
||||
end)
|
||||
36
exp_legacy/module/modules/commands/kill.lua
Normal file
36
exp_legacy/module/modules/commands/kill.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
--[[-- Commands Module - Kill
|
||||
- Adds a command that allows players to kill them selfs and others
|
||||
@commands Kill
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
require 'config.expcore.command_general_parse'
|
||||
require 'config.expcore.command_role_parse'
|
||||
|
||||
--- Kills yourself or another player.
|
||||
-- @command kill
|
||||
-- @tparam[opt=self] LuaPlayer player the player to kill, must be alive to be valid
|
||||
Commands.new_command('kill', {'expcom-kill.description'}, 'Kills yourself or another player.')
|
||||
:add_param('player', true, 'player-role-alive')
|
||||
:set_defaults{player=function(player)
|
||||
-- default is the player unless they are dead
|
||||
if player.character and player.character.health > 0 then
|
||||
return player
|
||||
end
|
||||
end}
|
||||
:register(function(player, action_player)
|
||||
if not action_player then
|
||||
-- can only be nil if no player given and the user is dead
|
||||
return Commands.error{'expcom-kill.already-dead'}
|
||||
end
|
||||
if player == action_player then
|
||||
action_player.character.die()
|
||||
|
||||
elseif Roles.player_allowed(player, 'command/kill/always') then
|
||||
action_player.character.die()
|
||||
|
||||
else
|
||||
return Commands.error{'expcore-commands.unauthorized'}
|
||||
end
|
||||
end)
|
||||
19
exp_legacy/module/modules/commands/last-location.lua
Normal file
19
exp_legacy/module/modules/commands/last-location.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
--[[-- Commands Module - Last location
|
||||
- Adds a command that will return the last location of a player
|
||||
@commands LastLocation
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Get the last location of a player.
|
||||
-- @command last-location
|
||||
-- @tparam LuaPlayer player the player that you want a location of
|
||||
Commands.new_command('last-location', {'expcom-lastlocation.description'}, 'Sends you the last location of a player')
|
||||
:add_alias('location')
|
||||
:add_param('player', false, 'player')
|
||||
:register(function(_, action_player)
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
return Commands.success{'expcom-lastlocation.response', action_player_name_color, string.format('%.1f', action_player.position.x), string.format('%.1f', action_player.position.y)}
|
||||
end)
|
||||
17
exp_legacy/module/modules/commands/me.lua
Normal file
17
exp_legacy/module/modules/commands/me.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
--[[-- Commands Module - Me
|
||||
- Adds a command that adds * around your message in the chat
|
||||
@commands Me
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
|
||||
--- Sends an action message in the chat
|
||||
-- @command me
|
||||
-- @tparam string action the action that follows your name in chat
|
||||
Commands.new_command('me', {'expcom-me.description'}, 'Sends an action message in the chat')
|
||||
:add_param('action', false)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, action)
|
||||
local player_name = player and player.name or '<Server>'
|
||||
game.print(string.format('* %s %s *', player_name, action), player.chat_color)
|
||||
end)
|
||||
34
exp_legacy/module/modules/commands/pollution.lua
Normal file
34
exp_legacy/module/modules/commands/pollution.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
--[[-- Commands Module - Pollution Handle
|
||||
- Adds a command that allows modifying pollution
|
||||
@commands Pollution Handle
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
Commands.new_command('pollution-clear', {'expcom-pol.description-clr'}, 'Clear pollution')
|
||||
:set_flag('admin_only')
|
||||
:add_alias('pol-clr')
|
||||
:add_param('surface', true, 'surface')
|
||||
:set_defaults{surface=function(player)
|
||||
return player.surface
|
||||
end}
|
||||
:register(function(player, surface)
|
||||
surface.clear_pollution()
|
||||
game.print{'expcom-pol.clr', player.name}
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
Commands.new_command('pollution-off', {'expcom-pol.description-off'}, 'Disable pollution')
|
||||
:set_flag('admin_only')
|
||||
:add_alias('pol-off')
|
||||
:register(function(player)
|
||||
game.map_settings.pollution.enabled = false
|
||||
|
||||
for _, v in pairs(game.surfaces) do
|
||||
v.clear_pollution()
|
||||
end
|
||||
|
||||
game.print{'expcom-pol.off', player.name}
|
||||
return Commands.success
|
||||
end)
|
||||
215
exp_legacy/module/modules/commands/protection.lua
Normal file
215
exp_legacy/module/modules/commands/protection.lua
Normal file
@@ -0,0 +1,215 @@
|
||||
--[[-- Commands Module - Protection
|
||||
- Adds commands that can add and remove protection
|
||||
@commands Protection
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
local EntityProtection = require 'modules.control.protection' --- @dep modules.control.protection
|
||||
local Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
|
||||
local SelectionProtectEntity = 'ProtectEntity'
|
||||
local SelectionProtectArea = 'ProtectArea'
|
||||
|
||||
local renders = {} -- Stores all renders for a player
|
||||
Global.register({
|
||||
renders = renders
|
||||
}, function(tbl)
|
||||
renders = tbl.renders
|
||||
end)
|
||||
|
||||
--- Test if a point is inside an aabb
|
||||
local function aabb_point_enclosed(point, aabb)
|
||||
return point.x >= aabb.left_top.x and point.y >= aabb.left_top.y
|
||||
and point.x <= aabb.right_bottom.x and point.y <= aabb.right_bottom.y
|
||||
end
|
||||
|
||||
--- Test if an aabb is inside another aabb
|
||||
local function aabb_area_enclosed(aabbOne, aabbTwo)
|
||||
return aabb_point_enclosed(aabbOne.left_top, aabbTwo)
|
||||
and aabb_point_enclosed(aabbOne.right_bottom, aabbTwo)
|
||||
end
|
||||
|
||||
--- Align an aabb to the grid by expanding it
|
||||
local function aabb_align_expand(aabb)
|
||||
return {
|
||||
left_top = {x = math.floor(aabb.left_top.x), y = math.floor(aabb.left_top.y)},
|
||||
right_bottom = {x = math.ceil(aabb.right_bottom.x), y = math.ceil(aabb.right_bottom.y)}
|
||||
}
|
||||
end
|
||||
|
||||
--- 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
|
||||
|
||||
--- Show a protected entity to a player
|
||||
local function show_protected_entity(player, entity)
|
||||
local key = get_entity_key(entity)
|
||||
if renders[player.index][key] then return end
|
||||
local rb = entity.selection_box.right_bottom
|
||||
local render_id = rendering.draw_sprite{
|
||||
sprite = 'utility/notification',
|
||||
target = entity,
|
||||
target_offset = {
|
||||
(rb.x-entity.position.x)*0.75,
|
||||
(rb.y-entity.position.y)*0.75
|
||||
},
|
||||
x_scale = 2,
|
||||
y_scale = 2,
|
||||
surface = entity.surface,
|
||||
players = { player }
|
||||
}
|
||||
renders[player.index][key] = render_id
|
||||
end
|
||||
|
||||
--- Show a protected area to a player
|
||||
local function show_protected_area(player, surface, area)
|
||||
local key = get_area_key(area)
|
||||
if renders[player.index][key] then return end
|
||||
local render_id = rendering.draw_rectangle{
|
||||
color = {1, 1, 0, 0.5},
|
||||
filled = false,
|
||||
width = 3,
|
||||
left_top = area.left_top,
|
||||
right_bottom = area.right_bottom,
|
||||
surface = surface,
|
||||
players = { player }
|
||||
}
|
||||
renders[player.index][key] = render_id
|
||||
end
|
||||
|
||||
--- Remove a render object for a player
|
||||
local function remove_render(player, key)
|
||||
local render = renders[player.index][key]
|
||||
if render and rendering.is_valid(render) then rendering.destroy(render) end
|
||||
renders[player.index][key] = nil
|
||||
end
|
||||
|
||||
--- Toggles entity protection selection
|
||||
-- @command protect-entity
|
||||
Commands.new_command('protect-entity', {'expcom-protection.description-pe'}, 'Toggles entity protection selection, hold shift to remove protection')
|
||||
:add_alias('pe')
|
||||
:register(function(player)
|
||||
if Selection.is_selecting(player, SelectionProtectEntity) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionProtectEntity)
|
||||
return Commands.success{'expcom-protection.entered-entity-selection'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Toggles area protection selection
|
||||
-- @command protect-area
|
||||
Commands.new_command('protect-area', {'expcom-protection.description-pa'}, 'Toggles area protection selection, hold shift to remove protection')
|
||||
:add_alias('pa')
|
||||
:register(function(player)
|
||||
if Selection.is_selecting(player, SelectionProtectArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionProtectArea)
|
||||
return Commands.success{'expcom-protection.entered-area-selection'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- When an area is selected to add protection to entities
|
||||
Selection.on_selection(SelectionProtectEntity, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, entity in ipairs(event.entities) do
|
||||
EntityProtection.add_entity(entity)
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
player.print{'expcom-protection.protected-entities', #event.entities}
|
||||
end)
|
||||
|
||||
--- When an area is selected to remove protection from entities
|
||||
Selection.on_alt_selection(SelectionProtectEntity, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, entity in ipairs(event.entities) do
|
||||
EntityProtection.remove_entity(entity)
|
||||
remove_render(player, get_entity_key(entity))
|
||||
end
|
||||
player.print{'expcom-protection.unprotected-entities', #event.entities}
|
||||
end)
|
||||
|
||||
--- When an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionProtectArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local areas = EntityProtection.get_areas(event.surface)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, next_area in pairs(areas) do
|
||||
if aabb_area_enclosed(area, next_area) then
|
||||
return player.print{'expcom-protection.already-protected'}
|
||||
end
|
||||
end
|
||||
EntityProtection.add_area(event.surface, area)
|
||||
show_protected_area(player, event.surface, area)
|
||||
player.print{'expcom-protection.protected-area'}
|
||||
end)
|
||||
|
||||
--- When an area is selected to remove protection from the area
|
||||
Selection.on_alt_selection(SelectionProtectArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local areas = EntityProtection.get_areas(event.surface)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, next_area in pairs(areas) do
|
||||
if aabb_area_enclosed(next_area, area) then
|
||||
EntityProtection.remove_area(event.surface, next_area)
|
||||
player.print{'expcom-protection.unprotected-area'}
|
||||
remove_render(player, get_area_key(next_area))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- When selection starts show all protected entities and protected areas
|
||||
Event.add(Selection.events.on_player_selection_start, function(event)
|
||||
if event.selection ~= SelectionProtectEntity and event.selection ~= SelectionProtectArea then return end
|
||||
local player = game.get_player(event.player_index)
|
||||
local surface = player.surface
|
||||
renders[player.index] = {}
|
||||
-- Show protected entities
|
||||
local entities = EntityProtection.get_entities(surface)
|
||||
for _, entity in pairs(entities) do
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
-- Show always protected entities by name
|
||||
if #EntityProtection.protected_entity_names > 0 then
|
||||
for _, entity in pairs(surface.find_entities_filtered{ name = EntityProtection.protected_entity_names, force = player.force }) do
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
end
|
||||
-- Show always protected entities by type
|
||||
if #EntityProtection.protected_entity_types > 0 then
|
||||
for _, entity in pairs(surface.find_entities_filtered{ type = EntityProtection.protected_entity_types, force = player.force }) do
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
end
|
||||
-- Show protected areas
|
||||
local areas = EntityProtection.get_areas(surface)
|
||||
for _, area in pairs(areas) do
|
||||
show_protected_area(player, surface, area)
|
||||
end
|
||||
end)
|
||||
|
||||
--- When selection ends hide protected entities and protected areas
|
||||
Event.add(Selection.events.on_player_selection_end, function(event)
|
||||
if event.selection ~= SelectionProtectEntity and event.selection ~= SelectionProtectArea then return end
|
||||
for _, id in pairs(renders[event.player_index]) do
|
||||
if rendering.is_valid(id) then rendering.destroy(id) end
|
||||
end
|
||||
renders[event.player_index] = nil
|
||||
end)
|
||||
|
||||
--- When there is a repeat offence print it in chat
|
||||
Event.add(EntityProtection.events.on_repeat_violation, function(event)
|
||||
local player_name = format_chat_player_name(event.player_index)
|
||||
Roles.print_to_roles_higher('Regular', {'expcom-protection.repeat-offence', player_name, event.entity.localised_name, event.entity.position.x, event.entity.position.y})
|
||||
end)
|
||||
62
exp_legacy/module/modules/commands/rainbow.lua
Normal file
62
exp_legacy/module/modules/commands/rainbow.lua
Normal file
@@ -0,0 +1,62 @@
|
||||
--[[-- Commands Module - Rainbow
|
||||
- Adds a command that prints your message in rainbow font
|
||||
@commands Rainbow
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local format_chat_colour = _C.format_chat_colour --- @dep expcore.common
|
||||
|
||||
local function step_component(c1, c2)
|
||||
if c1 < 0 then
|
||||
return 0, c2+c1
|
||||
elseif c1 > 1 then
|
||||
return 1, c2-c1+1
|
||||
else
|
||||
return c1, c2
|
||||
end
|
||||
end
|
||||
|
||||
local function step_color(color)
|
||||
color.r, color.g = step_component(color.r, color.g)
|
||||
color.g, color.b = step_component(color.g, color.b)
|
||||
color.b, color.r = step_component(color.b, color.r)
|
||||
color.r = step_component(color.r, 0)
|
||||
return color
|
||||
end
|
||||
|
||||
local function next_color(color, step)
|
||||
step = step or 0.1
|
||||
local new_color = {r=0, g=0, b=0}
|
||||
if color.b == 0 and color.r ~= 0 then
|
||||
new_color.r = color.r-step
|
||||
new_color.g = color.g+step
|
||||
elseif color.r == 0 and color.g ~= 0 then
|
||||
new_color.g = color.g-step
|
||||
new_color.b = color.b+step
|
||||
elseif color.g == 0 and color.b ~= 0 then
|
||||
new_color.b = color.b-step
|
||||
new_color.r = color.r+step
|
||||
end
|
||||
return step_color(new_color)
|
||||
end
|
||||
|
||||
--- Sends an rainbow message in the chat
|
||||
-- @command rainbow
|
||||
-- @tparam string message the message that will be printed in chat
|
||||
Commands.new_command('rainbow', {'expcom-rainbow.description'}, 'Sends an rainbow message in the chat')
|
||||
:add_param('message', false)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, message)
|
||||
local player_name = player and player.name or '<Server>'
|
||||
local player_color = player and player.color or nil
|
||||
local color_step = 3/message:len()
|
||||
if color_step > 1 then color_step = 1 end
|
||||
local current_color = {r=1, g=0, b=0}
|
||||
local output = format_chat_colour(player_name..': ', player_color)
|
||||
output = output..message:gsub('%S', function(letter)
|
||||
local rtn = format_chat_colour(letter, current_color)
|
||||
current_color = next_color(current_color, color_step)
|
||||
return rtn
|
||||
end)
|
||||
game.print(output)
|
||||
end)
|
||||
86
exp_legacy/module/modules/commands/ratio.lua
Normal file
86
exp_legacy/module/modules/commands/ratio.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
|
||||
local Commands = require 'expcore.commands'
|
||||
|
||||
local function Modules(moduleInventory) -- returns the multiplier of the modules
|
||||
local effect1 = moduleInventory.get_item_count("productivity-module") -- type 1
|
||||
local effect2 = moduleInventory.get_item_count("productivity-module-2")-- type 2
|
||||
local effect3 = moduleInventory.get_item_count("productivity-module-3") -- type 3
|
||||
|
||||
local multi = effect1*4+effect2*6+effect3*10
|
||||
return multi/100+1
|
||||
end
|
||||
|
||||
local function AmountOfMachines(itemsPerSecond, output)
|
||||
if(itemsPerSecond) then
|
||||
return itemsPerSecond/output
|
||||
end
|
||||
end
|
||||
|
||||
Commands.new_command('ratio', {'expcom-ratio.description'}, 'This command will give the input and output ratios of the selected machine. Use the parameter for calculating the machines needed for that amount of items per second.')
|
||||
:add_param('itemsPerSecond', true, 'number')
|
||||
:register(function(player, itemsPerSecond)
|
||||
local machine = player.selected -- selected machine
|
||||
|
||||
if not machine then --nil check
|
||||
return Commands.error{'expcom-ratio.notSelecting'}
|
||||
end
|
||||
|
||||
if machine.type ~= "assembling-machine" and machine.type ~= "furnace" then
|
||||
return Commands.error{'expcom-ratio.notSelecting'}
|
||||
end
|
||||
|
||||
local recipe = machine.get_recipe() -- recipe
|
||||
|
||||
if not recipe then --nil check
|
||||
return Commands.error{'expcom-ratio.notSelecting'}
|
||||
end
|
||||
|
||||
local items = recipe.ingredients -- items in that recipe
|
||||
local products = recipe.products -- output items
|
||||
local amountOfMachines
|
||||
local moduleInventory = machine.get_module_inventory()--the module Inventory of the machine
|
||||
local multi = Modules(moduleInventory) --function for the productively modals
|
||||
|
||||
if itemsPerSecond then
|
||||
amountOfMachines = math.ceil( AmountOfMachines(itemsPerSecond, 1/recipe.energy*machine.crafting_speed*products[1].amount*multi)) -- amount of machines
|
||||
end
|
||||
|
||||
if not amountOfMachines then
|
||||
amountOfMachines = 1 --set to 1 to make it not nil
|
||||
end
|
||||
|
||||
----------------------------items----------------------------
|
||||
for i, item in ipairs(items) do
|
||||
local sprite -- string to make the icon work either fluid ore item
|
||||
|
||||
if item.type == "item" then
|
||||
sprite = 'expcom-ratio.item-in'
|
||||
|
||||
else
|
||||
sprite = 'expcom-ratio.fluid-in'
|
||||
end
|
||||
|
||||
local ips = item.amount/recipe.energy*machine.crafting_speed*amountOfMachines --math on the items/fluids per second
|
||||
Commands.print{sprite, math.round(ips, 3), item.name} -- full string
|
||||
end
|
||||
|
||||
----------------------------products----------------------------
|
||||
for i, product in ipairs(products) do
|
||||
local sprite -- string to make the icon work either fluid ore item
|
||||
|
||||
if product.type == "item" then
|
||||
sprite = 'expcom-ratio.item-out'
|
||||
else
|
||||
sprite = 'expcom-ratio.fluid-out'
|
||||
end
|
||||
|
||||
local output = 1/recipe.energy*machine.crafting_speed*product.amount*multi --math on the outputs per second
|
||||
Commands.print {sprite, math.round(output*amountOfMachines, 3), product.name} -- full string
|
||||
|
||||
end
|
||||
|
||||
if amountOfMachines ~= 1 then
|
||||
Commands.print{'expcom-ratio.machines', amountOfMachines}
|
||||
end
|
||||
end)
|
||||
52
exp_legacy/module/modules/commands/repair.lua
Normal file
52
exp_legacy/module/modules/commands/repair.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--[[-- Commands Module - Repair
|
||||
- Adds a command that allows an admin to repair and revive a large area
|
||||
@commands Repair
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local config = require 'config.repair' --- @dep config.repair
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
local max_time_to_live = 4294967295 -- unit32 max
|
||||
--- Repairs entities on your force around you
|
||||
-- @command repair
|
||||
-- @tparam number range the range to repair stuff in, there is a max limit to this
|
||||
Commands.new_command('repair', {'expcom-repair.description'}, 'Repairs entities on your force around you')
|
||||
:add_param('range', false, 'integer-range', 1, config.max_range)
|
||||
:register(function(player, range)
|
||||
local revive_count = 0
|
||||
local heal_count = 0
|
||||
local range2 = range^2
|
||||
local surface = player.surface
|
||||
local center = player.position
|
||||
local area = {{x=center.x-range, y=center.y-range}, {x=center.x+range, y=center.y+range}}
|
||||
if config.allow_ghost_revive then
|
||||
local ghosts = surface.find_entities_filtered({area=area, type='entity-ghost', force=player.force})
|
||||
for _, ghost in pairs(ghosts) do
|
||||
if ghost.valid then
|
||||
local x = ghost.position.x-center.x
|
||||
local y = ghost.position.y-center.y
|
||||
if x^2+y^2 <= range2 then
|
||||
if config.allow_blueprint_repair or ghost.time_to_live ~= max_time_to_live then
|
||||
revive_count = revive_count+1
|
||||
if not config.disallow[ghost.ghost_name] then ghost.revive() end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if config.allow_heal_entities then
|
||||
local entities = surface.find_entities_filtered({area=area, force=player.force})
|
||||
for _, entity in pairs(entities) do
|
||||
if entity.valid then
|
||||
local x = entity.position.x-center.x
|
||||
local y = entity.position.y-center.y
|
||||
if entity.health and entity.get_health_ratio() ~= 1 and x^2+y^2 <= range2 then
|
||||
heal_count = heal_count+1
|
||||
entity.health = max_time_to_live
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return Commands.success{'expcom-repair.result', revive_count, heal_count}
|
||||
end)
|
||||
97
exp_legacy/module/modules/commands/reports.lua
Normal file
97
exp_legacy/module/modules/commands/reports.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
--[[-- Commands Module - Reports
|
||||
- Adds a commands that allow players to report other players
|
||||
@commands Reports
|
||||
]]
|
||||
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
|
||||
local format_chat_player_name = _C.format_chat_player_name--- @dep expcore.common
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Print a message to all players who match the value of admin
|
||||
local function print_to_players(admin, message)
|
||||
for _, player in ipairs(game.connected_players) do
|
||||
if player.admin == admin then
|
||||
player.print(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Reports a player and notifies moderators
|
||||
-- @command report
|
||||
-- @tparam LuaPlayer player the player to report, some players are immune
|
||||
-- @tparam string reason the reason the player is being reported
|
||||
Commands.new_command('report', {'expcom-report.description-report'}, 'Reports a player and notifies moderators')
|
||||
:add_param('player', false, function(input, player, reject)
|
||||
input = Commands.parse('player', input, player, reject)
|
||||
if not input then return end
|
||||
if Roles.player_has_flag(input, 'report-immune') then
|
||||
return reject{'expcom-report.player-immune'}
|
||||
elseif player == input then
|
||||
return reject{'expcom-report.self-report'}
|
||||
else
|
||||
return input
|
||||
end
|
||||
end)
|
||||
:add_param('reason', false)
|
||||
:add_alias('report-player')
|
||||
:enable_auto_concat()
|
||||
:register(function(player, action_player, reason)
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
local by_player_name_color = format_chat_player_name(player)
|
||||
if Reports.report_player(action_player, player.name, reason) then
|
||||
print_to_players(false, {'expcom-report.non-admin', action_player_name_color, reason})
|
||||
print_to_players(true, {'expcom-report.admin', action_player_name_color, by_player_name_color, reason})
|
||||
else
|
||||
return Commands.error{'expcom-report.already-reported'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Gets a list of all reports that a player has on them. If no player then lists all players and the number of reports on them.
|
||||
-- @command get-reports
|
||||
-- @tparam LuaPlayer player the player to get the report for
|
||||
Commands.new_command('get-reports', {'expcom-report.description-get-reports'}, 'Gets a list of all reports that a player has on them. If no player then lists all players and the number of reports on them.')
|
||||
:add_param('player', true, 'player')
|
||||
:add_alias('reports', 'list-reports')
|
||||
:register(function(_, player)
|
||||
if player then
|
||||
local reports = Reports.get_reports(player)
|
||||
local player_name_color = format_chat_player_name(player)
|
||||
Commands.print{'expcom-report.player-report-title', player_name_color}
|
||||
for player_name, reason in pairs(reports) do
|
||||
local by_player_name_color = format_chat_player_name(player_name)
|
||||
Commands.print{'expcom-report.list', by_player_name_color, reason}
|
||||
end
|
||||
else
|
||||
local user_reports = Reports.user_reports
|
||||
Commands.print{'expcom-report.player-count-title'}
|
||||
for player_name in pairs(user_reports) do
|
||||
local player_name_color = format_chat_player_name(player_name)
|
||||
local report_count = Reports.count_reports(player_name)
|
||||
Commands.print{'expcom-report.list', player_name_color, report_count}
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- Clears all reports from a player or just the report from one player.
|
||||
-- @command clear-reports
|
||||
-- @tparam LuaPlayer player the player to clear the report(s) from
|
||||
-- @tparam[opt=all] LuaPlayer from-player remove only the report made by this player
|
||||
Commands.new_command('clear-reports', {'expcom-report.description-clear-reports'}, 'Clears all reports from a player or just the report from one player.')
|
||||
:add_param('player', false, 'player')
|
||||
:add_param('from-player', true, 'player')
|
||||
:register(function(player, action_player, from_player)
|
||||
if from_player then
|
||||
if not Reports.remove_report(action_player, from_player.name, player.name) then
|
||||
return Commands.error{'expcom-report.not-reported'}
|
||||
end
|
||||
else
|
||||
if not Reports.remove_all(action_player, player.name) then
|
||||
return Commands.error{'expcom-report.not-reported'}
|
||||
end
|
||||
end
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
local by_player_name_color = format_chat_player_name(player)
|
||||
game.print{'expcom-report.removed', action_player_name_color, by_player_name_color}
|
||||
end)
|
||||
45
exp_legacy/module/modules/commands/research.lua
Normal file
45
exp_legacy/module/modules/commands/research.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local config = require 'config.research' --- @dep config.research
|
||||
|
||||
local research = {}
|
||||
Global.register(research, function(tbl)
|
||||
research = tbl
|
||||
end)
|
||||
|
||||
local function res_queue(force, by_script)
|
||||
local res_q = force.research_queue
|
||||
local res = force.technologies['mining-productivity-4']
|
||||
|
||||
if #res_q < config.queue_amount then
|
||||
for i=1, config.queue_amount - #res_q do
|
||||
force.add_research(res)
|
||||
|
||||
if not (by_script) then
|
||||
game.print{'expcom-res.inf-q', res.name, res.level + i}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Commands.new_command('auto-research', {'expcom-res.description-ares'}, 'Automatically queue up research')
|
||||
:add_alias('ares')
|
||||
:register(function(player)
|
||||
research.res_queue_enable = not research.res_queue_enable
|
||||
|
||||
if research.res_queue_enable then
|
||||
res_queue(player.force, true)
|
||||
end
|
||||
|
||||
game.print{'expcom-res.res', player.name, research.res_queue_enable}
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_research_finished, function(event)
|
||||
if research.res_queue_enable then
|
||||
if event.research.force.rockets_launched > 0 and event.research.force.technologies['mining-productivity-4'].level > 4 then
|
||||
res_queue(event.research.force, event.by_script)
|
||||
end
|
||||
end
|
||||
end)
|
||||
83
exp_legacy/module/modules/commands/roles.lua
Normal file
83
exp_legacy/module/modules/commands/roles.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
--[[-- Commands Module - Roles
|
||||
- Adds a commands that allow interaction with the role system
|
||||
@commands Roles
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Colours = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local format_chat_player_name, format_chat_colour_localized = _C.format_chat_player_name, _C.format_chat_colour_localized
|
||||
|
||||
--- Assigns a role to a player
|
||||
-- @command assign-role
|
||||
-- @tparam LuaPlayer player the player to assign the role to
|
||||
-- @tparam string role the name of the role to assign to the player, supports auto complete after enter
|
||||
Commands.new_command('assign-role', {'expcom-roles.description-assign-role'}, 'Assigns a role to a player')
|
||||
:add_param('player', false, 'player-role')
|
||||
:add_param('role', false, 'role')
|
||||
:set_flag('admin-only')
|
||||
:add_alias('rpromote', 'assign', 'role', 'add-role')
|
||||
:register(function(player, action_player, role)
|
||||
local player_highest = Roles.get_player_highest_role(player)
|
||||
|
||||
if player_highest.index < role.index then
|
||||
Roles.assign_player(action_player, role, player.name)
|
||||
|
||||
else
|
||||
return Commands.error{'expcom-roles.higher-role'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Unassigns a role from a player
|
||||
-- @command unassign-role
|
||||
-- @tparam LuaPlayer player the player to unassign the role from
|
||||
-- @tparam string role the name of the role to unassign from the player, supports auto complete after enter
|
||||
Commands.new_command('unassign-role', {'expcom-roles.description-unassign-role'}, 'Unassigns a role from a player')
|
||||
:add_param('player', false, 'player-role')
|
||||
:add_param('role', false, 'role')
|
||||
:set_flag('admin-only')
|
||||
:add_alias('rdemote', 'unassign', 'rerole', 'remove-role')
|
||||
:register(function(player, action_player, role)
|
||||
local player_highest = Roles.get_player_highest_role(player)
|
||||
|
||||
if player_highest.index < role.index then
|
||||
Roles.unassign_player(action_player, role, player.name)
|
||||
|
||||
else
|
||||
return Commands.error{'expcom-roles.higher-role'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Lists all roles in they correct order
|
||||
-- @command list-roles
|
||||
-- @tparam[opt=all] LuaPlayer player list only the roles which this player has
|
||||
Commands.new_command('list-roles', {'expcom-roles.description-list-roles'}, 'Lists all roles in they correct order')
|
||||
:add_param('player', true, 'player')
|
||||
:add_alias('lsroles', 'roles')
|
||||
:register(function(_, player)
|
||||
local roles = Roles.config.order
|
||||
local message = {'expcom-roles.list'}
|
||||
|
||||
if player then
|
||||
roles = Roles.get_player_roles(player)
|
||||
end
|
||||
|
||||
for index, role in pairs(roles) do
|
||||
role = Roles.get_role_from_any(role)
|
||||
local colour = role.custom_color or Colours.white
|
||||
local role_name = format_chat_colour_localized(role.name, colour)
|
||||
if index == 1 then
|
||||
message = {'expcom-roles.list', role_name}
|
||||
|
||||
if player then
|
||||
local player_name_colour = format_chat_player_name(player)
|
||||
message = {'expcom-roles.list-player', player_name_colour, role_name}
|
||||
end
|
||||
|
||||
else
|
||||
message = {'expcom-roles.list-element', message, role_name}
|
||||
end
|
||||
end
|
||||
|
||||
return Commands.success(message)
|
||||
end)
|
||||
167
exp_legacy/module/modules/commands/search.lua
Normal file
167
exp_legacy/module/modules/commands/search.lua
Normal file
@@ -0,0 +1,167 @@
|
||||
--[[-- Commands Module - Inventory Search
|
||||
- Adds commands that will search all players inventories for an item
|
||||
@commands InventorySearch
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
local format_time = _C.format_time
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Input parse for items by name
|
||||
local function item_parse(input, _, reject)
|
||||
if input == nil then return end
|
||||
local lower_input = input:lower():gsub(' ', '-')
|
||||
|
||||
-- Simple Case - internal name is given
|
||||
local item = game.item_prototypes[lower_input]
|
||||
if item then return item end
|
||||
|
||||
-- Second Case - rich text is given
|
||||
local item_name = input:match('%[item=([0-9a-z-]+)%]')
|
||||
item = game.item_prototypes[item_name]
|
||||
if item then return item end
|
||||
|
||||
-- No item found, we do not attempt to search all prototypes as this will be expensive
|
||||
return reject{'expcom-inv-search.reject-item', lower_input}
|
||||
end
|
||||
|
||||
--- Search all players for this item
|
||||
local function search_players(players, item)
|
||||
local head = 1
|
||||
local found = {}
|
||||
|
||||
-- Check the item count of all players
|
||||
for _, player in pairs(players) do
|
||||
local item_count = player.get_item_count(item.name)
|
||||
if item_count > 0 then
|
||||
-- Add the player to the array as they have the item
|
||||
found[head] = { player=player, count=item_count, online_time=player.online_time }
|
||||
head = head + 1
|
||||
end
|
||||
end
|
||||
|
||||
return found
|
||||
end
|
||||
|
||||
--- Custom sort function which only retains 5 greatest values
|
||||
local function sort_players(players, func)
|
||||
local sorted = {}
|
||||
local values = {}
|
||||
local threshold = nil
|
||||
|
||||
-- Loop over all provided players
|
||||
for index, player in ipairs(players) do
|
||||
local value = func(player)
|
||||
-- Check if the item will make the top 5 elements
|
||||
if index <= 5 or value > threshold then
|
||||
local inserted = false
|
||||
values[player] = value
|
||||
-- Find where in the top 5 to insert the element
|
||||
for next_index, next_player in ipairs(sorted) do
|
||||
if value > values[next_player] then
|
||||
table.insert(sorted, next_index, player)
|
||||
inserted = true
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Insert the element, this can only be called when index <= 5
|
||||
if not inserted then
|
||||
sorted[#sorted+1] = player
|
||||
end
|
||||
-- Update the threshold
|
||||
if sorted[6] then
|
||||
threshold = values[sorted[5]]
|
||||
values[sorted[6]] = nil
|
||||
sorted[6] = nil
|
||||
else
|
||||
threshold = values[sorted[#sorted]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return sorted
|
||||
end
|
||||
|
||||
--- Display to the player the top players which were found
|
||||
local function display_players(player, players, item)
|
||||
player.print{'expcom-inv-search.results-heading', item.name}
|
||||
for index, data in ipairs(players) do
|
||||
local player_name_color = format_chat_player_name(data.player)
|
||||
local amount = format_number(data.count)
|
||||
local time = format_time(data.online_time)
|
||||
player.print{'expcom-inv-search.results-item', index, player_name_color, amount, time}
|
||||
end
|
||||
end
|
||||
|
||||
--- Return the amount of an item a player has
|
||||
local function amount_sort(data)
|
||||
return data.count
|
||||
end
|
||||
|
||||
--- Get a list of players sorted by the quantity of an item in their inventory
|
||||
-- @command search-amount
|
||||
-- @tparam LuaItemPrototype item The item to search for in players inventories
|
||||
Commands.new_command('search-amount', {'expcom-inv-search.description-ia'}, 'Display players sorted by the quantity of an item held')
|
||||
:add_alias('ia')
|
||||
:add_param('item', false, item_parse)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, item)
|
||||
local players = search_players(game.players, item)
|
||||
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
|
||||
local top_players = sort_players(players, amount_sort)
|
||||
display_players(player, top_players, item)
|
||||
end)
|
||||
|
||||
--- Return the index of the player, higher means they joined more recently
|
||||
local function recent_sort(data)
|
||||
return data.player.index
|
||||
end
|
||||
|
||||
--- Get a list of players who have the given item, sorted by how recently they joined
|
||||
-- @command search-recent
|
||||
-- @tparam LuaItemPrototype item The item to search for in players inventories
|
||||
Commands.new_command('search-recent', {'expcom-inv-search.description-ir'}, 'Display players who hold an item sorted by join time')
|
||||
:add_alias('ir')
|
||||
:add_param('item', false, item_parse)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, item)
|
||||
local players = search_players(game.players, item)
|
||||
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
|
||||
local top_players = sort_players(players, recent_sort)
|
||||
display_players(player, top_players, item)
|
||||
end)
|
||||
|
||||
--- Return the the amount of an item a player has divided by their playtime
|
||||
local function combined_sort(data)
|
||||
return data.count/data.online_time
|
||||
end
|
||||
|
||||
--- Get a list of players sorted by quantity held and play time
|
||||
-- @command search
|
||||
-- @tparam LuaItemPrototype item The item to search for in players inventories
|
||||
Commands.new_command('search', {'expcom-inv-search.description-i'}, 'Display players sorted by the quantity of an item held and playtime')
|
||||
:add_alias('i')
|
||||
:add_param('item', false, item_parse)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, item)
|
||||
local players = search_players(game.players, item)
|
||||
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
|
||||
local top_players = sort_players(players, combined_sort)
|
||||
display_players(player, top_players, item)
|
||||
end)
|
||||
|
||||
--- Get a list of online players sorted by quantity held and play time
|
||||
-- @command search-online
|
||||
-- @tparam LuaItemPrototype item The item to search for in players inventories
|
||||
Commands.new_command('search-online', {'expcom-inv-search.description-io'}, 'Display online players sorted by the quantity of an item held and playtime')
|
||||
:add_alias('io')
|
||||
:add_param('item', false, item_parse)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, item)
|
||||
local players = search_players(game.connected_players, item)
|
||||
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
|
||||
local top_players = sort_players(players, combined_sort)
|
||||
display_players(player, top_players, item)
|
||||
end)
|
||||
69
exp_legacy/module/modules/commands/spawn.lua
Normal file
69
exp_legacy/module/modules/commands/spawn.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
--[[-- Commands Module - Spawn
|
||||
- Adds a command that allows players to teleport to their spawn point
|
||||
@commands Spawn
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
|
||||
local function teleport(player)
|
||||
local surface = player.surface
|
||||
local spawn = player.force.get_spawn_position(surface)
|
||||
local position = surface.find_non_colliding_position('character', spawn, 32, 1)
|
||||
-- return false if no new position
|
||||
if not position then
|
||||
return false
|
||||
end
|
||||
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
|
||||
player.teleport(position, surface)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Teleport the player
|
||||
player.teleport(position, surface)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Teleport to spawn
|
||||
-- @command go-to-spawn
|
||||
-- @tparam[opt=self] LuaPlayer player the player to teleport to their spawn point
|
||||
Commands.new_command('go-to-spawn', {'expcom-spawn.description'}, 'Teleport to spawn')
|
||||
:add_param('player', true, 'player-role-alive')
|
||||
:set_defaults{
|
||||
player=function(player)
|
||||
if player.connected and player.character and player.character.health > 0 then
|
||||
return player
|
||||
end
|
||||
end
|
||||
}
|
||||
:add_alias('spawn', 'tp-spawn')
|
||||
:register(function(player, action_player)
|
||||
if not action_player then
|
||||
return Commands.error{'expcom-spawn.unavailable'}
|
||||
|
||||
elseif action_player == player then
|
||||
if not teleport(player) then
|
||||
return Commands.error{'expcom-spawn.unavailable'}
|
||||
end
|
||||
|
||||
elseif Roles.player_allowed(player, 'command/go-to-spawn/always') then
|
||||
if not teleport(action_player) then
|
||||
return Commands.error{'expcom-spawn.unavailable'}
|
||||
end
|
||||
|
||||
else
|
||||
return Commands.error{'expcore-commands.unauthorized'}
|
||||
end
|
||||
end)
|
||||
35
exp_legacy/module/modules/commands/spectate.lua
Normal file
35
exp_legacy/module/modules/commands/spectate.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
--[[-- Commands Module - Spectate
|
||||
- Adds commands relating to spectate and follow
|
||||
@commands Spectate
|
||||
]]
|
||||
|
||||
local Spectate = require 'modules.control.spectate' --- @dep modules.control.spectate
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Toggles spectator mode for the caller
|
||||
-- @command spectate
|
||||
Commands.new_command('spectate', {'expcom-spectate.description-spectate'}, 'Toggles spectator mode')
|
||||
:register(function(player)
|
||||
if Spectate.is_spectating(player) then
|
||||
Spectate.stop_spectate(player)
|
||||
|
||||
else
|
||||
Spectate.start_spectate(player)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Enters follow mode for the caller, following the given player.
|
||||
-- @command follow
|
||||
-- @tparam LuaPlayer player The player that will be followed
|
||||
Commands.new_command('follow', {'expcom-spectate.description-follow'}, 'Start following a player in spectator')
|
||||
:add_alias('f')
|
||||
:add_param('player', false, 'player-online')
|
||||
:register(function(player, action_player)
|
||||
if player == action_player then
|
||||
return Commands.error{'expcom-spectate.follow-self'}
|
||||
|
||||
else
|
||||
Spectate.start_follow(player, action_player)
|
||||
end
|
||||
end)
|
||||
16
exp_legacy/module/modules/commands/speed.lua
Normal file
16
exp_legacy/module/modules/commands/speed.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
--[[-- Commands Module - Set game speed
|
||||
- Adds a command that allows changing game speed
|
||||
@commands Set game speed
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
Commands.new_command('game-speed', {'expcom-speed.description'}, 'Set game speed')
|
||||
:add_param('amount', 'number-range', 0.2, 8)
|
||||
:set_flag('admin_only')
|
||||
:register(function(player, amount)
|
||||
game.speed = math.round(amount, 3)
|
||||
game.print{'expcom-speed.result', player.name, string.format('%.3f', amount)}
|
||||
return Commands.success
|
||||
end)
|
||||
33
exp_legacy/module/modules/commands/surface-clearing.lua
Normal file
33
exp_legacy/module/modules/commands/surface-clearing.lua
Normal file
@@ -0,0 +1,33 @@
|
||||
--[[-- Commands Module - Clear Item On Ground
|
||||
- Adds a command that clear item on ground so blueprint can deploy safely
|
||||
@commands Clear Item On Ground
|
||||
]]
|
||||
|
||||
local copy_items_stack = _C.copy_items_stack --- @dep expcore.common
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
Commands.new_command('clear-item-on-ground', {'expcom-surface-clearing.description-ci'}, 'Clear Item On Ground')
|
||||
:add_param('range', false, 'integer-range', 1, 1000)
|
||||
:register(function(player, range)
|
||||
for _, e in pairs(player.surface.find_entities_filtered{position=player.position, radius=range, name='item-on-ground'}) do
|
||||
if e.stack then
|
||||
-- calling move_items_stack(e.stack) will crash to desktop
|
||||
-- https://forums.factorio.com/viewtopic.php?f=7&t=110322
|
||||
copy_items_stack{e.stack}
|
||||
e.stack.clear()
|
||||
end
|
||||
end
|
||||
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
Commands.new_command('clear-blueprint', {'expcom-surface-clearing.description-cb'}, 'Clear Blueprint')
|
||||
:add_param('range', false, 'integer-range', 1, 1000)
|
||||
:register(function(player, range)
|
||||
for _, e in pairs(player.surface.find_entities_filtered{position=player.position, radius=range, type='entity-ghost'}) do
|
||||
e.destroy()
|
||||
end
|
||||
|
||||
return Commands.success
|
||||
end)
|
||||
93
exp_legacy/module/modules/commands/teleport.lua
Normal file
93
exp_legacy/module/modules/commands/teleport.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
--[[-- Commands Module - Teleport
|
||||
- Adds a command that allows players to teleport to other players
|
||||
@commands Teleport
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
local function teleport(from_player, to_player)
|
||||
local surface = to_player.surface
|
||||
local position = surface.find_non_colliding_position('character', to_player.position, 32, 1)
|
||||
|
||||
-- return false if no new position
|
||||
if not position then
|
||||
return false
|
||||
end
|
||||
|
||||
if from_player.vehicle then
|
||||
-- Teleport the entity
|
||||
local entity = from_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
|
||||
from_player.driving = false
|
||||
from_player.teleport(position, surface)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Teleport the player
|
||||
from_player.teleport(position, surface)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Teleports a player to another player.
|
||||
-- @command teleport
|
||||
-- @tparam LuaPlayer from_player the player that will be teleported, must be alive
|
||||
-- @tparam LuaPlayer to_player the player to teleport to, must be online (if dead goes to where they died)
|
||||
Commands.new_command('teleport', {'expcom-tp.description-tp'}, 'Teleports a player to another player.')
|
||||
:add_param('from_player', false, 'player-alive')
|
||||
:add_param('to_player', false, 'player-online')
|
||||
:add_alias('tp')
|
||||
:set_flag('admin_only')
|
||||
:register(function(_, from_player, to_player)
|
||||
if from_player.index == to_player.index then
|
||||
-- return if attempting to teleport to self
|
||||
return Commands.error{'expcom-tp.to-self'}
|
||||
end
|
||||
if not teleport(from_player, to_player) then
|
||||
-- return if the teleport failed
|
||||
return Commands.error{'expcom-tp.no-position-found'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Teleports a player to you.
|
||||
-- @command bring
|
||||
-- @tparam LuaPlayer player the player that will be teleported, must be alive
|
||||
Commands.new_command('bring', {'expcom-tp.description-bring'}, 'Teleports a player to you.')
|
||||
:add_param('player', false, 'player-alive')
|
||||
:set_flag('admin_only')
|
||||
:register(function(player, from_player)
|
||||
if from_player.index == player.index then
|
||||
-- return if attempting to teleport to self
|
||||
return Commands.error{'expcom-tp.to-self'}
|
||||
end
|
||||
if not teleport(from_player, player) then
|
||||
-- return if the teleport failed
|
||||
return Commands.error{'expcom-tp.no-position-found'}
|
||||
end
|
||||
from_player.print('Come here my friend')
|
||||
end)
|
||||
|
||||
--- Teleports you to a player.
|
||||
-- @command goto
|
||||
-- @tparam LuaPlayer player the player to teleport to, must be online (if dead goes to where they died)
|
||||
Commands.new_command('goto', {'expcom-tp.description-goto'}, 'Teleports you to a player.')
|
||||
:add_param('player', false, 'player-online')
|
||||
:add_alias('tp-me', 'tpme')
|
||||
:register(function(player, to_player)
|
||||
if to_player.index == player.index then
|
||||
-- return if attempting to teleport to self
|
||||
return Commands.error{'expcom-tp.to-self'}
|
||||
end
|
||||
if not teleport(player, to_player) then
|
||||
-- return if the teleport failed
|
||||
return Commands.error{'expcom-tp.no-position-found'}
|
||||
end
|
||||
end)
|
||||
23
exp_legacy/module/modules/commands/train.lua
Normal file
23
exp_legacy/module/modules/commands/train.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
--[[-- Commands Module - Set Automatic Train
|
||||
- Adds a command that set all train back to automatic
|
||||
@commands Set Automatic Train
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
local format_number = require('util').format_number
|
||||
|
||||
Commands.new_command('set-trains-to-automatic', {'expcom-train.description'}, 'Set All Trains to Automatic')
|
||||
:register(function(player)
|
||||
local count = 0
|
||||
|
||||
for _, v in pairs(player.force.get_trains()) do
|
||||
if v.manual_mode then
|
||||
count = count + 1
|
||||
v.manual_mode = false
|
||||
end
|
||||
end
|
||||
|
||||
game.print{'expcom-train.manual-result', player.name, format_number(count)}
|
||||
return Commands.success
|
||||
end)
|
||||
15
exp_legacy/module/modules/commands/vlayer.lua
Normal file
15
exp_legacy/module/modules/commands/vlayer.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
--- Adds a virtual layer to store power to save space.
|
||||
-- @commands Vlayer
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
local vlayer = require 'modules.control.vlayer'
|
||||
|
||||
Commands.new_command('vlayer-info', {'vlayer.description-vi'}, 'Vlayer Info')
|
||||
:register(function(_)
|
||||
local c = vlayer.get_circuits()
|
||||
|
||||
for k, v in pairs(c) do
|
||||
Commands.print(v .. ' : ' .. k)
|
||||
end
|
||||
end)
|
||||
74
exp_legacy/module/modules/commands/warnings.lua
Normal file
74
exp_legacy/module/modules/commands/warnings.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
--[[-- Commands Module - Warnings
|
||||
- Adds a commands that allow admins to warn other players
|
||||
@commands Warnings
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Warnings = require 'modules.control.warnings' --- @dep modules.control.warnings
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
local config = require 'config.warnings' --- @dep config.warnings
|
||||
require 'config.expcore.command_role_parse'
|
||||
|
||||
--- Gives a warning to a player; may lead to automatic script action.
|
||||
-- @command give-warning
|
||||
-- @tparam LuaPlayer player the player the will recive a warning
|
||||
-- @tparam string reason the reason the player is being given a warning
|
||||
Commands.new_command('give-warning', {'expcom-warnings.description-give'}, 'Gives a warning to a player; may lead to automatic script action.')
|
||||
:add_param('player', false, 'player-role')
|
||||
:add_param('reason', false)
|
||||
:add_alias('warn')
|
||||
:enable_auto_concat()
|
||||
:register(function(player, action_player, reason)
|
||||
Warnings.add_warning(action_player, player.name, reason)
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
local by_player_name_color = format_chat_player_name(player)
|
||||
game.print{'expcom-warnings.received', action_player_name_color, by_player_name_color, reason}
|
||||
end)
|
||||
|
||||
--- Gets the number of warnings a player has. If no player then lists all players and the number of warnings they have.
|
||||
-- @command get-warnings
|
||||
-- @tparam[opt=list] LuaPlayer player the player to get the warning for, if nil all players are listed
|
||||
Commands.new_command('get-warnings', {'expcom-warnings.description-get'}, 'Gets the number of warnings a player has. If no player then lists all players and the number of warnings they have.')
|
||||
:add_param('player', true, 'player')
|
||||
:add_alias('warnings', 'list-warnings')
|
||||
:register(function(_, player)
|
||||
if player then
|
||||
local warnings = Warnings.get_warnings(player)
|
||||
local script_warnings = Warnings.get_script_warnings(player)
|
||||
local player_name_color = format_chat_player_name(player)
|
||||
Commands.print{'expcom-warnings.player', player_name_color, #warnings, #script_warnings, config.temp_warning_limit}
|
||||
for _, warning in ipairs(warnings) do
|
||||
Commands.print{'expcom-warnings.player-detail', format_chat_player_name(warning.by_player_name), warning.reason}
|
||||
end
|
||||
else
|
||||
local rtn = {}
|
||||
local user_script_warnings = Warnings.user_script_warnings
|
||||
for player_name, warnings in pairs(Warnings.user_warnings:get_all()) do
|
||||
rtn[player_name] = {#warnings, 0}
|
||||
end
|
||||
for player_name, warnings in pairs(user_script_warnings) do
|
||||
if not rtn[player_name] then
|
||||
rtn[player_name] = {0, 0}
|
||||
end
|
||||
rtn[player_name][2] = #warnings
|
||||
end
|
||||
Commands.print{'expcom-warnings.list-title'}
|
||||
for player_name, warnings in pairs(rtn) do
|
||||
local player_name_color = format_chat_player_name(player_name)
|
||||
Commands.print{'expcom-warnings.list', player_name_color, warnings[1], warnings[2], config.temp_warning_limit}
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- Clears all warnings (and script warnings) from a player
|
||||
-- @command clear-warnings
|
||||
-- @tparam LuaPlayer player the player to clear the warnings from
|
||||
Commands.new_command('clear-warnings', {'expcom-warnings.description-clear'}, 'Clears all warnings (and script warnings) from a player')
|
||||
:add_param('player', false, 'player')
|
||||
:register(function(player, action_player)
|
||||
Warnings.clear_warnings(action_player, player.name)
|
||||
Warnings.clear_script_warnings(action_player)
|
||||
local action_player_name_color = format_chat_player_name(action_player)
|
||||
local by_player_name_color = format_chat_player_name(player)
|
||||
game.print{'expcom-warnings.cleared', action_player_name_color, by_player_name_color}
|
||||
end)
|
||||
79
exp_legacy/module/modules/commands/waterfill.lua
Normal file
79
exp_legacy/module/modules/commands/waterfill.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
--- Adds a waterfill
|
||||
-- @commands Waterfill
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
local Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
local SelectionConvertArea = 'WaterfillConvertArea'
|
||||
|
||||
--- Align an aabb to the grid by expanding it
|
||||
local function aabb_align_expand(aabb)
|
||||
return {
|
||||
left_top = {x = math.floor(aabb.left_top.x), y = math.floor(aabb.left_top.y)},
|
||||
right_bottom = {x = math.ceil(aabb.right_bottom.x), y = math.ceil(aabb.right_bottom.y)}
|
||||
}
|
||||
end
|
||||
|
||||
Commands.new_command('waterfill', {'expcom-waterfill.description'}, 'Change tile to water')
|
||||
:register(function(player)
|
||||
local inv = player.get_main_inventory()
|
||||
|
||||
if (inv.get_item_count('cliff-explosives')) == 0 then
|
||||
return player.print{'expcom-waterfill.waterfill-cliff'}
|
||||
end
|
||||
|
||||
if Selection.is_selecting(player, SelectionConvertArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionConvertArea)
|
||||
return Commands.success{'expcom-waterfill.entered-area-selection'}
|
||||
end
|
||||
|
||||
return Commands.success
|
||||
end)
|
||||
|
||||
--- When an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionConvertArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
local entities = player.surface.find_entities_filtered{area=area, name='steel-chest'}
|
||||
|
||||
if #entities == 0 then
|
||||
player.print('No steel chest found')
|
||||
return
|
||||
end
|
||||
|
||||
local tiles_to_make = {}
|
||||
local inv = player.get_main_inventory()
|
||||
|
||||
if not inv then
|
||||
return
|
||||
end
|
||||
|
||||
local clf_exp = inv.get_item_count('cliff-explosives')
|
||||
|
||||
for _, entity in pairs(entities) do
|
||||
if clf_exp >= 1 then
|
||||
if entity.get_inventory(defines.inventory.chest).is_empty() then
|
||||
if (math.floor(player.position.x) ~= math.floor(entity.position.x)) or (math.floor(player.position.y) ~= math.floor(entity.position.y)) then
|
||||
table.insert(tiles_to_make, {name='water-mud', position=entity.position})
|
||||
entity.destroy()
|
||||
else
|
||||
player.print{'expcom-waterfill.waterfill-distance'}
|
||||
end
|
||||
end
|
||||
|
||||
clf_exp = clf_exp - 1
|
||||
inv.remove({name='cliff-explosives', count=1})
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
event.surface.set_tiles(tiles_to_make)
|
||||
end)
|
||||
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
|
||||
24
exp_legacy/module/modules/data/alt-view.lua
Normal file
24
exp_legacy/module/modules/data/alt-view.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
--- Stores if you use alt mode or not and auto applies it
|
||||
-- @data Alt-View
|
||||
|
||||
local Event = require 'utils.event' ---@dep utils.event
|
||||
|
||||
--- Stores the visible state of alt mode
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local UsesAlt = PlayerData.Settings:combine('UsesAlt')
|
||||
UsesAlt:set_default(false)
|
||||
UsesAlt:set_metadata{
|
||||
stringify = function(value) return value and 'Visible' or 'Hidden' end
|
||||
}
|
||||
|
||||
--- When your data loads apply alt view if you have it enabled
|
||||
UsesAlt:on_load(function(player_name, uses_alt)
|
||||
local player = game.players[player_name]
|
||||
player.game_view_settings.show_entity_info = uses_alt or false
|
||||
end)
|
||||
|
||||
--- When alt view is toggled update this
|
||||
Event.add(defines.events.on_player_toggled_alt_mode, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
UsesAlt:set(player, player.game_view_settings.show_entity_info)
|
||||
end)
|
||||
103
exp_legacy/module/modules/data/bonus.lua
Normal file
103
exp_legacy/module/modules/data/bonus.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
--[[-- Commands Module - Bonus
|
||||
- Adds a command that allows players to have increased stats
|
||||
@data Bonus
|
||||
]]
|
||||
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.bonus' --- @dep config.bonuses
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
-- Stores the bonus for the player
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local PlayerBonus = PlayerData.Settings:combine('Bonus')
|
||||
PlayerBonus:set_default(0)
|
||||
PlayerBonus:set_metadata{
|
||||
permission = 'command/bonus',
|
||||
stringify = function(value)
|
||||
if not value or value == 0 then
|
||||
return 'None set'
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
}
|
||||
|
||||
--- Apply a bonus to a player
|
||||
local function apply_bonus(player, stage)
|
||||
if not player.character then
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(config.player_bonus) do
|
||||
player[k] = v.value * stage / 10
|
||||
|
||||
if v.combined_bonus then
|
||||
for i=1, #v.combined_bonus, 1 do
|
||||
player[v.combined_bonus[i]] = v.value * stage / 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- When store is updated apply new bonus to the player
|
||||
PlayerBonus:on_update(function(player_name, player_bonus)
|
||||
apply_bonus(game.players[player_name], player_bonus or 0)
|
||||
end)
|
||||
|
||||
--- Changes the amount of bonus you receive
|
||||
-- @command bonus
|
||||
-- @tparam number amount range 0-10 the increase for your bonus
|
||||
Commands.new_command('bonus', {'expcom-bonus.description'}, 'Changes the amount of bonus you receive')
|
||||
:add_param('amount', 'integer-range', 0, 10)
|
||||
:register(function(player, amount)
|
||||
if not Roles.player_allowed(player, 'command/bonus') then
|
||||
Commands.print{'expcom-bonus.perm', 1}
|
||||
return
|
||||
end
|
||||
|
||||
PlayerBonus:set(player, amount)
|
||||
|
||||
Commands.print{'expcom-bonus.set', amount}
|
||||
end)
|
||||
|
||||
--- When a player respawns re-apply bonus
|
||||
Event.add(defines.events.on_player_respawned, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
apply_bonus(player, PlayerBonus:get(player))
|
||||
end)
|
||||
|
||||
--- Remove bonus if a player no longer has access to the command
|
||||
local function role_update(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not Roles.player_allowed(player, 'command/bonus') then
|
||||
apply_bonus(player, 0)
|
||||
end
|
||||
end
|
||||
|
||||
--- When a player dies allow them to have instant respawn
|
||||
Event.add(defines.events.on_player_died, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
if Roles.player_has_flag(player, 'instant-respawn') then
|
||||
player.ticks_to_respawn = 120
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
if event.player_index ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(config.force_bonus) do
|
||||
game.players[event.player_index].force[k] = v.value
|
||||
end
|
||||
|
||||
for k, v in pairs(config.surface_bonus) do
|
||||
game.players[event.player_index].surface[k] = v.value
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(Roles.events.on_role_assigned, role_update)
|
||||
Event.add(Roles.events.on_role_unassigned, role_update)
|
||||
43
exp_legacy/module/modules/data/greetings.lua
Normal file
43
exp_legacy/module/modules/data/greetings.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
--- Greets players on join
|
||||
-- @data Greetings
|
||||
|
||||
local config = require 'config.join_messages' --- @dep config.join_messages
|
||||
local Commands = require 'expcore.commands' ---@dep expcore.commands
|
||||
require 'config.expcore.command_general_parse'
|
||||
|
||||
--- Stores the join message that the player have
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local CustomMessages = PlayerData.Settings:combine('JoinMessage')
|
||||
CustomMessages:set_metadata{
|
||||
permission = 'command/join-message'
|
||||
}
|
||||
|
||||
--- When a players data loads show their message
|
||||
CustomMessages:on_load(function(player_name, player_message)
|
||||
local player = game.players[player_name]
|
||||
local custom_message = player_message or config[player_name]
|
||||
if custom_message then
|
||||
game.print(custom_message, player.color)
|
||||
else
|
||||
player.print{'join-message.greet', {'links.discord'}}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Set your custom join message
|
||||
-- @command join-message
|
||||
-- @tparam string message The custom join message that will be used
|
||||
Commands.new_command('join-message', {'expcom-join-message.description-msg'}, 'Sets your custom join message')
|
||||
:add_param('message', false, 'string-max-length', 255)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, message)
|
||||
if not player then return end
|
||||
CustomMessages:set(player, message)
|
||||
return {'join-message.message-set'}
|
||||
end)
|
||||
|
||||
Commands.new_command('join-message-clear', {'expcom-join-message.description-clr'}, 'Clear your join message')
|
||||
:register(function(player)
|
||||
if not player then return end
|
||||
CustomMessages:remove(player)
|
||||
return {'join-message.message-cleared'}
|
||||
end)
|
||||
34
exp_legacy/module/modules/data/language.lua
Normal file
34
exp_legacy/module/modules/data/language.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
--- Stores the language used to join the server
|
||||
-- @data Language
|
||||
|
||||
local Event = require 'utils.event' ---@dep utils.event
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local LocalLanguage = PlayerData.Statistics:combine('LocalLanguage')
|
||||
LocalLanguage:set_default("Unknown")
|
||||
|
||||
--- Creates translation request on_load of a player
|
||||
LocalLanguage:on_load(function(player_name, _)
|
||||
local player = game.players[player_name]
|
||||
player.request_translation({"language.local-language"})
|
||||
end)
|
||||
|
||||
--- Resolves translation request for language setting
|
||||
Event.add(defines.events.on_string_translated, function(event)
|
||||
-- Check if event.localised_string is a table
|
||||
-- Check if the translation request was for language setting
|
||||
if type(event.localised_string) ~= "table" or event.localised_string[1] ~= "language.local-language" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if the translation request was succesful
|
||||
local player = game.players[event.player_index]
|
||||
if not event.translated then
|
||||
player.print("Could not detect your language settings")
|
||||
-- Raise error
|
||||
return
|
||||
end
|
||||
|
||||
-- Change LocalLanguage value for the player to the recognized one
|
||||
local language = event.result
|
||||
LocalLanguage:set(player, language)
|
||||
end)
|
||||
97
exp_legacy/module/modules/data/personal-logistic.lua
Normal file
97
exp_legacy/module/modules/data/personal-logistic.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local config = require 'config.personal_logistic' --- @dep config.personal-logistic
|
||||
|
||||
local pl = {}
|
||||
|
||||
function pl.pl(type, target, amount)
|
||||
local c
|
||||
local s
|
||||
|
||||
if type == 'p' then
|
||||
c = target.clear_personal_logistic_slot
|
||||
s = target.set_personal_logistic_slot
|
||||
|
||||
elseif type == 's' then
|
||||
c = target.clear_vehicle_logistic_slot
|
||||
s = target.set_vehicle_logistic_slot
|
||||
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
for _, v in pairs(config.request) do
|
||||
c(v.key)
|
||||
end
|
||||
|
||||
if (amount < 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local stats = target.force.item_production_statistics
|
||||
|
||||
for k, v in pairs(config.request) do
|
||||
local v_min = math.ceil(v.min * amount)
|
||||
local v_max = math.ceil(v.max * amount)
|
||||
|
||||
if v.stack and v.stack ~= 1 and v.type ~= 'weapon' then
|
||||
v_min = math.floor(v_min / v.stack) * v.stack
|
||||
v_max = math.ceil(v_max / v.stack) * v.stack
|
||||
end
|
||||
|
||||
if v.upgrade_of == nil then
|
||||
if v.type then
|
||||
if stats.get_input_count(k) < config.production_required[v.type] then
|
||||
if v_min > 0 then
|
||||
if v_min == v_max then
|
||||
v_min = math.floor((v_max * 0.5) / v.stack) * v.stack
|
||||
end
|
||||
|
||||
else
|
||||
v_min = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
s(v.key, {name=k, min=v_min, max=v_max})
|
||||
|
||||
else
|
||||
if v.type then
|
||||
if stats.get_input_count(k) >= config.production_required[v.type] then
|
||||
s(v.key, {name=k, min=v_min, max=v_max})
|
||||
local vuo = v.upgrade_of
|
||||
|
||||
while vuo do
|
||||
s(config.request[vuo].key, {name=vuo, min=0, max=0})
|
||||
vuo = config.request[vuo].upgrade_of
|
||||
end
|
||||
|
||||
else
|
||||
s(v.key, {name=k, min=0, max=v_max})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Commands.new_command('personal-logistic', {'expcom-personal-logistics'}, 'Set Personal Logistic (-1 to cancel all) (Select spidertron to edit spidertron)')
|
||||
:add_param('amount', 'integer-range', -1, 10)
|
||||
:add_alias('pl')
|
||||
:register(function(player, amount)
|
||||
if player.force.technologies['logistic-robotics'].researched then
|
||||
if player.selected then
|
||||
if player.selected.name == 'spidertron' then
|
||||
pl.pl('s', player.selected, amount / 10)
|
||||
return Commands.success
|
||||
end
|
||||
|
||||
else
|
||||
pl.pl('p', player, amount / 10)
|
||||
return Commands.success
|
||||
end
|
||||
|
||||
else
|
||||
player.print('Personal Logistic not researched')
|
||||
end
|
||||
end)
|
||||
|
||||
return pl
|
||||
62
exp_legacy/module/modules/data/player-colours.lua
Normal file
62
exp_legacy/module/modules/data/player-colours.lua
Normal file
@@ -0,0 +1,62 @@
|
||||
--- Gives players random colours when they join, also applies preset colours to those who have them
|
||||
-- @data Player-Colours
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Colours = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local config = require 'config.preset_player_colours' --- @dep config.preset_player_colours
|
||||
|
||||
--- Stores the colour that the player wants
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local PlayerColours = PlayerData.Settings:combine('Colour')
|
||||
PlayerColours:set_metadata{
|
||||
stringify = function(value)
|
||||
if not value then return 'None set' end
|
||||
local c = value[1]
|
||||
return string.format('Red: %d Green: %d Blue: %d', c[1], c[2], c[3])
|
||||
end
|
||||
}
|
||||
|
||||
--- Used to compact player colours to take less space
|
||||
local floor = math.floor
|
||||
local function compact(colour)
|
||||
return {
|
||||
floor(colour.r * 255),
|
||||
floor(colour.g * 255),
|
||||
floor(colour.b * 255)
|
||||
}
|
||||
end
|
||||
|
||||
--- Returns a colour that is a bit lighter than the one given
|
||||
local function lighten(c)
|
||||
return {r = 255 - (255 - c.r) * 0.5, g = 255 - (255 - c.g) * 0.5, b = 255 - (255 - c.b) * 0.5, a = 255}
|
||||
end
|
||||
|
||||
--- When your data loads apply the players colour, or a random on if none is saved
|
||||
PlayerColours:on_load(function(player_name, player_colour)
|
||||
if not player_colour then
|
||||
local preset = config.players[player_name]
|
||||
if preset then
|
||||
player_colour = {preset, lighten(preset)}
|
||||
else
|
||||
local colour_name = 'white'
|
||||
while config.disallow[colour_name] do
|
||||
colour_name = table.get_random_dictionary_entry(Colours, true)
|
||||
end
|
||||
player_colour = {Colours[colour_name], lighten(Colours[colour_name])}
|
||||
end
|
||||
end
|
||||
|
||||
local player = game.players[player_name]
|
||||
player.color = player_colour[1]
|
||||
player.chat_color = player_colour[2]
|
||||
end)
|
||||
|
||||
--- Save the players color when they use the color command
|
||||
Event.add(defines.events.on_console_command, function(event)
|
||||
if event.command ~= 'color' then return end
|
||||
if event.parameters == '' then return end
|
||||
if not event.player_index then return end
|
||||
local player = game.players[event.player_index]
|
||||
if not player or not player.valid then return end
|
||||
PlayerColours:set(player, {compact(player.color), compact(player.chat_color)})
|
||||
end)
|
||||
67
exp_legacy/module/modules/data/quickbar.lua
Normal file
67
exp_legacy/module/modules/data/quickbar.lua
Normal file
@@ -0,0 +1,67 @@
|
||||
--[[-- Commands Module - Quickbar
|
||||
- Adds a command that allows players to load Quickbar presets
|
||||
@data Quickbar
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local config = require 'config.preset_player_quickbar' --- @dep config.preset_player_quickbar
|
||||
|
||||
--- Stores the quickbar filters for a player
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local PlayerFilters = PlayerData.Settings:combine('QuickbarFilters')
|
||||
PlayerFilters:set_metadata{
|
||||
permission = 'command/save-quickbar',
|
||||
stringify = function(value)
|
||||
if not value then return 'No filters set' end
|
||||
local count = 0
|
||||
for _ in pairs(value) do count = count + 1 end
|
||||
return count..' filters set'
|
||||
end
|
||||
}
|
||||
|
||||
--- Loads your quickbar preset
|
||||
PlayerFilters:on_load(function(player_name, filters)
|
||||
if not filters then filters = config[player_name] end
|
||||
if not filters then return end
|
||||
local player = game.players[player_name]
|
||||
for i, item_name in pairs(filters) do
|
||||
if item_name ~= nil and item_name ~= '' then
|
||||
player.set_quick_bar_slot(i, item_name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local ignoredItems = {
|
||||
["blueprint"] = true,
|
||||
["blueprint-book"] = true,
|
||||
["deconstruction-planner"] = true,
|
||||
["spidertron-remote"] = true,
|
||||
["upgrade-planner"] = true
|
||||
}
|
||||
|
||||
--- Saves your quickbar preset to the script-output folder
|
||||
-- @command save-quickbar
|
||||
Commands.new_command('save-quickbar', {'expcom-quickbar.description'}, 'Saves your quickbar preset items to file')
|
||||
:add_alias('save-toolbar')
|
||||
:register(function(player)
|
||||
local filters = {}
|
||||
|
||||
for i = 1, 100 do
|
||||
local slot = player.get_quick_bar_slot(i)
|
||||
-- Need to filter out blueprint and blueprint books because the slot is a LuaItemPrototype and does not contain a way to export blueprint data
|
||||
if slot ~= nil then
|
||||
local ignored = ignoredItems[slot.name]
|
||||
if ignored ~= true then
|
||||
filters[i] = slot.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if next(filters) then
|
||||
PlayerFilters:set(player, filters)
|
||||
else
|
||||
PlayerFilters:remove(player)
|
||||
end
|
||||
|
||||
return {'quickbar.saved'}
|
||||
end)
|
||||
189
exp_legacy/module/modules/data/statistics.lua
Normal file
189
exp_legacy/module/modules/data/statistics.lua
Normal file
@@ -0,0 +1,189 @@
|
||||
|
||||
local Event = require 'utils.event' ---@dep utils.event
|
||||
local Global = require 'utils.global' ---@dep utils.global
|
||||
local config = require 'config.statistics' ---@dep config.statistics
|
||||
local format_time = _C.format_time
|
||||
local floor = math.floor
|
||||
local afk_required = 5*3600 -- 5 minutes
|
||||
|
||||
--- Stores players who have been created, required to avoid loss of data
|
||||
local new_players = {}
|
||||
Global.register(new_players, function(tbl)
|
||||
new_players = tbl
|
||||
end)
|
||||
|
||||
--- Stores the statistics on a player
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local AllPlayerData = PlayerData.All
|
||||
local Statistics = PlayerData.Statistics
|
||||
Statistics:set_metadata{
|
||||
display_order = config.display_order
|
||||
}
|
||||
|
||||
--- Update your statistics with any which happened before the data was valid
|
||||
Statistics:on_load(function(player_name, player_statistics)
|
||||
local existing_data = AllPlayerData:get(player_name)
|
||||
if existing_data and existing_data.valid then return end
|
||||
local counters = config.counters
|
||||
|
||||
-- Merge all data from before you data loaded
|
||||
for key, value in pairs(Statistics:get(player_name, {})) do
|
||||
if config[key] or counters[key] then
|
||||
if not player_statistics[key] then
|
||||
player_statistics[key] = value
|
||||
else
|
||||
player_statistics[key] = player_statistics[key] + value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Increment your maps played if this is your first time on this map
|
||||
if new_players[player_name] then
|
||||
new_players[player_name] = nil
|
||||
local ctn = player_statistics.MapsPlayed
|
||||
player_statistics.MapsPlayed = ctn and ctn + 1 or 1
|
||||
end
|
||||
|
||||
return player_statistics
|
||||
end)
|
||||
|
||||
--- Used to format time in minute format
|
||||
local function format_minutes(value)
|
||||
return format_time(value*3600, {
|
||||
long = true,
|
||||
hours = true,
|
||||
minutes = true
|
||||
})
|
||||
end
|
||||
|
||||
--- Used to format time into a clock
|
||||
local function format_clock(value)
|
||||
return format_time(value*3600, {
|
||||
hours=true,
|
||||
minutes=true,
|
||||
seconds=false,
|
||||
time=false,
|
||||
string=true
|
||||
})
|
||||
end
|
||||
|
||||
--- Add MapsPlayed if it is enabled
|
||||
if config.MapsPlayed then
|
||||
Statistics:combine('MapsPlayed')
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
new_players[player.name] = true
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add Playtime and AfkTime if it is enabled
|
||||
if config.Playtime or config.AfkTime then
|
||||
local playtime, afk_time
|
||||
if config.Playtime then playtime = Statistics:combine('Playtime') playtime:set_metadata{stringify=format_minutes, stringify_short=format_clock} end
|
||||
if config.AfkTime then afk_time = Statistics:combine('AfkTime') afk_time:set_metadata{stringify=format_minutes, stringify_short=format_clock} end
|
||||
Event.on_nth_tick(3600, function()
|
||||
if game.tick == 0 then return end
|
||||
for _, player in pairs(game.connected_players) do
|
||||
if playtime then playtime:increment(player) end
|
||||
if afk_time and player.afk_time > afk_required then afk_time:increment(player) end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add DistanceTravelled if it is enabled
|
||||
if config.DistanceTravelled then
|
||||
local stat = Statistics:combine('DistanceTravelled')
|
||||
stat:set_metadata{unit=' tiles'}
|
||||
Event.add(defines.events.on_player_changed_position, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not player.valid or not player.connected or player.afk_time > afk_required then return end
|
||||
stat:increment(player)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add MachinesRemoved and TreesDestroyed and config.OreMined if it is enabled
|
||||
if config.MachinesRemoved or config.TreesDestroyed or config.OreMined then
|
||||
local machines, trees, ore
|
||||
if config.MachinesRemoved then machines = Statistics:combine('MachinesRemoved') end
|
||||
if config.TreesDestroyed then trees = Statistics:combine('TreesDestroyed') end
|
||||
if config.OreMined then ore = Statistics:combine('OreMined') end
|
||||
local function on_event(event)
|
||||
if not event.player_index then return end -- Check player is valid
|
||||
local player = game.players[event.player_index]
|
||||
if not player.valid or not player.connected then return end
|
||||
local entity = event.entity -- Check entity is valid
|
||||
if not entity.valid then return end
|
||||
if entity.type == 'resource' then ore:increment(player)
|
||||
elseif entity.type == 'tree' then trees:increment(player)
|
||||
elseif entity.force == player.force then machines:increment(player) end
|
||||
end
|
||||
Event.add(defines.events.on_marked_for_deconstruction, on_event)
|
||||
Event.add(defines.events.on_player_mined_entity, on_event)
|
||||
end
|
||||
|
||||
--- Add DamageDealt if it is enabled
|
||||
if config.DamageDealt then
|
||||
local stat = Statistics:combine('DamageDealt')
|
||||
Event.add(defines.events.on_entity_damaged, function(event)
|
||||
local character = event.cause -- Check character is valid
|
||||
if not character or not character.valid or character.type ~= 'character' then return end
|
||||
local player = character.player -- Check player is valid
|
||||
if not player.valid or not player.connected then return end
|
||||
local entity = event.entity -- Check entity is valid
|
||||
if not entity.valid or entity.force == player.force or entity.force.name == 'neutral' then return end
|
||||
stat:increment(player, floor(event.final_damage_amount))
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add Kills if it is enabled
|
||||
if config.Kills then
|
||||
local stat = Statistics:combine('Kills')
|
||||
Event.add(defines.events.on_entity_died, function(event)
|
||||
local character = event.cause -- Check character is valid
|
||||
if not character or not character.valid or character.type ~= 'character' then return end
|
||||
local player = character.player -- Check player is valid
|
||||
if not player or not player.valid or not player.connected then return end
|
||||
local entity = event.entity -- Check entity is valid
|
||||
if not entity.valid or entity.force == player.force or entity.force.name == 'neutral' then return end
|
||||
stat:increment(player)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add RocketsLaunched if it is enabled
|
||||
if config.RocketsLaunched then
|
||||
local stat = Statistics:combine('RocketsLaunched')
|
||||
Event.add(defines.events.on_rocket_launched, function(event)
|
||||
local silo = event.rocket_silo -- Check silo is valid
|
||||
if not silo or not silo.valid then return end
|
||||
local force = silo.force -- Check force is valid
|
||||
if not force or not force.valid then return end
|
||||
for _, player in pairs(force.connected_players) do
|
||||
stat:increment(player)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add RocketsLaunched if it is enabled
|
||||
if config.ResearchCompleted then
|
||||
local stat = Statistics:combine('ResearchCompleted')
|
||||
Event.add(defines.events.on_research_finished, function(event)
|
||||
local research = event.research -- Check research is valid
|
||||
if event.by_script or not research or not research.valid then return end
|
||||
local force = research.force -- Check force is valid
|
||||
if not force or not force.valid then return end
|
||||
for _, player in pairs(force.connected_players) do
|
||||
stat:increment(player)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Add all the remaining statistics from the config
|
||||
for statistic, event_name in pairs(config.counters) do
|
||||
local stat = Statistics:combine(statistic)
|
||||
Event.add(event_name, function(event)
|
||||
if not event.player_index then return end
|
||||
local player = game.players[event.player_index]
|
||||
if not player.valid or not player.connected then return end
|
||||
stat:increment(player)
|
||||
end)
|
||||
end
|
||||
90
exp_legacy/module/modules/data/tag.lua
Normal file
90
exp_legacy/module/modules/data/tag.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
--[[-- Commands Module - Tag
|
||||
- Adds a command that allows players to have a custom tag after their name
|
||||
@data Tag
|
||||
]]
|
||||
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
require 'config.expcore.command_general_parse'
|
||||
require 'config.expcore.command_role_parse'
|
||||
require 'config.expcore.command_color_parse'
|
||||
|
||||
--- Stores the tag for a player
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local PlayerTags = PlayerData.Settings:combine('Tag')
|
||||
local PlayerTagColors = PlayerData.Settings:combine('TagColor')
|
||||
PlayerTags:set_metadata{
|
||||
permission = 'command/tag'
|
||||
}
|
||||
PlayerTagColors:set_metadata{
|
||||
permission = 'command/tag-color'
|
||||
}
|
||||
|
||||
local set_tag = function (player, tag, color)
|
||||
if tag == nil or tag == '' then
|
||||
player.tag = ''
|
||||
elseif color then
|
||||
player.tag = '- [color='.. color ..']'..tag..'[/color]'
|
||||
else
|
||||
player.tag = '- '..tag
|
||||
end
|
||||
end
|
||||
|
||||
--- When your tag is updated then apply the changes
|
||||
PlayerTags:on_update(function(player_name, player_tag)
|
||||
local player = game.players[player_name]
|
||||
local player_tag_color = PlayerTagColors:get(player)
|
||||
|
||||
set_tag(player, player_tag, player_tag_color)
|
||||
end)
|
||||
|
||||
--- When your tag color is updated then apply the changes
|
||||
PlayerTagColors:on_update(function(player_name, player_tag_color)
|
||||
local player = game.players[player_name]
|
||||
local player_tag = PlayerTags:get(player)
|
||||
|
||||
set_tag(player, player_tag, player_tag_color)
|
||||
end)
|
||||
|
||||
--- Sets your player tag.
|
||||
-- @command tag
|
||||
-- @tparam string tag the tag that will be after the name, there is a max length
|
||||
Commands.new_command('tag', {'expcom-tag.description'}, 'Sets your player tag.')
|
||||
:add_param('tag', false, 'string-max-length', 20)
|
||||
:enable_auto_concat()
|
||||
:register(function(player, tag)
|
||||
PlayerTags:set(player, tag)
|
||||
end)
|
||||
|
||||
--- Sets your player tag color.
|
||||
-- @command tag
|
||||
-- @tparam string color name.
|
||||
Commands.new_command('tag-color', 'Sets your player tag color.')
|
||||
:add_param('color', false, 'color')
|
||||
:enable_auto_concat()
|
||||
:register(function(player, color)
|
||||
PlayerTagColors:set(player, color)
|
||||
end)
|
||||
|
||||
--- Clears your tag. Or another player if you are admin.
|
||||
-- @command tag-clear
|
||||
-- @tparam[opt=self] LuaPlayer player the player to remove the tag from, nil will apply to self
|
||||
Commands.new_command('tag-clear', {'expcom-tag.description-clear'}, 'Clears your tag. Or another player if you are admin.')
|
||||
:add_param('player', true, 'player-role')
|
||||
:set_defaults{player=function(player)
|
||||
return player -- default is the user using the command
|
||||
end}
|
||||
:register(function(player, action_player)
|
||||
if action_player.index == player.index then
|
||||
-- no player given so removes your tag
|
||||
PlayerTags:remove(action_player)
|
||||
|
||||
elseif Roles.player_allowed(player, 'command/clear-tag/always') then
|
||||
-- player given and user is admin so clears that player's tag
|
||||
PlayerTags:remove(action_player)
|
||||
|
||||
else
|
||||
-- user is not admin and tried to clear another users tag
|
||||
return Commands.error{'expcore-commands.unauthorized'}
|
||||
end
|
||||
end)
|
||||
124
exp_legacy/module/modules/factorio-control.lua
Normal file
124
exp_legacy/module/modules/factorio-control.lua
Normal file
@@ -0,0 +1,124 @@
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local config = require 'config.advanced_start' --- @dep config.advanced_start
|
||||
local use_silo_script = not config.disable_base_game_silo_script
|
||||
|
||||
local util = require("util")
|
||||
local silo_script
|
||||
if use_silo_script then
|
||||
silo_script = require("silo-script")
|
||||
end
|
||||
|
||||
local global = {}
|
||||
Global.register(global, function(tbl)
|
||||
global = tbl
|
||||
end)
|
||||
|
||||
local created_items = function()
|
||||
return
|
||||
{
|
||||
["iron-plate"] = 8,
|
||||
["wood"] = 1,
|
||||
["pistol"] = 1,
|
||||
["firearm-magazine"] = 10,
|
||||
["burner-mining-drill"] = 1,
|
||||
["stone-furnace"] = 1
|
||||
}
|
||||
end
|
||||
|
||||
local respawn_items = function()
|
||||
return
|
||||
{
|
||||
["pistol"] = 1,
|
||||
["firearm-magazine"] = 10
|
||||
}
|
||||
end
|
||||
|
||||
if use_silo_script then
|
||||
for k, v in pairs(silo_script.get_events()) do
|
||||
Event.add(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
util.insert_safe(player, global.created_items)
|
||||
|
||||
local r = global.chart_distance or 200
|
||||
player.force.chart(player.surface, {{player.position.x - r, player.position.y - r}, {player.position.x + r, player.position.y + r}})
|
||||
|
||||
if not global.skip_intro then
|
||||
if game.is_multiplayer() then
|
||||
player.print({"msg-intro"})
|
||||
else
|
||||
game.show_message_dialog{text = {"msg-intro"}}
|
||||
end
|
||||
end
|
||||
|
||||
if use_silo_script then
|
||||
silo_script.on_event(event)
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_respawned, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
util.insert_safe(player, global.respawn_items)
|
||||
if use_silo_script then
|
||||
silo_script.on_event(event)
|
||||
end
|
||||
end)
|
||||
|
||||
if use_silo_script then
|
||||
Event.on_load(function()
|
||||
silo_script.on_load()
|
||||
end)
|
||||
end
|
||||
|
||||
Event.on_init(function()
|
||||
global.created_items = created_items()
|
||||
global.respawn_items = respawn_items()
|
||||
if use_silo_script then
|
||||
silo_script.on_init()
|
||||
end
|
||||
end)
|
||||
|
||||
if use_silo_script then
|
||||
silo_script.add_remote_interface()
|
||||
silo_script.add_commands()
|
||||
end
|
||||
|
||||
remote.add_interface("freeplay",
|
||||
{
|
||||
get_created_items = function()
|
||||
return global.created_items
|
||||
end,
|
||||
set_created_items = function(map)
|
||||
global.created_items = map
|
||||
end,
|
||||
get_respawn_items = function()
|
||||
return global.respawn_items
|
||||
end,
|
||||
set_respawn_items = function(map)
|
||||
global.respawn_items = map
|
||||
end,
|
||||
set_skip_intro = function(bool)
|
||||
global.skip_intro = bool
|
||||
end,
|
||||
set_chart_distance = function(value)
|
||||
global.chart_distance = tonumber(value)
|
||||
end,
|
||||
set_disable_crashsite = function()
|
||||
end,
|
||||
get_ship_items = function()
|
||||
return {}
|
||||
end,
|
||||
set_ship_items = function()
|
||||
return
|
||||
end,
|
||||
get_debris_items = function ()
|
||||
return {}
|
||||
end,
|
||||
set_debris_items = function ()
|
||||
return
|
||||
end
|
||||
})
|
||||
187
exp_legacy/module/modules/graftorio/forcestats.lua
Normal file
187
exp_legacy/module/modules/graftorio/forcestats.lua
Normal file
@@ -0,0 +1,187 @@
|
||||
local Event = require("utils.event")
|
||||
local general = require("modules.graftorio.general")
|
||||
local config = require("config.graftorio")
|
||||
|
||||
local lib = {}
|
||||
|
||||
lib.collect_production = function()
|
||||
for _, force in pairs(game.forces) do
|
||||
---@class ItemStats
|
||||
---@field count number
|
||||
|
||||
---@class ProductionStatistics
|
||||
---@field item_input table<string, ItemStats>
|
||||
---@field item_output table<string, ItemStats>
|
||||
---@field fluid_input table<string, ItemStats>
|
||||
---@field fluid_output table<string, ItemStats>
|
||||
---@field kill_input table<string, ItemStats>
|
||||
---@field kill_output table<string, ItemStats>
|
||||
---@field build_input table<string, ItemStats>
|
||||
---@field build_output table<string, ItemStats>
|
||||
local stats = {
|
||||
item_input = {},
|
||||
item_output = {},
|
||||
fluid_input = {},
|
||||
fluid_output = {},
|
||||
kill_input = {},
|
||||
kill_output = {},
|
||||
build_input = {},
|
||||
build_output = {},
|
||||
}
|
||||
|
||||
for name, count in pairs(force.item_production_statistics.input_counts) do
|
||||
local itemstats = stats.item_input[name] or {}
|
||||
itemstats.count = count
|
||||
stats.item_input[name] = itemstats
|
||||
end
|
||||
for name, count in pairs(force.item_production_statistics.output_counts) do
|
||||
local itemstats = stats.item_output[name] or {}
|
||||
itemstats.count = count
|
||||
stats.item_output[name] = itemstats
|
||||
end
|
||||
|
||||
for name, count in pairs(force.fluid_production_statistics.input_counts) do
|
||||
local fluidstats = stats.fluid_input[name] or {}
|
||||
fluidstats.count = count
|
||||
stats.fluid_input[name] = fluidstats
|
||||
end
|
||||
for name, count in pairs(force.fluid_production_statistics.output_counts) do
|
||||
local fluidstats = stats.fluid_output[name] or {}
|
||||
fluidstats.count = count
|
||||
stats.fluid_output[name] = fluidstats
|
||||
end
|
||||
|
||||
for name, count in pairs(force.kill_count_statistics.input_counts) do
|
||||
local killstats = stats.kill_input[name] or {}
|
||||
killstats.count = count
|
||||
stats.kill_input[name] = killstats
|
||||
end
|
||||
for name, count in pairs(force.kill_count_statistics.output_counts) do
|
||||
local killstats = stats.kill_output[name] or {}
|
||||
killstats.count = count
|
||||
stats.kill_output[name] = killstats
|
||||
end
|
||||
|
||||
for name, count in pairs(force.entity_build_count_statistics.input_counts) do
|
||||
local buildstats = stats.build_input[name] or {}
|
||||
buildstats.count = count
|
||||
stats.build_input[name] = buildstats
|
||||
end
|
||||
for name, count in pairs(force.entity_build_count_statistics.output_counts) do
|
||||
local buildstats = stats.build_output[name] or {}
|
||||
buildstats.count = count
|
||||
stats.build_output[name] = buildstats
|
||||
end
|
||||
|
||||
general.data.output[force.name].production = stats
|
||||
end
|
||||
end
|
||||
|
||||
lib.collect_loginet = function()
|
||||
for _, force in pairs(game.forces) do
|
||||
---@class RobotStatistics
|
||||
---@field all_construction_robots uint
|
||||
---@field available_construction_robot uint
|
||||
---@field all_logistic_robots uint
|
||||
---@field available_logistic_robots uint
|
||||
---@field charging_robot_count uint
|
||||
---@field to_charge_robot_count uint
|
||||
---@field items table<string, uint>
|
||||
---@field pickups table<string, uint>
|
||||
---@field deliveries table<string, uint>
|
||||
local stats = {
|
||||
all_construction_robots = 0,
|
||||
available_construction_robots = 0,
|
||||
|
||||
all_logistic_robots = 0,
|
||||
available_logistic_robots = 0,
|
||||
|
||||
charging_robot_count = 0,
|
||||
to_charge_robot_count = 0,
|
||||
|
||||
items = {},
|
||||
pickups = {},
|
||||
deliveries = {},
|
||||
}
|
||||
for _, networks in pairs(force.logistic_networks) do
|
||||
for _, network in pairs(networks) do
|
||||
stats.available_construction_robots = network.available_construction_robots
|
||||
stats.all_construction_robots = network.all_construction_robots
|
||||
|
||||
stats.available_logistic_robots = network.available_logistic_robots
|
||||
stats.all_logistic_robots = network.all_logistic_robots
|
||||
|
||||
stats.charging_robot_count = 0
|
||||
stats.to_charge_robot_count = 0
|
||||
for _, cell in pairs(network.cells) do
|
||||
stats.charging_robot_count = (stats.charging_robot_count) + cell.charging_robot_count
|
||||
stats.to_charge_robot_count = (stats.to_charge_robot_count) + cell.to_charge_robot_count
|
||||
end
|
||||
|
||||
if config.modules.logistorage then
|
||||
for name, v in pairs(network.get_contents()) do
|
||||
stats.items[name] = (stats.items[name] or 0) + v
|
||||
end
|
||||
|
||||
-- pickups and deliveries of items
|
||||
for _, point_list in pairs({ network.provider_points, network.requester_points, network.storage_points }) do
|
||||
for _, point in pairs(point_list) do
|
||||
for name, qty in pairs(point.targeted_items_pickup) do
|
||||
stats.pickups[name] = (stats.pickups[name] or 0) + qty
|
||||
end
|
||||
for name, qty in pairs(point.targeted_items_deliver) do
|
||||
stats.deliveries[name] = (stats.deliveries[name] or 0) + qty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
general.data.output[force.name].robots = stats
|
||||
end
|
||||
end
|
||||
|
||||
---@class Research
|
||||
---@field name string
|
||||
---@field level uint
|
||||
---@field progress double
|
||||
|
||||
Event.add(defines.events.on_research_finished, function(evt)
|
||||
local research = evt.research
|
||||
if not general.data.output[research.force.name] then general.data.output[research.force.name] = {} end
|
||||
if not general.data.output[research.force.name].research then general.data.output[research.force.name].research = {} end
|
||||
|
||||
local force_research = general.data.output[research.force.name].research or {}
|
||||
table.remove(force_research, 1)
|
||||
general.data.output[research.force.name].research = force_research
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_research_started, function(evt)
|
||||
-- move queue up
|
||||
local research = evt.research
|
||||
if not general.data.output[research.force.name].research then general.data.output[research.force.name].research = {} end
|
||||
|
||||
local force_research = general.data.output[research.force.name].research or {}
|
||||
table.remove(force_research, 1)
|
||||
general.data.output[research.force.name].research = force_research
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(60, function()
|
||||
for _, force in pairs(game.forces) do
|
||||
if not general.data.output[force.name].research then general.data.output[force.name].research = {} end
|
||||
|
||||
local force_research = {}
|
||||
-- this works even if the queue is disabled, but it will always be just 1 long in that case
|
||||
for _, research in pairs(force.research_queue) do
|
||||
table.insert(force_research, {
|
||||
name = research.name,
|
||||
level = research.level,
|
||||
progress = force.get_saved_technology_progress(research) or 0,
|
||||
})
|
||||
end
|
||||
|
||||
general.data.output[force.name].research = force_research
|
||||
end
|
||||
end)
|
||||
|
||||
return lib
|
||||
63
exp_legacy/module/modules/graftorio/general.lua
Normal file
63
exp_legacy/module/modules/graftorio/general.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
local Event = require("utils.event")
|
||||
local Global = require("utils.global")
|
||||
|
||||
local lib = {}
|
||||
|
||||
lib.data = {
|
||||
output = {}
|
||||
}
|
||||
|
||||
Global.register(lib.data, function(tbl)
|
||||
lib.data = tbl
|
||||
end)
|
||||
|
||||
---@class Statistics
|
||||
---@field production ProductionStatistics?
|
||||
---@field robots RobotStatistics?
|
||||
---@field other OtherStatistics?
|
||||
---@field research Research[]?
|
||||
|
||||
Event.on_init(function()
|
||||
---@type table<string, Statistics>
|
||||
lib.data.output = {}
|
||||
for _, force in pairs(game.forces) do
|
||||
lib.data.output[force.name] = {}
|
||||
end
|
||||
end)
|
||||
|
||||
---@class OtherStatistics
|
||||
---@field tick uint
|
||||
---@field evolution EvolutionStatistics
|
||||
|
||||
---@class EvolutionStatistics
|
||||
---@field evolution_factor double
|
||||
---@field evolution_factor_by_pollution double
|
||||
---@field evolution_factor_by_time double
|
||||
---@field evolution_factor_by_killing_spawners double
|
||||
|
||||
lib.collect_other = function()
|
||||
for _, force in pairs(game.forces) do
|
||||
---@type OtherStatistics
|
||||
local other = lib.data.output[force.name].other or {}
|
||||
|
||||
other.evolution = {
|
||||
evolution_factor = force.evolution_factor,
|
||||
evolution_factor_by_pollution = force.evolution_factor_by_pollution,
|
||||
evolution_factor_by_time = force.evolution_factor_by_time,
|
||||
evolution_factor_by_killing_spawners = force.evolution_factor_by_killing_spawners
|
||||
}
|
||||
for k, v in pairs(other) do
|
||||
lib.data.output[force.name].other[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_force_created, function(evt)
|
||||
lib.data.output[evt.force.name] = {}
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_forces_merged, function(evt)
|
||||
lib.data.output[evt.source_name] = nil
|
||||
end)
|
||||
|
||||
return lib
|
||||
27
exp_legacy/module/modules/graftorio/require.lua
Normal file
27
exp_legacy/module/modules/graftorio/require.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
local Commands = require("expcore.commands")
|
||||
local config = require("config.graftorio")
|
||||
local statics = require("modules.graftorio.statics")
|
||||
local general = require("modules.graftorio.general")
|
||||
local forcestats = nil
|
||||
|
||||
if config.modules.forcestats then
|
||||
forcestats = require("modules.graftorio.forcestats")
|
||||
end
|
||||
|
||||
Commands.new_command("collectdata", "Collect data for RCON usage")
|
||||
:add_param("location", true)
|
||||
:register(function()
|
||||
-- this must be first as it overwrites the stats
|
||||
-- also makes the .other table for all forces
|
||||
statics.collect_statics()
|
||||
if config.modules.other then
|
||||
general.collect_other()
|
||||
end
|
||||
if config.modules.forcestats then
|
||||
---@cast forcestats -nil
|
||||
forcestats.collect_production()
|
||||
forcestats.collect_loginet()
|
||||
end
|
||||
rcon.print(game.table_to_json(general.data.output))
|
||||
return Commands.success()
|
||||
end)
|
||||
35
exp_legacy/module/modules/graftorio/statics.lua
Normal file
35
exp_legacy/module/modules/graftorio/statics.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
local general = require("modules.graftorio.general")
|
||||
|
||||
local lib = {}
|
||||
|
||||
---@class StaticStatistics
|
||||
---@field tick uint
|
||||
---@field online_players string[]
|
||||
---@field mods table<string, string>
|
||||
---@field seed table<string, uint>
|
||||
|
||||
lib.collect_statics = function()
|
||||
local stats = {}
|
||||
stats.tick = game.tick
|
||||
|
||||
stats.online_players = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(stats.online_players, player.name)
|
||||
end
|
||||
|
||||
stats.mods = {}
|
||||
for name, version in pairs(game.active_mods) do
|
||||
stats.mods[name] = version
|
||||
end
|
||||
|
||||
-- reason behind this is that the map gen settings can be changed during runtime so just get them fresh
|
||||
stats.seed = {}
|
||||
for _, surface in pairs(game.surfaces) do
|
||||
stats.seed[surface.name] = surface.map_gen_settings.seed
|
||||
end
|
||||
for _, force in pairs(game.forces) do
|
||||
general.data.output[force.name].other = stats
|
||||
end
|
||||
end
|
||||
|
||||
return lib
|
||||
364
exp_legacy/module/modules/gui/autofill.lua
Normal file
364
exp_legacy/module/modules/gui/autofill.lua
Normal file
@@ -0,0 +1,364 @@
|
||||
--[[-- Gui Module - Autofill
|
||||
- Adds a button to enable Autofill
|
||||
@gui Autofill
|
||||
@alias autofill
|
||||
]]
|
||||
|
||||
local Game = require 'utils.game' -- @dep utils.game
|
||||
local Gui = require 'expcore.gui' -- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' -- @dep expcore.gui
|
||||
local Global = require 'utils.global' -- @dep utils.global
|
||||
local config = require 'config.gui.autofill' -- @dep config.gui.autofill
|
||||
local Event = require 'utils.event' -- @dep utils.event
|
||||
local table = require 'overrides.table' -- @dep overrides.table
|
||||
|
||||
local print_text = Game.print_floating_text -- (surface, position, text, color)
|
||||
|
||||
--- Table that stores if autofill is enabled or not
|
||||
local autofill_player_settings = {}
|
||||
Global.register(autofill_player_settings, function(tbl)
|
||||
autofill_player_settings = tbl
|
||||
end)
|
||||
|
||||
local autofill_container
|
||||
|
||||
local function rich_img(type, value)
|
||||
return '[img='..type..'/'..value..']'
|
||||
end
|
||||
|
||||
--- Toggle entity section visibility
|
||||
-- @element toggle_item_button
|
||||
local toggle_section =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/expand_dark',
|
||||
hovered_sprite = 'utility/expand',
|
||||
tooltip = {'autofill.toggle-section-tooltip'},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(20))
|
||||
:on_click(function(_, element, _)
|
||||
local header_flow = element.parent
|
||||
local flow_name = header_flow.caption
|
||||
local flow = header_flow.parent.parent[flow_name]
|
||||
if Gui.toggle_visible_state(flow) then
|
||||
element.sprite = 'utility/collapse_dark'
|
||||
element.hovered_sprite = 'utility/collapse'
|
||||
element.tooltip = {'autofill.toggle-section-collapse-tooltip'}
|
||||
else
|
||||
element.sprite = 'utility/expand_dark'
|
||||
element.hovered_sprite = 'utility/expand'
|
||||
element.tooltip = {'autofill.toggle-section-tooltip'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Toggle enitity button, used for toggling autofill for the specific entity
|
||||
-- All entity autofill settings will be ignored if its disabled
|
||||
-- @element entity_toggle
|
||||
local entity_toggle =
|
||||
Gui.element(function(_, parent, entity_name)
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/confirm_slot',
|
||||
tooltip = {'autofill.toggle-entity-tooltip', rich_img('item', entity_name)},
|
||||
style = 'shortcut_bar_button_green'
|
||||
}
|
||||
end)
|
||||
:style(Gui.sprite_style(22))
|
||||
:on_click(function(player, element, _)
|
||||
local entity_name = string.match(element.parent.parent.name,'(.*)%-header')
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
local setting = autofill_player_settings[player.name][entity_name]
|
||||
if not setting then return end
|
||||
if setting.enabled then
|
||||
setting.enabled = false
|
||||
element.sprite = 'utility/close_black'
|
||||
element.style = 'shortcut_bar_button_red'
|
||||
else
|
||||
setting.enabled = true
|
||||
element.sprite = 'utility/confirm_slot'
|
||||
element.style = 'shortcut_bar_button_green'
|
||||
end
|
||||
-- Correct the button size
|
||||
local style = element.style
|
||||
style.padding = -2
|
||||
style.height = 22
|
||||
style.width = 22
|
||||
end)
|
||||
|
||||
--- Draw a section header and main scroll
|
||||
-- @element autofill_section_container
|
||||
local section =
|
||||
Gui.element(function(definition, parent, section_name, table_size)
|
||||
-- Draw the header for the section
|
||||
local header = Gui.header(
|
||||
parent,
|
||||
{'autofill.toggle-section-caption', rich_img('item', section_name), {'entity-name.'..section_name}},
|
||||
{'autofill.toggle-section-tooltip'},
|
||||
true,
|
||||
section_name..'-header'
|
||||
)
|
||||
|
||||
definition:triggers_events(header.parent.header_label)
|
||||
|
||||
-- Right aligned button to toggle the section
|
||||
header.caption = section_name
|
||||
entity_toggle(header, section_name)
|
||||
toggle_section(header)
|
||||
|
||||
local section_table = parent.add{
|
||||
type = 'table',
|
||||
name = section_name,
|
||||
column_count = table_size
|
||||
}
|
||||
|
||||
section_table.visible = false
|
||||
|
||||
return definition:no_events(section_table)
|
||||
end)
|
||||
:on_click(function(_, element, event)
|
||||
event.element = element.parent.alignment[toggle_section.name]
|
||||
toggle_section:raise_event(event)
|
||||
end)
|
||||
|
||||
--- Toggle item button, used for toggling autofill for the specific item
|
||||
-- @element toggle_item_button
|
||||
local toggle_item_button =
|
||||
Gui.element(function(_, parent, item)
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'item/'..item.name,
|
||||
tooltip = {'autofill.toggle-tooltip', rich_img('item', item.name), item.category},
|
||||
style = 'shortcut_bar_button_red'
|
||||
}
|
||||
end)
|
||||
:style(Gui.sprite_style(32, nil, { right_margin = -3 }))
|
||||
:on_click(function(player, element)
|
||||
local item_name = element.parent.tooltip
|
||||
local entity_name = element.parent.parent.parent.name
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
local setting = autofill_player_settings[player.name][entity_name]
|
||||
if not setting then return end
|
||||
local item = setting.items[item_name]
|
||||
if not item then return end
|
||||
if item.enabled then
|
||||
item.enabled = false
|
||||
element.style = 'shortcut_bar_button_red'
|
||||
else
|
||||
item.enabled = true
|
||||
element.style = 'shortcut_bar_button_green'
|
||||
end
|
||||
-- Correct the button size
|
||||
local style = element.style
|
||||
style.right_margin = -3
|
||||
style.padding = -2
|
||||
style.height = 32
|
||||
style.width = 32
|
||||
end)
|
||||
|
||||
|
||||
--- Amount text field for a autofill item
|
||||
-- @element amount_textfield
|
||||
local amount_textfield =
|
||||
Gui.element(function(_, parent, item)
|
||||
return parent.add{
|
||||
type = 'textfield',
|
||||
text = item.amount,
|
||||
tooltip = {'autofill.amount-tooltip', item.category },
|
||||
clear_and_focus_on_right_click = true,
|
||||
numeric = true,
|
||||
allow_decimal = false,
|
||||
allow_negative = false
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
maximal_width = 40,
|
||||
height = 31,
|
||||
padding = -2
|
||||
}
|
||||
:on_text_changed(function(player, element, _)
|
||||
local value = tonumber(element.text)
|
||||
if not value then value = 0 end
|
||||
local clamped = math.clamp(value, 0, 1000)
|
||||
local item_name = element.parent.tooltip
|
||||
local entity_name = element.parent.parent.parent.name
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
local setting = autofill_player_settings[player.name][entity_name]
|
||||
if not setting then return end
|
||||
local item = setting.items[item_name]
|
||||
if not item then return end
|
||||
item.amount = clamped
|
||||
if clamped ~= value then
|
||||
element.text = clamped
|
||||
player.print{'autofill.invalid', item.amount, rich_img('item', item.name), rich_img('entity', entity_name) }
|
||||
return
|
||||
end
|
||||
end)
|
||||
|
||||
--- Autofill setting, contains a button and a textbox
|
||||
-- @element add_autofill_setting
|
||||
local add_autofill_setting =
|
||||
Gui.element(function(_, parent, item)
|
||||
local toggle_flow = parent.add{ type = 'flow', name = 'toggle-setting-'..item.name, tooltip = item.name }
|
||||
local amount_flow = parent.add{ type = 'flow', name = 'amount-setting-'..item.name, tooltip = item.name }
|
||||
toggle_flow.style.padding = 0
|
||||
amount_flow.style.padding = 0
|
||||
toggle_item_button(toggle_flow, item)
|
||||
amount_textfield(amount_flow, item)
|
||||
end)
|
||||
|
||||
--- Autofill setting empty, contains filler button and textfield gui elements
|
||||
-- @element add_empty_autofill_setting
|
||||
local add_empty_autofill_setting =
|
||||
Gui.element(function(_, parent)
|
||||
local toggle_element = parent.add{
|
||||
type = 'sprite-button'
|
||||
}
|
||||
toggle_element.style.right_margin = -3
|
||||
toggle_element.style.width = 32
|
||||
toggle_element.style.height = 32
|
||||
toggle_element.enabled = false
|
||||
local amount_element = parent.add{
|
||||
type = 'textfield'
|
||||
}
|
||||
amount_element.style.maximal_width = 40
|
||||
amount_element.style.height = 31
|
||||
amount_element.style.padding = -2
|
||||
amount_element.enabled = false
|
||||
end)
|
||||
|
||||
|
||||
--- Main gui container for the left flow
|
||||
-- @element autofill_container
|
||||
autofill_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name)
|
||||
-- Draw the scroll container
|
||||
local scroll_table = Gui.scroll_table(container, 400, 1, 'autofill-scroll-table')
|
||||
-- Set the scroll panel to always show the scrollbar (not doing this will result in a changing gui size)
|
||||
scroll_table.parent.vertical_scroll_policy = 'always'
|
||||
-- Scroll panel has by default padding
|
||||
scroll_table.parent.style.padding = 0
|
||||
-- Remove the default gap that is added in a table between elements
|
||||
scroll_table.style.vertical_spacing = 0
|
||||
-- Center the first collumn in the table
|
||||
scroll_table.style.column_alignments[1] = 'center'
|
||||
-- Loop over each default entity config
|
||||
for _, setting in pairs(config.default_entities) do
|
||||
local table_sizes = {}
|
||||
local tables = {}
|
||||
-- Draw a section for the element
|
||||
local entity_table = section(scroll_table, setting.entity, 3)
|
||||
-- Add some padding around the table
|
||||
entity_table.style.padding = 3
|
||||
-- Make sure each collumn is alignment top center
|
||||
entity_table.style.column_alignments[1] = 'top-center'
|
||||
entity_table.style.column_alignments[2] = 'top-center'
|
||||
entity_table.style.column_alignments[3] = 'top-center'
|
||||
-- Loop over each item category
|
||||
for _, category in pairs(config.categories) do
|
||||
if not table_sizes[category] then table_sizes[category] = 0 end
|
||||
-- Draw table
|
||||
local category_table = entity_table.add{
|
||||
type = 'table',
|
||||
name = category..'-category',
|
||||
column_count = 2
|
||||
}
|
||||
-- Add padding between each item
|
||||
category_table.style.vertical_spacing = 1
|
||||
tables[category] = category_table
|
||||
-- Add item autofill setting gui elements to the table
|
||||
for _, item in pairs(setting.items) do
|
||||
if item.category == category then
|
||||
add_autofill_setting(category_table, item)
|
||||
table_sizes[category] = table_sizes[category] + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add empty gui elements for the categories with less items than the other categories
|
||||
local t = table.get_values(table_sizes)
|
||||
table.sort(t)
|
||||
local biggest = t[#t]
|
||||
for category, size in pairs(table_sizes) do
|
||||
for i=biggest-size,1,-1 do
|
||||
add_empty_autofill_setting(tables[category])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Return the external container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
--- Button on the top flow used to toggle autofill container
|
||||
-- @element autofill_toggle
|
||||
Gui.left_toolbar_button(config.icon, {'autofill.main-tooltip'}, autofill_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/autofill')
|
||||
end)
|
||||
|
||||
--- When a player is created make sure they have the default autofill settings
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not autofill_player_settings[player.name] then
|
||||
autofill_player_settings[player.name] = table.deep_copy(config.default_entities)
|
||||
end
|
||||
end)
|
||||
|
||||
local function entity_build(event)
|
||||
-- Check if player exists
|
||||
local player = game.players[event.player_index]
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
-- Check if the entity is in the config and enabled
|
||||
local entity = event.created_entity
|
||||
|
||||
-- Check if player has settings
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
|
||||
local entity_settings = autofill_player_settings[player.name][entity.name]
|
||||
-- Check if autofill for the entity is enabled
|
||||
if not entity_settings then return end
|
||||
if not entity_settings.enabled then return end
|
||||
|
||||
-- Get the inventory of the player
|
||||
local player_inventory = player.get_main_inventory()
|
||||
|
||||
local text_position = { x = entity.position.x, y = entity.position.y }
|
||||
-- Loop over all possible items to insert into the entity
|
||||
for _, item in pairs(entity_settings.items) do
|
||||
-- Check if the item is enabled or goto next item
|
||||
if not item.enabled then goto end_item end
|
||||
|
||||
-- Get the inventory of the entity or goto next item
|
||||
local entity_inventory = entity.get_inventory(item.inv)
|
||||
if not entity_inventory then goto end_item end
|
||||
|
||||
local preferd_amount = item.amount
|
||||
local item_amount = player_inventory.get_item_count(item.name)
|
||||
if item_amount ~= 0 then
|
||||
local inserted
|
||||
text_position.y = text_position.y - 0.5
|
||||
local color = { r = 0, g = 255, b = 0, a = 1}
|
||||
if item_amount >= preferd_amount then
|
||||
-- Can item be inserted? no, goto next item!
|
||||
if not entity_inventory.can_insert{name=item.name, count=preferd_amount} then
|
||||
goto end_item
|
||||
end
|
||||
inserted = entity_inventory.insert{name=item.name, count=preferd_amount}
|
||||
else
|
||||
inserted = entity_inventory.insert{name=item.name, count=item_amount}
|
||||
color = { r = 255, g = 165, b = 0, a = 1}
|
||||
end
|
||||
player_inventory.remove{name=item.name, count=inserted}
|
||||
print_text(entity.surface, text_position, {'autofill.inserted', inserted, rich_img('item', item.name), rich_img('entity', entity.name) }, color)
|
||||
end
|
||||
::end_item::
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_built_entity, entity_build)
|
||||
392
exp_legacy/module/modules/gui/bonus.lua
Normal file
392
exp_legacy/module/modules/gui/bonus.lua
Normal file
@@ -0,0 +1,392 @@
|
||||
--[[-- Gui Module - Bonus
|
||||
@gui Bonus
|
||||
@alias bonus_container
|
||||
]]
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.bonus' --- @dep config.bonus
|
||||
local vlayer = require 'modules.control.vlayer'
|
||||
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
local bonus_container
|
||||
|
||||
local function bonus_gui_pts_needed(player)
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
local total = 0
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
total = total + (disp['bonus_display_' .. k .. '_slider'].slider_value / config.player_bonus[v].cost_scale * config.player_bonus[v].cost)
|
||||
end
|
||||
|
||||
total = total + (disp['bonus_display_personal_battery_recharge_slider'].slider_value / config.player_special_bonus['personal_battery_recharge'].cost_scale * config.player_special_bonus['personal_battery_recharge'].cost)
|
||||
|
||||
return total
|
||||
end
|
||||
|
||||
local function apply_bonus(player)
|
||||
if not Roles.player_allowed(player, 'gui/bonus') then
|
||||
for k, v in pairs(config.player_bonus) do
|
||||
player[k] = 0
|
||||
|
||||
if v.combined_bonus then
|
||||
for i=1, #v.combined_bonus do
|
||||
player[v.combined_bonus[i]] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if not player.character then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
player[v] = disp['bonus_display_' .. k .. '_slider'].slider_value
|
||||
|
||||
if config.player_bonus[v].combined_bonus then
|
||||
for i=1, #config.player_bonus[v].combined_bonus do
|
||||
player[config.player_bonus[v].combined_bonus[i]] = disp['bonus_display_' .. k .. '_slider'].slider_value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_periodic_bonus(player)
|
||||
if not Roles.player_allowed(player, 'gui/bonus') then
|
||||
return
|
||||
end
|
||||
|
||||
if not player.character then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
|
||||
if vlayer.get_statistics()['energy_sustained'] > 0 then
|
||||
local armor = player.get_inventory(defines.inventory.character_armor)[1].grid
|
||||
|
||||
if armor then
|
||||
local slider = disp['bonus_display_personal_battery_recharge_slider'].slider_value * 100000 * config.player_special_bonus_rate / 6
|
||||
|
||||
for i=1, #armor.equipment do
|
||||
if armor.equipment[i].energy < armor.equipment[i].max_energy then
|
||||
local energy_required = math.min(math.floor(armor.equipment[i].max_energy - armor.equipment[i].energy), vlayer.get_statistics()['energy_storage'], slider)
|
||||
armor.equipment[i].energy = armor.equipment[i].energy + energy_required
|
||||
vlayer.energy_changed(- energy_required)
|
||||
|
||||
slider = slider - energy_required
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Control label for the bonus points available
|
||||
-- @element bonus_gui_control_pts_a
|
||||
local bonus_gui_control_pts_a =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_a',
|
||||
caption = {'bonus.control-pts-a'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
local bonus_gui_control_pts_a_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_a_count',
|
||||
caption = config.pts.base,
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
--- Control label for the bonus points needed
|
||||
-- @element bonus_gui_control_pts_n
|
||||
local bonus_gui_control_pts_n =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_n',
|
||||
caption = {'bonus.control-pts-n'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
local bonus_gui_control_pts_n_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_n_count',
|
||||
caption = '0',
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width =config.gui_display_width['half']
|
||||
}
|
||||
|
||||
--- Control label for the bonus points remaining
|
||||
-- @element bonus_gui_control_pts_r
|
||||
local bonus_gui_control_pts_r =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_r',
|
||||
caption = {'bonus.control-pts-r'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
local bonus_gui_control_pts_r_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_r_count',
|
||||
caption = '0',
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
--- A button used for pts calculations
|
||||
-- @element bonus_gui_control_refresh
|
||||
local bonus_gui_control_reset =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'bonus.control-reset'}
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}:on_click(function(player, element, _)
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
local s = 'bonus_display_' .. k .. '_slider'
|
||||
disp[s].slider_value = config.player_bonus[v].value
|
||||
|
||||
if config.player_bonus[v].is_percentage then
|
||||
disp[disp[s].tags.counter].caption = format_number(disp[s].slider_value * 100) .. ' %'
|
||||
|
||||
else
|
||||
disp[disp[s].tags.counter].caption = format_number(disp[s].slider_value)
|
||||
end
|
||||
end
|
||||
|
||||
local slider = disp['bonus_display_personal_battery_recharge_slider']
|
||||
slider.slider_value = config.player_special_bonus['personal_battery_recharge'].value
|
||||
disp[slider.tags.counter].caption = format_number(slider.slider_value)
|
||||
|
||||
local r = bonus_gui_pts_needed(player)
|
||||
element.parent[bonus_gui_control_pts_n_count.name].caption = r
|
||||
element.parent[bonus_gui_control_pts_r_count.name].caption = tonumber(element.parent[bonus_gui_control_pts_a_count.name].caption) - r
|
||||
end)
|
||||
|
||||
--- A button used for pts apply
|
||||
-- @element bonus_gui_control_apply
|
||||
local bonus_gui_control_apply =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'bonus.control-apply'}
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}:on_click(function(player, element, _)
|
||||
local n = bonus_gui_pts_needed(player)
|
||||
element.parent[bonus_gui_control_pts_n_count.name].caption = n
|
||||
local r = tonumber(element.parent[bonus_gui_control_pts_a_count.name].caption) - n
|
||||
element.parent[bonus_gui_control_pts_r_count.name].caption = r
|
||||
|
||||
if r >= 0 then
|
||||
apply_bonus(player)
|
||||
end
|
||||
end)
|
||||
|
||||
--- A vertical flow containing all the bonus control
|
||||
-- @element bonus_control_set
|
||||
local bonus_control_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local bonus_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(bonus_set, config.gui_display_width['half'] * 2, 2, 'disp')
|
||||
|
||||
bonus_gui_control_pts_a(disp)
|
||||
bonus_gui_control_pts_a_count(disp)
|
||||
|
||||
bonus_gui_control_pts_n(disp)
|
||||
bonus_gui_control_pts_n_count(disp)
|
||||
|
||||
bonus_gui_control_pts_r(disp)
|
||||
bonus_gui_control_pts_r_count(disp)
|
||||
|
||||
bonus_gui_control_reset(disp)
|
||||
bonus_gui_control_apply(disp)
|
||||
|
||||
return bonus_set
|
||||
end)
|
||||
|
||||
--- Display group
|
||||
-- @element bonus_gui_slider
|
||||
local bonus_gui_slider =
|
||||
Gui.element(function(_definition, parent, name, caption, tooltip, bonus)
|
||||
local label = parent.add{
|
||||
type = 'label',
|
||||
caption = caption,
|
||||
tooltip = tooltip,
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
label.style.width = config.gui_display_width['label']
|
||||
|
||||
local value = bonus.value
|
||||
|
||||
if bonus.is_percentage then
|
||||
value = format_number(value * 100) .. ' %'
|
||||
|
||||
else
|
||||
value = format_number(value)
|
||||
end
|
||||
|
||||
local slider = parent.add{
|
||||
type = 'slider',
|
||||
name = name .. '_slider',
|
||||
value = bonus.value,
|
||||
maximum_value = bonus.max,
|
||||
value_step = bonus.scale,
|
||||
discrete_values = true,
|
||||
style = 'notched_slider',
|
||||
tags = {
|
||||
counter = name .. '_count',
|
||||
is_percentage = bonus.is_percentage
|
||||
}
|
||||
}
|
||||
slider.style.width = config.gui_display_width['slider']
|
||||
slider.style.horizontally_stretchable = true
|
||||
|
||||
local count = parent.add{
|
||||
type = 'label',
|
||||
name = name .. '_count',
|
||||
caption = value,
|
||||
style = 'heading_2_label',
|
||||
}
|
||||
count.style.width = config.gui_display_width['count']
|
||||
|
||||
return slider
|
||||
end)
|
||||
:on_value_changed(function(player, element, _event)
|
||||
if element.tags.is_percentage then
|
||||
element.parent[element.tags.counter].caption = format_number(element.slider_value * 100) .. ' %'
|
||||
|
||||
else
|
||||
element.parent[element.tags.counter].caption = format_number(element.slider_value)
|
||||
end
|
||||
|
||||
local r = bonus_gui_pts_needed(player)
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_1'].disp.table
|
||||
disp[bonus_gui_control_pts_n_count.name].caption = r
|
||||
disp[bonus_gui_control_pts_r_count.name].caption = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - r
|
||||
end)
|
||||
|
||||
--- A vertical flow containing all the bonus data
|
||||
-- @element bonus_data_set
|
||||
local bonus_data_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local bonus_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(bonus_set, config.gui_display_width['half'] * 2, 3, 'disp')
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
bonus_gui_slider(disp, 'bonus_display_' .. k, {'bonus.display-' .. k}, {'bonus.display-' .. k .. '-tooltip'}, config.player_bonus[v])
|
||||
end
|
||||
|
||||
bonus_gui_slider(disp, 'bonus_display_personal_battery_recharge', {'bonus.display-personal-battery-recharge'}, {'bonus.display-personal-battery-recharge-tooltip'}, config.player_special_bonus['personal_battery_recharge'])
|
||||
|
||||
return bonus_set
|
||||
end)
|
||||
|
||||
--- The main container for the bonus gui
|
||||
-- @element bonus_container
|
||||
bonus_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local container = Gui.container(parent, definition.name, config.gui_display_width['half'] * 2)
|
||||
|
||||
bonus_control_set(container, 'bonus_st_1')
|
||||
bonus_data_set(container, 'bonus_st_2')
|
||||
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_1'].disp.table
|
||||
local n = bonus_gui_pts_needed(player)
|
||||
disp[bonus_gui_control_pts_n_count.name].caption = n
|
||||
local r = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - n
|
||||
disp[bonus_gui_control_pts_r_count.name].caption = r
|
||||
|
||||
apply_bonus(player)
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
--- Button on the top flow used to toggle the bonus container
|
||||
-- @element toggle_left_element
|
||||
Gui.left_toolbar_button('item/exoskeleton-equipment', {'bonus.main-tooltip'}, bonus_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/bonus')
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
if event.player_index ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(config.force_bonus) do
|
||||
game.players[event.player_index].force[k] = v.value
|
||||
end
|
||||
|
||||
for k, v in pairs(config.surface_bonus) do
|
||||
game.players[event.player_index].surface[k] = v.value
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(Roles.events.on_role_assigned, function(event)
|
||||
apply_bonus(game.players[event.player_index])
|
||||
end)
|
||||
|
||||
Event.add(Roles.events.on_role_unassigned, function(event)
|
||||
apply_bonus(game.players[event.player_index])
|
||||
end)
|
||||
|
||||
--- When a player respawns re-apply bonus
|
||||
Event.add(defines.events.on_player_respawned, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_1'].disp.table
|
||||
local n = bonus_gui_pts_needed(player)
|
||||
disp[bonus_gui_control_pts_n_count.name].caption = n
|
||||
local r = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - n
|
||||
disp[bonus_gui_control_pts_r_count.name].caption = r
|
||||
|
||||
if r >= 0 then
|
||||
apply_bonus(player)
|
||||
end
|
||||
end)
|
||||
|
||||
--- When a player dies allow them to have instant respawn
|
||||
Event.add(defines.events.on_player_died, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
if Roles.player_has_flag(player, 'instant-respawn') then
|
||||
player.ticks_to_respawn = 120
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(config.player_special_bonus_rate, function(_)
|
||||
for _, player in pairs(game.connected_players) do
|
||||
apply_periodic_bonus(player)
|
||||
end
|
||||
end)
|
||||
114
exp_legacy/module/modules/gui/debug/_g_view.lua
Normal file
114
exp_legacy/module/modules/gui/debug/_g_view.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
|
||||
local dump = Model.dump
|
||||
|
||||
local Public = {}
|
||||
|
||||
local ignore = {
|
||||
_G = true,
|
||||
assert = true,
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
load = true,
|
||||
loadstring = true,
|
||||
next = true,
|
||||
pairs = true,
|
||||
pcall = true,
|
||||
print = true,
|
||||
rawequal = true,
|
||||
rawlen = true,
|
||||
rawget = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setmetatable = true,
|
||||
tonumber = true,
|
||||
tostring = true,
|
||||
type = true,
|
||||
xpcall = true,
|
||||
_VERSION = true,
|
||||
['module'] = true,
|
||||
require = true,
|
||||
package = true,
|
||||
unpack = true,
|
||||
table = true,
|
||||
string = true,
|
||||
bit32 = true,
|
||||
math = true,
|
||||
debug = true,
|
||||
serpent = true,
|
||||
log = true,
|
||||
table_size = true,
|
||||
global = true,
|
||||
remote = true,
|
||||
commands = true,
|
||||
settings = true,
|
||||
rcon = true,
|
||||
script = true,
|
||||
util = true,
|
||||
mod_gui = true,
|
||||
game = true,
|
||||
rendering = true
|
||||
}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
|
||||
Public.name = '_G'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for key, value in pairs(_G) do
|
||||
if not ignore[key] then
|
||||
local header =
|
||||
left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = tostring(key)}
|
||||
Gui.set_data(header, value)
|
||||
end
|
||||
end
|
||||
|
||||
local right_panel = main_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
Gui.set_data(left_panel, {right_panel = right_panel, selected_header = nil})
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local value = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local left_panel_data = Gui.get_data(left_panel)
|
||||
local right_panel = left_panel_data.right_panel
|
||||
local selected_header = left_panel_data.selected_header
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
left_panel_data.selected_header = element
|
||||
|
||||
local content = dump(value)
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
169
exp_legacy/module/modules/gui/debug/event_view.lua
Normal file
169
exp_legacy/module/modules/gui/debug/event_view.lua
Normal file
@@ -0,0 +1,169 @@
|
||||
local Event = require 'utils.event'
|
||||
local table = require 'overrides.table'
|
||||
local Gui = require 'utils.gui'
|
||||
local Model = require 'modules.gui.debug.model'
|
||||
|
||||
local format = string.format
|
||||
local insert = table.insert
|
||||
|
||||
local events = defines.events
|
||||
|
||||
-- Constants
|
||||
local events_to_keep = 10
|
||||
|
||||
-- Local vars
|
||||
local Public = {
|
||||
name = 'Events'
|
||||
}
|
||||
local name_lookup = {}
|
||||
|
||||
-- GUI names
|
||||
local checkbox_name = Gui.uid_name()
|
||||
local filter_name = Gui.uid_name()
|
||||
local clear_filter_name = Gui.uid_name()
|
||||
|
||||
-- global tables
|
||||
local enabled = {}
|
||||
local last_events = {}
|
||||
global.debug_event_view = {
|
||||
enabled = enabled,
|
||||
last_events = last_events,
|
||||
filter = ''
|
||||
}
|
||||
|
||||
function Public.on_open_debug()
|
||||
local tbl = global.debug_event_view
|
||||
if tbl then
|
||||
enabled = tbl.enabled
|
||||
last_events = tbl.last_events
|
||||
else
|
||||
enabled = {}
|
||||
last_events = {}
|
||||
|
||||
global.debug_event_view = {
|
||||
enabled = enabled,
|
||||
last_events = last_events
|
||||
}
|
||||
end
|
||||
|
||||
Public.on_open_debug = nil
|
||||
end
|
||||
|
||||
-- Local functions
|
||||
local function event_callback(event)
|
||||
local id = event.name
|
||||
if not enabled[id] then
|
||||
return
|
||||
end
|
||||
local name = name_lookup[id]
|
||||
|
||||
if not last_events[name] then
|
||||
last_events[name] = {}
|
||||
end
|
||||
|
||||
insert(last_events[name], 1, event)
|
||||
last_events[name][events_to_keep + 1] = nil
|
||||
event.name = nil
|
||||
|
||||
local str = format('%s (id = %s): %s', name, id, Model.dump(event))
|
||||
game.print(str)
|
||||
log(str)
|
||||
end
|
||||
|
||||
local function on_gui_checked_state_changed(event)
|
||||
local element = event.element
|
||||
local name = element.caption
|
||||
local id = events[name]
|
||||
local state = element.state and true or false
|
||||
element.state = state
|
||||
if state then
|
||||
enabled[id] = true
|
||||
else
|
||||
enabled[id] = false
|
||||
end
|
||||
end
|
||||
|
||||
-- GUI
|
||||
|
||||
-- Create a table with events sorted by their names
|
||||
local grid_builder = {}
|
||||
for name, _ in pairs(events) do
|
||||
grid_builder[#grid_builder + 1] = name
|
||||
end
|
||||
|
||||
table.sort(grid_builder)
|
||||
|
||||
local function redraw_event_table(gui_table, filter)
|
||||
for _, event_name in pairs(grid_builder) do
|
||||
if filter == '' or event_name:find(filter) then
|
||||
local index = events[event_name]
|
||||
gui_table.add({type = 'flow'}).add {
|
||||
name = checkbox_name,
|
||||
type = 'checkbox',
|
||||
state = enabled[index] or false,
|
||||
caption = event_name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Public.show(container)
|
||||
local filter = global.debug_event_view.filter
|
||||
|
||||
local main_frame_flow = container.add({type = 'flow', direction = 'vertical'})
|
||||
|
||||
local filter_flow = main_frame_flow.add({type = 'flow', direction = 'horizontal'})
|
||||
filter_flow.add({type = 'label', caption = 'filter'})
|
||||
local filter_textfield = filter_flow.add({type = 'textfield', name = filter_name, text = filter})
|
||||
local clear_button = filter_flow.add({type = 'button', name = clear_filter_name, caption = 'clear'})
|
||||
|
||||
local scroll_pane = main_frame_flow.add({type = 'scroll-pane'})
|
||||
local gui_table = scroll_pane.add({type = 'table', column_count = 3, draw_horizontal_lines = true})
|
||||
|
||||
Gui.set_data(filter_textfield, gui_table)
|
||||
Gui.set_data(clear_button, {gui_table = gui_table, filter_textfield = filter_textfield})
|
||||
|
||||
redraw_event_table(gui_table, filter)
|
||||
end
|
||||
|
||||
Gui.on_checked_state_changed(checkbox_name, on_gui_checked_state_changed)
|
||||
|
||||
Gui.on_text_changed(
|
||||
filter_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local gui_table = Gui.get_data(element)
|
||||
|
||||
local filter = element.text:gsub(' ', '_')
|
||||
|
||||
global.debug_event_view.filter = filter
|
||||
element.text = filter
|
||||
|
||||
gui_table.clear()
|
||||
redraw_event_table(gui_table, filter)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
clear_filter_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
local filter_textfield = data.filter_textfield
|
||||
local gui_table = data.gui_table
|
||||
|
||||
filter_textfield.text = ''
|
||||
global.debug_event_view.filter = ''
|
||||
|
||||
gui_table.clear()
|
||||
redraw_event_table(gui_table, '')
|
||||
end
|
||||
)
|
||||
|
||||
-- Event registers (TODO: turn to removable hooks.. maybe)
|
||||
for name, id in pairs(events) do
|
||||
name_lookup[id] = name
|
||||
Event.add(id, event_callback)
|
||||
end
|
||||
|
||||
return Public
|
||||
130
exp_legacy/module/modules/gui/debug/expcore_datastore_view.lua
Normal file
130
exp_legacy/module/modules/gui/debug/expcore_datastore_view.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump = Model.dump
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'Datastore'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for name in pairs(table.keysort(Datastore.debug())) do
|
||||
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = name}
|
||||
Gui.set_data(header, name)
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local tableName = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = tableName
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = Datastore.debug(tableName)
|
||||
local content_string = {}
|
||||
for key, value in pairs(content) do
|
||||
content_string[#content_string+1] = key:gsub('^%l', string.upper)..' = '..dump(value)
|
||||
end
|
||||
right_panel.text = concat(content_string, '\n')
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data)
|
||||
local content = Datastore.debug(text_input.text)
|
||||
local content_string = {}
|
||||
for key, value in pairs(content) do
|
||||
content_string[#content_string+1] = key:gsub('^%l', string.upper)..' = '..dump(value)
|
||||
end
|
||||
data.right_panel.text = concat(content_string, '\n')
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
128
exp_legacy/module/modules/gui/debug/expcore_gui_view.lua
Normal file
128
exp_legacy/module/modules/gui/debug/expcore_gui_view.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local ExpGui = require 'expcore.gui' --- @dep utils.global
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump = Model.dump
|
||||
local dump_text = Model.dump_text
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'Elements'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for element_id, file_path in pairs(ExpGui.file_paths) do
|
||||
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = element_id..' - '..file_path}
|
||||
Gui.set_data(header, element_id)
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local element_id = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = concat {'Gui.defines[', element_id, ']'}
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = dump(ExpGui.debug_info[element_id]) or 'nil'
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data, player)
|
||||
local suc, ouput = dump_text(text_input.text, player)
|
||||
if not suc then
|
||||
text_input.style.font_color = Color.red
|
||||
else
|
||||
text_input.style.font_color = Color.black
|
||||
data.right_panel.text = ouput
|
||||
end
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
133
exp_legacy/module/modules/gui/debug/global_view.lua
Normal file
133
exp_legacy/module/modules/gui/debug/global_view.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
|
||||
local dump = Model.dump
|
||||
local dump_text = Model.dump_text
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local ignore = {tokens = true, data_store = true, datastores = true}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'global'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for key, _ in pairs(global) do
|
||||
if not ignore[key] then
|
||||
local header =
|
||||
left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = tostring(key)}
|
||||
Gui.set_data(header, key)
|
||||
end
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil,
|
||||
selected_token_id = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local key = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = concat {"global['", key, "']"}
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = dump(global[key]) or 'nil'
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data, player)
|
||||
local suc, ouput = dump_text(text_input.text, player)
|
||||
if not suc then
|
||||
text_input.style.font_color = Color.red
|
||||
else
|
||||
text_input.style.font_color = Color.black
|
||||
data.right_panel.text = ouput
|
||||
end
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
113
exp_legacy/module/modules/gui/debug/main_view.lua
Normal file
113
exp_legacy/module/modules/gui/debug/main_view.lua
Normal file
@@ -0,0 +1,113 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
|
||||
local Public = {}
|
||||
|
||||
local pages = {
|
||||
require 'modules.gui.debug.redmew_global_view',
|
||||
require 'modules.gui.debug.expcore_datastore_view',
|
||||
require 'modules.gui.debug.expcore_gui_view',
|
||||
require 'modules.gui.debug.global_view',
|
||||
require 'modules.gui.debug.package_view',
|
||||
require 'modules.gui.debug._g_view',
|
||||
require 'modules.gui.debug.event_view'
|
||||
}
|
||||
|
||||
local main_frame_name = Gui.uid_name()
|
||||
local close_name = Gui.uid_name()
|
||||
local tab_name = Gui.uid_name()
|
||||
|
||||
function Public.open_dubug(player)
|
||||
for i = 1, #pages do
|
||||
local page = pages[i]
|
||||
local callback = page.on_open_debug
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
local center = player.gui.center
|
||||
local frame = center[main_frame_name]
|
||||
if frame then
|
||||
return
|
||||
end
|
||||
|
||||
--[[
|
||||
local screen_element = player.gui.screen
|
||||
|
||||
frame = screen_element.add{type = 'frame', name = main_frame_name, caption = 'Debuggertron 3000'}
|
||||
frame.style.size = {900, 600}
|
||||
frame.auto_center = true
|
||||
]]
|
||||
|
||||
frame = center.add {type = 'frame', name = main_frame_name, caption = 'Debuggertron 3002', direction = 'vertical'}
|
||||
local frame_style = frame.style
|
||||
frame_style.height = 600
|
||||
frame_style.width = 900
|
||||
|
||||
local tab_flow = frame.add {type = 'flow', direction = 'horizontal'}
|
||||
local container = frame.add {type = 'flow'}
|
||||
container.style.vertically_stretchable = true
|
||||
|
||||
local data = {}
|
||||
|
||||
for i = 1, #pages do
|
||||
local page = pages[i]
|
||||
local tab_button = tab_flow.add({type = 'flow'}).add {type = 'button', name = tab_name, caption = page.name}
|
||||
local tab_button_style = tab_button.style
|
||||
|
||||
Gui.set_data(tab_button, {index = i, frame_data = data})
|
||||
|
||||
if i == 1 then
|
||||
tab_button_style.font_color = Color.orange
|
||||
|
||||
data.selected_index = i
|
||||
data.selected_tab_button = tab_button
|
||||
data.container = container
|
||||
|
||||
Gui.set_data(frame, data)
|
||||
page.show(container)
|
||||
end
|
||||
end
|
||||
|
||||
frame.add {type = 'button', name = close_name, caption = 'Close'}
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
tab_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local index = data.index
|
||||
local frame_data = data.frame_data
|
||||
local selected_index = frame_data.selected_index
|
||||
|
||||
if selected_index == index then
|
||||
return
|
||||
end
|
||||
|
||||
local selected_tab_button = frame_data.selected_tab_button
|
||||
selected_tab_button.style.font_color = Color.black
|
||||
|
||||
frame_data.selected_tab_button = element
|
||||
frame_data.selected_index = index
|
||||
element.style.font_color = Color.orange
|
||||
|
||||
local container = frame_data.container
|
||||
Gui.clear(container)
|
||||
pages[index].show(container)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
close_name,
|
||||
function(event)
|
||||
local frame = event.player.gui.center[main_frame_name]
|
||||
if frame then
|
||||
Gui.destroy(frame)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
147
exp_legacy/module/modules/gui/debug/model.lua
Normal file
147
exp_legacy/module/modules/gui/debug/model.lua
Normal file
@@ -0,0 +1,147 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local table = require 'overrides.table' --- @dep overrides.table
|
||||
|
||||
local gui_names = Gui.names
|
||||
local type = type
|
||||
local concat = table.concat
|
||||
local inspect = table.inspect
|
||||
local pcall = pcall
|
||||
local loadstring = loadstring ---@diagnostic disable-line
|
||||
local rawset = rawset
|
||||
|
||||
local Public = {}
|
||||
|
||||
local luaObject = {'{', nil, ", name = '", nil, "'}"}
|
||||
local luaPlayer = {"{LuaPlayer, name = '", nil, "', index = ", nil, '}'}
|
||||
local luaEntity = {"{LuaEntity, name = '", nil, "', unit_number = ", nil, '}'}
|
||||
local luaGuiElement = {"{LuaGuiElement, name = '", nil, "'}"}
|
||||
|
||||
local function get(obj, prop)
|
||||
return obj[prop]
|
||||
end
|
||||
|
||||
local function get_name_safe(obj)
|
||||
local s, r = pcall(get, obj, 'name')
|
||||
if not s then
|
||||
return 'nil'
|
||||
else
|
||||
return r or 'nil'
|
||||
end
|
||||
end
|
||||
|
||||
local function get_lua_object_type_safe(obj)
|
||||
local s, r = pcall(get, obj, 'help')
|
||||
|
||||
if not s then
|
||||
return
|
||||
end
|
||||
|
||||
return r():match('Lua%a+')
|
||||
end
|
||||
|
||||
local function inspect_process(item)
|
||||
if type(item) ~= 'table' or type(item.__self) ~= 'userdata' then
|
||||
return item
|
||||
end
|
||||
|
||||
local suc, valid = pcall(get, item, 'valid')
|
||||
if not suc then
|
||||
-- no 'valid' property
|
||||
return get_lua_object_type_safe(item) or '{NoHelp LuaObject}'
|
||||
end
|
||||
|
||||
if not valid then
|
||||
return '{Invalid LuaObject}'
|
||||
end
|
||||
|
||||
local obj_type = get_lua_object_type_safe(item)
|
||||
if not obj_type then
|
||||
return '{NoHelp LuaObject}'
|
||||
end
|
||||
|
||||
if obj_type == 'LuaPlayer' then
|
||||
luaPlayer[2] = item.name or 'nil'
|
||||
luaPlayer[4] = item.index or 'nil'
|
||||
|
||||
return concat(luaPlayer)
|
||||
elseif obj_type == 'LuaEntity' then
|
||||
luaEntity[2] = item.name or 'nil'
|
||||
luaEntity[4] = item.unit_number or 'nil'
|
||||
|
||||
return concat(luaEntity)
|
||||
elseif obj_type == 'LuaGuiElement' then
|
||||
local name = item.name
|
||||
luaGuiElement[2] = gui_names and gui_names[name] or name or 'nil'
|
||||
|
||||
return concat(luaGuiElement)
|
||||
else
|
||||
luaObject[2] = obj_type
|
||||
luaObject[4] = get_name_safe(item)
|
||||
|
||||
return concat(luaObject)
|
||||
end
|
||||
end
|
||||
|
||||
local inspect_options = {process = inspect_process}
|
||||
function Public.dump(data)
|
||||
return inspect(data, inspect_options)
|
||||
end
|
||||
local dump = Public.dump
|
||||
|
||||
function Public.dump_ignore_builder(ignore)
|
||||
local function process(item)
|
||||
if ignore[item] then
|
||||
return nil
|
||||
end
|
||||
|
||||
return inspect_process(item)
|
||||
end
|
||||
|
||||
local options = {process = process}
|
||||
return function(data)
|
||||
return inspect(data, options)
|
||||
end
|
||||
end
|
||||
|
||||
function Public.dump_function(func)
|
||||
local res = {'upvalues:\n'}
|
||||
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(func, i)
|
||||
|
||||
if n == nil then
|
||||
break
|
||||
elseif n ~= '_ENV' then
|
||||
res[#res + 1] = n
|
||||
res[#res + 1] = ' = '
|
||||
res[#res + 1] = dump(v)
|
||||
res[#res + 1] = '\n'
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return concat(res)
|
||||
end
|
||||
|
||||
function Public.dump_text(text, player)
|
||||
local func = loadstring('return ' .. text)
|
||||
if not func then
|
||||
return false
|
||||
end
|
||||
|
||||
rawset(game, 'player', player)
|
||||
|
||||
local suc, var = pcall(func)
|
||||
|
||||
rawset(game, 'player', nil)
|
||||
|
||||
if not suc then
|
||||
return false
|
||||
end
|
||||
|
||||
return true, dump(var)
|
||||
end
|
||||
|
||||
return Public
|
||||
161
exp_legacy/module/modules/gui/debug/package_view.lua
Normal file
161
exp_legacy/module/modules/gui/debug/package_view.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump_function = Model.dump_function
|
||||
local loaded = _G.package.loaded
|
||||
|
||||
local Public = {}
|
||||
|
||||
local ignore = {
|
||||
_G = true,
|
||||
package = true,
|
||||
coroutine = true,
|
||||
table = true,
|
||||
string = true,
|
||||
bit32 = true,
|
||||
math = true,
|
||||
debug = true,
|
||||
serpent = true,
|
||||
['overrides.math'] = true,
|
||||
util = true,
|
||||
['overrides.inspect'] = true,
|
||||
['mod-gui'] = true
|
||||
}
|
||||
|
||||
local file_label_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local breadcrumbs_name = Gui.uid_name()
|
||||
local top_panel_name = Gui.uid_name()
|
||||
local variable_label_name = Gui.uid_name()
|
||||
local text_box_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'package'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for name, file in pairs(loaded) do
|
||||
if not ignore[name] then
|
||||
local file_label =
|
||||
left_panel.add({type = 'flow'}).add {type = 'label', name = file_label_name, caption = name}
|
||||
Gui.set_data(file_label, file)
|
||||
end
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local breadcrumbs = right_flow.add {type = 'label', name = breadcrumbs_name}
|
||||
|
||||
local top_panel = right_flow.add {type = 'scroll-pane', name = top_panel_name}
|
||||
local top_panel_style = top_panel.style
|
||||
top_panel_style.height = 200
|
||||
top_panel_style.maximal_width = 1000
|
||||
top_panel_style.horizontally_stretchable = true
|
||||
|
||||
local text_box = right_flow.add {type = 'text-box', name = text_box_name}
|
||||
text_box.read_only = true
|
||||
text_box.selectable = true
|
||||
|
||||
local text_box_style = text_box.style
|
||||
text_box_style.vertically_stretchable = true
|
||||
text_box_style.horizontally_stretchable = true
|
||||
text_box_style.maximal_width = 1000
|
||||
text_box_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
left_panel = left_panel,
|
||||
breadcrumbs = breadcrumbs,
|
||||
top_panel = top_panel,
|
||||
text_box = text_box,
|
||||
selected_file_label = nil,
|
||||
selected_variable_label = nil
|
||||
}
|
||||
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(top_panel, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
file_label_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local file = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
|
||||
local selected_file_label = data.selected_file_label
|
||||
|
||||
if selected_file_label then
|
||||
selected_file_label.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_file_label = element
|
||||
|
||||
local top_panel = data.top_panel
|
||||
local text_box = data.text_box
|
||||
|
||||
Gui.clear(top_panel)
|
||||
|
||||
local file_type = type(file)
|
||||
|
||||
if file_type == 'table' then
|
||||
for k, v in pairs(file) do
|
||||
local label =
|
||||
top_panel.add({type = 'flow'}).add {type = 'label', name = variable_label_name, caption = k}
|
||||
Gui.set_data(label, v)
|
||||
end
|
||||
elseif file_type == 'function' then
|
||||
text_box.text = dump_function(file)
|
||||
else
|
||||
text_box.text = tostring(file)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
variable_label_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local variable = Gui.get_data(element)
|
||||
|
||||
local top_panel = element.parent.parent
|
||||
local data = Gui.get_data(top_panel)
|
||||
local text_box = data.text_box
|
||||
|
||||
local variable_type = type(variable)
|
||||
|
||||
if variable_type == 'table' then
|
||||
Gui.clear(top_panel)
|
||||
for k, v in pairs(variable) do
|
||||
local label =
|
||||
top_panel.add({type = 'flow'}).add {type = 'label', name = variable_label_name, caption = k}
|
||||
Gui.set_data(label, v)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local selected_label = data.selected_variable_label
|
||||
|
||||
if selected_label and selected_label.valid then
|
||||
selected_label.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_variable_label = element
|
||||
|
||||
if variable_type == 'function' then
|
||||
text_box.text = dump_function(variable)
|
||||
else
|
||||
text_box.text = tostring(variable)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
129
exp_legacy/module/modules/gui/debug/redmew_global_view.lua
Normal file
129
exp_legacy/module/modules/gui/debug/redmew_global_view.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Token = require 'utils.token' --- @dep utils.token
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump = Model.dump
|
||||
local dump_text = Model.dump_text
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'Global'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for token_id, token_name in pairs(Global.names) do
|
||||
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = token_name}
|
||||
Gui.set_data(header, token_id)
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local token_id = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = concat {'global.tokens[', token_id, ']'}
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = dump(Token.get_global(token_id)) or 'nil'
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data, player)
|
||||
local suc, ouput = dump_text(text_input.text, player)
|
||||
if not suc then
|
||||
text_input.style.font_color = Color.red
|
||||
else
|
||||
text_input.style.font_color = Color.black
|
||||
data.right_panel.text = ouput
|
||||
end
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
185
exp_legacy/module/modules/gui/landfill.lua
Normal file
185
exp_legacy/module/modules/gui/landfill.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
--[[-- Gui Module - Landfill
|
||||
- Landfill blueprint
|
||||
@gui Landfill
|
||||
@alias landfill_container
|
||||
]]
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
|
||||
local rolling_stocks = {}
|
||||
|
||||
local function landfill_init()
|
||||
for name, _ in pairs(game.get_filtered_entity_prototypes({{filter = 'rolling-stock'}})) do
|
||||
rolling_stocks[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function rotate_bounding_box(box)
|
||||
return {
|
||||
left_top = {
|
||||
x = -box.right_bottom.y,
|
||||
y = box.left_top.x
|
||||
},
|
||||
right_bottom = {
|
||||
x = -box.left_top.y,
|
||||
y = box.right_bottom.x
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function curve_flip_lr(oc)
|
||||
local nc = table.deepcopy(oc)
|
||||
|
||||
for r=1, 8 do
|
||||
for c=1, 8 do
|
||||
nc[r][c] = oc[r][9 - c]
|
||||
end
|
||||
end
|
||||
|
||||
return nc
|
||||
end
|
||||
|
||||
local function curve_flip_d(oc)
|
||||
local nc = table.deepcopy(oc)
|
||||
|
||||
for r=1, 8 do
|
||||
for c=1, 8 do
|
||||
nc[r][c] = oc[c][r]
|
||||
end
|
||||
end
|
||||
|
||||
return nc
|
||||
end
|
||||
|
||||
local curves = {}
|
||||
|
||||
curves[1] = {
|
||||
{0, 0, 0, 0, 0, 1, 0, 0},
|
||||
{0, 0, 0, 0, 1, 1, 1, 0},
|
||||
{0, 0, 0, 1, 1, 1, 1, 0},
|
||||
{0, 0, 0, 1, 1, 1, 0, 0},
|
||||
{0, 0, 1, 1, 1, 0, 0, 0},
|
||||
{0, 0, 1, 1, 1, 0, 0, 0},
|
||||
{0, 0, 1, 1, 0, 0, 0, 0},
|
||||
{0, 0, 1, 1, 0, 0, 0, 0}
|
||||
}
|
||||
curves[6] = curve_flip_d(curves[1])
|
||||
curves[3] = curve_flip_lr(curves[6])
|
||||
curves[4] = curve_flip_d(curves[3])
|
||||
curves[5] = curve_flip_lr(curves[4])
|
||||
curves[2] = curve_flip_d(curves[5])
|
||||
curves[7] = curve_flip_lr(curves[2])
|
||||
curves[8] = curve_flip_d(curves[7])
|
||||
|
||||
local curve_n = {}
|
||||
|
||||
for i, map in ipairs(curves) do
|
||||
curve_n[i] = {}
|
||||
local index = 1
|
||||
|
||||
for r=1, 8 do
|
||||
for c=1, 8 do
|
||||
if map[r][c] == 1 then
|
||||
curve_n[i][index] = {
|
||||
['x'] = c - 5,
|
||||
['y'] = r - 5
|
||||
}
|
||||
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function landfill_gui_add_landfill(blueprint)
|
||||
local entities = blueprint.get_blueprint_entities()
|
||||
local tile_index = 0
|
||||
local new_tiles = {}
|
||||
|
||||
for _, ent in pairs(entities) do
|
||||
-- vehicle
|
||||
if not (rolling_stocks[ent.name] or ent.name == 'offshore-pump') then
|
||||
-- curved rail, special
|
||||
if ent.name ~= 'curved-rail' then
|
||||
local box = game.entity_prototypes[ent.name].collision_box or game.entity_prototypes[ent.name].selection_box
|
||||
|
||||
if game.entity_prototypes[ent.name].collision_mask['ground-tile'] == nil then
|
||||
if ent.direction then
|
||||
if ent.direction ~= defines.direction.north then
|
||||
box = rotate_bounding_box(box)
|
||||
|
||||
if ent.direction ~= defines.direction.east then
|
||||
box = rotate_bounding_box(box)
|
||||
|
||||
if ent.direction ~= defines.direction.south then
|
||||
box = rotate_bounding_box(box)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for y = math.floor(ent.position.y + box.left_top.y), math.floor(ent.position.y + box.right_bottom.y), 1 do
|
||||
for x = math.floor(ent.position.x + box.left_top.x), math.floor(ent.position.x + box.right_bottom.x), 1 do
|
||||
tile_index = tile_index + 1
|
||||
new_tiles[tile_index] = {
|
||||
name = 'landfill',
|
||||
position = {x, y}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- curved rail
|
||||
else
|
||||
local curve_mask = curve_n[ent.direction or 8]
|
||||
|
||||
for m=1, #curve_mask do
|
||||
new_tiles[tile_index + 1] = {
|
||||
name = 'landfill',
|
||||
position = {curve_mask[m].x + ent.position.x, curve_mask[m].y + ent.position.y}
|
||||
}
|
||||
|
||||
tile_index = tile_index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local old_tiles = blueprint.get_blueprint_tiles()
|
||||
|
||||
if old_tiles then
|
||||
for _, old_tile in pairs(old_tiles) do
|
||||
new_tiles[tile_index + 1] = {
|
||||
name = 'landfill',
|
||||
position = {old_tile.position.x, old_tile.position.y}
|
||||
}
|
||||
|
||||
tile_index = tile_index + 1
|
||||
end
|
||||
end
|
||||
|
||||
return {tiles = new_tiles}
|
||||
end
|
||||
|
||||
-- @element toolbar_button
|
||||
Gui.toolbar_button('item/landfill', {'landfill.main-tooltip'}, function(player)
|
||||
return Roles.player_allowed(player, 'gui/landfill')
|
||||
end)
|
||||
:on_click(function(player, _, _)
|
||||
if player.cursor_stack and player.cursor_stack.valid_for_read then
|
||||
if player.cursor_stack.type == 'blueprint' and player.cursor_stack.is_blueprint_setup() then
|
||||
local modified = landfill_gui_add_landfill(player.cursor_stack)
|
||||
|
||||
if modified and next(modified.tiles) then
|
||||
player.cursor_stack.set_blueprint_tiles(modified.tiles)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
player.print{'landfill.cursor-none'}
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, landfill_init)
|
||||
BIN
exp_legacy/module/modules/gui/logo.png
Normal file
BIN
exp_legacy/module/modules/gui/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
315
exp_legacy/module/modules/gui/module.lua
Normal file
315
exp_legacy/module/modules/gui/module.lua
Normal file
@@ -0,0 +1,315 @@
|
||||
---- module inserter
|
||||
-- @gui Module
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local config = require 'config.module' --- @dep config.module
|
||||
local Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
local SelectionModuleArea = 'ModuleArea'
|
||||
|
||||
--- align an aabb to the grid by expanding it
|
||||
local function aabb_align_expand(aabb)
|
||||
return {
|
||||
left_top = {
|
||||
x = math.floor(aabb.left_top.x),
|
||||
y = math.floor(aabb.left_top.y)
|
||||
},
|
||||
right_bottom = {
|
||||
x = math.ceil(aabb.right_bottom.x),
|
||||
y = math.ceil(aabb.right_bottom.y)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local module_container
|
||||
local machine_name = {}
|
||||
|
||||
for k, _ in pairs(config.machine) do
|
||||
table.insert(machine_name, k)
|
||||
end
|
||||
|
||||
local prod_module_names = {}
|
||||
|
||||
local function get_module_name()
|
||||
for name, item in pairs(game.item_prototypes) do
|
||||
if item.module_effects and item.module_effects.productivity and item.module_effects.productivity.bonus > 0 then
|
||||
prod_module_names[#prod_module_names + 1] = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local elem_filter = {
|
||||
name = {{
|
||||
filter = 'name',
|
||||
name = machine_name
|
||||
}},
|
||||
normal = {{
|
||||
filter = 'type',
|
||||
type = 'module'
|
||||
}, {
|
||||
filter = 'name',
|
||||
name = prod_module_names,
|
||||
mode = 'and',
|
||||
invert = true
|
||||
}},
|
||||
prod = {{
|
||||
filter = 'type',
|
||||
type = 'module'
|
||||
}}
|
||||
}
|
||||
|
||||
local function clear_module(player, area, machine)
|
||||
for _, entity in pairs(player.surface.find_entities_filtered{area=area, name=machine, force=player.force}) do
|
||||
for _, r in pairs(player.surface.find_entities_filtered{position=entity.position, name='item-request-proxy', force=player.force}) do
|
||||
if r then
|
||||
r.destroy{raise_destroy=true}
|
||||
end
|
||||
end
|
||||
|
||||
local m_current_module = entity.get_module_inventory()
|
||||
|
||||
if m_current_module then
|
||||
local m_current_module_content = m_current_module.get_contents()
|
||||
|
||||
if m_current_module_content then
|
||||
for k, m in pairs(m_current_module_content) do
|
||||
player.surface.spill_item_stack(entity.bounding_box.left_top, {name=k, count=m}, true, player.force, false)
|
||||
end
|
||||
end
|
||||
|
||||
m_current_module.clear()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_module(player, area, machine, modules)
|
||||
for _, entity in pairs(player.surface.find_entities_filtered{area=area, name=machine, force=player.force}) do
|
||||
local m_current_recipe
|
||||
|
||||
if entity.prototype.crafting_speed then
|
||||
m_current_recipe= entity.get_recipe()
|
||||
end
|
||||
|
||||
if m_current_recipe then
|
||||
if config.module_allowed[m_current_recipe.name] then
|
||||
entity.surface.create_entity{name='item-request-proxy', target=entity, position=entity.position, force=entity.force, modules=modules['n']}
|
||||
entity.last_user = player
|
||||
|
||||
else
|
||||
entity.surface.create_entity{name='item-request-proxy', target=entity, position=entity.position, force=entity.force, modules=modules['p']}
|
||||
entity.last_user = player
|
||||
end
|
||||
|
||||
else
|
||||
entity.surface.create_entity{name='item-request-proxy', target=entity, position=entity.position, force=entity.force, modules=modules['n']}
|
||||
entity.last_user = player
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- when an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionModuleArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local player = game.get_player(event.player_index)
|
||||
local frame = Gui.get_left_element(player, module_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
|
||||
for i=1, config.default_module_row_count do
|
||||
local mma = scroll_table['module_mm_' .. i .. '_0'].elem_value
|
||||
|
||||
if mma then
|
||||
local mm = {
|
||||
['n'] = {},
|
||||
['p'] = {}
|
||||
}
|
||||
|
||||
for j=1, game.entity_prototypes[mma].module_inventory_size, 1 do
|
||||
local mmo = scroll_table['module_mm_' .. i .. '_' .. j].elem_value
|
||||
|
||||
if mmo then
|
||||
if mm['n'][mmo] then
|
||||
mm['n'][mmo] = mm['n'][mmo] + 1
|
||||
mm['p'][mmo] = mm['p'][mmo] + 1
|
||||
|
||||
else
|
||||
mm['n'][mmo] = 1
|
||||
mm['p'][mmo] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(mm['p']) do
|
||||
if k:find('productivity') then
|
||||
local module_name = k:gsub('productivity', 'effectivity')
|
||||
mm['p'][module_name] = (mm['p'][module_name] or 0) + v
|
||||
mm['p'][k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if mm then
|
||||
clear_module(player, area, mma)
|
||||
apply_module(player, area, mma, mm)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function row_set(player, element)
|
||||
local frame = Gui.get_left_element(player, module_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
|
||||
if scroll_table[element .. '0'].elem_value then
|
||||
for i=1, config.module_slot_max do
|
||||
if i <= game.entity_prototypes[scroll_table[element .. '0'].elem_value].module_inventory_size then
|
||||
if config.machine[scroll_table[element .. '0'].elem_value].prod then
|
||||
scroll_table[element .. i].elem_filters = elem_filter.prod
|
||||
|
||||
else
|
||||
scroll_table[element .. i].elem_filters = elem_filter.normal
|
||||
end
|
||||
|
||||
scroll_table[element .. i].enabled = true
|
||||
scroll_table[element .. i].elem_value = config.machine[scroll_table[element .. '0'].elem_value].module
|
||||
|
||||
else
|
||||
scroll_table[element .. i].enabled = false
|
||||
scroll_table[element .. i].elem_value = nil
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
local mf = elem_filter.normal
|
||||
|
||||
for i=1, config.module_slot_max do
|
||||
scroll_table[element .. i].enabled = false
|
||||
scroll_table[element .. i].elem_filters = mf
|
||||
scroll_table[element .. i].elem_value = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local button_apply =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
caption = 'Apply',
|
||||
style = 'button'
|
||||
}:on_click(function(player)
|
||||
if Selection.is_selecting(player, SelectionModuleArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionModuleArea)
|
||||
end
|
||||
end)
|
||||
|
||||
module_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, (config.module_slot_max + 2) * 36)
|
||||
Gui.header(container, 'Module Inserter', '', true)
|
||||
|
||||
local scroll_table = Gui.scroll_table(container, (config.module_slot_max + 2) * 36, config.module_slot_max + 1)
|
||||
|
||||
for i=1, config.default_module_row_count do
|
||||
scroll_table.add{
|
||||
name = 'module_mm_' .. i .. '_0',
|
||||
type = 'choose-elem-button',
|
||||
elem_type = 'entity',
|
||||
elem_filters = elem_filter.name,
|
||||
style = 'slot_button'
|
||||
}
|
||||
|
||||
for j=1, config.module_slot_max do
|
||||
scroll_table.add{
|
||||
name = 'module_mm_' .. i .. '_' .. j,
|
||||
type = 'choose-elem-button',
|
||||
elem_type = 'item',
|
||||
elem_filters = elem_filter.normal,
|
||||
style = 'slot_button',
|
||||
enabled = false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
button_apply(container)
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('item/productivity-module-3', {'module.main-tooltip'}, module_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/module')
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_gui_elem_changed, function(event)
|
||||
if event.element.name:sub(1, 10) == 'module_mm_' then
|
||||
if event.element.name:sub(-1) == '0' then
|
||||
row_set(game.players[event.player_index], 'module_mm_' .. event.element.name:sub(-3):sub(1, 1) .. '_')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, get_module_name)
|
||||
|
||||
Event.add(defines.events.on_entity_settings_pasted, function(event)
|
||||
local source = event.source
|
||||
local destination = event.destination
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
if not source or not source.valid then
|
||||
return
|
||||
end
|
||||
|
||||
if not destination or not destination.valid then
|
||||
return
|
||||
end
|
||||
|
||||
-- rotate machine also
|
||||
if config.copy_paste_rotation then
|
||||
if (source.name == destination.name or source.prototype.fast_replaceable_group == destination.prototype.fast_replaceable_group) then
|
||||
if source.supports_direction and destination.supports_direction and source.type ~= 'transport-belt' then
|
||||
local destination_box = destination.bounding_box
|
||||
|
||||
local ltx = destination_box.left_top.x
|
||||
local lty = destination_box.left_top.y
|
||||
local rbx = destination_box.right_bottom.x
|
||||
local rby = destination_box.right_bottom.y
|
||||
|
||||
local old_direction = destination.direction
|
||||
destination.direction = source.direction
|
||||
|
||||
if ltx ~= destination_box.left_top.x or lty ~= destination_box.left_top.y or rbx ~= destination_box.right_bottom.x or rby ~= destination_box.right_bottom.y then
|
||||
destination.direction = old_direction
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if config.copy_paste_module then
|
||||
if source.name ~= destination.name then
|
||||
return
|
||||
end
|
||||
|
||||
local source_inventory = source.get_module_inventory()
|
||||
|
||||
if not source_inventory then
|
||||
return
|
||||
end
|
||||
|
||||
local source_inventory_content = source_inventory.get_contents()
|
||||
|
||||
if not source_inventory_content then
|
||||
return
|
||||
end
|
||||
|
||||
clear_module(player, destination.bounding_box, destination.name)
|
||||
|
||||
if next(source_inventory_content) ~= nil then
|
||||
apply_module(player, destination.bounding_box, destination.name, {['n']=source_inventory_content, ['p']=source_inventory_content})
|
||||
end
|
||||
end
|
||||
end)
|
||||
434
exp_legacy/module/modules/gui/player-list.lua
Normal file
434
exp_legacy/module/modules/gui/player-list.lua
Normal file
@@ -0,0 +1,434 @@
|
||||
--[[-- Gui Module - Player List
|
||||
- Adds a player list to show names and play time; also includes action buttons which can preform actions to players
|
||||
@gui Player-List
|
||||
@alias player_list
|
||||
]]
|
||||
|
||||
-- luacheck:ignore 211/Colors
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.gui.player_list_actions' --- @dep config.gui.player_list_actions
|
||||
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
|
||||
--- Stores all data for the warp gui
|
||||
local PlayerListData = Datastore.connect('PlayerListData')
|
||||
PlayerListData:set_serializer(Datastore.name_serializer)
|
||||
local SelectedPlayer = PlayerListData:combine('SelectedPlayer')
|
||||
local SelectedAction = PlayerListData:combine('SelectedAction')
|
||||
|
||||
-- Set the config to use these stores
|
||||
config.set_datastores(SelectedPlayer, SelectedAction)
|
||||
|
||||
--- Button used to open the action bar
|
||||
-- @element open_action_bar
|
||||
local open_action_bar =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/expand_dots_white',
|
||||
tooltip = {'player-list.open-action-bar'},
|
||||
style = 'frame_button',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style{
|
||||
padding = -2,
|
||||
width = 8,
|
||||
height = 14
|
||||
}
|
||||
:on_click(function(player, element, _)
|
||||
local selected_player_name = element.parent.name
|
||||
local old_selected_player_name = SelectedPlayer:get(player)
|
||||
if selected_player_name == old_selected_player_name then
|
||||
SelectedPlayer:remove(player)
|
||||
else
|
||||
SelectedPlayer:set(player, selected_player_name)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Button used to close the action bar
|
||||
-- @element close_action_bar
|
||||
local close_action_bar =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/close_black',
|
||||
tooltip = {'player-list.close-action-bar'},
|
||||
style = 'slot_sized_button_red'
|
||||
}
|
||||
:style(Gui.sprite_style(30, -1, { top_margin = -1, right_margin = -1 }))
|
||||
:on_click(function(player, _)
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
end)
|
||||
|
||||
--- Button used to confirm a reason
|
||||
-- @element reason_confirm
|
||||
local reason_confirm =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/confirm_slot',
|
||||
tooltip = {'player-list.reason-confirm'},
|
||||
style = 'slot_sized_button_green'
|
||||
}
|
||||
:style(Gui.sprite_style(30, -1, { left_margin = -2, right_margin = -1 }))
|
||||
:on_click(function(player, element)
|
||||
local reason = element.parent.entry.text
|
||||
local action_name = SelectedAction:get(player)
|
||||
local reason_callback = config.buttons[action_name].reason_callback
|
||||
if reason == nil or not reason:find("%S") then reason = 'no reason given' end
|
||||
reason_callback(player, reason)
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
element.parent.entry.text = ''
|
||||
end)
|
||||
|
||||
--- Set of elements that are used to make up a row of the player table
|
||||
-- @element add_player_base
|
||||
local add_player_base =
|
||||
Gui.element(function(_, parent, player_data)
|
||||
-- Add the button to open the action bar
|
||||
local toggle_action_bar_flow = parent.add{ type = 'flow', name = player_data.name }
|
||||
open_action_bar(toggle_action_bar_flow)
|
||||
|
||||
-- Add the player name
|
||||
local player_name = parent.add{
|
||||
type = 'label',
|
||||
name = 'player-name-'..player_data.index,
|
||||
caption = player_data.name,
|
||||
tooltip = {'player-list.open-map', player_data.name, player_data.tag, player_data.role_name}
|
||||
}
|
||||
player_name.style.padding = {0, 2,0, 0}
|
||||
player_name.style.font_color = player_data.chat_color
|
||||
|
||||
-- Add the time played label
|
||||
local alignment = Gui.alignment(parent, 'player-time-'..player_data.index)
|
||||
local time_label = alignment.add{
|
||||
name = 'label',
|
||||
type = 'label',
|
||||
caption = player_data.caption,
|
||||
tooltip = player_data.tooltip
|
||||
}
|
||||
time_label.style.padding = 0
|
||||
|
||||
return player_name
|
||||
end)
|
||||
:on_click(function(player, element, event)
|
||||
local selected_player_name = element.caption
|
||||
local selected_player = game.players[selected_player_name]
|
||||
if event.button == defines.mouse_button_type.left then
|
||||
-- LMB will open the map to the selected player
|
||||
local position = selected_player.position
|
||||
event.player.zoom_to_world(position, 1.75)
|
||||
else
|
||||
-- RMB will toggle the settings
|
||||
local old_selected_player_name = SelectedPlayer:get(player)
|
||||
if selected_player_name == old_selected_player_name then
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
else
|
||||
SelectedPlayer:set(player, selected_player_name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Removes the three elements that are added as part of the base
|
||||
local function remove_player_base(parent, player)
|
||||
Gui.destroy_if_valid(parent[player.name])
|
||||
Gui.destroy_if_valid(parent['player-name-'..player.index])
|
||||
Gui.destroy_if_valid(parent['player-time-'..player.index])
|
||||
end
|
||||
|
||||
-- Update the time label for a player using there player time data
|
||||
local function update_player_base(parent, player_time)
|
||||
local time_element = parent[player_time.element_name]
|
||||
if time_element and time_element.valid then
|
||||
time_element.label.caption = player_time.caption
|
||||
time_element.label.tooltip = player_time.tooltip
|
||||
end
|
||||
end
|
||||
|
||||
--- Adds all the buttons and flows that make up the action bar
|
||||
-- @element add_action_bar
|
||||
local add_action_bar_buttons =
|
||||
Gui.element(function(_, parent)
|
||||
close_action_bar(parent)
|
||||
-- Loop over all the buttons in the config
|
||||
for action_name, button_data in pairs(config.buttons) do
|
||||
-- Added the permission flow
|
||||
local permission_flow = parent.add{ type = 'flow', name = action_name }
|
||||
permission_flow.visible = false
|
||||
-- Add the buttons under that permission
|
||||
for _, button in ipairs(button_data) do
|
||||
button(permission_flow)
|
||||
end
|
||||
end
|
||||
|
||||
return parent
|
||||
end)
|
||||
|
||||
--- Updates the visible state of the action bar buttons
|
||||
local function update_action_bar(element)
|
||||
local player = Gui.get_player_from_element(element)
|
||||
local selected_player_name = SelectedPlayer:get(player)
|
||||
|
||||
if not selected_player_name then
|
||||
-- Hide the action bar when no player is selected
|
||||
element.visible = false
|
||||
|
||||
else
|
||||
local selected_player = game.players[selected_player_name]
|
||||
if not selected_player.connected then
|
||||
-- If the player is offline then reest stores
|
||||
element.visible = false
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
|
||||
else
|
||||
-- Otherwise check what actions the player is allowed to use
|
||||
element.visible = true
|
||||
for action_name, buttons in pairs(config.buttons) do
|
||||
if buttons.auth and not buttons.auth(player, selected_player) then
|
||||
element[action_name].visible = false
|
||||
elseif Roles.player_allowed(player, action_name) then
|
||||
element[action_name].visible = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Main player list container for the left flow
|
||||
-- @element player_list_container
|
||||
local player_list_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 200)
|
||||
|
||||
-- Draw the scroll table for the players
|
||||
local scroll_table = Gui.scroll_table(container, 184, 3)
|
||||
|
||||
-- Change the style of the scroll table
|
||||
local scroll_table_style = scroll_table.style
|
||||
scroll_table_style.padding = {1, 0,1, 2}
|
||||
|
||||
-- Add the action bar
|
||||
local action_bar = Gui.footer(container, nil, nil, false, 'action_bar')
|
||||
|
||||
-- Change the style of the action bar
|
||||
local action_bar_style = action_bar.style
|
||||
action_bar_style.height = 35
|
||||
action_bar_style.padding = {1, 3}
|
||||
action_bar.visible = false
|
||||
|
||||
-- Add the buttons to the action bar
|
||||
add_action_bar_buttons(action_bar)
|
||||
|
||||
-- Add the reason bar
|
||||
local reason_bar = Gui.footer(container, nil, nil, false, 'reason_bar')
|
||||
|
||||
-- Change the style of the reason bar
|
||||
local reason_bar_style = reason_bar.style
|
||||
reason_bar_style.height = 35
|
||||
reason_bar_style.padding = {-1, 3}
|
||||
reason_bar.visible = false
|
||||
|
||||
-- Add the text entry for the reason bar
|
||||
local reason_field =
|
||||
reason_bar.add{
|
||||
name = 'entry',
|
||||
type = 'textfield',
|
||||
style = 'stretchable_textfield',
|
||||
tooltip = {'player-list.reason-entry'}
|
||||
}
|
||||
|
||||
-- Change the style of the text entry
|
||||
local reason_entry_style = reason_field.style
|
||||
reason_entry_style.padding = 0
|
||||
reason_entry_style.height = 28
|
||||
reason_entry_style.minimal_width = 160
|
||||
|
||||
-- Add the confirm reason button
|
||||
reason_confirm(reason_bar)
|
||||
|
||||
-- Return the exteral container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow(true)
|
||||
|
||||
--- Button on the top flow used to toggle the player list container
|
||||
-- @element toggle_player_list
|
||||
Gui.left_toolbar_button('entity/character', {'player-list.main-tooltip'}, player_list_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/player-list')
|
||||
end)
|
||||
|
||||
-- Get caption and tooltip format for a player
|
||||
local function get_time_formats(online_time, afk_time)
|
||||
local tick = game.tick > 0 and game.tick or 1
|
||||
local percent = math.round(online_time/tick, 3)*100
|
||||
local caption = format_time(online_time)
|
||||
local tooltip = {'player-list.afk-time', percent, format_time(afk_time, {minutes=true, long=true})}
|
||||
return caption, tooltip
|
||||
end
|
||||
|
||||
-- Get the player time to be used to update time label
|
||||
local function get_player_times()
|
||||
local ctn = 0
|
||||
local player_times = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
ctn = ctn + 1
|
||||
-- Add the player time details to the array
|
||||
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
|
||||
player_times[ctn] = {
|
||||
element_name = 'player-time-'..player.index,
|
||||
caption = caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
end
|
||||
|
||||
return player_times
|
||||
end
|
||||
|
||||
-- Get a sorted list of all online players
|
||||
local function get_player_list_order()
|
||||
-- Sort all the online players into roles
|
||||
local players = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local highest_role = Roles.get_player_highest_role(player)
|
||||
if not players[highest_role.name] then
|
||||
players[highest_role.name] = {}
|
||||
end
|
||||
table.insert(players[highest_role.name], player)
|
||||
end
|
||||
|
||||
-- Sort the players from roles into a set order
|
||||
local ctn = 0
|
||||
local player_list_order = {}
|
||||
for _, role_name in pairs(Roles.config.order) do
|
||||
if players[role_name] then
|
||||
for _, player in pairs(players[role_name]) do
|
||||
ctn = ctn + 1
|
||||
-- Add the player data to the array
|
||||
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
|
||||
player_list_order[ctn] = {
|
||||
name = player.name,
|
||||
index = player.index,
|
||||
tag = player.tag,
|
||||
role_name = role_name,
|
||||
chat_color = player.chat_color,
|
||||
caption = caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[Adds fake players to the player list
|
||||
local tick = game.tick+1
|
||||
for i = 1, 10 do
|
||||
local online_time = math.random(1, tick)
|
||||
local afk_time = math.random(online_time-(tick/10), tick)
|
||||
local caption, tooltip = get_time_formats(online_time, afk_time)
|
||||
player_list_order[ctn+i] = {
|
||||
name='Player '..i,
|
||||
index=0-i,
|
||||
tag='',
|
||||
role_name = 'Fake Player',
|
||||
chat_color = table.get_random_dictionary_entry(Colors),
|
||||
caption = caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
end--]]
|
||||
|
||||
return player_list_order
|
||||
end
|
||||
|
||||
--- Update the play times every 30 sections
|
||||
Event.on_nth_tick(1800, function()
|
||||
local player_times = get_player_times()
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
for _, player_time in pairs(player_times) do
|
||||
update_player_base(scroll_table, player_time)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- When a player leaves only remove they entry
|
||||
Event.add(defines.events.on_player_left_game, function(event)
|
||||
local remove_player = game.players[event.player_index]
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
remove_player_base(scroll_table, remove_player)
|
||||
|
||||
local selected_player_name = SelectedPlayer:get(player)
|
||||
if selected_player_name == remove_player.name then
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- All other events require a full redraw of the table
|
||||
local function redraw_player_list()
|
||||
local player_list_order = get_player_list_order()
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
scroll_table.clear()
|
||||
for _, next_player_data in ipairs(player_list_order) do
|
||||
add_player_base(scroll_table, next_player_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, redraw_player_list)
|
||||
Event.add(Roles.events.on_role_assigned, redraw_player_list)
|
||||
Event.add(Roles.events.on_role_unassigned, redraw_player_list)
|
||||
|
||||
--- When the action player is changed the action bar will update
|
||||
SelectedPlayer:on_update(function(player_name, selected_player)
|
||||
local player = game.players[player_name]
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
update_action_bar(frame.container.action_bar)
|
||||
for _, next_player in pairs(game.connected_players) do
|
||||
local element = scroll_table[next_player.name][open_action_bar.name]
|
||||
local style = 'frame_button'
|
||||
if next_player.name == selected_player then
|
||||
style = 'tool_button'
|
||||
end
|
||||
element.style = style
|
||||
local element_style = element.style
|
||||
element_style.padding = -2
|
||||
element_style.width = 8
|
||||
element_style.height = 14
|
||||
end
|
||||
end)
|
||||
|
||||
--- When the action name is changed the reason input will update
|
||||
SelectedAction:on_update(function(player_name, selected_action)
|
||||
local player = game.players[player_name]
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local element = frame.container.reason_bar
|
||||
if selected_action then
|
||||
-- if there is a new value then check the player is still online
|
||||
local selected_player_name = SelectedPlayer:get(player_name)
|
||||
local selected_player = game.players[selected_player_name]
|
||||
if selected_player.connected then
|
||||
element.visible = true
|
||||
else
|
||||
-- Clear if the player is offline
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
end
|
||||
|
||||
else
|
||||
element.visible = false
|
||||
|
||||
end
|
||||
end)
|
||||
219
exp_legacy/module/modules/gui/playerdata.lua
Normal file
219
exp_legacy/module/modules/gui/playerdata.lua
Normal file
@@ -0,0 +1,219 @@
|
||||
---- module pd
|
||||
-- @gui PlayerData
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
require 'modules.data.statistics'
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
|
||||
local pd_container
|
||||
local label_width = {
|
||||
['name'] = 135,
|
||||
['count'] = 105,
|
||||
['total'] = 480
|
||||
}
|
||||
|
||||
local function format_time_short(value)
|
||||
return format_time(value*3600, {
|
||||
hours=true,
|
||||
minutes=true,
|
||||
seconds=false
|
||||
})
|
||||
end
|
||||
|
||||
local function format_number_n(n)
|
||||
return format_number(math.floor(n)) .. string.format('%.2f', n % 1):sub(2)
|
||||
end
|
||||
|
||||
local playerStats = PlayerData.Statistics
|
||||
local computed_stats = {
|
||||
DamageDeathRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['DamageDealt']:get(player_name, 0) / playerStats['Deaths']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
KillDeathRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['Kills']:get(player_name, 0) / playerStats['Deaths']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
SessionTime = {
|
||||
default = format_time_short(0),
|
||||
calculate = function(player_name)
|
||||
return format_time_short((playerStats['Playtime']:get(player_name, 0) - playerStats['AfkTime']:get(player_name, 0)) / playerStats['JoinCount']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
BuildRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['MachinesBuilt']:get(player_name, 0) / playerStats['MachinesRemoved']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
RocketPerHour = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['RocketsLaunched']:get(player_name, 0) * 60 / playerStats['Playtime']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
TreeKillPerMinute = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['TreesDestroyed']:get(player_name, 0) / playerStats['Playtime']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
NetPlayTime = {
|
||||
default = format_time_short(0),
|
||||
calculate = function(player_name)
|
||||
return format_time_short((playerStats['Playtime']:get(player_name, 0) - playerStats['AfkTime']:get(player_name, 0)))
|
||||
end
|
||||
},
|
||||
AFKTimeRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['AfkTime']:get(player_name, 0) * 100 / playerStats['Playtime']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
}
|
||||
|
||||
local label =
|
||||
Gui.element(function(_, parent, width, caption, tooltip, name)
|
||||
local new_label = parent.add{
|
||||
type = 'label',
|
||||
caption = caption,
|
||||
tooltip = tooltip,
|
||||
name = name,
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
|
||||
new_label.style.width = width
|
||||
return new_label
|
||||
end)
|
||||
|
||||
local pd_data_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local pd_data_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(pd_data_set, label_width['total'], 4, 'disp')
|
||||
|
||||
for _, stat_name in pairs(PlayerData.Statistics.metadata.display_order) do
|
||||
local child = PlayerData.Statistics[stat_name]
|
||||
local metadata = child.metadata
|
||||
local value = metadata.stringify_short and metadata.stringify_short(0) or metadata.stringify and metadata.stringify(0) or format_number(0)
|
||||
label(disp, label_width['name'], metadata.name or {'exp-statistics.'..stat_name}, metadata.tooltip or {'exp-statistics.'..stat_name..'-tooltip'})
|
||||
label(disp, label_width['count'], {'readme.data-format', value, metadata.unit or ''}, metadata.value_tooltip or {'exp-statistics.'..stat_name..'-tooltip'}, stat_name)
|
||||
end
|
||||
|
||||
for stat_name, data in pairs(computed_stats) do
|
||||
label(disp, label_width['name'], {'exp-statistics.'..stat_name}, {'exp-statistics.'..stat_name..'-tooltip'})
|
||||
label(disp, label_width['count'], {'readme.data-format', data.default, ''}, {'exp-statistics.'..stat_name..'-tooltip'}, stat_name)
|
||||
end
|
||||
|
||||
return pd_data_set
|
||||
end)
|
||||
|
||||
local function pd_update(table, player_name)
|
||||
for _, stat_name in pairs(PlayerData.Statistics.metadata.display_order) do
|
||||
local child = PlayerData.Statistics[stat_name]
|
||||
local metadata = child.metadata
|
||||
local value = child:get(player_name)
|
||||
if metadata.stringify_short then
|
||||
value = metadata.stringify_short(value or 0)
|
||||
elseif metadata.stringify then
|
||||
value = metadata.stringify(value or 0)
|
||||
else
|
||||
value = format_number(value or 0)
|
||||
end
|
||||
table[stat_name].caption = {'readme.data-format', value, metadata.unit or ''}
|
||||
end
|
||||
|
||||
for stat_name, data in pairs(computed_stats) do
|
||||
table[stat_name].caption = {'readme.data-format', data.calculate(player_name), ''}
|
||||
end
|
||||
end
|
||||
|
||||
local pd_username_player =
|
||||
Gui.element(function(definition, parent, player_list)
|
||||
return parent.add{
|
||||
name = definition.name,
|
||||
type = 'drop-down',
|
||||
items = player_list,
|
||||
selected_index = #player_list > 0 and 1
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
horizontally_stretchable = true
|
||||
}:on_selection_changed(function(_, element, _)
|
||||
local player_name = game.connected_players[element.selected_index]
|
||||
local table = element.parent.parent.parent.parent['pd_st_2'].disp.table
|
||||
pd_update(table, player_name)
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
|
||||
local pd_username_update =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = 'update'
|
||||
}:style{
|
||||
width = 128
|
||||
}:on_click(function(_, element, _)
|
||||
local player_index = element.parent[pd_username_player.name].selected_index
|
||||
|
||||
if player_index > 0 then
|
||||
local player_name = game.connected_players[player_index]
|
||||
local table = element.parent.parent.parent.parent['pd_st_2'].disp.table
|
||||
pd_update(table, player_name)
|
||||
end
|
||||
end)
|
||||
|
||||
local pd_username_set =
|
||||
Gui.element(function(_, parent, name, player_list)
|
||||
local pd_username_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(pd_username_set, label_width['total'], 2, 'disp')
|
||||
|
||||
pd_username_player(disp, player_list)
|
||||
pd_username_update(disp)
|
||||
|
||||
return pd_username_set
|
||||
end)
|
||||
|
||||
pd_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, label_width['total'])
|
||||
local player_list = {}
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
|
||||
pd_username_set(container, 'pd_st_1', player_list)
|
||||
pd_data_set(container, 'pd_st_2')
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('item/power-armor-mk2', 'Player Data GUI', pd_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/playerdata')
|
||||
end)
|
||||
|
||||
local function gui_player_list_update()
|
||||
local player_list = {}
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, pd_container)
|
||||
frame.container['pd_st_1'].disp.table[pd_username_player.name].items = player_list
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, gui_player_list_update)
|
||||
Event.add(defines.events.on_player_left_game, gui_player_list_update)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user