z_mirror/BlueprintTools
git clone https://git.raiguard.me/z_mirror/BlueprintTools
cd858de
trunk
1local util =require ( "scripts.util" ) 2 3--- @class EntityPrototypeAndQualityName 4--- @field entity LuaEntityPrototype 5--- @field quality string 6 7---@param index uint 8---@return LuaItemStack? 9local function get_cursor_stack ( index ) 10local player =game . get_player ( index ) --[[@as LuaPlayer]] 11local cursor =player . cursor_stack 12if not ( cursor and cursor . valid_for_read ) then 13return 14end 15return cursor 16end 17 18---@param event EventData.CustomInputEvent 19---@return EntityPrototypeAndQualityName? 20local function get_selected_id ( event ) 21local selected =event . selected_prototype 22if not selected then 23return 24end 25 26if selected . base_type == "entity" then 27--- @cast selected.quality -? 28local player =game . get_player ( event . player_index ) --[[@as LuaPlayer]] 29local selected_entity =player . selected 30if not selected_entity then 31return { entity =prototypes . entity [ selected . name ], quality =selected . quality } 32end 33if selected_entity . type == "entity-ghost" and selected_entity . ghost_prototype . type ~= "tile" then 34return { 35entity =selected_entity . ghost_prototype --[[@as LuaEntityPrototype]] , 36quality =selected . quality , 37} 38end 39return { entity =selected_entity . prototype , quality =selected . quality } 40 41-- _CodeGreen: I couldn't find anything in game where I could click on an item or recipe that actually had a CustomInputEvent fire 42-- But the code to handle them is here, I guess 43elseif selected . base_type == "item" then 44--- @cast selected.quality -? 45local item =prototypes . item [ selected . name ] 46local place_result =item . place_result 47if not place_result then 48return 49end 50return { entity =place_result , quality =selected . quality } 51elseif selected . base_type == "recipe" then 52--- @cast selected.quality -? 53--- @type LuaRecipePrototype 54local recipe =prototypes . recipe [ selected . name ] 55local main_product =recipe . main_product 56local products =main_product and { main_product } or recipe . products 57local item_prototypes =prototypes . item 58for _ , product in pairs ( products ) do 59if product . type ~= "item" then 60goto continue 61end 62--- @cast product ItemProduct 63--- @type LuaItemPrototype 64local item =item_prototypes [ product . name ] 65local place_result =item . place_result 66if place_result then 67return { entity =place_result , quality =selected . quality } 68end 69:: continue :: 70end 71end 72end 73 74---@param cursor LuaItemStack 75---@param from string 76--- @param quality string? 77---@param to string? 78---@return LocalisedString? 79local function add_upgrade_filter ( cursor , from , quality , to ) 80if not to then 81return "message.bpt-entity-cannot-upgrade" 82end 83 84local first_index 85for i =1 , cursor . mapper_count do 86local mapper =cursor . get_mapper ( i , "from" ) 87if not mapper then 88first_index =first_index or i 89goto continue 90end 91 92if mapper . type == "entity" and mapper . name == from and mapper . quality == quality then 93local to_mapper =cursor . get_mapper ( i , "to" ) 94if to_mapper and to_mapper . name == to and to_mapper . quality == quality then 95return "message.bpt-filter-already-present" 96end 97cursor . set_mapper ( i , "to" , { type ="entity" , name =to , quality =quality , comparator =quality and "=" or nil } ) 98return 99end 100 101:: continue :: 102end 103 104if not first_index then 105first_index =cursor . mapper_count + 1 106end 107cursor . set_mapper ( 108first_index , 109"from" , 110{ type ="entity" , name =from , quality =quality , comparator =quality and "=" or nil } 111) 112cursor . set_mapper ( 113first_index , 114"to" , 115{ type ="entity" , name =to , quality =quality , comparator =quality and "=" or nil } 116) 117end 118 119---@param cursor LuaItemStack 120---@param id EntityPrototypeAndQualityName 121---@return LocalisedString? 122local function add_filter ( cursor , id ) 123if cursor . is_deconstruction_item then 124local first_empty =0 125local filters =cursor . entity_filters 126for i =1 , cursor . entity_filter_count do 127local filter =filters [ i ] 128if filter and filter . name == id . entity . name and filter . quality == id . quality then 129return "message.bpt-filter-already-present" 130elseif not filter and first_empty == 0 then 131first_empty =i 132end 133end 134if first_empty == 0 then 135first_empty =cursor . entity_filter_count + 1 136end 137--- @diagnostic disable-next-line: param-type-mismatch 138if not cursor . set_entity_filter ( first_empty , { name =id . entity , quality =id . quality } ) then 139return "message.bpt-unable-to-add-filter" 140end 141elseif cursor . is_upgrade_item then 142local next_upgrade =id . entity . next_upgrade 143local to =next_upgrade and next_upgrade . name or storage . downgrades [ id . entity . name ] 144return add_upgrade_filter ( cursor , id . entity . name , id . quality , to ) 145end 146end 147 148--- @param event EventData.CustomInputEvent 149script . on_event ( "bpt-pipette-add" , function ( event ) 150local cursor =get_cursor_stack ( event . player_index ) 151if not cursor then 152return 153end 154local entity_and_quality =get_selected_id ( event ) 155if not entity_and_quality then 156return 157end 158 159local player =game . get_player ( event . player_index ) --[[@as LuaPlayer]] 160local message =add_filter ( cursor , entity_and_quality ) 161if message then 162util . cursor_notification ( player , { message } , "utility/cannot_build" ) 163else 164player . play_sound ( { path ="utility/smart_pipette" } ) 165end 166end ) 167 168---@param cursor LuaItemStack 169---@param id EntityPrototypeAndQualityName 170---@return LocalisedString? 171local function remove_filter ( cursor , id ) 172if cursor . is_deconstruction_item then 173local filters =cursor . entity_filters 174for i =1 , cursor . entity_filter_count do 175local filter =filters [ i ] 176if filter and filter . name == id . entity . name and filter . quality == id . quality then 177cursor . set_entity_filter ( i , nil ) 178return 179end 180end 181elseif cursor . is_upgrade_item then 182for i =1 , cursor . mapper_count do 183local mapper =cursor . get_mapper ( i , "from" ) 184--- @cast mapper -? 185if mapper . type == "entity" and mapper . name == id . entity . name and mapper . quality == id . quality then 186cursor . set_mapper ( i , "from" , nil ) 187cursor . set_mapper ( i , "to" , nil ) 188return 189end 190end 191end 192 193return "message.bpt-filter-not-present" 194end 195 196--- @param event EventData.CustomInputEvent 197script . on_event ( "bpt-pipette-remove" , function ( event ) 198local cursor =get_cursor_stack ( event . player_index ) 199if not cursor then 200return 201end 202local id =get_selected_id ( event ) 203if not id then 204return 205end 206 207local player =game . get_player ( event . player_index ) --[[@as LuaPlayer]] 208local message =remove_filter ( cursor , id ) 209if message then 210util . cursor_notification ( player , { message } , "utility/cannot_build" ) 211else 212player . play_sound ( { path ="utility/clear_cursor" } ) 213end 214end ) 215 216--- @param event EventData.CustomInputEvent 217script . on_event ( "bpt-pipette-downgrade" , function ( event ) 218local cursor =get_cursor_stack ( event . player_index ) 219if not cursor then 220return 221end 222if not cursor . is_upgrade_item then 223return 224end 225 226local id =get_selected_id ( event ) 227if not id then 228return 229end 230 231local downgrade =storage . downgrades [ id . entity . name ] 232local next_upgrade =id . entity . next_upgrade 233local to =downgrade or ( next_upgrade and next_upgrade . name ) 234 235local player =game . get_player ( event . player_index ) --[[@as LuaPlayer]] 236local message =add_upgrade_filter ( cursor , id . entity . name , id . quality , to ) 237if message then 238if message == "message.bpt-entity-cannot-upgrade" then 239message ="message.bpt-entity-cannot-downgrade" 240end 241util . cursor_notification ( player , { message } , "utility/cannot_build" ) 242else 243player . play_sound ( { path ="utility/smart_pipette" } ) 244end 245end ) 246 247local lib ={} 248 249function lib . cache_downgrades () 250---@type table<string, string> 251local downgrades ={} 252storage . downgrades =downgrades 253 254for name , entity in pairs ( prototypes . entity ) do 255--- @cast name string 256--- @cast entity LuaEntityPrototype 257local next_upgrade =entity . next_upgrade 258if next_upgrade then 259downgrades [ next_upgrade . name ] =name 260end 261end 262end 263 264return lib