diff --git a/control.lua b/control.lua index df6f9f6..f2015a0 100644 --- a/control.lua +++ b/control.lua @@ -1,7 +1,6 @@ require("defines") require("util") -require("scripts.autofill-requester-chest") require("scripts.cheats") require("scripts.configurable-super-boiler") require("scripts.creative-cargo-wagon") diff --git a/data-final-fixes.lua b/data-final-fixes.lua index ed420a2..5fe8dc7 100644 --- a/data-final-fixes.lua +++ b/data-final-fixes.lua @@ -188,14 +188,25 @@ data.raw["construction-robot"][creative_mode_defines.names.entities.super_constr -------------------------------------------------------------- --- Allow the creative lab to accept all research materials. +-- Allow the creative lab to accept every science pack that any real lab can use. +-- Deriving the list from other labs' inputs (rather than matching the "science-pack" +-- subgroup) keeps non-pack items that merely share that subgroup -- such as "coin" and +-- the "science" pictogram -- out of the creative lab. +local creative_lab_name = creative_mode_defines.names.entities.creative_lab +local void_lab_name = creative_mode_defines.names.entities.void_lab +local seen_science_packs = {} local tools = {} -for _, item in pairs(data.raw["item"]) do - if item.subgroup == "science-pack" then - table.insert(tools, item.name) +for lab_name, lab in pairs(data.raw["lab"]) do + if lab_name ~= creative_lab_name and lab_name ~= void_lab_name and lab.inputs then + for _, input_name in pairs(lab.inputs) do + if not seen_science_packs[input_name] then + seen_science_packs[input_name] = true + table.insert(tools, input_name) + end + end end end -data.raw["lab"][creative_mode_defines.names.entities.creative_lab].inputs = tools +data.raw["lab"][creative_lab_name].inputs = tools -------------------------------------------------------------- diff --git a/defines.lua b/defines.lua index 9a6f3ea..a415822 100644 --- a/defines.lua +++ b/defines.lua @@ -44,7 +44,6 @@ creative_mode_defines.names.settings = { unhide_items = creative_mode_defines.name_prefix .. "unhide-items", creative_chest_size = creative_mode_defines.name_prefix .. "creative-chest-size", creative_chest_contains_hidden_items = creative_mode_defines.name_prefix .. "creative-chest-contains-hidden-items", - autofill_requester_chest_size = creative_mode_defines.name_prefix .. "autofill-requester-chest-size", duplicating_chest_size = creative_mode_defines.name_prefix .. "duplicating-chest-size", void_requester_chest_size = creative_mode_defines.name_prefix .. "void-requester-chest-size", void_chest_size = creative_mode_defines.name_prefix .. "void-chest-size", @@ -91,8 +90,6 @@ creative_mode_defines.names.entities = { inf_requester_chest = creative_mode_defines.name_prefix .. "inf-requester-chest", inf_provider_chest = creative_mode_defines.name_prefix .. "inf-provider-chest", new_creative_provider_chest = creative_mode_defines.name_prefix .. "new-creative-provider-chest", - autofill_requester_chest = creative_mode_defines.name_prefix .. "autofill-requester-chest", - new_autofill_requester_chest = creative_mode_defines.name_prefix .. "new-autofill-requester-chest", duplicating_chest = creative_mode_defines.name_prefix .. "duplicating-chest", duplicating_provider_chest = creative_mode_defines.name_prefix .. "duplicating-provider-chest", void_requester_chest = creative_mode_defines.name_prefix .. "void-requester-chest", @@ -160,7 +157,6 @@ creative_mode_defines.names.items = { creative_provider_chest = creative_mode_defines.name_prefix .. "creative-provider-chest", inf_requester_chest = creative_mode_defines.name_prefix .. "inf-requester-chest", inf_provider_chest = creative_mode_defines.name_prefix .. "inf-provider-chest", - autofill_requester_chest = creative_mode_defines.name_prefix .. "autofill-requester-chest", duplicating_chest = creative_mode_defines.name_prefix .. "duplicating-chest", duplicating_provider_chest = creative_mode_defines.name_prefix .. "duplicating-provider-chest", void_requester_chest = creative_mode_defines.name_prefix .. "void-requester-chest", @@ -249,7 +245,6 @@ creative_mode_defines.names.recipes = { creative_provider_chest = creative_mode_defines.name_prefix .. "creative-provider-chest", inf_requester_chest = creative_mode_defines.name_prefix .. "inf-requester-chest", inf_provider_chest = creative_mode_defines.name_prefix .. "inf-provider-chest", - autofill_requester_chest = creative_mode_defines.name_prefix .. "autofill-requester-chest", duplicating_chest = creative_mode_defines.name_prefix .. "duplicating-chest", duplicating_provider_chest = creative_mode_defines.name_prefix .. "duplicating-provider-chest", void_requester_chest = creative_mode_defines.name_prefix .. "void-requester-chest", @@ -1530,10 +1525,6 @@ table.insert( creative_mode_defines.values.creative_provider_chest_additional_content_names, creative_mode_defines.names.items.creative_provider_chest ) -table.insert( - creative_mode_defines.values.creative_provider_chest_additional_content_names, - creative_mode_defines.names.items.autofill_requester_chest -) table.insert( creative_mode_defines.values.creative_provider_chest_additional_content_names, creative_mode_defines.names.items.duplicating_chest diff --git a/locale/en/base.cfg b/locale/en/base.cfg index 9c89c23..657936a 100644 --- a/locale/en/base.cfg +++ b/locale/en/base.cfg @@ -5,8 +5,6 @@ creative-mod_creative-provider-chest=Creative provider chest creative-mod_new-creative-provider-chest=Creative provider chest creative-mod_inf-requester-chest=Infinity requester chest creative-mod_inf-provider-chest=Infinity provider chest -creative-mod_autofill-requester-chest=Autofill requester chest -creative-mod_new-autofill-requester-chest=Autofill requester chest creative-mod_duplicating-chest=Duplicating chest creative-mod_duplicating-provider-chest=Duplicating provider chest creative-mod_void-requester-chest=Void requester chest @@ -61,8 +59,6 @@ creative-mod_creative-provider-chest=A passive provider chest that contains all creative-mod_new-creative-provider-chest=A passive provider chest that contains all items in the game and can auto refill itself. Its last slot can be used for voiding unwanted items. (May need to place more than one to get more items.)\nClears contents upon removal. creative-mod_inf-requester-chest=A chest that can be used to create or destroy items, and acts as a requester chest. Set the infinity filters to be the item(s) and amount(s) you wish the chest to keep stocked. Checking the box will have it remove any items not in a filter. creative-mod_inf-provider-chest=A chest that can be used to create or destroy items, and acts as a provider chest. Set the infinity filters to be the item(s) and amount(s) you wish the chest to keep stocked. Checking the box will have it remove any items not in a filter. -creative-mod_autofill-requester-chest=A requester chest that automatically refills itself according to the requests. Unlike the normal requester chests, 2 identical requests in this chest will be filled twice. The unused slots will be emptied.\nClears contents upon removed. -creative-mod_new-autofill-requester-chest=A requester chest that automatically refills itself according to the requests. Unlike the normal requester chests, 2 identical requests in this chest will be filled twice. The unused slots will be emptied.\nClears contents upon removed. creative-mod_duplicating-chest=A steel chest that duplicates the item in its first slot.\nClears contents upon removal. creative-mod_duplicating-provider-chest=A passive provider chest that duplicates the item in its first slot.\nClears contents upon removed. creative-mod_void-requester-chest=A requester chest that removes all items inside it.\nClears contents upon removal. @@ -158,7 +154,6 @@ creative-mod_default-initial-action=Default initial action creative-mod_unhide-items=Unhide items creative-mod_creative-chest-size=Creative chest size creative-mod_creative-chest-contains-hidden-items=Creative chests contain hidden items -creative-mod_autofill-requester-chest-size=Autofill request chest size creative-mod_duplicating-chest-size=Duplicating chest size creative-mod_void-requester-chest-size=Void requester chest size creative-mod_void-chest-size=Void chest size @@ -185,7 +180,6 @@ creative-mod_default-initial-actions=What should be done to Creative Mode when a creative-mod_unhide-items=Unhide the items and entities provided by Creative Mode from the slot filters, so you can select them in matter source's filters, logistic requester slots, deconstruction filters, etc. But they will be visible even when Creative Mode is not enabled in the game. creative-mod_creative-chest-size=Usable inventory size of each __ENTITY__creative-mod_creative-chest__ and __ENTITY__creative-mod_creative-provider-chest__.\nAn extra slot will be added for trashing.\nIts value affects how many chests you need to place for one item cycle. Larger inventory size means fewer chests are needed. However, it is also more likely to cause performance issues when hovering the chests.\nChanging its value for an existing game will cause item regrouping. creative-mod_creative-chest-contains-hidden-items=Whether __ENTITY__creative-mod_creative-chest__ and __ENTITY__creative-mod_creative-provider-chest__ should contain hidden items.\nSome items are hidden for good reasons, like rocket parts, which is meaningless even if you can get it.\nChanging this value for an existing game will cause item regrouping. -creative-mod_autofill-requester-chest-size=Inventory size of each __ENTITY__creative-mod_autofill-requester-chest__. creative-mod_duplicating-chest-size=Inventory size of each __ENTITY__creative-mod_duplicating-chest__ and __ENTITY__creative-mod_duplicating-provider-chest__. creative-mod_void-requester-chest-size=Inventory size of each __ENTITY__creative-mod_void-requester-chest__. creative-mod_void-chest-size=Inventory size of each __ENTITY__creative-mod_void-chest__. diff --git a/locale/zh-CN/base.cfg b/locale/zh-CN/base.cfg index d107611..e2a4d4d 100644 --- a/locale/zh-CN/base.cfg +++ b/locale/zh-CN/base.cfg @@ -5,8 +5,6 @@ creative-mod_creative-provider-chest=创造提供箱 creative-mod_new-creative-provider-chest=创造提供箱 creative-mod_inf-requester-chest=无限请求箱 creative-mod_inf-provider-chest=无限提供箱 -creative-mod_autofill-requester-chest=自动填充请求箱 -creative-mod_new-autofill-requester-chest=自动填充请求箱 creative-mod_duplicating-chest=复制箱 creative-mod_duplicating-provider-chest=复制提供箱 creative-mod_void-requester-chest=虚空请求箱 @@ -59,8 +57,6 @@ creative-mod_creative-provider-chest=一个包含游戏中所有物品并能够 creative-mod_new-creative-provider-chest=一个包含游戏中所有物品并能够自动重新填充的被动提供箱子。它的最后一个槽位可以用来丢弃不需要的物品。(可能需要放置多个以获得更多物品。)\n移除时清空内容。 creative-mod_inf-requester-chest=一个可以用于创建或销毁物品,并作为请求箱的箱子。设置无限过滤器为你希望箱子保持的物品和数量。勾选框将移除不在过滤器中的物品。 creative-mod_inf-provider-chest=一个可以用于创建或销毁物品,并作为提供箱的箱子。设置无限过滤器为你希望箱子保持的物品和数量。勾选框将移除不在过滤器中的物品。 -creative-mod_autofill-requester-chest=一个根据请求自动重新填充自身的请求箱。与普通请求箱不同,这个箱子中的两个相同请求将被填充两次。未使用的槽位将被清空。\n移除时清空内容。 -creative-mod_new-autofill-requester-chest=一个根据请求自动重新填充自身的请求箱。与普通请求箱不同,这个箱子中的两个相同请求将被填充两次。未使用的槽位将被清空。\n移除时清空内容。 creative-mod_duplicating-chest=一个复制其第一个槽位物品的钢制箱子。\n移除时清空内容。 creative-mod_duplicating-provider-chest=一个复制其第一个槽位物品的被动提供箱子。\n移除时清空内容。 creative-mod_void-requester-chest=一个移除其内部所有物品的请求箱。\n移除时清空内容。 @@ -152,7 +148,6 @@ creative-mod_default-initial-action=默认初始操作 creative-mod_unhide-items=取消隐藏物品 creative-mod_creative-chest-size=创造箱大小 creative-mod_creative-chest-contains-hidden-items=创造箱包含隐藏物品 -creative-mod_autofill-requester-chest-size=自动填充请求箱大小 creative-mod_duplicating-chest-size=复制箱大小 creative-mod_void-requester-chest-size=虚空请求箱大小 creative-mod_void-chest-size=虚空箱大小 @@ -179,7 +174,6 @@ creative-mod_default-initial-actions=在新的游戏开始或在现有游戏中 creative-mod_unhide-items=取消隐藏创造模式提供的物品和实体,以便你可以在物质源的过滤器、物流请求槽、拆除过滤器等中选择它们。但即使游戏未启用创造模式,它们也将可见。 creative-mod_creative-chest-size=每个__ENTITY__creative-mod_creative-chest__和__ENTITY__creative-provider-chest__的可用库存大小。\n将添加一个额外的槽位用于丢弃。\n它的值影响你需要放置多少个箱子来完成一个物品循环。更大的库存大小意味着需要更少的箱子。然而,它也更可能导致在悬停箱子时出现性能问题。\n为现有游戏更改其值将导致物品重新分组。 creative-mod_creative-chest-contains-hidden-items=__ENTITY__creative-mod_creative-chest__和__ENTITY__creative-provider-chest__是否应包含隐藏物品。\n一些物品被隐藏是有原因的,比如火箭部件,即使你可以得到它,也是没有意义的。\n为现有游戏更改此值将导致物品重新分组。 -creative-mod_autofill-requester-chest-size=每个__ENTITY__creative-mod_autofill-requester-chest__的库存大小。 creative-mod_duplicating-chest-size=每个__ENTITY__creative-mod_duplicating-chest__和__ENTITY__creative-provider-chest__的库存大小。 creative-mod_void-requester-chest-size=每个__ENTITY__creative-mod_void-requester-chest__的库存大小。 creative-mod_void-chest-size=每个__ENTITY__creative-mod_void-chest__的库存大小。 diff --git a/locale/zh-TW/base.cfg b/locale/zh-TW/base.cfg index 1868963..ab39c51 100644 --- a/locale/zh-TW/base.cfg +++ b/locale/zh-TW/base.cfg @@ -1,7 +1,6 @@ [entity-name] creative-mod_creative-chest=創造箱 creative-mod_creative-provider-chest=創造供貨箱 -creative-mod_autofill-requester-chest=自動補給需求箱 creative-mod_duplicating-chest=物品複製箱 creative-mod_duplicating-provider-chest=複製供貨箱 creative-mod_void-requester-chest=虛空需求箱 @@ -47,7 +46,6 @@ creative-mode_infinite-resource=__1__ (無限) [entity-description] creative-mod_creative-chest=能夠提供遊戲內的所有物品的鋼箱,會自動補給,最後一格可以用來丟棄物品。 (可能需要放置多於一個以獲取更多物品)\n被移除時自動清空 creative-mod_creative-provider-chest=能夠提供遊戲內的所有物品的被動供貨箱,會自動補給,最後一格可以用來丟棄物品。 (可能需要放置多於一個以獲取更多物品)\n被移除時自動清空 -creative-mod_autofill-requester-chest=能夠自動補給所需物品的需求箱。不同於普通需求箱,兩個相同的需求會被補給兩次。未使用的欄位會被清空。\n被移除時自動清空 creative-mod_duplicating-chest=能夠不斷複製第一格物品的鋼箱。\n被移除時自動清空 creative-mod_duplicating-provider-chest=能夠不斷複製第一格物品的被動供貨箱。\n被移除時自動清空 creative-mod_void-requester-chest=能夠不斷銷毀物品的需求箱。\n被移除時自動清空 @@ -129,7 +127,6 @@ creative-mod_default-initial-action=預設初始動作 creative-mod_unhide-items=不隱藏物品 creative-mod_creative-chest-size=創造箱大小 creative-mod_creative-chest-contains-hidden-items=創造箱包含隱藏物品 -creative-mod_autofill-requester-chest-size=自動補給需求箱大小 creative-mod_duplicating-chest-size=複製箱大小 creative-mod_void-requester-chest-size=虛空需求箱大小 creative-mod_void-chest-size=虛空箱大小 @@ -154,7 +151,6 @@ creative-mod_default-initial-actions=當新遊戲開始時、或此模組第一 creative-mod_unhide-items=讓創造模式的物品顯示在物品篩選界面上,這樣就能在物質產生器的篩選欄、物流需求槽、拆除計畫者的篩選欄等選擇它們,但即使創造模式並未在遊戲內啟動也可看見 creative-mod_creative-chest-size=每個__ENTITY__creative-mod_creative-chest__和__ENTITY__creative-mod_creative-provider-chest__的可用格數\n外加一格用來棄置物品\n此數值會影響每一個物品週期需要多少個創造箱,箱子越大,需要的箱子越少,但也越容易在選取箱子時產生性能問題\n為運行中的遊戲更改此數值會使物品重新分組 creative-mod_creative-chest-contains-hidden-items=__ENTITY__creative-mod_creative-chest__和__ENTITY__creative-mod_creative-provider-chest__應否包含隱藏物品\n遊戲內有部份物品因為特殊理由而被隱藏,例如火箭零件,即使拿到手上也沒有任何作用\n為運行中的遊戲更改此數值會使物品重新分組 -creative-mod_autofill-requester-chest-size=每個__ENTITY__creative-mod_autofill-requester-chest__的大小 creative-mod_duplicating-chest-size=每個__ENTITY__creative-mod_duplicating-chest__和__ENTITY__creative-mod_duplicating-provider-chest__的大小 creative-mod_void-requester-chest-size=每個__ENTITY__creative-mod_void-requester-chest__的大小 creative-mod_void-chest-size=每個__ENTITY__creative-mod_void-chest__的大小 diff --git a/prototypes/entity.lua b/prototypes/entity.lua index 7b16d0f..6d1cf83 100644 --- a/prototypes/entity.lua +++ b/prototypes/entity.lua @@ -1215,16 +1215,6 @@ data:extend({ settings.startup[creative_mode_defines.names.settings.creative_chest_size].value + 1, "passive-provider" ), - -- Autofill requester chest - logistic_container( - creative_mode_defines.names.entities.autofill_requester_chest, - creative_mode_defines.names.items.autofill_requester_chest, - "autofill-requester-chest.png", - "autofill-requester-chest.png", - nil, - settings.startup[creative_mode_defines.names.settings.autofill_requester_chest_size].value, - "requester" - ), -- New Creative Chest infcontainer( creative_mode_defines.names.entities.new_creative_chest, @@ -1244,16 +1234,6 @@ data:extend({ settings.startup[creative_mode_defines.names.settings.creative_chest_size].value + 1, "passive-provider" ), - -- New Autofill requester chest - infcontainer( - creative_mode_defines.names.entities.new_autofill_requester_chest, - creative_mode_defines.names.items.autofill_requester_chest, - "autofill-requester-chest.png", - "autofill-requester-chest.png", - nil, - settings.startup[creative_mode_defines.names.settings.autofill_requester_chest_size].value, - "requester" - ), -- Duplicating chest container( creative_mode_defines.names.entities.duplicating_chest, diff --git a/prototypes/item.lua b/prototypes/item.lua index 5a7ab1d..4d4acbe 100644 --- a/prototypes/item.lua +++ b/prototypes/item.lua @@ -30,18 +30,6 @@ data:extend({ place_result = creative_mode_defines.names.entities.new_creative_provider_chest, stack_size = 50, }, - { - -- Autofill requester chest - type = "item", - name = creative_mode_defines.names.items.autofill_requester_chest, - icon_size = 32, - icon = creative_mode_defines.mod_directory .. "/graphics/icons/autofill-requester-chest.png", - hidden = hidden, - subgroup = creative_mode_defines.names.item_subgroups.items, - order = "c", - place_result = creative_mode_defines.names.entities.new_autofill_requester_chest, - stack_size = 50, - }, { -- Infinity Requester Chest type = "item", diff --git a/prototypes/recipe.lua b/prototypes/recipe.lua index 9883e34..5741530 100644 --- a/prototypes/recipe.lua +++ b/prototypes/recipe.lua @@ -45,14 +45,6 @@ data:extend({ results = { { type = "item", name = creative_mode_defines.names.items.creative_provider_chest, amount = 1 } }, enabled = false, }, - { - -- Autofill requester chest - type = "recipe", - name = creative_mode_defines.names.recipes.autofill_requester_chest, - ingredients = {}, - results = { { type = "item", name = creative_mode_defines.names.items.autofill_requester_chest, amount = 1 } }, - enabled = false, - }, { -- Duplicating chest type = "recipe", diff --git a/prototypes/technology.lua b/prototypes/technology.lua index aa61ae1..5af023b 100644 --- a/prototypes/technology.lua +++ b/prototypes/technology.lua @@ -1,11 +1,23 @@ local function create_void_technology_ingredients() + -- Require one of every science pack any real lab can consume. Deriving the list + -- from other labs' inputs (rather than matching the "science-pack" subgroup) keeps + -- non-pack items that merely share that subgroup -- such as "coin" and the "science" + -- pictogram -- out of the requirement, so a real lab can actually satisfy it. + local creative_lab_name = creative_mode_defines.names.entities.creative_lab + local void_lab_name = creative_mode_defines.names.entities.void_lab + local seen_science_packs = {} local research_items = {} - for _, item in pairs(data.raw.item) do - if item.subgroup == "science-pack" then - table.insert(research_items, { - item.name, - 1, - }) + for lab_name, lab in pairs(data.raw["lab"]) do + if lab_name ~= creative_lab_name and lab_name ~= void_lab_name and lab.inputs then + for _, input_name in pairs(lab.inputs) do + if not seen_science_packs[input_name] then + seen_science_packs[input_name] = true + table.insert(research_items, { + input_name, + 1, + }) + end + end end end diff --git a/scripts/autofill-requester-chest.lua b/scripts/autofill-requester-chest.lua deleted file mode 100644 index 35baeee..0000000 --- a/scripts/autofill-requester-chest.lua +++ /dev/null @@ -1,99 +0,0 @@ --- This file contains variables or functions that are related to the Autofill Requester Chest in this mod. -if not autofill_requester_chest then - autofill_requester_chest = {} -end - --- Refill the given chest according to its requests. -local function refill_chest(chest) - local inventory = chest.get_output_inventory() - local inventory_size = #inventory - local slot = 1 - local item_prototypes = prototypes.item - local math_ceil = math.ceil - - -- local request_slot_count = chest.request_slot_count - local request_slot_count = get_character_request_slot_count(chest) - for slot_index = 1, request_slot_count, 1 do - -- Get the request in the iterated request slot. - local points = chest.get_logistic_point() - for index, point in ipairs(points) do - if point ~= nil and point.enabled and point.filters ~= nil then - local requested_item_stack = point.filters[slot_index] - -- local request = chest.get_request_slot(request_slot) - if requested_item_stack then - -- Get the requested item name and count. - local item_name = requested_item_stack.name - local item_count = requested_item_stack.count - -- How many slots are required? - if item_count > 0 then - local stack_size = item_prototypes[item_name].stack_size - -- Set items until it fulfills the requested amount. - local is_last_stack = false - repeat - local set_count - if item_count <= stack_size then - set_count = item_count - is_last_stack = true - else - set_count = stack_size - end - inventory[slot].set_stack({ name = item_name, count = set_count }) - item_count = item_count - set_count - slot = slot + 1 - if slot > inventory_size then - -- No more slot. - return - end - until is_last_stack - end - end - end - end - end - -- Empty the remaining slots. - for i = slot, inventory_size, 1 do - inventory[i].clear() - end -end - --- Refill the chest contents among the given array of chests according to the given index of the next chest to update. --- Returns the index of the next chest to update in the next tick. -local function refill_chests(chests, next_chest_to_update) - if #chests <= 0 then - return 1 - end - - local chest_index = next_chest_to_update - for i = 1, 5, 1 do - local chest = chests[chest_index] - if chest.valid then - if not chest.to_be_deconstructed(chest.force) then - -- Refill the chest. - refill_chest(chest) - end - - -- Prepare for the next chest. - chest_index = chest_index + 1 - -- No more next chest. Return to the first chest. - if chest_index > #chests then - return 1 - end - else - table.remove(chests, chest_index) - if chest_index > #chests then - return 1 - end - end - end - -- Return the index of the next chest to update. - return chest_index -end - --- Processes the tables related to Autofill Requester Chests in storage. -function autofill_requester_chest.tick() - -- Refill the chests. - storage.creative_mode.autofill_requester_chest_next_update_index = refill_chests( - storage.creative_mode.autofill_requester_chest, - storage.creative_mode.autofill_requester_chest_next_update_index - ) -end diff --git a/scripts/cheats.lua b/scripts/cheats.lua index 66c9b3d..65ed131 100644 --- a/scripts/cheats.lua +++ b/scripts/cheats.lua @@ -2285,7 +2285,7 @@ cheats.magic_wand_modifications = { get_value_function = function(entity) if entity and entity.valid then if entity.health then - return entity.health >= entity.prototype.get_max_health() + return entity.health >= entity.max_health end end return nil @@ -2294,7 +2294,7 @@ cheats.magic_wand_modifications = { apply_to_target_function = function(entity, enable, source_player) if entity.health then if enable then - entity.health = entity.prototype.get_max_health() + util.restore_entity_to_full_health(entity) else entity.health = 1 end @@ -2844,9 +2844,10 @@ function cheats.on_pre_build(player) local cursor_stack = player.cursor_stack if cursor_stack.valid_for_read then -- Don't know why sometimes the stack becomes invalid and causes error. local cursor_stack_name = cursor_stack.name + local cursor_stack_quality = cursor_stack.quality.name -- Make sure the item is not blacklisted. if not keep_last_item_blacklist[cursor_stack_name] then - if player.get_item_count(cursor_stack_name) <= 1 then + if player.get_item_count({ name = cursor_stack_name, quality = cursor_stack_quality }) <= 1 then local cursor_stack_prototype = cursor_stack.prototype if cursor_stack_prototype.stackable and cursor_stack_prototype.stack_size > 1 then -- The item is stackable. Simply increase the stack size on the cursor. @@ -2857,6 +2858,7 @@ function cheats.on_pre_build(player) if player.insert({ name = cursor_stack_name, + quality = cursor_stack_quality, count = 1, }) > 0 then @@ -2870,10 +2872,13 @@ function cheats.on_pre_build(player) end else -- The item is not stackable. Insert the item to the player's inventory. - if player.insert({ - name = cursor_stack_name, - count = 1, - }) > 0 then + if + player.insert({ + name = cursor_stack_name, + quality = cursor_stack_quality, + count = 1, + }) > 0 + then storage.creative_mode.personal_cheats.has_restored_cursor_stack[player.index] = game.tick end end @@ -2924,6 +2929,7 @@ function cheats.on_built_entity(player, entity, is_ghost, is_entity_ghost, is_it else player.remove_item({ name = cursor_stack.name, + quality = cursor_stack.quality.name, count = 1, }) end @@ -3064,9 +3070,7 @@ end function cheats.on_preplayer_mined_item(player_index, entity) -- Repair mined item. if storage.creative_mode.personal_cheats.repair_mined_item[player_index] then - if entity.health ~= nil then - entity.health = entity.prototype.get_max_health() - end + util.restore_entity_to_full_health(entity) end end diff --git a/scripts/creative-chest-util.lua b/scripts/creative-chest-util.lua index 51bf621..913240d 100644 --- a/scripts/creative-chest-util.lua +++ b/scripts/creative-chest-util.lua @@ -219,7 +219,11 @@ function creative_chest_util.refill_chests( -- Fill the slot only if it is not filtered out. if next_filtered_slot_to_check <= 0 or filtered_slots[next_filtered_slot_to_check] ~= slot then local item = creative_chest_util.get_item_at(i, contain_hidden_items) - inventory[displayed_slot].set_stack({ name = item.name, count = item.stack_size }) + inventory[displayed_slot].set_stack({ + name = item.name, + quality = chest.quality.name, + count = item.stack_size, + }) displayed_slot = displayed_slot + 1 else -- Clear the slot. Only Original Display Mode will create holes in between. diff --git a/scripts/creative-chest.lua b/scripts/creative-chest.lua deleted file mode 100644 index 66770b1..0000000 --- a/scripts/creative-chest.lua +++ /dev/null @@ -1,16 +0,0 @@ --- This file contains variables or functions that are related to the Creative Chest in this mod. -if not creative_chest then - creative_chest = {} -end - --- Processes the tables related to Creative Chests in storage. -function creative_chest.tick() - -- Refill creative-chests. - storage.creative_mode.creative_chest_next_update_group, storage.creative_mode.creative_chest_next_update_group_subindex = - creative_chest_util.refill_chests( - storage.creative_mode.creative_chest_data_groups, - storage.creative_mode.creative_chest_next_update_group, - storage.creative_mode.creative_chest_next_update_group_subindex, - storage.creative_mode.last_creative_provider_chest_contain_hidden_items - ) -end diff --git a/scripts/creative-lab.lua b/scripts/creative-lab.lua index 2a7cf37..6067803 100644 --- a/scripts/creative-lab.lua +++ b/scripts/creative-lab.lua @@ -38,7 +38,11 @@ function creative_lab.tick() -- Update the lab. if not creative_lab.to_be_deconstructed(creative_lab.force) then for _, item in ipairs(storage.tool_item_list) do - creative_lab.insert({ name = item.name, count = prototypes.item[item.name].stack_size }) + creative_lab.insert({ + name = item.name, + quality = creative_lab.quality.name, + count = prototypes.item[item.name].stack_size, + }) end end -- Prepare for the next lab. diff --git a/scripts/creative-provider-chest.lua b/scripts/creative-provider-chest.lua deleted file mode 100644 index 2766b36..0000000 --- a/scripts/creative-provider-chest.lua +++ /dev/null @@ -1,16 +0,0 @@ --- This file contains variables or functions that are related to the Creative Provider Chest in this mod. -if not creative_provider_chest then - creative_provider_chest = {} -end - --- Processes the tables related to Creative Provider Chests in storage. -function creative_provider_chest.tick() - -- Refill creative-provider-chests. - storage.creative_mode.creative_provider_chest_next_update_group, storage.creative_mode.creative_provider_chest_next_update_group_subindex = - creative_chest_util.refill_chests( - storage.creative_mode.creative_provider_chest_data_groups, - storage.creative_mode.creative_provider_chest_next_update_group, - storage.creative_mode.creative_provider_chest_next_update_group_subindex, - storage.creative_mode.last_creative_provider_chest_contain_hidden_items - ) -end diff --git a/scripts/duplicating-chest-util.lua b/scripts/duplicating-chest-util.lua index df0483a..322db2e 100644 --- a/scripts/duplicating-chest-util.lua +++ b/scripts/duplicating-chest-util.lua @@ -12,11 +12,11 @@ function duplicating_chest_util.get_inventory(chest) return inventory end --- Fills the inventory of the given chest with the given item prototype. -local function fill_chest_with_item(chest, item_prototype) +-- Fills the inventory of the given chest with the given item prototype at the given quality. +local function fill_chest_with_item(chest, item_prototype, quality) local inventory = duplicating_chest_util.get_inventory(chest) for i = 1, #inventory, 1 do - inventory[i].set_stack(item_prototype.name) + inventory[i].set_stack({ name = item_prototype.name, quality = quality }) end end @@ -33,6 +33,7 @@ function duplicating_chest_util.duplicate_contents(chest_datas, next_chest_to_up local chest = chest_data.entity local lock_item = chest_data.lock_item local locked_item_name = chest_data.locked_item_name + local locked_item_quality = chest_data.locked_item_quality if chest.valid then if not chest.to_be_deconstructed(chest.force) then @@ -43,8 +44,8 @@ function duplicating_chest_util.duplicate_contents(chest_datas, next_chest_to_up item_to_be_duplicated = prototypes.item[locked_item_name] end if item_to_be_duplicated then - -- The locked item is valid. Fill the chest with such item. - fill_chest_with_item(chest, item_to_be_duplicated) + -- The locked item is valid. Fill the chest with such item, preserving its quality. + fill_chest_with_item(chest, item_to_be_duplicated, locked_item_quality) else local inventory = duplicating_chest_util.get_inventory(chest) local item = inventory[1] @@ -59,6 +60,7 @@ function duplicating_chest_util.duplicate_contents(chest_datas, next_chest_to_up -- Lock the item if it should be locked. if lock_item then chest_data.locked_item_name = item.name + chest_data.locked_item_quality = item.quality.name end end end @@ -128,6 +130,7 @@ function duplicating_chest_util.on_entity_copied_pasted(source_chest, destinatio if source_item ~= nil and source_item.valid_for_read then destination_inventory.insert({ name = source_item.name, + quality = source_item.quality.name, count = source_item.prototype.stack_size * #destination_inventory, }) end @@ -135,5 +138,6 @@ function duplicating_chest_util.on_entity_copied_pasted(source_chest, destinatio if source_data and destination_data then destination_data.lock_item = source_data.lock_item destination_data.locked_item_name = source_data.locked_item_name + destination_data.locked_item_quality = source_data.locked_item_quality end end diff --git a/scripts/duplicator.lua b/scripts/duplicator.lua index f3fe519..1e23d8e 100644 --- a/scripts/duplicator.lua +++ b/scripts/duplicator.lua @@ -29,13 +29,12 @@ function duplicator.tick() local pos = duplicator.position local dir = duplicator.direction local shift = duplicator_shift[dir] + -- Pass the whole {name, quality} filter object through so duplication targets the configured + -- quality (quality set) or any quality of the name (quality unset). local filter = nil if duplicator.use_filters then filter = duplicator.get_filter(1) end - if filter then - filter = filter.name - end -- Duplicate the items in front of it. item_providers_util.output_or_remove_item( surf, diff --git a/scripts/events.lua b/scripts/events.lua index f79cea3..ead42ab 100644 --- a/scripts/events.lua +++ b/scripts/events.lua @@ -109,127 +109,6 @@ function events.on_configuration_changed(data) -- Create main menu button if Creative Mode is enabled. gui_menu.create_or_destroy_main_menu_open_button_for_all_players() end - if our_mod_old_version <= "1.8.0" and our_mod_new_version >= "1.8.1" then - -- We can clear out some of these tables. It MAY be a lot of info if someone's built a lot. - storage.creative_mode.heat_source = nil - storage.creative_mode.heat_void = nil - storage.creative_mode.void_requester_chest = nil - storage.creative_mode.void_chest = nil - storage.creative_mode.void_storage_chest = nil - storage.creative_mode.super_roboport = nil - storage.creative_mode.fluid_source = nil - storage.creative_mode.super_radar = nil - storage.creative_mode.super_beacon = nil - storage.creative_mode.energy_void = nil - storage.creative_mode.passive_energy_void = nil - storage.creative_mode.passive_energy_source = nil - storage.creative_mode.energy_source = nil - -- Ensure the tables for the new creative/providers chests exist. - if not storage.creative_mode.new_creative_chest then - storage.creative_mode.new_creative_chests = {} - end - if not storage.creative_mode.new_creative_provider_chest then - storage.creative_mode.new_creative_provider_chest = {} - end - for _, surface in pairs(game.surfaces) do - -- Find all our entities so we can get them updated to the new methods. - -- We could just iterate our tables before clearing them, but this guarantees we get everything, and some data won't be good due to the prototype changes making the old entity references invalid. - for _, entity in - ipairs(surface.find_entities_filtered({ - name = { - creative_mode_defines.names.entities.void_chest, - creative_mode_defines.names.entities.void_storage_chest, - creative_mode_defines.names.entities.void_requester_chest, - creative_mode_defines.names.entities.heat_source, - creative_mode_defines.names.entities.heat_void, - creative_mode_defines.names.entities.creative_chest, - creative_mode_defines.names.entities.creative_provider_chest, - }, - })) - do - if entity.name == creative_mode_defines.names.entities.creative_chest then - -- We need to replace all the old creative chests with the new one - if entity.valid then - -- First we need to find the data for this chest - local old_data, group_number = creative_chest_util.get_creative_chest_data_group_number( - entity, - storage.creative_mode.creative_chest_data_groups - ) - -- Make the new chest over the old one - local new_entity = entity.surface.create_entity({ - name = creative_mode_defines.names.entities.new_creative_chest, - position = entity.position, - force = entity.force, - fast_replace = true, - spill = false, - raise_built = false, - create_build_effect_smoke = false, - }) - if old_data == nil or group_number == 0 then --No data found, or an invalid group number. - --Make a new chest and register it as if it were just built. - global_util.register_entity(new_entity) - else -- We have good data, so copy it to our new chest - new_entity.remove_unfiltered_items = true -- We don't want anything except our filters in these - local chest_data = { - entity = new_entity, -- The new-creative-chest entity. - group = group_number, -- Same group as the old chest. - filtered_slots = old_data.filtered_slots, -- The slot indexes that are filtered out in this chest. - inventory_display_mode = old_data.inventory_display_mode, -- The inventory display mode. Not very useful in the new chests. - is_cargo_wagon = false, -- Whether the entity is cargo wagon, such that when its speed is not 0, we should find the cargo-wagon inventory if output inventory is nil (when it is moving). - } - table.insert(storage.creative_mode.new_creative_chests, chest_data) - -- Now that all our data is set, set the filters. - creative_chest_util.set_chest_filter(chest_data) - end - entity.destroy() -- Destroy the old chest - it's useless. - end - elseif entity.name == creative_mode_defines.names.entities.creative_provider_chest then - -- We need to replace all the old creative provider chests with the new one - if entity.valid then - -- First we need to find the data for this chest - local old_data, group_number = creative_chest_util.get_creative_chest_data_group_number( - entity, - storage.creative_mode.creative_provider_chest_data_groups - ) - -- Make the new chest over the old one - local new_entity = entity.surface.create_entity({ - name = creative_mode_defines.names.entities.new_creative_provider_chest, - position = entity.position, - force = entity.force, - fast_replace = true, - spill = false, - raise_built = false, - create_build_effect_smoke = false, - }) - if old_data == nil or group_number == 0 then --No data found, or an invalid group number. - --Make a new chest and register it as if it were just built. - global_util.register_entity(new_entity) - else -- We have good data, so copy it to our new chest - new_entity.remove_unfiltered_items = true -- We don't want anything except our filters in these - local chest_data = { - entity = new_entity, -- The new-creative-chest entity. - group = group_number, -- Same group as the old chest. - filtered_slots = old_data.filtered_slots, -- The slot indexes that are filtered out in this chest. - inventory_display_mode = old_data.inventory_display_mode, -- The inventory display mode. Not very useful in the new chests. - is_cargo_wagon = false, -- Whether the entity is cargo wagon, such that when its speed is not 0, we should find the cargo-wagon inventory if output inventory is nil (when it is moving). - } - table.insert(storage.creative_mode.new_creative_provider_chests, chest_data) - -- Now that all our data is set, set the filters. - creative_chest_util.set_chest_filter(chest_data) - end - entity.destroy() -- Destroy the old chest - it's useless. - end - else -- For the rest of the types we can just run their placement function again to set what we need - --void_chest, void_storage_chest, void_requester_chest, heat_source, heat_void. - --log("Fixing entity: " .. entity.name) - global_util.register_entity(entity) - end - end - end - -- Remove the tables for creative chests/providers, as we don't need them anymore. - storage.creative_mode.creative_chest_data_groups = nil - storage.creative_mode.creative_provider_chest_data_groups = nil - end end -- Make sure the main menu and modifer popup is closed whenever our mod version has changed. @@ -313,7 +192,6 @@ function events.on_tick() creative_chest_util.tick() creative_lab.tick() void_lab.tick() - -- autofill_requester_chest.tick() duplicating_chest.tick() duplicating_provider_chest.tick() creative_cargo_wagon.tick() @@ -428,12 +306,6 @@ local function clear_inventory_before_mined_if_needed(entity) return true end - -- Autofill requester chest. - if entity.name == creative_mode_defines.names.entities.autofill_requester_chest then - entity.get_output_inventory().clear() - return true - end - -- Duplicating chest family. if is_entity_duplicating_chest_family(entity) then local inventory = duplicating_chest_util.get_inventory(entity) diff --git a/scripts/global-util.lua b/scripts/global-util.lua index 9f0a2ff..3b959ee 100644 --- a/scripts/global-util.lua +++ b/scripts/global-util.lua @@ -30,12 +30,6 @@ function global_util.initialize_or_update_global() if not storage.creative_mode.creative_provider_chest_next_group then storage.creative_mode.creative_provider_chest_next_group = 1 end - if not storage.creative_mode.autofill_requester_chest then - storage.creative_mode.autofill_requester_chest = {} - end - if not storage.creative_mode.autofill_requester_chest_next_update_index then - storage.creative_mode.autofill_requester_chest_next_update_index = 1 - end if not storage.creative_mode.duplicating_chest_data then storage.creative_mode.duplicating_chest_data = {} end @@ -540,8 +534,19 @@ function global_util.renew_item_lists() -- All enemy items created by us. They are hidden as well but should be included in the creative chests even if they do not contain hidden items. storage.hidden_creative_enemy_item_list = {} - -- List of items with type equals to "tool" + -- Science packs the Creative Lab refills with. Built from the lab's own inputs + -- (normalized at the data stage to exactly the items real labs consume) rather + -- than the "tool" prototype type, which was removed in Factorio 2.1. storage.tool_item_list = {} + local lab_inputs = prototypes.entity[creative_mode_defines.names.entities.creative_lab].lab_inputs + if lab_inputs then + for _, item_name in pairs(lab_inputs) do + local item_prototype = prototypes.item[item_name] + if item_prototype then + table.insert(storage.tool_item_list, item_prototype) + end + end + end for _, item in pairs(prototypes.item) do if item.hidden then @@ -562,17 +567,11 @@ function global_util.renew_item_lists() -- Non-hidden item. table.insert(storage.non_hidden_item_list, item) end - - if item.type == "tool" then - -- Tool item. - table.insert(storage.tool_item_list, item) - end end global_util.remove_obsolete_items(storage.non_hidden_item_list) global_util.remove_obsolete_items(storage.non_creative_hidden_item_list) global_util.remove_obsolete_items(storage.hidden_creative_enemy_item_list) - global_util.remove_obsolete_items(storage.tool_item_list) -- Update data for the Creative Chest family. creative_chest_util.update_item_lists_data() @@ -608,9 +607,6 @@ end -- Look up table for matching entity-register functions according to the entity name. local register_entity_look_up_functions = { - [creative_mode_defines.names.entities.autofill_requester_chest] = function(entity) - table.insert(storage.creative_mode.autofill_requester_chest, entity) - end, [creative_mode_defines.names.entities.new_creative_chest] = function(entity) entity.remove_unfiltered_items = true -- We don't want anything except our set items in these chest_data = { @@ -654,14 +650,12 @@ local register_entity_look_up_functions = { storage.creative_mode.creative_provider_chest_next_place_group = 1 end end, - [creative_mode_defines.names.entities.new_autofill_requester_chest] = function(entity) - table.insert(storage.creative_mode.autofill_requester_chest, entity) - end, [creative_mode_defines.names.entities.duplicating_chest] = function(entity) table.insert(storage.creative_mode.duplicating_chest_data, { entity = entity, lock_item = false, -- Whether the item to be duplicated is locked. locked_item_name = nil, -- Name of the item to be duplicated even if the first slot is empty. + locked_item_quality = nil, -- Quality of the locked item to be duplicated. }) end, [creative_mode_defines.names.entities.duplicating_provider_chest] = function(entity) @@ -669,6 +663,7 @@ local register_entity_look_up_functions = { entity = entity, lock_item = false, -- Whether the item to be duplicated is locked. locked_item_name = nil, -- Name of the item to be duplicated even if the first slot is empty. + locked_item_quality = nil, -- Quality of the locked item to be duplicated. }) end, [creative_mode_defines.names.entities.void_requester_chest] = function(entity) @@ -713,6 +708,7 @@ local register_entity_look_up_functions = { entity = entity, lock_item = false, -- Whether the item to be duplicated is locked. locked_item_name = nil, -- Name of the item to be duplicated even if the first slot is empty. + locked_item_quality = nil, -- Quality of the locked item to be duplicated. }) end, [creative_mode_defines.names.entities.void_cargo_wagon] = function(entity) diff --git a/scripts/gui-entity.lua b/scripts/gui-entity.lua index 86f23bf..f6f5739 100644 --- a/scripts/gui-entity.lua +++ b/scripts/gui-entity.lua @@ -1159,11 +1159,13 @@ function gui_entity.on_gui_checked_state_changed(element, element_name, player) local item = inventory[1] if item ~= nil and item.valid_for_read then data.locked_item_name = item.name + data.locked_item_quality = item.quality.name end end else -- Reset the locked item. data.locked_item_name = nil + data.locked_item_quality = nil end -- Update the GUI for all players who are checking the entity. diff --git a/scripts/item-providers-util.lua b/scripts/item-providers-util.lua index d9e7ae7..166db72 100644 --- a/scripts/item-providers-util.lua +++ b/scripts/item-providers-util.lua @@ -63,6 +63,18 @@ local static_fluidboxes = { ["mining-drill"] = true, } +-- Builds an item identification descriptor (for get_item_count / remove) from a quality-bearing filter. +-- Void/duplicate semantics: a filter with its quality set targets that single quality, so we pass +-- {name, quality}; a filter with no quality set targets all qualities of the name, so we pass the bare +-- name string (in 2.x a string identifier matches every quality, whereas {name=...} alone matches only +-- normal quality). The caller must have already checked that the filter is non-nil. +local function build_item_identification(filter) + if filter.quality then + return { name = filter.name, quality = filter.quality } + end + return filter.name +end + -- List of module inventories that matter source, duplicator and void shouldn't work on. local module_inventories = { [defines.inventory.crafter_modules] = true, -- replaces furnace_modules + assembling_machine_modules (removed in 2.1) @@ -72,30 +84,33 @@ local module_inventories = { } -- Duplicates the first itme in the given inventory. -local function duplicate_first_item_in_inventory(inventory, filter_item_name) +local function duplicate_first_item_in_inventory(inventory, filter) if inventory.is_empty() then return end local item_name = nil + local item_quality = nil -- If filter is set, there must be at least one of that item in the inventory. - if filter_item_name then - if inventory.get_item_count(filter_item_name) > 0 then - item_name = filter_item_name + if filter then + local item_identification = build_item_identification(filter) + if inventory.get_item_count(item_identification) > 0 then + item_name = filter.name + item_quality = filter.quality end else - local k - k, item_name = next(inventory.get_contents()) + local k, item = next(inventory.get_contents()) if k ~= nil then - if item_name then - item_name = item_name.name + if item then + item_name = item.name + item_quality = item.quality end end end if item_name then - print(item_name) local stack = { name = item_name, + quality = item_quality, count = 1, } inventory.insert(stack) @@ -106,7 +121,7 @@ end -- Returns the table of inventories that the given entity has if required, also returns the container type of the entity. local function duplicate_first_item_in_each_inventory( entity, - filter_item_name, + filter, entity_inventories, should_return_entity_inventories ) @@ -120,7 +135,7 @@ local function duplicate_first_item_in_each_inventory( for _, inv in ipairs(entity_inventories) do local inventory = entity.get_inventory(inv) if inventory then - duplicate_first_item_in_inventory(inventory, filter_item_name) + duplicate_first_item_in_inventory(inventory, filter) end end return entity_inventories @@ -136,40 +151,44 @@ local function duplicate_first_item_in_each_inventory( if should_return_entity_inventories then table.insert(entity_inventories, inv) end - duplicate_first_item_in_inventory(inventory, filter_item_name) + duplicate_first_item_in_inventory(inventory, filter) end end return entity_inventories end -- Removes one item from the given inventory. -local function remove_one_item_from_inventory(inventory, filter_item_name) +local function remove_one_item_from_inventory(inventory, filter) if inventory.is_empty() then return end - local item_name = nil -- If filter is set, there must be at least one of that item in the inventory. - if filter_item_name then - if inventory.get_item_count(filter_item_name) > 0 then - item_name = filter_item_name + if filter then + if filter.quality then + -- Quality set -> target that single quality. + if inventory.get_item_count({ name = filter.name, quality = filter.quality }) > 0 then + inventory.remove({ name = filter.name, quality = filter.quality, count = 1 }) + end + else + -- Quality unset -> remove one of that name across all qualities (today's behavior). Find the + -- quality of an actual stack so each tick removes a real item even once normal is exhausted. + for _, item in pairs(inventory.get_contents()) do + if item.name == filter.name then + inventory.remove({ name = item.name, quality = item.quality, count = 1 }) + break + end + end end else local _, item = next(inventory.get_contents()) - item_name = item.name - end - if item_name then - local stack = { - name = item_name, - count = 1, - } - inventory.remove(stack) + inventory.remove({ name = item.name, quality = item.quality, count = 1 }) end end -- Removes one item from an inventory of the given entity. -- Returns the table of inventories that the given entity has if required. -local function remove_one_item_in_entity(entity, filter_item_name, entity_inventories, should_return_entity_inventories) +local function remove_one_item_in_entity(entity, filter, entity_inventories, should_return_entity_inventories) -- If the entity has nothing inside, do nothing. if not entity.has_items_inside() then return entity_inventories @@ -180,7 +199,7 @@ local function remove_one_item_in_entity(entity, filter_item_name, entity_invent for _, inv in ipairs(entity_inventories) do local inventory = entity.get_inventory(inv) if inventory then - remove_one_item_from_inventory(inventory, filter_item_name) + remove_one_item_from_inventory(inventory, filter) end end return entity_inventories @@ -196,7 +215,7 @@ local function remove_one_item_in_entity(entity, filter_item_name, entity_invent if should_return_entity_inventories then table.insert(entity_inventories, inv) end - remove_one_item_from_inventory(inventory, filter_item_name) + remove_one_item_from_inventory(inventory, filter) end end return entity_inventories @@ -210,8 +229,9 @@ local function output_item_stack_according_to_inventory_slot_filters(inventory) local filter = inventory.get_filter(i) if filter then inventory[i].set_stack({ - name = filter, - count = prototypes.item[filter].stack_size, + name = filter.name, + quality = filter.quality, + count = prototypes.item[filter.name].stack_size, }) end end @@ -292,7 +312,7 @@ local function insert_itemstack_on_transport_line_compressed(transport_line, ite end -- Removes the first item on the given transport line. -local function remove_first_item_on_transport_line(line, filter_item_name) +local function remove_first_item_on_transport_line(line, filter) -- Only do action if the line is full at the end (position = 0). if line.can_insert_at(0) and line.can_insert_at(0.1) then return @@ -302,14 +322,24 @@ local function remove_first_item_on_transport_line(line, filter_item_name) return end -- Check the first item. - local item_name = line[1].name + local item = line[1] + local item_name = item.name + local item_quality = item.quality and item.quality.name or nil -- If filter is set, but the item mismatch, stop working. - if filter_item_name and filter_item_name ~= item_name then - return + if filter then + -- Name must match; if the filter pins a quality, the item's quality must match too. A filter with + -- no quality set matches the name across all qualities (today's behavior). + if filter.name ~= item_name then + return + end + if filter.quality and filter.quality ~= item_quality then + return + end end - -- Remove the item. + -- Remove the item, preserving its quality. local stack = { name = item_name, + quality = item_quality, count = 1, } line.remove_item(stack) @@ -322,7 +352,7 @@ local function output_or_remove_item_on_transport_line( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, output_last_item_position_on_belt ) @@ -350,21 +380,24 @@ local function output_or_remove_item_on_transport_line( end elseif operation_mode == output_or_remove_item_operation_mode.remove_mode then -- matter-void - remove_first_item_on_transport_line(transport_line, filter_item_name) + remove_first_item_on_transport_line(transport_line, filter) return nil else -- matter-duplicator local item_name = nil - if filter_item_name then - if transport_line.get_item_count(filter_item_name) > 0 then - item_name = filter_item_name + local item_quality = nil + if filter then + local item_identification = build_item_identification(filter) + if transport_line.get_item_count(item_identification) > 0 then + item_name = filter.name + item_quality = filter.quality end else - local k - k, item_name = next(transport_line.get_contents()) + local k, item = next(transport_line.get_contents()) if k ~= nil then - if item_name then - item_name = item_name.name + if item then + item_name = item.name + item_quality = item.quality end end end @@ -373,6 +406,7 @@ local function output_or_remove_item_on_transport_line( if item_name then stack = { name = item_name, + quality = item_quality, count = 1, } else @@ -467,17 +501,18 @@ function item_providers_util.output_or_remove_item( shift_x, shift_y, direction, - filter_item_name, + filter, operation_mode, output_item_slot, entity_data ) - -- Create a simple item stack according to the given item name of we are going to output it. + -- Create a simple item stack according to the given quality-bearing filter if we are going to output it. local output_stack = nil - if operation_mode == output_or_remove_item_operation_mode.output_mode and filter_item_name then + if operation_mode == output_or_remove_item_operation_mode.output_mode and filter then output_stack = { - name = filter_item_name, + name = filter.name, + quality = filter.quality, count = 1, } end @@ -904,7 +939,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, entity_data.slot1_last_item_position_on_belt ) @@ -914,7 +949,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, entity_data.slot2_last_item_position_on_belt ) @@ -925,7 +960,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, entity_data.line1_last_item_position_on_belt ) @@ -935,7 +970,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, nil ) @@ -949,7 +984,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, entity_data.slot1_last_item_position_on_belt ) @@ -959,7 +994,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, entity_data.slot2_last_item_position_on_belt ) @@ -970,7 +1005,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, entity_data.line2_last_item_position_on_belt ) @@ -980,7 +1015,7 @@ function item_providers_util.output_or_remove_item( belt_speed, operation_mode, output_stack, - filter_item_name, + filter, should_use_insert_at_back, nil ) @@ -1104,7 +1139,7 @@ function item_providers_util.output_or_remove_item( end elseif operation_mode == output_or_remove_item_operation_mode.remove_mode then local output_inventory_name = static_item_containers_with_output_slots[other_inventory_entity.type] - if not filter_item_name and output_inventory_name then + if not filter and output_inventory_name then -- No filter, and the target entity has output slots. -- Clear the output slots. local inventory = other_inventory_entity.get_inventory(output_inventory_name) @@ -1114,7 +1149,7 @@ function item_providers_util.output_or_remove_item( else entity_data.last_working_static_container_inventories = remove_one_item_in_entity( other_inventory_entity, - filter_item_name, + filter, entity_data.last_working_static_container_inventories, true ) @@ -1122,7 +1157,7 @@ function item_providers_util.output_or_remove_item( else entity_data.last_working_static_container_inventories = duplicate_first_item_in_each_inventory( other_inventory_entity, - filter_item_name, + filter, entity_data.last_working_static_container_inventories, true ) @@ -1141,7 +1176,7 @@ function item_providers_util.output_or_remove_item( end -- Doesn't work on fluid if filter is set. - if filter_item_name == nil then + if filter == nil then if operation_mode == output_or_remove_item_operation_mode.remove_mode then fluid_providers_util.remove_all_fluids(other_fluidbox_entity) elseif operation_mode == output_or_remove_item_operation_mode.duplicate_mode then @@ -1168,9 +1203,9 @@ function item_providers_util.output_or_remove_item( output_item_stack_according_to_inventories_slot_filters(train_entity, nil, false) end elseif operation_mode == output_or_remove_item_operation_mode.remove_mode then - remove_one_item_in_entity(train_entity, filter_item_name, nil, false) + remove_one_item_in_entity(train_entity, filter, nil, false) else - duplicate_first_item_in_each_inventory(train_entity, filter_item_name, nil, false) + duplicate_first_item_in_each_inventory(train_entity, filter, nil, false) end end end @@ -1214,9 +1249,9 @@ function item_providers_util.output_or_remove_item( output_item_stack_according_to_inventories_slot_filters(car, nil, false) end elseif operation_mode == output_or_remove_item_operation_mode.remove_mode then - remove_one_item_in_entity(car, filter_item_name, nil, false) + remove_one_item_in_entity(car, filter, nil, false) else - duplicate_first_item_in_each_inventory(car, filter_item_name, nil, false) + duplicate_first_item_in_each_inventory(car, filter, nil, false) end end end @@ -1260,11 +1295,11 @@ function item_providers_util.output_or_remove_item( end elseif operation_mode == output_or_remove_item_operation_mode.remove_mode then for _, player in ipairs(player_entities) do - remove_one_item_in_entity(player, filter_item_name, nil, false) + remove_one_item_in_entity(player, filter, nil, false) end else for _, player in ipairs(player_entities) do - duplicate_first_item_in_each_inventory(player, filter_item_name, nil, false) + duplicate_first_item_in_each_inventory(player, filter, nil, false) end end end @@ -1289,7 +1324,11 @@ function item_providers_util.output_or_remove_item( name = "item-on-ground", })) do - if filter_item_name == nil or item.stack.name == filter_item_name then + -- No filter -> remove everything. Filter with name only -> match the name across all qualities. + -- Filter with a quality -> match that name and quality only. + local matches = filter == nil + or (item.stack.name == filter.name and (filter.quality == nil or item.stack.quality.name == filter.quality)) + if matches then item.destroy() end end diff --git a/scripts/item-source.lua b/scripts/item-source.lua index 90fe525..ddedb89 100644 --- a/scripts/item-source.lua +++ b/scripts/item-source.lua @@ -24,19 +24,14 @@ function item_source.tick() if item_source.active and not item_source.to_be_deconstructed(item_source.force) then -- Check if it is enabled according to its circuit network state and logistic network state. if util.is_inserter_enabled(item_source) then - -- Get the item names in the 2 filter slots of the matter-source. + -- Get the {name, quality} filter objects in the 2 filter slots of the matter-source, so the + -- output stacks carry the configured quality instead of defaulting to normal. local slot1 = nil local slot2 = nil if item_source.use_filters then slot1 = item_source.get_filter(1) slot2 = item_source.get_filter(2) end - if slot1 then - slot1 = slot1.name - end - if slot2 then - slot2 = slot2.name - end -- Get the matter-source's surface, position and shift for output, so we can drop items accordingly. local surf = item_source.surface local pos = item_source.position diff --git a/scripts/item-void.lua b/scripts/item-void.lua index 3c10120..50f672e 100644 --- a/scripts/item-void.lua +++ b/scripts/item-void.lua @@ -29,13 +29,12 @@ function item_void.tick() local pos = item_void.position local dir = item_void.direction local shift = item_void_shift[dir] + -- Pass the whole {name, quality} filter object through so the engine can target a single + -- quality (quality set), all qualities of the name (quality unset), or everything (no filter). local filter = nil if item_void.use_filters then filter = item_void.get_filter(1) end - if filter then - filter = filter.name - end -- Remove the items in front of it. item_providers_util.output_or_remove_item( surf, diff --git a/scripts/magic-wand-healer.lua b/scripts/magic-wand-healer.lua index 78df8e6..c6a8da3 100644 --- a/scripts/magic-wand-healer.lua +++ b/scripts/magic-wand-healer.lua @@ -173,7 +173,7 @@ function magic_wand_healer.on_player_selected_area(player, area, item_name, enti if health ~= nil then -- The entity has health. It can be healed. if entity.force and magic_wand_healer.get_heal_entities_on_force(player, entity.force) then - local max_health = entity.prototype.get_max_health() -- For all entities in general + local max_health = entity.max_health -- For all entities in general (quality-aware ceiling) if entity.type == "character" then -- But players have individual and force bonuses to max health local p = entity.player max_health = max_health + p.force.character_health_bonus + p.character_health_bonus diff --git a/scripts/random-item-source.lua b/scripts/random-item-source.lua index 8654a2d..dd8f328 100644 --- a/scripts/random-item-source.lua +++ b/scripts/random-item-source.lua @@ -12,7 +12,8 @@ local random_item_source_shift = { [defines.direction.west] = { x1 = 0.9, y1 = -0.3, x2 = 0.9, y2 = 0.3, x0 = 0.9, y0 = 0 }, } --- Picks the name of item to be generated according to the given array of circuit signals. +-- Picks a quality-bearing {name, quality} filter for the item to be generated according to the given +-- array of circuit signals. Returns nil if no item signal is picked. local function pick_item_from_signals(signals) if signals == nil then return nil @@ -38,7 +39,7 @@ local function pick_item_from_signals(signals) if total_count <= 0 then local signal = signal_data.signal if signal.type == "item" then - return signal.name + return { name = signal.name, quality = signal.quality } end return nil end diff --git a/scripts/util.lua b/scripts/util.lua index fbb0cb5..a336df3 100644 --- a/scripts/util.lua +++ b/scripts/util.lua @@ -102,6 +102,16 @@ function util.get_tile_bb(position) return { { math_floor(position.x), math_floor(position.y) }, { math_ceil(position.x), math_ceil(position.y) } } end +-- Restores the given entity to its true, quality-aware full health. +-- Uses the instance read entity.max_health (which already accounts for the entity's +-- quality tier) rather than the level-0 prototype.get_max_health(), so higher-tier +-- entities are restored to their actual ceiling. No-ops on entities without health. +function util.restore_entity_to_full_health(entity) + if entity.health ~= nil then + entity.health = entity.max_health + end +end + -- Returns whether the given entity has at least one inventory. function util.has_inventory(entity) for _, inv in pairs(defines.inventory) do diff --git a/settings.lua b/settings.lua index 310e1ef..cdad374 100644 --- a/settings.lua +++ b/settings.lua @@ -36,16 +36,6 @@ data:extend({ default_value = false, order = "b-b", }, - { - -- Autofill requester chest size. - type = "int-setting", - name = creative_mode_defines.names.settings.autofill_requester_chest_size, - setting_type = "startup", - default_value = 48, - minimum_value = 1, - maximum_value = 255, - order = "c", - }, { -- Duplicating chest size. type = "int-setting", diff --git a/verify.py b/verify.py index a8ed253..9cfc536 100644 --- a/verify.py +++ b/verify.py @@ -513,6 +513,59 @@ def cmd_behavior(args: argparse.Namespace) -> int: "true", "super_quality_module_beacon_insertable", ), + # matter_source_outputs_quality_placed: place a Matter Source facing north with a wooden + # chest one tile in front of its output (south, +y). Set the source's slot-1 filter to a + # legendary item via the native inserter filter (use_filters + set_filter). raise_built + # registers it into item_source.tick(); after ticking the chest must hold a legendary stack, + # proving the filter's quality is threaded through to the output stack (was stripped to name). + _assert_rcon( + sb, + '/c local s = game.surfaces["cm_verify"] ' + 'local chest = s.create_entity{name="wooden-chest", position={20, 11}, force="player"} ' + 'local src = s.create_entity{name="creative-mod_item-source", position={20, 10}, direction=defines.direction.north, force="player", raise_built=true} ' + "src.use_filters = true " + 'src.set_filter(1, {name="iron-plate", quality="legendary"}) ' + "storage = storage or {} storage.cm_verify_matter_source_quality = chest " + "rcon.print(tostring(chest.valid and src.valid))", + "true", + "matter_source_outputs_quality_placed", + ), + # matter_void_targets_quality_placed: place a wooden chest pre-loaded with one normal and one + # rare iron-plate, with a Matter Void facing it (north of the chest, voiding southward). Set + # the void's slot-1 filter to rare iron-plate. After ticking, the rare must be gone and the + # normal untouched — proving the void targets the filter's quality instead of all qualities. + _assert_rcon( + sb, + '/c local s = game.surfaces["cm_verify"] ' + 'local chest = s.create_entity{name="wooden-chest", position={22, 11}, force="player"} ' + 'chest.insert({name="iron-plate", quality="normal", count=1}) ' + 'chest.insert({name="iron-plate", quality="rare", count=1}) ' + 'local void = s.create_entity{name="creative-mod_item-void", position={22, 12}, direction=defines.direction.north, force="player", raise_built=true} ' + "void.use_filters = true " + 'void.set_filter(1, {name="iron-plate", quality="rare"}) ' + "storage = storage or {} storage.cm_verify_matter_void_targeted = chest " + "rcon.print(tostring(chest.valid and void.valid))", + "true", + "matter_void_targets_quality_placed", + ), + # matter_void_unset_quality_removes_all_placed: same setup, but the void's filter is set to a + # name only (no quality). After ticking, BOTH the normal and rare must be gone — proving the + # unset-quality filter still spans all qualities (today's behavior) and pinning down that + # get_filter() returns a nil quality, not a concrete "normal", when none is picked. + _assert_rcon( + sb, + '/c local s = game.surfaces["cm_verify"] ' + 'local chest = s.create_entity{name="wooden-chest", position={24, 11}, force="player"} ' + 'chest.insert({name="iron-plate", quality="normal", count=1}) ' + 'chest.insert({name="iron-plate", quality="rare", count=1}) ' + 'local void = s.create_entity{name="creative-mod_item-void", position={24, 12}, direction=defines.direction.north, force="player", raise_built=true} ' + "void.use_filters = true " + 'void.set_filter(1, {name="iron-plate"}) ' + "storage = storage or {} storage.cm_verify_matter_void_unset = chest " + "rcon.print(tostring(chest.valid and void.valid))", + "true", + "matter_void_unset_quality_removes_all_placed", + ), ] # Let the server tick so the per-tick refill runs on the just-placed thruster. time.sleep(1.0) @@ -561,6 +614,47 @@ def cmd_behavior(args: argparse.Namespace) -> int: "super_boiler_heats_fluid", ) ) + results.append( + # matter_source_outputs_quality: after ticking, the chest fed by the legendary-filtered + # Matter Source must hold a legendary iron-plate. On pre-fix master the filter is stripped to + # its name, so the output stack defaults to normal quality and this read fails. + _assert_rcon( + sb, + "/c local chest = storage.cm_verify_matter_source_quality " + "if not (chest and chest.valid) then rcon.print('no-entity') return end " + "rcon.print(tostring(chest.get_item_count({name='iron-plate', quality='legendary'}) > 0))", + "true", + "matter_source_outputs_quality", + ) + ) + results.append( + # matter_void_targets_quality: after ticking, the rare iron-plate must be gone while the + # normal one remains. On pre-fix master the void matches by name across all qualities, so it + # would remove the normal too (and could never single out the rare), failing this read. + _assert_rcon( + sb, + "/c local chest = storage.cm_verify_matter_void_targeted " + "if not (chest and chest.valid) then rcon.print('no-entity') return end " + "local rare = chest.get_item_count({name='iron-plate', quality='rare'}) " + "local normal = chest.get_item_count({name='iron-plate', quality='normal'}) " + "rcon.print(tostring(rare == 0 and normal == 1))", + "true", + "matter_void_targets_quality", + ) + ) + results.append( + # matter_void_unset_quality_removes_all: after ticking, BOTH qualities must be gone when the + # filter has only a name. This both preserves today's "remove all qualities" behavior and + # confirms get_filter() hands back a nil quality (not "normal") when none is picked. + _assert_rcon( + sb, + "/c local chest = storage.cm_verify_matter_void_unset " + "if not (chest and chest.valid) then rcon.print('no-entity') return end " + "rcon.print(tostring(chest.get_item_count('iron-plate') == 0))", + "true", + "matter_void_unset_quality_removes_all", + ) + ) finally: _terminate_server(server) @@ -588,9 +682,15 @@ def cmd_behavior(args: argparse.Namespace) -> int: "super_boiler_placed", "super_quality_module_effect_applied", "super_quality_module_beacon_insertable", + "matter_source_outputs_quality_placed", + "matter_void_targets_quality_placed", + "matter_void_unset_quality_removes_all_placed", "creative_thruster_refuels", "item_source_feeds_crafter", "super_boiler_heats_fluid", + "matter_source_outputs_quality", + "matter_void_targets_quality", + "matter_void_unset_quality_removes_all", ), results, )