z_mirror/RateCalculator
git clone https://git.raiguard.me/z_mirror/RateCalculator
75c18ca
trunk
1local flib_dictionary =require ( "__flib__.dictionary" ) 2local flib_math =require ( "__flib__.math" ) 3 4--- @alias DivisorSource 5--- | "inserter_divisor" 6--- | "materials_divisor", 7--- | "transport_belt_divisor" 8 9--- @class TimescaleData 10--- @field divisor_required boolean? 11--- @field divisor_source DivisorSource 12--- @field multiplier double? 13--- @field prefer_si boolean? 14--- @field type_filter string? 15--- @field suffix LocalisedString? 16 17--- @class GuiUtil 18local gui_util ={} 19 20-- TODO: Move these to root scope 21function gui_util . build_divisor_filters () 22--- @type EntityPrototypeFilter[] 23local materials ={} 24for _ , entity in 25pairs ( prototypes . get_entity_filtered ( { 26{ filter ="type" , type ="container" } , 27{ filter ="type" , type ="logistic-container" } , 28} )) 29do 30local stacks =entity . get_inventory_size ( defines . inventory . chest ) 31if stacks and stacks > 0 and entity . group . name ~= "other" and entity . group . name ~= "environment" then 32materials [ # materials + 1 ] ={ filter ="name" , name =entity . name } 33end 34end 35for _ , entity in pairs ( prototypes . get_entity_filtered ( { { filter ="type" , type ="cargo-wagon" } } )) do 36local stacks =entity . get_inventory_size ( defines . inventory . cargo_wagon ) 37if stacks > 0 and entity . group . name ~= "other" and entity . group . name ~= "environment" then 38materials [ # materials + 1 ] ={ filter ="name" , name =entity . name } 39end 40end 41for _ , entity in 42pairs ( prototypes . get_entity_filtered ( { 43{ filter ="type" , type ="storage-tank" } , 44{ filter ="type" , type ="fluid-wagon" } , 45} )) 46do 47local capacity =entity . fluid_capacity 48if capacity > 0 and entity . group . name ~= "other" and entity . group . name ~= "environment" then 49materials [ # materials + 1 ] ={ filter ="name" , name =entity . name } 50end 51end 52 53--- @type table<DivisorSource, EntityPrototypeFilter[]> 54storage . elem_filters ={ 55inserter_divisor ={ { filter ="type" , type ="inserter" } } , 56materials_divisor =materials , 57transport_belt_divisor ={ { filter ="type" , type ="transport-belt" } } , 58} 59end 60 61function gui_util . build_dictionaries () 62flib_dictionary . new ( "search" ) 63for name , prototype in pairs ( prototypes . fluid ) do 64flib_dictionary . add ( "search" , "fluid/" .. name , prototype . localised_name ) 65end 66for name , prototype in pairs ( prototypes . item ) do 67flib_dictionary . add ( "search" , "item/" .. name , prototype . localised_name ) 68end 69end 70 71--- @param inserter LuaEntityPrototype 72--- @param quality QualityID 73--- @return double 74function gui_util . calc_inserter_cycles_per_second ( inserter , quality ) 75local pickup_vector =inserter . inserter_pickup_position --[[@as Vector]] 76local drop_vector =inserter . inserter_drop_position --[[@as Vector]] 77local pickup_x , pickup_y , drop_x , drop_y =pickup_vector [ 1 ], pickup_vector [ 2 ], drop_vector [ 1 ], drop_vector [ 2 ] 78local pickup_length =math . sqrt ( pickup_x * pickup_x + pickup_y * pickup_y ) 79local drop_length =math . sqrt ( drop_x * drop_x + drop_y * drop_y ) 80-- Get angle from the dot product 81-- XXX: Imprecision can make this return slightly outside the allowed bounds for acos, so clamp it 82local norm_dot =flib_math . clamp (( pickup_x * drop_x + pickup_y * drop_y ) / ( pickup_length * drop_length ), - 1 , 1 ) 83local angle =math . acos ( norm_dot ) 84-- Rotation speed is in full circles per tick 85local rotation_speed =inserter . get_inserter_rotation_speed ( quality ) 86local ticks_per_cycle =2 * math . ceil ( angle / ( math . pi * 2 ) / rotation_speed ) 87local extension_speed =inserter . get_inserter_extension_speed ( quality ) 88local extension_time =2 * math . ceil ( math . abs ( pickup_length - drop_length ) / extension_speed ) 89if ticks_per_cycle < extension_time then 90ticks_per_cycle =extension_time 91end 92return 60 / ticks_per_cycle -- 60 = ticks per second 93end 94 95--- @param self GuiData 96--- @return double|uint?, string?, boolean?, uint? 97function gui_util . get_divisor ( self ) 98local timescale_data =gui_util . timescale_data [ self . selected_timescale ] 99local type_filter 100 101--- @type double|uint? 102local divisor 103--- @type string? 104local divisor_source =timescale_data . divisor_source 105if not divisor_source then 106return 107end 108 109--- @type {name: string, quality: string}? 110local divisor_id =self [ divisor_source ] 111if not divisor_id then 112return 113end 114if timescale_data . divisor_required and not divisor_id then 115local entities =prototypes . get_entity_filtered ( storage . elem_filters [ timescale_data . divisor_source ]) 116-- LuaCustomTable does not work with next() 117for name in pairs ( entities ) do 118divisor_id ={ name =name , quality ="normal" } 119break 120end 121end 122 123local inserter_stack_size =0 124local divide_stacks =false 125if divisor_id then 126--- @type LuaEntityPrototype 127local prototype =prototypes . entity [ divisor_id . name ] 128if prototype . type == "container" or prototype . type == "logistic-container" then 129divisor =prototype . get_inventory_size ( defines . inventory . chest ) 130type_filter ="item" 131divide_stacks =true 132elseif prototype . type == "cargo-wagon" then 133divisor =prototype . get_inventory_size ( defines . inventory . cargo_wagon ) 134type_filter ="item" 135divide_stacks =true 136elseif prototype . type == "storage-tank" or prototype . type == "fluid-wagon" then 137divisor =prototype . fluid_capacity 138type_filter ="fluid" 139elseif prototype . type == "transport-belt" then 140divisor =prototype . belt_speed * 480 141type_filter ="item" 142elseif prototype . type == "inserter" then 143local cycles_per_second =gui_util . calc_inserter_cycles_per_second ( prototype , divisor_id . quality ) 144if prototype . bulk then 145inserter_stack_size =1 + prototype . inserter_stack_size_bonus + self . player . force . bulk_inserter_capacity_bonus 146else 147inserter_stack_size =1 + prototype . inserter_stack_size_bonus + self . player . force . inserter_stack_size_bonus 148end 149divisor =cycles_per_second * inserter_stack_size 150type_filter ="item" 151end 152end 153 154return divisor , type_filter , divide_stacks , inserter_stack_size 155end 156 157--- @param filters EntityPrototypeFilter[] 158--- @return EntityWithQualityID 159function gui_util . get_first_prototype ( filters ) 160-- XXX: next() doesn't work on LuaCustomTable 161for name in pairs ( prototypes . get_entity_filtered ( filters )) do 162return { name =name , quality ="normal" } 163end 164end 165 166--- @type table<Timescale, TimescaleData> 167gui_util . timescale_data ={ 168[ "per-second" ] ={ divisor_source ="materials_divisor" , multiplier =1 } , 169[ "per-minute" ] ={ divisor_source ="materials_divisor" , multiplier =60 } , 170[ "per-hour" ] ={ divisor_source ="materials_divisor" , multiplier =60 * 60 } , 171[ "transport-belts" ] ={ divisor_required =true , divisor_source ="transport_belt_divisor" , type_filter ="item" } , 172[ "inserters" ] ={ divisor_required =true , divisor_source ="inserter_divisor" , type_filter ="item" } , 173} 174 175--- @type Timescale[] 176gui_util . ordered_timescales ={ 177"per-second" , 178"per-minute" , 179"per-hour" , 180"transport-belts" , 181"inserters" , 182} 183 184return gui_util