mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-16 02:47:48 +02:00
c128c0b457
Fixes final battle where biters would not spawn at all Misc changes and tweaks.
1413 lines
44 KiB
Lua
1413 lines
44 KiB
Lua
--antigrief things made by mewmew
|
|
|
|
local Event = require 'utils.event'
|
|
local Jailed = require 'utils.datastore.jail_data'
|
|
local Gui = require 'utils.gui'
|
|
local AntiGrief = require 'utils.antigrief'
|
|
local SpamProtection = require 'utils.spam_protection'
|
|
local Color = require 'utils.color_presets'
|
|
local Server = require 'utils.server'
|
|
local Task = require 'utils.task_token'
|
|
local Token = require 'utils.token'
|
|
local Global = require 'utils.global'
|
|
local Discord = require 'utils.discord_handler'
|
|
|
|
local Public = {}
|
|
|
|
local insert = table.insert
|
|
local lower = string.lower
|
|
local ceil = math.ceil
|
|
local max = math.max
|
|
local min = math.min
|
|
local module_name = Gui.uid_name()
|
|
local next_button_name = Gui.uid_name()
|
|
local prev_button_name = Gui.uid_name()
|
|
local listable_players_name = Gui.uid_name()
|
|
local count_label_name = Gui.uid_name()
|
|
local rows_per_page = 500
|
|
local create_admin_panel
|
|
|
|
local this = {
|
|
player_data = {}
|
|
}
|
|
|
|
Global.register(
|
|
this,
|
|
function (tbl)
|
|
this = tbl
|
|
end
|
|
)
|
|
|
|
local function get_player_data(player, remove)
|
|
local data = this.player_data[player.name]
|
|
if remove and data then
|
|
if data and data.frame and data.frame.valid then
|
|
data.frame.destroy()
|
|
end
|
|
|
|
this.player_data[player.name] = nil
|
|
return
|
|
end
|
|
|
|
if not this.player_data[player.name] then
|
|
this.player_data[player.name] = {
|
|
selected_history_index = nil,
|
|
filter_player = nil,
|
|
show_all_players = nil,
|
|
current_page = nil
|
|
}
|
|
end
|
|
|
|
return this.player_data[player.name]
|
|
end
|
|
|
|
local delayed_last_page_token =
|
|
Task.register(
|
|
function (event)
|
|
local player_index = event.player_index
|
|
local player = game.get_player(player_index)
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
|
|
local element = event.element
|
|
if not element or not element.valid then
|
|
return
|
|
end
|
|
|
|
local player_data = get_player_data(player)
|
|
if not player_data or not player_data.table_count then
|
|
return
|
|
end
|
|
local last_page = ceil(player_data.table_count / rows_per_page)
|
|
|
|
player_data.current_page = last_page
|
|
local data = { player = player, frame = element }
|
|
create_admin_panel(data)
|
|
end
|
|
)
|
|
|
|
local function clear_validation_action(player_name, action)
|
|
local admin_button_validation = AntiGrief.get('admin_button_validation')
|
|
if admin_button_validation and admin_button_validation[action] then
|
|
admin_button_validation[action][player_name] = nil
|
|
end
|
|
end
|
|
|
|
local clear_validation_token =
|
|
Token.register(
|
|
function (event)
|
|
local action = event.action
|
|
if not action then
|
|
return
|
|
end
|
|
local player_name = event.player_name
|
|
if not player_name then
|
|
return
|
|
end
|
|
|
|
local admin_button_validation = AntiGrief.get('admin_button_validation')
|
|
if admin_button_validation and admin_button_validation[action] then
|
|
admin_button_validation[action][player_name] = nil
|
|
end
|
|
end
|
|
)
|
|
|
|
local function validate_action(player, action)
|
|
local admin_button_validation = AntiGrief.get('admin_button_validation')
|
|
if not admin_button_validation[action] then
|
|
admin_button_validation[action] = {}
|
|
end
|
|
|
|
if not admin_button_validation[action][player.name] then
|
|
admin_button_validation[action][player.name] = true
|
|
Task.set_timeout_in_ticks(200, clear_validation_token, { player_name = player.name, action = action })
|
|
player.print('Please run this again if you are certain that you want to run this action[' .. action .. '].', Color.warning)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function admin_only_message(str)
|
|
for _, player in pairs(game.connected_players) do
|
|
if player.admin == true then
|
|
player.print('Admins-only-message: ' .. str, { r = 0.88, g = 0.88, b = 0.88 })
|
|
end
|
|
end
|
|
end
|
|
|
|
local function jail(player, source_player)
|
|
if validate_action(source_player, 'jail') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'jail')
|
|
return
|
|
end
|
|
Jailed.try_ul_data(player.name, true, source_player.name, 'Jailed by ' .. source_player.name .. '!')
|
|
clear_validation_action(source_player.name, 'jail')
|
|
end
|
|
|
|
local function clear_biters(player)
|
|
if validate_action(player, 'clear_biters') then
|
|
return
|
|
end
|
|
|
|
local surface = player.surface
|
|
local count = 0
|
|
for c in surface.get_chunks() do
|
|
for _, entity in pairs(surface.find_entities_filtered({ area = { { c.x * 32, c.y * 32 }, { c.x * 32 + 32, c.y * 32 + 32 } }, type = "unit" })) do
|
|
if entity and entity.valid then
|
|
entity.destroy()
|
|
count = count + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
player.print('Cleared: ' .. count .. ' biters.', Color.warning)
|
|
|
|
clear_validation_action(player.name, 'clear_biters')
|
|
end
|
|
|
|
local function mute(player, source_player)
|
|
if validate_action(source_player, 'mute') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'mute')
|
|
return
|
|
end
|
|
Jailed.try_ul_data(player.name, true, source_player.name, 'Jailed and muted by ' .. source_player.name .. '!', true)
|
|
local muted = Jailed.mute_player(player)
|
|
local muted_str = muted and 'muted' or 'unmuted'
|
|
clear_validation_action(source_player.name, 'jail')
|
|
game.print(player.name .. ' was ' .. muted_str .. ' by player ' .. source_player.name .. '!', { r = 1, g = 0.5, b = 0.1 })
|
|
end
|
|
|
|
local function free(player, source_player)
|
|
if validate_action(source_player, 'free') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'free')
|
|
return
|
|
end
|
|
Jailed.try_ul_data(player.name, false, source_player.name)
|
|
clear_validation_action(source_player.name, 'free')
|
|
end
|
|
|
|
local bring_player_messages = {
|
|
'Come here my friend!',
|
|
'Papers, please.',
|
|
'What are you up to?'
|
|
}
|
|
|
|
local function bring_player(player, source_player)
|
|
if validate_action(source_player, 'bring_player') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'bring_player')
|
|
return
|
|
end
|
|
if player.driving == true then
|
|
source_player.print('Target player is in a vehicle, teleport not available.', { r = 0.88, g = 0.88, b = 0.88 })
|
|
clear_validation_action(source_player.name, 'bring_player')
|
|
return
|
|
end
|
|
local pos = source_player.surface.find_non_colliding_position('character', source_player.position, 50, 1)
|
|
if pos then
|
|
player.teleport(pos, source_player.surface)
|
|
game.print(player.name .. ' has been teleported to ' .. source_player.name .. '. ' .. bring_player_messages[math.random(1, #bring_player_messages)], { r = 0.98, g = 0.66, b = 0.22 })
|
|
clear_validation_action(source_player.name, 'bring_player')
|
|
end
|
|
end
|
|
|
|
local go_to_player_messages = {
|
|
'Papers, please.',
|
|
'What are you up to?'
|
|
}
|
|
local function go_to_player(player, source_player)
|
|
if validate_action(source_player, 'go_to_player') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'go_to_player')
|
|
return
|
|
end
|
|
local pos = player.surface.find_non_colliding_position('character', player.position, 50, 1)
|
|
if pos then
|
|
source_player.teleport(pos, player.surface)
|
|
game.print(source_player.name .. ' is visiting ' .. player.name .. '. ' .. go_to_player_messages[math.random(1, #go_to_player_messages)], { r = 0.98, g = 0.66, b = 0.22 })
|
|
clear_validation_action(source_player.name, 'go_to_player')
|
|
end
|
|
end
|
|
|
|
local function spank(player, source_player)
|
|
if player.name == source_player.name then
|
|
return player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
end
|
|
if player.character then
|
|
if player.character.health > 1 then
|
|
player.character.damage(1, 'player')
|
|
end
|
|
player.character.health = player.character.health - 5
|
|
player.surface.create_entity({ name = 'water-splash', position = player.position })
|
|
game.print(source_player.name .. ' spanked ' .. player.name, { r = 0.98, g = 0.66, b = 0.22 })
|
|
end
|
|
end
|
|
|
|
local damage_messages = {
|
|
' recieved a love letter from ',
|
|
' recieved a strange package from '
|
|
}
|
|
local function damage(player, source_player)
|
|
if player.name == source_player.name then
|
|
return player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
end
|
|
if player.character then
|
|
if player.character.health > 1 then
|
|
player.character.damage(1, 'player')
|
|
end
|
|
player.character.health = player.character.health - 125
|
|
player.surface.create_entity({ name = 'big-explosion', position = player.position })
|
|
game.print(player.name .. damage_messages[math.random(1, #damage_messages)] .. source_player.name, { r = 0.98, g = 0.66, b = 0.22 })
|
|
end
|
|
end
|
|
|
|
local kill_messages = {
|
|
' did not obey the law.',
|
|
' should not have triggered the admins.',
|
|
' did not respect authority.',
|
|
' had a strange accident.',
|
|
' was struck by lightning.'
|
|
}
|
|
local function kill(player, source_player)
|
|
if validate_action(source_player, 'kill') then
|
|
return
|
|
end
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'kill')
|
|
return
|
|
end
|
|
if player.character then
|
|
player.character.die('player')
|
|
game.print(player.name .. kill_messages[math.random(1, #kill_messages)], { r = 0.98, g = 0.66, b = 0.22 })
|
|
admin_only_message(source_player.name .. ' killed ' .. player.name)
|
|
clear_validation_action(source_player.name, 'kill')
|
|
end
|
|
end
|
|
|
|
local enemy_messages = {
|
|
'Shoot on sight!',
|
|
'Wanted dead or alive!'
|
|
}
|
|
local function enemy(player, source_player)
|
|
if validate_action(source_player, 'enemy') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'enemy')
|
|
return
|
|
end
|
|
if not game.forces.enemy_players then
|
|
game.create_force('enemy_players')
|
|
end
|
|
player.force = game.forces.enemy_players
|
|
game.print(player.name .. ' is now an enemy! ' .. enemy_messages[math.random(1, #enemy_messages)], { r = 0.95, g = 0.15, b = 0.15 })
|
|
admin_only_message(source_player.name .. ' has turned ' .. player.name .. ' into an enemy')
|
|
clear_validation_action(source_player.name, 'enemy')
|
|
end
|
|
|
|
local function ally(player, source_player)
|
|
if validate_action(source_player, 'ally') then
|
|
return
|
|
end
|
|
|
|
if player.name == source_player.name then
|
|
player.print("You can't select yourself!", { r = 1, g = 0.5, b = 0.1 })
|
|
clear_validation_action(source_player.name, 'ally')
|
|
return
|
|
end
|
|
player.force = game.forces.player
|
|
game.print(player.name .. ' is our ally again!', { r = 0.98, g = 0.66, b = 0.22 })
|
|
admin_only_message(source_player.name .. ' made ' .. player.name .. ' our ally')
|
|
clear_validation_action(source_player.name, 'ally')
|
|
end
|
|
|
|
local function turn_off_global_speakers(player)
|
|
if validate_action(player, 'turn_off_global_speakers') then
|
|
return
|
|
end
|
|
|
|
local counter = 0
|
|
for _, surface in pairs(game.surfaces) do
|
|
local speakers = surface.find_entities_filtered({ name = 'programmable-speaker' })
|
|
for _, speaker in pairs(speakers) do
|
|
if speaker.parameters.playback_globally == true then
|
|
speaker.surface.create_entity({ name = 'massive-explosion', position = speaker.position })
|
|
speaker.destroy()
|
|
counter = counter + 1
|
|
end
|
|
end
|
|
end
|
|
if counter == 0 then
|
|
return
|
|
end
|
|
if counter == 1 then
|
|
game.print(player.name .. ' has nuked ' .. counter .. ' global speaker.', { r = 0.98, g = 0.66, b = 0.22 })
|
|
else
|
|
game.print(player.name .. ' has nuked ' .. counter .. ' global speakers.', { r = 0.98, g = 0.66, b = 0.22 })
|
|
end
|
|
clear_validation_action(player.name, 'turn_off_global_speakers')
|
|
end
|
|
|
|
local function delete_all_blueprints(player)
|
|
if validate_action(player, 'delete_all_blueprints') then
|
|
return
|
|
end
|
|
|
|
local counter = 0
|
|
for _, surface in pairs(game.surfaces) do
|
|
for _, ghost in pairs(surface.find_entities_filtered({ type = { 'entity-ghost', 'tile-ghost' } })) do
|
|
ghost.destroy()
|
|
counter = counter + 1
|
|
end
|
|
end
|
|
if counter == 0 then
|
|
clear_validation_action(player.name, 'delete_all_blueprints')
|
|
return
|
|
end
|
|
if counter == 1 then
|
|
game.print(counter .. ' blueprint has been cleared!', { r = 0.98, g = 0.66, b = 0.22 })
|
|
else
|
|
game.print(counter .. ' blueprints have been cleared!', { r = 0.98, g = 0.66, b = 0.22 })
|
|
end
|
|
local server_name = Server.get_server_name() or 'CommandHandler'
|
|
Discord.send_notification_raw(server_name, player.name .. ' cleared all the blueprints on the map.')
|
|
admin_only_message(player.name .. ' has cleared all blueprints.')
|
|
clear_validation_action(player.name, 'delete_all_blueprints')
|
|
end
|
|
|
|
local function pause_game_tick(player)
|
|
if validate_action(player, 'pause_game_tick') then
|
|
return
|
|
end
|
|
|
|
local paused = game.tick_paused
|
|
local paused_str = paused and 'unpaused' or 'paused'
|
|
game.tick_paused = not paused
|
|
game.print('Game has been ' .. paused_str .. ' by ' .. player.name, { r = 0.98, g = 0.66, b = 0.22 })
|
|
local server_name = Server.get_server_name() or 'CommandHandler'
|
|
Discord.send_notification_raw(server_name, player.name .. ' ' .. paused_str .. ' the game.')
|
|
clear_validation_action(player.name, 'pause_game_tick')
|
|
end
|
|
|
|
local function save_game(player)
|
|
if validate_action(player, 'save_game') then
|
|
return
|
|
end
|
|
|
|
local date = Server.get_start_time() or game.tick
|
|
game.server_save('_currently_running' .. tostring(date) .. '_' .. player.name)
|
|
clear_validation_action(player.name, 'save_game')
|
|
end
|
|
|
|
local function clear_items_on_ground(player)
|
|
if validate_action(player, 'clear_items_on_ground') then
|
|
return
|
|
end
|
|
|
|
local i = 0
|
|
for _, entity in pairs(player.surface.find_entities_filtered { type = 'item-entity', name = 'item-on-ground' }) do
|
|
if entity and entity.valid then
|
|
entity.destroy()
|
|
i = i + 1
|
|
end
|
|
end
|
|
if i == 0 then
|
|
return player.print('No items to clear!', Color.warning)
|
|
end
|
|
|
|
player.print('Cleared: ' .. i .. ' items.', Color.success)
|
|
clear_validation_action(player.name, 'clear_items_on_ground')
|
|
end
|
|
|
|
local function create_mini_camera_gui(player, caption, position, surface)
|
|
if player.gui.center['mini_camera'] then
|
|
player.gui.center['mini_camera'].destroy()
|
|
end
|
|
local frame = player.gui.center.add({ type = 'frame', name = 'mini_camera', caption = caption })
|
|
surface = tonumber(surface)
|
|
surface = game.surfaces[surface]
|
|
if not surface or not surface.valid then
|
|
return
|
|
end
|
|
|
|
local camera =
|
|
frame.add(
|
|
{
|
|
type = 'camera',
|
|
name = 'mini_cam_element',
|
|
position = position,
|
|
zoom = 0.6,
|
|
surface_index = surface.index
|
|
}
|
|
)
|
|
camera.style.minimal_width = 640
|
|
camera.style.minimal_height = 480
|
|
end
|
|
|
|
local function filter_brackets(str)
|
|
return (string.find(str, '%[') ~= nil)
|
|
end
|
|
|
|
local function match_test(value, pattern)
|
|
return lower(value:gsub('-', ' ')):find(pattern)
|
|
end
|
|
|
|
local function contains_text(key, value, search_text)
|
|
if not key then
|
|
return false
|
|
end
|
|
if filter_brackets(search_text) then
|
|
return false
|
|
end
|
|
if value then
|
|
if not match_test(key[value], search_text) then
|
|
return false
|
|
end
|
|
else
|
|
if not match_test(key, search_text) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function search_text_locally(history, player_data, callback)
|
|
local antigrief = AntiGrief.get()
|
|
local history_index = {
|
|
['Capsule History'] = antigrief.capsule_history,
|
|
['Message History'] = antigrief.message_history,
|
|
['Friendly Fire History'] = antigrief.friendly_fire_history,
|
|
['Mining History'] = antigrief.mining_history,
|
|
['Mining Override History'] = antigrief.whitelist_mining_history,
|
|
['Landfill History'] = antigrief.landfill_history,
|
|
['Corpse Looting History'] = antigrief.corpse_history,
|
|
['Cancel Crafting History'] = antigrief.cancel_crafting_history,
|
|
['Deconstruct History'] = antigrief.deconstruct_history,
|
|
['Scenario History'] = antigrief.scenario_history,
|
|
['Whisper History'] = antigrief.whisper_history
|
|
}
|
|
|
|
local tooltip = 'Click to open mini camera.'
|
|
if not player_data.current_page then
|
|
player_data.current_page = 1
|
|
end
|
|
|
|
local target = game.get_player(player_data.target_player_name)
|
|
local search_text = player_data.search_text
|
|
|
|
local start_index = (player_data.current_page - 1) * rows_per_page + 1
|
|
local end_index = start_index + rows_per_page - 1
|
|
|
|
if target ~= nil then
|
|
if not history_index or not history_index[history] or #history_index[history] <= 0 then
|
|
return
|
|
end
|
|
|
|
if search_text then
|
|
for i = end_index, start_index, -1 do
|
|
local success = contains_text(history_index[history][i], nil, search_text)
|
|
if success then
|
|
if history == 'Message History' then
|
|
tooltip = ''
|
|
end
|
|
|
|
callback(history_index[history][i], tooltip)
|
|
end
|
|
end
|
|
else
|
|
for i = end_index, start_index, -1 do
|
|
if history_index[history][i] and history_index[history][i]:find(player_data.target_player_name) then
|
|
callback(history_index[history][i], tooltip)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if search_text then
|
|
for i = end_index, start_index, -1 do
|
|
local success = contains_text(history_index[history][i], nil, search_text)
|
|
if success then
|
|
callback(history_index[history][i], tooltip)
|
|
end
|
|
end
|
|
else
|
|
for i = end_index, start_index, -1 do
|
|
callback(history_index[history][i], tooltip)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function draw_events(player_data)
|
|
local frame = player_data.frame
|
|
local history = frame.pagination_table.admin_history_select.items[frame.pagination_table.admin_history_select.selected_index]
|
|
local target_player_name = frame['admin_player_select'].items[frame['admin_player_select'].selected_index]
|
|
player_data.target_player_name = target_player_name
|
|
|
|
local scroll_pane
|
|
if frame.datalog then
|
|
frame.datalog.clear()
|
|
else
|
|
scroll_pane =
|
|
frame.add(
|
|
{
|
|
type = 'scroll-pane',
|
|
name = 'datalog',
|
|
direction = 'vertical',
|
|
horizontal_scroll_policy = 'never',
|
|
vertical_scroll_policy = 'auto'
|
|
}
|
|
)
|
|
scroll_pane.style.maximal_height = 200
|
|
scroll_pane.style.minimal_width = 790
|
|
end
|
|
|
|
search_text_locally(
|
|
history,
|
|
player_data,
|
|
function (history_label, tooltip)
|
|
if not history_label then
|
|
return
|
|
end
|
|
frame.datalog.add(
|
|
{
|
|
type = 'label',
|
|
caption = history_label,
|
|
tooltip = tooltip
|
|
}
|
|
)
|
|
end
|
|
)
|
|
end
|
|
|
|
local function text_changed(event)
|
|
local element = event.element
|
|
if not element then
|
|
return
|
|
end
|
|
if not element.valid then
|
|
return
|
|
end
|
|
|
|
local player = game.get_player(event.player_index)
|
|
|
|
local frame = Gui.get_player_active_frame(player)
|
|
if not frame then
|
|
return
|
|
end
|
|
if frame.name ~= 'Admin' then
|
|
return
|
|
end
|
|
|
|
local is_spamming = SpamProtection.is_spamming(player, nil, 'Admin Text Changed')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
|
|
local player_data = get_player_data(player)
|
|
player_data.frame = frame
|
|
player_data.search_text = element.text:lower()
|
|
|
|
local value = string.len(element.text)
|
|
if value >= 1000 then
|
|
player_data.search_text = nil
|
|
element.text = ''
|
|
return
|
|
end
|
|
|
|
if player_data.search_text == '' then
|
|
player_data.search_text = nil
|
|
local last_page = ceil(player_data.table_count / rows_per_page)
|
|
player_data.current_page = last_page
|
|
local data = { player = player, frame = frame }
|
|
create_admin_panel(data)
|
|
return
|
|
end
|
|
|
|
draw_events(player_data)
|
|
end
|
|
|
|
local function create_pagination_buttons(player_data, frame, table_count)
|
|
if table_count == 0 then
|
|
return
|
|
end
|
|
local last_page = ceil(table_count / rows_per_page)
|
|
|
|
if not player_data.current_page then
|
|
player_data.current_page = last_page
|
|
end
|
|
|
|
local current_page = player_data.current_page
|
|
|
|
if current_page == 1 and current_page == last_page then
|
|
return
|
|
end
|
|
|
|
local button_flow =
|
|
frame.add {
|
|
type = 'flow',
|
|
direction = 'horizontal'
|
|
}
|
|
local prev_button =
|
|
button_flow.add {
|
|
type = 'button',
|
|
name = prev_button_name,
|
|
caption = '◀️',
|
|
style = 'back_button'
|
|
}
|
|
prev_button.style.font = 'default-bold'
|
|
prev_button.style.minimal_width = 32
|
|
prev_button.tooltip = 'Previous page\nHolding [color=yellow]shift[/color] while pressing LMB/RMB will jump to the first page.'
|
|
|
|
local count_label =
|
|
button_flow.add {
|
|
type = 'label',
|
|
name = count_label_name,
|
|
caption = current_page .. '/' .. last_page
|
|
}
|
|
count_label.style.font = 'default-bold'
|
|
player_data.count_label = count_label
|
|
|
|
local next_button =
|
|
button_flow.add {
|
|
type = 'button',
|
|
name = next_button_name,
|
|
caption = '▶️',
|
|
style = 'forward_button'
|
|
}
|
|
next_button.style.font = 'default-bold'
|
|
next_button.style.minimal_width = 32
|
|
next_button.tooltip = 'Next page\nHolding [color=yellow]shift[/color] while pressing LMB/RMB will jump to the last page.'
|
|
|
|
player_data.table_count = table_count
|
|
end
|
|
|
|
create_admin_panel = function (data)
|
|
local player = data.player
|
|
local frame = data.frame
|
|
local antigrief = AntiGrief.get()
|
|
if not antigrief then
|
|
return
|
|
end
|
|
|
|
local player_data = get_player_data(player)
|
|
|
|
local checkbox_state = player_data.show_all_players
|
|
|
|
frame.clear()
|
|
|
|
local players = game.connected_players
|
|
|
|
if checkbox_state then
|
|
players = game.players
|
|
end
|
|
|
|
local player_names = {}
|
|
for _, p in pairs(players) do
|
|
insert(player_names, tostring(p.name))
|
|
end
|
|
insert(player_names, 'Select Player')
|
|
|
|
local selected_index = #player_names
|
|
local selected = player_data.filter_player
|
|
if selected then
|
|
if player_names[selected] then
|
|
selected_index = selected
|
|
end
|
|
end
|
|
|
|
local checkbox_caption = 'Currently showing: connected players only.'
|
|
if checkbox_state then
|
|
checkbox_caption = 'Currently showing: all players that have played on this server.'
|
|
end
|
|
|
|
frame.add({ type = 'checkbox', name = listable_players_name, caption = checkbox_caption, state = checkbox_state or false })
|
|
|
|
local drop_down = frame.add({ type = 'drop-down', name = 'admin_player_select', items = player_names, selected_index = selected_index })
|
|
drop_down.style.minimal_width = 326
|
|
drop_down.style.right_padding = 12
|
|
drop_down.style.left_padding = 12
|
|
|
|
local t = frame.add({ type = 'table', column_count = 4 })
|
|
local buttons = {
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Jail',
|
|
name = 'jail',
|
|
tooltip = 'Jails the player, they will no longer be able to perform any actions except writing in chat.'
|
|
}
|
|
),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Mute',
|
|
name = 'mute',
|
|
tooltip = 'Jails and mutes the player, they will no longer be able to chat.'
|
|
}
|
|
),
|
|
t.add({ type = 'button', caption = 'Free', name = 'free', tooltip = 'Frees the player from jail.' }),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Bring Player',
|
|
name = 'bring_player',
|
|
tooltip = 'Teleports the selected player to your position.'
|
|
}
|
|
),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Make Enemy',
|
|
name = 'enemy',
|
|
tooltip = 'Sets the selected players force to enemy_players.\nDO NOT USE IN PVP MAPS!!'
|
|
}
|
|
),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Make Ally',
|
|
name = 'ally',
|
|
tooltip = 'Sets the selected players force back to the default player force.\nDO NOT USE IN PVP MAPS!!'
|
|
}
|
|
),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Go to Player',
|
|
name = 'go_to_player',
|
|
tooltip = 'Teleport yourself to the selected player.'
|
|
}
|
|
),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Spank',
|
|
name = 'spank',
|
|
tooltip = 'Hurts the selected player with minor damage.\nCan not kill the player.'
|
|
}
|
|
),
|
|
t.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Damage',
|
|
name = 'damage',
|
|
tooltip = 'Damages the selected player with greater damage.\nCan not kill the player.'
|
|
}
|
|
),
|
|
t.add({ type = 'button', caption = 'Kill', name = 'kill', tooltip = 'Kills the selected player instantly.' })
|
|
}
|
|
for _, button in pairs(buttons) do
|
|
button.style.font = 'default-bold'
|
|
--button.style.font_color = { r=0.99, g=0.11, b=0.11}
|
|
button.style.minimal_width = 106
|
|
end
|
|
|
|
local line = frame.add { type = 'line' }
|
|
line.style.top_margin = 8
|
|
line.style.bottom_margin = 8
|
|
|
|
frame.add({ type = 'label', caption = 'Global Actions:' })
|
|
local actionTable = frame.add({ type = 'table', column_count = 4 })
|
|
local bottomButtons = {
|
|
actionTable.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Destroy global speakers',
|
|
name = 'turn_off_global_speakers',
|
|
tooltip = 'Destroys all speakers that are set to play sounds globally.'
|
|
}
|
|
),
|
|
actionTable.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Delete blueprints',
|
|
name = 'delete_all_blueprints',
|
|
tooltip = 'Deletes all placed blueprints on the map.'
|
|
}
|
|
),
|
|
actionTable.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Pause game tick',
|
|
name = 'pause_game_tick',
|
|
tooltip = 'Pauses the game tick.'
|
|
}
|
|
),
|
|
actionTable.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Save game',
|
|
name = 'save_game',
|
|
tooltip = 'Saves the game.'
|
|
}
|
|
),
|
|
actionTable.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Clear items on ground',
|
|
name = 'clear_items_on_ground',
|
|
tooltip = 'Clears all items on the ground.\nThis might lag the game!'
|
|
}
|
|
),
|
|
actionTable.add(
|
|
{
|
|
type = 'button',
|
|
caption = 'Remove biters',
|
|
name = 'clear_biters',
|
|
tooltip = 'Clears out all biters (friendly and enemy).\nThis might lag the game!'
|
|
}
|
|
)
|
|
--- t.add({type = "button", caption = "Cancel all deconstruction orders", name = "remove_all_deconstruction_orders"})
|
|
}
|
|
for _, button in pairs(bottomButtons) do
|
|
button.style.font = 'default-bold'
|
|
button.style.minimal_width = 80
|
|
end
|
|
|
|
local bottomLine = frame.add { type = 'line' }
|
|
bottomLine.style.top_margin = 8
|
|
bottomLine.style.bottom_margin = 8
|
|
|
|
local histories = {}
|
|
if antigrief.capsule_history then
|
|
insert(histories, 'Capsule History')
|
|
end
|
|
if antigrief.message_history then
|
|
insert(histories, 'Message History')
|
|
end
|
|
if antigrief.friendly_fire_history then
|
|
insert(histories, 'Friendly Fire History')
|
|
end
|
|
if antigrief.mining_history then
|
|
insert(histories, 'Mining History')
|
|
end
|
|
if antigrief.whitelist_mining_history then
|
|
insert(histories, 'Mining Override History')
|
|
end
|
|
if antigrief.landfill_history then
|
|
insert(histories, 'Landfill History')
|
|
end
|
|
if antigrief.corpse_history then
|
|
insert(histories, 'Corpse Looting History')
|
|
end
|
|
if antigrief.cancel_crafting_history then
|
|
insert(histories, 'Cancel Crafting History')
|
|
end
|
|
if antigrief.deconstruct_history then
|
|
insert(histories, 'Deconstruct History')
|
|
end
|
|
if antigrief.scenario_history then
|
|
insert(histories, 'Scenario History')
|
|
end
|
|
if antigrief.whisper_history then
|
|
insert(histories, 'Whisper History')
|
|
end
|
|
|
|
if #histories == 0 then
|
|
return
|
|
end
|
|
|
|
local search_table = frame.add({ type = 'table', column_count = 3 })
|
|
search_table.add({ type = 'label', caption = 'Search: ' })
|
|
local search_text = search_table.add({ type = 'textfield' })
|
|
search_text.text = player_data.search_text or ''
|
|
search_text.style.width = 140
|
|
local btn =
|
|
search_table.add {
|
|
type = 'sprite-button',
|
|
tooltip = '[color=blue]Info![/color]\nSearching does not filter the amount of pages shown.\nThis is a limitation in the Factorio engine.\nIterating over the whole table would lag the game.\nSo when searching, you will still see the same amount of pages.\nAnd the results will be "janky".',
|
|
sprite = 'utility/questionmark'
|
|
}
|
|
btn.style.height = 20
|
|
btn.style.width = 20
|
|
btn.enabled = false
|
|
btn.focus()
|
|
|
|
local bottomLine2 = frame.add({ type = 'label', caption = '----------------------------------------------' })
|
|
bottomLine2.style.font = 'default-listbox'
|
|
bottomLine2.style.font_color = { r = 0.98, g = 0.66, b = 0.22 }
|
|
|
|
local selected_index_2 = 1
|
|
if player_data and player_data.selected_history_index then
|
|
selected_index_2 = player_data.selected_history_index
|
|
end
|
|
|
|
local pagination_table = frame.add({ type = 'table', column_count = 2, name = 'pagination_table' })
|
|
|
|
local drop_down_2 = pagination_table.add({ type = 'drop-down', name = 'admin_history_select', items = histories, selected_index = selected_index_2 })
|
|
drop_down_2.style.right_padding = 12
|
|
drop_down_2.style.left_padding = 12
|
|
|
|
local history_index = {
|
|
['Capsule History'] = antigrief.capsule_history,
|
|
['Message History'] = antigrief.message_history,
|
|
['Friendly Fire History'] = antigrief.friendly_fire_history,
|
|
['Mining History'] = antigrief.mining_history,
|
|
['Mining Override History'] = antigrief.whitelist_mining_history,
|
|
['Landfill History'] = antigrief.landfill_history,
|
|
['Corpse Looting History'] = antigrief.corpse_history,
|
|
['Cancel Crafting History'] = antigrief.cancel_crafting_history,
|
|
['Deconstruct History'] = antigrief.deconstruct_history,
|
|
['Scenario History'] = antigrief.scenario_history,
|
|
['Whisper History'] = antigrief.whisper_history
|
|
}
|
|
|
|
local history = frame.pagination_table.admin_history_select.items[frame.pagination_table.admin_history_select.selected_index]
|
|
|
|
create_pagination_buttons(player_data, pagination_table, #history_index[history])
|
|
|
|
player_data.frame = frame
|
|
|
|
draw_events(player_data)
|
|
end
|
|
|
|
local create_admin_panel_token = Token.register(create_admin_panel)
|
|
|
|
local admin_functions = {
|
|
['jail'] = jail,
|
|
['mute'] = mute,
|
|
['free'] = free,
|
|
['bring_player'] = bring_player,
|
|
['spank'] = spank,
|
|
['damage'] = damage,
|
|
['kill'] = kill,
|
|
['enemy'] = enemy,
|
|
['ally'] = ally,
|
|
['go_to_player'] = go_to_player
|
|
}
|
|
|
|
local admin_global_functions = {
|
|
['turn_off_global_speakers'] = turn_off_global_speakers,
|
|
['delete_all_blueprints'] = delete_all_blueprints,
|
|
['pause_game_tick'] = pause_game_tick,
|
|
['save_game'] = save_game,
|
|
['clear_items_on_ground'] = clear_items_on_ground,
|
|
['clear_biters'] = clear_biters
|
|
}
|
|
|
|
local function get_surface_from_string(str)
|
|
if not str then
|
|
return
|
|
end
|
|
if str == '' then
|
|
return
|
|
end
|
|
str = string.lower(str)
|
|
local start = string.find(str, 'surface:')
|
|
local sname = string.len(str)
|
|
local surface = string.sub(str, start + 8, sname)
|
|
if not surface then
|
|
return false
|
|
end
|
|
|
|
return surface
|
|
end
|
|
|
|
local function get_position_from_string(str)
|
|
if not str then
|
|
return
|
|
end
|
|
if str == '' then
|
|
return
|
|
end
|
|
str = string.lower(str)
|
|
local x_pos = string.find(str, 'x:')
|
|
local y_pos = string.find(str, 'y:')
|
|
if not x_pos then
|
|
return false
|
|
end
|
|
if not y_pos then
|
|
return false
|
|
end
|
|
x_pos = x_pos + 2
|
|
y_pos = y_pos + 2
|
|
|
|
local a = 1
|
|
for i = 1, string.len(str), 1 do
|
|
local s = string.sub(str, x_pos + i, x_pos + i)
|
|
if not s then
|
|
break
|
|
end
|
|
if string.byte(s) == 32 then
|
|
break
|
|
end
|
|
a = a + 1
|
|
end
|
|
local x = string.sub(str, x_pos, x_pos + a)
|
|
|
|
local a1 = 1
|
|
for i = 1, string.len(str), 1 do
|
|
local s = string.sub(str, y_pos + i, y_pos + i)
|
|
if not s then
|
|
break
|
|
end
|
|
if string.byte(s) == 32 then
|
|
break
|
|
end
|
|
a1 = a1 + 1
|
|
end
|
|
|
|
local y = string.sub(str, y_pos, y_pos + a1)
|
|
x = tonumber(x)
|
|
y = tonumber(y)
|
|
local position = { x = x, y = y }
|
|
return position
|
|
end
|
|
|
|
local function on_gui_click(event)
|
|
local element = event.element
|
|
if not element or not element.valid then
|
|
return
|
|
end
|
|
local player = game.get_player(event.player_index)
|
|
|
|
local name = event.element.name
|
|
|
|
local frame = Gui.get_player_active_frame(player)
|
|
if not frame then
|
|
return
|
|
end
|
|
|
|
if frame.name ~= 'Admin' then
|
|
return
|
|
end
|
|
|
|
if name == 'mini_camera' or name == 'mini_cam_element' then
|
|
player.gui.center['mini_camera'].destroy()
|
|
return
|
|
end
|
|
|
|
local is_spamming = SpamProtection.is_spamming(player, nil, 'Admin Gui Click')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
|
|
if admin_functions[name] then
|
|
local target_player_name = frame['admin_player_select'].items[frame['admin_player_select'].selected_index]
|
|
if not target_player_name then
|
|
return
|
|
end
|
|
if target_player_name == 'Select Player' then
|
|
player.print('[AdminGui] No target player selected.', { r = 0.88, g = 0.88, b = 0.88 })
|
|
return
|
|
end
|
|
local target_player = game.players[target_player_name]
|
|
if target_player.connected == true then
|
|
admin_functions[name](target_player, player)
|
|
end
|
|
return
|
|
end
|
|
|
|
if admin_global_functions[name] then
|
|
admin_global_functions[name](player)
|
|
return
|
|
end
|
|
|
|
if not frame then
|
|
return
|
|
end
|
|
if not element.caption then
|
|
return
|
|
end
|
|
local position = get_position_from_string(element.caption)
|
|
if not position then
|
|
return
|
|
end
|
|
|
|
local surface = get_surface_from_string(element.caption)
|
|
if not surface then
|
|
return
|
|
end
|
|
|
|
if player.gui.center['mini_camera'] then
|
|
if player.gui.center['mini_camera'].caption == element.caption then
|
|
player.gui.center['mini_camera'].destroy()
|
|
return
|
|
end
|
|
end
|
|
|
|
create_mini_camera_gui(player, element.caption, position, surface)
|
|
end
|
|
|
|
local function on_gui_closed(event)
|
|
local player = game.get_player(event.player_index)
|
|
|
|
get_player_data(player, true)
|
|
end
|
|
|
|
local function on_gui_selection_state_changed(event)
|
|
local player = game.get_player(event.player_index)
|
|
local name = event.element.name
|
|
|
|
if name == 'admin_history_select' then
|
|
local player_data = get_player_data(player)
|
|
player_data.selected_history_index = event.element.selected_index
|
|
|
|
local frame = Gui.get_player_active_frame(player)
|
|
if not frame then
|
|
return
|
|
end
|
|
if frame.name ~= 'Admin' then
|
|
return
|
|
end
|
|
|
|
Task.set_timeout_in_ticks(5, delayed_last_page_token, { player_index = player.index, element = frame })
|
|
|
|
player_data.current_page = 1
|
|
|
|
local is_spamming = SpamProtection.is_spamming(player, nil, 'Admin Selection Changed')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
local data = { player = player, frame = frame }
|
|
create_admin_panel(data)
|
|
end
|
|
if name == 'admin_player_select' then
|
|
local player_data = get_player_data(player)
|
|
player_data.filter_player = event.element.selected_index
|
|
|
|
local frame = Gui.get_player_active_frame(player)
|
|
if not frame then
|
|
return
|
|
end
|
|
if frame.name ~= 'Admin' then
|
|
return
|
|
end
|
|
|
|
local is_spamming = SpamProtection.is_spamming(player, nil, 'Admin Player Select')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
|
|
local data = { player = player, frame = frame }
|
|
create_admin_panel(data)
|
|
end
|
|
end
|
|
|
|
Gui.add_tab_to_gui({ name = module_name, caption = 'Admin', id = create_admin_panel_token, admin = true })
|
|
|
|
Gui.on_click(
|
|
module_name,
|
|
function (event)
|
|
local player = event.player
|
|
Gui.reload_active_tab(player)
|
|
end
|
|
)
|
|
|
|
function Public.contains_text(history, search_text, target_player_name)
|
|
local antigrief = AntiGrief.get()
|
|
local history_index = {
|
|
['Capsule History'] = antigrief.capsule_history,
|
|
['Message History'] = antigrief.message_history,
|
|
['Friendly Fire History'] = antigrief.friendly_fire_history,
|
|
['Mining History'] = antigrief.mining_history,
|
|
['Mining Override History'] = antigrief.whitelist_mining_history,
|
|
['Landfill History'] = antigrief.landfill_history,
|
|
['Corpse Looting History'] = antigrief.corpse_history,
|
|
['Cancel Crafting History'] = antigrief.cancel_crafting_history,
|
|
['Deconstruct History'] = antigrief.deconstruct_history,
|
|
['Scenario History'] = antigrief.scenario_history,
|
|
['Whisper History'] = antigrief.whisper_history
|
|
}
|
|
|
|
local remote_tbl = {}
|
|
|
|
if target_player_name and string.len(target_player_name) > 0 and game.get_player(target_player_name) ~= nil then
|
|
if not history_index or not history_index[history] or #history_index[history] <= 0 then
|
|
return
|
|
end
|
|
|
|
for i = #history_index[history], 1, -1 do
|
|
if history_index[history][i]:find(target_player_name) then
|
|
if search_text then
|
|
local success = contains_text(history_index[history][i], nil, search_text)
|
|
if not success then
|
|
goto continue
|
|
end
|
|
end
|
|
|
|
remote_tbl[#remote_tbl + 1] = history_index[history][i]
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
else
|
|
for i = #history_index[history], 1, -1 do
|
|
if search_text then
|
|
local success = contains_text(history_index[history][i], nil, search_text)
|
|
if not success then
|
|
goto continue
|
|
end
|
|
end
|
|
|
|
remote_tbl[#remote_tbl + 1] = history_index[history][i]
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
return remote_tbl
|
|
end
|
|
|
|
Gui.on_click(
|
|
prev_button_name,
|
|
function (event)
|
|
local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Prev button click')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
local player = event.player
|
|
if not player or not player.valid or not player.character then
|
|
return
|
|
end
|
|
|
|
local player_data = get_player_data(player)
|
|
|
|
local element = event.element
|
|
if not element or not element.valid then
|
|
return
|
|
end
|
|
|
|
local last_page = ceil(player_data.table_count / rows_per_page)
|
|
|
|
if not player_data.current_page then
|
|
player_data.current_page = last_page
|
|
end
|
|
|
|
local current_page = player_data.current_page
|
|
|
|
if current_page == 1 then
|
|
current_page = 1
|
|
player_data.current_page = current_page
|
|
player.print('[Admin] There are no more pages beyond this point.', Color.warning)
|
|
return
|
|
end
|
|
|
|
local shift = event.shift
|
|
if shift then
|
|
current_page = 1
|
|
else
|
|
current_page = max(1, current_page - 1)
|
|
end
|
|
|
|
player_data.current_page = current_page
|
|
|
|
local data = { player = player, frame = element.parent.parent.parent }
|
|
create_admin_panel(data)
|
|
end
|
|
)
|
|
|
|
Gui.on_click(
|
|
next_button_name,
|
|
function (event)
|
|
local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Next button click')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
|
|
local element = event.element
|
|
if not element or not element.valid then
|
|
return
|
|
end
|
|
|
|
local player = event.player
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
|
|
local player_data = get_player_data(player)
|
|
|
|
local table_count = player_data.table_count
|
|
if not table_count then
|
|
return
|
|
end
|
|
|
|
local last_page = ceil(table_count / rows_per_page)
|
|
if not player_data.current_page then
|
|
player_data.current_page = last_page
|
|
end
|
|
|
|
local current_page = player_data.current_page
|
|
|
|
if current_page == last_page then
|
|
current_page = last_page
|
|
player_data.current_page = current_page
|
|
player.print('[Admin] There are no more pages beyond this point.', Color.warning)
|
|
return
|
|
end
|
|
|
|
local shift = event.shift
|
|
if shift then
|
|
current_page = last_page
|
|
else
|
|
current_page = min(last_page, current_page + 1)
|
|
end
|
|
|
|
player_data.current_page = current_page
|
|
|
|
local data = { player = player, frame = element.parent.parent.parent }
|
|
create_admin_panel(data)
|
|
end
|
|
)
|
|
|
|
Gui.on_checked_state_changed(
|
|
listable_players_name,
|
|
function (event)
|
|
local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Listable players click')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
|
|
local player = event.player
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
|
|
local player_data = get_player_data(player)
|
|
|
|
local element = event.element
|
|
if not element or not element.valid then
|
|
return
|
|
end
|
|
|
|
player_data.show_all_players = element.state
|
|
|
|
local data = { player = player, frame = element.parent }
|
|
create_admin_panel(data)
|
|
end
|
|
)
|
|
|
|
Event.add(defines.events.on_gui_text_changed, text_changed)
|
|
Event.add(defines.events.on_gui_click, on_gui_click)
|
|
Event.add(defines.events.on_gui_selection_state_changed, on_gui_selection_state_changed)
|
|
Event.add(Gui.events.on_gui_closed_main_frame, on_gui_closed)
|
|
|
|
return Public
|