diff --git a/CHANGES.md b/CHANGES.md index e9f0ae72..2f5f3701 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,33 @@ +# Version 0.14.1 BETA +Released on September 7th, 2024. + +## Summary +- This small update adds, re-adds, or improves minor features, such as player and vehicle health and armor info, infinity pipe filter setting, basic game console access, and mining drill drop chute position info. + +## Features +- Infinity pipe filters can now be set using fluid barrels. + * With a fluid barrel in hand, select the infinity pipe and press `ALT + LEFT BRACKET` to set the relevant fluid. + * As a special case, an empty barrel is used to set extra hot steam at 500 degrees. + * If you press with an empty hand, the filter is cleared and the pipe becomes an infinite drain. + +- Added limited support for console reading. + * When you press `GRAVE` or the relevant key, the console being opened is now reported. + * When a player enters a chat message, all players are informed about it live. + * The same as above happens for command messages. + * There is not yet support for chat history or for non-player console messages. + +## Changes +- Changed the keybind to set entity filters from `CONTROL + LEFT BRACKET` to `ALT + LEFT BRACKET`. This applies for chests, inserters, splitters, etc. + +- Improved health and armor stats reporting with the `G` key to now include vehicles and to check the menu context. + * When no menus are open, the mod reports only health and shield level info for your character. + * For most open menus such as the inventory, the full set of stats about player armor is reported. + * If the character is in a vehicle, or the vehicle's menu is open, then the name and full info for it is read instead. + +## Bugfixes +- Re-added the reporting of a mining drill's drop chute position. + + # Version 0.14.0 BETA Released on September 2nd, 2024. diff --git a/README.md b/README.md index 889d3284..059830d0 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,12 @@ Insert 1 stack of the item in hand where applicable: CONTROL + LEFT BRACKET. Wor Insert half a stack of the item in hand where applicable: CONTROL + RIGHT BRACKET. Works for chests or for smartly feeding machines and vehicles. +Set as entity filter: ALT + LEFT BRACKET, for the item in hand. + +Clear entity filter: ALT + LEFT BRACKET, with an empty hand. + +Note: Entity filters include inserters, splitters, infinity chests, infinity pipes using relevant fluid barrels, and constant combinators. + ## Building from the hand Items in hand that can be placed will have their previews active @@ -653,13 +659,11 @@ Set input priority side: ALT + SHIFT + LEFT ARROW, or ALT + SHIFT + RIGHT ARROW. Set output priority side: ALT + CONTROL + LEFT ARROW, or ALT + CONTROL + RIGHT ARROW. Press the same side again to reset to equal priority. -Set an item filter: With the item in hand, CONTROL + LEFT BRACKET - Set item filter output side: ALT + CONTROL + LEFT ARROW, or ALT + CONTROL + RIGHT ARROW -Set an item filter: With the item in hand, CONTROL + LEFT BRACKET +Set an item filter: With the item in hand, ALT + LEFT BRACKET -Clear the item filter: With an empty hand, CONTROL + LEFT BRACKET +Clear the item filter: With an empty hand, ALT + LEFT BRACKET Copy and paste splitter settings: SHIFT + RIGHT BRACKET and then SHIFT + LEFT BRACKET diff --git a/changelog.txt b/changelog.txt index 2d97ae78..3dd0d02e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,39 @@ +--------------------------------------------------------------------------------------------------- +Version: 0.14.1 +Date: 2024.09.07 + + Summary: + - This small update adds, re-adds, or improves minor features, such as player and vehicle + health and armor info, infinity pipe filter setting, basic game console access, + and mining drill drop chute position info. + + Features: + - Infinity pipe filters can now be set using fluid barrels. + * With a fluid barrel in hand, select the infinity pipe and press `ALT + LEFT BRACKET` to + set the relevant fluid. + * As a special case, an empty barrel is used to set extra hot steam at 500 degrees. + * If you press with an empty hand, the filter is cleared and the pipe becomes an infinite drain. + + - Added limited support for console reading. + * When you press `GRAVE` or the relevant key, the console being opened is now reported. + * When a player enters a chat message, all players are informed about it live. + * The same as above happens for command messages. + * There is not yet support for chat history or for non-player console messages. + + Changes: + - Changed the keybind to set entity filters from `CONTROL + LEFT BRACKET` to `ALT + LEFT BRACKET`. + This applies for chests, inserters, splitters, etc. + + - Improved health and armor stats reporting with the `G` key to now include vehicles and + to check the menu context. + * When no menus are open, the mod reports only health and shield level info for your character. + * For most open menus such as the inventory, the full set of stats about player armor is reported. + * If the character is in a vehicle, or the vehicle's menu is open, then the name and full info + for it is read instead. + + Bugfixes: + - Re-added the reporting of a mining drill's drop chute position. + --------------------------------------------------------------------------------------------------- Version: 0.14.0 Date: 2024.09.02 diff --git a/control.lua b/control.lua index dd058cb5..cbfd188b 100644 --- a/control.lua +++ b/control.lua @@ -6044,14 +6044,6 @@ script.on_event("read-entity-status", function(event) if result ~= nil and result ~= "" then printout(result, pindex) end end) -script.on_event("read-character-status", function(event) - pindex = event.player_index - if not check_for_player(pindex) then return end - local hand = game.get_player(pindex).cursor_stack - if hand and hand.valid_for_read and (hand.is_blueprint or hand.is_blueprint_book) then return end - fa_info.read_character_status(pindex) -end) - script.on_event("rotate-building", function(event) fa_building_tools.rotate_building_info_read(event, true) end) @@ -7762,13 +7754,14 @@ script.on_event("set-entity-filter-from-hand", function(event) --Remove the last signal fa_circuits.constant_combinator_remove_last_signal(ent, pindex) elseif ent.type == "inserter" then - --Call the filter setter local result = set_inserter_filter_by_hand(pindex, ent) printout(result, pindex) elseif ent.type == "infinity-container" then - --Call the filter setter local result = set_infinity_chest_filter_by_hand(pindex, ent) printout(result, pindex) + elseif ent.type == "infinity-pipe" then + local result = set_infinity_pipe_filter_by_hand(pindex, ent) + printout(result, pindex) end else if ent.type == "splitter" then @@ -7779,13 +7772,14 @@ script.on_event("set-entity-filter-from-hand", function(event) --Add a new signal fa_circuits.constant_combinator_add_stack_signal(ent, stack, pindex) elseif ent.type == "inserter" then - --Call the filter setter local result = set_inserter_filter_by_hand(pindex, ent) printout(result, pindex) elseif ent.type == "infinity-container" then - --Call the filter setter local result = set_infinity_chest_filter_by_hand(pindex, ent) printout(result, pindex) + elseif ent.type == "infinity-pipe" then + local result = set_infinity_pipe_filter_by_hand(pindex, ent) + printout(result, pindex) end end end @@ -7961,18 +7955,36 @@ script.on_event("disconnect-rail-vehicles", function(event) end end) -script.on_event("inventory-read-armor-stats", function(event) - local pindex = event.player_index - local vehicle = nil - if not check_for_player(pindex) or not players[pindex].in_menu then return end - if - (players[pindex].in_menu and players[pindex].menu == "inventory") - or (players[pindex].menu == "vehicle" and game.get_player(pindex).opened.type == "spider-vehicle") - then - local result = fa_equipment.read_armor_stats(pindex) - --game.get_player(pindex).print(result)-- - printout(result, pindex) +script.on_event("read-health-and-armor-stats", function(event) + pindex = event.player_index + if not check_for_player(pindex) then return end + local p = game.get_player(pindex) + local output = { "" } + --Skip blueprint flipping + local hand = game.get_player(pindex).cursor_stack + if hand and hand.valid_for_read and (hand.is_blueprint or hand.is_blueprint_book) then return end + if players[pindex].in_menu then + if players[pindex].menu == "vehicle" then + --Vehicle health and armor equipment stats + local result = fa_equipment.read_armor_stats(pindex, p.opened) + table.insert(output, result) + else + --Player health and armor equipment stats + local result = fa_equipment.read_armor_stats(pindex, nil) + table.insert(output, result) + end + else + if p.vehicle then + --Vehicle health and armor equipment stats + local result = fa_equipment.read_armor_stats(pindex, p.vehicle) + table.insert(output, result) + else + --Player health stats only + local result = fa_equipment.read_shield_and_health_level(pindex, nil) + table.insert(output, result) + end end + printout(output, pindex) end) script.on_event("inventory-read-equipment-list", function(event) @@ -8618,6 +8630,37 @@ function set_infinity_chest_filter_by_hand(pindex, ent) end end +function set_infinity_pipe_filter_by_hand(pindex, ent) + local stack = game.get_player(pindex).cursor_stack + if stack == nil or stack.valid_for_read == false or stack.valid == false then + --Delete filters + ent.set_infinity_pipe_filter(nil) + return "All filters cleared" + else + --Get the fluid from the barrel in hand + local name = stack.name + local first, last = string.find(name, "-barrel") + if first then + local fluid_name = string.sub(name, 1, first - 1) + local temp = 25 + if fluid_name == "water" then + temp = 15 + elseif fluid_name == "empty" then + --Special case: Empty barrel sets steam + fluid_name = "steam" + temp = 500 + end + if game.fluid_prototypes[fluid_name] then + ent.set_infinity_pipe_filter({ name = fluid_name, temperature = temp, percentage = 1.00, mode = "exactly" }) + return "Set filter to fluid in hand" + end + return "Error: Unknown fluid in hand " .. fluid_name + end + return "Error: Not a fluid barrel in hand" + end + return "Error setting fluid" +end + --Feature for typing in coordinates for moving the mod cursor. function type_cursor_position(pindex) printout("Enter new co-ordinates for the cursor, separated by a space", pindex) @@ -9058,6 +9101,27 @@ function all_ents_are_walkable(pos) return true end +script.on_event("console", function(event) + printout("Opened console", pindex) +end) + +script.on_event(defines.events.on_console_chat, function(event) + local speaker = game.get_player(event.player_index).name + if speaker == nil or speaker == "" then speaker = "Player" end + local message = event.message + for pindex, player in pairs(players) do + printout(speaker .. " says, " .. message, pindex) + end +end) + +script.on_event(defines.events.on_console_command, function(event) + local speaker = game.get_player(event.player_index).name + if speaker == nil or speaker == "" then speaker = "Player" end + for pindex, player in pairs(players) do + printout(speaker .. " commands, " .. event.command .. " " .. event.parameters, pindex) + end +end) + --WIP. This function can be called via the console: /c __FactorioAccess__ regenerate_all_uncharted_spawners() --laterdo fix bugs? function regenerate_all_uncharted_spawners(surface_in) local surf = surface_in or game.surfaces["nauvis"] diff --git a/data.lua b/data.lua index 6c3f7123..1e7385f4 100644 --- a/data.lua +++ b/data.lua @@ -1490,7 +1490,7 @@ data:extend({ { type = "custom-input", - name = "read-character-status", + name = "read-health-and-armor-stats", key_sequence = "G", consuming = "none", }, @@ -1780,6 +1780,13 @@ data:extend({ consuming = "none", }, + { + type = "custom-input", + name = "console", + key_sequence = "GRAVE", + consuming = "none", + }, + { type = "custom-input", name = "open-warnings-menu", @@ -1902,7 +1909,7 @@ data:extend({ { type = "custom-input", name = "set-entity-filter-from-hand", - key_sequence = "CONTROL + LEFTBRACKET", + key_sequence = "ALT + LEFTBRACKET", consuming = "none", }, @@ -1920,13 +1927,6 @@ data:extend({ consuming = "none", }, - { - type = "custom-input", - name = "inventory-read-armor-stats", - key_sequence = "G", - consuming = "none", - }, - { type = "custom-input", name = "inventory-read-equipment-list", diff --git a/info.json b/info.json index 9c207e96..4c2c9114 100644 --- a/info.json +++ b/info.json @@ -1,6 +1,6 @@ { "name": "FactorioAccess", - "version": "0.14.0", + "version": "0.14.1", "title": "Factorio Access Mod", "author": "Crimso, SirFendi, Gweneph, Austin Hicks", "homepage": "https://github.com/Factorio-Access/FactorioAccess", diff --git a/scripts/equipment.lua b/scripts/equipment.lua index 90254a43..bbfa07bd 100644 --- a/scripts/equipment.lua +++ b/scripts/equipment.lua @@ -249,70 +249,122 @@ function mod.count_empty_equipment_slots(grid) return slots_left end ---Read armor stats such as type and bonuses -function mod.read_armor_stats(pindex) - local armor_inv = game.get_player(pindex).get_inventory(defines.inventory.character_armor) - local result = "" - if armor_inv.is_empty() then return "No armor equipped." end - if armor_inv[1].grid == nil or not armor_inv[1].grid.valid then - return armor_inv[1].name .. " equipped, with no equipment grid." - end - --Armor with Equipment +function mod.read_shield_and_health_level(pindex, ent_in) + local p = game.get_player(pindex) + local char = p.character + local ent local grid - if players[pindex].menu == "vehicle" and game.get_player(pindex).opened.type == "spider-vehicle" then - grid = game.get_player(pindex).opened.grid - result = localising.get_alt(game.entity_prototypes["spidertron"]) - if result == nil then - result = "Spidertron " --laterdo possible bug here - end + local result = { "" } + if ent_in then + --Report for the ent + ent = ent_in + grid = ent.grid + table.insert(result, ent.localised_name) else - grid = armor_inv[1].grid - result = armor_inv[1].name .. " equipped, " + --Report for this player + if char == nil or char.valid == false then + table.insert(result, "No character") + return result + end + ent = char + local armor_inv = p.get_inventory(defines.inventory.character_armor) + if armor_inv[1] and armor_inv[1].valid_for_read and armor_inv[1].grid and armor_inv[1].grid.valid then + grid = armor_inv[1].grid + end + end + + --Check shield health remaining (if supported) + local empty_shield = false + if grid then + if grid.shield > 0 and grid.shield == grid.max_shield then + table.insert(result, " Shield full, ") + elseif grid.shield > 0 then + local shield_left = math.floor(grid.shield / grid.max_shield * 100 + 0.5) + table.insert(result, " Shield " .. shield_left .. " percent, ") + else + empty_shield = true + end end - if grid.count() == 0 then return result .. " no armor equipment installed. " end - --Read shield level - if grid.max_shield > 0 then - if grid.shield == grid.max_shield then - result = result .. " shields full, " + --Check health + if ent.is_entity_with_health then + if ent.get_health_ratio() == 1 then + table.insert(result, { "access.full-health" }) else - result = result .. " shields at " .. math.ceil(100 * grid.shield / grid.max_shield) .. " percent, " + table.insert(result, { "access.percent-health", math.floor(ent.get_health_ratio() * 100) }) + end + end + -- State shield empty at the end (if supported) + if grid and empty_shield then table.insert(result, ", shield empty ") end + return result +end + +--Read armor stats such as type and bonuses. Default option is the player's own armor. +function mod.read_armor_stats(pindex, ent_in) + local ent = ent_in + local armor_inv = game.get_player(pindex).get_inventory(defines.inventory.character_armor) + local result = mod.read_shield_and_health_level(pindex, ent_in) --First report health and shield + table.insert(result, ", ") + local grid + if ent_in == nil then + --Player armor + if armor_inv.is_empty() then + table.insert(result, "No armor equipped.") + return result + elseif armor_inv[1].grid == nil or not armor_inv[1].grid.valid then + table.insert(result, armor_inv[1].name .. " equipped, with no equipment grid.") + return result end + --Player armor with non-empty equipment grid + grid = armor_inv[1].grid + table.insert(result, armor_inv[1].name .. " equipped, ") + else + --Entity grid + grid = ent.grid + if grid == nil or grid.valid == false then + --No more info to report + return result + end + --Entity with non-empty equipment grid + --(continue) + end + --Stop if no equipment + if grid.count() == 0 then + table.insert(result, " no armor equipment installed. ") + return result end --Read battery level if grid.battery_capacity > 0 then if grid.available_in_batteries == grid.battery_capacity then - result = result .. " batteries full, " + table.insert(result, " batteries full, ") elseif grid.available_in_batteries == 0 then - result = result .. " batteries empty " + table.insert(result, " batteries empty ") else - result = result - .. " batteries at " - .. math.ceil(100 * grid.available_in_batteries / grid.battery_capacity) - .. " percent, " + local battery_level = math.ceil(100 * grid.available_in_batteries / grid.battery_capacity) + table.insert(result, " batteries at " .. battery_level .. " percent, ") end else - result = result .. " no batteries, " + table.insert(result, " no batteries, ") end --Energy Producers if grid.generator_energy > 0 or grid.max_solar_energy > 0 then - result = result .. " generating " + table.insert(result, " generating ") if grid.generator_energy > 0 then - result = result .. fa_electrical.get_power_string(grid.generator_energy * 60) .. " nonstop, " + table.insert(result, fa_electrical.get_power_string(grid.generator_energy * 60) .. " nonstop, ") end if grid.max_solar_energy > 0 then - result = result .. fa_electrical.get_power_string(grid.max_solar_energy * 60) .. " at daytime, " + table.insert(result, fa_electrical.get_power_string(grid.max_solar_energy * 60) .. " at daytime, ") end end - --Movement bonus if grid.count("exoskeleton-equipment") > 0 then - result = result - .. " movement bonus " - .. grid.count("exoskeleton-equipment") * 30 - .. " percent for " - .. fa_electrical.get_power_string(grid.count("exoskeleton-equipment") * 200000) + table.insert( + result, + " movement bonus " + .. grid.count("exoskeleton-equipment") * 30 + .. " percent for " + .. fa_electrical.get_power_string(grid.count("exoskeleton-equipment") * 200000) + ) end - return result end @@ -507,14 +559,14 @@ function mod.guns_menu_click_slot(pindex) end if hand and hand.valid_for_read then --FUll hand operations - if selected_stack == nil then + if selected_stack == nil or selected_stack.valid_for_read == false then --Empty slot if menu.ammo_selected and hand.type ~= "ammo" then printout("Error: Slot reserved for ammo types only", pindex) elseif not menu.ammo_selected and hand.type ~= "gun" then printout("Error: Slot reserved for gun types only", pindex) else - hand.swap_stack(selected_stack) + if selected_stack ~= nil then hand.swap_stack(selected_stack) end --If the swap is successful then the following print statement is overwritten. printout("Error: Incompatible gun and ammo types", pindex) end diff --git a/scripts/fa-info.lua b/scripts/fa-info.lua index cd4c2d26..034f3cc2 100644 --- a/scripts/fa-info.lua +++ b/scripts/fa-info.lua @@ -157,6 +157,13 @@ function mod.ent_info(pindex, ent, description) result = result .. ", in network " .. network_name end end + elseif ent.name == "infinity-pipe" then + local filter = ent.get_infinity_pipe_filter() + if filter == nil then + result = result .. " draining " + else + result = result .. " of " .. filter.name + end end --Pipe ends are labelled to distinguish them if ent.name == "pipe" and fa_building_tools.is_a_pipe_end(ent, pindex) then result = result .. " end, " end @@ -412,6 +419,9 @@ function mod.ent_info(pindex, ent, description) result = result .. ", " .. fa_rails.get_signal_state_info(ent) end end + if ent.type == "mining-drill" and mod.cursor_is_at_mining_drill_output_part(pindex, ent) then + result = result .. " drop chute " + end --Report the entity facing direction if (ent.prototype.is_building and ent.supports_direction) @@ -842,6 +852,7 @@ function mod.ent_info(pindex, ent, description) local pos = ent.position local radius = ent.prototype.mining_drill_radius local area = { { pos.x - radius, pos.y - radius }, { pos.x + radius, pos.y + radius } } + --Compute resources covered local resources = ent.surface.find_entities_filtered({ area = area, type = "resource" }) local dict = {} for i, resource in pairs(resources) do @@ -851,6 +862,7 @@ function mod.ent_info(pindex, ent, description) dict[resource.name] = dict[resource.name] + resource.amount end end + --Compute drop position local drop = ent.drop_target local drop_name = nil if drop ~= nil and drop.valid then @@ -864,9 +876,10 @@ function mod.ent_info(pindex, ent, description) end end end + --Report info if drop ~= nil and drop.valid then result = result .. " outputs to " .. drop_name end if ent.status == defines.entity_status.waiting_for_space_in_destination then - result = result .. " output full " + result = result .. ", output full " end if table_size(dict) > 0 then result = result .. ", Mining from " @@ -1474,43 +1487,10 @@ function mod.read_selected_entity_status(pindex) return result end -function mod.read_character_status(pindex) - local p = game.get_player(pindex) - local char = p.character - local result = { "" } - --Report if character missing - if char == nil or char.valid == false then - printout(pindex, "No character") - return - end - --Check character has any energy shield health remaining - local shield_left = 0 - local armor_inv = p.get_inventory(defines.inventory.character_armor) - if - armor_inv[1] - and armor_inv[1].valid_for_read - and armor_inv[1].valid - and armor_inv[1].grid - and armor_inv[1].grid.valid - then - local grid = armor_inv[1].grid - if grid.shield > 0 and grid.shield == grid.max_shield then - table.insert(result, "Shield full, ") - elseif grid.shield > 0 then - shield_left = math.floor(grid.shield / grid.max_shield * 100 + 0.5) - table.insert(result, "Shield " .. shield_left .. " percent, ") - else - --Say nothing - end - end - --Character health - if char.is_entity_with_health and char.get_health_ratio() == 1 then - table.insert(result, { "access.full-health" }) - elseif char.is_entity_with_health then - table.insert(result, { "access.percent-health", math.floor(char.get_health_ratio() * 100) }) - end - printout(result, pindex) - return +function mod.cursor_is_at_mining_drill_output_part(pindex, ent) + local dir = ent.direction + local correct_pos = fa_utils.offset_position(ent.drop_position, fa_utils.rotate_180(dir), 1) + return util.distance(correct_pos, players[pindex].cursor_pos) < 0.6 end return mod