diff --git a/locale/en/pirates.cfg b/locale/en/pirates.cfg index fdfcd54b..88f1adcf 100644 --- a/locale/en/pirates.cfg +++ b/locale/en/pirates.cfg @@ -279,12 +279,12 @@ class_purchase=__1__ bought the class __2__. ([font=scenario-message-dialog]__3_ class_upgrade=__1__ upgraded their class from __2__ to __3__ ([font=scenario-message-dialog]__4__[/font]). -class_take_spare=__1__ took the spare class __2__. ([font=scenario-message-dialog]__3__[/font]) -class_give_spare=A spare __1__ class with given to __2__. ([font=scenario-message-dialog]__3__[/font]) -class_give_up=__1__ gave up __2__. -class_becomes_spare=A __1__ class is now spare. -class_give_up_error_no_class=Class error: You don't have any class to give up. -class_revoke=__1__ revoked __2__ from __3__. +# class_take_spare=__1__ took the spare class __2__. ([font=scenario-message-dialog]__3__[/font]) +# class_give_spare=A spare __1__ class with given to __2__. ([font=scenario-message-dialog]__3__[/font]) +# class_give_up=__1__ gave up __2__. +# class_becomes_spare=A __1__ class is now spare. +# class_give_up_error_no_class=Class error: You don't have any class to give up. +# class_revoke=__1__ revoked __2__ from __3__. class_purchase_error_prerequisite_class=Class purchase error: You need to be a __1__ to buy this. roles_confirm_captain=__1__ accepted the role of captain. @@ -310,8 +310,8 @@ roles_notify_looking_for_captain=Looking for a suitable captain... warn_nearly_afk_captain=Note: If you go idle as captain for too long, the role passes to another crewmember. -error_class_assign_redundant=Class error: You're already a __1__. -error_class_assign_unavailable_class=Class error: No spare class of that type is available. +# error_class_assign_redundant=Class error: You're already a __1__. +# error_class_assign_unavailable_class=Class error: No spare class of that type is available. @@ -405,17 +405,17 @@ gui_crew_window_buttons_join_crew=Join Crew gui_crew_window_buttons_join_spectators=Spectate gui_crew_window_buttons_join_spectators_tooltip=You won't be able to rejoin the crew for a short while after you do this. gui_crew_window_crewmembers=Crew Members -gui_crew_window_crewmembers_give_up_class=Give Up Class -gui_crew_window_crewmembers_give_up_class_tooltip=Give Up Class +# gui_crew_window_crewmembers_give_up_class=Give Up Class +# gui_crew_window_crewmembers_give_up_class_tooltip=Give Up Class gui_crew_window_crewmembers_resign_as_officer=Resign as Officer gui_crew_window_crewmembers_resign_as_officer_tooltip=Give up the officer role. gui_crew_window_spectators=Spectators gui_crew_window_vote_for_difficulty=Vote for Difficulty -gui_crew_window_spare_classes=Spare Classes -gui_crew_window_assign_class_button=Give class: __1__ -gui_crew_window_assign_class_button_tooltip=Give the selected player the class __1__.\n\n Class description: __2__ -gui_crew_window_selfassign_class_button=Take class: __1__ -gui_crew_window_selfassign_class_button_tooltip=Give yourself the spare class __1__.\n\n Class description: __2__ +# gui_crew_window_spare_classes=Spare Classes +# gui_crew_window_assign_class_button=Give class: __1__ +# gui_crew_window_assign_class_button_tooltip=Give the selected player the class __1__.\n\n Class description: __2__ +# gui_crew_window_selfassign_class_button=Take class: __1__ +# gui_crew_window_selfassign_class_button_tooltip=Give yourself the spare class __1__.\n\n Class description: __2__ gui_crew_window_captains_actions=Captain's Actions gui_crew_window_captains_actions_disband_crew=Disband Crew gui_crew_window_captains_actions_disband_crew_tooltip=End the run. You will be prompted again after clicking. @@ -431,8 +431,8 @@ gui_crew_window_captains_actions_make_officer=Make Officer gui_crew_window_captains_actions_make_officer_tooltip=Make this player an Officer. (or use /officer {player}) gui_crew_window_captains_actions_unmake_officer=Unmake Officer gui_crew_window_captains_actions_unmake_officer_tooltip=Remove this player as an Officer. (or use /officer {player}) -gui_crew_window_captains_actions_revoke_class=Revoke Class -gui_crew_window_captains_actions_revoke_class_tooltip=Put this player's class back in the Spare Classes pool. +# gui_crew_window_captains_actions_revoke_class=Revoke Class +# gui_crew_window_captains_actions_revoke_class_tooltip=Put this player's class back in the Spare Classes pool. gui_crew_window_captains_actions_summon_crew=Summon Crew to Ship gui_crew_window_captains_actions_summon_crew_tooltip=Teleport crewmembers to the ship. gui_crew_window_captains_actions_tax=Tax Crew @@ -443,6 +443,16 @@ gui_crew_window_crew_age=Age: __1__ gui_crew_window_crew_capacity_and_difficulty=__1__, Capacity __2__. gui_crew_window_crew_count=Crew Members (__1__) +gui_classes=Classes +gui_classes_available_classes=Available classes +gui_classes_taken_by=Taken by +gui_classes_actions=Actions +gui_classes_take=Take +gui_classes_drop=Drop +gui_classes_take_enabled_tooltip=Equip the class to receive bonuses! +gui_classes_take_disabled_tooltip=This class is currently in use by someone +gui_classes_drop_tooltip=Unequip the class to allow someone else to take it + gui_evo_tooltip_1=Local Biter Evolution: __1__ gui_evo_tooltip_2=Leagues: __1__ gui_evo_tooltip_3=Kraken: __1__ @@ -534,8 +544,13 @@ gui_questframe_nodamage=Island Quest: No Damage\n\nLaunch a rocket without the s gui_crew_tooltip_1=Your Crew\n\nPerform crew actions, such as selecting a class if any are available. gui_crew_tooltip_2=Your Crew\n\nYou're a free agent, so there's nothing to do here. +gui_classes_tooltip_1=Class panel\n\nShows list of unlocked classes and let's you take them.\nClasses will start to appear in the list as you unlock them. +gui_classes_tooltip_2=Class panel\n\nYou're a free agent, so there's nothing to do here. + gui_progress_tooltip=Progress: __1__ leagues.\n\nTravel __2__ leagues to win the game. +gui_close_button=Close + cmd_notify_set_max_crews=The maximum number of concurrent crews has been set to __1__. cmd_error_not_admin=[ERROR] Only admins are allowed to run this command! diff --git a/maps/pirates/api_events.lua b/maps/pirates/api_events.lua index 6af213ae..d45fbee3 100644 --- a/maps/pirates/api_events.lua +++ b/maps/pirates/api_events.lua @@ -216,7 +216,14 @@ local function damage_to_artillery(event) if not event.cause.name then return end - if (event.cause.name == 'small-biter') or (event.cause.name == 'small-spitter') or (event.cause.name == 'medium-biter') or (event.cause.name == 'medium-spitter') or (event.cause.name == 'big-biter') or (event.cause.name == 'big-spitter') or (event.cause.name == 'behemoth-biter') or (event.cause.name == 'behemoth-spitter') then + if (event.cause.name == 'small-biter') + or (event.cause.name == 'small-spitter') + or (event.cause.name == 'medium-biter') + or (event.cause.name == 'medium-spitter') + or (event.cause.name == 'big-biter') + or (event.cause.name == 'big-spitter') + or (event.cause.name == 'behemoth-biter') + or (event.cause.name == 'behemoth-spitter') then if event.cause.force.name ~= memory.enemy_force_name then return end -- play alert sound for all crew members @@ -1064,7 +1071,7 @@ local function event_on_entity_died(event) local crew_id = Common.get_id_from_force_name(entity.force.name) Memory.set_working_id(crew_id) local memory = Memory.get_crew_memory() - if memory.id == 0 then return end + if not Common.is_id_valid(memory.id) then return end base_kill_rewards(event) @@ -1228,9 +1235,9 @@ local function event_on_player_joined_game(event) end local crew_to_put_back_in = nil - for _, mem in pairs(global_memory.crew_memories) do - if mem.id and mem.crewstatus and mem.crewstatus == Crew.enum.ADVENTURING and mem.temporarily_logged_off_characters[player.index] then - crew_to_put_back_in = mem.id + for _, memory in pairs(global_memory.crew_memories) do + if Common.is_id_valid(memory.id) and memory.crewstatus and memory.crewstatus == Crew.enum.ADVENTURING and memory.temporarily_logged_off_characters[player.index] then + crew_to_put_back_in = memory.id break end end @@ -1267,9 +1274,17 @@ local function event_on_player_joined_game(event) -- Auto-join the oldest crew: local ages = {} - for _, mem in pairs(global_memory.crew_memories) do - if mem.id and mem.crewstatus and mem.crewstatus == Crew.enum.ADVENTURING and mem.capacity and mem.crewplayerindices and #mem.crewplayerindices < mem.capacity and (not (mem.tempbanned_from_joining_data and mem.tempbanned_from_joining_data[player.index] and game.tick < mem.tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks)) then - ages[#ages+1] = {id = mem.id, age = mem.age, large = (mem.capacity >= Common.minimum_run_capacity_to_enforce_space_for)} + for _, memory in pairs(global_memory.crew_memories) do + if Common.is_id_valid(memory.id) + and memory.crewstatus + and memory.crewstatus == Crew.enum.ADVENTURING + and memory.capacity + and memory.crewplayerindices + and #memory.crewplayerindices < memory.capacity + and (not (memory.tempbanned_from_joining_data + and memory.tempbanned_from_joining_data[player.index] + and game.tick < memory.tempbanned_from_joining_data[player.index] + Common.ban_from_rejoining_crew_ticks)) then + ages[#ages+1] = {id = memory.id, age = memory.age, large = (memory.capacity >= Common.minimum_run_capacity_to_enforce_space_for)} end end table.sort( @@ -1570,14 +1585,38 @@ function Public.event_on_chunk_generated(event) if (p.x >= -width/2 and p.y >=-height/2 and p.x <= width/2 and p.y <= height/2) then - terrain_fn{p = Utils.psum{p, {1, terraingen_coordinates_offset}}, true_p = p, true_left_top = chunk_left_top, left_top = Utils.psum{chunk_left_top, {1, terraingen_coordinates_offset}}, noise_generator = noise_generator, static_params = static_params, tiles = tiles, entities = entities, decoratives = decoratives, specials = specials, seed = seed, other_map_generation_data = other_map_generation_data, iconized_generation = false} + terrain_fn{ + p = Utils.psum{p, {1, terraingen_coordinates_offset}}, + true_p = p, + true_left_top = chunk_left_top, + left_top = Utils.psum{chunk_left_top, {1, terraingen_coordinates_offset}}, + noise_generator = noise_generator, + static_params = static_params, + tiles = tiles, + entities = entities, + decoratives = decoratives, + specials = specials, + seed = seed, + other_map_generation_data = other_map_generation_data, + iconized_generation = false + } else tiles[#tiles + 1] = {name = 'out-of-map', position = Utils.psum{p, {1, terraingen_coordinates_offset}}} end end end - chunk_structures_fn{true_left_top = chunk_left_top, left_top = Utils.psum{chunk_left_top, {1, terraingen_coordinates_offset}}, noise_generator = noise_generator, static_params = static_params, specials = specials, entities = entities, seed = seed, other_map_generation_data = other_map_generation_data, biter_base_density_scale = Balance.biter_base_density_scale()} + chunk_structures_fn{ + true_left_top = chunk_left_top, + left_top = Utils.psum{chunk_left_top, {1, terraingen_coordinates_offset}}, + noise_generator = noise_generator, + static_params = static_params, + specials = specials, + entities = entities, + seed = seed, + other_map_generation_data = other_map_generation_data, + biter_base_density_scale = Balance.biter_base_density_scale() + } local tiles_corrected = {} for i = 1, #tiles do @@ -1810,7 +1849,7 @@ local remove_boost_movement_speed_on_respawn = Memory.set_working_id(crew_id) local memory = Memory.get_crew_memory() - if not (memory.id and memory.id > 0) then return end --check if crew disbanded + if not Common.is_id_valid(memory.id) then return end --check if crew disbanded if memory.game_lost then return end memory.speed_boost_characters[player.index] = nil @@ -1830,7 +1869,7 @@ local boost_movement_speed_on_respawn = Memory.set_working_id(crew_id) local memory = Memory.get_crew_memory() - if not (memory.id and memory.id > 0) then return end --check if crew disbanded + if not Common.is_id_valid(memory.id) then return end --check if crew disbanded if memory.game_lost then return end memory.speed_boost_characters[player.index] = true diff --git a/maps/pirates/api_on_tick.lua b/maps/pirates/api_on_tick.lua index 580c22f8..afed2e4d 100644 --- a/maps/pirates/api_on_tick.lua +++ b/maps/pirates/api_on_tick.lua @@ -1443,7 +1443,7 @@ end function Public.update_alert_sound_frequency_tracker() local memory = Memory.get_crew_memory() - if memory.seconds_until_alert_sound_can_be_played_again > 0 then + if memory.seconds_until_alert_sound_can_be_played_again and memory.seconds_until_alert_sound_can_be_played_again > 0 then memory.seconds_until_alert_sound_can_be_played_again = memory.seconds_until_alert_sound_can_be_played_again - 1 memory.seconds_until_alert_sound_can_be_played_again = Math.max(0, memory.seconds_until_alert_sound_can_be_played_again) end diff --git a/maps/pirates/commands.lua b/maps/pirates/commands.lua index 77b37abd..85710195 100644 --- a/maps/pirates/commands.lua +++ b/maps/pirates/commands.lua @@ -156,6 +156,9 @@ commands.add_command( function(cmd) cmd_set_memory(cmd) + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + --local param = tostring(cmd.parameter) if check_admin(cmd) then --local player = game.players[cmd.player_index] @@ -173,6 +176,9 @@ commands.add_command( function(cmd) cmd_set_memory(cmd) + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + local param = tostring(cmd.parameter) if check_admin(cmd) then local player = game.players[cmd.player_index] @@ -190,6 +196,9 @@ commands.add_command( function(cmd) cmd_set_memory(cmd) + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + --local param = tostring(cmd.parameter) if check_admin(cmd) then Crew.summon_crew() @@ -201,6 +210,10 @@ commands.add_command( {'pirates.cmd_explain_ok'}, function(cmd) cmd_set_memory(cmd) + + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + local player = game.players[cmd.player_index] if not Common.validate_player(player) then return end @@ -249,7 +262,7 @@ function(cmd) if not Common.validate_player(player) then return end if param and param ~= 'nil' then - local string = Roles.get_class_print_string(param, true) + local string = Roles.get_class_print_string(param, true, true) if string then Common.notify_player_expected(player, {'', {'pirates.class_definition_for'}, ' ', string}) else @@ -265,6 +278,10 @@ commands.add_command( {'pirates.cmd_explain_take'}, function(cmd) cmd_set_memory(cmd) + + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + local param = tostring(cmd.parameter) local player = game.players[cmd.player_index] if not Common.validate_player(player) then return end @@ -272,7 +289,7 @@ function(cmd) if param and param ~= 'nil' then for _, class in pairs(Classes.enum) do if Classes.eng_form[class]:lower() == param:lower() then - Classes.assign_class(player.index, class, true) + Classes.assign_class(player.index, class) return true end end @@ -289,11 +306,15 @@ commands.add_command( {'pirates.cmd_explain_giveup'}, function(cmd) cmd_set_memory(cmd) + + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + --local param = tostring(cmd.parameter) local player = game.players[cmd.player_index] if not Common.validate_player(player) then return end - Classes.try_renounce_class(player, true) + Classes.assign_class(player.index, nil) end) commands.add_command( @@ -371,6 +392,9 @@ commands.add_command( function(cmd) cmd_set_memory(cmd) + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end + local player = game.players[cmd.player_index] local param = tostring(cmd.parameter) if check_captain_or_admin(cmd) then @@ -387,6 +411,9 @@ commands.add_command( {'pirates.cmd_explain_officer'}, function(cmd) cmd_set_memory(cmd) + + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end local player = game.players[cmd.player_index] local param = tostring(cmd.parameter) @@ -409,6 +436,9 @@ commands.add_command( {'pirates.cmd_explain_undock'}, function(cmd) cmd_set_memory(cmd) + + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end --local param = tostring(cmd.parameter) if check_captain_or_admin(cmd) then @@ -427,6 +457,9 @@ commands.add_command( {'pirates.cmd_explain_tax'}, function(cmd) cmd_set_memory(cmd) + + local memory = Memory.get_crew_memory() + if not Common.is_id_valid(memory.id) then return end --local param = tostring(cmd.parameter) if check_captain(cmd) then diff --git a/maps/pirates/common.lua b/maps/pirates/common.lua index cfdc6401..c2b6fd14 100644 --- a/maps/pirates/common.lua +++ b/maps/pirates/common.lua @@ -112,7 +112,7 @@ end function Public.activecrewcount() local global_memory = Memory.get_global_memory() local memory = Memory.get_crew_memory() - if memory.id == 0 then return 0 end + if not Public.is_id_valid(memory.id) then return 0 end local count = 0 for _, id in pairs(memory.crewplayerindices) do @@ -815,7 +815,7 @@ end function Public.crew_get_crew_members() local memory = Memory.get_crew_memory() - if memory.id == 0 then return {} end + if not Public.is_id_valid(memory.id) then return {} end local playerlist = {} for _, id in pairs(memory.crewplayerindices) do @@ -828,7 +828,7 @@ end function Public.crew_get_crew_members_and_spectators() local memory = Memory.get_crew_memory() - if memory.id == 0 then return {} end + if not Public.is_id_valid(memory.id) then return {} end local playerlist = {} for _, id in pairs(memory.crewplayerindices) do @@ -848,7 +848,7 @@ end function Public.crew_get_nonafk_crew_members() local global_memory = Memory.get_global_memory() local memory = Memory.get_crew_memory() - if memory.id == 0 then return {} end + if not Public.is_id_valid(memory.id) then return {} end local playerlist = {} for _, id in pairs(memory.crewplayerindices) do @@ -1510,4 +1510,12 @@ function Public.get_id_from_force_name(force_name) return tonumber(string.sub(force_name, -3, -1)) or nil end +function Public.is_id_valid(id) + if id and id ~= 0 then + return true + else + return false + end +end + return Public \ No newline at end of file diff --git a/maps/pirates/crew.lua b/maps/pirates/crew.lua index 3703672f..b1c8b64f 100644 --- a/maps/pirates/crew.lua +++ b/maps/pirates/crew.lua @@ -734,6 +734,7 @@ function Public.initialise_crew(accepted_proposal) memory.classes_table = {} memory.officers_table = {} memory.spare_classes = {} + memory.unlocked_classes = {} memory.healthbars = {} memory.overworld_krakens = {} @@ -1034,5 +1035,4 @@ function Public.reset_crew_and_enemy_force(id) end - return Public \ No newline at end of file diff --git a/maps/pirates/gui/classes.lua b/maps/pirates/gui/classes.lua new file mode 100644 index 00000000..333c7664 --- /dev/null +++ b/maps/pirates/gui/classes.lua @@ -0,0 +1,251 @@ +-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/danielmartin0/ComfyFactorio-Pirates. + + +local Memory = require 'maps.pirates.memory' +local Common = require 'maps.pirates.common' +local Classes = require 'maps.pirates.roles.classes' +local GuiCommon = require 'maps.pirates.gui.common' +local Public = {} + + +local window_name = 'classes' + +local widths = {} +widths['available_classes'] = 150 +widths['taken_by'] = 150 +widths['action_buttons'] = 100 + +-- used to track whether new class entries should be added during "full_update" +local entry_count = 0 + +local function add_class_entry(player, class, taken_by_player_index, index) + if not player.gui.screen[window_name .. '_piratewindow'] then return end + local flow + flow = player.gui.screen[window_name .. '_piratewindow'] + + local class_list_panel_table = flow.scroll_pane.class_list_panel_table + + + -- Class label + local explanation = Classes.explanation_advanced(class, false) + local full_explanation + + if Classes.class_purchase_requirement[class] then + full_explanation = {'pirates.class_explanation_upgraded_class', Classes.display_form(class), Classes.display_form(Classes.class_purchase_requirement[class]), explanation} + else + full_explanation = {'pirates.class_explanation', Classes.display_form(class), explanation} + end + + local available_class_label = class_list_panel_table.add({ + type = 'label', + caption = Classes.display_form(class), + tooltip = full_explanation, + }) + available_class_label.style.minimal_width = widths['available_classes'] + available_class_label.style.maximal_width = widths['available_classes'] + + + -- Player label + local taken_by_player_name = taken_by_player_index and game.players[taken_by_player_index].name or '' + local taken_by_label = class_list_panel_table.add({ + name = 'player_label' .. index, + type = 'label', + caption = taken_by_player_name, + }) + taken_by_label.style.minimal_width = widths['taken_by'] + taken_by_label.style.maximal_width = widths['taken_by'] + + -- Button + local button + + if not taken_by_player_index then + button = class_list_panel_table.add({ + name = 'button' .. index, + type = 'button', + caption = {'pirates.gui_classes_take'}, + tooltip = {'pirates.gui_classes_take_enabled_tooltip'}, + }) + elseif taken_by_player_index == player.index then + button = class_list_panel_table.add({ + name = 'button' .. index, + type = 'button', + caption = {'pirates.gui_classes_drop'}, + tooltip = {'pirates.gui_classes_drop_tooltip'}, + }) + button.style.font_color = {r=1, g=0, b=0} + button.style.hovered_font_color = {r=1, g=0, b=0} + button.style.clicked_font_color = {r=1, g=0, b=0} + else + button = class_list_panel_table.add({ + name = 'button' .. index, + type = 'button', + enabled = false, -- wanted to make "visble = false" instead, but table doesn't like that + caption = {'pirates.gui_classes_take'}, + tooltip = {'pirates.gui_classes_take_disabled_tooltip'}, + }) + end + button.tags = {type = 'pirates_' .. window_name, index = index} + + button.style.minimal_width = widths['action_buttons'] + button.style.maximal_width = widths['action_buttons'] +end + +function Public.toggle_window(player) + local memory = Memory.get_crew_memory() + local flow, flow2, flow3, flow4 + + --*** OVERALL FLOW ***-- + if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end + + if not Common.is_id_valid(memory.id) then return end + + flow = GuiCommon.new_window(player, window_name) + flow.caption = {'pirates.gui_classes'} + flow.auto_center = true + flow.style.maximal_width = 500 + + flow2 = flow.add({ + name = 'headers', + type = 'flow', + direction = 'horizontal', + }) + + flow3 = flow2.add({ + name = 'available_classes', + type = 'label', + caption = {'pirates.gui_classes_available_classes'}, + }) + flow3.style.minimal_width = widths['available_classes'] + flow3.style.maximal_width = widths['available_classes'] + flow3.style.font = 'heading-2' + flow3.style.font_color = GuiCommon.section_header_font_color + + flow3 = flow2.add({ + name = 'taken_by', + type = 'label', + caption = {'pirates.gui_classes_taken_by'}, + }) + flow3.style.minimal_width = widths['taken_by'] + flow3.style.maximal_width = widths['taken_by'] + flow3.style.font = 'heading-2' + flow3.style.font_color = GuiCommon.section_header_font_color + + flow3 = flow2.add({ + name = 'action_buttons', + type = 'label', + caption = {'pirates.gui_classes_actions'}, + }) + flow3.style.minimal_width = widths['action_buttons'] + flow3.style.maximal_width = widths['action_buttons'] + flow3.style.font = 'heading-2' + flow3.style.font_color = GuiCommon.section_header_font_color + + -- List management + local scroll_pane = flow.add { + type = 'scroll-pane', + name = 'scroll_pane', + direction = 'vertical', + horizontal_scroll_policy = 'never', + vertical_scroll_policy = 'auto' + } + scroll_pane.style.maximal_height = 500 + scroll_pane.style.bottom_padding = 20 + + scroll_pane.add{ + type = 'table', + name = 'class_list_panel_table', + column_count = 3 + } + + for i, class_entry in ipairs(memory.unlocked_classes) do + add_class_entry(player, class_entry.class, class_entry.taken_by, i) + end + + entry_count = #memory.unlocked_classes + + GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton') +end + + + +function Public.full_update(player) + if not player.gui.screen[window_name .. '_piratewindow'] then return end + local flow = player.gui.screen[window_name .. '_piratewindow'] + + local memory = Memory.get_crew_memory() + + --*** Overwrite contents ***-- + + local class_list_panel_table = flow.scroll_pane.class_list_panel_table + + -- Currently assuming class list size never decreases + + -- Update current content table + for i = 1, entry_count do + local label = class_list_panel_table['player_label' .. i] + local class_entry = memory.unlocked_classes[i] + label.caption = class_entry.taken_by and game.players[class_entry.taken_by].name or '' + + local black = {r=0, g=0, b=0} + local red = {r=1, g=0, b=0} + + local button = class_list_panel_table['button' .. i] + if not class_entry.taken_by then + button.caption = {'pirates.gui_classes_take'} + button.tooltip = {'pirates.gui_classes_take_enabled_tooltip'} + button.style.font_color = black + button.style.hovered_font_color = black + button.style.clicked_font_color = black + elseif class_entry.taken_by == player.index then + button.caption = {'pirates.gui_classes_drop'} + button.tooltip = {'pirates.gui_classes_drop_tooltip'} + button.style.font_color = red + button.style.hovered_font_color = red + button.style.clicked_font_color = red + else + button.caption = {'pirates.gui_classes_take'} + button.tooltip = {'pirates.gui_classes_take_disabled_tooltip'} + button.style.font_color = black + button.style.hovered_font_color = black + button.style.clicked_font_color = black + end + end + + + -- If new entries were added since last update, add them to GUI + if entry_count ~= #memory.unlocked_classes then + for i = entry_count + 1, #memory.unlocked_classes do + local class_entry = memory.unlocked_classes[i] + add_class_entry(player, class_entry.class, class_entry.taken_by, i) + end + + entry_count = #memory.unlocked_classes + end +end + + +function Public.click(event) + local player = game.players[event.element.player_index] + if not player.gui.screen[window_name .. '_piratewindow'] then return end + + local tags = event.element.tags + if not tags then return end + if tags.type ~= 'pirates_' .. window_name then return end + + local memory = Memory.get_crew_memory() + + if tags.index then + local button_is_take_class = (not memory.unlocked_classes[tags.index].taken_by) and true or false + + if button_is_take_class then + local class_to_assign = memory.unlocked_classes[tags.index].class + Classes.assign_class(player.index, class_to_assign, tags.index) + return + else -- button is drop class + Classes.assign_class(player.index, nil, tags.index) + return + end + end +end + +return Public \ No newline at end of file diff --git a/maps/pirates/gui/common.lua b/maps/pirates/gui/common.lua index cdbace5d..99f6d35f 100644 --- a/maps/pirates/gui/common.lua +++ b/maps/pirates/gui/common.lua @@ -323,7 +323,7 @@ function Public.flow_add_close_button(flow, close_button_name) flow4 = flow3.add({ type = 'button', name = close_button_name, - caption = 'Close', + caption = {'pirates.gui_close_button'}, }) flow4.style = 'back_button' flow4.style.minimal_width = 90 diff --git a/maps/pirates/gui/crew.lua b/maps/pirates/gui/crew.lua index c9d30437..aa2fbdd9 100644 --- a/maps/pirates/gui/crew.lua +++ b/maps/pirates/gui/crew.lua @@ -4,15 +4,8 @@ local Memory = require 'maps.pirates.memory' local Common = require 'maps.pirates.common' local Utils = require 'maps.pirates.utils_local' --- local Math = require 'maps.pirates.math' --- local Surfaces = require 'maps.pirates.surfaces.surfaces' local Roles = require 'maps.pirates.roles.roles' -local Classes = require 'maps.pirates.roles.classes' local Crew = require 'maps.pirates.crew' --- local Progression = require 'maps.pirates.progression' --- local Structures = require 'maps.pirates.structures.structures' -local _inspect = require 'utils.inspect'.inspect --- local Boats = require 'maps.pirates.structures.boats.boats' local GuiCommon = require 'maps.pirates.gui.common' local CoreData = require 'maps.pirates.coredata' local Server = require 'utils.server' @@ -25,13 +18,24 @@ local window_name = 'crew' function Public.toggle_window(player) local memory = Memory.get_crew_memory() local flow, flow2, flow3, flow4 + local window --*** OVERALL FLOW ***-- if player.gui.screen[window_name .. '_piratewindow'] then player.gui.screen[window_name .. '_piratewindow'].destroy() return end - if not memory.id then return end + if not Common.is_id_valid(memory.id) then return end - flow = GuiCommon.new_window(player, window_name) + window = GuiCommon.new_window(player, window_name) + + flow = window.add { + type = 'scroll-pane', + name = 'scroll_pane', + direction = 'vertical', + horizontal_scroll_policy = 'never', + vertical_scroll_policy = 'auto' + } + flow.style.maximal_height = 500 + flow.style.bottom_padding = 20 --*** PARAMETERS OF RUN ***-- @@ -126,16 +130,6 @@ function Public.toggle_window(player) flow3.style.margin = 2 flow3.style.maximal_height = 350 - flow3 = flow2.add({ - name = 'class_renounce', - type = 'button', - caption = {'pirates.gui_crew_window_crewmembers_give_up_class'}, - }) - flow3.style.minimal_width = 95 - flow3.style.font = 'default-bold' - flow3.style.font_color = {r=0.10, g=0.10, b=0.10} - flow3.tooltip = {'pirates.gui_crew_window_crewmembers_give_up_class_tooltip'} - flow3 = flow2.add({ name = 'officer_resign', type = 'button', @@ -171,53 +165,6 @@ function Public.toggle_window(player) flow3.style.font_color = {r=0.10, g=0.10, b=0.10} end - --*** SPARE CLASSES ***-- - - flow2 = GuiCommon.flow_add_section(flow, 'spare_classes', {'pirates.gui_crew_window_spare_classes'}) - - flow3 = flow2.add({ - name = 'list', - type = 'label', - }) - flow3.style.left_margin = 5 - flow3.style.top_margin = -3 - flow3.style.bottom_margin = -3 - flow3.style.single_line = false - flow3.style.maximal_width = 160 - flow3.style.font = 'default-dropdown' - - flow3 = flow2.add({ - name = 'assign_flow', - type = 'flow', - direction = 'vertical', - }) - flow3.style.top_margin = 3 - - for _, c in pairs(Classes.enum) do - flow4 = flow3.add({ - name = 'assign_class_' .. c, - type = 'button', - caption = {'pirates.gui_crew_window_assign_class_button', Classes.display_form(c)}, - }) - flow4.style.minimal_width = 95 - flow4.style.font = 'default-bold' - flow4.style.font_color = {r=0.10, g=0.10, b=0.10} - flow4.tooltip = {'pirates.gui_crew_window_assign_class_button_tooltip', Classes.display_form(c), Classes.explanation(c)} - -- flow4.tooltip = 'Give this class to the selected player.' - end - - for _, c in pairs(Classes.enum) do - flow4 = flow3.add({ - name = 'selfassign_class_' .. c, - type = 'button', - caption = {'pirates.gui_crew_window_selfassign_class_button', Classes.display_form(c)}, - }) - flow4.style.minimal_width = 95 - flow4.style.font = 'default-bold' - flow4.style.font_color = {r=0.10, g=0.10, b=0.10} - flow4.tooltip = {'pirates.gui_crew_window_selfassign_class_button_tooltip', Classes.display_form(c), Classes.explanation(c)} - end - --*** CAPTAIN's ACTIONS ***-- flow2 = GuiCommon.flow_add_section(flow, 'captain', {'pirates.gui_crew_window_captains_actions'}) @@ -310,16 +257,6 @@ function Public.toggle_window(player) flow3.style.font_color = {r=0.10, g=0.10, b=0.10} flow3.tooltip = {'pirates.gui_crew_window_captains_actions_unmake_officer_tooltip'} - flow3 = flow2.add({ - name = 'revoke_class', - type = 'button', - caption = {'pirates.gui_crew_window_captains_actions_revoke_class'}, - }) - flow3.style.minimal_width = 95 - flow3.style.font = 'default-bold' - flow3.style.font_color = {r=0.10, g=0.10, b=0.10} - flow3.tooltip = {'pirates.gui_crew_window_captains_actions_revoke_class_tooltip'} - flow3 = flow2.add({ name = 'capn_summon_crew', type = 'button', @@ -353,9 +290,8 @@ function Public.toggle_window(player) flow2.style.font = 'default' flow2.caption = {'pirates.gui_crew_window_captains_actions_undock_tip'} - -- - GuiCommon.flow_add_close_button(flow, window_name .. '_piratebutton') + GuiCommon.flow_add_close_button(window, window_name .. '_piratebutton') end @@ -370,58 +306,22 @@ function Public.full_update(player) if Public.regular_update then Public.regular_update(player) end if not player.gui.screen[window_name .. '_piratewindow'] then return end - local flow = player.gui.screen[window_name .. '_piratewindow'] + local window = player.gui.screen[window_name .. '_piratewindow'] + local flow = window.scroll_pane + local memory = Memory.get_crew_memory() local playercrew_status = GuiCommon.crew_overall_state_bools(player.index) - -- local destination = Common.current_destination() --*** WHAT TO SHOW ***-- flow.difficulty_vote.visible = memory.overworldx and memory.overworldx == 0 - flow.members.body.class_renounce.visible = memory.classes_table and memory.classes_table[player.index] - flow.members.body.officer_resign.visible = memory.officers_table and memory.officers_table[player.index] - flow.spare_classes.visible = memory.spare_classes and #memory.spare_classes > 0 - local other_player_selected = flow.members.body.members_listbox.selected_index ~= 0 and tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2]) ~= player.index - local any_class_button = false - for _, c in pairs(Classes.enum) do - if memory.spare_classes and Utils.contains(memory.spare_classes, c) and (not (player.controller_type == defines.controllers.spectator)) then - if Common.is_captain(player) and memory.crewplayerindices and #memory.crewplayerindices > 1 then - if other_player_selected and (not (memory.classes_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])])) then - flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false - flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = true - any_class_button = true - else - flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = false - if (not memory.classes_table[player.index]) then - flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = true - any_class_button = true - else - flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false - end - end - else - flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = false - if (not memory.classes_table[player.index]) then - flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = true - any_class_button = true - else - flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false - end - end - else - flow.spare_classes.body.assign_flow['assign_class_' .. c].visible = false - flow.spare_classes.body.assign_flow['selfassign_class_' .. c].visible = false - end - end - flow.spare_classes.body.assign_flow.visible = any_class_button - flow.captain.visible = Common.is_captain(player) flow.undock_tip.visible = Common.is_captain(player) @@ -431,8 +331,6 @@ function Public.full_update(player) flow.captain.body.make_officer.visible = other_player_selected and (not (memory.officers_table and memory.officers_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])])) flow.captain.body.unmake_officer.visible = other_player_selected and ((memory.officers_table and memory.officers_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])])) - flow.captain.body.revoke_class.visible = other_player_selected and (memory.classes_table[tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2])]) - -- flow.captain.body.capn_undock_normal.visible = memory.boat and memory.boat.state and ((memory.boat.state == Boats.enum_state.LANDED) or (memory.boat.state == Boats.enum_state.APPROACHING) or (memory.boat.state == Boats.enum_state.DOCKED)) flow.captain.body.capn_summon_crew.visible = false @@ -467,27 +365,11 @@ function Public.full_update(player) --== UPDATE CONTENT ==-- - if memory.id then - flow.caption = memory.name + if Common.is_id_valid(memory.id) then + window.caption = memory.name flow.crew_age.caption = {'pirates.gui_crew_window_crew_age', Utils.time_mediumform((memory.age or 0)/60)} flow.crew_capacity_and_difficulty.caption = {'pirates.gui_crew_window_crew_capacity_and_difficulty', CoreData.difficulty_options[memory.difficulty_option].text, CoreData.capacity_options[memory.capacity_option].text3} - - if flow.spare_classes.visible then - local str = {''} - - for i, c in ipairs(memory.spare_classes) do - if i>1 then - str[#str+1] = {'', {'pirates.separator_1'}, Classes.display_form(c)} -- we need to do nesting here, because you can't contanenate more than 20 localised strings. Watch out! - --@TODO: In fact we should nest iteratively, as this still caps out around 19 classes. - else - str[#str+1] = {'', Classes.display_form(c)} - end - end - str[#str+1] = '.' - - flow.spare_classes.body.list.caption = str - end end @@ -563,18 +445,6 @@ function Public.click(event) -- return -- end - - if string.sub(eventname, 1, 13) and string.sub(eventname, 1, 13) == 'assign_class_' then - local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2]) - Classes.assign_class(other_id, string.sub(eventname, 14, -1)) - return - end - - if string.sub(eventname, 1, 17) and string.sub(eventname, 1, 17) == 'selfassign_class_' then - Classes.assign_class(player.index, string.sub(eventname, 18, -1), true) - return - end - if string.sub(eventname, 1, 18) and string.sub(eventname, 1, 18) == 'difficulty_option_' then Crew.difficulty_vote(player.index, tonumber(string.sub(eventname, 19, -1))) return @@ -597,11 +467,6 @@ function Public.click(event) return end - if eventname == 'class_renounce' then - Classes.try_renounce_class(player, true) - return - end - if eventname == 'capn_renounce' then Roles.renounce_captainhood(player) return @@ -653,12 +518,6 @@ function Public.click(event) return end - if eventname == 'revoke_class' then - local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2]) - Roles.revoke_class(player, game.players[other_id]) - return - end - if eventname == 'capn_plank' then local other_id = tonumber(flow.members.body.members_listbox.get_item(flow.members.body.members_listbox.selected_index)[2]) diff --git a/maps/pirates/gui/gui.lua b/maps/pirates/gui/gui.lua index 89225f3d..12ea8ffa 100644 --- a/maps/pirates/gui/gui.lua +++ b/maps/pirates/gui/gui.lua @@ -9,6 +9,7 @@ local GuiEvo = require 'maps.pirates.gui.evo' local GuiProgress = require 'maps.pirates.gui.progress' local GuiRuns = require 'maps.pirates.gui.runs' local GuiCrew = require 'maps.pirates.gui.crew' +local GuiClasses = require 'maps.pirates.gui.classes' local GuiFuel = require 'maps.pirates.gui.fuel' local GuiMinimap = require 'maps.pirates.gui.minimap' local GuiInfo = require 'maps.pirates.gui.info' @@ -45,6 +46,7 @@ Public.enum = enum Public.progress = require 'maps.pirates.gui.progress' Public.runs = require 'maps.pirates.gui.runs' Public.crew = require 'maps.pirates.gui.crew' +Public.classes = require 'maps.pirates.gui.classes' Public.fuel = require 'maps.pirates.gui.fuel' Public.minimap = require 'maps.pirates.gui.minimap' Public.info = require 'maps.pirates.gui.info' @@ -101,6 +103,9 @@ local function create_gui(player) flow2 = GuiCommon.flow_add_floating_sprite_button(flow1, 'crew_piratebutton') flow2.sprite = 'utility/spawn_flag' + flow2 = GuiCommon.flow_add_floating_sprite_button(flow1, 'classes_piratebutton') + flow2.sprite = 'item/light-armor' + -- flow2 = GuiCommon.flow_add_floating_sprite_button(flow1, 'lives_piratebutton') -- flow2.tooltip = 'Lives\n\nWhen a silo is destroyed before its rocket is launched, you lose a life.\n\nLosing all your lives is one way to lose the game.' -- flow2.mouse_button_filter = {'middle'} @@ -902,7 +907,7 @@ function Public.update_gui(player) flow1 = pirates_flow.crew_piratebutton_frame.crew_piratebutton - if memory.id and memory.id ~= 0 then + if Common.is_id_valid(memory.id) then flow1.tooltip = {'pirates.gui_crew_tooltip_1'} flow1.mouse_button_filter = {'left','right'} else @@ -913,10 +918,24 @@ function Public.update_gui(player) end end + flow1 = pirates_flow.classes_piratebutton_frame.classes_piratebutton + + if Common.is_id_valid(memory.id) then + flow1.tooltip = {'pirates.gui_classes_tooltip_1'} + flow1.mouse_button_filter = {'left','right'} + else + flow1.tooltip = {'pirates.gui_classes_tooltip_2'} + flow1.mouse_button_filter = {'middle'} --hack to avoid press visual + if player.gui.screen['classes_piratewindow'] then + player.gui.screen['classes_piratewindow'].destroy() + end + end + if GuiEvo.full_update then GuiEvo.full_update(player) end if GuiProgress.regular_update then GuiProgress.regular_update(player) end --moved to event if GuiRuns.full_update then GuiRuns.full_update(player) end if GuiCrew.full_update then GuiCrew.full_update(player) end + if GuiClasses.full_update then GuiClasses.full_update(player) end if GuiFuel.regular_update then GuiFuel.regular_update(player) end --moved to event if GuiMinimap.full_update then GuiMinimap.full_update(player) end if GuiInfo.full_update then GuiInfo.full_update(player) end @@ -961,7 +980,7 @@ function Public.update_gui(player) flow1 = pirates_flow.progress_piratebutton_frame.progress_piratebutton flow1.number = (memory.overworldx or 0) - flow1.caption = {'pirates.gui_progress_tooltip', memory.overworldx or 0, CoreData.victory_x} + flow1.tooltip = {'pirates.gui_progress_tooltip', memory.overworldx or 0, CoreData.victory_x} -- pirates_flow.destination_piratebutton_frame.destination_piratebutton.number = memory.destinationsvisited_indices and #memory.destinationsvisited_indices or 0 @@ -1201,6 +1220,7 @@ local function on_gui_click(event) else if GuiRuns.click then GuiRuns.click(event) end if GuiCrew.click then GuiCrew.click(event) end + if GuiClasses.click then GuiClasses.click(event) end if GuiFuel.click then GuiFuel.click(event) end if GuiMinimap.click then GuiMinimap.click(event) end if GuiInfo.click then GuiInfo.click(event) end diff --git a/maps/pirates/memory.lua b/maps/pirates/memory.lua index aedb8302..e76d7694 100644 --- a/maps/pirates/memory.lua +++ b/maps/pirates/memory.lua @@ -78,6 +78,8 @@ function Public.initialise_crew_memory(id) --mostly serves as a dev reference of memory.boat = nil memory.available_classes_pool = nil + -- Duplicating unlocked classes data for consistency reasons (this way, entries will remain in the same order when unlocked class data changes) + memory.unlocked_classes = nil memory.seconds_until_alert_sound_can_be_played_again = 0 memory.crewplayerindices = nil diff --git a/maps/pirates/progression.lua b/maps/pirates/progression.lua index fc01c447..acf0ca9c 100644 --- a/maps/pirates/progression.lua +++ b/maps/pirates/progression.lua @@ -546,7 +546,7 @@ local parrot_set_sail_advice = Memory.set_working_id(crew_id) local memory = Memory.get_crew_memory() - if not (memory.id and memory.id > 0) then return end --check if crew disbanded + if not Common.is_id_valid(memory.id) then return end --check if crew disbanded if memory.game_lost then return end if memory.boat and memory.boat.state and memory.boat.state == Boats.enum_state.ATSEA_WAITING_TO_SAIL then diff --git a/maps/pirates/roles/classes.lua b/maps/pirates/roles/classes.lua index 157f62c2..7b47eb02 100644 --- a/maps/pirates/roles/classes.lua +++ b/maps/pirates/roles/classes.lua @@ -70,7 +70,7 @@ function Public.explanation(class) return {'pirates.class_' .. class .. '_explanation'} end -function Public.explanation_advanced(class) +function Public.explanation_advanced(class, add_is_class_obstainable) local explanation = 'pirates.class_' .. class .. '_explanation_advanced' local full_explanation @@ -124,7 +124,9 @@ function Public.explanation_advanced(class) full_explanation = {'', {explanation}} end - full_explanation[#full_explanation + 1] = Public.class_is_obtainable(class) and {'', ' ', {'pirates.class_obtainable'}} or {'', ' ', {'pirates.class_unobtainable'}} + if add_is_class_obstainable then + full_explanation[#full_explanation + 1] = Public.class_is_obtainable(class) and {'', ' ', {'pirates.class_obtainable'}} or {'', ' ', {'pirates.class_unobtainable'}} + end return full_explanation end @@ -204,59 +206,61 @@ function Public.class_is_obtainable(class) return false end - -function Public.assign_class(player_index, class, self_assigned) +-- When "class" is nil, player drops equipped class +-- "class_entry_index" only relevant for GUI order consistency +function Public.assign_class(player_index, class, class_entry_index) local memory = Memory.get_crew_memory() local player = game.players[player_index] if not memory.classes_table then memory.classes_table = {} end - if memory.classes_table[player_index] == class then - Common.notify_player_error(player, {'pirates.error_class_assign_redundant', Public.display_form(class)}) - return false - end - if Utils.contains(memory.spare_classes, class) then -- verify that one is spare + if class then + if Utils.contains(memory.spare_classes, class) then + -- drop class + if memory.classes_table[player_index] then + memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player_index] + memory.classes_table[player_index] = nil - Public.try_renounce_class(player, false) - - memory.classes_table[player_index] = class - - local force = memory.force - if force and force.valid then - if self_assigned then - Common.notify_force_light(force,{'pirates.class_take_spare', player.name, Public.display_form(memory.classes_table[player_index]), Public.explanation(memory.classes_table[player_index])}) - else - Common.notify_force_light(force,{'pirates.class_give_spare', Public.display_form(memory.classes_table[player_index]), player.name, Public.explanation(memory.classes_table[player_index])}) - end - end - - memory.spare_classes = Utils.ordered_table_with_single_value_removed(memory.spare_classes, class) - return true - else - Common.notify_player_error(player, {'pirates.error_class_assign_unavailable_class'}) - return false - end -end - -function Public.try_renounce_class(player, whisper_failure_message, impersonal_bool) - local memory = Memory.get_crew_memory() - - local force = memory.force - if force and force.valid and player and player.index then - if memory.classes_table and memory.classes_table[player.index] then - if force and force.valid then - if impersonal_bool then - Common.notify_force_light(force,{'pirates.class_becomes_spare', Public.display_form(memory.classes_table[player.index])}) - else - Common.notify_force_light(force,{'pirates.class_give_up', player.name, Public.display_form(memory.classes_table[player.index])}) + for _, class_entry in ipairs(memory.unlocked_classes) do + if class_entry.taken_by == player.index then + class_entry.taken_by = nil + break + end end end - memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index] - memory.classes_table[player.index] = nil - elseif whisper_failure_message then - Common.notify_player_error(player, {'pirates.class_give_up_error_no_class'}) + -- assign class + memory.classes_table[player_index] = class + memory.spare_classes = Utils.ordered_table_with_single_value_removed(memory.spare_classes, class) + + if class_entry_index then + memory.unlocked_classes[class_entry_index].taken_by = player.index + else + for _, class_entry in ipairs(memory.unlocked_classes) do + if class_entry.class == class and (not class_entry.taken_by) then + class_entry.taken_by = player.index + break + end + end + end + end + else + -- drop class + if memory.classes_table[player_index] then + memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player_index] + memory.classes_table[player_index] = nil + + if class_entry_index then + memory.unlocked_classes[class_entry_index].taken_by = nil + else + for _, class_entry in ipairs(memory.unlocked_classes) do + if class_entry.taken_by == player.index then + class_entry.taken_by = nil + break + end + end + end end end end @@ -380,6 +384,78 @@ function Public.lumberjack_bonus_items(give_table) end end +function Public.try_unlock_class(class_for_sale, player, force_unlock) + local memory = Memory.get_crew_memory() + + local required_class = Public.class_purchase_requirement[class_for_sale] + + if not (memory.classes_table and memory.spare_classes) then + return false + end + + if required_class then + local chosen_class_assigned = false + + for p_index, chosen_class in pairs(memory.classes_table) do + if chosen_class == required_class then + memory.classes_table[p_index] = class_for_sale + chosen_class_assigned = true + + -- update GUI data + for _, class_entry in ipairs(memory.unlocked_classes) do + if class_entry.taken_by == player.index then + class_entry.class = class_for_sale + break + end + end + return true + end + end + + if not chosen_class_assigned then + for i, spare_class in pairs(memory.spare_classes) do + if spare_class == required_class then + memory.spare_classes[i] = class_for_sale + + -- update GUI data + for _, class_entry in ipairs(memory.unlocked_classes) do + if required_class == class_entry.class and (not class_entry.taken_by) then + class_entry.class = class_for_sale + break + end + end + return true + end + end + end + + -- allows to unlock class even if pre-requisite is missing + if force_unlock then + memory.spare_classes[#memory.spare_classes + 1] = class_for_sale + + -- update GUI data + memory.unlocked_classes[#memory.unlocked_classes + 1] = {class = class_for_sale} + end + else -- there is no required class + -- if player who unlocked class doesn't have one equipped, equip it for him + if not memory.classes_table[player.index] then + memory.classes_table[player.index] = class_for_sale + + -- update GUI data + memory.unlocked_classes[#memory.unlocked_classes + 1] = {class = class_for_sale, taken_by = player.index} + else + memory.spare_classes[#memory.spare_classes + 1] = class_for_sale + + -- update GUI data + memory.unlocked_classes[#memory.unlocked_classes + 1] = {class = class_for_sale} + end + return true + end + + return false +end + + local event = require 'utils.event' event.add(defines.events.on_player_used_capsule, class_on_player_used_capsule) diff --git a/maps/pirates/roles/roles.lua b/maps/pirates/roles/roles.lua index adb98b1a..8c7e6b67 100644 --- a/maps/pirates/roles/roles.lua +++ b/maps/pirates/roles/roles.lua @@ -74,17 +74,17 @@ function Public.unmake_officer(captain, player) end end -function Public.revoke_class(captain, player) - local memory = Memory.get_crew_memory() - local force = memory.force +-- function Public.revoke_class(captain, player) +-- local memory = Memory.get_crew_memory() +-- local force = memory.force - if force and force.valid and player.index and memory.classes_table[player.index] then - memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index] - memory.classes_table[player.index] = nil +-- if force and force.valid and player.index and memory.classes_table[player.index] then +-- memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index] +-- memory.classes_table[player.index] = nil - Common.notify_force_light(captain,{'pirates.class_revoke', captain.name, Classes.display_form(memory.classes_table[player.index]), player.name}) - end -end +-- Common.notify_force_light(captain,{'pirates.class_revoke', captain.name, Classes.display_form(memory.classes_table[player.index]), player.name}) +-- end +-- end function Public.tag_text(player) local memory = Memory.get_crew_memory() @@ -92,7 +92,7 @@ function Public.tag_text(player) local str = '' local tags = {} - if memory.id ~= 0 and Common.is_captain(player) then + if Common.is_id_valid(memory.id) and Common.is_captain(player) then tags[#tags + 1] = 'Cap\'n' elseif player.controller_type == defines.controllers.spectator then tags[#tags + 1] = 'Spectating' @@ -131,13 +131,13 @@ end -- return str -- end -function Public.get_class_print_string(class, full) +function Public.get_class_print_string(class, full, add_is_class_obstainable) for _, class2 in pairs(Classes.enum) do if Classes.eng_form[class2]:lower() == class:lower() or class2 == class:lower() then local explanation if full then - explanation = Classes.explanation_advanced(class2) + explanation = Classes.explanation_advanced(class2, add_is_class_obstainable) else explanation = Classes.explanation(class2) end @@ -164,7 +164,7 @@ end function Public.player_privilege_level(player) local memory = Memory.get_crew_memory() - if memory.id ~= 0 and Common.is_captain(player) then + if Common.is_id_valid(memory.id) and Common.is_captain(player) then return Public.privilege_levels.CAPTAIN elseif memory.officers_table and memory.officers_table[player.index] then return Public.privilege_levels.OFFICER @@ -215,18 +215,31 @@ end function Public.player_left_so_redestribute_roles(player) -- local memory = Memory.get_crew_memory() - if player and player.index then - if Common.is_captain(player) then - Public.assign_captain_based_on_priorities() - end + if not (player and player.index) then return end - -- no need to do this, as long as officers get reset when the captainhood changes hands - -- if memory.officers_table and memory.officers_table[player.index] then - -- memory.officers_table[player.index] = nil - -- end + if Common.is_captain(player) then + Public.assign_captain_based_on_priorities() end - Classes.try_renounce_class(player, false, true) + -- no need to do this, as long as officers get reset when the captainhood changes hands + -- if memory.officers_table and memory.officers_table[player.index] then + -- memory.officers_table[player.index] = nil + -- end + + local memory = Memory.get_crew_memory() + + -- free up the class + if memory.classes_table and memory.classes_table[player.index] then + memory.spare_classes[#memory.spare_classes + 1] = memory.classes_table[player.index] + memory.classes_table[player.index] = nil + + for _, class_entry in ipairs(memory.unlocked_classes) do + if class_entry.taken_by == player.index then + class_entry.taken_by = nil + break + end + end + end end @@ -274,7 +287,7 @@ function Public.confirm_captain_exists(player_to_make_captain_otherwise) local memory = Memory.get_crew_memory() -- Currently this catches an issue where a crew drops to zero players, and then someone else joins. - if (memory.id and memory.id > 0 and memory.crewstatus and memory.crewstatus == 'adventuring') and (not (memory.playerindex_captain and game.players[memory.playerindex_captain] and Common.validate_player(game.players[memory.playerindex_captain]))) then --fixme: enum hacked + if (Common.is_id_valid(memory.id) and memory.crewstatus and memory.crewstatus == 'adventuring') and (not (memory.playerindex_captain and game.players[memory.playerindex_captain] and Common.validate_player(game.players[memory.playerindex_captain]))) then --fixme: enum hacked if player_to_make_captain_otherwise then Public.make_captain(player_to_make_captain_otherwise) -- game.print('Auto-reassigning captain.') diff --git a/maps/pirates/shop/shop.lua b/maps/pirates/shop/shop.lua index 39477e1c..1b011818 100644 --- a/maps/pirates/shop/shop.lua +++ b/maps/pirates/shop/shop.lua @@ -211,41 +211,7 @@ function Public.event_on_market_item_purchased(event) -- if not class_for_sale then return end local required_class = Classes.class_purchase_requirement[class_for_sale] - local ok = false - - if memory.classes_table and memory.spare_classes then - if required_class then - local chosen_class_assigned = false - - if required_class then - for p_index, chosen_class in pairs(memory.classes_table) do - if chosen_class == required_class then - memory.classes_table[p_index] = class_for_sale - chosen_class_assigned = true - ok = true - break - end - end - end - - if not chosen_class_assigned then - for i, spare_class in pairs(memory.spare_classes) do - if spare_class == required_class then - memory.spare_classes[i] = class_for_sale - ok = true - break - end - end - end - else -- there is no required class - if not memory.classes_table[player.index] then - memory.classes_table[player.index] = class_for_sale - else - memory.spare_classes[#memory.spare_classes + 1] = class_for_sale - end - ok = true - end - end + local ok = Classes.try_unlock_class(class_for_sale, player) if ok then if force and force.valid then @@ -266,7 +232,7 @@ function Public.event_on_market_item_purchased(event) memory.available_classes_pool[#memory.available_classes_pool + 1] = upgrade end end - else + else -- if this happens, I believe there is something wrong with code if force and force.valid then Common.notify_force_error(force, {'pirates.class_purchase_error_prerequisite_class', Classes.display_form(required_class)}) end @@ -278,6 +244,8 @@ function Public.event_on_market_item_purchased(event) inv.insert{name = p.name, count = p.amount} end refunds = refunds + 1 + + log('Error purchasing class: ' .. class_for_sale) end else Common.notify_force_light(player.force, {'pirates.market_event_buy', player.name, thisPurchaseData.offer_giveitem_count .. ' ' .. thisPurchaseData.offer_giveitem_name, thisPurchaseData.price[1].amount .. ' ' .. thisPurchaseData.price[1].name}) diff --git a/maps/pirates/surfaces/sea/kraken.lua b/maps/pirates/surfaces/sea/kraken.lua index 24623c1a..697d2a46 100644 --- a/maps/pirates/surfaces/sea/kraken.lua +++ b/maps/pirates/surfaces/sea/kraken.lua @@ -54,7 +54,7 @@ local swimming_biters_tick_token = function Public.swimming_biters_tick(crew_id, kraken_id) Memory.set_working_id(crew_id) local memory = Memory.get_crew_memory() - if not (memory.id and memory.id > 0) then return end --check if crew disbanded + if not Common.is_id_valid(memory.id) then return end --check if crew disbanded if memory.game_lost then return end local kraken_data = memory.active_sea_enemies.krakens[kraken_id] if not kraken_data then return end --check if kraken died @@ -87,7 +87,7 @@ end function Public.kraken_tick(crew_id, kraken_id, step, substep) Memory.set_working_id(crew_id) local memory = Memory.get_crew_memory() - if not (memory.id and memory.id > 0) then return end --check if crew disbanded + if not Common.is_id_valid(memory.id) then return end --check if crew disbanded if memory.game_lost then return end local kraken_data = memory.active_sea_enemies.krakens[kraken_id] if not kraken_data then return end --check if kraken died diff --git a/maps/pirates/surfaces/surfaces.lua b/maps/pirates/surfaces/surfaces.lua index 4f2bc799..525d5df0 100644 --- a/maps/pirates/surfaces/surfaces.lua +++ b/maps/pirates/surfaces/surfaces.lua @@ -338,16 +338,17 @@ function Public.destination_on_arrival(destination) -- add special chunk structures(small mining base, etc.) to avoid list local structures = destination.dynamic_data.structures_waiting_to_be_placed - for i = #structures, 1, -1 do - local structure = structures[i] - local special = structure.data - local position = special.position - local avoid_radius = math.sqrt(special.width * special.width + special.height * special.height) / 2 + 4 + if structures then + for i = #structures, 1, -1 do + local structure = structures[i] + local special = structure.data + local position = special.position + local avoid_radius = math.sqrt(special.width * special.width + special.height * special.height) / 2 + 5 - points_to_avoid[#points_to_avoid + 1] = {x = position.x, y = position.y, r = avoid_radius} + points_to_avoid[#points_to_avoid + 1] = {x = position.x, y = position.y, r = avoid_radius} + end end - -- game.print('spawning silo') if destination.subtype ~= Islands.enum.RADIOACTIVE then local silo_position = Islands.spawn_silo_setup(points_to_avoid)