diff --git a/locale/en/caravan.cfg b/locale/en/caravan.cfg index b3e2711c..9639fe1c 100644 --- a/locale/en/caravan.cfg +++ b/locale/en/caravan.cfg @@ -18,15 +18,16 @@ starved=Starved wounded=Wounded idle=Idle -[controls] -caravan-organizer=Open the Caravan organizer GUI - [caravan-shared] open=Open __1__ +view-on-map=View on map +current-action=Current action: __1__ +current-destination=__1__m from __2__ [caravan-global-gui] -caption=Caravan organizer -empty=No caravans found +caption=Caravan manager +empty=This is the caravan manager. It will display all placed caravans and their current status. +empty-2=Currently you have no caravans. [caravan-gui] add-outpost=+ Add destination @@ -48,6 +49,7 @@ inverse-item-count=Until target has exactly N items detonate=Detonate circuit-condition=Circuit condition empty-autotrash=Collect all autotrash +traveling=Traveling [item-name] caravan-control=Caravan control diff --git a/scripts/caravan/caravan-connected-gui.lua b/scripts/caravan/caravan-connected-gui.lua index 23687b21..a0fb6905 100644 --- a/scripts/caravan/caravan-connected-gui.lua +++ b/scripts/caravan/caravan-connected-gui.lua @@ -32,14 +32,20 @@ local function instantiate_main_frame(gui, anchor) } end +function Caravan.has_any_caravan(entity) + for _, caravan_data in pairs(global.caravans) do + if has_schedule(caravan_data, entity) then return true end + end + return false +end + --anchor is optional Caravan.build_gui_connected = function(player, entity, anchor) if not entity then return end if not Caravan.has_any_caravan(entity) then return end local main_frame = instantiate_main_frame(player.gui, anchor) if not main_frame then return end - main_frame.style.horizontally_stretchable = true - main_frame.style.vertically_stretchable = true + main_frame.style.minimal_width = 300 main_frame.tags = {unit_number = entity.unit_number} local scroll_pane = main_frame.add{type = 'scroll-pane'} diff --git a/scripts/caravan/caravan-global-gui.lua b/scripts/caravan/caravan-global-gui.lua index 3ca80c28..6b4e58c1 100644 --- a/scripts/caravan/caravan-global-gui.lua +++ b/scripts/caravan/caravan-global-gui.lua @@ -9,30 +9,25 @@ end local function create_gui(gui, player) if not Caravan.has_any_caravan_at_all() then - gui.add{type = 'label', caption = {'caravan-global-gui.empty'}} + gui = gui.add{type = 'flow', direction = 'vertical'} + gui.style.horizontal_align = 'center' + gui.style.horizontally_stretchable = true + gui.add{type = 'label', caption = ''} + gui.add{type = 'label', caption = {'caravan-global-gui.empty'}}.style.single_line = false + gui.add{type = 'label', caption = {'caravan-global-gui.empty-2'}}.style.single_line = false return end local table = gui.add{ type = 'table', - column_count = 2 + column_count = 4 } for key, caravan_data in pairs(global.caravans) do - if Caravan.validity_check(caravan_data) then + if Caravan.validity_check(caravan_data) and caravan_data.entity.force_index == player.force_index then Caravan.add_gui_row(caravan_data, key, table) end end end -gui_events[defines.events.on_gui_click]['py_click_caravan_.'] = function(event) - local player = game.get_player(event.player_index) - local element = event.element - local tags = element.tags - local caravan_data = global.caravans[tags.unit_number] - if Caravan.validity_check(caravan_data) then - Caravan.build_gui(player, caravan_data.entity) - end -end - remote.add_interface('pywiki_caravan_manager', { create_gui = create_gui }) \ No newline at end of file diff --git a/scripts/caravan/caravan-gui-shared.lua b/scripts/caravan/caravan-gui-shared.lua index 76b0b541..cd570fdd 100644 --- a/scripts/caravan/caravan-gui-shared.lua +++ b/scripts/caravan/caravan-gui-shared.lua @@ -9,14 +9,6 @@ function has_schedule(caravan_data, entity) return false end --- probably bad if you have 10 k caravans... but is that even remotely relevant? -function Caravan.has_any_caravan(entity) - for _, caravan_data in pairs(global.caravans) do - if has_schedule(caravan_data, entity) then return true end - end - return false -end - function Caravan.status_img(caravan_data) local entity = caravan_data.entity if caravan_data.is_aerial then @@ -32,24 +24,179 @@ function Caravan.status_img(caravan_data) end end +local function convert_to_tooltip_row(name, count) + return {'', '\n[item=' .. name .. '] ', game.item_prototypes[name].localised_name, ' ×', count} +end + +function Caravan.get_inventory_tooltip(caravan_data) + local has_anything = false + local entity = caravan_data.entity + + local schedule = caravan_data.schedule[caravan_data.schedule_id] + local current_action = '' + if schedule then + has_anything = true + local action_id = caravan_data.action_id + local action = schedule.actions[action_id] + current_action = {'', {'caravan-shared.current-action', action and action.localised_name or {'caravan-actions.traveling'}}, '\n'} + + local destination = schedule.position + local localised_destination_name + if destination then + localised_destination_name = {'caravan-gui.map-position', math.floor(destination.x), math.floor(destination.y)} + else + local destination_entity = schedule.entity + if destination_entity and destination_entity.valid then + destination = destination_entity.position + localised_destination_name = { + 'caravan-gui.entity-position', + destination_entity.prototype.localised_name, + math.floor(destination.x), + math.floor(destination.y) + } + end + end + + if localised_destination_name then + local distance = math.sqrt((entity.position.x - destination.x) ^ 2 + (entity.position.y - destination.y) ^ 2) + distance = math.floor(distance * 10) / 10 + current_action[#current_action + 1] = {'caravan-shared.current-destination', distance, localised_destination_name} + end + end + + local fuel_inventory = caravan_data.fuel_inventory + local fuel_inventory_contents = {''} + if fuel_inventory and fuel_inventory.valid then + local i = 0 + for name, count in pairs(fuel_inventory.get_contents()) do + has_anything = true + if i == 0 then fuel_inventory_contents[#fuel_inventory_contents + 1] = '\n' end + fuel_inventory_contents[#fuel_inventory_contents + 1] = convert_to_tooltip_row(name, count) + i = i + 1 + if i == 10 then break end + end + end + + local inventory = caravan_data.inventory + local inventory_contents = {''} + if inventory and inventory.valid then + local sorted_contents = {} + for name, count in pairs(inventory.get_contents()) do + has_anything = true + sorted_contents[#sorted_contents + 1] = {name = name, count = count} + end + table.sort(sorted_contents, function(a, b) return a.count > b.count end) + + local i = 0 + for _, item in pairs(sorted_contents) do + if i == 0 then inventory_contents[#inventory_contents + 1] = '\n' end + local name, count = item.name, item.count + inventory_contents[#inventory_contents + 1] = convert_to_tooltip_row(name, count) + i = i + 1 + if i == 10 then + if #sorted_contents > 10 then + inventory_contents[#inventory_contents + 1] = {'', '\n [font=default-semibold]...[/font]'} + end + break + end + end + end + + return has_anything, {'', current_action, fuel_inventory_contents, inventory_contents} +end + +function Caravan.name_fallback(caravan_data) + local name = caravan_data.name + if name and name ~= '' then return name end + local entity = caravan_data.entity + if entity and entity.valid then return entity.prototype.localised_name end + return '' +end + function Caravan.add_gui_row(caravan_data, key, table) local entity = caravan_data.entity local prototype = prototypes[entity.name] table = table.add{type = 'frame', style = 'inside_shallow_frame_with_padding'} + table.style.maximal_width = 300 local right_flow = table.add{type = 'flow', direction = 'vertical'} local status_flow = right_flow.add{type = 'flow', direction = 'horizontal'} - status_flow.style.vertical_align = 'center' + status_flow.style.vertical_align = 'top' + + local caption_flow = status_flow.add{type = 'flow', direction = 'horizontal'} + + local title = caption_flow.add{ + name = 'title', + type = 'label', + caption = Caravan.name_fallback(caravan_data), + style = 'frame_title', + ignored_by_interaction = true + } + title.style.maximal_width = 120 + + local rename_button = caption_flow.add{ + type = 'sprite-button', + name = 'py_rename_caravan_button', + style = 'frame_action_button', + sprite = 'utility/rename_icon_small_white', + hovered_sprite = 'utility/rename_icon_small_black', + clicked_sprite = 'utility/rename_icon_small_black', + tags = {unit_number = key} + } + + status_flow.add{type = 'empty-widget'}.style.horizontally_stretchable = true + local status_sprite = status_flow.add{type = 'sprite'} status_sprite.resize_to_sprite = false status_sprite.style.size = {16, 16} local status_text = status_flow.add{type = 'label'} - status_flow.add{type = 'empty-widget', style = 'py_empty_widget'} local state, img = Caravan.status_img(caravan_data) status_text.caption = state status_sprite.sprite = img + status_text.style.right_margin = 4 + + local has_anything, tooltip = Caravan.get_inventory_tooltip(caravan_data) + local view_inventory_button = status_flow.add{ + type = 'sprite-button', + name = 'py_view_inventory_button', + style = 'frame_action_button', + sprite = 'utility/expand_dots_white', + hovered_sprite = 'utility/expand_dots', + clicked_sprite = 'utility/expand_dots', + tooltip = tooltip, + tags = {unit_number = caravan_data.unit_number} + } + view_inventory_button.visible = has_anything + + local open_caravan_button = status_flow.add{ + type = 'sprite-button', + name = 'py_click_caravan', + style = 'frame_action_button', + sprite = 'utility/logistic_network_panel_white', + hovered_sprite = 'utility/logistic_network_panel_black', + clicked_sprite = 'utility/logistic_network_panel_black', + tooltip = {'caravan-shared.open', {'entity-name.' .. entity.name}}, + tags = {unit_number = caravan_data.unit_number} + } + + local open_map_button = status_flow.add{ + type = 'sprite-button', + name = 'py_open_map_button', + style = 'frame_action_button', + sprite = 'utility/search_white', + hovered_sprite = 'utility/search_black', + clicked_sprite = 'utility/search_black', + tooltip = {'caravan-shared.view-on-map'}, + tags = {unit_number = caravan_data.unit_number} + } + + for _, button in pairs{rename_button, open_caravan_button, view_inventory_button, open_map_button} do + button.style.size = {26, 26} + button.style.top_margin = -2 + button.style.bottom_margin = -4 + end local camera_frame = right_flow.add{type = 'frame', name = 'camera_frame', style = 'py_nice_frame'} local camera = camera_frame.add{type = 'camera', name = 'camera', style = 'py_caravan_camera', position = entity.position, surface_index = entity.surface.index} @@ -57,12 +204,100 @@ function Caravan.add_gui_row(caravan_data, key, table) camera.visible = true camera.style.height = 155 camera.zoom = prototype.camera_zoom or 1 +end - local button = table.add{type = 'button', - name = 'py_click_caravan_.' .. tostring(key), - -- style = 'train_schedule_add_station_button', - caption = {'caravan-shared.open', tostring(entity.name)}, - tags = {unit_number = key, entity = caravan_data} +gui_events[defines.events.on_gui_click]['py_click_caravan'] = function(event) + local player = game.get_player(event.player_index) + local element = event.element + local tags = element.tags + local caravan_data = global.caravans[tags.unit_number] + if Caravan.validity_check(caravan_data) then + Caravan.build_gui(player, caravan_data.entity, true) + end +end + +gui_events[defines.events.on_gui_click]['py_view_inventory_button'] = function(event) + local element = event.element + local tags = element.tags + local caravan_data = global.caravans[tags.unit_number] + local has_anything, tooltip = Caravan.get_inventory_tooltip(caravan_data) + element.tooltip = tooltip + element.visible = has_anything +end + +gui_events[defines.events.on_gui_click]['py_open_map_button'] = function(event) + local player = game.get_player(event.player_index) + local element = event.element + local tags = element.tags + local caravan_data = global.caravans[tags.unit_number] + local entity = caravan_data.entity + + player.opened = nil + player.zoom_to_world(entity.position, 0.5, entity) +end + +local function title_edit_mode(caption_flow, caravan_data) + local title = caption_flow.title + local index = title.get_index_in_parent() + title.destroy() + + local textfield = caption_flow.add{ + type = 'textfield', + name = 'py_rename_caravan_textfield', + text = caravan_data.name or '', + tags = {index = caravan_data.entity.unit_number}, + index = index } - button.style.maximal_height = 24 + textfield.style.horizontally_stretchable = true + textfield.focus() + textfield.select_all() + local button = caption_flow.py_rename_caravan_button + button.style = 'item_and_count_select_confirm' + button.sprite = 'utility/check_mark' + button.hovered_sprite = 'utility/check_mark' + button.clicked_sprite = 'utility/check_mark' +end + +local function title_display_mode(caption_flow, caravan_data) + local textfield = caption_flow.py_rename_caravan_textfield + local index = textfield.get_index_in_parent() + textfield.destroy() + + local title = caption_flow.add{ + type = 'label', + name = 'title', + caption = Caravan.name_fallback(caravan_data), + style = 'frame_title', + ignored_by_interaction = true, + index = index + } + title.style.maximal_width = 120 + local button = caption_flow.py_rename_caravan_button + button.style = 'frame_action_button' + button.sprite = 'utility/rename_icon_small_white' + button.hovered_sprite = 'utility/rename_icon_small_black' + button.clicked_sprite = 'utility/rename_icon_small_black' +end + +gui_events[defines.events.on_gui_click]['py_rename_caravan_button'] = function(event) + local element = event.element + local caravan_data = global.caravans[element.tags.unit_number] + local caption_flow = element.parent + if caption_flow.title then + title_edit_mode(caption_flow, caravan_data) + else + title_display_mode(caption_flow, caravan_data) + end +end + +gui_events[defines.events.on_gui_text_changed]['py_rename_caravan_textfield'] = function(event) + local element = event.element + local caravan_data = global.caravans[element.tags.index] + caravan_data.name = element.text +end + +gui_events[defines.events.on_gui_confirmed]['py_rename_caravan_textfield'] = function(event) + local element = event.element + local caravan_data = global.caravans[element.tags.index] + title_display_mode(element.parent, caravan_data) end \ No newline at end of file diff --git a/scripts/caravan/caravan-gui.lua b/scripts/caravan/caravan-gui.lua index 0a714844..d118e3dc 100644 --- a/scripts/caravan/caravan-gui.lua +++ b/scripts/caravan/caravan-gui.lua @@ -137,12 +137,17 @@ function Caravan.build_schedule_gui(gui, caravan_data) gui.add{type = 'button', name = 'py_add_outpost', style = 'train_schedule_add_station_button', caption = {'caravan-gui.add-outpost'}} end -function Caravan.build_gui(player, entity) +function Caravan.build_gui(player, entity, from_remote_manager) local caravan_data = global.caravans[entity.unit_number] local prototype = prototypes[entity.name] local main_frame - if prototype.opens_player_inventory then + if from_remote_manager then + player.opened = nil + main_frame = player.gui.screen.add{type = 'frame', name = 'caravan_gui', caption = Caravan.name_fallback(caravan_data), direction = 'vertical'} + main_frame.auto_center = true + player.opened = main_frame + else player.opened = caravan_data.inventory local flow = player.gui.relative.add { type = 'flow', name = 'caravan_flow', @@ -154,13 +159,8 @@ function Caravan.build_gui(player, entity) } flow.style.horizontal_spacing = 0 main_frame = flow.add { - type = 'frame', name = 'caravan_gui', caption = entity.prototype.localised_name, direction = 'vertical', + type = 'frame', name = 'caravan_gui', caption = Caravan.name_fallback(caravan_data), direction = 'vertical', } - else - -- I assume this is unused? when or how is this reachable?... - main_frame = player.gui.screen.add{type = 'frame', name = 'caravan_gui', caption = entity.prototype.localised_name, direction = 'vertical'} - main_frame.auto_center = true - player.opened = main_frame end main_frame.style.width = 436 main_frame.style.minimal_height = 710