417 lines
15 KiB
Lua
417 lines
15 KiB
Lua
local RecipeChange = require "__RemoteConfiguration__/recipe-change"
|
|
local blacklist_groups = {
|
|
-- exotic industries related
|
|
-- maybe add interface to extend this?
|
|
["gate-user"] = true,
|
|
["drone-user"] = true
|
|
}
|
|
|
|
-- These entity types can be opened remotely anyway
|
|
local entity_type_blacklist = {
|
|
["train-stop"] = true,
|
|
["electric-pole"] = true,
|
|
}
|
|
|
|
local function increase_range(player)
|
|
if player.character and player.character_reach_distance_bonus < 200000 then
|
|
player.character_reach_distance_bonus = player.character_reach_distance_bonus + 200000
|
|
end
|
|
end
|
|
|
|
local ranges_reset_this_tick = {}
|
|
local function reset_range(player)
|
|
if not player.character then return end
|
|
while player.character_reach_distance_bonus >= 200000 do
|
|
player.character_reach_distance_bonus = player.character_reach_distance_bonus - 200000
|
|
ranges_reset_this_tick[player.index] = game.tick
|
|
end
|
|
end
|
|
local function reset_player(player)
|
|
-- dont reset the group if its currently a blacklist group
|
|
-- game.print("trying to reset player")
|
|
if blacklist_groups[player.permission_group.name] then return end
|
|
player.permission_group = game.permissions.get_group("Default")
|
|
-- game.print("resetting player")
|
|
reset_range(player)
|
|
end
|
|
|
|
local function can_reach_entity(player, entity, ignore_map_only)
|
|
-- Check if player can reach entity disregarding whatever reach bonus we have given the player
|
|
if not player.character then return true end
|
|
if not ignore_map_only and not player.mod_settings["rc-interact-in-game"].value and player.render_mode == defines.render_mode.game and not (script.active_mods["SpidertronEnhancements"] and entity.type == "spider-vehicle") then return true end
|
|
local reach_distance_bonus = player.character_reach_distance_bonus
|
|
reset_range(player)
|
|
local can_reach = player.can_reach_entity(entity)
|
|
player.character_reach_distance_bonus = reach_distance_bonus
|
|
return can_reach
|
|
end
|
|
|
|
local function is_out_of_range_gui_open(player)
|
|
local opened = player.opened
|
|
if opened and player.opened_gui_type == defines.gui_type.entity and not can_reach_entity(player, opened) then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local wires = {["red-wire"] = true, ["green-wire"] = true, ["copper-cable"] = true}
|
|
local function is_holding_wire(player)
|
|
local cursor_stack = player.cursor_stack
|
|
if cursor_stack and cursor_stack.valid_for_read then
|
|
return wires[cursor_stack.name]
|
|
end
|
|
end
|
|
|
|
local function open_entity(player, entity, ignore_map_only)
|
|
if entity_type_blacklist[entity.type] then return end
|
|
reset_player(player) -- Ensures that can_reach_entity is accurate, not needed any more?
|
|
local out_of_reach = not can_reach_entity(player, entity, ignore_map_only)
|
|
local map_mode = player.render_mode == defines.render_mode.chart_zoomed_in or player.render_mode == defines.render_mode.chart
|
|
if out_of_reach or map_mode then
|
|
if player.force.is_enemy(entity.force) then
|
|
if map_mode then
|
|
player.create_local_flying_text{text = {"cant-open-enemy-structures"}, create_at_cursor = true}
|
|
end
|
|
return
|
|
end
|
|
|
|
player.opened = nil -- Triggers on_gui_closed before we open the GUI we care about
|
|
if out_of_reach then
|
|
increase_range(player)
|
|
player.permission_group = game.permissions.get_group("Remote Configuration GUI opened")
|
|
end
|
|
player.opened = entity
|
|
if player.opened_gui_type == defines.gui_type.entity then
|
|
RecipeChange.on_remote_gui_opened(player)
|
|
if not map_mode then
|
|
player.create_local_flying_text{
|
|
text = {"remote-configuration.opened-gui-remotely"},
|
|
create_at_cursor = true,
|
|
}
|
|
player.play_sound{ path = "rc-warning-sound" }
|
|
end
|
|
else
|
|
-- Opening GUI failed
|
|
reset_player(player)
|
|
end
|
|
end
|
|
end
|
|
|
|
script.on_event("rc-open-gui",
|
|
function(event)
|
|
local player = game.get_player(event.player_index)
|
|
if not player.is_cursor_empty() then return end
|
|
local selected = player.selected
|
|
|
|
if not selected then
|
|
-- Try from the map for trains (and other vehicles)
|
|
if player.render_mode == defines.render_mode.chart or player.render_mode == defines.render_mode.chart_zoomed_in then
|
|
-- Don't need to check chart_zoomed_in because spidertrons have radars, so would be selectable
|
|
local position = event.cursor_position
|
|
local vehicles = player.surface.find_entities_filtered{type = {"locomotive", "spider-vehicle"}, position = position, radius = 4.5, limit = 1}
|
|
if #vehicles > 0 then
|
|
selected = vehicles[1]
|
|
end
|
|
end
|
|
end
|
|
if selected then
|
|
open_entity(player, selected)
|
|
end
|
|
end
|
|
)
|
|
|
|
remote.add_interface("RemoteConfiguration",
|
|
{
|
|
open_entity = function(player, entity) open_entity(player, entity, true) end,
|
|
reset_this_tick = function(player) if game.tick == ranges_reset_this_tick[player.index] then return true end end,
|
|
}
|
|
)
|
|
|
|
script.on_event(defines.events.on_gui_closed,
|
|
function(event)
|
|
local player = game.get_player(event.player_index)
|
|
RecipeChange.on_gui_closed(player)
|
|
if is_holding_wire(player) then return end
|
|
reset_player(player)
|
|
end
|
|
)
|
|
|
|
-- Allows wires to be fast-transfered or placed in chests when close, but not when far away
|
|
local function recalculate_wire_permissions(event)
|
|
local player = game.get_player(event.player_index)
|
|
if not is_holding_wire(player) then return end
|
|
|
|
local opened = player.opened
|
|
if player.selected and can_reach_entity(player, player.selected) then
|
|
reset_player(player)
|
|
elseif player.opened_self or (opened and player.opened_gui_type == defines.gui_type.entity and can_reach_entity(player, opened)) then
|
|
reset_player(player)
|
|
else
|
|
increase_range(player)
|
|
player.permission_group = game.permissions.get_group("Remote Configuration GUI opened")
|
|
end
|
|
end
|
|
script.on_event(defines.events.on_player_changed_position, recalculate_wire_permissions)
|
|
script.on_event(defines.events.on_selected_entity_changed, recalculate_wire_permissions)
|
|
script.on_event(defines.events.on_gui_opened, recalculate_wire_permissions)
|
|
|
|
script.on_event(defines.events.on_player_cursor_stack_changed,
|
|
function(event)
|
|
local player = game.get_player(event.player_index)
|
|
if is_holding_wire(player) then
|
|
recalculate_wire_permissions(event)
|
|
else
|
|
if not is_out_of_range_gui_open(player) then
|
|
reset_player(player)
|
|
end
|
|
end
|
|
end
|
|
)
|
|
|
|
script.on_event("rc-paste-entity-settings",
|
|
function(event)
|
|
local player = game.get_player(event.player_index)
|
|
if not player.is_cursor_empty() then return end
|
|
|
|
local selected = player.selected
|
|
if not selected then return end
|
|
|
|
local in_reach = can_reach_entity(player, selected)
|
|
if in_reach then return end -- Let vanilla handle this
|
|
|
|
local entity_copy_source = player.entity_copy_source
|
|
if not entity_copy_source then return end
|
|
if player.force.is_enemy(selected.force) then
|
|
player.create_local_flying_text{text = {"cant-paste-enemy-structure-settings"}, create_at_cursor = true}
|
|
return
|
|
end
|
|
|
|
removed_items = selected.copy_settings(entity_copy_source, player)
|
|
local surface = selected.surface
|
|
local position = selected.position
|
|
local force = player.force
|
|
for name, count in pairs(removed_items) do
|
|
surface.spill_item_stack(
|
|
position,
|
|
{name = name, count = count},
|
|
true, -- enabled_looted
|
|
force, -- force for deconstruction
|
|
false -- allow_on_belts
|
|
)
|
|
end
|
|
end
|
|
)
|
|
|
|
function is_cheating(player)
|
|
if
|
|
player.cheat_mode
|
|
and not (player.controller_type == defines.controllers.god and script.active_mods["space-exploration"])
|
|
then
|
|
return true
|
|
end
|
|
return player.controller_type == defines.controllers.editor
|
|
end
|
|
|
|
function set_cursor(player, item)
|
|
local inventory = player.get_main_inventory()
|
|
if not inventory then
|
|
return false
|
|
end
|
|
local cursor_stack = player.cursor_stack
|
|
if not cursor_stack or not player.clear_cursor() then
|
|
return false
|
|
end
|
|
local inventory_stack, stack_index = inventory.find_item_stack(item)
|
|
if not inventory_stack or not stack_index then
|
|
local stack_size = game.item_prototypes[item].stack_size
|
|
if is_cheating(player) and inventory.can_insert({ name = item, count = stack_size }) then
|
|
inventory.insert({ name = item, count = stack_size })
|
|
inventory_stack, stack_index = inventory.find_item_stack(item)
|
|
else
|
|
player.cursor_ghost = item
|
|
return true
|
|
end
|
|
end
|
|
--- @cast inventory_stack LuaItemStack
|
|
--- @cast stack_index uint
|
|
if not cursor_stack.transfer_stack(inventory_stack) then
|
|
return false
|
|
end
|
|
local inventory_def
|
|
if player.controller_type == defines.controllers.character then
|
|
inventory_def = defines.inventory.character_main
|
|
elseif player.controller_type == defines.controllers.editor then
|
|
inventory_def = defines.inventory.editor_main
|
|
elseif player.controller_type == defines.controllers.god then
|
|
inventory_def = defines.inventory.god_main
|
|
end
|
|
player.hand_location = { inventory = inventory_def, slot = stack_index }
|
|
return true
|
|
end
|
|
|
|
local function remote_build(event)
|
|
local player = game.get_player(event.player_index)
|
|
if not player.mod_settings["rc-ghost-build-in-map"].value then return end
|
|
if player.render_mode == defines.render_mode.game then
|
|
-- Try and put the real item back in the cursor
|
|
local cursor_ghost = player.cursor_ghost
|
|
if not cursor_ghost then return end
|
|
|
|
local main_inventory = player.get_main_inventory()
|
|
if not main_inventory then
|
|
return
|
|
end
|
|
|
|
local count = main_inventory.get_item_count(cursor_ghost.name)
|
|
if count == 0 then
|
|
return
|
|
end
|
|
set_cursor(player, cursor_ghost.name)
|
|
else
|
|
local cursor_stack = player.cursor_stack
|
|
if not (cursor_stack and cursor_stack.valid_for_read) then return end
|
|
if not cursor_stack.prototype.place_result then return end
|
|
local cursor_stack_name = cursor_stack.name
|
|
if player.clear_cursor() then
|
|
player.cursor_ghost = cursor_stack_name
|
|
end
|
|
end
|
|
end
|
|
script.on_event("rc-build", remote_build)
|
|
|
|
local direction_modifiers = {
|
|
["straight-rail"] = 0, -- 0 means ignore
|
|
["curved-rail"] = 0,
|
|
["rail-signal"] = 0,
|
|
["rail-chain-signal"] = 0,
|
|
["generator"] = 0,
|
|
["burner-generator"] = 0,
|
|
["assembling-machine"] = -1, -- -1 means rotate immediately
|
|
["splitter"] = 4,
|
|
["underground-belt"] = 4,
|
|
["pipe-to-ground"] = 4,
|
|
["pump"] = 4,
|
|
}
|
|
local function remote_rotate(event, direction)
|
|
local player = game.get_player(event.player_index)
|
|
if not player.is_cursor_empty() then return end
|
|
|
|
local selected = player.selected
|
|
if not selected then return end
|
|
|
|
if can_reach_entity(player, selected) then return end -- Let vanilla handle this
|
|
if not selected.supports_direction then return end
|
|
if player.force.is_enemy(selected.force) then
|
|
return
|
|
end
|
|
|
|
local current_direction = selected.get_upgrade_direction()
|
|
|
|
if not current_direction then current_direction = selected.direction end
|
|
|
|
local direction_modifier = direction_modifiers[selected.type] or 2
|
|
if direction_modifier == 0 then return end
|
|
if direction_modifier == -1 then
|
|
local next_direction = (current_direction + 2 * direction) % 8
|
|
selected.direction = next_direction
|
|
else
|
|
local next_direction = (current_direction + direction * direction_modifier) % 8
|
|
if next_direction == selected.direction and selected.get_upgrade_target() and selected.get_upgrade_target().name == selected.name then
|
|
selected.cancel_upgrade(player.force, player)
|
|
else
|
|
selected.order_upgrade{force = player.force, target = selected, player = player, direction = next_direction}
|
|
end
|
|
end
|
|
end
|
|
script.on_event("rc-rotate", function(event) remote_rotate(event, 1) end)
|
|
script.on_event("rc-reverse-rotate", function(event) remote_rotate(event, -1) end)
|
|
|
|
local function remote_deconstruct(event)
|
|
local player = game.get_player(event.player_index)
|
|
|
|
local selected = player.selected
|
|
if not selected then return end
|
|
|
|
if player.render_mode == defines.render_mode.game and can_reach_entity(player, selected) then return end -- Let vanilla handle this
|
|
if player.force ~= selected.force then
|
|
player.create_local_flying_text{text = {"remote-configuration.cant-decon-unowned-structure"}, create_at_cursor = true}
|
|
return
|
|
end
|
|
selected.order_deconstruction(player.force, player)
|
|
end
|
|
script.on_event("rc-deconstruct", remote_deconstruct)
|
|
|
|
local function remote_cancel_deconstruct(event)
|
|
local player = game.get_player(event.player_index)
|
|
|
|
local selected = player.selected
|
|
if not selected then return end
|
|
if player.force ~= selected.force then
|
|
player.create_local_flying_text{text = {"remote-configuration.cant-cancel-decon-unowned-structure"}, create_at_cursor = true}
|
|
return
|
|
end
|
|
|
|
selected.cancel_deconstruction(player.force, player)
|
|
end
|
|
script.on_event("rc-cancel-deconstruct", remote_cancel_deconstruct)
|
|
|
|
local function create_permission_group(config_changed_data)
|
|
local permissions = game.permissions
|
|
|
|
if config_changed_data then
|
|
-- in on_configuration_changed
|
|
if config_changed_data.mod_changes and config_changed_data.mod_changes["RemoteConfiguration"] then
|
|
for _, player in pairs(game.players) do
|
|
reset_player(player)
|
|
player.opened = nil
|
|
end
|
|
end
|
|
if permissions.get_group("Remote Configuration GUI opened") then
|
|
permissions.get_group("Remote Configuration GUI opened").destroy()
|
|
end
|
|
end
|
|
if permissions.get_group("Remote Configuration GUI opened") then
|
|
return
|
|
end
|
|
local group = permissions.create_group("Remote Configuration GUI opened")
|
|
if not group then
|
|
log("Group not created!!!")
|
|
game.print("[Remote Configuration] Group not created, please report!")
|
|
return
|
|
end
|
|
group.set_allows_action(defines.input_action.begin_mining, false)
|
|
group.set_allows_action(defines.input_action.begin_mining_terrain, false)
|
|
group.set_allows_action(defines.input_action.build, false)
|
|
group.set_allows_action(defines.input_action.build_rail, false)
|
|
group.set_allows_action(defines.input_action.build_terrain, false)
|
|
group.set_allows_action(defines.input_action.cursor_split, false)
|
|
group.set_allows_action(defines.input_action.cursor_transfer, false)
|
|
group.set_allows_action(defines.input_action.fast_entity_split, false)
|
|
group.set_allows_action(defines.input_action.fast_entity_transfer, false)
|
|
group.set_allows_action(defines.input_action.inventory_split, false)
|
|
group.set_allows_action(defines.input_action.inventory_transfer, false)
|
|
group.set_allows_action(defines.input_action.place_equipment, false)
|
|
group.set_allows_action(defines.input_action.stack_split, false)
|
|
group.set_allows_action(defines.input_action.stack_transfer, false)
|
|
group.set_allows_action(defines.input_action.take_equipment, false)
|
|
group.set_allows_action(defines.input_action.paste_entity_settings, false)
|
|
end
|
|
|
|
script.on_init(create_permission_group)
|
|
script.on_configuration_changed(create_permission_group)
|
|
|
|
remote.add_interface("remote-configuration-informatron", {
|
|
informatron_menu = function(data)
|
|
return {}
|
|
end,
|
|
informatron_page_content = function(data)
|
|
-- data.page_name, data.player_index, data.element
|
|
if data.page_name == "remote-configuration-informatron" then
|
|
data.element.add{
|
|
type = "label",
|
|
caption = {"tips-and-tricks-item-description.rc-introduction"},
|
|
}
|
|
end
|
|
end
|
|
})
|