Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modfiles/locale/en/config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ warning_no_other_machine_choice=No other machine can craft this recipe
warning_no_prioritizing_single_product=Recipes with a single relevant product can’t be prioritized
hint_tutorial=If you are new to Factory Planner, consider taking a look the the tutorial ⟶
hint_byproducts_removed=Disabling the matrix solver removes all byproduct recipes
hint_fluid_belt_barrel=assuming 50 fluid/barrel

# Units
prefix_kilo=k
Expand Down
2 changes: 1 addition & 1 deletion modfiles/ui/elements/production_handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ local function compile_machine_chooser_buttons(player, line, applicable_prototyp
local machine_count = calculation.util.determine_machine_count(crafts_per_tick,
line.uncapped_production_ratio, timescale, machine_proto.launch_sequence_time)

local button_number = (round_button_numbers) and math.ceil(machine_count) or machine_count
local button_number = ui_util.format_number_ceil((round_button_numbers) and math.ceil(machine_count) or machine_count)

-- Have to do this stupid crap because localisation plurals only work on integers
local formatted_number = ui_util.format_number(machine_count, 4)
Expand Down
14 changes: 7 additions & 7 deletions modfiles/ui/elements/production_table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ function builders.machine(line, parent_flow, metadata)
enabled=false, number=machine_count, tooltip=tooltip}
else
-- Machine
machine_count = ui_util.format_number(machine_count, 4)
local tooltip_count = machine_count
if machine_count == "0" and line.production_ratio > 0 then
local tooltip_count = ui_util.format_number(machine_count, 4)
local icon_count = ui_util.format_number_ceil(machine_count)
if tooltip_count == "0" and line.production_ratio > 0 then
tooltip_count = "<0.0001"
machine_count = "0.01" -- shows up as 0.0 on the button
icon_count = 0 -- shows up as 0.0 on the button
end
if metadata.round_button_numbers then machine_count = math.ceil(machine_count) end
if metadata.round_button_numbers then icon_count = math.ceil(icon_count) end

local style, indication, machine_limit = "flib_slot_button_default_small", "", line.machine.limit
if not metadata.matrix_solver_active and machine_limit ~= nil then
Expand All @@ -147,13 +147,13 @@ function builders.machine(line, parent_flow, metadata)
effects_tooltip = data_util.format_module_effects(module_effects, 1, true)
end

local plural_parameter = (machine_count == "1") and 1 or 2
local plural_parameter = (tooltip_count == "1") and 1 or 2
local number_line = {"fp.newline", {"fp.two_word_title", tooltip_count, {"fp.pl_machine", plural_parameter}}}
local tutorial_tooltip = metadata.machine_tutorial_tooltip
local tooltip = {"", machine_proto.localised_name, number_line, indication, effects_tooltip, tutorial_tooltip}

parent_flow.add{type="sprite-button", tags={mod="fp", on_gui_click="act_on_line_machine", line_id=line.id},
style=style, sprite=machine_proto.sprite, number=machine_count, tooltip=tooltip,
style=style, sprite=machine_proto.sprite, number=icon_count, tooltip=tooltip,
mouse_button_filter={"left-and-right"}}

-- Modules - can only be added to machines that have any module slots
Expand Down
49 changes: 28 additions & 21 deletions modfiles/ui/elements/view_state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,37 @@ view_state = {}
-- ** LOCAL UTIL **
local processors = {} -- individual functions for each kind of view state
function processors.items_per_timescale(metadata, raw_amount, item_proto, _)
local number = ui_util.format_number(raw_amount, metadata.formatting_precision)

local tooltip = nil
if metadata.include_tooltip then
local plural_parameter = (number == "1") and 1 or 2
local tooltip_number = ui_util.format_number(raw_amount, metadata.formatting_precision)
local plural_parameter = (tooltip_number == "1") and 1 or 2
local type_string = (item_proto.type == "fluid") and {"fp.l_fluid"} or {"fp.pl_item", plural_parameter}
tooltip = {"fp.two_word_title", number, {"fp.per_title", type_string, metadata.timescale_string}}
tooltip = {"fp.two_word_title", tooltip_number, {"fp.per_title", type_string, metadata.timescale_string}}
end

return number, tooltip
local icon_number = ui_util.format_number_ceil(raw_amount)
return icon_number, tooltip
end

