z_mirror/RecipeBook
git clone https://git.raiguard.me/z_mirror/RecipeBook
bcd2b2e
trunk
1local flib_table =require ( "__flib__.table" ) 2local util =require ( "scripts.util" ) 3 4--- @type table<uint, table<SpritePath, boolean>> 5local entries ={} 6 7--- @param prototype GenericPrototype 8--- @param force_index uint 9local function research ( prototype , force_index ) 10assert ( prototype , "Prototype was nil" ) 11local path =util . get_path ( prototype ) 12local researched =flib_table . get_or_insert ( entries , force_index , {} ) 13 14if not util . should_include ( prototype ) then 15return 16end 17if researched [ path ] then 18return 19end 20researched [ path ] =true 21 22if prototype . object_name == "LuaTilePrototype" then 23local fluid =prototype . fluid 24if fluid then 25for _ , offshore_pump in pairs ( prototypes . get_entity_filtered ( { { filter ="type" , type ="offshore-pump" } } )) do 26if researched [ util . get_path ( offshore_pump )] then 27research ( fluid , force_index ) 28break 29end 30end 31end 32elseif prototype . object_name == "LuaTechnologyPrototype" then 33for _ , effect in pairs ( prototype . effects ) do 34if effect . type == "unlock-recipe" then 35research ( prototypes . recipe [ effect . recipe ], force_index ) 36end 37end 38elseif prototype . object_name == "LuaRecipePrototype" then 39if prototype . unlock_results then 40for _ , product in pairs ( prototype . products ) do 41if product . type ~= "research-progress" then 42research ( prototypes [ product . type ][ product . name ], force_index ) 43end 44end 45end 46elseif prototype . object_name == "LuaItemPrototype" then 47for _ , product in pairs ( prototype . rocket_launch_products ) do 48if product . type ~= "research-progress" then 49research ( prototypes [ product . type ][ product . name ], force_index ) 50end 51end 52local burnt_result =prototype . burnt_result 53if burnt_result then 54research ( burnt_result , force_index ) 55end 56local place_result =prototype . place_result 57if place_result then 58research ( place_result , force_index ) 59end 60local place_as_tile_result =prototype . place_as_tile_result 61if place_as_tile_result then 62research ( place_as_tile_result . result , force_index ) 63end 64local place_as_equipment_result =prototype . place_as_equipment_result 65if place_as_equipment_result then 66research ( place_as_equipment_result , force_index ) 67end 68local spoil_result =prototype . spoil_result 69if spoil_result then 70research ( spoil_result , force_index ) 71end 72-- TODO: Spoil trigger results 73elseif prototype . object_name == "LuaEntityPrototype" then 74local mineable_properties =prototype . mineable_properties 75if mineable_properties then 76for _ , product in pairs ( mineable_properties . products or {} ) do 77if product . type ~= "research-progress" then 78research ( prototypes [ product . type ][ product . name ], force_index ) 79end 80end 81end 82if prototype . type == "boiler" then 83for _ , fluidbox in pairs ( prototype . fluidbox_prototypes ) do 84-- TODO: Are multiple outputs possible? 85if fluidbox . production_type == "output" and fluidbox . filter then 86research ( fluidbox . filter , force_index ) 87end 88end 89end 90if prototype . type == "mining-drill" then 91--- @type string|boolean? 92local filter 93for _ , fluidbox_prototype in pairs ( prototype . fluidbox_prototypes ) do 94local production_type =fluidbox_prototype . production_type 95if production_type ~= "output" then 96filter =fluidbox_prototype . filter and fluidbox_prototype . filter . name or true 97break 98end 99end 100local resource_categories =prototype . resource_categories or {} 101for _ , resource in pairs ( prototypes . get_entity_filtered ( { { filter ="type" , type ="resource" } } )) do 102local mineable =resource . mineable_properties 103local required_fluid =mineable . required_fluid 104if 105resource_categories [ resource . resource_category ] 106and ( not required_fluid or filter == true or filter == required_fluid ) 107then 108research ( resource , force_index ) 109end 110end 111end 112if prototype . type == "offshore-pump" then 113for _ , tile in pairs ( prototypes . tile ) do 114if tile . fluid and researched [ util . get_path ( tile )] then 115research ( tile . fluid , force_index ) 116end 117end 118end 119end 120end 121 122--- @param e EventData.on_research_finished 123local function on_research_finished ( e ) 124research ( e . research . prototype , e . research . force . index ) 125end 126 127--- @param surface LuaSurface 128local function research_surface ( surface ) 129local autoplace_settings =surface . map_gen_settings . autoplace_settings 130local entity_settings =autoplace_settings . entity 131if entity_settings then 132for entity_name in pairs ( entity_settings . settings or {} ) do 133local entity =prototypes . entity [ entity_name ] 134-- TODO: Check cases where you can hand-mine but don't have any mining drills that can mine. 135if entity and entity . type ~= "resource" then 136for _ , force in pairs ( storage . forces ) do 137if force . valid then 138research ( entity , force . index ) 139end 140end 141end 142end 143end 144local tile_settings =autoplace_settings . tile 145if tile_settings then 146for tile_name in pairs ( tile_settings . settings or {} ) do 147local tile =prototypes . tile [ tile_name ] 148if tile then 149for _ , force in pairs ( storage . forces ) do 150if force . valid then 151research ( tile , force . index ) 152end 153end 154end 155end 156end 157end 158 159--- @param e EventData.on_surface_created 160local function on_surface_created ( e ) 161local surface =game . get_surface ( e . surface_index ) 162if not surface then 163return 164end 165research_surface ( surface ) 166end 167 168--- @param force LuaForce 169local function rebuild_force ( force ) 170local force_index =force . index 171-- Gather-able items 172for _ , entity in 173pairs ( prototypes . get_entity_filtered ( { 174--- @diagnostic disable-next-line unused-fields 175{ filter ="type" , type ="tree" } , 176--- @diagnostic disable-next-line unused-fields 177{ filter ="type" , type ="fish" } , 178} )) 179do 180research ( entity , force_index ) 181end 182-- Technologies 183for _ , technology in pairs ( force . technologies ) do 184if technology . researched then 185research ( technology . prototype , force_index ) 186end 187end 188-- Recipes (some may be enabled without technologies) 189for _ , recipe in pairs ( force . recipes ) do 190-- Some recipes will be enabled from the start, but will only be craftable in machines 191if recipe . enabled and not ( recipe . prototype . enabled and recipe . prototype . hidden_from_player_crafting ) then 192research ( recipe . prototype , force_index ) 193end 194end 195-- Characters 196--- @diagnostic disable-next-line unused-fields 197for _ , character in pairs ( prototypes . get_entity_filtered ( { { filter ="type" , type ="character" } } )) do 198research ( character , force_index ) 199end 200end 201 202local function rebuild () 203if not storage . forces or not storage . surfaces then 204return 205end 206for _ , surface in pairs ( storage . surfaces ) do 207if surface . valid then 208research_surface ( surface ) 209end 210end 211for _ , force in pairs ( storage . forces or {} ) do 212if force . valid then 213rebuild_force ( force ) 214end 215end 216end 217 218--- @param e EventData.on_force_created 219local function on_force_created ( e ) 220if not storage . forces then 221return 222end 223storage . forces [ e . force . index ] =e . force 224rebuild_force ( e . force ) 225end 226 227local function setup_cache () 228storage . forces ={} 229for _ , force in pairs ( game . forces ) do 230storage . forces [ force . index ] =force 231end 232storage . surfaces ={} 233for _ , surface in pairs ( game . surfaces ) do 234storage . surfaces [ surface . index ] =surface 235end 236end 237 238--- @class Researched 239local M ={} 240 241function M . on_init () 242setup_cache () 243rebuild () 244end 245 246M . on_load =rebuild 247 248function M . on_configuration_changed () 249setup_cache () 250rebuild () 251end 252 253M . events ={ 254[ defines . events . on_force_created ] =on_force_created , 255-- [defines.events.on_forces_merged] = on_forces_merged, -- TODO: 256[ defines . events . on_research_finished ] =on_research_finished , 257[ defines . events . on_surface_created ] =on_surface_created , 258-- [defines.events.on_surface_deleted] = on_surface_destroyed, -- TODO: 259} 260 261--- @param prototype GenericPrototype 262--- @param force_index uint 263--- @return boolean 264function M . is ( prototype , force_index ) 265local force_entries =entries [ force_index ] 266if not force_entries then 267return false 268end 269return force_entries [ util . get_path ( prototype )] or false 270end 271 272return M 273 274--[[ 275Database topology 276- Build per-player search tree depending on their preferences 277- Track researched objects with a simple map<SpritePath, boolean> 278- Memoize results as they are browsed 279- Consider pregenerating expensive lists - or maybe just pregen everything like in RB3? 280]] 281--