function processors.belts_or_lanes(metadata, raw_amount, item_proto, _)
if item_proto.type == "fluid" then return nil, nil end

local raw_number = raw_amount * metadata.throughput_multiplier * metadata.timescale_inverse
local number = ui_util.format_number(raw_number, metadata.formatting_precision)
local raw_number = raw_amount * metadata.throughput_multiplier * metadata.timescale_inverse / (item_proto.type == "fluid" and 50 or 1)

local tooltip = nil
if metadata.include_tooltip then
local plural_parameter = (number == "1") and 1 or 2
tooltip = {"fp.two_word_title", number, {"fp.pl_" .. metadata.belt_or_lane, plural_parameter}}
local tooltip_number = ui_util.format_number(raw_number, metadata.formatting_precision)
local plural_parameter = (tooltip_number == "1") and 1 or 2

if item_proto.type == "fluid" then
-- 3.5 belts (assuming 50 fluid/barrel)
tooltip = {"fp.annotated_title", {"fp.two_word_title", tooltip_number, {"fp.pl_" .. metadata.belt_or_lane, plural_parameter}}, {"fp.hint_fluid_belt_barrel"}}
else
-- 3.5 belts
tooltip = {"fp.two_word_title", tooltip_number, {"fp.pl_" .. metadata.belt_or_lane, plural_parameter}}
end
end

local return_number = (metadata.round_button_numbers) and math.ceil(raw_number) or number
return return_number, tooltip
local icon_number = ui_util.format_number_ceil((metadata.round_button_numbers) and math.ceil(raw_number) or raw_number)
return icon_number, tooltip
end

function processors.wagons_per_timescale(metadata, raw_amount, item_proto, _)
Expand All @@ -38,33 +43,35 @@ function processors.wagons_per_timescale(metadata, raw_amount, item_proto, _)
local wagon_capacity = (item_proto.type == "fluid") and metadata.fluid_wagon_capacity
or metadata.cargo_wagon_capactiy * item_proto.stack_size
local wagon_count = raw_amount / wagon_capacity
local number = ui_util.format_number(wagon_count, metadata.formatting_precision)

local tooltip = nil
if metadata.include_tooltip then
local plural_parameter = (number == "1") and 1 or 2
tooltip = {"fp.two_word_title", number, {"fp.per_title", {"fp.pl_wagon", plural_parameter},
local tooltip_number = ui_util.format_number(wagon_count, metadata.formatting_precision)
local plural_parameter = (tooltip_number == "1") and 1 or 2
tooltip = {"fp.two_word_title", tooltip_number, {"fp.per_title", {"fp.pl_wagon", plural_parameter},
metadata.timescale_string}}
end

return number, tooltip
local icon_number = ui_util.format_number_ceil(wagon_count)
return icon_number, tooltip
end

function processors.items_per_second_per_machine(metadata, raw_amount, item_proto, machine_count)
local raw_number = raw_amount * metadata.timescale_inverse / (math.ceil(machine_count or 1))
local number = ui_util.format_number(raw_number, metadata.formatting_precision)

local tooltip = nil
if metadata.include_tooltip then
local plural_parameter = (number == "1") and 1 or 2
local tooltip_number = ui_util.format_number(raw_number, metadata.formatting_precision)
local plural_parameter = (tooltip_number == "1") and 1 or 2
local type_string = (item_proto.type == "fluid") and {"fp.l_fluid"} or {"fp.pl_item", plural_parameter}
local item_per_second = {"fp.per_title", type_string, {"fp.second"}}
-- If machine_count is nil, this shouldn't show /machine
local per_machine = (machine_count ~= nil) and {"fp.per_title", "", {"fp.pl_machine", 1}} or ""
tooltip = {"fp.two_word_title", number, {"", item_per_second, per_machine}}
tooltip = {"fp.two_word_title", tooltip_number, {"", item_per_second, per_machine}}
end

return number, tooltip
local icon_number = ui_util.format_number_ceil(raw_number)
return icon_number, tooltip
end


Expand Down
96 changes: 96 additions & 0 deletions modfiles/ui/ui_util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,102 @@ function ui_util.format_number(number, precision)
end
end

-- Formats given number to fixed number of significant digits based on icon size, using ceil rounding
function ui_util.format_number_ceil(number)
if number == nil then return nil end

-- Set very small numbers to 0
if number < 0.0001 then
return 0
end

-- Figure out how many decimals we have
local base_decimals = math.floor(math.log10(number))
-- Example 1 for the sake of documentation: pretend our number is 23456.78912
-- base_decimals is math.floor(4.3702) = 4
-- Example 2 for the sake of documentation: pretend our number is 999.9
-- base_decimals is math.floor(2.99995656838) = 2

-- Visual example of the mapping we intend:
-- decimals = -2 -> .0234567891 -> 0.1 (we just round up to 0.1)
-- decimals = -1 -> .2345678912 -> 0.2 (one decimal of data)
-- decimals = 0 -> 2.345678912 -> 2.4 (two decimals of data)
-- decimals = 1 -> 23.45678912 -> 23.5 (three decimals of data)
-- decimals = 2 -> 234.5678912 -> 235 (three decimals of data)
-- decimals = 3 -> 2345.678912 -> 2.4k (two decimals of data)
-- decimals = 4 -> 23456.78912 -> 24k (two decimals of data)
-- decimals = 5 -> 234567.8912 -> 235k (three decimals of data)
-- decimals = 6 -> 2345678.912 -> 2.4M (two decimals of data)
-- decimals = 7 -> 23456789.12 -> 24M (two decimals of data)
-- decimals = 8 -> 234567891.2 -> 235M (three decimals of data)
-- tl;dr: hardcoded result if it's <-1, one decimal if it's <0, three decimals if it's in [1, 2) or if (it%3) is in [2, 3), otherwise two
local desired_decimals = 2
if base_decimals < -1 then
return "0.1"
elseif base_decimals < 0 then
desired_decimals = 1
elseif base_decimals == 1 or base_decimals % 3 == 2 then
desired_decimals = 3
end
-- Example 1: desired_decimals is 2
-- Example 2: desired_decimals is 3

-- Take the number, shove it down to our target, ceil it, and bring it back up
local shift = (10 ^ (math.floor(base_decimals) - desired_decimals + 1))
local shifted_number = number / shift
-- Example 1: shifted_number is 23456.78912 / (10 ^ (4 - 2 + 1)) = 23456.78912 / (10 ^ 3) = 23.45678912
-- Example 2: shifted_number is 999.9 / (10 ^ (2 - 3 + 1)) = 999.9 / (10 ^ 0) = 999.9

-- Add a slight magic number adjustment to compensate for floating-point inaccuracy
local ceiled_number = math.ceil(shifted_number - 0.00001)
-- Example 1: ceiled_number is 24
-- Example 2: ceiled_number is 1000

-- Uhoh, we have a problem! In `math.ceil()`, example 2 has now gained a digit.
-- But this is actually OK! It's gained a digit, but all digits aside from the first one are 0's.
-- Factorio's formatting code is going to just do the right thing here.

local returned_number = ceiled_number * shift
-- Example 1: returned_number is 24000
-- Example 2: returned_number is 1000

-- This is just to add a decimal at the end if it's not an actual perfect round (plus or minus an epsilon value)
if math.abs(number - returned_number) > 0.00001 then
returned_number = returned_number + 0.00001
end

return returned_number
end

local function format_number_tests()
local errlist = ""
local function check(input, expected)
local result = ui_util.format_number_ceil(input)
if math.abs(result - expected) > 0.000001 then -- less than the final adjustment value
errlist = errlist .. ("%f -> %f, should be %f\n"):format(input, result, expected)
end
end

check(0, 0)
check(0.1, 0.1)
check(0.08, 0.1) -- doesn't get the added decimal addition because it drops out much earlier
check(0.002, 0.1) -- doesn't get the added decimal addition because it drops out much earlier
check(23456.78912, 24000.00001)
check(1000, 1000)
check(3, 3)
check(2.99999, 3.00001)
check(3.5, 3.5)
check(3.56, 3.60001)
check(0.123, 0.20001)
check(999, 999)
check(999.9, 1000.00001)

if errlist ~= "" then
error(errlist)
end
end
format_number_tests()

-- Returns string representing the given power
function ui_util.format_SI_value(value, unit, precision)
local prefixes = {"", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"}
Expand Down